--gc-sections 必须与-ffunction-sections和-fdata-sections配合使用才有效;单独使用无效甚至导致空文件,因其仅在section粒度回收,而默认编译将整文件代码塞入单个.text节。

--gc-sections 确实能显著减小最终二进制体积,但必须配合 -ffunction-sections 和 -fdata-sections 才生效;单独加 --gc-sections 没用,甚至可能链接出空文件。
为什么只加 --gc-sections 会失败
LLD 的 --gc-sections 不是“智能扫描所有函数然后删掉没调用的”,它只在 section 粒度上做垃圾回收。默认编译下,整个源文件的代码被塞进一个 .text section,链接器只要发现这个 section 里有任一符号被引用(比如 main),就会保留整个 section —— 里面所有未调用函数全跟着留下。
所以必须先让编译器把每个函数、每个全局变量都拆成独立 section:
-
-ffunction-sections:让每个函数生成形如.text.func_a的独立 section -
-fdata-sections:让每个全局/静态变量生成形如.data.var_x的独立 section -
-Wl,--gc-sections:链接时从入口符号(如_start或main)开始 DFS 遍历,只保留可达的 section
--gc-sections 在 LLD 中的实际行为
LLD 的 --gc-sections 是保守的:它只丢弃「完全未被任何存活 section 引用」的 section。这意味着:
- 如果某个函数被
__attribute__((constructor))标记,它会被自动加入初始化列表,--gc-sections不会删它 - 通过函数指针间接调用的函数(如注册到回调表里的 handler),若该指针变量本身没被直接引用,对应函数仍可能被误删
- C++ 虚函数表(vtable)、RTTI 数据、模板实例化体,若未显式取地址或动态绑定触发,也可能被 GC 掉 —— 加
-fno-rtti -fno-exceptions可减少这类风险 - 静态库(
.a)中未被当前链接命令显式引用的 object 文件,根本不会进入 GC 流程;--gc-sections只作用于已参与链接的目标文件
常见坑与绕过方式
你可能会遇到这些典型问题:
-
链接后程序为空或段错误:大概率是入口符号未正确定义。检查是否在链接脚本(.lds)里写了
ENTRY(_start),或确保main符号可被识别(避免-nostdlib后漏掉启动代码) -
加了
-fdata-sections就报错:某些平台(如裸机 ARM)的 C 运行时依赖特定命名的 data section(如.data、.bss),拆分会破坏初始化逻辑。此时应只用-ffunction-sections+--gc-sections,放弃 data 级别裁剪 -
体积没变小:确认你没在链接时混用
-shared或-pie—— 这些模式下--gc-sections默认禁用,需显式加-Wl,--gc-sections -
调试信息膨胀掩盖效果:
--gc-sections不处理.debug_*节。用-Wl,--strip-debug或-Wl,--strip-all配合使用,否则 map 文件看着小,实际 .bin 还是大
真正起效的关键不在链接命令本身,而在编译阶段是否完成细粒度切分。很多团队只改了链接参数就以为搞定,结果白忙活——-ffunction-sections 必须出现在所有 .c/.cpp 的编译命令中,漏一个源文件,它里面的全部函数就保底留下。











