假设我在某个 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/