Linker 了解STM32F103C8T6的链接器脚本

Linker 了解STM32F103C8T6的链接器脚本,linker,firmware,bare-metal,linker-scripts,Linker,Firmware,Bare Metal,Linker Scripts,我最近接触了STM32F103C8T6裸机编程,链接器脚本实现似乎有点混乱。我在网上发现了两个版本的链接器脚本,令人惊讶的是,尽管它们的内容有巨大差异,但它们都能按预期工作 版本1,如使用 生成的固件以flash write_bank 0 add.bin 0 版本2,如使用 生成的固件将以flash write_image erase main.bin 0x8000000 如您所见,在链接器脚本和用于闪存固件的OpenOCD命令中,版本1将闪烁.text至0x00000000,而版本2将闪烁至0

我最近接触了STM32F103C8T6裸机编程,链接器脚本实现似乎有点混乱。我在网上发现了两个版本的链接器脚本,令人惊讶的是,尽管它们的内容有巨大差异,但它们都能按预期工作

版本1,如使用

生成的固件以
flash write_bank 0 add.bin 0

版本2,如使用

生成的固件将以
flash write_image erase main.bin 0x8000000

如您所见,在链接器脚本和用于闪存固件的OpenOCD命令中,版本1将闪烁
.text
0x00000000
,而版本2将闪烁至
0x8000000
。首先,我不确定这些地址指的是什么:它们是LMA还是VMA?第二,为什么闪烁到不同的地址有相同的效果


我做了一些研究,但似乎没有解决我的困惑。

处理器启动,在arms地址空间中查找地址0x00000000处的向量表。ST已经实现了它们的部分,因此应用程序闪存位于arms地址空间中的地址0x08000000处

根据引导模式,ST可以将内置引导加载程序镜像到地址0x00000000或应用程序。这样,arm对地址0x00000000的访问将返回闪存中的值。如果镜像应用程序,则0x00000000和0x08000000将从同一物理闪存设备读取值并返回它们。这里没有魔术,ARM总线有一个地址和它想要读取的数据量,逻辑有掩码和匹配来确定它是哪个地址空间,然后如果0x00000000到某个KB数,那么如果strap是从一个闪存库读取的,那么从另一个闪存库读取。写作也是如此

在零件的参考手册中搜索BOOT0

理想情况下,您希望链接0x08000000(或0x00200000用于部分而非全部),以便在0x00000004、0x00000008等处读取的向量表条目返回0x0800xxx地址,这样,实际上只有向量表从0x00000000读取,然后在应用程序地址空间中读取程序的其余部分。您将在文档中看到,含有大量闪存的部件的0x00000000地址空间不支持闪存的全部大小,因此如果链接0x00000000,则无法使用所有闪存

现在这些链接器脚本很有趣,首先,如果您在cortex-m的向量表中看到这一点

.word       _start + 1
.word       _nmi_handler + 1
.word       _hard_fault + 1
再找一个例子

.thumb

.section isr_vector
.word 0x20001000
.word one
.word two

.text
.thumb_func
one:
    b one
.thumb_func
two:
    b two
.thumb_func
第一个链接器脚本

Disassembly of section .text:

00000000 <one-0xc>:
   0:   20001000    andcs   r1, r0, r0
   4:   0000000d    andeq   r0, r0, sp
   8:   0000000f    andeq   r0, r0, pc

0000000c <one>:
   c:   e7fe        b.n c <one>

0000000e <two>:
   e:   e7fe        b.n e <two>
.thumb

.section .isr_vector
.globl _isr_vector
_isr_vector:
.word 0x20001000
.word Reset_Handler

.text
.globl Reset_Handler
.thumb_func
Reset_Handler:
    b .


Disassembly of section .text:

08000000 <_isr_vector>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000009    stmdaeq r0, {r0, r3}

08000008 <Reset_Handler>:
 8000008:   e7fe        b.n 8000008 <Reset_Handler>
注意,在第二个示例中,作者有额外的代码,.thumb_func和.type将该标签标记为函数(ORR使用1设置地址的lsbit,这样就不必使用丑陋的向量表,请使用工具)

例如:

.thumb

.section .isr_vector
.globl _isr_vector
_isr_vector:
.word 0x20001000
.word Reset_Handler
.word Something_Else

.text
.globl Reset_Handler
.thumb_func
Reset_Handler:
    b .

.type Something_Else, %function
Something_Else:
    b .
    

Disassembly of section .text:

08000000 <_isr_vector>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   0800000d    stmdaeq r0, {r0, r2, r3}
 8000008:   0800000f    stmdaeq r0, {r0, r1, r2, r3}

0800000c <Reset_Handler>:
 800000c:   e7fe        b.n 800000c <Reset_Handler>

0800000e <Something_Else>:
 800000e:   e7fe        b.n 800000e <Something_Else>
简洁明了,不需要匹配标签,但它取决于位置,下一个标签是标记为函数的标签。也没有.arm_func

.type labelname, %function
适用于arm和thumb代码可以说需要更多的键入,并且您必须匹配标签名称,但这也有好处,因为您清楚地说明了希望标识为函数地址的标签,并且这种习惯适用于arm和thumb模式

两位作者(或者是同一个人两次?)都创作了不必要的作品

想想这些

那么,s

下一个

add r1,r2,r3
add r2,r3,r4
add r3,r4,r5
so.ld

内存
{
xyz:原点=0x08000000,长度=0x1000
}
部分
{
.text:{*(.text*)}>xyz
}
臂无eabi as so.s-o so.o
将所有eabi设为next.s-o next.o
武装无eabi ld-T so.ld so.o next.o-o so.elf
arm none eabi objdump-D so.elf
so.elf:文件格式elf32 littlearm
第节的分解。正文:
08000000 :
8000000:20001000和CS r1、r0、r0
8000004:0800000d stmdaeq r0,{r0,r2,r3}
8000008:0800000f stmdaeq r0,{r0,r1,r2,r3}
0800000c:
800000c:e7fe b.n 800000c
0800000e:
800000e:e7fe b.n 800000e
8000010:e0821003添加r1、r2、r3
8000014:e0832004添加r2、r3、r4
8000018:e0843005添加r3、r4、r5
那很好,但是如果你

arm-none-eabi-ld -T so.ld next.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf

so.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <one-0x18>:
 8000000:   e0821003    add r1, r2, r3
 8000004:   e0832004    add r2, r3, r4
 8000008:   e0843005    add r3, r4, r5
 800000c:   20001000    andcs   r1, r0, r0
 8000010:   08000019    stmdaeq r0, {r0, r3, r4}
 8000014:   0800001b    stmdaeq r0, {r0, r1, r3, r4}

08000018 <one>:
 8000018:   e7fe        b.n 8000018 <one>

0800001a <two>:
 800001a:   e7fe        b.n 800001a <two>
arm none eabi ld-T so.ld next.o so.o-o so.elf
arm none eabi objdump-D so.elf
so.elf:文件格式elf32 littlearm
第节的分解。正文:
08000000 :
8000000:e0821003添加r1、r2、r3
8000004:e0832004添加r2、r3、r4
8000008:e0843005添加r3、r4、r5
800000c:2000000和CS r1、r0、r0
8000010:0800019 stmdaeq r0,{r0,r3,r4}
8000014:080001b stmdaeq r0,{r0,r1,r3,r4}
08000018 :
8000018:e7fe b.n 8000018
080001A:
800001a:e7fe b.n 800001a
那太糟糕了

如果未在链接器脚本中调用,则链接的命令行位置决定文件中.text项的顺序。当提供一个示例或创建一个项目时,您不应该有一个makefile或构建说明来与代码一起使用吗?你真的需要在链接器脚本中做额外的工作吗?YMMV

还要注意,链接器脚本中的内存标签只是连接内存和节之间点的标签。您可以在限制范围内使用任何字符串,但有一些例外

我曾经使用过(rw)功能,但由于不得不在两个版本的binutils之间重新构建less is more链接器脚本,所以出现了问题。(在我看来,该示例中有一个bug)如果链接器抱怨缺少节(.rodata),只需添加它

裸机的美妙之处在于,你可以自由选择如何实现这一点,除非你使用库,否则你必须在它们的沙箱中玩。通常,你会发现过度设计的链接器脚本和引导程序等试图覆盖所有可能的用例和特性。只需制作一个涵盖您的用例的

您可以编写代码,这样您就不需要
.thumb_func
.type labelname, %function
.thumb
.word 0x20001000
.word one
.word two

.thumb_func
one:
    b .

.thumb_func
two:
    b .
    
add r1,r2,r3
add r2,r3,r4
add r3,r4,r5
MEMORY
{
    xyz : ORIGIN = 0x08000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > xyz
}

arm-none-eabi-as so.s -o so.o
arm-none-eabi-as next.s -o next.o
arm-none-eabi-ld -T so.ld so.o next.o -o so.elf
arm-none-eabi-objdump -D so.elf

so.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <one-0xc>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   0800000d    stmdaeq r0, {r0, r2, r3}
 8000008:   0800000f    stmdaeq r0, {r0, r1, r2, r3}

0800000c <one>:
 800000c:   e7fe        b.n 800000c <one>

0800000e <two>:
 800000e:   e7fe        b.n 800000e <two>
 8000010:   e0821003    add r1, r2, r3
 8000014:   e0832004    add r2, r3, r4
 8000018:   e0843005    add r3, r4, r5
arm-none-eabi-ld -T so.ld next.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf

so.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <one-0x18>:
 8000000:   e0821003    add r1, r2, r3
 8000004:   e0832004    add r2, r3, r4
 8000008:   e0843005    add r3, r4, r5
 800000c:   20001000    andcs   r1, r0, r0
 8000010:   08000019    stmdaeq r0, {r0, r3, r4}
 8000014:   0800001b    stmdaeq r0, {r0, r1, r3, r4}

08000018 <one>:
 8000018:   e7fe        b.n 8000018 <one>

0800001a <two>:
 800001a:   e7fe        b.n 800001a <two>