在Linux中,针对结构体的赋值在ARM架构上失败,但在x86架构上成功。

4

我注意到了一件非常奇怪的事情。 假设我定义了以下结构:

typedef struct
{
  uint32_t a;
  uint16_t b;
  uint32_t c;
} foo;

这个结构体被包含在我从网络接收到的一个大缓冲区中。

以下代码在x86上可以正常工作,但在ARM上会收到SIGBUS信号。

extern void * buffer;
foo my_foo;
my_foo = (( foo * ) buffer)[0];

使用memcpy代替指针解引用解决了这个问题。

在ARM上搜索关于SIGBUS的内容,指出这与内存对齐有关。

有人能解释一下发生了什么吗?


谷歌:结构序列化,并查看Wikipedia的序列化对齐字节序是一个不断出现的主题。即使ARM支持这一点,它也会在大端CPU上崩溃。您需要将struct注释为紧凑型,或者memcpy()可能会工作(而不是SIGBUS),但它不会正常运行。 - artless noise
如何捕获未对齐的内存访问ARM Linux上提供了一些关于这个问题的额外信息。然而,我认为你的基本机制是有缺陷的。有很多序列化例程、协议和框架(ntohl(), ntohs(), protobufs, JSON, XSD, XDR,等等)。 - artless noise
@artlessnoise 感谢您的评论,但为什么要打-1呢? - stdcall
4个回答

5
你自己说了:你的特定处理器有内存对齐限制,而buffer没有正确对齐,无法从中读取大于一个字节的内容。该赋值可能被编译成较大实体的三个移动。
使用memcpy()时,没有对齐限制,它必须能够在任何两个地址之间进行复制,因此它会执行必要的操作来实现这一点。可能会按字节逐个复制,直到地址对齐,这是常见的模式。
另外,我发现不使用数组索引编写代码更清晰:
extern const void *buffer;
const foo my_foo = *(const foo *) buffer;

3

C标准[ISO/IEC 9899:2011] - 6.3.2.3,第7段:

指向对象或不完整类型的指针可以转换为指向不同对象或不完整类型的指针。如果所得到的指针未正确对齐指向的类型,则行为是未定义的。

来源:CERT安全编码标准


1
ARM架构的系统要求结构体在字边界上对齐。如果不是这种情况,可能会出现不同的行为(例如,在Linux内核中,这些行为在/proc/cpu/alignement中描述,其中之一是发送SIGBUS信号)。
你使用memcpy()强制数据结构对齐。

0
我之前在Freescale iMX上开发了一些下载应用程序...遇到了内存对齐问题(要求可执行文件是512字节的倍数)...这是ARM和x86之间的根本区别...但请记住,memcpy会进行逐字节复制...所以它可能有效,但请务必检查运行时问题...不要被memcpy所迷惑...为您特定的平台拥有一个内存对齐的结构总是一个好主意。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接