链接程序和库指南

x64: 过程链接表

对于 x64 动态库,过程链接表位于共享文本中,但使用专用全局偏移表中的地址。运行时链接程序可确定目标的绝对地址,并相应地修改全局偏移表的内存映像。这样,运行时链接程序就会重定向各项,而不会破坏程序文本的位置独立性和共享性。可执行文件和共享库文件包含不同的过程链接表。

表 7–41 x64: 过程链接表示例
.PLT0:

    pushq   GOT+8(%rip)                         # GOT[1]

    jmp     *GOT+16(%rip)                       # GOT[2]

    nop;    nop

    nop;    nop

.PLT1:

    jmp     *name1@GOTPCREL(%rip)               # 16 bytes from .PLT0

    pushq   $index1

    jmp     .PLT0

.PLT2:

    jmp     *name2@GOTPCREL(%rip)               # 16 bytes from .PLT1

    pushl   $index2

    jmp     .PLT0

以下步骤介绍了运行时链接程序和程序如何通过过程链接表和全局偏移表来协作解析符号引用。

  1. 初始创建程序的内存映像时,运行时链接程序会将全局偏移表中的第二项和第三项设置为特殊值。以下步骤说明了这些值。

  2. 进程映像中的每个共享库文件都有各自的过程链接表,并且控制权仅转移给位于同一目标文件内的过程链接表项。

  3. 例如,该程序会调用 name1,以将控制权转移给标签 .PLT1

  4. 第一条指令会跳至全局偏移表项中对应于 name1 的地址。最初,全局偏移表包含以下 pushq 指令的地址,而不是 name1 的实际地址。

  5. 该程序将在栈中推送一个重定位索引 (index1)。该重定位索引是重定位表中一个 32 位的非负索引。重定位表由 DT_JUMPREL 动态节项标识。指定的重定位项的类型为 R_AMD64_JMP_SLOT,其偏移指定了前面的 jmp 指令中使用的全局偏移表项。该重定位项还包含符号表索引,以供运行时链接程序用于获取引用的符号 name1

  6. 推送该重定位索引后,程序将跳至过程链接表中的第一项 .PLT0pushq 指令会在栈中推送全局偏移表的第二项 (GOT+8) 的值,从而为运行时链接程序提供一个字的标识信息。然后,程序将跳至第三个全局偏移表项 (GOT+16) 中的地址,以继续跳至运行时链接程序。

  7. 运行时链接程序将展开栈、检查指定的重定位项、获取符号的值、在全局偏移项表中存储 name1 的实际地址并跳至目标。

  8. 过程链接表项的后续执行结果会直接传输给 name1,而不会再次调用运行时链接程序。位于 .PLT1jmp 指令将跳至 name1,而不是对 pushq 指令失败。

LD_BIND_NOW 环境变量可更改动态链接行为。如果其值不为空,则运行时链接程序会在将控制权转移给程序之前处理 R_AMD64_JMP_SLOT 重定位项。