深入理解计算机系统—链接——静态链接

程序执行前经历了啥

当我们编写了一个程序,你想法设法让他运行,在你的编辑器编译运行。
编译是啥,运行前经历了啥。
在这里插入图片描述

用一张图就是这样表示的。

预处理器cpp:

gcc -E a.c -o a.i

假设看一个程序没有include:
在这里插入图片描述
在这里插入图片描述

加入加了include
在这里插入图片描述
在这里插入图片描述

所以预处理器cpp就是对这个程序进行预处理,可以展开头文件或者宏替换还有去掉注释,有时候头文件里面有条件编译,
也可以翻译成一个.i文件。
编译器cc1:
编译阶段是检查语法,生成汇编:gcc -S a.i -o a.s

将预处理过的.i文件搞成了汇编程序,至于里面进行了怎样的复杂过程,怎样优化,我也不是很清楚,有时间研究,只要知道它是用来翻译成汇编程序就行了。
在这里插入图片描述
注意上面的两个文件都是ASCII文件

汇编器as:
汇编代码转换机器码 gcc -c a.s -o a.o

这个汇编器之后就是一个可重定位的目标程序了,等会儿看看啥样子

链接器:
链接过程将多个目标文以及所需的库文件链接成最终的可执行文件
gcc a.o -o a

今天就重点来初识一下链接器所做的链接过程


静态链接


静态链接就是根据一系列可重定位的目标文件和命令行的参数,来生成一个已经完全链接的,而且可以加载运行的可执行文件的过程

不知道什么意思没有关系,我们一步一步来梳理就行。

这个静态链接由一个静态链接器完成,主要执行两个过程。
一个是符号解析,一个是重定位

又懵了,什么是符号,解析他干嘛,重定位又是啥?

还是一步一步来
首先要知道啥时符号吧

符号:不说了,上图吧。

在这里插入图片描述
似乎明白了,不就是一些符号啥的嘛,好像没啥了不起的。先有个印象

符号解析这个过程先不说,先来看看那个我们之前所说的可重定位的目标文件
它是经汇编程序翻译过来的二进制文件
首先要知道Linux系统使用可执行可链接格式,即ELF
可重定位文件分成了一节节的内容,即符合ELF格式的一种二进制文件。

先来上一份代码吧
int count = 0;
int boy = 1;
int k;
void main(){
        static int p = 1;
        count += p;
}

在这里插入图片描述
我们使用readelf -S b.o查看一下内容
我们需要重点观察.bss,.data,.text.symtab
我们通过图片可以知道它是一节节的组成的。

在这里插入图片描述
我们重点在于符号表.symtab
使用readelf -s b.o(这里是小s)可以查看符号表的内容
在这里插入图片描述

.symtab:
就是通过readelf -s> 命令弄出来的,上面这张图,value对应的在对应节的偏移量,Size大小,Type类型,Bind是本地还是全局还是弱符号,Ndx对应于ELF文件(可重定位目标文件)的哪一节。
.bss:
对应上面图片Ndx项的4,用于存放未被初始化的全局变量和静态C变量,但是有个情况,如果你指定了一个为初值为0的变量,就像上面的count,它是一个初始化为0的全局变量,但是它被分配在了.bss字段。还需要注意的就是,如果一个静态变量被分配空间的话,它有个特殊的,它只适合本地模块,也就是说只需要在本地保证唯一就行了,就算其他模块有同名的强符号或者弱符号,本地模块只会引用本地的这个静态变量,除非你指定了用外部模块的。对于bss更细微的定义就是,它一般只保存未初始化的静态变量和初始化位0的全局变量,静态变量,而未初始化的全局变量暂时在符号表中定义为COMMON,就像上面的k一样。
.data:
对应上面图片的3,用于存放已经初始化的全局变量和静态C变量,但是初始值为0的没有分配在这个地方,和上面一样。
.text:
存放是编译后的机器代码

我们了解了符号和符号表,就可以来进行符号解析了


符号解析


就是将某个模块的符号引用与这个符号在另一个模块的定义相关联


在这里插入图片描述
写了两个程序
我们把他们编译成可重定位的文件

有三种集合,链接器进行符号解析时,需要创建维护这三个集合。

  • E:合并在一起的所有目标文件(还未重定位)
  • U:没有解析的符号(定义符号和引用符号还没有被建立联系)
  • D:定义符号的集合

当链接器接收到了一个可重定位文件
看看它是不是库文件
库文件:将所有没有解析的符号(U)与库文件匹配,如果匹配上了,就将匹配的模块放入E中,然后将这个U中的符号放到D中,知道U和D不在变化,库文件剩下的全不要了。
非库文件:就是可重定位的目标文件,先把它放到E中,分别将未解析的符号和定义的符号放到U和D中,如果D中有未解析符号的定义,那么就可以将U中的那个未解析的符号和D中定义的相关联。
在这里插入图片描述
在这里插入图片描述

比如如果链接上面的两个可重定位文件
在这里插入图片描述
虽然说符号建立的关联,但是仅仅建立了管理而已,我们根本不知道符号对应地址在哪里。
在这里插入图片描述

你看这些全是0,根本不知道对应的符号地址在哪里,你要它怎么去执行命令,这不是为难它吗?

所以我们还需要一个过程,就是重定位


重定位


重定位就是为每个符号分配对应的地址

  • 合并相同类型的节,然后链接器将运行时的内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。
  • 然后对定义符号进行重定位
  • 对引用符号进行重定位,那么每个被引用符号的位置,都为改成对应分配的地址
    你看将两个可重定位文件重定位后:
    在这里插入图片描述

所有的地址都变了。
相对寻址
对于一个重定位条目,里面保存了一个数据结构的信息
第一就是给定重定位偏移量offset,还有就是修正值addend,还有重定位寻址类型(相对,绝对)
还有就是重定位的符号r.symbol
计算机会给我们的信息就是每个节的信息(比如.text代码节,记作ADDR(s)),然后还有重定位符号地址(ADDR(r.symbol))
我们先根据重定位偏移量找到我们需要重定位的地址address = ADDR(s)+offset
然后我们要根据重定位符号地址和修正值,来更新重定位的引用地址
则ADDR(r.symbol) +addent-address就填充进我们要重定位的引用地址值

绝对寻址:
绝对寻址比较粗暴简单,直接对符号地址加以修正 ADDR(r.symbol+addend)即可

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小满锅lock

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值