Go to main content
Oracle® Solaris 11.3 リンカーとライブラリガイド

印刷ビューの終了

更新: 2015 年 10 月
 
 

動的依存関係の遅延読み込み

メモリーに動的オブジェクトが読み込まれる際、その動的オブジェクトに追加の依存関係がないか検査されます。デフォルトでは、存在する依存関係がただちに読み込まれます。このサイクルは、依存関係のツリー全体を使い果たすまで続けられます。最終的に、再配置で指定されたオブジェクト間のデータ参照すべてが解決されます。この処理は、これらの依存関係内のコードが実行中にアプリケーションによって実際に参照されるかどうかに関係なく、行われます。

遅延読み込みモデルでは、遅延読み込みのラベルが付いた依存関係は、明示的に参照が行われるまで読み込まれません。関数呼び出しの遅延結合を利用して、関数が最初に参照されるまで、依存関係の読み込みを延期することができます。結果として、参照されないオブジェクトは読み込まれません。

再配置参照は、即時か遅延です。即時参照はオブジェクトが初期化された時に解決される必要があるため、この参照を満たすすべての依存関係はすぐに読み込まれる必要があります。そのため、そういった依存関係を遅延読み込み可能として示すことは、あまり効果がありません。詳細は、再配置が実行されるときを参照してください。動的オブジェクト間の即時参照は、概してあまり推奨されません。

遅延読み込みは、デバッグライブラリ liblddbg への参照のためにリンカーが使用します。デバッギングを呼び出すことはまれなので、リンカーを呼び出すたびにこのライブラリを読み込むことは不要で、コストがかさみます。このライブラリを遅延読み込みできるように指定することにより、ライブラリの処理コストをデバッギング出力を必要とする読み込みに使うことができます。

遅延読み込みモデルを実行するための代替メソッドは、必要に応じて依存関係に dlopen() または dlsym() を実行することです。このモデルは、dlsym() 参照の数が少ない場合に最適です。またこのモデルは、リンク編集時に依存関係の名前あるいは位置がわからない場合にも適しています。名前や位置がわかっている依存関係のより複雑な相互作用については、通常のシンボル参照のコードを使用し、依存関係を遅延読み込みに指定する方が簡単です。

特定のオブジェクトを遅延読み込み、通常ロードとして指定するには、リンカーのオプション –z lazyload–z nolazyload をそれぞれ使用します。これらのオプションは、リンク編集コマンド行の位置に依存します。このオプションよりあとに指定される依存関係には、このオプションで指定されている読み込み属性が適用されます。デフォルトでは、–z nolazyload オプションが有効です。

次の単純なプログラムでは、libdebug.so.1 に対する依存関係が指定されています。動的セクション (.dynamic) では、libdebug.so.1 に対して遅延読み込みが指定されています。シンボル情報セクション (.SUNW_syminfo) では、libdebug.so.1 の読み込みをトリガーするシンボル参照が指定されています。

$ cc -o prog prog.c -L. -zlazyload -ldebug -znolazyload -lelf -R'$ORIGIN'
$ elfdump -d prog

Dynamic Section:  .dynamic
   index  tag           value
     [0]  POSFLAG_1       0x1         [ LAZY ]
     [1]  NEEDED        0x123         libdebug.so.1
     [2]  NEEDED        0x131         libelf.so.1
     [3]  NEEDED        0x13d         libc.so.1
     [4]  RUNPATH       0x147         $ORIGIN
     ...
$ elfdump -y prog

Syminfo section: .SUNW_syminfo
   index  flgs        bound to        symbol
    ....
    [52]  DL      [1] libdebug.so.1   debug

値に LAZY が指定された POSFLAG_1 は、次の NEEDED エントリ libdebug.so.1 が遅延読み込みされることを示しています。libelf.so.1 は前に LAZY フラグがないため、このライブラリはプログラムの初期始動時に読み込まれます。


注 -  libc.so.1 には、ファイルが遅延読み込みされてはならないという特別なシステム要件があります。libc.so.1 が処理される時点で –z lazyload が有効である場合、フラグは実際には無視されます。

遅延読み込みを使用するには、アプリケーションで使用されるオブジェクト全体に渡り依存関係と「実行パス」を正確に宣言しなければならない場合があります。たとえば、libX.so 内のシンボルを参照する 2 つのオブジェクト libA.solibB.so があるとします。libA.solibX.so を依存関係として宣言しますが、libB.so は宣言しません。通常、libA.solibB.so が併用される場合、libB.solibX.so を参照できます。これは、libA.so によってこの依存関係が利用可能になっているためです。しかし、libX.so が遅延読み込みされるように libA.so で宣言した場合、libB.so がこの依存関係を参照するときに libX.so を読み込めない可能性があります。libB.solibX.so を依存関係として宣言していても、その依存関係の特定に必要な実行パスを指定しなかった場合には、同様のエラーが発生する可能性があります。

遅延読み込みに関わらず、動的オブジェクトは、すべての依存関係と依存関係の特定方法を宣言しなければなりません。遅延読み込みでは、この依存情報がより重要な意味合いを持ちます。


注 -  環境変数 LD_NOLAZYLOAD をヌル以外の値に設定すれば、実行時に遅延ロードを無効にできます。

dlopen() の代替手段の提供

遅延読み込みは、dlopen(3C)dlsym(3C) を使用する代わりになります。実行時リンクのプログラミングインタフェースを参照してください。たとえば、libfoo.so.1 の次のコードは、オブジェクトが読み込まれることを確認し、そのオブジェクトのインタフェースを呼び出します。

void foo()
{
        void *handle;

        if ((handle = dlopen("libbar.so.1", RTLD_LAZY)) != NULL) {
                int (*fptr)();

                if ((fptr = (int (*)())dlsym(handle, "bar1")) != NULL)
                        (*fptr)(arg1);
                if ((fptr = (int (*)())dlsym(handle, "bar2")) != NULL)
                        (*fptr)(arg2);
                ....
        }
}

dlopen()dlsym() を使用するこのモデルは、非常に柔軟ですが、不自然なコーディングスタイルであり、欠点がいくつかあります。

  • シンボルが終了すると予想されるオブジェクトは既知である必要があります。

  • 関数ポインタを介した呼び出しには、コンパイラまたは lint(1) で検証する手段がありません。

このコードは、必要なインタフェースを提供するオブジェクトが次の条件を満たす場合に、単純化できます。

  • オブジェクトが、リンク編集時の依存関係として構築できる。

  • オブジェクトが、常に利用できる。

関数参照が遅延読み込みをトリガーできることを利用すると、libbar.so.1 と同じ遅延読み込みが実現できます。この場合、関数 bar1() を参照すると、関連依存関係が遅延ロードされます。このコーディングの方が自然です。また、標準関数呼び出しを使用することによって、コンパイラまたは lint(1) で検証できるようになります。

void foo()
{
        bar1(arg1);
        bar2(arg2);
        ....
}
$ cc -G -o libfoo.so.1 foo.c -L. -zdefs -zlazyload -lbar -R'$ORIGIN'

ただし、必要なインタフェースを提供するオブジェクトが常に利用できるとは限らない場合、このモデルは失敗します。この場合、依存関係の名前がわからなくても、その依存関係の有無をテストする機能が必要です。関数参照を満足する依存関係を使用できるかどうかをテストする手段が必要になります。

関数の有無をテストするための堅牢なモデルは、明示的に定義された遅延依存関係を使用することによって、また RTLD_PROBE ハンドルを指定して dlsym(3C) を使用することによって実現できます。

明示的に定義された遅延依存関係は、遅延ロード可能な依存関係の拡張です。遅延依存関係に関係付けられたシンボル参照は、遅延シンボルと呼ばれます。このシンボルに対する再配置が処理されるのは、シンボルが最初に参照されるときだけです。これらの再配置は、LD_BIND_NOW 処理の一部としても、また RTLD_NOW フラグ付きの dlopen(3C) を介しても処理されません。

遅延依存関係は、リンク編集時にリンカーの –z deferred オプションを使用して確立されます。

$ cc -G -o libfoo.so.1 foo.c -L. -zdefs -zdeferred -lbar -R'$ORIGIN'

遅延依存関係として libbar.so.1 を確立した場合、bar1() への参照によって、その依存関係が利用できることを検証できます。このテストを使用すると、dlsym(3C) を使用した場合と同じ方法で、依存関係によって提供される関数への参照を制御できます。次に、このコードは bar1() および bar2() を自然に呼び出すことができます。これらの呼び出しは判読しやすく、コーディングも容易であるため、コンパイラが呼び出しシーケンスでのエラーを見つけられるようになります。

void foo()
{
        if (dlsym(RTLD_PROBE, "bar1")) {
                bar1(arg1);
                bar2(arg2);
                ....
        }
}

遅延依存関係によって柔軟性が向上します。依存関係がまだ読み込まれていない場合、その依存関係は実行時に変更できます。このメカニズムにより、dlopen(3C) と同じ程度の柔軟性が得られます。つまり、呼び出し元によって、異なるオブジェクトを読み込ませたり、異なるオブジェクトに結合させたりできるようになります。

元の依存関係名が既知の場合、RTLD_DI_DEFERRED 引数を指定して dlinfo(3C) を使用すると、元の依存関係を新しい依存関係と交換できます。 また、依存関係に関連する遅延シンボルを使用すると、RTLD_DI_DEFERRED_SYM 引数を持つ dlinfo(3C) を使用して遅延依存関係を特定できます。