下面的代码演示了预期的(可以说是直观的)行为。就像在输入main()
之前初始化可执行文件中的静态对象一样,人们会期望在dlopen()
返回之前初始化动态加载的库中的静态对象。
问题:运行时加载的库的这种行为是以任何方式得到保证,还是只是一个方便的事故或幸运的实现细节?我们是否可以依赖于被调用的库中的静态对象的构造函数,或者我们是否必须使用标记为__attribute__((constructor))
的函数等替代方法,以确保dlopen()
调用范围内的某些所需行为?
// libtest.cpp
#include <iostream>
namespace
{
class Test
{
public:
Test() { std::cerr << "In Test()...\n"; }
~Test() { std::cerr << "In ~Test()...\n"; }
};
Test test; // when is this initialized?
}
// testso.cpp
#include <dlfcn.h>
#include <iostream>
int main( int ac, char* av[] )
{
if ( ac < 2 )
{
std::cerr << "Usage: " << av[0] << "library-name\n";
return 1;
}
std::cerr << "Before dlopen()...\n";
::dlerror();
void* _handle(::dlopen( av[1], RTLD_NOW ));
std::cerr << "After dlopen()...\n";
if ( !_handle )
{
std::cerr << "Error: " << ::dlerror() << ", exiting...\n";
return 2;
}
::dlclose( _handle );
std::cerr << "After dlclose()...\n";
return 0;
}
在Test()
返回之前编译并运行(注意dlopen()
调用):
$ g++ -o libtest.so -shared -fPIC libtest.cpp
$ g++ -o testso -ldl testso.cpp
$ ./testso ./libtest.so
Before dlopen()...
In Test()...
After dlopen()...
In ~Test()...
After dlclose()...
$
。
答案 0 :(得分:4)
首先要做的事情:dlopen()
令人难以置信的平台特定,就像早期疾病症状和WebMD一样,您应该始终查阅平台的相关man
页面。
即使dlopen()
系列函数似乎符合IEEE Standard 1003.1, 2004 Edition,这也是正确的,尽管我无法告诉您今天的系统是如何符合此类标准的(例如Windows具有长期目标)历史shoddy POSIX compliance)。
在OS / X / BSD上,是:
dlopen()
检查path
指定的mach-o文件。如果文件与当前进程兼容并且尚未加载到 当前进程,它被加载和链接。 链接后,如果它包含任何初始化函数,则在dlopen()
之前调用它们 回报。强>
强调我的。
在Linux上,是:
共享对象可以使用导出函数
__attribute__((constructor))
和__attribute__((destructor))
功能 属性。 构造函数在dlopen()
之前执行 返回,析构函数在dlclose()
之前执行 回报。强>
强调我的。
在Solaris上,是:
作为加载新对象的一部分,在
dlopen()
返回之前调用对象内的初始化代码。此初始化是用户代码,因此可能产生无法解决的错误被dlopen()
抓住。
强调我的。