如何获取ELF可执行文件中RELA跳转槽的符号名称
获取ELF RELA跳转槽对应的符号名称
我看了你的代码,问题主要出在字符串表索引的使用错误,还有一些RELA段遍历的小问题,下面给你一步步修正:
关键错误分析
elf_strptr参数错误:你现在用了shdr_rela.sh_link和sym.st_shndx,但正确的应该是用符号表段(DYNSYM)的sh_link(这个值指向存储符号名的字符串表节),然后传入sym.st_name(符号名在字符串表中的偏移),而不是sym.st_shndx。- RELA段遍历逻辑错误:你找到第一个RELA段后又调用了
elf_nextscn,这会跳过当前找到的RELA段,直接处理下一个节,这会导致你漏过第一个RELA段(比如.rela.plt)。 - 未检查符号索引有效性:
GELF_R_SYM(rela.r_info)可能返回无效索引(比如0,对应未定义符号的占位符),需要先判断再处理。 - 符号表遍历逻辑:你只找第一个DYNSYM段,但其实应该确保找到正确的符号表,不过一般可执行文件里DYNSYM只有一个,这个问题不大,但可以加个判断确保找到。
修正后的完整代码
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <libelf.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <gelf.h> #define LIB_PATH "/tmp/a.out" int main(void) { int fd = open(LIB_PATH, O_RDONLY); Elf *elf; Elf_Scn *scn = NULL; Elf_Data *rela_data = NULL; Elf_Data *sym_data = NULL; GElf_Shdr shdr_rela; GElf_Shdr shdr_sym; GElf_Rela rela; GElf_Sym sym; size_t sym_strtab_idx; // 符号字符串表的节索引 // 初始化libelf版本 elf_version(EV_CURRENT); elf = elf_begin(fd, ELF_C_READ, NULL); if (!elf) { fprintf(stderr, "ERROR: %s\n", elf_errmsg(elf_errno())); close(fd); return EXIT_FAILURE; } // 查找DYNSYM段 for (scn = elf_getscn(elf, 0); scn; scn = elf_nextscn(elf, scn)) { gelf_getshdr(scn, &shdr_sym); if (shdr_sym.sh_type == SHT_DYNSYM) { sym_data = elf_getdata(scn, sym_data); sym_strtab_idx = shdr_sym.sh_link; // 保存符号字符串表的索引 break; } } if (!sym_data) { fprintf(stderr, "ERROR: Could not find DYNSYM section\n"); elf_end(elf); close(fd); return EXIT_FAILURE; } // 遍历所有RELA段(处理.rela.plt和.rela.dyn等) for (scn = elf_getscn(elf, 0); scn; scn = elf_nextscn(elf, scn)) { gelf_getshdr(scn, &shdr_rela); if (shdr_rela.sh_type != SHT_RELA) { continue; } printf("=== Processing RELA section: %s ===\n", elf_strptr(elf, elf_getshdrstrndx(elf), shdr_rela.sh_name)); rela_data = elf_getdata(scn, rela_data); if (!rela_data) { fprintf(stderr, "ERROR: Could not get RELA data\n"); continue; } // 遍历当前RELA段的所有条目 size_t rela_count = shdr_rela.sh_size / shdr_rela.sh_entsize; for (size_t it = 0; it < rela_count; ++it) { if (gelf_getrela(rela_data, it, &rela) != &rela) { fprintf(stderr, "ERROR: Failed to get RELA entry at index %zu\n", it); continue; } // 获取符号索引 size_t sym_idx = GELF_R_SYM(rela.r_info); // 检查符号索引是否有效(0是未定义符号的占位符,一般对应外部符号) if (sym_idx == 0 || sym_idx >= shdr_sym.sh_size / shdr_sym.sh_entsize) { printf("[%p]: (sym_idx=%zu) UNDEFINED\n", (void*)rela.r_offset, sym_idx); continue; } // 获取符号信息 if (gelf_getsym(sym_data, sym_idx, &sym) != &sym) { fprintf(stderr, "ERROR: Failed to get symbol at index %zu\n", sym_idx); continue; } // 获取符号名称 const char *sym_name = elf_strptr(elf, sym_strtab_idx, sym.st_name); if (!sym_name) { sym_name = "(unknown)"; } printf("[%p]: (sym_idx=%zu) %s\n", (void*)rela.r_offset, sym_idx, sym_name); } } elf_end(elf); close(fd); return EXIT_SUCCESS; }
关键修改说明
- 符号名获取逻辑:用
elf_strptr(elf, sym_strtab_idx, sym.st_name)来获取符号名,其中sym_strtab_idx是DYNSYM段的sh_link,指向存储符号名的字符串表(一般是.dynstr),sym.st_name是符号名在字符串表中的偏移。 - 遍历所有RELA段:不再找到第一个就break,而是处理所有类型为
SHT_RELA的段,这样不会漏过.rela.plt(PLT跳转槽)和.rela.dyn(动态重定位)。 - 有效性检查:增加了对符号索引、RELA条目、符号信息的有效性检查,避免程序崩溃或输出错误信息。
- 修复RELA段跳过问题:移除了错误的
scn = elf_nextscn(elf, scn)调用,确保找到的RELA段被正确处理。
编译与运行
编译时需要链接libelf库:
gcc -o rela_sym rela_sym.c -lelf
运行后就能看到每个RELA条目对应的符号名称了。
内容的提问来源于stack exchange,提问作者Ra'Jiska
相关产品推荐
相关产品推荐

