7.1为什么要动态链接
用动态链接的原因是因为静态链接有这样那样的问题。对比二者我们可以总结出静态链接的俩大缺点,和动态链接的两大优点:
(1)静态链接浪费内存和磁盘空间
静态链接会把所有链接到的库装载入内存,而却这些库装载入内存后只能给进程自己使用,不能共享给别的程序进程使用。用书中的例子说,1个程序需要用100MB的内存,那么100个程序就要用100MB,对空间的浪费很严重。
(2)程序的更新和升级
书中的原话就是,程序中有任何模块更新,整个程序都需要更新重新链接,十分麻烦。
因此与此对立的就是动态链接库的优点:
(1)当然是节省空间,同样的库只需存在一份副本即可
(2)便于升级,便于编写插件,便于跨平台使用(书上有例子)
7.2简单的动态链接的例子
我觉得书上的图7-3就可以说明一个基本过程,不过书中让我们需要知道的是为什么链接器知道foobar函数是动态链接函数(即地址在装载的时候才确定)。这是因为lib.so(动态共享对象)里面有完整符号信息,把lib.so作为链接之一,就会让链接器知道这是一个对动态对象的引用。
动态链接程序运行时地址空间的分布。
这个怎么说呢?书中的意思,就是让大家知道程序运行时这些库已经被加载到内存了。。。
另外,还有个值得注意的地方就是,书中在本章的最后做了个示例,即共享对象在被程序加载之前,其装载绝对地址是0x00000000。从这点就可以推断共享对象的最终装载地址在编译时时是不确定的。。
7.3地址无关代码
7.3.1固定装载地址的困扰
哦。。。。他在这里就是说实现动态链接库需要面对的问题。7.3.1中指出的问题就是不同模块不能有同样的装载地址。一旦冲突了,就只能用其中之一,另一个必须永远放弃。这就是传统的静态共享库,这种方式还会导致升级很不方便。库本身不能有太大的变化。
7.3.2装载时重定位
gcc编译共享库时,如果不加上-fPIC,则会采用动态共享库的方式加载库。但这种方式会产生另外一个问题,就是不能被多个进程共享。以为装载时重定位需要修改指令(call的地址不同)。
7.3.3地址无关代码
首先先按书上说的分析模块中各种类型的地址引用方式。
第一种是模块内部的函数调用,跳转。很简单直接call
第二种时模块内部的数据访问。利用i686.get_pc_thunk.cx来获得下条指令的
地址,并存入ecx中。然后再加上偏移即可。
类型三模块间数据访问。访问存在于数据段段中的GOT(全局偏移表)
类型四模块间调用,跳转。访问GOT,找到函数地址在GOT中的偏移,然后间接调用。
另外,书中还提了fPIC和fpic的区别。fpic产生的代码相对较小,速度快,但由于地址无关代码都和硬件平台相关,不同的平台有不同的限制,所以为了方便起见大部分情况下我们都用fPIC。
7.3.4 模块内部的全局变量访问
由于存在一种特殊的情况,比如:
extern int globle;
int foo()
{
globle=1;
}
此时编译器无法判断globle是模块间调用,还是模块内部调用。于是对他采用了类似于上述类型4的方法,即GOT段中的相应地址指向.bss中的副本。(还是有点浆糊.........)
7.3.5数据段地址无关性
对于共享对象来说,如果数据段中有对绝对地址的引用,那么编译器和链接器就会产生一个重定位表,这个重定位表里面包含了“R_386_RELATIVE”类型的重定位入口,用于解决上述问题。
转载于:https://www.cnblogs.com/GrowUP-EveryDay/archive/2013/01/03/2843426.html