没有RTLD_GLOBAL的typeinfo,共享库和dlopen()

时间:2011-02-18 18:15:21

标签: c++ exception matlab shared-libraries dlopen

在使用 dlopen 加载时,我遇到的问题是异常无法正常运行(或者至少,我希望这些问题;我知道这有问题)。我在这里包含一些简化的示例代码。实际情况是 myapp = Matlab, myext1 = mexglx matlab扩展, mylib 是我的代码在两个扩展之间的共享库( myext1 myext2

mylib.h

struct Foo { Foo(int a); m_a; }
void throwFoo();

mylib.cpp

#include "mylib.h"
Foo::Foo(int a): m_a(a) {}
void throwFoo() { throw Foo(123); }

myext1.cpp

#include "mylib.h" 
#include <iostream>
extern "C" void entrypoint()    
{ 
   try { throwFoo(); } 
   catch (Foo &e) { std::cout << "Caught foo\n"; }
}

myext2.cpp 与myext1.cpp相同

MyApp.cpp中

#include <dlfcn.h>
int main()
{
  void *fh1 = dlopen("./myext1.so",RTLD_LAZY);
  void *fh2 = dlopen("./myext2.so",RTLD_LAZY);
  void *f1  = dlsym(fh1,"entrypoint");
  void *f2  = dlsym(fh2,"entrypoint");
  ((void (*)())func1)();  // call myext1 (A)
  ((void (*)())func2)();  // call myext2 (B)
}

编译此代码:

g++ mylib.cpp -fPIC  -o libmylib.so -shared
g++ myext1.cpp -fPIC -o myext1.so -shared -L. -lmylib -Wl,-rpath=.
g++ myext2.cpp -fPIC -o myext2.so -shared -L. -lmylib -Wl,-rpath=. 
g++ myapp.cpp -fPIC -o myapp -ldl

A 入口点()的调用按预期工作, throwFoo()抛出异常并且入口点() 抓住它。然而, B 的调用无法捕获异常。添加更多诊断代码表明 Foo 类的typeinfo在两个扩展中有所不同。更改两个 dlopen 调用的顺序没有区别,第二个加载的扩展失败。

我知道我可以通过使用 RTLD_GLOBAL 作为 dlopen 的附加标志来解决此问题,但使用dlopen的应用程序(Matlab)不受我的控制。我可以使用 mylib myext1 myext2 来解决此问题吗?

我必须避免在运行时使用LD标志(因为我无法控制运行Matlab二进制文件的用户)。还有其他建议吗?

3 个答案:

答案 0 :(得分:3)

Alexandrescu&amp ;;的“C ++编码标准”中的规则62萨特:

“62。不允许异常跨模块边界传播。”

虽然它可以在您仔细操作时使用,但对于真正的便携式和可重用代码,这是不可能的。我会说在编写共享库或DLL时,这是一个非常常见的一般规则,不会跨模块边界传播异常。只需使用C风格的界面,返回错误代码,并在try { } catch(...) { };块内的导出函数内执行所有操作。此外,RTTI不是跨模块共享的,所以不要指望Foo在不同的模块中具有相同的typeinfo。

答案 1 :(得分:3)

简单的解决方法是让您的库dlopen本身在首次使用时带有RTLD_GLOBAL标志。这将覆盖先前使用RTLD_LOCAL打开并将所有内容放在全局符号命名空间中。

答案 2 :(得分:3)

  

我可以用mylib或myext1,myext2做些什么来解决这个问题   问题

作为使用RTLD_GLOBAL的替代方法,您只需在运行应用程序时使用环境变量LD_PRELOAD即可解决问题。您无需重新编译任何内容:

LD_PRELOAD=libmylib.so ./myapp