动态链接
- 1. 静态链接与动态链接
- 2. 地址无关
- 3. PLT 和 GOT
- 参考
1. 静态链接与动态链接
静态链接(Static Link)是通过合并代码段的方法来使程序装载至内存;
动态链接(Dynamic Link)则是链接加载到内存中的共享库(Shared Libraries)。
在 Windows 下,这些共享库文件就是 .dll 文件,也就是 Dynamic-Link Library(DLL,动态链接库)。
在 Linux 下,这些共享库文件就是 .so 文件,也就是 Shared Object(动态链接库)。
2. 地址无关
在程序运行的时候共享代码,这些机器码必须满足地址无关。也就是说,编译出来的共享库文件的指令代码,是地址无关码(Position-Independent Code)。即,这段代码无论加载在哪个内存地址,都能够正常执行。
而常见的地址相关的代码,比如绝对地址代码(Absolute Code)、利用重定位表的代码等,都是地址相关的代码。例如重定位表,在程序链接的时候,就把函数调用后要跳转访问的地址确定下来了,这意味着,如果这个函数加载到一个不同的内存地址,跳转就会失败。
对于所有动态链接共享库的程序来讲,虽然共享库用的都是同一段物理内存地址,但是在不同的应用程序里,它所在的虚拟内存地址是不同的。
我们没办法、也不应该要求动态链接同一个共享库的不同程序,必须把这个共享库所使用的虚拟内存地址变成一致。如果这样的话,我们写的程序就必须明确地知道内存的内存地址分配。
动态代码库内部的变量和函数调用都很容易解决,我们只需要使用相对地址(Relative Address)就好了。各种指令中使用到的内存地址,给出的不是一个绝对的地址空间,而是一个相对于当前指令偏移量的内存地址。因为整个共享库是放在一段连续的虚拟内存地址中的,无论装载到哪一段地址,不同指令之间的相对地址都是不变的。
3. PLT 和 GOT
PLT 即 程序链接表(Procedure Link Table)。
在共享库的 data section 里面,保存了一张 全局偏移表(GOT,Global Offset Table)。虽然共享库的代码部分的物理内存是共享的,但是数据部是使用动态链接的应用程序里面各加载一份的。
所有需要引用当前共享库外部的地址的指令,都会查询 GOT,来找到当前运行程序的虚拟内存里的对应位置。而 GOT 表里面的数据,则是在加载一个个共享库的时候写进去的。
不同的进程,调用同样的动态链接文件,各自 GOT 里面指向最终加载的动态链接库里面的虚拟内存地址是不同的。
这样,虽然不同的程序调用的同样的动态库,各自的内存地址是独立的,调用的又都是同一个动态库,但是不需要去修改动态库里面的代码所使用的地址,而是各个程序各自维护好自己的 GOT,能够找到对应的动态库就好了。
我们的 GOT 表位于共享库自己的数据段里。GOT 表在内存里和对应的代码段位置之间的偏移量,始终是确定的。这样,我们的共享库就是地址无关的代码,对应的各个程序只需要在物理内存里面加载同一份代码。而我们又要通过各个可执行程序在加载时,生成的各不相同的 GOT 表,来找到它需要调用到的外部变量和函数的地址。
参考
极客时间《深入浅出计算机组成原理》:http://gk.link/a/11UMi