12

我正在编写一个 ELF 分析器,但是在正确转换字节序时遇到了一些麻烦。我有函数来确定分析器的字节序和目标文件的字节序。

基本上,有四种可能的情况:

  1. 大端编译分析器在大端目标文件上运行
    • 没有什么需要转换
  2. 大端编译分析器在小端目标文件上运行
    • 字节顺序需要交换,但 ntohs/l() 和 htons/l() 在大端机器上都是空宏,因此它们不会交换字节顺序。这就是问题
  3. 小端编译分析器在大端目标文件上运行
    • 字节顺序需要交换,所以使用 htons() 交换字节顺序
  4. 小端编译分析器在小端目标文件上运行。
    • 没有什么需要转换

有没有我可以用来显式交换字节顺序/更改字节序的函数,因为 ntohs/l() 和 htons/l() 考虑了主机的字节序并且有时不转换?还是我需要查找/编写我自己的交换字节顺序函数?

4

5 回答 5

12

我认为值得在这里提出由 Rob Pyke(Go 的作者之一)撰写的The Byte Order Fallacy文章。

如果你做对了——即你不对你的平台字节顺序做任何假设——那么它就会起作用。您只需要关心 ELF 格式文件是处于Little Endian模式还是Big Endian模式。

来自文章:

假设您的数据流有一个小端编码的 32 位整数。以下是如何提取它(假设无符号字节):

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

如果它是大端的,这里是如何提取它:

i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

让编译器担心优化它。

于 2012-04-27T06:49:15.470 回答
8

在 Linux中有几个转换函数endian.h允许在任意字节顺序之间进行转换:

uint16_t htobe16(uint16_t host_16bits);
uint16_t htole16(uint16_t host_16bits);
uint16_t be16toh(uint16_t big_endian_16bits);
uint16_t le16toh(uint16_t little_endian_16bits);

uint32_t htobe32(uint32_t host_32bits);
uint32_t htole32(uint32_t host_32bits);
uint32_t be32toh(uint32_t big_endian_32bits);
uint32_t le32toh(uint32_t little_endian_32bits);

uint64_t htobe64(uint64_t host_64bits);
uint64_t htole64(uint64_t host_64bits);
uint64_t be64toh(uint64_t big_endian_64bits);
uint64_t le64toh(uint64_t little_endian_64bits);

已编辑,不太可靠的解决方案。您可以使用 union 以任何顺序访问字节。这很方便:

union {
    short number;
    char bytes[sizeof(number)];
};
于 2012-04-26T21:12:02.340 回答
4

我需要查找/编写自己的交换字节顺序函数吗?

是的你是。但是,为了方便起见,我建议您参考这个问题:如何在 C++ 中的大端和小端值之间进行转换?它给出了编译器特定的字节顺序交换函数的列表,以及字节顺序交换函数的一些实现。

于 2012-04-26T21:12:39.970 回答
1

ntoh 函数不仅可以在大端和小端之间进行交换。一些系统也是“中间端”,其中字节被打乱,而不是仅仅以一种或另一种方式排序。

无论如何,如果您只关心大端和小端,那么您只需要知道主机和目标文件的字节序是否不同。您将拥有自己的函数,该函数无条件交换字节顺序,您将根据是否调用它host_endianess()==objectfile_endianess()

于 2012-04-26T21:17:57.173 回答
0

如果我想考虑一个可以在 Windows 或 linux 上运行的跨平台解决方案,我会写如下内容:

#include <algorithm>

// dataSize is the number of bytes to convert.
char le[dataSize];// little-endian
char be[dataSize];// big-endian

// Fill contents in le here...
std::reverse_copy(le, le + dataSize, be);
于 2012-04-27T02:27:29.987 回答