我正在执行一些测试,gcc以了解该规则可以智能地排除未使用的符号。
// main.c
#include <stdio.h>
void foo()
{
}
int main( int argc, char* argv[] )
{
return 0;
}
Run Code Online (Sandbox Code Playgroud)
。
// bar.c
int bar()
{
return 42;
}
Run Code Online (Sandbox Code Playgroud)
。
> gcc --version
gcc (GCC) 8.2.1 20181215 (Red Hat 8.2.1-6)
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>
> gcc -c bar.c
> gcc -g main.c bar.o
> nm a.out | grep "foo\|bar"
000000000040111f T bar
0000000000401106 T foo
Run Code Online (Sandbox Code Playgroud)
上面,我已经编译了bar.o,并a.out在编译时将其链接了main.c。
Listing a.out的符号表明,未使用的函数- foo()和bar()-都包含在可执行文件中。
> ar -r libbar.a bar.o
ar: creating libbar.a
> gcc -g main.c -L ./ -lbar
> nm a.out | grep "foo\|bar"
0000000000401106 T foo
Run Code Online (Sandbox Code Playgroud)
上面,我已存档bar.o到libbar.a并重新创建了a.out,这次与libbar.a而不是链接bar.o。这次,未使用的功能foo()仍然存在,但bar()不存在。
从这个实验中,我可以推测出以下“规则”:
foo()总是存在的原因:是否main.o创建了一个临时/匿名文件?如果是这样,它将包括在内foo())gcc将智能地找出要排除的不必要符号。以上是基于此实验的假设-但它的正确性如何?如果有人对链接的复杂性有所了解,那么我将不胜感激,希望您能了解一些背景信息,解释发生的原因和原因。
注意静态库链接实际上没有每个符号的粒度,这在很大程度上是正确的。它具有每个成员对象文件的粒度。
例:
如果静态库包含文件:
a.o
foo
bar
b.o
baz
Run Code Online (Sandbox Code Playgroud)
并且会引入foo需要解决的未定义引用a.o,并带有bar符号。
在进行编译-ffunction-sections -fdata-sections然后再与之链接时,您可以得到每个符号粒度的效果 -Wl,--gc-sections(gc代表垃圾收集),但请记住,编译器/链接器选项是gcc / clang特定的,并且它们的性能较差/代码大小的成本。
-ffunction-sections将每个函数放在自己的部分(有点像自己的目标文件),-fdata-sections并对外部可见的全局变量执行相同的操作。-Wl,--gc-sections然后在照常链接目标文件后使垃圾收集器运行,并且垃圾收集器删除了所有不可访问的节(=>符号)。
(-ffunction-sections如果size -A the_objectfile.o要给您提供函数大小,并且如果您还希望这些函数大小不随函数的位置而略有波动(由于对齐要求),这也很有用。)