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

时间:2010-08-30 14:52:13

标签: c++ linux shared-libraries

我正在从dlopen()动态加载(libprofile1.so}个共享对象(名为main)。

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

在main中,在加载libprofile1.so之后,我正在调用CreateProfile方法,该方法返回指向Profile类对象的指针(称之为p)。 /> 之后,我正在针对对象pMethodp)调用p->pMethod方法。在这个方法中,我正在动态加载其他共享对象(libdatasources.so)。

在这个共享对象中,我有一个工厂函数CreateDataSource和类DataSource CreateDataSource函数创建DataSource类的实例并返回指向它的指针。 DataSource类有方法dsMethod

您可以注意到,两个共享对象的结构都相似。

加载pMethod后从libdatasources.so我正在调用CreateDataSource方法,该方法返回指向DataSource类实例的指针,称之为ds 。 然后我正在呼叫dsMethod对象的dsds->dsMethod)。


现在,问题出现了。

当我尝试调用dsMethod ds对象时,我首次加载的共享对象(libprofile1.so)无法加载。实际上dlopen()会返回NULL。当我在dlerror之后阅读dlopen时,我得到了:

./libprofile1.so: undefined symbol: _ZN18DataSource13dsMethod

因此,如果我有一个电话ds->Method,则不会加载第一个共享对象! 如果我从来源发表评论ds->dsMethod,那么我的libprofile1.solibdatasources.so会毫无问题地加载。
我没有看到第二个SO的方法调用与首先加载SO ???

之间的联系

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

顺便说一下,dlopenRTLD_NOW|RTLD_GLOBAL一起使用。我尝试使用RTLD_LAZY,但问题仍然存在。

更新

库是在Eclipse中构建的。两个库的G ++编译器和链接器选项相同 这是G ++编译器:

-O0 -g3 -Wall -c -fmessage-length=0

和G ++链接器:

-shared

选项,粘贴自Project Properties -> Settings -> Tool Settings

提前致谢。

2 个答案:

答案 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.solibtwo.so中的two.cpp,test.cpp被编译为test二进制文件。 这将失败,只有在注释行被取消注释时才会成功。

答案 1 :(得分:3)

如果动态加载DLL,则必须确保它没有未解析的符号。

最简单的方法是将它与其所需的其他DLL链接,以便自动加载,而不必手动加载和解析所有依赖项。

因此,如果libprofile1始终使用libdatasources,请确保它们相互关联。

如果必须手动执行,请反转加载库的顺序。因此,当您加载libprofile1时,它所需的功能已经加载。