导致硬件故障的 Arm Cortex-M4 LDRD 指令
Arm Cortex-M4 LDRD instruction causing hardfault
我注意到在 Cortex-M3 中 LDRD
(加载双字)是 listed in the errata,但我没有找到 Cortex-M4 的相似之处,无论如何,那里似乎不是执行期间发生的中断。我正在使用 M4 微控制器,并将数据 to/from 传递给主机。以主机(相同架构)处理的形式处理数据很方便——例如,如果主机传递一个无符号的 16 位整数,我接受它作为 uint16_t
,即使它在两个字节数组 data_in
:
uint16_t some_data = *(uint16_t *)data_in;
然而,当我尝试使用无符号 64 位整数执行此操作时,我在生成的 LDRD
指令上遇到硬错误:
uint64_t some_data = *(uint64_t *)data_in;
生成:
9B01 ldr r3, [sp, #4]
330C adds r3, #12
E9D32300 ldrd r2, r3, [r3, #0]
4902 ldr r1, =SOME_ADDR <some_data>
E9C12306 strd r2, r3, [r1, #24]
我在 E9D32300 ldrd r2, r3, [r3, #0]
.
上犯了错误
所以问题是,除了可能的可移植性问题(这里不是问题)之外,我是否通过指向 uint64_t 的位置并尝试将其读取为 [=33= 来做一些根本性的错误]?无论哪种方式,有没有人在任何地方看到过此指令的勘误表?我没有在官方文档中找到它。
此外,为了完整起见,这段不那么有趣的代码也能正常工作:
uint64_t some_data = ((uint64_t)data_in[7] << 8*7) |
((uint64_t)data_in[6] << 8*6) |
((uint64_t)data_in[5] << 8*5) |
((uint64_t)data_in[4] << 8*4) |
((uint64_t)data_in[3] << 8*3) |
((uint64_t)data_in[2] << 8*2) |
((uint64_t)data_in[1] << 8*1) |
((uint64_t)data_in[0] << 8*0);
在 ARMv7M 体系结构参考手册的 A3.2.1 节“对齐行为”中,它说:
以下数据访问总是产生对齐错误:
- 非半字对齐的 LDREXH 和 STREXH。
- 非字对齐的 LDREX 和 STREX。
- 非字对齐的 LDRD、LDMIA、LDMDB、POP、LDC、VLDR、VLDM 和 VPOP。
- 非字对齐 STRD、STMIA、STMDB、PUSH、STC、VSTR、VSTM 和 VPUSH。
因此,除非您知道 data_in
至少是 32 位对齐的,否则您不能将其强制转换为 (uint64_t*)
并期望它起作用。
我注意到在 Cortex-M3 中 LDRD
(加载双字)是 listed in the errata,但我没有找到 Cortex-M4 的相似之处,无论如何,那里似乎不是执行期间发生的中断。我正在使用 M4 微控制器,并将数据 to/from 传递给主机。以主机(相同架构)处理的形式处理数据很方便——例如,如果主机传递一个无符号的 16 位整数,我接受它作为 uint16_t
,即使它在两个字节数组 data_in
:
uint16_t some_data = *(uint16_t *)data_in;
然而,当我尝试使用无符号 64 位整数执行此操作时,我在生成的 LDRD
指令上遇到硬错误:
uint64_t some_data = *(uint64_t *)data_in;
生成:
9B01 ldr r3, [sp, #4]
330C adds r3, #12
E9D32300 ldrd r2, r3, [r3, #0]
4902 ldr r1, =SOME_ADDR <some_data>
E9C12306 strd r2, r3, [r1, #24]
我在 E9D32300 ldrd r2, r3, [r3, #0]
.
所以问题是,除了可能的可移植性问题(这里不是问题)之外,我是否通过指向 uint64_t 的位置并尝试将其读取为 [=33= 来做一些根本性的错误]?无论哪种方式,有没有人在任何地方看到过此指令的勘误表?我没有在官方文档中找到它。
此外,为了完整起见,这段不那么有趣的代码也能正常工作:
uint64_t some_data = ((uint64_t)data_in[7] << 8*7) |
((uint64_t)data_in[6] << 8*6) |
((uint64_t)data_in[5] << 8*5) |
((uint64_t)data_in[4] << 8*4) |
((uint64_t)data_in[3] << 8*3) |
((uint64_t)data_in[2] << 8*2) |
((uint64_t)data_in[1] << 8*1) |
((uint64_t)data_in[0] << 8*0);
在 ARMv7M 体系结构参考手册的 A3.2.1 节“对齐行为”中,它说:
以下数据访问总是产生对齐错误:
- 非半字对齐的 LDREXH 和 STREXH。
- 非字对齐的 LDREX 和 STREX。
- 非字对齐的 LDRD、LDMIA、LDMDB、POP、LDC、VLDR、VLDM 和 VPOP。
- 非字对齐 STRD、STMIA、STMDB、PUSH、STC、VSTR、VSTM 和 VPUSH。
因此,除非您知道 data_in
至少是 32 位对齐的,否则您不能将其强制转换为 (uint64_t*)
并期望它起作用。