| ナビゲーションリンクをスキップ | |
| 印刷ビューの終了 | |
|
リンカーとライブラリ Oracle Solaris 11 Information Library (日本語) |
LOAD_SEGMENT/NOTE_SEGMENT/NULL_SEGMENT 指令
SIZE_SYMBOL 属性 (LOAD_SEGMENT のみ)
SYMBOL_SCOPE/SYMBOL_VERSION 指令
パート IV ELF アプリケーションバイナリインタフェース
ここでは、セクションを出力セグメントに割り当てるためにリンカーによって使用される内部処理について説明します。この情報は、mapfile を使用するためには必要ありません。この情報は主に、リンカーの内部情報に関心を持ち、セグメントの mapfile 指令がリンカーによってどのように解釈されて実行されるかについて深く理解することが必要な読者を対象としています。
入力セクションを出力セグメントに割り当てるプロセスには、次のデータ構造が関与します。
入力セクション
入力セクションは再配置可能オブジェクト入力からリンカーに読み取られます。一部の情報はリンカーによって検査および処理されますが、その他の情報は内容が検査されることなく単純に出力に渡されます (PROGBITS など)。
出力セクション
出力セクションは、出力オブジェクトに書き込まれるセクションです。一部のセクションは、入力オブジェクトから渡されたセクションの連結によって形成されます。シンボルテーブルや再配置セクションなどのその他のセクションは、一般的には入力オブジェクトから読み込んだ情報を組み込んで、リンカー自体によって生成されます。
リンカーが入力セクションを通過させて出力セクションにした場合、そのセクションは通常、入力セクションの名前を保持します。ただし、リンカーは特定の状況で名前を変更できます。たとえば、リンカーは name%XXX という形式の入力セクション名を変換し、% 文字とその後に続く文字を出力セクションから削除します。
セグメント記述子
リンカーは既知のセグメントのリストを保持しています。このリストは最初は、「定義済みセグメント」に記載されている定義済みセグメントが格納されています。LOAD_SEGMENT、NOTE_SEGMENT、または NULL_SEGMENT の mapfile 指令が使用されて新しいセグメントが作成されると、新しいセグメント用の追加のセグメント記述子がこのリストに追加されます。新しいセグメントは、仮想アドレスの設定 (LOAD_SEGMENT) や、SEGMENT_ORDER 指令を使用して明示的に順序付けされないかぎり、このリストの末尾、同じタイプのほかのセグメントの後に配置されます。
出力オブジェクトを作成するとき、リンカーはセクションを受け取るセグメントについてのみプログラムヘッダーを作成します。空のセグメントは自動的に無視されます。したがって、リンカーのリストからセグメント定義を削除するための明示的な機能はありませんが、ユーザー指定のセグメント定義を使用すると、定義済みセグメントの定義の使用を完全に置き換えることができます。
エントランス基準
セクションを所定のセグメントに配置するために必要な一連のセクション属性を、そのセグメントのエントランス基準と呼びます。セグメントは任意の数のエントランス基準を持つことができます。
リンカーは、定義済みのすべてのエントランス基準の内部リストを保持しています。このリストは、次に説明するように、セクションをセグメントに配置するために使用されます。各 mapfile は、LOAD_SEGMENT、NOTE_SEGMENT、または NULL_SEGMENT の mapfile 指令の ASSIGN_SECTION 属性によって作成されたエントランス基準を、mapfile 内で見つかった順序でこのリストの上部に挿入します。 「定義済みセグメント」で説明されている組み込みセグメントのエントランス基準は、このリストの最後に配置されます。したがって、mapfile で定義されるエントランス基準は組み込み規則よりも優先され、コマンド行の最後にある mapfile は、最初に検出されたものよりも優先されます。
出力オブジェクトに書き込まれる各セクションについて、リンカーは次の手順を実行して、セクションを出力セグメントに配置します。
セクションの属性は、内部エントランス基準リストの先頭から各レコードと比較され、各エントランス基準が順番に検証されます。エントランス基準内のすべての属性が完全に一致したときにエントランス基準と一致したことになります。そのエントランス基準に関連付けられたセグメントは無効化されません。検索は、一致した最初のエントランス基準で停止し、セクションは関連付けられたセグメントに指定されます。
エントランス基準に一致するものが見つからない場合、セクションはその他すべてのセグメントの後の、出力ファイルの最後に置かれます。この情報に関するプログラムヘッダーエントリは作成されません。デバッグセクションなどの割り当て不能なセクションは、ほとんどがこの領域に配置されることになります。
セクションがセグメントの中に入る際に、リンカーは次のようにそのセグメントの既存の一連の出力セクションを検査します。
セクションの属性値が既存の出力セクションの属性値と完全に一致する場合、セクションはその出力セクションに対応するセクションの列挙の最後に置かれます。
一致する出力セクションが見つからない場合、配置されるセクションの属性を使用して新しい出力セクションが作成され、新しい出力セクション内に入力セクションが配置されます。この新しい出力セクションは、セグメント内で同じセクションタイプを持つほかの出力セクションの後ろに配置されますが、ほかの出力セクションが存在しない場合はセグメントの末尾に配置されます。
注 - 入力セクションが、SHT_LOUSER と SHT_HIUSER の間にユーザー定義のセクションタイプ値を保持する場合、このセクションは PROGBITS セクションとして処理されます。mapfile でこのセクションタイプ値に名前を付ける方法はありませんが、これらのセクションは、エントランス基準でその他の属性値指定 (セクションフラグ、セクション名) を使って付け直すことができます。
リンカーには、「定義済みセグメント」で説明されているように、定義済みの出力セグメント記述子とエントランス基準のセットが提供されています。リンカーはこれらのセクションについて認識しているため、mapfile 指令でこれらを作成する必要はありません。これらの作成に使用できる mapfile 指令は、説明のため、または比較的複雑な mapfile を指定する例として示されています。mapfile セグメント指令は、これらの組み込み定義を変更または拡張するために使用できます。
通常、セクションからセグメントへの割り当ては、単一のセグメント指令の内部で行われます。しかし、定義済みのセクションにはより複雑な要件があるため、セグメントがメモリーに展開されている順序と異なる順序でエントランス基準を処理する必要があります。これを実現するには 2 つの方法が使用されます。1 つ目は、すべてのセグメントを望ましい順序で定義する方法、2 つ目は、望ましい結果が得られる順序でエントランス基準を設定する方法です。ユーザーの mapfile でこの方法が必要になることはまれです。
# Predefined segments and entrance criteria for the Oracle Solaris
# link-editor
$mapfile_version 2
# The lrodata and ldata segments only apply to x86-64 objects.
# Establish amd64 as a convenient token for conditional input
$if _ELF64 && _x86
$add amd64
$endif
# Pass 1: Define the segments and their attributes, but
# defer the entrance criteria details to the 2nd pass.
LOAD_SEGMENT text {
FLAGS = READ EXECUTE;
};
LOAD_SEGMENT data {
FLAGS = READ WRITE EXECUTE;
};
LOAD_SEGMENT bss {
DISABLE;
FLAGS=DATA;
};
$if amd64
LOAD_SEGMENT lrodata {
FLAGS = READ
};
LOAD_SEGMENT ldata {
FLAGS = READ WRITE;
};
$endif
NOTE_SEGMENT note;
NULL_SEGMENT extra;
# Pass 2: Define ASSIGN_SECTION attributes for the segments defined
# above, in the order the link-editor should evaluate them.
# All SHT_NOTE sections go to the note segment
NOTE_SEGMENT note {
ASSIGN_SECTION {
TYPE = NOTE;
};
};
$if amd64
# Medium/large model x86-64 readonly sections to lrodata
LOAD_SEGMENT lrodata {
ASSIGN_SECTION {
FLAGS = ALLOC AMD64_LARGE;
};
};
$endif
# text receives all readonly allocable sections
LOAD_SEGMENT text {
ASSIGN_SECTION {
FLAGS = ALLOC !WRITE;
};
};
# If bss is enabled, it takes the writable NOBITS sections
# that would otherwise end up in ldata or data.
LOAD_SEGMENT bss {
DISABLE;
ASSIGN_SECTION {
FLAGS = ALLOC WRITE;
TYPE = NOBITS;
};
};
$if amd64
# Medium/large model x86-64 writable sections to ldata
LOAD_SEGMENT ldata {
ASSIGN_SECTION {
FLAGS = ALLOC WRITE AMD64_LARGE;
};
ASSIGN_SECTION {
TYPE = NOBITS;
FLAGS = AMD64_LARGE
};
};
$endif
# Any writable allocable sections not taken above go to data
LOAD_SEGMENT data {
ASSIGN_SECTION {
FLAGS = ALLOC WRITE;
};
};
# Any section that makes it to this point ends up at the
# end of the object file in the extra segment. This accounts
# for the bulk of non-allocable sections.
NULL_SEGMENT extra {
ASSIGN_SECTION;
};