2009-07-01 66 views
15

共有ライブラリを使用するアプリケーションがあります。これらのライブラリはコンパイル時にリンクされています。
実行時に、ローダは共有オブジェクトがLD_LIBRARY_PATHにあることを期待していますが、アプリケーション全体がクラッシュして「共有ライブラリをロードできません」というエラーでクラッシュすることを期待しています。クライアントがライブラリを持っているという保証はありません私はアプリケーションが適切なエラーメッセージを残すようにしたい場合も、独立した部分が正しく動作するはずです。C++でのdlsym()とdlopen()の代替方法

この目的のために、私はdlsym()dlopen()を使用して共有ライブラリにAPIを使用しています。この問題は、APIに多くの機能がある場合、私はdlsym()とptrsを使用して個別にアクセスする必要があり、私のケースではメモリ破損とコードクラッシュが発生していることです。

これに代わる方法はありますか?

答えて

29

問題の一般的な解決策は、関数ポインタのテーブルを宣言し、それを見つけるために単一のdlsym()を実行し、そのテーブルへのポインタを通して他のすべての関数を呼び出すことです。例(未テスト):

// libfoo.h 
struct APIs { 
    void (*api1)(void); 
    void *(*api2)(int); 
    long (*api3)(int, void *); 
}; 

// libfoo.cc 
void fn1(void) { ... } 
void *fn2(int) { ... } 
long fn3(int, void *) { ... } 

APIs api_table = { fn1, fn2, fn3 }; 


// client.cc 
#include "libfoo.h" 
... 
    void *foo_handle = dlopen("libfoo.so", RTLD_LAZY); 
    if (!foo_handle) { 
    return false;   // library not present 
    } 
    APIs *table = dlsym(foo_handle, "api_table"); 
    table->api1();    // calls fn1 
    void *p = table->api2(42); // calls fn2 
    long x = table->api3(1, p); // calls fn3 

P.S. dlsymとポインタを使用してAPI関数に個別にアクセスすると、自体がメモリの破損やクラッシュにつながりません。ほとんどの場合、バグがあります。

編集:
第三者のライブラリでもまったく同じ手法を使用できます。 libdrmaa_wrapper.soを作成し、api_tableを入れます。ラッパーをlibdrmaa.soと直接リンクします。

メイン実行可能ファイルでは、dlopen("libdrmaa_wrapper.so", RTLD_NOW)です。このdlopenは、実行時にlibdrmaa.soが存在する場合にのみ成功し、api_tableで使用したすべてのAPI関数を提供します。成功した場合は、1回のdlsym呼び出しでAPI全体にアクセスできます。実行時にこのライブラリをロードするためのコード

Class DynLib 
{ 
    /* All your functions */ 
    void fun1() {}; 
    void fun2() {}; 
    . 
    . 
    . 
} 

DynLib* getDynLibPointer() 
{ 
    DynLib* x = new Dynlib; 
    return x; 
} 

使用dlopen()の種類以下

+0

とdooopen()を呼び出してfoo_handleを取得するにはどうすればよいですか?私はそれが自動的にlibAPI.soと言う共有オブジェクトをロードするのだろうか? – sud03r

+0

実際にはライブラリを開くためにdlopen()が使用されています... .. nyways私はur pointを取得しましたが、ライブラリにAPI構造体がある場合にのみ機能します。サードパーティでは期待できませんライブラリ..私はsungrid api libdrmaa.soを使用しようとしています.. – sud03r

+0

テーブルへのポインタを取得する代わりに、呼び出されたときにテーブルへのポインタを返す関数へのポインタを取得できます。これにより、他の関数が呼び出される前にプラグインを初期化したり、動的にテーブルを作成したりすることができます。 – CesarB

2

アプリケーションをラップして、必要なすべてのライブラリを最初にチェックすることができます。見つからない場合はエラーはうまくいきますが、すべてが正しければ、実際のアプリケーションを実行します。

+0

アプリケーションは、LD_LIBRARY_PATHを設定するスクリプトを持つことが非常に一般的ですアプリケーションを起動する前に –

+0

問題はそうではありません..問題は、ライブラリがクライアントに存在しない場合、モジュールは動作しません。残りのコードは正しく動作するはずですが、この場合、ライブラリが見つからない場合、コードは失敗します。実行する。 – sud03r

-1

あなたの問題は、未解決のシンボルの解決がLinux上で非常に早いことです。私はデータシンボルがプロセスの起動時に解決され、機能シンボルが遅れて実行されると思います。したがって、未解決のシンボルや、どのような静的初期化を行っているかによっては、コードに乗る機会が得られないことがあります。

私は、「共有ライブラリをロードできません」というリターンコード/エラー文字列をトラップするラッパーアプリケーションを作成し、これをより意味のあるものに変換することをお勧めします。これが汎用の場合は、新しい共有ライブラリを追加するたびに更新する必要はありません。

また、ラッパースクリプトでlddを実行し、出力を解析すると、特定のアプリケーションで見つからないすべてのライブラリが報告されます。ldd

0

使用。 を使用し、dlsym()を使用し、DynLibオブジェクトを返すgetDynLibPointer()を呼び出します。 このオブジェクトからすべての関数jstにアクセスできますobj.fun1() .....

これは先に提案されたC++スタイルの構造メソッドです。

0

おそらく、Linuxでの遅延ライブラリの読み込みのいくつかの形態があります。すぐに使用できるわけではありませんが、dlopenの機能の最初の呼び出し時に必要なライブラリ(診断メッセージを出力し、dlopenが失敗した場合に終了する)を試みる小さなスタティックスタブライブラリを作成して簡単に模倣することができますすべての呼び出しを転送します。

このようなスタブライブラリはプロジェクト/ライブラリー固有のスクリプトによって生成された、またはユニバーサルツールImplib.soによって生成され、手で書くことができます。

$ gen-implib.py libxyz.so 
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...