我正在从dlopen()
动态加载(libprofile1.so
}个共享对象(名为main
)。
在libprofile1.so
中,我定义了工厂函数CreateProfile
和类Profile
。 CreateProfile
函数创建Profile
类的实例并返回指向它的指针。类Profile
有一个方法pMethod
。
在main中,在加载libprofile1.so
之后,我正在调用CreateProfile
方法,该方法返回指向Profile
类对象的指针(称之为p
)。 />
之后,我正在针对对象pMethod
(p
)调用p->pMethod
方法。在这个方法中,我正在动态加载其他共享对象(libdatasources.so
)。
在这个共享对象中,我有一个工厂函数CreateDataSource
和类DataSource
CreateDataSource
函数创建DataSource
类的实例并返回指向它的指针。 DataSource
类有方法dsMethod
。
您可以注意到,两个共享对象的结构都相似。
加载pMethod
后从libdatasources.so
我正在调用CreateDataSource
方法,该方法返回指向DataSource
类实例的指针,称之为ds
。
然后我正在呼叫dsMethod
对象的ds
(ds->dsMethod
)。
现在,问题出现了。
当我尝试调用dsMethod
ds
对象时,我首次加载的共享对象(libprofile1.so
)无法加载。实际上dlopen()
会返回NULL
。当我在dlerror
之后阅读dlopen
时,我得到了:
./libprofile1.so: undefined symbol: _ZN18DataSource13dsMethod
因此,如果我有一个电话ds->Method
,则不会加载第一个共享对象!
如果我从来源发表评论ds->dsMethod
,那么我的libprofile1.so
和libdatasources.so
会毫无问题地加载。
我没有看到第二个SO的方法调用与首先加载SO ???
也许我不知道,但是从动态加载的共享对象动态加载共享对象时是否有任何约束?
顺便说一下,dlopen
与RTLD_NOW|RTLD_GLOBAL
一起使用。我尝试使用RTLD_LAZY
,但问题仍然存在。
更新
库是在Eclipse中构建的。两个库的G ++编译器和链接器选项相同 这是G ++编译器:
-O0 -g3 -Wall -c -fmessage-length=0
和G ++链接器:
-shared
选项,粘贴自Project Properties -> Settings -> Tool Settings
提前致谢。
答案 0 :(得分:4)
正如Martin York指出的那样,这就是它在Linux中的运作方式。链接库时,您还必须链接到所有依赖项。这在Windows中是不同的,DLL会自己处理它们的依赖关系。当动态加载具有另一个库作为依赖关系的库时,您需要首先使用RTLD_GLOBAL标志加载该库。这非常麻烦,因为您可能无法知道另一个共享对象需要哪些依赖项,或者依赖项可能会因为二进制兼容的较新版本而发生变化。根据我所知(以及阅读g ++和ld联机帮助页),不可能创建类似于Windows的DLL的行为。 这是一个小测试用例:
two.cpp:
#include <iostream>
extern "C"
{
void bar()
{
std::cout << "bar()\n";
}
}
one.cpp:
#include <iostream>
extern "C"
{
void bar();
void foo()
{
std::cout << "foo()\n";
bar ();
}
}
TEST.CPP:
#include <dlfcn.h>
#include <iostream>
int main (int argc, char *argv[])
{
using namespace std;
// void *libtwo = dlopen("./libtwo.so", RTLD_NOW | RTLD_GLOBAL);
void *libone = dlopen("./libone.so", RTLD_NOW);
if (!libone)
{
cout << "dlopen(libone.so) failed: " << dlerror() << "\n";
return 1;
}
typedef void (*foo_t)();
foo_t foo = reinterpret_cast<foo_t>(dlsym(libone, "foo"));
if (!foo)
{
cout << "dlsym(libone.so, foo) failed\n";
return 2;
}
}
one.cpp被编译为libone.so
和libtwo.so
中的two.cpp,test.cpp被编译为test
二进制文件。
这将失败,只有在注释行被取消注释时才会成功。
答案 1 :(得分:3)
如果动态加载DLL,则必须确保它没有未解析的符号。
最简单的方法是将它与其所需的其他DLL链接,以便自动加载,而不必手动加载和解析所有依赖项。
因此,如果libprofile1
始终使用libdatasources
,请确保它们相互关联。
如果必须手动执行,请反转加载库的顺序。因此,当您加载libprofile1
时,它所需的功能已经加载。