Arm® 勘误表 #752770:SP 的负载中断可能导致错误行为
受影响的版本:
版本 C 及更早版本
详细信息:
在向堆栈指针 (SP/R13) 进行单字加载的数据阶段发生中断,可能会导致器件出现错误行为。在所有情况下,从中断返回都将导致加载指令额外执行一段时间。对于所有执行基址寄存器更新的指令,基址寄存器将在每次执行时被错误更新,从而导致堆栈指针从错误的内存位置加载。可能导致重复加载事务的受影响指令为:
- LDR SP,[Rn],#imm
- LDR SP,[Rn,#imm]!
- LDR SP,[Rn,#imm]
- LDR SP,[Rn]
- LDR SP,[Rn,Rm]
受影响的指令可能导致从不正确的存储器地址加载栈指针:
- LDR SP,[Rn],#imm
- LDR SP,[Rn,#imm]!
条件:
- 执行了以 SP/R13 为目标的 LDR 指令。
- LDR 的地址已成功发出到存储器系统。
- 在数据返回并写入栈指针之前会获取中断。
影响:
除非该加载操作是针对器件内存或强序内存进行,否则重复加载不应产生任何影响。
- 在极少数情况下,如果该加载操作是针对器件内存或强序内存进行,重复读取可能会导致最终的堆栈指针值与仅执行一次加载时不同。
- 对该指令的两种回写形式产生中断,可能会导致基址寄存器值和最终的堆栈指针值均不正确。这可能会导致明显的堆栈损坏,以及随后对内存的意外修改。
解决方法:
大多数编译器通过不生成受影响的指令序列,并且不在编译器运行时库中使用这些指令,从而确保不触发此缺陷。这包括:
- 来自 v6.21 的 IAR
- TI Arm 编译器 (CCS) 的所有版本
针对这两个问题的解决方法可以通过以下方式实现:将直接向堆栈指针加载数据的操作,替换为先向通用寄存器进行中间加载,然后再将该寄存器的值移至堆栈指针。
如果可以接受重复读取,则可以通过执行不带基址递增的堆栈指针加载,随后跟一条 ADD 或 SUB 指令来对基址寄存器执行适当的更新,从而规避基址寄存器更新问题。