c++ - dlclose() 没有卸载链接到 boost 的 .so 文件

标签 c++ linux boost

如果我的应用加载(使用 dlopen)一个链接到 Boost 测试框架的 .so 文件,我无法卸载该 so 文件。没有链接到 boost 似乎可以卸载它。

应用文件 main.cpp:

#include <dlfcn.h>
#include <iostream>

int main()
{
   auto sFileName = "./libtest_library.so";
   auto handle = dlopen(sFileName, RTLD_LAZY | RTLD_LOCAL);

   if (!handle) 
      std::cerr << "Error: " << dlerror() << std::endl;

   auto closing = dlclose(handle);
   while(1);
   return 0;
}

库.so文件(libtest_library.so):

#include <iostream>
//#include "boost/test/unit_test.hpp"

static void con() __attribute__((constructor));
static void dcon() __attribute__((destructor));

void con()
{
   std::cout << "Constructing library..." << std::endl;
}

void dcon()
{
   std::cout << "Destructing library..." << std::endl;
}

运行这个我得到输出:

Constructing library...
Destructing library...

如果我在 libtest_library.so 中链接到 Boost 的单元测试框架,我只会得到 Constructing library... 输出。 dlclose(handle) 返回 0(即成功)。

当前链接到 Boost v.1.60.0,在 Ubuntu 14.04 上使用 gcc 5_2_0 编译。 这是Boost中的错误吗?编译器?有任何想法吗?

我需要在项目中多次重新加载.so文件,并且需要完全卸载(不存在于内存中)。我该如何解决这个问题?谢谢。


更新 1:
似乎如果我只链接到 boost libtest_library 析构函数实际上被调用但 boost_test_framework 库没有卸载。但是,如果我包含“boost/test/unit_test.hpp”,则不会调用析构函数(libtest_library.so 拒绝卸载)。

更新 2:
通过查看 boost 的源代码,我发现 boost 中有一个 c++ 单例导致了这个问题。

我可以在简化版本中重现该问题。 基本上,如果我将以下单例添加到 libtest_library,它就不起作用(无法卸载 .so 文件):

替代 1

class Singleton
{
public:
   static Singleton & getInstance() { static Singleton instance; return instance; }    
private:
   Singleton() {}
   ~Singleton() {}
};

static Singleton & singleton = Singleton::getInstance();

但是使用这个有效:

备选方案 2

class Singleton
{
public:
    static Singleton & getInstance();
private:
   Singleton() {}
   ~Singleton() {}
};

Singleton & Singleton::getInstance() { static Singleton instance; return instance; }

static Singleton & singleton = Singleton::getInstance();

我尝试了不同的 GCC 编译器,结果都是一样的。对我来说这似乎是错误?

符号也有点不同:做 nm –C libtest_library.so | grep –i singleton 我明白了

alt 1(不工作):

0000000000201460 u guard variable for Singleton::getInstance()::instance
0000000000201458 b singleton
0000000000000e66 W Singleton::getInstance()
0000000000000f08 W Singleton::Singleton()
0000000000000f08 W Singleton::Singleton()
0000000000000f1c W Singleton::~Singleton()
0000000000000f1c W Singleton::~Singleton()
0000000000201468 u Singleton::getInstance()::instance

和备选方案 2:

00000000002012f8 b guard variable for Singleton::getInstance()::instance
0000000000201300 b singleton
0000000000000bb0 T Singleton::getInstance()
0000000000000cec W Singleton::Singleton()
0000000000000cec W Singleton::Singleton()
0000000000000d00 W Singleton::~Singleton()
0000000000000d00 W Singleton::~Singleton()
0000000000201308 b Singleton::getInstance()::instance

有什么想法吗?

更新 3

我已经提取了 boost 中似乎会产生问题的部分,并创建了一个演示问题的最小示例:

ma​​in_app.cpp - 主应用

#include <dlfcn.h>
#include <iostream>

int main()
{
   for(auto i = 0; i < 2; i++) {
      auto sFileName = "./libtest_library.so";
      auto handle = dlopen(sFileName, RTLD_LAZY | RTLD_LOCAL);

      if (!handle) {
         printf("Dlerror: %s\n", dlerror());
         continue;
      }

      auto closing = dlclose(handle);
      printf("Dlerror: %s\n", dlerror());
   }

   return 0;
}

ma​​in_lib.cpp - libtest_library.so

#include <iostream>

template<typename Derived>
class trivial_singleton_t {
public:
   static Derived& instance() { static Derived the_inst; return the_inst; }
protected:
   trivial_singleton_t() {}
   ~trivial_singleton_t() {}
};

class singleton_t : public trivial_singleton_t<singleton_t> {

private:
   friend class trivial_singleton_t<singleton_t>;
   singleton_t() {}
};

singleton_t & singleton = singleton_t::instance();

static void con() __attribute__((constructor));
static void dcon() __attribute__((destructor));

void con()
{
   std::cout << "Constructing library..." << std::endl;
}

void dcon()
{
   std::cout << "Destructing library..." << std::endl;
}

我得到以下输出:

Constructing library...
Dlerror: (null)
DLerror: (null)
Destructing library...

因此只有当 main 存在时才会卸载库。

最佳答案

如问题中所述,编译的二进制文件中有 STB_GNU_UNIQUE 符号。

问题是使用这些符号并加载了 dlopen 的库将被标记为 NODELETE,因此在 dlopen/dlclose< 之间持续存在 调用。请在此处查看第 445 行:http://osxr.org:8080/glibc/source/elf/dl-lookup.c

https://sourceware.org/binutils/docs/binutils/nm.html , STB_GNU_UNIQUEu:

The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use.

这些是在匿名命名空间中或作为静态全局变量创建变量/方法时创建的。

最快的解决方案是使用链接器标志 --no-gnu-unique 强制编译器不将这些符号构建为 STB_GNU_UNIQUE

不幸的是,这对我不起作用,因为我没有足够新的链接器,幸​​运的是我可以使用以下配置选项重建 gcc:--disable-gnu-unique-object。请记住还要使用链接器标志或新的 gcc 重建 boost 库。

关于c++ - dlclose() 没有卸载链接到 boost 的 .so 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38869657/

相关文章:

c++ - 如何在 Spirit Lex 模式中使用斜杠?

c++ - #ifndef 和#pragma once 有什么区别, "same file"是什么意思?

c++ - 关于 typedef 的基本 C++ 语法,谁能解释一下?

linux - 如何在 ListView 中剪切 .txt 文件

带参数的 Linux bash 脚本?

c++ - boost 序列化多个对象

c++ - 构造函数、类、 union 、结构

c++ - 使用字符串在C++中编写代码

linux - 如何使用 x86-64 Linux 系统调用附加到文件?

c++ - Boost.Bind - 理解占位符