重定位, pic & 延迟绑定
重定位 (Relocation)
让我们抛出两个问题:什么是重定位?为什么要重定位呢?
在一个 C 源码被经过编译和汇编,生成一个由机器码组成的 ELF
文件(这是一个可重定位文件 ET_REL
)之后,要想成功执行它的话,我们要考虑到这样一个问题:
- 这个 C 源码可能使用了库函数,比如
printf
,malloc
等等,这些库函数是定义在标准库中的,仅仅依靠这一个 C 源码的话是无法运行的。
那么编译器如何处理这个问题呢?有两个方案:
- 使用静态链接,将标准库中的代码与 C 源码直接整合到一起,形成一个更大的
ELF
文件,自己本身就包含了所有所需的代码,可以独立完成任务。 - 使用动态链接,首先使用
如果要静态链接一个程序,只需要将多个可重定位文件(.o 文件)通过 linker(链接器)直接链接为一个完整的可执行文件,所有代码和数据都包含在最终的 ELF 文件中,运行时不再依赖外部的共享库,也不需要动态链接器(interpreter)。
而如果要动态链接一个程序,首先也需要通过 linker 对多个可重定位文件进行处理,但这时 linker 会:
在 ELF 文件中 添加 .dynamic 段(记录动态链接相关信息);
在 Program Header 中 写入一个 PT_INTERP 段(指定运行时需要的动态链接器/interpreter);
列出所有依赖的共享库(如 libc.so.6);
设置必要的重定位信息、符号表、GOT/PLT 等数据结构;
最终生成的可执行文件在运行时会由 内核读取 ELF 文件中的 PT_INTERP 字段,自动调用指定的 interpreter(动态链接器) 来:
加载共享库;
解析符号;
完成重定位;
启动程序的主函数。
现在我们要说清楚,重定位有三个东西要说: 运行时重定位,
PIC
pic 是位置无关代码,就是不管加载的时候把代码放在哪个地址,都能够正常执行(其它的都没什么好说的,主要是能够正常访问库函数和全局变量的地址)
这里有两个东西要说:
一个是传统的
PIC
(32位),还有一个是RIP-relative PIC
(64位)。- 传统
PIC
,这个的原理是这样的:首先,在编译的时候,编译器把 C 源码中访问全局变量的
- 传统
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 HaoIne!