第一步:在二进制文件编译完成后,二进制文件的PLT表在0x80482b0处,留出了一个指向GOT表的0x80496f8指针。由于编译完成后PLT的值无法修改,所以在动态链接时,链接器将动态库的真实逻辑地址填到GOT表,使得程序在真正运行时,可以找到每次运行都随机分配内存位置的动态库。
第二步:当程序第一次运行,调用puts函数时,会首先默认到编译时定好的PLT表的0x80482b0寻找,接着跳到编译时定好的GOT表的0x80486f8处,发现该内存位置填的是PLT的地址,而不是0xF7开头的逻辑内存地址。因此链接器知道puts乃至libc都还没有被加载到内存中。因此顺着此时GOT表中的0x80482b6的地址,回到PLT表,把0x0的这个偏移(相对于GOT表头)压栈后,跳到PLT表头。
这个过程可以用如下伪代码表示:
load_func( (int*)GOT_Base_Addr, (int)func_offset )
0x80482a0处将GOT表的基地址压栈后,跳往GOT表中定好指向_dl_runtime_resolve的地址。这个程序是用来将动态库的各个函数偏移转换成逻辑地址后,放到GOT表中。放完后,GOT表中地址更新为逻辑地址。
第三步,下一次调用puts函数,依然向PLT表找入口,跳到GOT表后,发现GOT表项的地址为逻辑地址,于是默认动态库已载入,放心跳往该地址。
Comments