Linker Options
Table of Contents
1. Linker Options
1.1. init
默认情况下 linker 把发现 _init 函数并把它设置 .dynamic section 中的 DT_INIT
#include <stdio.h> void _init() { printf("%s\n", "init"); } int main(int argc, char *argv[]) { printf("main: %p\n", &main); return 0; }
$> arm-linux-androideabi-gcc test.c -fPIE -pie -O3 -g3 00000474 <_init>: 474: e59f0004 ldr r0, [pc, #4] ; 480 <_init+0xc> 478: e08f0000 add r0, pc, r0 47c: eaffffc2 b 38c <puts@plt> 480: 00000010 andeq r0, r0, r0, lsl r0 Dynamic section at offset 0xec0 contains 29 entries: Tag Type Name/Value ... 0x0000000c (INIT) 0x474 ...
也可以通过 linker 的 -init 来指定而不使用默认的 _init 函数, 效果是一样的.
1.2. verbose
1.3. script
1.4. entry
1.5. gc-sections
use `gc-sections`, `-ffunction-sections`, `-fdata-sections` to remove unused symbol
正常情况下, ld 在链接时会以 object 为单位, 把所有的同类 input section 集合成一个 section, 其中 ld 的 gc-sections 参数会导致没有用到的 input section 会被删除
gc-sections decides which input sections are used by examining symbols and relocations. The section containing the entry symbol and all sections containing symbols undefined on the command-line will be kept, as will sections containing symbols referenced by dynamic objects. Note that when building shared libraries, the linker must assume that any visible symbol is referenced. Once this initial set of sections has been determined, the linker recursively marks as used any section referenced by their relocations.
默认情况下同一个 object 中的所有函数都在同一个 input section, 导致只要有一个函数被引用, 整个 input section 被会被保存.
通过 ffunction-sections, 可以使每个函数都在它自己的 section 中, 例如 foo 函数会放在 .text.foo 中, 这样配合 gc-sections, 就可以删除没使用的函数
#include <stdio.h> void foo() { printf("hello\n"); } int main(int argc, char** argv) {}
$> gcc test.c $> nm ./a.out|grep foo 0000000000001149 T foo $> gcc -ffunction-sections test.c -c $> readelf -S ./test.o|grep text [ 1] .text PROGBITS 0000000000000000 00000040 [ 5] .text.foo PROGBITS 0000000000000000 00000046 [ 6] .rela.text.foo RELA 0000000000000000 000002c8 [ 7] .text.main PROGBITS 0000000000000000 0000005d $> gcc -ffunction-sections test.c -Wl,-gc-sections $> nm ./a.out|grep foo
1.6. nostdlib
1.7. pie
1.8. rpath
1.9. no-as-needed/whole-archive
正常情况下 -lxx 需要放在 .o 之后, 因为 libxx 是否需要链接进来取决于前面是否有缺失的符号.
gcc -lfoo test.o -o test 时, lfoo 并不会被链接, 因为处理命令行参数 lfoo 时, 当前并没有解析 test.o, 也就是没有缺失的符号需要从 foo 中链接
通过 no-as-needed, 可以强制链接后面的 so whole-archive 和 no-as-needed 类似, 但它对应的是静态库
–no-as-needed
This option affects ELF DT_NEEDED tags for dynamic libraries mentioned on the command line after the –as-needed option. Normally the linker will add a DT_NEEDED tag for each dynamic library mentioned on the command line, regardless of whether the library is actually needed or not. –as-needed causes a DT_NEEDED tag to only be emitted for a library that at that point in the link satisfies a non-weak undefined symbol reference from a regular object file or, if the library is not found in the DT_NEEDED lists of other needed libraries, a non-weak undefined symbol reference from another needed dynamic library. Object files or libraries appearing on the command line after the library in question do not affect whether the library is seen as needed. This is similar to the rules for extraction of object files from archives. –no-as-needed restores the default behaviour.
–whole-archive
For each archive mentioned on the command line after the –whole-archive option, include every object file in the archive in the link, rather than searching the archive for the required object files. This is normally used to turn an archive file into a shared library, forcing every object to be included in the resulting shared library. This option may be used more than once.
Two notes when using this option from gcc: First, gcc doesn't know about this option, so you have to use -Wl,-whole-archive. Second, don't forget to use -Wl,-no-whole-archive after your list of archives, because gcc will add its own list of archives to your link and you may not want this flag to affect those as well.
由于 -lxxx 的先后顺序很重要, 所以 Makefile 的 explicit rule 里有一个单独的 LDLIBS, 用来指定 -lxxx, 用来和 LDFLAGS 区分, 因为 explicit rule 大约是:
gcc $CPPFLAGS $CFLAGS $LDFLAGS test.o -o test $LDLIBS
以确保 -lxxx 会被放在 test.o 之后
1.10. section-start
1.11. version_script
1.12. soname
1.13. Bsymbolic
1.14. muldefs
`-z muldefs` 允许有重复的符号定义
1.15. dynamic-linker
1.16. relro
1.17. wrap
如何给重写 malloc 并调用原来的 malloc
/* main.c */ #include <stdlib.h> int main(int argc, char** argv) { malloc(10); } /* my_malloc.c */ #include <stdlib.h> void* __real_malloc(size_t size); void* __wrap_malloc(size_t n) { printf("hello\n"); return __real_malloc(n); }
$> gcc test.c my_malloc.c -Wl,--wrap,malloc -static -g $> gdb ./a.out (gdb) disass main Dump of assembler code for function main: 0x0000000000401d35 <+0>: endbr64 0x0000000000401d39 <+4>: push %rbp 0x0000000000401d3a <+5>: mov %rsp,%rbp 0x0000000000401d3d <+8>: sub $0x10,%rsp 0x0000000000401d41 <+12>: mov %edi,-0x4(%rbp) 0x0000000000401d44 <+15>: mov %rsi,-0x10(%rbp) 0x0000000000401d48 <+19>: mov $0xa,%edi 0x0000000000401d4d <+24>: callq 0x401d59 <__wrap_malloc> 0x0000000000401d52 <+29>: mov $0x0,%eax 0x0000000000401d57 <+34>: leaveq 0x0000000000401d58 <+35>: retq $> (gdb) disass __wrap_malloc Dump of assembler code for function __wrap_malloc: 0x0000000000401d59 <+0>: endbr64 0x0000000000401d5d <+4>: push %rbp 0x0000000000401d5e <+5>: mov %rsp,%rbp 0x0000000000401d61 <+8>: sub $0x10,%rsp 0x0000000000401d65 <+12>: mov %rdi,-0x8(%rbp) 0x0000000000401d69 <+16>: lea 0x93294(%rip),%rdi # 0x495004 0x0000000000401d70 <+23>: callq 0x4118f0 <puts> 0x0000000000401d75 <+28>: mov -0x8(%rbp),%rax 0x0000000000401d79 <+32>: mov %rax,%rdi 0x0000000000401d7c <+35>: callq 0x41f1f0 <malloc> 0x0000000000401d81 <+40>: leaveq 0x0000000000401d82 <+41>: retq End of assembler dump.
通过 `Wl,–wrap,malloc`, 所有对 malloc 的调用被 ld 修改为 __wrap_malloc, 且代码中通过 __real_malloc 可以引用原来的 malloc
Backlinks
Static Linker (Static Linker > Linker Options): Linker Options