链接脚本LinkerScript

HarderHeng Lv5

链接脚本的作用

在嵌入式开发中,需要指定不同的段烧录到单片机的地址,这个过程由链接脚本指定。

一般来说链接脚本根据芯片的不同,设定不同的RAM和FLASH起始地址和长度。

除了指示链接的过程,还可以将地址变成变量,传递给启动文件,例如bss段和data段的起始位置和结束位置,能够让启动文件从flash中搬运data段到RAM中,以及初始化bss段的数据为0。

链接脚本和段

现在想象有一个RAM,知道其起始地址和长度,有一个FLASH,也知道其起始地址和长度。

那么链接脚本会将

  • 所有的text段,包括中断向量表
  • 只读数据段rodata

定位到FLASH的位置,尤其是中断向量表一定会被放在FLASH的起始位置,而且中断向量表的第一个位置一定是主栈指针,其实也就是RAM的结束位置。

并且将

  • data数据段,写到Flash中,并且规定data段将写入到RAM的起始位置
  • bss段,规定将bss段写到data段后面。

这样就得到了RAM的内存布局。那堆和栈呢?编译器并不知道程序需要使用多少堆,只能通过编译知道栈的深度。

链接脚本中设置了Min_Heap_SizeMin_Stack_Size两个变量,并且在bss段后面创建了这样的一个用户堆栈段,其作用就是剩下的内存是否能满足最小栈和最小堆的使用。

也就是说RAM中可以动态使用的内存就是在bss后面。

基本语法

1
2
3
4
5
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
FLASH (rx) : ORIGIN = 0x80000000, LENGTH = 512K
}

使用MEMORY定义RAM和FLASH。

1
2
3
4
5
6
7
8
9
10
11
SECTIONS
{
.text:
{
. = ALIGN(4)
*(.text)
. = ALIGN(4)
PROVIDE(etext = .)
}>FLASH

}
  • 使用SECTIONS定义一个段,其中 .相当于一个指针,指向我们当前分配内存的位置。指针默认位置是0。

  • 使用ALIGN来使.指针4字节对齐。

  • 使用*(section_name)将这个段放在整个text的这个位置。*是通配符,表示将所有的文件的.text段都放在这里。如果需要指定某一个文件的某一个段,就将其名称制定出来file.o(.text)

    很多时候写一个段可能会给一个段起名,比如.text.resethandler,此时也可以用通配符(.text.*),也就是把所有.text开头的段都囊括进去。

  • 最后使用一次ALIGN保证4字节对齐,不对齐则填充0。

  • PROVIDE命令用来定义一个变量,有点像汇编中的.weak,也就是如果在别的地方定义了这个变量,就会覆盖掉这里的定义,如果没有定义则使用这里的定义。

  • >FLASH强制移动指针到FLASH中的空闲位置起点,也就说虽然指针默认为0,只需要写上>FLASH,在一开始就会把指针设为FLASH的起始位置。如果是在RAM中也是一样的道理。

源文件和链接文件

链接文件很多时候可以向源文件提供一些变量。

比如在stm32的链接脚本中提供了data段的起始地址结束地址,还有bss段的起始地址还有结束地址。

在这里就可以实现将所有剩余的RAM纳入动态内存的管理,以实现操作系统的动态内存管理。

  • Title: 链接脚本LinkerScript
  • Author: HarderHeng
  • Created at : 2025-03-07 12:10:00
  • Updated at : 2025-03-07 15:50:59
  • Link: https://harderheng.life/2025/03/07/链接脚本LinkerScript/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments