链接脚本(Linker Script)完全解析
一、基本概念
链接脚本的作用
- 控制目标文件(.o)如何组合成可执行文件
- 定义内存布局(Memory Layout)
- 指定各段(Section)的物理存储位置和运行地址
核心概念对照表
术语 全称 中文解释 示例 VMA Virtual Memory Address 运行时内存地址 0x20000000 (SRAM) LMA Load Memory Address 加载/存储地址 0x08004000 (FLASH) ORIGIN Memory Region Origin 内存区域起始地址 MEMORY区块定义 LENGTH Memory Region Length 内存区域长度 256K SECTION Output Section 输出段 .text, .data ENTRY Program Entry Point 程序入口点 _start
二、核心指令详解
1. MEMORY 区块
1 | MEMORY { |
- 功能:定义物理内存区域
- 参数说明:
(rx)
:访问权限(r=读, w=写, x=执行)ORIGIN
:起始地址(别名:org, o)LENGTH
:区域长度(别名:len, l)
2. SECTIONS 区块
1 | SECTIONS { |
结构:
段名 : { 内容 } > VMA区域 AT> LMA区域
特殊符号:
.
:当前位置计数器(Location Counter)*
:通配所有目标文件KEEP()
:防止链接器优化删除
3. 入口点设置
1 | ENTRY(_start) |
- 指定程序执行的第一个指令符号
- 对应ELF头的
e_entry
字段
4. 符号导出
1 | PROVIDE(__stack_top = ORIGIN(SRAM) + LENGTH(SRAM)); |
- 定义全局符号供C代码使用
- 避免与用户定义符号冲突
三、标准段类型详解
段名 | 全称 | 内容 | 典型存储位置 |
---|---|---|---|
.text | Text Section | 可执行代码 | FLASH |
.rodata | Read-Only Data | 常量数据 | FLASH |
.data | Data Section | 已初始化全局变量 | SRAM |
.bss | Block Started by Symbol | 未初始化全局变量(清零段) | SRAM |
.heap | Heap Area | 动态内存区域 | SRAM末尾 |
.stack | Stack Area | 函数调用栈 | SRAM顶端 |
.vectors | Interrupt Vector Table | 中断向量表 | 特定地址 |
四、高级指令解析
1. PHDRS(Program Headers)
1 | PHDRS { |
- 控制ELF程序头信息
- 类型包括:PT_LOAD(可加载段)、PT_NOTE(注释信息)等
2. ALIGN 对齐控制
1 | . = ALIGN(4); /* 4字节对齐 */ |
- 确保当前位置按指定字节对齐
- 对性能关键区域(如向量表)必须使用
3. AT> 与 OVERLAY
1 | .data : { ... } > SRAM AT> FLASH |
- 实现非连续地址映射
- OVERLAY用于共享内存区域:
1
2
3
4OVERLAY 0x1000 : {
.task1 { ... }
.task2 { ... }
}
五、RISC-V 专用配置
1. 中断向量表定位
1 | .vectors : { |
- 需与
mtvec
寄存器设置一致 - 典型地址:0x00000000(机器模式)
2. 指令压缩扩展
1 | /* 确保16位对齐 */ |
3. 原子操作区
1 | .lrsc : { |
- 对LR/SC指令需要严格对齐
六、调试技巧
1. 生成映射文件
1 | riscv64-unknown-elf-ld -T script.ld -Map=map.txt |
关键信息检查点:
- 各段的VMA/LMA
- 符号地址是否正确
- 内存区域使用率
2. 符号查看
1 | riscv64-unknown-elf-nm -n program.elf |
输出示例:
1 | 08000000 T _start |
3. 段内容检查
1 | riscv64-unknown-elf-objdump -s -j .data program.elf |
七、典型链接脚本模板
1 | ENTRY(_start) |
八、常见问题速查
现象 | 可能原因 | 解决方案 |
---|---|---|
程序无法启动 | 入口地址错误 | 检查ENTRY()与复位向量 |
变量初始值丢失 | .data段未正确拷贝 | 验证启动代码的拷贝逻辑 |
函数调用崩溃 | 栈指针未初始化 | 检查_stack_top符号定义 |
代码执行异常 | .text段地址与MTVEC不匹配 | 核对异常入口地址 |
内存区域溢出 | LENGTH设置过小 | 使用链接器生成的填充模式调试 |
九、扩展学习建议
官方文档
- GNU ld手册:https://sourceware.org/binutils/docs/ld/
- RISC-V特权架构手册:第3章内存管理
调试工具链
1
2
3
4# 查看支持的架构
riscv64-unknown-elf-ld --verbose
# 生成默认链接脚本
riscv64-unknown-elf-ld -verbose
本文作者:
ICXNM-ZLin
本文链接: https://talent-tudou.github.io/2025/02/22/RISC-V/RSIC-V之链接文件/
版权声明: 本作品采用 CC BY-NC-SA 4.0 进行许可。转载请注明出处!
本文链接: https://talent-tudou.github.io/2025/02/22/RISC-V/RSIC-V之链接文件/
版权声明: 本作品采用 CC BY-NC-SA 4.0 进行许可。转载请注明出处!