Duplicating a static initialization variable in C ++

I am creating a shared library "libMyLibrary.so" with the class "MyClass", which contains a static variable like "MyClass". Then I create the executable file "MyLibraryTest", which I link to "libMyLibrary.so". The main program uses "dlopen" to dynamically load ".so" as an argument.

When building a library and an executable file are generated in the directory, for example, "buildDir / bin". Then I install the library in "installDir / lib" and the executable in "installDir / bin" (removing the execution path from the executable).

When I run "buildDir / MyLibraryTest buildDir / MyLibrary.so" with LD_LIBRARY_PATH = buildDir, everything is fine.

But when I run "buildDir / MyLibraryTest installDir / lib / MyLibrary.so" with LD_LIBRARY_PATH = installDir / lib, a very strange thing occurs: - The static variable constructor is called twice (once before dlopen, once during dlopen) - At the end of execution the destructor is called twice, and this leads to a failure.

Here is my code:

Myclass.h

#ifndef _MyClass_h__ #define _MyClass_h__ #include <string> class MyClass { private: static MyClass myStaticObjOfMyClass; public: MyClass(const std::string& name, bool trace); virtual ~MyClass(); private: std::string myName; bool myTrace; }; #endif // _MyClass_h__ 

Myclass.cpp

 #include "MyClass.h" #include <iostream> MyClass MyClass::myStaticObjOfMyClass("myStaticObjOfMyClass", true); MyClass::MyClass(const std::string& name, bool trace) : myName(name), myTrace(trace) { if (myTrace) std::cout << "MyClass::MyClass(name=" << myName << ", address=" << this << ")" << std::endl; } MyClass::~MyClass() { if (myTrace) std::cout << "MyClass::~MyClass(name=" << myName << ", address=" << this << ")" << std::endl; } 

MyLibraryTest.cpp

 #include <MyClass.h> #include <iostream> #include <string> #include <dlfcn.h> int main(int argc, char* argv[]) { const std::string sharedLibraryFullName((const char*)argv[1]); // std::cout << "Try to load library " << sharedLibraryFullName << std::endl; void* handle = NULL; std::cout << "dlopen(" << sharedLibraryFullName << ")" << std::endl; handle = dlopen(sharedLibraryFullName.c_str(), RTLD_LAZY | RTLD_GLOBAL); if (handle == NULL) { std::cout << "ERROR : Could not load shared library " << sharedLibraryFullName << std::endl; } else { std::cout << "OK, shared library " << sharedLibraryFullName << " is now loaded" << std::endl; } } 

Here are the compilation commands and links:

 /usr/local/bin/g++ -DMyLibrary_DEFINED -DMyLibrary_EXPORTS -O3 -DNDEBUG -fPIC -o CMakeFiles/MyLibrary.dir/MyClass.cpp.o -c MyClass.cpp /usr/local/bin/g++ -fPIC -O3 -DNDEBUG -shared -Wl,-soname,libMyLibrary.so -o ../bin/libMyLibrary.so CMakeFiles/MyLibrary.dir/MyClass.cpp.o 

And finally, here's what happens in the second case (duplicate initialization of a static variable):

 MyClass::MyClass(name=myStaticObjOfMyClass, address=0x7fa710cabb40) dlopen(/tmp/Install/MyLibraryTest/lib/libMyLibrary.so) MyClass::MyClass(name=myStaticObjOfMyClass, address=0x7fa710cabb40) OK, shared library /tmp/Install/MyLibraryTest/lib/libMyLibrary.so is now loaded MyClass::~MyClass(name=myStaticObjOfMyClass, address=0x7fa710cabb40) MyClass::~MyClass(name=  ObjOfMyClass, address=0x7fa710cabb40) *** glibc detected *** /tmp/Build/MyLibraryTest/Release/bin/MyLibraryTest: double free or corruption (fasttop): 0x0000000000cfb330 *** ======= Backtrace: ========= /lib64/libc.so.6[0x322f275dee] /lib64/libc.so.6[0x322f278c3d] /lib64/libc.so.6(__cxa_finalize+0x9d)[0x322f235d2d] /tmp/Build/MyLibraryTest/Release/bin/libMyLibrary.so(+0x1076)[0x7fa710aab076] ======= Memory map: ======== 00400000-00402000 r-xp 00000000 fd:00 1325638 /tmp/Build/MyLibraryTest/Release/bin/MyLibraryTest 00601000-00602000 rw-p 00001000 fd:00 1325638 /tmp/Build/MyLibraryTest/Release/bin/MyLibraryTest 00ce9000-00d1b000 rw-p 00000000 00:00 0 [heap] 322ee00000-322ee20000 r-xp 00000000 fd:00 545634 /lib64/ld-2.12.so 322f020000-322f021000 r--p 00020000 fd:00 545634 /lib64/ld-2.12.so 322f021000-322f022000 rw-p 00021000 fd:00 545634 /lib64/ld-2.12.so 322f022000-322f023000 rw-p 00000000 00:00 0 322f200000-322f38a000 r-xp 00000000 fd:00 545642 /lib64/libc-2.12.so 322f38a000-322f58a000 ---p 0018a000 fd:00 545642 /lib64/libc-2.12.so 322f58a000-322f58e000 r--p 0018a000 fd:00 545642 /lib64/libc-2.12.so 322f58e000-322f590000 rw-p 0018e000 fd:00 545642 /lib64/libc-2.12.so 322f590000-322f594000 rw-p 00000000 00:00 0 322fa00000-322fa02000 r-xp 00000000 fd:00 545709 /lib64/libdl-2.12.so 322fa02000-322fc02000 ---p 00002000 fd:00 545709 /lib64/libdl-2.12.so 322fc02000-322fc03000 r--p 00002000 fd:00 545709 /lib64/libdl-2.12.so 322fc03000-322fc04000 rw-p 00003000 fd:00 545709 /lib64/libdl-2.12.so 3230600000-3230683000 r-xp 00000000 fd:00 545684 /lib64/libm-2.12.so 3230683000-3230882000 ---p 00083000 fd:00 545684 /lib64/libm-2.12.so 3230882000-3230883000 r--p 00082000 fd:00 545684 /lib64/libm-2.12.so 3230883000-3230884000 rw-p 00083000 fd:00 545684 /lib64/libm-2.12.so 7fa70c000000-7fa70c021000 rw-p 00000000 00:00 0 7fa70c021000-7fa710000000 ---p 00000000 00:00 0 7fa7102e7000-7fa7102e9000 r-xp 00000000 fd:00 1320668 /tmp/Install/MyLibraryTest/lib/libMyLibrary.so 7fa7102e9000-7fa7104e8000 ---p 00002000 fd:00 1320668 /tmp/Install/MyLibraryTest/lib/libMyLibrary.so 7fa7104e8000-7fa7104e9000 rw-p 00001000 fd:00 1320668 /tmp/Install/MyLibraryTest/lib/libMyLibrary.so 7fa7104e9000-7fa7104ed000 rw-p 00000000 00:00 0 7fa7104ed000-7fa710503000 r-xp 00000000 fd:00 708322 /usr/local/lib64/libgcc_s.so.1 7fa710503000-7fa710702000 ---p 00016000 fd:00 708322 /usr/local/lib64/libgcc_s.so.1 7fa710702000-7fa710703000 rw-p 00015000 fd:00 708322 /usr/local/lib64/libgcc_s.so.1 7fa710703000-7fa710704000 rw-p 00000000 00:00 0 7fa710704000-7fa710883000 r-xp 00000000 fd:00 708539 /usr/local/lib64/libstdc++.so.6.0.21 7fa710883000-7fa710a83000 ---p 0017f000 fd:00 708539 /usr/local/lib64/libstdc++.so.6.0.21 7fa710a83000-7fa710a8d000 r--p 0017f000 fd:00 708539 /usr/local/lib64/libstdc++.so.6.0.21 7fa710a8d000-7fa710a8f000 rw-p 00189000 fd:00 708539 /usr/local/lib64/libstdc++.so.6.0.21 7fa710a8f000-7fa710a94000 rw-p 00000000 00:00 0 7fa710aa8000-7fa710aaa000 rw-p 00000000 00:00 0 7fa710aaa000-7fa710aac000 r-xp 00000000 fd:00 1325633 /tmp/Build/MyLibraryTest/Release/bin/libMyLibrary.so 7fa710aac000-7fa710cab000 ---p 00002000 fd:00 1325633 /tmp/Build/MyLibraryTest/Release/bin/libMyLibrary.so 7fa710cab000-7fa710cac000 rw-p 00001000 fd:00 1325633 /tmp/Build/MyLibraryTest/Release/bin/libMyLibrary.so 7fa710cac000-7fa710cad000 rw-p 00000000 00:00 0 7fff2fc61000-7fff2fc76000 rw-p 00000000 00:00 0 [stack] 7fff2fde5000-7fff2fde6000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] ./test_dyn_libs.sh: line 21: 30880 Abandon (core dumped) ${BuildDir}/MyLibraryTest ${InstallDir}/lib/libMyLibrary.so --- End of tests 

Any help would be greatly appreciated.

+5
source share
3 answers

Thank you all for your answers. As Todd said, I forgot to include a link for MyLibraryTest. Here he is:

 /usr/local/bin/g++ -O3 -DNDEBUG -rdynamic CMakeFiles/MyLibraryTest.dir/MyLibraryTest.cpp.o -o ../bin/MyLibraryTest -Wl,-rpath,/tmp/Build/MyLibraryTest/Release/bin: ../bin/libMyLibrary.so -ldl 

The problem was related to RPATH. If I no longer use the -Wl, -rpath option, the test works fine!

I use CMake to create my projects and found this: https://cmake.org/Wiki/CMake_RPATH_handling . Now I use the following command in CMakeLists.txt, which removes the -Wl, -rpath link.

 set (CMAKE_SKIP_RPATH ON) 
0
source

As Todd Fleming commented, I think that you probably mistakenly linked your MyLibrary.so with your test executable when compiling the test executable.

You can use ldd to check if your executable has been linked to the library.

I tried my code on my Linux node with exactly the same compilation options. For the test executable, that MyLibrary.so should not be linked, so I did not link it. It turns out that strange behavior did not happen. When I linked the test executable to MyLibrary.so, the result is exactly what you said (including a glibc dump). This is not too surprising, because when you install your library on a different path, the loader will consider it as another completely inappropriate library, thus double-loaded.

PS: What surprised me was that these 2 singletones are located in the same memory location, which is somewhat unbelievable. I also tested it (the case when the executable is associated with the library) on my mac os x, it turns out that on os x these two instances have different memory. This is more reasonable for me.

UPDATE:

About why these 2 instances have the same memory location, this is actually a Linux character resolution feature. See comments for details. Thanks n.m. to indicate.

+3
source

I think the static initialization problem is resolved if you hide your static variable in a static getter. So in the header you are replacing a static class variable

 static MyClass myStaticObjOfMyClass; 

with static class function

 static MyClass& getInstance(); 

and implement it in cpp:

 static MyClass& getInstance() { static MyClass instance("myStaticObjOfMyClass", true); return instance; } 

A static object is initialized the first time getInstance() called.

0
source

Source: https://habr.com/ru/post/1274773/


All Articles