10

dlopenを使用してロードしたときに、共有ライブラリ全体で例外が正しく機能しないという問題があります (または、少なくとも、期待どおりです。これには問題があることはわかっています) 。ここにいくつかの単純化されたサンプル コードを含めます。実際の状況はmyapp =Matlab, myext1 =mexglx matlab 拡張機能です。mylibは 2 つの拡張機能 ( myext1myext2 )間の私のコードの共有ライブラリです。

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での entrypoint( )の呼び出しは期待どおりに機能し、throwFoo()が例外をスローし、 entrypoint( )が例外をキャッチします。ただし、 Bでの呼び出しは例外をキャッチできません。さらに診断コードを追加すると、 Fooクラスの typeinfo が2 つの拡張機能で異なることがわかります。2 つのdlopen呼び出しの順序を変更しても違いはなく、2 番目にロードされた拡張機能は失敗します。

RTLD_GLOBALをdlopenの追加フラグとして使用することでこれを修正できることはわかっていますが、 dlopen を使用するアプリケーション (Matlab) は制御できません。この問題を解決するためにmylibまたはmyext1myext2でできることはありますか?

実行時に LD フラグを使用しないようにする必要があります (Matlab バイナリを実行しているユーザーを制御できないため)。他の提案はありますか?

4

3 に答える 3

3

この問題を解決するためにmylibまたはmyext1、myext2でできることはありますか?

LD_PRELOADRTLD_GLOBALを使用する代わりに、アプリケーションを実行するときにenviroment変数を使用して、問題を修正することができます。何も再コンパイルする必要はありません。

LD_PRELOAD=libmylib.so ./myapp
于 2012-02-27T10:10:54.567 に答える
3

Alexandrescu & Sutter による "C++ Coding Standards" のルール 62:

「62. 例外がモジュールの境界を越えて伝播することを許可しないでください。」

慎重に実行すれば機能しますが、真に移植可能で再利用可能なコードの場合、これは実行できません。共有ライブラリまたは DLL をプログラミングするときは、モジュールの境界を越えて例外を伝播しないというのが、かなり一般的な一般規則だと思います。C スタイルのインターフェイスを使用し、エラー コードを返し、try { } catch(...) { };ブロック内のエクスポートされた関数内ですべてを実行します。また、RTTI はモジュール間で共有されないため、Foo が異なるモジュールで同じ typeinfo を持つとは思わないでください。

于 2011-02-18T18:32:46.293 に答える
3

簡単な回避策は、最初の使用時にライブラリdlopen自体にRTLD_GLOBALフラグを設定することです。これにより、以前の open with が上書きさRTLD_LOCALれ、すべてがグローバル シンボル名前空間に配置されます。

于 2011-06-27T02:56:08.663 に答える