4. 与 GNU ld 的行为差异
KF32 LLVM 工具链采用定制过的 LLD 链接器。脚本语法(MEMORY / SECTIONS / KEEP / PROVIDE / DEFINED / SORT / NOLOAD / ORIGIN / LENGTH 等)与 GNU ld 基本兼容,但以下行为存在差异,编写或修改脚本时需特别注意:
| # | 差异点 | 影响 | 规避方式 |
|---|---|---|---|
| 1 | 定位计数器 . 为绝对地址语义 | 漏加 ORIGIN 导致布局错乱 | 区域内赋值一律显式加 ORIGIN(...);预留空间改用 . = . + 长度 |
| 2 | 孤儿段处理不同 | 未列出的输入段直接报错 | 脚本显式覆盖所有输入段 |
| 3 | 装载地址与 NOLOAD 需显式声明 | 合并段中的 .bss / 堆栈也写入 Flash;.data 缺 AT > flash 时 HEX 阶段报错 | .data 用 > ram AT > flash;.bss / .heap / .stack 拆分并标 (NOLOAD) |
| 4 | 库文件自动迭代 | 符号成环无需 --start-group | 无需处理(传入亦兼容) |
4.1 定位计数器 . 的语义
GNU ld 中段内的 . 表示相对该段起始的偏移;LLD 中 . 是绝对地址语义,. = 数值 直接设定绝对地址。因此凡是为 . 或符号赋一个区域内的目标地址时,都必须显式加上 ORIGIN:
__initial_sp = ORIGIN(ram) + LENGTH(ram); /* RAM 顶地址:必须加 ORIGIN(ram) */
. = ORIGIN(ram) + 0x8000 ; /* lpdata 占满保持区 32K:必须加 ORIGIN(ram) */
. = ORIGIN(flash) + 0x200; /* flash 内偏移 0x200:必须加 ORIGIN(flash) */
漏加 ORIGIN 表现为链接阶段报错:unable to move location counter backward for: <段名>;若赋值行落在参与松弛(relaxation)的代码段内,则报输入段重叠 relaxation: overlapping input sections in <段名> …。任何情况下修改脚本都要核对区域内的赋值是否加上了 ORIGIN。
4.1.1 相对推进:. = . + 长度
如果只是从当前位置向后预留空间,不需要指定绝对地址,可以对 . 做相对推进:
. = . + 0x100; /* 从当前位置起预留 0x100 字节;等价写法 . += 0x100; */
右值中的 . 取当前地址,因此这种写法在 GNU ld 与 LLD 下语义一致,也无须 ORIGIN;为子区预留固定长度时优先使用,不会因区域起始地址变化而失效。
4.2 孤儿段检查
脚本中未显式列出的输入段称为孤儿段。使用含 SECTIONS 命令的链接脚本时,孤儿段导致链接失败,报错如下:
kf32ld: error: unplaced orphan section '.mysec' from 'main.o'
unplaced orphan section 表示该段未被链接脚本放置;引号内依次为段名(.mysec)与来源文件(main.o),段来自库成员时来源显示为库文件名加成员名,如 libfoo.a(foo.o)。未覆盖的输入段若由链接器自动放置,可能落到不符合预期的地址,造成数据覆盖或运行异常,因此按错误处理。
脚本应显式覆盖所有预期的输入段(含调试段)。
4.3 装载地址与 NOLOAD
从 GNU ld 工程迁移时,.data、.bss 与堆栈常合并在同一输出段,并用 AT(__text_end__) 指定装载地址。LLD 按输出段整体生成装载映像:段内所有内容(包括 .bss 与堆栈)都会进入 Flash,HEX 明显膨胀;LLD 也不会因为段名是 .bss / .heap / .stack 就自动按不装载处理。
迁移时应拆成独立输出段:
.data : { … } > ram AT > flash
.bss (NOLOAD) : { … } > ram
.heap (NOLOAD) : { … } > ram
.stack (NOLOAD) : { … } > ram
.bss / .heap / .stack 必须显式标注 (NOLOAD),否则按普通已初始化段处理,未使用空间以 0 填充写入 HEX。.data 必须显式声明装载区域:去掉 AT > flash 后 LLD 仍能完成链接(LMA = VMA),但 kf32objcopy 生成 HEX 时报错:
__data_start__, __data_end__, __bss_start__, and __bss_end__ must be defined in section .data
若沿用迁移前的合并段写法且未声明装载地址,链接与 HEX 均能生成,但 HEX 中不含 .data 初值映像。
4.4 库文件的自动迭代
- GNU ld 对归档库按命令行顺序单遍扫描,符号出现循环依赖时需要
--start-group/--end-group(或-Wl,--start-group)包裹。 - LLD 会自动重复扫描归档直至无新符号引入,无需 group;命令行中传入
--start-group/--end-group亦兼容,不会报错。