请考虑以下程序hello.c
:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("hello");
return 0;
}
该文件使用gcc -o hello -Og -g hello.c
编译,然后加载gdb hello
。
检查 GOT 以p 'printf@got.plt'
printf
调用给出
$1 = (<text from jump slot in .got.plt, no debug info>) 0x1036 <printf@plt+6>
这是相应PLT条目中第二条指令相对于该部分开头的偏移量。
启动程序并将其与starti
链接后,p 'printf@got.plt'
现在给出
$2 = (<text from jump slot in .got.plt, no debug info>) 0x555555555036 <printf@plt+6>
这是相应PLT条目中第二条指令的绝对地址。
我明白发生了什么以及为什么。我的问题是动态链接器/加载器如何知道将部分偏移量 (0x1036( 更新为绝对地址 (0x555555555036(?
链接前的p &'printf@got.plt'
给出
$1 = (<text from jump slot in .got.plt, no debug info> *) 0x4018 <printf@got.plt>
和readelf -r simple
显示此地址的重新定位条目
Relocation section '.rela.plt' at offset 0x550 contains 1 entry:
Offset Info Type Sym. Value Sym. Name + Addend
000000004018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
但我对 System V 应用程序二进制接口 AMD64 架构处理器补充(第 76 页(的解读是,这些重定位条目仅在LD_BIND_NOW
为非空时使用。我是否遗漏了其他搬迁条目?相对于 GOT 的最终地址重新调整偏移量的机制是什么?
根据 Drepper 的《如何编写共享库》,动态链接器重新定位了两种依赖项:
- 相对重定位:对同一对象中位置的依赖关系。链接器只是将对象的加载地址添加到目标目标的偏移量。 符号
- 重定位:基于复杂符号解析算法的更复杂和昂贵的过程。
对于PLT的GOT,Drepper指出(§1.5.5(在启动时,动态链接器用指向相应PLT条目的第二条指令的地址填充GOT插槽。对 glibc 源代码的阅读表明链接器确实循环遍历R_X86_64_JUMP_SLOT
重定位 (elf/do-rel.h:elf_dynamic_do_Rel
( 并递增它们包含的偏移量 (sysdeps/x86_64/dl-machine.h:elf_machine_lazy_rel
(:
if (__glibc_likely (r_type == R_X86_64_JUMP_SLOT))
{
/* Prelink has been deprecated. */
if (__glibc_likely (map->l_mach.plt == 0))
*reloc_addr += l_addr;
else
...
使用延迟 PLT 绑定时(默认情况(。