4.可执行文件的格式(ELF格式)详解

下一讲介绍可执行程序的装载,也就是为可执行文件创建内存映像。在这之前我们要先了解可执行文件的格式,在Windows下可执行文件的格式一般为PE,而在Linux下可执行文件的格式为ELF。ELF文件的全称是Executable and Linkable Format,意为可执行的、可连接的格式。

ELF文件分为三类:

1.可重定位(relocabtable)文件,保存着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者是一个共享文件。

2.可执行(executable)文件,保存着一个用来执行的程序,该文件指出了exec(BA_OS)如何来创建程序进程映像。

3.共享object文件,保存着代码和合适的数据,用来被下面的两个链接器链接。第一个是链接编辑器(静态链接),可以和其他的可重定位和共享object文件一起来创建object文件;第二个是动态链接器,联合一个可执行文件和其他的共享object文件来创建一个进程印象。

注意,ELF文件中是二进制兼容的文件(ABI,应用程序二进制接口),也就是说ELF文件已经是适应到某一种CPU体系结构的二进制文件了。可以这样来理解:ELF文件是经过编译或链接生成的文件,而编译或链接必须指定具体的CPU体系结构,故ELF文件是针对某一种CPU体系结构的二进制文件。


有位大神已经对ELF文件做了详细的介绍,我就不班门弄斧了,直接转载过来,并结合我以上的分析以供其他童鞋参考。

转载自:点击打开链接

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

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

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

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

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

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

[plain]  view plain  copy
  1. .section .data  
  2. data_items:  
  3.     .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0  
  4.   
  5. .section .text  
  6. .globl _start  
  7. _start:  
  8.     movl $0, %edi  
  9.     movl data_items(,%edi,4), %eax  # data_items+ 4*(edi) --> eax  
  10.     movl %eax, %ebx         # (eax) --> ebx  
  11. start_loop:             # ebx store the max value  
  12.     cmpl $0, %eax     
  13.     je loop_exit  
  14.     incl %edi  
  15.     movl data_items(,%edi,4), %eax  # data_items+ 4*(edi) --> eax  
  16.     cmpl %ebx, %eax           
  17.     jle start_loop          # eax <= ebx  
  18.     movl %eax, %ebx         # eax > ebx  
  19.     jmp start_loop  
  20.   
  21. loop_exit:  
  22.     movl $1, %eax           # exit system call.  
  23.     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文件

[plain]  view plain  copy
  1. $ du -b max.o  
  2. 704 max.o    #此elf文件大小为704B  
  3. $ readelf -a max.o    #读取elf文件  
  4. ELF Header:  
  5.   Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00   
  6.   Class:                             ELF32  
  7.   Data:                              2's complement, little endian  
  8.   Version:                           1 (current)  
  9.   OS/ABI:                            UNIX - System V  
  10.   ABI Version:                       0  
  11.   Type:                              REL (Relocatable file)  
  12.   Machine:                           Intel 80386                     #运行机器  
  13.   Version:                           0x1  
  14.   Entry point address:               0x0  
  15.   Start of program headers:          0 (bytes into file)  
  16.   Start of section headers:          200 (bytes into file)           #section headers table在文件中的偏移  
  17.   Flags:                             0x0  
  18.   Size of this header:               52 (bytes)                      #elf header在文件中占了52个字节  
  19.   Size of program headers:           0 (bytes)  
  20.   Number of program headers:         0                               #文件中无program headers  
  21.   Size of section headers:           40 (bytes)                      #section headers table 中的每个section header descriptor有40B  
  22.   Number of section headers:         8                               #文件中有8个section headers  
  23.   Section header string table index: 5  
  24.   
  25. Section Headers:  
  26.   [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al  
  27.   [ 0]                   NULL            00000000 000000 000000 00      0   0  0  
  28.   [ 1] .text             PROGBITS        00000000 000034 00002a 00  AX  0   0  4    #这是我们在max.s中定义的section, .text section  
  29.   [ 2] .rel.text         REL             00000000 0002b0 000010 08      6   1  4  
  30.   [ 3] .data             PROGBITS        00000000 000060 000038 00  WA  0   0  4    #这是我们在max.s中定义的section, .data section,section size 为 0x38B,即56B(14*4B)  
  31.   [ 4] .bss              NOBITS          00000000 000098 000000 00  WA  0   0  4  
  32.   [ 5] .shstrtab         STRTAB          00000000 000098 000030 00      0   0  1    #.shstrtab 存放各section的名字,比如".text" ".data"  
  33.   [ 6] .symtab           SYMTAB          00000000 000208 000080 10      7   7  4    #.symtab 存放所有section中定义的的符号名字,比如 "data_items","start_loop"  
  34.   [ 7] .strtab           STRTAB          00000000 000288 000028 00      0   0  1  
  35. Key to Flags:  
  36.   W (write), A (alloc), X (execute), M (merge), S (strings)  
  37.   I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)  
  38.   O (extra OS processing required) o (OS specific), p (processor specific)  
  39.   
  40. There are no section groups in this file.  
  41.   
  42. There are no program headers in this file.  
  43.   
  44. Relocation section '.rel.text' at offset 0x2b0 contains 2 entries:    #.rel.text 告诉链接器指令哪些地方需要定位,这里表示的是.text section中需要改动的地方,在section中的偏移是8和17  
  45.  Offset     Info    Type            Sym.Value  Sym. Name  
  46. 00000008  00000201 R_386_32          00000000   .data  
  47. 00000017  00000201 R_386_32          00000000   .data  
  48.   
  49. There are no unwind sections in this file.  
  50.   
  51. Symbol table '.symtab' contains 8 entries:                            #符号就是为一个内存地址起了一个名字。  
  52.    Num:    Value  Size Type    Bind   Vis      Ndx Name               #Ndx表示 符号所在的的section编号见Section Headers 中的[Nr]列  
  53.      0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND                    #Value 表示此符号在相应section中的偏移  
  54.      1: 00000000     0 SECTION LOCAL  DEFAULT    1   
  55.      2: 00000000     0 SECTION LOCAL  DEFAULT    3   
  56.      3: 00000000     0 SECTION LOCAL  DEFAULT    4   
  57.      4: 00000000     0 NOTYPE  LOCAL  DEFAULT    3 data_items  
  58.      5: 0000000e     0 NOTYPE  LOCAL  DEFAULT    1 start_loop  
  59.      6: 00000023     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit  
  60.      7: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _start             #这里_start 符号是GLOBAL的, 因为源代码中使用.globl _start 标明此符号为全局的  
  61.   
  62. 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

[plain]  view plain  copy
  1. max.o:     file format elf32-i386  
  2.   
  3.   
  4. Disassembly of section .text:  
  5.   
  6. 00000000 <_start>:  
  7.    0:   bf 00 00 00 00          mov    $0x0,%edi  
  8.    5:   8b 04 bd 00 00 00 00    mov    0x0(,%edi,4),%eax  
  9.    c:   89 c3                   mov    %eax,%ebx  
  10.   
  11. 0000000e <start_loop>:  
  12.    e:   83 f8 00                cmp    $0x0,%eax  
  13.   11:   74 10                   je     23 <loop_exit>  
  14.   13:   47                      inc    %edi  
  15.   14:   8b 04 bd 00 00 00 00    mov    0x0(,%edi,4),%eax  
  16.   1b:   39 d8                   cmp    %ebx,%eax  
  17.   1d:   7e ef                   jle    e <start_loop>  
  18.   1f:   89 c3                   mov    %eax,%ebx  
  19.   21:   eb eb                   jmp    e <start_loop>  
  20.   
  21. 00000023 <loop_exit>:  
  22.   23:   b8 01 00 00 00          mov    $0x1,%eax  
  23.   28:   cd 80                   int    $0x80  

看一下链接之后的代码

$ld  -o max max.o

$objdump -d max

[plain]  view plain  copy
  1. max:     file format elf32-i386  
  2.   
  3.   
  4. Disassembly of section .text:  
  5.   
  6. 08048074 <_start>:  
  7.  8048074:   bf 00 00 00 00          mov    $0x0,%edi  
  8.  8048079:   8b 04 bd a0 90 04 08    mov    0x80490a0(,%edi,4),%eax  
  9.  8048080:   89 c3                   mov    %eax,%ebx  
  10.   
  11. 08048082 <start_loop>:  
  12.  8048082:   83 f8 00                cmp    $0x0,%eax  
  13.  8048085:   74 10                   je     8048097 <loop_exit>  
  14.  8048087:   47                      inc    %edi  
  15.  8048088:   8b 04 bd a0 90 04 08    mov    0x80490a0(,%edi,4),%eax  
  16.  804808f:   39 d8                   cmp    %ebx,%eax  
  17.  8048091:   7e ef                   jle    8048082 <start_loop>  
  18.  8048093:   89 c3                   mov    %eax,%ebx  
  19.  8048095:   eb eb                   jmp    8048082 <start_loop>  
  20.   
  21. 08048097 <loop_exit>:  
  22.  8048097:   b8 01 00 00 00          mov    $0x1,%eax  
  23.  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文件

[plain]  view plain  copy
  1. $ readelf -a max  
  2. ELF Header:  
  3.   Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00   
  4.   Class:                             ELF32  
  5.   Data:                              2's complement, little endian  
  6.   Version:                           1 (current)  
  7.   OS/ABI:                            UNIX - System V  
  8.   ABI Version:                       0  
  9.   Type:                              EXEC (Executable file)          #类型变为可执行文件  
  10.   Machine:                           Intel 80386  
  11.   Version:                           0x1  
  12.   Entry point address:               0x8048074                       #elf文件的内存入口地址由0变为0x8048074了  
  13.   Start of program headers:          52 (bytes into file)            #program headers table 在文件中的偏移  
  14.   Start of section headers:          256 (bytes into file)           #section headers table 在文件中的偏移  
  15.   Flags:                             0x0  
  16.   Size of this header:               52 (bytes)  
  17.   Size of program headers:           32 (bytes)                      #program headers  
  18.   Number of program headers:         2                               #多了2个program headers  
  19.   Size of section headers:           40 (bytes)  
  20.   Number of section headers:         6                               #少了2个section headers  
  21.   Section header string table index: 3  
  22.    
  23. Section Headers:                                                     #与max.o文件对比可以发现少了.bss 和 .rel.text两个section headers  
  24.   [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al  
  25.   [ 0]                   NULL            00000000 000000 000000 00      0   0  0  
  26.   [ 1] .text             PROGBITS        08048074 000074 00002a 00  AX  0   0  4  
  27.   [ 2] .data             PROGBITS        080490a0 0000a0 000038 00  WA  0   0  4  
  28.   [ 3] .shstrtab         STRTAB          00000000 0000d8 000027 00      0   0  1  
  29.   [ 4] .symtab           SYMTAB          00000000 0001f0 0000a0 10      5   6  4  
  30.   [ 5] .strtab           STRTAB          00000000 000290 000040 00      0   0  1  
  31. Key to Flags:  
  32.   W (write), A (alloc), X (execute), M (merge), S (strings)  
  33.   I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)  
  34.   O (extra OS processing required) o (OS specific), p (processor specific)  
  35.   
  36. There are no section groups in this file.  
  37.   
  38. Program Headers:                                                              #此2个program headers 将被装入至内存中分别的2个物理页中  
  39.   Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align  
  40.   LOAD           0x000000 0x08048000 0x08048000 0x0009e 0x0009e R E 0x1000    #装入至物理页0x8048000~0x8049000  
  41.   LOAD           0x0000a0 0x080490a0 0x080490a0 0x00038 0x00038 RW  0x1000    #装入至物理页0x8049000~0x804a000  
  42.   
  43.  Section to Segment mapping:  
  44.   Segment Sections...  
  45.    00     .text   
  46.    01     .data   
  47.   
  48. There is no dynamic section in this file.  
  49.   
  50. There are no relocations in this file.  
  51.   
  52. There are no unwind sections in this file.  
  53.   
  54. Symbol table '.symtab' contains 10 entries:  
  55.    Num:    Value  Size Type    Bind   Vis      Ndx Name  
  56.      0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND   
  57.      1: 08048074     0 SECTION LOCAL  DEFAULT    1   
  58.      2: 080490a0     0 SECTION LOCAL  DEFAULT    2   
  59.      3: 080490a0     0 NOTYPE  LOCAL  DEFAULT    2 data_items  
  60.      4: 08048082     0 NOTYPE  LOCAL  DEFAULT    1 start_loop  
  61.      5: 08048097     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit  
  62.      6: 08048074     0 NOTYPE  GLOBAL DEFAULT    1 _start  
  63.      7: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start  
  64.      8: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS _edata  
  65.      9: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS _end  
  66.   
  67. No version information found in this file.  

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
elf(可执行链接格式)是一种用于存储可执行文件、共享库和核心转储文件的文件格式。它是操作系统级的标准格式,用于各种UNIX类操作系统中。elf中文版手册对elf格式进行了详细的解释和说明。 该手册主要包含以下几个方面的内容: 1. ELF概述:介绍了ELF格式的起源、历史和用途。解释了ELF文件的结构,包括头部、节区、程序头表等,以及其在共享库、可执行文件和核心转储文件中的使用。 2. ELF文件头:详细描述了ELF文件头的结构和字段。解释了每个字段的含义,如文件类型、入口点、节区表偏移等。描述了不同类别的ELF文件头的差异。 3. 节区:对ELF格式中的节区进行了详细的讲解。解释了不同类型的节区,如代码节区、数据节区、符号节区等。说明了每个节区的作用和属性,以及如何查找和解析节区。 4. 程序头表:介绍了程序头表的结构和作用。解释了程序头表中每个条目的含义,如段类型、段偏移、内存对齐等。说明了如何通过程序头表来定位并加载ELF文件中的段。 5. 符号表和重定位:详细说明了ELF文件中的符号表和重定位表的结构和用法。解释了如何解析符号表,查找和绑定符号。介绍了重定位表的作用,以及如何通过重定位表来修正链接地址。 6. 动态链接:讲解了ELF文件中的动态链接机制。解释了动态链接器的作用和原理。介绍了如何使用共享库,并解释了共享库的加载和重定位过程。 通过阅读elf中文版手册,可以更深入地了解ELF格式的细节和使用方法。它为程序员、系统管理员和操作系统开发者提供了一个重要的参考资料,帮助他们理解和使用ELF文件格式。这对于开发、调试和管理运行在UNIX类操作系统上的程序和库非常有价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值