动态链接(上):地址无关代码是如何生成的

328 阅读2分钟

将常用的公共的函数都放到一个文件中,在整个系统里只会被加载到内存中一次,无论有多少个进程使用它,这个文件在内存中只有一个副本,这种文件就是动态链接库文件。

它在 Linux 里是共享目标文件 (share object, so),在 windows 下是动态链接库文件 (dynamic linking library, dll)。

动态链接第一个特点是,动态库的数据段和代码段是靠在一起的,它并没有和可执行程序的数据段,代码段分别合并,这是与静态链接不同的地方。第二个特点是,同一个动态库文件在两个进程中的虚拟地址并不相同。

正是虚拟地址技术让我们在进程间共享动态库变得容易,我们只需要在虚拟空间里设置一下到物理地址的映射即可完成共享。虽然 libc.so 在物理内存中只有一份,但它可以被多个进程进行映射。而且进程 1 映射 libc.so 代码段的虚拟地址与进程 2 映射 libc.so 代码段的虚拟地址可以不相等。

image.png

引入一个固定地址,让引用者与这个固定地址之间的相对偏移是固定的,然后这个地址处再填入 foo 函数真正的地址。当然,这个地方必然位于数据段中,是每个进程私有的,这样才能做到在不同的进程里,可以访问不同的虚拟地址。这个新引入的固定地址就是全局偏移表 (Global Offset Table, GOT)。GOT 的工作原理如下图所示:

image.png

如果两个共享库之间有引用关系的话,引用者和被引用者之间的相对位置就不能确定了,这时就需要引入地址无关代码技术。对于内部函数或数据访问,因为其相对偏移是固定的,所以可以通过相对偏移寻址的方式来生成代码;对于外部和全局函数或数据访问,则通过 GOT 表的方式,利用间接跳转将对绝对地址的访问转换为对 GOT 表的相对偏移寻址,由此得到了地址无关的代码。


此文章为7月Day7学习笔记,内容来源于极客时间《编程高手必学的内存知识》