Dlclose()没有卸载链接到boost的.so-file

dlclose() not unloading .so-file which is linking to boost

本文关键字:boost so-file 链接 卸载 Dlclose      更新时间:2023-10-16

如果我的应用程序加载(使用dlopen)一个。so文件,这是链接到Boost测试框架,我不能卸载so文件。没有链接到boost,卸载它似乎很好。

App file 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;
}

Library .so file (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中链接到Boost的单元测试框架。所以我只得到Constructing library...的输出。Dlclose (handle)返回0(表示成功)。

目前与Boost v. 1.60.0链接,在Ubuntu 14.04上使用gcc 5_2_0编译。这是Boost中的一个bug吗?编译器吗?什么好主意吗?

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


更新1:


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

更新2:


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

我可以用一个简化的版本复制这个问题。基本上,如果我将以下单例添加到libtest_library,它不会工作(无法卸载。so文件):

1 <<p> alt/strong>
class Singleton
{
public:
   static Singleton & getInstance() { static Singleton instance; return instance; }    
private:
   Singleton() {}
   ~Singleton() {}
};
static Singleton & singleton = Singleton::getInstance();

但是使用这个可以:

2 <<p> alt/strong>
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

And alt 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中似乎产生问题的部分,并创建了一个演示问题的最小示例:

main_app.cpp -主app

#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: %sn", dlerror());
         continue;
      }
      auto closing = dlclose(handle);
      printf("Dlerror: %sn", dlerror());
   }
   return 0;
}

main_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:

该符号是唯一的全局符号。这是ELF符号绑定标准集的GNU扩展。对于这样的符号,动态链接器将确保在整个过程中只有一个具有此名称和类型的符号在使用。

当变量/方法在匿名命名空间中创建或作为静态全局变量创建时生成。

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

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

模板trivial_singleton_t对singleton_t和singleton_t的循环依赖会混淆运行时链接。这里有一个更简单的例子。

#include <iostream>
template<class T>
struct base
{
   static T& method() { static T t; return t;} 
};
struct derived:public base<derived>
{
};

static void con() __attribute__((constructor));
static void dcon() __attribute__((destructor));
void never_called()
{
   base<derived>::method();
}
void con()
{
   std::cout << "Constructing library..." << std::endl;
}
void dcon()
{
   std::cout << "Destructing library..." << std::endl;
}

似乎在编译时创建模板类型会导致循环依赖。该示例实际上并没有在运行时创建实例。

这是一个将单例委托给派生类的破解方法。对于单例示例不太有用,但显示了一种打破循环的可能方法。

template<class T>
struct base
{
   static T& method() { return T::delegate(); }
};

struct derived;
derived * d_ptr;
struct derived:public base<derived>
{
   friend class base<derived>;
   static derived& delegate() { if(d_ptr == nullptr) d_ptr = new derived; return * d_ptr; }
};

static void con() __attribute__((constructor));
static void dcon() __attribute__((destructor));
void never_called()
{
   derived & d = base<derived>::method();
}

我不确定这是不是一个bug。这似乎是个特例。这个问题很酷!

相关文章: