2016-10-18 4 views
1

以下のコードは、予想される(また間違いなく直感的な)動作を示しています。 main()が入力される前に実行可能ファイルの静的オブジェクトが初期化されるのと同様に、動的にロードされるライブラリの静的オブジェクトは、dlopen()が返される前に初期化されることが予想されます。dlopen()が返す前に、動的にロードされたライブラリの静的C++オブジェクトが初期化されていますか?

問題点:ランタイムにロードされたライブラリの動作は何らかの形で保証されていますか、それとも便利な事故やラッキーな実装の詳細なのでしょうか?呼び出し中のライブラリ内の静的オブジェクトのコンストラクタを使用することができますか?呼び出しの範囲内でいくつかの望ましい動作を保証するために、__attribute__((constructor))とマークされた関数のような代替手段に頼らなければなりませんか?

// libtest.cpp 
#include <iostream> 

namespace 
{ 
    class Test 
    { 
    public: 
     Test() { std::cerr << "In Test()...\n"; } 
     ~Test() { std::cerr << "In ~Test()...\n"; } 
    }; 

    Test test; // when is this initialized? 
} 

// testso.cpp 
#include <dlfcn.h> 
#include <iostream> 

int main(int ac, char* av[]) 
{ 
    if (ac < 2) 
    { 
     std::cerr << "Usage: " << av[0] << "library-name\n"; 
     return 1; 
    } 
    std::cerr << "Before dlopen()...\n"; 
    ::dlerror(); 
    void* _handle(::dlopen(av[1], RTLD_NOW)); 
    std::cerr << "After dlopen()...\n"; 
    if (!_handle) 
    { 
     std::cerr << "Error: " << ::dlerror() << ", exiting...\n"; 
     return 2; 
    } 
    ::dlclose(_handle); 
    std::cerr << "After dlclose()...\n"; 
    return 0; 
} 

コンパイルと実行(dlopen()戻る前Test()呼び出しに注意してください):

$ g++ -o libtest.so -shared -fPIC libtest.cpp 
$ g++ -o testso -ldl testso.cpp 
$ ./testso ./libtest.so 
Before dlopen()... 
In Test()... 
After dlopen()... 
In ~Test()... 
After dlclose()... 
$ 

を。最初

+0

おそらく、http://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynamの複製ですか? –

+0

最初のヒント: '__attribute __((コンストラクタ))'を使わないでください。それを避けることができます。これは一般的にハックとみなされ、移植性がありません。代わりにエクスポートされた関数を使用してください。その義務的な警告ラベルをそこに投げるだけです。 – Qix

+0

エクスポートされた関数を外部から呼び出す必要があります。コンストラクタールーチンは自動的に実行されますが、外部の操作は必要ありません(ライブラリー自体の負荷を引き出す以外)。後者の動作が必要な場合、問題は静的オブジェクトのコンストラクタがこれを満たすことができるかどうかです。 「コンストラクタ関数」は、コンストラクタのコンセプトをネイティブに持たないCなどの言語用です。 – arayq2

答えて

2

まず物事:dlopen()は信じられないほどプラットフォーム固有の、あなたは常にあなたのプラットフォームの関連manページに相談してください初期の病気の症状の場合とWebMDのと同様にです。

これは私が今日のシステムは、(例えば、Windowsがshoddy POSIX complianceの長年の歴史を持っている)、このような規格にどのように対応を教えてくれませんでしたけれども機能のdlopen()家族は、IEEE Standard 1003.1, 2004 Editionに適合するように見えるにもかかわらず当てはまります。 OS/X/BSDで


はい

dlopen()pathで指定されたマッハ-Oファイルを調べます。ファイルが現在のプロセスと互換性があり、現在のプロセスにまだロードされていない場合は、ロードされてリンクされます。 リンクされた後に、イニシャライザ関数が含まれている場合は、dlopen() が返される前に呼び出されます。

強調鉱山。 Linuxでは、


はい

共有オブジェクトは、 __attribute__((constructor))__attribute__((destructor))機能 属性を使用して関数をエクスポートします。 コンストラクタ関数はdlopen() が返される前に実行され、デストラクタ関数はdlclose() が返される前に実行されます。

強調鉱山。Solarisの


はい

新しいオブジェクトをロードの一部として、オブジェクト内の初期化コードは、dlopen()戻る前に呼び出されます。この初期化はユーザコードであり、したがって、dlopen()によって捕らえられないエラーを生成する可能性があります。

強調鉱山。

+0

プロセスにすでに複数のスレッドがある場合、dlopenが返す前に他のスレッドがdlを使用し始める可能性はありますか?確かに、初期化子は他のスレッドを関与させるために何かを行うことができますが、明示的に何もしないと仮定します。 – joeking

+0

@joekingそれは良い並行性の実践です:)私は、システムコールの並行性に関して、特に壊れやすい 'dlopen()'ファミリ(一言ではあるが一般的には良いアドバイス)を想定しません。 – Qix

+0

えええええええええええええええええええええええええええええええええええええええええええええええええええええええええええええレビュー者Windowsでは、 "DllMain()"はイニシャライザを呼び出すもので、OSがローダーロックを保持している点で多少シングルスレッドです(ただし、その効果ははっきりとは証明されていません)。結果は、どのようなOS関数がこれらの初期化子から呼び出されるのかを慎重にしなければならないということです。 – joeking

関連する問題