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