如何在自有二进制中调用动态链接非剥离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可执行文件与共享库结构相似,只需修改文件头标识并移除冲突符号,即可转成可链接的共享库:
- 使用
patchelf修改文件类型为共享库:patchelf --set-type shared executable mv executable executable.so - 移除冲突符号(如
main、_start)避免链接报错:objcopy --strip-symbol=main --strip-symbol=_start executable.so - 直接链接共享库编译
main.c:
运行gcc -o main main.c ./executable.so./main即可得到预期输出87。
方案二:生成符号存根库
若不想修改原可执行文件,可提取目标符号地址生成存根库,引导链接器指向原文件的对应符号:
- 从
nm输出中获取foobar的地址(示例中为0000000000001129)。 - 创建存根汇编文件
foobar_stub.s:.global foobar foobar = 0x1129 - 编译为静态库:
as -o foobar_stub.o foobar_stub.s ar rcs libfoobar.a foobar_stub.o - 调整原可执行文件的
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
相关产品推荐
相关产品推荐

