如何用C语言实现列出共享对象的所有导出函数
用C语言借助libelf实现ELF导出符号解析(类似
objdump -T) 要实现类似objdump -T的ELF导出符号解析功能,libelf确实是最直接的工具——虽然官方文档不够直观,但核心流程清晰。下面是完整的实现思路和代码示例:
核心实现逻辑
- 初始化libelf库并打开目标ELF文件
- 读取ELF头部,兼容32/64位格式
- 遍历节区,找到动态符号表(
SHT_DYNSYM类型) - 获取对应字符串表,用于解析符号名称
- 遍历符号表,过滤出全局/弱绑定的导出符号,格式化输出
完整代码示例
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <libelf.h> #include <gelf.h> int main(int argc, char *argv[]) { int fd; Elf *elf; Elf_Scn *scn = NULL; GElf_Ehdr ehdr; GElf_Shdr shdr; Elf_Data *data; int symcnt, i; char *strtab = NULL; if (argc != 2) { fprintf(stderr, "Usage: %s <ELF-file>\n", argv[0]); exit(EXIT_FAILURE); } // 初始化libelf版本,必须调用 if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr, "libelf init failed: %s\n", elf_errmsg(-1)); exit(EXIT_FAILURE); } // 打开ELF文件 if ((fd = open(argv[1], O_RDONLY)) < 0) { perror("open"); exit(EXIT_FAILURE); } // 创建Elf操作对象 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { fprintf(stderr, "elf_begin failed: %s\n", elf_errmsg(-1)); close(fd); exit(EXIT_FAILURE); } // 获取ELF头部信息 if (gelf_getehdr(elf, &ehdr) == NULL) { fprintf(stderr, "gelf_getehdr failed: %s\n", elf_errmsg(-1)); elf_end(elf); close(fd); exit(EXIT_FAILURE); } // 遍历所有节区,寻找动态符号表 while ((scn = elf_nextscn(elf, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) == NULL) { fprintf(stderr, "gelf_getshdr failed: %s\n", elf_errmsg(-1)); elf_end(elf); close(fd); exit(EXIT_FAILURE); } if (shdr.sh_type == SHT_DYNSYM) { // 读取符号表数据 if ((data = elf_getdata(scn, NULL)) == NULL) { fprintf(stderr, "elf_getdata failed: %s\n", elf_errmsg(-1)); elf_end(elf); close(fd); exit(EXIT_FAILURE); } // 计算符号总数 symcnt = shdr.sh_size / shdr.sh_entsize; // 获取符号对应的字符串表 Elf_Scn *str_scn = elf_getscn(elf, shdr.sh_link); if (str_scn == NULL) { fprintf(stderr, "get strtab section failed: %s\n", elf_errmsg(-1)); elf_end(elf); close(fd); exit(EXIT_FAILURE); } GElf_Shdr str_shdr; if (gelf_getshdr(str_scn, &str_shdr) == NULL) { fprintf(stderr, "get strtab header failed: %s\n", elf_errmsg(-1)); elf_end(elf); close(fd); exit(EXIT_FAILURE); } Elf_Data *str_data = elf_getdata(str_scn, NULL); if (str_data == NULL) { fprintf(stderr, "get strtab data failed: %s\n", elf_errmsg(-1)); elf_end(elf); close(fd); exit(EXIT_FAILURE); } strtab = (char *)str_data->d_buf; // 格式化输出符号信息 printf("%-16s %-8s %-4s %-16s %s\n", "Value", "Size", "Type", "Bind", "Name"); printf("-------------------------------------------------------------\n"); for (i = 0; i < symcnt; i++) { GElf_Sym sym; if (gelf_getsym(data, i, &sym) == NULL) { fprintf(stderr, "get sym %d failed: %s\n", i, elf_errmsg(-1)); continue; } // 过滤:只保留全局/弱绑定的非未定义符号 int bind = GELF_ST_BIND(sym.st_info); if ((bind != STB_GLOBAL && bind != STB_WEAK) || (sym.st_shndx == SHN_UNDEF && sym.st_value == 0)) { continue; } // 解析符号类型 const char *type_str; switch (GELF_ST_TYPE(sym.st_info)) { case STT_NOTYPE: type_str = "NOTYPE"; break; case STT_OBJECT: type_str = "OBJECT"; break; case STT_FUNC: type_str = "FUNC"; break; case STT_SECTION: type_str = "SECTION"; break; case STT_FILE: type_str = "FILE"; break; default: type_str = "UNKNOWN"; break; } // 解析符号绑定类型 const char *bind_str; switch (bind) { case STB_GLOBAL: bind_str = "GLOBAL"; break; case STB_WEAK: bind_str = "WEAK"; break; default: bind_str = "UNKNOWN"; break; } // 输出对齐格式 printf("%016lx %08lx %-4s %-16s %s\n", (unsigned long)sym.st_value, (unsigned long)sym.st_size, type_str, bind_str, strtab + sym.st_name); } break; } } if (strtab == NULL) { fprintf(stderr, "No dynamic symbol table found in %s\n", argv[1]); } // 清理资源 elf_end(elf); close(fd); exit(EXIT_SUCCESS); }
关键说明
- 使用
gelf_*系列函数:兼容32位和64位ELF文件,无需单独处理两种格式 - 符号过滤逻辑:和
objdump -T一致,只保留全局/弱绑定的已定义符号,跳过未导入的外部符号 - 编译命令:需要链接libelf库,执行
gcc -o elf_syms elf_syms.c -lelf
内容的提问来源于stack exchange,提问作者user877329
相关产品推荐
相关产品推荐

