Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Duplicate static variable initialization in C++

I build a shared library "libMyLibrary.so" with a class "MyClass" that contains a static variable of type "MyClass". Then I build an executable "MyLibraryTest" that I link against "libMyLibrary.so". The main program uses "dlopen" to load dynamically a ".so" given as an argument.

While building, the library and executable are generated in a directory, say, "buildDir/bin". Then I install the library into "installDir/lib" and the executable into "installDir/bin" (removing the runtime 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's constructor is called twice (once before dlopen, once during dlopen) - At the end of the execution, the destructor is called twice, and this results into a crash.

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 and link commands :

/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 at last here is what happens in the second case (duplicate initialization of the 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 !!!

like image 892
ConanLord Avatar asked Jan 17 '18 18:01

ConanLord


People also ask

Can static variables be initialized twice?

But in some cases it generates code that uses two guard variables and initialize each static variable separately. The problem is when such two types of initialization are mixed in executable binary. In such case it may happen that second static variable will get initialized twice.

Can static variables be initialized in C?

In C, static variables can only be initialized using constant literals.

Are static variables always initialized to zero in C?

3) Static variables (like global variables) are initialized as 0 if not initialized explicitly.

Is static variable initialized only once?

A static variable in a block is initialized only one time, prior to program execution, whereas an auto variable that has an initializer is initialized every time it comes into existence. A static object of class type will use the default constructor if you do not initialize it.


1 Answers

As Todd Fleming commented, I think probably you have mistakenly linked your MyLibrary.so into your test executable when you compile the test executable.

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

I have tried your code on my ubuntu linux with exactly the same compiling options. For the test executable, that MyLibrary.so should not be linked, so I didn't link it. It turns out the bizarre behavior didn't occur. When I linked the test executable with MyLibrary.so, the result is exactly what you said (including the glibc dump). It is not too surprising, because when you install your library to another path, the loader will regard it as another totally irrelevant library, thus double-loaded.

PS: What surprised me is that these 2 singletons are placed in the same memory location, it's somewhat unbelievable. I also tested it (the case when the executable is linked to the library) on my mac os x, it turns out that on os x, these 2 instances have different memory location. It's more reasonable to me.

UPDATE:

About why these 2 instances have the same memory location, actually it's a feature of symbol resolution in Linux. Please see the comments for details. Thanks n.m. for pointing out.

like image 53
llllllllll Avatar answered Oct 07 '22 02:10

llllllllll