Поддержка порядка байтов в пользовательском пространстве Linux

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

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

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

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

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

Ответ 1

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

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

Ответ 2

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

Ответ 3

Если у вас есть доступ к неоновому сопроцессору, и память смежна (например, видеокадр), вы можете выполнить 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"
        );
}

Ответ 4

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

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

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

Ответ 5

Зачем вам нужен 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;

Ответ 6

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

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

Ответ 7

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

Ответ 8

Вы можете просто написать свои собственные (они основаны на подпрограмме 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

Ответ 9

Вы можете использовать стандартные функции отключения сети в 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

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