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

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

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

(如果存在的话)如何修改我的构建管道,以便从结果文件中去除未使用的符号?


我甚至不会想到这一点,但我目前的嵌入式环境不是非常“强大”和 将 500K2M中保存下来,会带来非常好的加载性能提升。

更新:

不幸的是,我当前使用的 gcc版本没有 -dead-strip选项,而 ld-ffunction-sections... + --gc-sections对于结果输出没有任何显著差异。

我感到震惊,这甚至成为一个问题,因为我确信 gcc + ld应该自动删除未使用的符号(为什么他们甚至必须保留它们?).

141824 次浏览

如果 这根线是可信的,那么您需要向 gcc 提供 ABC0和 -fdata-sections,它将把每个函数和数据对象放在它自己的部分中。然后给 GNU ld 赋值和 --gc-sections以删除未使用的部分。

您可能希望检查您的文档中的 gcc & ld 版本:

但是对于我来说(OS X gcc 4.0.1)我发现这些是用于 ld 的

-dead_strip

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

-dead_strip_dylibs

删除入口点或导出符号无法访问的 dylibs。也就是说,取消为在链接期间没有提供任何符号的 dylibs 生成加载命令命令。当对 dylib 进行链接时,不应使用此选项,因为在运行时由于某些间接原因(如 dylib 具有重要的初始值设定项)而需要使用 dylib。

还有这个有用的选择

-why_live symbol_name

记录对 sign _ name 的引用链。只适用于 -dead_strip。它可以帮助调试为什么你认为应该死条删除的东西没有被删除。

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

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

strip --strip-unneeded只对可执行文件的符号表进行操作。它实际上不删除任何可执行代码。

标准库通过将它们的所有函数分割成单独的目标文件(使用 ar组合)来实现您想要的结果。如果然后将结果归档文件链接为库(即。给出选项 -l your_library to ld) ,那么 ld 将只包含实际使用的对象文件,因此也包含符号。

您还可以找到对这个使用的 类似的问题的一些响应。

虽然不是严格的符号,如果要大小-始终编译与 -Os-s标志。-Os为最小可执行文件大小优化生成的代码,-s从可执行文件中删除符号表和重定位信息。

有时候——如果需要较小的尺寸——使用不同的优化标志可能有意义,也可能没有意义。例如,切换 -ffast-math和/或 -fomit-frame-pointer有时甚至可以节省几十个字节。

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

我不知道这是否有助于解决您目前的困境,因为这是最近的一个特性,但是您可以以全局方式指定符号的可见性。在编译时传递 -fvisibility=hidden -fvisibility-inlines-hidden可以帮助链接器以后去除不需要的符号。如果您正在生成一个可执行文件(而不是共享库) ,那么就没有什么可做的了。

海湾合作委员会维基百科上可以获得更多的信息(比如图书馆的细粒度方法)。

在我看来,尼莫给出的答案是正确的。如果这些指令不起作用,问题可能与您正在使用的 gcc/ld 版本有关,作为练习,我使用详细的 给你指令编译了一个示例程序

#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字节,其中最重要的贡献来自“全带”声明。如果您不能在您的平台上实现类似的减少,那么您的 gcc 版本可能不支持这个功能。我在 Linux Mint 2.6.38-8-general x86 _ 64上使用 gcc (4.5.2-8ubuntu4) ,ld (2.21.0.20110327)

对于海湾合作委员会而言,这将分两个阶段实现:

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

-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 优化大小)

摘自海湾合作委员会4.2.1手册 -fwhole-program章节:

假设当前编译单元表示正在编译的整个程序。除了 main和由属性 externally_visible合并的公共函数和变量之外,所有的公共函数和变量都变成了静态函数,并且在某种程度上得到了过程间优化器更积极的优化。虽然这个选项相当于对由单个文件组成的程序正确使用 static关键字,但是与选项 --combine结合,这个标志可以用来编译大多数较小规模的 C 程序,因为函数和变量对于整个组合编译单元来说是局部的,而不是对于单个源文件本身来说。

答案是 -flto。您必须将它传递给编译和链接步骤,否则它什么也不做。

它实际上工作得非常好-减少了一个微控制器程序的大小,我写的不到50% ,以前的大小!

不幸的是,它似乎有点错误-我有事情没有正确构建的实例。这可能是因为我使用的构建系统(QBS; 它非常新) ,但无论如何,我建议您只在最终构建时启用它,并彻底测试该构建。

您可以在目标文件(例如,可执行文件)上使用剥离二进制文件来剥离其中的所有符号。

注意: 它改变文件本身,不创建副本。