C 链接器何时以及为何会排除未使用的符号?



我正在用gcc执行一些测试,以了解它智能地排除未使用符号的规则。

// main.c
#include <stdio.h>
void foo()
{
}
int main( int argc, char* argv[] )
{
return 0;
}

.

// bar.c
int bar()
{
return 42;
}

.

> 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

上面,我编译了bar.o,并在编译main.c时将其与a.out链接。
清单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

上面,我已经将bar.o存档到libbar.a,并重新创建了a.out,这次与libbar.a而不是bar.o链接。这一次,未使用的功能foo()仍然存在,但bar()不存在。

从这个实验中,我可以推测出以下"规则":

  1. 从对象文件链接的符号始终存在于可执行文件中。(也许这解释了为什么foo()总是存在:是否有一个临时/匿名main.o被创建?如果是这样,它将包括foo()
  2. )
  3. 如果可执行文件与链接,gcc将智能地找出要排除的不必要的符号。

以上是我基于这个实验的假设——但它的正确性如何?如果有人了解链接如何工作的复杂性,我将不胜感激一些背景信息来解释正在发生的事情的原因和原因。

需要注意的是,静态库链接实际上没有每个符号的粒度,这基本上是正确的。它具有每个成员对象文件的粒度。

例:

如果静态库包含文件:

a.o 
foo
bar
b.o 
baz

并且需要解决对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为您提供函数大小,并且还希望这些函数大小 不会根据功能的位置略有波动(由于对齐要求)。

最新更新