我正在尝试使用裸程序集和链接器脚本为 STM32F103 组装一个小型应用程序。
这是我想出的:
链接器.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
}
眨眼:
.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
生成文件:
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 |........|
因此,将在 0x08000000 加载的第一个值是 20005000,这是 20k SRAM 的末尾。
第二个值是复位异常处理程序地址,即08000130。但是对于ARM,它必须用较低位进行编码,因此它实际上必须是08000131。如果我将当前应用程序代码加载到MCU,它将生成硬故障而不是运行reset_exception_handler。
当然,我可以在链接器脚本中编写
LONG(reset_exception_handler + 1);
,它就会按预期工作。
但是我实际上期望指令
.thumb_func
修改符号reset_exception_handler
。
编写链接器脚本和汇编文件以便生成正确地址的正确方法是什么?
想一或一不加一,如果你加一,那么当你正确使用工具时,加一现在会破坏它,或一你可以生存(稍后修复)。
如你的问题中所写(据我阅读)
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
}
.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
Disassembly of section vector_table:
08000000 <sp_initial_value-0x18005000>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000130 stmdaeq r0, {r4, r5, r8}
Disassembly of section .text:
08000130 <reset_exception_handler>:
8000130: f100 0001 add.w r0, r0, #1
8000134: f7ff bffc b.w 8000130 <reset_exception_handler>
没有理由为向量表提供单独的部分。尽可能避免链接器脚本,它不是解决问题的地方。
MEMORY {
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
SRAM : ORIGIN = 0x20000000, LENGTH = 20K
}
sp_initial_value = ORIGIN(SRAM) + LENGTH(SRAM);
SECTIONS {
.text : { *(.text) } > FLASH
.rodata : { *(.rodata) } > FLASH
.bss : { *(.bss) } > SRAM
}
.cpu cortex-m3
.syntax unified
.thumb
.text
.word sp_initial_value
.word reset_exception_handler
.thumb_func
reset_exception_handler:
b .
08000000 <reset_exception_handler-0x8>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <reset_exception_handler>:
8000008: e7fe b.n 8000008 <reset_exception_handler>
更简单、更干净、更美好。并正确。
现在你也可以像你看到的 gcc 那样:
.cpu cortex-m3
.syntax unified
.thumb
.text
.word sp_initial_value
.word reset_exception_handler
.thumb_func
.type reset_exception_handler,%function
reset_exception_handler:
b .
这只是多余的,选择一个。
.cpu cortex-m3
.syntax unified
.thumb
.text
.word sp_initial_value
.word reset_exception_handler
.type reset_exception_handler,%function
reset_exception_handler:
b .
08000000 <reset_exception_handler-0x8>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <reset_exception_handler>:
8000008: e7fe b.n 8000008 <reset_exception_handler>
但你必须选择一个
.cpu cortex-m3
.syntax unified
.thumb
.text
.word sp_initial_value
.word reset_exception_handler
reset_exception_handler:
b .
08000000 <reset_exception_handler-0x8>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000008 stmdaeq r0, {r3}
08000008 <reset_exception_handler>:
8000008: e7fe b.n 8000008 <reset_exception_handler>
.type..function 与位置无关,并且在arm 和thumb 模式下使用相同的语法,没有.arm_func。因此,如果您只是一个拇指/皮质-M 程序员和/或有点懒(我就是这种情况),那么 .thumb_func 很好,因为您可以剪切并粘贴它或键入它,而不必键入函数名称。但仅适用于拇指。拥有 .type 习惯会更好,因为您可以为所有函数执行此操作,但需要多做一点工作。
.cpu cortex-m3
.syntax unified
.thumb
.text
.type two,%function
.word sp_initial_value
.word reset_exception_handler
.word one
.word two
.thumb_func
one:
b .
two:
b .
reset_exception_handler:
b .
08000000 <one-0x10>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000014 stmdaeq r0, {r2, r4}
8000008: 08000011 stmdaeq r0, {r0, r4}
800000c: 08000013 stmdaeq r0, {r0, r1, r4}
08000010 <one>:
8000010: e7fe b.n 8000010 <one>
08000012 <two>:
8000012: e7fe b.n 8000012 <two>
08000014 <reset_exception_handler>:
8000014: e7fe b.n 8000014 <reset_exception_handler>
正如评论中基本回答的那样,没有理由假设链接器脚本知道拇指内容。 (链接器当然知道拇指)。另外,正如该评论中的回答,只需使用汇编语言。
而且正如所指出的,如果您没有放置至少最少数量的向量,那么如果您遇到其他问题,例如未对齐或未定义的指令等,那么您将出现故障并崩溃(理想情况下)或降落在某个奇怪的地方(重新)运行代码的一部分(不幸的是)。
除非您完成工作,否则中断本身不会发挥作用,我认为芯片供应商决定特定芯片/核心使用多少个中断(以及向量),最多可达该核心的最大值(不同的核心有不同的最大值)。