LV16-07-开发工具-04-ld链接文件
本文主要是STM32开发——链接文件分析的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
- 通用
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.st.com/content/st_com/zh.html | ST官方网站,在这里我们可以找到STM32的相关文档 | |
https://www.stmcu.com.cn/ | 意法半导体ST中文官方网站,在这里我们可以找到STM32的相关中文参考文档 | |
http://elm-chan.org/fsw/ff/00index_e.html | FatFs文件系统官网 | |
教程书籍 | 《ARM Cortex-M3权威指南》 | ARM公司专家Joseph Yiu(姚文祥)的力作,中文翻译是NXP的宋岩 |
《ARM Cortex-M0权威指南》 | ||
《ARM Cortex-M3与Cortex-M4权威指南》 | ||
开发论坛 | http://47.111.11.73/forum.php | 开源电子网,正点原子的资料下载及问题讨论论坛 |
https://www.firebbs.cn/forum.php | 国内Kinetis开发板-野火/秉火(刘火良)主持的论坛,现也做STM32和i.MX RT | |
https://www.amobbs.com/index.php | 阿莫(莫进明)主持的论坛,号称国内最早最火的电子论坛,以交流Atmel AVR系列单片机起家,现已拓展到嵌入式全平台,其STM32系列帖子有70W+。 | |
http://download.100ask.net/index.html | 韦东山嵌入式资料中心,有些STM32和linux的相关资料也可以来这里找。 | |
博客参考 | http://www.openedv.com/ | 开源网-原子哥个人博客 |
http://blog.chinaaet.com/jihceng0622 | 博主是原Freescale现NXP的现场应用工程师 | |
cortex-m-resources | 这其实并不算是一个博客,这是ARM公司专家Joseph Yiu收集整理的所有对开发者有用的官方Cortex-M资料链接(也包含极少数外部资源链接) |
- STM32
STM32 | STM32 HAL库开发实战指南——基于F103系列开发板 | 野火STM32开发教程在线文档 |
STM32库开发实战指南——基于野火霸道开发板 | 野火STM32开发教程在线文档 |
- SD卡
SD Association | 提供了SD存储卡和SDIO卡系统规范 |
点击查看相关文件下载
STM32F103xx英文数据手册 | STM32F103xC/D/E系列的英文数据手册 |
STM32F103xx中文数据手册 | STM32F103xC/D/E系列的中文数据手册 |
STM32F10xxx英文参考手册(RM0008) | STM32F10xxx系列的英文参考手册 |
STM32F10xxx中文参考手册(RM0008) | STM32F10xxx系列的中文参考手册 |
Arm Cortex-M3 处理器技术参考手册-英文版 | Cortex-M3技术参考手册-英文版 |
STM32F10xxx Cortex-M3编程手册-英文版(PM0056) | STM32F10xxx/20xxx/21xxx/L1xxxx系列Cortex-M3编程手册-英文版 |
SD卡相关资料——最新版本 | 有关SD卡的一些资料可以从这里下载 |
SD卡相关资料——历史版本 | 有关SD卡的一些历史版本资料可以从这里下载,比如后边看的SD卡2.0协议 |
SD 2.0 协议标准完整版 | 这是一篇关于SD卡2.0协议的中文文档,还是比较有参考价值的,可以一看 |
这篇笔记主要是STM32CubeMX软件生成的STM32F103ZETx_FLASH.ld链接文件的学习笔记。这篇笔记按照顺序从上到下分析STM32F103ZETx_FLASH.ld文件。关于GNU链接脚本的详细语法我们可以参考这里:Documentation for binutils 2.40 (sourceware.org),这里好像还有在线的文档。【注意】生成Makefile文件的话,链接脚本是自动生成的,并且每一次重新生成的时候,链接脚本将会重新生成,也就是说,若是我们修改了链接脚本内容的话,只要我们通过STM32CubeMX更新工程,我们修改的部分都会被删除。
一、参考资料
关于链接脚本,其实在GNU的官方文档中是有相关的内容的。
- GNU官网:GNU 操作系统和自由软件运动
- Documentation for binutils 2.40 :Documentation for binutils 2.40 (sourceware.org)
- Linker (ld) 在线文档:ld.pdf (sourceware.org)
GNU与GCC什么关系?这里简单了解一下吧,至少知道为什么我们找链接脚本的语法要到GNU的官网去,我查了下维基百科,是这样说的:
GNU编译器套装(英语:GNU Compiler Collection,缩写为GCC)是GNU计划制作的一种优化编译器,支持各种编程语言、操作系统、计算机系统结构。该编译器是以GPL及LGPL许可证所发行的自由软体,也是GNU计划的关键部分,还是GNU工具链的主要组成部份之一。GCC(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。1985年由理查德·马修·斯托曼开始发展,现在由自由软体基金会负责维护工作。截至2019年,GCC大约有1500万行代码,是现存最大的自由程序之一。[3] 它在自由软件的发展中发挥了重要作用,不仅是一个工具,还是一个典例。
原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。同年12月,新的GCC编译器可以编译C++语言。后来又为Fortran、Pascal、Objective-C、Java、Ada,Go等其他语言开发了前端。C和C++编译器也支持OpenMP和OpenACC规范。
GCC编译器已经被移植到比其他编译器更多的平台和指令集架构上,并被广泛部署在开发自由和专有软件的工具中。GCC还可用于许多嵌入式系统,包括基于ARM和Power ISA的芯片。
GCC不仅是GNU操作系统的官方编译器,还是许多类UNIX系统和Linux发行版的标准编译器。BSD家族中的大部分操作系统也在GCC发布之后转用GCC;不过FreeBSD、OpenBSD和Apple macOS已经转向了Clang编译器[4],主要是因为许可问题。[5][6][7]GCC也可以编译Windows、Android、iOS、Solaris、HP-UX、IBM AIX和DOS系统的代码。GCC原本用C开发,后来因为LLVM、Clang的崛起,它更快地将开发语言转换为C++。许多C的爱好者在对C++一知半解的情况下主观认定C++的性能一定会输给C,但是Ian Lance Taylor给出了不同的意见,并表明C++不但性能不输给C,而且能设计出更好,更容易维护的程式[8][9]。
二、一个完整的链接文件
点击查看 STM32F103ZETx_FLASH.ld 文件完整内容
1 | /* Entry Point */ |
三、各部分说明
1. 入口与堆栈
1 | /* Entry Point */ |
(1)第2行:指定入口地址。
(2)第5行:RAM的结束地址
(3)第7、8行:指定堆的大小和栈的大小分别为0x200和0x400。
2. 内存布局
2.1 MEMORY
1 | MEMORY |
(1)MEMORY关键字用于描述一个MCU ROM和RAM的内存地址分布(Memory Map),MEMORY中所做的内存描述主要用于SECTIONS中LMA和VMA的定义。
(2)name :是所要定义的内存区域的名字。
(3)attr :是可选的,并不重要,具体用法可参考GNU Linker的语法说明。
(4)origin :是其起始地址。
(5)len :为内存区域的大小,MEMORY语法中可以使用如K、M和G这样的内存单位。
【注意】ORIGIN可以写为org,而LENGTH可以写为l。
2.2 实例分析
1 | /* Specify the memory areas */ |
这一部分给出地址的划分区间。
(1)RAM和FLASH表示内存名称;
(2)后边的括号表示权限(x:可运行;r:可读;w:可写);
(3)ORIGIN表示起始地址,LENGTH表示这段内存区域的长度。可以看到这里RAM是可运行可读可写,而FLASH只可运行可读。我们知道STM32的FLASH是可以写的,但这里的权限只针对链接脚本,并不代表代码执行。
我们可以在这里增加一个由malloc使用的MALLOC段,放在外部SRAM上,地址0x68000000:
1 | /* Specify the memory areas */ |
3. 段的定义
3.1 SECTIONS
3.1.1 框架
1 | SECTIONS |
(1)SECTIONS关键字用于定义output section(输出段)的相应input section(输入段)、LMA和VMA,是整个连接脚本中最为重要的部分。注:output section是实际存储在内存中的“段”,而input section是其构成成员,如.data为数据段,由所有全局变量构成(默认情况下);.text为代码段,由所有函数构成(默认情况下)。
3.1.2 command
主要的部分是 sections−command ,SECTIONS{ }只是属于框架。 sections−command 的语法如下:
1 | section [address] [(type)] : |
我们从使用的角度来讲解其语法:(假设有一个全局变量myData,我们用#pragma section命令将其定义为.myData段(input section))
(1)我们首先可以定义output section的名字,随便什么都可以,比如.my_data;
(2)然后我们可以定义其构成成员,*(.myData);
(3)接下来我们就要指定 .my_data 的LMA和VMA了,有4种方法:
1 | [<address>] + [AT(<lma>)] |
但是要注意这些用法的不同:[\<address>] 和 [AT(<lma>)]必须指定具体的地址,而\ [><region>] 和\ [AT><lma region>]只需指定内存空间,具体地址紧接着上一个output section的末尾地址。
经过以上步骤,我们得出如下section定义:(这里只列出2种)
1 | SECTIONS |
3.2 段的作用
1 | /* Define output sections */ |
这里就是链接脚本的核心功能,我们先不考虑每个段的功能,先了解一下链接脚本到底用来干什么。因为一个工程内可能有多个.c文件,比如代码里面一共3个文件:分别是 main.c 、usart.c 、 start.s 。很明显一个主程序,一个串口驱动,一个启动文件。在实际编译中,会把它们分别编译为 main.o 、usart.o 、 start.o ,假如每个.o文件中都包含.text、.data、.bss段,但是我们知道,程序最后烧入MCU是只有一个文件,这里就涉及到段的合并,也就是上面的命令所实现的功能,把每个.o文件中的相同段合一。
- 一般的程序中包含常见的几个段:text(存放程序),rodata(存放被初始化的数据),data(表示初始化不为0的变量),bss(表示初始化值为默认的全局变量)。
- text,rodata放在flash中,而data中的初始化值作为rodata放在flash中,变量在ram中占有空间,bss占ram空间。
3.2 isr_vector
1 | /* The startup code goes first into FLASH */ |
这一部分表示中断向量表被链接到段 .isr_vector 内。
(1)点( . ) :表示当前虚拟内存地址(location counter)
(2)ALIGN(4) :表示四字节对齐。
(3)KEEP :这个命令来保存所有文件中的 .isr_vector 内容,即使它们没有被调用。也就是在保存中断向量前做四字节对齐,保存后做四字节对齐。
(3)>FLASH :内容存在上面MEMORY中定义的FLASH中。
3.3 text
1 | /* The program code and other data goes into FLASH */ |
第一个冒号左边的.text就是合并后的.text段,它是由符合花括号内规则的所有内容合并得到的,在刚才的例子里就是 main.o 、usart.o 、 start.o 中的.text段。* 号代表通配符,这里它没有前缀和后缀,也没有 [ ] 内容修饰,所以它代表所有匹配文件。
- . = ALIGN(4); :是指4字节对齐。
- . :小数点表示当前的地址位置。
- eh_frame :段可以自定义,由于编译obj过程中不会生成用户自定义的段,因此在源码中需要指定需要特殊处理的段。
- 结尾的 >FLASH指上面花括号内的内容都放在第二部分中定义的FLASH空间中。
剩下的暂时就不说了,详细的语法结构,我们可以看ld.pdf (sourceware.org)的3.6 SECTIONS Command一节,有超级详细的说明。
四、多个链接脚本
我们的工程中只能有一个链接脚本吗?一开始我以为只能有一个,后来发现,其实可以有多个的,最终他们都会汇集到一起。比如,后边我们学习SRAM的时候,可能会使用链接脚本来定义一个段,然后预存数组到指定的地址。我们直接在STM32CubeMX生成的链接脚本中添加也是没问题的,但是就一个坏处,那就是每次STM32CubeMX更新工程的时候,这个脚本都会重新生成,我们添加的东西也都不复存在,所以我们可以自己新建一个链接脚本,然后在编译的时候一起编译就好啦。
1. 新建链接脚本 mine.ld
1 | SECTIONS |
2. 修改Makefile
我们需要把我们自己的链接脚本也添加到Makefile中:
1 | ####################################### |
这样我们那就不用担心更新工程的时候把我们添加的内容清理掉啦。