编译过程
在之前的加载过程详解里我们简单了解了elf文件的格式,编译就是产出目标文件的过程
这个过程在c或者c++的编译流程中大致被分为了三个步骤:预处理,汇编,编译
as作为gcc常用的编译器存在的时间其实要比cl1这种汇编器更久远,编译的过程是将汇编语言转换为硬件电路所对应的机械语言也就是代表了高低电平的串
c语言的产生是为了避免汇编语言的直接编写,他是通过一种更好理解的文本格式并通过一个可执行程序将这种文本转换为汇编之后的操作其实就跟写汇编然后通过nsam编译是一样的了只是选择的编译器叫as
预处理的过程可以看做是对c/c++文本格式的一种补充就像当人门发现单纯的硬编码已经无法满足需求时就提供了汇编到机械码的这一映射通过编译器作为中间层建立映射的联系,然后人们又觉得汇编不够方便就搞出了所谓的高级语言例如我们说的c从c到汇编和从汇编到机械码的过程不是很像么,通过增加中间层来建立两个互不相干的事物之间的映射,那么预处理可以理解为就是从宏定义格式到c语言文本格式的映射只不过这个映射只是一小部分扩展而已。就像c++在一开始就是通过一个特制的预处理程序来做到从当时的c++到c文本的映射来实现这种带类的c,这部分有兴趣可以看一下《c++语言的设计和演化》这本书翻译的不怎么样但是还是可以看的。
预处理
预处理主要作的事情如下:
-
去注释
这个很好理解因为注释是给人看的,机器并不需要,而且汇编的注释和c的注释格式也不一样,而且汇编过程也不借助于注释那么在第一步就直接全部去掉就ok了
-
宏替换
宏替换就是将字符进行替换,这部在汇编之前因为在汇编过程中符号和c文本格式就需要被确定下来了,所以在预处理阶段就需要将宏定义这种符号替换动作做完
-
条件编译
这个就是借助于预处理阶段的条件判断来决定是否保留部分的符号替换
-
头文件展开
这是最重要的一个功能,c/c++就是借助头文件来实现多个单元之间的符号定义关系,在汇编中会通过.globl来描述一个符号说这个符号在编译之后依旧要保留,说连接器还需要这个符号,而一个单元在编译时会引入自己的头文件也就是函数定义,这部分里大多数函数默认是extern的等同于加了.globl,c++的public也是这个意思,这样编译出来来自头文件所定义的符号就不会丢失其他单元就可以通过引入跟当前单元相同的头文件保留相同的符号来链接这个单元了
汇编
汇编过程就比较复杂了,他的目的是将c文本转换为汇编文本,这个过程包括了常见的,词法分析、语法分析、语义分析、中间代码生成、代码优化、转换汇编代码这几个步骤,这部分就不详细展开了主要是一些编译原理相关的东西。
编译
从编译过程开始我们就脱离了c/c++语言的制约,从这里开始马上就会变得硬件和系统相关而语言无关了。
编译是将汇编转换为二进制文件的过程,在Linux下是生成可重定位的elf格式文件,这个文件里除了指令对应的二进制码和数据对应的二进制码以外还有一系列称为header的数据结构的二进制码,这些数据结构是链接过程所需要的。
我们**加载过程详解的可重定位的elf文件格式简介**这一章介绍的就是二进制格式目标文件里的一系列重要结构体
后面一章我们会详细的介绍一下链接过程,大家会看到之前编译过程生成的sectionheadertable在加载过程中毫无用处的而在链接过程才会展现他真正的作用