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

Sto*_*row 5 c linker gcc

我正在执行一些测试,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()不存在。

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

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

以上是基于此实验的假设-但它的正确性如何?如果有人对链接的复杂性有所了解,那么我将不胜感激,希望您能了解一些背景信息,解释发生的原因和原因。

PSk*_*cik 8

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

例:

如果静态库包含文件:

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要给您提供函数大小,并且如果您还希望这些函数大小不随函数的位置而略有波动(由于对齐要求),这也很有用。)