如何移除C语言BSS恢复段中未使用的数据?
未使用BSS段未被
--gc-sections剥离的原因及解决办法 核心原因
1. 符号被隐式引用
即便你没有直接调用某个BSS变量,编译器或链接器仍可能判定该符号被引用:
- 开启
-g调试编译时,生成的符号表会保留所有符号的关联关系,干扰链接器对"未使用"的判断。 - 动态链接场景下,PLT/GOT表会为全局符号自动生成条目,哪怕符号未被实际调用,链接器也会保留对应BSS段。
- 低优化等级下,编译器生成的辅助代码(比如全局构造/析构函数的注册逻辑)可能间接引用BSS变量,导致段无法被剥离。
2. 链接脚本的合并行为
多数默认链接脚本会将所有.bss.*子段合并为一个全局.bss段。一旦合并完成,--gc-sections无法单独剥离其中未使用的部分——链接器只会对独立的section做GC判定,而非合并后段内的单个变量。
3. 低优化等级的限制
使用-O0编译时,编译器不会进行死代码消除,也不会生成清晰的符号使用标记。链接器无法准确识别未被使用的BSS变量,自然不会剥离对应段。
解决措施
1. 调整编译优化与符号可见性
- 改用
-O1及以上优化等级(推荐-O2),让编译器主动消除未使用变量,并生成明确的符号使用标记。 - 动态链接场景配合
-fvisibility=hidden编译标志,隐藏未导出的符号,避免PLT/GOT表生成不必要条目,帮助链接器精准判断未使用段。
2. 自定义链接脚本
修改链接脚本,避免提前合并BSS子段,示例片段如下:
SECTIONS { /* 保留其他段的原有定义 */ .bss : { *(.bss .bss.*) *(COMMON) } /* 不使用*(.bss)这类合并写法,保留.bss.*子段的独立性 */ }
同时可在脚本中为目标section添加!keep属性,明确允许链接器剥离未使用部分。
3. 排查隐式引用来源
- 添加
-Wl,--print-gc-sections链接标志,打印被GC的段列表,对比未被剥离的BSS段,定位隐式引用的源头。 - 临时关闭调试信息(去掉
-g)后重新编译,验证是否是调试符号导致的问题。
4. 静态链接场景的特殊处理
如果是静态链接,确保静态库同样使用-ffunction-sections -fdata-sections编译,这样链接器才能对静态库内的单个section做GC判定。
内容的提问来源于stack exchange,提问作者Sababoni
相关产品推荐
相关产品推荐

