In the main program, I dlopen and dlclose (LoadLibrary and FreeLibrary respectively) are a shared library. The shared library contains a static variable that is created on dlopen and destroyed on dlclose. This behavior is consistent with MSVC 2008 and 2013, GCC 3.4.6 and Sunstudio 12.1. However, with GCC 4.9.1 and GCC 5.2.1, the destructor was no longer called on dlclose. Instead, it is called before the program exits.
A feature of the static class of variables is that the constructor has a call to the get template function (global scope), which returns the local static variable.
I was able to reproduce this behavior with the following single cpp file linked in a shared library:
#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;
I explored the tricks that can be done to have a destructor called dlclose and concluded the following:
- If the get function has not been templated
- else if the variable i in the get function was not static
- else if the get function is static
The main program code is as follows:
#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; }
I created binaries with the following commands:
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
The output that I get after calling the destructor before exiting the program is as follows:
Calling Constructor DLL unloaded Calling Destructor
The output that I get when calling the destructor in dlclose is this:
Calling Constructor Calling Destructor DLL unloaded
Questions:
- If changing behavior between versions of GCC is not an error, can you explain why the destructor is not called on dlclose?
- Can you explain for each setting: why is the destructor called in dlclose in this case?