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

Bri*_*edy 10 c++ matlab exception shared-libraries dlopen

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

mylib.h

struct Foo { Foo(int a); m_a; }
void throwFoo();
Run Code Online (Sandbox Code Playgroud)

mylib.cpp

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

myext1.cpp

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

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)
}
Run Code Online (Sandbox Code Playgroud)

编译此代码:

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
Run Code Online (Sandbox Code Playgroud)

要调用入口点()一个正常工作,与throwFoo()抛出异常和入口点()捕获它.然而,B的呼叫未能发现异常.添加更多诊断代码表明Foo类的typeinfo 在两个扩展中有所不同.更改两个dlopen调用的顺序没有区别,第二个加载的扩展失败.

我知道我可以通过使用RTLD_GLOBAL作为dlopen的附加标志来解决这个问题,但使用dlopen的应用程序(Matlab)是我无法控制的.有什么我可以用mylibmyext1,myext2来解决这个问题吗?

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

R..*_*R.. 5

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


Mik*_*son 4

Alexandrescu & Sutter 的《C++ 编码标准》中的规则 62:

\n\n

“62.Don\xe2\x80\x99t 不允许异常跨模块边界传播。”

\n\n

虽然当你小心翼翼地做时它可以工作,但对于真正的可移植和可重用的代码来说,这是不可能的。我想说,在对共享库或 DLL 进行编程时,这是一个非常常见的一般规则,即不要跨模块边界传播异常。只需使用 C 风格的接口,返回错误代码,并在块内的导出函数内执行所有操作try { } catch(...) { };。另外,RTTI 不在模块之间共享,因此不要期望 Foo 在不同模块中具有相同的类型信息。

\n

  • 根据规则 62 的摘要:“不允许异常在两段代码之间传播**除非**您控制用于构建双方的编译器和编译器选项”。我上面传播的例外是在 _mylib_ 和 _myext1_ 或 _myext1_ 之间,所有这些我都可以完全控制。我非常感谢将它们泄漏到 _myapp_ 中将是一件“坏”事情,但我希望有一种方法可以在由许多共享库组成的代码库中使用常见异常。 (5认同)