编译器角度C++的编译链接原理

编译过程有四个过程:预处理 编译 汇编 链接

预处理阶段

预处理器(preprocessor)会处理源代码文件中的预处理指令,如#include#define等。#include指令用于插入指定的头文件,#define用于定义宏。预处理器还会进行条件编译,处理#if#ifdef#ifndef#else#elif#endif等指令。

编译阶段

在这个阶段,编译器将预处理后的代码(已经是纯C/C++代码,没有任何预处理指令)转换成汇编代码。编译器在这个阶段也会检查代码的语法错误,如果有错就报错。

汇编阶段

汇编器将编译阶段生成的汇编代码转换成机器语言代码,也就是目标代码文件

链接阶段

符合合并,进行符号解析

链接器将一个或多个目标代码文件以及需要的库文件合并成一个单独的可执行文件。在这个过程中,链接器解析未定义的符号(例如函数或变量的引用),并将它们链接到正确的位置。

示例

sum.cpp

1
2
3
4
5
6
int data = 10;

int sum(int a, int b)
{
return a + b;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

extern int data;

int sum(int a, int b);

int main()
{
int data2 = 20;
int ret = sum(data, data2);
cout << "ret is: " << ret << endl;
return 0;
}

执行g++ -c sum.cpp 生成 sum.o文件

并通过objdump -t 显示目标文件的符号表信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
D:\梦想\C++>g++ -c sum.cpp

D:\梦想\C++>objdump -t sum.o

sum.o: file format pe-x86-64

SYMBOL TABLE:
[ 0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x0000000000000000 sum.cpp
File
[ 2](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 1) 0x0000000000000000 _Z3sumii
AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0
[ 4](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .text
AUX scnlen 0x14 nreloc 0 nlnno 0
[ 6](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .data
AUX scnlen 0x4 nreloc 0 nlnno 0
[ 8](sec 3)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .bss
AUX scnlen 0x0 nreloc 0 nlnno 0
[ 10](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .xdata
AUX scnlen 0x8 nreloc 0 nlnno 0
[ 12](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .pdata
AUX scnlen 0xc nreloc 3 nlnno 0
[ 14](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata$zzz
AUX scnlen 0x3f nreloc 0 nlnno 0
[ 16](sec 2)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 data

执行g++ -c main.cpp 生成 main.o文件

并通过objdump -t 显示目标文件的符号表信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
D:\梦想\C++>g++ -c main.cpp

D:\梦想\C++>objdump -t main.o

main.o: file format pe-x86-64

SYMBOL TABLE:
[ 0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x0000000000000000 main.cpp
File
[ 2](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x0000000000000000 _ZStL19piecewise_construct
[ 3](sec 3)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x0000000000000000 _ZStL8__ioinit
[ 4](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 1) 0x0000000000000000 main
AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0
[ 6](sec 11)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata$.refptr.data
AUX scnlen 0x8 nreloc 1 nlnno 0 checksum 0x0 assoc 0 comdat 2
[ 8](sec 10)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata$.refptr._ZSt4cout
AUX scnlen 0x8 nreloc 1 nlnno 0 checksum 0x0 assoc 0 comdat 2
[ 10](sec 9)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata$.refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
AUX scnlen 0x8 nreloc 1 nlnno 0 checksum 0x0 assoc 0 comdat 2
[ 12](sec 1)(fl 0x00)(ty 20)(scl 3) (nx 0) 0x0000000000000064 __tcf_0
[ 13](sec 1)(fl 0x00)(ty 20)(scl 3) (nx 0) 0x000000000000007f _Z41__static_initialization_and_destruction_0ii
[ 14](sec 1)(fl 0x00)(ty 20)(scl 3) (nx 0) 0x00000000000000bb _GLOBAL__sub_I_main
[ 15](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .text
AUX scnlen 0xd9 nreloc 14 nlnno 0
[ 17](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .data
AUX scnlen 0x0 nreloc 0 nlnno 0
[ 19](sec 3)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .bss
AUX scnlen 0x1 nreloc 0 nlnno 0
[ 21](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata
AUX scnlen 0xa nreloc 0 nlnno 0
[ 23](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .xdata
AUX scnlen 0x30 nreloc 0 nlnno 0
[ 25](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .pdata
AUX scnlen 0x30 nreloc 12 nlnno 0
[ 27](sec 7)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .ctors
AUX scnlen 0x8 nreloc 1 nlnno 0
[ 29](sec 8)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata$zzz
AUX scnlen 0x3f nreloc 0 nlnno 0
[ 31](sec 11)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 .refptr.data
[ 32](sec 10)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 .refptr._ZSt4cout
[ 33](sec 9)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
[ 34](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 __main
[ 35](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 _Z3sumii
[ 36](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
[ 37](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 _ZNSolsEi
[ 38](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 _ZNSolsEPFRSoS_E
[ 39](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 _ZNSt8ios_base4InitD1Ev
[ 40](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 _ZNSt8ios_base4InitC1Ev
[ 41](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 atexit
[ 42](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
[ 43](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 _ZSt4cout
[ 44](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 data
总结

符号的引用能找到唯一的符号定义

作者:张泽中