ARM GNU工具链中如何在汇编常量内对符号执行位运算?
我在为Cortex-M4编写ARM固件,使用GNU工具链创建向量表时,编写了如下汇编代码:
MY_VECTOR_TABLE: .long 0 // SP Init .long SYMBOL_TO_HANDLER1 | +1l // Reset .long SYMBOL_TO_HANDLER2 & -2l // NMI ...
但汇编器抛出错误:
Error: invalid operands (MY_SECTION_NAME and ABS sections) for
|' Error: invalid operands (*MY_SECTION_NAME* and *ABS* sections) for&'
原因很明确:符号的最终地址要到链接阶段才能确定,汇编器无法将位运算编码进可重定位目标文件——只有加减运算能被汇编器通过重定位条目记录,位运算做不到。由于Cortex-M4仅支持Thumb指令,Thumb例程地址的bit[0]必须置位,禁用的中断则需要设为0,但向量表存在内存映射的Flash中,无法在运行时修改,也不想手动编辑二进制镜像,以下是可行的解决方法:
方法1:通过链接器脚本处理位运算
在链接器脚本中定义经过位运算处理后的符号,然后在汇编代码中直接引用这些符号:
链接器脚本添加内容:
SYMBOL_TO_HANDLER1_THUMB = SYMBOL_TO_HANDLER1 | 1; SYMBOL_TO_HANDLER2_DISABLED = SYMBOL_TO_HANDLER2 & ~1;
修改后的汇编代码:
MY_VECTOR_TABLE: .long 0 // SP Init .long SYMBOL_TO_HANDLER1_THUMB // Reset .long SYMBOL_TO_HANDLER2_DISABLED // NMI ...
链接器在链接阶段已经掌握所有符号的最终地址,完全支持对可重定位符号执行位运算。
方法2:用汇编.set伪指令结合链接器表达式
直接在汇编代码中通过.set定义包含位运算的符号,这些运算会延迟到链接阶段执行:
.set SYMBOL_TO_HANDLER1_THUMB, SYMBOL_TO_HANDLER1 | 1 .set SYMBOL_TO_HANDLER2_DISABLED, SYMBOL_TO_HANDLER2 & -2 MY_VECTOR_TABLE: .long 0 // SP Init .long SYMBOL_TO_HANDLER1_THUMB // Reset .long SYMBOL_TO_HANDLER2_DISABLED // NMI ...
GNU汇编器允许在.set中使用链接器可解析的表达式,这样就绕过了汇编阶段无法处理位运算的限制。
方法3:利用Thumb函数的自动位0置位特性
如果SYMBOL_TO_HANDLER1是用-mthumb编译的Thumb函数,GNU工具链会自动在函数符号的地址中置位bit0,此时直接写.long SYMBOL_TO_HANDLER1即可,无需手动|1。而对于需要禁用的中断(比如NMI),直接写.long 0即可,不需要对无效符号做&-2操作。
内容的提问来源于stack exchange,提问作者Echelon X-Ray

