libpthread.so继续在`dlclose()`之后使用TLS空间和DL命名空间

我正在处理一个项目,需要随意地将基于Rust的插件(共享对象)加载/卸载到独立的dynamic库名称空间中。

我使用dlmopen(LM_ID_NEWLM, "rust-plugin.so", RTLD_LAZY)为共享对象创build一个新的名称空间。 当共享对象不再需要时,我调用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文件,我已经尝试过)可以通过无限期地被下面的代码打开和closures,而libpthread.so错误在我打开和closures它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.0NODELETE标志:

 readelf -d /lib/x86_64-linux-gnu/libpthread.so.0 | grep NODELETE 0x000000006ffffffb (FLAGS_1) Flags: NODELETE INITFIRST 

这使得dlclose()在它上面没有任何操作。 另请参阅此答案 。

鉴于dlclose()是no-op,所有其他内容都是有意义的:GLIBC配置了16个加载器名称空间,其中一个是为主应用程序保留的。 一旦调用dlmopen (不调用dlclose )15次,就会耗尽它们,随后的尝试失败,并且no more namespaces available

使用NODELETE标记libpthread是有道理的:一旦它在图片中,就会从根本上改变GLIBC操作(例如malloc开始获取锁, errno切换到线程本地等等)。

有没有什么办法可以让libpthread正确清理,这样我可以避免这个问题?

我相信你唯一现实的选择是尽量避免从你的插件依赖libpthread

其他事情你可以做:

  1. 打开一个针对GLIBC的错误(但是您可能需要等很长时间才能修复)。 我不确定从非默认加载器作用域卸载libpthread的影响是什么; 也许它应该是无法载入的,
  2. 建立你自己的libpthread.so,与系统一样,但没有-z,nodelete链接器标志,并安排这个libpthread.so.0作为插件依赖项加​​载(确保这个版本与系统保持同步一个,否则你会看到很难调试崩溃)。