Поддержка порядка байтов в пользовательском пространстве Linux - PullRequest
4 голосов
/ 17 мая 2009

Я пишу программу для Linux на C для анализа файлов ядра, созданных из встроенной системы. Базовые файлы могут быть с прямым порядком байтов (ARM) или с прямым порядком байтов (MIPS), и программа для их анализа может работать на хосте с прямым порядком байтов (x86) или с прямым порядком байтов (PowerPC).

Глядя на заголовки, я знаю, является ли ядро ​​LE или BE. Я предпочел бы, чтобы моя программа не должна была знать, является ли хост, на котором она работает, прямым или младшим порядком байтов, я хотел бы использовать API для ее обработки. Если нет лучшего варианта, думаю, я начну полагаться на #ifdef __BIG_ENDIAN __.

В ядре Linux у нас есть cpu_to_le32 и др. Для преобразования из нативного порядка байтов в младший и т. Д. В пространстве пользователя есть htonl и др., Которые конвертируют из нативного в старший, но не эквивалентны можно найти.

Может кто-нибудь предложить подходящий API для пространства пользователя?

Редактировать : Просто чтобы прояснить ситуацию, я ищу API, который уже знает, является ли мой процессор большим или мало-порядковым, и соответственно меняет местами байты. Я не хочу засорять мой код #ifdefs для этого. Я не просто ищу фрагменты кода для замены байтов; спасибо за это, но не в этом дело.

Ответы [ 9 ]

19 голосов
/ 19 мая 2009

#include <arpa/inet.h> приятный и портативный, но только гарантирует {ntoh,hton}{s,l}. Если вам нужны преобразования для 64-битных значений или обратный порядок байтов для big-endian (где ntoh и hton ничего не делают), этого будет недостаточно.

В Linux (glibc) #include <endian.h> обеспечивает следующее, определенное в соответствии с текущим компьютером.

htobe16  be16toh    htole16  le16toh
htobe32  be32toh    htole32  le32toh
htobe64  be64toh    htole64  le64toh

На * BSD, #include <sys/endian.h> предоставляет эти же макросы.

5 голосов
/ 05 ноября 2012

Если у вас есть доступ к неоновому сопроцессору и память непрерывна (например, видеокадр), вы можете выполнить swap16 для кадра, используя регистры q (128 байт) таким образом; Конечно, вы должны следить за проблемами выравнивания

void swap16(void *__restrict src16)
{
    const void *start = src16;
    const void *end = src16 + FRAME_SIZE;
    asm volatile (
        "1: pld     [%0, #0x100]\n"
        "vld2.8         {q0,q1}, [%0]\n"
        "vmov           q2,q0\n"
        "vst2.8         {q1,q2}, [%0]!\n"
        "cmp            %1,%0\n"
        "bne            1b\n"
        : /* empty output operands */
        : "r" (start), "r" (end)
        : "cc", "memory"
        );
}
3 голосов
/ 19 мая 2009

Если вы обрабатываете файл как байтовый массив, то вы контролируете, в каком порядке вы выбираете байты, и порядковый номер вашего ЦП фактически не имеет значения.

Это также очень полезно с точки зрения решения проблем выравнивания. Ваш основной дамп может содержать ссылки без выравнивания. Я знаю, что это очень маловероятно, но это также может быть связано с коррупцией. Это еще одна проблема, которую можно избежать, рассматривая файл как байтовый массив.

Я использовал этот подход при реализации jhead.

3 голосов
/ 18 мая 2009

Исходя из того, что вы на самом деле пытаетесь сделать (читайте файлы дампа памяти ELF, не беспокоясь о проблемах с порядком байтов), я полагаю, что с помощью libelf, доступно здесь с хорошим руководством здесь , будет хорошим выбором.

Эта библиотека прозрачно работает с файлами ELF с большим и меньшим порядком байтов и прекрасно работает под Linux, несмотря на ее происхождение из FreeBSD (обычные последовательности "./configure" и "make" - это все, что вам нужно для ее создания). Для ухмылок я попробовал пример «чтение таблицы заголовков программы» (с небольшими изменениями, чтобы заставить его построить) для файла ядра x86, а также файла ядра с прямым порядком байтов MIPS, он «просто работает».

2 голосов
/ 18 мая 2009

Вы можете использовать стандартные функции сетевой перестановки в apa / inet.h:

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong); // Host to network
uint16_t htons(uint16_t hostshort); // Host to network
uint32_t ntohl(uint32_t netlong); // Network to host
uint16_t ntohs(uint16_t netshort); // Network to host

Порядок байтов в сети - big-endian. Итак, эти функции означают:

hton*: Host endian to big endian
ntoh*: Big endian to host endian

Надеюсь, это поможет.

1 голос
/ 17 мая 2009

Вы можете просто написать свои собственные (они основаны на рутинах Apple):

static inline uint16_t Swap16(uint16_t x)
{
    return ( (x << 8) | (x >> 8) );
}

static inline uint32_t Swap32(uint32_t x)
{
    return ( (((x ^ (x >> 16 | (x << 16))) & 0xff00ffff) >> 8) ^ (x >> 8 | data << 24) );
}

Тогда вы можете определить условные макросы:

#ifdef __BIG_ENDIAN__
# define htols(x) Swap16(x)
# define htoll(x) Swap32(x)
#else
# define htols(x) (x)
# define htoll(x) (x)
#endif

Если вы довольны кодом ассемблера Intel, вы даже можете сделать это:

// Swap16 is unchanged

static inline uint32_t Swap32(uint32_t x)
{
    __asm__ ("bswap %0" : "+r" (x));
    return ( x );
}
#ifdef __i386__
static inline uint64_t Swap64(uint64_t x)
{
    __asm__ ("bswap  %%eax\n\t"
             "bswap  %%edx\n\t"
             "xchgl  %%eax, %%edx"
             : "+A" (x));
    return ( x );
}
#elif defined(__x86_64__)
static inline uint64_t Swap64( uint64_t x )
{
    __asm__ ("bswap  %0" : "+r" (x));
    return ( x );
}
#endif
1 голос
/ 17 мая 2009

Учитывая, что переключение endian-ess просто , я всегда заканчивал тем, что использовал такой код, придерживаясь строгого правила относительно того, какое представление я использую в коде, и обрабатывая порядок байтов в конечных точках (ввод и вывод).

1 голос
/ 17 мая 2009

Посмотрите на предоставленные ядром заголовки в / usr / include / linux / byteorder /, таких как __cpu_to_be32 () и __be32_to_cpu ()

Взгляните также на файл /usr/include/linux/types.h, в котором вы можете определить типы как явные целые числа с прямым и старшим порядком байтов, которые очень помогают, поскольку любое несоответствие будет обнаружено во время компиляции. *

1 голос
/ 17 мая 2009

Зачем вам API для этого? Просто напишите свою собственную функцию для вызова htonl() (или что-либо, производящее BE), а затем просто переверните байты. Звучит не так сложно.

Что-то вроде:

union {
    struct {
        unsigned char c0;
        unsigned char c1;
        unsigned char c2;
        unsigned char c3;
    } ch;
    uint32_t ui;
} u;
unsigned char t;

u.ui = htonl (hostlong);
t = u.ch.c0; u.ch.c0 = u.ch.c3 ; u.ch.c3 = t;
t = u.ch.c1; u.ch.c1 = u.ch.c2 ; u.ch.c2 = t;
...