简单描述ELF动态链接重定位的延迟绑定以及GOT表、PLT表的意义

GOT表
GOT表(Global Offset Table) ,又称全局偏移表,位于.data节首,记录着外部符号动态加载后的首地址信息。在静态链接时,每一个外部符号都会在GOT表对应一个表项,静态链接器并每一个表项生成一个对应的重定位项(数据位于.rel.data节,函数位于.rel.text节)。在动态加载时,动态链接器将根据重定位项,修改对应的GOT表中信息,完成重定位。
【简单描述ELF动态链接重定位的延迟绑定以及GOT表、PLT表的意义】在访问动态库数据或调用动态库函数时,直接通过相对位移,找到对应的GOT表项,然后根据GOT表项中记录的目标地址信息访问数据或调用函数,从而实现动态加载。
举例说明:

extern int b; extern void ext(); int main() { ext(); }

简单描述ELF动态链接重定位的延迟绑定以及GOT表、PLT表的意义
文章图片

汇编伪代码如下:
0000050c
: 0000050c:55pushl %ebp .... 00000557:e8 00 00 00 00call0000055c 0000055c:5bpopl%ebx 0000055d:addl $0x1204, %ebx 0000055e:call *(%ebx)

通过这种方式,可以发现在调用函数时多使用了三条指令,并多使用了一个%ebx寄存器,加法过程可能会出现寄存器溢出问题。为解决以上问题,ELF采用延迟绑定技术来解决。
延迟绑定与PLT表
所谓延迟绑定,就是不在加载动态链接库时进行绑定(重定位填写GOT表),而是延迟到每一个外部符号第一次被调用时进行,这种方式所导致的效果显而易见:第一次调用时重定位会较慢,再次调用时则明显加快,这也可以避免加载动态库中没有使用的符号信息。延迟绑定也将涉及到PLT表(Procedure Linkage Table),又称过程链接表,位于.text节(可以看出PLT表中每一项都对应一个代码块)。
此时,GOT表作为.data节的一部分,开始的三项是固定的,含义如下:
GOT[0]: 记录.dynamic节首地址,该节中记录了动态链接器所需的基本信息,如符号表位置,重定位表位置等。
GOT[1]: 记录动态链接器的标示信息。
GOT[2]:记录动态链接器延迟绑定代码的入口地址。
这里假设ext函数对应的是GOT[3]。
PLT作为.text节的一部分,开始的一项是固定的,其目的是设置动态链接器的标示信息与符号ID作为参数,然后调用动态连机器延迟绑定代码的入口。
简单描述ELF动态链接重定位的延迟绑定以及GOT表、PLT表的意义
文章图片

汇编伪代码如下:
PLT[0] 0804833c: ff 35 88 95 04 08pushl 0x8049588 08048342: ff 25 8c 95 04 08jmp *0x804958c 08048348: 00 00 00 00 PLT[1] 0804834c: ff 25 90 95 04 08pushl 0x8049590 08048352: 68 00 00 00 00pushl 0x0 08048357: e9 e0 ff ff ffjmp 0x0804833c

第一次调用ext函数时,会相对寻址到对应的PLT表项,然后跳转到对应的GOT表项中记录的地址,第一次调用时GOT表项记录的是PLT表中下一条指令的地址,然后向堆栈中push符号ID后跳转至PLT[0]表项,然后PLT[0]表项向堆栈push动态链接器的标示信息后跳转至动态连机器延迟绑定代码的入口。在完成绑定后,由动态链接器修改对应GOT表项中记录的函数地址,从而实现延迟绑定的重定向。

    推荐阅读