自我修养的拷贝笔记(二)

189 阅读6分钟

编译流程

流程 可以分解为4步:预处理、编译、汇编和链接。



预编译

作用:它主要处理源代码中带以‘#’字符开始的预编译指令。主要的处理的规则在书中第39页里提到。预编译过后,会从‘.c’文件生成出‘.i’文件。预编译后的文件是不包含任何宏定义的,因为所有的宏已经被展开。



编译

作用:它把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生产相应的汇编代码文件。

在GCC中 ,它用程序ccl把预编译和编译这两步合并为一步。


词法分析

过程:源码输入到扫描器,扫描器进行词法分析,然后运用算法(类似于有限状态机)把源码里面的字符一个一个分割,最后给他们记上记号。

词法分析 产生的记号一般可以分为关键字 、标识符 、字面量(包含数字、字符串等)、特殊符号(如加号、等号)。


语法分析

过程 :分析器将分割出来的记号进行语法分析,从而产生语法树。它采用了上下文无关语法的分析手段,意思是,语法树是以表达式为节点的树。


语义分析

编译器 只分析静态语义,静态语义就是在编译器可以确定的语义。而动态语义只有在运行期才能确定。

静态语义 一般包括 声明、类型的匹配、类型的转换。比如,浮点型的表达式赋值给整型表达式,这里面有浮点型到整型转换的过程,而语义分析的过程中需要完成这个步骤。


中间语言生成

现代编译 在源码级别会有一个优化,这些工作由源码级优化器来完成。由于直接在语法树上优化困难,所以优化器会将语法树转换成中间代码,中间代码不跟目标机器和运行环境相关,它不包含数据类型、变量地址和寄存器名字等等。

中间代码 的表现形式一般有三地址码 和 P-代码。


目标代码生成与优化

中间代码标志 的生成过程属于编译器后端,编译器后端主要包括代码生成器 和 目标代码优化器。代码生成器将中间代码转换成机器代码,这个过程依赖目标机器,因为不同的机器有不同的字长、寄存器、整数类型和浮点类型等等。

不过,中间代码转换成机器代码之前,生成器可能会生成代码序列,然后由目标代码优化器对代码序列进行优化。比如,选择合适的寻址方式、删除多余的指令等等。


现代编译器复杂的原因

首先,复杂的原因是因为现代高级编程语言本身非常地复杂。其次,CPU本身采用了多发射、超标量、流水线等诸多复杂特性,这些特性导致编译器地机器指令优化变得复杂。最后,编译器为了支持多平台编译,因此导致指令生成地过程变得更加复杂。



汇编

作用 :它将汇编代码转变成机器可以看得懂的指令,每个汇编语句一般对应一条机器指令。



链接

目标代码中有一些变量定义在其他模块,该怎么办?

定义其他模块的全局变量和函数的运行时绝对地址都在最终链接的时候才能确定,所以编译器可以将一个源代码文件编译成一个未链接的目标文件。然后由链接器最终将这些目标文件链接起来形成可执行文件。

汇编语言的符号 :符号(symbol)用来表示一个地址,这个地址可能是一段函数的起始地址,也可以是一个变量的起始地址。

模块之间的组合 可以看成模块之间的通信,静态语言 C/C++模块之间通信有两种方式,一种是模块间的函数调用,另外一种是模块间的变量访问。这两种方式都可以理解为模块间的符号引用。模块间依靠符号来通信类似拼图,定义符号的模块多出一块区域,而引用该符号的模块刚好少这块区域,两个模块拼接就会完美组合在一起。


静态链接

链接过程 主要包括地址和空间分配、符号决议、重定位。每个模块的源码文件(.c)经过编译器编译成目标文件(.o或.obj),目标文件和库一起链接形成可执行文件。例如:如果Aaa.c 使用了 Bbb.c 文件里面的 Bbb(),那么,Aaa.c 里面所有调用了 Bbb() 的地方都需要知道 Bbb() 的函数地址。但是,每个模块都是单独编译,所以,在未链接完成之前,Aaa.c 文件并不清楚 Bbb() 的函数地址。因此,Aaa.c 暂时把调用 Bbb() 的指令中的目标地址搁置为0了。在链接器进行链接时,会根据 Aaa.c 所引用的符号 Bbb,自动去相应的 Bbb.c 模块查找 Bbb 的地址。然后,将 Aaa.c 模块中所引用 Bbb() 的指令重新修正,让它们的目标地址为真正 Bbb 函数的地址。

符号决议 :也可以称为 符号绑定、名称绑定、名称决议、地址绑定、指令绑定。“决议“更倾向于静态链接,而”绑定“更倾向于动态链接,它们使用的范围不一样。

重定位和重定位入口:在 Aaa.c 和 Bbb.c 链接后,函数 Bbb() 的地址确定下来为 0x1000,那么,链接器将会把这个指令的目标地址部分修改为 0x1000。地址修正的过程为 重定位,每个被修正的地方叫 每个重定位入口。

重定位的作用:它给程序中每个这样的绝对地址引用的位置”打补丁“,使它们指向正确的地址。

常见的库 是运行时库,它是支持程序运行的基本函数的集合。库是一组目标文件的包,是一些最常用的代码编译成目标文件后打包存放的文件。