mez*_*zda 11 c linux x86 assembly
要了解重定位的概念,我写了一个简单的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 }
Run Code Online (Sandbox Code Playgroud)
它的等效汇编代码,使用"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
Run Code Online (Sandbox Code Playgroud)
使用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
Run Code Online (Sandbox Code Playgroud)
我有以下问题:
1)从.rel.text部分的第二个条目,我能够理解.text部分(这里是0xfcffffff)的偏移量0x39处的值必须替换为与符号表的索引9相关联的符号的地址(&这就是printf).但我在这里无法清楚地理解0x02(ELF32_R_TYPE)的含义.R_386_PC32在这里指定了什么?任何人都可以清楚地解释其含义.
2)我也无法理解第一项.什么需要在.text部分的偏移量为0x29时替换,为什么不清楚这里.我想再次知道R_386_32的含义.我发现了一个pdf elf_format.pdf,但是我无法清楚地理解.rel.text部分中"Type"的含义.
3)我也想知道汇编inst"lea(%edx,%eax,1),%eax"的含义.虽然我找到了一个非常好的链接(LEA指令的目的是什么?),描述了lea的含义,但是lea的格式(括号内的3个arg)并不清楚.
如果有人能够清楚地解释上述问题的答案,将不胜感激.我仍在努力寻找这些问题的答案,尽管我已经尝试了很多谷歌.
还有一个问题.我已经显示了下面的偏移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'
Run Code Online (Sandbox Code Playgroud)
.rel.text表中第一个条目的info字段是0x05,表示符号表的索引.我已经在上面显示了索引5的符号表条目,但是无法理解它是如何告诉我们它是.rodata的.
nne*_*neo 14
1),2):R_386_32是一个重定位,它将符号的绝对32位地址放入指定的存储单元.R_386_PC32是一个重定位,它将符号的PC相关32位地址放入指定的内存位置.R_386_32对于静态数据很有用,如此处所示,因为编译器只是将重定位的符号地址加载到某个寄存器中,然后将其视为指针.R_386_PC32对函数引用很有用,因为它可以用作直接参数call.有关如何处理重定位的示例,请参阅elf_machdep.c.
3)lea (%edx,%eax,1),%eax意味着简单地%eax = %edx + 1*%eax用C语法表达.在这里,它基本上被用作add操作码的替代品.
编辑:这是一个例子.
假设您的代码从0x401000开始加载到内存中,该字符串"sum = %d\n"最终在0x401800(在该.rodata部分的开头),并且printf在libc中为0x1400ab80.
然后,R_386_320x29处的重定位将字节00 18 40 00置于0x401029(只需复制符号的绝对地址),使指令位于0x401028
401028: b8 00 18 40 00 mov $0x401800,%eax
Run Code Online (Sandbox Code Playgroud)
的R_386_PC32在0x39地方字节重定位43 9b c0 13在0x401039(值0x1400ab80 - 0x40103d =十六进制0x13c09b43),从而使该指令
401038: e8 43 9b c0 13 call $0x1400ab80 <printf>
Run Code Online (Sandbox Code Playgroud)
我们减去0x40103d以考虑%pc的值(后面是指令的地址call).
第一个重定位条目是"sum = ..."在设置调用的过程中获取指向格式字符串()的指针printf.由于该.rodata部分以及部分都是重定位的.text,因此对字符串和其他常量数据的引用将需要修复.
考虑到这一点,似乎R_386_32重定位处理数据,R_386_PC32处理代码地址,但ELF规范(我没有方便的副本)可能解释了各种细节.
该lea指令是编译器选择为此例程执行添加的指令.它本可以选择add或其他几种可能性,但这种形式lea似乎经常用于某些情况,因为它可以将加法与乘法相结合.指令的结果lea (%edx,%eax,1),%eax是%eax将得到的值%edx + 1 * %eax.1可以用一组有限的小整数代替.该lea指令的最初目的是"加载有效地址" - 获取基指针,索引和大小,并产生数组中元素的地址.但是,正如您所看到的,编译器可以选择将其用于其他事情,以及......