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

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

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

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

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

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

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

 
  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文件

 
  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

 
  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

 
  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文件

 
  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.

猜你喜欢

转载自blog.csdn.net/li_Xing666/article/details/81487595