第二章 编译和链接词法分析语法分析语义分析中间语言生成目标代码生成与优化链接器
第二章 编译和链接
平时我们使用的IDE把编译链接的过程都合并在一起了,这个称之为构建(Build),但实际上是分为两个过程 的
对于代码
#include <stdio.h>
int main()
{
/* code */
printf("Hello World\n");
return 0;
}
我们执行
$ gcc hello.c
通常有4个步骤 预处理(Prepressing)、编译(Compilation)、汇编(Assembly) 和链接(Linking)
- 预处理
$ gcc -E hello.c -o hello.i
- 预处理会删除“#define”,展开宏定义
- 处理预编译指令 "#if"等
- 处理""#include",把包含的文件拷贝过来
- 删除注释"//"等
- 添加行号和文件名标识,方便编译时能够对应行号
- 保留#pragma
- 编译
$ gcc -S hello.i -o hello.s
进行此法分析、语法分析、语义分析及优化产生汇编代码文件。
- 汇编
gcc -c hello.s -o hello.o
把汇编代码转变成机器码
-
链接
需要链接很多.o文件才能让最终产物跑起来
对于代码
array[index] = (index + 4) * (2 + 6);
词法分析
通过扫描器(Scanner)扫描,切割代码的字符序列为一列的记号(Token)
语法分析
通过词法分析器生成以表达式为节点的语法树
语义分析
这一步的目的是弄懂语句是否是合法的,比如两个指针做乘法就没什么意义。分为静态语义(在编译期确定)和动态语义,在运行期才能确定的语义。
中间语言生成
将语法树转成中间代码,并进行优化,比如把一些可以编译器确定的计算入2+6=8这种给提前计算出来。
目标代码生成与优化
编译器后端主要包括代码生成器和目标代码优化器,这两步会对目标代码进行优化,删除多余的指令,但是还没有确定变量的地址,这就需要用到链接器。
链接器
重定位 重新计算各个目标的地址的过程,因为修改过一次代码后,一些符号的目标地址肯定要发生改变。
符号 这个是从机器码到汇编的过程产生的,就是用来替代一些地址,它就是用来表示一个地址,可能是一段子程序(比如说函数)的起始地址,也可以是一个变量的起始地址
一个文件用到另外一个文件的函数或者变量,但是一开始又不知道这个函数的具体地址,链接要做的事就是把一些指令对其他符号地址的引用加以修正,让文件和文件之间的跳转可以有具体的地址。
链接过程包括 地址和空间分配、符号决议、重定位
比如目标文件B中有如下指令
movl $0x2a, var
这句相当于var = 42
,假设var在目标文件A中,那么编译器编译B的时候一开始不知道var的目标地址,所以就在var那里打个补丁,叫做重定位入口(Relocation Entry),直到链接器去修正这个地址