ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
リンカーとライブラリ Oracle Solaris 11 Information Library (日本語) |
入力ファイルの処理とシンボル解決がすべて重大なエラーが発生することもなく完了すると、リンカーは出力ファイルを生成します。リンカーは、出力ファイルの完成に必要な追加セクションをまず生成します。これらのセクションには、すべての入力ファイルから解決済みの大域およびウィークシンボル情報とともに局所シンボル定義を含むシンボルテーブルが含まれます。
また、実行時リンカーが必要とする、出力の再配置および動的情報セクションも組み込まれます。すべての出力セクション情報が設定された後、出力ファイルサイズの合計が計算されます。次に出力ファイルイメージが適宜作成されます。
動的実行可能プログラムまたは共有オブジェクトを作成するときに、通常、2 つのシンボルテーブルが生成されます。.dynsym とその関連文字列テーブル .dynstr には、レジスタ、大域シンボル、ウィークシンボル、およびセクションシンボルが組み込まれます。これらのセクションは、実行時処理イメージの一部としてマッピングされる text セグメントの一部となります。mmapobj(2) を参照してください。このマッピングにより、実行時リンカーは、これらのセクションを読み取り、必要な再配置を実行できます。
.symtab テーブルと、その関連文字列テーブル .strtab には、入力ファイル処理から収集されたすべてのシンボルが含まれています。これらのセクションは、プロセスイメージの一部として対応付けられません。これらのセクションは、リンカーの -z strip-class オプションを使用して、またはリンク編集後に strip(1) を使用してイメージから取り除くことができます。
予約シンボルは、シンボルテーブルの生成中に作成されます。これらのシンボルは、リンクプロセスに対して特別な意味を持っています。コードでは、これらのシンボルを定義しないでください。
_end と同じ。このシンボルは、_START_ シンボルとともに、ローカル範囲を持ち、オブジェクトのアドレス範囲を確立する簡単な手段を提供します。
リンカーが提供するアドレステーブル ( .got セクション) への位置独立の参照。このテーブルは、-K pic オプションを指定してコンパイルしたオブジェクトで発生する、位置独立のデータ参照から構築されます。「位置独立のコード」を参照してください。
リンカーが提供するアドレステーブル (.plt セクション) への、位置独立の参照。このテーブルは、-K pic オプションを指定してコンパイルしたオブジェクトで発生する、位置独立の関数参照から構築されます。「位置独立のコード」を参照してください。
テキストセグメント内の最初の位置。このシンボルは、_END_ シンボルとともに、ローカル範囲を持ち、オブジェクトのアドレス範囲を確立する簡単な手段を提供します。
リンカーは、実行可能ファイルを生成する場合、追加シンボルを検出して実行可能ファイルのエントリポイントを定義します。シンボルがリンカーの -e オプションを使用して指定された場合、そのシンボルが使用されます。それ以外の場合は、リンカーは予約シンボル名 _start と main を検出します。
機能によって、コードを実行するために必要なシステム属性が決まります。使用できる機能は、優先順に次のとおりです。
これらの各機能は個々に定義したり、組み合わせて機能グループを作成したりできます。
ある機能が利用可能な場合にだけ実行できるコードは、関連する ELF オブジェクト内の機能セクションで、これらの要件を指定する必要があります。オブジェクト内に機能要件を記録すると、関連コードの実行を試みる前に、システムがそのオブジェクトを検証できます。これらの要件によって、オブジェクトファミリの中からもっとも適したオブジェクトをシステムが選択できるフレームワークを規定することもできます。ファミリは同じオブジェクトの派生から構成されます。ただし、各派生は異なる機能を要求します。
動的オブジェクトに加えて、オブジェクト内の各関数または初期化済みデータ項目を、機能要件に関連付けることができます。理想的には、機能要件はコンパイラによって作成される再配置可能オブジェクトに記録され、コンパイル時に指定されたオプションまたは最適化を反映します。リンカーは入力再配置可能オブジェクトの機能を組み合わせて、出力ファイルの最終機能セクションを作成します。「機能セクション」を参照してください。
さらに、リンカーが出力ファイルを作成するときにも機能を定義できます。これらの機能は、mapfile とリンカーの -M オプションを使用して特定します。mapfile を使用して定義された機能は、入力再配置可能オブジェクト内で指定された機能を強化したり、無効にしたりすることができます。通常 mapfile は、必要な機能情報を生成しないコンパイラを補強するために使用されます。
システム機能は、実行中のシステムを記述する機能です。プラットフォーム名およびマシンハードウェア名は、それぞれ -i オプションと -m オプションを使用すると、uname(1) で表示できます。システムのハードウェア機能は、-v オプションを使用して isainfo(1) で表示できます。実行時、オブジェクトの機能要件はシステムの機能と比較され、オブジェクトを読み込むことができるかどうか、またはオブジェクト内のシンボルを利用できるかどうかを判断します。
オブジェクト機能は、オブジェクトに関連する機能です。これらの機能はオブジェクト全体の要件を定義して、実行時にオブジェクトを読み込むことができるかどうかを制御します。あるオブジェクトが、システムで満たすことができない機能を要求している場合、そのオブジェクトは実行時に読み込めません。機能を使用すると任意のオブジェクトから複数のインスタンスを作成でき、各インスタンスはオブジェクト要件に一致するシステムに最適化されます。実行時リンカーは、オブジェクトの機能要件をシステムが提供する機能と比較することで、このようなオブジェクトインスタンスファミリの中から最適なインスタンスを透過的に選択します。
シンボル機能は、オブジェクト内の各関数または初期化されたデータ項目に関連する機能です。これらの機能は、オブジェクト内の 1 つまたは複数のシンボルの要件を定義し、実行時にそのシンボルを使用できるかどうかを制御します。シンボル機能によって、1 つの関数の複数のインスタンスが 1 つのオブジェクト内に存在できます。関数の各インスタンスは、別々の機能を持つシステムに対して最適化できます。また、1 つの初期化されたデータ項目の複数のインスタンスも 1 つのオブジェクト内に存在できます。データの各インスタンスはシステム固有のデータを定義できます。シンボルのインスタンスが、システムによって満たすことができない機能を要求している場合、そのシンボルインスタンスは実行時に使用できません。代わりに、同じシンボル名の代替インスタンスを使用する必要があります。シンボル機能では、単一のオブジェクトをさまざまな機能のシステムで使用できるようにオブジェクトを構築できます。機能ファミリは、機能に対応できるシステム向けの最適化されたインスタンスと、機能が比較的低いほかのシステム向けのより汎用的なインスタンスを提供できます。初期化されたデータ項目のファミリはシステム固有データを提供できます。実行時リンカーは、シンボルの機能要件をシステムが提供する機能と比較することで、このようなシンボルインスタンスファミリの中から最適なインスタンスを透過的に選択します。
オブジェクトとシンボルの機能は、現在動作中のシステムに対して最適なオブジェクト、およびオブジェクト内の最適なシンボルを選択できます。オブジェクトとシンボルの機能はオプションであり、互いに独立しています。ただし、シンボル機能を定義しているオブジェクトがオブジェクト機能を定義してもかまいません。この場合、機能シンボルファミリには、オブジェクト機能を満たすシンボルの 1 つのインスタンスが伴われることになります。オブジェクトの機能が存在しない場合、機能シンボルファミリには、機能を要求しないシンボルの 1 つのインスタンスが伴われることになります。指定されたシステムに適合できる機能インスタンスがない場合、このシンボルインスタンスによってデフォルト実装が提供されます。
次の x86 の例は、foo.o のオブジェクト機能を示します。これらの機能はオブジェクト全体に適用されます。この例には、シンボル機能がありません。
$ elfdump -H foo.o Capabilities Section: .SUNW_cap Object Capabilities: index tag value [0] CA_SUNW_HW_1 0x840 [ SSE MMX ]
次の x86 の例は、bar.o のシンボル機能を示します。これらの機能は個々の関数 foo および bar に適用されます。各シンボルのインスタンスは 2 つ存在し、それぞれのインスタンスは別々の機能セットに割り当てられています。この例には、オブジェクト機能がありません。
$ elfdump -H bar.o Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_HW_1 0x40 [ MMX ] Symbols: index value size type bind oth ver shndx name [25] 0x00000000 0x00000021 FUNC LOCL D 0 .text foo%mmx [26] 0x00000024 0x0000001e FUNC LOCL D 0 .text bar%mmx Symbol Capabilities: index tag value [3] CA_SUNW_HW_1 0x800 [ SSE ] Symbols: index value size type bind oth ver shndx name [33] 0x00000044 0x00000021 FUNC LOCL D 0 .text foo%sse [34] 0x00000068 0x0000001e FUNC LOCL D 0 .text bar%sse
注 - この例では、機能シンボルは、機能識別子を汎用のシンボル名に追加する命名規則に従っています。リンカーはこの規則に従ってオブジェクト機能をシンボル機能に変換できます。あとで説明する「オブジェクト機能のシンボル機能への変換」を参照してください。
機能定義には、オブジェクトの要件、またはオブジェクト内の各シンボルの要件を特定できる多くの組み合わせが用意されています。ハードウェア機能には、非常に高い柔軟性が備わっています。ハードウェア機能でハードウェア要件を定義するときは、特定のマシンハードウェア名またはプラットフォーム名を指定しません。しかし、ベースとなるシステムの属性の中には、マシンのハードウェア名またはプラットフォーム名からしか判断できないものもあります。機能名を指定すると、特定のシステム機能向けにコーディングできますが、指定したオブジェクトの利用が制限される可能性があります。あるオブジェクトに対して新しいマシンハードウェア名またはプラットフォーム名が利用できるようになったときに、その新しい機能名を特定するために、オブジェクトの再作成が必要になります。
次の節では、機能の定義方法とリンカーによる使用方法について説明します。
オブジェクトのプラットフォーム機能では、オブジェクトまたはオブジェクト内の特定のシンボルが実行できるシステムのプラットフォーム名を指定します。複数のプラットフォーム機能を定義できます。これはもっとも具体的であり、ほかの機能タイプに優先します。
システムのプラットフォーム名は、ユーティリティー uname(1) に -i オプションを付けて実行すると表示できます。
プラットフォーム機能要件は次の mapfile 構文を使用すると定義できます。
$mapfile_version 2 CAPABILITY { PLATFORM = platform_name...; PLATFORM += platform_name...; PLATFORM -= platform_name...; };
PLATFORM 属性は 1 つまたは複数のプラットフォーム名で修飾されます。「+=」形式の代入を使用すると、入力オブジェクトで指定されたプラットフォーム機能に追加できます。「=」形式は上書きします。「-=」形式の代入を使用すると、出力オブジェクトからプラットフォーム機能を削除できます。次の SPARC の例では、オブジェクト foo.so.1 を SUNW,SPARC-Enterprise プラットフォームに固有であることを指定します。
$ cat mapfile $mapfile_version 2 CAPABILITY { PLATFORM = 'SUNW,SPARC-Enterprise'; }; $ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc $ elfdump -H foo.so.1 Capabilities Section: .SUNW_cap Object Capabilities: index tag value [0] CA_SUNW_PLAT SUNW,SPARC-Enterprise
再配置可能オブジェクトはプラットフォーム機能を定義できます。これらの機能がまとめられて、作成中のオブジェクトの最終的な機能要件が定義されます。
オブジェクトのプラットフォーム機能は、「=」形式の代入を使用すると mapfile から明示的に制御でき、入力の再配置可能オブジェクトから指定される可能性のあるプラットフォーム機能を上書きできます。「=」形式の代入を使用して空の PLATFORM 属性を指定すると、作成中のオブジェクトからプラットフォームのすべての機能要件が削除されます。
動的オブジェクトで定義されたプラットフォーム機能要件は、システムのプラットフォーム名に照らして、実行時リンカーによって検証されます。オブジェクトが使用されるのは、オブジェクトに記録されたプラットフォーム名の 1 つがシステムのプラットフォーム名に一致した場合だけです。
コードを特定のプラットフォーム用に限定することは、場合によって有効となります。しかし、ハードウェア機能ファミリを作成する方が柔軟性を高めることができるため、この方法を推奨します。ハードウェア機能ファミリは、コードを幅広いシステムで実行できるように最適化できます。
オブジェクトのマシン機能では、オブジェクトまたはオブジェクト内の特定のシンボルが実行できるシステムのマシンハードウェア名を指定します。複数のマシン機能を定義できます。この指定はプラットフォーム機能定義に比べて優先度が低いですが、ほかの機能タイプより優先します。
システムのマシンハードウェア名は、ユーティリティー uname(1) に -m オプションを付けて実行すると表示できます。
マシン機能要件は次の mapfile 構文を使用すると定義できます。
$mapfile_version 2 CAPABILITY { MACHINE = machine_name...; MACHINE += machine_name...; MACHINE -= machine_name...; };
MACHINE 属性は 1 つまたは複数のマシンハードウェア名で修飾されます。「+=」形式の代入を使用すると、入力オブジェクトで指定されたマシン機能に追加できます。「=」形式は上書きします。「-=」形式の代入を使用すると、出力オブジェクトからマシン機能を削除できます。次の SPARC の例では、オブジェクト foo.so.1 を sun4u マシンハードウェア名に固有であることを指定します。
$ cat mapfile $mapfile_version 2 CAPABILITY { MACHINE = sun4u; }; $ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc $ elfdump -H foo.so.1 Capabilities Section: .SUNW_cap Object Capabilities: index tag value [0] CA_SUNW_MACH sun4u
再配置可能オブジェクトはマシン機能を定義できます。これらの機能がまとめられて、作成中のオブジェクトの最終的な機能要件が定義されます。
オブジェクトのマシン機能は、「=」形式の代入を使用すると mapfile から明示的に制御でき、入力の再配置可能オブジェクトから指定される可能性のあるマシン機能を上書きできます。「=」形式の代入を使用して空の MACHINE 属性を指定すると、作成中のオブジェクトからマシンのすべての機能要件が削除されます。
動的オブジェクトで定義されたマシン機能要件は、システムのマシンハードウェア名に照らして、実行時リンカーによって検証されます。オブジェクトが使用されるのは、オブジェクトに記録されたマシン名の 1 つがシステムのマシン名に一致した場合だけです。
コードを特定のマシン用に限定することは、場合によって有効となります。しかし、ハードウェア機能ファミリを作成する方が柔軟性を高めることができるため、この方法を推奨します。ハードウェア機能ファミリは、コードを幅広いシステムで実行できるように最適化できます。
オブジェクトのハードウェア機能は、オブジェクトまたは特定のシンボルを正しく実行するために必要なシステムのハードウェア要件を特定します。この要件の例としては、一部の x86 アーキテクチャーで利用できる MMX または SSE の機能を必要とするコードの特定があります。
ハードウェア機能要件は、次の mapfile 構文を使用して特定できます。
$mapfile_version 2 CAPABILITY { HW = hwcap_flag...; HW += hwcap_flag...; HW -= hwcap_flag...; };
CAPABILITY 指令に対する HW 属性は、ハードウェア機能のシンボル表現である 1 つまたは複数のトークンで修飾されます。「+=」形式の代入を使用すると、入力オブジェクトで指定されたハードウェア機能に追加できます。「=」形式は上書きします。「-=」形式の代入を使用すると、出力オブジェクトからハードウェア機能を削除できます。
SPARC システムでは、ハードウェア機能は sys/auxv_SPARC.h の AV_ の値として定義されます。x86 システムでは、ハードウェア機能は sys/auxv_386.h の AV_ の値として定義されます。
次の x86 の例では、オブジェクト foo.so.1 に必要なハードウェア機能として MMX と SSE が宣言されています。
$ egrep "MMX|SSE" /usr/include/sys/auxv_386.h #define AV_386_MMX 0x0040 #define AV_386_SSE 0x0800 $ cat mapfile $mapfile_version 2 CAPABILITY { HW += SSE MMX; }; $ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc $ elfdump -H foo.so.1 Capabilities Section: .SUNW_cap Object Capabilities: index tag value [0] CA_SUNW_HW_1 0x840 [ SSE MMX ]
再配置可能オブジェクトには、ハードウェア機能の値を含めることができます。リンカーは、複数の入力再配置可能オブジェクトからのハードウェア機能値を組み合わせます。この結果生じる CA_SUNW_HW_1 の値は、関連入力値のビット単位の OR となります。デフォルトでは、これらの値は、mapfile で指定されたハードウェア機能と組み合わせられます。
オブジェクトのハードウェア機能要件は、「=」形式の代入を使用すると mapfile から明示的に制御でき、入力の再配置可能オブジェクトから指定される可能性のあるハードウェア機能を上書きできます。「=」形式の代入を使用して空の HW 属性を指定すると、作成中のオブジェクトからハードウェアのすべての機能要件が削除されます。
次の例では、入力の再配置可能オブジェクト foo.o で定義されたハードウェア機能のデータが出力ファイル bar.o に含まれないように隠されています。
$ elfdump -H foo.o Capabilities Section: .SUNW_cap Object Capabilities: index tag value [0] CA_SUNW_HW_1 0x840 [ SSE MMX ] $ cat mapfile $mapfile_version 2 CAPABILITY { HW = ; }; $ ld -o bar.o -r -Mmapfile foo.o $ elfdump -H bar.o $
動的オブジェクトが定義したハードウェア機能要件は、システムが提供するハードウェア機能に照らして、実行時リンカーによって検証されます。ハードウェア機能要件の一部を満足できない場合、そのオブジェクトは実行時に読み込みされません。たとえば、SSE 機能がプロセスで利用できない場合、ldd(1) は次のエラーを示します。
$ ldd prog foo.so.1 => ./foo.so.1 - hardware capability unsupported: \ 0x800 [ SSE ] ....
別々のハードウェア機能を利用する動的オブジェクトの複数のバリエーションは、フィルタを使用することによって、柔軟な実行時環境を提供できます。「機能固有の共有オブジェクト」を参照してください。
ハードウェア機能は、1 つのオブジェクト内の個々の関数の機能を指定するためにも使用できます。この場合、実行時リンカーは現在のシステム機能に基づいて使用する最適な関数を選択できます。「シンボル機能関数ファミリの作成」を参照してください。
オブジェクトのソフトウェア機能は、プロセスのデバッグまたは監視にとって重要なことがあるソフトウェアの特徴を特定します。ソフトウェア機能はプロセスの実行にも影響を与えることができます。現在のところ、オブジェクトによるフレームポインタの使用、およびプロセスアドレス空間の制限に関連したソフトウェア機能のみが存在します。
オブジェクトは、フレームポインタ使用を認識することを示せます。この状態は、フレームポインタを使用中または未使用として宣言することで、修飾されます。
64 ビットのオブジェクトは、実行時に 32 ビットのアドレス空間内で実行しなければならないことを指定できます。
ソフトウェア機能フラグは、sys/elf.h で定義されています。
#define SF1_SUNW_FPKNWN 0x001 #define SF1_SUNW_FPUSED 0x002 #define SF1_SUNW_ADDR32 0x004
これらのソフトウェア機能要件は、次の mapfile 構文を使用して特定できます。
$mapfile_version 2 CAPABILITY { SF = sfcap_flags...; SF += sfcap_flags...; SF -= sfcap_flags...; };
CAPABILITY 指令に対する SF 属性は、トークン FPKNWN、 FPUSED、および ADDR32 のいずれにも割り当てることができます。
再配置可能オブジェクトには、ソフトウェア機能の値を含めることができます。リンカーは、複数の入力再配置可能オブジェクトからのソフトウェア機能値を組み合わせます。ソフトウェア機能は、mapfile も提供されます。デフォルトでは、mapfile のすべての値が、再配置可能オブジェクトで提供される値と組み合わせられます。
オブジェクトのソフトウェア機能要件は、「=」形式の代入を使用すると mapfile から明示的に制御でき、入力の再配置可能オブジェクトから指定される可能性のあるソフトウェア機能を上書きできます。「=」形式の代入を使用して空の HW 属性を指定すると、作成中のオブジェクトからソフトウェアのすべての機能要件が削除されます。
次の例では、入力の再配置可能オブジェクト foo.o で定義されたソフトウェア機能のデータが出力ファイル bar.o に含まれないように隠されています。
$ elfdump -H foo.o Object Capabilities: index tag value [0] CA_SUNW_SF_1 0x3 [ SF1_SUNW_FPKNWN SF1_SUNW_FPUSED ] $ cat mapfile $mapfile_version 2 CAPABILITY { SF = ; }; $ ld -o bar.o -r -Mmapfile foo.o $ elfdump -H bar.o $
2 つのフレームポインタ入力値からの CA_SUNW_SF_1 値は、次のように計算されます。
表 2-1 CA_SUNW_SF_1 フレームポインタフラグ組み合わせ状態テーブル
|
この計算は、再配置可能オブジェクト値と mapfile 値にそれぞれ適用されます。.SUNW_cap セクションが存在しない場合や、このセクションに CA_SUNW_SF_1 の値が含まれない場合、SF1_SUNW_FPKNW フラグも SF1_SUNW_FPUSED フラグも設定されていない場合は、オブジェクトのフレームポインタソフトウェア機能は不明になります。
SF1_SUNW_ADDR32 ソフトウェア機能フラグによって識別される 64 ビットオブジェクトは、32 ビットアドレス空間を必要とする最適化されたコードを含むことができます。この方法で識別される 64 ビットオブジェクトは、SF1_SUNW_ADDR32 フラグで識別されるかどうかにかかわらず、他の 64 ビットオブジェクトと相互運用できます。64 ビット入力再配置可能オブジェクト内で SF1_SUNW_ADDR32 フラグが現れると、リンカーによって作成される出力ファイル用に作成される CA_SUNW_SF_1 値に反映されます。
64 ビット実行可能ファイル内に SF1_SUNW_ADDR32 フラグが存在すると、関連付けられたプロセスは下位 32 ビットアドレス空間に確実に制限されます。この制限付きアドレス空間には、プロセススタックと、すべてのプロセス依存関係が含まれます。そのようなプロセスの内部では、すべてのオブジェクトは SF1_SUNW_ADDR32 フラグで識別されるかどうかにかかわらず、制限付き 32 ビットアドレス空間内に読み込まれます。
64 ビット共有オブジェクトは SF1_SUNW_ADDR32 フラグを含むことができます。ただし、制限付きアドレス空間要件は、SF1_SUNW_ADDR32 フラグを含む 64 ビット実行可能ファイルによってのみ設定できます。したがって、64 ビットの SF1_SUNW_ADDR32 共有オブジェクトは64 ビットの SF1_SUNW_ADDR32 実行可能ファイルに依存する必要があります。
制限付きでない 64 ビット実行可能ファイルを構築するとき、64 ビットの SF1_SUNW_ADDR32 共有オブジェクトがリンカーによって検出されると、警告が発生します。
$ cc -m64 -o main main.c -lfoo ld: warning: file libfoo.so: section .SUNW_cap: software capability ADDR32: \ requires executable be built with ADDR32 capability
制限付きでない 64 ビットの実行可能ファイルから作成されたプロセスによって 64 ビットの SF1_SUNW_ADDR32 共有オブジェクトが実行時に検出されると、致命的エラーが発生します。
$ ldd main libfoo.so => ./libfoo.so - software capability unsupported: \ 0x4 [ ADDR32 ] .... $ main ld.so.1: main: fatal: ./libfoo.so: software capability unsupported: 0x4 [ ADDR32 ]
実行可能ファイルには、mapfile を使用して SF1_SUNW_ADDR32 をシードできます。
$ cat mapfile $mapfile_version 2 CAPABILITY { SF += ADDR32; }; $ cc -m64 -o main main.c -Mmapfile -lfoo $ elfdump -H main Object Capabilities: index tag value [0] CA_SUNW_SF_1 0x4 [ SF1_SUNW_ADDR32 ]
開発者は、関数の複数のインスタンス (それぞれは特定の機能セット向けに最適化) を 1 つのオブジェクト内に用意したいと考えることがよくあります。インスタンスの選択と使用が消費者に対して透過的であることが望まれます。外部インタフェースとして、汎用的なフロントエンドの関数を作成できます。この汎用インスタンスと最適化されたインスタンスを 1 つのオブジェクトに結合できます。タスクを処理するために汎用インスタンスは、getisax(2) を使用してシステムの機能を判断してから、最適化された該当する関数インスタンスを呼び出します。このモデルは適切に機能しますが、汎用性に欠け、実行時のオーバーヘッドが発生します。
シンボル機能はこのようなオブジェクトを作成するために別の方法を提供します。この方法はより簡単で効率が良く、さらにフロントエンドのコードを書く必要がありません。1 つの関数の複数のインスタンスを作成できます。また、さまざまな機能に関連付けることができます。これらのインスタンスと、いずれのシステムにも適した関数のデフォルトのインスタンスは、1 つの動的オブジェクトに結合できます。このファミリのシンボルの中でもっとも適したメンバーが、シンボルの機能情報を使用して、実行時リンカーによって選択されます。
次の例では、x86 オブジェクト foobar.mmx.o および foobar.sse.o が同じ関数 foo() と bar() を保持しており、それぞれ MMX と SSE の命令を使用するようにコンパイルされました。
$ elfdump -H foobar.mmx.o Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_ID mmx [2] CA_SUNW_HW_1 0x40 [ MMX ] Symbols: index value size type bind oth ver shndx name [10] 0x00000000 0x00000021 FUNC LOCL D 0 .text foo%mmx [16] 0x00000024 0x0000001e FUNC LOCL D 0 .text bar%mmx $ elfdump -H foobar.sse.o Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_ID sse [2] CA_SUNW_HW_1 0x800 [ SSE ] Capabilities symbols: index value size type bind oth ver shndx name [16] 0x00000000 0x0000002f FUNC LOCL D 0 .text foo%sse [18] 0x00000048 0x00000030 FUNC LOCL D 0 .text bar%sse
これらの各オブジェクトは機能関数 foo%*() および bar%*() を指定する局所シンボルを含んでいます。また、各オブジェクトは関数 foo() および bar() への大域参照も定義します。foo() または bar() への内部参照はすべて、これらの大域参照を介して、外部インタフェースのように再配置されます。
これら 2 つのオブジェクトは、foo() および bar() のデフォルトのインスタンスと結合できるようになりました。これらのデフォルトインスタンスは大域参照を満たし、どのオブジェクト機能とも互換性を持つ実装を提供します。これらのデフォルトインスタンスは各機能ファミリの先頭になります。オブジェクトの機能が存在しない場合、このデフォルトインスタンスも機能を要求してはいけません。実質的に、foo() と bar() の 3 つのインスタンスが存在します。大域インスタンスはデフォルトを提供し、局所インスタンスは関連機能が使用できる場合に、実行時に使用される実装を提供します。
$ cc -o libfoobar.so.1 -G foobar.o foobar.sse.o foobar.mmx.o $ elfdump -sN.dynsym libfoobar.so.1 | egrep "foo|bar" [2] 0x00000700 0x00000021 FUNC LOCL D 0 .text foo%mmx [4] 0x00000750 0x0000002f FUNC LOCL D 0 .text foo%sse [8] 0x00000784 0x0000001e FUNC LOCL D 0 .text bar%mmx [9] 0x000007b0 0x00000030 FUNC LOCL D 0 .text bar%sse [15] 0x000007a0 0x00000014 FUNC GLOB D 1 .text foo [17] 0x000007c0 0x00000014 FUNC GLOB D 1 .text bar
動的オブジェクトの機能情報には機能シンボルが表示され、利用できる機能ファミリがわかります。
$ elfdump -H libfoobar.so.1 Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_ID mmx [2] CA_SUNW_HW_1 0x40 [ MMX ] Symbols: index value size type bind oth ver shndx name [2] 0x00000700 0x00000021 FUNC LOCL D 0 .text foo%mmx [8] 0x00000784 0x0000001e FUNC LOCL D 0 .text bar%mmx Symbol Capabilities: index tag value [4] CA_SUNW_ID sse [5] CA_SUNW_HW_1 0x800 [ SSE ] Symbols: index value size type bind oth ver shndx name [4] 0x00000750 0x0000002f FUNC LOCL D 0 .text foo%sse [9] 0x000007b0 0x00000030 FUNC LOCL D 0 .text bar%sse Capabilities Chain Section: .SUNW_capchain Capabilities family: foo chainndx symndx name 1 [15] foo 2 [2] foo%mmx 3 [4] foo%sse Capabilities family: bar chainndx symndx name 5 [17] bar 6 [8] bar%mmx 7 [9] bar%sse
実行時、foo() と bar() へのすべての参照は、まず大域シンボルに結合されます。しかし、実行時リンカーはこれらの関数が機能ファミリの先頭のインスタンスであることを認識しています。実行時リンカーは各ファミリメンバーを検査して、より適した機能関数が使用できるかどうかを判断します。この処理には 1 回限りのコストがかかり、関数の最初の呼び出し時に発生します。foo() および bar() への次の呼び出しは、最初の呼び出しで選択された関数のインスタンスに直接結合されます。この関数の選択は実行時リンカーのデバッグ機能を使用すると確認できます。
次の例では、ベースとなるシステムが MMX または SSE をサポートしていません。foo() の先頭のインスタンスには特別な機能のサポートは必要ないため、どの再配置参照も満たします。
$ LD_DEBUG=symbols main .... debug: symbol=foo; lookup in file=./libfoo.so.1 [ ELF ] debug: symbol=foo[15]: capability family default debug: symbol=foo%mmx[2]: capability specific (CA_SUNW_HW_1): [ 0x40 [ MMX ] ] debug: symbol=foo%mmx[2]: capability rejected debug: symbol=foo%sse[4]: capability specific (CA_SUNW_HW_1): [ 0x800 [ SSE ] ] debug: symbol=foo%sse[4]: capability rejected debug: symbol=foo[15]: used
次の例では、MMX は使用できますが、SSE は使用できません。MMX が使用できる foo() のインスタンスは、どの再配置参照も満たします。
$ LD_DEBUG=symbols main .... debug: symbol=foo; lookup in file=./libfoo.so.1 [ ELF ] debug: symbol=foo[15]: capability family default debug: symbol=foo[2]: capability specific (CA_SUNW_HW_1): [ 0x40 [ MMX ] ] debug: symbol=foo[2]: capability candidate debug: symbol=foo[4]: capability specific (CA_SUNW_HW_1): [ 0x800 [ SSE ] ] debug: symbol=foo[4]: capability rejected debug: symbol=foo[2]: used
複数の機能インスタンスが同じシステムで実行できる場合、一連の優先規則を利用して 1 つのインスタンスを選択します。
プラットフォーム名を定義している機能グループは、プラットフォーム名を定義していないグループに優先します。
マシンのハードウェア名を定義している機能グループは、マシンのハードウェア名を定義していないグループに優先します。
ハードウェア機能の値は、大きい値が小さい値より優先します。
機能関数インスタンスのファミリはプロシージャーのリンクテーブルエントリからアクセスできる必要があります。「プロシージャーのリンクテーブル (プロセッサ固有)」を参照してください。このプロシージャーリンクの参照には、実行時リンカーが関数を解決する必要があります。このプロセス中、実行時リンカーは関連のシンボル機能情報を処理して、使用できる関数インスタンスファミリから最適な関数を選択できます。
シンボル機能が使用されないときに、リンカーがプロシージャーのリンクテーブルエントリを必要としないでコードへの参照を解決できる場合があります。たとえば、動的実行可能ファイル内では、実行可能ファイル内に存在する関数への参照が、リンク編集時に内部的に結合できます。共有オブジェクト内に隠されて保護されている関数も、リンク編集時に内部的に結合できます。この場合、一般的に、実行時リンカーはこれらの関数への参照の解決に関与する必要はありません。
しかし、シンボル機能が使用された場合、関数はプロシージャーのリンクテーブルエントリから解決される必要があります。このエントリが必要なのは、実行時リンカーが読み取り専用のテキストセグメントを維持しながら、適切な関数を選択することに関与するためです。このメカニズムでは、機能関数へのすべての呼び出しが、プロシージャーのリンクテーブルエントリを介した間接参照になります。この間接参照は、シンボル機能が使用されない場合は必要ない可能性があります。このため、機能関数を呼び出すコストと、機能関数を使用して得られるデフォルトに対する性能の改善との間に小さいトレードオフがあります。
注 - 機能関数はプロシージャーのリンクテーブルエントリを介してアクセスする必要がありますが、この関数は隠された、または保護されたものとして定義できます。実行時リンカーはこれらの可視性に従って、関数への結合を制限します。この動作により、シンボル機能が関数と関連付けられていないときに作成される結合と同じ結合になります。隠された関数は外部オブジェクトから結合できません。保護された関数へのオブジェクト内からの参照は、同じオブジェクト内でのみ結合されます。
初期化されたデータの複数のインスタンス (各インスタンスはシステムに固有) を同じオブジェクト内で提供できます。しかし、このようなデータは、関数インタフェースを介して提供する方法が簡単で、お勧めです。「シンボル機能関数ファミリの作成」を参照してください。実行可能ファイル内に初期化データの複数のインスタンスを提供するには、特別な配慮が必要です。
次の例では、foo.c 内のデータ項目 foo を初期化して、マシン名の文字列を指しています。このファイルはさまざまなマシン用にコンパイルでき、各インスタンスはマシン機能で特定されます。このデータ項目への参照は、ファイル bar.c の bar() から行われます。次に共有オブジェクト foobar.so.1 が、foo の 2 つの機能インスタンスと bar() を結合することで作成されます。
$ cat foo.c char *foo = MACHINE; $ cat bar.c #include <stdio.h> extern char *foo = MACHINE; void bar() { (void) printf("machine: %s\n", foo); } $ elfdump -H foobar.so.1 Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_ID sun4u [2] CA_SUNW_MACH sun4u Symbols: index value size type bind oth ver shndx name [1] 0x000108d4 0x00000004 OBJT LOCL D 0 .data foo%sun4u Symbol Capabilities: index tag value [4] CA_SUNW_ID sun4v [5] CA_SUNW_MACH sun4v Symbols: index value size type bind oth ver shndx name [2] 0x000108d8 0x00000004 OBJT LOCL D 0 .data foo%sun4v
アプリケーションは bar() を参照できます。実行時リンカーはベースとなるシステムに関連する foo のインスタンスに結合します。
$ uname -m sun4u $ main machine: sun4u
このコードが適切に動作するには、コードが位置独立になるようにコンパイルされる必要があります。共有可能なオブジェクト内のコードではこれは一般的です。「位置独立のコード」を参照してください。位置独立のデータ参照は間接参照であるため、実行時リンカーは必要な参照を検索して、データセグメントの要素を更新できます。データセグメントのこの再配置更新では、テキストセグメントが読み取り専用として保持されます。
しかし、実行可能ファイル内のコードは位置依存であることが一般的です。また、実行可能ファイル内のデータ参照はリンク編集時に結合されます。実行可能ファイル内では、実行時リンカーがシンボル機能ファミリから選択できるように、シンボル機能のデータ参照は、大域データ項目を介して解決されない状態のままである必要があります。前の例の bar.c にある bar() からの参照が位置依存コードとしてコンパイルされた場合、実行可能ファイルのテキストセグメントは実行時に再配置される必要があります。デフォルトでは、この状態は重大なリンク時エラーとなります。
$ cc -o main main.c bar.c foo.o foo.1.o foo.2.o ... warning: Text relocation remains referenced against symbol offset in file foo 0x0 bar.o foo 0x8 bar.o
このエラー状態を解決する 1 つの方法は、bar.c を位置独立としてコンパイルすることです。ただし、この方法を正常に動作させるには、シンボル機能データ項目への実行可能ファイル内からの参照をすべて位置独立でコンパイルする必要がある点に注意してください。
データはシンボル機能メカニズムを使用してアクセスできますが、データ項目をオブジェクトへの公開インタフェースの一部にすることは問題となる可能性があります。より柔軟な別のモデルは、シンボル機能関数に各データ項目をカプセル化することです。この関数が、データをアクセスするための唯一の手段を提供します。シンボル機能関数にデータを隠すことによって、データを静的に定義し、非公開のままに維持できるという重要なメリットが得られます。前の例は、シンボル機能関数を使用するようにコーディングできます。
$ cat foobar.c cat bar.c #include <stdio.h> static char *foo = MACHINE; void bar() { (void) printf("machine: %s\n", foo); } $ elfdump -H main Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_ID sun4u [2] CA_SUNW_MACH sun4u Symbols: index value size type bind oth ver shndx name [1] 0x0001111c 0x0000001c FUNC LOCL D 0 .text bar%sun4u Symbol Capabilities: index tag value [4] CA_SUNW_ID sun4v [5] CA_SUNW_MACH sun4v Symbols: index value size type bind oth ver shndx name [2] 0x00011138 0x0000001c FUNC LOCL D 0 .text bar%sun4v $ uname -m sun4u $ main machine: sun4u
理想的には、コンパイラはシンボル機能で特定されたオブジェクトを生成できます。コンパイラがシンボル機能を解決できない場合は、リンカーが解決します。
オブジェクト機能を定義している再配置可能オブジェクトは、リンカーを使用してシンボル機能を定義する再配置可能オブジェクトに変換できます。リンカーの -z symbolcap オプションを使用すると、機能データセクションがすべて変換されて、シンボル機能が定義されます。オブジェクト内のすべての大域関数は局所関数に変換され、シンボル機能に関連付けられます。初期化された大域データ項目は局所データ項目に変換され、シンボル機能に関連付けられます。変換されたこれらのシンボルには、オブジェクト機能グループの一部として指定された機能識別子が追加されます。機能識別子が定義されていない場合は、デフォルトのグループ名が追加されます。
オリジナルの大域関数または初期化されたデータ項目のそれぞれに対して、大域参照が作成されます。この参照は再配置要件に関連付けられており、動的実行可能ファイルまたは共有オブジェクトを作成するためにこのオブジェクトを最終的に結合するときに、デフォルトの大域シンボルに結合できます。
注 - -z symbolcap オプションは、オブジェクト機能セクションを含んでいるオブジェクトに適用されます。このオプションは、すでにシンボル機能を含んでいる再配置可能オブジェクト、またはオブジェクトおよびシンボルの機能を含む再配置可能オブジェクトに影響しません。この設計により、複数のオブジェクトがリンカーによって結合でき、オブジェクト機能を含むオブジェクトだけがこのオプションの影響を受けます。
次の例では、x86 再配置可能オブジェクトは 2 つの大域関数 foo() および bar() を含んでいます。このオブジェクトは MMX と SSE のハードウェア機能を要求するようにコンパイルされました。これらの例では、機能グループは機能識別子エントリを使用して名前が付けられました。この識別子名は変換されたシンボル名に追加されます。この明示的な識別子がないと、リンカーはデフォルトの機能グループ名を追加します。
$ elfdump -H foo.o Capabilities Section: .SUNW_cap Object Capabilities: index tag value [0] CA_SUNW_ID sse,mmx [1] CA_SUNW_HW_1 0x840 [ SSE MMX ] $ elfdump -s foo.o | egrep "foo|bar" [25] 0x00000000 0x00000021 FUNC GLOB D 0 .text foo [26] 0x00000024 0x0000001e FUNC GLOB D 0 .text bar $ elfdump -r foo.o | fgrep foo R_386_PLT32 0x38 .rel.text foo
これで、この再配置可能オブジェクトはシンボル機能の再配置可能オブジェクトに変換できます。
$ ld -r -o foo.1.o -z symbolcap foo.o $ elfdump -H foo.1.o Capabilities Section: .SUNW_cap Symbol Capabilities: index tag value [1] CA_SUNW_ID sse,mmx [2] CA_SUNW_HW_1 0x840 [ SSE MMX ] Symbols: index value size type bind oth ver shndx name [25] 0x00000000 0x00000021 FUNC LOCL D 0 .text foo%sse,mmx [26] 0x00000024 0x0000001e FUNC LOCL D 0 .text bar%sse,mmx $ elfdump -s foo.1.o | egrep "foo|bar" [25] 0x00000000 0x00000021 FUNC LOCL D 0 .text foo%sse,mmx [26] 0x00000024 0x0000001e FUNC LOCL D 0 .text bar%sse,mmx [37] 0x00000000 0x00000000 FUNC GLOB D 0 UNDEF foo [38] 0x00000000 0x00000000 FUNC GLOB D 0 UNDEF bar $ elfdump -r foo.1.o | fgrep foo R_386_PLT32 0x38 .rel.text foo
このオブジェクトは、同じ関数 (別のシンボル機能に関連) のインスタンスを含む別のオブジェクトと結合でき、実行可能ファイルまたは共有オブジェクトを作成できるようになりました。また、シンボル機能と関連付けられていない、各機能ファミリの先頭となる各関数のデフォルトインスタンスが提供される必要があります。このデフォルトのインスタンスはすべての外部参照に対応しており、どのシステムでも関数のインスタンスが確実に使用できるようになります。
実行時、foo() と bar() へのすべての参照は、先頭のインスタンスに向けられます。ただし、システムが適切な機能に対応している場合、実行時リンカーは最適なシンボル機能インスタンスを選択します。
通常、オブジェクトは特定のアーキテクチャーのすべてのシステム上で実行できるように設計され、構築されています。しかし、各システムが特別の機能を持っていると、多くの場合、最適化の対象となります。最適化されたコードは、前の節で説明したメカニズムを使用して、そのコードを実行するために必要とする機能で特定できます。
最適化されたインスタンスを実行したり、テストしたりするには、必要な機能を備えたシステムを使用する必要があります。システムごとに、実行時リンカーは使用可能な機能を判断してから、機能にもっとも対応できるインスタンスを選択します。テストと実験を支援するため、実行時リンカーに対してシステムに備わった機能ではなく機能の代替セットを使用するように指示できます。また、これらの代替機能に対して特定のファイルだけが検証されるようにも指定できます。
機能の代替セットはシステム機能から派生したもので、再度初期化したり、機能を追加または削除したりできます。
環境変数のファミリを使用すると、機能の代替セットを作成したり、その使用対象を設定したりできます。
代替プラットフォームの名前を識別します。
代替マシンハードウェアの名前を識別します。
代替ハードウェア機能の値を識別します。
代替ソフトウェア機能の値を識別します。
代替機能に対して検証すべきファイルを指定します。
機能環境変数 LD_PLATCAP および LD_MACHCAP は、プラットフォーム名とマシンのハードウェア名をそれぞれ定義する文字列を受け入れます。「プラットフォーム機能の特定」および 「マシン機能の指定」を参照してください。
機能環境変数 LD_HWCAP および LD_SFCAP は、機能のシンボル表現としてコンマ区切りのトークンリストを受け入れます。「ハードウェア機能の特定」および 「ソフトウェア機能の特定」を参照してください。トークンには数値も指定できます。
「+」接頭辞を付けると、次に続く機能が代替機能に追加されます。「-」接頭辞を付けると、次に続く機能が代替機能から削除されます。「+-」がないと、次に続く機能が代替機能と置き換わります。
機能を削除すると、エミュレートされる機能環境がより制限されます。一般的に、機能インスタンスのファミリが存在する場合、汎用的で機能に固有でないインスタンスも提供されます。このため、より制限された機能環境を使用すると、機能が少ない、または汎用のコードのインスタンスを使用するように強制できます。
機能を追加すると、エミュレートされる機能環境がより向上します。この環境は慎重に構築する必要がありますが、機能ファミリのフレームワークを実行するために使用できます。たとえば、mapfile を使用すると、期待される機能を定義する関数ファミリを作成できます。これらの関数は printf(3C) を使用すると、その実行を確認できます。関連オブジェクトの作成を検証したり、さまざまな機能を組み合わせて実行したりできるようになります。関数の実際の機能要件をコーディングする前に、このように機能ファミリをプロトタイピングすると有益です。しかし、ファミリインスタンス内のコードが適切に動作するために特定機能を必要とし、この機能がシステムに備わっていないが代替機能として設定されている場合、このコードインスタンスは正しく動作しません。
LD_CAP_FILES も使用しないで一連の代替機能を作成すると、プロセスのすべての機能固有オブジェクトが代替機能に対して検証されます。この方法も注意深く実行してください。多くのシステムオブジェクトは、正常に動作するために、システム機能を必要とするためです。機能を変更すると、システムオブジェクトが正常に動作しなくなる場合があります。
機能の実験を行うために最適な環境は、オブジェクトで使用するすべての機能を備えたシステムを使用することです。LD_CAP_FILES も使用して、実験を行うオブジェクトを分離してください。次に、「-」構文を使用して機能を無効にすると、ユーザーのさまざまな機能ファミリのインスタンスを実行できます。各インスタンスは、システムの本物の機能で完全にサポートされます。
たとえば、x86 の 2 つの機能オブジェクト libfoo.so および libbar.so があるとします。これらのオブジェクトには、SSE2 命令を使用するように最適化された機能関数、MMX 命令を使用するように最適化された関数、および機能を要求しない汎用の関数が含まれています。ベースとなるシステムには SSE2 と MMX が両方ともに備わっています。デフォルトでは、完全に最適化された SSE2 関数が使用されます。
LD_HWCAP 定義を使用することによって、SSE2 機能を削除して、MMX 命令用に最適化された関数を使用するように、libfoo.so および libbar.so を制限できます。LD_CAP_FILES を定義するもっとも柔軟な方法は、必要なファイルのベース名を使用することです。
$ LD_HWCAP=-sse2 LD_CAP_FILES=libfoo.so,libbar.so ./main
SSE2 および MMX の機能を削除して、汎用の関数だけを使用するように libfoo.so および libbar.so をさらに制限できます。
$ LD_HWCAP=-sse2,mmx LD_CAP_FILES=libfoo.so,libbar.so ./main
注 - アプリケーションで使用できる機能および設定されたすべての代替機能は、実行時リンカーの診断機能を使用すると確認できます。
$ LD_DEBUG=basic LD_HWCAP=-sse2,mmx,cx8 ./main .... 02328: hardware capabilities (CA_SUNW_HW_1) - 0x5c6f \ [ SSE3 SSE2 SSE FXSR MMX CMOV SEP CX8 TSC FPU ] 02328: alternative hardware capabilities (CA_SUNW_HW_1) - 0x4c2b \ [ SSE3 SSE FXSR CMOV SEP TSC FPU ] ....