如何用GCC和ld去除未使用的C / C ++符号?
我需要严格地优化我的可执行文件的大小( ARM
开发),我注意到在我的当前构buildscheme( gcc
+ ld
)中未使用的符号没有被剥离。
对于生成的可执行文件/库, arm-strip --strip-unneeded
使用arm-strip --strip-unneeded
不会改变可执行文件的输出大小(我不知道为什么,也许根本就不行) 。
修改我的build筑物pipe道的方式(如果存在的话)会是什么方式,以便从结果文件中去除未使用的符号?
我甚至不会想到这一点,但是我目前的embedded式环境并不是非常“强大”,即使在2M
节省了500K
,也可以获得非常好的加载性能。
更新:
不幸的是,我使用的当前gcc
版本没有-dead-strip
选项和-ffunction-sections... + --gc-sections
对于ld
-ffunction-sections... + --gc-sections
没有给出任何显着差异的结果输出。
我感到震惊的是,这甚至成为一个问题,因为我确信gcc + ld
应该自动剥离未使用的符号(为什么他们甚至不得不保留它们?)。
对于海合会来说,这是分两个阶段完成的:
首先编译数据,但是告诉编译器将代码分离到翻译单元内的不同部分。 这将通过使用以下两个编译器标志完成函数,类和外部variables:
-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优化大小)
如果这个线程是-ffunction-sections
,你需要提供-ffunction-sections
和-fdata-sections
到gcc,这将把每个函数和数据对象放在它自己的部分。 然后你给--gc-sections
到GNU ld去除未使用的部分。
您需要检查您的文档是否为您的gcc&ld版本:
但是对于我(OS X gcc 4.0.1)我find这些为ld
-dead_strip
删除入口点或导出符号无法访问的函数和数据。
-dead_strip_dylibs
删除入口点或导出符号无法访问的dylib。 即,禁止在链接期间不提供符号的dylibs生成加载命令命令。 链接到运行时需要的dylib时,不应该使用这个选项,因为dylib有一个重要的初始值设定项。
而这个有用的选项
-why_live symbol_name
logging对symbol_name的引用链。 只适用于
-dead_strip
。 它可以帮助debugging为什么你认为应该死掉去除的东西不会被删除。
在gcc / g ++ man中还有一个注释,那就是只有在编译时启用了优化才能执行某些types的死代码清除。
虽然这些选项/条件可能不适合您的编译器,但我build议您在文档中寻找类似的东西。
编程习惯也可以帮助; 例如将static
添加到不在特定文件外部访问的函数; 使用较短的名称符号(可以帮助一点,可能不会太多); 尽可能使用const char x[]
; … 本文尽pipe讨论了dynamic共享对象,但可以包含一些build议,如果遵循这些build议,可以帮助使最终的二进制输出大小更小(如果您的目标是ELF)。
虽然不严格符号,如果要大小 – 总是用-Os
和-s
标志编译。 -Os
优化最小可执行文件大小的结果代码, -s
从可执行文件中删除符号表和重定位信息。
有时候 – 如果需要小尺寸的话 – 玩弄不同的优化标志可能会 – 也可能不会 – 具有重要意义。 例如,切换-ffast-math
和/或-fomit-frame-pointer
有时可以节省数十个字节。
答案是-flto
。 你必须把它传递给你的编译和链接步骤,否则它不会做任何事情。
它实际上工作得非常好 – 将我写入的微控制器程序的大小减小到以前的不到50%!
不幸的是,它似乎有点错误 – 我有东西没有正确构build的实例。 这可能是由于我正在使用的构build系统(QBS;这是非常新的),但无论如何我build议您只在可能的情况下为最终构build启用它,然后彻底testing构build。
在我看来,尼摩提供的答案是正确的。 如果这些说明不起作用,这个问题可能与您正在使用的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字节的可执行文件,这是来自“strip-all”声明的最重要的贡献。 如果你不能在你的平台上产生类似的减less,那么你的gcc版本可能不支持这个function。 我在Linux Mint 2.6.38-8-generic x86_64上使用gcc(4.5.2-8ubuntu4),ld(2.21.0.20110327)
strip --strip-unneeded
只在您的可执行文件的符号表上运行。 它实际上并没有删除任何可执行的代码。
标准库通过将所有的函数分割成独立的对象文件,然后用ar
。 如果你把结果档案作为一个库链接起来(例如把-l your_library
选项给ld),那么ld将只包含实际使用的对象文件,因此也包括符号。
你也可以find一些类似的使用问题的答复。
我不知道这是否会有助于解决当前的困境,因为这是最近的一个function,但是您可以用全局的方式指定符号的可见性。 在编译时传递-fvisibility=hidden -fvisibility-inlines-hidden
可以帮助链接器稍后摆脱不需要的符号。 如果你正在生成一个可执行文件(而不是共享库),那么没有什么可做的了。
更多信息(以及对于例如图书馆的细粒度方法)可在GCC wiki上获得 。
从GCC 4.2.1手册中,部分-fwhole-program
:
假设当前的编译单元代表正在编译的整个程序。 所有的公共函数和variables,除了
main
和由externally_visible
属性合并的variables,都变成了静态函数,并且在一个影响中被过程优化器进行了更积极的优化。 虽然此选项相当于正确使用由单个文件组成的程序的static
关键字,但结合使用选项--combine
此标志可用于编译大多数较小规模的C程序,因为函数和variables成为整个组合编译的局部单位,而不是单个源文件本身。
你可以在目标文件(例如可执行文件)上使用strip二进制来去除它的所有符号。
注意:它会更改文件本身,不会创build副本。