必须先校验魔数\x7fELF、EI_CLASS和EI_DATA字段以确定位宽与字节序,再安全读取对应大小的ELF头(32位52字节/64位64字节),并进行必要的字节序转换,否则直接fread或memcpy会导致字段错位崩溃。

ELF文件头结构在C++里怎么读出来
直接用 fread 读前52字节(32位)或64字节(64位)就能拿到 Elf32_Ehdr 或 Elf64_Ehdr,但别急着 memcpy——头里有字节序和架构标识,必须先验证 e_ident 字段。常见错误是拿64位头结构去解析32位文件,结果 e_phoff、e_shoff 全错乱。
- 先用
open()或fopen()打开文件,读取前16字节检查e_ident[0..3]是否为{0x7f, 'E', 'L', 'F'} - 查
e_ident[EI_CLASS]:值为ELFCLASS32就用Elf32_Ehdr,ELFCLASS64则用Elf64_Ehdr - 查
e_ident[EI_DATA]判断字节序:ELFDATA2LSB是小端,ELFDATA2MSB是大端;若主机与ELF不一致,需对字段逐个ntohl()/ntohs()
为什么用 libelf 比手撸更稳
手动解析要处理对齐、重定位、节头/程序头偏移计算、字符串表索引映射……稍有偏差就段错误。而 libelf 把这些封装成 elf_begin()、elf_getehdr()、elf_getphdr() 等接口,自动适配32/64位和字节序。
- 链接时加
-lelf,头文件包含<libelf.h>和<gelf.h> - 调用前必须
elf_version(EV_CURRENT)初始化版本,否则elf_begin()返回NULL -
GElf_Ehdr是统一接口结构体,无论输入是32还是64位ELF,gelf_getehdr()都能正确填充
用 std::ifstream 读ELF头容易踩哪些坑
用C++流读二进制文件,不设 std::ios::binary 标志会导致换行符被误转,e_ident 第5字节(EI_VERSION)可能读错;另外流默认按字符读,sizeof(Elf64_Ehdr) 可能因结构体对齐被编译器填充,直接 read() 整块会越界或漏字节。
- 务必用
std::ifstream file("a.out", std::ios::binary) - 读头前先
file.seekg(0),再用file.read(reinterpret_cast<char>(&ehdr), sizeof(ehdr))</char> - 不要依赖
sizeof(Elf64_Ehdr)—— 实际ELF规范定义大小是固定的(64字节),应显式读64字节而非结构体大小
如何快速验证解析结果是否可信
光读出 e_type 是 ET_EXEC 不够,得交叉验证关键字段逻辑关系。比如 e_phoff 必须非零且小于文件大小,e_phnum × e_phentsize 要能覆盖所有程序头区域;否则大概率是解析错位或文件损坏。
立即学习“C++免费学习笔记(深入)”;
- 检查
e_ehsize:32位必须为52,64位必须为64,否则头格式异常 - 检查
e_phoff+e_phnum * e_phentsize≤ 文件总长度,否则程序头越界 - 用
readelf -h a.out输出比对e_entry、e_flags、e_machine,三者一致才说明解析没偏移
ELF头本身不复杂,难的是后续节头、符号表、重定位项的联动解析;一开始就把头读歪了,后面全崩。尤其注意 e_ident 前16字节里的魔数、类、数据编码、版本这四个字段,它们决定了整个解析路径走向,跳过校验等于蒙眼开车。


















