我正在用C编写Linux程序来分析嵌入式系统生成的核心文件。核心文件可能是小端(ARM)或大端(MIPS),分析它们的程序可能在小端主机(x86)或大端(PowerPC)上运行。
通过查看标题,我知道核心是LE还是BE。我宁愿我的程序不需要知道它运行的主机是小端还是大端,我想用API来处理它。如果没有更好的选择,我想我会开始依赖#ifdef __BIG_ENDIAN __。
在Linux内核中我们有cpu_to_le32等从原生字节排序转换为小端等等。在用户空间中有htonl等,它从native到big endian转换但是没有等效的native到little endian我可以找到。
有人可以为用户空间建议合适的API吗?
编辑:为了清楚起见,我正在寻找一个已经知道我的CPU是大端还是小端的API,并相应地交换。我不想为#ifdefs乱丢我的代码。我不只是在寻找代码片段来交换字节;谢谢你们,但那不是重点。
答案 0 :(得分: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>
提供了这些相同的宏。
答案 1 :(得分:5)
如果您可以访问霓虹灯协处理器并且内存是连续的(例如视频帧),您可以使用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"
);
}
答案 2 :(得分:3)
基于你真正想要做的事情(阅读ELF核心转储文件而不必担心端点问题),我相信使用libelf,可用here和一个很好的教程here,是个不错的选择。
这个库透明地使用大端和小端ELF文件,并且在Linux下运行得很好,尽管它的FreeBSD起源(通常的“./configure”和“make”序列就是构建它所需要的)。为了咧嘴笑,我在x86核心文件以及MIPS big-endian核心文件上尝试了“读取程序头表”示例(稍加修改以使其构建),它似乎“正常工作”。
答案 3 :(得分:3)
如果您将文件视为字节数组,那么您可以控制选择字节数的顺序,并且CPU的结尾实际上最终无关紧要。
这在处理对齐问题方面也非常有用。您的核心转储可能在其中具有未对齐的引用。我知道这种可能性很小,但也可能是由于腐败造成的。这是通过将文件视为字节数组而避免的另一个问题。
我在实现jhead时使用了这种方法。
答案 4 :(得分: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
网络字节顺序是big-endian。所以,这些功能意味着:
hton*: Host endian to big endian
ntoh*: Big endian to host endian
希望这有帮助。
答案 5 :(得分: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;
答案 6 :(得分:1)
查看/ usr / include / linux / byteorder /中的内核提供的头文件,例如__cpu_to_be32()和__be32_to_cpu()
还可以查看/usr/include/linux/types.h文件,您可以在其中将类型定义为显式大/小端的纯整数,这有助于在编译时检测到任何不匹配。
答案 7 :(得分:1)
鉴于切换endian-ess is easy,我总是最终使用这样的自定义代码,严格遵守我在代码中使用的表示,并在端点处处理字节顺序(输入和输出)
答案 8 :(得分: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