一链接脚本的简单常识
有时候我们很多文件需要链接到指定的区域,或者叫做段里面,比如在 Linux 里面初始化函数就会放到 init 段里面。因此我们需要能够自定义一些段,这些段的起始地址我们可以自由指定,同样的我们也可以指定一个文件或者函数应该存放到哪个段里面去。要完成这个功能我们就需要使用到链接脚本,链接脚本用于描述文件应该如何被链接在一起形成最终的可执行文件。其主要目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布。比如我们编译生成的文件一般都包含 text 段、 data 段等等。 链接脚本的语法很简单,就是编写一系列的命令,这些命令组成了链接脚本,每个命令是一个带有参数的关键字或者一个对符号的赋值,可以使用分号分隔命令。像文件名之类的字符串可以直接键入,也可以使用通配符“*”。最简单的链接脚本可以只包含一个命令“SECTIONS”,我们可以在这一个“SECTIONS”里面来描述输出文件的内存布局。我们一般编译出来的代码都包含在 text、 data、 bss 和 rodata 这四个段内,假设现在的代码要被链接到 0X10000000 这个地址,数据要被链接到 0X30000000 这个地方,下面就是完成此功能的最简单的链接脚本:
SECTIONS{
. = 0X10000000;
.text : {*(.text)}
. = 0X30000000;
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4) : { *(.bss) }
}
第 1 行我们先写了一个关键字“SECTIONS”,后面跟了一个大括号,这个大括号和第 7 行的大括号是一对,这是必须的。看起来就跟 C 语言里面的函数一样。 第 2 行对一个特殊符号“.”进行赋值,“.”在链接脚本里面叫做定位计数器,默认的定位计数器为 0。我们要求代码链接到以 0X10000000 为起始地址的地方,因此这一行给“.”赋值0X10000000,表示以 0X10000000 开始,后面的文件或者段都会以 0X10000000 为起始地址开始链接。 第 3 行的“.text”是段名,后面的冒号是语法要求,冒号后面的大括号里面可以填上要链接到“.text”这个段里面的所有文件,“(.text)”中的“”是通配符,表示所有输入文件的.text段都放到“.text”中。 第 4 行,我们的要求是数据放到 0X30000000 开始的地方,所以我们需要重新设置定位计数器“.”,将其改为 0X30000000。如果不重新设置的话会怎么样?假设“.text”段大小为 0X10000,那么接下来的.data 段开始地址就是 0X10000000+0X10000=0X10010000,这明显不符合我们的要求。所以我们必须调整定位计数器为 0X30000000。 第 5 行跟第 3 行一样,定义了一个名为“.data”的段,然后所有文件的“.data”段都放到这里面。但是这一行多了一个“ALIGN(4)”,这是什么意思呢?这是用来对“.data”这个段的起始地址做字节对齐的, ALIGN(4)表示 4 字节对齐。也就是说段“.data”的起始地址要能被 4 整除,一般常见的都是 ALIGN(4)或者 ALIGN(8),也就是 4 字节或者 8 字节对齐。 第 6 行定义了一个“.bss”段,所有文件中的“.bss”数据都会被放到这个里面,“.bss”数据就是那些定义了但是没有被初始化的变量。
二 STM32跑MicroPython的连接脚本分析
MicroPython官方已经将的固件移植到STM32上,而且支持许多种型号的STM32,分析以1.16版本MicroPython的PYBV10为例。 首先在工程的Makefile中看到一句: include $(BOARD_DIR)/mpconfigboard.mk 知道Makefile要包含mpconfigboard.mk文件。
MCU_SERIES = f4
CMSIS_MCU = STM32F405xx
AF_FILE = boards/stm32f405_af.csv
ifeq ($(USE_MBOOT),1)
# When using Mboot all the text goes together after the filesystem
LD_FILES = boards/stm32f405.ld boards/common_blifs.ld
TEXT0_ADDR = 0x08020000
else
# When not using Mboot the ISR text goes first, then the rest after the filesystem
LD_FILES = boards/stm32f405.ld boards/common_ifs.ld
TEXT0_ADDR = 0x08000000
TEXT1_ADDR = 0x08020000
endif
# MicroPython settings
MICROPY_VFS_LFS2 = 1
从mpconfigboard.mk文件中可以看出PYBV10根据是否使用USE_MBOOT分两种工作模式。USE_MBOOT工作模式是可IAP升级的模式。 首先不可升级时stm32f405.ld和common_ifs.ld如下
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K
FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K
FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
_minimum_stack_size = 2K;
_minimum_heap_size = 16K;
_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K;
_ram_start = ORIGIN(RAM);
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
_heap_start = _ebss;
_heap_end = _sstack;
ENTRY(Reset_Handler)
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
*/ff.o(.text*)
*/vfs_fat_*.o(.text*)
*/py/formatfloat.o(.text*)
*/py/parsenum.o(.text*)
*/py/mpprint.o(.text*)
. = ALIGN(4);
} >FLASH_ISR
.text :
{
. = ALIGN(4);
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
} >FLASH_TEXT
INCLUDE common_extratext_data_in_flash_text.ld
INCLUDE common_bss_heap_stack.ld
}
首先在stm32f405.ld中指定了存储的区域。主要定义了中断 、文件系统、 代码和RAM段等。common_ifs.ld文件中说明.isr_vector 放置中断(>FLASH_ISR)中,.text放置代码段中(>FLASH_TEXT)。中间的112K为文件系统。
可升级(使用Mboot)时,common_blifs.ld的文件为
ENTRY(Reset_Handler)
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} >FLASH_TEXT
.text :
{
. = ALIGN(4);
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
} >FLASH_TEXT
INCLUDE common_extratext_data_in_flash_text.ld
INCLUDE common_bss_heap_stack.ld
}
说明.isr_vector和.text都放到了代码段内(>FLASH_TEXT),前面的FLASH_ISR段预留给了Mboot使用,打开mboot文件夹找到了stm32_memory.ld文件和stm32_sections.ld文件。 stm32_sections.ld文件内容为:
MEMORY
{
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 32K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 120K
}
stm32_memory.ld文件内容为:
_minimum_stack_size = 8K;
_estack = ORIGIN(RAM) + LENGTH(RAM);
ENTRY(Reset_Handler)
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} >FLASH_BL
.text :
{
. = ALIGN(4);
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
} >FLASH_BL
_sidata = LOADADDR(.data);
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} >RAM AT> FLASH_BL
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >RAM
.nozero_bss (NOLOAD) :
{
. = ALIGN(4);
*(.nozero_bss*)
. = ALIGN(4);
} >RAM
.stack :
{
. = ALIGN(4);
. = . + _minimum_stack_size;
. = ALIGN(4);
} >RAM
.ARM.attributes 0 : { *(.ARM.attributes) }
}
可以看到,mboot相当于bootloader,它只是依据FLSH和RAM将存储划分内存FLASH_BL和RAM。并将isr_vector和text链接到FLASH_BL,将data、bss 、nozero_bss和stack链接到RAM。
|