首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 其他相关 >

ELF文件【转:给小弟我一个全局的视野】

2013-08-16 
ELF文件【转:给我一个全局的视野】转自http://learn.akae.cn/media/ch18s05.html5.?ELF文件 请点评ELF文件格

ELF文件【转:给我一个全局的视野】

转自http://learn.akae.cn/media/ch18s05.html

5.?ELF文件 请点评

ELF文件格式是一个开放标准,各种UNIX系统的可执行文件都采用ELF格式,它有三种不同的类型:

?

左 边是从链接器的视角来看ELF文件,开头的ELF Header描述了体系结构和操作系统等基本信息,并指出Section Header Table和Program Header Table在文件中的什么位置,Program Header Table在链接过程中用不到,所以是可有可无的,Section Header Table中保存了所有Section的描述信息,通过Section Header Table可以找到每个Section在文件中的位置。右边是从加载器的视角来看ELF文件,开头是ELF Header,Program Header Table中保存了所有Segment的描述信息,Section Header Table在加载过程中用不到,所以是可有可无的。从上图可以看出,一个Segment由一个或多个Section组成,这些Section加载到内存时 具有相同的访问权限。有些Section只对链接器有意义,在运行时用不到,也不需要加载到内存,那么就不属于任何Segment。注意Section Header Table和Program Header Table并不是一定要位于文件的开头和结尾,其位置由ELF Header指出,上图这么画只是为了清晰。

目标文件需要链接器做进一步处理,所以一定有Section Header Table;可执行文件需要加载运行,所以一定有Program Header Table;而共享库既要加载运行,又要在加载时做动态链接,所以既有Section Header Table又有Program Header Table。


这 个可执行文件很小,总共也不超过一页大小,但是两个Segment必须加载到内存中两个不同的页面,因为MMU的权限保护机制是以页为单位的,一个页面只 能设置一种权限。此外还规定每个Segment在文件页面内偏移多少加载到内存页面仍然要偏移多少,比如第二个Segment在文件中的偏移是0xa0, 在内存页面0x08049000中的偏移仍然是0xa0,所以从0x080490a0开始,这样规定是为了简化链接器和加载器的实现。从上图也可以看出.text段的加载地址应该是0x08048074_start符号位于.text段的开头,所以_start符号的地址也是0x08048074,从符号表中可以验证这一点。

原来目标文件符号表中的Value都是相对地址,现在都改成绝对地址了。此外还多了三个符号__bss_start_edata_end,这些符号在链接脚本中定义,被链接器添加到可执行文件中,链接脚本在第?1?节 “多目标文件的链接”介绍。

再看一下反汇编的结果:

$ objdump -d maxmax:     file format elf32-i386Disassembly of section .text:08048074 <_start>: 8048074:bf 00 00 00 00       mov    $0x0,%edi 8048079:8b 04 bd a0 90 04 08 mov    0x80490a0(,%edi,4),%eax 8048080:89 c3                mov    %eax,%ebx08048082 <start_loop>: 8048082:83 f8 00             cmp    $0x0,%eax 8048085:74 10                je     8048097 <loop_exit> 8048087:47                   inc    %edi 8048088:8b 04 bd a0 90 04 08 mov    0x80490a0(,%edi,4),%eax 804808f:39 d8                cmp    %ebx,%eax 8048091:7e ef                jle    8048082 <start_loop> 8048093:89 c3                mov    %eax,%ebx 8048095:eb eb                jmp    8048082 <start_loop>08048097 <loop_exit>: 8048097:b8 01 00 00 00       mov    $0x1,%eax 804809c:cd 80                int    $0x80

指令中的相对地址都改成绝对地址了。我们仔细检查一下改了哪些地方。首先看跳转指令,原来目标文件的指令是这样:

...  11:74 10                je     23 <loop_exit>...  1d:7e ef                jle    e <start_loop>...  21:eb eb                jmp    e <start_loop>...

现在改成了这样:

... 8048085:74 10                je     8048097 <loop_exit>... 8048091:7e ef                jle    8048082 <start_loop>... 8048095:eb eb                jmp    8048082 <start_loop>...

改了吗?其实只是反汇编的结果不同了,指令的机器码根本没变。为什么不用改指令就能跳转到新的地址呢?因为跳转指令中指定的是相 对于当前指令向前或向后跳多少字节,而不是指定一个完整的内存地址,内存地址有32位,这些跳转指令只有16位,显然也不可能指定一个完整的内存地址,这 称为相对跳转。这种相对跳转指令只有16位,只能在当前指令前后的一个小范围内跳转,不可能跳得太远,也有的跳转指令指定一个完整的内存地址,可以跳到任 何地方,这称绝对跳转,在第?4.2?节 “动态链接的过程”我们会看到这样的例子。

再看内存访问指令,原来目标文件的指令是这样:

...   5:8b 04 bd 00 00 00 00 mov    0x0(,%edi,4),%eax...  14:8b 04 bd 00 00 00 00 mov    0x0(,%edi,4),%eax...

现在改成了这样:

... 8048079:8b 04 bd a0 90 04 08 mov    0x80490a0(,%edi,4),%eax... 8048088:8b 04 bd a0 90 04 08 mov    0x80490a0(,%edi,4),%eax...

指令中的地址原本是0x00000000,现在改成了0x080490a0(注意是小端字节序)。那么链接器怎么知道要改这两处呢?是根据目标文件中的.rel.text段提供的重定位信息来改的:

...Relocation section '.rel.text' at offset 0x2b0 contains 2 entries: Offset     Info    Type            Sym.Value  Sym. Name00000008  00000201 R_386_32          00000000   .data00000017  00000201 R_386_32          00000000   .data...

第一列Offset的值就是.text段需要改的地方,在.text段中的相对地址是8和0x17,正是这两条指令中00 00 00 00的位置。



[28] Segment也可以翻译成“段”,为了避免混淆,在本书中只把Section称为段,而Segment直接用英文。

热点排行