C++ 强制卸载共享库

Posted

技术标签:

【中文标题】C++ 强制卸载共享库【英文标题】:C++ force unloading shared library 【发布时间】:2016-06-08 22:12:54 【问题描述】:

我正在尝试创建一个多次重新加载共享库的应用程序。但是在某个时间点,dlmopen 失败并出现错误

/usr/lib/libc.so.6: cannot allocate memory in static TLS block

这是重现此问题的最少代码:

#include <dlfcn.h>
#include <cstdio>
#include <vector>

int main() 
  for (int i = 0; i < 100; ++i) 
    void *lib_so = dlmopen(LM_ID_NEWLM, "lib.so", RTLD_LAZY | RTLD_LOCAL);
    if (lib_so == NULL) 
      printf("Iteration %i loading failed: %s\n", i, dlerror());
      return 1;
    
    dlclose(lib_so);
  

  return 0;

和空的lib.cpp,用

编译
g++ -rdynamic -ldl -Wl,-R . -o test main.cpp
g++ -fPIC -shared lib.cpp -o lib.so

更新

似乎即使只有一个线程它也会崩溃。问题是:如何强制卸载库或销毁使用LM_ID_NEWLM 创建的未使用命名空间?

【问题讨论】:

可能的答案可以在这里找到:***.com/questions/14892101/…。我怀疑您的 lib.so 正在分配 TLS 数据,并且由于您将每个线程加载 10 次,因此可能会耗尽线程的存储空间。 TLS = Thread Local Storage,& ThreadLocal 对象中可能有太多内容。 A link 描述分配 TLS。 test_thread 不需要将 thread_id 作为参数传递。 thread_id 不是 int 类型,并且每个 id 都可以通过 thread.get_id 访问。启动线程的更短方法:for(int i=0; i&lt;10;i++) threads.push_back(thread(test_thread)); 并加入它们...for(auto&amp; thread: threads) thread.join(); @qexyn lib.so 在这个例子中是从空文件编译的,所以只有 dlmopen 分配 TLS 数据。我以前见过这个问题,但是自从 lib.so 使用-fPIC 编译后,它已经使用了-ftls-model=global-dynamic。此外,看起来dlclose 实际上并没有释放内存,接下来dlmopen 创建了导致泄漏的新对象。 你能不能通过使用dlopen并添加属性RTLD_NOW | RTLD_GROUP | RTLD_LOCAL来获得相同的结果。 你不能有很多命名空间——它在我系统的手册页中提到了The glibc implementation supports a maximum of 16 namespaces,并且没有清理链接映射命名空间的工具。您最好通过仅卸载和重新加载库来为库回收相同的命名空间,而不是尝试继续创建新的命名空间(但这都是猜测)。您可以使用dlinfo 来获取链接映射ID,以继续将其用于同一库的后续dlmopen 调用;您只需要在重新打开之前继续关闭。 【参考方案1】:

对进程可用的链接映射命名空间的数量存在内置限制。这在评论中记录得很差:

glibc 实现最多支持 16 个命名空间

在手册页中。

创建链接映射命名空间后,不支持通过任何 API 将其“擦除”。这只是它的设计方式,如果不编辑 glibc 源代码并添加一些钩子,就没有真正的解决方法。

使用命名空间重新加载库实际上并不是重新加载库 - 您只是加载库的新副本。这是命名空间的用例之一——如果您多次尝试dlopen 同一个库,您将获得同一个库的相同句柄;但是,如果您在不同的命名空间中加载第二个实例,您将不会获得相同的句柄。如果要完成重新加载,则需要使用dlclose 卸载库,这将在释放对库的最后剩余引用后卸载库。

如果您想尝试“强制卸载”一个库,那么您可以尝试发出多个 dlclose 调用,直到它卸载;但是,如果您不知道库做了什么(例如生成的线程),那么在这种情况下可能无法防止崩溃。

【讨论】:

【参考方案2】:

旧的 glibc 版本可能有一些与此相关的错误:

https://bugzilla.redhat.com/show_bug.cgi?id=89692 https://sourceware.org/bugzilla/show_bug.cgi?id=14898

你用的是什么版本?尝试使用较新的 glibc 版本,您的代码在我的计算机上运行良好(glibc 2.23)。

【讨论】:

有趣。在 Arch Linux 上使用 glibc 2.24 崩溃。你用什么系统? 我使用的是 Ubuntu 16.04

以上是关于C++ 强制卸载共享库的主要内容,如果未能解决你的问题,请参考以下文章

强制 GCC 通知共享库中未定义的引用

当存在同名的共享库时,如何强制链接静态库

在共享库中全局声明的非 POD 对象的语义是啥?

强制链接到未使用的共享库

ubuntulinux链接库

自行卸载的 Linux 共享库