【发布时间】: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