Hi all! I've faced with strange behavior when I investigated a bug on a rather new distros of Linux. I'm not sure that it is a bug of gcc, but may be someone can bring some light to it.
So, my project heavily uses plugins which are dynamically loaded with dlopen() and unloaded with dlclose() functions. I've got report that some (not all) plugins are not unloaded: they remain in the process map and if you put a new version of binary it's not loaded until program is finished and started again. This happens only on rather new distros: RHEL 7x (gcc 4.8), Debian 7 (gcc 4.7) and last versions of Ubuntu (gcc 4.7 - 4.9). While on RHEL 6.x and Debian 6.x (gcc 4.4 on both) everything works as expected. Checked only on x86_64 architecture. So I "bisected" the code to find why this happens. I've found that if plugin contains a static variable in the inlined function, then dlclose() leaves the SO in the memory. Also I've found that it happens only if it is compiled with g++. The same example renamed to *.c and compiled with gcc works OK. Attached is the sample which reproduces this effect. Use make command to build 'test' executable and 'test.so' shared object, then run: $ ./test If it outputs something like: 7fc80cdf2000-7fc80cdf3000 r-xp 00000000 fe:02 5902461 /home/tests/so/test.so 7fc80cdf3000-7fc80cff2000 ---p 00001000 fe:02 5902461 /home/tests/so/test.so 7fc80cff2000-7fc80cff3000 rw-p 00000000 fe:02 5902461 /home/tests/so/test.so then shared object was not unloaded. Remove "inline" keyword in so.cpp and everything works fine. That bad thing is that some parts of boost also make the plugin unloadable and this can be controlled. So, is it a bug? If no, can it be somehow workarounded? -- With best regards Max Dmitrichenko
#include <dlfcn.h> #include <iostream> #include <fstream> #include <string> int main() { void* handle = dlopen("test.so", RTLD_NOW); if(!handle) { std::cerr << "Failed to load shared object: " << dlerror() << std::endl; return 1; } int rc = dlclose(handle); if(rc) { std::cerr << "Failed to unload shared object: " << dlerror() << std::endl; return 1; } std::ifstream map("/proc/self/maps"); while(!map.eof()) { std::string line; std::getline(map, line); if(line.find("test.so") != std::string::npos) { std::cout << line << std::endl; } } return 0; }
Makefile
Description: Binary data
inline int b() { static int x = 0; return x; } int a() { return b(); }