现在的位置: 首页 > 综合 > 正文

elf文件格式分析

2014年02月07日 ⁄ 综合 ⁄ 共 7222字 ⁄ 字号 评论关闭

各种讲解elf文件格式一上来就是各种数据类型,看了半天却不知道这些数据类型是干啥的,所以咱就先找个例子直接上手,这样对elf文件格式有个具体而生动的了解。

然后再去看那些手册,就完全不惧了~。

我们使用一个汇编程序max.s并对其进行编译链接产生的两个elf文件来对比分析elf文件。

例子程序max.s来自《Linux C 一站式编程》。

ps:这是一本看完可以真正可以深入理解C语言精华的书,涵盖面极广,上到数据结构、linux系统、网络通信,下到编译链接、汇编语言、内存寻址。真的很好的哦亲。

汇编程序max.s用于取一组正整数的最大值,使用的是AT&T语法,程序源代码如下

.section .data
data_items:
	.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0

.section .text
.globl _start
_start:
	movl $0, %edi
	movl data_items(,%edi,4), %eax  # data_items+ 4*(edi) --> eax
	movl %eax, %ebx			# (eax) --> ebx
start_loop:				# ebx store the max value
	cmpl $0, %eax	
	je loop_exit
	incl %edi
	movl data_items(,%edi,4), %eax	# data_items+ 4*(edi) --> eax
	cmpl %ebx, %eax			
	jle start_loop			# eax <= ebx
	movl %eax, %ebx			# eax > ebx
	jmp start_loop

loop_exit:
	movl $1, %eax			# exit system call.
	int $0x80

程序解释:

在源代码中定义了2个section,一个是section名字叫.data,另一个section叫.text, 声明了_start为全局的符号。

在.data section中定义了一个符号data_items,在.text section中定义了3个符号_start 、 start_loop、loop_exit。其中 _start符号被定义为全局符号。

程序逻辑也很简单,依次遍历数组并比较就得出了最大值,将最大值存储在ebx中,最后使用系统调用退出。

编译

$as -o max.o max.s

链接

$ld -o max max.o

执行并测试程序

$./max

$echo $?

222

222就是max.s运行返回的最大值。

下面先来分析编译出的max.o文件

$ du -b max.o
704	max.o    #此elf文件大小为704B
$ readelf -a max.o    #读取elf文件
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386                     #运行机器
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          200 (bytes into file)           #section headers table在文件中的偏移
  Flags:                             0x0
  Size of this header:               52 (bytes)                      #elf header在文件中占了52个字节
  Size of program headers:           0 (bytes)
  Number of program headers:         0                               #文件中无program headers
  Size of section headers:           40 (bytes)                      #section headers table 中的每个section header descriptor有40B
  Number of section headers:         8                               #文件中有8个section headers
  Section header string table index: 5

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 00002a 00  AX  0   0  4    #这是我们在max.s中定义的section, .text section
  [ 2] .rel.text         REL             00000000 0002b0 000010 08      6   1  4
  [ 3] .data             PROGBITS        00000000 000060 000038 00  WA  0   0  4    #这是我们在max.s中定义的section, .data section,section size 为 0x38B,即56B(14*4B)
  [ 4] .bss              NOBITS          00000000 000098 000000 00  WA  0   0  4
  [ 5] .shstrtab         STRTAB          00000000 000098 000030 00      0   0  1    #.shstrtab 存放各section的名字,比如".text" ".data"
  [ 6] .symtab           SYMTAB          00000000 000208 000080 10      7   7  4    #.symtab 存放所有section中定义的的符号名字,比如 "data_items","start_loop"
  [ 7] .strtab           STRTAB          00000000 000288 000028 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

There are no program headers in this file.

Relocation section '.rel.text' at offset 0x2b0 contains 2 entries:    #.rel.text 告诉链接器指令哪些地方需要定位,这里表示的是.text section中需要改动的地方,在section中的偏移是8和17
 Offset     Info    Type            Sym.Value  Sym. Name
00000008  00000201 R_386_32          00000000   .data
00000017  00000201 R_386_32          00000000   .data

There are no unwind sections in this file.

Symbol table '.symtab' contains 8 entries:                            #符号就是为一个内存地址起了一个名字。
   Num:    Value  Size Type    Bind   Vis      Ndx Name               #Ndx表示 符号所在的的section编号见Section Headers 中的[Nr]列
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND                    #Value 表示此符号在相应section中的偏移
     1: 00000000     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 SECTION LOCAL  DEFAULT    3 
     3: 00000000     0 SECTION LOCAL  DEFAULT    4 
     4: 00000000     0 NOTYPE  LOCAL  DEFAULT    3 data_items
     5: 0000000e     0 NOTYPE  LOCAL  DEFAULT    1 start_loop
     6: 00000023     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit
     7: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _start             #这里_start 符号是GLOBAL的, 因为源代码中使用.globl _start 标明此符号为全局的

No version information found in this file.

这是max.o文件详细的区域信息

结合readelf读出的信息,可以看到,在max.o的这个elf文件中,有3种类型的数据"区域",分别是elf header、section、section headers。

[1] elf header描述了这个elf文件的一些信息,如数据格式是big-endian 或者 little-endian、运行平台、section header 的个数等。

[2] section headers是一个表,表中的每个条目描述了一个section,如section在文件中的偏移,大小等。

[3] section中就是elf文件中“真正”的信息了。

下面来依次解释max.o中的各个section。

.data 和.text 属于PROGBITS类型的section,这是将来要正常运行的程序和代码。

.shstrtab和.strtab属于STRTAB类型的section,可以在文件中看到,它们都存着字符串,shstrtab存的是section的名字,而.strtab存的是符号的名字(符号表示一个固定的内存地址)。

.symtab是属于SYMTAB类型的section,它描述了.strtab中的符号在"内存"中对应的"内存地址",当然这里的还不是真正的内存地址,只是一个偏移量,等到链接之后就是真正的了。

.rel.text是属于REL类型的section,它为链接器正确链接提供了信息,在下面会详细解释。

$objdump -d max.o

max.o:     file format elf32-i386


Disassembly of section .text:

00000000 <_start>:
   0:	bf 00 00 00 00       	mov    $0x0,%edi
   5:	8b 04 bd 00 00 00 00 	mov    0x0(,%edi,4),%eax
   c:	89 c3                	mov    %eax,%ebx

0000000e <start_loop>:
   e:	83 f8 00             	cmp    $0x0,%eax
  11:	74 10                	je     23 <loop_exit>
  13:	47                   	inc    %edi
  14:	8b 04 bd 00 00 00 00 	mov    0x0(,%edi,4),%eax
  1b:	39 d8                	cmp    %ebx,%eax
  1d:	7e ef                	jle    e <start_loop>
  1f:	89 c3                	mov    %eax,%ebx
  21:	eb eb                	jmp    e <start_loop>

00000023 <loop_exit>:
  23:	b8 01 00 00 00       	mov    $0x1,%eax
  28:	cd 80                	int    $0x80

看一下链接之后的代码

$ld  -o max max.o

$objdump -d max

max:     file format elf32-i386


Disassembly 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,%ebx

08048082 <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

经过链接,.text代码可以真正的正确运行了,可以看到:

1.跳转指令中的跳转地址由文件偏移改成了实际的内存地址。

2.注意从.data section中取数的这句,max.o中是mov    0x0(,%edi,4),%eax ,链接后被换成了正确的mov 0x80490a0(,%edi,4),%eax。

链接后的文件max区域结构如图所示

可以看到,max文件中多了一个program headers区域,以及2个segment section。

program headers 是一张表,用于描述segment section。

segment section就是真正拷贝到内存并运行的代码。

映射图如下

再使用readelf查看经过链接后的elf文件

$ readelf -a max
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)          #类型变为可执行文件
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048074                       #elf文件的内存入口地址由0变为0x8048074了
  Start of program headers:          52 (bytes into file)            #program headers table 在文件中的偏移
  Start of section headers:          256 (bytes into file)           #section headers table 在文件中的偏移
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)                      #program headers
  Number of program headers:         2                               #多了2个program headers
  Size of section headers:           40 (bytes)
  Number of section headers:         6                               #少了2个section headers
  Section header string table index: 3
 
Section Headers:                                                     #与max.o文件对比可以发现少了.bss 和 .rel.text两个section headers
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08048074 000074 00002a 00  AX  0   0  4
  [ 2] .data             PROGBITS        080490a0 0000a0 000038 00  WA  0   0  4
  [ 3] .shstrtab         STRTAB          00000000 0000d8 000027 00      0   0  1
  [ 4] .symtab           SYMTAB          00000000 0001f0 0000a0 10      5   6  4
  [ 5] .strtab           STRTAB          00000000 000290 000040 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:                                                              #此2个program headers 将被装入至内存中分别的2个物理页中
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x0009e 0x0009e R E 0x1000    #装入至物理页0x8048000~0x8049000
  LOAD           0x0000a0 0x080490a0 0x080490a0 0x00038 0x00038 RW  0x1000    #装入至物理页0x8049000~0x804a000

 Section to Segment mapping:
  Segment Sections...
   00     .text 
   01     .data 

There is no dynamic section in this file.

There are no relocations in this file.

There are no unwind sections in this file.

Symbol table '.symtab' contains 10 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 08048074     0 SECTION LOCAL  DEFAULT    1 
     2: 080490a0     0 SECTION LOCAL  DEFAULT    2 
     3: 080490a0     0 NOTYPE  LOCAL  DEFAULT    2 data_items
     4: 08048082     0 NOTYPE  LOCAL  DEFAULT    1 start_loop
     5: 08048097     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit
     6: 08048074     0 NOTYPE  GLOBAL DEFAULT    1 _start
     7: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
     8: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
     9: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS _end

No version information found in this file.

抱歉!评论已关闭.