Oracle® Solaris 11.2 リンカーとライブラリガイド

印刷ビューの終了

更新: 2014 年 7 月
 
 

機能要件の特定

機能によって、コードを実行するために必要なシステム属性が決まります。使用できる機能は、優先順に次のとおりです。

  • プラットフォーム機能 - 特定のプラットフォームを名前で指定します。

  • マシン機能 - 特定マシンのハードウェアを名前で指定します。

  • ハードウェア機能 - 命令セットの拡張やほかのハードウェアの詳細情報を、機能フラグを使用して指定します。

  • ソフトウェア機能 - ソフトウェア環境の属性を、機能フラグを使用して反映します。

これらの各機能は個々に定義したり、組み合わせて機能グループを作成したりできます。

ある機能が利用可能な場合にだけ実行できるコードは、関連する ELF オブジェクト内の機能セクションで、これらの要件を指定する必要があります。オブジェクト内に機能要件を記録すると、関連コードの実行を試みる前に、システムがそのオブジェクトを検証できます。これらの要件によって、オブジェクトファミリの中からもっとも適したオブジェクトをシステムが選択できるフレームワークを規定することもできます。ファミリは同じオブジェクトの派生から構成されます。ただし、各派生は異なる機能を要求します。

動的オブジェクトに加えて、オブジェクト内の各関数または初期化済みデータ項目を、機能要件に関連付けることができます。理想的には、機能要件はコンパイラによって作成される再配置可能オブジェクトに記録され、コンパイル時に指定されたオプションまたは最適化を反映します。リンカーは入力再配置可能オブジェクトの機能を組み合わせて、出力ファイルの最終機能セクションを作成します。機能セクションを参照してください。

さらに、リンカーが出力ファイルを作成するときにも機能を定義できます。これらの機能は、mapfile とリンカーの –M オプションを使用して特定します。mapfile を使用して定義された機能は、入力再配置可能オブジェクト内で指定された機能を強化したり、オーバーライドしたりすることができます。通常 mapfile は、必要な機能情報を生成しないコンパイラを補強するために使用されます。

システム機能は、実行中のシステムを記述する機能です。プラットフォーム名およびマシンハードウェア名は、それぞれ –i オプションと –m オプションを使用すると、uname(1)で表示できます。システムのハードウェア機能は、–v オプションを使用してisainfo(1)で表示できます。実行時、オブジェクトの機能要件はシステムの機能と比較され、オブジェクトを読み込むことができるかどうか、またはオブジェクト内のシンボルを利用できるかどうかを判断します。

オブジェクト機能は、オブジェクトに関連する機能です。これらの機能はオブジェクト全体の要件を定義して、実行時にオブジェクトを読み込むことができるかどうかを制御します。あるオブジェクトが、システムで満たすことができない機能を要求している場合、そのオブジェクトは実行時に読み込めません。機能を使用すると任意のオブジェクトから複数のインスタンスを作成でき、各インスタンスはオブジェクト要件に一致するシステムに最適化されます。実行時リンカーは、オブジェクトの機能要件をシステムが提供する機能と比較することで、このようなオブジェクトインスタンスファミリの中から最適なインスタンスを透過的に選択します。

シンボル機能は、オブジェクト内の各関数または初期化されたデータ項目に関連する機能です。これらの機能は、オブジェクト内の 1 つまたは複数のシンボルの要件を定義し、実行時にそのシンボルを使用できるかどうかを制御します。シンボル機能によって、1 つの関数の複数のインスタンスが 1 つのオブジェクト内に存在できます。関数の各インスタンスは、別々の機能を持つシステムに対して最適化できます。また、1 つの初期化されたデータ項目の複数のインスタンスも 1 つのオブジェクト内に存在できます。データの各インスタンスはシステム固有のデータを定義できます。シンボルのインスタンスが、システムによって満たすことができない機能を要求している場合、そのシンボルインスタンスは実行時に使用できません。代わりに、同じシンボル名の代替インスタンスを使用する必要があります。シンボル機能では、単一のオブジェクトをさまざまな機能のシステムで使用できるようにオブジェクトを構築できます。機能ファミリは、機能に対応できるシステム向けの最適化されたインスタンスと、機能が比較的低いほかのシステム向けのより汎用的なインスタンスを提供できます。初期化されたデータ項目のファミリはシステム固有データを提供できます。実行時リンカーは、シンボルの機能要件をシステムが提供する機能と比較することで、このようなシンボルインスタンスファミリの中から最適なインスタンスを透過的に選択します。

オブジェクトとシンボルの機能は、現在動作中のシステムに対して最適なオブジェクト、およびオブジェクト内の最適なシンボルを選択できます。オブジェクトとシンボルの機能はオプションであり、互いに独立しています。ただし、シンボル機能を定義しているオブジェクトがオブジェクト機能を定義してもかまいません。この場合、機能シンボルファミリには、オブジェクト機能を満たすシンボルの 1 つのインスタンスが伴われることになります。オブジェクトの機能が存在しない場合、機能シンボルファミリには、機能を要求しないシンボルの 1 つのインスタンスが伴われることになります。指定されたシステムに適合できる機能インスタンスがない場合、このシンボルインスタンスによってデフォルト実装が提供されます。

次の x86 の例は、foo.o のオブジェクト機能を示します。これらの機能はオブジェクト全体に適用されます。この例には、シンボル機能がありません。

$ elfdump -H foo.o

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_HW_1      0x840  [ SSE  MMX ]

次の x86 の例は、bar.o のシンボル機能を示します。これらの機能は個々の関数 foo および bar に適用されます。各シンボルのインスタンスは 2 つ存在し、それぞれのインスタンスは別々の機能セットに割り当てられています。この例には、オブジェクト機能がありません。

$ elfdump -H bar.o

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
     index  tag               value
       [1]  CA_SUNW_HW_1      0x40  [ MMX ]

  Symbols:
     index    value     size  type bind oth ver shndx    name
      [25]        0     0x21  FUNC LOCL  D    0 .text    foo%mmx
      [26]     0x24     0x1e  FUNC LOCL  D    0 .text    bar%mmx

 Symbol Capabilities:
     index  tag               value
       [3]  CA_SUNW_HW_1      0x800  [ SSE ]

  Symbols:
     index    value     size  type bind oth ver shndx    name
      [33]     0x44     0x21  FUNC LOCL  D    0 .text    foo%sse
      [34]     0x68     0x1e  FUNC LOCL  D    0 .text    bar%sse

注 -  この例では、機能シンボルは、機能識別子を汎用のシンボル名に追加する命名規則に従っています。リンカーはこの規則に従ってオブジェクト機能をシンボル機能に変換できます。あとで説明するオブジェクト機能のシンボル機能への変換を参照してください。

機能定義には、オブジェクトの要件、またはオブジェクト内の各シンボルの要件を特定できる多くの組み合わせが用意されています。ハードウェア機能には、非常に高い柔軟性が備わっています。ハードウェア機能でハードウェア要件を定義するときは、特定のマシンハードウェア名またはプラットフォーム名を指定しません。しかし、ベースとなるシステムの属性の中には、マシンのハードウェア名またはプラットフォーム名からしか判断できないものもあります。機能名を指定すると、特定のシステム機能向けにコーディングできますが、指定したオブジェクトの利用が制限される可能性があります。あるオブジェクトに対して新しいマシンハードウェア名またはプラットフォーム名が利用できるようになったときに、その新しい機能名を特定するために、オブジェクトの再作成が必要になります。

次のセクションでは、機能の定義方法とリンカーによる使用方法について説明します。

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

オブジェクトのプラットフォーム機能では、オブジェクトまたはオブジェクト内の特定のシンボルが実行できるシステムのプラットフォーム名を指定します。複数のプラットフォーム機能を定義できます。これはもっとも具体的であり、ほかの機能タイプに優先します。

システムのプラットフォーム名は、ユーティリティーuname(1)–i オプションを付けて実行すると表示できます。

プラットフォーム機能要件は次の mapfile 構文を使用すると定義できます。

    $mapfile_version 2
        CAPABILITY {
                PLATFORM  = platform_name...;
                PLATFORM += platform_name...;
                PLATFORM -= platform_name...;
        };

PLATFORM 属性は 1 つまたは複数のプラットフォーム名で修飾されます。「+=」形式の代入を使用すると、入力オブジェクトで指定されたプラットフォーム機能に追加できます。「=」形式はこれらをオーバーライドします。「-=」形式の代入を使用すると、出力オブジェクトからプラットフォーム機能を削除できます。次の SPARC の例では、オブジェクト foo.so.1SUNW,SPARC-Enterprise プラットフォームに固有であることを指定します。

$ cat mapfile
$mapfile_version 2
CAPABILITY {
        PLATFORM = 'SUNW,SPARC-Enterprise';
};
$ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc
$ elfdump -H foo.so.1

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_PLAT      SUNW,SPARC-Enterprise

再配置可能オブジェクトはプラットフォーム機能を定義できます。これらの機能がまとめられて、作成中のオブジェクトの最終的な機能要件が定義されます。

オブジェクトのプラットフォーム機能は、「=」形式の代入を使用すると mapfile から明示的に制御でき、入力の再配置可能オブジェクトから指定される可能性のあるプラットフォーム機能をオーバーライドできます。「=」形式の代入を使用して空の PLATFORM 属性を指定すると、作成中のオブジェクトからプラットフォームのすべての機能要件が削除されます。

動的オブジェクトで定義されたプラットフォーム機能要件は、システムのプラットフォーム名に照らして、実行時リンカーによって検証されます。オブジェクトが使用されるのは、オブジェクトに記録されたプラットフォーム名の 1 つがシステムのプラットフォーム名に一致した場合だけです。

コードを特定のプラットフォーム用に限定することは、場合によって有効となります。しかし、ハードウェア機能ファミリを作成する方が柔軟性を高めることができるため、この方法を推奨します。ハードウェア機能ファミリは、コードを幅広いシステムで実行できるように最適化できます。

マシン機能の指定

オブジェクトのマシン機能では、オブジェクトまたはオブジェクト内の特定のシンボルが実行できるシステムのマシンハードウェア名を指定します。複数のマシン機能を定義できます。この指定はプラットフォーム機能定義に比べて優先度が低いですが、ほかの機能タイプより優先します。

システムのマシンハードウェア名は、ユーティリティーuname(1)–m オプションを付けて実行すると表示できます。

マシン機能要件は次の mapfile 構文を使用すると定義できます。

    $mapfile_version 2
        CAPABILITY {
                MACHINE  = machine_name...;
                MACHINE += machine_name...;
                MACHINE -= machine_name...;
        };

MACHINE 属性は 1 つまたは複数のマシンハードウェア名で修飾されます。「+=」形式の代入を使用すると、入力オブジェクトで指定されたマシン機能に追加できます。「=」形式はこれらをオーバーライドします。「-=」形式の代入を使用すると、出力オブジェクトからマシン機能を削除できます。次の SPARC の例では、オブジェクト foo.so.1sun4u マシンハードウェア名に固有であることを指定します。

$ cat mapfile
$mapfile_version 2
CAPABILITY {
        MACHINE = sun4u;
};
$ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc
$ elfdump -H foo.so.1

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_MACH      sun4u

再配置可能オブジェクトはマシン機能を定義できます。これらの機能がまとめられて、作成中のオブジェクトの最終的な機能要件が定義されます。

オブジェクトのマシン機能は、「=」形式の代入を使用すると mapfile から明示的に制御でき、入力の再配置可能オブジェクトから指定される可能性のあるマシン機能をオーバーライドできます。「=」形式の代入を使用して空の MACHINE 属性を指定すると、作成中のオブジェクトからマシンのすべての機能要件が削除されます。

動的オブジェクトで定義されたマシン機能要件は、システムのマシンハードウェア名に照らして、実行時リンカーによって検証されます。オブジェクトが使用されるのは、オブジェクトに記録されたマシン名の 1 つがシステムのマシン名に一致した場合だけです。

コードを特定のマシン用に限定することは、場合によって有効となります。しかし、ハードウェア機能ファミリを作成する方が柔軟性を高めることができるため、この方法を推奨します。ハードウェア機能ファミリは、コードを幅広いシステムで実行できるように最適化できます。

ハードウェア機能の特定

オブジェクトのハードウェア機能は、オブジェクトまたは特定のシンボルを正しく実行するために必要なシステムのハードウェア要件を特定します。この要件の例としては、一部の x86 アーキテクチャーで利用できる MMX または SSE の機能を必要とするコードの特定があります。

ハードウェア機能要件は、次の mapfile 構文を使用して特定できます。

    $mapfile_version 2
        CAPABILITY {
                HW  = hwcap_flag...;
                HW += hwcap_flag...;
                HW -= hwcap_flag...;
        };

CAPABILITY 指令に対する HW 属性は、ハードウェア機能のシンボル表現である 1 つまたは複数のトークンで修飾されます。「+=」形式の代入を使用すると、入力オブジェクトで指定されたハードウェア機能に追加できます。「=」形式はオーバーライドします。「-=」形式の代入を使用すると、出力オブジェクトからハードウェア機能を削除できます。

SPARC システムでは、ハードウェア機能は sys/auxv_SPARC.hAV_ の値として定義されます。x86 システムでは、ハードウェア機能は sys/auxv_386.hAV_ の値として定義されます。

次の x86 の例では、オブジェクト foo.so.1 に必要なハードウェア機能として MMXSSE が宣言されています。

$ egrep "MMX|SSE" /usr/include/sys/auxv_386.h
#define AV_386_MMX    0x0040
#define AV_386_SSE    0x0800
$ cat mapfile
$mapfile_version 2
CAPABILITY {
        HW += SSE MMX;
};
$ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc
$ elfdump -H foo.so.1

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_HW_1      0x840  [ SSE  MMX ]

再配置可能オブジェクトには、ハードウェア機能の値を含めることができます。リンカーは、複数の入力再配置可能オブジェクトからのハードウェア機能値を組み合わせます。この結果生じる CA_SUNW_HW_1 の値は、関連入力値のビット単位の OR となります。デフォルトでは、これらの値は、mapfile で指定されたハードウェア機能と組み合わせられます。

オブジェクトのハードウェア機能要件は、「=」形式の代入を使用すると mapfile から明示的に制御でき、入力の再配置可能オブジェクトから指定される可能性のあるハードウェア機能をオーバーライドできます。「=」形式の代入を使用して空の HW 属性を指定すると、作成中のオブジェクトからハードウェアのすべての機能要件が削除されます。

次の例では、入力の再配置可能オブジェクト foo.o で定義されたハードウェア機能のデータが出力ファイル bar.o に含まれないように隠されています。

$ elfdump -H foo.o

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_HW_1      0x840  [ SSE  MMX ]
$ cat mapfile
$mapfile_version 2
CAPABILITY {
        HW = ;
};
$ ld -o bar.o -r -Mmapfile foo.o
$ elfdump -H bar.o
$ 

動的オブジェクトが定義したハードウェア機能要件は、システムが提供するハードウェア機能に照らして、実行時リンカーによって検証されます。ハードウェア機能要件の一部を満足できない場合、そのオブジェクトは実行時に読み込みされません。たとえば、SSE 機能がプロセスで利用できない場合、ldd(1)は次のエラーを示します。

$ ldd prog
        foo.so.1 =>      ./foo.so.1  - hardware capability unsupported: 0x800 [ SSE ]
        ....

別々のハードウェア機能を利用する動的オブジェクトの複数のバリエーションは、フィルタを使用することによって、柔軟な実行時環境を提供できます。機能固有の共有オブジェクトを参照してください。

ハードウェア機能は、1 つのオブジェクト内の個々の関数の機能を指定するためにも使用できます。この場合、実行時リンカーは現在のシステム機能に基づいて使用する最適な関数を選択できます。シンボル機能関数ファミリの作成を参照してください。

ソフトウェア機能の特定

オブジェクトのソフトウェア機能は、プロセスのデバッグまたはモニタリングにとって重要なことがあるソフトウェアの特徴を特定します。ソフトウェア機能はプロセスの実行にも影響を与えることができます。現在のところ、オブジェクトによるフレームポインタの使用、およびプロセスアドレス空間の制限に関連したソフトウェア機能のみが存在します。

オブジェクトは、フレームポインタ使用を認識することを示せます。この状態は、フレームポインタを使用中または未使用として宣言することで、修飾されます。

64 ビットのオブジェクトは、実行時に 32 ビットのアドレス空間内で実行しなければならないことを指定できます。

ソフトウェア機能フラグは、sys/elf.h で定義されています。

#define  SF1_SUNW_FPKNWN    0x001
#define  SF1_SUNW_FPUSED    0x002
#define  SF1_SUNW_ADDR32    0x004

これらのソフトウェア機能要件は、次の mapfile 構文を使用して特定できます。

        $mapfile_version 2
CAPABILITY {
        SF  = sfcap_flags...;
        SF += sfcap_flags...;
        SF -= sfcap_flags...;
};

CAPABILITY 指令に対する SF 属性は、トークン FPKNWN FPUSED、および ADDR32 のいずれにも割り当てることができます。

再配置可能オブジェクトには、ソフトウェア機能の値を含めることができます。リンカーは、複数の入力再配置可能オブジェクトからのソフトウェア機能値を組み合わせます。ソフトウェア機能は、mapfile も提供されます。デフォルトでは、mapfile のすべての値が、再配置可能オブジェクトで提供される値と組み合わせられます。

オブジェクトのソフトウェア機能要件は、「=」形式の代入を使用すると mapfile から明示的に制御でき、入力の再配置可能オブジェクトから指定される可能性のあるソフトウェア機能をオーバーライドできます。「=」形式の代入を使用して空の HW 属性を指定すると、作成中のオブジェクトからソフトウェアのすべての機能要件が削除されます。

次の例では、入力の再配置可能オブジェクト foo.o で定義されたソフトウェア機能のデータが出力ファイル bar.o に含まれないように隠されています。

$ elfdump -H foo.o

Object Capabilities:
    index  tag               value
      [0]  CA_SUNW_SF_1      0x3  [ SF1_SUNW_FPKNWN  SF1_SUNW_FPUSED ]
$ cat mapfile
$mapfile_version 2
CAPABILITY {
        SF = ;
};
$ ld -o bar.o -r -Mmapfile foo.o
$ elfdump -H bar.o
$ 
ソフトウェア機能フレームポインタの処理

2 つのフレームポインタ入力値からの CA_SUNW_SF_1 値は、次のように計算されます。

表 2-1  CA_SUNW_SF_1 フレームポインタフラグ組み合わせ状態テーブル
入力ファイル 1
入力ファイル 2
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN
<unknown>
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN
SF1_SUNW_FPKNWN
SF1_SUNW_FPKNWN
SF1_SUNW_FPKNWN
<unknown>
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN
<unknown>

この計算は、再配置可能オブジェクト値と mapfile 値にそれぞれ適用されます。.SUNW_cap セクションが存在しない場合や、このセクションに CA_SUNW_SF_1 の値が含まれない場合、SF1_SUNW_FPKNW フラグも SF1_SUNW_FPUSED フラグも設定されていない場合は、オブジェクトのフレームポインタソフトウェア機能は不明になります。

ソフトウェア機能アドレス空間制限処理

SF1_SUNW_ADDR32 ソフトウェア機能フラグによって識別される 64 ビットオブジェクトは、32 ビットアドレス空間を必要とする最適化されたコードを含むことができます。この方法で識別される 64 ビットオブジェクトは、SF1_SUNW_ADDR32 フラグで識別されるかどうかにかかわらず、他の 64 ビットオブジェクトと相互運用できます。64 ビット入力再配置可能オブジェクト内で SF1_SUNW_ADDR32 フラグが現れると、リンカーによって作成される出力ファイル用に作成される CA_SUNW_SF_1 値に反映されます。

64 ビット実行可能ファイル内に SF1_SUNW_ADDR32 フラグが存在すると、関連付けられたプロセスは下位 32 ビットアドレス空間に確実に制限されます。この制限付きアドレス空間には、プロセススタックと、すべてのプロセス依存関係が含まれます。そのようなプロセスの内部では、すべてのオブジェクトは SF1_SUNW_ADDR32 フラグで識別されるかどうかにかかわらず、制限付き 32 ビットアドレス空間内に読み込まれます。

64 ビット共有オブジェクトは SF1_SUNW_ADDR32 フラグを含むことができます。ただし、制限付きアドレス空間要件は、SF1_SUNW_ADDR32 フラグを含む 64 ビット実行可能ファイルによってのみ設定できます。したがって、64 ビットの SF1_SUNW_ADDR32 共有オブジェクトは64 ビットの SF1_SUNW_ADDR32 実行可能ファイルに依存する必要があります。

制限付きでない 64 ビット実行可能ファイルを構築するとき、64 ビットの SF1_SUNW_ADDR32 共有オブジェクトがリンカーによって検出されると、警告が発生します。

$ cc -m64 -o main main.c -lfoo
ld: warning: file libfoo.so: section .SUNW_cap: software capability ADDR32: \
    requires executable be built with ADDR32 capability

制限付きでない 64 ビットの実行可能ファイルから作成されたプロセスによって 64 ビットの SF1_SUNW_ADDR32 共有オブジェクトが実行時に検出されると、致命的エラーが発生します。

$ ldd main
    libfoo.so =>     ./libfoo.so  - software capability unsupported: 0x4  [ ADDR32 ]
    ....
$ main
ld.so.1: main: fatal: ./libfoo.so: software capability unsupported: 0x4  [ ADDR32 ]

実行可能ファイルには、mapfile を使用して SF1_SUNW_ADDR32 をシードできます。

$ cat mapfile
$mapfile_version 2
CAPABILITY {
        SF += ADDR32;
};
$ cc -m64 -o main main.c -Mmapfile -lfoo
$ elfdump -H main

Object Capabilities:
    index  tag               value
      [0]  CA_SUNW_SF_1      0x4  [ SF1_SUNW_ADDR32 ]

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

開発者は、関数の複数のインスタンス (それぞれは特定の機能セット向けに最適化) を 1 つのオブジェクト内に用意したいと考えることがよくあります。インスタンスの選択と使用が消費者に対して透過的であることが望まれます。外部インタフェースとして、汎用的なフロントエンドの関数を作成できます。この汎用インスタンスと最適化されたインスタンスを 1 つのオブジェクトに結合できます。タスクを処理するために汎用インスタンスはgetisax(2)を使用してシステムの機能を判断し、最適化された適切な関数インスタンスを呼び出すことができます。このモデルは適切に機能しますが、汎用性に欠け、実行時のオーバーヘッドが発生します。

シンボル機能はこのようなオブジェクトを作成するために別の方法を提供します。この方法はより簡単で効率が良く、さらにフロントエンドのコードを書く必要がありません。1 つの関数の複数のインスタンスを作成できます。また、さまざまな機能に関連付けることができます。これらのインスタンスと、いずれのシステムにも適した関数のデフォルトのインスタンスは、1 つの動的オブジェクトに結合できます。このファミリのシンボルの中でもっとも適したメンバーが、シンボルの機能情報を使用して、実行時リンカーによって選択されます。

次の例では、x86 オブジェクト foobar.mmx.o および foobar.sse.o が同じ関数 foo()bar() を保持しており、それぞれ MMXSSE の命令を使用するようにコンパイルされています。

$ elfdump -H foobar.mmx.o

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
    index  tag               value
      [1]  CA_SUNW_ID        mmx
      [2]  CA_SUNW_HW_1      0x40  [ MMX ]

  Symbols:
     index    value      size  type bind oth ver shndx    name
      [10]        0      0x21  FUNC LOCL  D    0 .text    foo%mmx
      [16]     0x24      0x1e  FUNC LOCL  D    0 .text    bar%mmx

$ elfdump -H foobar.sse.o

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
    index  tag               value
      [1]  CA_SUNW_ID        sse
      [2]  CA_SUNW_HW_1      0x800  [ SSE ]

  Capabilities symbols:
     index    value      size  type bind oth ver shndx    name
      [16]        0      0x2f  FUNC LOCL  D    0 .text    foo%sse
      [18]     0x48      0x30  FUNC LOCL  D    0 .text    bar%sse

これらの各オブジェクトは機能関数 foo%*() および bar%*() を指定する局所シンボルを含んでいます。また、各オブジェクトは関数 foo() および bar() への大域参照も定義します。foo() または bar() への内部参照はすべて、これらの大域参照を介して、外部参照のように再配置されます。

これら 2 つのオブジェクトは、foo() および bar() のデフォルトのインスタンスと結合できるようになりました。これらのデフォルトインスタンスは大域参照を満たし、どのオブジェクト機能とも互換性を持つ実装を提供します。これらのデフォルトインスタンスは各機能ファミリの先頭になります。オブジェクトの機能が存在しない場合、このデフォルトインスタンスも機能を要求してはいけません。実質的に、foo()bar() の 3 つのインスタンスが存在し、大域インスタンスはデフォルトを提供し、局所インスタンスは関連機能が使用できる場合に、実行時に使用される実装を提供します。

$ cc -o libfoobar.so.1 -G foobar.o foobar.sse.o foobar.mmx.o
$ elfdump -sN.dynsym libfoobar.so.1 | egrep "foo|bar"
     [2]     0x700    0x21  FUNC LOCL  D    0 .text    foo%mmx
     [4]     0x750    0x2f  FUNC LOCL  D    0 .text    foo%sse
     [8]     0x784    0x1e  FUNC LOCL  D    0 .text    bar%mmx
     [9]     0x7b0    0x30  FUNC LOCL  D    0 .text    bar%sse
    [15]     0x7a0    0x14  FUNC GLOB  D    1 .text    foo
    [17]     0x7c0    0x14  FUNC GLOB  D    1 .text    bar

動的オブジェクトの機能情報には機能シンボルが表示され、利用できる機能ファミリがわかります。

$ elfdump -H libfoobar.so.1

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
   index  tag               value
     [1]  CA_SUNW_ID        mmx
     [2]  CA_SUNW_HW_1      0x40  [ MMX ]

  Symbols:
   index    value     size  type bind oth ver shndx    name
     [2]    0x700     0x21  FUNC LOCL  D    0 .text    foo%mmx
     [8]    0x784     0x1e  FUNC LOCL  D    0 .text    bar%mmx

 Symbol Capabilities:
   index  tag               value
     [4]  CA_SUNW_ID        sse
     [5]  CA_SUNW_HW_1      0x800  [ SSE ]

  Symbols:
   index    value     size  type bind oth ver shndx    name
     [4]    0x750     0x2f  FUNC LOCL  D    0 .text    foo%sse
     [9]    0x7b0     0x30  FUNC LOCL  D    0 .text    bar%sse

Capabilities Chain Section:  .SUNW_capchain

 Capabilities family: foo
  chainndx  symndx      name
         1  [15]        foo
         2  [2]         foo%mmx
         3  [4]         foo%sse

 Capabilities family: bar
  chainndx  symndx      name
         5  [17]        bar
         6  [8]         bar%mmx
         7  [9]         bar%sse

実行時、foo()bar() へのすべての参照は、まず大域シンボルに結合されます。しかし、実行時リンカーはこれらの関数が機能ファミリの先頭のインスタンスであることを認識しています。実行時リンカーは各ファミリメンバーを検査して、より適した機能関数が使用できるかどうかを判断します。この処理には 1 回限りのコストがかかり、関数の最初の呼び出し時に発生します。foo() および bar() への後続の呼び出しは、最初の呼び出しで選択された関数のインスタンスに直接結合されます。この関数の選択は実行時リンカーのデバッグ機能を使用すると確認できます。

次の例では、ベースとなるシステムが MMX または SSE をサポートしていません。foo() の先頭のインスタンスには特別な機能のサポートは必要ないため、どの再配置参照も満たします。

$ LD_DEBUG=symbols  main
....
debug: symbol=foo;  lookup in file=./libfoo.so.1  [ ELF ]
debug:   symbol=foo[15]:  capability family default
debug:   symbol=foo%mmx[2]:  capability specific (CA_SUNW_HW_1):  [ 0x40  [ MMX ] ]
debug:   symbol=foo%mmx[2]:  capability rejected
debug:   symbol=foo%sse[4]:  capability specific (CA_SUNW_HW_1):  [ 0x800  [ SSE ] ]
debug:   symbol=foo%sse[4]:  capability rejected
debug:   symbol=foo[15]:  used

次の例では、MMX は使用できますが、SSE は使用できません。MMX が使用できる foo() のインスタンスは、どの再配置参照も解決します。

$ LD_DEBUG=symbols  main
....
debug: symbol=foo;  lookup in file=./libfoo.so.1  [ ELF ]
debug:   symbol=foo[15]:  capability family default
debug:   symbol=foo%mmx[2]:  capability specific (CA_SUNW_HW_1):  [ 0x40  [ MMX ] ]
debug:   symbol=foo%mmx[2]:  capability candidate
debug:   symbol=foo%sse[4]:  capability specific (CA_SUNW_HW_1):  [ 0x800  [ SSE ] ]
debug:   symbol=foo%sse[4]:  capability rejected
debug:   symbol=foo[2]:  used

複数の機能インスタンスが同じシステムで実行できる場合、一連の優先規則を利用して 1 つのインスタンスを選択します。

  • プラットフォーム名を定義している機能グループは、プラットフォーム名を定義していないグループに優先します。

  • マシンのハードウェア名を定義している機能グループは、マシンのハードウェア名を定義していないグループに優先します。

  • ハードウェア機能の値は、大きい値が小さい値より優先します。

機能関数インスタンスのファミリはプロシージャーのリンクテーブルエントリからアクセスできる必要があります。プロシージャーのリンクテーブル (プロセッサ固有)を参照してください。このプロシージャーリンクの参照には、実行時リンカーが関数を解決する必要があります。このプロセス中、実行時リンカーは関連のシンボル機能情報を処理して、使用できる関数インスタンスファミリから最適な関数を選択できます。

シンボル機能が使用されないときに、リンカーがプロシージャーのリンクテーブルエントリを必要としないでコードへの参照を解決できる場合があります。たとえば、動的実行可能ファイル内では、実行可能ファイル内に存在する関数への参照が、リンク編集時に内部的に結合できます。共有オブジェクト内に隠されて保護されている関数も、リンク編集時に内部的に結合できます。この場合、一般的に、実行時リンカーはこれらの関数への参照の解決に関与する必要はありません。

しかし、シンボル機能が使用された場合、関数はプロシージャーのリンクテーブルエントリから解決される必要があります。このエントリが必要なのは、実行時リンカーが読み取り専用のテキストセグメントを維持しながら、適切な関数を選択することに関与するためです。このメカニズムでは、機能関数へのすべての呼び出しが、プロシージャーのリンクテーブルエントリを介した間接参照になります。この間接参照は、シンボル機能が使用されない場合は必要ない可能性があります。このため、機能関数を呼び出すコストと、機能関数を使用して得られるデフォルトに対する性能の改善との間に小さいトレードオフがあります。


注 -  機能関数はプロシージャーのリンクテーブルエントリを介してアクセスする必要がありますが、この関数は隠された、または保護されたものとして定義できます。実行時リンカーはこれらの可視性に従って、関数への結合を制限します。この動作により、シンボル機能が関数と関連付けられていないときに作成される結合と同じ結合になります。隠された関数は外部オブジェクトから結合できません。保護された関数へのオブジェクト内からの参照は、同じオブジェクト内でのみ結合されます。

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

初期化されたデータの複数のインスタンス (各インスタンスはシステムに固有) を同じオブジェクト内で提供できます。しかし、このようなデータは、関数インタフェースを介して提供する方法が簡単で、お勧めです。シンボル機能関数ファミリの作成を参照してください。実行可能ファイル内に初期化データの複数のインスタンスを提供するには、特別な配慮が必要です。

次の例では、foo.c 内のデータ項目 foo を初期化して、マシン名の文字列を指しています。このファイルはさまざまなマシン用にコンパイルでき、各インスタンスはマシン機能で特定されます。このデータ項目への参照は、ファイル bar.cbar() から行われます。次に共有オブジェクト foobar.so.1 が、foo の 2 つの機能インスタンスと bar() を結合することで作成されます。

$ cat foo.c
char *foo = MACHINE;
$ cat bar.c
#include	<stdio.h>

extern char *foo = MACHINE;

void bar()
{
        (void) printf("machine: %s\n", foo);
}

$ elfdump -H foobar.so.1

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
   index  tag               value
     [1]  CA_SUNW_ID        sun4u
     [2]  CA_SUNW_MACH      sun4u

  Symbols:
   index    value     size  type bind oth ver shndx          name
     [1]  0x108d4      0x4  OBJT LOCL  D    0 .data          foo%sun4u

 Symbol Capabilities:
   index  tag               value
     [4]  CA_SUNW_ID        sun4v
     [5]  CA_SUNW_MACH      sun4v

  Symbols:
   index    value     size  type bind oth ver shndx          name
     [2]  0x108d8      0x4  OBJT LOCL  D    0 .data          foo%sun4v

アプリケーションは bar() を参照できます。実行時リンカーはベースとなるシステムに関連する foo のインスタンスに結合します。

$ uname -m
sun4u
$ main
machine: sun4u

このコードが適切に動作するには、コードが位置独立になるようにコンパイルされる必要があります。共有可能なオブジェクト内のコードではこれは一般的です。位置独立のコードを参照してください。位置独立のデータ参照は間接参照であるため、実行時リンカーは必要な参照を検索して、データセグメントの要素を更新できます。データセグメントのこの再配置更新では、テキストセグメントが読み取り専用として保持されます。

しかし、実行可能ファイル内のコードは位置依存であることが一般的です。また、実行可能ファイル内のデータ参照はリンク編集時に結合されます。実行可能ファイル内では、実行時リンカーがシンボル機能ファミリから選択できるように、シンボル機能のデータ参照は、大域データ項目を介して解決されない状態のままである必要があります。前の例の bar.c にある bar() からの参照が位置依存コードとしてコンパイルされた場合、実行可能ファイルのテキストセグメントは実行時に再配置される必要があります。デフォルトでは、この状態は重大なリンク時エラーとなります。

$ cc -o main main.c bar.c foo.o foo.1.o foo.2.o ...
warning: Text relocation remains                referenced
    against symbol                  offset      in file
foo                                 0x0         bar.o
foo                                 0x8         bar.o

このエラー状態を解決する 1 つの方法は、bar.c を位置独立としてコンパイルすることです。ただし、この方法を正常に動作させるには、シンボル機能データ項目への実行可能ファイル内からの参照をすべて位置独立でコンパイルする必要がある点に注意してください。

データはシンボル機能メカニズムを使用してアクセスできますが、データ項目をオブジェクトへの公開インタフェースの一部にすることは問題となる可能性があります。より柔軟な別のモデルは、シンボル機能関数に各データ項目をカプセル化することです。この関数が、データをアクセスするための唯一の手段を提供します。シンボル機能関数にデータを隠すことによって、データを静的に定義し、非公開のままに維持できるという重要なメリットが得られます。前の例は、シンボル機能関数を使用するようにコーディングできます。

$ cat foobar.c
cat bar.c
#include	<stdio.h>

static char *foo = MACHINE;

void bar()
{
        (void) printf("machine: %s\n", foo);
}
$ elfdump -H main

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
   index  tag               value
     [1]  CA_SUNW_ID        sun4u
     [2]  CA_SUNW_MACH      sun4u

  Symbols:
   index    value     size  type bind oth ver shndx          name
     [1]  0x1111c     0x1c  FUNC LOCL  D    0 .text          bar%sun4u

 Symbol Capabilities:
   index  tag               value
     [4]  CA_SUNW_ID        sun4v
     [5]  CA_SUNW_MACH      sun4v

  Symbols:
   index    value     size  type bind oth ver shndx          name
     [2]  0x11138     0x1c  FUNC LOCL  D    0 .text          bar%sun4v

$ uname -m
sun4u
$ main
machine: sun4u

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

理想的には、コンパイラはシンボル機能で特定されたオブジェクトを生成できます。コンパイラがシンボル機能を解決できない場合は、リンカーが解決します。

オブジェクト機能を定義している再配置可能オブジェクトは、リンカーを使用してシンボル機能を定義する再配置可能オブジェクトに変換できます。リンカーの –z symbolcap オプションを使用すると、機能データセクションがすべて変換されて、シンボル機能が定義されます。オブジェクト内のすべての大域関数は局所関数に変換され、シンボル機能に関連付けられます。初期化された大域データ項目は局所データ項目に変換され、シンボル機能に関連付けられます。変換されたこれらのシンボルには、オブジェクト機能グループの一部として指定された機能識別子が追加されます。機能識別子が定義されていない場合は、デフォルトのグループ名が追加されます。

オリジナルの大域関数または初期化されたデータ項目のそれぞれに対して、大域参照が作成されます。この参照は再配置要件に関連付けられており、動的オブジェクトを作成するためにこのオブジェクトを最終的に結合するときに、デフォルトの大域シンボルに結合できます。


注 -  –z symbolcap オプションは、オブジェクト機能セクションを含んでいるオブジェクトに適用されます。このオプションは、すでにシンボル機能を含んでいる再配置可能オブジェクト、またはオブジェクトおよびシンボルの機能を含む再配置可能オブジェクトに影響しません。この設計により、複数のオブジェクトがリンカーによって結合でき、オブジェクト機能を含むオブジェクトだけがこのオプションの影響を受けます。

次の例では、x86 再配置可能オブジェクトは 2 つの大域関数 foo() および bar() を含んでいます。このオブジェクトは MMXSSE のハードウェア機能を要求するようにコンパイルされました。これらの例では、機能グループは機能識別子エントリを使用して名前が付けられました。この識別子名は変換されたシンボル名に追加されます。この明示的な識別子がないと、リンカーはデフォルトの機能グループ名を追加します。

$ elfdump -H foo.o

Capabilities Section:  .SUNW_cap

 Object Capabilities:
   index  tag               value
     [0]  CA_SUNW_ID        sse,mmx
     [1]  CA_SUNW_HW_1      0x840  [ SSE MMX ]

$ elfdump -s foo.o | egrep "foo|bar"
    [25]         0   0x21  FUNC GLOB  D    0 .text    foo
    [26]      0x24   0x1e  FUNC GLOB  D    0 .text    bar

$ elfdump -r foo.o | fgrep foo
  R_386_PLT32                0x38       .rel.text             foo

これで、この再配置可能オブジェクトはシンボル機能の再配置可能オブジェクトに変換できます。

$ ld -r -o foo.1.o -z symbolcap foo.o
$ elfdump -H foo.1.o

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
   index  tag               value
     [1]  CA_SUNW_ID        sse,mmx
     [2]  CA_SUNW_HW_1      0x840  [ SSE MMX ]

  Symbols:
   index    value     size  type bind oth ver shndx    name
    [25]        0     0x21  FUNC LOCL  D    0 .text    foo%sse,mmx
    [26]     0x24     0x1e  FUNC LOCL  D    0 .text    bar%sse,mmx

$ elfdump -s foo.1.o | egrep "foo|bar"
    [25]        0     0x21  FUNC LOCL  D    0 .text    foo%sse,mmx
    [26]     0x24     0x1e  FUNC LOCL  D    0 .text    bar%sse,mmx
    [37]        0        0  FUNC GLOB  D    0 UNDEF    foo
    [38]        0        0  FUNC GLOB  D    0 UNDEF    bar

$ elfdump -r foo.1.o | fgrep foo
  R_386_PLT32                0x38       .rel.text             foo

このオブジェクトは、同じ関数 (別のシンボル機能に関連) のインスタンスを含む別のオブジェクトと結合でき、実行可能ファイルまたは共有オブジェクトを作成できるようになりました。また、シンボル機能と関連付けられていない、各機能ファミリの先頭となる各関数のデフォルトインスタンスが提供される必要があります。このデフォルトのインスタンスはすべての外部参照に対応しており、どのシステムでも関数のインスタンスが確実に使用できるようになります。

実行時、foo()bar() へのすべての参照は、先頭のインスタンスに向けられます。ただし、システムが適切な機能に対応している場合、実行時リンカーは最適なシンボル機能インスタンスを選択します。

アーカイブに関する考慮事項

アーカイブライブラリには通常、再配置可能オブジェクトの集合が含まれます。リンカーは、個々の再配置可能なオブジェクトを抽出して、未解決のシンボル参照を解決できます。アーカイブ処理を参照してください。

機能再配置可能オブジェクトのファミリがアーカイブに追加された場合、先頭機能シンボルのすべての参照は、そのシンボルを定義する汎用再配置可能オブジェクトだけを抽出します。ほかの機能オブジェクトは抽出されません。

アーカイブライブラリを使用して機能オブジェクトを配備する必要がある場合、単一の機能ファミリ再配置オブジェクトを作成する必要があります。すべての機能オブジェクト、および機能の先頭のシンボルを含んだすべての汎用オブジェクトを 1 つの再配置可能オブジェクトに組み合わせます。機能ファミリコレクション全体を含むこの単一オブジェクトを、アーカイブに追加します。

$ ld -r -o all.foo.o foo.o foo.1.o foo.2.o ....
$ ar -cr libfoo.o all.foo.o