是否有相当于PE基础重定位的ELF?

时间:2019-06-10 18:28:11

标签: x86 elf portable-executable relocation

我一直在查看一些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文件时如何固定这些地址?

1 个答案:

答案 0 :(得分:2)

  

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

不是,从这些地址中我们可以看到,这是链接到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相对LEA大小相同)而不是5字节mov edi, imm32的优化,这是手写的asm。 x86-64 System V ABI中的默认非PIE代码模型将所有静态代码/数据放入虚拟地址空间的低2GiB中,因此可以使用零地址或符号扩展为64位的静态地址。 / p>


可以在随机基地址中加载

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的效率低即使不是ELF程序加载器或动态链接程序必须为这些重定位修改程序文本的LEA。

例如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
...

请注意“类型”字段: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选择)。