web-dev-qa-db-ja.com

共有ライブラリをロードするときに自動的に実行される関数

Windowsで共有ライブラリをロードする場合、LoadLibrary()呼び出しにより、ライブラリ内のDllMainが、新しいプロセスとスレッドライブラリのアタッチごと、およびプロセスとスレッドライブラリのデアタッチごとに実行されます。

Mac OS X、Linux、およびおそらく他のPOSIX互換OSにも同様のメカニズムがありますか?

40
toriningen

_.init_メカニズムを使用して、Linuxライブラリのオンロード関数を定義できます。これは、バイナリのロード時エントリポイントを指定することと同じです(たとえば、プログラムのエントリポイントとしてmain以外のものを使用する)。

ldを直接使用してリンクする場合、次を使用します。

_-init <function name>
_

または、cc/gccを使用してリンクする場合は、次を使用します。

_-Wl,-init,<function name>
_

これは最も単純なレベルです。

Editデストラクタ/ファイナライザでは、_.fini_メカニズムを使用します。これはinitオプションと同じ方法で動作し、次を使用します。

_-fini <function name>
_

ldを呼び出すとき。可用性は、Mac OSXプラットフォームの_-init_オプションに制限されています。

また、gccに__attribute__((constructor))構文を使用できる必要があります。

_static void con() __attribute__((constructor));

void con() {
    printf("I'm a constructor\n");
}
_

これはおそらく、リンカーオプションを使用するよりも、より移植性の高い方法です。すべてのコンストラクターはロード時に呼び出される必要がありますが、do n'tは初期化の順序に依存しないため、デバッグに時間と労力を要する狂気と再現性のないバグにつながります。

Edit 2__attribute__((constructor))/__attribute__((destructor))セマンティックの使用は、C/C++プログラミング言語にとって最も望ましいメカニズムです。

Dプログラミング言語の場合は、静的モジュールのコンストラクター/デストラクターを実際に使用する必要があります。

_static this() {
    printf("static this for mymodule\n");
}
static ~this() {
    printf("static ~this for mymodule\n");
}
_

または、静的クラスコンストラクター:

_class Foo {
    static this() {
        printf("static this for Foo\n");
    }
}
_

これは、 win32 DLLSの作成 および言語仕様 静的コンストラクタ/デストラクタに関連 で強く示唆されています。

Edit 3静的イニシャライザの使用を許可するコンストラクタ/デストラクタルーチンをエクスポートする_.o_にリンクする必要があります。 Runtime.initialize()を呼び出すだけなので、Dコード内のすべての静的コンストラクター/デストラクターを実際に呼び出します。

初期化子のスタブdコード(_myshared.d_というファイル内):

_import core.runtime;

extern (C) {
    void attach();
    void detach();
}

export void attach() {
    Runtime.initialize();
}

export void detach() {
    Runtime.terminate();
}
_

このスタブの.oを作成します。

_ dmd -m32 -c myshared.d
_

アタッチ/デタッチ関数の名前を確認します。

_nm myshared.o
_

ショー(他の出力の中で):

_0000001c S _D8myshared6attachFZv
00000034 S _D8myshared6detachFZv
_

これを呼び出すためのサンプル.cコード(この場合はexport.cと呼ばれます)では、_my shared.o_ファイルからエクスポートされたルーチンの名前を参照します。

_extern void D8myshared6attachFZv(void);
extern void D8myshared6detachFZv(void);

void __attach(void) __attribute__((constructor));
void __detach(void) __attribute__((destructor));

void __attach(void)
{
    D8myshared6attachFZv();
}

void __detach(void)
{
    D8myshared6detachFZv();
}
_

_extern void_参照は、エクスポートされた関数のマングルされた名前を使用する必要があることに注意してください。これらは一致する必要があります。一致しない場合、コードはリンクしません。

以下を使用してCコードをコンパイルします。

_gcc -m32 -c export.c
_

以下を使用して、.c.oファイルと.d.oファイルをリンクします。

_cc -o libmyshared.dylib -m32 -shared myshared.o export.o -lphobos2
_

Phobos2ライブラリが標準のリンカー検索パスにあると仮定します。コンパイラとリンカーの_-m32_オプションの一部は、ローカルでビルドしたDコンパイラのバージョンが32ビットのみをサポートしていたためです。

これにより、リンク可能な.dylibが生成されます。私が実行した限られたテストに基づいて動作するようです。共有オブジェクト/動的ライブラリのサポートは非​​常に限られているように見えるため、克服するべき別のハードルがある可能性が十分にあります。

52
Petesh

共有ライブラリがロードまたはアンロードされるたびに関数を実行するには、GCC固有の属性構文を使用してコンストラクターとデストラクタ関数をマークできます。

__attribute__((constructor)) void init(void) { ... }
__attribute__((destructor))  void fini(void) { ... }

C環境のさまざまな部分は、GCCによって舞台裏で追加された標準.initコードで初期化されているものに依存するため、-Wl,-init,<function name>を直接使用するとプログラムがクラッシュする可能性があります。

詳細については、 Library constructor and destructor functions のLibary HOWTOを参照してください。

11

GCC、およびclang AFAIKは、GCCのコンストラクター属性とデストラクター属性をサポートします。詳細については、 __ attribute __((constructor))はどのように機能しますか? を参照してください

6
janneb

C++の場合、クラスを作成し、彼のコンストラクタとデストラクタを使用してライブラリを初期化できます。

その後、このクラスの変数を定義するだけです。

ライブラリでopensslを初期化する例:

class InitLibrary {
public:
  InitLibrary() {
    CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
    SSL_library_init(); // Initialize OpenSSL's SSL libraries
    SSL_load_error_strings(); // Load SSL error strings
    ERR_load_BIO_strings(); // Load BIO error strings
    OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
  }

  ~InitLibrary() {
    ERR_remove_state(0);
    CRYPTO_cleanup_all_ex_data();
    ENGINE_cleanup();
  }
};

cppファイルに次の行のみを追加します:InitLibrary InitLib;

0
oml