STM32F103裸机汇编中.thumb_func未生效的问题及正确实现方法
问题描述
我尝试用裸机汇编与链接脚本为STM32F103开发小型应用,已编写linker.ld、blink.s及Makefile。生成的blink-asm.bin中,向量表内复位异常处理程序地址为0x08000130,但ARM Cortex-M架构要求该地址需置位最低位(即0x08000131),否则加载到MCU会触发硬 fault。虽然在链接脚本中写LONG(reset_exception_handler + 1)可临时解决,但我预期.thumb_func指令能自动修正reset_exception_handler符号地址。请问编写链接脚本与汇编文件以生成正确地址的规范做法是什么?
现有代码
linker.ld
MEMORY { FLASH : ORIGIN = 0x08000000, LENGTH = 64K SRAM : ORIGIN = 0x20000000, LENGTH = 20K } sp_initial_value = ORIGIN(SRAM) + LENGTH(SRAM); vector_table_size = 0x130; SECTIONS { vector_table ORIGIN(FLASH) : { LONG(sp_initial_value); LONG(reset_exception_handler); } > FLASH .text ADDR(vector_table) + vector_table_size : { } > FLASH }
blink.s
.cpu cortex-m3 .syntax unified .thumb .text .global reset_exception_handler .thumb_func .type reset_exception_handler, %function reset_exception_handler: add r0, 1 b reset_exception_handler
Makefile
blink-asm.bin: blink-asm.elf arm-none-eabi-objcopy -O binary blink-asm.elf blink-asm.bin flash: blink-asm.bin st-flash write blink-asm.bin 0x08000000 blink-asm.elf: blink.o arm-none-eabi-ld -T linker.ld -o blink-asm.elf blink.o blink.o: blink.s arm-none-eabi-as -o blink.o blink.s clean: rm -f blink.o blink-asm.elf blink-asm.bin
blink-asm.bin十六进制内容
hexdump -C blink-asm.bin 00000000 00 50 00 20 30 01 00 08 00 00 00 00 00 00 00 00 |.P. 0...........| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000130 00 f1 01 00 ff f7 fc bf |........|
规范解决方案
先明确.thumb_func的实际作用
.thumb_func仅告诉汇编器:后续的标签是Thumb模式函数,汇编器会生成Thumb指令集的代码,并在符号属性中标记其为Thumb函数,但符号本身的数值仍是函数在内存中的实际起始地址(偶数)。链接器不会自动为向量表中的地址添加最低位的模式标记位,因为向量表条目需要的是「内存地址 + 模式标识位」,而非单纯的函数符号地址。
方案1:汇编中定义向量表(推荐)
将向量表定义在汇编文件中,利用汇编器对Thumb函数符号的处理能力,自动为向量表条目添加最低位的1。这种方式更直观,也避免链接脚本中的手动地址计算。
修改后的blink.s:
.cpu cortex-m3 .syntax unified .thumb /* 定义向量表段,"a"表示可分配 */ .section .vector_table, "a" .global __vector_table __vector_table: .word _estack /* 初始栈指针 */ .word reset_exception_handler /* 汇编器自动为Thumb函数地址加1 */ /* 可继续添加其他异常向量(如NMI、硬fault等) */ /* 定义栈顶地址,等价于原链接脚本中的sp_initial_value */ .global _estack _estack = 0x20005000 /* 0x20000000 + 20K SRAM */ .text .global reset_exception_handler .thumb_func .type reset_exception_handler, %function reset_exception_handler: add r0, #1 b reset_exception_handler
简化后的linker.ld:
MEMORY { FLASH : ORIGIN = 0x08000000, LENGTH = 64K SRAM : ORIGIN = 0x20000000, LENGTH = 20K } SECTIONS { /* 向量表放在FLASH起始地址 */ .vector_table ORIGIN(FLASH) : { __vector_table = .; *(.vector_table) /* 包含汇编中定义的向量表段 */ } > FLASH /* 代码段紧跟向量表 */ .text : { *(.text) } > FLASH }
方案2:链接脚本中显式处理地址
如果坚持在链接脚本中定义向量表,可直接对函数地址进行位或1操作(因为Thumb函数地址都是偶数,位或1不会改变实际内存地址,仅设置模式位):
修改linker.ld中的向量表部分:
vector_table ORIGIN(FLASH) : { LONG(sp_initial_value); LONG(reset_exception_handler | 1); /* 显式设置最低位 */ } > FLASH
这种方式简单直接,且Cortex-M系列仅支持Thumb模式,无需担心兼容性问题。
验证效果
修改后重新编译,查看blink-asm.bin的前8字节,复位向量地址会变为0x08000xxx1(最低位为1),加载到STM32F103后即可正常运行,不会触发硬 fault。
内容的提问来源于stack exchange,提问作者vbezhenar

