不在dlclose

时间:2016-07-21 17:23:31

标签: c++ gcc shared-libraries dynamic-linking dlopen

在主程序中,我dlopendlclose(分别为LoadLibraryFreeLibrary)共享库。共享库包含一个静态变量,该变量在dlopen上实例化,并在dlclose时销毁。这种行为在MSVC 2008和2013,GCC 3.4.6和Sunstudio 12.1上是一致的。 但是,使用GCC 4.9.1和GCC 5.2.1,不再在dlclose上调用析构函数。相反,它是在程序退出之前调用的。

静态变量类的特殊性在于,在构造函数中,调用模板化函数 get (全局范围),返回本地静态变量。

我能够使用链接到共享库的以下一个cpp文件重现此行为:

#include <iostream>

template <typename T> // In my actual code, i is of type T, however, this has no effect
int get()
{
   static int i = 0;
   return i;
}

class Dictionary {
public:
   Dictionary()
   {
      std::cout << "Calling Constructor" << std::endl;
      get<int>();
   }
   ~Dictionary(){
      std::cout << "Calling Destructor" << std::endl;
   }

private:
   Dictionary(const Dictionary&);
   Dictionary& operator=(const Dictionary&);
};
static Dictionary d;

我研究了为了在dlclose上调用析构函数而进行的调整,并得出以下结论:

  • 如果函数 get 未模板化
  • 如果函数中的变量i 不是静态的
  • 如果函数 get 是静态的
  • ,则为

主程序的代码如下:

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

void* LoadLib(std::string name)
{
      void* libInstance;
      name = "lib" + name + ".so";
      libInstance = dlopen(name.c_str(), RTLD_NOW);
      if ( ! libInstance ) std::cout << "Loading of dictionary library failed. Reason: " << dlerror() << std::endl;
      return libInstance;
}

bool UnloadLib(void* libInstance)
{
     int ret = dlclose(libInstance);
     if (ret == -1)
     {
        std::cout << "Unloading of dictionary library failed. Reason: " << dlerror() << std::endl;
        return false;
     }
     return true;
}

int main()
{
   void* instance = LoadLib("dll");
   assert(instance != 0);

   assert(UnloadLib(instance));
   std::cout << "DLL unloaded" << std::endl;
}

我使用以下命令构建了二进制文件:

g++ -m64 -g -std=c++11 -shared -fPIC dll.cpp -o libdll.so
g++ -m64 -g -std=c++11 -ldl main.cpp -o main.out

在程序退出之前调用析构函数时得到的输出如下:

Calling Constructor
DLL unloaded
Calling Destructor

在dlclose上调用析构函数时得到的输出如下:

Calling Constructor
Calling Destructor
DLL unloaded

问题:

  • 如果GCC版本之间的行为更改不是错误,请解释为什么不在dlclose上调用析构函数?
  • 你可以解释一下每个调整:为什么在这种情况下,析构函数被调用dlclose?

1 个答案:

答案 0 :(得分:4)

无法保证在dlclose上发生卸载(析构函数被调用)。在musl(而不是glibc)上,构造函数仅在第一次运行库时运行,而析构函数仅在退出时运行。对于可移植代码,不能假定dlclose立即卸载符号。

卸载行为取决于glibc在进行动态链接时的符号绑定,并且独立于GCC。

静态变量get::i具有STB_GNU_UNIQUE绑定。对于内联函数中的静态变量,ELF链接器确保了对象的唯一性。但是,对于动态加载,动态链接器通过标记符号STB_GNU_UNIQUE来确保唯一性。因此,另一个尝试通过其他代码来删除相同的共享库将查找该符号并发现它是唯一的并从唯一符号表返回现有的符号。无法卸载具有唯一绑定的符号。

如果不需要,可以使用-fno-gnu-unique禁用唯一绑定。

<强>参考

Bug that I raised to GCC

STB_GNU_UNIQUE