预处理-编译-汇编-链接
我们来编译一个hello world
程序。编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件。编译器就是将高级语言翻译成机器语言的一个工具。
#include
int main(int argc,const char* argv[])
{
printf("hello world!\n");
return
0;
}
1)预处理(Pre-processing)在该阶段,编译器将C源代码中的包含的头文件如stdio.h编译进来,用户可以使用gcc的选项”-E”进行查看。
用法:#gcc -E main.c -o main.i
作用:将main.c预处理输出main.i文件
[user:test] ls
main.c
[user:test] gcc -E main.c -o main.i
[user:test] ls
main.c main.i
2)编译阶段(Compiling)第二步进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
选项 -S
用法:[user]# gcc –S main.i –o main.s
作用:将预处理输出文件main.i汇编成main.s文件。
[user:test] ls
main.c main.i
[user:test] gcc -S main.i -o main.s
[user:test] ls
main.c main.i main.s
3)汇编阶段(Assembling)汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码.
选项 -c
用法:[user]# gcc –c main.s –o main.o
作用:将汇编输出文件main.s编译输出main.o文件。
[user:test] ls
main.c main.i main.s
[user:test] gcc -c main.s -o main.o
[user:test] ls
main.c main.i main.o main.s
4)链接阶段(Link)在成功编译之后,就进入了链接阶段。
无选项链接
用法:[user]# gcc main.o –o main.exe
作用:将编译输出文件main.o链接成最终可执行文件main.elf
[user:test] ls
main.c main.i main.o main.s
[user:test] gcc main.o -o main.elf
[user:test] ls
main.c main.elf* main.i main.o main.s
模块之间的通信有两种方式:一种是模块间的函数调用,另一种是模块间的变量访问。函数访问需知道目标函数的地址,变量访问也需要知道目标变量的地址,所以这两种方式都可以归结为一种方式,那就是模块间符号的引用。模块间依靠符号来通信类似于拼图版,定义符号的模块多出一块区域,引用该符号的模块刚好少了那一块区域,两者一拼接刚好完美组合。这个模块的拼接过程就是“链接”。
在链接中,函数和变量统称为符号(symbol),函数名或变量名就是符号名(symbol
name)。可以将符号看做是链接中的粘合剂,整个链接过程正是基于符号才能够正确完成。链接过程中很关键的一部分就是符号的管理,每一个目标文件都会有一个相应的符号表(symbol
table),这个表里面记录了目标文件中所用到的所有符号。每个定义的符号有一个对应的值,叫做符号值(symbol
value),对于变量和函数来说,符号值就是它们的地址。符号表中所有的符号分类:
1、定义在本目标文件的全局符号,可以被其他目标文件引用。
2、在本目标文件中引用的全局符号,却没有定义在本目标文件,这一般叫做外部符号(external
symbol),比如printf。
3、段名,这种符号往往由编译器产生,它的值就是该段的起始地址,比如“.text”、“.data”。
4、局部符号,这类符号只在编译单元内部可见。这些局部符号对于链接过程没有作用,链接器往往忽略它们。
5、行号信息,即目标文件指令与源代码中代码行的对应关系。
链接过程主要包括了地址和空间分配、符号决议和重定位。符号决议有时候也叫做符号绑定、名称绑定、名称决议,甚至还有叫做地址绑定、指令绑定,大体上它们的意思都一样,但从细节角度来区分,它们之间还存在一定区别,比如“决议”更倾向于静态链接,而“绑定”更倾向于动态链接,即它们所使用的范围不一样。
每个目标文件都可能定义一些符号,也可能引用到定义咋其他目标文件的符号。重定位的过程中,每个重定位的入口都是对一个符号的引用,那么当链接器须要对某个符号的引用重定位时,它就是要确定这个符号的目标地址。这时候链接器就会去查找由所有输入目标文件的符号表组成的全局符号表,找到相应的符号后进行重定位。
参考:
[1]《程序员的自我修养》
[2]http://blog.csdn.net/eastonwoo/article/details/8655243