如何用GCC和ld删除未使用的C / C ++符号?

时间:2011-07-14 01:51:48

标签: c++ c gcc ld strip

我需要严格优化可执行文件的大小(ARM开发)和 我注意到在我当前的构建方案(gcc + ld)中,未使用的符号不会被剥离。

arm-strip --strip-unneeded对生成的可执行文件/库的使用不会改变可执行文件的输出大小(我不知道为什么,也许它根本就不能)

(如果存在)修改构建管道的方式是什么,以便从结果文件中删除未使用的符号?


我甚至不会想到这一点,但我当前的嵌入式环境并不是非常“强大” 将500K保存在2M之外会导致非常好的加载性能提升。

更新

不幸的是,我使用的当前gcc版本没有-dead-strip选项,-ffunction-sections... + --gc-sections的{​​{1}}没有给出结果输出的任何显着差异。

我很震惊,这甚至成了一个问题,因为我确信ld应该自动删除未使用的符号(为什么他们甚至要保留它们?)。

11 个答案:

答案 0 :(得分:118)

对于海湾合作委员会来说,这是分两个阶段完成的:

首先编译数据,但告诉编译器将代码分成翻译单元中的单独部分。这将通过使用以下两个编译器标志来完成函数,类和外部变量:

-fdata-sections -ffunction-sections

使用链接器优化标志将转换单元链接在一起(这会导致链接器丢弃未引用的部分):

-Wl,--gc-sections

因此,如果您有一个名为test.cpp的文件,其中声明了两个函数,但其​​中一个未使用,您可以使用以下命令省略未使用的文件到gcc(g ++):

gcc -Os -fdata-sections -ffunction-sections test.cpp -o test -Wl,--gc-sections

(注意-Os是一个额外的编译器标志,告诉GCC优化大小)

答案 1 :(得分:33)

如果要相信this thread,则需要向gcc提供-ffunction-sections and -fdata-sections,这会将每个函数和数据对象放在自己的部分中。然后你给GNU ld和--gc-sections删除未使用的部分。

答案 2 :(得分:24)

您需要查看您的文档,了解您的gcc& ld:

然而对我来说(OS X gcc 4.0.1)我发现这些是ld

-dead_strip
     

删除入口点或导出符号无法访问的函数和数据。

-dead_strip_dylibs
     

删除入口点或导出符号无法访问的dylib。也就是说,禁止生成在链接期间不提供符号的dylib的加载命令命令。当链接到运行时需要的dylib时,不应该使用此选项,因为dylib有一个重要的初始化程序。

这个有用的选项

-why_live symbol_name
     

记录对symbol_name的引用链。仅适用于-dead_strip。它可以帮助调试为什么你认为应该删除死区的东西不被删除。

gcc / g ++ man中还有一个注释,即只有在编译时启用优化时才会执行某些类型的死代码消除。

虽然这些选项/条件可能不适用于您的编译器,但我建议您在文档中查找类似的内容。

答案 3 :(得分:19)

编程习惯也有帮助;例如将static添加到未在特定文件外访问的函数;使用较短的符号作为符号(可以帮助一点,可能不会太多);尽可能使用const char x[]; ... this paper,虽然它讨论动态共享对象,但可以包含一些建议,如果遵循这些建议,可以帮助您缩小最终的二进制输出大小(如果您的目标是ELF)。

答案 4 :(得分:16)

答案是-flto。您必须将它传递给编译和链接步骤,否则它不会执行任何操作。

它实际效果非常好 - 将我写的微控制器程序的大小减小到不到以前尺寸的50%!

不幸的是它看起来确实有些错误 - 我的事情没有正确构建。这可能是由于我正在使用的构建系统(QBS;它是非常新的),但无论如何我建议你只在可能的情况下为最终构建启用它,并对该构建进行彻底测试。

答案 5 :(得分:13)

虽然不是严格意义上的符号,但如果要求大小 - 总是使用-Os-s标志进行编译。 -Os优化生成的代码以获得最小的可执行文件大小,-s从可执行文件中删除符号表和重定位信息。

有时 - 如果需要小尺寸 - 使用不同的优化标记可能 - 或可能不 - 具有重要性。例如,切换-ffast-math和/或-fomit-frame-pointer有时甚至可以节省数十个字节。

答案 6 :(得分:11)

在我看来,Nemo提供的答案是正确的。如果这些说明不起作用,问题可能与你正在使用的gcc / ld版本有关,作为练习我使用详细的here

指令编译了一个示例程序
#include <stdio.h>
void deadcode() { printf("This is d dead codez\n"); }
int main(void) { printf("This is main\n"); return 0 ; }

然后我使用逐步更积极的死代码删除开关编译代码:

gcc -Os test.c -o test.elf
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections -Wl,--strip-all

这些编译和链接参数分别产生大小为8457,8164和6160字节的可执行文件,这是来自'strip-all'声明的最重要的贡献。如果您无法在您的平台上产生类似的缩减,那么您的gcc版本可能不支持此功能。我在Linux Mint 2.6.38-8-generic x86_64上使用gcc(4.5.2-8ubuntu4),ld(2.21.0.20110327)

答案 7 :(得分:8)

strip --strip-unneeded仅在您的可执行文件的符号表上运行。它实际上并没有删除任何可执行代码。

标准库通过将所有函数拆分为单独的目标文件(使用ar组合)来实现您所获得的结果。然后,如果您将结果存档链接为库(即将选项-l your_library提供给ld),则ld将仅包含实际使用的目标文件,因此也包括符号。

您也可以找到对此similar question使用的一些回复。

答案 8 :(得分:4)

我不知道这是否会对您当前的困境有所帮助,因为这是最近的功能,但您可以以全局方式指定符号的可见性。在编译时传递-fvisibility=hidden -fvisibility-inlines-hidden可以帮助链接器稍后删除不需要的符号。如果您正在生成可执行文件(而不是共享库),则无需执行任何操作。

the GCC wiki上提供了更多信息(以及针对例如图书馆的细粒度方法)。

答案 9 :(得分:4)

从GCC 4.2.1手册,-fwhole-program部分:

  

假设当前编译单元表示正在编译的整个程序。除main之外的所有公共函数和变量以及由属性externally_visible合并的公共函数和变量都将成为静态函数,并且通过过程间优化器可以更有效地优化影响。虽然此选项等同于对由单个文件组成的程序正确使用static关键字,但结合选项--combine,此标志可用于编译大多数较小规模的C程序,因为函数和变量变为整个组合编译单元的本地,而不是单个源文件本身。

答案 10 :(得分:-1)

您可以在目标文件(例如可执行文件)上使用strip binary来从中删除所有符号。

注意:它会更改文件本身并且不会创建副本。