一个简单的个人博客, 可以通过邮件eptansuo@126.com与我联系
说明ELF文件格式可以通过键入命令man 5 elf来查看，本文介绍不如man手册详尽严谨，具体内容还需查看手册！对于使用C语言读取ELF文件，可以参考我写的代码：https://github.com/EPTansuo/elfread与ELF的文件信息相关的结构体在<elf.h>中定义。首先介绍下该头文件中定义的一些数据类型，其中的N=32或64：ElfN_Addr Unsigned program address, uintN_t ElfN_Off Unsigned file offset, uintN_t ElfN_Section Unsigned section index, uint16_t ElfN_Versym Unsigned version symbol information, uint16_t Elf_Byte unsigned char ElfN_Half uint16_t ElfN_Sword int32_t ElfN_Word uint32_t ElfN_Sxword int64_t ElfN_Xword uint64_tELF文件头ELF的文件头由结构体ElfN_Ehdr（ELF32_Ehdr或ELF64_Ehdr）来描述。#define EI_NIDENT 16 typedef struct { unsigned char e_ident[EI_NIDENT]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; ElfN_Addr e_entry; ElfN_Off e_phoff; ElfN_Off e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; } ElfN_Ehdr;关于其成员的解释:e_ident: 16个字节的数组，包含文件的"magic numer"以及表征32位或64位的信息，还有大端小端的信息，以及OS/ABI的信息等。具体可查询man手册。e_type: 代表 了文件的类型，如可执行文件，动态链接库文件、核心转储文件等。e_machine：代表了架构的类型，如Intel 80386， ARM， PowerPC等。e_version: 文件的版本，但是只有EV_NONE(无效版本)和EV_CURRENT（当前版本）两个值。e_entry: 程序的入口地址e_phoff: 代表程序头表(program header table)偏移量。e_shoff: 代表节头表(section header table)偏移量。e_flags: 和处理器的编译选项或特性有关的标志。e_ehsize: 代表文件头的大小。e_phentsize: 代表程序头表的每个条目的大小，所有的条目都是相同的大小。e_phnum: 程序头表条目的数量。如果该值超过了0xffff，则该值设为0xffff，实际的条目数量存储在sh_info（见后文）中。e_shentsize: 代表节头表的每个条目的大小，所有的条目都是相同的大小。e_phnum: 节头表条目的数量。如果该值超过了0xff00，则该值设为0，实际的条目数量存储在sh_size（见后文）中。e_shstrndx: 节名称字符串表的索引。程序头（Program Header）程序头表由结构体Elf32_Phdr或者Elf64_Phdr组成的数组来存放。数组元素的大小由e_phentsize定义，元素的个数由e_phnum定义。typedef struct{ uint32_t p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; uint32_t p_filesz; uint32_t p_memsz; uint32_t p_flags; uint32_t p_align; } Elf32_Phdr; typedef struct{ uint32_t p_type; uint32_t p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; uint64_t p_filesz; uint64_t p_memsz; uint64_t p_align; } Elf64_Phdr; 注意：在32位和64位的系统中，p_flags在结构体中的位置是不同的，这是为了内存对齐，更高效处理数据。对其成员变量的解释p_type: 代表了段（Segment）的类型和如何去解析数组的元素信息。p_offset: 该值代表了段的偏移量。p_vaddr: 代表段第一个字节在内存中的虚拟地址。p_paddr: 在与物理寻址相关的系统上，为物理地址保留。p_filesz: 段在文件中的大小。p_memsz: 段在内存中的大小。p_flags: 代表了段的权限，如可读、可写、可执行。p_align: 代表了段在内存和文件中的对齐要求。节头(Section Header)节头表是Elf32_Shdr或Elf64_Shdr结构体的数组。其偏移量由ELF文件头的e_shoff成员变量给出。e_shnum代表其数组的元素个数，e_shentsize代表每个元素的大小。typedef struct{ uint32_t sh_name; uint32_t sh_type; uint32_t sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; uint32_t sh_size; uint32_t sh_link; uint32_t sh_info; uint32_t sh_addralign; uint32_t sh_entsize; } Elf32_Shdr; typedef struct{ uint32_t sh_name; uint32_t sh_type; uint64_t sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; uint64_t sh_size; uint32_t sh_link; uint32_t sh_info; uint64_t sh_addralign; uint64_t sh_entsize; } Elf64_Shdr;对于其成员的解释sh_name: 节字符头表的索引，代表了该节的名字。sh_type: 节头的类型。sh_flags: 节头的属性标志，如可写，可执行等。sh_addr: 节头在内存中的起始地址。sh_offset: 节头在文件中的偏移地址。sh_size: 节头的大小。sh_link: 通常用作索引，其解析功能由sh_type决定。sh_info: 一些额外的信息，其解析功能由sh_type决定。sh_addralign: 和对齐有关的要求。sh_entsize: 对于固定长度的节，这个代表了大小。当节的大小不固定时，则为0。符号表(Symbol Table)符号表存储了程序中定义和引用的所有符号信息（如函数等）。符号表的索引可以看成ElfN_Sym结构体数组的索引。typedef struct { uint32_t st_name; Elf32_Addr st_value; uint32_t st_size; unsigned char st_info; unsigned char st_other; uint16_t st_shndx; } Elf32_Sym; typedef struct { uint32_t st_name; unsigned char st_info; unsigned char st_other; uint16_t st_shndx; Elf64_Addr st_value; uint64_t st_size; } Elf64_Sym;成员变量解析st_name: 符号表的名字，实际上是对字符串表的一个索引。st_value: 符号的值。st_size: 符号的大小。如果符号大小未知或没有大小，则为0。st_info：低4位代表符号的类型，其余位代表绑定的信息。st_other: 定义了符号的可见性。st_shndx: 定义了与之相关联的节头表的相对索引。