动态链接

为什么要动态链接?
如果有多个程序同时使用某一静态链接库,必须将其分别连接进来,占用重复的存储空间。
静态链接不利于模块升级。任何模块更新,整个程序都要重新链接。
动态链接的基本思想就是:不对那些组成程序的目标文件进行链接,把链接这个过程推迟到运行时再进行。(其实是把地址重定位工作推迟进行,由动态链接器完成。)
动态链接除了可以弥补静态库的缺点之外,还可以加强程序的兼容性。一个程序在不同的平台运行时可以动态地链接到由操作系统提供的动态链接库(使用相同的接口),这些动态链接库相当于在程序和操作系统之间增加了一个中间层,从而消除了程序对不同平台之间依赖的差异性。
解决共享对象指令中对绝对地址的重定位问题。希望程序模块中共享的指令部分在装载时不需要因为装载地址的改变而改变,所以实现的基本想法就是把指令中那些需要被修改的部分分离出来,根数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以在每个进程中拥有一个副本。这种方案就是目前被称为地址无关代码(PIC, Position-Independent Code)的技术。
指令中那些需要被修改的部分包括对外部模块的数据访问和函数调用。MSVC编译器提供了__declspec(dllimport)编译器扩展来表示一个符号是模块内部的还是模块外部的。

 

延迟绑定(Lazy Binding)
在一个程序运行过程中,可能很多函数在程序执行完时都不会被用到,比如一些错误处理函数或者是一些用户很少用到的功能模块等,如果一开始就把所有函数都连接好实际上是一种浪费。所以ELF采用了一种叫做延迟绑定的做法,基本的思想就是当函数第一次被用到时才进行绑定(符号查找、重定位等),如果没有用到则不进行绑定。所以程序开始执行时,模块间的函数都没有进行绑定,而是需要用到时才由动态链接器来负责绑定。这样的做法可以大大加快程序的启动速度,特别有利于一些有大量函数引用和大量模块的程序。

 

动态链接基本上分3步:
先是启动动态链接器本身,然后装在所有需要的共享对象,最后是重定位和初始化。
链接器列出可执行文件所需要的所有共享对象,并将这些共享对象的名字放入到一个装载集合中。然后链接器开始从集合里取一个所需要的共享对象的名字,找到相应的文件后打开该文件,读取相应的ELF文件头和“.dynamic”段,然后将它相应的代码段和数据段映射到进程空间中。如果这个ELF共享对象还依赖于其他共享对象,那么将所依赖的共享对象的名字放到装载集合中。如此循环直到所有依赖的共享对象都被装载近来为止。
当一个新的共享对象被装载进来的时候,他的符号表会被合并到全局符号表中,所以当所有的共享对象都被装载进来的时候,全局符号表里边将包含进程中夺油的动态链接所需要的符号。
当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。为此可以在全局符号前使用“static”关键字。这样一来,编译器采用模块内部调用指令,确保符号具备其它模块覆盖,同时还可以加快函数的调用速度。

 

动态链接器实现
系统内核在装载完ELF可执行文件以后就返回到用户空间,将控制权交给程序的入口。对于不同链接形式的ELF可执行文件,这个程序的入口是有区别的。对于静态链接的可执行文件来说,程序的入口就是ELF文件头里面的e_entry指定的入口;对于动态链接的可执行文件来说,内核会分析它的动态链接器地址(在”.interp”段),将动态链接器映射至进程地址空间,然后把控制权交给动态链接器。

 

显式运行时链接
支持动态链接的系统往往都支持一种更加灵活的模块加载方式,叫做显示运行时链接(Explicit Run-time Linking),有时候也叫做运行时加载。也就是让程序自己在运行时控制家在制定的模块,并且可以在不需要该模块时将其卸载。这种共享对象往往被叫做动态装载库(Dynamic Loading Library),其实本质上它跟一般的共享对象没什么区别,只是程序开发者使用它的角度不同。比如著名的ActiveX技术就是基于这种运行时加载机制实现的。
如果被加载的模块之间有依赖关系,比如模块A依赖于模块B,那么程序员须手工家在被依赖的模块,比如先加载B,再加载A。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值