10

使用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 相同

我的应用程序.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处对entrypoint()的调用按预期工作,throwFoo()抛出异常,entrypoint()捕获它。然而,在B处的调用未能捕捉到异常。添加更多诊断代码表明Foo类的 typeinfo 在两个扩展中不同。更改两个dlopen调用的顺序没有区别,第二个加载的扩展失败。

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

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

4

3 回答 3

3

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

于 2011-06-27T02:56:08.663 回答
3

Alexandrescu & Sutter 的“C++ 编码标准”中的规则 62:

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

尽管仔细操作时它可以工作,但对于真正可移植和可重用的代码,这是无法做到的。我想说这是在编程共享库或 DLL 时非常普遍的一般规则,不要跨模块边界传播异常。只需使用 C 风格的界面,返回错误代码,然后在try { } catch(...) { };块内的导出函数内执行所有操作。此外,RTTI 不是跨模块共享的,所以不要期望 Foo 在不同的模块中具有相同的类型信息。

于 2011-02-18T18:32:46.293 回答
3

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

作为使用 RTLD_GLOBAL 的替代方法,您可以LD_PRELOAD在运行应用程序时简单地使用环境变量来解决您的问题。你不必重新编译任何东西:

LD_PRELOAD=libmylib.so ./myapp
于 2012-02-27T10:10:54.567 回答