前言
plt表和got表是和链接过程相关的表。我们知道,一个可执行文件的生成过程需要经过预处理,编译,汇编,链接四个过程。链接又分为静态链接和动态链接。静态链接是发生在程序执行之前,动态链接是发生在程序执行中。
链接方式不同,plt表和got的功能会有一些区别。
动态链接中的plt和got表
plt表的形式如下,下图是gdb调试过程中的图
plt表又叫过程链接表(Procedure Linkage Table)。
可以看到,plt表中存放的是代码,事实上,plt表就位于代码段。
如图可见,plt表的开头是一些指令,和下面的函数顺序对应。比方说程序第一次调用read函数,那么会先去找read的plt对应的内容,里面存的是直接跳转到got表的指令。跳到got之后,由于是第一次调用函数,got表里的内容是一条指令,jump到plt开头的对应的指令去。
注意前面的指令:
push xxx
:先将导入函数的标识(Elf Rel
在.rel.plt
的偏移)压栈- 然后跳转到plt表的开头,又把linkmap的地址压栈,然后跳转到GOT[2](0x404010)保存的地址处,也就是
_dl_runtime_resolve()
函数
.got和.got.plt段的区别
先来张图看看got表长啥样 Global Offset Table (GOT)
__gmon_start___ptr
是编译器生成的一个符号,通常在使用 GCC 编译器时会出现。这个符号通常与程序的性能分析和优化有关。在程序中,__gmon_start___ptr
实际上是一个指向 __gmon_start__
函数的指针,__gmon_start__
函数是用于支持程序性能计数的库函数。
然后是got.plt表
got.plt[0],如上图所示,存的是这个值是 _DYNAMIC
符号的偏移量。 _DYNAMIC
符号通常用于动态链接过程中,在程序启动时由动态链接器使用。
got.plt[1]放的是link_map的地址。
got.plt[2]放的是_dl_runtime_resolve函数的地址,这个地址是动态装载的,因此在IDA里显示为0。
静态链接中的plt和got表
如图,静态链接程序的plt表如下
可以看到,push的数字是0,这里push指令其实没有什么意义。然后会发现有两个jump,第一个jump跳到了下一条指令,然后跳到了got表。
got表里存的就是函数的地址了,不过这个地址并不是动态装载的,在IDA中就可以看到。