リンカーとライブラリ

初期設定および終了ルーチン

制御をアプリケーションに転送する前に、実行時リンカーは、アプリケーションおよびその依存関係内で検出された初期設定セクションを処理します。これらの .preinit_array.init_array.init セクションは、動的オブジェクトが構築される際にリンカーによって作成され、それぞれ .dynamic タグの DT_PREINIT_ARRAYDT_INIT_ARRAYDT_INIT でラベル付けされます (「初期設定および終了セクション」を参照)。

DT_PREINIT_ARRAY および DT_INIT_ARRAY で指定された配列内に含まれたアドレスを持つ関数は、配列内でアドレスが出現する順と同じ順序で実行時リンカーによって実行されます。1 つのオブジェクトに DT_INIT エントリと DT_INIT_ARRAY エントリの両方が含まれる場合は、そのオブジェクトについて DT_INIT エントリによって参照される関数は、DT_INIT_ARRAY エントリによって参照される関数より前に処理されます。

動的実行可能ファイルは、.preinit_array セクション内で初期設定前関数を提供することができます。これらの関数は、実行時リンカーがプロセスイメージを構築して再配置を実行し終わった後で、かつ他の初期設定関数の前に実行されます。初期設定前関数は、共有オブジェクト内では許可されません。


注 -

動的実行可能ファイル内のすべての DT_INIT セクションは、コンパイラドライバから供給されるプロセスの起動メカニズムによって、アプリケーション自体から呼び出されます。動的実行可能ファイルの DT_INIT セクションは、そのすべての依存関係の初期設定セクションが実行された後で、最後に呼び出されます。


Solaris 2.6 より前のリリースでは、依存関係からの初期設定ルーチンは、読み込まれた順序の降順で、つまり、ldd(1) を使用して表示された依存関係の順と逆の順序で呼び出されていました。

Solaris 2.6 リリースからは、実行時リンカーは、読み込まれた依存関係から、初期設定ルーチンの依存関係の配列リストを作成します。このリストは、各オブジェクトが表す依存関係の相関関係に加えて、表示された依存関係の外部で発生した結合から構成されます。

初期設定セクションは、依存関係が配列された順序の逆の順序で実行されます。循環性のある依存関係が検出された場合は、その周期を形成するオブジェクトは、トポロジカルソートは実行できません。そのため、この初期設定セクションは、読み込まれた順序の逆の順序で実行されることになります。

-i オプションを指定した ldd(1) を使用すると、オブジェクトの依存関係の初期設定の順番を表示できます。たとえば、次の動的実行プログラムとその依存関係は、循環性のある依存関係を示しています。


$ dump -Lv B.so.1 | grep NEEDED
[1]     NEEDED      C.so.1
$ dump -Lv C.so.1 | grep NEEDED
[1]     NEEDED      B.so.1
$ dump -Lv main | grep NEEDED
[1]     NEEDED      A.so.1
[2]     NEEDED      B.so.1
[3]     NEEDED      libc.so.1
$ ldd -i main
        A.so.1 =>        ./A.so.1
        B.so.1 =>        ./B.so.1
        libc.so.1 =>     /usr/lib/libc.so.1
        C.so.1 =>        ./C.so.1
        libdl.so.1 =>    /usr/lib/libdl.so.1

   cyclic dependencies detected, group[1]:
        ./libC.so.1
        ./libB.so.1

   init object=/usr/lib/libc.so.1
   init object=./A.so.1
   init object=./C.so.1 - cyclic group [1], referenced by:
        ./B.so.1
   init object=./B.so.1 - cyclic group [1], referenced by:
        ./C.so.1

注 -

Solaris 8 10/00 より前のリリースでは、環境変数 LD_BREADTH に空文字以外の値を設定すると、リンカーで初期設定セクションを Solaris 2.6 より前の順序で実行することができました。しかし、多数のアプリケーションを初期化すると、依存関係が複雑になり、位相的な並び替えが必要になるため、この機能は Solaris 8 10/00 から無効にされています。LD_BREADTH の設定は無視され、メッセージは表示されません。


初期設定処理は、dlopen(3DL) が指定された実行中のプロセスに追加されたオブジェクトごとに繰り返されます。

循環的な依存関係が存在することはよくあるため、実行時リンカーはさらに、初期設定を動的に呼び出して、そのオブジェクトのコードが呼び出される前に初期設定セクションを実行しようとします。実行時リンカーは、シンボルの結合の際に、結合する先のオブジェクトの初期設定セクションがすでに呼び出されているかどうかを判定し、呼び出されていなければ、シンボル結合手順を終る前に、それを呼び出します (「再配置が実行されるとき」を参照)。初期設定呼び出しの正確な順序は、環境変数 LD_DEBUGbasic を指定すれば見ることができます (「デバッギングエイド」を参照)。

動的オブジェクトは、終了セクションも提供できます。これらの .fini_array および .fini セクションは、動的オブジェクトが構築され、.dynamic タグの DT_FINI_ARRAY および DT_FINI でラベル付けされる際に、リンカーによって作成されます (「初期設定および終了セクション」 を参照)。

終了ルーチンは、atexit(3C) によって記録できるように構成されます。これらのルーチンは、プロセスが exit(2) を呼び出したとき、またはオブジェクトが、dlclose(3DL) が指定された実行プロセスから除去されたときに呼び出されます。

DT_FINI_ARRAY によって指定された配列内に含まれたアドレスを持つ関数は、その配列内でアドレスが出現する順序と逆の順序で、実行時リンカーに実行されます。1 つのオブジェクトに DT_FINI エントリと DT_FINI_ARRAY エントリの両方が含まれる場合は、そのオブジェクトについて DT_FINI_ARRAY エントリによって参照される関数は、DT_FINI エントリによって参照される関数よりも前に処理されます。


注 -

動的実行プログラム内の .fini セクションは、コンパイラドライバから提供されるプロセスの終了メカニズムによってアプリケーション自体から呼び出されます。動的実行プログラムの .fini セクションは、その依存関係の終了セクションが実行される前に、最初に呼び出されます。


Solaris 2.6 リリースからは、終了ルーチンが依存関係の配列順に呼び出されます。Solaris 2.6 より前のリリースの場合、終了ルーチンは読み込まれた順に呼び出されていました。

この初期設定および終了の呼び出し順序は、簡単明瞭に見えますが、この順序を強調しすぎないように注意が必要です。オブジェクトの順序は、共有オブジェクトとアプリケーションの開発によって左右される場合があるからです (詳細については、「依存関係の並べ変え」を参照)。