编译链接过程原理

首先我们引入4G虚拟地址空间,来进一步阐述编译链接原理。

编译链接过程原理

保留区:举个例,我们经常使用NULL初始化指针变量,NULL的地址为0x00,因为NULL == 0x0,将其指针指向0x0这个地址时,因为0x0这个地址属于保留区,没有访问权限的。通常是128M,但也不是固定的。

.text:代码段,这个区域存储的是代码中的指令。指令:一份代码是由数据和指令构成的,除数据外剩下的都叫做指令,另外,局部变量也属于指令,但是局部变量存储在栈上,代码运行时才在栈中预留好的区域中开辟;可读可执行。

.data:数据段,存放初始化且初始化不为0的数据;可读可写。

.bss:数据段,存放未初始化的数据,或者初始化为0的数据,可读可写;这个段其实在可执行文件中不占空间,(.bss段)在段表中保存着,因为它记录的都是一些未初始化的值要不就是初始化了但是为零的值,我们可以只记录符号和符号所要占用的大小,在程序运行时,才进行开辟并将其空间全部清零。

heap:用户动态申请内存的地方,例如c中用malloc申请,free释放,c++中用new申请,delete释放;不过在堆上申请空间都需要手动去释放,防止内存泄露。

Stack:所有局部变量存储在这里,且函数的运行也需要栈的开辟,不过空间释放由系统完成。

栈与堆之间的区域:栈和堆都是动态的扩展和缩小的,中间有一块区域我们进程的共享库,即lib.so,程序运行时需要动态链接的。

内核与栈之间的区域:存放的是命令行参数和环境变量;

 

编译链接过程
1.预编译(生成.i文件) gcc -E main.c -o main.i

内容:(1)去掉注释;(2)处理以#开头的预处理文件等

2、编译(生成 .s 文件,即汇编文件)gcc -S main.i -o main.s

内容: 

  1. 生成符号(符号:所有的数据都会生成符号,指令只有函数才会生成符号。符号存放在.bss段中,因为.bss段中存放的都是未初始化的或者初始化为0的数据,只需要记录其符号即可,不占内存;强符号:初始化了的非静态数据;弱符号:没有初始化的非静态数据) ;弱符号不往数据段存储(指的是不参与.bss段对数据个数的计算),只会产生一个/*COM*标志,也就是说弱符号会被强符号替换;

(2) 生成汇编指令;

3、汇编(生成 .o 文件,即二进制可重定位文件)gcc -c main.s -o main.o

内容:将汇编语言翻译成机器语言(二进制)代码。

4.链接(生成可执行的程序)gcc -o main main.s

内容:将有关的目标文件彼此链接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义链接起来,是的所有的这些文件成为一个能够让操作系统装入执行的统一整体,可分为动态链接和静态链接

1、 合并段表:将多个二进制可重定位文件中的段信息整合放到一个文件中

2、 调整段偏移:段表经过合并之后大小发生了变化,所以地址需要适当的偏移

3、 合并符号表:将多个二进制可重定位文件中的符号整合到一个文件中

4、 完成符号的重定位:连接器把每个符号定义与一个虚拟地址联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储位置,从而重定位这些节。

 

5.运行:按照不同段的属性相同的不同的段划分到同一页装入虚拟地址空间