嵌入式 C:链接脚本入门
更新时间:2026-09-21。嵌入式/裸机视角。交叉工具链见 arm-none-eabi-gcc,启动交接见 启动汇编与 C 交接。
一句话回答
链接脚本(.ld)告诉链接器「代码和数据分别放到芯片的哪块物理地址」:先用 MEMORY 声明 FLASH/RAM 的起止与大小,再用 SECTIONS 把 .text/.rodata 放进 Flash、把 .data(有初值的全局变量)放 RAM 且其初值存一份在 Flash 启动时拷过去、.bss 放 RAM 并在启动时清零;ENTRY 指定复位后第一个执行的符号。
一、最小链接脚本
ld
ENTRY(Reset_Handler)
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
.text : {
KEEP(*(.isr_vector)) /* 向量表必须在最前面 */
*(.text*)
*(.rodata*)
} > FLASH
.data : {
_sdata = .;
*(.data*)
_edata = .;
} > RAM AT > FLASH /* 运行在 RAM,初值镜像存放在 Flash */
.bss : {
_sbss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > RAM
_estack = ORIGIN(RAM) + LENGTH(RAM);
}二、关键概念
- 链接地址决定绝对地址:函数和全局变量的地址在链接时就按这个脚本固定,所以 ORIGIN 必须与芯片手册一致;
.data的 LMA/VMA:VMA 是运行地址(RAM),LMA 是初值装载地址(Flash),启动代码把初值从 LMA 拷到 VMA;.bss不占 Flash:只有运行时清零的零初始化变量,省镜像空间;- 向量表
KEEP:防止链接器把看似无用的向量表当垃圾回收,并靠它放在 Flash 起点。
启动代码(startup)正是用 _sdata/_edata/_sbss/_ebss 这些符号完成拷贝与清零,再调用 main。
常见坑
- MEMORY 地址/大小抄错芯片:超出实际 Flash/RAM 会链接报错或运行跑飞。
- 忘了 AT > FLASH 保存 .data 初值:全局变量初值丢失、变成随机值。
- 向量表没放最前 / 没用 KEEP:复位取到错误入口,直接死机。
- 栈/堆位置没规划:
_estack和_end(堆起点)要留给启动文件,栈溢出会踩全局数据。 - 混用主机默认布局:裸机必须
-T xxx.ld,默认脚本按操作系统虚拟地址布局,无法烧录。
总结
链接脚本用 MEMORY 描述芯片 FLASH/RAM、用 SECTIONS 安排 .text 入 Flash、.data 在 RAM 运行而初值镜像在 Flash、.bss 在 RAM 清零,ENTRY 指定入口;它决定所有绝对地址,启动代码据脚本里的符号完成搬运和清零,是裸机能正确启动的布局蓝图。