共享库上的Dynamic_cast问题

Dynamic_cast troubles over shared libraries

本文关键字:cast 问题 Dynamic 共享      更新时间:2023-10-16

我在共享库创建的对象上遇到了一些dynamic_cast问题:

架构类似于:

class A;
class B : virtual public A; // one of the several interfaces 
class C : public B; // defined only in the shared library
  1. 主应用程序加载共享库并请求新的对象
  2. 共享库创建一个新的C对象,但它返回dynamic_cast<A*>(pointerToCclass),因为主要应用程序不知道C
  3. 当主应用程序尝试向下转换到B时根据返回的CCD_ 6指针,它失败了

我怀疑在主代码和共享代码中创建的vtables之间的一些差异可能是原因。不管怎样,最初我并没有意识到这个问题,因为主要的应用程序调用方法void * A::getInterface( int ifEnum ),因此共享库代码成功执行了下变频,并作为空指针返回。然后主应用程序执行reinterpret_cast以将空指针绑定到所需接口。

到目前为止,当多继承模式(当C实现多个接口时(似乎不稳定并导致Segmentation错误时,一切都有效。

我的怀疑是真的吗?有更好的方法或众所周知的方法来实现类似的体系结构吗?

谢谢


我附上了一些我的实际应用程序的语义化代码和基本的参与者。首先是常见的定义:

class A
{
public:
  typedef A* (*getAobject_fn)(void);
  static A * Load( char * filename ) {
     void * objhlib = dlopen( filename, RTLD_NOW );
     getAobject_fn fp = (getAobject_fn) dlsym( objhlib, "getAobject" );     
     return fp();
  }
  virtual A * Create() = 0;
  virtual void * getInterface( int ifEnum ) = 0;
};
class B1 : virtual public A {
public:
  // some inline or pure virtual functions here
};
class B2 : virtual public A {
public:
  // some other inline or pure virtual functions here
};

共享库的头(实际上C类对主应用程序不可见,因为.so是在运行时加载的,.h不包括在主应用程序中(:

class C : public B1, public B2
{
public:
  A * Create() { return new C; }
  void * getInterface( int ifEnum ) {
     if( ifEnum==INTERFACE_ID_B1 )
        return dynamic_cast<B1*>(this);
     if( ifEnum==INTERFACE_ID_B2 )
        return dynamic_cast<B2*>(this);
    return 0;
  }
};
extern "C" { A * getAobject(); }  // probably useless

在共享库的主体中:

C obj;
A * getAobject() { return dynamic_cast<A*>(&obj); } // equivalent to return &obj;

最后,在主要应用程序中:

// In the Init procedure
A * p = A::Load( "foo.so" );
A * pBobj = p->Create();   // pBobj is kept for the entire lifetime
B1 * b1 = reinterpret_cast<B1*>(pBobj->getInterface( INTERFACE_ID_B1 ));
B2 * b2 = reinterpret_cast<B2*>(pBobj->getInterface( INTERFACE_ID_B2 ));
// NOTE:
// b1 = dynamic_cast<B1*>(pBobj) and
// b2 = dynamic_cast<B2*>(pBobj)
// will fail

如何加载共享对象?g++使用RTTI信息来解析CCD_ 10。传统上在Unix中默认情况下,要加载的第一个符号将由所有的共享对象,所以不会有任何问题。这取决于dlopen中使用的模式;如果RTLD_LOCAL指定时,该共享对象中的符号(以及共享对象中由于加载而隐式加载的对象共享对象(在共享对象之外将不可见。(我在Java插件方面遇到过这个问题与RTLD_LOCALdynamic_cast共享的对象将不起作用交叉共享对象隐式加载的共享对象Java加载。(

关于主要可执行文件:大多数Unix链接器将可用的符号,就好像已加载可执行文件一样使用CCD_ 15。大多数,但不是全部;GNU链接器,用于例如,这样做吗,以及主可执行文件中的符号不可用于共享对象。如果你需要他们可用,在生成时必须使用-rdynamic选项可执行文件(转换为-export-dynamic选项链接器(。或者,如果你需要打破应用程序分解为单独的共享对象,您可能考虑将几乎所有的东西都放在共享对象中,以及使得主可执行程序只不过是一个简单的库加载器,在所有共享对象上调用dlopen,然后调用dlsym要获取要执行的实际函数的地址,并通过地址呼叫它。(我们基本上就是这样解决了Java插件的问题。所有加载的Java我们的加载器模块,然后执行dlopen。通过做它们明确地,我们可以控制dlopen的选项。(

编辑:

重读你的问题,我不确定这是对的答复你正在通过void*,这意味着你无法访问RTTI(甚至无法访问vtables(。规则关于void*(C++规则,这次是而不是g++(是清楚的:使用void*唯一能做的就是将其转换回原始指针类型。特别是Derived*&rar;void*&rar;Base*是未定义的行为。如果只涉及单个继承,它通常会工作(但即便如此,它仍然是未定义的行为(,但不是否则因此,如果共享对象将A*转换为void*,并且void*稍后转换为B*有未定义的行为,不应该期望它工作。先将void*转换为A*,然后再进行转换到B*应该工作。然而,更好的是:宣布函数返回A*,而不是一个CCD_ 36。一般来说,你应该尽量避免void*可能,尤其是在C++中。