1. .plt 是什么?

.plt (Procedure Linkage Table,过程链接表) 是 ELF 文件代码段中的一个特殊区域,用于实现动态链接中的延迟绑定

简单来说,它是一个**“跳板”“中转站”**。当你的代码调用一个共享库(如 libc.so)中的函数时,程序并不直接跳转到该函数的真实地址,而是先跳转到 .plt 中的对应条目。

2. 为什么需要 .plt

问题背景
当你在程序中调用 printf("Hello") 时,编译器生成代码时并不知道 printf 在内存中的确切位置。因为 printf 位于外部的动态库中,只有程序运行时,动态链接器才能知道它被加载到了哪里。

解决方案
如果不使用 .plt,动态链接器需要在程序启动时,找出所有用到的外部函数地址并填入代码中。这会导致程序启动非常慢。

.plt 的作用(延迟绑定)
利用 .plt,程序可以推迟查找外部函数地址的时机。

  • 第一次调用时:通过 .plt 触发动态链接器去查找 printf 的真实地址,填入记录表,然后执行。
  • 后续调用时:直接通过 .plt 跳转到已记录好的真实地址,不再查找。

3. 具体例子说明

假设我们有一个简单的 C 程序 main.c,它调用了标准库函数 printf

#include <stdio.h>

void say_hello() {
    printf("Hello World!\n"); // 调用外部函数
}

int main() {
    say_hello();
    say_hello(); // 调用第二次
    return 0;
}
场景推演:从汇编指令到内存跳转

当编译器生成汇编代码时,call printf 指令实际上被改写成了调用 .plt 中的条目。

第一步:程序执行 call printf
汇编代码实际上长这样:

call printf@plt

这行指令的意思是:跳转到 .plt 段中专门处理 printf 的那个代码块。

第二步:进入 .plt 段 (第一次调用)
程序跳转到了 .plt 中的 printf 条目。该条目通常包含三条指令(简化逻辑):

printf@plt:
    ; 1. 跳转到 GOT 表中记录的地址
    jmp    *GOT[n]   
    
    ; 2. (如果上面跳转失败,说明是第一次调用,会执行下面)
    push   n         ; 压入参数 n (代表 printf 的 ID)
    jmp    resolver  ; 跳转到动态链接器的解析函数
  • 关键点GOT[n] 是全局偏移表中的一个槽位。
    • 初始状态GOT[n] 里存的并不是 printf 的真实地址,而是上面 push n 这条指令的地址。
    • 结果:因为 GOT[n] 指向了下一条指令,所以第一次执行 jmp *GOT[n] 时,实际上没跳走,而是继续往下执行了 push njmp resolver

第三步:动态链接器介入
resolver 是动态链接器提供的代码。它做以下事情:

  1. 根据参数 n,知道程序想找 printf
  2. 在内存中找到 libc.so 中 printf真实加载地址(假设是 0x7fff1234)。
  3. 将这个真实地址 0x7fff1234 回写GOT[n] 中。
  4. 跳转到 0x7fff1234 执行 printf 函数体。

第四步:进入 .plt 段 (第二次调用)
main 函数第二次调用 say_hello 时,流程再次来到 printf@plt

printf@plt:
    jmp    *GOT[n]   ; 此时 GOT[n] 已经被修改为 0x7fff1234
  • 现状:此时 GOT[n] 里存的是刚才解析好的真实地址 0x7fff1234
  • 结果jmp 指令直接跳转到了 printf 的真实代码,不再执行后面的 pushresolver

4. 总结流程图

第二次调用

第一次调用

GOT指向下一条指令

查找libc中printf地址

GOT已存真实地址

用户代码: call printf

PLT条目: jmp *GOT

执行解析器 Resolver

更新GOT表

跳转到printf真实代码

直接跳转到printf真实代码

5. 核心价值总结

  1. 性能优化:如果不使用 PLT/GOT 机制,程序启动时必须解析所有可能用到的几百个库函数,导致启动极慢。使用 PLT 后,只有真正用到的函数才会被解析(按需解析)。
  2. 代码独立性:用户代码不需要在编译时硬编码函数地址,所有的外部调用都通过 .plt 这个统一的中转站进行。

一句话概括.plt 就是一个带有“自动寻址功能”的跳板,第一次跳过去时它会负责找到目标并记下来,之后再跳过去时它就直接把你送到目标地址。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐