libpthread.so 在 `dlclose()` 之后继续使用 TLS space 和 DL 名称space
libpthread.so continues to use TLS space and DL namespaces after `dlclose()`
我正在做一个项目,需要任意 load/unload 基于 Rust 的插件(共享对象)到隔离的动态库命名空间中。
我使用 dlmopen(LM_ID_NEWLM, "rust-plugin.so", RTLD_LAZY)
为共享对象创建新的命名空间。当不再需要共享对象时,我调用 dlclose()
.
不幸的是,我发现即使我 dlclose()
一次只有一个共享对象有效,在 dlmopen()
ing 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()
是空操作,其他一切都有意义:GLIBC 配置了 16 个加载程序命名空间,其中一个是为主应用程序保留的。一旦你调用 dlmopen
(没有调用 dlclose
)15 次,你就用尽了它们,随后的尝试失败并显示 no more namespaces available
.
用 NODELETE
标记 libpthread
是有道理的:一旦它出现在图片中,它就会从根本上改变 GLIBC 操作(例如 malloc
开始获取锁,errno
切换到线程-本地等)。
Is there some way I can have libpthread properly clean up so I can avoid this issue?
我相信对你来说唯一现实的选择是尽量避免依赖你的插件 libpthread
。
您可以做的其他事情:
- 打开一个针对 GLIBC 的错误(但您可能需要等待很长时间才能修复它)。我不确定从非默认加载程序范围卸载
libpthread
的含义是什么;也许它应该是可卸载的,
- 构建你自己的 libpthread.so,与系统相同,但没有
-z,nodelete
链接器标志,并安排此 libpthread.so.0
作为插件依赖项加载(确保此版本与系统版本保持同步,否则您将很难调试崩溃。
我正在做一个项目,需要任意 load/unload 基于 Rust 的插件(共享对象)到隔离的动态库命名空间中。
我使用 dlmopen(LM_ID_NEWLM, "rust-plugin.so", RTLD_LAZY)
为共享对象创建新的命名空间。当不再需要共享对象时,我调用 dlclose()
.
不幸的是,我发现即使我 dlclose()
一次只有一个共享对象有效,在 dlmopen()
ing 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()
是空操作,其他一切都有意义:GLIBC 配置了 16 个加载程序命名空间,其中一个是为主应用程序保留的。一旦你调用 dlmopen
(没有调用 dlclose
)15 次,你就用尽了它们,随后的尝试失败并显示 no more namespaces available
.
用 NODELETE
标记 libpthread
是有道理的:一旦它出现在图片中,它就会从根本上改变 GLIBC 操作(例如 malloc
开始获取锁,errno
切换到线程-本地等)。
Is there some way I can have libpthread properly clean up so I can avoid this issue?
我相信对你来说唯一现实的选择是尽量避免依赖你的插件 libpthread
。
您可以做的其他事情:
- 打开一个针对 GLIBC 的错误(但您可能需要等待很长时间才能修复它)。我不确定从非默认加载程序范围卸载
libpthread
的含义是什么;也许它应该是可卸载的, - 构建你自己的 libpthread.so,与系统相同,但没有
-z,nodelete
链接器标志,并安排此libpthread.so.0
作为插件依赖项加载(确保此版本与系统版本保持同步,否则您将很难调试崩溃。