リンカーは、動的リンクを使用する実行可能ファイルを作成するとき、PT_INTERP 型のプログラムヘッダー要素を実行可能ファイルに付加し、実行時リンカーをプログラムインタプリタとして呼び出すようにシステムに指示します。exec(2) と実行時リンカーは、協調してプログラムのプロセスイメージを作成します。この処理には以下の動作が伴います。
実行可能ファイルのメモリーセグメントをプロセスイメージに付加する
共有オブジェクトメモリーセグメントをプロセスイメージに付加する
実行可能ファイルと共有オブジェクトに対して再配置を行う
実行可能ファイルの読み取りに使用されたファイル記述子を閉じる (このファイル記述子が実行時リンカーに与えられている場合)
読み込まれるオブジェクトで提供される初期設定セクションを呼び出す。「初期設定関数および終了関数」を参照
プログラムがあたかも exec(2) から直接制御を受け取ったように見えるように、プログラムに制御を渡す
リンカーはまた、実行時リンカーが実行可能オブジェクトファイルと共有オブジェクトファイルを処理するときの手助けになるさまざまなデータを作成します。先出のプログラムヘッダーセグメントで示されているとおり、これらのデータは読み込み可能セグメントに存在します。したがって、これらのデータは実行時に使用可能です。(正確なセグメント内容はプロセッサに固有であることを思い出してください。)
SHT_DYNAMIC 型の .dynamic セクションには、さまざまなデータが存在する。このセクションの始まりに存在する構造体には、他の動的リンク情報のアドレスが存在する。
SHT_HASH 型の .hash セクションには、シンボルハッシュテーブルが存在する
SHT_PROGBITS 型の .got セクションと .plt セクションには、2 つの別個のテーブルが存在する。つまり、大域オフセットテーブルとプロシージャのリンクテーブルが存在する。次の項では、オブジェクトファイルのメモリーイメージを作成するために実行時リンカーがテーブルをどのように使用するかを説明する
「プログラムの読み込み (プロセッサ固有)」で説明しているとおり、共有オブジェクトは、ファイルのプログラムヘッダーテーブルに記録されているアドレスとは異なる仮想メモリーアドレスに置かれることができます。実行時リンカーは、アプリケーションが制御を取得する前に、メモリーイメージを再配置して絶対アドレスを更新します。プログラムヘッダーテーブルに指定されているアドレスにライブラリが読み込まれた場合、絶対アドレス値は正しいのですが、通常、このようなことは起きません。
プロセス環境 (exec(2) を参照) に、空ではない値を持つ LD_BIND_NOW
という変数が存在する場合、実行時リンカーは、プログラムに制御を渡す前にすべての再配置を処理します。たとえば、以下の各環境エントリは、この動作を指定します。
LD_BIND_NOW=1 LD_BIND_NOW=on LD_BIND_NOW=off |
実行時リンカーは、プロシージャのリンクテーブルエントリを遅延評価できます。その場合、呼び出されない関数の解決/再配置に費やされる無駄を回避できます。詳細は、「プロシージャのリンクテーブル (プロセッサ固有)」を参照してください。
オブジェクトファイルが動的リンクに関係している場合、このオブジェクトファイルのプログラムヘッダーテーブルには、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 の解釈に影響します。
このオブジェクトは、さまざまに解釈される整数値を表します。
このオブジェクトは、プログラムの仮想アドレスを表わします。すでに述べたように、ファイルの仮想アドレスは、実行時にメモリーの仮想アドレスに一致しないことがあります。実行時リンカーは、動的構造体に存在するアドレスを解釈するとき、元のファイル値とメモリーのベースアドレスに基づいて実際のアドレスを計算します。整合性のため、ファイルには動的構造体内のアドレスを補正するための再配置エントリは存在しません。
ツールによる動的セクションエントリの内容の解釈をシンプルにするために、各タグの値 (2 つの特別な互換性の範囲は除く) で d_un union の解釈を決定するとします。偶数の値を持つタグは、d_ptr を使用する動的セクションのエントリを示します。奇数の値を持つタグは、d_val を使用する動的セクションのエントリ、または d_ptr と d_val のどちらも使用しない動的セクションのエントリを示します。特殊値 DT_ENCODING より小さい値を持つタグ、および DT_HIOS と DT_LOPROC 間の範囲に入る値を持つタグは、これらの規則には従いません。
表 7-43 は、実行可能オブジェクトファイルと共有オブジェクトファイルのタグ要求についてまとめています。タグに「必須」という印が付いている場合、動的リンク配列にはその型のエントリが存在しなければなりません。また、「任意」は、タグのエントリが現れてもよいですが必須ではないことを意味します
表 7-43 動的配列タグ d_tag
名前 |
値 |
d_un |
実行可能ファイル |
共有オブジェクトファイル |
---|---|---|---|---|
DT_NULL |
0 |
無視される |
必須 |
必須 |
DT_NEEDED |
1 |
d_val |
任意 |
任意 |
DT_PLTRELSZ |
2 |
d_val |
任意 |
任意 |
DT_PLTGOT |
3 |
d_ptr |
任意 |
任意 |
DT_HASH |
4 |
d_ptr |
必須 |
必須 |
DT_STRTAB |
5 |
d_ptr |
必須 |
必須 |
DT_SYMTAB |
6 |
d_ptr |
必須 |
必須 |
DT_RELA |
7 |
d_ptr |
必須 |
任意 |
DT_RELASZ |
8 |
d_val |
必須 |
任意 |
DT_RELAENT |
9 |
d_val |
必須 |
任意 |
DT_STRSZ |
10 |
d_val |
必須 |
必須 |
DT_SYMENT |
11 |
d_val |
必須 |
必須 |
DT_INIT |
12 |
d_ptr |
任意 |
任意 |
DT_FINI |
13 |
d_ptr |
任意 |
任意 |
DT_SONAME |
14 |
d_val |
無視される |
任意 |
DT_RPATH |
15 |
d_val |
任意 |
任意 |
DT_SYMBOLIC |
16 |
無視される |
無視される |
任意 |
DT_REL |
17 |
d_ptr |
必須 |
任意 |
DT_RELSZ |
18 |
d_val |
必須 |
任意 |
DT_RELENT |
19 |
d_val |
必須 |
任意 |
DT_PLTREL |
20 |
d_val |
任意 |
任意 |
DT_DEBUG |
21 |
d_ptr |
任意 |
無視される |
DT_TEXTREL |
22 |
無視される |
任意 |
任意 |
DT_JMPREL |
23 |
d_ptr |
任意 |
任意 |
DT_BIND_NOW |
24 |
無視される |
任意 |
任意 |
DT_INIT_ARRAY |
25 |
d_ptr |
任意 |
任意 |
DT_FINI_ARRAY |
26 |
d_ptr |
任意 |
任意 |
DT_INIT_ARRAYSZ |
27 |
d_val |
任意 |
任意 |
DT_FINI_ARRAYSZ |
28 |
d_val |
任意 |
任意 |
DT_RUNPATH |
29 |
d_val |
任意 |
任意 |
DT_FLAGS |
30 |
d_val |
任意 |
任意 |
DT_ENCODINGS |
32 |
未指定 |
未指定 |
未指定 |
DT_PREINIT_ARRAY |
32 |
d_ptr |
任意 |
任意 |
DT_PREINIT_ARRAYSZ |
33 |
d_val |
任意 |
任意 |
DT_LOOS |
0x6000000d |
未指定 |
未指定 |
未指定 |
DT_HIOS |
0x6ffff000 |
未指定 |
未指定 |
未指定 |
DT_VALRNGLO |
0x6ffffd00 |
未指定 |
未指定 |
未指定 |
DT_CHECKSUM |
0x6ffffdf8 |
d_val |
任意 |
任意 |
DT_PLTPADSZ |
0x6ffffdf9 |
d_val |
任意 |
任意 |
DT_MOVEENT |
0x6ffffdfa |
d_val |
任意 |
任意 |
DT_MOVESZ |
0x6ffffdfb |
d_val |
任意 |
任意 |
DT_FEATURE |
0x6ffffdfc |
d_val |
任意 |
任意 |
DT_POSFLAG_1 |
0x6ffffdfd |
d_val |
任意 |
任意 |
DT_SYMINSZ |
0x6ffffdfe |
d_val |
任意 |
任意 |
DT_SYMINENT |
0x6ffffdff |
d_val |
任意 |
任意 |
DT_VALRNGHI |
0x6ffffdff |
未指定 |
未指定 |
未指定 |
DT_ADDRRNGLO |
0x6ffffe00 |
未指定 |
未指定 |
未指定 |
DT_CONFIG |
0x6ffffefa |
d_ptr |
任意 |
任意 |
DT_DEPAUDIT |
0x6ffffefb |
d_ptr |
任意 |
任意 |
DT_AUDIT |
0x6ffffefc |
d_ptr |
任意 |
任意 |
DT_PLTPAD |
0x6ffffefd |
d_ptr |
任意 |
任意 |
DT_MOVETAB |
0x6ffffefe |
d_ptr |
任意 |
任意 |
DT_SYMINFO |
0x6ffffeff |
d_ptr |
任意 |
任意 |
DT_ADDRRNGHI |
0x6ffffeff |
未指定 |
未指定 |
未指定 |
DT_RELACOUNT |
0x6ffffff9 |
d_val |
任意 |
任意 |
DT_RELCOUNT |
0x6ffffffa |
d_val |
任意 |
任意 |
DT_FLAGS_1 |
0x6ffffffb |
d_val |
任意 |
任意 |
DT_VERDEF |
0x6ffffffc |
d_ptr |
任意 |
任意 |
DT_VERDEFNUM |
0x6ffffffd |
d_val |
任意 |
任意 |
DT_VERNEED |
0x6ffffffe |
d_ptr |
任意 |
任意 |
DT_VERNEEDNUM |
0x6fffffff |
d_val |
任意 |
任意 |
DT_AUXILIARY |
0x7ffffffd |
d_val |
未指定 |
任意 |
DT_USED |
0x7ffffffe |
d_val |
任意 |
任意 |
DT_FILTER |
0x7fffffff |
d_val |
未指定 |
任意 |
DT_LOPROC |
0x70000000 |
未指定 |
未指定 |
未指定 |
DT_SPARC_REGISTER |
0x70000001 |
d_val |
任意 |
任意 |
DT_HIPROC |
0x7fffffff |
未指定 |
未指定 |
未指定 |
DT_NULL タグが付けられたエントリは、_DYNAMIC 配列の終わりを示します。
この要素は、空文字で終わっている文字列の文字列テーブルオフセットを保持し、必要な依存性の名前を与えます。オフセットは、DT_STRTAB エントリに記録されるテーブルへのインデックスです。これらの名前については、「共有オブジェクトの依存性」を参照してください。動的配列には、この型の複数のエントリが存在できます。これらのエントリの相対順序は意味がありますが、他の型のエントリに対するこれらのエントリの相対順序には意味がありません。
この要素は、プロシージャのリンクテーブルに関連付けられている再配置エントリの合計サイズ (単位: バイト) を保持します。DT_JMPREL 型のエントリが存在する場合、DT_PLTRELSZ 型のエントリも必要です。
この要素は、プロシージャのリンクテーブルまたは大域オフセットテーブルに関連付けられているアドレスを保持します。
この要素は、シンボルハッシュテーブル (「ハッシュテーブル」に記述されている) を指し示します。ハッシュテーブルは、DT_SYMTAB 要素で示されるシンボルテーブルを参照します。
この要素は、文字列テーブル (この章の前半部分で記述している) のアドレスを保持します。文字列テーブルには、実行時リンカーが必要とするシンボル名、依存性名、および他の文字列が存在します。
この要素は、再配置テーブル (この章の前半部分で記述している) のアドレスを保持します。32 ビットクラスのファイルに対しては Elf32_Sym エントリ、64 ビットクラスのファイルに対しては ELF 64_sym エントリが存在します。
この要素は、再配置テーブル (この章の前半部分で記述している) のアドレスを保持します。再配置テーブルのエントリ (32 ビットファイルクラス用の Elf32_Rela、64 ビットファイルクラス用の ELF 64_Rela など) には、明示的加数が存在します。
オブジェクトファイルには、複数の再配置セクションが存在できます。リンカーは、実行可能オブジェクトファイルまたは共有オブジェクトファイルの再配置テーブルを作成するとき、これらのセクションを連結して単一のテーブルを作成します。これらの各セクションはオブジェクトファイル内で個々に独立したままですが、実行時リンカーは単一のテーブルとして扱います。実行時リンカーは、実行可能ファイルのプロセスイメージを作成したり、またはプロセスイメージに共有オブジェクトを付加したりするとき、再配置テーブルを読み取り、関連付けられている動作を実行します。
この要素が存在する場合、動的構造体には DT_RELASZ 要素と DT_RELAENT 要素も存在しなければなりません。再配置がファイルに対して「必須」の場合、DT_RELA または DT_REL が使用可能です (これらは両方とも使用可能ですが、必要ではありません)。
この要素は、DT_RELA 再配置テーブルの合計サイズ (単位: バイト) を保持します。
この要素は、DT_RELA 再配置エントリのサイズ (単位: バイト) を保持します。
この要素は、文字列テーブルのサイズ (単位: バイト) を保持します。
この要素は、シンボルテーブルエントリのサイズ (単位: バイト) を保持します。
この要素は、初期化関数のアドレスを保持します (初期化関数については、「初期設定関数および終了関数」で記述しています)。
この要素は、終了関数のアドレスを保持します (終了関数については、「初期設定関数および終了関数」に記述されています)。
この要素は、空文字で終わっている文字列の文字列テーブルオフセットを保持し、共有オブジェクトの名前を与えます。このオフセットは、DT_STRTAB エントリに記録されているテーブルへのインデックスです。これらの名前については、「共有オブジェクトの依存性」を参照してください。
この要素は、ライブラリ検索パス (空文字で終わっている文字列) 文字列テーブルオフセットを保持します (ライブラリ検索パスについては、「依存関係を持つ共有オブジェクト」で記述しています)。このオフセットは、DT_STRTAB エントリに記録されているテーブルへのインデックスです。この使用は、DT_RUNPATH に置き換えられました。
この要素は、オブジェクトが、リンク編集時に適用されたシンボル結合を含むかどうかを示します。「-Bsymbolic の使用」も参照してください。この使用は、DF_SYMBOLIC に置き換えられました。
この要素は DT_RELA に似ていますが、テーブルに暗黙の加数 (32 ビットファイルクラス用の Elf32_Rel など) が存在する点が異なります。この要素が存在する場合、動的構造体には DT_RELSZ 要素と DT_RELENT 要素も存在しなければなりません。
この要素は、DT_REL 再配置テーブルの合計サイズ (単位: バイト) を保持します。
この要素は、DT_REL 再配置エントリのサイズ (単位: バイト) を保持します。
この要素は、プロシージャのリンクテーブルが参照する再配置エントリの型を指定します。d_val 要素は、必要に応じて DT_REL または DT_RELA を保持します。1 つのプロシージャのリンクテーブルでは、すべての再配置は、同じ再配置を使用しなければなりません。
この要素は、デバッグ時に使用されます。
この構成要素が存在しない場合、再配置エントリが書き込み不可セグメント (プログラムヘッダーテーブルのセグメントのアクセス権で指定されます) に対する変更を発生させてはならないことを意味します。この構成要素が存在する場合、1 つまたは複数の再配置エントリが書き込み不可セグメントに対する変更を要求する可能性があり、実行時リンカーはそれに応じて対応することができます。この使用は、DF_TEXTREL フラグに置き換えられました。
このエントリが存在する場合、このエントリの d_ptr 要素は、プロシージャのリンクテーブルにのみ関連付けられている再配置エントリのアドレスを保持します。これらの再配置エントリを分離しておくと、遅延結合が有効の場合、実行時リンカーはプロセス初期化時にこれらの再配置エントリを無視します。このエントリが存在する場合、DT_PLTRELSZ 型と DT_PLTREL 型の関連エントリも存在しなければなりません。
このエントリが存在する場合、このエントリの d_val 要素は、さまざまな状態フラグを保持します。これらのフラグは、.dynamic セクション内の直後の DT_* エントリに適用されます。
このエントリが共有オブジェクトまたは実行可能ファイル内に存在する場合、このエントリは、プログラムに制御を渡す前に、このエントリを含むオブジェクトについてのすべての再配置を処理するよう実行時リンカーに指示します。環境で指定された、または dlopen(3DL) を介した場合、このエントリの存在は、このオブジェクトについてレイジー結合を使用するようにという指示よりも優先されます。この使用は、DF_BIND_NOW フラグに置き換えられました。
この要素は、「初期設定関数および終了関数」に記述されている、初期設定関数へのポインタの配列のアドレスを保持します。
この要素は、「初期設定関数および終了関数」に記述されている、終了関数へのポインタの配列のアドレスを保持します。
この要素は、「依存関係を持つ共有オブジェクト」に記述されている、NULL で終わる検索ライブラリの検索パス文字列の文字列テーブルオフセットを保持します。このオフセットは、DT_STRTAB エントリに記録されたテーブルに対するインデックスです。
この要素は、読み込まれるオブジェクトに特有のフラグ値を保持します。各フラグ値は、DF_flag_name という名前を持ちます。定義された値とその意味は後述されています。その他のすべての値は予約されています。
DT_ENCODING と等しいかそれより大きく、かつ DT_LOOS より小さい値は、前述の d_un union の解釈の規則に従います。
この要素は、「初期設定関数および終了関数」に記述されている、初期設定前関数へのポインタの配列のアドレスを保持します。DT_PREINIT_ARRAY テーブルは実行可能ファイル内でのみ処理されます。共有オブジェクト内に含まれている場合は無視されます。
この要素は、DT_PREINIT_ARRAY エントリにポイントされた初期設定前関数の配列のサイズ (単位: バイト) を保持します。1 つのオブジェクトが DT_PREINIT_ARRAY エントリを持つ場合は、このオブジェクトは DT_PREINIT_ARRAYSZ エントリも持つ必要があります。DT_PREINIT_ARRAY と同様、このエントリは共有オブジェクト内に現れた場合は無視されます。
この範囲内の値 (両端の値も含む) は、オペレーティングシステム特有のセマンティクスのために予約されています。このような値はすべて、前述の d_un union の解釈の規則に従います。
この要素は、SHT_SUNW_syminfo セクションのアドレスを保持します。
この要素は、SHT_SUNW_syminfo テーブルエントリのサイズ (単位: バイト) を保持します。
この要素は、SHT_SUNW_syminfo セクションのサイズ (単位: バイト) を保持します。
この要素は、バージョン定義テーブル (この章の前半部分で記述している) のアドレスを保持します。32 ビットクラスのファイルに対しては Elf32_Verdef エントリ、64 ビットクラスのファイルに対しては Elf64_Verdef エントリが存在します。詳細は、「バージョン定義セクション」を参照してください。これらのエントリ内の要素には、DT_STRTAB エントリに記録されているテーブルへのインデックスが存在します。
この要素は、バージョン定義テーブルのエントリ数を指定します。
この要素は、バージョン依存性テーブル (この章の前半部分で記述している) のアドレスを保持します。32 ビットクラスのファイルに対しては Elf32_Verneed エントリ、64 ビットクラスのファイルに対しては Elf64_Verneed エントリが存在します。詳細は、「バージョン依存セクション」を参照してください。これらのエントリ内の要素には、DT_STRTAB エントリに記録されているテーブルへのインデックスが存在します。
この要素は、バージョン依存性テーブルのエントリ数を指定します。
すべての Elf32_Rela あるいは Elf64_Rela R_*_RELATIVE 再配置は単一のブロックに入っており、このエントリはそのブロックのエントリ数を指定します。これにより、実行時リンカー は RELATIVE 再配置の処理を合理化できます。
すべての Elf32_Rel R_*_RELATIVE 再配置は単一のブロックに入っており、このエントリはそのブロックのエントリ数を指定します。これにより、実行時リンカーは RELATIVE 再配置の処理を合理化できます。
この要素は、空文字で終わっている文字列 (オブジェクトに命名している) の文字列テーブルオフセットを保持します。このオフセットは、DT_STRTAB エントリに記録されているテーブルへのインデックスです。補助オブジェクト内のシンボルは、このオブジェクト内のシンボルに優先して使用されます。
この要素は、オブジェクトを示す文字列 (空文字で終わっている文字列) の文字列テーブルオフセットを保持します。このオフセットは、DT_STRTAB エントリに記録されているテーブルへのインデックスです。このオブジェクトのシンボルテーブルは、命名されたオブジェクトのシンボルテーブルのフィルタとして使用されます。
この要素は、オブジェクトの選択されたセクションの簡単なチェックサムを保持します。gelf_checksum(3ELF) を参照してください。
この要素は、SHT_SUNW_move で定義されるセクションの合計サイズを保持します。「移動セクション」を参照してください。
この要素は、DT_MOVETAB 移動エントリのサイズ (単位: バイト) を保持します。
この要素は、移動テーブル (この章の前半部分で記述している) のアドレスを保持します。移動テーブルのエントリには、32 ビットクラスのファイルに対する Elf32_Move エントリ、64 ビットクラスのファイルに対する Elf64_Move エントリが存在します。
この要素は、構成ファイルを定義する文字列 (ヌル文字で終端している文字列) の文字列テーブルオフセットを保持します。このオフセットは DT_STRTAB エントリに記録されているテーブルへのインデックスです。構成ファイルは、検索パス、ディレクトリキャッシュ、または代替オブジェクトを提供し、通常 DT_CONFIG エントリを指定するアプリケーションに固有です。crle(1) およびリンカーの -c オプションを参照してください。
この要素は、1 つあるいは複数の監査ライブラリを定義する文字列 (ヌル文字で終端している文字列) の文字列テーブルオフセットを保持します。このオフセットは DT_STRTAB エントリに記録されているテーブルへのインデックスです。監査ライブラリは実行時に、このオブジェクトを監査するために使用されます。リンカーの-P オプションを参照してください。
この要素は、1 つあるいは複数の監査ライブラリを定義する文字列 (ヌル文字で終端している文字列) の文字列テーブルオフセットを保持します。このオフセットは DT_STRTAB エントリに記録されているテーブルへのインデックスです。監査ライブラリは実行時に、このオブジェクトを監査するために使用されます。リンカーの -p オプションを参照してください。
このエントリが存在する場合、このエントリの d_val 要素は、さまざまな状態フラグを保持します。表 7-45 を参照してください。
このエントリが存在する場合、このエントリの d_val 構成要素は実行時機能要求を定義する DTF_1_ フラグを保持します。関数 _check_rtld_feature() は、通常、コンパイラ供給の起動コードから実行時に呼び出され、要求された機能が存在することを確認します。「機能チェッカ」、および表 7-47 を参照してください。
この範囲の値は、動的構造体内の d_un.d_val フィールドで使用されます。
この範囲の値は、動的構造体内の d_un.d_ptr フィールドで使用されます。ELF オブジェクトが作成後に調整された場合、これらのエントリも更新する必要があります。
この要素には、STT_SPARC_REGISTER シンボルのインデックスが存在します。シンボルテーブルの各 STT_SPARC_REGISTER シンボルテーブルエントリには、これらのエントリの 1 つが存在します。
この範囲の値は、プロセッサに固有の使用方法に対して予約されています。
動的配列の最後にある DT_NULL 要素と、DT_NEEDED と DT_POSFLAG_1 要素の相対的な順序を除くと、エントリはどの順序で現れてもかまいません。表に示されていないタグ値は予約されています。
次の表に示す動的状態フラグ DT_FLAGS が現在、使用可能です。
表 7-44 動的タグ DT_FLAGS
名前 |
値 |
意味 |
---|---|---|
DF_ORIGIN |
0x1 |
$ORIGIN 処理が必要 |
DF_SYMBOLIC |
0x2 |
シンボリックシンボル解決が必要 |
DF_TEXTREL |
0x4 |
テキストの再配置が存在する |
DF_BIND_NOW |
0x8 |
非レイジー結合が必要 |
オブジェクトに $ORIGIN 処理が必要であることを示します。「関連する依存関係の配置」を参照してください。
オブジェクトが、リンク編集中に適用されたシンボリック結合を含むことを示します。「-Bsymbolic の使用」も参照してください。
このフラグが無いことは、再配置エントリが書き込み不可セグメント (プログラムヘッダーテーブル内のセグメントのアクセス権で指定される) に対する変更を発生させてはならないことを示します。このフラグが存在する場合は、1 つまたは複数の再配置エントリが書き込み不可セグメントに対する変更を要求する可能性があり、実行時リンカーはそれに応じて対応することができます。
共有オブジェクトまたは実行可能ファイル内で設定された場合、このフラグは、プログラムに制御を渡す前にこのエントリを含むオブジェクトについてすべての再配置を処理するよう動的リンカーに指示します。環境で指定された、または dlopen(3DL) を介した場合、このエントリの存在は、このオブジェクトについてレイジー結合を使用するようにという指示よりも優先されます。この状態は、リンカーの -z now オプションを使用してオブジェクト内に記録されます。
次の表に示す動的状態フラグ DT_FLAGS_1 が現在、使用可能です。
表 7-45 動的タグ DT_FLAGS_1
名前 |
値 |
意味 |
---|---|---|
DF_1_NOW |
0x1 |
完全な再配置処理を行います |
DF_1_GLOBAL |
0x2 |
未使用 |
DF_1_GROUP |
0x4 |
オブジェクトがグループの構成要素であることを示します |
DF_1_NODELETE |
0x8 |
オブジェクトがプロセスから削除できないことを示します |
DF_1_LOADFLTR |
0x10 |
フィルティー (1 つまたは複数)の即時読み込みを保証します |
DF_1_INITFIRST |
0x20 |
オブジェクトの初期化を最初に実行します |
DF_1_NOOPEN |
0x40 |
オブジェクトを dlopen(3DL) で使用できません |
DF_1_ORIGIN |
0x80 |
$ORIGIN 処理が必要です |
DF_1_DIRECT |
0x100 |
直接結合が有効です |
DF_1_INTERPOSE |
0x400 |
オブジェクトは割り込み処理です |
DF_1_NODEFLIB |
0x800 |
デフォルトのライブラリ検索パスを無視します |
DF_1_NODUMP |
0x1000 |
オブジェクトを dldump(3DL) でダンプできません |
DF_1_CONFALT |
0x2000 |
オブジェクトは代替構成です |
DF_1_ENDFILTEE |
0x4000 |
フィルティーがフィルタの検索を終了します |
DF_1_DISPRELDNE |
0x8000 |
ディスプレイスメント再配置の終了 |
DF_1_DISPRELPND |
0x10000 |
ディスプレイスメント再配置の保留 |
オブジェクトが読み込まれると、すべての再配置処理が行われます。「再配置が実行されるとき」を参照してください。この状態フラグは、リンカーの -z now オプションを使うことによって有効になります。
オブジェクトがグループの構成要素であることを示します。「シンボル検索」を参照してください。この状態フラグは、リンカーの -B group オプションを使うことによって有効になります。
オブジェクトがプロセスから削除できないことを示します。したがってオブジェクトは、dlopen(3DL) で直接または依存性としてプロセスに読み込まれた場合、dlclose(3DL) で読み込み解除できません。この状態フラグは、リンカーの -z nodelete オプションを使うことによってオブジェクトに有効になります。
この状態フラグは、「フィルタ」に対してのみ意味があります (「フィルタとしての共有オブジェクト」を参照)。フィルタが読み込まれると、関連付けられているすべてのフィルティーが直ちに処理されます (「フィルティーの処理」を参照)。この状態フラグは、リンカーの -z loadfltr オプションを使うことによってオブジェクトに有効になります。
オブジェクトが読み込まれると、このオブジェクトと共に読み込まれた他のオブジェクトより先に、このオブジェクトの初期化セクションが実行されます。「デバッギングエイド」を参照してください。この特殊な状態フラグは、libthread.so.1 で使用されることを意図しています。この状態フラグは、リンカーの -z initfirst オプションを使うことによって有効になります。
dlopen(3DL) を使ってオブジェクトを実行中プロセスに追加できないことを示します。この状態フラグは、リンカーの -z nodlopen オプションを使うことによってオブジェクトに有効になります。
オブジェクトに対し $ORIGIN 処理が必要なことを表します。「関連する依存関係の配置」を参照してください。
オブジェクトには、直接結合情報が存在します。「直接結合」を参照してください。
オブジェクトシンボルテーブルは、起動性実行可能プログラム以外のすべてのシンボルの前に割り込みされます。この状態フラグは、リンカーの -z interpose オプションを使うことによって有効になります。「直接結合」 を参照してください。
このオブジェクトの依存関係を検索する場合、デフォルトのライブラリ検索パスを無視します。「実行時リンカーが検索するディレクトリ」を参照してください。この状態は、リンカーの -z nodefaultlib オプションを使用してオブジェクトに記録されます。
このオブジェクトは dldump(3DL) によってダンプできません。このオプションの候補には、libdl.so.1 などのフィルタが含まれます。libdl.so.1 は、crle(1) を使用して構成ファイルを生成する際に含めることができます。この状態は、リンカーの -z nodump オプションを使用してオブジェクトに記録されます。
crle(1) によって生成された代替構成オブジェクトであることを示します。この状態により実行時リンカーがトリガーされ、構成ファイル $ORIGIN/ld.config.app-name が検索されます。
この状態はフィルティーに対してのみ意味があります (「フィルタとしての共有オブジェクト」を参照)。このフラグが設定されていると、それ以後のフィルティーに対するフィルタ検索は行われません (「補助検索の縮小」を参照)。この状態をオブジェクトに記録するには、リンカーの -z endfiltee オプションを使用します。
このオブジェクトにディスプレイスメント再配置が適用されたことを示します。再配置が適用されると再配置レコードは破棄されるため、オブジェクト内の再配置レコードはもはや存在しません。「ディスプレイスメント再配置」を参照してください。
このオブジェクトのディスプレイスメント再配置が保留されていることを示します。再配置レコードはオブジェクト内に存在しているため、再配置は実行時に完了することができます。「ディスプレイスメント再配置」 を参照してください。
名前 |
値 |
意味 |
---|---|---|
DF_P1_LAZYLOAD |
0x1 |
後続のオブジェクトは、レイジーロードされる |
DF_P1_GROUPPERM |
0x2 |
後続のオブジェクトは、グループパーミッションの状態でロードされる |
後続の DT_* エントリは、読み込まれるオブジェクトを示します。このオブジェクトのローディングは、Syminfo テーブルエントリで示されるシンボル結合がこのオブジェクトを明確に参照するまで遅延されます。この状態は、リンカーの -z lazyload オプションを使用してオブジェクト内に記録されます。
後続の DT_* エントリは、読み込まれるオブジェクトを示します。このオブジェクト内のシンボルは、一般的なシンボル解決に対しては使用できません。このオブジェクト内のシンボルは、このオブジェクトを読み込ませたオブジェクト (1 つまたは複数) に対してのみ使用可能です。この状態は、リンカーの -z groupperm オプションを使用してオブジェクト内に記録されます。
表 7-47 に示す動的機能フラグが現在、使用可能です。
表 7-47 動的機能フラグ DT_FEATURE
名前 |
値 |
意味 |
---|---|---|
DTF_1_PARINIT |
0x1 |
部分的な初期化機能が必要 |
DTF_1_CONFEXP |
0x2 |
構成ファイルが必要 (代替オブジェクトについては、crle(1) によって設定される。DF_1_CONFALT と同じ効果がある) |
実行時リンカーがオブジェクトファイルのメモリーセグメントを作成するとき、依存性 (動的構造体の DT_NEEDED エントリに記録される) は、プログラムのサービスを提供するためにどの共有オブジェクトが必要であるかを示します。参照された共有オブジェクトとそれが依存するものを繰り返し結合することによって、実行時リンカーは完全なプロセスイメージを生成します。
実行時リンカーは、記号参照を解決するとき、幅優先検索を使用してシンボルテーブルを調べます。つまり、実行時リンカーはまず実行可能プログラム自身のシンボルテーブルを参照し、次に DT_NEEDED エントリのシンボルテーブルを (順番に) 参照し、次に第 2 レベルの DT_NEEDED エントリといった具合に参照します。
共有オブジェクトが依存性リストにおいて複数回参照されるときでも、実行時リンカーはこの共有オブジェクトをプロセスに 1 回だけ結合します。
依存リストに示されている名前は、オブジェクトファイルの作成に使用される共有オブジェクトの DT_SONAME 文字列またはパス名です。
一般に位置に依存しないコードには絶対仮想アドレスは存在できません。大域オフセットテーブルには内部で使用するデータの絶対アドレスが存在しており、したがって、位置からの独立性とプログラムのテキストの共有性を低下させることなくアドレスが使用可能になります。プログラムは、位置に依存しないアドレス指定を使用して大域オフセットテーブルを参照し、絶対値を抜き出すことで位置に依存しない参照を、絶対位置に向け直します。
最初は、大域オフセットテーブルには再配置エントリで要求される情報が存在します (詳細は、「再配置」を参照)。システムが読み込み可能オブジェクトファイルのメモリーセグメントを作成した後、実行時リンカーが再配置エントリを処理します。これらの再配置エントリのいくつかは、R_SPARC_GLOB_DAT 型 (SPARC の場合) または R_386_GLOB_DAT 型 (IA の場合) であり、大域オフセットテーブルを参照します。
実行時リンカーは、関連付けられているシンボル値を判定し、絶対アドレスを計算し、適切なメモリーテーブルエントリにを正しい値を設定します。リンカーがオブジェクトファイルを作成するとき、絶対アドレスは認識されていませんが、実行時リンカーはすべてのメモリーセグメントのアドレスを認識しており、したがって、これらのメモリーセグメントに存在するシンボルの絶対アドレスを計算できます。
プログラムがシンボルの絶対アドレスへの直接アクセスを必要とする場合、このシンボルには大域オフセットテーブルエントリが存在します。実行可能ファイルと共有オブジェクトには別個の大域オフセットテーブルが存在するので、シンボルのアドレスはいくつかのテーブルに現れることがあります。実行時リンカーは、プロセスイメージのコードに制御を与える前に大域オフセットテーブルのすべての再配置を処理します。したがって、実行時に絶対アドレスが使用可能になります。
テーブルのエントリ 0 は、_DYNAMIC シンボルで参照される動的構造体のアドレスを保持するために予約されています。したがって、実行時リンカーなどのプログラムは、再配置エントリを処理していなくても自身の動的構造体を見つけることができます。このことは、実行時リンカーにとって特に重要です。なぜなら、実行時リンカーは他のプログラムに頼ることなく自身を初期化してメモリーイメージを再配置しなければならないからです。
システムは、異なるのプログラムの同じ共有オブジェクトに対して、異なるメモリーセグメントアドレスを与えることがあります。さらに、システムはプログラムを実行するごとに異なるライブラリアドレスを与えることさえあります。しかし、プロセスイメージがいったん作成されると、メモリーセグメントのアドレスは変更されません。プロセスが存在する限り、そのプロセスのメモリーセグメントは固定仮想されたアドレスに存在します。
大域オフセットテーブルの形式と解釈は、プロセッサに固有です。SPARC プロセッサと IA プロセッサの場合、_GLOBAL_OFFSET_TABLE_ シンボルは、テーブルをアクセスするために使用できます。64 ビットコードの場合、シンボルタイプは Elf64_Addr の配列です。
extern Elf32_Addr _GLOBAL_OFFSET_TABLE_[]; |
シンボル _GLOBAL_OFFSET_TABLE_ は、.got セクションの中央に存在でき、その場合は、負の添字と負でない添字の両方がアドレスの配列になることができます。
大域オフセットテーブルは位置に依存しないアドレスの計算を絶対位置に変換するので、プロシージャのリンクテーブルは位置に依存しない関数呼び出しを絶対位置に変換します。リンカーは、ある 1 つの実行可能オブジェクトまたは共有オブジェクトから別の実行可能オブジェクトまたは共有オブジェクトへの実行転送 (関数呼び出しなど) を解決できません。したがって、リンカーはプログラム転送制御をプロシージャのリンクテーブルのエントリに与えます。
SPARC アーキテクチャの場合、プロシージャのリンクテーブルは私用 (private) データに存在します。実行時リンカーは、宛先の絶対アドレスを判定し、これらの絶対アドレスに従ってプロシージャのリンクテーブルのメモリーイメージに変更を加えます。このようにして実行時リンカーは、位置からの独立性とプログラムのテキストの共有性を低下させることなくエントリを向け直します。実行可能ファイルと共有オブジェクトファイルには、別個のプロシージャのリンクテーブルが存在します。
最初の 4 つのプロシージャのリンクテーブルエントリは、予約されます。テーブルの各エントリは 3 ワード (12 バイト) を占めており、最後のテーブルエントリの後には nop 命令が続きます。64 ビット SPARC オブジェクトの場合、各エントリは 8 つの命令 (32 バイト) を占めており、32 バイト境界で整列されなければなりません (テーブル全体は 256 バイト境界で整列されなければなりません)。
再配置テーブルは、プロシージャのリンクテーブルに関連付けられています。_DYNAMIC 配列の DT_JMP_REL エントリは、最初の再配置エントリの位置を与えます。再配置テーブルには、各プロシージャのリンクテーブルエントリに対して 1 つのエントリが同じ順番で存在します。最初の 4 つのエントリを除き、再配置タイプは R_SPARC_JMP_SLOT であり、再配置オフセットは関連付けられているプロシージャのリンクテーブルエントリの先頭バイトのアドレスを指定し、シンボルテーブルインデックスは該当するシンボルを参照します。
プロシージャのリンクテーブルの説明のため、表 7-48 は 4 つのエントリを示しています。最初の 2 つのエントリは予約されている最初の 4 つのエントリの内の 2 つであり、3 番目のエントリは name1 に対する呼び出しであり、4 番目のエントリは name2 に対する呼び出しです。この例では、name2 のエントリがテーブルの最後のエントリであることを前提としており、後に続く nop 命令が示されています。左欄は、動的リンクが行われる前のオブジェクトファイルの命令を示しています。右欄は、実行時リンカーがプロシージャのリンクテーブルエントリを修正することにしてとり得る方法を示しています。
表 7-48 SPARC: プロシージャのリンクテーブルの例オブジェクトファイル | メモリーセグメント |
---|---|
.PLT0: unimp unimp unimp .PLT1: unimp unimp unimp ... |
.PLT0: save %sp,-64,%sp call runtime-linker nop .PLT1: .word identification unimp unimp ... |
... .PLT101: sethi (.-.PLT0),%g1 ba,a .PLT0 nop .PLT102: sethi (.-.PLT0),%g1 ba,a .PLT0 nop |
... .PLT101: sethi (.-.PLT0),%g1 sethi %hi(name1),%g1 jmp1 %g1+%lo(name1),%g0 .PLT102: sethi (.-.PLT0),%g1 sethi %hi(name2),%g1 jmp1 %g1+%lo(name2),%g0 |
nop |
nop |
実行時リンカーとプログラムは、以下の手順に従ってプロシージャのリンクテーブル内のシンボル参照を協調して解決します。ただし、以下に記述されている手順は、単に説明用のためのものです。実行時リンカーの正確な実行時動作については、記述されていません。
実行時リンカーは、プログラムのメモリーイメージを最初に作成するとき、プロシージャのリンクテーブルの初期エントリに、実行時リンカー自身のルーチンの 1 つに制御を渡すように変更を加える。実行時リンカーはまた、識別情報 (identification) を 2 番目のエントリに格納する。リンカー自身のルーチンが制御を受け取った時、このワードを調べることで、このルーチンを呼び出したオブジェクトを見つけることができる
他のすべてのプロシージャのリンクテーブルエントリは、最初は先頭エントリに渡される。これで、実行時リンカーは各テーブルエントリの最初の実行時に制御を取得する。たとえば、プログラムが name1 を呼び出すと、制御が .PLT101 に渡される
sethi 命令は、現在のプロシージャのリンクテーブルエントリと最初のプロシージャのリンクテーブルエントリの距離を計算する。つまり、.PLT101 と .PLT0 の距離を計算する。この値は、%g1 レジスタの最上位 22 ビットを占める。この例では、実行時リンカーが制御を受け取ると、%g1 には 0x12f000 が格納される
次に、ba,a 命令が .PLT0 にジャンプして、スタックフレームを作成し、実行時リンカーを呼び出す
実行時リンカーは、識別情報の値を使うことによってオブジェクトのデータ構造体 (再配置テーブルを含む) を取得する
実行時リンカーは、%g1 値をシフトしプロシージャのリンクテーブルエントリのサイズで除算することで、name1 の再配置エントリのインデックスを計算する。再配置エントリ 101 には R_SPARC_JMP_SLOT が存在し、オフセットは .PLT101 のアドレスを指定し、シンボルテーブルインデックスは name1 を参照。したがって、実行時リンカーはシンボルの実際の値を取得し、スタックを戻し、プロシージャのリンクテーブルエントリに変更を加え、本来の宛先に制御を渡す
実行時リンカーは、メモリーセグメント欄に示された命令シーケンスを作成しなければならないということはないのですが、作成することがあります。もし作成した場合は、いくつかの点についてより詳しい説明が必要です。
コードを再入可能にするためには、プロシージャのリンクテーブルの命令は、特定の順番で変更が加えられる。実行時リンカーが関数のプロシージャのリンクテーブルエントリを修正中に信号が到達した場合、信号処理コードは、予想可能であり (かつ正しい) 結果を与える元の関数を呼び出すことができなければならない
実行時リンカーは、2 つのワードに変更を加えてエントリを変換する。実行時リンカーは、各ワードを自動的に更新する。再入可能性は、まず nop を jmp1 命令で上書きし、次に ba,a を sethi 命令で上書きすることで実現される。再入可能関数呼び出しがワードの 2 つの更新間に発生した場合、jmp1 は ba,a 命令の遅延スロットに存在し、遅延命令を取り消す。したがって、実行時リンカーは再び制御を取得す。実行時リンカーに対する両方の呼び出しで、同じプロシージャのリンクテーブルエントリに変更が加えられますが、これらの変更は互いに干渉しない
プロシージャのリンクテーブルエントリの最初の sethi 命令は、1 つ前のエントリの jmp1 命令の遅延スロットを埋める。sethi は %g1 レジスタの値を変更するが、以前の内容を捨てても問題はない
変換後、最後のプロシージャのリンクテーブルエントリ (前述した .PLT102) は、jmp1 に対して遅延命令を必要とする。要求されている後続の nop は、この遅延スロットを埋める
LD_BIND_NOW
環境変数は、動的リンクの動作を変更します。この環境変数の値が空文字列以外の場合、実行時リンカーは、プログラムに制御を渡す前に R_SPARC_JMP_SLOT 再配置エントリ (プロシージャのリンクテーブルエントリ) を処理します。LD_BIND_NOW
が空文字列の場合、実行時リンカーは、各テーブルエントリの最初の実行時にリンクテーブルエントリを評価します。
IA アーキテクチャの場合、プロシージャのリンクテーブルは共有テキストに存在しますが、私用大域オフセットテーブルのアドレスを使用します。実行時リンカーは、宛先の絶対アドレスを判定し、これらの絶対アドレスに従って大域オフセットテーブルのメモリーイメージに変更を加えます。このようにして実行時リンカーは、位置からの独立性とプログラムのテキストの共有性を低下させることなくエントリを向け直します。実行可能ファイルと共有オブジェクトファイルには、別個のプロシージャのリンクテーブルが存在します。
表 7-49 IA: プロシージャのリンクテーブルの例
.PLT0: pushl got_plus_4 jmp *got_plus_8 nop; nop nop; nop .PLT1: jmp *name1_in_GOT pushl $offset jmp .PLT0@PC .PLT2: jmp *name2_in_GOT pushl $offset jmp .PLT0@PC ... |
.PLT0: pushl 4(%ebx) jmp *8(%ebx) nop; nop nop; nop .PLT1: jmp *name1@GOT(%ebx) pushl $offset jmp .PLT0@PC .PLT2: jmp *name2@GOT(%ebx) pushl $offset jmp .PLT0@PC ... |
実行時リンカーとプログラムは、以下の手順に従ってプロシージャのリンクテーブル内と大域オフセットテーブル内のシンボル参照を協調して解決します。
実行時リンカーは、プログラムのメモリーイメージを最初に作成するとき、大域オフセットテーブルの 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 再配置エントリ (プロシージャのリンクテーブルエントリ) を処理します。LD_BIND_NOW
が空文字列の場合、実行時リンカーは、各テーブルエントリの最初の実行時にリンクテーブルエントリを評価します。