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

如何在自有二进制中调用动态链接非剥离ELF可执行文件的指定符号?

问题场景与疑问

仅获取一个动态链接、未剥离的ELF可执行文件,希望在自有二进制中调用其text段内的特定函数符号。

简化示例

现有通过gcc -o executable executable.c编译得到的ELF可执行文件executable(源码不可得),以及主C文件main.c。

示例可执行文件源码(仅作参考,实际不可得)

// executable.c
// gcc -o executable executable.c
// 实际仅拥有`executable`文件,源码不可获取

foobar() { return 87; }
main() { return 0; }

通过nm executable可验证其包含foobar等符号:

0000000000003e00 d _DYNAMIC
0000000000003fc0 d _GLOBAL_OFFSET_TABLE_
0000000000002000 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
00000000000020e8 r __FRAME_END__
0000000000002004 r __GNU_EH_FRAME_HDR
0000000000004010 D __TMC_END__
000000000000038c r __abi_tag
0000000000004010 B __bss_start
                 w __cxa_finalize@GLIBC_2.2.5
0000000000004000 D __data_start
00000000000010e0 t __do_global_dtors_aux
0000000000003df8 d __do_global_dtors_aux_fini_array_entry
0000000000004008 D __dso_handle
0000000000003df0 d __frame_dummy_init_array_entry
                 w __gmon_start__
                 U __libc_start_main@GLIBC_2.34
0000000000004010 D _edata
0000000000004018 B _end
0000000000001148 T _fini
0000000000001000 T _init
0000000000001040 T _start
0000000000004010 b completed.0
0000000000004000 W data_start
0000000000001070 t deregister_tm_clones
0000000000001129 T foobar
0000000000001120 t frame_dummy
0000000000001138 T main
00000000000010a0 t register_tm_clones

自有主文件main.c

// main.c

#include <stdio.h>

extern foobar();

main()
{
    printf("%d\n", foobar());
    return 0;
}

直接执行gcc -o main main.c executable会出现_init、_start、main等符号重复定义问题,新版本编译器/链接器甚至会提示cannot use executable file 'executable' as input to a link。

疑问: 能否编译出可链接该可执行文件特定符号的二进制或库?是否存在提取这些符号的方法?


解决方法

可以实现,以下是几种可行方案:

方案一:将可执行文件转换为共享库

ELF可执行文件与共享库结构相似,只需修改文件头标识并移除冲突符号,即可转成可链接的共享库:

  1. 使用patchelf修改文件类型为共享库:
    patchelf --set-type shared executable
    mv executable executable.so
    
  2. 移除冲突符号(如main、_start)避免链接报错:
    objcopy --strip-symbol=main --strip-symbol=_start executable.so
    
  3. 直接链接共享库编译main.c:
    gcc -o main main.c ./executable.so
    
    运行./main即可得到预期输出87。

方案二:生成符号存根库

若不想修改原可执行文件,可提取目标符号地址生成存根库,引导链接器指向原文件的对应符号:

  1. 从nm输出中获取foobar的地址(示例中为0000000000001129)。
  2. 创建存根汇编文件foobar_stub.s:
    .global foobar
    foobar = 0x1129
    
  3. 编译为静态库:
    as -o foobar_stub.o foobar_stub.s
    ar rcs libfoobar.a foobar_stub.o
    
  4. 调整原可执行文件的SONAME并编译main.c:
    patchelf --set-soname libexecutable.so executable
    mv executable libexecutable.so
    gcc -o main main.c libfoobar.a -Wl,-rpath=. -lexecutable
    

方案三:动态加载(运行时解析符号)

无需修改编译流程,通过dlopen系列函数在运行时加载原可执行文件并获取符号:
修改main.c如下:

#include <stdio.h>
#include <dlfcn.h>

int main()
{
    void* handle = dlopen("./executable", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "dlopen failed: %s\n", dlerror());
        return 1;
    }
    int (*foobar)() = dlsym(handle, "foobar");
    if (!foobar) {
        fprintf(stderr, "dlsym failed: %s\n", dlerror());
        dlclose(handle);
        return 1;
    }
    printf("%d\n", foobar());
    dlclose(handle);
    return 0;
}

编译时链接动态加载库:

gcc -o main main.c -ldl

运行./main即可直接调用原可执行文件中的foobar函数。


内容的提问来源于stack exchange,提问作者nevikw39

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 02:57:02