4

我正在用 C 语言在 Linux 上编写一个程序来分析嵌入式系统生成的核心文件。核心文件可能是小端 (ARM) 或大端 (MIPS),分析它们的程序可能在小端主机 (x86) 或大端 (PowerPC) 上运行。

通过查看标题,我知道核心是 LE 还是 BE。我宁愿我的程序不需要知道它运行的主机是小端还是大端,我想使用 API 为我处理它。如果没有更好的选择,我想我会开始依赖#ifdef __BIG_ENDIAN__。

在 Linux 内核中,我们有 cpu_to_le32 等人将本机字节顺序转换为小端等。在用户空间中有 htonl 等人,它们从本机转换为大端,但我找不到本地到小端的等价物。

任何人都可以为用户空间推荐一个合适的 API 吗?

编辑:为了清楚起见,我正在寻找一个 API,它已经知道我的 CPU 是大端还是小端并相应地交换字节。我不想为此用#ifdefs 乱扔我的代码。我不只是在寻找代码片段来交换字节;谢谢你,但这不是重点。

4

9 回答 9

19

#include <arpa/inet.h>很好且便携,但只有保证{ntoh,hton}{s,l}。如果您需要对 64 位值进行转换,或者需要在 big-endian 上进行 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>提供这些相同的宏。

于 2009-05-18T20:37:40.217 回答
5

如果您可以访问 neon 协处理器并且内存是连续的(例如视频帧),您可以通过这种方式使用 q 寄存器(128 字节)在帧上执行 swap16;当然,您必须注意对齐问题

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"
        );
}
于 2012-11-05T14:42:41.433 回答
3

根据您实际尝试做的事情(阅读 ELF 核心转储文件而不必担心字节序问题),我相信使用 libelf(此处提供了一个不错教程)将是一个不错的选择。

这个库透明地与大端和小端 ELF 文件一起工作,并且在 Linux 下运行得很好,尽管它起源于 FreeBSD(通常的“./configure”和“make”序列就是你构建它所需要的全部)。对于咧嘴笑,我在 x86 核心文件和 MIPS big-endian 核心文件上尝试了“读取程序头表”示例(稍作修改以使其构建),它似乎“正常工作”。

于 2009-05-18T19:11:09.407 回答
3

如果您将文件视为字节数组,那么您可以控制选择字节的顺序,并且 CPU 的字节序实际上最终无关紧要。

这在处理对齐问题方面也非常有用。您的核心转储中可能包含未对齐的引用。我知道这不太可能,但也可能是由于腐败。这是通过将文件视为字节数组来避免的另一个问题。

我在实现 jhead 时使用了这种方法。

于 2009-05-18T20:54:23.397 回答
2

您可以使用 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

网络字节顺序是大端。因此,这些功能意味着:

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

希望这可以帮助。

于 2009-05-18T19:49:29.767 回答
1

为什么需要 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;
于 2009-05-17T13:40:14.117 回答
1

查看 /usr/include/linux/byteorder/ 中内核提供的头文件,例如 __cpu_to_be32() 和 __be32_to_cpu()

还可以查看 /usr/include/linux/types.h 文件,您可以在其中将类型定义为显式的大/小端普通整数,这很有帮助,因为在编译时会检测到任何不匹配。

于 2009-05-17T13:44:49.330 回答
1

鉴于切换 endian-ess很容易,我总是最终使用这样的自定义代码,对我在代码中使用的表示形式保持严格的规则,并在端点(输入和输出)处理字节序。

于 2009-05-17T13:46:04.680 回答
1

您可以自己编写(这些基于 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

如果您对英特尔汇编代码感到满意,您甚至可以这样做:

// 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
于 2009-05-17T13:52:30.047 回答