ELF 文件格式初步探索

ELF 文件格式初步探索

在Linux下编译一个汇编程序需要的工具为 as(汇编器) ld(链接器)

e.g: 一个非常简单的AT&T汇编程序

.section .data
.section .text

.globl _start

_start:
    movl $1, %eax
    movl $4, %ebx
    int $0x80

.section .globl 这种叫做 伪操作, .section将代码分为若干段

_start 是 symbol(符号)  符号在汇编程序中代表一个地址 .globl 表示一个符号会被链接器使用 as –32 将 汇编代码编译为 relocatable object file elf32_i386

 

解读ELF 文件格式

对于汇编 , 链接器来看 , elf文件是由 Section Header Table 描述的一系列Section 集合, 而执行一个ELF文件的时候, 在loader看来 它是由 Programer Header Table 描述的一系列Segment的集合

对于程序运行的时候 Program Header Table 是必须

程序链接的时候 Section Header Table 是必须的

对于一个在运行的时候动态链接的程序, Program Header Table 和 Section Header Table 都是需要的, 因为程序既要加载 也要动态链接

ELF文件可以简单分为以下部分:(通过readelf 打印出来的 一个汇编程序的 结构)

汇编代码如下:

.section .data
data_items:
    .long 1,2,3,4,5,6,7,8,9,10,0
    .globl _start
_start:
    movl $0, %edi
    movl data_items(,%edi,4), %eax
    movl %eax, %ebx
start_loop:
    cmpl $0, %eax
    je loop_exit
    incl %edi
    movl %ebx, %eax
    jle start_loop

    movl %eax, %ebx
    jmp start_loop

loop_exit:
    movl $1, %eax
    int $0x80

 

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:          356 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 5

ELF 文件由ELF Header 和各个Section组成 上面给出的文件是一个 *.o 文件的 ELF Header , 通过ELF Header 可以计算出Section Header 在文件中的位置

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 000023 00  AX  0   0  1
  [ 2] .rel.text   REL         00000000 00012c 000008 08   I  6   1  4
  [ 3] .data       PROGBITS    00000000 000057 00002c 00  WA  0   0  1
  [ 4] .bss        NOBITS      00000000 000083 000000 00  WA  0   0  1
  [ 5] .shstrtab   STRTAB      00000000 000134 000030 00      0   0  1
  [ 6] .symtab     SYMTAB      00000000 000084 000080 10      7   7  4
  [ 7] .strtab     STRTAB      00000000 000104 000028 00      0   0  1

上面的内容是Section Header Table中含有的各个段的信息, 因为这是一个 Relocatable Object File(*.o) 还没有被链接,  因此所有的加载到内存中的地址都是 0 , 这里的Offset表示每个Section在 ELF文件中的位置, Size 为每个Section的大小, 通过这些信息,可以将ELF文件的结构做出来 如图所示:

 

通过hexdump -C 可以查看整个二进制文件的每一位内容, 有了上面的结构图, 可以很容易的和二进制内容对应起来

Relocation section '.rel.text' at offset 0x12c contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000008  00000201 R_386_32          00000000   .data

The decoding of unwind sections for machine type Intel 80386 is not currently supported.

Symbol table '.symtab' contains 8 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     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: 0000001c     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit
     7: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _start

 

注意, .bss段中没有任何信息, 在Section 中只占一个Section Header , 没有对应的Section, .shstrtab 用来保存各个Section的名字 .strtab 保存程序中的符号的名字  .rel.text 提供重定位需要的信息, 告诉linker 哪些地方要重定位 .symtab是符号表  Ndx表示这个符号在哪个Section内 Section的编号从 Section Header Table里查到 

.text 段保存的就是程序的汇编代码了

Executable ELF Format

上面我们说的都是Relocatable Object File 而不是 Executable File  我们下面来看一下 Object File 在链接之后成为Executable 有什么区别

我们将刚刚的 max.o 编译为 a.out 看ELF Header

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
  Start of program headers:          52 (bytes into file)
  Start of section headers:          476 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         2
  Size of section headers:           40 (bytes)
  Number of section headers:         6
  Section header string table index: 3

这个Header 和 之前的Header没有太大区别 , Type 变为了EXEC, 并且多了Program Header, 下面我们看一下Section Header

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 000023 00  AX  0   0  1
  [ 2] .data      PROGBITS    08049097 000097 00002c 00  WA  0   0  1
  [ 3] .shstrtab  STRTAB      00000000 0001b3 000027 00      0   0  1
  [ 4] .symtab    SYMTAB      00000000 0000c4 0000b0 10      5   7  4
  [ 5] .strtab    STRTAB      00000000 000174 00003f 00      0   0  1

对比前面的可以发现, section header少了一些 , .rel.text .bss 没有了, .rel.text只是用于链接的信息, 链接之后就没有用了 (如果使用 strip 会将符号表symtab 以及 保存符号的Section .strtab也删除, 只保留 .data 和 .shstrtab)

下面看一下多出来的Program Header

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x00097 0x00097 R E 0x1000
  LOAD           0x000097 0x08049097 0x08049097 0x0002c 0x0002c RW  0x1000

两个Program Header 一个是从 0x00 -> 0x97 (通过查看该Program 的Section Header Table 能够看出来 这部分内容就是ELF Header 加上 .text的内容 (根据Size和 Offset来看), 这个被加载到了0x08048000 这个虚拟地址处 (物理地址无意义) 而虽然0x08048000-0x08049097 的空间比 0x00 — 0x97占的空间要大, 但是第二个Segment却没有加载到紧接着它0x08048097之后, 原因是  MMU 的权限保护机制 权限保护是以页面为单位的,也就是说,每一个页面只能设置一种保护权限, 而 .text 以及 ELF Header 的权限是 Read Execute 而 .data段的权限是Read Write 二者权限不同, 应该放在不同的页面内, 内存每一个页面的大小是由Align列表示的, 在x86平台一页为 4K 大小(0x1000) 另外要注意的就是, 每个Segment在文件内的Offset多少,加载到页面内的时候, 要有相同的Offset, 所以第二个Segment加载到了 0x08049097 而不是 0x08049000

以上就是对ELF文件格式的一个简单的解读

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

3 × four =

This site uses Akismet to reduce spam. Learn how your comment data is processed.