通常情况下,我们编写的.c或.cpp文件称为源文件,这一类文件是不能够在计算机上运行的,因为计算机只认识机器指令(及01代码),而我们通常使用的C语言、C++或者是Java都属于高级语言(高级语言的引入很大程度提高了程序的易读性、开发效率和可移植性),因此,源文件要想执行,就必须将源文件经过处理转换成机器指令,这就引入了我们的编译链接原理。
1.编译链接原理
首先从我们的编译指令(Linux环境下的编译)开始逐步深入:
1.1预编译
预编译指令:gcc -E main.c -o main.i
用tail -n 20 main.i查看main.i文件的最后20行,结果如下
综合以上信息,我们可以得出预编译阶段做了以下事情:
a.删除#define,进行文本替换
b.处理预编译指令(#if #dendif #elif)
c.递归展开#include
d.删除注释
e.添加行号(便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能欧够显示行号 )和文件标识
f.保留所用的#program编译器指令,因为编译器需要使用它们
1.2编译
编译指令:gcc -S main.c -o main.s
查看mian.s的内容如下:
综合以上信息,我们可以得出编译阶段做了以下事情:
a.词法分析(产生一堆单词)
b.语法分析(生成语法树)
c.语义分析(带变量的语法树)
d.代码优化
e.生成汇编指令
1.3汇编
汇编指令:gcc -c main.s -o main.o
使用readelf -s main.o查看符号表
综合以上信息,我们可以得出汇编阶段做了以下事情:
a.将汇编指令转换为二进制指令
b.形成符号表(将编译阶段汇总的符号形成一张符号表)
1.4链接
链接的过程如下图所示:
汇编指令:gcc -o main main.o
综合以上信息,我们可以得出链接阶段做了以下事情:
a.合并段和符号表
b.符号解析
c.分配地址和空间
4.符号重定位(text段)
将汇编阶段生成的符号表进行合并。可重定位目标文件之中用来存放变量和其入口地址的符号表,重定位是指在链接阶段连接器会查找符号表,当发现某个符号表存在没有决议的内存地址时,连接器就会查找所有符号表,一直发现这些这些尚未决议的符号变量的内存地址写进符号表。直达所有的符号变量都能够找到合法的内存地址时,链接阶段重定位完成。否则会出现链接错误。
运行阶段在后边的地址空间介绍。