我需要严格优化可执行文件的大小(ARM
开发)和
我注意到在我当前的构建方案(gcc
+ ld
)中,未使用的符号不会被剥离。
arm-strip --strip-unneeded
对生成的可执行文件/库的使用不会改变可执行文件的输出大小(我不知道为什么,也许它根本就不能)。
(如果存在)修改构建管道的方式是什么,以便从结果文件中删除未使用的符号?
我甚至不会想到这一点,但我当前的嵌入式环境并不是非常“强大”
将500K
保存在2M
之外会导致非常好的加载性能提升。
更新
不幸的是,我使用的当前gcc
版本没有-dead-strip
选项,-ffunction-sections... + --gc-sections
的{{1}}没有给出结果输出的任何显着差异。
我很震惊,这甚至成了一个问题,因为我确信ld
应该自动删除未使用的符号(为什么他们甚至要保留它们?)。
答案 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来从中删除所有符号。
注意:它会更改文件本身并且不会创建副本。