使用库优化 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(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 

但是在构建 gcc test.c libfoo.a 之后 它没有出现在可执行文件中。

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
0000000000000680 g     F .text  0000000000000002              __libc_csu_fini
0000000000000610 g     F .text  0000000000000065              __libc_csu_init
00000000000004f0 g     F .text  000000000000002b              _start
00000000000005fa g     F .text  000000000000000b              main

当你在 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 上)。 (参见 https://gcc.gnu.org/onlinedocs/gnat_ugn/Compilation-options.html)

想法是,您使用 -ffunction-sections -fdata-sections 构建所有目标文件,这会导致为每个函数创建一个唯一的部分。 (通常它们都在同一个 .text 中)

gcc -c foo.c -ffunction-sections -fdata-sections
ar rcs libfoo.a foo.o
objdump -S libfoo.a

会告诉你这个:

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 

使用 -Wl,--gc-sections 将在链接过程中启用垃圾收集,其中未使用的部分将被忽略/删除。

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

关于使用库优化 C 链接器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51927634/

相关文章:

(!pointer) 和 (pointer != NULL) 之间的 C 区别,

mysql - 如何使用 mysql 连接器创建共享对象库 (*.so)?

c - 互斥体 |自旋锁 | ???在龙记

c - 为什么 gets(stdin) 返回一个整数?和其他错误

c - char 指针 (strdup) 时使用 printf 的段错误

c - 字符串格式漏洞: set negative value = "-1" use %n

c - 为什么会导致执行错误呢?

c - C中的进度字符串解析

c - 如何在 DE2 上通过串口发送数据

javascript - 我需要将一组文件从原始位置复制到新文件夹