You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32F4自定义链接脚本中全局对象未初始化问题排查

解决STM32F4自定义链接脚本中全局对象未初始化(__init_array_end异常)的问题

从你的描述和调试输出来看,核心问题是链接器没有正确设置__init_array_end符号,导致全局对象的构造函数数组没有被遍历执行。下面一步步分析问题并给出针对性解决方案:

1. 先拆解调试输出的异常信号

从你打印的信息:

preinit 080506E9 080506E9
init 080506E9 00000000

能看出两个关键问题:

  • .preinit_array段是空的(起始和结束地址一致),这通常是正常情况——大多数嵌入式场景下不会用到预初始化函数
  • __init_array_end被错误设置为0x00000000,这个符号本应指向FLASH中.init_array段的结束地址,而你打印*__init_array_end得到RAM地址0x20000418,说明该符号要么未被链接器正确生成,要么被错误重定向了。

2. 调整链接脚本的细节问题

你的链接脚本框架是对的,但几个细节可以优化,解决符号异常:

修正段匹配规则

把*(.init_array*)改为*(.init_array)——*.init_array*会匹配所有以.init_array开头的无关段,而标准的构造函数段只有.init_array和按优先级排序的.init_array.*,精准匹配能避免干扰。

修正后的.init_array段定义:

. = ALIGN(4);
.init_array : {
  PROVIDE_HIDDEN (__init_array_start = .);
  KEEP (*(SORT(.init_array.*)))
  KEEP (*(.init_array))
  PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH

确保段的位置正确

把初始化数组段放在.text段之后的FLASH代码区域,避免被分配到错误地址空间,比如:

/* 先定义.text段 */
.text :
{
  /* 你的.text段原有定义 */
} >FLASH

/* 紧接着放置初始化数组段 */
. = ALIGN(4);
.preinit_array : {
  PROVIDE_HIDDEN (__preinit_array_start = .);
  KEEP (*(.preinit_array))
  PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH

. = ALIGN(4);
.init_array : {
  PROVIDE_HIDDEN (__init_array_start = .);
  KEEP (*(SORT(.init_array.*)))
  KEEP (*(.init_array))
  PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH

. = ALIGN(4);
.fini_array : {
  PROVIDE_HIDDEN (__fini_array_start = .);
  KEEP (*(SORT(.fini_array.*)))
  KEEP (*(.fini_array))
  PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH

可选:移除HIDDEN标记

如果你的链接器对PROVIDE_HIDDEN处理有异常,可以换成普通的PROVIDE,让符号被导出到全局符号表,方便调试和引用:

PROVIDE (__init_array_start = .);
/* ... */
PROVIDE (__init_array_end = .);

3. 排查编译/链接选项的干扰

如果链接脚本调整后仍有问题,检查以下选项:

  • 如果你用了-ffunction-sections和-fdata-sections分节编译,链接时要确保--gc-sections(垃圾回收)没误删.init_array段。可以在链接选项中添加-Wl,--undefined=__init_array_start和-Wl,--undefined=__init_array_end,强制链接器保留这些符号和对应段。
  • 确认没有开启禁用构造函数的编译选项,比如-fno-constructors(这个选项很少用,但不小心开启会直接跳过全局对象构造函数的生成)。

4. 验证构造函数是否被正确生成

用nm工具检查编译后的ELF文件,确认构造函数和初始化段符号存在:

# 查看构造函数
nm -C your_app.elf | grep "constructor"
# 查看初始化数组符号
nm your_app.elf | grep __init_array

如果看不到__init_array_start和__init_array_end,说明链接器根本没生成这些符号,需要回到链接脚本重新调整。

5. 对比ST官方脚本的参考点

ST官方启动脚本的初始化逻辑和你的类似,但要注意:

  • 官方脚本会确保__init_array_start和__init_array_end的指针类型正确,你启动代码中的extern void (**__init_array_start)();声明是正确的,等价于数组指针声明extern void (*__init_array_start[])();。
  • 官方链接脚本中,初始化段会被放在FLASH的TEXT区域,和.text段同属一个加载区。

最后验证

调整后重新编译链接,再次打印__init_array_start和__init_array_end,如果两个地址不同且都指向FLASH区域,说明问题解决,此时static_init()会正确遍历并执行所有全局对象的构造函数。

内容的提问来源于stack exchange,提问作者mauro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:36:25