编译链接原理
编译阶段一共分为3部:预编译阶段,编译阶段,和汇编阶段。
我们先来看第一阶段:
预编译:
将源代码文件.c和相关的头文件.h等 预编译成一个.i文件
gcc -E hello.c -o hello.i (-E表示只预编译)
预编译阶段主要处理以”#”开始的预编译指令
主要处理规则如下:
①:将所有的”define”删除,展开所有的宏定义
②:处理所有条件预编译指令,比如“if”,“ifdef”,“ifndef”,“endif”,“else”
③:处理”#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,因为被包含的文件中可能还包含着其他文件。
④:删除所有的注释,比如“//”,“/**/”
⑤:添加行号和文件名标识,比如#2,“hello.c” 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够产生行号。
⑥:保留所有的#pragma编译器指令,因为编译器要使用它们。
再来看第二阶段:
编译:
将预编译后的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件
gcc -S hello.i -o hello.s (-S表示只编译)
gcc这个命令只是后台程序的包装,它会根据不同的参数要求去调用预编译编译程序cc1, 汇编器as,链接器ld。
编译过程一般可以分为6步,扫描并词法分析,语法分析,语义分析,源代码优化,中间代码生成,和目标代码生成和优化。
①:词法分析
根据类似于有限状态机的算法可以轻松地将源代码的字符序列分割成一系列的记号。词法分析产生的记号一般分为如下几类:关键字,标识符,字面量(包含数字,字符串等),和特殊符号(如加号,等号)。
②:语法分析
语法分析器将由词法分析产生的记号进行语法分析,从而产生语法树(Syntax Tree)。
语法分析仅仅完成了对表达式的语法层面的分析,它并不了解这个语句是否真正有意义。
③:语义分析
对表达式所包含的真正意义进行确定,隐式类型转换会在这里进行,若转换不了,则进行报错。
由语义分析器来完成,经过语义分析阶段后,整个语法树的表达式都被标记了类型,如果有些类型需要做隐式类型转换,语义分析程序会在语法树中插入相应的转换节点。
④:中间代码生成
中间代码使得编译器可以被分为前端和后端。编译器前端负责生产机器无关的中间代码,
编译器后端将中间代码转换成目标机器代码。对于一些跨平台的编译器而言,可以根据不同的平台使用同一个前端和针对不同机器平台的数个后端。
现在的编译器往往在源代码级别会有一个优化过程,一般,像(2+6)这个表达式会被优化掉,直接优化成8。
源代码优化器往往会将整个语法树转换成中间代码,它是语法树的顺序表示,其实它已经非常接近目标代码了。
我们拿最常见的中间代码(三地址码)作为例子,最基本的三地址码是这样的:
x = y op z
这个三地址码表示将变量y和z进行op操作后,赋值给x。
⑤:目标代码生成和优化
编译器后端主要包括代码生成器,和目标代码优化器。
首先代码生成器将中间代码转换成目标机器代码
最后目标代码优化器对上述的目标代码进行优化,比如选择合适的寻址方式,使用位移来代替乘法运算,删除多余的指令等。
最后看第三阶段:
汇编:
汇编器是将汇编代码转换成机器可执行的二进制指令。按照汇编指令和机器指令的对照表一一翻译就可以了。
上面的汇编过程我们可以调用汇编器as来完成
as hello.s -o hello.o
或者
gcc -c hello.s -o hello.o
编译整个过程:使用gcc命令从.c文件一步生成.o文件
gcc -c hello.c -o hello.o
链接阶段:
链接过程主要包括了地址和空间分配,符号决议,和重定位等这些步骤。
其中包括合并各个section,调整段的起始位置以及段大小,进行符号解析,符号重定位。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)