首页 > 解决方案 > 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 的调用printfwithp 'printf@got.plt'给出

$1 = (<text from jump slot in .got.plt, no debug info>) 0x1036 <printf@plt+6>

这是相应 PLT 条目中的第二条指令相对于节开头的偏移量。

启动并链接程序后startip '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'链接前的 A

$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 Application Binary Interface AMD64 Architecture Processor Supplement的阅读,第76 页是,这些重定位条目仅在LD_BIND_NOW非空时使用。还有其他我错过的搬迁条目吗?相对于 GOT 的最终地址重新设置偏移量的机制是什么?

标签: linuxelfglibc

解决方案


根据 Drepper 的How To Write Shared Libraries,动态链接器重定位了两种依赖项:

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

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


推荐阅读