我有一个对象,其地址不是4字节对齐的。当存在STR指令保存2个寄存器时,这会在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]
执行得很好。但它是下一个STRD r3, r1, [r4, #8]
的HardFaults。如果我手动将寄存器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) {
m_start
是该类中的第一个变量,其地址与this (08738B82)
相同,m_end在0x08738B86
后跟随。
如何在4字节上对齐对象? 任何人都有其他解决方案吗?
答案 0 :(得分:15)
在基于ARM的系统上,您经常无法处理未与4字节边界对齐的32位字(正如您的错误告诉您的那样)。在x86上,您可以访问非对齐数据,但性能会受到很大影响。如果ARM部件支持未对齐访问(例如单字正常负载),则存在性能损失和应该存在可配置的异常陷阱。
ARM上的边界错误示例(here),TLDR:存储指向unsigned char
的指针,然后尝试将其转换为double *
(双指针)。
要解决您的问题,您需要请求一个4字节对齐的内存块并复制非对齐字节+用垃圾字节填充它以确保它是4字节对齐(因此执行数据结构对齐手动地)。然后,您可以将该对象解释为与其新地址对齐的4字节。
来自TurboJ的评论中,显式错误:
Cortex-M3和M4默认允许未对齐访问。但是它们不允许使用STRD指令进行未经授权的访问,因此就是错误。
您可能还会发现在this上强制推断ARM上的数据结构对齐会很有帮助。
答案 1 :(得分:0)
至少在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整除。