You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C语言实现列出共享对象的所有导出函数

用C语言借助libelf实现ELF导出符号解析(类似objdump -T)

要实现类似objdump -T的ELF导出符号解析功能,libelf确实是最直接的工具——虽然官方文档不够直观,但核心流程清晰。下面是完整的实现思路和代码示例:

核心实现逻辑

  1. 初始化libelf库并打开目标ELF文件
  2. 读取ELF头部,兼容32/64位格式
  3. 遍历节区,找到动态符号表(SHT_DYNSYM类型)
  4. 获取对应字符串表,用于解析符号名称
  5. 遍历符号表,过滤出全局/弱绑定的导出符号,格式化输出

完整代码示例

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 19:50:45