代码:
//a.c I don't use header files as this is just for demo purpose.
extern void function_b(int num);
void function_a(int num) {
function_b(num)
}
//b.c
void function_b(int num) {
...
}
//dll.c
#include <dlfcn.h>
int main() {
void *handle_a;
void *handle_b;
void (*pfunc_a)(int);
...
handle_a = dlopen("./a.so", RTLD_LAZY);
...
pfunc_a = dlsym(handle_a, "function_a");
...
handle_b = dlopen("./b.so", RTLD_GLOBAL);
...
pfunc_a(2020);
...
return 0;
}
我们可以看到,dll.c
尝试在运行时加载共享库,并且 modulea
有 的引用function_b
,并且 moduleb
有 的定义function_b
。假设我们已经创建了共享库a.so
,b.so
因此这些共享库在程序运行之前就存在于磁盘上,但是当我运行程序时,它会抛出符号查找错误:
./a.so:undefined symbol: function_b
handle_a = dlopen("./a.so", RTLD_LAZY);
但是对于我在这里使用的这行代码RTLD_LAZY
,运行时链接器不会尝试解析符号function_b
,并且我有机会dlopen("b.so", RTLD_GLOBAL)
在调用之前调用function_a
。这样,动态链接器将使用b.so 中a.so
的定义修改引用。function_b
我的问题是:
我的理解是否正确,动态链接器应该修改
.got
or.got.plt
部分,a.so
以便它可以链接/重定位到.text
部分中 function_b 的指令地址b.so
。function_b
如果我的理解是正确的,那么为什么动态链接器在这种情况下仍然无法解析?
答案1
问题不是动态链接器无法解析function_b
,而是您的第二次调用dlopen
不正确:你需要包括RTLD_LAZY
或RTLD_NOW
,其他标志与这两个标志是互补的。
必须包含以下两个值之一旗帜:
b.so
将您的负载更改为
handle_b = dlopen("./b.so", RTLD_NOW | RTLD_GLOBAL);
产生一个工作程序。
每次调用都dlopen
必须在RTLD_LAZY
和之间进行选择RTLD_NOW
;因为b.so
是最后一个加载的库,我NOW
在上面指定了(我们不会通过延迟加载获得任何东西),但LAZY
在这种情况下效果同样好。除此之外,还可以添加其他标志;这里我们需要,RTLD_GLOBAL
因为我们需要b.so
全局可用的符号,以便在它运行时function_a
可以找到。function_b
看中的例子dlopen(3)
有关您需要执行的错误处理的详细信息dlopen
等,这揭示了问题。