ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
リンカーとライブラリ Oracle Solaris 10 8/11 Information Library (日本語) |
このセクションは、オブジェクトファイル情報と、プログラムの実行イメージを作成するシステム動作を記述します。ここで説明する情報の大半は、すべてのシステムに適用されます。プロセッサに固有の情報はその旨が示されたセクションに存在します。
実行可能オブジェクトファイルと共有オブジェクトファイルは、アプリケーションプログラムを静的に表現します。このようなプログラムを実行するためには、システムはこれらのファイルを使用して動的なプログラムの表現、すなわちプロセスイメージを作成します。プロセスイメージには、テキスト、データ、スタックなどがあるセグメントが存在します。次の主な細目があります。
「プログラムヘッダー」では、プログラム実行に直接関係するオブジェクトファイルの構造を記述します。重要なデータ構造体であるプログラムヘッダーテーブルは、ファイル内のセグメントイメージの位置を示します。また、このプログラムヘッダーテーブルは、プログラムのメモリーイメージの作成に必要なほかの情報が存在します。
「プログラムの読み込み (プロセッサ固有)」では、メモリーにプログラムを読み込むために使用する情報を記述します。
「実行時リンカー」では、プロセスイメージのオブジェクトファイル間でシンボル参照を指定、解決するために使用する情報を記述します。
実行可能オブジェクトファイルまたは共有オブジェクトファイルのプログラムヘッダーテーブルは、構造体の配列です。各構造体は、実行されるプログラムを準備するためにシステムが必要とするセグメントなどの情報を記述します。各オブジェクトファイルセグメントには、「セグメントの内容」で説明しているように、1 つ以上のセクションが存在します。
プログラムヘッダーは、実行可能オブジェクトファイルと共有オブジェクトファイルに対してのみ意味があります。プログラムヘッダーサイズは、ELF ヘッダーの e_phentsize メンバーと e_phnum メンバーで指定されます。
プログラムヘッダーの構造体は、次のとおりです。sys/elf.h を参照してください。
typedef struct { Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; } Elf32_Phdr; typedef struct { Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf64_Xword p_align; } Elf64_Phdr;
この配列要素が記述するセグメント型、または配列要素情報の解釈方法。型の値とその意味は、表 7-25 を参照してください。
ファイルの先頭から、セグメントの先頭バイトが存在する位置までのオフセット。
セグメントの物理アドレス (物理アドレス指定が適切なシステムの場合)。本システムはアプリケーションプログラムに対して物理アドレス指定を無視するので、このメンバーには実行可能ファイルと共有オブジェクトに対する指定されていない内容が存在します。
セグメントのファイルイメージのバイト数 (0 の場合もある)。
セグメントのメモリーイメージのバイト数 (0 の場合もある)。
セグメントに関係するフラグ。型の値とその意味は、表 7-26 を参照してください。
読み込み可能なプロセスセグメントは、ページサイズを基にして、p_vaddr と p_offset に対して同じ値を保持する必要があります。このメンバーは、セグメントがメモリーとファイルにおいて整列される値を与えます。値 0 と 1 は、整列が必要ないことを意味します。その他の値の場合、p_align は 2 の正整数累乗でなければならず、また p_vaddr は p_align を法として p_offset に等しくなければなりません。「プログラムの読み込み (プロセッサ固有)」を参照してください。
エントリの中には、プロセスセグメントを記述するものもあります。それ以外のエントリは補足情報を与え、プロセスイメージには関与しません。セグメントエントリが現れる順序は、明示されている場合を除き任意です。定義されている型の値を、次の表に示します。
表 7-25 ELF セグメント型
|
未使用メンバーの値は不定です。この型を使用すると、プログラムヘッダーテーブルに、無視されるエントリを入れることができます。
p_filesz と p_memsz により記述される読み込み可能セグメントを指定します。ファイルのバイト列は、メモリーセグメントの先頭に対応付けられます。セグメントのメモリーサイズ (p_memsz) がファイルサイズ (p_filesz) より大きい場合、不足するバイトは、値 0 を保持するように定義されます。これらのバイトはセグメントの初期化領域に続きます。ファイルサイズがメモリーサイズより大きくなることは許可されません。プログラムヘッダーテーブルの読み込み可能セグメントエントリは昇順に現れ、p_vaddr メンバーでソートされます。
動的リンクに関する情報を指定します。「動的セクション」を参照してください。
インタプリタとして呼び出される、ヌル文字で終了しているパス名の位置とサイズを指定します。動的実行可能ファイルの場合、この型は必須です。共有オブジェクトの場合は、この型を指定することができます。この型は、ファイル内で複数指定することはできません。この型が存在する場合、この型はすべての読み込み可能セグメントエントリの前に存在しなければなりません。詳細は、「プログラムインタプリタ」を参照してください。
補助情報の位置とサイズを指定します。詳細は、「注釈セクション」を参照してください。
このセグメント型は、予約済みですが、セマンティクスは定義されていません。
プログラムヘッダーテーブルの、ファイル、およびプログラムのメモリーイメージにおける位置とサイズを指定します。このセグメント型を、ファイル内に複数指定することはできません。また、このセグメント型は、プログラムヘッダーテーブルがプログラムのメモリーイメージの一部になる場合にかぎり指定できます。この型が存在する場合、この型はすべての読み込み可能セグメントエントリの前に存在しなければなりません。詳細は、「プログラムインタプリタ」を参照してください。
スレッド固有領域のテンプレートを指定します。詳細は、「スレッド固有領域 (TLS) セクション」を参照してください。
このセグメントは、スタック巻き戻し (unwind) テーブルを含んでいます。
このセグメントは、スタック巻き戻し (unwind) テーブルを含んでいます。PT_SUNW_EH_FRAME は PT_SUNW_EH_UNWIND に相当します。
PT_LOAD 要素と同じ属性で、.SUNW_bss セクションの記述に使用します。
プロセススタックを記述します。PT_SUNWSTACK 要素は 1 つのみ存在できます。p_flags フィールドで定義されたアクセス権のみが意味を持ちます。
dtrace(1M) の内部使用のため予約されています。
機能要件を指定します。詳細については、「機能セクション」を参照してください。
注 - ほかの箇所で特に要求されないかぎり、すべてのプログラムヘッダーセグメントタイプはそれぞれ存在することもありますし、存在しないこともあります。ファイルのプログラムヘッダーテーブルには、このプログラムの内容に関係する要素のみが存在できます。
実行可能オブジェクトファイルと共有オブジェクトファイルには、ベースアドレス (プログラムのオブジェクトファイルのメモリーイメージに関連付けられている最下位仮想アドレス) が存在します。ベースアドレスは、たとえば動的リンク時にプログラムのメモリーイメージを再配置するために使用されます。
実行可能オブジェクトファイルと共有オブジェクトファイルのベースアドレスは、実行時に 3 つの値 (プログラムの読み込み可能セグメントのメモリー読み込みアドレス、最大ページサイズ、最下位仮想アドレス) から計算されます。プログラムヘッダーの仮想アドレスは、プログラムのメモリーイメージの実際の仮想アドレスを表さないことがあります。「プログラムの読み込み (プロセッサ固有)」を参照してください。
ベースアドレスを計算するには、PT_LOAD セグメントの最下位 p_vaddr 値に関連付けられているメモリーアドレスを判定します。次に、メモリーアドレスを最大ページサイズの最近倍数に切り捨てることで、ベースアドレスが求められます。メモリーに読み込まれるファイルの型によって、メモリーアドレスは p_vaddr 値に一致しない場合もあります。
システムで読み込まれるプログラムには、少なくとも 1 つの読み込み可能セグメントが存在しなければなりません (ただし、この制限はファイル形式による要件ではありません)。システムは、読み込み可能セグメントのメモリーイメージを作成するとき、p_flags メンバーで指定されるアクセス権を与えます。PF_MASKPROC マスクのすべてのビットは、プロセッサ固有のセマンティクスのために予約されます。
表 7-26 ELF セグメントフラグ
|
アクセス権ビットが 0 の場合、そのビットのアクセスは拒否されます。実際のメモリーアクセス権は、メモリー管理ユニット (システムによって異なることがある) に依存します。すべてのフラグ組み合わせが有効ですが、システムは要求以上のアクセスを与えることがあります。ただしどんな場合も、特に断りが明示的に記述されていないかぎり、セグメントは書き込み権を持ちません。次の表に、正確なフラグ解釈と許容されるフラグ解釈を示します。
表 7-27 ELF セグメントへのアクセス権
|
たとえば、標準的なテキストセグメントは読み取り権と実行権を持っていますが、書き込み権は持っていません。データセグメントは通常、読み取り権、書き込み権、および実行権を持っています。
オブジェクトファイルセグメントは、1 つまたは複数のセクションで構成されます。ただし、プログラムヘッダーはこの事実には関与しません。ファイルセグメントに 1 つのセクションが存在するか複数のセクションが存在するかもまた、プログラム読み込み時に重要ではありません。しかし、さまざまなデータが、プログラム実行時や動的リンク時などには存在しなければなりません。次に、セグメントの内容を一般的な言葉で説明します。セグメント内のセクションの順序と帰属関係は、異なることがあります。
テキストセグメントには、読み取り専用の命令/データが存在します。データセグメントには、書き込み可能のデータ/命令が存在します。すべての特殊セクションの一覧については、表 7-10 を参照してください。
PT_DYNAMIC プログラムヘッダー要素は、.dynamic セクションを指し示します。さらに、.got セクションと .plt セクションには、位置独立のコードと動的リンクに関係する情報が存在します。
.plt は、テキストセグメントまたはデータセグメントに存在できます (どちらのセグメントに存在するかはプロセッサに依存します)。詳細は、「大域オフセットテーブル (プロセッサ固有)」と 「プロシージャーのリンクテーブル (プロセッサ固有)」を参照してください。
タイプ SHT_NOBITS のセクションは、ファイル内の領域を占有しませんが、セグメントのメモリーイメージには反映されます。通常、これらの初期化されていないデータはセグメントの終わりに存在し、その結果、関連付けられているプログラムヘッダー要素において p_memsz が p_filesz より大きくなります。
システムは、プロセスイメージを作成または拡張するとき、ファイルのセグメントを仮想メモリーセグメントに論理的にコピーします。システムがファイルをいつ物理的に読み取るかは、プログラムの挙動やシステムの負荷などに依存します。
プロセスは実行時に論理ページを参照しないかぎり物理ページを必要としません。プロセスは一般に多くのページを未参照状態のままにします。したがって、物理読み取りを遅延させると、システム性能を向上させることができます。この効率性を実現するには、実行可能ファイルと共有オブジェクトファイルには、ファイルオフセットと仮想アドレスがページサイズを法として同じであるセグメントイメージが存在する必要があります。
32 ビットのセグメントの仮想アドレスとファイルオフセットは、64K (0x10000) を法として同じです。64 ビットのセグメントの仮想アドレスとファイルオフセットは、1M バイト (0x100000) を法として同じです。セグメントを最大ページサイズに整列すると、ファイルは物理ページサイズには関係なくページング処理に対して適切になります。
デフォルトでは 64 ビット SPARC プログラムは開始アドレス (0x100000000) にリンクされます。プログラム全体は、テキスト、データ、ヒープ、スタック、および共用オブジェクト依存関係を含めて、4G バイトより上に存在します。そうすることにより、プログラムがポインタを切り捨てると、アドレス空間の最下位 4G バイトでフォルトが発生することになるので、64 ビットプログラムが正しいことをより簡単に確認できます。64 ビットプログラムは 4G バイトより上でリンクされていますが、リンカーに mapfile および -M オプションを使用することにより、プログラムを 4G バイト未満でリンクすることも可能です。詳細は、/usr/lib/ld/sparcv9/map.below4G を参照してください。
次の図に、SPARC バージョンの実行可能ファイルの例を示します。
図 7-8 SPARC: 実行可能ファイル (64K に整列)
次の表に、前の図に示した読み込み可能セグメント要素の定義を示します。
表 7-28 SPARC: ELF プログラムヘッダーセグメント (64K に整列)
|
次の図に、x86 バージョンの実行可能ファイルの例を示します。
図 7-9 32 ビット x86: 実行可能ファイル (64K に整列)
次の表に、前の図に示した読み込み可能セグメント要素の定義を示します。
表 7-29 32 ビット x86: ELF プログラムヘッダーセグメント (64K に整列)
|
例に示したファイルオフセットと仮想アドレスは、テキストとデータの両方に対して最大ページサイズを法として同じですが、最大 4 ファイルページ (ページサイズとファイルシステムブロックサイズに依存) に、純粋ではないテキストやデータが含まれます。
先頭テキストページには、ELF ヘッダー、プログラムヘッダーテーブル、およびほかの情報が存在します。
最終テキストページには、データの始まりのコピーが存在します。
先頭データページには、テキストの終わりのコピーが存在します。
最後のデータページには、実行中プロセスに関連していないファイル情報が存在できます。論理的にはシステムは、あたかも各セグメントが完全であり分離されているようにメモリーアクセス権を設定します。セグメントのアドレスは調整され、アドレス空間の各論理ページに同じアクセス権セットが確実に存在するようになります。前の例では、テキストの終わりとデータの始まりを保持しているファイル領域が 2 回対応付けされます。 1 回はテキストに関して 1 つの仮想アドレスで対応付けされ、もう 1 回はデータに関して別の仮想アドレスで対応付けされます。
注 - 前の例は、テキストセグメントを丸めた、典型的な Oracle Solaris OS のバイナリを反映したものです。
データセグメントの終わりは、初期化されていないデータに対して特別な処理を必要とします (初期値が 0 になるようにシステムで定義されている)。ファイルの最後のデータページに、論理メモリーページに存在しない情報が存在する場合、これらのデータは 0 に設定しなければなりません (実行可能ファイルの未知の内容のままにしてはならない)。
ほかの 3 ページに含まれる純粋でないテキストまたはデータは、論理的にはプロセスイメージの一部ではありません。システムがこれらの純粋でないテキストまたはデータを除去するかどうかについては、規定されていません。このプログラムのメモリーイメージが 4K バイト (0x1000) ページを使用する例を、次の図に示します。単純化するために次の図では、1 ページのサイズのみを示しています。
図 7-10 32 ビット SPARC: プロセスイメージセグメント
図 7-11 x86: プロセスイメージセグメント
セグメント読み込みは、実行可能ファイルと共有オブジェクトでは異なる側面が 1 つ存在します。実行可能ファイルのセグメントには、標準的には絶対コードが存在します。プロセスを正しく実行するには、セグメントは実行可能ファイルを作成するために使用された仮想アドレスに存在しなければなりません。システムは変化しない p_vaddr 値を仮想アドレスとして使用します。
一方、通常は共有オブジェクトのセグメントには、位置独立のコードが存在します。したがって、セグメントの仮想アドレスは、実行動作を無効にすることなくプロセス間で変化させることができます。
システムは個々のプロセスごとに仮想アドレスを選択しますが、セグメントの相対位置は維持します。位置独立のコードはセグメント間で相対アドレス指定を使用するので、メモリーの仮想アドレス間の差は、ファイルの仮想アドレス間の差に一致しなければなりません。
次の表は、いくつかのプロセスに対する共有オブジェクト仮想アドレスの割り当ての例で、一定の相対位置になることを示しています。これらの表は、ベースアドレスの計算も示しています。
表 7-30 32 ビット SPARC: ELF 共有オブジェクトセグメントアドレスの例
|
表 7-31 32 ビット x86: ELF 共有オブジェクトセグメントアドレスの例
|
動的リンクを開始する動的実行可能ファイルまたは共有オブジェクトは、1 つの PT_INTERP プログラムヘッダー要素を保持できます。システムは exec(2) の実行中に、PT_INTERP セグメントからパス名を取り出し、そのインタプリタファイルのセグメントから初期プロセスイメージを作成します。インタプリタはシステムから制御を受け取り、アプリケーションプログラムに対して環境を提供する必要があります。
Oracle Solaris OS では、インタプリタは実行時リンカー ld.so.1(1) として知られています。
リンカーは、動的リンクを開始する動的オブジェクトを作成する際、PT_INTERP 型のプログラムヘッダー要素を実行可能ファイルに付加します。この要素は、実行時リンカーをプログラムインタプリタとして呼び出すようにシステムに指示します。exec(2) と実行時リンカーは、協調してプログラムのプロセスイメージを作成します。
リンカーはまた、実行時リンカーを支援する、実行可能ファイルと共有オブジェクトファイル用のさまざまなデータを作成します。これらのデータは読み込み可能セグメントに存在するため、データを実行時に使用できます。これらのセグメントには、次のものが含まれます。
タイプ SHT_DYNAMIC の .dynamic セクション。このセクションにはさまざまなデータが格納されます。このセクションの始まりに存在する構造体には、ほかの動的リンク情報のアドレスが存在します。
タイプ SHT_PROGBITS の .got セクションと .plt セクション。このセクションには独立した次の 2 つのテーブルが格納されます。 大域オフセットテーブルとプロシージャーリンクテーブルです。これ以降の節では、オブジェクトファイルのメモリーイメージを作成するために実行時リンカーがテーブルをどのように使用および変更するかを説明します。
共有オブジェクトは、ファイルのプログラムヘッダーテーブルに記録されているアドレスとは異なる仮想メモリーアドレスを占有することが可能です。実行時リンカーは、アプリケーションが制御を取得する前に、メモリーイメージを再配置して絶対アドレスを更新します。
オブジェクトファイルが動的リンクに関係している場合、このオブジェクトファイルのプログラムヘッダーテーブルには、PT_DYNAMIC 型の要素が存在します。このセグメントには、.dynamic セクションが存在します。特殊なシンボル _DYNAMIC は、このセクションを示し、このセクションには、次の構造体を持つ配列が存在します。sys/link.h を参照してください。
typedef struct { Elf32_Sword d_tag; union { Elf32_Word d_val; Elf32_Addr d_ptr; Elf32_Off d_off; } d_un; } Elf32_Dyn; typedef struct { Elf64_Xword d_tag; union { Elf64_Xword d_val; Elf64_Addr d_ptr; } d_un; } Elf64_Dyn;
このタイプの各オブジェクトの場合、d_tag は d_un の解釈に影響します。
このオブジェクトは、さまざまに解釈される整数値を表します。
このオブジェクトは、プログラムの仮想アドレスを表します。ファイルの仮想アドレスは、実行時にメモリーの仮想アドレスに一致しないことがあります。実行時リンカーは、動的構造体に存在するアドレスを解釈するとき、元のファイル値とメモリーのベースアドレスに基づいて実際のアドレスを計算します。整合性のため、ファイルには動的構造体内のアドレスを「補正」するための再配置エントリは存在しません。
一般的に、各動的タグ値によって d_un union の解釈が決まります。この規則は、他社製ツールによる動的タグの解釈をよりシンプルにします。偶数の値を持つタグは、d_ptr を使用する動的セクションのエントリを示します。奇数の値を持つタグは、d_val を使用する動的セクションのエントリ、または d_ptr と d_val のどちらも使用しない動的セクションのエントリを示します。互換性のために次のような特別な範囲の値を持つタグは、これらの規則に従いません。他社製ツールは、これらの例外的な範囲に項目ごとに明示的に対応する必要があります。
特別な値 DT_ENCODING より小さい値を持つタグ。
DT_LOOS - DT_SUNW_ENCODING の値を持つタグ。
DT_HIOS - DT_LOPROC の値を持つタグ。
次の表は、実行可能オブジェクトファイルと共有オブジェクトファイルのタグ要求についてまとめています。タグに「必須」という印が付いている場合、動的リンク配列にはその型のエントリが存在しなければなりません。また、「任意」は、タグのエントリが現れてもよいですが必須ではないことを意味します。
表 7-32 ELF 動的配列タグ
|
_DYNAMIC 配列の終わりを示します。
ヌル文字で終わっている文字列の DT_STRTAB 文字列テーブルオフセットであり、必要な依存性の名前を示します。動的配列には、この型の複数のエントリが存在できます。これらのエントリの相対順序は意味がありますが、ほかの型のエントリに対するこれらのエントリの相対順序には意味がありません。「共有オブジェクトの依存性」を参照してください。
プロシージャーのリンクテーブルに関連付けられている再配置エントリの合計サイズ (単位: バイト)。「プロシージャーのリンクテーブル (プロセッサ固有)」を参照してください。
プロシージャーのリンクテーブルまたは大域オフセットテーブルに関連付けられるアドレス。「プロシージャーのリンクテーブル (プロセッサ固有)」と「大域オフセットテーブル (プロセッサ固有)」を参照してください。
シンボルハッシュテーブルのアドレス。このテーブルは、DT_SYMTAB 要素で示されるシンボルテーブルを参照します。「ハッシュテーブルセクション」を参照してください。
文字列テーブルのアドレス。文字列テーブルには、実行時リンカーが必要とするシンボル名、依存性名、およびほかの文字列が存在します。「文字列テーブルセクション」を参照してください。
シンボルテーブルのアドレス。「シンボルテーブルセクション」を参照してください。
再配置テーブルのアドレス。「再配置セクション」を参照してください。
オブジェクトファイルには、複数の再配置セクションを指定できます。リンカーは、実行可能オブジェクトファイルまたは共有オブジェクトファイルの再配置テーブルを作成するとき、これらのセクションを連結して単一のテーブルを作成します。これらの各セクションはオブジェクトファイル内で独立している場合がありますが、実行時リンカーは単一のテーブルとして扱います。実行時リンカーは、実行可能ファイルのプロセスイメージを作成したり、またはプロセスイメージに共有オブジェクトを付加したりするとき、再配置テーブルを読み取り、関連付けられている動作を実行します。
この要素が存在する場合、DT_RELASZ 要素と DT_RELAENT 要素も存在する必要があります。再配置がファイルに対して必須の場合、DT_RELA または DT_REL が使用可能です。
DT_RELA 再配置テーブルの合計サイズ (単位: バイト)。
DT_RELA 再配置エントリのサイズ (単位: バイト)。
DT_STRTAB 文字列テーブルの合計サイズ (単位: バイト)。
DT_SYMTAB シンボルエントリのサイズ (単位: バイト)。
初期化関数のアドレス。「初期設定および終了セクション」を参照してください。
終了関数のアドレス。「初期設定および終了セクション」を参照してください。
ヌル文字で終わっている文字列の DT_STRTAB 文字列テーブルオフセットで、共有オブジェクトの名前を示します。「共有オブジェクト名の記録」を参照してください。
ヌル文字で終わっているライブラリ検索パス文字列の DT_STRTAB 文字列テーブルオフセット。この要素の使用は、DT_RUNPATH に置き換えられました。「実行時リンカーが検索するディレクトリ」を参照してください。
オブジェクトが、リンク編集中に適用されたシンボリック結合を含むことを示します。この要素の使用は、DF_SYMBOLIC フラグに置き換えられました。「-B symbolic オプションの使用」を参照してください。
DT_RELA に似ていますが、テーブルに暗黙の加数が存在する点が異なります。この要素が存在する場合、DT_RELSZ 要素と DT_RELENT 要素も存在する必要があります。
DT_REL 再配置テーブルの合計サイズ (単位: バイト)。
DT_REL 再配置エントリのサイズ (単位: バイト)。
プロシージャーのリンクテーブルが参照する再配置エントリの型 (DT_REL または DT_RELA) を示します。1 つのプロシージャーのリンクテーブルでは、すべての再配置は、同じ再配置を使用しなければなりません。「プロシージャーのリンクテーブル (プロセッサ固有)」を参照してください。この要素が存在する場合、DT_JMPREL 要素も存在する必要があります。
デバッグに使用されます。
1 つまたは複数の再配置エントリが書き込み不可セグメントに対する変更を要求する可能性があり、実行時リンカーはそれに応じて対応できることを示します。この要素の使用は、DF_TEXTREL フラグに置き換えられました。「位置独立のコード」を参照してください。
プロシージャーのリンクテーブルにのみ関連付けられている再配置エントリのアドレス。「プロシージャーのリンクテーブル (プロセッサ固有)」を参照してください。これらの再配置エントリを分離しておくと、遅延結合が有効なオブジェクトの読み込み時に、実行時リンカーはこれらのエントリを無視できます。この要素が存在する場合、DT_PLTRELSZ 要素と DT_PLTREL 要素も存在する必要があります。
直後の DT_ 要素に適用されるさまざまな状態フラグ。表 7-35 を参照してください。
プログラムに制御を渡す前に、このオブジェクトについてのすべての再配置を処理するよう実行時リンカーに指示します。環境または dlopen(3C) で指定された場合、このエントリは遅延結合の使用指令よりも優先されます。この要素の使用は、DF_BIND_NOW フラグに置き換えられました。詳細は、「再配置が実行されるとき」を参照してください。
初期設定関数へのポインタの配列のアドレス。この要素が存在する場合、DT_INIT_ARRAYSZ 要素も存在する必要があります。「初期設定および終了セクション」を参照してください。
終了関数へのポインタの配列のアドレス。この要素が存在する場合、DT_FINI_ARRAYSZ 要素も存在する必要があります。「初期設定および終了セクション」を参照してください。
DT_INIT_ARRAY 配列の合計サイズ (単位: バイト)。
DT_FINI_ARRAY 配列の合計サイズ (単位: バイト)。
ヌル文字で終わっているライブラリ検索パス文字列の DT_STRTAB 文字列テーブルオフセット。「実行時リンカーが検索するディレクトリ」を参照してください。
このオブジェクトに特有のフラグ値。表 7-33 を参照してください。
DT_ENCODING と等しいかそれより大きく、かつ DT_LOOS と等しいかそれより小さい動的タグ値は、 d_un union の解釈の規則に従います。
「初期設定前」関数へのポインタの配列のアドレス。この要素が存在する場合、DT_PREINIT_ARRAYSZ 要素も存在する必要があります。この配列は、実行可能ファイル内でのみ処理されます。共有オブジェクト内に含まれている場合、この配列は無視されます。「初期設定および終了セクション」を参照してください。
DT_PREINIT_ARRAY 配列の合計サイズ (単位: バイト)。
値が正である動的配列タグの数。
この範囲の値 (両端の値を含む) は、オペレーティングシステム固有のセマンティクスのために予約されています。このような値はすべて、d_un union の解釈の規則に従います。
ヌル文字で終わっている文字列の DT_STRTAB 文字列テーブルオフセットで、シンボル別の補助フィルティーを 1 つ以上指定します。「補助フィルタの生成」を参照してください。
実行時リンカーによる使用のために予約されています。
ヌル文字で終わっている文字列の DT_STRTAB 文字列テーブルオフセットで、シンボル別の標準フィルティーを 1 つ以上指定します。「標準フィルタの生成」を参照してください。
機能セクションのアドレス。「機能セクション」を参照してください。
シンボルテーブルのアドレス。局所関数シンボルが含まれ、DT_SYMTAB から提供されるシンボルを拡張します。これらのシンボルは常に、DT_SYMTAB から提供されるシンボルの直前に並んで配置されます。「シンボルテーブルセクション」を参照してください。
DT_SUNW_SYMTAB と DT_SYMTAB から提供されるシンボルテーブルを結合したサイズ。
DT_SUNW_ENCODING と等しいかそれより大きく、かつ DT_HIOS と等しいかそれより小さい動的タグ値は、d_un union の解釈の規則に従います。
DT_SUNW_SYMSORT および DT_SUNW_TLSSORT シンボルソートエントリのサイズ (単位: バイト)。
シンボルテーブルインデックスの配列のアドレス。DT_SUNW_SYMTAB が参照するシンボルテーブル内の関数シンボルと変数シンボルに、ソートキーに基づいてアクセスできます。「シンボルソートセクション」を参照してください。
DT_SUNW_SYMSORT 配列の合計サイズ (単位: バイト)。
シンボルテーブルインデックスの配列のアドレス。DT_SUNW_SYMTAB が参照するシンボルテーブル内のスレッド固有シンボルに、ソートキーに基づいてアクセスできます。「シンボルソートセクション」を参照してください。
DT_SUNW_TLSSORT 配列の合計サイズ (単位: バイト)。
シンボルをその機能要件に関連付けることができるシンボルテーブルインデックスの配列のアドレス。「機能セクション」を参照してください。
動的文字列テーブルの末尾に予約されている未使用領域の合計サイズ (単位: バイト)。オブジェクト内に DT_SUNW_STRPAD が存在しない場合は、予約されている領域はありません。
機能ファミリインデックスの配列のアドレス。インデックスの各ファミリは 0 エントリで終了します。
このオブジェクトを生成したリンカーのマシンアーキテクチャー。DT_SUNW_LDMACH では、ELF ヘッダーの e_machine フィールドに使用される EM_ 整数値と同じ値が使用されます。「ELF ヘッダー」を参照してください。DT_SUNW_LDMACH は、オブジェクトを構築するリンカーのクラス (32 ビットまたは 64 ビット) とプラットフォームを識別するために使用されます。この情報は、実行時リンカーでは使用されず、ドキュメントのためだけに使用されます。
DT_SUNW_CAPCHAIN エントリのサイズ (バイト単位)。
合計サイズ (バイト単位)、または DT_SUNW_CAPCHAIN 連鎖。
シンボル情報テーブルのアドレス。この要素が存在する場合、DT_SYMINENT 要素と DT_SYMINSZ 要素も存在する必要があります。「Syminfo テーブルセクション」を参照してください。
DT_SYMINFO 情報エントリのサイズ (単位: バイト)。
DT_SYMINFO テーブルのサイズ (単位: バイト)。
バージョン定義テーブルのアドレス。このテーブル内の要素には、文字列テーブル DT_STRTAB のインデックスが含まれます。この要素が存在する場合、DT_VERDEFNUM 要素も存在する必要があります。「バージョン定義セクション」を参照してください。
DT_VERDEF テーブルのエントリ数。
バージョン依存性テーブルのアドレス。このテーブル内の要素には、文字列テーブル DT_STRTAB のインデックスが含まれます。この要素が存在する場合、DT_VERNEEDNUM 要素も存在する必要があります。「バージョン依存セクション」を参照してください。
DT_VERNEEDNUM テーブルのエントリ数。
すべての Elf32_Rela または Elf64_Rela 再配置の連結から生成される RELATIVE 再配置回数を示します。「再配置セクションの結合」を参照してください。
すべての Elf32_Rel 再配置の連結から生成される RELATIVE 再配置回数を示します。「再配置セクションの結合」を参照してください。
ヌル文字で終わっている DT_STRTAB 文字列テーブルオフセットで、1 つ以上の補助フィルティーを指定します。「補助フィルタの生成」を参照してください。
ヌル文字で終わっている DT_STRTAB 文字列テーブルオフセットで、1 つ以上の標準「フィルティー」を指定します。「標準フィルタの生成」を参照してください。
オブジェクトの選択されたセクションの簡単なチェックサム。gelf_checksum(3ELF) のマニュアルページを参照してください。
DT_MOVETAB 移動エントリのサイズ (単位: バイト)。
DT_MOVETAB テーブルの合計サイズ (単位: バイト)。
移動テーブルのアドレス。この要素が存在する場合、DT_MOVEENT 要素と DT_MOVESZ 要素も存在する必要があります。「移動セクション」を参照してください。
ヌル文字で終わっている DT_STRTAB 文字列テーブルオフセットで、構成ファイルを定義します。構成ファイルは、実行可能ファイルでのみ有効であり、通常このオブジェクトに固有のファイルです。「デフォルトの検索パスの設定」を参照してください。
ヌル文字で終わっている DT_STRTAB 文字列テーブルオフセットで、1 つ以上の監査ライブラリを定義します。「実行時リンカーの監査インタフェース」を参照してください。
ヌル文字で終わっている DT_STRTAB 文字列テーブルオフセットで、1 つ以上の監査ライブラリを定義します。「実行時リンカーの監査インタフェース」を参照してください。
このオブジェクトに特有のフラグ値。表 7-34 を参照してください。
この範囲の値 (両端の値を含む) は、動的構造体の d_un.d_val フィールドによって使用されます。
この範囲の値 (両端の値を含む) は、動的構造体の d_un.d_ptr フィールドによって使用されます。ELF オブジェクトが作成後に調整された場合、これらのエントリも更新する必要があります。
DT_SYMTAB シンボルテーブル内の STT_SPARC_REGISTER シンボルのインデックス。シンボルテーブルの各 STT_SPARC_REGISTER シンボルには、1 つの動的エントリが存在します。「レジスタシンボル」を参照してください。
この範囲の値は、プロセッサ固有のセマンティクスのために予約されています。
動的配列の最後にある DT_NULL 要素と、DT_NEEDED と DT_POSFLAG_1 要素の相対的な順序を除くと、エントリはどの順序で現れてもかまいません。表に示されていないタグ値は予約されています。
表 7-33 ELF 動的フラグ DT_FLAGS
|
オブジェクトに $ORIGIN 処理が必要であることを示します。「関連する依存関係の配置」を参照してください。
オブジェクトが、リンク編集中に適用されたシンボリック結合を含むことを示します。「-B symbolic オプションの使用」を参照してください。
1 つまたは複数の再配置エントリが書き込み不可セグメントに対する変更を要求する可能性があり、実行時リンカーはそれに応じて対応できることを示します。「位置独立のコード」を参照してください。
プログラムに制御を渡す前に、このオブジェクトについてのすべての再配置を処理するよう実行時リンカーに指示します。環境または dlopen(3C) で指定された場合、このエントリは遅延結合の使用指令よりも優先されます。詳細は、「再配置が実行されるとき」を参照してください。
静的なスレッド固有領域方式を使用するコードがオブジェクトに含まれていることを示します。静的なスレッド固有領域は、dlopen(3C) または遅延読み込みを使用して動的に読み込まれるオブジェクトでは使用すべきではありません。
表 7-34 ELF 動的フラグ DT_FLAGS_1
|
プログラムに制御を渡す前に、このオブジェクトについてのすべての再配置を処理するよう実行時リンカーに指示します。環境または dlopen(3C) で指定された場合、このフラグは遅延結合の使用指令よりも優先されます。詳細は、「再配置が実行されるとき」を参照してください。
オブジェクトがグループのメンバーであることを示します。このフラグは、リンカーの -B group オプションを使用してオブジェクトに記録されます。「オブジェクト階層」を参照してください。
オブジェクトがプロセスから削除できないことを示します。オブジェクトは、dlopen(3C) で直接または依存性としてプロセスに読み込まれた場合、dlclose(3C) で読み込み解除できません。このフラグは、リンカーの -z nodelete オプションを使用してオブジェクトに記録されます。
フィルタに対してのみ意味があります。関連付けられているすべてのフィルティーがただちに処理されることを示します。このフラグは、リンカーの -z loadfltr オプションを使用してオブジェクトに記録されます。「「フィルティー」の処理」を参照してください。
読み込まれたほかのオブジェクトよりも先に、このオブジェクトの初期化セクションが実行されることを示します。このフラグは特殊なシステムライブラリでのみ使用するもので、リンカーの -z initfirst オプションを使用してオブジェクトに記録されます。
dlopen(3C) を使ってオブジェクトを実行中のプロセスに追加できないことを示します。このフラグは、リンカーの -z nodlopen オプションを使用してオブジェクトに記録されます。
オブジェクトに $ORIGIN 処理が必要であることを示します。「関連する依存関係の配置」を参照してください。
オブジェクトが直接結合情報を使用することを示します。付録 D 直接結合を参照してください。
オブジェクトシンボルテーブルの割り込みが、一次読み込みオブジェクト (通常は実行可能ファイル) 以外のすべてのシンボルの前で発生します。このフラグは、リンカーの -z interpose オプションを使用して記録されます。「実行時割り込み」を参照してください。
このオブジェクトの依存関係を検索する際、デフォルトのライブラリ検索パスがすべて無視されることを示します。このフラグは、リンカーの -z nodefaultlib オプションを使用してオブジェクトに記録されます。「実行時リンカーが検索するディレクトリ」を参照してください。
このオブジェクトが dldump(3C) によってダンプされないことを示します。このオプションの候補には、再配置を保持しないオブジェクトが含まれ、これらのオブジェクトは、crle(1) を使用して代替オブジェクトを生成する際に含めることができます。このフラグは、リンカーの -z nodump オプションを使用してオブジェクトに記録されます。
このオブジェクトが、crle(1) によって生成された代替構成オブジェクトであることを示します。このフラグにより実行時リンカーがトリガーされ、構成ファイル $ORIGIN/ld.config. app-name が検索されます。
「フィルティー」に対してのみ意味があります。以降の「フィルティー」に対するフィルタ検索は行われません。このフラグは、リンカーの -z endfiltee オプションを使用してオブジェクトに記録されます。「「フィルティー」検索の縮小」を参照してください。
このオブジェクトにディスプレイスメント再配置が適用されたことを示します。再配置が適用されるとレコードは破棄されるため、オブジェクト内のディスプレイスメント再配置レコードはもはや存在しません。「ディスプレイスメント再配置」を参照してください。
このオブジェクトのディスプレイスメント再配置が保留されていることを示します。ディスプレイスメント再配置はオブジェクト内部で終了するため、再配置は実行時に完了できます。「ディスプレイスメント再配置」を参照してください。
このオブジェクトに、直接結合できないシンボルが含まれることを示します。「SYMBOL_SCOPE/SYMBOL_VERSION 指令」を参照してください。
カーネルの実行時リンカーによる使用のために予約されています。
カーネルの実行時リンカーによる使用のために予約されています。
カーネルの実行時リンカーによる使用のために予約されています。
このオブジェクトがリンカーによって最初に構築されたあとに編集または変更されたことを示します。このフラグは、オブジェクトが最初に構築されたあとになんらかの変更が加えられたことをデバッガに警告するために使用されます。
カーネルの実行時リンカーによる使用のために予約されています。
一次読み込みオブジェクト (通常は実行可能ファイル) 以外のすべてのシンボルの前に割り込むべき個々のシンボルが、オブジェクトに含まれることを示します。このフラグは、mapfile と INTERPOSE キーワードを使ってオブジェクトが構築されるときに記録されます。「SYMBOL_SCOPE/SYMBOL_VERSION 指令」を参照してください。
動的実行可能ファイルで大域監査が必要であることを示します。「大域監査の記録」を参照してください。
このオブジェクトに singleton シンボルが定義されている、またはオブジェクトがこのシンボルを参照していることを示します。「SYMBOL_SCOPE/SYMBOL_VERSION 指令」を参照してください。
表 7-35 ELF 動的位置フラグ DT_POSFLAG_1
|
後続の DT_NEEDED エントリが遅延読み込み対象のオブジェクトであることを示します。このフラグは、リンカーの -z lazyload オプションを使用してオブジェクトに記録されます。「動的依存関係の遅延読み込み」を参照してください。
後続の DT_NEEDED エントリがグループとして読み込まれるオブジェクトであることを示します。このフラグは、リンカーの -z groupperm オプションを使用してオブジェクトに記録されます。「グループの分離」を参照してください。
一般に位置独立のコードには絶対仮想アドレスは存在できません。大域オフセットテーブルは、内部で使用するデータ内に絶対アドレスを保持します。このため、位置からの独立性とプログラムのテキストの共有性を低下させることなくアドレスが使用可能になります。プログラムは、位置独立のアドレス指定を使用して GOT を参照し、絶対値を抽出します。この方法により、位置独立の参照を、絶対位置にリダイレクトできます。
最初は、GOT は再配置エントリで要求される情報を保持します。システムが読み込み可能オブジェクトファイルのメモリーセグメントを作成したあと、実行時リンカーが再配置エントリを処理します。これらの再配置のいくつかは、R_xxxx_GLOB_DAT タイプで GOT を参照する場合があります。
実行時リンカーは、関連付けられているシンボル値を判定し、絶対アドレスを計算し、適切なメモリーテーブルエントリに正しい値を設定します。リンカーがオブジェクトファイルを作成するとき、絶対アドレスは認識されていませんが、実行時リンカーはすべてのメモリーセグメントのアドレスを認識しており、したがって、これらのメモリーセグメントに存在するシンボルの絶対アドレスを計算できます。
プログラムがシンボルの絶対アドレスへの直接アクセスを必要とする場合、このシンボルには GOT エントリが存在します。実行可能ファイルと共有オブジェクトには別個の GOT が存在するので、シンボルのアドレスはいくつかのテーブルに現れることがあります。実行時リンカーは、すべての GOT の再配置を処理してから、プロセスイメージ内のいずれかのコードに制御を渡します。この処理により、実行時に絶対アドレスが利用可能になります。
テーブルのエントリ 0 は、_DYNAMIC シンボルで参照される動的構造体のアドレスを保持するために予約されています。このシンボルを利用することにより、実行時リンカーなどのプログラムは、再配置エントリを処理していなくても自身の動的構造体を見つけることができます。この方法は、実行時リンカーにとって特に重要です。なぜなら、実行時リンカーはほかのプログラムに頼ることなく自身を初期化してメモリーイメージを再配置しなければならないからです。
システムは、異なるプログラムの同じ共有オブジェクトに対して、異なるメモリーセグメントアドレスを与えることがあります。さらに、システムはプログラムを実行するごとに異なるライブラリアドレスを与えることさえあります。しかし、プロセスイメージがいったん作成されると、メモリーセグメントのアドレスは変更されません。プロセスが存在するかぎり、そのプロセスのメモリーセグメントは固定仮想されたアドレスに存在します。
GOT の形式と解釈は、プロセッサに固有です。_GLOBAL_OFFSET_TABLE_ シンボルは、テーブルをアクセスするために使用できます。このシンボルは、.got セクションの中央に存在可能であるため、負の添字と負でない添字の両方をアドレスの配列に含めることができます。シンボルタイプは、32 ビットコードの場合、Elf 32_Addr の配列で、64 ビットコードの場合、Elf 64_Addr の配列です。
extern Elf32_Addr _GLOBAL_OFFSET_TABLE_[]; extern Elf64_Addr _GLOBAL_OFFSET_TABLE_[];
大域オフセットテーブルは位置独立のアドレスの計算を絶対位置に変換します。同様に、プロシージャーのリンクテーブルは位置独立の関数呼び出しを絶対位置に変換します。リンカーは、異なる動的オブジェクト間の実行転送 (関数呼び出しなど) を解決できません。このため、リンカーはプログラム転送制御をプロシージャーのリンクテーブルのエントリに与えます。このようにして実行時リンカーは、位置からの独立性とプログラムのテキストの共有性を低下させることなくエントリをリダイレクトします。実行可能ファイルと共有オブジェクトファイルには、別個のプロシージャーのリンクテーブルが存在します。
32 ビット SPARC 動的オブジェクトの場合、プロシージャーのリンクテーブルは専用データ内に存在します。実行時リンカーは、宛先の絶対アドレスを判定し、これらの絶対アドレスに従ってプロシージャーのリンクテーブルのメモリーイメージに変更を加えます。
最初の 4 つのプロシージャーのリンクテーブルエントリは、予約されています。表 7-36 に例示されてはいますが、これらのエントリの元の内容は指定されていません。テーブル内の各エントリは 3 ワード (12 バイト) を占めており、最後のテーブルエントリの後には nop 命令が続きます。
再配置テーブルは、プロシージャーのリンクテーブルに関連付けられています。_DYNAMIC 配列の DT_JMP_REL エントリは、最初の再配置エントリの位置を与えます。再配置テーブルには、予約されていないプロシージャーのリンクテーブルエントリごとに 1 つのエントリが同じ順番で存在します。各エントリの再配置タイプは、R_SPARC_JMP_SLOT です。再配置オフセットは関連付けられているプロシージャーのリンクテーブルエントリの先頭バイトのアドレスを指定します。シンボルテーブルインデックスは適切なシンボルを参照します。
プロシージャーのリンクテーブルを説明するため、表 7-36 に 4 つのエントリが示されています。4 つのエントリのうちの 2 つは初期状態で予約されているエントリです。3 番目のエントリは name101 に対する呼び出しです。4 番目のエントリは name102 に対する呼び出しです。この例では、name102 のエントリがテーブルの最後のエントリであることを前提としています。この最後のエントリの後には nop 命令が続きます。左欄は、動的リンクが行われる前のオブジェクトファイルの命令を示しています。右欄は、実行時リンカーがプロシージャーリンクテーブルのエントリを変更するために使用できる命令シーケンスを示しています。
表 7-36 32 ビット SPARC: プロシージャーのリンクテーブルの例
|
次の手順は、実行時リンカーとプログラムがプロシージャーのリンクテーブルによってシンボル参照をどのように協調して解決するかを示しています。ただし、次に記述されている手順は、単に説明のためのものです。実行時リンカーの正確な実行時動作については、記述されていません。
プログラムのメモリーイメージが最初に作成されると、実行時リンカーはプロシージャーのリンクテーブルの初期エントリを変更します。これらのエントリは、実行時リンカー自身のルーチンの 1 つに制御を渡すように修正されます。実行時リンカーはまた、識別情報 (identification) を 2 番目のエントリに格納します。実行時リンカーが制御を受け取ると、このワードは呼び出したオブジェクトを見つけるために調べられます。
ほかのすべてのプロシージャーのリンクテーブルエントリは、最初は先頭エントリに渡されます。これで、実行時リンカーは各テーブルエントリの最初の実行時に制御を取得します。たとえば、プログラムが name101 を呼び出すと、制御がラベル .PLT101 に渡されます。
sethi 命令は、現在のプロシージャーのリンクテーブルエントリ (.PLT101) と最初のプロシージャーのリンクテーブルエントリ (.PLT0) の距離を計算します。この値は、%g1 レジスタの最上位 22 ビットを占めます。
次に、ba,a 命令が .PLT0 にジャンプして、スタックフレームを作成し、実行時リンカーを呼び出します。
実行時リンカーは、識別情報の値を使うことによってオブジェクトのデータ構造体 (再配置テーブルを含む) を取得します。
実行時リンカーは、%g1 値をシフトしプロシージャーのリンクテーブルエントリのサイズで除算することで、 name101 の再配置エントリのインデックスを計算します。再配置エントリ 101 のタイプは R_SPARC_JMP_SLOT です。再配置オフセットは .PLT101 のアドレスを指定し、また、そのシンボルテーブルインデックスは name101 を参照します。したがって、実行時リンカーはシンボルの実際の値を取得し、スタックを戻し、プロシージャーのリンクテーブルエントリに変更を加え、本来の宛先に制御を渡します。
実行時リンカーは、メモリーセグメント欄に示された命令シーケンスを必ずしも作成するとは限りません。ただし、作成する場合は、いくつかの点でより詳細な説明が必要です。
コードを再入可能にするため、プロシージャーのリンクテーブルの命令に、特定の順番で変更が加えられます。実行時リンカーが関数のプロシージャーのリンクテーブルエントリを修正中に信号が到達した場合、信号処理コードは、予想可能かつ正しい結果を与える元の関数を呼び出すことができなければなりません。
実行時リンカーは、エントリを変換するために 3 つのワードを変更します。実行時リンカーは、命令を実行する際、ワード単位でのみ不可分に更新できます。このため、各ワードを逆順に更新して再入を可能にします。再入可能関数呼び出しが最後のパッチの直前に発生した場合、実行時リンカーは再度制御を取得します。実行時リンカーに対する両方の呼び出しで、同じプロシージャーのリンクテーブルエントリに変更が加えられるが、これらの変更は互いに干渉しません。
プロシージャーのリンクテーブルエントリの最初の sethi 命令は、1 つ前のエントリの jmp1 命令の遅延スロットを埋めます。sethi は %g1 レジスタの値を変更するが、以前の内容を破棄しても問題はありません。
変換後、最後のプロシージャーのリンクテーブルエントリ (.PLT102) は、jmp1 の遅延命令を必要とします。要求されている後続の nop は、この遅延スロットを埋めます。
注 - .PLT101 と .PLT102 の命令シーケンスの違いから、関連する宛先に合わせた最適化の方法を知ることができます。
LD_BIND_NOW 環境変数は、動的リンク動作を変更します。この環境変数の値がヌル文字以外の場合、実行時リンカーは、プログラムに制御を渡す前に R_SPARC_JMP_SLOT 再配置エントリを処理します。
64 ビット SPARC 動的オブジェクトの場合、プロシージャーのリンクテーブルは専用データ内に存在します。実行時リンカーは、宛先の絶対アドレスを判定し、これらの絶対アドレスに従ってプロシージャーのリンクテーブルのメモリーイメージに変更を加えます。
最初の 4 つのプロシージャーのリンクテーブルエントリは、予約されています。表 7-37 に例示されてはいますが、これらのエントリの元の内容は指定されていません。テーブル内の先頭 32,768 エントリは、それぞれ 8 ワード (32 バイト) を占め、32 バイト境界で整列する必要があります。テーブル全体は 256 バイト境界で整列する必要があります。32,768 を超えるエントリが必要な場合、残りのエントリは 6 ワード (24 バイト) および 1 つのポインタ (8 バイト) で構成されます。命令は、160 エントリのブロックにまとめられ、その次に 160 個ポインタが続きます。最後のグループのエントリとポインタは、160 未満でもかまいません。パディングの必要はありません。
注 - 32,768 および 160 という数字は、それぞれ分岐と読み込み置換の制限に基づいており、また、キャッシュの効率を向上させるために、コードとデータの間の区分を 256 バイト境界に合わせています。
再配置テーブルは、プロシージャーのリンクテーブルに関連付けられています。_DYNAMIC 配列の DT_JMP_REL エントリは、最初の再配置エントリの位置を与えます。再配置テーブルには、予約されていないプロシージャーのリンクテーブルエントリごとに 1 つのエントリが同じ順番で存在します。各エントリの再配置タイプは、R_SPARC_JMP_SLOT です。最初の 32,767 スロットでは、再配置オフセットは関連するプロシージャーのリンクテーブルエントリの先頭バイトのアドレスを指定します。加数フィールドはゼロになります。シンボルテーブルインデックスは適切なシンボルを参照します。32,768 以後のスロットでは、再配置オフセットは関連するポインタの先頭バイトのアドレスを指定します。加数フィールドは、再配置されていない値 -(.PLTN + 4) になります。シンボルテーブルインデックスは適切なシンボルを参照します。
プロシージャーのリンクテーブルを説明するため、表 7-37 にいくつかのエントリが示されています。最初の 3 つのエントリは、予約済みの初期エントリを示します。続く 3 つのエントリは、32,768 エントリの初期状態と、それぞれ、対象アドレスがエントリの +/- 2G バイト以内の場合、アドレス空間の下位 4G バイト以内の場合、およびその他の場合に適用されると考えられる、変換された状態を示しています。最後の 2 つのエントリは、命令とポインタのペアで構成される、後のエントリの例を示します。左欄は、動的リンクが行われる前のオブジェクトファイルの命令を示しています。右欄は、実行時リンカーがプロシージャーリンクテーブルのエントリを変更するために使用できる命令シーケンスを示しています。
表 7-37 64 ビット SPARC: プロシージャーのリンクテーブルの例
|
次の手順は、実行時リンカーとプログラムがプロシージャーのリンクテーブルによってシンボル参照をどのように協調して解決するかを示しています。ただし、次に記述されている手順は、単に説明のためのものです。実行時リンカーの正確な実行時動作については、記述されていません。
プログラムのメモリーイメージが最初に作成されると、実行時リンカーはプロシージャーのリンクテーブルの初期エントリを変更します。エントリは、実行時リンカー自身のルーチンに制御を渡すように修正されます。実行時リンカーはまた、識別情報 (identification) の拡張ワードを 3 番目のエントリに格納します。実行時リンカーが制御を受け取ると、このワードは呼び出したオブジェクトを見つけるために調べられます。
ほかのすべてのプロシージャーのリンクテーブルエントリは、最初、先頭または 2 番目のエントリに渡されます。これらのエントリは、スタックフレームを確立して、実行時リンカーを呼び出します。
実行時リンカーは、識別情報の値を使うことによってオブジェクトのデータ構造体 (再配置テーブルを含む) を取得します。
実行時リンカーは、テーブルスロットの再配置エントリのインデックスを計算します。
インデックス情報に関しては、実行時リンカーはシンボルの実際の値を取得し、スタックを戻し、プロシージャーのリンクテーブルエントリを変更してから、制御を宛先に渡します。
実行時リンカーは、メモリーセグメント欄に示された命令シーケンスを必ずしも作成するとは限りません。ただし、作成する場合は、いくつかの点でより詳細な説明が必要です。
コードを再入可能にするため、プロシージャーのリンクテーブルの命令に、特定の順番で変更が加えられます。実行時リンカーが関数のプロシージャーのリンクテーブルエントリを修正中に信号が到達した場合、信号処理コードは、予想可能かつ正しい結果を与える元の関数を呼び出すことができなければなりません。
実行時リンカーは、8 ワードまで変更を加えてエントリを変換できます。実行時リンカーは、命令を実行する際、ワード単位でのみ不可分に更新できます。このため、64 ビットストアを使用している場合、再入可能性は、まず nop 命令を置換命令で上書きし、次に ba、a および sethi をパッチ適用することで実現されます。再入可能関数呼び出しが最後のパッチの直前に発生した場合、実行時リンカーは再度制御を取得します。実行時リンカーに対する両方の呼び出しで、同じプロシージャーのリンクテーブルエントリに変更が加えられるが、これらの変更は互いに干渉しません。
最初の sethi 命令が変更されると、この命令を変更するには nop を使用する必要があります。
エントリの 2 番目のフォームに示すように、ポインタの変更は、単一の不可分 64 ビットストアを使用して行われます。
注 - .PLT101、.PLT102、および .PLT103 の命令シーケンスの違いから、関連する宛先に合わせた最適化の方法を知ることができます。
LD_BIND_NOW 環境変数は、動的リンク動作を変更します。この環境変数の値がヌル文字以外の場合、実行時リンカーは、プログラムに制御を渡す前に R_SPARC_JMP_SLOT 再配置エントリを処理します。
32 ビット x86 動的オブジェクトの場合、プロシージャーリンクテーブルは共有テキスト内に存在しますが、非公開の大域オフセットテーブル内のアドレスを使用します。実行時リンカーは、宛先の絶対アドレスを判定し、これらの絶対アドレスに従って大域オフセットテーブルのメモリーイメージに変更を加えます。このようにして実行時リンカーは、位置からの独立性とプログラムのテキストの共有性を低下させることなくエントリをリダイレクトします。実行可能ファイルと共有オブジェクトファイルには、別個のプロシージャーのリンクテーブルが存在します。
表 7-38 32 ビット x86: 絶対プロシージャーのリンクテーブルの例
|
表 7-39 32 ビット x86: 位置独立のプロシージャーリンクテーブルの例
|
注 - 前述の例が示すとおり、プロシージャーリンクテーブルの命令は、絶対コードと位置独立のコードで異なるオペランドアドレス指定モードを使用します。それでも、実行時リンカーへのインタフェースは同一です。
次の手順は、実行時リンカーとプログラムがプロシージャーのリンクテーブルおよび大域オフセットテーブルによってシンボル参照をどのように協同で解決するかを示しています。
実行時リンカーは、プログラムのメモリーイメージを最初に作成するとき、大域オフセットテーブルの 2 番目と 3 番目のエントリに特殊な値を設定します。これらの値については、次の手順で説明します。
プロシージャーのリンクテーブルが位置独立の場合、大域オフセットテーブルのアドレスは、%ebx に存在しなければなりません。プロセスイメージにおける各共有オブジェクトファイルには自身のプロシージャーのリンクテーブルが存在しており、制御は同じオブジェクトファイル内からのみプロシージャーのリンクテーブルエントリに渡されます。したがって、呼び出し側関数は、プロシージャーのリンクテーブルエントリを呼び出す前に、大域オフセットテーブルベースレジスタをセットしなければなりません。
たとえば、プログラムが name1 を呼び出すと、制御が .PLT1 に渡されます。
最初の命令は、name1 の大域オフセットテーブルエントリのアドレスにジャンプします。大域オフセットテーブルは最初は、後続の pushl 命令のアドレスを保持します (name1 の実アドレスは保持しない)。
プログラムは再配置オフセット (offset) をスタックにプッシュします。再配置オフセットは、再配置テーブルへの 32 ビットの負ではないバイトオフセットです。指定された再配置エントリには R_386_JMP_SLOT が存在しており、オフセットは、前の jmp 命令で使用された大域オフセットテーブルエントリを指定します。再配置エントリにはシンボルテーブルインデックスも存在しており、実行時リンカーはこれを使って参照されたシンボル name1 を取得します。
プログラムは、再配置オフセットをプッシュした後、.PLT0 (プロシージャーのリンクテーブルの先頭エントリ) にジャンプします。pushl 命令は、2 番目の大域オフセットテーブルエントリ (got_plus_4 または 4(%ebx)) の値をスタックにプッシュして、実行時リンカーに 1 ワードの識別情報を与えます。プログラムは次に、3 番目の大域オフセットテーブルエントリ (got_plus_8 または 8(%ebx)) のアドレスにジャンプして、実行時リンカーにジャンプします。
実行時リンカーはスタックを戻し、指定された再配置エントリを調べ、シンボルの値を取得し、name1 の実際のアドレスを大域オフセットテーブルエントリに格納し、そして宛先にジャンプします。
その後のプロシージャーのリンクテーブルエントリに対する実行は、name1 に直接渡されます (実行時リンカーの再呼び出しは行われない)。.PLT1 における jmp 命令は、pushl 命令にジャンプする代わりに、name1 にジャンプします。
LD_BIND_NOW 環境変数は、動的リンク処理の動作を変更します。この環境変数の値がヌル文字以外の場合、実行時リンカーは、プログラムに制御を渡す前に R_386_JMP_SLOT 再配置エントリを処理します。
x64 動的オブジェクトの場合、プロシージャーリンクテーブルは共有テキスト内に存在しますが、非公開の大域オフセットテーブル内のアドレスを使用します。実行時リンカーは、宛先の絶対アドレスを判定し、これらの絶対アドレスに従って大域オフセットテーブルのメモリーイメージに変更を加えます。このようにして実行時リンカーは、位置からの独立性とプログラムのテキストの共有性を低下させることなくエントリをリダイレクトします。実行可能ファイルと共有オブジェクトファイルには、別個のプロシージャーのリンクテーブルが存在します。
表 7-40 x64: プロシージャーのリンクテーブルの例
|
次の手順は、実行時リンカーとプログラムがプロシージャーのリンクテーブルおよび大域オフセットテーブルによってシンボル参照をどのように協同で解決するかを示しています。
実行時リンカーは、プログラムのメモリーイメージを最初に作成するとき、大域オフセットテーブルの 2 番目と 3 番目のエントリに特殊な値を設定します。これらの値については、次の手順で説明します。
プロセスイメージにおける各共有オブジェクトファイルには自身のプロシージャーのリンクテーブルが存在しており、制御は同じオブジェクトファイル内からのみプロシージャーのリンクテーブルエントリに渡されます。
たとえば、プログラムが name1 を呼び出すと、制御が .PLT1 に渡されます。
最初の命令は、name1 の大域オフセットテーブルエントリのアドレスにジャンプします。大域オフセットテーブルは最初は、後続の pushq 命令のアドレスを保持します (name1 の実アドレスは保持しない)。
プログラムは再配置インデックス (index1) をスタックにプッシュします。再配置オフセットは、再配置テーブルへの 32 ビットの負ではないインデックスです。再配置テーブルは DT_JUMPREL 動的セクションエントリによって識別されます。指定された再配置エントリには R_AMD64_JMP_SLOT が存在しており、オフセットは、前の jmp 命令で使用された大域オフセットテーブルエントリを指定します。再配置エントリにはシンボルテーブルインデックスも存在しており、実行時リンカーはこれを使って参照されたシンボル name1 を取得します。
プログラムは、再配置インデックスをプッシュした後、.PLT0 (プロシージャーのリンクテーブルの先頭エントリ) にジャンプします。pushq 命令は、2 番目の大域オフセットテーブルエントリ (GOT+8) の値をスタックにプッシュして、実行時リンカーに 1 ワードの識別情報を与えます。プログラムは次に、3 番目の大域オフセットテーブルエントリ (GOT+16) のアドレスにジャンプして、実行時リンカーにジャンプします。
実行時リンカーはスタックを戻し、指定された再配置エントリを調べ、シンボルの値を取得し、name1 の実際のアドレスを大域オフセットテーブルエントリに格納し、そして宛先にジャンプします。
その後のプロシージャーのリンクテーブルエントリに対する実行は、name1 に直接渡されます (実行時リンカーの再呼び出しは行われない)。.PLT1 における jmp 命令は、pushq 命令にジャンプする代わりに、name1 にジャンプします。
LD_BIND_NOW 環境変数は、動的リンク処理の動作を変更します。この環境変数の値がヌル文字以外の場合、実行時リンカーは、プログラムに制御を渡す前に R_AMD64_JMP_SLOT 再配置エントリを処理します。