我有一些具有循环依赖关系的共享库,我想用dlopen
加载它。为此,我使用dlopen
标记呼叫RTLD_LAZY | RTLD_GLOBAL
。
这很好用。
现在,我想检查所有共享库中的所有符号是否已完全解析,因此我再次使用dlopen
调用RTLD_NOW
,如果无法解析所有符号,则会失败。
至少,这是我理解手册页的方式:
但是,后续的dlopen()调用会加载相同的共享对象 RTLD_NOW可能会强制对先前加载的共享对象执行符号解析 RTLD_LAZY。
这是一个简单的例子,说明了我的问题:
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include "h.h"
#define CNT 3
static const char *libs[CNT] = {"./liba.so", "./libb.so", "./libc.so"};
typedef int (*f_ptr)();
int main() {
void *h[CNT];
f_ptr f[CNT];
for (int i = 0; i < CNT; i++) {
printf("dlopen: %s\n", libs[i]);
h[i] = dlopen(libs[i], RTLD_LAZY | RTLD_GLOBAL);
if (!h[i]) {
fprintf(stderr, "%d %s: %s\n", __LINE__, libs[i], dlerror());
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < CNT; i++) {
printf("dlopen again: %s\n", libs[i]);
void *xx = dlopen(libs[i], RTLD_NOW | RTLD_GLOBAL);
if (!xx) {
fprintf(stderr, "%d %s: %s\n", __LINE__, libs[i], dlerror());
dlclose(h[i]);
h[i] = 0;
} else {
dlclose(xx);
}
}
for (int i = 0; i < CNT; i++) {
if (h[i]) {
f[i] = (int (*)())dlsym(h[i], "init");
if (!f[i]) {
fprintf(stderr, "%d %s: %s\n", __LINE__, libs[i], dlerror());
exit(EXIT_FAILURE);
}
}
}
for (int i = 0; i < CNT; i++) {
if (f[i]) {
printf("%s %08d\n", libs[i], f[i]());
}
}
return 0;
}
#include "h.h"
int init() { return 1 + b_(); }
int a_() { return 10; }
#include "h.h"
int init() { return 100 + a_(); }
int b_() { return 1000; }
#include "h.h"
int init() { return 10000 + x_(); }
int c_() { return 100000; }
int init();
int a_();
int b_();
int x_();
project(xx)
add_library(a SHARED a.c)
add_library(b SHARED b.c)
add_library(c SHARED c.c)
add_executable(main main.c)
target_link_libraries(main dl)
$ mkdir b && cd b && cmake .. && make && ./main
...
[100%] Built target b
dlopen: ./liba.so
dlopen: ./libb.so
dlopen: ./libc.so
dlopen again: ./liba.so
dlopen again: ./libb.so
dlopen again: ./libc.so
./liba.so 00001001
./libb.so 00000110
./main: symbol lookup error: ./libc.so: undefined symbol: x_
如何在调用之前检测到libc.so无法完全加载 导致它崩溃的符号?
答案 0 :(得分:0)
理想情况下,您需要按照其他人的建议(通过--no-allow-shlib-undefined)将符号交叉匹配的负担放在静态链接器上,而不是将其延迟到启动。现在它对于循环依赖有点复杂,但Solaris(其中Linux shlibs是一个暗淡的副本)提供了一个聪明的解决方案 - shlib filters。这个想法是你链接到虚拟shlibs,它们导出同样的虚拟符号并且具有与最终生产库相同的soname。这些当然仅在链接时用于验证依赖性。
作为旁注,我很确定虚拟库的生成可以自动化,如下所示: