静态对象在dlclose()之前已销毁

时间:2019-06-28 14:40:00

标签: c++ gcc shared-libraries

我正在一个跨平台的项目中,该项目由多个库组成,根据运行时条件动态地相互加载和卸载。当前,我观察到一个崩溃,这似乎是由于在使用dlclose()卸载共享库之前,其中一个共享库中的静态对象已被破坏而造成的。这似乎很奇怪,对我来说更像是个虫子。

为了研究这个问题,我创建了一个简单的项目,该项目包含三个源文件:main.cpp,lib1.cpp和lib2.cpp(分别用于可执行文件和两个库)。主可执行文件动态加载lib1,而lib1又动态加载lib2。

main.cpp:

Logger mainGlobal("mainGlobal");

int main(int argc, char * argv[])
{
    Logger mainFunction("mainFunction");
    try
    {
        Logger mainTry("mainTry");
        libutil::AutoLib lib("lib1");
        lib.call("loadLib2");
    }
    catch (std::exception & e)
    {
        std::cerr << "Fatal: " << e.what() << std::endl;
    }
    std::cout << "Exiting main" << std::endl;
}

lib1.cpp:

Logger lib1Global("lib1Global");

std::auto_ptr<libutil::AutoLib> lib2;

DLL_EXPORT void loadLib2()
{
    std::cout << "loadLib2" << std::endl;
    lib2.reset(new libutil::AutoLib("lib2"));
}

lib2.cpp:

Logger lib2Global("lib2Global");

Logger是一个简单的结构,只需登录其构造函数和析构函数即可。 libutil::AutoLib是一个共享库加载器,它在其ctor中调用dlopen(path, RTLD_LAZY),在其dtor中调用dlclose(),并允许调用从共享库导出的函数。这些类的代码很简单,但是如果需要,我也可以在这里发布。

长话短说,如果我调用主可执行文件,则会看到以下日志:

mainGlobal ctor
mainFunction ctor
mainTry ctor
Loading library lib1.so
lib1Global ctor
dlopen(lib1.so) returned 0x14cd050
Library lib1.so loaded with handle 0x14cd050
Calling loadLib2 in library 0x14cd050
loadLib2
Loading library lib2.so
lib2Global ctor
dlopen(lib2.so) returned 0x14cd710
Library lib2.so loaded with handle 0x14cd710
Unloading library 0x14cd050
Calling dlclose(0x14cd050)
Library unloaded 0x14cd050
mainTry dtor
Exiting main
mainFunction dtor
lib2Global dtor
Unloading library 0x14cd710
Calling dlclose(0x14cd710)
Library unloaded 0x14cd710
lib1Global dtor
mainGlobal dtor

请注意lib2Global dtor行之前的Calling dlclose(0x14cd710)行。

问题是,这是错误还是正确的行为?

SO中存在一些关于dlclose()之后不会被销毁的静态对象的问题,但是我没有发现有关相反情况的任何问题。

我正在使用GCC 5.4.0-6ubuntu1〜16.04.10。

1 个答案:

答案 0 :(得分:0)

感谢戴维斯·鲱鱼的提示,我找到了原因。某种程度上是将lib1.so保留在内存中,不允许其卸载。事实证明,lib1.so调用了一个inline函数,该函数包含static const变量,这使gcc为该变量创建STB_GNU_UNIQUE绑定。即使lib1.so已加载RTLD_LOCAL,它实际上也使static不可加载。因此,要解决此问题,我可以从变量定义中删除inline限定符,或者从函数定义中删除-fno-gnu-unique限定符,或者使用mainGlobal ctor mainFunction ctor mainTry ctor Loading library lib1.so lib1Global ctor dlopen(lib1.so) returned 0x1cfe050 Library lib1.so loaded with handle 0x1cfe050 Calling loadLib2 in library 0x1cfe050 loadLib2 Loading library lib2.so lib2Global ctor dlopen(lib2.so) returned 0x1cfe710 Library lib2.so loaded with handle 0x1cfe710 Unloading library 0x1cfe050 Calling dlclose(0x1cfe050) Unloading library 0x1cfe710 Calling dlclose(0x1cfe710) Library unloaded 0x1cfe710 lib1Global dtor lib2Global dtor Library unloaded 0x1cfe050 mainTry dtor Exiting main mainFunction dtor mainGlobal dtor g ++标志。 完成此操作后,问题就消失了:

-fno-gnu-unique
    On systems with recent GNU assembler and C library, the C++ compiler 
    uses the STB_GNU_UNIQUE binding to make sure that definitions of template 
    static data members and static local variables in inline functions are unique
    even in the presence of RTLD_LOCAL ; this is necessary to avoid problems 
    with a library used by two different RTLD_LOCAL plugins depending on a definition
    in one of them and therefore disagreeing with the other one about the binding of 
    the symbol. But this causes dlclose to be ignored for affected DSOs; if your
    program relies on reinitialization of a DSO via dlclose and dlopen , you can use 
    -fno-gnu-unique .

以下是GNU GCC帮助的摘录:

tick()

Here是带有相关问题的问题。