无论调用多少次,dlclose都不会真正卸载共享对象

Ele*_*ito 12 c c++ linux gcc dlopen

我的程序dlopen用来加载共享对象,然后dlclose卸载它.有时这个共享对象会再次加载.我注意到静态变量没有重新初始化(这对我的程序来说至关重要)所以我在之后添加了一个测试(dlopenwith RTLD_NOLOAD)dlclose来查看库是否真的被卸载了.果然,它还在记忆中.

然后我尝试dlclose反复调用,直到库真的被卸载,但我得到的是一个无限循环.这是我用来检查库是否已卸载的代码:

dlclose(handles[name]);

do {
  void *handle = dlopen(filenames[name], RTLD_NOW | RTLD_NOLOAD);
  if (!handle)
    break;

  dlclose(handle);
} while (true);
Run Code Online (Sandbox Code Playgroud)

我的问题是,dlclose考虑到我的dlopen调用是唯一加载它的地方,我的共享对象之后没有被卸载的可能原因是什么.你能否提出一个行动方案来追查问题的根源?另外,为什么重复调用dlclose没有效果,它们每个都递减引用计数,不是吗?

编辑:刚刚发现只有当我用gcc编译时才会发生这种情况.有了clang,一切都很好.

Mec*_*cki 16

POSIX标准实际上不需要dlclose从地址空间卸载库:

虽然从地址空间中删除结构不需要 dlclose()操作,但禁止实现这样做.

资料来源:公开集团基本规格问题6

这意味着除了使句柄无效之外,dlclose根本不需要做任何事情.

有时卸载也会被系统延迟,它只是将库标记为"要删除",并且实际上会在稍后的某个时间执行该操作(为了提高效率,或者因为现在根本无法执行该操作).但是,如果dlopen在执行之前再次调用,则清除该标志并重新使用仍然加载的库.

在某些情况下,系统肯定知道库中的某些符号仍在使用中,在这种情况下,它不会从地址空间中卸载它以避免悬空指针.在某些情况下,系统并不确定它们是否正在使用中,但它也可能无法确定它们不是,更好的安全而不是遗憾,在这种情况下它永远不会真正从内存中删除该库.

还有其他更加模糊的情况取决于操作系统类型,通常也在版本上.例如,一个常见的Linux问题是如果您创建了一个使用STB_GNU_UNIQUE符号的库,该库被标记为"不可卸载",因此将永远不会被卸载.见这里,这里(DF_1_NODELETE指未卸),并在这里.因此,它还可以取决于编译器生成的符号或符号类型.尝试readelf -Ws在您的库上运行并查找标记为的对象UNIQUE.

一般来说,你不能真正依赖于dlclose你所期望的工作.在实践中,我看到它在过去十年中"失败"而不是"成功"(好吧,它从未真正失败过,它通常不会从内存中卸载库;但它按照标准的要求工作).

  • 非常有帮助的答案!对因“STB_GNU_UNIQUE”而遇到此问题的人的提示:使用 GCC,您可以设置编译选项“--no-gnu-unique”来避免此问题。 (3认同)

scr*_*ari 7

这不是您所有问题的答案,但这是可以帮助您避免dlclose. 这个问题提出了一个关于如何影响重新加载共享库行为的线索:您可以使用编译器标志-fno-gnu-unique

来自gcc/ 的手册页g++

-fno-gnu-unique

在具有最新 GNU 汇编器和 C 库的系统上,C++ 编译器使用“STB_GNU_UNIQUE”绑定来确保内联函数中的模板静态数据成员和静态局部变量的定义即使在存在“RTLD_LOCAL”的情况下也是唯一的;这是必要的,以避免两个不同的“RTLD_LOCAL”插件使用的库出现问题,这取决于其中一个插件中的定义,因此在符号绑定方面与另一个不同意。但这会导致受影响的 DSO 忽略“dlclose”;如果您的程序依赖于通过“dlclose”和“dlopen”重新初始化 DSO,您可以使用 -fno-gnu-unique。

是否-fno-gnu-unique默认使用取决于 GCC 的配置方式:--disable-gnu-unique-object默认启用此标志,--enable-gnu-unique-object禁用它。


jco*_*and 1

动态库加载有很多怪癖。依赖操作系统来初始化静态变量充满了问题。您最好要么完全避免它,要么使用插件加载器来为您处理所有特殊情况。

我建议您查看glib 模块Glib提供了一种独立于平台的加载动态库的方式。您可以使用这些回调:

他们可以处理分配和取消分配任何资源。您可以动态分配您需要的内容,而不是依靠操作系统以可靠的方式为您分配静态数据。

您需要做的就是在动态库中定义这些函数,然后使用以下命令加载和卸载它们:

  • glib 模块还在 Windows 以外的所有平台上使用 dlopen/dlclose,并且如果 dlclose 不卸载库,则在不同的系统上可能由于多种原因而发生这种情况(有些实际上根本不卸载库,dlclose 只是那里的一个空方法)那么使用 glib 模块在该平台上不会给你带来任何好处。 (2认同)