10

为了理解重定位的概念,我写了一个简单的 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)我也想知道汇编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' 

.rel.text 表中第一个条目的信息字段是 0x05,表示符号表的索引。我已经显示了上面索引 5 的符号表条目,但无法理解它是如何告诉我们它是用于 .rodata 的。

4

2 回答 2

13

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_32在 0x29 处的重定位会将字节00 18 40 00放在 0x401029 处(只是复制符号的绝对地址),使指令位于 0x401028

  401028:   b8 00 18 40 00          mov    $0x401800,%eax

R_386_PC320x39 处的重定位将字节放置43 9b c0 13在 0x401039 处(值 0x1400ab80 - 0x40103d = 0x13c09b43 十六进制),使该指令

  401038:   e8 43 9b c0 13          call   $0x1400ab80 <printf>

我们减去 0x40103d 来计算 %pc 的值(即 之后指令的地址call)。

于 2012-09-13T18:55:21.317 回答
5

"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指令的最初目的是“加载有效地址”——获取基指针、索引和大小并产生数组中元素的地址。但是,正如您所看到的,编译器也可以选择将它用于其他事情......

于 2012-09-13T18:43:12.827 回答