dynamic sections
[TOC]
Global Offset Table
GOT 表在 ELF 文件中分为两个部分:
- .got,存储导入变量的地址。
- .got.plt,存储导入函数的地址。
在 Linux 的实现中,.got.plt 的前三项的具体的含义如下:
GOT[0],.dynamic 的地址。
GOT[1],指向 link_map 的指针,只会在动态装载器中使用,包含了进行符号解析需要的当前 ELF 对象的信息。每个 link_map 都是一条双向链表的一个节点,而这个链表保存了所有加载的 ELF 对象的信息。
GOT[2],指向动态装载器中 _dl_runtime_resolve 函数的指针。
.got.plt 后面的项则是程序中不同 .so 中函数的引用地址。下面给出一个相应的关系
Procedure Linkage Table
- PLT 表将导入函数重定向到绝对地址。主要包括两部分 .plt,与常见导入的函数有关,如 read 等函数。 .plt.got,与动态链接有关系。
准确的说,plt 表不是查询表,而是一块代码。这一块内容是与代码相关的。 在动态链接下,程序模块之间包含了大量的函数引用。如果在程序开始执行前就把所有的函数地址都解析好,动态链接就会耗费不少时间用于解决模块之间的函数引用的符号查找以及重定位。但是,在一个程序运行过程中,可能很多函数在程序执行完时都不会用到,因此一开始就把所有函数都链接好可能会浪费大量资源,所以 ELF 采用了一种延迟绑定的做法,其基本思想是函数第一次被用到时才进行绑定(符号查找,重定位等),如果没有用则不进行绑定。所以程序开始执行前,模块间的函数调用都没有进行绑定,而是需要用到时才由动态链接器负责绑定。
- 在延迟绑定的情况下,总体流程如下图所示,蓝线表示首次执行的流程图,红线表示第二次以后调用的流程图
plt与got
- 为什么栈溢出时调用puts函数要使用plt表中的puts?能不能用GOT表puts呢? 因为程序执行流调用函数时,跳转到的位置是plt表,plt表第一条指令是跳转到GOT表,但是,如果修改此jmp语句为类似system等函数的地址,程序不会遇到jmp语句,从而卡在这个“地址”处,无法接着执行。相反,修改GOT表的内容,在执行流到了plt时,会跳转到GOT表上的地址执行对应的函数,从而完成了hijack
- 不能调用GOT中的puts,理由和之前的类似,GOT表无论在修改前后,都只储存一个地址,无法实现函数的“调用” 。GOT表中,只有地址,而没有执行控制!