用 -T 指定自定义链接脚本需路径正确、位置恰当(在输入文件后、-o 前),并显式声明 .init_array、.fini_array 等关键段,配合 KEEP 防 --gc-sections 误删,确保 C++ 全局构造函数正常执行。

怎么让 gcc 用上自定义的 .lds 文件
直接加 -T 参数,路径写对就行。比如你的脚本叫 memmap.lds,编译命令就是:g++ -T memmap.lds main.cpp -o app。注意 -T 必须放在所有输入文件(.o、.cpp)之后、输出选项(-o)之前,否则链接器可能忽略它。
常见错误现象:ld: cannot find script file memmap.lds——不是路径错就是当前工作目录不对;undefined reference to `_start'——脚本里没正确定义入口或没保留默认的 _start 符号。
- 推荐用绝对路径或相对于
gcc当前执行目录的相对路径,别依赖LD_LIBRARY_PATH或其他环境变量 - 如果脚本在子目录,写成
-T ./link/memmap.lds比-T link/memmap.lds更稳妥 -
-T会完全取代默认链接脚本,想复用原逻辑?得在自己的.lds里显式INCLUDE进去,或者用-Ttext等小参数临时覆盖
SECTIONS 里怎么放 C++ 全局对象构造函数
C++ 的全局对象构造需要 .init_array 和 .fini_array 段被正确收集并传给运行时,否则 main() 之前啥也不执行。这不是可选功能,是 ABI 强制要求。
典型错误:自己写 SECTIONS 时只写了 .text、.data、.bss,漏掉初始化段,结果程序启动后变量全是零值,甚至直接崩溃。
立即学习“C++免费学习笔记(深入)”;
- 必须显式声明
.init_array和.fini_array,并用*(.init_array)收集所有输入目标里的对应内容 - 顺序很重要:
.init_array得在.text之后、.data之前(某些平台要求),不然运行时找不到入口 - 加上
PROVIDE(__init_array_start = .);和PROVIDE(__init_array_end = .);,方便 C 运行时遍历调用
为什么 __attribute__((section(".mysec"))) 变量没进指定段
链接器脚本里没写对应段声明,或者段名拼错了,ld 就当它不存在,直接扔进 .data 或报错。C++ 编译器生成的段名是字面量,大小写、点号、下划线一个都不能差。
比如你写了 int foo __attribute__((section(".mydata")));,但 .lds 里写的是 .MYDATA 或 mydata,那就匹配不上。
- 检查实际生成的
.o文件里段名:objdump -h test.o | grep mysec - 在
.lds的SECTIONS里加一行:.mysec : { *(.mysec) },确保括号里和__attribute__完全一致 - 如果要用通配符批量收(比如所有
.log.*),写成.log : { *(.log.*) },但注意*不匹配点号开头以外的段
gcc 链接 C++ 项目时 --gc-sections 和 .lds 冲突怎么办
--gc-sections 会删掉“未被引用”的段,但它不理解 C++ 运行时的隐式依赖——比如 .init_array 里的函数指针,从源码看根本没被调用,但 runtime 会扫表执行。一旦被删,构造函数就消失了。
现象很隐蔽:程序能链接成功、能跑起来,但某个全局 std::string 或 std::vector 始终为空,调试发现构造函数根本没进断点。
- 要么关掉
--gc-sections(加-Wl,--no-gc-sections) - 要么在
.lds里用KEEP显式保活关键段:.init_array : { KEEP(*(.init_array)) } - 别信“只删死代码”,C++ 模板实例化、异常表、RTTI 都可能产生看似无引用实则必需的段
真正麻烦的不是语法写错,而是你以为段进了内存,其实被链接器悄悄优化掉了——尤其在嵌入式或裁剪场景下,这种问题一卡就是半天。


















