Chinaunix首页 | 论坛 | 博客
  • 博客访问: 104688
  • 博文数量: 11
  • 博客积分: 2520
  • 博客等级: 少校
  • 技术积分: 172
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-17 09:26
文章分类
文章存档

2009年(11)

我的朋友

分类: LINUX

2009-05-14 10:10:11

ELF简介

可执行链接格式 (Executable and Linking Format)最初由UNIX系统实验室(USL)发布,由于该格式强大灵活,ARM Linux采用了这种格式作为可执行文件的二进制格式。
ELF标准的目的是为软件开发人员提供一组二进制接口定义, 这些接口可以延伸到多种操作环境,从而减少重新编码、重新编译程序的需要。接口的内容包括目标模块格式、可执行文件格式以及调试记录信息与格式等。

ELF文件格式的格式

  • 可重定位文件(Relocatable File)   包含适合于与其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。
  • 可执行文件(Executable File)   包含适合于执行的一个程序,此文件规定了 exec() 如何创建一个程序的进程映像。 
  • 共享目标文件(Shared Object File)   包含可在两种上下文中链接的代码和数据。首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理,生成另外一个目标文件。其次,动态链接器(Dynamic Linker)可能将它与某个可执行文件以及其它共享目标一起组合,创建进程映像。

目标文件全部是程序的二进制表示,目的是直接在某种处理器上直接执行。

目标文件的格式

目标文件既要参与程序链接又要参与程序执行。出于方便性和效率考虑,目标文件格式提供了两种并行视图,分别反映了这些活动的不同需求。
文件开始处是一个  ELF  头部(ELF Header) ,用来描述整个文件的组织。节区部分包含链接视图的大量信息:指令、数据、符号表、重定位信息等等。
程序头部表(Program Header Table) ,如果存在的话,告诉系统如何创建进程映像。用来构造进程映像的目标文件必须具有程序头部表,可重定位文件不需要这个表。
节区头部表(Section Heade Table)包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。
NOTE:尽管图中显示的各个组成部分是有顺序的,实际上除了 ELF  头部表以外,其他节区和段都没有规定的顺序

下面用readelf工具读出目标文件hello.o的ELF Header和Section Header Table,然后我们逐段分析。

arm-linux-readelf -a hello.o

ELF Header中描述了操作系统是ARM Linux,体系结构是ARM。Section Header Table中有7个Section Header,在文件中的位置(或者叫文件地址)从112(0x70)开始,每个40字节,共280字节,到文件地址0x187结束。这个目标文件没有Program Header。文件类型是重定位文件,和实际情况相符。

从Section Header中读出各Section的描述信息,其中.text、.data、.bss是我们在汇编程序中声明的Section,而其它Section是汇编器自动添加的。Addr是这些段加载到内存中的地址(我们讲过程序中的地址都是虚拟地址),加载地址要在链接时填写,现在空缺,所以是全0。OffSize两列指出了各Section的文件地址,比如.text从文件地址0x34开始,一共0xc个字节,回去翻一下程序,.text中定义了3个4字节的整数,一共是12个字节,也就是0xc个。根据以上信息可以描绘出整个目标文件的布局。

目标文件的布局

起始文件地址 Section或Header
0 ELF Header
0x34 .text
0x40 .data
0x44 .bss(此段为空)
0x44 .shstrtab
0x70 Section Header Table
0x188 .symtab
0x228 .strtab

这个文件不大,我们直接用hexdump工具把目标文件的字节全部打印出来看。

hexdump -C hello.o

左边一列是文件中的地址,中间是每个字节的16进制表示,右边是把这些字节解释成ASCII码所对应的字符。中间有一个*号表示省略的部分全是0。.shstrtab.strtab这两个Section中存放的都是ASCII码.这些对照section headers的地址,和上图看出来。

我们知道,C语言的全局变量如果在代码中没有初始化,就会在程序加载时用0初始化。这种数据属于.bss段,在加载时它和.data段一样都是可读可写的数据,但是在ELF文件中.data段需要占用一部分空间保存初始值,而.bss段则不需要。也就是说,.bss段在文件中只占一个Section Header而没有对应的Section,程序加载时.bss段占多大内存空间在Section Header中描述。上例中在section headers中发现bss加载时使用1024个字节的空间。

Section Headers:
[Nr] Name  Type  Addr     Off    Size   ES Flg Lk Inf Al

...

[ 3] .bss NOBITS 00000000 000044 000400 00 WA  0   0   1

...

继续分析readelf输出的最后一部分,是从.rel.text.symtab这两个Section中读出的信息。

rel.text告诉链接器指令中的哪些地方需要重定位,这里没有。

.symtab是符号表。Ndx列是每个符号所在的Section编号,例如data在第3个Section里(也就是.bss),各Section的编号见Section Header Table。Value列是每个符号所代表的地址,在目标文件中,符号地址都是相对于该符号所在Section的相对地址。比如data在bss第一个所以是0,sqr在.text中第一个所以也是0。Bind这一列可以看出_relocation、num、sqr这个符号是GLOBAL的,而其它符号是LOCAL的,GLOBAL符号是在汇编程序中用.globl指示声明过的符号。

.text段没有分析,objdump工具可以把程序中的机器指令反汇编(Disassemble),那么反汇编的结果是否跟原来写的汇编代码一模一样呢?我们对比分析一下。

bigtree@just:~/notebook$ arm-linux-objdump -dS hello.o

hello.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <sqr>:
   0: e0010090 mul r1, r0, r0
   4: e1a00001 mov r0, r1
   8: e1a0f00e mov pc, lr

左边是机器指令的字节,右边是反汇编结果。一般汇编过后符号都要改变成地址。

 

阅读(2548) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~