这里给出一个 ELF 文件类型的表格:
pEfltw4.png

重定位 (Relocation)

让我们抛出两个问题:什么是重定位?为什么要重定位呢?

在一个 C 源码被经过编译和汇编,生成一个由机器码组成的 ELF 文件(这是一个可重定位文件 ET_REL)之后,要想成功执行它的话,我们要考虑到这样一个问题:

  • 这个 C 源码可能使用了库函数,比如 printfmalloc 等等,这些库函数是定义在标准库中的,仅仅依靠这一个 C 源码的话是无法运行的。

那么编译器如何处理这个问题呢?有两个方案:

  1. 使用静态链接,将标准库中的代码与 C 源码直接整合到一起,形成一个更大的 ELF 文件,自己本身就包含了所有所需的代码,可以独立完成任务。
  2. 使用动态链接,首先使用

如果要静态链接一个程序,只需要将多个可重定位文件(.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 源码中访问全局变量的