ELF 文件格式

#宏观结构

运行视图:分为若干个程序段(Segment),一个段表示一种加载类型,包括需要加载到内存的地址和属性(是否可读, 是否可写, 是否可执行);

链接视图:分为若干个程序节(Section),表示不同作用的程序组成部分,用于链接,例如 .data, .rodata, .text;类似标签的作用;文件中的节区不能重叠;不允许一个字节存在于两个节区中的情况发生。

一般来说一个Segment会包含多个Section,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Segment 1: Text Segment
* .text
* .rodata
* .hash
* .dynsym
* .dynstr
* .plt
* .rel.got

Segment 2: Data Segment
* .data
* .dynamic
* .got
* .bss

#数据类型定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* Type for a 16-bit quantity.  */
typedef uint16_t Elf32_Half;
typedef uint16_t Elf64_Half;

/* Types for signed and unsigned 32-bit quantities. */
typedef uint32_t Elf32_Word;
typedef int32_t Elf32_Sword;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;

/* Types for signed and unsigned 64-bit quantities. */
typedef uint64_t Elf32_Xword;
typedef int64_t Elf32_Sxword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;

/* Type of addresses. */
typedef uint32_t Elf32_Addr;
typedef uint64_t Elf64_Addr;

/* Type of file offsets. */
typedef uint32_t Elf32_Off;
typedef uint64_t Elf64_Off;

/* Type for section indices, which are 16-bit quantities. */
typedef uint16_t Elf32_Section;
typedef uint16_t Elf64_Section;

/* Type for version symbol information. */
typedef Elf32_Half Elf32_Versym;
typedef Elf64_Half Elf64_Versym;

#ELF Header (Ehdr)

  • 魔数:[0x7f, 'E', 'L', 'F']
  • Phdr大小,Phdr个数
  • Shdr大小,Shdr个数
  • 入口地址 e_entry
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;

typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Architecture */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;

#Program Header (Phdr)

一般在文件开头紧邻 EHdr,有多个Phdr,起始偏移量由 e_phoff 指定,数量由 e_phnum 指定;

  • p_vaddr 段加载的虚拟地址
  • p_flags 段属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;

typedef struct
{
Elf64_Word p_type; /* Segment type */
Elf64_Word p_flags; /* Segment flags */
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment */
} Elf64_Phdr;

#Section Header (Shdr)

一般在文件末尾,起始偏移量由 e_shoff 指定,数量由 e_shnum 指定;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;

typedef struct
{
Elf64_Word sh_name; /* Section name (string tbl index) */
Elf64_Word sh_type; /* Section type */
Elf64_Xword sh_flags; /* Section flags */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Section size in bytes */
Elf64_Word sh_link; /* Link to another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;

#实例

  • EHdr
  • PHdr 1
  • PHdr 2
  • PHdr …
  • Segment: PT_PHDR (self)
  • Segment: PT_INTERP
    • ---- .interp ----
    • "/lib64/ld-linux-x86-64.so.2"
  • Segment: PT_NOTE r–
    • ---- .note.gnu.property ----
    • ---- .note.gnu.build-id ----
    • ---- .note.ABI-tag ----
  • Segment: PT_GNU_HASH
  • Segment: Symbol Table
  • Segment: PT_LOAD r-x
    • ---- .init ----
      • _init_proc
    • ---- .plt ----
      • sub_4020
      • sub_4030
      • ...
    • ---- .plt.got ----
      • free
      • strcmp
      • ...
    • ---- .plt.sec ----
      • ___ctype_toupper_loc
      • ...
    • ---- .text ----
      • loc_4D70
      • ...
    • ---- .fini ----
      • _term_proc
  • Segment: PT_LOAD r–
    • ---- .rodata ----
      • unk_19000
      • ...
    • ---- .eh_frame_hdr ----
    • ---- .eh_frame ----
  • Segment: PT_LOAD rw-
  • Segment: PT_GNU_RELRO rw-
    • ---- .init_array ----
      • 0x6E10
    • ---- .fini_array ----
      • 0x6DD0
  • SHdr 1:
    • ---- .data.rel.ro ----
  • SHdr …n
    • – Segment: PT_DYNAMIC rw-
    • stru_22A38
    • ---- .got ----
    • ---- .data ----
    • ---- .bss ----
  • Segment: Externs
  • Segment: String Table

#典型 Segment

#PT_LOAD

普通的需要加载的段

#PT_INTERP

包含一个C字符串,指向动态链接器的路径,

当创建一个可执行文件时,如果依赖其它的动态链接库,那么链接编辑器会在可执行文件的程序头中加入一个 PT_INTERP 项,告诉系统这里需要使用动态链接器,一般链接器为ld。

#PT_NOTE

附加信息,例如ABI版本,构建ID等

#PT_PHDR

包含一个程序头表的副本,用于动态链接器的加载

#典型 Section

Section 类型表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#define SHT_NULL   0  /* Section header table entry unused */
#define SHT_PROGBITS 1 /* Program data */
#define SHT_SYMTAB 2 /* Symbol table */
#define SHT_STRTAB 3 /* String table */
#define SHT_RELA 4 /* Relocation entries with addends */
#define SHT_HASH 5 /* Symbol hash table */
#define SHT_DYNAMIC 6 /* Dynamic linking information */
#define SHT_NOTE 7 /* Notes */
#define SHT_NOBITS 8 /* Program space with no data (bss) */
#define SHT_REL 9 /* Relocation entries, no addends */
#define SHT_SHLIB 10 /* Reserved */
#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
#define SHT_INIT_ARRAY 14 /* Array of constructors */
#define SHT_FINI_ARRAY 15 /* Array of destructors */
#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
#define SHT_GROUP 17 /* Section group */
#define SHT_SYMTAB_SHNDX 18 /* Extended section indices */
#define SHT_RELR 19 /* RELR relative relocations */
#define SHT_NUM 20 /* Number of defined types. */
#define SHT_LOOS 0x60000000 /* Start OS-specific. */
#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */
#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
#define SHT_SUNW_move 0x6ffffffa
#define SHT_SUNW_COMDAT 0x6ffffffb
#define SHT_SUNW_syminfo 0x6ffffffc
#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
#define SHT_HIOS 0x6fffffff /* End OS-specific type */
#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
#define SHT_LOUSER 0x80000000 /* Start of application-specific */
#define SHT_HIUSER 0x8fffffff /* End of application-specific */

#.shstrtab 节名字符串表

字符串表,存储ELF结构中所有的Section的名字。类型是 SHT_STRTAB;由EHdr中的 e_shstrndx 确定本节的位置;

#.dyntab 符号名字符串表

类型 SHT_STRTAB; 存储程序中的符号名,包含变量名、函数名等;

  • 这部分信息在进行 strip 后就会消失

#SHT_SYMTAB 符号表

包含

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;

typedef struct
{
Elf64_Word st_name; /* Symbol name (string tbl index) */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
} Elf64_Sym;

#.dynamic 动态链接节