セグメントの宣言により、a.out に新しいセグメントを作ったり、既存のセグメントの属性値を変更したりできます (既存のセグメントとは、以前に定義したもの、あるいは以下に述べる 3 つの組み込みセグメントの 1 つのことです)。
セグメントの宣言は以下の構文で行います。
segment_name = {segment_attribute_value}*; |
各 segment_names について、任意の数の segment_attribute_values を任意の順序で指定でき、それぞれは空白文字で区切ります (セグメント属性ごとに、1 つの値だけ指定できます)。セグメント属性と、それらの有効な値は以下のとおりです。
表 8-1 mapfile セグメント属性
属性 |
値 |
---|---|
segment_type |
LOAD|NOTE |
segment_flags |
?[E][N][O][R][W][X] |
virtual_address |
Vnumber |
physical_address |
Pnumber |
長さ |
Lnumber |
丸め |
Rnumber |
整列 |
Anumber |
3 つの組み込みセグメントが存在し、以下のような初期値の属性値を持っています。
テキスト (LOAD、?RX、virtual_address と physical_address と長さは指定なし、CPU 固有の整列値)
データ (LOAD、?RWX、virtual_address と physical_address と長さは指定なし、CPU 固有の整列値)
注釈 (NOTE)
リンカーは、mapfile を読み取る前に、これらのセグメントが宣言されたように動作します。詳細は、「mapfile オプションの初期値」を参照してください。
セグメント宣言を入力する場合、以下のことに注意してください。
数字には、C 言語と同じ形式で、16 進数、10 進数、あるいは 8 進数が使える
V、P、L、R、あるいは A と数字の間には空白文字を入れてはいけない
segment_type 値は、LOAD あるいは NOTE のどちらかになる
segment_type の初期値は、LOAD
segment_flags 値は、R は読み取り可能 、W は書き込み可能、X は実行可能、O は順番です。疑問符 ? と segment_flags 値を構成する個々のフラグの間には、空白文字を入れてはいけない
LOAD セグメントの segment_flags 値は、初期値で RWX になる
NOTE セグメントには、segment_type 以外のセグメント属性値は割り当てられない
暗示的に宣言されたセグメントでは、segment_type 値は LOAD、segment_flags 値は RWX、 virtual_address と physical_address と整列値は初期値、そして長さは無制限になる
リンカーは、1 つ前のセグメントの属性値に基づいて、現在のセグメントのアドレスや長さを計算します。また、暗示的に宣言されたセグメントがデフォルトで 「長さ制限なし」になっていても、マシンのメモリー限界は適用されます。
LOAD セグメントには、 virtual_address 値、 physical_address 値、最大セグメント長値のいずれかまたはすべてを明示的に指定できる
セグメントに ? の segment_flags 値があって後に何もない場合、値は読み取り不可、書き込み不可、および実行不可になる
整列値は、セグメントの最初の仮想アドレスを計算する際に使われる。この整列は、整列指定されたセグメントにだけ影響する。その他のセグメントは、その整列が変更されない限り、デフォルトの整列が使われる
virtual_address、physical_address、あるいは長さの属性値のいずれかが設定されていない場合、リンカーは a.out を作成する際に、これらの値を計算する
セグメントに対して整列値が指定されていない場合、組み込みの初期値に設定される (初期値は CPU により異なり、カーネルのバージョンによっても異ることがある。これらの数字については、適切な資料で確認のこと)
virtual_address と整列値の両方がセグメントに対して指定されている場合、virtual_address の方が優先される
virtual_address 値がセグメントに対して指定されている場合、プログラムヘッダーの整列フィールドには、初期値の整列値が設定される
丸め値がセグメントに対して設定されている場合、そのセグメントの仮想アドレスは与えられた値に一致する次のアドレスに丸められる。この値は、指定の対象となるセグメントにしか効力はない。値が入力されないと、丸めは行われない
?E フラグにより、空のセグメントが作れます。これは、関連するセクションがないセグメントです。このセグメントは実行プログラムについてのみ指定でき、LOAD 型で、長さおよび整列が指定されていなければなりません。この種類のセグメントは複数定義することもできます。
?N フラグにより、ELF ヘッダー、および任意のプログラムヘッダーを最初の読み込み可能なセグメントの一部分として含めるかどうかを制御できます。初期値では、ELF ヘッダーおよびプログラムヘッダーは、これらのヘッダーの情報が対応付けられたイメージ内 (一般に実行時リンカーによって対応付けられる) で使われるため、最初のセグメントに含まれます。?N オプションを使用すると、イメージの仮想アドレス計算が最初のセグメントの最初の「セクション」で開始します。
?O フラグにより、最終的な再配置可能オブジェクト、実行可能ファイル、あるいは共有オブジェクトのセクションの順序を制御できます。このフラグは、コンパイラの -xF オプションと合わせて使うようになっています。ファイルを -xF オプションを使ってコンパイルすると、そのファイル内の各関数が、.text セクションと同じ属性を持つ別個のセクションに置かれます。これらのセクションは、.text%function_name (function_name は関数名) という名前になります。
たとえば、main()、foo()、および bar() の 3 つの関数を持つファイルを、 -xF オプションを使ってコンパイルすると、 3 つの関数のテキストが .text%main、.text%foo、および .text%bar という名前のセクションにあるオブジェクトファイルが作成されます。 -xF オプションは 1 つのセクションに 1 つの関数を割り当てるので、セクションの順番を制御するために ?O フラグを使うと、実際には関数の順番を制御することになります。
次のユーザー定義の mapfile を検討します。
text = LOAD ?RXO; text: .text%foo; text: .text%bar; text: .text%main; |
ソースファイルの関数定義の順序が、main、foo、および bar の場合、最終的な実行プログラムには foo、bar、および main の順序で関数が含まれます。
同じ名前の静的関数を対象とする場合、ファイル名も指定する必要があります。?O フラグを指定すると、mapfile で指定されたとおりにセクションの順序付けが行われます。たとえば、静的関数 bar() がファイル a.o および b.o にあって、ファイル a.o の関数 bar() を、ファイル b.o の関数 bar() の前に置く場合、mapfile のエントリは次のようになります。
text: .text%bar: a.o; text: .text%bar: b.o; |
次の構文を使用することも可能ですが、ファイル a.o の関数 bar() が、ファイル b.o の関数 bar() の前に置かれることが保障されません。2 番目の形式は、結果が予測できないため、お勧めしません。
text: .text%bar: a.o b.o; |
virtual_address が指定されている場合、セグメントはその仮想アドレスに置かれます。これは、システムカーネルに対して、正しい結果を出します。exec(2) を介して開始するファイルの場合、この方法では、セグメントにそのページ境界に対応する正しいオフセットがないため、間違った a.out ファイルが作成されることになります。