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

GOT表

        GOT表(Global Offset Table) ,又称全局偏移表,位于.data节首,记录着外部符号动态加载后的首地址信息。在静态链接时,每一个外部符号都会在GOT表对应一个表项,静态链接器并每一个表项生成一个对应的重定位项(数据位于.rel.data节,函数位于.rel.text节)。在动态加载时,动态链接器将根据重定位项,修改对应的GOT表中信息,完成重定位。

         在访问动态库数据或调用动态库函数时,直接通过相对位移,找到对应的GOT表项,然后根据GOT表项中记录的目标地址信息访问数据或调用函数,从而实现动态加载。

举例说明:

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

简单描述ELF动态链接重定位的延迟绑定以及GOT表、PLT表的意义_第1张图片
汇编伪代码如下:

	0000050c <main>:
	  0000050c:    55                   pushl %ebp
	  ....
	  00000557:    e8 00 00 00 00       call  0000055c
	  0000055c:    5b                   popl  %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表的意义_第2张图片
汇编伪代码如下:

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

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

你可能感兴趣的:(Android学习)