我有一些功能,比如我从DLL获取的int foo(int x)
(使用dlsym()
)。所以,目前我的代码看起来像这样:
void foo(int x) {
void (*foo)(int x);
foo = dlsym(dll_handle, "foo");
int y = foo(x);
printf("y is %d", y);
}
我想要的是(像这样的代码):
void bar(int x) {
int y = foo(x);
printf("y is %d", y);
}
所以foo()
是一个调用dll函数的存根(但不必每次都搜索DLL)。
我在下面的答案中有一个解决方案的基本想法,但我不太确定,我想在这里采用“最佳实践”方法。
你已经在问题中回答了自己。一个小的改进可能是检查dll的“更新”,如果有的话。
int foo(int x) {
static void (*dll_foo)(int x) = NULL;
static void *foo_dll_handle = NULL;
if (dll_foo == NULL || foo_dll_handle != dll_handle) {
dll_foo = dlsym(dll_handle, "foo");
foo_dll_handle = dll_handle;
}
return dll_foo(x);
}
对于许多函数的情况,我如何避免编写一堆复制粘贴存根?
它已经很长时间但是为了完整性,您可以使用Implib.so自动生成这样的包装器:
$ implib-gen.py mylib.so
这将生成两个文件,mylib.so.tramp.S和mylib.so.init.c。汇编文件包含库函数的包装器(可选择dlopen
库并使用dlsym
来定位正确的实现):
// Wrapper for bar symbol
.globl bar
bar:
.cfi_startproc
// Check if library function address is resolved
cmp $0, _libtest_so_tramp_table+0(%rip)
je 2f
1:
// Fast path
jmp *_libtest_so_tramp_table+0
2:
// Slow path
pushq $0
.cfi_adjust_cfa_offset 8
call save_regs_and_resolve
addq $8, %rsp
.cfi_adjust_cfa_offset -8
jmp 1b
.cfi_endproc
生成的C代码处理dlopen
和dlsym
部分:
void _libtest_so_tramp_resolve(int i) {
assert(i < sizeof(sym_names) / sizeof(sym_names[0]) - 1);
if(!lib_handle) {
lib_handle = dlopen("libtest.so", RTLD_LAZY | RTLD_GLOBAL);
}
CHECK(lib_handle, "failed to load library 'libtest.so': %s", dlerror());
// Can be sped up by manually parsing library symtab...
_libtest_so_tramp_table[i] = dlsym(lib_handle, sym_names[i]);
CHECK(_libtest_so_tramp_table[i], "failed to resolve symbol '%s' in library 'libtest.so'", sym_names[i]);
}
有关更多信息,请查看projects Github page。
对于单个函数的情况,我认为这样的事情应该是正确的:
int foo(int x) {
static void (*dll_foo)(int x) = NULL;
if (dll_foo == NULL) {
dll_foo = dlsym(dll_handle, "foo");
}
return dll_foo(x);
}