C++----编译链接原理

编译链接可分为四个阶段

1、预编译(生成.i文件)
(1).将所有的“#define"删除,并且展开所有的宏定义;
(2)处理所有条件预编译指令,比如:“#if","#ifend"
(3)处理"#include"预编译指令,将包含的文件插入到该预编译指令的位置,这个过程是** 递归的**,也就是说被包含的文件可能还包含其他文件
(4)删除所有的注释
(5)添加行号和文件名标识,以便于编译时编译器产生调用的行号信息及用于编译时产生 编译错误或警告时能够显示行号
(6)保留所有的#pragma编译器指令,因为编译器要使用它们

2、编译(生成*.s文件)
把预处理完的文件进行一系列的词法分析语法分析语义分析优化后生成相对应的汇编代码文件。
生成符号
3、汇编(生成*.o文件,也叫目标文件)
汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。
生成符号表和各个段

4、链接(生成*.exe文件,也叫可执行文件)
(1)合成段表
(2)调整段偏移
(3)合成符号表
(4)符号解析;
(5)符号重定位;

二、具体解析
1、什么是符号表?
2、什么是符号解析和符号重定位?

存放在.data和.bss段的变量都要有一个名称来标识这个变量,这个名称称为符号
在每个文件符号引用(引用外部符号)的地方找到符号的定义。这就是符号解析
符号重定向就是对.o文件中.text段指令中的无效地址给出具体的虚拟地址或者相对位移偏移量。

首先看这样一段代码。两个源文件main.c和sum.c
在这里插入图片描述

main.c本文件中(不包含引用的外部变量)每个变量具体存放的位置
.data: gdata1,gdata4,data1;
.bss: gdata2,gdata3,gdata5,gdata6,data2,data3;
int data4 = 40;
int data5 = 0;
int data6;生成的是指令;

首先看下.o文件简单的文件格式布局。

在这里插入图片描述

上面就是.o文件也就是目标文件的简单格式布局。.bss段去哪里了呢?
下面我们通过查看目标文件的段布局可以看到目标文件中有.bss这个段。为什么我上面画的图却没有呢?

在这里插入图片描述

大家可以自己根据段的起始偏移和段大小计算一下可以得到.bss不占用目标文件的空间。既然.bss段没有占目标文件的空间,编译器怎么能打印出.bss段的大小呢。当然细心的同学们还会发现一个问题:.bss段的大小并不是我们想象中的24个字节,而是20个字节,少了4个字节的数据,到底少了哪些数据呢?咱们接着往下看。

在这里插入图片描述
这是ELF Header中的信息,大家可以看到ELF Header中有一个叫做section headers的段,这个段保存了目标文件中每个段的详细信息,包括段的大小,起始偏移等等信息。编译器只要访问这个段就可以知道每个段的详细信息。.bss虽然不占空间但是.bss段的详细信息都被保存在section headers中了

同时大家可以看到这个Entry point address的值是0,还没有给出具体的函数入口地址,

存放在.data和.bss段的变量都要有一个名称来标识这个变量,这个名称称为符号
下面我们就一看看符号表的内容。顺便解决.bss段少4个字节这个问题。

在这里插入图片描述

可以看到gdata3并不是存放在.bss段。这个数据存在COM这个块中了。为什么要这样做呢。这个和C语言中的强弱符号有关系。
强符号:全局的已初始化的符号
弱符号:全局的未初始化的符号
强弱符号的选取规则:
1.两个强符号 编译报错
2.一个强符号 一个弱符号 选择强符号
3.两个弱符号 根据不同编译器处理方式不同

大家都知道一个项目可能有多个源文件。编译阶段都是每个文件单独编译的。可能在其他文件中存在强符号,所以没办法在编译期间确定具体的符号。因此将本文件的弱符号存放在COM块,而不是.bss段。
对于引用的外部符号大家可以看到符号表中是UND标志。也就是说这个符号没有确定具体定义的地方。

接着我们来看看链接完成后的可执行文件
在这里插入图片描述
可以看到链接后,对每个符号给出了具体的虚拟地址。gdata3存放在.bss段了。链接完了以后就可以确定gdata3这个弱符号的符号选择了。对于.o文件中引用的外部符号也确定了具体的定义位置,
把这两个外部符号放到对应的段中。
符号解析和符号重定位就发生在这个过程中。

什么是符号解析:
在每个文件符号引用(引用外部符号)的地方找到符号的定义。这就是符号解析
什么是符号的重定向:
先看下链接前生成的汇编代码。

在这里插入图片描述

这两处分别是 0000 0000代表链接前编译器给gdata给出的地址,FF FF FF FC表示链接前编译器给Sum函数给出的函数的入口地址。这两个地址都是无效的地址。再看看链接后这两条地址。

在这里插入图片描述

这两个地址变成gdata的具体虚拟地址和Sum函数的相对位移偏移量。而符号重定向就是对.o文件中.text段指令中的无效地址给出具体的虚拟地址或者相对位移偏移量。

最后我们来看看可执行文件的头部信息。
在这里插入图片描述
大家可以看到这个时候这个程序的入口地址已经给定080480A4这个就是main函数的入口地址。现在程序就可以运行了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值