9

我有一个对象的地址不是 4 字节对齐的。当存在保存 2 个寄存器的 STR 指令时,这会导致 cpu 中出现 HardFault 错误。

这是生成的代码:

   00000000 <_ZN8BaseAreaC1EPcmm>:
   0:   b510            push    {r4, lr}
   2:   4604            mov     r4, r0
   4:   6042            str     r2, [r0, #4]
   6:   e9c4 3102       strd    r3, r1, [r4, #8]
   a:   2001            movs    r0, #1
   c:   7420            strb    r0, [r4, #16]
   e:   b921            cbnz    r1, 1a <_ZN8BaseAreaC1EPcmm+0x1a>

这些是在“4:6042 ...”行时的寄存器

R0   08738B82  R8          0  
R1   08738BAE  R9          0  
R2          0  R10  082723E0  
R3       2FCC  R11         0  
R4   08738B82  R12         0  
R5   20007630  R13  2000CB38  

正如所见,STR 指令的目标寄存器未在 4 字节上对齐。该指令STR r2, [r0, #4]执行良好。但它的HardFaults 就在下STRD r3, r1, [r4, #8]。如果我手动将寄存器 R4 更改为08738B80它不会出现硬故障。

这是生成上述 asm 的 C++ 代码:

BaseArea::BaseArea(char * const pAddress, unsigned long startOffset, unsigned long endOffset) : 
m_pAddress(pAddress), m_start(startOffset), m_end(endOffset), m_eAreaType(BASE_AREA) {

And是类中的第一个变量,与( )m_start具有相同的地址,在 on 之后。this0x08738B82m_end0x08738B86

如何让对象对齐 4 字节?有人对此有其他解决方案吗?

4

2 回答 2

18

在基于 ARM 的系统上,您经常无法寻址未与 4 字节边界对齐的 32 位字(正如您的错误告诉您的那样)。在 x86 上,您可以访问未对齐的数据,但是对性能有很大影响。如果 ARM 部件确实支持非对齐访问(例如,单字正常加载),则会有性能损失,并且应该有一个可配置的异常陷阱。

ARM 上的边界错误示例(此处),TLDR:存储指向 an 的指针unsigned char,然后尝试将其转换为double *(双指针)。

为了解决您的问题,您需要请求一个 4 字节对齐的内存块并复制未对齐的字节 + 用垃圾字节填充它以确保它是 4 字节对齐的(因此手动执行数据结构对齐)。然后,您可以将该对象解释为与其新地址对齐的 4 字节。

从 TurboJ 在评论中,明确的错误:

Cortex-M3 和 M4 默认允许非对齐访问。但是它们不允许使用 STRD 指令进行无限制访问,因此是错误的。

您可能还会发现查看内容对强制 ARM 上的数据结构对齐很有帮助。

于 2013-08-16T08:53:55.010 回答
1

至少对于 ARM 架构,以下是正确的(在 cortex M0 上验证):

当使用加载和存储指令时,我们访问的内存必须能被我们试图从/向内存访问的字节数整除,否则我们会得到一个硬故障异常。

例如:

LDR r0, = 0x1001
LDR r1, [r0]

上面代码中的第二行将给出硬错误,因为试图读取 4 个字节但内存地址不能被 4 整除

如果我们将上面代码中的第二行更改为以下

LDRB r1, [r0];//从地址加载1个字节

上述行不会产生硬故障,因为我们正在尝试访问 1 个字节(1 个字节可以从任何内存位置访问)

另请注意以下示例;

LDR r0,= 0x1002
LDRH r1,[r0];   //Load half word from 0x1002

上述行不会产生硬故障,因为内存访问是 2 个字节,地址可以被 2 整除。

于 2014-08-26T02:54:27.280 回答