Linux链接脚本的核心概念与作用
Linux链接脚本(Linker Script)是链接器(如ld)在链接过程中使用的配置文件,它决定了输入目标文件(.o文件)和库文件如何被组合成最终的可执行文件或共享库,链接脚本的核心作用是控制内存布局、符号解析、段(Section)的合并与定位等关键环节,通过自定义链接脚本,开发者可以精确管理程序的加载地址、对齐方式以及数据段的存放位置,这在嵌入式开发、操作系统内核构建或需要特定内存布局的场景中尤为重要。

链接脚本的基本结构与语法
链接脚本主要由命令(Commands)、赋值语句(Assignments)和符号定义(Symbol Definitions)三部分组成,其语法采用类似C语言的简洁风格,核心元素包括段(SECTIONS)、内存区域(MEMORY)以及输出入口(ENTRY)。
MEMORY命令:定义内存区域
MEMORY命令用于描述目标系统的物理内存布局,例如RAM、ROM等区域的起始地址和大小,链接器会根据此信息决定哪些段可以存放在哪些内存区域中,语法如下:
MEMORY {
name1 (attr) : ORIGIN = origin, LENGTH = len
name2 (attr) : ORIGIN = origin, LENGTH = len
}
name为内存区域名称,ORIGIN(简写为org)表示起始地址,LENGTH(简写为len)表示区域大小,attr为属性(如R只读、W可写、X可执行),定义一个起始地址为0x08000000、大小为1MB的RAM区域:
MEMORY {
RAM (rwx) : ORIGIN = 0x08000000, LENGTH = 1M
}
SECTIONS命令:组织段布局
SECTIONS命令是链接脚本的核心,用于定义输出文件的段结构,包括输入段的合并顺序、存放位置以及对齐方式,其基本语法为:
SECTIONS {
section_name {
output-section-command
output-section-command
...
}
...
}
常见的输出段命令包括:
input-sections:指定输入目标文件中的段,如.text(代码段)、.data(已初始化数据段)、.bss(未初始化数据段),将所有输入文件的.text段合并到输出文件的.text段:.text : { *(.text) }symbol = value:定义符号及其值,常用于设置入口点或全局变量地址:_start = 0x08000000;
ALIGN:对齐当前地址到指定边界,如.data ALIGN(4) : { ... }表示.data段按4字节对齐。
ENTRY命令:指定程序入口
ENTRY命令用于定义程序的入口点,即链接器首先执行的位置,语法为ENTRY(symbol),其中symbol为入口符号名。

ENTRY(_start)
链接脚本的执行流程
链接器在处理链接脚本时,遵循以下核心步骤:
- 解析MEMORY区域:根据MEMORY命令定义的内存布局,确定可用的内存范围及属性。
- 处理SECTIONS命令:遍历SECTIONS中定义的输出段,依次将输入目标文件中的对应段合并到输出段中,所有
.text段会被合并到输出文件的.text段,.data和.bss段同理。 - 符号解析与重定位:解析输入文件中的符号(如函数、变量),确定它们的最终地址,并处理重定位表(如
.rel.text),确保代码中的绝对地址或相对引用正确。 - 地址分配与对齐:根据SECTIONS中的对齐要求(如
ALIGN)和内存区域限制,为每个段分配最终地址。.bss段通常紧邻.data段,并按指定对齐边界填充零。 - 生成输出文件:根据分配的地址和合并后的段内容,生成最终的可执行文件(ELF格式)或共享库。
常用链接脚本示例
示例1:基础可执行文件链接脚本
以下是一个简单的链接脚本,适用于将.text、.data和.bss段依次存放在RAM中:
ENTRY(_start)
MEMORY {
RAM (rwx) : ORIGIN = 0x08000000, LENGTH = 64K
}
SECTIONS {
.text : {
*(.text)
*(.rodata)
} > RAM
.data : {
*(.data)
} > RAM
.bss : {
*(.bss)
*(COMMON)
} > RAM
}
说明:
_start为入口符号,需在汇编或C代码中定义(如通过__attribute__((section(".text"))))。.text段包含代码和只读数据(.rodata),.data段包含已初始化数据,.bss段包含未初始化数据,均存放在RAM中。
示例2:嵌入式系统多段布局
在嵌入式开发中,常需将代码段存放在Flash(ROM),数据段存放在RAM。
ENTRY(reset_handler)
MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS {
.isr_vector : {
KEEP(*(.isr_vector))
} > FLASH
.text : {
*(.text)
} > FLASH
.data : {
_sdata = .;
*(.data)
_edata = .;
} > RAM AT > FLASH
.bss : {
_sbss = .;
*(.bss)
*(COMMON)
_ebss = .;
} > RAM
/* 将.data段从Flash复制到RAM的启动代码 */
_sidata = LOADADDR(.data);
}
说明:
.isr_vector(中断向量表)存放在Flash,通过KEEP确保链接器不优化掉该段。.data段在RAM中运行,但初始数据存放在Flash,需在启动时复制到RAM(通过_sdata、_edata等符号定位)。.bss段需在启动时清零,通过_sbss、_ebss确定范围。
链接脚本的进阶技巧
使用符号控制内存布局
通过定义符号(如_stack_top),可在C代码中访问栈顶地址,用于初始化栈指针:

SECTIONS {
...
_stack_top = ORIGIN(RAM) + LENGTH(RAM);
}
在C代码中可通过extern uint32_t _stack_top;获取栈顶地址。
条件判断与文件包含
链接脚本支持简单的条件判断(如if/else)和文件包含(INCLUDE),便于模块化管理,根据编译选项选择不同的内存布局:
INCLUDE "memory.ld" /* 包含基础内存定义 */
SECTIONS {
#ifdef USE_RAM_ONLY
.text : { *(.text) } > RAM
#else
.text : { *(.text) } > FLASH
#endif
}
处理特殊段与垃圾回收
通过KEEP命令可强制保留某些段(如调试信息、中断向量表),防止链接器因优化而丢弃:
KEEP(*(.debug_info))
Linux链接脚本是链接器的“配置说明书”,通过精确控制内存布局、段合并和符号解析,满足不同场景下的程序构建需求,从基础的.text/.data/.bss段管理,到嵌入式系统的多存储器布局,再到进阶的符号控制与模块化设计,掌握链接脚本能够帮助开发者优化程序性能、解决内存冲突,并实现复杂的系统级功能,无论是内核开发、嵌入式固件编写,还是需要特定内存布局的应用程序,合理编写链接脚本都是提升开发效率与系统稳定性的关键环节。

















