为什么动态库源代码要用位置无关代码编译?
Why dynamic libraries source code should be compiled with position-independent code?
我对位置无关代码及其在动态库中的用法感到很困惑。
我发现了这个关于 GCC 的 -fPIC 选项的好例子:GCC -fPIC option
我弄清楚它是如何工作的。
但是,我很难理解为什么动态库需要独立于特定地址的代码。
当加载动态库时,为什么我们不能只保存它的绝对地址(例如库中函数的地址)并使用它们?
为什么在这种情况下必须使用相对地址?
用“gcc main.c”编译的简单程序int main() { return 0; }
将始终依赖于位置?
-fPIC
绝不是共享库问题的唯一解决方案。在 ELF Linux 之前使用 a.out executable format。在 a.out
中,所有共享库都在全局地址 space 中使用了唯一地址,因此它们总是被所有进程加载到相同的固定地址。事实证明,这非常难以管理:所有发行版软件包必须相互同意为哪个库保留哪个地址范围,并随着库的发展不断修改该协议。
-fPIC
让我们摆脱了困境。
根据您的建议,所有进程的地址范围的全局动态保留,一旦某个进程在某个内存区域映射了一个库,即使它从未真正加载该库,也没有其他进程能够重用该区域。对于具有 4G 地址 space 的 32 位系统(甚至 2G 是上层 2G 为内核保留)可能会很快耗尽 VM。另一个问题是主可执行文件的大小在进程之间是不同的,因此没有可以安全加载库的全局起始地址。
我对位置无关代码及其在动态库中的用法感到很困惑。
我发现了这个关于 GCC 的 -fPIC 选项的好例子:GCC -fPIC option 我弄清楚它是如何工作的。
但是,我很难理解为什么动态库需要独立于特定地址的代码。 当加载动态库时,为什么我们不能只保存它的绝对地址(例如库中函数的地址)并使用它们? 为什么在这种情况下必须使用相对地址?
用“gcc main.c”编译的简单程序int main() { return 0; }
将始终依赖于位置?
-fPIC
绝不是共享库问题的唯一解决方案。在 ELF Linux 之前使用 a.out executable format。在 a.out
中,所有共享库都在全局地址 space 中使用了唯一地址,因此它们总是被所有进程加载到相同的固定地址。事实证明,这非常难以管理:所有发行版软件包必须相互同意为哪个库保留哪个地址范围,并随着库的发展不断修改该协议。
-fPIC
让我们摆脱了困境。
根据您的建议,所有进程的地址范围的全局动态保留,一旦某个进程在某个内存区域映射了一个库,即使它从未真正加载该库,也没有其他进程能够重用该区域。对于具有 4G 地址 space 的 32 位系统(甚至 2G 是上层 2G 为内核保留)可能会很快耗尽 VM。另一个问题是主可执行文件的大小在进程之间是不同的,因此没有可以安全加载库的全局起始地址。