分享

符号重定位理解

 renhl252 2015-02-07

  1.测试源代码
//foo1.c:
int foo1 = 10;
void foo1_func()
{
    int ret = foo1;
}

//foo2.c:
int foo2 = 20;
void foo2_func(int x)
{
    int ret = foo2;
}

//hello.c:
#include <stdio.h>
extern int foo2;
int main(int argc, char *argv[])
{
    foo2 = 5;
    foo2_func(50);
    return 0;
}

2.链接时,在第一阶段完成后,目标文件已经合并完成,并且已经为符号分配了运行时地址,链接器将进行符号重定位。

3.查看重定位的符号
模块hello.o中有两处需要重定位,一处是偏移0xb处的变量foo2,另外一处是偏移0x1b处的函数foo2_func。汇编器已经将这两处需要重定位的符号记录在了重定位表中。
root@baisheng:~/demo# readelf -r hello.o
Relocation section '.rel.text' at offset 0x3c8 contains 2 entries:
 Offset     Info    Type             Sym.Value  Sym. Name
0000000b  00000901 R_386_32          00000000   foo2
0000001b  00000a02 R_386_PC32        00000000   foo2_func
...

符号foo2的重定位类型是R_386_32,ELF标准规定的计算修订值的公式是:
S + A
其中,S表示符号的运行时地址(绝对地址),A就是汇编器填充在引用外部符号处的Addend。

符号foo2_func的重定位类型是R_386_PC32,ELF标准规定的计算修订值的公式是:
S + A - P
其中S、A的意义与前面完全相同,P为修订处的运行时地址或者偏移。对于目标文件,P为修订处在段内的偏移。对于可执行文件和动态库,P为修订处的运行时地址。

4.查看符号绝对地址
首先我们先来确定S。运行时地址在链接时才分配,因此,变量foo2和函数foo2_func的运行时地址在链接后的可执行文件hello的符号表中:
root@baisheng:~/demo# readelf -s hello | grep foo2
    38: 00000000     0 FILE    LOCAL  DEFAULT  ABS foo2.c
    53: 0804a020     4 OBJECT  GLOBAL DEFAULT   24 foo2
    68: 08048414    16 FUNC    GLOBAL DEFAULT   13 foo2_func

可见,符号foo2的运行时地址为0x0804a020(即符号的绝对地址),符号foo2_func的运行时地址是0x08048414(即符号的绝对地址)。

5.查看符号的临时填充地址,即Addend
接下来,我们再来看看汇编器为这两个符号填充的Addend是多少。我们使用工具objdump反汇编hello.o,其中黑体标识的分别是汇编器在引用foo2和foo2_func的地址处填充的Addend:
root@baisheng:~/demo# objdump -d hello.o
hello.o:     file format elf32-i386
Disassembly of section .text:
00000000 <main>:
   0: 55                    push   %ebp
   1: 89 e5                 mov    %esp,%ebp
   3: 83 e4 f0              and    $0xfffffff0,%esp
   6: 83 ec 10              sub    $0x10,%esp
   9: c7 05 00 00 00 00 05  movl   $0x5,0x0
  10: 00 00 00
  13: c7 04 24 32 00 00 00  movl   $0x32,(%esp)
  1a: e8 fc ff ff ff        call   1b <main+0x1b>
  1f: b8 00 00 00 00        mov    $0x0,%eax
  24: c9                    leave
  25: c3                    ret  

根据输出可见,汇编器在引用符号foo2处(偏移地址0x0b处)填充的Addend是四个00,在引用符号foo2_func处填充的Addend是fc ff ff ff,即–4。
于是,可执行文件hello中引用符号foo2的位置的修订值为:
S + A = 0x0804a020 + 0 = 0x0804a020

6.模块加载重定位的理解
所以在模块加载过程中重定位时:rel.r_offset在这里对应着0x0b,rel.r_info对应着foo2符号的索引值。即在这个节区0x0b处会引用符号foo2,所以这里要填充该符号的地址,但是在未链接时并不知道该符号的绝对地址,所以这里先是填充该符号的临时地址00 00 00 00 替代,到这里真正链接时再进行重定位,此时会得到符号的真正运行地址0x0804a020(sym->value),把符号真正的运行地址0x0804a020(sym->value)填写到偏移0x0b处,即替代先前的临时地址0x00000000,这样在该位置处就能正确引用到该符号了。

7.查看结果
我们反汇编可执行文件hello,来验证一下引用符号foo2处的值是否修订为我们计算的这个值:
root@baisheng:~/demo# objdump -d hello
hello:     file format elf32-i386
...
080483dc <main>:
 80483dc: 55                    push   %ebp
 80483dd: 89 e5                 mov    %esp,%ebp
 80483df: 83 e4 f0              and    $0xfffffff0,%esp
 80483e2: 83 ec 10              sub    $0x10,%esp
 80483e5: c7 05 20 a0 04 08 05  movl   $0x5,0x804a020
 80483ec: 00 00 00
 80483ef: c7 04 24 32 00 00 00  movl   $0x32,(%esp)
 80483f6: e8 19 00 00 00        call   8048414
                                         <foo2_func>
 80483fb: b8 00 00 00 00        mov    $0x0,%eax
 8048400: c9                    leave
 8048401: c3                    ret  
 8048402: 66 90                 xchg   %ax,%ax
...

注意偏移0x1b处,确实已经被链接器修订为0x0804a020了。

8.相对地址重定位
对于符号foo2_func的修订值,还需要变量P,即引用符号foo2_func处的运行时地址。根据可执行文件hello的反汇编代码可见,引用符号foo2_func的指令的地址是:
0x80483f6 + 1 = 0x80483f7
所以,可执行文件hello中引用符号foo2_func的位置的修订值为:
S + A – P = 0x08048414 + (-4) - 0x80483f7 = 0x19

观察hello的反汇编代码,从地址0x80483f7开始处的4字节,确实也已经被链接器修订为0x19。

9.结语
这里提醒一下读者,如果foo2_func占据的运行时地址小于main函数,那么这里foo2_func与PC的相对地址将是负数。在机器指令中,使用的是数的补码形式,所以一定要注意,以免造成困惑。

事实上,对于符号foo2使用的重定位类型R_386_32,是绝对地址重定位,链接器只要解析符号foo2的运行时地址替换修订处即可。而对于符号foo2_func,其使用的重定位类型是R_386_PC32,这是一个PC相对地址重定位。而当执行当前指令时,PC中已经加载了下一条指令的地址,并不是当前指令的地址,这就是在引用符号foo2_func处填充“–4”的原因。

我们看到,在链接时,链接器在需要重定位的符号所在的偏移处直接进行了编辑修订,所以人们通常也将链接器形象地称为“link editor”。
http://book./201309/33370.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多