リンカーとライブラリ

第 8 章 mapfile のオプション

リンカーは、再配置可能オブジェクトの入力セクションを、作成中の出力ファイル内のセグメントに、自動的にかつ効率的に対応付けします。対応する mapfile-M オプションで指定すれば、リンカーの初期値の対応付けの方法を変更できます。また、mapfile を使用して、新規セグメントの作成、属性の変更、およびシンボルのバージョン情報管理情報の指定を実行できます。


注 –

mapfile オプションを使用すると、実行されない出力ファイルを簡単に作成できます。 リンカーは、mapfile オプションなしでも、正しい出力ファイルを作成できます。


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

mapfile の構造と構文

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

それぞれの指示は複数の行にまたがることができ、最後にセミコロンを付ければ、いくつでも空白 (改行を含む) を入れることができます。

通常、セグメント宣言の後に、対応付け指示を記述します。セグメントを宣言してから、セクションがそのセグメントの一部分になる条件を定義します。対応付け先のセグメントを最初に宣言せずに、対応付け指示あるいはサイズシンボル宣言を入力した場合、組み込みのセグメント以外のセグメントには、デフォルト属性が付与されます。この種のセグメントは、「暗示的に」宣言されたセグメントになります。

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

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

セグメントの宣言

セグメントの宣言により、出力ファイルに新しいセグメントを作成したり、既存のセグメントの属性値を変更したりできます。既存のセグメントとは、以前に定義したもの、あるいは以下に述べる 4 つの組み込みセグメントの 1 つのことです。

セグメントの宣言は以下の構文で行います。

        segment_name = {segment_attribute_value}*;

segment_name に対して、任意の数の segment_attribute_values を任意の順序で指定し、それぞれは空白文字で区切ります。セグメント属性ごとに 1 つの値だけを指定できます。 セグメント属性および有効な値を、次の表に示します。

表 8–1 Mapfile セグメント属性

属性 

値 

segment_type

LOAD | NOTE | STACK

segment_flags

? [E] [N] [O] [R] [W] [X]

virtual_address

V number

physical_address

P number

length

Lnumber

rounding

Rnumber

alignment

Anumber

4 つの組み込みセグメントが存在し、以下のデフォルト属性値を保持します。

デフォルトでは、bss セグメントは無効に設定されています。この唯一の入力部分である SHT_NOBITS タイプのセクションは、データセグメント内でキャプチャされます。SHT_NOBITS セクションの詳細は、表 7–12 を参照してください。最も単純な bss 宣言を次に示します。

        bss =;

この宣言で、bss セグメント作成を有効にできます。すべての SHT_NOBITS セクションは、データセグメントではなく、このセグメントによりキャプチャされるようになります。最も単純な場合、他のセグメントにも適用されるデフォルトを使用してこのセグメントの整列が行われます。宣言を実行してセグメントの追加属性を指定することにより、セグメント作成を有効にしたり、指定した属性を付与したりすることもできます。

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

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


注 –

virtual_address 値が指定されている場合、セグメントはその仮想アドレスに置かれます。システムカーネルの場合、この方法で正しい結果が生成されます。exec(2) を介して開始するファイルの場合、この方法では、セグメントがページ境界に対応する正しいオフセットを保持しないため、不正な出力ファイルが作成されることになります。


?E フラグにより、空のセグメントが作れます。この空のセグメントには、関連付けられたセクションが存在しません。このセグメントは実行プログラムについてのみ指定でき、LOAD 型で、長さおよび整列が指定されていなければなりません。この種類のセグメントは複数定義することもできます。

?N フラグにより、ELF ヘッダー、および任意のプログラムヘッダーを最初の読み込み可能なセグメントの一部分として含めるかどうかを制御できます。特に指定しない場合、 ELF ヘッダーおよびプログラムヘッダーは、最初のセグメントに含まれます。これらのヘッダー内の情報は、対応付けられたイメージ内で (通常は実行時リンカーにより) 使用されます。?N オプションを使用すると、イメージの仮想アドレス計算が最初のセグメントの最初のセクションで開始されます。

?O フラグを使用すると、出力ファイル内のセクションの順序を制御できます。このフラグは、コンパイラの -xF オプションと合わせて使うようになっています。ファイルを -xF オプションを使ってコンパイルすると、そのファイル内の各関数が、.text セクションと同じ属性を持つ別個のセクションに置かれます。これらのセクションは、 .text%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;

最初の宣言により、?O フラグがデフォルトのテキストセグメントに関連付けられます。

ソースファイルの関数定義の順序が、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;

次のエントリも構文上は可能です。

        text: .text%bar: a.o b.o;

しかし、ファイル a.o の関数 bar() がファイル b.o の関数 bar() の前に置かれることは保証されません。2 番目の形式は、結果が予測できないため、お勧めしません。

対応付け指示

対応付け指示は、入力セクションをどのように出力セグメントに対応付けするかをリンカーに伝えます。基本的には、対応付け先のセグメントの名前を指定し、名前を指定したセグメントに対応付けするためにセクションの属性をどうすべきかを指定します。特定のセグメントに対応付けするためにセクションが持っていなければならないセクション属性値 (section_attribute_values) のセットは、そのセグメントの「エントランス基準」と呼ばれます。出力ファイル内の指定されたセグメントにセクションを置くには、セクションがセグメントのエントランス基準に正確に合致している必要があります。

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

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

セグメント名 (segment_name) に対して、任意の数のセクション属性値 (section_attribute_values) を任意の順序で指定し、それぞれは空白文字で区切ります。セクション属性ごとに 1 つの値だけを指定できます。file_name 宣言を使用して、特定の .o ファイルに由来するセクションだけに限定することも可能です。セクション属性とその有効値は表 8–2 に示すとおりです。

表 8–2 セクション属性

セクション属性 

値 

section_name

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

section_type

$PROGBITS

$SYMTAB

$STRTAB

$REL

$RELA

$NOTE

$NOBITS

section_flags

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

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

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

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

        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 あるいは割当て可能な実行プログラムである必要はありません。

「and」の関係は、2 行目の $PROGBITS?AX の関係に示されるように、同じ行の属性の間に存在します。「or」の関係は、2 行目の $PROGBITS ?AX と 3 行目の .data の関係に示されるように、複数の行にまたがる同じセグメントの属性の間に存在します。

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

5 行目では、donkey セグメントを暗黙の内に宣言しています。エントランス基準 は、このすべての .data セクションをこのセグメントに振り向けるようになっています。実際には、3 行目の monkey のエントランス基準はこれらのセクションのすべてを取り込むので、このセグメントに入るセクションはありません。6 行目では、segment_flags 値は ?RX に、alignment 値は 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 で 4 つの組み込みセグメント (textdatabss、および 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 はすべて、初期対応付け構造内に特定の値を追加または上書きします。

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

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

簡単な対応付け構造。

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

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

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

    エントランス基準に合致するものが見つからない場合、セクションはその他すべてのセグメントの後の出力ファイルの最後に置かれます。この情報に関するプログラムヘッダーエントリは作成されません。

  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 の出力セクション記述子が他に存在する場合、セクションとして同じ属性値で新しい出力セクション記述子が作られ、そのセクションは新しい出力セクション記述子と対応づけられます。出力セクション記述子およびセクションは、同じセクションタイプの最後の出力セクション記述子の後に置かれます。図 8–1.data2 セクションはこの方法で配置されました。

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


    注 –

    入力セクションが、SHT_LOUSERSHT_HIUSER の間にユーザー定義のセクションタイプ値を保持する場合、このセクションタイプ値は $PROGBITS セクションとして処理されます。mapfile でこの section_type 値に名前を付ける方法はありませんが、これらのセクションは、エントランス基準でその他の属性値指定 (section_flagssection_name) を使って付け直すことができます。


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


注 –

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