name: linker-script description: GNU链接器脚本生成与优化,适用于嵌入式系统。具备内存布局定义、段放置、多镜像链接和内存保护配置的专业技能。 allowed-tools: Read, Grep, Write, Edit, Bash, Glob
链接器脚本技能
适用于嵌入式系统的GNU链接器脚本生成与优化专家技能。提供内存布局定义、段放置、符号管理和高级链接场景的深度专业知识。
概述
链接器脚本技能支持嵌入式系统的全面链接器脚本开发,包括:
- 针对MCU目标的内存区域定义
- 段放置和对齐配置
- 引导加载程序/应用程序接口的符号生成
- 多镜像链接(引导加载程序 + 应用程序)
- 覆盖和存储体切换支持
- 自定义段创建和管理
- 填充模式和校验和放置
- MPU对齐区域配置
能力
1. 内存区域定义
基于MCU规格定义内存区域:
/* 示例:STM32F407 内存布局 */
MEMORY
{
/* Flash 内存区域 */
FLASH_BOOT (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* 引导加载程序 */
FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 480K /* 应用程序 */
FLASH_DATA (r) : ORIGIN = 0x08080000, LENGTH = 128K /* 配置/OTA */
/* RAM 区域 */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K /* 主 SRAM */
CCMRAM (rwx) : ORIGIN = 0x10000000, LENGTH = 64K /* 核心耦合 RAM */
BKPSRAM (rw) : ORIGIN = 0x40024000, LENGTH = 4K /* 备份 SRAM */
}
2. 段放置配置
配置段放置及对齐要求:
SECTIONS
{
/* 向量表位于 Flash 起始处 */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} > FLASH_APP
/* 代码段 */
.text :
{
. = ALIGN(4);
*(.text)
*(.text*)
*(.glue_7) /* ARM/Thumb 交互工作 */
*(.glue_7t)
*(.eh_frame)
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
_etext = .;
} > FLASH_APP
/* 只读数据 */
.rodata :
{
. = ALIGN(4);
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} > FLASH_APP
}
3. 符号生成
生成固件接口符号:
/* 引导加载程序/应用程序接口的符号 */
PROVIDE(_app_start = ORIGIN(FLASH_APP));
PROVIDE(_app_end = ORIGIN(FLASH_APP) + LENGTH(FLASH_APP));
PROVIDE(_app_checksum = _app_end - 4);
/* 栈和堆边界 */
PROVIDE(_estack = ORIGIN(RAM) + LENGTH(RAM));
PROVIDE(_Min_Heap_Size = 0x2000); /* 8KB 堆 */
PROVIDE(_Min_Stack_Size = 0x1000); /* 4KB 栈 */
/* RAM 函数执行 */
PROVIDE(_siramfunc = LOADADDR(.ramfunc));
PROVIDE(_sramfunc = ADDR(.ramfunc));
PROVIDE(_eramfunc = ADDR(.ramfunc) + SIZEOF(.ramfunc));
4. 多镜像链接
支持引导加载程序和应用程序作为独立镜像:
/* 引导加载程序链接器脚本摘录 */
MEMORY
{
FLASH_BOOT (rx) : ORIGIN = 0x08000000, LENGTH = 32K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
/* 用于引导到应用程序通信的共享数据区域 */
.shared_data (NOLOAD) :
{
. = ALIGN(4);
_shared_data_start = .;
KEEP(*(.shared_data))
. = ALIGN(4);
_shared_data_end = .;
} > RAM
/* 应用程序头位置(引导加载程序已知) */
_app_header_addr = 0x08008000;
5. MPU 区域配置
定义MPU对齐的内存区域:
/* MPU 对齐区域(2的幂次方大小,按大小对齐) */
.mpu_region_stack (NOLOAD) :
{
. = ALIGN(1024); /* MPU 最小区域大小 */
_mpu_stack_start = .;
. = . + 4096; /* 4KB 栈区域 */
_mpu_stack_end = .;
} > RAM
.mpu_region_heap (NOLOAD) :
{
. = ALIGN(8192); /* 8KB 对齐 */
_mpu_heap_start = .;
. = . + 8192;
_mpu_heap_end = .;
} > RAM
6. 覆盖支持
为内存受限系统配置覆盖段:
OVERLAY : NOCROSSREFS
{
.overlay_a { *(.overlay_a) }
.overlay_b { *(.overlay_b) }
.overlay_c { *(.overlay_c) }
} > RAM AT > FLASH_APP
/* 覆盖管理符号 */
_overlay_a_load = LOADADDR(.overlay_a);
_overlay_b_load = LOADADDR(.overlay_b);
_overlay_c_load = LOADADDR(.overlay_c);
7. 校验和放置
配置固件验证的校验和/CRC放置:
/* 为固件校验和预留空间 */
.checksum :
{
. = ALIGN(4);
_firmware_checksum = .;
LONG(0xFFFFFFFF); /* 占位符,构建后填充 */
. = ALIGN(4);
} > FLASH_APP
/* 用于校验和计算的镜像长度 */
_firmware_length = _firmware_checksum - ORIGIN(FLASH_APP);
流程集成
此技能与以下流程集成:
| 流程 | 集成点 |
|---|---|
memory-architecture-planning.js |
内存映射设计和验证 |
bootloader-implementation.js |
引导/应用程序接口定义 |
bsp-development.js |
BSP 内存配置 |
code-size-optimization.js |
段优化分析 |
工作流程
1. 收集 MCU 规格
# 从数据手册或参考手册中提取内存映射
# 所需关键信息:
# - Flash 基地址和大小
# - RAM 区域(主 SRAM、CCMRAM、备份 SRAM)
# - 外设内存区域
# - MPU 区域要求
2. 定义内存需求
## 内存需求分析
| 组件 | Flash | RAM | 备注 |
|-----------|-------|-----|-------|
| 引导加载程序 | 32K | 8K | 固定位置 |
| 应用程序 | 480K | 96K | 主固件 |
| 配置数据 | 8K | - | 非易失性设置 |
| OTA 暂存 | 120K | - | A/B 更新支持 |
总 Flash: 640K / 1024K (62.5% 已使用)
总 RAM: 104K / 128K (81.3% 已使用)
3. 生成链接器脚本
该技能根据需求生成完整的链接器脚本:
# 输出文件:
# - memory.ld - 内存区域定义(由主脚本包含)
# - sections.ld - 段定义(由主脚本包含)
# - application.ld - 主应用程序链接器脚本
# - bootloader.ld - 引导加载程序链接器脚本(如需要)
4. 验证内存使用
# 分析二进制大小
arm-none-eabi-size firmware.elf
# 详细映射文件分析
arm-none-eabi-nm -S --size-sort firmware.elf
# 生成内存使用报告
python scripts/analyze_map.py build/firmware.map
输出模式
{
"linkerScript": {
"mainScript": "application.ld",
"includes": ["memory.ld", "sections.ld"],
"bootloaderScript": "bootloader.ld"
},
"memoryMap": {
"flash": {
"total": 1048576,
"regions": [
{ "name": "FLASH_BOOT", "origin": "0x08000000", "length": 32768 },
{ "name": "FLASH_APP", "origin": "0x08008000", "length": 491520 }
]
},
"ram": {
"total": 131072,
"regions": [
{ "name": "RAM", "origin": "0x20000000", "length": 131072 }
]
}
},
"symbols": {
"exportedToBootloader": ["_app_start", "_app_end", "_app_checksum"],
"importedFromBootloader": ["_shared_data"],
"stackHeap": ["_estack", "_Min_Heap_Size", "_Min_Stack_Size"]
},
"validation": {
"flashUtilization": 0.625,
"ramUtilization": 0.813,
"mpuCompatible": true,
"warnings": []
},
"artifacts": [
"application.ld",
"bootloader.ld",
"memory.ld",
"sections.ld",
"memory-map.md"
]
}
常见模式
RAM 函数(从 RAM 执行)
/* 复制到 RAM 执行的函数(例如,Flash 编程) */
.ramfunc :
{
. = ALIGN(4);
_sramfunc = .;
*(.ramfunc)
*(.ramfunc*)
. = ALIGN(4);
_eramfunc = .;
} > RAM AT > FLASH_APP
_siramfunc = LOADADDR(.ramfunc);
初始化数据(.data 段)
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .;
} > RAM AT > FLASH_APP
_sidata = LOADADDR(.data);
零初始化数据(.bss 段)
.bss (NOLOAD) :
{
. = ALIGN(4);
_sbss = .;
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
__bss_end__ = _ebss;
} > RAM
栈和堆
/* 用户堆和栈 */
._user_heap_stack (NOLOAD) :
{
. = ALIGN(8);
PROVIDE(end = .);
PROVIDE(_end = .);
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} > RAM
/* 验证有足够空间 */
ASSERT((_estack - _ebss) >= (_Min_Heap_Size + _Min_Stack_Size),
"堆和栈的 RAM 不足")
最佳实践
内存对齐
- 将向量表对齐到 2 的幂次方(通常为 256 或 512 字节)
- 将代码段对齐到 4 字节(针对 ARM)
- 将 MPU 区域对齐到其大小(2 的幂次方)
- 将 DMA 缓冲区对齐到缓存行大小
段组织
- 将中断向量表保持在固定位置
- 将频繁执行的代码放在更快的内存中
- 将相关函数分组以最小化缓存未命中
- 对可能被垃圾回收的段使用 KEEP()
安全性
- 添加 ASSERT 语句以捕获内存溢出
- 为运行时栈检查定义符号
- 为校验和/签名预留空间
- 记录所有魔术数字
参考资料
- GNU LD 手册:https://sourceware.org/binutils/docs/ld/
- ARM Cortex-M 链接器脚本应用笔记
- STM32 链接器配置示例
- Zephyr 内存配置文档
另请参阅
memory-architecture-planning.js- 内存规划流程bootloader-implementation.js- 引导加载程序开发- SK-010: 内存分析技能
- AG-001: 固件架构师代理