我正在开发一个项目,该项目需要将基于Rust的插件(共享对象)任意加载/卸载到隔离的动态库 namespace 中。
我使用dlmopen(LM_ID_NEWLM, "rust-plugin.so", RTLD_LAZY)
为共享对象创建新的 namespace 。当不再需要共享对象时,我称为dlclose()
。
不幸的是,我发现即使当我使用dlclose()
以便一次仅一个共享对象有效时,在dlmopen()
处理14个Rust插件对象后,我仍然得到错误:
dlmopen(rust-plugin.so) failed: /lib/x86_64-linux-gnu/libc.so.6: cannot allocate memory in static TLS block
在此失败之后继续尝试
dlmopen()
会导致段错误和no more namespaces available for dlmopen()
。我似乎已经将问题隔离到Rust共享对象的
libpthread.so
依赖关系。其他共享对象的依赖项,例如libgcc_s.so.1
(以及我尝试过的任何.so文件),可以通过以下代码无限期地打开和关闭,而libpthread.so
在我打开和关闭14次后出错。#include <link.h>
#include <stdio.h>
#include <dlfcn.h>
#include <cstdlib>
void load(char const *file) {
void *handle_ = dlmopen(LM_ID_NEWLM, file, RTLD_LAZY);
if (!handle_) {
printf("dlmopen(%s) failed: %s\n", file, dlerror());
exit(1);
}
if (dlclose(handle_) != 0) {
exit(2);
}
}
int main() {
void *handle_;
for (int i = 0; true; i++) {
printf("%d\n", i);
load("libpthread.so.0");
}
}
有什么办法可以使libpthread正确清理,从而避免出现此问题?
最佳答案
libpthread.so.0
具有NODELETE
标志:
readelf -d /lib/x86_64-linux-gnu/libpthread.so.0 | grep NODELETE
0x000000006ffffffb (FLAGS_1) Flags: NODELETE INITFIRST
这使得
dlclose()
成为无操作对象。另请参见answer。假定
dlclose()
是no-op,那么其他一切都说得通:GLIBC总共配置了16个加载器 namespace ,其中一个保留给主应用程序。一旦您调用dlmopen
(不调用dlclose
)15次,就将它们全部用尽,随后的尝试会失败,并使用no more namespaces available
失败。用
libpthread
标记NODELETE
是有道理的:一旦在图片中,它就会从根本上改变GLIBC操作(例如malloc
开始获取锁,errno
切换到线程本地等)。我认为,唯一可行的选择是避免依赖插件中的
libpthread
。您可以做的其他事情:
libpthread
的含义是什么;也许应该是可卸载的,-z,nodelete
链接器标志,并安排将此libpthread.so.0
作为插件依赖项加载(确保此版本与系统版本保持同步,否则您将看到非常难以调试的崩溃)。 关于c++ - `dlclose()`之后,libpthread.so继续使用TLS空间和DL namespace ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44446683/