动态连结出现的原因就是为了解决静态连结中提到的两个问题:
// main.c
void func1();
int main() {
func1();
return 0;
}
#include <stdio.h>
void func() {
printf("func \n");
}
void func1() {
printf("func 1\n");
}
生成地址无关可执行文件 (position-independent executable)
$gcc -shared -fPIC -o func.so func.o
-fPIC 作用於编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code)
-shared 告诉连结器创建一个共享目标文件
生成可重定位目标文件
$gcc -c main.c
与动态连结库(.so)产生可执行文件
$gcc -o main main.o ./func.o
如果一个程序是动态连结,那麽他的function位置会在执行时才会固定,而引入一个library有好几个函式,我们不见得每个都会用到,所以当真正调用时,才会去载入它。这就是Lazy-binding的机制。
怎麽看一个程序有没有使用Lazy-binding呢?我们常常在做objdump -d elf 时,会看到call puts@plt这样的调用方式,这便是Lazy-binding调用函式的方式了
无论我们记忆体载入任何一个目标模组,资料段 和 程序码段的距离都是保持不变的,因此,程序码段 中 任何指令与 资料段 任何变数的距离都是一个常数,与程序码段 和 资料段 记忆体位址是无关的。
然而现代作业系统不允许修改程序码段,只能修改资料段,那麽 GOT(Global Offset Table)和 PLT(Procedure Linkage Table) 就为此而生
GOT(Global Offset Table):全局偏移表用於记录在 ELF 文件中所用到的共享库中符号的绝对地址。在程序刚开始运行时,GOT 表项是空的,当符号第一次被调用时会动态解析符号的绝对地址然後转去执行,并将被解析符号的绝对地址记录在 GOT 中,第二次调用同一符号时,由於 GOT 中已经记录了其绝对地址,直接转去执行即可(不用重新解析)。
PLT(Procedure Linkage Table):过程链接表的作用是将位置无关的符号转移到绝对地址。当一个外部符号被调用时,PLT 去引用 GOT 中的其符号对应的绝对地址,然後转入并执行。
那麽在下次呼叫时就避免再重定位,直接跳到 printf 地址了
透过GDB可以观看详细流程
这边可以参考
深入理解计算机系统
组译器与连结器 (下)
动态连结的PLT与GOT
<<: 跨网域传值的神队友——window.postMessage
前言 程序写了一阵子之後,工程师就会发展出自己习惯的模式,小到命名方式,大到系统架构,而在我们团队中...
Odoo模组开发实战 目录 开发Odoo应用程序 1.建立addons资料夹 2.建构模块 3.创建...
Day 6 - Kotlin变数var与val和型态 过了那麽久,终於进到了Kotlin程序的部分,...
这个部落格练习难度明显比前一份的难度要提升不少 也使用到一些共用的概念 首先介绍了工厂模式 [烧瓶里...
MDN: https://developer.mozilla.org/zh-CN/docs/Glos...