リンカーとライブラリ

第 8 章 mapfile のオプション

概要

リンカーは、入力配置可能オプジェクトの入力セクションを出力ファイルオブジェクトのセグメントに、自動的にかつ効率的に対応付けします。対応する mapfile-M オプションに指定すれば、リンカーの初期値の対応付けの方法を変更できます。

この mapfile オプションを使えば、以下のことができます。

mapfile オプションによって、ifiles (以前のリンカーで使用できた機能で、コマンド言語による指示を使用する) のユーザーは、mapfiles に移行できます。ただし、以前の ifiles で利用できた機能で、前述の説明に含まれていないものすべては、mapfile オプションでは利用できません。


注 -

mapfile オプションを使う場合、実行できない a.out ファイルを簡単に作れてしまうことに留意してください。リンカーは、mapfile オプションなしでも、正しい a.out を作ることができます。mapfile オプションは、システムプログラム用のもので、アプリケーションプログラムでの使用を意図したものではありません。


mapfile オプションの使い方

mapfile オプションを使う場合、次のことを行います。

mapfile が現在のディレクトリにない場合、フルパス名を入れます。初期値のサーチパスは存在しません。

mapfile の構造と構文

以下の 4 つの基本的な指示を mapfile に入力できます。

それぞれの指示は複数の行にまたがることができ、最後にセミコロンを付ければ、いくつでも空白 (改行を含む) を入れることができます。mapfile に指示をまったく入れないことも、複数入れることもできます (指示をまったく入れない場合、リンカーは mapfile を無視し、自らの初期値を使います)。

通常、セグメント宣言の後には、対応付け指示がきます。つまり、セグメントを宣言し、次にセクションがそのセグメントの一部分になる条件を定義するわけです。対応付け先のセグメントを最初に宣言しないで、対応付け指示あるいはサイズシンボル宣言を入力した場合、後で説明する組み込みのセグメント以外のセグメントには、以下で説明するように初期値の属性が与えられます。この場合このようなセグメントは「暗示的に宣言されたセグメント」 になります。

サイズシンボル宣言、およびファイル制御指示は、mapfile のどこにでも入れることができます。

以後の節では、それぞれの指示について説明します。すべての構文説明について、次の表記が適用されます。

セグメントの宣言

セグメントの宣言により、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 つの組み込みセグメントが存在し、以下のような初期値の属性値を持っています。

リンカーは、mapfile を読み取る前に、これらのセグメントが宣言されたように動作します。詳細は、「mapfile オプションの初期値」を参照してください。

セグメント宣言を入力する場合、以下のことに注意してください。

?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;

ソースファイルの関数定義の順序が、mainfoo、および bar の場合、最終的な実行プログラムには foobar、および 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 ファイルが作成されることになります。


対応付け指示

対応付け指示は、入力セクションをどのように出力セグメントに対応付けするかをリンカーに伝えます。基本的には、対応付け先のセグメントの名前を指定し、名前を指定したセグメントに対応付けするためにセクションの属性をどうすべきかを指定します。特定のセグメントに対応付けするためにセクションが持っていなければならないセクション属性値 (section_attribute_values) は、そのセグメントの「入口条件」と呼ばれます。a.out の指定したセグメントにセクションを置くには、セクションはセグメントの入口条件に正確に合致していなければなりません。

対応付け指示には以下のような構文があります。


segment_name : {section_attribute_value}* [: {file_name}+];

セグメント名 (segment_name) に対して、任意の数のセクション属性値 (section_attribute_values) を任意の順序で指定し、それぞれは空白文字で区切ります (セクション属性ごとに 1 つの値だけ指定できます)。ファイル名 (file_name) を指定して、特定の .o ファイルからセクションを持ってこなければならないというように指定することもできます。セクション属性とその有効値は以下のとおりです。

表 8-2 セクション属性

セクション属性 

値 

section_name

任意の有効なセクション名 

section_type

$PROGBITS

$SYMTAB

$STRTAB

$REL

$RELA

$NOTE

$NOBITS

section_flags

?[[!]A][[!]W][[!]X]

対応付け指示を入力する場合、以下の点に注意してください。


S1 : $PROGBITS; 
S1 : $NOBITS; 

1 つのセグメントに対して複数の対応付け命令行を指示することは、複数のセクション属性値を指定するための唯一の方法です。


S1 : $PROGBITS; 
S2 : $PROGBITS; 

この場合、$PROGBITS セクションは、セグメント S1 に対応付けられます。

セグメント内セクションの順序

以下のような表記を使うことにより、セグメント内にセクションを配置する順序を指定できます。


segment_name | section_name1;
segment_name | section_name2;
segment_name | section_name3;

上記の形式で指定されたセクションは、すべての名前なしセクションの前に、mapfile に列挙されている順序で配置されます。

大きさシンボル宣言

大きさシンボル宣言を使って、指定したセグメントの大きさをバイトで示す大域絶対シンボルを定義できます。このシンボルは、オブジェクトファイル内で参照できます。大きさシンボルは次の構文で宣言します。


segment_name @ symbol_name; 

symbol_name (シンボル名) には、任意の正当な C 識別子が入ります。ただし、リンカーは、symbol_name の構文の確認は行いません。

ファイル制御指示

ファイル制御指示により、共有オブジェクト内のどのバージョンの定義をリンク編集時に使用可能にするかを指定できます。ファイル制御構文で宣言します。


shared_object_name - version_name [ version_name ... ];

version_name (バージョン名) には、指定した shared_object_name (共有オブジェクト名) 内のバージョン定義名が入ります。バージョン制御の詳細については、「バージョン結合の指定」を参照してください。

対応付けの例

以下はユーザー定義の mapfile の例です。左の数字は、説明のためにつけたものです。実際には、数字の右の情報だけが、mapfile に含まれます。


例 8-1 ユーザー定義の mapfile


1. 	 	 elephant : .data : peanuts.o *popcorn.o; 
2. 	 	 monkey : $PROGBITS ?AX; 
3. 	 	 monkey : .data; 
4. 	 	 monkey = LOAD V0x80000000 L0x4000; 
5. 	 	 donkey : .data; 
6. 	 	 donkey = ?RX A0x1000; 
7. 	 	 text = V0x80008000; 

4 つの別々のセグメントがこの例では扱われています。暗黙の内に宣言されたセグメント elephant (1 行目) は、ファイル peanuts.o および popcorn.o からすべての .data セクションを受け取ります。*popcorn.o は、リンカーに与えられるすべての popcorn.o ファイルと一致します。ファイルは、現在のディレクトリにある必要はありません。他方、/var/tmp/peanuts.o がリンカーに提供された場合、これには * が前に付かないため peanuts.o とは一致しません。

暗黙の内に宣言されたセグメント monkey (2 行目) は $PROGBITS および割当て可能な実行プログラム (?AX) の両方になっているすべてのセクション、さらに .data (3 行目) という名前のすべてのセクション (セグメント elephant に入らなかったもの) を受け取ります。別の行で section_namesection_typesection_flags の値が指定されているので、monkey セグメントに入る .data セクションが、$PROGBITS あるいは割当て可能な実行プログラムである必要はありません (「か」の関係は、2 行目の $PROGBITS「か」?AX で示されるように、同じ行の属性の間に存在します。「または」の関係は、2 行目の $PROGBITS ?AX 「または」3 行目の .data のように、複数の行にまたがる同じセグメントの属性の間に存在します)。

monkey セグメントは、segment_typeLOADsegment_flagsRWXvirtual_addressphysical_address は無指定、長さ整列の値は、初期値として 2 行目で暗黙の内に宣言されています。4 行目では、monkeysegment_type 値は LOAD に (segment_type 属性は変わっていないので、警告は出ません)、virtual_address 値は 0x80000000 に、最大値は 0x4000 に設定されています。

5 行目では、donkey セグメントを暗黙の内に宣言しています。入口条件は、このすべての .data セクションをこのセグメントに振り向けるようになっています。実際には、3 行目の monkey の入口条件はこれらのセクションのすべてを取り込むので、このセグメントに入るセクションはありません。6 行目では、segment_flags 値は ?RX に設定され、整列値は 0x1000 に設定されています (これらの属性値の両方が変わったので警告が出ます)。

7 行目では、テキストセグメントの virtual_address 値を 0x80008000 に設定します。

ユーザー定義の mapfile の例は、説明のために警告を出すように設計されています。次の例では命令の順序を変更して警告を避けています。


1. 	 	 elephant : .data : peanuts.o *popcorn.o; 
4. 	 	 monkey = LOAD V0x80000000 L0x4000; 
2. 	 	 monkey : $PROGBITS ?AX; 
3. 	 	 monkey : .data; 
6. 	 	 donkey = ?RX A0x1000; 
5. 	 	 donkey : .data; 
7. 	 	 text = V0x80008000; 

次の mapfile の例では、セグメント内セクションの順序付けを使っています。


1. 	 	 text = LOAD ?RXN V0xf0004000; 
2. 	 	 text | .text; 
3. 	 	 text | .rodata; 
4. 	 	 text : $PROGBITS ?A!W; 
5. 	 	 data = LOAD ?RWX R0x1000; 

text セグメントおよび data セグメントが、この例では扱われています。1 行目では、text セグメントの virtual_address が 0xf0004000 になるように、またこのセグメントのアドレス計算の一部分として ELF ヘッダーやプログラムヘッダーを含めないように宣言しています。2 行目と 3 行目では、セグメント内セクションの順序付けを有効にし、このセグメントでは .text セクションと .rodata セクションが最初の 2 つのセクションになるように指定しています。この結果、.text セクションは仮想アドレスが 0xf0004000 になり、その直後に .rodata セクションがきます。

text セグメントを構成するその他の $PROGBITS セクションは、.rodata セクションの後にきます。5 行目では data セグメントを指定し、その仮想アドレスは 0x1000 バイト境界で始まらなければならないと指定しています。data を構成する最初のセクションも、ファイルイメージ内の 0x1000 バイト境界に存在します。

mapfile オプションの初期値

リンカーは、「セグメントの宣言」で説明したように、初期値の segment_attribute_values で 3 つの組み込みセグメント (textdata、および note)、および対応する初期値の対応付け命令を定義します。リンカーは初期値を提供するのに実際の mapfile は使いませんが、ここでは初期値の内容を mapfile に書かれているものとして、リンカーがユーザーの mapfile を処理する際にどのようなことが起こるかを説明します。

以下に示す例は、リンカーの初期値を mapfile として表現したものです。リンカーは、mapfile をすでに読み取ったかのように実行を開始します。その後でリンカーは、mapfile を読み取ったり、あるいは初期値への追加や変更を行なったりします。


text = LOAD ?RX; 
text : ?A!W; 
data = LOAD ?RWX; 
data : ?AW; 
note = NOTE; 
note : $NOTE; 

mapfile の各セグメント宣言は読み取られる際、以下のようにセグメント宣言の既存リストと比較されます。

  1. セグメントが mapfile に存在しておらず、同じ segment-type 値の別のセグメントが存在する場合、そのセグメントは、すべての同じ segment_type の既存セグメントの前に追加される

  2. セグメントが読み取られたとき、既存の mapfile に同じ segment_type のセグメントがない場合、セグメントは、segment_type 値が以下の順序になるように追加される

    INTERP

    LOAD

    DYNAMIC

    NOTE

  3. セグメントの segment_typeLOAD で、この LOAD 可能なセグメントに virtual_address 値を定義していた場合、セグメントは、virtual_address 値が定義されていない、あるいはより高い virtual_address 値の LOAD が指定されたセグメントの前、そしてそれよりも低い virtual_address 値のセグメントの後に置かれる

mapfile の各対応付け指示が読み取られる際、同じセグメントに対してすでに指定されたその他の対応付け指示の後に、そのセグメントの初期値の対応付け指示の前に、指示が追加されます。

内部対応付け構造

ELF 基底のリンカーのもっとも重要なデータ構造の 1 つとして対応付け構造があります。上記で述べた初期値 mapfile に対応する初期対応付け構造は、コマンド実行時にリンカーで使われます。mapfile オプションが使われる場合、リンカーは mapfile を解析して、特定の値を初期対応付け構造に追加または上書きします。

一般的な (ただし多少簡略化した) 対応付け構造を図 8-1 に示します。「入口条件」ボックスは初期値の対応付け指示の情報に対応しており、「セグメント属性記述子」ボックスは、初期値のセグメント宣言の情報に対応しています。「出力記述子」ボックスは、各セグメントの下に来るセクションの詳細な属性を示します。セクション自体は丸で囲んであります。

図 8-1 簡単な対応付け構造

Graphic

リンカーはセクションをセグメントに対応付ける際に、以下の手順で行います。

  1. セクションを読み取る際に、リンカーは合致するものを見つけるために、一連の入口条件を検査します。指定したすべての条件が合致する必要があります。

    図 8-1 では、text セグメントに入るセクションの場合、section_type$PROGBITSsection_flags?A!W でなければなりません。入口条件では名前は指定されていないので、名前 .text は必要ありません。入口条件では実行ビットは何も指定されていないので、セクションは (section_flags 値の) X または !X のどちらかでもかまいません。

    入口条件に合致するものが見つからない場合、セクションはその他すべてのセグメントの後の a.out ファイルの最後に置かれます (この情報については、プログラムヘッダーエントリは作られません。詳細については、「プログラムヘッダー」を参照してください)。

  2. セクションがセグメントの中に入る際に、リンカーは以下のようにそのセグメントの既存の一連出力セクション記述子を検査します。

    セクションの属性値が既存の出力セクション記述子の属性値とまったく合致する場合、セクションは出力セクション記述子に対応するセクションの列挙の最後に置かれます。

    たとえば、section_name.data1section_type$PROGBITS、および section_flags?AWX のセクションは、図 8-1 の 2 番目の入口条件ボックスにあてはまり、data セグメントに置かれます。セクションは 2 番目の出力セクション記述子ボックスにちょうど一致し (.data1$PROGBITS?AWX)、このボックスに対応する連のセクションの最後に追加されます。fido.orover.o、および sam.o.data1 セクションは、このことを示しています。

    一致する出力セクション記述子が見つからず、同じ section_type の出力セクション記述子が他に存在する場合、セクションとして同じ属性値で新しい出力セクション記述子が作られ、そのセクションは新しい出力セクション記述子と対応づけられます。出力セクション記述子 (およびセクション) は、同じ section_type の最後の出力セクション記述子の後に置かれます。図 8-1.data2 セクションはこのやり方で置かれました。

    示された section_type の出力セクション記述子が他に存在しない場合、新しい出力セクション記述子が作られ、セクションはそのセクション内に置かれます。


    注 -

    入力セクションにユーザー定義の section_type が設定されている場合 (つまり、「セクション」で説明しているように、SHT_LOUSERSHT_HIUSER の間)、これは $PROGBITS セクションとして扱われます。mapfile でこの section_type 値に名前を付ける方法はありませんが、これらのセクションは、入口条件でその他の属性値指定 (section_flagssection_name) を使って付け直すことができます。


  3. すべてのコマンド行で指定されたオブジェクトファイルとライブラリが読み取られた後、セグメントがセクションをまったく含まない場合、そのセグメントに対してはプログラムヘッダーエントリは作られません。


注 -

$SYMTAB$STRTAB$REL、および $RELA 型の入力セクションは、リンカーにより内部で使用されます。これらの section_type を参照する指示は、リンカーで作った出力セクションしかセグメントに対応付けできません。


エラーメッセージ

警告

この範疇のエラーメッセージが出力される場合、リンカーが停止したり、リンカーが実行可能な a.out を作成できないといったことはありません。次のような条件で、警告が発せられます。

致命的エラー

この範疇のエラーが発生した場合、その時点でリンカーの実行を停止します。以下の条件で致命的エラーが発生します。

提供される mapfile

mapfile のサンプルは、/usr/lib/ld ディレクトリにあります。