ELF文件格式详解

ELF文件格式详解

ELF文件格式详解

什么是ELF文件格式

ELF(Executable and Linkable Format)是一种用于应用程序、库和操作系统的二进制文件格式。它是目前主流的可执行文件格式,在大多数现代操作系统中广泛使用。

ELF文件格式定义了二进制文件的组织结构和加载方式,使得操作系统能够正确加载运行程序。它包含了程序的代码、数据、符号表、重定位表等信息。

ELF文件格式的结构

ELF文件格式可以分为三个部分:ELF头部、程序头部表和节头部表。

ELF头部(ELF Header)

ELF头部位于文件的开头,它包含了一些关键信息,用于描述整个ELF文件的结构。

ELF头部的结构如下:

typedef struct {
    unsigned char e_ident[16];      // ELF标识
    uint16_t e_type;                // 对象文件类型
    uint16_t e_machine;             // 目标体系结构
    uint32_t e_version;             // 版本号
    uint64_t e_entry;               // 程序入口地址
    uint64_t e_phoff;               // 程序头部表的偏移量
    uint64_t e_shoff;               // 节头部表的偏移量
    uint32_t e_flags;               // 处理器特定标志
    uint16_t e_ehsize;              // ELF头部的大小
    uint16_t e_phentsize;           // 程序头部表项的大小
    uint16_t e_phnum;               // 程序头部表的表项数目
    uint16_t e_shentsize;           // 节头部表项的大小
    uint16_t e_shnum;               // 节头部表的表项数目
    uint16_t e_shstrndx;            // 节头部表字符串表索引
} Elf64_Ehdr;

其中,e_ident数组用于识别ELF文件类型,e_type表示ELF文件的类型(可执行文件、共享库、目标文件等),e_machine表示目标体系结构(如x86、ARM、MIPS等),e_entry是程序入口地址,e_phoff是程序头部表的偏移量,e_shoff是节头部表的偏移量,e_phnume_shnum分别是程序头部表和节头部表的表项数目。

程序头部表(Program Header Table)

程序头部表位于ELF头部之后,用于描述如何将ELF文件中的各个段加载到内存中。

程序头部表的结构如下:

typedef struct {
    uint32_t p_type;                // 段类型
    uint32_t p_flags;               // 段标志
    uint64_t p_offset;              // 段在文件中的偏移量
    uint64_t p_vaddr;               // 段在内存中的虚拟地址
    uint64_t p_paddr;               // 段在内存中的物理地址
    uint64_t p_filesz;              // 段在文件中的长度
    uint64_t p_memsz;               // 段在内存中的长度(可能会大于文件长度)
    uint64_t p_align;               // 段在内存中的对齐方式
} Elf64_Phdr;

程序头部表中的每个表项对应一个段(Segment),段是ELF文件的基本组成单位。常见的段类型有可执行代码段(PT_LOAD)、数据段(PT_DYNAMIC)、动态链接表段(PT_INTERP)等。

节头部表(Section Header Table)

节头部表位于程序头部表之后,用于描述ELF文件中各个节的信息。

节头部表的结构如下:

typedef struct {
    uint32_t sh_name;               // 节名称在节头部字符串表中的索引
    uint32_t sh_type;               // 节类型
    uint64_t sh_flags;              // 节标志
    uint64_t sh_addr;               // 节在内存中的虚拟地址
    uint64_t sh_offset;             // 节在文件中的偏移量
    uint64_t sh_size;               // 节在文件中的长度
    uint32_t sh_link;               // 链接到的其他节的索引
    uint32_t sh_info;               // 附加信息
    uint64_t sh_addralign;          // 节在内存中的对齐方式
    uint64_t sh_entsize;            // 节中表项(如符号表、重定位表)的大小
} Elf64_Shdr;

节头部表中的每个表项对应一个节(Section),节是ELF文件中的数据块,用于存储不同类型的数据,如代码、数据、符号表等。常见的节类型有代码段(.text)、数据段(.data)、BSS段(.bss)、字符串表节(.strtab)、符号表节(.symtab)等。

ELF文件的加载和运行

操作系统通过解析ELF文件的结构,将ELF文件加载到合适的内存地址,并启动程序执行。

代码段加载

操作系统根据程序头部表中的段类型和偏移量,将代码段加载到内存中的合适位置。代码段是可执行代码的存储区域,对应ELF文件中的.text节。

数据段加载

操作系统同样根据程序头部表中的段类型和偏移量,将数据段加载到内存中。数据段用于存储程序需要使用的全局变量和静态变量等,对应ELF文件中的.data节和.bss节。

符号解析

程序中的符号(如函数、变量)需要在运行时能够正确地被链接和使用。符号解析的过程主要依赖于符号表和重定位表。

符号表记录了ELF文件中的符号信息,包括符号名、符号类型、所在节等。重定位表则用于指导链接器进行符号的重定位操作,使得程序能够正确地访问符号。

其他节的加载

除了代码段和数据段外,ELF文件中还可以包含其他类型的节,如重定位表节、字符串表节等。操作系统在加载完代码段和数据段后,会根据节头部表的信息,加载其他需要的节。

示例代码

下面是一个使用C语言编写的ELF文件的示例代码,可以生成一个简单的ELF可执行文件:

#include <stdio.h>

int main() {
    printf("Hello, ELF!\n");
    return 0;
}

编译并运行上述代码,可得到一个名为hello的ELF可执行文件。我们可以使用readelf命令查看该文件的结构:

我们可以使用readelf命令查看该文件的结构:

readelf -a hello

运行命令后,可以看到ELF文件的详细信息,包括ELF头部、程序头部表、节头部表等:

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:               0x401040
  Start of program headers:          64 (bytes into file)
  Start of section headers:          177040 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         28
  Section header string table index: 27

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text            PROGBITS         0000000000401000  00001000
       000000000000007b  0000000000000000  AX       0     0     16
  [ 2] .data            PROGBITS         0000000000602000  00002000
       000000000000000c  0000000000000000  WA       0     0     4
  [ 3] .bss             NOBITS           0000000000602010  0000200c
       0000000000000004  0000000000000000  WA       0     0     1
  [ 4] .rodata          PROGBITS         0000000000602014  00002010
       000000000000000f  0000000000000000   A       0     0     1
  [ 5] .eh_frame        PROGBITS         0000000000602024  00002024
       0000000000000048  0000000000000000   A       0     0     8
  [ 6] .shstrtab        STRTAB           0000000000000000  0000206c
       000000000000011d  0000000000000000           0     0     1
  [ 7] .symtab          SYMTAB           0000000000000000  0000208c
       00000000000005d0  0000000000000018          26    19     8

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程