为了理解重定位的概念,我编写了一个简单的 chk.c 程序如下:
1 #include<stdio.h>
2 main(){
3 int x,y,sum;
4 x = 3;
5 y = 4;
6 sum = x + y;
7 printf("sum = %d\n",sum);
8 }
它的等效汇编代码,使用“objdump -d chk.o”是:
00000000 <main>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 e4 f0 and $0xfffffff0,%esp
6: 83 ec 20 sub $0x20,%esp
9: c7 44 24 1c 03 00 00 movl $0x3,0x1c(%esp)
10: 00
11: c7 44 24 18 04 00 00 movl $0x4,0x18(%esp)
18: 00
19: 8b 44 24 18 mov 0x18(%esp),%eax
1d: 8b 54 24 1c mov 0x1c(%esp),%edx
21: 8d 04 02 lea (%edx,%eax,1),%eax
24: 89 44 24 14 mov %eax,0x14(%esp)
28: b8 00 00 00 00 mov $0x0,%eax
2d: 8b 54 24 14 mov 0x14(%esp),%edx
31: 89 54 24 04 mov %edx,0x4(%esp)
35: 89 04 24 mov %eax,(%esp)
38: e8 fc ff ff ff call 39 <main+0x39>
3d: c9 leave
3e: c3 ret
和使用readelf看到的.rel.text部分如下:
Relocation section '.rel.text' at offset 0x360 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
00000029 00000501 R_386_32 00000000 .rodata
00000039 00000902 R_386_PC32 00000000 printf
基于此我有以下问题:
1) 从 .rel.text 部分的第二个条目,我能够理解 .text 部分中偏移量 0x39 处的值(此处为 0xfcffffff)必须替换为与符号的索引 9 关联的符号的地址表(& 结果是 printf)。但是我在这里无法清楚地理解0x02(ELF32_R_TYPE)的含义。 R_386_PC32 在这里指定了什么?谁能解释清楚它的意思。
2) 我也无法理解第一个条目。在 .text 部分的 0x29 偏移处需要替换什么,为什么这里不清楚。我再次想知道 R_386_32 在这里的含义。我找到了一个 pdf elf_format.pdf,但我无法从中清楚地理解 .rel.text 部分中“类型”的含义。
3) 另外我想知道 assembly inst "lea (%edx,%eax,1),%eax"的含义。虽然我找到了一个很好的链接(What's the purpose of the LEA instruction?)来描述 lea 的含义,但是 lea 的格式(括号内的 3 个参数是什么)并不清楚。
如果有人能清楚地解释上述问题的答案,将不胜感激。尽管我已经用谷歌尝试了很多,但我仍在努力寻找这些问题的答案。
还有一个问题。我在下面显示了偏移量 5 和 9 的符号表条目。
Num: Value Size Type Bind Vis Ndx Name
5: 00000000 0 SECTION LOCAL DEFAULT 5
9: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf'
.rel.text 表中第一个条目的信息字段是0x05,表示符号表的索引。我已经在上面显示了索引 5 的符号表条目,但无法理解它如何告诉我们它是用于 .rodata 的。
最佳答案
1), 2): R_386_32
是将符号的绝对 32 位地址 放入指定内存位置的重定位。 R_386_PC32
是一个重定位,它将符号的PC 相对 32 位地址 放入指定的内存位置。 R_386_32
对于静态数据很有用,如此处所示,因为编译器只是将重定位的符号地址加载到某个寄存器中,然后将其视为指针。 R_386_PC32
对于函数引用很有用,因为它可以用作 call
的直接参数。参见 elf_machdep.c有关如何处理重定位的示例。
3) 如果用 C 语法表示,lea (%edx,%eax,1),%eax
表示简单的 %eax = %edx + 1*%eax
。在这里,它基本上被用作 add
操作码的替代品。
编辑:这是一个例子。
假设您的代码从 0x401000 开始加载到内存中,字符串 "sum = %d\n"
结束于 0x401800(在 .rodata
的开头) > 部分),并且 printf
在 libc 中位于 0x1400ab80。
然后,位于 0x29 的 R_386_32
重定位会将字节 00 18 40 00
放置在 0x401029(简单地复制符号的绝对地址),使指令位于 0x401028
401028: b8 00 18 40 00 mov $0x401800,%eax
在 0x39 处的 R_386_PC32
重定位将字节 43 9b c0 13
放在 0x401039 处(值 0x1400ab80 - 0x40103d = 0x13c09b43,十六进制),使该指令
401038: e8 43 9b c0 13 call $0x1400ab80 <printf>
我们减去 0x40103d 以计算 %pc 的值(这是 call
之后指令的地址)。
关于c - elf .rel.text 部分 R_386_32/R_386_PC32 的含义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12412064/