地址无关代码的核心结构 #
在计算机科学领域,有一句名言:“计算机领域的所有问题都可以使用新加一层抽象来解决”。这句话的应用在计算机领域随处可见。同样地,要实现代码段的地址无关代码,思路也是通过添加一个中间层,使得对全局符号的访问由直接访问变成间接访问。
可以引入一个固定地址,让引用者与这个固定地址之间的相对偏移是固定的,然后这个地址处再填入 foo 函数真正的地址。当然,这个地方必然位于数据段中,是每个进程私有的,这样才能做到在不同的进程里,可以访问不同的虚拟地址。这个新引入的固定地址就是全局偏移表 (Global Offset Table, GOT)。GOT 的工作原理如下图所示:
在上图中,call 指令处被填入了 0x3000,这是因为进程 1 的 GOT 与 call 指令之间的偏移是 0x5000-0x2000=0x3000,同时进程 2 的 GOT 与 call 指令之间的偏移是 0x8000-0x5000=0x3000。所以对于这一段共享代码,不管是进程 1 执行还是进程 2 执行,它们都能跳到自己的 GOT 表里。
然后,进程 1 通过访问自己的 GOT 表,查到 foo 函数的地址是 0x1000,它就能真正地调用到 foo 函数了。进程 2 访问自己的 GOT 表,查到 foo 函数的地址是 0x2000,它也能顺利地调用 foo 函数。这样就通过引入了 GOT 这个间接层,解决了 call 指令和 foo 函数定义之间的偏移不固定的问题。
这种技术就是地址无关代码 (Position Independent Code, PIC)。