这一部分一直很好奇,不过一直也没仔细记。这次记一下。
源代码 --(预处理、编译、优化)–> 汇编代码 --(汇编)–> 目标文件 --(链接)–> 可执行文件
预处理
读取源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。
主要包括:宏定义,条件编译指令,头文件包含指令,特殊符号(如LINE,FILE)。
通常使用以下命令来进行预处理:
$ gcc -E hello.c -o hello.i
参数-E表示只进行预处理 或者也可以使用以下指令完成预处理过程
$ cpp hello.c > hello.i
编译
编译阶段就是把预处理后的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码。
$ gcc –S hello.i –o hello.s
或者
$ /usr/lib/gcc/i486-linux-gnu/4.4/cc1 hello.c
优化
优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。
另一部分优化则主要针对目标代码的生成而进行的,同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高。
汇编
汇编过程就是把汇编语言翻译成机器码。
$ gcc –c hello.c –o hello.o
或者
$ as hello.s –o hello.co
链接
由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
通过调用链接器ld来链接程序运行需要的一大堆目标文件,以及所依赖的其它库文件,最后生成可执行文件。
ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o (省略了文件的路径名)。
根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:
- 静态链接库
库中函数将从其所在的静态链接库中被拷贝到最终的可执行程序中。静态链接库实际上是一个目标文件的合集,其中的每个文件含有库中的一个或者一组相关函数的代码。 - 动态链接库
函数代码放在动态链接库或者共享对象的某个目标文件中。而在生成的程序中只加入一些描述信息。程序执行时,再从系统中把相应的动态库加载到内存中。