延迟绑定

静态链接:

静态链接是将一个或多个库或目标文件(先前由编译器或汇编器生成)链接到一起,生成一个可执行文件

程序运行速度快,但是浪费空间,更新库函数代码也困难,需要重新编译链接

动态链接:

动态链接是在程序运行时将程序与共享库链接起来,允许多个程序共享一个库。当一个程序被加载到内存中运行时,动态链接器会检查程序所需的共享库,并将它们加载到内存中,然后动态链接器也会将程序中对共享库中函数和变量的引用重定位实际的地址。

动态链接的优点是它可以减少内存占用和磁盘空间的浪费,并且多个程序可以共享同一个库,也更容易更新升级。但是由于把链接推迟到了程序运行时,所以每次执行程序时都需要链接,性能会降低。

因为Gcc默认是动态链接,所以为了解决动态链接缺点的问题有了延迟绑定

延迟绑定是动态链接器用来减少程序启动时间的一种技术。延迟绑定就是在函数第一次被调用的时候再和函数地址绑定

GOT 全称是全局偏移量表(Global Offset Table),用来存储外部函数在内存的确切地址。GOT 存储在数据段(Data Segment)内,可以在程序运行中被修改。

PLT 全称是程序链接表(Procedure Linkage Table),PLT 存储在代码段(Code Segment)内,在运行之前就已经确定并且不会被修改,所以 PLT 并不会知道程序运行时动态链接库被加载的确切位置。PLT 表内存储的入口点就是GOT表对应的条目地址。

首次调用:

第一次调用 func 函数时,首先会跳转到 PLT 执行 jmp *(func@got),由于该函数没被调用过,func@got 中的值不是 func 函数的地址,而是 PLT 中的 “preapre resolver” 指令的地址,所以会跳转到 “preapre resolver” 执行,接着会调用 _dl_runtime_resolve 解析 func 函数的地址,并将该函数真正的地址填充到 func@got,最后跳转到 func 函数继续执行代码。

非首次调用:

当再次调用 func 函数时,由于 func@got 中已填充正确的函数地址,此时执行 PLT 中的 jmp *(func@got) 即可成功跳转到 func 函数中执行。