我是靠谱客的博主 热情雪糕,最近开发中收集的这篇文章主要介绍《程序员自我修养》阅读笔记-动态链接,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1、动态链接的含义。动态链接就是将链接时的重定位推迟到加载时。相比于静态链接,动态链接的一个优点是可以节省内存。因为共享文件的代码可以共享。使用动态链接的时候,可执行文件和共享文件都会加载到内存。但是,如果很多可执行文件都使用了同一个共享文件的时候,共享文件的代码部分只需要装载一次,这样就达到了节省内存的目的。在这里,共享文件的数据部分在每个可执行文件中都要保存一份。所以,共享文件中跟自己的数据有关的代码就可能会变化,因为数据的地址不确定。一旦变化,就不能达到代码共享的目的了。所以,在这里,一般共享文件是地址无关代码。

2、地址无关代码。目的是指令部分在装载时不需要因为装载地址的改变而改变,所以应该把指令中那些需要修改的部分分离出来跟数据放在一起,这样指令部分就可以保持不变。这种技术就叫地址无关代码技术。下面分几种情况介绍如何实现地址无关:

(1)模块内部的数据访问。模块内部的指令和该指令要访问的数据之间的距离是固定的,但是又不能使用绝对地址,因为共享文件最后加载的地址不能确定,所以这时候该指令的地址加上一个差值就能访问到数据了。

(2)模块间的数据访问。其他模块的数据的地址到了装载时才能确定,所以此时的做法是在数据段里建立一个指向这些变量的数组(全局偏移表GOT),等到装载的时候由动态链接器来负责填写GOT中的数据。因为GOT是在自己这个模块的数据段里,所以这个GOT的地址在编译的时候就可以确定,所以GOT相应于指令的偏移是确定的。

(3)模块间的函数访问。模块间的函数访问跟模块间的数据访问差不多,也是将其他模块的函数地址放到GOT里。

(4)模块内部的函数访问。这个跟模块内部的数据访问可能看起来差不多。但是实际上不太一样。因为linux有一个问题叫做全局符号介入,比如说可执行文件需要共享文件A和B,但是A和B都都定义了函数f(),这时候不会报错,动态链接器会简单地用其中的一个函数覆盖另一个,也就是说这时候本模块中的函数不一定就是最后链接后使用的函数,这时候,编译器会把这种情况当做(3)来处理,即将这个函数加入GOT里。

3、延迟绑定。在装载的时候将所有的函数引用进行重定位是一种浪费,因为可能有些函数在整个运行过程中不会用到。ELF使用一种叫做PLT的方法来实现延迟绑定。有一个专门的段叫做.got.plt,所有引用的外部函数都放在这个里,而不是.got里。.got.plt的前三项是固定的,第一项是.dynamic段的地址,第二项是本模块的ID,第三项是_dl_runtime_resolve的地址,这个函数进进行符号解析和重定位工作的。.got.plt后面的项是外部函数的引用,每一项中存放了三条指令(假设外部函数是bar):

jmp *(bar@GOT)

push n

jump PLT0

其中PLT0的内容是:

push *(GOT+4)

jump *(GOT+8)

n指的是bar函数在重定位表中的下标,GOT+4的位置是本模块的地址,GOT+8的是_dl_runtime_resolve。初始时,bar@GOT存放的不是bar函数的真实地址,而是“push n”这条指令的地址,第一次调用bar函数时会进行绑定,_dl_runtime_resolve会把bar函数真实的地址写入bar@GOT,写入之后应该调用该函数。

4 、在动态链接中,操作系统在装载完可执行文件后,不会把控制权交给可执行文件,而是先将动态链接器加载到进程的地址空间,然后把控制权交给动态链接器,动态链接器进行动态链接工作,最后交给可执行文件。可执行文件中专门有个段".interp",指定使用的动态链接器。

5、.dynamic段包括:(1)动态链接符号表的地址;(2)动态链接字符串表的地址;(3)动态链接字符串表的大小;(4)依赖的共享文件:(5)动态链接重定位表的地址。

6、动态符号表:描述符号的导入导出关系,即定义了哪些符号,引用了哪些符号。同时有动态符号字符串表。对于每个符号项的结构跟目标文件中对静态符号表的描述是一样的。

7、动态链接重定位表:包括两个段,.rel.dyn,这个用于对外部数据引用的修正,.rel.plt,这个用于对外部函数引用的修正,它修正的函数在.got.plt中。在这里,函数的重定位类型一般是R_386_JUMP_SLOT,这个只需要找到相应函数的地址,直接填进去就行。R_386_GLOB_DAT跟R_386_JUMP_SLOT类似,只需要找到地址填进去就行。还有一种是R_386_RELATIVE,这个修正的是数据中对绝对地址的引用,比如下面这个p:

static int a;

static int* p=&a;

编译时,只知道a相对起始位置的偏移,在装载时,共享对象装载的地址确定后,a的最终地址才能确定,这时候才能确定p指向的地址。

8、动态链接时堆栈的初始化:在动态链接器开始工作的时候,需要知道可执行文件和本进程的一些信息,比如可执行文件程序头表的入口数量啊,可执行文件入口地址啊等。

9、动态链接的步骤:(1)动态链接器自举;(2)装载共享对象;(3)重定位和初始化

最后

以上就是热情雪糕为你收集整理的《程序员自我修养》阅读笔记-动态链接的全部内容,希望文章能够帮你解决《程序员自我修养》阅读笔记-动态链接所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(40)

评论列表共有 0 条评论

立即
投稿
返回
顶部