0 c assembly gcc arm memory-alignment
我在某些计算带有整数操作数的浮点表达式的代码中遇到了 HardFault 异常。操作数按地址传递,然后将其转换(隐式或显式转换)为浮点数。当操作数不是 32 位对齐时(这不在我的控制之下),我得到异常。
我试图在这里重现 Godbolt 上的行为,生成的代码与我在设备上得到的一致。
基本上,下面的反汇编代码
vldr.32 s0, [r0] @ int
Run Code Online (Sandbox Code Playgroud)
在vldr需要对齐地址的指令中直接使用传递给函数的可能未对齐的地址。
我发现这个问题解决了类似的问题,但他们在那里谈论浮点指针。在这种情况下,我知道浮动不能未对齐。
在我的情况下,我正在处理整数,允许未对齐,但编译器假定它仍然可以使用 vldr 指令中的地址。更让我困惑的是这段代码
uint32_t pippo = *(uint32_t *)src;
float pippof = pippo * 10.0f;
Run Code Online (Sandbox Code Playgroud)
当提供未对齐的地址时,可能会或可能不会产生异常,这取决于优化级别,因为-O0例如在堆栈上分配了一个整数。
所以我的问题是:
当操作数不是 32 位对齐时(这不在我的控制之下)
alignof(uint32_t)是 4,因此允许编译器假设 4 字节对齐。取消引用uint32_t*不是 4 字节对齐的 a 是 C 未定义的行为,所以是的,编译器 100% 允许假设不会发生。
特别*(uint32_t *)src是在您的情况下,如果src未对齐,则是未定义的行为。这就是为什么允许为以后使用该数据而生成的代码假定它是对齐的。ARM 程序集恰好可以处理未对齐的整数加载这一事实与任何事情无关,除了为什么它碰巧在禁用优化的情况下工作。请参阅https://trust-in-soft.com/blog/2020/04/06/gcc-always-assumes-aligned-pointers/和
为什么对 mmap'ed 内存的未对齐访问有时会在 AMD64 上出现段错误?有关目标 ISA 未对齐行为/保证在 C 中不使其安全的更多示例和讨论。
如果您的数据比这少对齐,则需要某种方法来进行安全的未对齐加载。一种 ISO-C 标准方式是使用memcpy. (GCC 将可靠地将它内联到它知道如何执行未对齐整数加载的目标上,例如具有足够新的 ARM-march=或-mcpu=。除非您使用过-fno-builtin-memcpy或类似,否则这将是一个开销太大的错误选择。)
另一种方法是像 GNU C typedef 一样
typedef uint32_t unaligned_u32 __attribute__((aligned(1)))使用unaligned_u32*.
这让编译器知道它不是一个普通的 ABI 兼容uint32_t对象,并且必须发出以一种即使没有对齐也能工作的方式加载的代码。这可能非常低效;我没有检查 GCC 的 asm 输出。
(您可以将此 GNU C 类型属性用于任何类型,包括float您是否需要 unaligned_float。)
__attribute__((may_alias, aligned(1)))如果您还需要别名安全的uint32_t. (许多嵌入式构建使用 -fno-strict-aliasing 进行编译,其中每种类型都隐式为 may_alias,但如果您在实际需要的任何地方严格使用它,您就可以使您的代码严格别名安全。)