Day.7 深入理解动态连结

为甚麽会出现动态连结?

动态连结出现的原因就是为了解决静态连结中提到的两个问题:

  • 浪费空间,因为每个可执行程序中对所有需要的Obj file都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个Obj file 都在记忆体中存在多个副本。
  • 每当函数库的程序码修改了,这个时候就需要重新进行编译连结形成可执行程序。

如何使用动态连结 ?

// main.c
void func1();
int main() {
    func1();
    return 0;
}

#include <stdio.h>


void func() {
    printf("func \n");
}

void func1() {
    printf("func 1\n");
}
  1. 生成地址无关可执行文件 (position-independent executable)
    $gcc -shared -fPIC -o func.so func.o
    -fPIC 作用於编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code)

    -shared 告诉连结器创建一个共享目标文件

  2. 生成可重定位目标文件
    $gcc -c main.c

  3. 与动态连结库(.so)产生可执行文件
    $gcc -o main main.o ./func.o

Lazy-binding

如果一个程序是动态连结,那麽他的function位置会在执行时才会固定,而引入一个library有好几个函式,我们不见得每个都会用到,所以当真正调用时,才会去载入它。这就是Lazy-binding的机制。

怎麽看一个程序有没有使用Lazy-binding呢?我们常常在做objdump -d elf 时,会看到call puts@plt这样的调用方式,这便是Lazy-binding调用函式的方式了

地址无关可执行文件(PIC)

无论我们记忆体载入任何一个目标模组,资料段 和 程序码段的距离都是保持不变的,因此,程序码段 中 任何指令与 资料段 任何变数的距离都是一个常数,与程序码段 和 资料段 记忆体位址是无关的。

GOT/PLT

然而现代作业系统不允许修改程序码段,只能修改资料段,那麽 GOT(Global Offset Table)和 PLT(Procedure Linkage Table) 就为此而生

  • GOT(Global Offset Table):全局偏移表用於记录在 ELF 文件中所用到的共享库中符号的绝对地址。在程序刚开始运行时,GOT 表项是空的,当符号第一次被调用时会动态解析符号的绝对地址然後转去执行,并将被解析符号的绝对地址记录在 GOT 中,第二次调用同一符号时,由於 GOT 中已经记录了其绝对地址,直接转去执行即可(不用重新解析)。

  • PLT(Procedure Linkage Table):过程链接表的作用是将位置无关的符号转移到绝对地址。当一个外部符号被调用时,PLT 去引用 GOT 中的其符号对应的绝对地址,然後转入并执行。

流程

  • 当程序码中 Call Printf(),在对应的组语会看见 看到call printf@plt。
  • printf@GOT会回去向.got.plt取值
    • 由於是第一次使用函数,在GOT 表中并不会找到该函数地址,因此必须透过 PLT 将 GOT 重定位
  • 将找到的函数位置所需的参数 推入 stack中
  • 透过执行dl_runtime_resolve 找出函式位址
  • 这时系统就会把 func 的位址写进 .got.plt 当中。

那麽在下次呼叫时就避免再重定位,直接跳到 printf 地址了

透过GDB可以观看详细流程
这边可以参考

参考资料

深入理解计算机系统
组译器与连结器 (下)
动态连结的PLT与GOT


<<:  跨网域传值的神队友——window.postMessage

>>:  追求JS小姊姊系列 Day5 -- 工具人登场

day 1 - 魔鬼藏在细节里

前言 程序写了一阵子之後,工程师就会发展出自己习惯的模式,小到命名方式,大到系统架构,而在我们团队中...

Day 3 Odoo模组开发

Odoo模组开发实战 目录 开发Odoo应用程序 1.建立addons资料夹 2.建构模块 3.创建...

Day 6 - Kotlin变数var与val和型态

Day 6 - Kotlin变数var与val和型态 过了那麽久,终於进到了Kotlin程序的部分,...

[20] [烧瓶里的部落格] 10. 心得

这个部落格练习难度明显比前一份的难度要提升不少 也使用到一些共用的概念 首先介绍了工厂模式 [烧瓶里...

JS Truthy 与 Falsy DAY55

MDN: https://developer.mozilla.org/zh-CN/docs/Glos...