JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
リンカーとライブラリ     Oracle Solaris 11 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

パート I リンカーおよび実行時リンカーの使用

1.  Oracle Solaris リンカーの紹介

2.  リンカー

リンカーの起動

直接起動

コンパイラドライバを使用する

クロスリンク編集

リンカーオプションの指定

入力ファイルの処理

アーカイブ処理

共有オブジェクトの処理

追加ライブラリとのリンク

ライブラリの命名規約

共有オブジェクトとアーカイブとの混合体へのリンク

コマンド行上のアーカイブの位置

リンカーが検索するディレクトリ

実行時リンカーが検索するディレクトリ

初期設定および終了セクション

シンボルの処理

シンボル解決

単純な解決

複雑な解決

重大な解決

未定義シンボル

実行可能ファイルの作成

共有オブジェクト出力ファイルの生成

ウィークシンボル

出力ファイル内の一時的シンボル順序

追加シンボルの定義

-u オプションを使用した追加シンボルの定義

シンボル参照の定義

絶対シンボルの定義

一時的シンボルの定義

シンボル定義の増強

シンボル範囲の縮小

シンボル削除

外部結合

文字列テーブルの圧縮

出力ファイルの生成

機能要件の特定

プラットフォーム機能の特定

マシン機能の指定

ハードウェア機能の特定

ソフトウェア機能の特定

シンボル機能関数ファミリの作成

シンボル機能データ項目のファミリの作成

オブジェクト機能のシンボル機能への変換

機能ファミリの実行

再配置処理

ディスプレイスメント再配置

スタブオブジェクト

デバッグ支援

3.  実行時リンカー

4.  共有オブジェクト

5.  インタフェースおよびバージョン管理

6.  動的ストリングトークンによる依存関係の確立

パート II クイックリファレンス

7.  リンカーのクイックリファレンス

8.  バージョン管理の手引き

パート III 詳細情報

9.  直接結合

10.  mapfile

11.  拡張性メカニズム

パート IV ELF アプリケーションバイナリインタフェース

12.  オブジェクトファイル形式

13.  プログラムの読み込みと動的リンク

14.  スレッド固有領域 (TLS)

パート V 付録

A.  リンカーとライブラリのアップデートおよび新機能

B.  System V Release 4 (バージョン 1) Mapfile

索引

シンボルの処理

入力ファイルの処理中に、入力再配置可能オブジェクトからすべての「ローカル」シンボルが出力ファイルイメージに渡されます。入力再配置可能オブジェクトから渡される大域シンボルはすべて、共有オブジェクト依存関係から渡される大域シンボルとともにリンカーの内部に蓄積されます。

入力ファイルから大域シンボルが供給されると、この内部シンボルテーブル内が検索されます。過去の入力ファイルで、同じ名前のシンボルに遭遇したことがある場合には、シンボル解決プロセスが呼び出されます。この解決プロセスは、再配置可能オブジェクトから渡される 2 つのエントリのうちどちらを保持するかを決定します。この解決プロセスは、共有オブジェクト依存関係への外部参照を確立する方法も決定します。

入力ファイルの処理が完了し、致命的なシンボル解決エラーが発生していない場合、リンカーは未解決のシンボル参照が残っていないかどうかを判断します。未解決のシンボル参照があると、リンク編集は強制終了します。

最後に、リンカーの内部シンボルテーブルが、作成されるイメージのシンボルテーブルに追加されます。

次の項では、シンボル解決と未定義シンボルの処理について詳しく説明します。

シンボル解決

シンボル解決は、簡単で直感的に分かるものから、複雑で当惑するようなものまで、すべての範囲を実行します。ほとんどのシンボル解決は、リンカーによって自動的に実行されます。ただし、警告診断を伴う再配置や、致命的なエラー状態の原因となる再配置もあります。

もっとも一般的で単純な解決は、あるオブジェクトから別のオブジェクト内部のシンボル定義へのシンボル参照の結合です。この結合は、2 つの再配置可能オブジェクト間、および再配置可能オブジェクトと共有オブジェクト依存関係内で検出された最初の定義の間で発生する場合があります。通常、複雑な解決は、2 つ以上の再配置可能オブジェクトの間で発生します。

2 つのシンボルの解決は、シンボルの属性、シンボルを入手したファイルのタイプおよび生成されるファイルのタイプによって異なります。シンボルの属性についての詳細は、「シンボルテーブルセクション」を参照してください。ただし、次の説明では、次の 3 つのシンボルタイプが特定されます。

簡単な形式では、シンボル解決で優先関係が使用されます。この関係では、定義シンボルが一時的シンボルより優先され、一時的シンボルは未定義シンボルより優先されます。

次の C コードの例では、これらのシンボルタイプがどのようにして生成されるかを示しています。未定義シンボルの接頭辞は、u_ です。一時的シンボルの接頭辞は、t_ です。定義シンボルの接頭辞は、d_ です。

$ cat main.c
extern int      u_bar;
extern int      u_foo();

int             t_bar;
int             d_bar = 1;

int d_foo()
{
        return (u_foo(u_bar, t_bar, d_bar));
}
$ cc -o main.o -c main.c
$ elfdump -s main.o

Symbol Table Section:  .symtab
     index    value      size      type bind oth ver shndx          name
     ....
       [7]  0x00000000 0x00000000  FUNC GLOB  D    0 UNDEF          u_foo
       [8]  0x00000010 0x00000040  FUNC GLOB  D    0 .text          d_foo
       [9]  0x00000004 0x00000004  OBJT GLOB  D    0 COMMON         t_bar
      [10]  0x00000000 0x00000004  NOTY GLOB  D    0 UNDEF          u_bar
      [11]  0x00000000 0x00000004  OBJT GLOB  D    0 .data          d_bar

単純な解決

単純なシンボル解決は、もっとも一般的です。この場合、類似する特徴を持ち、どちらかが優先される 2 つのシンボルが検出されます。このシンボル解決は、リンカーによって自動的に実行されます。たとえば、同じ結合を持つシンボルがあり、1 つのファイルからのシンボル参照が、別のファイルの定義または一時的シンボル定義に結合されているとします。あるいは、あるファイルからの一時的シンボル定義は、ほかのファイルからの定義シンボルの定義に結合されます。この解決は、2 つの再配置可能オブジェクト間、および再配置可能オブジェクトと共有オブジェクト依存関係内で検出された最初の定義の間で発生する場合があります。

解決されるシンボルは、大域結合またはウィーク結合されます。再配置可能オブジェクトの処理中は、ウィーク結合の方が、大域結合よりも優先度が低くなります。ウィークシンボル定義は同じ名前の大域定義によって暗黙のうちに上書きされます。

単純なシンボル解決のもう 1 つの形式である「割り込み」は、再配置可能オブジェクトと共有オブジェクト間、または複数の共有オブジェクト間で発生します。この場合、シンボルが複数回定義されていれば、再配置可能オブジェクト、または複数の共有オブジェクト間の最初の定義がリンカーによって暗黙のうちに採用されます。再配置可能オブジェクトの定義、または最初の共有オブジェクトの定義は、ほかのすべての定義上に割り込みを行うといわれます。この割り込みを使用して、別の共有オブジェクトが提供する機能を無効にすることができます。再配置可能オブジェクトと共有オブジェクトの間、または複数の共有オブジェクト間で発生する複数回定義されたシンボルは、同一に扱われます。シンボルのウィーク結合や大域結合は、これとは無関係です。最初の定義を解決することにより、シンボルの結合に関係なく、リンカーと実行時リンカーの両方が一貫して動作します。

リンカーの -m オプションを使用して、割り込みされるすべてのシンボル参照のリストを、セクションの読み込みアドレス情報とともに標準出力に書き込んでください。

複雑な解決

複雑な解決は、同じ名前を持つ 2 つのシンボルが、異なる属性とともに検出された場合に発生します。これらの場合、リンカーは警告メッセージを生成し、もっとも適切なシンボルを選択します。このメッセージは、シンボル、相反する属性、シンボル定義の元になるファイルの識別情報を示します。次の例では、データ項目の配列の定義が指定された 2 つのファイルで、サイズの必要条件が異なっています。

$ cat foo.c
int array[1];

$ cat bar.c
int array[2] = { 1, 2 };

$ ld -r -o temp.o foo.c bar.c
ld: warning: symbol `array' has differing sizes:
        (file foo.o value=0x4; file bar.o value=0x8);
        bar.o definition taken

シンボルの整列要件が異なっている場合も、同様の診断が生成されます。この 2 つのケースの場合、リンカーの -t オプションを使用すると、診断を抑制できます。

異なる属性のもう 1 つの形式は、シンボルのタイプの違いです。次の例では、シンボル bar() は、データ項目と関数の両方として定義されています。

$ cat foo.c
int bar()
{
        return (0);
}
$ cc -o libfoo.so -G -K pic foo.c
$ cat main.c
int     bar = 1;

int main()
{
        return (bar);
}
$ cc -o main main.c -L. -lfoo
ld: warning: symbol `bar' has differing types:
        (file main.o type=OBJT; file ./libfoo.so type=FUNC);
        main.o definition taken

注 - この文脈では、シンボルのタイプは ELF で使用されるタイプです。このシンボルタイプは、単純な形式であることを除けば、プログラミング言語で使用されるデータ型には関連していません。


前の例のような場合、解決が再配置可能オブジェクトと共有オブジェクト間で行われる場合に再配置可能オブジェクトの定義が使用されます。または、2 つの共有オブジェクト間で解決が行われる場合は、最初の定義が使用されます。ウィーク結合または大域結合のシンボル間でこのような解決を行うと、警告も発せられます。

リンカーの -t オプションを使用しても、シンボルタイプ間の不一致は抑制できません。

重大な解決

解決できないシンボルの矛盾は、致命的なエラー状態や該当エラーメッセージの原因となります。このメッセージは、シンボルを提供したファイルの名前とともに、シンボル名を示します。出力ファイルは生成されません。この重大なエラー状態によってリンカーは停止しますが、すべての入力ファイルの処理が、まず最初に完了します。この要領で、重大な解決エラーをすべて識別できます。

もっとも一般的な致命的エラー状態は、2 つの再配置可能オブジェクト両方が、同じ名前のウィーク以外のシンボルを定義した場合に起こります。

$ cat foo.c
int bar = 1;

$ cat bar.c
int bar()
{ 
        return (0);
}

$ ld -r -o temp.o foo.c bar.c
ld: fatal: symbol `bar' is multiply-defined:
        (file foo.o and file bar.o);
ld: fatal: File processing errors. No output written to int.o

foo.cbar.c に含まれるシンボル bar の定義が、互いに矛盾しています。リンカーは、どちらを優先すべきか判別できないため、通常はエラーメッセージを出力して終了します。リンカーの -z muldefs を使用すると、エラー状態を抑制できます。このオプションによって、最初のシンボル定義が使用されます。

未定義シンボル

すべての入力ファイルを読み取り、シンボル解決がすべて完了すると、リンカーは、シンボル定義に結合されていないシンボル参照の内部シンボルテーブルを検索します。これらのシンボル参照は、未定義シンボルと呼ばれます。未定義シンボルがリンク編集処理に及ぼす影響は、生成される出力ファイルのタイプや、シンボルのタイプによって異なります。

実行可能ファイルの作成

リンカーが実行可能出力ファイルを生成する際のデフォルト動作は、「未定義のままのシンボルが存在するかぎり、適切なエラーメッセージを出力して処理を終了する」というものです。次のように、再配置可能オブジェクト内のシンボル参照が、シンボル定義と絶対に一致しない場合に、シンボルは定義されないままの状態になります。

$ cat main.c
extern int foo();

int main()
{
        return (foo());
}
$ cc -o prog main.c
Undefined           first referenced
 symbol                 in file
foo                     main.o
ld: fatal: Symbol referencing errors. No output written to prog

同様に、共有オブジェクトを使って動的実行可能ファイルを作成する場合、未解決のままのシンボル定義が存在していると、未定義シンボルエラーが発生します。

$ cat foo.c
extern int bar;
int foo()
{
        return (bar);
}

$ cc -o libfoo.so -G -K pic foo.c
$ cc -o prog main.c -L. -lfoo
Undefined           first referenced
 symbol                 in file
bar                     ./libfoo.so
ld: fatal: Symbol referencing errors. No output written to prog

前の例のように未定義シンボルを許可するには、リンカーの -z nodefs オプションを使用して、デフォルトエラー条件を抑制します。


注 - -z nodefs オプションを使用する場合は、注意が必要です。処理の実行中に使用できないシンボル参照が要求されると、重大な実行時再配置エラーが発生します。このエラーは、アプリケーションをはじめて実行およびテストした際に検出される場合があります。しかし、実行パスがより複雑であるとエラー状態の検出に時間がかかり、時間とコストが浪費される場合があります。


シンボルは、再配置可能オブジェクト内のシンボル参照が、暗黙の内に定義された共有オブジェクト内のシンボル定義に結合されている場合にも、未定義シンボルのままになる場合があります。たとえば、上記の例で使用したファイル main.c および foo.c に次のように続く場合です。

$ cat bar.c
int bar = 1;

$ cc -o libbar.so -R. -G -K pic bar.c -L. -lfoo
$ ldd libbar.so
        libfoo.so =>     ./libfoo.so

$ cc -o prog main.c -L. -lbar
Undefined           first referenced
 symbol                 in file
foo                     main.o  (symbol belongs to implicit \
                        dependency ./libfoo.so)
ld: fatal: Symbol referencing errors. No output written to prog

prog は、libbar.so に対する「明示的」な参照に基づいて構築されます。libbar.so は、libfoo.so に依存しています。したがって、libfoo.so への暗黙的参照が prog から確立されます。

main.c は、libfoo.so によって作成されたインタフェースへの特定の参照を実行するため、prog は、実際に libfoo.so に依存性を持つことになります。ただし、生成される出力ファイル内に記録されるのは、明示的な共有オブジェクトの依存関係だけです。そのため、libbar.so の新しいバージョンが開発され、libfoo.so への依存性がなくなった場合、prog は実行に失敗します。

このため、このタイプの結合は致命的とみなされます。暗黙的参照は、prog のリンク編集中に直接ライブラリを参照することで明示的に行います。この例で示した重大なエラーメッセージ内に必要な参照のヒントがあります。

共有オブジェクト出力ファイルの生成

リンカーが共有オブジェクト出力ファイルを生成する場合、未定義シンボルをリンク編集の後も残すことができます。このデフォルト動作により、共有オブジェクトが、依存関係として共有オブジェクトを定義する動的実行可能ファイルからシンボルをインポートできます。

リンカーの -z defs オプションを使用すると、未定義シンボルが残っていた場合に、強制的に重大エラーにすることができます。共有オブジェクトを作成するときには、このオプションの使用をお勧めします。アプリケーションからシンボルを参照する共有オブジェクトは、extern mapfile 指令でシンボルを定義するとともに、-z defs オプションを使用できます。「SYMBOL_SCOPE/SYMBOL_VERSION 指令」を参照してください。

自己完結型の共有オブジェクトは、外部シンボルへのすべての参照は指定された依存関係によって満たされ、最大の柔軟性が提供されます。この共有オブジェクトは、共有オブジェクトの必要条件を満たす依存関係を判別し確立する手間をユーザーにかけることなく、多数のユーザーによって使用されます。

ウィークシンボル

ウィークシンボルは歴史的に、割り込みを回避したり、オプション機能をテストしたりするために使用されてきました。ただし、近年のプログラミング環境ではウィークシンボルは脆弱で信頼性が低いことが経験によって示されており、使用は推奨されません。

ウィークシンボル別名はシステム共有オブジェクト内で頻繁に使用されてきました。その意図は、代替のインタフェース名を提供することであり、典型的には「_」文字が前に付いたシンボル名です。このエイリアス名は、アプリケーションがシンボル名の独自の実装をエクスポートすることによる割り込みの問題を回避するために、他のシステム共有オブジェクトから参照できます。現実的には、この手法は複雑すぎることが証明され、整合性を持たずに使用されていました。最近の Oracle Solaris バージョンでは、システムオブジェクト間の明示的な結合を直接結合によって確立します。第 9 章直接結合を参照してください。

ウィークシンボル参照は、実行時でのインタフェースの存在をテストするためにしばしば使用されていました。この手法は、構築環境および実行環境に制限を設け、コンパイラ最適化によって回避できます。dlsym(3C)RTLD_DEFAULT または RTLD_PROBE ハンドルと一緒に使用することで、シンボルの存在をテストするための一貫性のある堅牢な手段が提供されます。「機能のテスト」を参照してください。

出力ファイル内の一時的シンボル順序

入力ファイルの追加は、通常、その追加の順に出力ファイルに表示されます。一時的シンボルはこの規則の例外であり、シンボルが完全に解決されるまで完全には定義されません。出力ファイル内の一時的シンボルの順番は、追加順にならない場合があります。

シンボルグループの順序を制御する必要がある場合には、一時的定義は、ゼロで初期化されたデータ項目に再定義する必要があります。たとえば、次のような一時的定義をすると、出力ファイル内のデータ項目が、ソースファイル foo.c に記述された元の順序と比較されて再配列されます。

$ cat foo.c
char One_array[0x10];
char Two_array[0x20];
char Three_array[0x30];

$ cc -o libfoo.so -G -Kpic foo.c
$ elfdump -sN.dynsym libfoo.so | grep array | sort -k 2,2
      [11]  0x00010614 0x00000020  OBJT GLOB  D    0 .bss           Two_array
       [3]  0x00010634 0x00000030  OBJT GLOB  D    0 .bss           Three_array
       [4]  0x00010664 0x00000010  OBJT GLOB  D    0 .bss           One_array

シンボルをアドレス順にソートすると、その出力順序はソース内で定義された順序と異なることがわかります。反対に、これらのシンボルを初期化されたデータ項目として定義すると、入力ファイル内のこれらのシンボルの相対順序は、確実に出力ファイル内に引き継がれます。

$ cat foo.c
char A_array[0x10] = { 0 };
char B_array[0x20] = { 0 };
char C_array[0x30] = { 0 };

$ cc -o libfoo.so -G -Kpic foo.c
$ elfdump -sN.dynsym libfoo.so | grep array | sort -k 2,2
       [4]  0x00010614 0x00000010  OBJT GLOB  D    0 .data          One_array
      [11]  0x00010624 0x00000020  OBJT GLOB  D    0 .data          Two_array
       [3]  0x00010644 0x00000030  OBJT GLOB  D    0 .data          Three_array

追加シンボルの定義

入力ファイルから提供されるシンボルのほかに、追加の大域シンボル参照や大域シンボル定義をリンク編集に対して指定できます。もっとも簡単な形式で、シンボル参照は、リンカーの -u オプションを使用して作成できます。リンカーの -M オプションと関連 mapfile を使用すると柔軟性が高まります。この mapfile を使用すると、大域シンボル参照およびさまざまな大域シンボル定義を定義できます。可視性や型などのシンボルの属性を指定できます。使用可能なオプションの詳細な説明については、「SYMBOL_SCOPE/SYMBOL_VERSION 指令」を参照してください。

-u オプションを使用した追加シンボルの定義

-u オプションを指定すると、リンク編集コマンド行から大域シンボル参照を作成するためのメカニズムが使用できます。このオプションを使用して、リンク編集を完全にアーカイブから実行することができます。このオプションは、複数のアーカイブから抽出するオブジェクトを選択する際の柔軟性も高めます。アーカイブの抽出については、「アーカイブ処理」を参照してください。

たとえば、動的実行可能プログラムを、シンボル foobar への参照を実行する再配置可能オブジェクト 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 の直接的または間接的な参照は行いません。lib1.abar を参照する場合、処理中に再配置可能オブジェクト bar.olib1.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]  0x00010f30 0x00000024  FUNC LOCL  H    0 .text          bar
      [30]  0x00010ef8 0x00000024  FUNC LOCL  H    0 .text          foo
      [55]  0x00010f68 0x00000024  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]  0x00000800 0x00000000  OBJT GLOB  D    0 ABS            bar
      [69]  0x00000400 0x00000000  FUNC GLOB  D    0 ABS            foo

入力ファイルから入手される場合、関数のシンボル定義またはデータ項目は、通常、データ記憶域の要素に関連しています。mapfile 定義は、このデータ記憶域を構成するためには不十分であるため、これらのシンボルは、絶対値として残しておく必要があります。size が関連付けられるが、value関連付けられない単純な mapfile 定義では、データ記憶域が作成されます。この場合、シンボル定義にはセクションインデックスが伴います。ただし、mapfile 定義に value を関連付けると、絶対シンボルが作成されます。シンボルが共有オブジェクト内で定義される場合、絶対定義は避けるようにしてください。「シンボル定義の増強」を参照してください。

一時的シンボルの定義

mapfileCOMMON または一時的シンボルを定義する場合にも使用できます。ほかのタイプのシンボル定義とは違って、一時的シンボルは、ファイル内の記憶域を占有しませんが、実行時に割り当てる記憶域の定義は行います。そのため、このタイプのシンボル定義は、作成される出力ファイルの記憶域割り当ての一因となります。

一時的シンボルの特徴は、ほかのシンボルタイプとは異なり、その「値」の属性によって、その配列要件が示される点です。そのため、リンク編集の入力ファイルから入手される一時的定義の再配列に 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]  0x00021224 0x00000040  OBJT GLOB  D    0 .bss           bar
      [69]  0x00021264 0x00000200  OBJT GLOB  D    0 .bss           foo

注 - このシンボル解決の診断は、リンカーの -t オプションを使用すると表示されません。


シンボル定義の増強

共有オブジェクト内での絶対データシンボルの作成は避けるべきです。通常、動的実行可能ファイルから、共有オブジェクト内のデータ項目への外部参照には、コピー再配置の作成が必要になります。「コピー再配置」を参照してください。このような再配置を行う場合は、データ項目をデータ記憶領域と関連付けるべきです。この関連付けは、再配置可能なオブジェクトファイル内にシンボルを定義することで行うことができます。この関連付けは、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]  0x000105f8 0x00000004  OBJT GLOB  D    1 .data          bar
       [7]  0x00000000 0x00000000  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

実行時に、外部オブジェクトからこれらのシンボルのいずれかへの参照は、「フィルティー」内の定義に解決されます。

シンボル範囲の縮小

mapfile 内のローカル範囲を持つようにシンボル定義を定義するとシンボルの最終的な結合を縮小できます。このメカニズムによって、入力の一部として生成ファイルを使用する将来のリンク編集でシンボルが表示されなくなります。実際、このメカニズムは、ファイルのインタフェースの厳密な定義をするために提供されているため、ほかのユーザーに対して、機能の使用を制限できます。

たとえば、簡単な共有オブジェクトを、ファイル foo.cbar.c から生成するとします。ファイル foo.c には、ほかのユーザーも使用できるように設定するサービスを提供する大域シンボル foo が組み込まれています。ファイル bar.c には、共有オブジェクトの根底となるインプリメンテーションを提供するシンボル barstr が組み込まれています。これらのファイルを使用して共有オブジェクトを作成すると、通常、次のように大域範囲が指定された 3 つのシンボルが作成されます。

$ cat foo.c
extern  const char *bar();

const char *foo()
{
        return (bar());
}
$ cat bar.c
const char *str = "returned from bar.c";

const char *bar()
{
        return (str);
}
$ cc -o libfoo.so.1 -G foo.c bar.c
$ elfdump -sN.symtab libfoo.so.1 | egrep 'foo$|bar$|str$'
      [41]  0x00000560 0x00000018  FUNC GLOB  D    0 .text          bar
      [44]  0x00000520 0x0000002c  FUNC GLOB  D    0 .text          foo
      [45]  0x000106b8 0x00000004  OBJT GLOB  D    0 .data          str

これで、libfoo.so.1 により提供された機能を、別のアプリケーションのリンク編集の一部として使用できます。シンボル foo への参照は、共有オブジェクトによって提供されたインプリメンテーションに結合されます。

大域結合により、シンボル barstr への直接参照も可能です。ただし、この可視性は危険な結果を招く場合があります。関数 foo の基礎となるインプリメンテーションは、後から変更することがあるためです。それが原因で知らないうちに、bar または str に結合された既存のアプリケーションが失敗または誤作動を起こす可能性があります。

また、シンボル barstr を大域結合すると、同じ名前のシンボルによって割り込まれる可能性があります。共有オブジェクト内へのシンボルの割り込みについては、「単純な解決」 で説明しています。この割り込みは、意図的に行うことができ、これを使用することにより、共有オブジェクトが提供する目的の機能を取り囲むことができます。また反対に、この割り込みは、同じ共通のシンボル名をアプリケーションと共有オブジェクトの両方に使用した結果として、知らないうちに実行される場合もあります。

共有オブジェクトを開発する場合は、シンボル barstr の範囲をローカル結合に縮小して、このような事態から保護できます。次の例では、シンボル barstr は、共有オブジェクトのインタフェースの一部としては利用できなくなっています。そのため、これらのシンボルは、外部のオブジェクトによって参照されることができないか、割り込みはできません。ユーザーは、インタフェースをこの共有オブジェクト用に効果的に定義できます。インプリメンテーションの基礎となる詳細を隠している間は、このインタフェースを管理できます。

$ cat mapfile
$mapfile_version 2
SYMBOL_SCOPE {
        local:
                bar;
                str;
};
$ cc -o libfoo.so.1 -M mapfile -G foo.c bar.c
$ elfdump -sN.symtab libfoo.so.1 | egrep 'foo$|bar$|str$'
      [24]  0x00000548 0x00000018  FUNC LOCL  H    0 .text          bar
      [25]  0x000106a0 0x00000004  OBJT LOCL  H    0 .data          str
      [45]  0x00000508 0x0000002c  FUNC GLOB  D    0 .text          foo

このようなシンボル範囲の縮小には、このほかにもパフォーマンスにおける利点があります。実行時に必要だったシンボル barstr に対するシンボルの再配置は、現在は、関連する再配置に縮小されます。シンボル再配置のオーバーヘッドの詳細は、「再配置が実行されるとき」を参照してください。

リンク編集の間に処理されるシンボル数が多くなると、mapfile 内で各ローカル範囲の縮小を定義するのが困難になります。代わりとなる、より柔軟なメカニズムを使用すると、維持しなければならない大域シンボルの点で共有オブジェクトのインタフェースを定義できます。大域シンボルを定義すると、リンカーはその他のシンボルすべてをローカル結合にすることができます。このメカニズムは、特別な自動縮小指令の「*」を使用して実行します。たとえば、前の mapfile 定義を書き換えて、生成される出力ファイル内で必要な唯一の大域シンボルとして foo を定義します。

$ cat mapfile
$mapfile_version 2
SYMBOL_VERSION ISV_1.1 {
        global:
                foo;
        local:
                *;
};
$ cc -o libfoo.so.1 -M mapfile -G foo.c bar.c
$ elfdump -sN.symtab libfoo.so.1 | egrep 'foo$|bar$|str$'
      [26]  0x00000570 0x00000018  FUNC LOCL  H    0 .text          bar
      [27]  0x000106d8 0x00000004  OBJT LOCL  H    0 .data          str
      [50]  0x00000530 0x0000002c  FUNC GLOB  D    0 .text          foo

この例では、バージョン名 libfoo.so.1.1mapfile 指令の一部として定義しています。このバージョン名により、ファイルのシンボルインタフェースを定義する、内部バージョン定義が確立されます。バージョン定義はできるだけ作成してください。バージョン定義によって、ファイルの展開全体を通して使用できる、内部バージョンメカニズムの基礎が形成されます。第 5 章インタフェースおよびバージョン管理を参照してください。


注 - バージョン名が指定されていないと、出力ファイル名がバージョン定義のラベル付けに使用されます。出力ファイル内に作成されたバージョン情報は、リンカーの -z noversion オプションを使用して表示しないようにできます。


バージョン名を指定する場合は必ず、「すべて」の大域シンボルをバージョン定義に割り当てる必要があります。バージョン定義に割り当てられていない大域シンボルが残っていると、リンカーにより重大なエラー状態が発生します。

$ cat mapfile
$mapfile_version 2
SYMBOL_VERSION ISV_1.1 {
        global:
                foo;
};
$ cc -o libfoo.so.1 -M mapfile -G foo.c bar.c
Undefined           first referenced
 symbol                 in file
str                     bar.o  (symbol has no version assigned)
bar                     bar.o  (symbol has no version assigned)
ld: fatal: Symbol referencing errors. No output written to libfoo.so.1

-B local オプションを使用して、コマンド行から自動縮小指令「*」を表明することができます。前の例は、次のようにコンパイルすることもできます。

$ cc -o libfoo.so.1 -M mapfile -B local -G foo.c bar.c

実行可能ファイルまたは共有オブジェクトを生成すると、シンボルの縮小によって、出力イメージ内にバージョン定義が記録されます。再配置可能オブジェクトの生成時にバージョン定義は作成されますが、シンボルの縮小処理は行われません。その結果、シンボル縮小のシンボルエントリは、大域のまま残されます。たとえば、自動縮小指令が指定された前の mapfile と、関連する再配置可能オブジェクトを使用して、シンボル縮小が表示されていない中間再配置可能オブジェクトが作成されます。

$ cat mapfile
$mapfile_version 2
SYMBOL_VERSION ISV_1.1 {
        global:
                foo;
        local:
                *;
};
$ ld -o libfoo.o -M mapfile -r foo.o bar.o
$ elfdump -s libfoo.o | egrep 'foo$|bar$|str$'
      [28]  0x00000050 0x00000018  FUNC GLOB  H    0 .text          bar
      [29]  0x00000010 0x0000002c  FUNC GLOB  D    2 .text          foo
      [30]  0x00000000 0x00000004  OBJT GLOB  H    0 .data          str

このイメージ内に作成されたバージョン定義は、シンボル縮小が要求されたという事実を記録します。再配置可能オブジェクトが、最終的に、実行可能ファイルまたは共有オブジェクトの生成に使用されるときに、シンボル縮小が実行されます。すなわち、リンカーは、mapfile からバージョン管理データを処理するのと同じ方法で、再配置可能オブジェクト内に組み込まれたシンボル縮小を読み取り、解釈します。

そのため、上記の例で作成された中間再配置可能オブジェクトは、ここで、共有オブジェクトの生成に使用されます。

$ ld -o libfoo.so.1 -G libfoo.o
$ elfdump -sN.symtab libfoo.so.1 | egrep 'foo$|bar$|str$'
      [24]  0x00000508 0x00000018  FUNC LOCL  H    0 .text          bar
      [25]  0x00010644 0x00000004  OBJT LOCL  H    0 .data          str
      [42]  0x000004c8 0x0000002c  FUNC GLOB  D    0 .text          foo

シンボル縮小は、通常、実行可能ファイルまたは共有オブジェクトが作成されたときに行う必要があります。ただし、再配置可能オブジェクトが作成されたときは、リンカーの -B reduce オプションを使用して強制的に実行されます。

$ ld -o libfoo.o -M mapfile -B reduce -r foo.o bar.o
$ elfdump -sN.symtab libfoo.o | egrep 'foo$|bar$|str$'
      [20]  0x00000050 0x00000018  FUNC LOCL  H    0 .text          bar
      [21]  0x00000000 0x00000004  OBJT LOCL  H    0 .data          str
      [30]  0x00000010 0x0000002c  FUNC GLOB  D    2 .text          foo

シンボル削除

シンボル縮小の拡張の 1 つは、オブジェクトのシンボルテーブルから特定のシンボルエントリを削除することです。局所シンボルは、オブジェクトの .symtab シンボルテーブルだけで管理されます。このテーブル全体は、リンカーの -z strip-class オプションを使用して、またはリンク編集後に strip(1) を使用してオブジェクトから取り除くことができます。しかし、.symtab シンボルテーブルは削除しないで、特定の局所シンボルだけを削除したいこともあります。

シンボルの削除は mapfile キーワード ELIMINATE を使用して実行できます。local 指令と同様に個別にシンボルを定義することも、特殊な自動削除指令「*」としてシンボル名を定義することもできます。次の例では、前述のシンボル縮小の例で使用したシンボル bar を削除しています。

$ cat mapfile
$mapfile_version 2
SYMBOL_VERSION ISV_1.1 {
        global:
                foo;
        local:
                str;
        eliminate:
                *;
};
$ cc -o libfoo.so.1 -M mapfile -G foo.c bar.c
$ elfdump -sN.symtab libfoo.so.1 | egrep 'foo$|bar$|str$'
      [26]  0x00010690 0x00000004  OBJT LOCL  H    0 .data          str
      [44]  0x000004e8 0x0000002c  FUNC GLOB  D    0 .text          foo

-B eliminate オプションを使用して、コマンド行から「自動削除」指令「*」を表明することもできます。

外部結合

作成するオブジェクトのシンボル参照が共有オブジェクト内の定義によって解決されると、そのシンボルは未定義のまま残ります。シンボルに対応する再配置情報が実行時の検索で使用されます。定義を提供する共有オブジェクトは、通常、1 つの依存条件になります。

実行時リンカーは、実行時にデフォルト検索モデルを使ってこの定義を見つけます。一般にオブジェクトは 1 つずつ検索されますが、その際、動的実行可能プログラムから、オブジェクトが読み込まれた順に各依存関係が処理されます 。

オブジェクトは、直接結合を使用するように作成することもできます。この方法では、シンボル参照と、シンボル定義を提供するオブジェクトとの関係は、作成されるオブジェクト内に維持されます。この情報を使えば、実行時リンカーは、参照先とシンボルを定義するオブジェクトを直接結合し、デフォルトのシンボル検索モデルをバイパスできます。第 9 章直接結合を参照してください。

文字列テーブルの圧縮

リンカーは、重複したエントリと末尾部分文字列を削除することによって、文字列テーブルを圧縮します。この圧縮により、どのような文字列テーブルでもサイズが相当小さくなります。たとえば、.dynstr テーブルを圧縮すると、テキストセグメントが小さくなるため、実行時のページング作業が減ります。このような利点があるため、文字列テーブルの圧縮はデフォルトで有効に設定されています。

非常に多くのシンボルを提供するオブジェクトによって、文字列テーブルの圧縮のためにリンク編集時間が延びる可能性があります。開発時にこの負担を避けるには、リンカーの -z nocompstrtab オプションを使用してください。リンク編集時に行われる文字列テーブルの圧縮は、リンカーのデバッグトークン -D strtab,detail を使用して表示できます。