与dlopen / dlsym一起使用时,dynamic_cast失败

介绍

对于长期的问题,让我先道歉。 它尽可能短,不幸的是,它不是很短。

build立

我定义了两个接口A和B:

class A // An interface { public: virtual ~A() {} virtual void whatever_A()=0; }; class B // Another interface { public: virtual ~B() {} virtual void whatever_B()=0; }; 

然后,我有一个共享库“testc”构造类C的对象,同时实现A和B,然后将指针传递给它们的A接口:

 class C: public A, public B { public: C(); ~C(); virtual void whatever_A(); virtual void whatever_B(); }; A* create() { return new C(); } 

最后,我有第二个共享库“testd”,它将A*作为input,并尝试将其转换为B* ,使用dynamic_cast

 void process(A* a) { B* b = dynamic_cast<B*>(a); if(b) b->whatever_B(); else printf("Failed!\n"); } 

最后,我有主要的应用程序,在库之间传递A*

 A* a = create(); process(a); 

如果我构build我的主应用程序,与“testc”和“testd”库链接,一切都按预期工作。 但是,如果我修改主应用程序不与“testc”和“testd”链接,而是在运行时使用dlopen / dlsym加载它们,那么dynamic_cast失败。

我不懂为什么。 任何线索?

附加信息

  • 使用gcc 4.4.1,libc6 2.10.1(Ubuntu 9.10)
  • 示例代码可用

Solutions Collecting From Web of "与dlopen / dlsym一起使用时,dynamic_cast失败"

我在这里找到了我的问题的答案。 据我所知,我需要使'testc'中的typeinfo可用于'testd'库。 为了在使用dlopen()时做到这一点,需要做两件事情:

  • 链接库时,传递链接器的-E选项,以确保它将所有符号导出到可执行文件中,而不仅仅是那些没有解析的符号(因为没有)
  • 使用dlopen()加载库时,添加RTLD_GLOBAL选项,以确保testc导出的符号也可用于testd

一般来说,gcc不支持跨dlopen边界的RTTI。 我有这个搞砸try / catch的个人经验,但你的问题看起来更像是相同的。 可悲的是,我恐怕你需要坚持简单的东西,跨越dlopen。

自从我遇到这个问题以后,我必须补充这个问题。

即使在提供-Wl,-E和使用RTLD_GLOBAL ,dynamic_casts仍然失败。 但是,在实际应用程序的链接中,不仅在库中传递-Wl,-E似乎已经修复了它。

如果无法控制主应用程序的来源,-Wl,-E不适用。 在构建自己的二进制文件(主机和插件)时,将-Wl,-E传递给链接器也没有帮助。 在我的情况下,唯一的工作解决方案是加载和卸载主机的_init函数,所以使用RTLD_GLOBAL标志(参见下面的代码)。 这种解决方案在两种情况下都可以工

  1. 主应用程序链接主机如此。
  2. 主应用程序使用dlopen(不带RTLD_GLOBAL)加载主机。

在这两种情况下,都必须遵循gcc可见性wiki所述的指示。

如果使插件和主机的符号相互可见(通过使用#pragma GCC可见性推/弹出或相应的属性),并通过使用RTLD_GLOBAL 1加载插件(从主机)。也可以不加载并卸载自己的(如上面给出的链接所述)。 这个解决方案使得以前还没有这样的工作。

 // get the path to the module itself static std::string get_module_path() { Dl_info info; int res = dladdr( (void*)&get_module_path, &info); assert(res != 0); //failure... std::string module_path(info.dli_fname); assert(!module_path.empty()); // no name? should not happen! return module_path; } void __attribute__ ((constructor)) init_module() { std::string module = get_module_path(); // here the magic happens :) // without this 2. fails dlclose(dlopen(module.c_str(), RTLD_LAZY | RTLD_GLOBAL)); }