シンボルを入力ファイルから提供することに加えて、ユーザーは、リンク編集に、追加のシンボルリファレンスまたは定義を指定できます。最も簡単な形式で、シンボルリファレンスは、リンカーの -u オプションを使用して作成できます。より柔軟性の高いものは、リンカーの -M オプションと、それに関連した、シンボルリファレンスと種々のシンボル定義を定義できる mapfile を使用して作成できます。
-u オプションを指定すると、リンク編集コマンド行からシンボルリファレンスを作成するためのメカニズムが使用できます。このオプションは、リンク編集をすべてアーカイブから実行する場合に使用でき、また、複数のアーカイブから抽出するオブジェクトの選択における柔軟性を向上させることができます (アーカイブの抽出については、「アーカイブ処理」の項を参照)。
たとえば、動的実行可能ファイルの生成を、シンボル foo と bar へのリファレンスを実行する再配置可能オブジェクト main.o から抽出してみましょう。この場合、lib1.a 内に組み込まれた再配置可能オブジェクト foo.o からシンボル定義 foo を入手し、さらに lib2.a 内に組み込まれた再配置可能オブジェクト bar.o からシンボル定義 bar を入手します。
ただし、アーカイブ lib1.a にも、シンボル bar を定義する再配置可能オブジェクトが組み込まれています (lib2.a に提供されたものとは機能的に異なると仮定した場合)。必要なアーカイブ抽出を指定する場合は、次のようなリンク編集を使用できます。
$ cc -o prog -L. -u foo -l1 main.o -l2 |
ここで、-u オプションは、シンボル foo へのリファレンスを生成します。このリファレンスによって、再配置可能オブジェクト foo.o がアーカイブ lib1.a から抽出されます。シンボル bar への最初のリファレンスは、lib1.a が処理されてから生じる main.o 内で実行されるため、再配置可能オブジェクト bar.o はアーカイブ lib2.a から入手されます。
この単純な例では、lib1.a からの 再配置可能オブジェクト foo.o は、シンボル bar の直接的または間接的な参照は行いません。この参照を行なった場合、再配置可能オブジェクト bar.o は、その処理中に、lib1.a から抽出されます (アーカイブを処理するリンカーの多重パスについては、「アーカイブ処理」を参照)。
より広範囲なシンボル定義のセットは、リンカーの -M オプションと関連する mapfile を使用して入手できます。これらの mapfile エントリの構文は次のとおりです。
[ name ] { scope: symbol [ = [ type ] [ value ] [ size ] [ extern ] ]; } [ dependency ]; |
このシンボル定義のセットのラベルは、もしあれば、イメージ内のバージョン定義を識別できます。詳細については、第 5 章「バージョンアップ」を参照してください。
生成される出力ファイル内のシンボルのバインディングの可視性を示しています。これには、値 local (ローカル) または global (大域) のいずれかが入ります。mapfile で定義されたすべてのシンボルは、リンク編集プロセス中に、scope (スコープ) 内で global (大域) として処理されます。つまり、これらのシンボルは、入力ファイルのいずれかから入手された、同じ名前の他のシンボルに対して解析されます。ただし、local (ローカル) scope として定義シンボルは、生成される実行可能ファイルまたは共有オブジェクトのファイル内のローカル結合が指定されたシンボルに変更されます。
要求されたシンボルの名前です。この名前の後に、シンボル属性 (type、value、size のいずれか) が付いていない場合には、シンボルリファレンスの作成になります。このリファレンスは、この項の最初に説明した -u オプションを使用して生成するリファレンスとまったく同じものです。このシンボル名にシンボル属性が付いている場合には、シンボル定義は、関連する属性を使用して生成されます。
local スコープ内では、このシンボル名は、特別な「auto-reduction」(自動縮小) 指示語「*」として定義できます。この指示語を使用すると、すべての大域シンボル (mapfile 内に global と明示的に定義されていないもの) が、生成される実行可能ファイルまたは共有オブジェクトファイル内で、ローカル結合を受け取ります。
シンボルのタイプ属性を示します。また、ここには、data、function、common のいずれかが入ります。最初の 2 つのタイプ属性の結果は、絶対的なシンボル定義になります (「シンボルテーブル」を参照)。後者のタイプ属性の結果は、一時的シンボル定義になります。
シンボルの値属性を示し、Vnumber の書式をとります。
シンボルのサイズ属性を示し、Snumber の書式をとります。
シンボルが、作成されているオブジェクトに外部的に定義されていることを示します。このオプションを使用して、-z defs オプションで示された未定義シンボル (「共有オブジェクトの生成」を参照) を抑制できます。
この定義が継承する version definition (バージョン定義) を示します。詳細については、第 5 章「バージョンアップ」を参照してください。
バージョン定義または自動縮小のいずれかの指示語が指定されている場合、バージョン情報が作成されるイメージ内に記録されます。このイメージが実行可能ファイルまたは共有オブジェクトである場合には、シンボル縮小も適用されます。
作成されるイメージが再配置可能オブジェクトである場合は、デフォルトにより、シンボル縮小は適用されません。この場合、シンボル縮小はバージョン情報の一部として記録され、これらの縮小は、再配置可能オブジェクトが最終的に実行可能ファイルまたは共有オブジェクトの生成に使用されるときに適用されます。リンカーの -B reduce オプションを使用すると、再配置可能オブジェクトを生成するときに、強制的にシンボル縮小を実行できます。
バージョン情報の詳細については、第 5 章「バージョンアップ」に記載してあります。
インタフェース定義を確実に安定させるためには、シンボル名の定義に対しワイルドカードによる拡張を行わないようにします。
この項では、この後、この mapfile 構文を使用した例をいくつか示します。
以下の例は、3 つのシンボルリファレンスを定義する方法と、これらを使用してアーカイブから構成要素を抽出する方法を示しています。このアーカイブ抽出は、複数の -u オプションをリンク編集に指定することにより実現できますが、この例では、最終的なシンボルの範囲を、ローカルに縮小する方法も示しています。
$ cat foo.c foo() { (void) printf("foo: called from lib.a¥n"); } $ cat bar.c bar() { (void) printf("bar: called from lib.a¥n"); } $ cat main.c extern void foo(), bar(); main() { foo(); bar(); } $ ar -rc lib.a foo.o bar.o main.o $ cat mapfile { local: foo; bar; global: main; }; $ cc -o prog -M mapfile lib.a $ prog foo: called from lib.a bar: called from lib.a $ nm -x prog | egrep "main$|foo$|bar$" [28] |0x00010604|0x00000024|FUNC |LOCL |0x0 |7 |foo [30] |0x00010628|0x00000024|FUNC |LOCL |0x0 |7 |bar [49] |0x0001064c|0x00000024|FUNC |GLOB |0x0 |7 |main |
大域からローカルへのシンボル範囲の縮小の重要性については、「シンボル範囲の縮小」で説明しています。
次の例では、2 つの絶対シンボル定義を定義し、これらを使用して入力ファイル main.c からのリファレンスを解析する方法を示しています。
$ cat main.c extern int foo(); extern int bar; main() { (void) printf("&foo = %x¥n", &foo); (void) printf("&bar = %x¥n", &bar); } $ cat mapfile { global: foo = FUNCTION V0x400; bar = DATA V0x800; }; $ cc -o prog -M mapfile main.c $ prog &foo = 400 &bar = 800 $ nm -x prog | egrep "foo$|bar$" [37] |0x00000800|0x00000000|OBJT |GLOB |0x0 |ABS |bar [42] |0x00000400|0x00000000|FUNC |GLOB |0x0 |ABS |foo |
入力ファイルから入手される場合、関数のシンボル定義またはデータ項目は、通常、データ記憶域の要素に関連しています。mapfile 定義は、このデータ記憶域を構成するためには不十分であるため、これらのシンボルは、絶対値として残しておく必要があります。
ただし、mapfile は、common または一時的シンボルを定義する場合にも使用できます。他のタイプのシンボル定義とは違って、一時的シンボルは、ファイル内の記憶域を占有しませんが、実行時に割り当てる記憶域の定義は行います。そのため、このタイプのシンボル定義は、作成される出力ファイルの記憶域割り当ての一因となります。
一時的シンボルの特徴は、他のシンボルタイプとは異なり、その値の属性によって、その配列条件が示される点です。mapfile 定義は、リンク編集の入力ファイルから入手された一時的定義の再配列に使用されます。
次の例では、2 つの一時的シンボルの定義を示しています。シンボル foo は、新しい記憶領域を定義しているのに対し、シンボル bar は、実際に、ファイル main.c 内の同じ一時的定義の配列を変更するために使用されます。
$ cat main.c extern int foo; int bar[0x10]; main() { (void) printf("&foo = %x¥n", &foo); (void) printf("&bar = %x¥n", &bar); } $ cat mapfile { global: foo = COMMON V0x4 S0x200; bar = COMMON V0x100 S0x40; }; $ cc -o prog -M mapfile main.c ld: warning: symbol `bar' has differing alignments: (file mapfile value=0x100; file main.o value=0x4); largest value applied $ prog &foo = 20940 &bar = 20900 $ nm -x prog | egrep "foo$|bar$" [37] |0x00020900|0x00000040|OBJT |GLOB |0x0 |16 |bar [42] |0x00020940|0x00000200|OBJT |GLOB |0x0 |16 |foo |
このシンボル解析の診断は、リンカーの -t オプションを使用すると表示されません。