摘自 深入理解计算机系统
共享库的一个主要目的是允许多个正在运行的进程共享存储器中的相同库代码,因为可以节约宝贵的存储器资源,那么多个进程是如何共享一个程序的copy。
方法一 给每个共享库分配一个事先预备的专用的地址空间,加载器将共享库加载至这个空间中,这种方法会产生以下缺点:
1 即使程序不被使用,也会占用了一个存储器空间, 会造成空间上的浪费。
2 如果库被修改后变大,那么还需要另外寻找适合存储库的空间,以前的空间被浪费掉。
3 如果系统拥有成千上万个库,那么系统的存储器会被这么库占用很多,造成很大的空间浪费。
方法二 编译库代码,使在任何地址都可以加载和执行这些代码,这种代码就叫做PIC(position-independent code PIC)。
PIC数据引用:
无论在存储器中的任何地方加载一个目标模块/共享目标模块,数据段都会被分配在代码段后面,估代码段的任何指令和数据段的任何变量之间都是一个运行时常量的距离。利用这一点,编译器在数据段开始的位置创建一个全局偏移量表(GOT)。(GOT)包含每个被这个目标模块引用的全局数据目标的表目。编译器还为GOT每个表目生成一个重定位记录。在加载时,动态链接器会重定位GOT中的每个表目,使得它包含正确的绝对地址。每个引用全局数据的目标模块都有一张自己的GOT。
下面代码为GOT将变量修改为绝对地址(引用地址)
call L1
L1: popl %ebx;
addl $VAROFF , %ebx
movl (%ebx) , %eax
movl (%eax) , %eax
PIC 函数调用:
call L1
L1: popl %ebx;
addl $PROCOFF ,%ebx
call *(%ebx)