【问题标题】:Is there an ELF equivalent of PE base relocations?是否有相当于 PE 基址重定位的 ELF?
【发布时间】:2019-06-10 18:28:11
【问题描述】:

我一直在查看一些 ELF 二进制文件的反汇编,我注意到了这一点:

   0000000000401020 <_start>:
      401020:   31 ed                   xor    ebp,ebp
      401022:   49 89 d1                mov    r9,rdx
      401025:   5e                      pop    rsi
      401026:   48 89 e2                mov    rdx,rsp
      401029:   48 83 e4 f0             and    rsp,0xfffffffffffffff0
      40102d:   50                      push   rax
      40102e:   54                      push   rsp
      40102f:   49 c7 c0 30 13 40 00    mov    r8,0x401330
      401036:   48 c7 c1 d0 12 40 00    mov    rcx,0x4012d0
      40103d:   48 c7 c7 72 12 40 00    mov    rdi,0x401272
      401044:   ff 15 a6 2f 00 00       call   QWORD PTR [rip+0x2fa6]        # 403ff0 <__libc_start_main@GLIBC_2.2.5>
      40104a:   f4                      hlt    
      40104b:   0f 1f 44 00 00          nop    DWORD PTR [rax+rax*1+0x0]

__libc_start_main 被调用时,我们将这三个立即值作为参数通过寄存器传递。这些显然是在__libc_start_main(包括main)中调用的函数指针。但这些是虚拟地址,我的理解是二进制文件在加载到内存并运行时的实际映射地址不一定相同。因此,这些函数指针可能无法反映它们在内存中的实际位置。

由于更熟悉 PE 文件,IMAGE_DIRECTORY_BASERELOC 部分为我们提供了IMAGE_BASE_RELOCATION 结构,可帮助我们调整这些常量值以反映新的图像库。但是对于 ELF 文件,我看不到任何等效项。我在这里错过了什么吗?加载 ELF 文件时如何修复这些地址?

【问题讨论】:

    标签: x86 elf portable-executable relocation


    【解决方案1】:

    我的理解是二进制文件在加载到内存并运行时的实际映射地址不一定相同。

    不,从这些地址中我们可以看到这是一个链接在ld 的默认基地址的非 PIE ELF 可执行文件。这是一个位置相关的可执行文件。

    可执行文件本身将始终加载到固定的虚拟地址,因此可以使用 32 位立即数而不是 RIP 相对 LEA 将静态地址放入寄存器。可执行文件本身的 ASLR 是不允许/不可能的。

    libc 是一个 ELF “共享对象”,可以是 ALSRed,因此通过 GOT 中的指针调用 __libc_start_main。在这个 CRT 启动代码的 gcc 源代码中,这可能看起来像 call *__libc_start_main@GOTPCREL(%rip)(AT&T 语法)。

    顺便说一句,我们可以从使用 7 字节 mov rdi, sign_extended_imm32(与 RIP-relative LEA 大小相同)而不是 5 字节 mov edi, imm32 的优化中看出这是手写 asm。 x86-64 System V ABI 中的默认非 PIE 代码模型将所有静态代码/数据放在虚拟地址空间的低 2GiB 中,因此静态地址可以使用零或符号扩展至 64 位。


    可以在随机基址加载的ELF“可执行文件”称为PIE(位置独立可执行文件)。在 ELF 细节方面,它们使用与共享库相同的 ELF“类型”,因此它们实际上是具有“入口点”并标记为可执行的 ELF 共享对象。

    现代 Linux 发行版默认使用 gcc 构建 PIE。请参阅32-bit absolute addresses no longer allowed in x86-64 Linux?(可重定位的 ELF 共享对象可以重定位在地址空间中的任何位置,不限于低 2GiB,因此对于 32 位绝对地址的运行时修复没有重定位类型。)

    对于 64 位绝对地址有一个重定位类型,因此(函数/代码指针的)跳转表仍然是可能的,10 字节 mov rdi, imm64 也是可能的,但这甚至比 RIP 相对 LEA 效率低如果不是因为 ELF 程序加载器或动态链接器不得不为这些重定位修改程序文本。

    例如readelf -a /bin/ls

    ELF Header:
      Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
      Class:                             ELF64
      Data:                              2's complement, little endian
      Version:                           1 (current)
      OS/ABI:                            UNIX - System V
      ABI Version:                       0
      Type:                              DYN (Shared object file)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      Entry point address:               0x5ae0
    ...
    

    注意 Type 字段:DYN,与 readelf -a /lib/libc.so.6 等实际库中的相同。入口点是一个相对地址,相对于它所映射的基地址。

    非 PIE 可执行文件(例如静态链接或使用 -fno-pie -no-pie 构建)如下所示:

    ELF Header:
      Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
      Class:                             ELF64
      Data:                              2's complement, little endian
      Version:                           1 (current)
      OS/ABI:                            UNIX - System V
      ABI Version:                       0
      Type:                              EXEC (Executable file)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      Entry point address:               0x401000
    

    注意Type: EXEC 和绝对入口点(在链接时由ld 选择)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-20
      • 2011-12-12
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 2013-08-20
      相关资源
      最近更新 更多