linux 实例讲解elf文件

elf是一种文件格式,用于存储linux程序,它内部有什么信息呢?大概包括编制好的计算机指令,数据,计算机在需要的时候把这个文件读取到内存中,cpu就可以从内存中一条一条的读取指令来执行。要想明白elf的格式,应该先了解下计算机执行程序需要哪些信息,对此我们先来回顾下计算机系统的一些基础知识:

虚拟内存

对于32位的总线,linux系统给每个进程分配了4g的空间,其中 0xC0000000到0xFFFFFFFF 这个地址段是留给系统使用的,主要用于linux内核程序的运行, 用户可以使用3GB的空间从(0x00000000-0xBFFFFFFF)。当然这4g的内存是虚拟内存,一个系统有那么多进程,要是物理内存那还得了,操作系统的虚拟性也是其一重大特征,使用虚拟内存的好处就是各个进程之间的运行空间看起来是独立的,彼此相互不干扰。那么一个进程的创建过程是什么样的呢?

程序执行过程
  1. 用户请求运行程序时,操作系统会读取存储在磁盘上的可执行文件,(对于linux系统,这个文件就是elf格式文件),然后为用户分配4g的虚拟内存空间。
  2. 根据可执行文件的信息指示,把不同的文件内容放到为你分配的这3g虚拟内存。
  3. 根据可执行文件的信息指示,系统设置代码段和数据段寄存器
  4. 根据可执行文件的信息指示,跳转到用户的代码入口地址(一般就是我们的main函数)
  5. 从main开始,cpu一条条执行我们给的指令,处理我们的数据,直到程序结束。

我们看到在执行指令前有多次“根据可执行文件提示”,而学习elf就是学习到底是指示了哪些信息。

可执行的elf文件

elf文件分三种类型: 目标文件(.o)、可执行文件、动态库(.so),可执行文件一般分成,elf文件头、segment表、section表。下面我们通过一个例子一一介绍下:

  • elf文件头: 这个文件是对elf文件整体信息的描述,在32位系统下是56的字节,在64位系统下是64个字节。
  • segment表:这个表是加载指示器。
 .section .data

.global data_item

data_item:

.long 3,67,28

.section .text

.global _start

_start:

    mov $1,%eax

    mov $4,%ebx

    int $0x80

上面是一段简单的汇编代码,编译: as -o hello.o hello.s 链接: ld -o hello hello.o 完成之后执行readelf -h hello (-h 表示读取elf文件头的命令)

readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x4000b0   // 程序的入口地址
  Start of program headers:          64 (bytes into file)
  Start of section headers:          472 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes) 
  Size of program headers:           56 (bytes) //  头长度是56字节(32位系统是56字节)
  Number of program headers:         2
  Size of section headers:           64 (bytes)
  Number of section headers:         6
  Section header string table index: 3

对于程序的装载,我们关心这三项:

Entry point address:               0x4000b0   // 程序入口地址
 Start of program headers:          64 (bytes into file) // segment表在文件64字节偏移处
  Size of program headers:           56 (bytes)

上面内容告诉我们segment表在文件的64字节处,我们看看64字节处有什么内容:
执行readelf -l hello 输出segments信息,(-l 表示读取segment)

 readelf -l hello

Elf file type is EXEC (Executable file)
Entry point 0x4000b0
There are 2 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000000bc 0x00000000000000bc  R E    200000
  LOAD           0x00000000000000bc 0x00000000006000bc 0x00000000006000bc
                 0x000000000000000c 0x000000000000000c  RW     200000

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

我们看到程序有两个segment,分别是.text和.data,

  • .text的Offset是0,FileSiz是0x0,MemSiz是0xbc, VirtAddr是0x400000,Flags是R E,表示加载起将把elf文件中从0字节开始直到oxbc处的内容加载到虚拟内存中的0x400000处,占用0xbc长度的内存,设置该内存的权限是RE(可读,可执行),这一段的内容正好是elf头,segments table,和代码段,而elfheader的entry地址是0x4000b0,这个地址是代码段的起始地址。
  • .data的Offset是0,FileSiz是0xbc,MemSiz是0x0c, VirtAddr是0x6000bc,Flags是R W,表示加载起将把elf文件中从bc字节开始直到oxbc + 0xc处的内容加载到虚拟内存中的0x6000bc处,占用0x0c长度的内存。设置该内存的权限是RE(可读,可执行)
    这样系统就可以根据elf文件提供的这些信息创建进程了。

你可能感兴趣的:(linux 实例讲解elf文件)