读书频道 > 系统 > linux > 深度探索Linux操作系统系统构建和原理解析
3. 重定位表
2013-09-28 13:14:23     我来说两句 
收藏    我要投稿   
全书一共8章:第1章介绍了如何准备工作环境。在第2章中构建了编译工具链,这是后面构建操作系统各个组件的基础。在这一章中,不仅详细讲解了工具链的构建过程,而且还通过对编译链接过程的探讨,深入讨论了工具链  立即去当当网订购

在进行汇编时,在一个模块(这里我们将一个.c文件称为一个模块)内,如果引用了其他模块或库中的变量或者函数,汇编器并不会解析引用的外部符号。因为在汇编时,模块是独立编译的,所以对于引用的外部的符号一无所知。而且退一步说,在汇编时并没有为符号分配运行时地址(行文中有时也称为虚拟地址),所以即使汇编器找到了这些符号,也没有任何意义,这些符号的地址只是临时的,在进行链接时链接器才会为这些符号分配运行时地址。

因此,在目标文件的机器指令中,汇编器基本上是留“空”引用的外部符号的地址。然后,在链接时,在符号地址确定后,链接器再来修订这些位置,这个修订过程被称为重定位。当然除了编译时重定位,还有加载和运行时重定位,本章讨论前者,我们在第5章讨论后者。事实上,为了辅助链接器在链接时计算修订值,这些需要修订的位置并不是全部都置为0,有时这里填充的是一个Addend,这就是之所以使用引号将空引用起来的原因。下面我们将会看到这个Addend。

但是链接器并不能聪明到可以自动找到目标文件中引用外部符号的地方,所以在目标文件中需要建立一个表格,这个表格中的每一条记录对应的就是一个需要重定位的符号,这个表格通常称为重定位表,汇编器将为可重定位文件中每个包含需要重定位符号的段都建立一个重定位表。ELF标准规定,重定位表中的表项可以使用如下两种格式:
glibc-2.15/elf/elf.h:
typedef struct
{
  Elf32_Addr    r_offset;       /* Address */
  Elf32_Word    r_info;         /* Relocation type and symbol index */
} Elf32_Rel;
typedef struct
{
  Elf32_Addr    r_offset;       /* Address */
  Elf32_Word    r_info;         /* Relocation type and symbol index */
  Elf32_Sword   r_addend;       /* Addend */
} Elf32_Rela;

这两种格式唯一的不同是成员r_addend。这个成员一般是个常量,用来辅助计算修订值。如果使用了第一种格式,那么r_addend将被填充在引用外部符号的地址处,也就是前面所说的留“空”处。具体的体系结构可以选择适合自己的一种格式,或者两种格式都使用,只不过在不同的上下文中使用更合适的格式。IA32主要使用了前者,但是也在个别的情况下了使用了一点后者。

r_offset为需要重定位的符号在目标文件中的偏移。需要注意的是,对于目标文件与可执行文件或者动态库,这个值是不同的。对于目标文件,r_offset是相对于段的,是段内偏移;而对于可执行文件或者动态库,r_offset是虚拟地址。

r_info中包含重定位类型和此处引用的外部符号在符号表中的索引。根据符号在符号表中的索引,链接器就可以从符号表中解析出符号的地址。因为指令中包含多种不同的寻址方式,并且还要针对不同的情况,所以有多种不同的重定位类型。不同的重定位类型,重定位的方法也不同。在2.1.4节中讨论“符号重定位”时,我们将讨论编译时使用的典型的重定位类型,包括R_386_32和R_386_PC32。在第5章讨论动态重定位时,我们将讨论加载和运行时使用的典型的重定位类型R_386_GLOB_DAT和R_386_JMP_SLOT等。

了解了重定位的基本理论后,下面我们来看一下具体的实例。使用工具readelf查看目标文件hello.o的重定位表:
root@baisheng:~/demo# readelf -r hello.o
Relocation section '.rel.text' at offset 0x3c4 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
Relocation section '.rel.eh_frame' at offset 0x3d4 contains 1 entries:
 Offset     Info    Type             Sym.Value     Sym. Name
00000020  00000202 R_386_PC32        00000000      .text

根据输出可见,hello.o中“.text”段和“.eh_frame”段中都有符号需要重定位,所以建立了两重定位表。

在“.text”段的重定位表中,我们看到,目标文件hello.o引用的外部符号foo2和foo2_func分别占据表中的第一条和第二条重定位记录。根据前面目标文件hello.o的反汇编结果,foo2在偏移0xb处,foo2_func在偏移0x1b处,与这里的输出完全一致。

看过重定位表后,我们再来看看汇编器在目标文件hello.o中引用符号foo2和foo2_func处填充的Addend是什么。我们使用工具objdump查看目标文件hello.o:
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: c9                    leave 
  20: c3                    ret   

根据objdump的输出可见:

在偏移0xb处,对应的就是变量foo2的地址,汇编器填充的Addend是0。

在偏移0x1b处,对应的是函数foo2_func的地址,汇编器填充的Addend是“fcffffff”,因为IA32使用的是little-endian字节序,补码“fffffffc”对应的原码是4。

在引用符号foo2的位置,填充0是比较容易理解的,链接器只需要找到符号foo2的运行时地址替换这里的0就好了。但是在引用符号foo2_func的位置,为什么使用–4呢,这究竟是一个什么魔数?我们在2.1.4节中讨论“符号重定位”时,再讨论这个–4的由来。

点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:2. 翻译机器指令
下一篇:4. 符号表
相关文章
图文推荐
3.3.6 GNOME的软件管
3.3.5 GNOME的文件管
3.3.4 GNOME的窗口管
3.3.3 收藏夹和快捷
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站