目录
环境
参考
链接脚本简介
STM32F407ZETx_FLASH.ld文件分析
控制输出文件中内存布局
SECTIONS
代码:STM32CUBEMX-linux版V6.5.0 + STM32Cube_FW_F4_V1.27.0 + Makefile方式
目标SOC:STM32F407ZET6
1、GUN Binutils
2、《程序员的自我修养》
Every link is controlled by a linker script. This script is written in the linker command language.
The main purpose of the linker script is to describe how the sections in the input files should be mapped into the output file, and to control the memory layout of the output file. Most inker scripts do nothing more than this.
上述描述来自于文首参考项的GNU Binutils中关于 Linker Scripts的描述。
如果把整个链接过程比作一台计算机,那么ld链接器就是计算机的CPU,所有的目标文件、库文件就是输入,链接结果输出的可执行文件就是输出,而链接控制脚本正是这台计算机的“程序”,它控制CPU的运行,以“程序”要求的方式将输入加工成所须要的输出结果。链接控制脚本“程序”使用一种特殊的语言写成,即ld的链接脚本语言,这种语言并不复杂,只有为数不多的几种操作。无论是输出文件还是输入文件,它们的主要的数据就是文件中的各种段,我们把输入文件中的段称为输入段(Input Sections),输出文件中的段称为输出段(OutputSections)。简单来讲,控制链接过程无非是控制输入段如何变成输出段,比如哪些输入段要合并一个输出段,哪些输入段要丢弃;指定输出段的名字、装载地址、属性,等等。(一般链接脚本名都以lds作为扩展名ld script)。
上述描述来自于文首参考项《程序员的自我修养》一书中。
在研究链接脚本之间,最好对程序的编译和链接需要有一定的了解,可以参考这篇文章,戳编译与链接。
文首环境项中说明了STM32F407ZETx_FLASH.ld的由来,下面就对针对实际的链接脚本分析其代码。
图1
见图1,打开工程的链接脚本 STM32F407ZETx_FLASH.ld,开头摘要部分简单的介绍了功能如下:
1、根据应用程序的要求,设置heap、stack的大小和stack的位置
2、如果使用了外部存储器,则设置存储bank的区域和大小
图2
见图2,是将SECTION段折叠起来后的STM32F407ZETx_FLASH.ld的所有内容。
ENTRY(Reset_Handler):将符号Reset_Handler设置为入口地址,入口地址是进程执行的第一条指令在进程地址空间的地址, ENTRY(Reset_Handler) 表示进程最开始从复位中断服务函数处执行。Reset_Handler定义在startup_stm32f407xx.s启动文件中。
_estack:该值记录着用户模式堆栈的最高地址,也就是RAM最后的地址。
ORIGIN:ORIGIN(MEMORY)表示返回名为MEMORY的内存区域的原点。
LENGTH:LENFTH(MEMORY)表示返回名为MEMORY的内存区域的长度。
_Min_Heap_Size:Heap(堆)的大小。
_Min_Stack_Size:Stack(栈)的大小。
MEMORY:链接器默认配置就允许分配所有可用的内存,你可以通过使用MEMORY命令来覆盖这一点,MEMORY命令描述了目标中内存块的位置和大小。
语法如下:
MEMORY
{
name [(attr)] : ORIGIN = origin, LENGTH = len
...
}
(attr)中的xrw分别表示了可执行、可读和可写。
至于为什么要这么分配,请看图3。
图3
图3是STM32F40xxx memory map,只截取了一部分。
从图中可以看出,Flash的起始地址为0X08000000,长度最大为0XFFFFF,即1M,STM32F407ZET6的FLASH大小为512KB,所以MEMORY命令中FLASH的LENGTH为512K
CCM的起始地址是0X10000000,长度最大为0XFFFF,即64K
SRAM有两块,不过是挨着一起的,起始地址为0X20000000,长度为0X1FFFF,即128K。
所以,结合图2和图3,不难看出MEMORY命令中对RAM,CCMRAM,FLASH的地址和长度定义。接下来看看SECTIONS命令中定义的内容。
图5
图5是对SECTIONS命令下定义段的部分截取,可以看到两个完整的段的定义,即 .isr_vector和 .test,这些段的段名并不是固定的,可以任取。
单独的“.”号表示 Location Counter,它是个特殊的链接变量,总是包含当前输出位置计数器,和段尾的 >FLASH 结合起来,就可以确定地址。
ALIGN(4):4字节对齐可以提高效率,ALIGN函数可以返回位置计数器(.)或任意表达式,并将其对齐到下一个对齐边界。
*(.xxx):表示将输入文件(linux下的.o)中符合的段都放到输出文件(linux下的可执行文件)的定义段中。
KEEP:当 link-time garbage collection使用('--gc-sections')时,标记不应该被消除的部分通常是有用的。这可以通过用KEEP()包围输入部分的通配符。
>FLASH:表示在FLASH内存区域上存放SECTION
其余的段还有许多,但我们一般只会关注.text、.data、.rodata、.bss…段,关于这块可以参考这篇文章,戳可执行文件中的段。
编译此工程得到STM32F407ZET6.elf可执行文件,可以使用下述命令来查看输出可执行文件中各段的分配及大小,如图6。
图6
可以从图6中看出,段的顺序和在链接脚本中定义的段顺序一致。至此,已经从STM32F407ZETx_FLASH.ld链接脚本中看出链接脚本大致所做的工作了,和官网描述基本一致,有通过SECTIONS命令描述如何将输入文件中的SECTIONS mapped 到输出文件的SECTIONS中,也有通过MEMORY命令在输出文件中控制内存布局。