Bal*_*ala 4 x86 linker x86-64 elf loader
我通过静态链接 libc 库在 x86 arch 中为一个简单的程序构建了一个可执行文件。正如预期的那样,该可执行文件的重定位表为空:
$ readelf -r 测试 此文件中没有重定位。 $
当我通过静态链接 libc 库为同一程序构建可执行文件时,在 x86_64 arch 中,重定位表不为空:
$ readelf -r 测试 偏移量 0x1d8 处的重定位节“.rela.plt”包含 12 个条目: 偏移信息类型符号。价值符号。名称 + 加数 0000006c2058 000000000025 R_X86_64_IRELATIV 000000000042de70 0000006c2050 000000000025 R_X86_64_IRELATIV 00000000004829d0 0000006c2048 000000000025 R_X86_64_IRELATIV 000000000042dfe0 0000006c2040 000000000025 R_X86_64_IRELATIV 000000000040a330 0000006c2038 000000000025 R_X86_64_IRELATIV 0000000000432520 0000006c2030 000000000025 R_X86_64_IRELATIV 0000000000409ef0 0000006c2028 000000000025 R_X86_64_IRELATIV 0000000000445ca0 0000006c2020 000000000025 R_X86_64_IRELATIV 0000000000437f40 0000006c2018 000000000025 R_X86_64_IRELATIV 00000000004323b0 0000006c2010 000000000025 R_X86_64_IRELATIV 0000000000430540 0000006c2008 000000000025 R_X86_64_IRELATIV 0000000000430210 0000006c2000 000000000025 R_X86_64_IRELATIV 0000000000432400 $
我在谷歌上搜索了重定位类型“R_X86_64_IRELATIV”,但我可以找到有关它的任何信息。那么有人可以告诉我这是什么意思吗?
我想如果我用 gdb 调试可执行文件我可能会找到答案。但它实际上提出了很多问题:)这是我的分析:
上表中的 Sym.Name 字段列出了一些 libc 函数的虚拟地址。当我 objdump'd 可执行文件“测试”时,我发现虚拟地址 0x430210 包含 strcpy 函数。加载在位置 0x6c2008 找到的相应 PLT 条目时,从 0x400326(下一条指令的虚拟地址,即设置解析器)更改为 0x0x443cc0(名为 __strcpy_sse2_unaligned 的 libc 函数的虚拟地址),我不知道为什么它会解析为不同的函数而不是 strcpy?我认为它是 strcpy 的不同变体。
完成此分析后,我意识到我错过了前面的基本点“加载静态可执行文件时动态链接器如何进入图片?” 我没有找到 .interp 部分,因此肯定不涉及动态链接器。然后我观察到,libc 函数“__libc_csu_irel()”修改了 PLT 条目,而不是动态链接器。
如果我的分析对任何人来说更有意义,请告诉我这到底是怎么回事。我很高兴知道其背后的原因。
多谢!!!
你是对的。这些重定位只是试图找出应该使用(不仅仅是)libc 函数的哪些实现。它们在链接时插入二进制文件main的函数执行之前解析。__libc_start_main
我将尝试解释这种重定位类型的工作原理。
我使用这段代码作为参考
//test.c
#include <stdio.h>
#include <string.h>
int main(void)
{
char tmp[10];
char target[10];
fgets(tmp, 10, stdin);
strcpy(target, tmp);
}
Run Code Online (Sandbox Code Playgroud)
使用 GCC 7.3.1 编译
gcc -O0 -g -no-pie -fno-pie -o test -static test.c
Run Code Online (Sandbox Code Playgroud)
重定位表( )的缩短输出readelf -r test:
Relocation section '.rela.plt' at offset 0x1d8 contains 21 entries:
Offset Info Type Sym. Value Sym. Name + Addend
...
00000069bfd8 000000000025 R_X86_64_IRELATIV 415fe0
00000069c018 000000000025 R_X86_64_IRELATIV 416060
Run Code Online (Sandbox Code Playgroud)
节标题的缩短输出 ( readelf -S test):
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
...
[19] .got.plt PROGBITS 000000000069c000 0009c000
0000000000000020 0000000000000008 WA 0 0 8
...
Run Code Online (Sandbox Code Playgroud)
它说该.got.plt部分位于地址上0x69c000。
重定位表中的每条记录都包含偏移量和加数两个重要信息。换句话说,加数是指向函数的指针(也称为间接函数),它不带参数并返回指向函数的指针。返回的指针放置在距重定位记录的偏移量上。
简单的重分配解析器实现:
void reolve_reloc(uintptr_t* offset, void* (*addend)())
{
//addend is pointer to function
*offset = addend();
}
Run Code Online (Sandbox Code Playgroud)
从这个答案开始的例子来看。重定位表中的最后一个加数指向0x416060函数的地址strcpy_ifunc。查看反汇编的输出:
0000000000416060 <strcpy_ifunc>:
416060: f6 05 05 8d 28 00 10 testb $0x10,0x288d05(%rip) # 69ed6c <_dl_x86_cpu_features+0x4c>
416067: 75 27 jne 416090 <strcpy_ifunc+0x30>
416069: f6 05 c1 8c 28 00 02 testb $0x2,0x288cc1(%rip) # 69ed31 <_dl_x86_cpu_features+0x11>
416070: 75 0e jne 416080 <strcpy_ifunc+0x20>
416072: 48 c7 c0 70 dd 42 00 mov $0x42dd70,%rax
416079: c3 retq
41607a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
416080: 48 c7 c0 30 df 42 00 mov $0x42df30,%rax
416087: c3 retq
416088: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
41608f: 00
416090: 48 c7 c0 f0 0e 43 00 mov $0x430ef0,%rax
416097: c3 retq
416098: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
41609f: 00
Run Code Online (Sandbox Code Playgroud)
选择strcpy_ifunc所有实现中的最佳替代方案strcpy并返回其上的指针。就我而言,它返回地址0x430ef0是
__strcpy_sse2_unaligned. 这个地址是十放在0x69c018的.glob.plt + 0x18
通常,重新分配的第一个想法是所有这些东西都处理动态解释器(ldd)。但在这种情况下,程序是静态链接的,并且该.interp部分是空的。__libc_start_main在本例中,它在属于 GLIBC 的函数中解析。除了解决重定位问题之外,该函数还负责将命令行参数传递给您main并执行其他一些操作。
当我弄清楚时,我有最后一个问题,如何__libc_start_main访问保存在 ELF 标头中的重定位表?第一个想法是它以某种方式打开正在运行的二进制文件以进行读取和处理。当然这是完全错误的。如果您查看可执行文件的程序头,您将看到类似以下内容 ( readlef -l test):
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000098451 0x0000000000098451 R E 0x200000
...
Run Code Online (Sandbox Code Playgroud)
该标头中的偏移量是相对于可执行文件的第一个字节的偏移量。所以程序头中的第一项表示的是将test文件的前 0x98451 字节复制到内存中。但偏移量 0x0 上是 ELF 标头。因此,通过代码段,它还会将 ELF 标头加载到内存中并__libc_start_main可以轻松访问它。
| 归档时间: |
|
| 查看次数: |
1489 次 |
| 最近记录: |