如何移除.exe/.elf文件中理论被引用但实际不会执行的函数以减小二进制体积
问题结论
是可以实现的,以下是几种适合嵌入式场景、无需重新编译第三方库的可行方案:
方案1:使用链接器--wrap选项(最推荐,GCC/Linux/嵌入式场景通用)
这是最稳妥无副作用的方案,原理是通过链接器重定向引用,从根源消除对目标函数的依赖:
- 首先开启基础的无用段回收能力:编译所有自有代码时加
-ffunction-sections -fdata-sections参数,最终链接时加-Wl,--gc-sections参数 - 链接参数额外增加
-Wl,--wrap=foo(把foo替换为你要移除的函数名),此时链接器会把所有对foo的引用自动重定向到__wrap_foo符号 - 自有代码中添加极简的
__wrap_foo实现,因为确认不会走到该分支,实现可以尽可能小:
int __wrap_foo(void) { // 调试版本可加断言确认分支不会触发 // assert(0); return 0; }
此时第三方库中的原foo函数已经没有任何有效引用,--gc-sections会自动回收其对应的.text.foo段,不会出现在最终二进制中,也不会报丢弃段的引用错误。
方案2:同名弱符号覆盖
如果不想修改链接参数,可以通过在自有代码中定义同名弱符号的方式覆盖第三方库的实现:
- 在自有代码中添加如下定义:
int __attribute__((weak)) foo(void) { return 0; }
链接器会优先选择你定义的弱符号(如果库中的符号是强符号的话可以换用__attribute__((alias))配合链接器脚本调整优先级),原本库中的foo实现会因为没有被引用被GC回收,同样可以达到移除的效果。
方案3:Windows PE格式适配
如果是Windows的.exe场景,可以用MSVC的/ALTERNATENAME链接选项实现和--wrap完全一致的效果,链接参数加/ALTERNATENAME:foo=__wrap_foo即可。
注意事项
- 所有方案的前提是你100%确认目标函数对应的代码路径永远不会被执行,否则会触发未定义行为,嵌入式场景下可能导致设备变砖
- 建议调试版本在替代实现中加入断言、错误打印等逻辑,确认线上运行时不会走到该分支,再在发布版本中移除调试逻辑压缩体积
内容的提问来源于stack exchange,提问作者Viatorus
相关产品推荐
相关产品推荐

