必须先调用elf_version(EVCURRENT),再用elf_getscn遍历节区索引、gelf_getshdr提取头信息;节区名需通过.shstrtab查偏移获得,sh_type为整型宏值不可直接打印;符号表和重定位表解析须依赖elf_getdata+gelf_getsym/gelf_getrel(a)并正确解包字段。

用 libelf 读取 ELF 节区头(elf_getscn 和 gelf_getshdr)
直接调用系统提供的 libelf 是最稳妥的方案,避免手撕 ELF header 结构。注意:必须先调用 elf_version(EV<em>CURRENT)</em>,否则后续所有 elf* 函数都会返回 NULL 或失败。节区头不是按顺序连续存储在文件里,得用 elf_getscn(elf, index) 遍历索引,再用 gelf_getshdr(scn, &shdr) 提取信息。常见错误是把 sh_type 当成字符串打印——它是个整数,比如 SHT_SYMTAB 是 2,SHT_REL 是 9,需查 <elf.h></elf.h> 宏定义或用 shdr.sh_type 做 switch 判断。
- 确保编译时链接
-lelf,且开发机装了libelf-dev(Debian/Ubuntu)或elfutils-libelf-devel(RHEL/Fedora) - 节区名存在对应字符串表中(
.shstrtab),不能直接从shdr.sh_name取字符串,得先定位该字符串表节区,再用偏移查表 -
sh_offset是节区内容在文件中的字节偏移,不是内存地址;sh_addr在可执行文件中才有意义,重定位目标中通常为 0
解析符号表(gelf_getsym + elf_getdata)
符号表节区(.symtab 或 .dynsym)本质是一组连续的 GElf_Sym 结构体。不能直接 mmap 或 fread 整块二进制去解析——得用 elf_getdata(scn, NULL) 获取数据块,再用 gelf_getsym(data, idx, &sym) 按索引安全提取。符号名存在单独的字符串表节区(.strtab 或 .dynstr),sym.st_name 是该字符串表内的偏移,不是 C 字符串指针。
- 若
st_name == 0,表示无名称(如某些局部符号或特殊符号) -
st_info高 4 位是绑定类型(STB_LOCAL/STB_GLOBAL),低 4 位是类型(STT_FUNC/STT_OBJECT),需用GELF_ST_BIND(sym.st_info)和GELF_ST_TYPE(sym.st_info)宏解包 -
.dynsym通常比.symtab小,只含动态链接所需符号;若只关心运行时可见符号,优先查.dynsym
遍历重定位表(SHT_REL / SHT_RELA 节区)
重定位节区类型有两种:SHT_REL(无 addend 字段)和 SHT_RELA(含显式 addend)。必须通过 shdr.sh_type 区分,二者结构不同:GElf_Rel vs GElf_Rela。用 gelf_getrel 或 gelf_getrela 读取时,传错函数会导致字段错位、数值全乱。典型错误是看到 .rela.dyn 就默认用 gelf_getrela,但实际要先确认 shdr.sh_type == SHT_RELA。
-
rel.r_offset是待修正位置的地址(在目标节区中的偏移,非文件偏移),需结合该重定位节区的sh_info字段查到关联的被修改节区(如.text或.data) -
GELF_R_TYPE(rel.r_info)解出重定位类型(如R_X86_64_JUMP_SLOT),GELF_R_SYM(rel.r_info)解出对应符号索引,再回符号表查符号 - 多数重定位节区自身不带字符串名,其作用对象靠
sh_link(指向符号表节区)和sh_info(指向被重定位节区)两个字段联合确定
为什么不用自己 fread 解析 ELF header?
ELF header 固定 56 字节(64 位),看似简单,但真实文件充满陷阱:e_ident 中的字节序标识(ELFDATA2LSB/ ELFDATA2MSB)影响所有后续多字节字段解析;e_machine 决定寄存器宽度与重定位类型含义;节区头数量(e_shnum)可能为 0,此时需从 .shstrtab 节区的 sh_size 反推;更关键的是,很多现代 ELF 启用了 section header string table index > SHN_LORESERVE,即 e_shstrndx 字段无效,必须读 .section 的 sh_link 才能找到字符串表。这些边界 case 全部由 libelf 封装处理,手动实现极易漏判或崩溃。
立即学习“C++免费学习笔记(深入)”;
- 不要信任
file命令输出的 “ELF 64-bit LSB pie executable”,它不保证e_ident[EI_DATA]和实际字段字节序一致 -
libelf默认以只读模式打开,若尝试写入会失败;如需修改 ELF(如 patch 符号),得用elf_begin(fd, ELF_C_WRITE, NULL)并确保文件有写权限 -
libelf不解析 DWARF 调试信息或 GNU 属性扩展,那些需要额外库(如libdwarf)
解析 ELF 不是解格式题,而是对接操作系统加载逻辑。每个字段背后都绑着 loader 行为,跳过 libelf 直接裸读,等于绕过 ABI 合规性检查。


















