从另一个共享对象dynamic加载共享对象时的约束?

我从maindynamic加载(whith dlopen() )共享对象(名为libprofile1.so )。

libprofile1.so我定义了工厂函数CreateProfile和class ProfileCreateProfile函数创build一个Profile类的实例并返回一个指向它的指针。 Class Profile有一个方法pMethod

主要是在加载libprofile1.so ,我调用了CreateProfile方法,它返回指向Profile类对象的指针(称为p )。
之后,我调用pMethod方法对象pp->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.solibdatasources.so加载没有任何问题。
我没有看到第二个SO的方法调用之间的连接,加载第一个SO ???

也许我不知道,但dynamic加载共享对象,还有dynamic加载的共享对象时是否有任何约束?

顺便说一下, dlopenRTLD_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二进制文件。 这将失败,只有当注释行被取消注释时才会成功。