第二章 编译和链接
1. 构建
-
构建(Build):将编译和链接合并到一起执行。例如使用GCC构建出一个程序时可分解为四个步骤:预处理(Preprocessing),编译(Compilation),汇编(Assembly)和链接(Linking)。
$ gcc hello.c
-
预处理(Preprocessing):主要处理代码文件中的预处理指令。例如删除所有
#define
,展开所有的宏定义;删除所有的注释;添加行号和文件名标识;将#include
的文件内容展开到该预处理指令的位置等。
$ gcc -E hello.c -o hello.i
-
编译(Compilation):将预处理生成的文件进行此词法分析,语法分析,语义分析以及优化后生成对应的汇编代码文件。
$ gcc -S hello.i -o hello.s
-
汇编(Assembly):将汇编代码文件转变成机器码,机器可以执行的指令。输出目标文件(Object File)。
$ gcc -c hello.s -o hello.o
-
链接(Linking):将所有的目标文件链接起来,生成可执行文件。
$ gcc hello.o -o hello -L /usr/include/c++/7.3.0
-
gcc命令实际上是一系列编译工具的封装,它会根据参数去调用预编译编译程序cc1(cc1plus),汇编器as,链接器ld。
2. 编译器
-
编译器就是一个将高级语言翻译成机器语言的一个工具。编译过程可分为六步:词法分析,语法分析,语义分析,源代码优化,代码生成和目标代码生成。
-
词法分析:将代码文件输入扫描器(Scanner),扫描器把代码分割成一系列记号(Token)。
-
语法分析:语法分析器(Grammer Parser)将对由扫描器产生的记号进行词法分析,从而产生语法树(Syntax Tree)。语法分析只是语法层面,不去分析这个语句是否有意义。
-
语义分析:语义分析器(Semantic Analyzer)对语义进行分析,例如声明和类型的匹配,类型的转换等。语义分析后,语法树的表达式都会被标识了类型。
-
中间语言生成:源码优化器(Source Code Optimizer)将整个语法树转换为中间代码(Intermediate Code)。
-
目标代码生成与优化:代码生成器(Code Generator)将中间代码转换为机器代码。然后经过目标代码优化器(Target Code Optimizer)进行优化。
3. 链接
-
链接(Linking): 每个源码模块独立地编译,然后将它们组装起来,这个组装模块的过程就是链接。链接的主要工作就是把各个模块之间互相引用的部分处理好,使得各个模块之间能够正确衔接。
-
运行时库(Runtime Library):支持程序运行的基本函数的集合。
-
链接过程主要包含了:地址和空间分配(Address and Storage Allocation),符号决议(Symbol Resolution)和重定位(Relocation)等。
-
重定位(Relocation):重新计算各个目标地址的过程。
-
最基本的静态链接过程,将目标文件和库一起链接生成可执行文件。
-
链接过程和作用举例:main.c中调用了func.c中的foo()函数。编译器在编译main.c的时候并不知道foo()这个函数的地址,暂时将这个地址搁置。链接器在链接的时候,会根据引用的符号foo来找到func.c中的foo()的地址,然后将main.c中所有引用到foo的指令重新修正,让它们的目标地址成为真正foo函数的地址。这个地址修正的过程就是重定位。