链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储器并执行。
在现代系统中,链接是由链接器的程序自动执行的。
理解链接器将帮助你构造大型程序。
理解链接器将帮助你避免一些危险的编程错误
理解链接将帮助你理解语言的作用域规则是如何实现的
理解链接将帮助你理解其他重要的系统概念
理解链接将使你能够利用共享库
编译器驱动程序:
1.预编译
[xyk@xyk linking]$ gcc -E main.c -o main.i
[xyk@xyk linking]$ gcc -E swap.c -o swap.i
2.编译
[xyk@xyk linking]$ gcc -S swap.i -o swap.s
[xyk@xyk linking]$ gcc -S main.i -o main.s
3.汇编
[xyk@xyk linking]$ gcc -c main.s -o main.o
[xyk@xyk linking]$ gcc -c swap.s -o swap.o
4.链接
[xyk@xyk linking]$ gcc -o p main.o swap.o
静态链接
静态链接器:以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出。
为了构造可执行文件,链接器必须完成两个主要任务:
1.符号解析:目标文件定义和引用符号。符号解析的目的是将每个符号的引用刚好和一个符号定义联系起来。
2.重定位:编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而重定位这些节。
目标文件:可重定位目标文件;可执行目标文件;共享目标文件。
可重定位目标文件:
ELF可重定位目标文件的格式
ELF头 |
.text:已编译程序的机器代码 |
.rodata:只读数据 |
.data:已初始化的全局C变量 |
.bss:未初始化的全局C变量 |
.symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息 |
.rel.text:一个.text节中位置的列表 |
.rel.data:被模块引用或定义的任何全局变量的重定位信息 |
.debug:一个调试符号表 |
.line:原始C源程序中的行号和.text节中机器指令之间的映射。 |
.strtab:一个字符串表,其内容包括.symtab和.debug节中的符号表以及节头部中的节名字。字符串表就是以null结尾的字符串序列. |
节头部表 |
符号和符号表
在链接器的上下文中,有三种不同的符号:
1.由目标模块定义并能被其他模块引用的全局符号
2.由其他模块定义并被目标模块引用的全局变量
3.只被目标模块定义和引用的本地符号。
[xyk@xyk linking]$ readelf -a main.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 200 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 10
Section header string table index: 7
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000014 00 AX 0 0 4
[ 2] .rel.text REL 00000000 000310 000008 08 8 1 4
[ 3] .data PROGBITS 00000000 000048 000008 00 WA 0 0 4
[ 4] .bss NOBITS 00000000 000050 000000 00 WA 0 0 4
[ 5] .comment PROGBITS 00000000 000050 00002d 01 MS 0 0 1
[ 6] .note.GNU-stack PROGBITS 00000000 00007d 000000 00 0 0 1
[ 7] .shstrtab STRTAB 00000000 00007d 000049 00 0 0 1
[ 8] .symtab SYMTAB 00000000 000258 0000a0 10 9 7 4
[ 9] .strtab STRTAB 00000000 0002f8 000016 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
Relocation section '.rel.text' at offset 0x310 contains 1 entries:
Offset Info Type Sym.Value Sym. Name
00000007 00000902 R_386_PC32 00000000 swap
There are no unwind sections in this file.
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS main.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 6
6: 00000000 0 SECTION LOCAL DEFAULT 5
7: 00000000 8 OBJECT GLOBAL DEFAULT 3 buf
8: 00000000 20 FUNC GLOBAL DEFAULT 1 main
9: 00000000 0 NOTYPE GLOBAL DEFAULT UND swap
[xyk@xyk linking]$ readelf -a swap.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 236 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 11
Section header string table index: 8
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000035 00 AX 0 0 4
[ 2] .rel.text REL 00000000 000374 000030 08 9 1 4
[ 3] .data PROGBITS 00000000 00006c 000004 00 WA 0 0 4
[ 4] .rel.data REL 00000000 0003a4 000008 08 9 3 4
[ 5] .bss NOBITS 00000000 000070 000000 00 WA 0 0 4
[ 6] .comment PROGBITS 00000000 000070 00002d 01 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 00000000 00009d 000000 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 00009d 00004d 00 0 0 1
[ 9] .symtab SYMTAB 00000000 0002a4 0000b0 10 10 7 4
[10] .strtab STRTAB 00000000 000354 00001d 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
Relocation section '.rel.text' at offset 0x374 contains 6 entries:
Offset Info Type Sym.Value Sym. Name
00000008 00000901 R_386_32 00000004 bufp1
0000000c 00000801 R_386_32 00000000 buf
00000011 00000701 R_386_32 00000000 bufp0
0000001b 00000701 R_386_32 00000000 bufp0
00000021 00000901 R_386_32 00000004 bufp1
0000002a 00000901 R_386_32 00000004 bufp1
Relocation section '.rel.data' at offset 0x3a4 contains 1 entries:
Offset Info Type Sym.Value Sym. Name
00000000 00000801 R_386_32 00000000 buf
There are no unwind sections in this file.
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS swap.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 5
5: 00000000 0 SECTION LOCAL DEFAULT 7
6: 00000000 0 SECTION LOCAL DEFAULT 6
7: 00000000 4 OBJECT GLOBAL DEFAULT 3 bufp0
8: 00000000 0 NOTYPE GLOBAL DEFAULT UND buf
9: 00000004 4 OBJECT GLOBAL DEFAULT COM bufp1
10: 00000000 53 FUNC GLOBAL DEFAULT 1 swap
符号解析
编译器如何解析多重定义的全局符号
在编译时,编译器向汇编器输出每个全局符号,或者强或者弱,而汇编器把这个信息隐含的编码在可重定位目标文件的符号表里。函数和已初始化的的全局变量是强符号,未初始化的全局变量是弱符号。根据强弱符号的定义,Unix链接器使用下面的规则来处理多重定义的符号:
规则1:不允许有多个强符号
规则2:如果有一个强符号和多个弱符号,那么选择强符号
规则3:如果有多个弱符号,那么从这些弱符号中任意选择一个。