板级适配_链接脚本lds下② _契约断言与照妖镜

发布时间:2026/10/1 10:25:54
板级适配_链接脚本lds下② _契约断言与照妖镜 系列目录本篇是板级适配系列第 4 篇下的第 2 部分承接第 11 篇的逐段解剖。讲清三件事① lds 和启动文件之间的「符号契约」② 怎么用ASSERT把越界 bug 提前到编译期③ 拿到任何新工程照 9 条「照妖镜」查它写没写对。第 09 篇思考题的答案SystemInit 时 .data/.bss 好了没也在这揭晓。本文承接第 11 篇已经把.isr_vector/.text/.data/.bss每间房看了一遍。本篇看「房与房之间的门牌交接单」。四、接口符号总表lds 和启动文件的契约一句话理解链接脚本和启动文件是合同双方——lds 供给符号startup.S 消费符号这张表就是它们的契约。先建立全局观——启动五步走上电后 startup.S 按固定顺序跑这 5 步每一步都需要 lds 提前备好一个「门牌号」① 设栈指针 SP _estack → 给芯片一个能用的工作台 ② 拷 .data 初值_sidata→_sdata → 让全局变量有正确初值 ③ 清零 .bss_sbss→_ebss → 没初始化的全局变量统一 0 ④ 跳进 Reset_Handler → 开始执行我们的代码 ⑤ 跑构造器 __libc_init_array → 然后 bl main 进 main 函数下面这张「交接单」把每一格拆成人话——符号是啥、lds 怎么写出来、startup.S 拿它干啥、缺了会怎样门牌号符号它其实是啥人话lds 怎么「写出来」的startup.S 拿它干嘛第几步要是 lds 没给会怎样_estack栈的「天花板」地址 SRAM 最高处PROVIDE(_estack ORIGIN(SRAM)LENGTH(SRAM))第①步ldr sp,_estack把栈顶交给芯片芯片不知道栈在哪上电立刻跑飞_sidata.data初值的「老家」地址在 Flash段属性AT FLASH自动算出第②步从这里读初值.data初值全错 / 为 0_sdata/_edata.data的「新家」起 / 止在 SRAM.data : { ... } SRAM第②步把初值拷到这区间全局变量初值错误_sbss/_ebss.bss清零区间的起 / 止.bss : { ... } SRAM第③步把这整块填 0未初始化变量不为 0诡异 bug__Vectors含Reset_Handler向量表第 2 项就是复位入口ENTRY(Reset_Handler).isr_vector段第④步向量表第 2 项 → PC芯片不知道复位去哪 → 跑飞init_array区间界C 构造器清单的边界. ALIGN(4); __preinit_array_start .; ...第⑤步__libc_init_array遍历调用构造器不跑本工程没用留空也安全现在揭晓 09 篇思考题SystemInit 执行时 .data 拷没拷、.bss 清没清答案在启动文件的五步顺序里——① 设 SP → ② CopyDataInit → ③ FillZerobss → ④ bl SystemInit → ⑤ bl __libc_init_array → bl mainSystemInit 排在 ②③之后。所以它运行时 C 环境全局变量初值、清零内存已经就绪——SystemInit 自己是 C 函数这件事正是靠这个顺序才成立。这也解释了为什么 VTOR/FPU 放 SystemInit 是安全的它前面已经有人在保证 C 环境了。五、ASSERT把烧进去才发现变成编译时就骂你一句话理解ASSERT是链接器的硬性检查——固件一旦超出 256KB链接阶段就直接报错而不是烧进板子后随机死机。ASSERT(. ORIGIN(SRAM) LENGTH(SRAM), SRAM overflow! 链接结果超出 M4 256KB SRAM 区间请检查裁剪或减小 LOSCFG_SYS_HEAP_SIZE)SECTIONS 排完.停在镜像末尾。这一行断言末尾没越过 SRAM 上限。越界时你得到的是编译期的红色报错和一句人话提示而不是板子上毫无规律的 HardFault。编译期拦截 vs 运行期死机好比交警在出厂前就拦下超载的大车而不是等它上了高速才翻车。一行 ASSERT换一类 bug 从此绝迹。六、/DISCARD/之死一个优化引发的血案一句话理解旧版想用/DISCARD/把 C 库没用的部分丢掉省空间结果误伤了__libc_init_array依赖的库成员链接直接报错——所以删了它。/DISCARD/ : { *(.glue_7) *(libc.a) *(libm.a) *(libgcc.a) } /* 旧版已删 */启动文件bl __libc_init_array→ 链接器要从libc_nano.a里拉出这个函数的实现/DISCARD/一声令下库里对应的节被连根丢弃链接器两手一摊undefined reference to __libc_init_array。可复用经验别在 lds 里用/DISCARD/砍 C 库瘦身——它会把__libc_init_array依赖的库成员一起连根丢弃链接报错。防膨胀交给 Makefile 的--gc-sections没被引用的库成员根本不进镜像DISCARD 这把砍刀退休。八、通用性拿到新工程怎么照这套逻辑查一句话理解本篇讲的不是只适用于 STM32MP157 的私房写法而是嵌入式 Cortex-M 链接脚本的通用骨架——语法全平台通用填进去的地址/段名因芯片而异拿到任何新工程照 9 条照妖镜逐条核一遍就能判断它写没写对。语法通用怎么写MEMORY、怎么写 REGION、AT、位置计数器.怎么累加——是 GNU ld 的跨平台标准。你会读一个工程的 lds就能读懂所有工程的 lds。填法不通用ORIGIN/LENGTH换芯片基址就变很多 STM32F4 是0x20000000、段名、VMA/LMA 策略、栈顶算法——框架能抄地址/长度/段名必须对着手册重填。#查什么怎么判断对不对①地皮MEMORYORIGIN/LENGTH和芯片手册的 SRAM 起始/大小一致②栈顶_estack是否 ORIGINLENGTHMSP 向下长别和 .bss/堆撞③向量表排最前第一个段是.isr_vector1KB 对齐和 VTOR 数字一致④段齐不齐.text/.rodata/.data/.bss/构造器区间都在⑤LMA/VMA有AT双区域真搬运无单区域自拷贝⑥对齐向量表 1024、代码/数据 4错会 HardFault 或 VTOR 失效⑦接口符号lds 导出的_estack/_sidata/_sdata/_sbss等启动文件真在消费⑧ASSERT尾部有没有越界断言没有则越界只能上板才死⑨/DISCARD/有没有砍 C 库有则确认没误伤__libc_init_array可复用经验换芯片 / 接手新工程先花 5 分钟照这 9 条过一遍能提前消灭 80% 的链接报错 / 上电跑飞 / 随机 HardFault。参考文档GNU ld 手册Linker ScriptsMEMORY、ORIGIN/LENGTH、 REGION、AT、ASSERT等语法权威定义。在线https://sourceware.org/binutils/docs/ld/Scripts.htmlARM — 《ARMv7-M Architecture Reference Manual》复位行为、向量表基址由VTOR设定。术语表本文出现缩写/符号含义ASSERT链接器断言条件不成立就报错、终止链接把越界拦在编译期/DISCARD/链接脚本特殊段列在这里的段/库会被连根丢弃旧版用过现工程已删--gc-sections死代码消除删未被引用的函数/段与 KEEP 配合防误删__libc_init_arraynewlib 的 C 库初始化函数遍历构造器区间调用构造器CopyDataInit启动汇编标号把 .data 初值从加载地址拷到运行地址FillZerobss启动汇编标号清零 .bss 区间用 _sbss/_ebssReset_Handler复位处理例程芯片复位后执行的第一段代码HardFaultCortex-M 最严重异常兜不住的 fault 都落这里VTOR向量表偏移寄存器指向当前向量表第 09 篇设成 0x10000000libc_nano.anewlib 精简版 C 库__libc_init_array 实现来自它MSPMain Stack Pointer主栈指针从 _estack 向下长系列导航上一篇第 11 篇 · 链接脚本 lds下① 逐段解剖各内存段 本篇第 12 篇 · 链接脚本 lds下② 接口契约、ASSERT 与照妖镜 下一篇第 13 篇 · 启动文件 startup 与五步启动流程板级适配与应用 07–2507 工程全景与启动四步法 08 BSP 契约与实现 09 SystemInit 与 CMSIS 裁剪 10 lds 上 11 lds 下① 12 lds 下② 13 启动文件与五步启动 14 弱符号覆盖与三特殊中断 15 target_config 清单(上) 16 FPU 三件套与栈档 17 改错速查 18 LED 任务与 LOS_TaskCreate 19 LED 实战与错误码 20 los_exc 与三破坏现场 21 FPU 栈帧与 backtrace 22 八环节与四铁律 23 全文件对账与目录 24 六类判据与换板五步 25 经验与变量级增删改留代码仓库stm32mp157-liteos-m Gitee