我从main
dynamic加载(whith dlopen()
)共享对象(名为libprofile1.so
)。
在libprofile1.so
我定义了工厂函数CreateProfile
和class Profile
。 CreateProfile
函数创build一个Profile
类的实例并返回一个指向它的指针。 Class Profile
有一个方法pMethod
。
主要是在加载libprofile1.so
,我调用了CreateProfile
方法,它返回指向Profile
类对象的指针(称为p
)。
之后,我调用pMethod
方法对象p
( p->pMethod
)。 在这个方法中,我dynamic加载其他共享对象( libdatasources.so
)。
在这个共享对象中,我有一个工厂函数CreateDataSource
和类DataSource
。
CreateDataSource
函数创build一个DataSource
类的实例并返回一个指向它的指针。 DataSource
类有方法dsMethod
。
你可以注意到,这两个共享对象的结构是相似的。
从pMethod
加载pMethod
之后,我调用CreateDataSource
方法,它返回一个指向DataSource
类的实例的指针,称之为ds
。 然后我打电话dsMethod
ds
对象的dsMethod
( ds->dsMethod
)。
现在,问题在于。
当我尝试调用ds
对象的dsMethod
时,我第一次加载的共享对象( libprofile1.so
)不会加载。 其实dlopen()
返回NULL
。 当我在dlopen
之后读取dlerror
,我得到:
./libprofile1.so: undefined symbol: _ZN18DataSource13dsMethod
所以,如果我有一个调用ds->Method
,比第一个共享对象不加载!
如果我从源代码注释掉ds->dsMethod
,那么我的libprofile1.so
和libdatasources.so
加载没有任何问题。
我没有看到第二个SO的方法调用之间的连接,加载第一个SO ???
也许我不知道,但dynamic加载共享对象,还有dynamic加载的共享对象时是否有任何约束?
顺便说一下, dlopen
与RTLD_NOW|RTLD_GLOBAL
。 我尝试了RTLD_LAZY
,但仍然是同样的问题。
更新:
Eclipse中构build了库。 G ++编译器和链接器的选项对于这两个库都是相同的。
这里是G ++编译器:
-O0 -g3 -Wall -c -fmessage-length=0
和G ++连接器:
-shared
选项,从Project Properties -> Settings -> Tool Settings
粘贴
提前致谢。
如果动态加载DLL,则必须确保它没有未解析的符号。
最简单的方法是将其链接到所需的其他DLL,以便自动加载,而不必手动加载和解析所有依赖关系。
所以如果libprofile1
总是使用libdatasources
确保它们彼此链接。
如果您必须手动执行,则反转加载库的顺序。 所以,当你加载libprofile1
它所需要的功能已经被加载。
正如马丁·约克指出的,这是它在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
和two.cpp,test.cpp被编译成test
二进制文件。 这将失败,只有当注释行被取消注释时才会成功。