基于库的C链接器优化

基于库的C链接器优化,c,C,假设我在某个C库中有一个函数foo(),并且静态地将该库链接到某个可执行文件,但是没有从库或可执行文件中的代码调用该方法。 链接器会通过删除函数定义来优化最终可执行文件,还是它仍然是代码的一部分? 是否有任何链接器优化可以打开/关闭此行为?但是,它应该取决于您的工具链 我的gcc-7不包括它 您可以使用objdump //foo.h #pragma once int foo(int x); int foo2(int x); //foo.c #include "foo.h" int foo(in

假设我在某个C库中有一个函数foo(),并且静态地将该库链接到某个可执行文件,但是没有从库或可执行文件中的代码调用该方法。 链接器会通过删除函数定义来优化最终可执行文件,还是它仍然是代码的一部分?
是否有任何链接器优化可以打开/关闭此行为?

但是,它应该取决于您的工具链 我的gcc-7不包括它

您可以使用
objdump

//foo.h
#pragma once
int foo(int x);
int foo2(int x);

//foo.c
#include "foo.h"
int foo(int x) {
    return x * 2; //whatever
}
int foo2(int x) {
    return x * 2; //whatever
}

//test.c
#include "foo.h"

int main() {
    //foo(2);
    return 0;
}
使用

gcc -c foo.c
ar rcs libfoo.a foo.o
您可以使用查看结果

objdump -j .text -S libfoo.a
应该是这样的:

Disassembly of section .text:

0000000000000000 <foo>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   8b 45 fc                mov    -0x4(%rbp),%eax
   a:   01 c0                   add    %eax,%eax
   c:   5d                      pop    %rbp
   d:   c3                      retq   

000000000000000e <foo2>:
   e:   55                      push   %rbp
   f:   48 89 e5                mov    %rsp,%rbp
  12:   89 7d fc                mov    %edi,-0x4(%rbp)
  15:   8b 45 fc                mov    -0x4(%rbp),%eax
  18:   c1 e0 02                shl    $0x2,%eax
  1b:   5d                      pop    %rbp
  1c:   c3                      retq 
在test.c中使用函数时,它将出现在符号表中。 但是,如果库包含多个函数,则无论其使用情况如何,都将包含所有函数

SYMBOL TABLE:
00000000000004f0 l    d  .text  0000000000000000              .text
0000000000000520 l     F .text  0000000000000000              deregister_tm_clones
0000000000000560 l     F .text  0000000000000000              register_tm_clones
00000000000005b0 l     F .text  0000000000000000              __do_global_dtors_aux
00000000000005f0 l     F .text  0000000000000000              frame_dummy
00000000000006a0 g     F .text  0000000000000002              __libc_csu_fini
000000000000061d g     F .text  000000000000000f              foo2
0000000000000630 g     F .text  0000000000000065              __libc_csu_init
000000000000060f g     F .text  000000000000000e              foo
00000000000004f0 g     F .text  000000000000002b              _start
00000000000005fa g     F .text  0000000000000015              main
您可以使用编译器标志解决这个问题(在gcc上)。 (见附件)

其思想是,使用
-fffunction sections-fdata sections
构建所有对象文件,从而为每个函数创建一个唯一的节。(通常它们都在同一个.text中)

将向您展示以下内容:

Disassembly of section .text.foo:

0000000000000000 <foo>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   8b 45 fc                mov    -0x4(%rbp),%eax
   a:   01 c0                   add    %eax,%eax
   c:   5d                      pop    %rbp
   d:   c3                      retq   

Disassembly of section .text.foo2:

0000000000000000 <foo2>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   8b 45 fc                mov    -0x4(%rbp),%eax
   a:   c1 e0 02                shl    $0x2,%eax
   d:   5d                      pop    %rbp
   e:   c3                      retq 
这将导致:

SYMBOL TABLE:
00000000000004f0 l    d  .text  0000000000000000              .text
0000000000000520 l     F .text  0000000000000000              deregister_tm_clones
0000000000000560 l     F .text  0000000000000000              register_tm_clones
00000000000005b0 l     F .text  0000000000000000              __do_global_dtors_aux
00000000000005f0 l     F .text  0000000000000000              frame_dummy
0000000000000690 g     F .text  0000000000000002              __libc_csu_fini
0000000000000620 g     F .text  0000000000000065              __libc_csu_init
000000000000060f g     F .text  000000000000000e              foo
00000000000004f0 g     F .text  000000000000002b              _start
00000000000005fa g     F .text  0000000000000015              main

通常是的,链接器应该能够进行这种优化,因为这通常是首先进行静态链接(而不是通常更可取的动态链接)的原因。

您询问的是特定的工具链还是一般的(在这种情况下,答案是:这取决于您的工具链)你必须指定操作系统的链接。对于绝大多数系统来说:不,一个不被调用的函数(或者它的地址)不会被链接。请不要在询问C时加上C++。我猜答案是:有链接器会打开/关闭这种行为。
Disassembly of section .text.foo:

0000000000000000 <foo>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   8b 45 fc                mov    -0x4(%rbp),%eax
   a:   01 c0                   add    %eax,%eax
   c:   5d                      pop    %rbp
   d:   c3                      retq   

Disassembly of section .text.foo2:

0000000000000000 <foo2>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   8b 45 fc                mov    -0x4(%rbp),%eax
   a:   c1 e0 02                shl    $0x2,%eax
   d:   5d                      pop    %rbp
   e:   c3                      retq 
gcc test.c libfoo.a -Wl,--gc-sections
objdump -j .text -x a.out
SYMBOL TABLE:
00000000000004f0 l    d  .text  0000000000000000              .text
0000000000000520 l     F .text  0000000000000000              deregister_tm_clones
0000000000000560 l     F .text  0000000000000000              register_tm_clones
00000000000005b0 l     F .text  0000000000000000              __do_global_dtors_aux
00000000000005f0 l     F .text  0000000000000000              frame_dummy
0000000000000690 g     F .text  0000000000000002              __libc_csu_fini
0000000000000620 g     F .text  0000000000000065              __libc_csu_init
000000000000060f g     F .text  000000000000000e              foo
00000000000004f0 g     F .text  000000000000002b              _start
00000000000005fa g     F .text  0000000000000015              main