要使.text段落在0x08000000,需在sections中设地址计数器. = 0x08000000(紧跟align),显式收集.text及.text.*,并绑定> rom,而rom须在memory中定义;.data与.bss须分开声明且.bss加noload;自定义段需匹配实际段名、用keep保留、映射到有效内存区域,并通过.map文件验证。

直接用 SECTIONS 块 + 地址计数器 . + > 输出内存区域,三者缺一不可。 光写 .text : { *(.text) } 不指定地址或内存区域,LLD 会按默认布局走,根本不会落到你想要的位置。
怎么在 SECTIONS 里让 .text 落到 0x08000000
关键不是“赋值”,而是控制地址计数器 . 的当前位置,并确保对齐和内存区域匹配:
-
.是当前链接地址指针,. = 0x08000000;把它设到起始点,但必须紧跟ALIGN(0x1000)(假设页大小 4KB),否则可能跨页破坏 MMU 映射 - 显式写出段内容:
.text : { *(.text) *(.text.*) },漏掉.text.*可能导致__attribute__((section(".text.isr")))的函数被丢弃 - 必须绑定内存区域:
.text : { ... } > rom,而rom需提前在MEMORY块中定义:rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K - 如果没写
MEMORY块,>后面的区域名无效,LLD 会静默回退到默认布局,不报错也不警告
为什么 .data 和 .bss 必须分开声明且加 NOLOAD
嵌入式裸机启动代码依赖符号定位清零范围,.bss 错位会导致全局变量未初始化:
-
.data段结尾要放一个符号:_edata = .;,供 C runtime 获取数据段末尾 -
.bss必须显式声明为.bss (NOLOAD) : { *(.bss) *(COMMON) },否则链接器可能把它合并进.data或直接丢弃 -
NOLOAD表示该段不写入输出文件(如 .bin),只保留运行时地址;漏掉它,烧录时会把全零的.bss写进 Flash,浪费空间还拖慢下载 - 如果用了
-fno-common,*(COMMON)可省,但留着更安全——某些旧版 GCC 或内联汇编仍可能生成 COMMON 符号
lld 下自定义段(如 .mydata)不生效的常见原因
不是脚本写错了,而是 GCC 输出段名和链接器匹配不上,或者段被静默丢弃:
- 用
__attribute__((section(".mydata")))声明变量后,在脚本里写.mydata : { *(.mydata) },但变量没进去 → 很可能是 GCC 实际生成的段名是.mydata.foo(启用-ffunction-sections或 ARM AArch64 目标时常见) - 段完全为空(无变量引用、无函数调用)→ LLD 默认丢弃,需加
KEEP(*(mydata))强制保留 - 脚本里写了
.mydata,但没把它映射到任何MEMORY区域 → 它会被归入默认DEFAULT_MEMORY_REGION,地址不可控 - 想确认是否被丢弃:加
--gc-sections --print-gc-sections编译,看终端是否输出discarded section '.mydata'
最易忽略的一点:LLD 对链接脚本语法容错极低。少一个分号、多一个空格、> 后面区域名拼错,它都可能静默忽略整段 SECTIONS 规则,而不是报错。验证唯一可靠方式是查 .map 文件里的 .text、_edata 等符号地址,别信构建日志里“success”。











