ELF文件
编译和链接
ELF代表Executable and Linkable Format,是类Unix平台最通用的二进制文件格式。下面三种文件的格式都是ELF。
- 目标文件
.o - 动态库文件
.so .o和.so链接得到的二进制可执行文件
编译链接与执行过程中的文件转换如下图所示。
文件结构
根据冯诺伊曼原理,程序有指令和数据构成,因此ELF文件存储的内容即代码(指令)+数据+其他元信息。
ELF文件是静态程序转换为进程的桥梁,结构概览如下图:
- FILE HEDER:描述整个文件的组织结构。
- Program Header Table:描述如何将 ELF 文件中的段映射到内存中,它为操作系统的加载器提供信息,告知哪些段需要被加载到内存中、它们的权限以及如何映射。
- Section Header Table:描述 ELF 文件中的各个节,提供了每个节的详细信息,如名称、大小、类型和位置等。
- Section / Segment:节从链接角度描述elf文件,段从内存加载角度描述elf文件。

ELF文件用于链接和加载两个阶段,有两个视图,链接视图和执行视图。
| 视图 | 存储内容 | 数据结构 | 使用阶段 | 文件格式 |
|---|---|---|---|---|
| 链接视图 | 静态程序,用节(Section)组织 | Section Header Table | 编译,链接 | .o ,.so |
| 执行视图 | 加载后到内存分布,用段(Segment)组织 | Program Header Table | 加载 | 可执行程序 |
Section 和 Segment 是 逻辑到物理的映射关系:
- 一个Segment对应多个Section
- 一个Section只能对应一个Segment
典型的对应关系:
| 执行段名称(Segments) | 包含的节(Sections) | 权限 |
|---|---|---|
| PT_LOAD(代码段) | .text,.rodata | R E |
| PT_LOAD(数据段) | .data,.bss | RW |
| PT_DYNAMIC | .dynamic,.got,.plt | RW |
| PT_INTERP | .interp | R |
| PT_NOTE | .note | R |
| PT_SHLIB | .shstrtab,.symtab | RW |
| PT_TLS | .tbss,.tdata | R,RW |
加载阶段,加载器会按照Segment来组织虚拟内存,构造一个进程的内存空间,完成一个静态文件到进程的转换。
ELF文件解析
基本思路:根据ELF Header中的元信息,跳转到对应部分进行解析。

readelf -l fileName
解析ELF文件的文件头,数据结构如下,逐个解析即可:
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;
| 成员 | 含义 | 备注 |
|---|---|---|
| e_ident | 文件信息 | 下标: [0.3]:魔数 4:文件类 5:数据编码 6:文件版本 7:补齐 |
| e_type | 文件类型 | ET_NONE,ET_REL,ER_EXEC,ET_DYN,ET_CORE |
| e_machine | 机器架构 | EM_NONE,EM_M32,EM_SPARC,EM_386,EM_68K,EM_88K,EM_860,EM_MIPS |
| e_version | 目标文件版本 | EV_NONE,EV_CURRENT |
| e_entry | 入口项地址 | 上文图中的Entry Point指针 |
| e_phoff | 程序头部表偏移 | Program Header Table Offset |
| e_shoff | 节头表偏移 | Section Header Table Offset |
| e_flags | 文件中与特定处理器相关的标志 | |
| e_ehsize | ELF 文件头部的字节长度 | ELF Header Size |
| e_phentsize | 程序头部表中每个表项的字节长度 | Program Header Entry Size |
| e_phnum | 程序头部表的项数 | Program Header Entry Number |
| e_shentsize | 节头的字节长度 | Section Header Entry Size |
| e_shnum | 节头表中的项数 | Section Header Number |
| e_shstrndx | 节头表中与节名字符串表相关的表项的索引值 | Section Header Table Index Related With Section Name String Table |
readelf -S fileName
解析ELF文件的节头表。
依照文件头信息得到节头表:(elf_header为Elf64_Ehdr类型指针)
-
获得节头表地址:
elf_header + elf_header->e_shoff -
遍历节头表:表大小:
elf_header->e_shnum -
节头表中每个元素的数据结构如下
typedef struct { Elf64_Word sh_name; /* Section name (string tbl index) */ Elf64_Word sh_type; /* Section type */ Elf64_Word sh_flags; /* Section flags */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ Elf64_Word sh_size; /* Section size in bytes */ Elf64_Word sh_link; /* Link to another section */ Elf64_Word sh_info; /* Additional section information */ Elf64_Word sh_addralign; /* Section alignment */ Elf64_Word sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr;
readelf -s fileName
解析ELF文件中符号表。
符号表作为一个节存储,遍历所有节,根据``elf_shdr->sh_type`判断是否为符号表,如果是则解析该节(elf_shdr为Elf64_Shdr类型指针)
-
节中元素数量:
elf_shdr->sh_size / elf_shdr->sh_entsize -
符号表作为节中元素结构如下:
typedef struct { Elf64_Word st_name; /* Symbol name (string tbl index) */ Elf64_Addr st_value; /* Symbol value */ Elf64_Word st_size; /* Symbol size */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility */ Elf64_Section st_shndx; /* Section index */ } Elf64_Sym;
程序执行结果
编译: gcc -o elf_reader elf_reader.c
./elf_reader -h a.out

./elf_reader -S a.out

./elf_reader -s a.out

源代码
#include <stdio.h> #include <stdlib.h> #include <elf.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> // 主要函数:进行ELF文件解析 void parse_elf_header(const Elf64_Ehdr *elf_header); //-h: 解析文件头 void parse_section_headers(const Elf64_Ehdr *elf_header); //-S: 解析节头表 void parse_symbol_table(const Elf64_Ehdr *elf_header); //-s: 解析符号表 // 辅助函数:格式化输出 const char* get_elf64_st_type_name(unsigned char info); const char* get_elf64_st_bind_name(unsigned char info); const char* get_elf64_st_visibility_name(unsigned char other); const char* get_section_type_name(Elf64_Word type); const char* get_section_flags_name(Elf64_Xword flags); const char* get_class_name(unsigned char class_value); const char* get_data_name(unsigned char data_value); const char* get_version_name(unsigned char version_value); const char* get_os_name(unsigned char os_value); const char* get_type_name(unsigned char type_value); const char* get_machine_name(unsigned char machine_value); void print_symbol_table(const char *strtab, Elf64_Sym *symbols, int count, const char *symtab_name); int main(int argc, char * argv[]) { // 获取程序参数 if (argc != 3) { fprintf(stderr, "Usage: %s <elf-file> <-h|-S|-s>n", argv[0]); exit(EXIT_FAILURE); } const char *option = argv[1]; const char *filename = argv[2]; // 打开文件 int fd = open(filename, O_RDONLY); if (fd == -1) { perror("Failed to open ELF file"); exit(EXIT_FAILURE); } // mmap映射 off_t file_size = lseek(fd, 0, SEEK_END); char *map = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) { perror("Memory mapping failed"); close(fd); exit(EXIT_FAILURE); } Elf64_Ehdr *elf_header = (Elf64_Ehdr *)map; // 处理不同参数对应的情况 if(strcmp(option, "-h") == 0) { parse_elf_header(elf_header); } else if(strcmp(option, "-S") == 0) { parse_section_headers(elf_header); } else if(strcmp(option, "-s") == 0) { parse_symbol_table(elf_header); } // 关闭文件 munmap(map, file_size); close(fd); return 0; } // -h: 解析文件头 void parse_elf_header(const Elf64_Ehdr *elf_header) { printf("ELF Header:n"); printf(" Magic: "); for (int i = 0; i < EI_NIDENT; ++i) { printf("%02X ", elf_header->e_ident[i]); } printf("n"); printf(" Class: %sn", get_class_name(elf_header->e_ident[EI_CLASS])); printf(" Data: %sn", get_data_name(elf_header->e_ident[EI_DATA])); printf(" Version: %sn", get_version_name(elf_header->e_ident[EI_VERSION])); printf(" OS/ABI: %sn", get_os_name(elf_header->e_ident[EI_OSABI])); printf(" Type: %sn", get_type_name(elf_header->e_type)); printf(" Machine: %sn", get_machine_name(elf_header->e_machine)); printf(" Version: %dn", elf_header->e_version); printf(" Entry point address: %#lxn", elf_header->e_entry); printf(" Start of program headers: %ld (bytes into file)n", elf_header->e_phoff); printf(" Start of section headers: %ld (bytes into file)n", elf_header->e_shoff); printf(" Flags: %#lxn", elf_header->e_flags); printf(" Size of this header: %d (bytes)n", elf_header->e_ehsize); printf(" Size of program headers: %d (bytes)n", elf_header->e_phentsize); printf(" Number of program headers: %dn", elf_header->e_phnum); printf(" Size of section headers: %d (bytes)n", elf_header->e_shentsize); printf(" Number of section headers: %dn", elf_header->e_shnum); printf(" Section header string table index: %dn", elf_header->e_shstrndx); } // -S: 解析节头表 void parse_section_headers(const Elf64_Ehdr *elf_header) { // 找到节头表和字符串表 Elf64_Shdr *sections = (Elf64_Shdr *)((char *)elf_header + elf_header->e_shoff); const char *strtab = (char *)elf_header + sections[elf_header->e_shstrndx].sh_offset; printf("Section Headers:n"); printf(" [Nr] Name Type Address Offsetn"); printf(" Size EntSize Flags Link Info Alignn"); // 遍历节头 for (int i = 0; i < elf_header->e_shnum; i++) { // 打印节号、名称、类型、地址、偏移 printf(" [%2d] %-17s %-16s %016lx %08lxn", i, &strtab[sections[i].sh_name], get_section_type_name(sections[i].sh_type), sections[i].sh_addr, sections[i].sh_offset); // 打印节大小、条目大小、标志、链接索引、信息、对齐 printf(" %016lx %016lx %-6s %4u %4u %5lun", sections[i].sh_size, sections[i].sh_entsize, get_section_flags_name(sections[i].sh_flags), sections[i].sh_link, sections[i].sh_info, sections[i].sh_addralign); } } // -s: 解析符号表 void parse_symbol_table(const Elf64_Ehdr *elf_header) { // 找到节头表指针和字符串表 Elf64_Shdr *sections = (Elf64_Shdr *)((char *)elf_header + elf_header->e_shoff); const char *strtab = (char *)elf_header + sections[elf_header->e_shstrndx].sh_offset; // 遍历每个节 for (int i = 0; i < elf_header->e_shnum; i++) { // 如果是符号表 if (sections[i].sh_type == SHT_SYMTAB) { // .symtab符号表 Elf64_Shdr *symtab = §ions[i]; Elf64_Sym *symbols = (Elf64_Sym *)((char *)elf_header + symtab->sh_offset); int count = symtab->sh_size / symtab->sh_entsize; // 符号个数 const char *symstrtab = (char *)elf_header + sections[symtab->sh_link].sh_offset; print_symbol_table(symstrtab, symbols, count, ".symtab"); } // 如果是动态符号表 else if (sections[i].sh_type == SHT_DYNSYM) { // .dynsym符号表 Elf64_Shdr *dynsymtab = §ions[i]; Elf64_Sym *dynsymbols = (Elf64_Sym *)((char *)elf_header + dynsymtab->sh_offset); int dynsym_count = dynsymtab->sh_size / dynsymtab->sh_entsize; // 动态符号个数 const char *dynsymstrtab = (char *)elf_header + sections[dynsymtab->sh_link].sh_offset; print_symbol_table(dynsymstrtab, dynsymbols, dynsym_count, ".dynsym"); } } } // 获取Class字段信息 const char* get_class_name(unsigned char class_value) { switch(class_value) { case ELFCLASS32: return "ELF32"; case ELFCLASS64: return "ELF64"; default: return "Unknown"; } } // 获取Data字段信息 const char* get_data_name(unsigned char data_value) { switch(data_value) { case ELFDATA2LSB: return "2's complement, little endian"; case ELFDATA2MSB: return "2's complement, big endian"; default: return "Unknown"; } } // 获取Version字段信息 const char* get_version_name(unsigned char version_value) { switch(version_value) { case 0: return "Invalid Version"; case 1: return "1 (current)"; default: return "Invalid Version"; } } // 获取OS字段信息 const char* get_os_name(unsigned char os_value) { switch(os_value) { case ELFOSABI_NONE: return "UNIX - System V"; case ELFOSABI_LINUX: return "Linux"; case ELFOSABI_SOLARIS: return "Solaris"; case ELFOSABI_FREEBSD: return "FreeBSD"; default: return "Others"; } } // 获取Type字段信息 const char* get_type_name(unsigned char type_value) { switch(type_value) { case ET_NONE: return "NONE (None)"; case ET_REL: return "REL (Relocatable file)"; case ET_EXEC: return "EXEC (Executable file)"; case ET_DYN: return "DYN (Shared object file)"; case ET_CORE: return "CORE (Core file)"; default: return "Unknown"; } } // 获取Machine字段信息 const char* get_machine_name(unsigned char machine_value) { switch(machine_value) { case EM_386: return "Intel 80386"; case EM_ARM: return "ARM"; case EM_X86_64: return "AMD x86-64"; case EM_AARCH64: return "ARM AARCH64"; default: return "Unknown"; } } // 解析节类型 const char* get_section_type_name(Elf64_Word type) { switch (type) { case SHT_NULL: return "NULL"; case SHT_PROGBITS: return "PROGBITS"; case SHT_SYMTAB: return "SYMTAB"; case SHT_STRTAB: return "STRTAB"; case SHT_RELA: return "RELA"; case SHT_HASH: return "HASH"; case SHT_DYNAMIC: return "DYNAMIC"; case SHT_NOTE: return "NOTE"; case SHT_NOBITS: return "NOBITS"; case SHT_REL: return "REL"; case SHT_SHLIB: return "SHLIB"; case SHT_DYNSYM: return "DYNSYM"; default: return "UNKNOWN"; } } // 解析节标志 const char* get_section_flags_name(Elf64_Xword flags) { static char flag_str[64]; flag_str[0] = ' '; if (flags & SHF_WRITE) strcat(flag_str, "W"); if (flags & SHF_ALLOC) strcat(flag_str, "A"); if (flags & SHF_EXECINSTR) strcat(flag_str, "X"); if (flags & SHF_MERGE) strcat(flag_str, "M"); if (flags & SHF_STRINGS) strcat(flag_str, "S"); return flag_str[0] == ' ' ? "None" : flag_str; } // 获取符号类型 const char* get_elf64_st_type_name(unsigned char info) { switch (ELF64_ST_TYPE(info)) { case STT_NOTYPE: return "NOTYPE"; case STT_OBJECT: return "OBJECT"; case STT_FUNC: return "FUNC"; case STT_SECTION: return "SECTION"; case STT_FILE: return "FILE"; default: return "UNKNOWN"; } } // 获取符号绑定 const char* get_elf64_st_bind_name(unsigned char info) { switch (ELF64_ST_BIND(info)) { case STB_LOCAL: return "LOCAL"; case STB_GLOBAL: return "GLOBAL"; case STB_WEAK: return "WEAK"; default: return "UNKNOWN"; } } // 获取符号可见性 const char* get_elf64_st_visibility_name(unsigned char other) { switch (ELF64_ST_VISIBILITY(other)) { case STV_DEFAULT: return "DEFAULT"; case STV_INTERNAL: return "INTERNAL"; case STV_HIDDEN: return "HIDDEN"; case STV_PROTECTED: return "PROTECTED"; default: return "UNKNOWN"; } } void print_symbol_table(const char *strtab, Elf64_Sym *symbols, int count, const char *symtab_name) { printf("Symbol table '%s' contains %d entries:n", symtab_name, count); printf(" Num: Value Size Type Bind Vis Ndx Namen"); for (int i = 0; i < count; i++) { // 符号表内容输出,按照readelf -s格式进行对齐 printf("%6d: %016lx %-5lu %-7s %-6s %-8s %-4d %sn", i, symbols[i].st_value, symbols[i].st_size, // 解析符号类型 get_elf64_st_type_name(symbols[i].st_info), // 解析符号绑定 get_elf64_st_bind_name(symbols[i].st_info), // 解析符号可见性 get_elf64_st_visibility_name(symbols[i].st_other), symbols[i].st_shndx, &strtab[symbols[i].st_name]); } }