リンカーは、入力ファイルをコマンド行上に表示された順番に読み取ります。各ファイルは、オープンされ、その ELF ファイルタイプを判別するために検査され、どのように処理する必要があるかが決定されます。リンク編集に必要な入力に適用するファイルタイプは、リンク編集の結合モード、「静的」または「動的」のいずれかによって決定されます。
「静的」方法では、リンカーが入力ファイルとして受け入れるのは、再配置可能オプションまたはアーカイブライブラリだけです。「動的」方法では、リンカーは、共有オブジェクトも受け入れます。
再配置可能オブジェクトは、リンク編集プロセスへの最も基本的な入力ファイルタイプを示しています。これらのファイル内の「プログラムデータ」のセクションは、生成される出力ファイルイメージに結合されます。「リンク編集情報」のセクションは、今後の利用のために構成されますが、出力ファイルイメージには組み込まれません。それは、これを配置する場所として、新しいセクションが生成されるからです。シンボルは、その検査および解析を考慮して、最終的に出力イメージ内に 1 つまたは複数のシンボルテーブルを作成できるように、特定の内部シンボルテーブル内に集められます。
入力ファイルは、リンク編集のコマンド行上に直接指定できますが、アーカイブライブラリと共有オブジェクトは、通常、 -l オプションを使用して指定します (このメカニズムについてと、それが 2 種類のリンクモードにどのように関係するかについては、「追加ライブラリとのリンク」 を参照してください)。ただし、通常、共有オブジェクトが共有ライブラリと呼ばれ、さらに、これら 2 つのオブジェクトを同じオプションを使用して指定したとしても、共有オブジェクトとアーカイブライブラリは全く別のものです。次の 2 つの項で、この違いについて説明します。
アーカイブは、ar(1) を使用して構築され、通常、アーカイブシンボルテーブルとともに再配置可能オブジェクトの集合で構成されます。このシンボルテーブルにより、これらの定義の提供するオブジェクトとシンボル定義との関係がわかります。デフォルトでは、リンカーを使用すると、アーカイブ構成要素を選択して抽出できます。リンカーがアーカイブを読み取る場合は、結合処理を完了させるために必要なアーカイブから、オブジェクトだけを選択するように作成された内部シンボルテーブル内の情報を使用します。さらに、1 つのアーカイブのすべての構成要素を明示的に抽出することも可能です。
次のような場合に、リンカーはアーカイブから再配置可能オブジェクトを抽出します。
アーカイブに、現在リンカーの内部シンボルテーブル内に保持されている、「未確認」シンボル定義を満たす「データ」シンボル定義が入っている場合。この例としては、FORTRAN
COMMON ブロック定義があります。この定義により、同じ DATA シンボルを定義する再配置可能オブジェクトが抽出される
選択式アーカイブ抽出においては、ウィークシンボルリファレンスでは、-z weakextract オプションが発効されていない限り、アーカイブからの抽出は実行されません。ウィークシンボルについては、「単純な解析」の項で詳しく説明しています。
オプション -z weakextract、-z allextract および -z defaultextract により、複数のアーカイブ間でアーカイブメカニズムを切り替えることができます。
選択式アーカイブ抽出の場合、リンカーは、リンカーの内部シンボルテーブル内に蓄積されたシンボル情報を満たすために必要な、再配置可能オブジェクトを抽出するアーカイブを通る複数のパスを作成します。リンカーが、再配置可能オブジェクトを抽出せずに、アーカイブを通る完全なパスを作成すると、リンカーは次の入力ファイルの処理に移ります。
アーカイブに遭遇した時点で、必要な再配置可能オブジェクトだけをアーカイブから抽出することによりメカニズムは、入力ファイルリスト内のアーカイブの位置が重要であることを意味しています (詳細については、「コマンド行上のアーカイブの位置」 を参照してください)。
リンカーは、アーカイブを通る複数のパスを作成し、シンボルを解析しますが、このメカニズムは、ランダムに構成された再配置可能オブジェクトが組み込まれた大容量のアーカイブの場合には、非常にコストがかかります。このような場合は、lorder(1) や tsort(1) などのツールを使用してアーカイブ内の再配置可能オブジェクトを配列し、リンカーが実行しなければならないパスの数を削減することができます。
共有オブジェクトは、分割不可能な、1 つまたは複数の入力ファイルの以前の編集によって生成された総体単位です。リンカーが共有オブジェクトを処理すると、共有オブジェクトの全内容は、その結果作成された出力ファイルイメージの「論理的」な部分になります。共有オブジェクトは、リンク編集中には物理的にコピーされないため、プロセスが実行されるまで実際には組み込まれません。この論理的な組み込みは、リンク編集プロセスにとって共有オブジェクト内に定義された「すべての」シンボルエントリが利用可能になることを意味しています。
リンカーは、共有オブジェクトの「プログラムデータ」セクションと、ほとんどの「リンク編集情報」セクションを使用しません。そのため、これらのセクションは、共有オブジェクトが結合された実行可能プロセスに生成されるときに、実行時リンカーによって変換されます。ただし、共有オブジェクトのエントリが記憶され、情報は出力ファイルイメージ内に格納されて、このオブジェクトには依存関係があり、実行時に使用可能にする必要があるかどうかが指示されます。
デフォルトでは、リンク編集の一部として指定された共有オブジェクトはすべて、作成されるオブジェクト内に依存関係として記録されます。この記録は、そのオブジェクトが、共有オブジェクトによって提供された実際のリファレンスシンボルを生成するかどうかに関係なく実行されます。実行時リンクのオーバヘッドを最小限にするには、作成されたオブジェクトからシンボルリファレンスを解析するために必要な依存関係だけを、リンク編集の一部として指定します。または、リンカーの -z ignore オプションを使用すると、使用しない共有オブジェクトの依存関係の記録を抑制できます。
共有オブジェクトに、他の共有オブジェクトに対する依存関係がある場合、この依存関係も処理されます。この処理は、すべてのコマンド行入力ファイルの処理が終了したあとで実行されます。このような共有オブジェクトは、シンボル解析処理を完了させるために使用されますが、生成される出力ファイルイメージ内に、その名前は依存関係として記録されません。
リンク編集コマンド行上の共有オブジェクトの位置は、アーカイブ処理のための位置に比べるとそれほど重要ではありませんが、大域な効力を持たせることができます。複数のシンボルに同じ名前を付けると、再配置可能オブジェクトと共有オブジェクト間や複数の共有オブジェクト間に出現させることができます (詳細については、「シンボル解析」を参照してください)。
リンカーによって処理される共有オブジェクトの順序は、出力ファイルイメージ内に格納された従属情報に保持されます。実行時リンカーがこの情報を読み取るため、指定された共有オブジェクトは同じ順序で読み込まれます。そのため、リンカーと実行時リンカーは、多重に定義された一連のシンボルのうち、1 つのシンボルの最初のエントリを選択します。
複数のシンボル定義と、他のシンボル用に 1 つのシンボル定義の挿入を説明した情報は、-m オプションを使用して生成されたロードマップ出力内に報告されます。
通常、コンパイラドライバによって、適切なライブラリがリンカーに指定されているかどうかが確認されますが、ほとんどの場合、自分独自のライブラリを指定することが必要です。共有オブジェクトとアーカイブは、リンカーに必要な入力ファイルに明示的に命名することによって指定できますが、より一般的で柔軟性のある方法として、リンカーの -l オプションを使用する方法があります。
規則により、通常、共有オブジェクトは接頭辞 lib と接尾辞 .so で指定され、アーカイブは接頭辞 lib と接尾辞 .a で指定されます。たとえば、libc.so とは、コンパイル環境で使用可能になった標準 C ライブラリの共有オブジェクトバージョンで、libc.a とは、そのアーカイブバージョンです。
これらの規則は、リンカーの -l オプションによって認識されます。このオプションは、通常、追加ライブラリをリンク編集に供給する場合に使用します。次の例では、
$ cc -o prog file1.c file2.c -lfoo |
リンカーに libfoo.so を検索するように指示し、これが検出できない場合には、libfoo.a を検索してから次の検索ディレクトリに移動するように指示しています。
命名規約には、共有オブジェクトの「コンパイル」環境での使用に関するものと、共有オブジェクトの実行時環境での使用に関するものがあります。コンパイル環境では、単に .so 接尾辞を使用するのに対し、実行時環境では、通常、追加のバージョン番号を指定した接尾辞を使用します。詳細については、「命名規約」および 「バージョンアップファイル名の管理」を参照してください。
動的モードでリンク編集を行う場合、共有オブジェクトとアーカイブとを組み合わせたものへのリンクを選択できます。静的モードでリンク編集を行う場合、入力を受け入れるのはアーカイブライブラリだけです。
動的モードで、ライブラリの検索を可能にする -l オプションを使用すると、リンカーは、まず、指定されたディレクトリ内で、指定された名前と一致する共有オブジェクトを検索します。一致するものが見つからない場合、リンカーは、次に同じディレクトリ内でアーカイブライブラリを検索します。静的モードで -l オプションを使用する場合は、アーカイブライブラリだけが検索されます。
動的モードでのライブラリ検索メカニズムでは、指定されたディレクトリを調べて共有オブジェクトを検索し、次にアーカイブライブラリを検索しますが、-B オプションを使用すると必要な検索タイプのより適切なコントロールを保存できます。
コマンド行上に -Bdynamic と -Bstatic オプションを必要な回数だけ指定することによって、ライブラリ検索は共有オブジェクト またはアーカイブをそれぞれ切り替えることができます。たとえば、アーカイブ libfoo.a と共有オブジェクト libbar.so とリンクするには、次のコマンドを発効します。
$ cc -o prog main.o file1.c -Bstatic -lfoo -Bdynamic -lbar |
キーワード -Bstatic と -Bdynamic は、正確には対称ではありません。 -Bstatic を指定すると、リンカーは、次の -Bdynamic の発生まで入力として共有オブジェクトを受け入れませんが、-Bdynamic を指定すると、リンカーは、指定されたディレクトリ内で、まず最初に共有オブジェクトを検索し、次にアーカイブを検索します。
上記の例をより正確に説明すると、リンカーは、最初に libfoo.a を検索し、次に libbar.so を検索します。そしてこれに失敗すると libbar.a を検索します。最後に、libc.so を検索し、これに失敗すると libc.a を検索します。
コマンド行上のアーカイブの位置は、作成される出力ファイルに影響を及ぼします。リンカーがアーカイブを検索する唯一の目的は、以前に参照したことのある定義されていない仮の外部リファレンスを解析することです。この検索が完了し、必要な再配置可能オブジェクトが抽出されると、このアーカイブは、コマンド行上のアーカイブに続く入力ファイルから入手した新しいシンボルの解析には使用できません。たとえば、次のコマンドでは、
$ cc -o prog file1.c -Bstatic -lfoo file2.c file3.c -Bdynamic |
file1.c から入手したことのあるシンボルリファレンスを解析するためだけに、libfoo.a を検索するように、リンカーに指示しています。libfoo.a は、file2.c または file3.c のシンボルリファレンスを解析するためには、使用できません。
原則として、コマンド行の最後にアーカイブを指定するのが最善の方法です。ただし、最後にアーカイブを配置するには、複数の定義を重複させる必要がある場合は除きます。
ここまでの例はすべて、リンカーが、コマンド行上にリストされたライブラリを検索する場所を認識していることを前提としていました。デフォルトでは、リンカーがライブラリを検索するディレクトリとして認識しているのは、2 つの標準的な場所で、32 ビットのリンク編集では /usr/ccs/lib と /usr/lib、または 64 ビット SPARCV9 リンク編集では /usr/lib/sparcv9 です。これ以外のディレクトリを検索させたい場合には、これをリンカーの検索パスに明示的に付加する必要があります。
リンカー検索パスを変更するには、コマンド行オプションを使用するか、または環境変数を使用する、2 種類の方法があります。
-L オプションを使用すると、ライブラリ検索に新しいパス名を追加できます。このオプションは、コマンド行上で遭遇したその地点で、検索パスに影響を与えます。たとえば、次のコマンドは、
$ cc -o prog main.o -Lpath1 file1.c -lfoo file2.c -Lpath2 -lbar |
path1 (次に /usr/ccs/lib と /usr/lib) を検索し、libfoo を検出しますが、libbar を検出する場合は、path1 と path2 (次に /usr/ccs/lib と /usr/lib) を検索します。
-L オプションを使用して定義されたパス名は、リンカー専用で、実行時リンカーが使用するために作成される出力ファイルイメージ内には記録されません。
カレントディレクトリ内のライブラリの検索にリンカーを使用する場合は、-L を指定する必要があります。ピリオド (.) を使用して、カレントディレクトリを示すことができます。
-Y オプションを使用すると、リンカーが検索するデフォルトのディレクトリを変更できます。このオプションに指定する引数は、ディレクトリのリストをコロンで区切った書式で示します。たとえば、次のコマンドは、
$ cc -o prog main.c -YP,/opt/COMPILER/lib:/home/me/lib -lfoo |
ディレクトリ /opt/COMPILER/lib と /home/me/lib 内だけを調べて libfoo を検索します。-Y オプションを使用して指定したディレクトリは、-L オプションを使用して補足できます。
コロンで区切られたディレクトリリストをとる環境変数 LD_LIBRARY_PATH
を使用しても、リンカーのライブラリ検索パスを付加できます。この最も一般的な書式 LD_LIBRARY_PATH
では、セミコロンで区切られた 2 つのディレクトリリストをとります。最初のリストは、コマンド行上に指定されたリストよりも前に検索され、2 番めのリストはコマンド行上のリストよりもあとに検索されます。 64 ビットオブジェクトを処理ときには、アクティブな LD_LIBRARY_PATH
設定を上書きする LD_LIBRARY_PATH_64
環境変数もあります。
ここでは、LD_LIBRARY_PATH
の設定と、いくつかの -Lオプションを指定したリンカーの呼出しを組み合わせています。
$ LD_LIBRARY_PATH=dir1:dir2;dir3 $ export LD_LIBRARY_PATH $ cc -o prog main.c -Lpath1 ... -Lpath2 ... -Lpathn -lfoo |
有効な検索パスは、
dir1:dir2:path1:path2... pathn:dir3:/usr/ccs/lib:/usr/lib
です。
LD_LIBRARY_PATH
定義の一部にセミコロンが指定されてない場合は、指定されたディレクトリリストは、-L オプションのあとで解釈されます。次に例を示します。
$ LD_LIBRARY_PATH=dir1:dir2 $ export LD_LIBRARY_PATH $ cc -o prog main.c -Lpath1 ... -Lpath2 ... -Lpathn -lfoo |
ここでは、有効な検索パスは次のとおりです。
path1:path2... pathn:dir1:dir2:/usr/ccs/lib:/usr/lib
この環境変数は、実行時リンカーの検索パスを拡張する場合にも使用できます (詳細については、「実行時リンカーによって検索されるディレクトリ」 を参照してください)。この環境変数がリンカーに影響しないようにするには、-i オプションを使用します。
実行時リンカーが、/usr/lib、または /usr/lib/sparcv9 (64 ビット SPARCV9 ライブラリ) ライブラリを検出する場所として認識する標準的な場所は 1 つだけです。この他のディレクトリを検索する場合は、実行時リンカーの検索パスに明示的に追加する必要があります。
動的な実行可能プログラムまたは共有オブジェクトは、付加された共有オブジェクトとリンクされ、これらの共有オブジェクトは、実行時リンカーによるプロセスの実行中に再び配置される必要がある従属物として記録されます。リンク編集中には、1 つまたは複数のパス名を出力ファイル内に記録できます。これらのパス名は、実行時リンカーが共有オブジェクトの従属物を検索する場合に使用されます。この記録されたパス名は、「実行パス」と呼ばれます。
実行時リンカーのライブラリ検索パスをどのように修正したとしても、その最後の構成要素は、必ず /usr/lib になります。
コロンで区切られたディレクトリリストをともなう、-R オプションを使用すると、動的実行可能プログラムまたは共有ライブラリ内に実行パスを記録できます。次に例を示します。
$ cc -o prog main.c -R/home/me/lib:/home/you/lib -Lpath1 ¥ -Lpath2 file1.c file2.c -lfoo -lbar |
上記の例では、動的実行可能プログラム prog 内に、実行パス /home/me/lib:/home/you/lib が記録されます。実行時リンカーは、共有オブジェクトの依存関係を配置する場合に、これらのパスを使用してから、デフォルトのロケーション /usr/lib を使用します。この場合、この実行パスは、libfoo.so.1 と libbar.so.1 の配置に使用されます。
リンカーは、複数の -R オプションを受け取り、コロンで区切られたこれらの指定内容をそれぞれ結合します。そのため、上記の例は、次のようにも示すこともできます。
$ cc -o prog main.c -R/home/me/lib -Lpath1 ¥ -R/home/you/lib -Lpath2 file1.c file2.c -lfoo -lbar |
-R オプションを指定することによる履歴的に代替とは、環境変数 LD_RUN_PATH
を設定して、リンカーがこれを使用できるようにすることです。LD_RUN_PATH
および -R の適用範囲と機能は全く同じですが、この両方を指定した場合は、-R によって LD_RUN_PATH
は上書きされます。
.init と .fini セクションタイプを使用すると、実行時の初期設定および終了処理が行えます。これらのセクションタイプは、他のセクションと同様に入力再配置可能オブジェクトから結合されます。ただし、コンパイラドライバからも、入力ファイルリストの冒頭部分と末尾に付加する追加ファイルの一部として、.init と .fini を指定できます。
これらのファイルには、.init および .fini コードを、それぞれ予約シンボル名 _init と _fini のにより識別される個別の機能にカプセル化する効果があります。
動的実行可能プログラムまたは共有オブジェクトを構築すると、リンカーにより出力ファイルのイメージ内のこれらのシンボルアドレスが記録されます。そのため、初期設定および終了処理中は、動的実行可能プログラムまたは共有オブジェクトは実行時リンカーによって呼び出すことができます。これらのセクションの実行時処理の詳細については、「初期設定および終了ルーチン」を参照してください。
.init と .fini セクションの作成は、アセンブラを使用して直接実行できます。あるいは、コンパイラの中にはその宣言を単純化するための特別なプリミティブを提供しているものもあります。たとえば、次のコードセグメントの結果、関数 foo に対する「呼び出し」が .init セクション内に配置され、関数 bar に対する「呼び出し」が .fini セクション内に配置されます。
#pragma init (foo) #pragma fini (bar) foo() { /* Perform some initialization processing. */ ...... } bar() { /* Perform some termination processing. */ ....... } |
共有オブジェクトとアーカイブライブラリの両方に組み込むことができる、初期設定コードと終了コードの設計をするときには注意が必要です。このコードが、アーカイブライブラリ内のいくつかの再配置可能オブジェクト全体に分散された場合、このアーカイブを使用したアプリケーションのリンク編集は、モジュールの一部しか抽出できないため、初期設定コードと終了コードの一部だけしか抽出できません。そして実行時に、コードのこの部分だけが実行されます。
共有オブジェクトがアプリケーションの依存関係の一つとして対応付けされている場合は、共有オブジェクトに対して構築された同じアプリケーションには、実行時に実行される、累積した初期設定コードと終了コードがすべて入っています。
また、.init コードが、dldump(3X) を使用してメモリーをダンプできる動的オブジェクトとともに組み込まれている場合は、データの初期設定は独立させることをお勧めします。