我有一个非线程安全的共享库(C/Fortran),即它使用全局变量定义其状态。因此,当我使用 dlopen 从同一进程多次打开该库时,全局变量是共享的,即状态会受到干扰(混乱),因为仅增加了引用计数。 (当我 dlopen 库的两个物理副本时,我的应用程序可以正常工作。)。
我发现上面链接的问题和另一个提到 dlmopen 的问题表明我can加载库的单独实例(带有它们自己的全局变量),这将消除问题。 dlmopen 的文档对我来说很难理解,但我尝试了一下并尝试用这样的东西打开我的库:
inline handle_type open(std::string const& filename) {
handle_type h = dlmopen(LM_ID_NEWLM, filename.c_str(), RTLD_LAZY);
return h;
}
但是,唉,我遇到了一次崩溃,valgrind 报告从我的代码中一个奇怪的(看似无辜的)位置读取了大小为 32 的无效读取(位于 0x2833DE35:???(在 /usr/lib64/libc-2.28.so 中)) (
a = &static_function;
),即将静态函数的地址分配给某个函数指针。
所以我的问题是:库的一份副本上的 dlmopen 是否应该与两份副本上的 dlopen 得到相同的结果?我是否以错误的方式调用 dlmopen ?或者当我使用 dlmopen 时分配静态函数的地址是否存在根本问题?
所以我的问题是:库的一份副本上的 dlmopen 是否应该与两份副本上的 dlopen 得到相同的结果?
不一定1.
我调用 dlmopen 的方式是否错误?
我建议使用
RTLD_NOW
而不是 RTLD_LAZY
。
如果您这样做,并且
dlmopen
调用没有失败(不返回NULL
),然后您可以得出结论,该库已与其所有依赖项正确链接。
如果
dlmopen(..., RTLD_NOW)
失败,您可以断定在此库上使用 dlmopen
不起作用。
如果
dlmopen
应该 有效,您的下一个任务就是找出崩溃的原因。不幸的是,用于调试的工具支持几乎不存在。例如,Valgrind 很可能会完全混乱。
您可能能够使用GDB来理解崩溃,但即使这样也可能需要手动告诉GDB新添加的库所在的位置(请参阅this错误)。注意:该错误已修复,因此最新版本的 GDB 可能可以正常工作。
或者当我使用 dlmopen 时分配静态函数的地址是否存在根本问题?
没有。
1 有很多差异。作为一个示例,考虑一下当您调用此库中的函数(例如
foo()
)时会发生什么,该函数返回用 malloc
分配的内存,并且您负责 free
ing。当使用单个链接器命名空间时,图中只有一个 malloc
,一切正常。
当你
dlmopen
图书馆两次时,就有三个! malloc
的实例(一个位于默认命名空间中,两个位于两个新命名空间中)。如果您现在将由 malloc
的一个实例分配的指针传递给位于另一命名空间中的 free
,则可能会发生崩溃或堆损坏。