动态链接

1、为什么要动态链接?

静态链接虽然速度快,但是对于多进程操作系统来说,会浪费大量的内存空间;如果遇到程序更新,需要整个程序下载,更新不方便。基于以上两点,我们引入了动态链接。

 

2、什么是动态链接以及它的基本实现?

动态链接就是不对那些组成程序的目标文件进行链接,而是在程序运行时才进行链接。动态链接的基本思想就是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是象静态链接那样所有的程序模块都链接成一个单独的可执行文件。

在Linux系统中,ELF动态链接文件被称为动态共享对象(DSO,Dynamic Shared Objects),简称共享对象,一般以“so”作为拓展名;在Windows中,动态链接文件被称为动态链接库(DLL,Dynamic Linking Library),通常以“dll”作为拓展名。

 

3、可执行文件和共享对象的区别

在静态链接时,整个程序最终只有一个可执行文件,它是一个不可分割的整体;而在动态链接下,一个程序会被分割成若干个文件,即可执行文件和程序所依赖的共享对象。

在动态链接中,对于可执行文件,它是第一个被加载的文件,所以它可以选择一个固定的地址,所以它可以确定自己在进程虚拟空间的起始位置;而对于共享对象来说,它在编译时不能假设自己在进程虚拟地址空间中的位置。

 

4、装载时重定位

在链接时,对所有绝对地址的引用不作重定位,而是把重定位放在装载的时候。一旦模块装载地址确定,即目标地址确定,那么系统这个时候就对程序中所有的绝对地址引用进行重定位。

我们前面在链接时提过的重定位叫做链接时重定位(Linking Time Relocation),而现在这种情况叫做装载时重定位(Load Time Relocation),在Windows中,这种装载时重定位的方式叫做基址重置(Rebasing)

对于动态链接来说,它的可修改数据部分在不同的进程都有一个副本,可以采用装载时重定位来解决;但是对于指令来说,它在重定位时也是需要修改的,那么就是说每个进程也得有个副本了,这样就和动态链接节约内存空间的初衷违背了,所以加载时重定位行不通,这个时候我们就引入了地址无关码!

 

5、什么是地址无关代码(Position-independent Code,PIC)?

所谓地址无关代码技术就是将指令中那些需要修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以在每个进程中拥有一个副本

 

6、什么是全局偏移表(Global Offset Table,GOT)?

我们将共享对象模块的地址引用分成以下4类,其中模块内部的非常好确认它的地址。

模块外部的间接访问是需要装载时才能确定的。针对数据访问,对于ELF来说,在数据段里面建立一个指向这些变量的指针数组,也称为全局偏移表(GOT),当代码需要引用该全局变量时,可以通过GOT中相对应的项间接引用,机制如下图。

7、什么是延迟绑定?

由于数据访问都要进行复杂的GOT定位和间接寻址,然后链接工作在运行时完成,即程序开始执行时,动态链接器都要进行一次链接工作,所以动态链接比静态链接要慢。

为了解决一开始就把所有函数都链接好实际是一种浪费的问题,我们采用了一种延迟绑定的做法。所谓延迟绑定就是函数在第一次被使用时才进行绑定(符号查找、重定位等)。这样做可以大大加快程序的启动速度。

 

8、动态链接相关结构有哪些?

<1> “.interp”段

在动态链接的ELF可执行文件中,有一个专门的段叫做“.interp”段(“.interp”是”interpreter”的缩写)。输入:objdump -s a.out就能查看,如下所示。

<2>“.dynamic”段

该段里面保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象、动态链接符号表的位置、动态链接符号表的位置、共享对象初始化代码的地址等。输入:readelf -d Lib.so可以查看,如下:

<3>动态符号表

为了表示动态链接各个模块之间的符号导出导入关系,ELF专门有一个叫做动态符号表(Dynamic Symbol Table)的段用来保存这些信息,这个段名通常叫做“.dynsym”。这个表只保存与动态链接相关的符号,而“.symtab”保存所有的符号。输入readelf -s Lib.so可以查看,如下:

<4>动态链接重定位表

共享对象需要重定位的主要原因是导入符号的存在。输入readelf -r Lib.so可以查看,如下:

9、动态链接的步骤

<1>动态链接器自举(Bootstrap)

动态链接器以一段不用到全局变量和静态变量以及不使用任何系统库、运行库的代码的入口为入口地址,当操作系统将进程控制权交给动态链接器时,动态链接器的自举代码即开始执行。自举代码首先会找到它自己的GOT。而GOT的第一个入口保存的即是“.dynamic”段的偏移地址,由此找到了动态链接器本身的“.dynamic”段。通过“.dynamic”中的信息,自举代码便可以获得链接器本身的重定位表和符号表等,从而得到动态链接器本身的重定位入口,先将它们重定位。从这一步开始,动态链接器代码中才可以使用自己的全局变量和静态变量。

<2>装载所需共享对象

完成自举之后,动态链接器将可执行文件和链接器本身的符号表都合并成一个符号表当中,我们称之为全局符号表。然后链接器以“.dynamic”段中的DT_NEEDED为入口,该值指出的就是可执行文件(或共享对象)所依赖的共享对象。

<3>重定位和初始化

 

10、显式运行时链接

所谓显式运行时链接(ExplicitRun-time Linking)(有时叫做运行时加载)就是让程序自己在运行时控制加载指定的模块,并且在不需要该模块时将其卸载。运行时装载的共享对象叫做动态装载库(DLL)。

共享对象是由动态链接器在程序启动之前负责装载和链接的,这一系列步骤都由动态链接器自动完成,对于程序本身来说是透明的;而动态库的装载则是一系列由动态链接器提供的API,具体有下面4个函数:dlopen(打动态库)、dlsym(查找符号)、dlerror(错误处理)、dlclose(关闭动态库)。

 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值