代码之家  ›  专栏  ›  技术社区  ›  ConanLord

在C中重复静态变量初始化++

  •  6
  • ConanLord  · 技术社区  · 7 年前

    我用一个包含“MyClass”类型静态变量的类“MyClass”构建了一个共享库“libMyLibrary.so”。 然后,我构建了一个可执行的“MyLibraryTest”,并将其链接到“libMyLibrary.so”。 主程序使用“dlopen”动态加载作为参数给定的“.so”。

    构建时,库和可执行文件在一个目录中生成,例如“buildDir/bin”。 然后,我将库安装到“installDir/lib”中,将可执行文件安装到“installDir/bin”(从可执行文件中删除运行时路径)。

    当我用LD\u LIBRARY\u PATH=buildDir运行“buildDir/MyLibraryTest buildDir/MyLibrary.so”时,一切都很好。

    但当我用LD\u LIBRARY\u PATH=installDir/lib运行“buildDir/MyLibraryTest installDir/lib/MyLibrary.so”时,发生了一件非常奇怪的事情: -静态变量的构造函数被调用 两次 (dlopen前一次,dlopen期间一次) -在执行结束时,析构函数被调用两次,这将导致崩溃。

    这是我的代码:

    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;
    }
    

    我的图书馆测试。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;
        }
    }
    

    以下是编译和链接命令:

    /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
    

    最后是在第二种情况下发生的情况(静态变量的重复初始化):

    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
    

    任何帮助都将不胜感激!!!

    3 回复  |  直到 7 年前
        1
  •  3
  •   llllllllll    7 年前

    正如托德·弗莱明(ToddFleming)所评论的那样,我认为可能是您错误地链接了您的MyLibrary。因此,在编译测试可执行文件时,请将其转换为测试可执行文件。

    您可以使用 ldd公司 检查可执行文件是否已链接到库。

    我在我的ubuntu linux上用完全相同的编译选项尝试了你的代码。对于测试可执行文件,即MyLibrary。所以不应该链接,所以我没有链接它。事实证明,这种奇怪的行为并没有发生。 当我将测试可执行文件与MyLibrary链接时。因此,结果正是您所说的(包括glibc转储)。这并不奇怪,因为当您将库安装到另一个路径时,加载程序会将其视为另一个完全不相关的库,因此会进行双重加载。

    PS:让我惊讶的是,这两个单件放在同一个内存位置,这有点难以置信。我还在mac os x上测试了它(当可执行文件链接到库时),结果表明,在os x上,这两个实例具有不同的内存位置。这对我来说更合理。

    更新:

    关于为什么这两个实例具有相同的内存位置,实际上这是Linux中的符号解析功能。详情请参见评论。感谢n.m.指出。

        2
  •  0
  •   pschill    7 年前

    我认为,如果将静态变量隐藏在静态getter中,静态初始化问题就会得到解决。因此,在标题中替换静态类变量

    static MyClass myStaticObjOfMyClass;
    

    使用静态类函数

    static MyClass& getInstance();
    

    并在cpp中实施:

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

    静态对象在以下情况下初始化 getInstance() 是第一次打电话。

        3
  •  0
  •   ConanLord    7 年前

    谢谢大家的回答。 正如Todd所说,我忘记了为MyLibraryTest包含link命令。这是:

    /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
    

    该问题与RPATH有关。如果我不再使用-Wl,-rpath选项,测试就可以了!

    我使用CMake构建我的项目,并发现: https://cmake.org/Wiki/CMake_RPATH_handling . 现在,我在CMakeLists中使用以下命令。txt,它删除了-Wl,-rpath链接选项。

    set (CMAKE_SKIP_RPATH ON)