如何使用 GCC 和 ld 删除未使用的 C/C++ 符号?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6687630/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to remove unused C/C++ symbols with GCC and ld?
提问by Yippie-Ki-Yay
I need to optimize the size of my executable severely (ARM
development) and
I noticed that in my current build scheme (gcc
+ ld
) unused symbols are not getting stripped.
我需要严格优化我的可执行文件的大小(ARM
开发),我注意到在我当前的构建方案(gcc
+ ld
)中未使用的符号没有被剥离。
The usage of the arm-strip --strip-unneeded
for the resulting executables / libraries doesn't change the output size of the executable (I have no idea why, maybe it simply can't).
的用法arm-strip --strip-unneeded
为生成的可执行文件/库不改变可执行文件的输出大小(我不知道为什么,也许它根本不能)。
What would be the way (if it exists)to modify my building pipeline, so that the unused symbols are stripped from the resulting file?
修改我的构建管道的方法是什么(如果存在),以便从结果文件中删除未使用的符号?
I wouldn't even think of this, but my current embedded environment isn't very "powerful" and
saving even 500K
out of 2M
results in a very nice loading performance boost.
我什至不会想到这一点,但是我当前的嵌入式环境并不是很“强大”,并且即使节省500K
了2M
结果也能很好地提升加载性能。
Update:
更新:
Unfortunately the current gcc
version I use doesn't have the -dead-strip
option and the -ffunction-sections... + --gc-sections
for ld
doesn't give any significant difference for the resulting output.
不幸的是,目前的gcc
版本我使用不具备-dead-strip
选项和-ffunction-sections... + --gc-sections
用于ld
没有给出结果输出任何显著差异。
I'm shocked that this even became a problem, because I was sure that gcc + ld
should automatically strip unused symbols (why do they even have to keep them?).
我很震惊这甚至成为一个问题,因为我确信gcc + ld
应该自动删除未使用的符号(为什么他们甚至必须保留它们?)。
回答by J T
For GCC, this is accomplished in two stages:
对于 GCC,这分两个阶段完成:
First compile the data but tell the compiler to separate the code into separate sections within the translation unit. This will be done for functions, classes, and external variables by using the following two compiler flags:
首先编译数据,但告诉编译器将代码分成翻译单元内的单独部分。这将通过使用以下两个编译器标志为函数、类和外部变量完成:
-fdata-sections -ffunction-sections
Link the translation units together using the linker optimization flag (this causes the linker to discard unreferenced sections):
使用链接器优化标志将翻译单元链接在一起(这会导致链接器丢弃未引用的部分):
-Wl,--gc-sections
So if you had one file called test.cpp that had two functions declared in it, but one of them was unused, you could omit the unused one with the following command to gcc(g++):
因此,如果您有一个名为 test.cpp 的文件,其中声明了两个函数,但其中一个未使用,您可以使用以下 gcc(g++) 命令省略未使用的一个:
gcc -Os -fdata-sections -ffunction-sections test.cpp -o test -Wl,--gc-sections
(Note that -Os is an additional compiler flag that tells GCC to optimize for size)
(注意 -Os 是一个额外的编译器标志,它告诉 GCC 优化大小)
回答by Nemo
If this threadis to be believed, you need to supply the -ffunction-sections
and -fdata-sections
to gcc, which will put each function and data object in its own section. Then you give and --gc-sections
to GNU ld to remove the unused sections.
如果要相信这个线程,您需要将-ffunction-sections
and 提供-fdata-sections
给 gcc,它将把每个函数和数据对象放在它自己的部分中。然后你给 和--gc-sections
GNU ld 删除未使用的部分。
回答by Michael Anderson
You'll want to check your docs for your version of gcc & ld:
您需要检查您的文档以了解您的 gcc 和 ld 版本:
However for me (OS X gcc 4.0.1) I find these for ld
但是对我来说(OS X gcc 4.0.1)我发现这些是为了 ld
-dead_strip
Remove functions and data that are unreachable by the entry point or exported symbols.
-dead_strip_dylibs
Remove dylibs that are unreachable by the entry point or exported symbols. That is, suppresses the generation of load command commands for dylibs which supplied no symbols during the link. This option should not be used when linking against a dylib which is required at runtime for some indirect reason such as the dylib has an important initializer.
-dead_strip
删除入口点或导出符号无法访问的函数和数据。
-dead_strip_dylibs
删除入口点或导出符号无法访问的 dylib。也就是说,禁止为链接期间不提供符号的 dylib 生成加载命令命令。由于某些间接原因(例如 dylib 具有重要的初始值设定项)而在运行时需要链接 dylib 时,不应使用此选项。
And this helpful option
这个有用的选项
-why_live symbol_name
Logs a chain of references to symbol_name. Only applicable with
-dead_strip
. It can help debug why something that you think should be dead strip removed is not removed.
-why_live symbol_name
记录对symbol_name 的引用链。仅适用于
-dead_strip
. 它可以帮助调试为什么您认为应该删除的东西没有被删除。
There's also a note in the gcc/g++ man that certain kinds of dead code elimination are only performed if optimization is enabled when compiling.
gcc/g++ man 中还有一个注释,只有在编译时启用了优化,才会执行某些类型的死代码消除。
While these options/conditions may not hold for your compiler, I suggest you look for something similar in your docs.
虽然这些选项/条件可能不适用于您的编译器,但我建议您在文档中查找类似的内容。
回答by ShinTakezou
Programming habits could help too; e.g. add static
to functions that are not accessed outside a specific file; use shorter names for symbols (can help a bit, likely not too much); use const char x[]
where possible; ... this paper, though it talks about dynamic shared objects, can contain suggestions that, if followed, can help to make your final binary output size smaller (if your target is ELF).
编程习惯也有帮助;例如,添加static
到在特定文件之外无法访问的函数;为符号使用较短的名称(可能会有所帮助,可能不会太多);const char x[]
尽可能使用;...本文虽然讨论了动态共享对象,但可以包含一些建议,如果遵循这些建议,可以帮助使您的最终二进制输出大小更小(如果您的目标是 ELF)。
回答by Timmmm
The answer is -flto
. You have to pass it to both your compilation and link steps, otherwise it doesn't do anything.
答案是-flto
。你必须将它传递给你的编译和链接步骤,否则它不会做任何事情。
It actually works very well - reduced the size of a microcontroller program I wrote to less than 50% of its previous size!
它实际上工作得很好 - 将我编写的微控制器程序的大小减小到其先前大小的 50% 以下!
Unfortunately it did seem a bit buggy - I had instances of things not being built correctly. It may have been due to the build system I'm using (QBS; it's very new), but in any case I'd recommend you only enable it for your final build if possible, and test that build thoroughly.
不幸的是,它确实看起来有点问题 - 我有一些事情没有正确构建的实例。这可能是由于我使用的构建系统(QBS;它很新),但无论如何,我建议您尽可能只为最终构建启用它,并彻底测试该构建。
回答by zxcdw
While not strictly about symbols, if going for size - always compile with -Os
and -s
flags. -Os
optimizes the resulting code for minimum executable size and -s
removes the symbol table and relocation information from the executable.
虽然不是严格关于符号,但如果要考虑大小 - 总是用-Os
和-s
标志编译。-Os
优化生成的代码以获得最小的可执行文件大小,并-s
从可执行文件中删除符号表和重定位信息。
Sometimes - if small size is desired - playing around with different optimization flags may - or may not - have significance. For example toggling -ffast-math
and/or -fomit-frame-pointer
may at times save you even dozens of bytes.
有时 - 如果需要小尺寸 - 使用不同的优化标志可能 - 也可能没有 - 有意义。例如,切换-ffast-math
和/或-fomit-frame-pointer
有时可能会为您节省数十个字节。
回答by Gearoid Murphy
It seems to me that the answer provided by Nemo is the correct one. If those instructions do not work, the issue may be related to the version of gcc/ld you're using, as an exercise I compiled an example program using instructions detailed here
在我看来,Nemo 提供的答案是正确的。如果这些说明不起作用,则问题可能与您使用的 gcc/ld 版本有关,作为练习,我使用此处详述的说明编译了一个示例程序
#include <stdio.h>
void deadcode() { printf("This is d dead codez\n"); }
int main(void) { printf("This is main\n"); return 0 ; }
Then I compiled the code using progressively more aggressive dead-code removal switches:
然后我使用越来越激进的死代码删除开关编译代码:
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
These compilation and linking parameters produced executables of size 8457, 8164 and 6160 bytes, respectively, the most substantial contribution coming from the 'strip-all' declaration. If you cannot produce similar reductions on your platform,then maybe your version of gcc does not support this functionality. I'm using gcc(4.5.2-8ubuntu4), ld(2.21.0.20110327) on Linux Mint 2.6.38-8-generic x86_64
这些编译和链接参数分别生成大小为 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)
回答by Andrew Edgecombe
strip --strip-unneeded
only operates on the symbol table of your executable. It doesn't actually remove any executable code.
strip --strip-unneeded
只对可执行文件的符号表进行操作。它实际上并没有删除任何可执行代码。
The standard libraries achieve the result you're after by splitting all of their functions into seperate object files, which are combined using ar
. If you then link the resultant archive as a library (ie. give the option -l your_library
to ld) then ld will only include the object files, and therefore the symbols, that are actually used.
标准库通过将它们的所有函数拆分为单独的对象文件来实现您所追求的结果,这些文件使用ar
. 如果您随后将生成的存档链接为库(即,将选项提供-l your_library
给 ld),则 ld 将仅包含目标文件,因此包含实际使用的符号。
You may also find some of the responses to this similar questionof use.
回答by Luc Danton
I don't know if this will help with your current predicament as this is a recent feature, but you can specify the visibility of symbols in a global manner. Passing -fvisibility=hidden -fvisibility-inlines-hidden
at compilation can help the linker to later get rid of unneeded symbols. If you're producing an executable (as opposed to a shared library) there's nothing more to do.
我不知道这是否有助于解决您当前的困境,因为这是最近的功能,但您可以以全局方式指定符号的可见性。-fvisibility=hidden -fvisibility-inlines-hidden
在编译时传递可以帮助链接器稍后删除不需要的符号。如果您正在生成一个可执行文件(而不是共享库),那就没有什么可做的了。
More information (and a fine-grained approach for e.g. libraries) is available on the GCC wiki.
回答by awiebe
From the GCC 4.2.1 manual, section -fwhole-program
:
从 GCC 4.2.1 手册,部分-fwhole-program
:
Assume that the current compilation unit represents whole program being compiled. All public functions and variables with the exception of
main
and those merged by attributeexternally_visible
become static functions and in a affect gets more aggressively optimized by interprocedural optimizers. While this option is equivalent to proper use ofstatic
keyword for programs consisting of single file, in combination with option--combine
this flag can be used to compile most of smaller scale C programs since the functions and variables become local for the whole combined compilation unit, not for the single source file itself.
假设当前编译单元代表正在编译的整个程序。所有公共函数和变量(除了
main
和 按属性合并的那些之外)都externally_visible
成为静态函数,并且在影响过程中被过程间优化器更积极地优化。虽然此选项等效于static
为由单个文件组成的程序正确使用关键字,但与选项结合使用--combine
此标志可用于编译大多数较小规模的 C 程序,因为函数和变量对于整个组合编译单元而言是局部的,而不是用于编译单个源文件本身。