目录
编译步骤:
-
预处理
-
编译
- 汇编
- 链接
-
预处理(cpp)
预处理是读取C源程序,对其中的伪指令(以#开头的指令也就是宏)和特殊符号进行“替代”处理,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,仍然是C文件,但内容有所不同
- 将所有的#define删除,并且展开所有的宏定义
- 处理所有的条件预编译指令,比如#if #ifdef #elif #else #endif等
- 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。
- 删除所有注释
- 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号
- 保留所有 的#progma编译器指令,因为编译器需要使用它们
通常使用 gcc -E 文件名 -o 新文件名(.i)
或者使用 cpp 文件名 > 新文件名(.i)
-
编译
编译所要做的工作是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。
通常使用 gcc -S 文件名(.i)> 新文件名(.s)
使用不同的编译程序,编译出的汇编代码是不同的。Pc编译器会将代码编译成为x86的汇编代码。
-
汇编(汇编器nasm)
汇编过程实际上指吧汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等价的目标的机器语言代码。目标文件有段组成。通常一个目标文件至少有两段:
- 代码段(文本段):该段同所包含的主要是程序的指令。该段一般是刻度和可执行的,但是一般却不可写;
- 数据段:主要存放程序中要用到的各种常量、全局变量、静态数据。一般数据段都是可读、可写、可执行的;
通常使用:gcc -c 文件名 -o 新文件名(.o)
链接(ld)
汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,某个源文件中的函数可能引有了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经过链接程序的处理方能得以解决。链接程序的主要工作就是将有关的目标文件彼此相连接,即,将在一个文件中引用的符号同该符号在另一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体,结局认识可执行程序。根据开发人员指定的库函数的链接方式的不同,链接处理可分为两种:
- 静态链接
- 动态链接
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够最终的可执行文件比较短小,并且当共享对象被多个而进程使用是能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用景天链接要优越。在某些情况下动态链接可能带来一些性能上的损害。
五、ELF可执行文件
Linux/unix的可执行文件以及动态库都是以ELF形式存在的。在linux下,可以使用的readelf命令查看ELF文件,关于加载过程所需要的信息都在ELF文件头里面,可以用使用readelf filename -e来查看ELF文件所有的头。
函数的文本段和数据段的地址是在链接阶段决定的。