说明
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_t
ELF文件头
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: 定义了与之相关联的节头表的相对索引。
最新回复