链接脚本(Linker Script,.lds)基础语法
·
链接脚本(Linker Script,通常以.lds 为扩展名)是 GNU 链接器(ld)使用的脚本文件,用于控制程序的链接过程,定义内存布局、段(section)的排列方式以及符号的地址分配等。以下是其核心语法和常用结构:
1. 基本结构
链接脚本的基本格式如下,主要包含内存布局定义和段布局定义两部分:
/* 注释:以/* ... */ 或 // 开头 */
/* 定义内存区域(可选)*/
MEMORY {
内存块名称 (属性) : ORIGIN = 起始地址, LENGTH = 长度
/* 示例: */
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
/* 定义输出文件的段布局(必选)*/
SECTIONS {
/* 段定义 */
.text 0x08000000 : { /* 代码段 */
*(.text) /* 所有输入文件的.text段 */
} > ROM /* 绑定到MEMORY中定义的ROM区域 */
.data : { /* 数据段(初始化的全局变量) */
*(.data)
} > RAM AT > ROM /* 加载到ROM,运行时复制到RAM */
.bss : { /* 未初始化数据段 */
*(.bss)
__bss_end = .; /* 定义符号记录.bss段结束地址 */
} > RAM
}
2. 核心语法元素
(1)内存区域定义(MEMORY)
用于描述目标系统的内存分布(如 ROM、RAM 的地址范围和属性),格式:
MEMORY {
内存块名 [(属性)] : ORIGIN = 起始地址, LENGTH = 大小
}
- 属性:
r(可读)、w(可写)、x(可执行)、!(禁止),如(rx)表示只读可执行。 - 地址和大小:支持十六进制(
0x前缀)、十进制,单位可加K(1024)、M(1024K)。
(2)段定义(SECTIONS)
控制输入文件中的段(如.text、.data)如何映射到输出文件的段,是链接脚本的核心。
基本格式:
SECTIONS {
输出段名 [起始地址] : {
输入段列表 /* 哪些输入文件的段放入此输出段 */
[符号定义] /* 在此位置定义符号 */
} [> 内存块名] [AT 加载地址]
}
- 输出段名:通常与输入段名一致(如
.text、.data),也可自定义(如.mysection)。 - 起始地址:可选,指定段的起始地址(如
.text 0x08000000)。 - 输入段列表:
*(.text):匹配所有文件的.text段。file.o(.data):仅匹配file.o的.data段。*.o(.rodata*):匹配所有.o文件中以.rodata开头的段。
- 符号定义:
.表示当前地址(类似指针)。__text_start = .;:定义符号__text_start为当前地址(即段的起始)。KEEP(*(.init)):强制保留.init段(防止链接器删除未引用的段)。
- 内存绑定:
> 内存块名将段放入指定的内存区域(如> RAM)。 - 加载地址:
AT 地址指定段在存储介质中的地址(运行时可能被复制到其他地址,如.data段通常存储在 ROM,运行时复制到 RAM)。
(3)符号操作
- 内置符号:
_start:程序入口点(可通过ENTRY(_start)指定)。__executable_start:程序起始地址。
- 自定义符号:通过
符号名 = 表达式;定义,常用场景:.text : { __text_start = .; /* 段起始地址 */ *(.text) __text_end = .; /* 段结束地址 */ } .bss : { __bss_start = .; *(.bss) . = ALIGN(4); /* 地址对齐到4字节 */ __bss_end = .; }
(4)常用命令
ENTRY(symbol):指定程序入口点(如ENTRY(main))。ALIGN(n):将当前地址对齐到n字节边界(如. = ALIGN(8);)。INCLUDE filename:包含其他脚本文件。EXTERN(symbol):声明外部符号(确保链接器不会删除引用此符号的代码)。
3. 示例:嵌入式系统常用 lds 脚本
/* 定义内存:Flash起始地址0x08000000,大小1MB;RAM起始0x20000000,大小128KB */
MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1M
RAM (rwx): ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS {
/* 代码段:放在Flash,起始地址0x08000000 */
.text : {
KEEP(*(.isr_vector)) /* 中断向量表(必须保留在最开始) */
*(.text) /* 所有代码段 */
*(.rodata) /* 只读数据 */
. = ALIGN(4);
__text_end = .;
} > FLASH
/* 初始化数据段:加载在Flash,运行时复制到RAM */
.data : AT (__text_end) {
__data_start = .;
*(.data)
. = ALIGN(4);
__data_end = .;
} > RAM
/* 未初始化数据段:仅占用RAM空间 */
.bss : {
__bss_start = .;
*(.bss)
*(.COMMON) /* 未初始化的全局变量 */
. = ALIGN(4);
__bss_end = .;
} > RAM
/* 栈顶地址定义(通常在RAM末尾) */
_estack = ORIGIN(RAM) + LENGTH(RAM);
}
4. 关键作用
- 控制程序在内存中的布局(如代码放 ROM,数据放 RAM)。
- 定义特殊地址的符号(如中断向量表位置、栈顶地址)。
- 处理段的加载与运行地址分离(如嵌入式系统中
.data段的复制)。
通过链接脚本,开发者可以精确控制程序的内存分布,这在嵌入式开发、操作系统内核等场景中尤为重要。
更多推荐
所有评论(0)