ღ Miranda

Lazy binding in detail

本文整理一下和延迟绑定技术有关的细节。

About GOT and PLT

为了了解GOTPLT,首先要知道关于PIC的知识,Position Independent Code(PIC)是为了是为了重定位动态链接库的symbols,现代操作系统不允许修改代码段,只能修改数据段,而使用了动态链接库后函数地址只有在执行时才能确定,所以程序内调用的库中的函数地址在编译时不知道,所以,编译时将函数调用返回.data段,而包含PIC的程序在运行时需要更改.data段中的GOTPLT来重定位全局变量。

Global Offset Table,也就是GOT表为每个全局变量保存了入口地址,在调用全局变量时,会直接调用对应GOT表条目中保存的地址,而不调用绝对地址。

Procedural Linkage Table,也就是PLT是过程链接表,为每个全局变量保存了一段代码,第一次调用一个函数会调用形如function@PLT的函数,这就是跳到了函数对应的PLT表开头执行,会解析出函数真正的地址填入GOT表中,以后调用时会从GOT表中取出函数真正的起始地址执行,下面会说具体的细节。

Environment

测试代码:

编译链接:

Something about .dynamic

ELF.dynamic section里包含了和重定位有关的很多信息,完整的.dynamic段:

GOT表分成两部分.got.got.plt,前一个保存全局变量引用位置,后一个保存函数引用位置,通常说的GOT指后面一个,下文GOT即代表.got.plt

GOT表的起始地址:

GOT表的前三项有特殊含义:

第一项是.dynamic段的地址,第二个是link_map的地址,第三个是_dl_runtime_resolve函数的地址,第四项开始就是函数的GOT表了,第一项就是printf条目:

PLTRELSZ指定了.rel.plt大小,RELENT指定每一项大小,PLTREL指定条目类型为RELJMPREL对应.rel.plt地址,保存了重定位表,保存的是结构体信息:

REL的数据结构为:

r_offset就是对应函数GOT表地址,看看.rel.plt第一项和第二项:

再看:

分别和printffree对应,0x0804a000处就是printfGOT表地址。

根据宏定义,由r_info=0x107可以知道ELF32_R_TYPE(r_info)=7,对应于R_386_JUMP_SLOT;其symbol index则为RLF32_R_SYM(r_info)=1

还有一个需要注意的就是字符串表,保存了一些符号表,在重定位时会用到:

查看:

How ELF relocation works

在第一次call 0x8048340 <printf@plt>时会跳到PLT段中,第一句会跳到GOT条目指向的地址:

第一次调用函数时,GOT表中的地址为PLT表的第二句地址:

push reloc_offset,这里是0,再push link_map,也就是GOT表的第二项,再调用_dl_runtime_resolve函数。

_dl_runtime_resolve根据reloc_offset找到.rel.plt段中的结构体:

r_info为0x107。

然后根据ELF32_R_SYM(r_info)找到.dynsym中对应的结构体:

.dynsym有关的信息为:

其实地址为0x80481cc,每个结构体大小为16bytes

结构体为:

所以SYMTAB[1]0x80481cc+16:

然后根据sym->st_name=0x29.dynstr中,也就是STRTAB找到函数对应的字符串:

根据函数名字找到对应的地址,填入GOT表对应的位置,跳到函数起始地址执行,执行完后,printf对应的GOT表处已经填上了函数真正的地址:

发表评论

电子邮件地址不会被公开。