アプリケーションが要求する依存関係をすべて読み込んだ後、実行時リンカーは各オブジェクトを処理し、必要な再配置すべてを実行します。
オブジェクトのリンク編集中に、入力再配置の可能なオブジェクトとともに提供された再配置の情報が、出力ファイルに適用されます。ただし、動的実行プログラムまたは共有オブジェクトを作成している場合には、ほとんどの再配置は、リンク編集時には完了できません。それは、再配置には、オブジェクトがメモリー内に読み込まれる時だけ入手することができる、論理アドレスが必要だからです。このような場合、リンカーは新しい再配置を出力ファイルイメージの一部として記録します。実行時リンカーは、新しい再配置レコードを処理する必要があります。
再配置のさまざまなタイプの詳細については、再配置型 (プロセッサ固有) を参照してください。再配置には、大きく分けて 2 つのタイプがあります。
オブジェクトの再配置記録は、dump(1) を使用して表示できます。次の例では、ファイル libbar.so.1 には、「大域オフセットテーブル」(.got セクション) が更新される必要があることを示す、2 つの再配置記録が組み込まれています。
$ dump -rvp libbar.so.1 libbar.so.1: .rela.got: Offset Symndx Type Addend 0x10438 0 R_SPARC_RELATIVE 0 0x1043c foo R_SPARC_GLOB_DAT 0 |
最初の再配置は、単純な相対再配置です。このことは、再配置タイプとシンボルインデックス (Symndx) フィールドがゼロであることから分かります。この再配置では、オブジェクトがメモリーに読み込まれたベースアドレスを使用して、関連する .got オフセットを更新する必要があります。
2 番目の再配置では、シンボル foo のアドレスが必要です。この再配置を完了させるには、実行時リンカーが、これまでに読み込まれた動的実行プログラムと依存関係からこのシンボルを配置する必要があります。
オブジェクトがシンボルを必要とする場合、実行時リンカーはそのシンボルを、オブジェクトのシンボル要求の検索範囲と、プロセス内の各オブジェクトによって提供されるシンボルの可視性に基づいて検索します。これらの属性は、読み込まれる時に、オブジェクトのデフォルトとして使用され、dlopen(3DL) の特別なモードとしても使用され、さらに、場合によっては、オブジェクトの構築時に、オブジェクト内に記録されます。
平均的なユーザーであれば、動的実行プログラムとその依存関係、および dlopen(3DL) を通じて入手したオブジェクトに適用されるデフォルトのシンボル検索モードが、理解できるようになります。前者の検索モードについては、次の項、デフォルトの検索で概要を説明します。種々のシンボル検索に活用できる後者については、シンボル検索で説明しています。
動的なオブジェクトで直接結合を行うと、別のシンボル検索モデルが提供されます。 このモデルでは、実行時リンカーは、リンク編集時に結合されたオブジェクトからシンボルを直接検索します。直接結合を参照してください。
動的実行プログラムと、ともに読み込まれるすべての依存関係には、「ワールド」検索範囲と、「大域」シンボル可視性が割り当てられます 。シンボル検索を参照してください。 実行時リンカーは、動的実行プログラムまたはこの実行プログラムとともに読み込まれた依存関係すべてを調べてシンボルを検出するために、オブジェクトを順番に検索します。まず動的実行プログラムから検索してから、 オブジェクトが読み込まれた順番に依存関係を検索します。
前の項で説明したように、ldd(1) を使用すると、動的実行プログラムの依存関係は読み込まれた順番にリストされます。たとえば、共有オブジェクト libbar.so.1 がシンボル foo の再配置を行うためにそのアドレスを必要とし、かつ共有オブジェクトが動的実行プログラム prog の依存関係であるとします。
$ ldd prog libfoo.so.1 => /home/me/lib/libfoo.so.1 libbar.so.1 => /home/me/lib/libbar.so.1 |
実行時リンカーは、foo の検索を、最初に動的実行プログラム prog 内で実行し、次に共有オブジェクト /home/me/lib/libfoo.so.1 内で、最後に共有オブジェクト /home/me/lib/libbar.so.1 内で実行します。
シンボル検索は、シンボル名のサイズが増大し依存関係の数が増加すると、特にコストのかかる処理になる可能性があります。このパフォーマンスについての詳細は、性能に関する考慮事項で説明しています。これに代わる検索モデルについては、直接結合を参照してください。
最初に動的実行プログラム内でシンボルの検索を行い、次に各依存関係内で検索を行うという実行時リンカーのデフォルトのメカニズムは、要求されたシンボルの最初の出現が、この検索を満足させることを意味しています。そのため、同じシンボルの複数のインスタンスが存在する場合は、最初のインスタンスが、ほかのすべてのインスタンスに割り込みされます。共有オブジェクトの処理も参照してください。
直接結合を行うオブジェクトを作成すると、参照されるシンボルと、定義を提供する依存との関係は、作成されるオブジェクトに記録されます。実行時リンカーは、デフォルトのシンボル検索モデルを使用する代わりに、この情報を使って関連するオブジェクトから直接シンボルを検索します。直接結合情報は、リンク編集で指定される依存関係に対してのみ確立されます。したがって、-z defs オプションを推奨します。
直接結合は、以下のメカニズムのどれかを使用して確立できます。
-B direct オプションを使用する。このオプションは、構築されるオブジェクトとそのすべての依存関係との間に直接結合を確立する。-B direct オプションを使用すると、遅延読み込みも有効になる。これは、リンク編集のコマンド行の先頭に -z lazyload オプションを指定するのと同じことである。動的依存関係の遅延読み込みを参照
-z direct オプションを使用する。このオプションは、構築されるオブジェクトと、コマンド行上でこのオプションに続いて指定される依存関係との間に直接結合を確立する。このオプションは、-z nodirect オプションと併用して、依存関係との間の直接結合を使用するかどうかを切り替えることができる
DIRECT mapfile 属性を使用する。この属性は、直接結合する個々のシンボルに対応する。個々のシンボルへの直接結合を防止するには、代わりに属性 NODIRECT を使用する。追加シンボルの定義を参照
直接結合モデルでは、多数のシンボル再配置や依存関係を伴う動的プロセスでのシンボル検索オーバーヘッドを大幅に削減できます。さらに、このモデルでは、同じ名前の複数のシンボルを、それらが直接結合されている個々のオブジェクトから見つけることができます。
直接結合ではデフォルトの検索モデルがバイパスされるため、従来から使用されている割り込みシンボルが迂回される可能性があります。デフォルトのモデルでは必ず、1 つのシンボルへのすべての参照は同じ 1 つの定義に結合されます。
直接結合環境でも、オブジェクト単位で、割り込みを行うことができます。それには、オブジェクトが割り込み処理として識別される必要があります。環境変数 LD_PRELOAD
を使ってオブジェクトを読み込むか、リンカーの -z interpose オプションを使ってオブジェクトを作成すると、オブジェクトは割り込み処理として識別されます。 実行時リンカーは、直接結合されたシンボルを検索する際に、割り込み処理として識別されたオブジェクトを最初に探してから、シンボル定義を指定するオブジェクトを探します。
直接結合を実行時に無効にするには、環境変数 LD_NODIRECT
にヌル以外の値を設定します。
デフォルト手法の代替実装を提供するインタフェースがいくつか存在します。これらの実装は、自身が、プロセス内におけるその手法の唯一のインスタンスであることを前提とします。この例の 1 つにmalloc(3C) ファミリがあります。同じプロセスによってその手法のインスタンスが複数参照される可能性があるため、このようなファミリ内でのインタフェースへの直接結合は避ける必要があります。たとえば、プロセス内の 1 つの依存関係が libc.so.1 に直接結合し、一方で別の依存関係が libmapmalloc.so.1 に直接結合する可能性があります。
プロセスに単一の実装を提供するオブジェクトは、mapfile 指令の NODIRECT を使用してその実装にインタフェースを定義する必要があります。この指令によって、どのユーザーも実装に直接結合することなく、デフォルトのシンボル検索モデルを使用するよう、保証されます。
NODIRECT mapfile 指令は、コマンド行オプション -B direct や -z direct と組み合わせることができます。シンボルに NODIRECT を明示的に定義しないと、そのシンボルはコマンド行の指令に従います。
再配置は、実行されるタイミングで区別できます。この区別は、再配置されたオフセットに対する参照の種類によるもので、次のいずれかになります。
「即時参照」とは、オブジェクトが読み込まれたときにただちに決定しなければならない再配置のことです。この参照は、一般にオブジェクトコードで使用されるデータ項目、関数ポインタ、および位置依存共有オブジェクトからの関数呼び出しに対するものです。即時参照では、再配置された項目が参照されたことを実行時リンカーは認識できません。このため、すべての即時参照は、オブジェクトが読み込まれたら、アプリケーションが制御を獲得または再獲得する前に、再配置が完了する必要があります。
「遅延参照」とは、オブジェクトの実行時に決定できる再配置のことです。通常は、位置独立共有オブジェクトから大域関数への呼び出しか、動的実行可能ファイルから外部関数への呼び出しです。遅延参照を行う動的モジュールをコンパイルおよびリンク編集しているときに、関連付けられた関数呼び出しは、プロシージャリンクテーブルのエントリへの呼び出しに変換されます。 これらのエントリは、.plt セクションを構成します。 プロシージャのリンクテーブルの各エントリは、再配置への遅延参照になります。
プロシージャのリンクテーブルのエントリは、最初に呼び出されたときに、制御が実行時リンカーに渡るように生成されます。実行時リンカーは、関連付けられたオブジェクト内で必要なシンボルを検索し、情報を書き換え、その後のプロシージャリンクテーブルのエントリへの呼び出しが、関数を直接呼び出すようにします。遅延参照では、関数が最初に呼び出されるまで、再配置を遅延させることができます。この処理は、「遅延」結合と呼ばれることがあります。
実行時リンカーのデフォルトモードでは、プロシージャのリンクテーブルの再配置は、常に遅延結合として実行されます。デフォルトモードを無効にするには、環境変数 LD_BIND_NOW
にヌル以外の任意の値を設定します。この環境変数に値を設定すると、実行時リンカーは、オブジェクトが読み込まれてから、アプリケーションが制御を獲得または再獲得するまでの間に、即時参照および遅延参照を再配置します。たとえば、環境変数を次のように設定すると、ファイル prog とその依存先の再配置は、制御がアプリケーションに移る前に行われます。
$ LD_BIND_NOW=1 prog |
オブジェクトにアクセスするときは、RTLD_NOW として定義されたモードで、 dlopen(3DL) を使用することもできます。リンカーの -z now オプションを使用してオブジェクトを構築すれば、オブジェクトが読み込まれたときに再配置を完了させることができます。この再配置オプションは、実行時に指定したオブジェクトの依存先すべてに波及します。
前述の即時参照と遅延参照の例は、標準的なものですが、プロシージャのリンクテーブルエントリの作成は、リンク編集の入力として使用する再配置可能オブジェクトファイルが提供する再配置情報によって制御されます。R_SPARC_WPLT30 や R_386_PLT32 などの再配置レコードには、プロシージャのリンクテーブルエントリの作成が指定されています。これらのレコードは、位置独立コードで常に使用されます。ただし、動的実行可能ファイルの位置は固定されているため、リンク編集時に決定された外部関数参照は、元の再配置レコードに関係なく、プロシージャのリンクテーブルのエントリに変換できます。
最も一般的な再配置エラーは、シンボルが検出されるときに発生します。この状態になると、適切な実行時リンカーのエラーメッセージが表示され、アプリケーションは終了します。次に例を示します。
$ ldd prog libfoo.so.1 => ./libfoo.so.1 libc.so.1 => /usr/lib/libc.so.1 libbar.so.1 => ./libbar.so.1 libdl.so.1 => /usr/lib/libdl.so.1 $ prog ld.so.1: prog: fatal: relocation error: file ./libfoo.so.1: \ symbol bar: referenced symbol not found |
ファイル libfoo.so.1 内で参照されたシンボル bar は配置できません。
動的実行プログラムのリンク編集中に、このソートの再配置エラーが起こる可能性は、定義されていない重大なシンボルとしてフラグが付けられます。例について、実行可能ファイルの作成 を参照してください。この実行時の再配置エラーは、main のリンク編集が、bar のシンボル定義が組み込まれた共有オブジェクト libbar.so.1 の異なったバージョンを使用した場合、または -z nodefs オプションがリンク編集の一部として使用された場合に発生します。
即時参照として使用されたシンボルが配置できないために、このタイプの再配置エラーが発生した場合は、そのエラー状態は、プロセスの初期設定の直後にも発生します。遅延結合のデフォルトモードが原因で、遅延参照として使用されるシンボルが検出できない場合は、このエラー状態は、アプリケーションが制御を受け取ってから発生します。後者の場合、コードを実行する実行パスによって、エラー状態が発生するまでに数分または数ヶ月かかる場合もあり、あるいは発生しない場合もあります。
この種のエラーを防ぐためには、動的実行プログラムまたは共有オブジェクトの再配置の必要条件を、ldd(1) を使用して有効にしておきます。
ldd(1) とともに -d オプションを指定すると、すべての依存関係が出力され、すべての即時参照の再配置処理が実行されます。即時参照が解決できない場合には、診断メッセージが作成されます。上記の例でこのオプションを使用すると、次のようになります。
$ ldd -d prog libfoo.so.1 => ./libfoo.so.1 libc.so.1 => /usr/lib/libc.so.1 libbar.so.1 => ./libbar.so.1 libdl.so.1 => /usr/lib/libdl.so.1 symbol not found: bar (./libfoo.so.1) |
ldd(1) とともに -r オプションを指定すると、すべてのデータと遅延参照の再配置が処理されます。また、このどちらかの再配置が解決できない場合には、診断メッセージが作成されます。