宏观结构
运行视图:分为若干个程序段(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 typedef uint16_t Elf32_Half;typedef uint16_t Elf64_Half;typedef uint32_t Elf32_Word;typedef int32_t Elf32_Sword;typedef uint32_t Elf64_Word;typedef int32_t Elf64_Sword;typedef uint64_t Elf32_Xword;typedef int64_t Elf32_Sxword;typedef uint64_t Elf64_Xword;typedef int64_t Elf64_Sxword;typedef uint32_t Elf32_Addr;typedef uint64_t Elf64_Addr;typedef uint32_t Elf32_Off;typedef uint64_t Elf64_Off;typedef uint16_t Elf32_Section;typedef uint16_t Elf64_Section;typedef Elf32_Half Elf32_Versym;typedef Elf64_Half Elf64_Versym;
魔数:[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]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; } Elf32_Ehdr; typedef struct { unsigned char e_ident[EI_NIDENT]; Elf64_Half e_type; Elf64_Half e_machine; Elf64_Word e_version; Elf64_Addr e_entry; Elf64_Off e_phoff; Elf64_Off e_shoff; Elf64_Word e_flags; Elf64_Half e_ehsize; Elf64_Half e_phentsize; Elf64_Half e_phnum; Elf64_Half e_shentsize; Elf64_Half e_shnum; Elf64_Half e_shstrndx; } Elf64_Ehdr;
一般在文件开头紧邻 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; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; } Elf32_Phdr; typedef struct { Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf64_Xword p_align; } Elf64_Phdr;
一般在文件末尾,起始偏移量由 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; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; } Elf32_Shdr; typedef struct { Elf64_Word sh_name; Elf64_Word sh_type; Elf64_Xword sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; Elf64_Xword sh_size; Elf64_Word sh_link; Elf64_Word sh_info; Elf64_Xword sh_addralign; Elf64_Xword sh_entsize; } 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 ---- ---- .plt ---- ---- .plt.got ---- ---- .plt.sec ---- ---- .text ---- ---- .fini ---- Segment: PT_LOAD r–---- .rodata ---- ---- .eh_frame_hdr ---- ---- .eh_frame ---- Segment: PT_LOAD rw- Segment: PT_GNU_RELRO rw----- .init_array ---- ---- .fini_array ---- SHdr 1: 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 #define SHT_PROGBITS 1 #define SHT_SYMTAB 2 #define SHT_STRTAB 3 #define SHT_RELA 4 #define SHT_HASH 5 #define SHT_DYNAMIC 6 #define SHT_NOTE 7 #define SHT_NOBITS 8 #define SHT_REL 9 #define SHT_SHLIB 10 #define SHT_DYNSYM 11 #define SHT_INIT_ARRAY 14 #define SHT_FINI_ARRAY 15 #define SHT_PREINIT_ARRAY 16 #define SHT_GROUP 17 #define SHT_SYMTAB_SHNDX 18 #define SHT_RELR 19 #define SHT_NUM 20 #define SHT_LOOS 0x60000000 #define SHT_GNU_ATTRIBUTES 0x6ffffff5 #define SHT_GNU_HASH 0x6ffffff6 #define SHT_GNU_LIBLIST 0x6ffffff7 #define SHT_CHECKSUM 0x6ffffff8 #define SHT_LOSUNW 0x6ffffffa #define SHT_SUNW_move 0x6ffffffa #define SHT_SUNW_COMDAT 0x6ffffffb #define SHT_SUNW_syminfo 0x6ffffffc #define SHT_GNU_verdef 0x6ffffffd #define SHT_GNU_verneed 0x6ffffffe #define SHT_GNU_versym 0x6fffffff #define SHT_HISUNW 0x6fffffff #define SHT_HIOS 0x6fffffff #define SHT_LOPROC 0x70000000 #define SHT_HIPROC 0x7fffffff #define SHT_LOUSER 0x80000000 #define SHT_HIUSER 0x8fffffff
.shstrtab
节名字符串表字符串表,存储ELF结构中所有的Section的名字。类型是 SHT_STRTAB
;由EHdr中的 e_shstrndx
确定本节的位置;
.dyntab
符号名字符串表类型 SHT_STRTAB
; 存储程序中的符号名,包含变量名、函数名等;
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; Elf32_Addr st_value; Elf32_Word st_size; unsigned char st_info; unsigned char st_other; Elf32_Section st_shndx; } Elf32_Sym; typedef struct { Elf64_Word st_name; unsigned char st_info; unsigned char st_other; Elf64_Section st_shndx; Elf64_Addr st_value; Elf64_Xword st_size; } Elf64_Sym;
.dynamic
动态链接节