ELF64 加载器如何知道更新 .got.plt 中的初始地址?



请考虑以下程序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 的《如何编写共享库》,动态链接器重新定位了两种依赖项:

  1. 相对重定位:对同一对象中位置的依赖关系。链接器只是将对象的加载地址添加到目标目标的偏移量。
  2. 符号
  3. 重定位:基于复杂符号解析算法的更复杂和昂贵的过程。

对于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 绑定时(默认情况(。

最新更新