JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
Oracle Solaris 11.1 リンカーとライブラリガイド     Oracle Solaris 11.1 Information Library (日本語)
このドキュメントの評価
search filter icon
search icon

ドキュメントの情報

はじめに

パート I リンカーおよび実行時リンカーの使用

1.  Oracle Solaris リンカーの紹介

2.  リンカー

3.  実行時リンカー

4.  共有オブジェクト

パート II クイックリファレンス

5.  リンカーのクイックリファレンス

パート III 詳細情報

6.  直接結合

シンボル結合の確認

直接結合の有効化

-B direct オプションの使用方法

-z direct オプションの使用方法

DIRECT mapfile キーワードの使用方法

直接結合と割り込み

シンボルインスタンスのローカライズ

同じ名前の多重定義シンボルの削除

明示的な割り込みの定義

シンボルの直接結合の回避

-B nodirect オプションの使用方法

NODIRECT mapfile キーワードの使用方法

7.  システムのパフォーマンスを最適化するオブジェクトの構築

8.  mapfile

9.  インタフェースおよびバージョン管理

10.  動的ストリングトークンによる依存関係の確立

11.  拡張性メカニズム

パート IV ELF アプリケーションバイナリインタフェース

12.  オブジェクトファイル形式

13.  プログラムの読み込みと動的リンク

14.  スレッド固有ストレージ (TLS)

パート V 付録

A.  リンカーとライブラリのアップデートおよび新機能

B.  System V Release 4 (バージョン 1) Mapfile

索引

ドキュメントの品質向上のためのご意見をください
簡潔すぎた
読みづらかった、または難し過ぎた
重要な情報が欠けていた
内容が間違っていた
翻訳版が必要
その他
Your rating has been updated
貴重なご意見を有り難うございました!

あなたの貴重なご意見はより良いドキュメント作成の手助けとなります 内容の品質向上と追加コメントのためのアンケートに参加されますか?

直接結合と割り込み

割り込みが発生する可能性があるのは、あるシンボルの複数のインスタンス (名前は同じ) が、プロセスに読み込まれた別々の動的オブジェクトに存在する場合です。デフォルト検索モデルでは、シンボル参照は、読み込まれた一連の依存関係で検出された最初の定義に結合されます。この場合、最初のシンボルが、同じ名前の別のシンボルに「割り込む」と言います。

直接結合は暗黙の割り込みを回避できます。参照と関連する依存関係の中で直接結合された参照を検索するとき、割り込みを有効にするデフォルトのシンボル検索モデルはバイパスされます。直接結合の環境では、同じ名前を持つ、シンボルの別々の定義に対して結合を確立できます。

同じ名前を持つ、シンボルの別々の定義に結合できるという点は、直接結合の有用な機能の 1 つです。ただし、アプリケーションが割り込みのインスタンスに依存していると、直接結合を使用した場合に、アプリケーションの期待動作が阻害される可能性があります。既存のアプリケーションで直接結合の使用を決める前に、割り込みが存在するかどうかを判断するために、アプリケーションを分析する必要があります。

アプリケーション内で割り込みが可能かどうかを判断するには、lari(1) を使用します。デフォルトでは、lari興味深い情報を表示します。この情報は、シンボル定義の複数のインスタンスから生じているため、割り込みに繋がる可能性があります。

割り込みは、シンボルの 1 つのインスタンスが結合されたときにのみ発生します。シンボルの複数のインスタンスが lari によって表示された場合には、割り込みが発生しない可能性があります。別の複数のインスタンスシンボルが存在する場合がありますが、参照されない可能性があります。これらの未参照のシンボルは引き続き割り込みの候補であり、今後のコード開発でこれらのシンボルが参照される可能性があります。多重定義されたシンボルのすべてのインスタンスについて、直接結合の使用を検討するときに分析する必要があります。

同じ名前のシンボルのインスタンスが複数存在する場合で、特に割り込みが確認された場合は、次の処理のいずれかを実行する必要があります。

次のセクションでは、これらの処理を詳細に説明します。

シンボルインスタンスのローカライズ

別々の実装を提供する、同じ名前の多重定義シンボルは、偶発的な割り込みを回避するために分離する必要があります。オブジェクトによってエクスポートされたインタフェースからシンボルを削除するもっとも簡単な方法は、シンボルを局所に限定することです。シンボルを局所に限定するには、シンボル「static」を定義することで、またはコンパイラが提供するシンボル属性を使用することでも、実現できます。

シンボルは、リンカーと mapfile を使用しても、局所に限定できます。次の例では、local スコープ指令を使用して大域関数 error() を局所シンボルに限定する mapfile を示します。

$ cc -o A.so.1 -G -Kpic error.c a.c b.c ...
$ elfdump -sN.symtab A.so.1 | fgrep error
    [36]  0x000002d0 0x00000014  FUNC GLOB  D    0 .text      error
$ cat mapfile
$mapfile_version 2
SYMBOL_SCOPE {
        local:
                error;
};
$ cc -o A.so.2 -G -Kpic -M mapfile error.c a.c b.c ...
$ elfdump -sN.symtab A.so.2 | fgrep error
    [24]  0x000002c8 0x00000014  FUNC LOCL  H    0 .text      error

mapfile の明示的な定義を使用して個々のシンボルを局所に限定することはできますが、シンボルのバージョン管理を介して全体のインタフェースファミリを定義することをお勧めします。第 9 章インタフェースおよびバージョン管理を参照してください。

バージョン管理は、主に共有オブジェクトからエクスポートされたインタフェースを特定するために採用された、有用な技術です。同じように、動的実行可能ファイルをバージョン管理すると、エクスポートされたインタフェースを定義できます。動的実行可能ファイルに必要なことは、結合するオブジェクトの依存関係に対して利用できるようにしなければならないインタフェースをエクスポートするだけです。多くの場合、動的実行可能ファイルに追加するコードは、インタフェースをエクスポートする必要がありません。

動的実行可能ファイルからエクスポートされたインタフェースを削除するには、コンパイラドライバによって確立されたすべてのシンボル定義を考慮する必要があります。これらの定義は、コンパイラドライバが最後のリンク編集に追加する補助ファイルが元になっています。「コンパイラドライバを使用する」を参照してください。

次の例の mapfile では、コンパイラドライバが確立する可能性がある、共通のシンボル定義セットをエクスポートする一方で、ほかのすべての大域定義を局所に限定します。

$ cat mapfile
$mapfile_version 2
SYMBOL_SCOPE {
        global:
                __Argv;
                __environ_lock;
                _environ;
                _lib_version;
                environ;
        local:
                *;
};

コンパイラドライバが確立するシンボル定義を決定する必要があります。動的実行可能ファイル内で使用されるすべての定義は大域のままにします。

動的実行可能ファイルからエクスポートされたインタフェースを削除することで、オブジェクトの依存関係が変わっていくにつれて今後発生する可能性がある割り込みの問題から、この実行可能ファイルが保護されます。

同じ名前の多重定義シンボルの削除

同じ名前の多重定義シンボルは、シンボルに関連付けられた実装が状態を保持していると、直接結合された環境内で問題となる可能性があります。典型的にはデータシンボルが違反していますが、状態を保持する関数も問題になる場合があります。

直接結合された環境では、同じシンボルの複数のインスタンスに結合できます。このため、もともとプロセス内ではインスタンスが 1 つであることを意図していた別々の状態変数を、結合している別々のインスタンスが操作できます。

たとえば、2 つの共有オブジェクトが同じデータ項目 errval を含むものとします。また、2 つの関数 action()inspect() が別々の共有オブジェクトに存在するものとします。これらの関数は、値 errval をそれぞれ読み書きします。

デフォルトの検索モデルの場合、errval の 1 つの定義がその他の定義に割り込みます。action()inspect() の両方の関数は、errval の同じインスタンスに結合されます。このため、action() によって errval にエラーコードが書き込まれた場合、inspect() がこのコードを読み込んで、このエラー条件に従って動作します。

しかし、action() および inspect() を含むオブジェクトが、errval をそれぞれで定義した別々の依存関係に結合されたものと仮定します。直接結合された環境では、これらの関数は errval の別々の定義に結合されます。エラーコードは action() によって errval の 1 つのインスタンスに書き込まれ、その一方で inspect()errval のもう一方の初期化されていない定義を読み込みます。その結果、inspect() では、処理対象となるエラー条件を検出しません。

一般的に、データシンボルの複数のインスタンスは、シンボルがヘッダーで宣言された場合に発生します。

int bar;

このデータ宣言では、ヘッダーを含むコンパイル単位ごとに、データ項目が作成されることになります。その結果生じる一時的なデータ項目によって、シンボルの複数のインスタンスが別々の動的オブジェクトに定義される場合があります。

しかし、データ項目を明示的に外部として定義することで、データ項目への参照は、ヘッダーを含むコンパイル単位ごとに作成されます。

extern int bar;

その結果、これらの参照を実行時に 1 つのデータインスタンスに解決できます。

シンボル実装を削除するときに、そのインタフェースの保持が必要な場合があります。同じインタフェースの複数のインスタンスは、既存のすべてのインタフェースを保持しながら、1 つの実装にすることができます。このモデルは、FILTER mapfile キーワードを使用して個々のシンボルフィルタを作成することで実現できます。このキーワードは、「SYMBOL_SCOPE/SYMBOL_VERSION 指令」で説明されています。

個々のシンボルフィルタを作成することが有効なのは、シンボルの実装が削除されたオブジェクト内で、依存関係がそのシンボルの検出を期待している場合です。

たとえば、関数 error()A.so.1B.so.1 の 2 つの共有オブジェクトに存在するものとします。シンボルの重複を削除するには、A.so.1 から実装を削除する必要があります。しかし、別の依存関係が A.so.1 から得られる error() に依存しています。次の例は、A.so.1 での error() の定義を示しています。mapfile を使用すると、B.so.1 に指定されたこのシンボルのフィルタを残したまま、error() の実装を削除できます。

$ cc -o A.so.1 -G -Kpic error.c a.c b.c ...
$ elfdump -sN.dynsym A.so.1 | fgrep error
    [3]  0x00000300 0x00000014  FUNC GLOB  D    0 .text      error
$ cat mapfile
$mapfile_version 2
SYMBOL_SCOPE {
        global:
                error { TYPE=FUNCTION; FILTER=B.so.1 };
};
$ cc -o A.so.2 -G -Kpic -M mapfile a.c b.c ...
$ elfdump -sN.dynsym A.so.2 | fgrep error
    [3]  0x00000000 0x00000000  FUNC GLOB  D    0 ABS        error
$ elfdump -y A.so.2 | fgrep error
    [3]  F       [0] B.so.1         error

関数 error() は大域であり、A.so.2 のエクスポートされたインタフェースのままです。しかし、このシンボルへの実行時結合はフィルティー B.so.1 になります。文字「F」はこのシンボルのフィルタ特性を表しています。

既存のインタフェースを保持したまま 1 つの実装にするこのモデルは、複数の Oracle Solaris ライブラリで使用されています。たとえば、以前に libc.so.1 に定義された多くの数値演算インタフェースは、現在は libm.so.2 の推奨される関数実装を指しています。

明示的な割り込みの定義

デフォルトの検索モデルでは、同じ名前のシンボルのインスタンスが、同じ名前の後のインスタンスに割り込む可能性があります。明示的なラベル付けをしなくても割り込みは発生するため、1 つのシンボル定義がすべての参照から結合されます。この暗黙の割り込みはシンボル検索の結果として起こるもので、実行時リンカーに指定された明示的な命令によるものではありません。この暗黙の割り込みは直接結合によって回避できます。

直接結合は、関連するシンボル定義に対する直接のシンボル参照を解決する働きをしますが、明示的な割り込みは直接結合の検索の前に処理されます。このため、直接結合の環境であっても、割り込み処理を設計でき、直接結合の関連付けに割り込みを期待できます。割り込み処理は、次の方法を利用して明示的に定義できます。

LD_PRELOAD 環境変数の割り込み機能と -z interpose オプションが、しばらく前から利用可能です。「実行時割り込み」を参照してください。これらのオブジェクトは割り込み処理であると明示的に定義されているため、実行時リンカーは直接結合を処理する前にこれらのオブジェクトを検査します。

共有オブジェクトに対して確立された割り込みは、その動的オブジェクトのすべてのインタフェースに適用されます。LD_PRELOAD 環境変数を使用してオブジェクトが読み込まれたときに、このオブジェクトの割り込みが確立されます。オブジェクトの割り込みは、-z interpose オプションで作成されたオブジェクトが読み込まれたときにも確立されます。このオブジェクトモデルが重要なのは、特別なハンドル RTLD_NEXT を持つ dlsym(3C) などの技術が使用されたときです。割り込むオブジェクトは、次のオブジェクトの一貫したビューを常に持つべきです。

動的実行可能ファイルは、INTERPOSE mapfile キーワードを使用して個々の割り込みシンボルを定義できるという点で柔軟性が増します。動的実行可能ファイルはプロセスにロードされる最初のオブジェクトであるため、次のオブジェクトの実行可能ファイルのビューは常に一貫したものになります。

次の例は、exit() 関数に明示的に割り込みを行うアプリケーションを示しています。

$ cat mapfile
$mapfile_version 2
SYMBOL_SCOPE {
        global:
                exit    { FLAGS = INTERPOSE };
};
$ cc -o prog -M mapfile exit.c a.c b.c ...
$ elfdump -y prog | fgrep exit
    [6]  DI          <self>         exit

文字「I」は、このシンボルの割り込み特性を表しています。おそらく、この exit() 関数の実装では、システム関数 _exit() が直接参照されるか、または RTLD_NEXT ハンドルを持つ dlsym() を使ってシステム関数 exit() に達するまで呼び出しが続けられると考えられます。

最初は、-z interpose オプションを使ってこのオブジェクトを特定することを考えてはどうでしょうか。ただし、この手法は、アプリケーションによってエクスポートされたすべてのインタフェースが割り込み処理として機能するため、かなりの重量になります。より適切な代替方法は、-z interpose オプションを使用するとともに、割り込み処理を除く、アプリケーションで提供されたすべてシンボルをローカライズすることです。

しかし、INTERPOSE mapfile キーワードを使用すると、柔軟性が向上します。このキーワードを使用すると、アプリケーションでいくつかのインタフェースをエクスポートしながら、それらのインタフェースから割り込み処理として機能させるものを選択できます。

STV_SINGLETON 可視性が割り当てられているシンボルには、効果的な割り込みの形態が備わっています。表 12-21 を参照してください。これらのシンボルは、コンパイルシステムによって、プロセス内の多数のオブジェクトで多重インスタンス化状態になる可能性がある実装に割り当てることができます。singleton シンボルへのすべての参照は、プロセス内で最初に定義された singleton に結合されます。