Поддержка порядка байтов в пользовательском пространстве 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
Надеюсь, что это поможет.