【问题标题】:Static objects destructed BEFORE dlclose()在 dlclose() 之前销毁的静态对象
【发布时间】:2019-11-10 12:42:22
【问题描述】:

我正在开发一个跨平台项目,该项目由多个库组成,根据运行时条件动态加载和卸载另一个库。目前我观察到一个崩溃,这似乎是由于一个共享库中的静态对象在使用 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(),并允许调用从共享库中导出的函数。这些类的代码很简单,但如果需要,我也可以在这里发布。

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

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) 行之前。

所以问题是,这是一个错误还是正确的行为?

这里有关于在dlclose() 之后静态对象没有被销毁的问题,但我没有发现任何关于相反情况的问题。

我使用的是 GCC 5.4.0-6ubuntu1~16.04.10。

【问题讨论】:

  • 你使用过-fuse-cxa-atexit编译器标志吗?这是否回答了您的问题:stackoverflow.com/questions/42912038/…
  • @IgorG 感谢您的建议。我尝试添加-fuse-cxa-atexit,没有明显区别。但是在我把它改成相反的-fno-use-cxa-atexit之后,最后破坏的顺序改变了:mainFunction dtor, mainGlobal dtor, Unloading library 0x95a710, Calling dlclose(0x95a710), Library unloaded 0x95a710, lib1Global dtor, lib2Global dtor。所以,现在似乎共享对象的静态在最后被破坏了。
  • 由于在执行过程中调用exit 是合法的,它并不假定您将自己处理所有事情并为您执行dlclose。但这意味着您不能从exit 期间可能运行的东西中自己调用它。我不明白为什么lib1.so 在你执行dlclose 之后仍然加载;在其中调用 dlopen 可能存在隐式反向引用。

标签: c++ gcc shared-libraries


【解决方案1】:

感谢 Davis Herring 的提示,我找到了原因。所以有点在内存中持有lib1.so,不允许它卸载。事实证明,lib1.so 调用了一个包含static const 变量的inline 函数,这使得gcc 为这个变量创建了STB_GNU_UNIQUE 绑定。这反过来又有效地使lib1.so 无法加载,即使它是用 RTLD_LOCAL 加载的。所以,要解决这个问题,我可以从变量定义中删除static 限定符,或者从函数定义中删除inline 限定符,或者使用-fno-gnu-unique g++ 标志。 在我这样做之后,问题就消失了:

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

以下是 GNU GCC 帮助的摘录,关于此:

-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 .

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2019-06-30
    • 2018-05-02
    • 2015-07-26
    • 2014-09-24
    相关资源
    最近更新 更多