入力ファイルから提供されるシンボルのほかに、追加の大域シンボル参照や大域シンボル定義をリンク編集に対して指定できます。もっとも簡単な形式で、シンボル参照は、リンカーの –u オプションを使用して作成できます。リンカーの –M オプションと関連 mapfile を使用すると柔軟性が高まります。この mapfile を使用すると、大域シンボル参照およびさまざまな大域シンボル定義を定義できます。可視性や型などのシンボルの属性を指定できます。使用可能なオプションの詳細な説明については、SYMBOL_SCOPE/SYMBOL_VERSION 指令を参照してください。
–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 から入手されます。
次の例では、3 つのシンボル参照を定義する方法を示します。これらの参照を使用して、アーカイブのメンバーを抽出します。このアーカイブ抽出は、複数の –u オプションをリンク編集に指定することにより実現できますが、この例では、最終的なシンボルの範囲を、ローカルに縮小する方法も示しています。
$ cat foo.c #include <stdio.h> void foo() { (void) printf("foo: called from lib.a\n"); } $ cat bar.c #include <stdio.h> void bar() { (void) printf("bar: called from lib.a\n"); } $ cat main.c extern void foo(), bar(); void main() { foo(); bar(); } $ cc -c foo.c bar.c main.c $ ar -rc lib.a foo.o bar.o main.o $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { local: foo; bar; global: main; }; $ cc -o prog -M mapfile lib.a $ prog foo: called from lib.a bar: called from lib.a $ elfdump -sN.symtab prog | egrep 'main$|foo$|bar$' [29] 0x10f30 0x24 FUNC LOCL H 0 .text bar [30] 0x10ef8 0x24 FUNC LOCL H 0 .text foo [55] 0x10f68 0x24 FUNC GLOB D 0 .text main
大域からローカルへのシンボル範囲の縮小の重要性については、シンボル範囲の縮小で説明しています。
次の例では、2 つの絶対シンボル定義を定義する方法を示します。そして、これらの定義を使用して、入力ファイル main.c からの参照を解決します。
$ cat main.c #include <stdio.h> extern int foo(); extern int bar; void main() { (void) printf("&foo = 0x%p\n", &foo); (void) printf("&bar = 0x%p\n", &bar); } $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: foo { TYPE=FUNCTION; VALUE=0x400 }; bar { TYPE=DATA; VALUE=0x800 }; }; $ cc -o prog -M mapfile main.c $ prog &foo = 0x400 &bar = 0x800 $ elfdump -sN.symtab prog | egrep 'foo$|bar$' [45] 0x800 0 OBJT GLOB D 0 ABS bar [69] 0x400 0 FUNC GLOB D 0 ABS foo
入力ファイルから入手される場合、関数のシンボル定義またはデータ項目は、通常、データストレージの要素に関連しています。mapfile 定義は、このデータストレージを構成するためには不十分であるため、これらのシンボルは、絶対値として残しておく必要があります。size が関連付けられるが、value は関連付けられない単純な mapfile 定義では、データストレージが作成されます。この場合、シンボル定義にはセクションインデックスが伴います。ただし、mapfile 定義に value を関連付けると、絶対シンボルが作成されます。シンボルが共有オブジェクト内で定義される場合、絶対定義は避けるようにしてください。シンボル定義の増強を参照してください。
mapfile は COMMON または一時的シンボルを定義する場合にも使用できます。ほかのタイプのシンボル定義とは違って、一時的シンボルは、ファイル内のストレージを占有しませんが、実行時に割り当てるストレージの定義は行います。そのため、このタイプのシンボル定義は、作成される出力ファイルのストレージ割り当ての一因となります。
一時的シンボルの特徴は、ほかのシンボルタイプとは異なり、その値の属性によって、その配列要件が示される点です。そのため、リンク編集の入力ファイルから入手される一時的定義の再配列に mapfile 定義を使用できます。
次の例では、2 つの一時的シンボルの定義を示しています。シンボル foo は、新しいストレージ領域を定義しているのに対し、シンボル bar は、実際に、ファイル main.c 内の同じ一時的定義の配列を変更するために使用されます。
$ cat main.c #include <stdio.h> extern int foo; int bar[0x10]; void main() { (void) printf("&foo = 0x%p\n", &foo); (void) printf("&bar = 0x%p\n", &bar); } $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: foo { TYPE=COMMON; VALUE=0x4; SIZE=0x200 }; bar { TYPE=COMMON; VALUE=0x102; SIZE=0x40 }; }; $ cc -o prog -M mapfile main.c ld: warning: symbol 'bar' has differing alignments: (file mapfile value=0x102; file main.o value=0x4); largest value applied $ prog &foo = 0x21264 &bar = 0x21224 $ elfdump -sN.symtab prog | egrep 'foo$|bar$' [45] 0x21224 0x40 OBJT GLOB D 0 .bss bar [69] 0x21264 0x200 OBJT GLOB D 0 .bss foo
共有オブジェクト内での絶対データシンボルの作成は避けるべきです。通常、動的実行可能ファイルから、共有オブジェクト内のデータ項目への外部参照には、コピー再配置の作成が必要になります。コピー再配置を参照してください。このような再配置を行う場合は、データ項目をデータストレージと関連付けるべきです。この関連付けは、再配置可能なオブジェクトファイル内にシンボルを定義することで行うことができます。この関連付けは、mapfile 内でシンボルを size 宣言あり、 value 宣言なしで定義しても行うことができます。SYMBOL_SCOPE/SYMBOL_VERSION 指令を参照してください。
データシンボルにはフィルタを適用できます。フィルタとしての共有オブジェクトを参照してください。このようなフィルタ適用を行うため、オブジェクトファイル定義は mapfile 定義で増強できます。次の例では、関数定義とデータ定義を含むフィルタを作成します。
$ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: foo { TYPE=FUNCTION; FILTER=filtee.so.1 }; bar { TYPE=DATA; SIZE=0x4; FILTER=filtee.so.1 }; local: *; }; $ cc -o filter.so.1 -G -Kpic -h filter.so.1 -M mapfile -R. $ elfdump -sN.dynsym filter.so.1 | egrep 'foo|bar' [1] 0x105f8 0x4 OBJT GLOB D 1 .data bar [7] 0 0 FUNC GLOB D 1 ABS foo $ elfdump -y filter.so.1 | egrep 'foo|bar' [1] F [0] filtee.so.1 bar [7] F [0] filtee.so.1 foo
実行時に、外部オブジェクトからこれらのシンボルのいずれかへの参照は、「フィルティー」内の定義に解決されます。