链接器和链接脚本

链接器通过ld命令将编译后的.o文件转化为可执行文件,主要涉及选项如-T指定链接脚本,-Map输出符号表,-l和-L添加库和搜索路径等。链接脚本用于控制链接过程,包括MEMORY声明内存区域,SECTIONS定义段布局。ENTRY设置程序入口,MEMORY定义内存属性,如只读、读写、可执行等。

链接器

将经过编译或者汇编的二进制文件(.o文件) 生成可执行二进制文件。
例如GNU Linker,提供一个链接命令ld,常用的链接选项如下:

选项注释
-T指定链接脚本
-Map输出一个符号表文件
-o输出最终可执行二进制文件
-b指定目标代码输入文件的格式
-e使用指定的符号作为程序的初始执行点
-l把指定的库文件添加到要链接的文件清单中
-L把指定的路径添加到搜索库的目标清单中
-S忽略来自输出文件的调试器符号信息
-s忽略来自输出文件的所有符号信息
-t在处理输入文件时显示它们的名称
-Ttext使用指定的地址作为代码段的起点
-Tdata使用指定的地址作为数据段的起点
-Tbss使用指定的地址作为未初始化的数据段的起点
-Bstatic只使用静态库
-Bdynamic只使用动态库
-defsym在输出文件中定义指定的全局符号

链接脚本

链接器的链接过程由一个链接脚本控制,该脚本使用链接器命令语言编写,主要用于规定各输入文件中的程序、数据等内容段在输出文件中的空间和地址如何分配。
如果没有通过 “-T” 参数指定链接脚本时,链接器会使用内置的链接脚本。

常用语法汇总

定位符.

定位符.表示当前地址,根据 section 的大小不断增加,不能倒退。对定位符.赋值可修改当前地址,指定其后内容的存储位置,如果没有以其它的方式指定输出节的地址,则地址值会根据定位器的当前值计算。

. = 0x1000;
.text :
{}

. = 0x2000;
.rodata :
{}

先修改起始地址为 0x1000,指定 text 段从当前位置存储;再修改当前地址为 0x2000,指定 rodata 段的存储位置。

ENTRY

ENTRY(SYMBOL) :将符号 SYMBOL 对应的地址设置成程序入口地址。常见的实例比如:ENTRY(_start),表示程序从 _start 处开始运行。设置程序入口的方法有好几种,根据优先级从高到低依次为:

  • ld 命令行的 -e 选项
  • 链接脚本的ENTRY(SYMBOL)命令
  • 在汇编程序中定义了 _start 符号,使用 _start 符号值(如.global _start)
  • 如果存在 .text section,使用 .text section 首地址的值
  • 从地址 0 开始运行
MEMORY
MEMORY 
{
    NAME1 [(ATTR)] : ORIGIN = ORIGIN1, LENGTH = LEN2
    NAME2 [(ATTR)] : ORIGIN = ORIGIN2, LENGTH = LEN2
}

MEMORY声明一个或多个内存区域,名称必须唯一,其属性指定该区域是否可以写入、读取或执行。
ATTR属性支持7种模式:

  • R 只读section
  • W 读/写section
  • X 可执行section
  • A 可分配的section
  • I 初始化了的section
  • L 同 I
  • ! 反转以上任何属性
    常见的内存设置一般有一个flash和一个sram区域:
MEMORY
{
	flash (rxa) : ORIGIN = 0x80000000, LENGTH = 128K
	sram (wxa) : ORIGIN = 0x20000000, LENGTH = 32K
}
SECTIONS
SECTIONS
{
	secname [VMA_ADDR] [(TYPE)] : [AT (LMA_ADDR)]
	{ 
		contents 
	} [>VMA_REGION] [AT>LMA_REGION] [:PHDR HDR ...] [=FILLEXP]
}
  • secname
    表示输出文件的段名,后面必须紧跟一个空格,使用冒号**:**分割
  • contents
    描述该段的内容包括哪些文件、符号
  • TYPE
    每个输出section都有一个类型,如果没有指定TYPE类型,那么连接器根据输出section引用的输入section的类型设置该输出section的类型。例如 NOLOAD,表示该section在程序运行时,不被载入内存。
  • VMA_REGION
    对应 MEMORY 定义的内存区域。
  • LMA_REGION
    对应 MEMORY 定义的内存区域。
  • VMA_ADDR
    虚地址,即输出文件VMA(运行地址)。表示将该段强制链接到的地址,会改变定位符.的值。
  • LMA_ADDR
    加载地址,即数据实际存储的地。表示该段的 LMA(加载地址),默认情况下 LMA = VMA。数据段加载时会存至 Flash 中(使用LMA地址),运行时将其搬运到 RAM(使用VMA地址)。
.text :
{
} >flash AT>flash

.bss :
{
} >flash AT>sram
ALIGN

表示字节对齐, 如. = ALIGN(4)表示从该地址开始后面的存储内容进行4字节对齐。

PROVIDE

定义一个符号,相当于定义一个全局变量的符号表,其他C文件可以通过该符号来操作对应的存储内存。程序中可以先使用该符号,虽然此时未定义,但是链接时会识别到该符号。

KEEP

当链接器使用–gc-sections进行垃圾回收时,链接器可能将某些它认为没用的 section 过滤掉,此时就有必要强制让链接器保留一些特定的 section,KEEP()可以使得被标记section的内容不被清除(即防止被优化)。

参考链接

在 Keil MDK 中,连接器脚本的配置主要通过工程选项中的 **Linker** 页面进行设置。Keil 使用的是 ARM 自带的连接器(armlink),其行为由链接脚本文件(通常以 `.sct` 为扩展名)控制。以下是详细的配置方法: ### 配置步骤 1. **打开工程选项** 在 Project 窗口中右键点击目标(Target 1),选择 “Options for Target ‘Target1’” 或者使用菜单栏的 “Project → Option for target ‘target1’”。[^2] 2. **进入 Linker 设置页面** 在弹出的对话框中,切换到 **Linker** 页面。该页面允许用户对链接器进行详细配置。 3. **使用默认链接脚本** Keil 默认会自动生成一个链接脚本,用于定义内存布局段分配。如果不需要特殊定制,可以勾选 **Use Memory Layout from Target Dialog** 选项,让 Keil 根据 Target 页面中的内存设置生成链接脚本。 4. **手动指定链接脚本** 如果需要自定义链接行为,取消勾选 **Use Memory Layout from Target Dialog**,然后点击 **Scatter File** 旁边的按钮,选择或创建一个 `.sct` 文件。这个文件描述了代码数据在内存中的布局,例如: ```text LR_IROM1 0x08000000 0x00080000 { ; load region size_region ER_IROM1 0x08000000 0x00080000 { ; execution region *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00010000 { ; RW data .ANY (+RW +ZI) } } ``` 这个脚本定义了程序代码(RO)、读写数据(RW)零初始化数据(ZI)在内存中的分布方式。 5. **高级链接器参数** 在 **Linker** 页面下方的 **Misc controls** 输入框中,可以输入一些额外的链接器选项,例如 `-Map` 用于生成映射文件,帮助分析内存使用情况。 6. **调试符号与优化选项** Linker 页面还提供了一些关于调试信息优化的选项,确保在调试阶段保留完整的调试信息,而在最终发布时启用适当的优化级别。 ### 脚本编写注意事项 - **内存区域定义** 确保 `.sct` 文件中的内存地址与芯片的实际 Flash RAM 容量匹配。例如 STM32F103 系列的 Flash 起始地址是 `0x08000000`,而 SRAM 的起始地址通常是 `0x20000000`。 - **段分配策略** 根据应用需求调整 `.sct` 文件中的段分配策略,例如将某些关键函数放在高速缓存区域,或者将常量数据放在特定的只读段中。 - **分散加载** 如果项目涉及多个存储区域(如外部 SDRAM),可以通过分散加载机制将不同部分的代码数据分配到不同的存储器中,提高执行效率。 ### 示例:查看链接器输出 可以在命令窗口中使用以下命令查看链接器的输出信息: ```shell dir ``` 此命令可用于显示当前的符号表,帮助确认链接器是否正确地处理了所有符号。[^3] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怦然心动如往昔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值