## 链接脚本基本结构一个标准的链接脚本包含以下部分:
链接脚本(LD)段书写规范详解
链接脚本(Linker Script)是控制程序内存布局的关键文件,其语法有严格的规范要求。下面我将详细介绍LD脚本中各个段的书写规范,并附图示说明。
链接脚本基本结构
/* 注释格式:使用C风格注释 */
MEMORY {
/* 内存区域定义 */
}
SECTIONS {
/* 段定义 */
}
1. 内存区域定义规范(MEMORY)
MEMORY {
/* 基本语法:名称 (属性) : ORIGIN = 起始地址, LENGTH = 长度 */
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 512K /* 可读可执行 */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K /* 可读可写可执行 */
/* 可选特殊区域 */
ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 16K /* 紧耦合内存 */
DTCM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
属性说明:
| 属性 | 含义 | 是否必需 |
|---|---|---|
| r | 可读 | 是 |
| w | 可写 | 是 |
| x | 可执行 | 是 |
| a | 可分配 | 可选 |
| i | 初始化 | 可选 |
| l | 类似i | 可选 |
内存布局图示:
┌──────────────────────┐ 0x08000000 │ ROM │ │ (512K, rx) │ ├──────────────────────┤ 0x08080000 │ RAM │ │ (128K, rwx) │ ├──────────────────────┤ 0x20020000 │ ITCM │ │ (16K, rwx) │ ├──────────────────────┤ 0x00004000 │ DTCM │ │ (64K, rw) │ └──────────────────────┘ 0x10010000
2. 段定义规范(SECTIONS)
2.1 基本段定义
SECTIONS {
/* 定位符:. 表示当前位置 */
. = 0x08000000; /* 设置起始地址 */
/* 文本段(代码) */
.text : {
/* 输入段排序 */
KEEP(*(.vectors)) /* 必须保留中断向量表 */
*(.text*) /* 所有.text开头的段 */
*(.rodata*) /* 只读数据 */
. = ALIGN(4); /* 4字节对齐 */
} > ROM
/* 数据段 */
.data : {
_sdata = .; /* 定义开始符号 */
*(.data*)
. = ALIGN(4);
_edata = .; /* 定义结束符号 */
} > RAM AT > ROM /* VMA在RAM,LMA在ROM */
/* BSS段 */
.bss (NOLOAD) : { /* NOLOAD表示不加载到ROM */
_sbss = .;
*(.bss*)
*(COMMON) /* 公共块 */
. = ALIGN(4);
_ebss = .;
} > RAM
/* 堆栈段 */
.stack (NOLOAD) : {
. = ALIGN(8);
_sstack = .;
. += _stack_size; /* 预定义堆栈大小 */
. = ALIGN(8);
_estack = .;
} > RAM
/* 自定义段 */
.my_section : {
KEEP(*(.custom*)) /* 保留自定义段 */
} > ITCM
}
2.2 关键指令规范
| 指令 | 语法 | 说明 | 示例 |
|---|---|---|---|
| ALIGN | . = ALIGN(n); | n字节对齐 | . = ALIGN(8); |
| KEEP | KEEP(*(section)) | 防止链接器优化 | KEEP(*(.init)) |
| AT | > region AT > lma_region | 指定加载地址 | > RAM AT > ROM |
| NOLOAD | (NOLOAD) | 不加载到目标文件 | .bss (NOLOAD) : {...} |
| PROVIDE | PROVIDE(symbol = expression) | 定义符号 | PROVIDE(_heap_start = .); |
| ENTRY | ENTRY(symbol) | 指定入口点 | ENTRY(Reset_Handler) |
3. 段属性详解
3.1 输出段属性
.text : {
/* 内容 */
} > ROM /* 目标内存区域 */
AT > FLASH /* 加载地址 */
ALIGN_WITH_INPUT /* 输入段对齐 */
SUBALIGN(4) /* 子对齐 */
: { /* 输入段描述 */ } /* 输入段过滤 */
3.2 输入段排序规范
*(.text .text.*) /* 所有.text和.text.*段 */ *(.rodata) /* 所有.rodata段 */ *(SORT_BY_NAME(.data*)) /* 按名称排序 */ *(SORT_BY_ALIGNMENT(.bss*)) /* 按对齐排序 */
4. 符号定义规范
/* 全局符号定义 */ _etext = .; /* 当前地址赋值 */ _flash_end = ORIGIN(ROM) + LENGTH(ROM); /* 计算结束地址 */ _stack_size = DEFINED(_stack_size) ? _stack_size : 0x2000; /* 带默认值 */ /* 条件定义 */ PROVIDE(__heap_start = .); PROVIDE(__heap_end = ORIGIN(RAM) + LENGTH(RAM));
5. 完整示例模板
/* 链接脚本模板 */
MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
}
_estack = ORIGIN(RAM) + LENGTH(RAM); /* 栈顶 */
_stack_size = 0x4000; /* 栈大小 */
_heap_size = 0x8000; /* 堆大小 */
ENTRY(Reset_Handler); /* 入口点 */
SECTIONS {
/* 中断向量表 */
.isr_vector : {
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} > FLASH
/* 文本段 */
.text : {
. = ALIGN(4);
*(.text)
*(.text*)
*(.glue_7)
*(.glue_7t)
*(.eh_frame)
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
_etext = .;
} > FLASH
/* 只读数据 */
.rodata : {
. = ALIGN(4);
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} > FLASH
/* ARM特定段 */
.ARM.extab : {
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > FLASH
/* 初始化数据 */
_sidata = LOADADDR(.data); /* 加载地址 */
.data : {
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .;
} > RAM AT > FLASH
/* 未初始化数据 */
.bss : {
. = ALIGN(4);
_sbss = .;
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
__bss_end__ = _ebss;
} > RAM
/* 用户堆栈 */
._user_heap_stack : {
. = ALIGN(8);
PROVIDE(end = .);
PROVIDE(_end = .);
. += _heap_size;
. += _stack_size;
. = ALIGN(8);
} > RAM
/* 调试信息 */
/DISCARD/ : {
libc.a(*)
libm.a(*)
libgcc.a(*)
*(.note*)
*(.comment)
}
}
6. 段映射图示
内存布局示例:
┌──────────────────────┐ 0x08000000 (FLASH)
│ .isr_vector │
├──────────────────────┤
│ .text │
├──────────────────────┤
│ .rodata │
├──────────────────────┤
│ .ARM.extab │
├──────────────────────┤
│ .ARM │
├──────────────────────┤
│ .data │ (LMA)
└──────────────────────┘
↓ 加载时复制
┌──────────────────────┐ 0x20000000 (RAM)
│ .data │ (VMA)
├──────────────────────┤
│ .bss │
├──────────────────────┤
│ .user_heap_stack │
└──────────────────────┘ 0x20040000
7. 最佳实践与注意事项
-
对齐要求:
-
始终使用
ALIGN(4)或ALIGN(8)保证对齐 -
特别关注向量表和栈地址对齐
-
-
内存保护:
.privileged_data : { *(.privileged*) } > RAM AT > FLASH -
多核系统:
.core0_text : { *(.core0.text*) } > FLASH0 .core1_text : { *(.core1.text*) } > FLASH1 -
安全启动:
.secure : { KEEP(*(.secure_entry)) *(.secure_code*) . = ALIGN(128K); /* 对齐到扇区边界 */ } > FLASH -
版本信息:
.fw_info : { KEEP(*(.fw_version)) KEEP(*(.fw_checksum)) } > FLASH AT > FLASH -
错误处理:
-
检查内存区域重叠
-
验证段大小是否超出内存限制
-
使用链接器映射文件验证布局
-
8. 调试与验证
生成内存映射文件:
arm-none-eabi-ld -T link.ld -Map=output.map ...
关键检查点:
-
入口点地址是否正确
-
堆栈空间是否足够
-
关键段是否对齐
-
内存区域是否溢出
-
符号地址是否符合预期
通过遵循这些规范,可以创建出高效、可靠且易于维护的链接脚本,确保嵌入式系统程序正确加载和执行。
链接脚本(LD)段书写规范详解
1065

被折叠的 条评论
为什么被折叠?



