リンカーとライブラリ

シンボルの検索

dlopen(3C) によって取得したオブジェクトが大域シンボルを参照する場合は、実行時リンカーは、プロセスを作成するオブジェクトのプールからこのシンボルを配置する必要があります。直接結合がない場合は、dlopen() によって入手されたオブジェクトには、デフォルトのシンボル検索モデルが適用されます。ただし、プロセスを作成するオブジェクトの属性と結合される dlopen() のモードは、代わりのシンボル検索モデルに提供されます。

直接結合を指定されたオブジェクトでは、それに対応する依存関係から直接、シンボルが検索されます。ただし、この後で述べるすべての属性はそのまま有効です。「直接結合」を参照してください。

デフォルトにより、dlopen(3C) を使用して入手したオブジェクトには、「ワールド」シンボル検索範囲と「ローカル」シンボル可視性が割り当てられます。「デフォルトのシンボル検索モデル」では、このデフォルトモデルを使用して、典型的なオブジェクトグループのインタラクションについて説明しています。「大域オブジェクトの定義」「グループの分離」、および 「オブジェクト階層」では、デフォルトのシンボル検索モデルの展開に dlopen(3C) モードとファイル属性を使用する例を示しています。

デフォルトのシンボル検索モデル

基本的な dlopen(3C) によって追加された各オブジェクトでは、実行時リンカーは、最初に動的実行可能ファイル内でシンボルを検索します。次に実行時リンカーは、プロセスの初期設定中に提供されたそれぞれのオブジェクト内を検索します。シンボルが見つからない場合、実行時リンカーは検索を続けます。実行時リンカーは次に dlopen(3C) によって取得されたオブジェクトおよびそのオブジェクトと依存関係にあるオブジェクトを検索します。

デフォルトのシンボル検索モデルは、遅延読み込み環境の遷移も行います。現在読み込まれているオブジェクト内でシンボルが見つからない場合は、そのシンボルを特定するために、保留となっている遅延読み込みオブジェクトが処理されます。この読み込みによって、依存関係を完全には定義していないオブジェクトを補います。ただし、これにより遅延読み込みの利点が失われることがあります。

次の例の動的実行可能プログラム prog と共有オブジェクト B.so.1 には、依存関係が付いています。


$ ldd prog
        A.so.1 =>        ./A.so.1
$ ldd B.so.1
        C.so.1 =>        ./C.so.1

prog が、dlopen(3C) を使用して共有オブジェクト B.so.1 を入手した場合、共有オブジェクト B.so.1C.so.1 の再配置に必要なシンボルが、最初に prog 内で検索され、A.so.1B.so.1C.so.1 の順に検索されます。このような単純なケースでは、dlopen(3C) によって入手された共有オブジェクトは、アプリケーションの元のリンク編集の末尾に追加されたと考えます。たとえば、上記のオブジェクトの参照を図示すると、次のようになります。

図 3–1 単一の dlopen() 要求

単一の dlopen() 要求。

dlopen(3C) から入手されたオブジェクトによって要求されたシンボル検索は、影付きのブロックで示しています。このシンボル検索は、動的実行プログラム prog から、最後の共有オブジェクト C.so.1 へと進みます。

このシンボル検索は、読み込まれたオブジェクトに割り当てられた属性によって確立されます。動的実行可能ファイルとそれと同時に読み込まれたすべての依存関係には、大域シンボル可視性が割り当てられ、新しいオブジェクトにはワールドシンボルの検索範囲が割り当てられることを思い出してください。これによって、新しいオブジェクトは元のオブジェクト内を調べてシンボルを検索できます。また、新しいオブジェクトは、固有のグループを形成し、このグループ内では、各オブジェクトは局所シンボル可視性を持ちます。そのため、グループ内の各オブジェクトは、ほかのグループメンバー内でシンボルを検索できます。

これらの新しいオブジェクトは、アプリケーションまたはアプリケーションの最初の依存関係によって要求される、通常のシンボル検索には影響を与えません。たとえば、上記の dlopen(3C) が実行された後で、A.so.1 に関数再配置が必要な場合、実行時リンカーの再配置シンボルの通常の検索は、順に progA.so.1 で実施されます。B.so.1 または C.so.1 は検索されません。

このシンボル検索は、読み込まれたときにオブジェクトに割り当てられた属性によって実行されます。ワールドシンボルの検索範囲が、動的実行可能プログラムとこれとともに読み込まれた依存関係に割り当てられます。この検索範囲では、局所シンボル可視性だけを提供する新しいオブジェクト内を検索できません。

これらのシンボル検索とシンボル可視性の属性は、オブジェクト間の関係を保持します。これらの関係は、そのプロセスのアドレススペースへの投入とオブジェクト間の依存の関係に基づいています。指定された dlopen(3C) に関連したオブジェクトを固有のグループに割り当てることにより、同じ dlopen(3C) と関連したオブジェクトだけが、グループ内のシンボルと、関連する依存関係の中の検索ができます。

このオブジェクト間の関係を定義するという概念は、複数の dlopen(3C) を実行するアプリケーション内では、より明確になります。たとえば、共有オブジェクト D.so.1 に次の依存関係があるとします。


$ ldd D.so.1
        E.so.1 =>         ./E.so.1

このとき、prog アプリケーションが、共有オブジェクト B.so.1 に加えてこの共有オブジェクトも dlopen(3C) を使って読み込んだとします。次の図は、オブジェクト間のシンボル検索の関係を示しています。

図 3–2 複数の dlopen() 要求

複数の dlopen() 要求。

B.so.1D.so.1 の両方にシンボル foo の定義が組み込まれ、C.so.1E.so.1 にこのシンボルを必要とする再配置が組み込まれているとします。固有のグループに対するオブジェクトの関係によって、C.so.1B.so.1 の定義に結合され、E.so.1D.so.1 の定義に結合されます。このメカニズムは、dlopen(3C) への複数の呼び出しにより入手されたオブジェクトのもっとも直感的な結合を提供するためのものです。

オブジェクトが、前述した処理の進行の中で使用される場合、それぞれの dlopen(3C) が実施された順序は、結果として発生するシンボル結合には影響しません。ただし、複数のオブジェクトに共通の依存関係がある場合は、結果の結び付きは、dlopen(3C) 呼び出しが実行された順序の影響を受けます。

次に、同じ共通依存関係を持つ共有オブジェクト O.so.1P.so.1 の例を示します。


$ ldd O.so.1
        Z.so.1 =>        ./Z.so.1
$ ldd P.so.1
        Z.so.1 =>        ./Z.so.1

この例では、prog アプリケーションは、各共有オブジェクトに dlopen(3C) を使用しています。共有オブジェクト Z.so.1 が、O.so.1P.so.1 両方の共通依存関係であるため、Z.so.1 は 2 つの dlopen(3C) 呼び出しに関連する両方のグループに割り当てられます。この依存関係を次の図に示します。

図 3–3 共通依存関係を伴う複数の dlopen() 要求

共通依存関係を伴う複数の dlopen() 要求。

この結果、O.so.1P.so.1 の両方がシンボルの検索に Z.so.1 を使用できます。ここで重要なのは、dlopen(3C)の順序に限って言えば、Z.so.1O.so.1P.so.1 の両方の中でシンボルを検索できることです。

そのため、O.so.1P.so.1 の両方に、Z.so.1 の再配置に必要なシンボル foo の定義が組み込まれている場合、実際に発生する結び付きを予期することはできません。それは、この結び付きが dlopen(3C) 呼び出しの順序の影響を受けるからです。シンボル foo の機能が、シンボルが定義されている 2 つの共有オブジェクト間で異なる場合、Z.so.1 でコードを実行したすべての結果は、アプリケーションの dlopen(3C) の順序によって異なる可能性があります。

大域オブジェクトの定義

dlopen(3C) で取得されるオブジェクトにデフォルトで割り当てられる「ローカル」シンボル可視性を「大域」に拡張するには、モード引数に RTLD_GLOBAL フラグを指定します。このモードでは、dlopen(3C) によって入手されたオブジェクトは、シンボルを配置するための、ワールドシンボルの検索範囲が指定されたほかのオブジェクトによって使用することができます。

また、RTLD_GLOBAL フラグが指定された dlopen(3C) によって入手されたオブジェクトは、dlopen() (値 0 のパス名を指定) を使用したシンボル検索にも使用できます。


注 –

局所シンボルの可視性を持つグループのメンバーが、ほかの大域シンボルの可視性を必要とするグループによって参照される場合、オブジェクトの可視性はローカルと大域の両方を連結したものになります。この後大域グループの参照が削除されても、この格上げされた属性はそのまま残ります。


グループの分離

dlopen(3C) で取得されるオブジェクトにデフォルトで割り当てられる「ワールド」シンボル検索範囲を「グループ」に縮小するには、モード引数に RTLD_GROUP フラグを指定します。このモードでは、dlopen(3C) によって入手されたオブジェクトは、そのオブジェクト固有のグループ内でしかシンボルの検索ができません。

リンカーの -B group オプションを使用して構築したオブジェクトには、グループのシンボル検索範囲を割り当てることができます。


注 –

グループ検索機能を持つグループのメンバーが、ワールド検索機能を必要とするほかのグループによって参照された場合、オブジェクトの検索機能はグループとワールドが連結したものになります。この後ワールドグループの参照が削除されても、この格上げされた属性はそのまま残ります。


オブジェクト階層

最初のオブジェクトが dlopen(3C) によって入手され、dlopen() を使用して 2 番目のオブジェクトを開いた場合、両方のオブジェクトは同じ固有のグループに割り当てられます。これにより、オブジェクトが互いにシンボルを配置し合うことを防ぐことができます。

実装の中には、最初のオブジェクトの場合、シンボルを 2 番目のオブジェクトの再配置用にエクスポートする必要がある場合もあります。この必要条件は、次の 2 つのメカニズムのいずれかによって満たすことができます。

最初のオブジェクトを 2 番目のオブジェクトの明示的な依存関係にした場合、これは 2 番目のオブジェクトのグループにも割り当てられます。そのため、最初のオブジェクトは、2 番目のオブジェクトの再配置に必要なシンボルも提供できます。

多くのオブジェクトが dlopen(3C) を使って 2 番目のオブジェクトを開くことができ、かつそれらの初期オブジェクトが 2 番目のオブジェクトの再配置を満たすために同じシンボルをエクスポートしなければならない場合、その 2 番目のオブジェクトに明示的な依存関係を割り当てることはできません。この場合、2 番目のオブジェクトの dlopen(3C) モードは、RTLD_PARENT フラグを使用して補強できます。このフラグによって、2 番目のオブジェクトのグループが、明示的な依存関係が伝達されたのと同じ方法で、最初のオブジェクトに伝達されます。

これら 2 つの手法の間には、小さな相違点が 1 つ存在しています。明示的な依存関係を指定する場合、その依存関係そのものは、2 番目のオブジェクトの dlopen(3C) 依存関係ツリーの一部になるため、dlsym(3C) を使用したシンボル検索が可能になります。RTLD_PARENT を使用して 2 番目のオブジェクトを入手する場合、最初のオブジェクトは、dlsym(3C) を使用したシンボルの検索に使用できるようにはなりません。

「大域」シンボル可視性を持つ初期オブジェクトが dlopen(3C) を使って 2 番目のオブジェクトを取得する場合、RTLD_PARENT モードは冗長かつ無害になります。このような状態は、dlopen(3C) がアプリケーションから呼び出されたとき、またはアプリケーションの中の依存関係の 1 つから呼び出されたときに多く発生します。