Cortex-M0+下从Thumb内联汇编分支到C符号的问题
解决Cortex-M0+中bl指令被转为blx导致HardFault的问题
这个问题我之前在做M0+的RAM动态代码生成时也碰到过,核心原因和Cortex-M的模式识别规则有关,咱们一步步来解决:
为什么会生成blx指令?
Cortex-M系列用地址的**最低位(LSB)**来区分指令模式:
- LSB=1:目标是Thumb模式代码(M0+唯一支持的模式)
- LSB=0:目标是ARM模式代码
你的code_buf是uint16_t数组,RAM里的数组地址是对齐的(偶数),所以LSB为0。编译器看到这个地址时,误以为要跳转到ARM模式代码,所以自动生成blx指令——而M0+根本不支持ARM模式,直接触发HardFault。
强制编译器生成bl指令的方法
方法1:将数组地址转为Thumb模式函数指针
先定义一个Thumb函数指针类型,然后把code_buf的地址置位LSB(与1做或操作),明确告诉编译器这是Thumb代码的入口:
// 定义Thumb函数指针类型 typedef void (*thumb_func_t)(void); // 把数组地址转为Thumb模式函数指针(置位LSB) thumb_func_t ram_code_entry = (thumb_func_t)((uintptr_t)code_buf | 1); void jump() { register volatile uint32_t* PASET asm("r0") = &(PA->OUTSET.reg); register volatile uint32_t* PACLR asm("r1") = &(PA->OUTCLR.reg); register uint32_t set asm("r2") = startset; register uint32_t cl0 asm("r3") = clears[0]; register uint32_t cl1 asm("r4") = clears[1]; register uint32_t cl2 asm("r5") = clears[2]; register uint32_t cl3 asm("r6") = clears[3]; register uint32_t dl0 asm("r8") = delays[0]; register uint32_t dl1 asm("r9") = delays[1]; register uint32_t dl2 asm("r10") = delays[2]; register uint32_t dl3 asm("r11") = delays[3]; asm volatile ( "bl %0\n" : [set]"+r" (set) : [target]"r" (ram_code_entry), [PASET]"r" (PASET), [PACLR]"r" (PACLR), [cl0]"r" (cl0), [cl1]"r" (cl1), [cl2]"r" (cl2), [cl3]"r" (cl3), [dl0]"r" (dl0), [dl1]"r" (dl1), [dl2]"r" (dl2), [dl3]"r" (dl3) : "lr" ); }
方法2:直接在汇编参数中处理地址
如果不想额外定义函数指针,也可以直接在汇编的输入参数里把地址的LSB置1,效果是一样的:
void jump() { register volatile uint32_t* PASET asm("r0") = &(PA->OUTSET.reg); register volatile uint32_t* PACLR asm("r1") = &(PA->OUTCLR.reg); register uint32_t set asm("r2") = startset; register uint32_t cl0 asm("r3") = clears[0]; register uint32_t cl1 asm("r4") = clears[1]; register uint32_t cl2 asm("r5") = clears[2]; register uint32_t cl3 asm("r6") = clears[3]; register uint32_t dl0 asm("r8") = delays[0]; register uint32_t dl1 asm("r9") = delays[1]; register uint32_t dl2 asm("r10") = delays[2]; register uint32_t dl3 asm("r11") = delays[3]; asm volatile ( "bl %0\n" : [set]"+r" (set) : [target]"r" ((uintptr_t)code_buf | 1), [PASET]"r" (PASET), [PACLR]"r" (PACLR), [cl0]"r" (cl0), [cl1]"r" (cl1), [cl2]"r" (cl2), [cl3]"r" (cl3), [dl0]"r" (dl0), [dl1]"r" (dl1), [dl2]"r" (dl2), [dl3]"r" (dl3) : "lr" ); }
额外注意事项
- 确保你的RAM区域在链接脚本中被标记为可执行(RX权限),否则即使指令正确,也会触发内存访问错误。
- 动态生成的Thumb代码必须是16位对齐的(
uint16_t数组天然满足这一点),否则执行时也会出问题。
内容的提问来源于stack exchange,提问作者Alberto
相关产品推荐
相关产品推荐

