ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
Oracle Solaris 11.1 リンカーとライブラリガイド Oracle Solaris 11.1 Information Library (日本語) |
7. システムのパフォーマンスを最適化するオブジェクトの構築
パート IV ELF アプリケーションバイナリインタフェース
動的オブジェクトには、1 つまたは複数の内部バージョン定義を関連付けることができます。各バージョン定義は通常、1 つまたは複数の名前に関連付けられます。シンボル名は、「1 つ」のバージョン定義にしか関連付けられません。ただし、バージョン定義はほかのバージョン定義からシンボルを継承できます。したがって、1 つまたは複数の独立した、または関連するバージョン定義を作成中のオブジェクト内に定義するための構造が存在します。オブジェクトに新しい変更が加えられたら、新しいバージョン定義を追加してこれらの変更を表現することができます。
共有オブジェクト内でバージョン定義を行うと、次の 2 つの機能が利用できます。
バージョン定義を与えられた共有オブジェクトに対して構築された動的オブジェクトは、それらが結合されているバージョン定義への依存関係を記録できます。これらのバージョンの依存関係は、アプリケーションの正しい実行に適切なインタフェースまたは機能を使用できるかどうかを確認するため、実行時に検査されます。
動的オブジェクトは、結合する共有オブジェクトのバージョン定義をリンク編集中に選択できます。このメカニズムを使用すると、開発者は、共有オブジェクト内のもっとも適したインタフェースまたは機能への、依存関係を制御することができます。
バージョン定義は、一般にシンボル名と一意のバージョン名との関連付けからなります。これらの関連付けは、mapfile 内に確立され、リンカーの -M オプションを使用して、オブジェクトの最終リンク編集に与えられます。この手法については、「シンボル範囲の縮小」セクションを参照してください。
バージョン定義は、バージョン名が mapfile 指令の一部として指定されている場合は必ず確立されます。次の例では、2 つのソースファイルが mapfile 指令とともに結合されて、定義済み公開インタフェースを持つオブジェクトを作成しています。
$ cat foo.c #include <stdio.h> extern const char *_foo1; void foo1() { (void) printf(_foo1); } $ cat data.c const char *_foo1 = "string used by foo1()\n"; $ cat mapfile $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { # Release X global: foo1; local: *; }; $ cc -c -Kpic foo.c data.c $ cc -o libfoo.so.1 -M mapfile -G foo.o data.o $ elfdump -sN.symtab libfoo.so.1 | grep 'foo.$' [32] 0x0001074c 0x00000004 OBJT LOCL H 0 .data _foo1 [53] 0x00000560 0x00000038 FUNC GLOB D 0 .text foo1
シンボル foo1 は、共有オブジェクトの公開インタフェースを提供するために定義された唯一の大域シンボルです。特殊な自動縮小指令「*」は、ほかの大域シンボルすべてを縮小することによって、生成中のオブジェクト内にローカル結合が生じるようにします。自動縮小指令については、「SYMBOL_SCOPE/SYMBOL_VERSION 指令」で説明されます。関連バージョン名 SUNW_1.1 は、バージョン定義を生成させます。したがって、共有オブジェクトの公開インタフェースは、内部バージョン定義 SUNW_1.1 に関連付けられた大域シンボル foo1 で構成されます。
バージョン定義または自動縮小指令によってオブジェクトが生成されると、基本バージョン定義も必ず作成されます。この基本バージョンは、作成されるオブジェクトの名前を使用して定義されます。この基本バージョンは、リンカーによって生成された予約シンボルすべてを関連付けるために使用されます。予約シンボルのリストについては、「出力ファイルの生成」を参照してください。
オブジェクト内に含まれるバージョン定義は、-d オプションを付けた pvs(1) を使用して表示できます。
$ pvs -d libfoo.so.1 libfoo.so.1; SUNW_1.1;
オブジェクト libfoo.so.1 には、基本バージョン定義 libfoo.so.1 とともに、SUNW_1.1 という名前の内部バージョン定義があります。
この初期バージョン定義から、新しいインタフェースと更新された機能を追加することによって、オブジェクトを展開させることができます。たとえば、新機能 foo2 は、それがサポートするデータ構造とともに、ソースファイル foo.c および data.c を更新することによってオブジェクトに追加することができます。
$ cat foo.c #include <stdio.h> extern const char *_foo1; extern const char *_foo2; void foo1() { (void) printf(_foo1); } void foo2() { (void) printf(_foo2); } $ cat data.c const char *_foo1 = "string used by foo1()\n"; const char *_foo2 = "string used by foo2()\n";
新しいバージョン定義 SUNW_1.2 を作成すると、シンボル foo2 を表す新しいインタフェースを定義できます。また、この新しいインタフェースは、元のバージョン定義 SUNW_1.1 を継承するように定義できます。
この新しいインタフェースにはオブジェクトの展開を記述できるため、このインタフェースを作成することは重要です。ユーザーはこれらのインタフェースを使って、結合先のインタフェースを検査して選択できます。これらの概念については、「バージョン定義への結合」と「バージョン結合の指定」で詳しく説明します。
次の例は、これらの 1 つのインタフェースを作成する mapfile 指令を示しています。
$ cat mapfile $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { # Release X global: foo1; local: *; }; SYMBOL_VERSION SUNW_1.2 { # Release X+1 global: foo2; } SUNW_1.1; $ cc -o libfoo.so.1 -M mapfile -G foo.o data.o $ elfdump -sN.symtab libfoo.so.1 | grep 'foo.$' [28] 0x000107a4 0x00000004 OBJT LOCL H 0 .data _foo1 [29] 0x000107a8 0x00000004 OBJT LOCL H 0 .data _foo2 [48] 0x000005e8 0x00000020 FUNC GLOB D 0 .text foo1 [51] 0x00000618 0x00000020 FUNC GLOB D 0 .text foo2
foo1 と foo2 は、いずれも共有オブジェクトの公開インタフェースの一部として定義されています。ただし、これらのシンボルはそれぞれ別のバージョン定義に割り当てられます。foo1 は、バージョン SUNW_1.1 に割り当てられます。foo2 は、バージョン SUNW_1.2 に割り当てられます。
これらのバージョン定義、その継承、およびそのシンボル関連付けは、pvs(1) に -d、-v、および -s オプションをつけて表示できます。
$ pvs -dsv libfoo.so.1 libfoo.so.1: _end; _GLOBAL_OFFSET_TABLE_; _DYNAMIC; _edata; _PROCEDURE_LINKAGE_TABLE_; _etext; SUNW_1.1: foo1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}: foo2; SUNW_1.2
バージョン定義 SUNW_1.2 は、バージョン定義 SUNW_1.1 に対する依存関係を持っています。
あるバージョン定義から別のバージョン定義への継承は、便利な手法です。この継承によって、バージョン依存関係に結合するオブジェクトによって最終的に記録されるバージョン情報が削減されます。バージョン継承については、「バージョン定義への結合」セクションで詳しく説明します。
バージョン定義シンボルが作成され、バージョン定義に関連付けられます。pvs(1) の例で示したように、これらのシンボルは -v オプションを使用して表示されます。
オブジェクトに対する新しいインタフェース定義の照会を必要としない内部変更は、ウィークバージョン定義を作成することによって定義できます。このような変更の例としては、バグ修正や性能の改善があります。このようなバージョン定義は空です。このバージョン定義には、大域インタフェースシンボルが関連付けられません。
たとえば、以前の例で使用したデータファイル data.c が、次のようにより詳しい文字列定義を提供するように更新されたとします。
$ cat data.c const char *_foo1 = "string used by function foo1()\n"; const char *_foo2 = "string used by function foo2()\n";
ウィークバージョン定義を照会すると、この変更を次のように識別できます。
$ cat mapfile $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { # Release X global: foo1; local: *; }; SYMBOL_VERSION SUNW_1.2 { # Release X+1 global: foo2; } SUNW_1.1; SYMBOL_VERSION SUNW_1.2.1 { } SUNW_1.2; # Release X+2 $ cc -o libfoo.so.1 -M mapfile -G foo.o data.o $ pvs -dv libfoo.so.1 libfoo.so.1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}; SUNW_1.2.1 [WEAK]: {SUNW_1.2};
空のバージョン定義は、ウィークラベルによって示されます。これらのウィークバージョン定義を使用すると、アプリケーションは特定の実装詳細の存在を検査できます。アプリケーションは、必要とする実装詳細に関連付けられたバージョン定義に結合できます。「バージョン定義への結合」セクションでは、これらの定義を使用する方法について詳しく説明します。
以前の例は、オブジェクトに追加された新しいバージョン定義は、既存のバージョン定義をどのように継承するかを示しています。一意の依存しないバージョン定義を作成することもできます。次の例では、2 つの新しいファイル bar1.c と bar2.c がオブジェクト libfoo.so.1 に追加されています。これらのファイルは、2 つの新しいシンボル bar1 と bar2 をそれぞれ提供します。
$ cat bar1.c extern void foo1(); void bar1() { foo1(); } $ cat bar2.c extern void foo2(); void bar2() { foo2(); }
これらの 2 つのシンボルは、2 つの新しい公開インタフェースの定義を目的としています。新しいインタフェースはどちらも相互に関連がありません。ただし、それぞれのインタフェースは、元の SUNW_1.2 インタフェースへの依存関係を表します。
次の mapfile 定義は、必要な関連付けを作成します。
$ cat mapfile $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { # Release X global: foo1; local: *; }; SYMBOL_VERSION SUNW_1.2 { # Release X+1 global: foo2; } SUNW_1.1; SYMBOL_VERSION SUNW_1.2.1 { } SUNW_1.2; # Release X+2 SYMBOL_VERSION SUNW_1.3a { # Release X+3 global: bar1; } SUNW_1.2; SYMBOL_VERSION SUNW_1.3b { # Release X+3 global: bar2; } SUNW_1.2;
この mapfile を使用して libfoo.so.1 に作成されたバージョン定義とそれらに関連する依存関係は、pvs(1) を使用して検査できます。
$ cc -o libfoo.so.1 -M mapfile -G foo.o bar1.o bar2.o data.o $ pvs -dv libfoo.so.1 libfoo.so.1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}; SUNW_1.2.1 [WEAK]: {SUNW_1.2}; SUNW_1.3a: {SUNW_1.2}; SUNW_1.3b: {SUNW_1.2};
バージョン定義を使用して、実行時結合の要件を検査できます。また、バージョン定義を使用して、オブジェクトの作成中にオブジェクトの結合を制御することもできます。次のセクションでは、これらのバージョン定義の使用方法について詳細に説明します。
動的実行可能ファイルまたは共有オブジェクトが、ほかの共有オブジェクトに対して構築される場合、これらの依存関係は結果オブジェクトに記録されます。詳細は、「共有オブジェクトの処理」と 「共有オブジェクト名の記録」を参照してください。依存関係にバージョン定義も含まれる場合、関連のバージョン依存関係は構築されたオブジェクトに記録されます。
次の例は、前のセクションのデータファイルを使用して、コンパイル時環境に適した共有オブジェクト libfoo.so.1 を生成しています。
$ cc -o libfoo.so.1 -h libfoo.so.1 -M mapfile -G foo.o bar.o \ data.o $ ln -s libfoo.so.1 libfoo.so $ pvs -dsv libfoo.so.1 libfoo.so.1: _end; _GLOBAL_OFFSET_TABLE_; _DYNAMIC; _edata; _PROCEDURE_LINKAGE_TABLE_; _etext; SUNW_1.1: foo1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}: foo2; SUNW_1.2; SUNW_1.2.1 [WEAK]: {SUNW_1.2}: SUNW_1.2.1; SUNW_1.3a: {SUNW_1.2}: bar1; SUNW_1.3a; SUNW_1.3b: {SUNW_1.2}: bar2; SUNW_1.3b
6 つの公開インタフェースが、共有オブジェクト libfoo.so.1 によって提供されています。これらのインタフェースのうち 4 つ (SUNW_1.1、SUNW_1.2、SUNW_1.3a、SUNW_1.3b) はエクスポートされたシンボル名を定義します。1 つのインタフェース SUNW_1.2.1 は、オブジェクトに対する内部実装の変更を記述します。もう1 つのインタフェース libfoo.so.1 は、いくつかの予約ラベルを定義します。libfoo.so.1 によって依存関係として作成される動的オブジェクトは、その動的オブジェクトが結合するインタフェースのバージョン名を記録します。
次の例では、シンボル foo1 と foo2 を参照するアプリケーションを作成しています。アプリケーションに記録されるバージョン管理依存関係に関する情報は、-r オプションを付けた pvs(1) を使用して調べることができます。
$ cat prog.c extern void foo1(); extern void foo2(); main() { foo1(); foo2(); } $ cc -o prog prog.c -L. -R. -lfoo $ pvs -r prog libfoo.so.1 (SUNW_1.2, SUNW_1.2.1);
この例では、アプリケーション prog は、実際に 2 つのインタフェース SUNW_1.1 と SUNW_1.2 に結合されています。これらのインタフェースは、それぞれ大域シンボル foo1 と foo2 を提供しました。
バージョン定義 SUNW_1.1 はバージョン定義 SUNW_1.2 から継承されたものとして libfoo.so.1 内に定義されているため、記録が必要なのは 1 つの依存関係だけです。この継承によって、バージョン定義の依存関係が正規化されます。この正規化によって、オブジェクト内に保持されているバージョン情報の量は削減されます。また、この正規化によって実行時に必要なバージョン検査の処理も縮小されます。
アプリケーション prog は、ウィークバージョン定義 SUNW_1.2.1 を含む共有オブジェクトの実装状態に対して構築されるため、この依存関係も記録されます。このバージョン定義は、バージョン定義 SUNW_1.2 を継承するように定義されていますが、バージョンのウィーク性は SUNW_1.1 によるその正規化を阻害します。ウィークバージョン定義の依存関係は、別々に記録されることになります。
相互に継承される複数のウィークバージョン定義がある場合、これらの定義は、ウィークでないバージョン定義と同じ方法で正規化されます。
実行時リンカーは、アプリケーションの実行時に結合されたオブジェクトから、記録されたバージョン定義があるかどうかを検証します。この検証は、-v オプションを付けた ldd(1) を使用して表示できます。たとえば、アプリケーション prog に対して、ldd(1) を実行すると、バージョン定義依存関係は、依存関係 libfoo.so.1 で正しく検出されることがわかります。
$ ldd -v prog find object=libfoo.so.1; required by prog libfoo.so.1 => ./libfoo.so.1 find version=libfoo.so.1; libfoo.so.1 (SUNW_1.2) => ./libfoo.so.1 libfoo.so.1 (SUNW_1.2.1) => ./libfoo.so.1 ....
ウィークでないバージョン定義依存関係を検出できないと、アプリケーションの初期設定中に重大なエラーが起こります。検出できないウィークバージョン定義依存関係は、暗黙の内に無視されます。たとえば、libfoo.so.1 がバージョン定義 SUNW_1.1 だけを含む環境で、アプリケーション prog が実行された場合は、次の重大なエラーが生じます。
$ pvs -dv libfoo.so.1 libfoo.so.1; SUNW_1.1; $ prog ld.so.1: prog: fatal: libfoo.so.1: version `SUNW_1.2' not \ found (required by file prog)
アプリケーション prog がバージョン定義依存関係を記録しなかった場合、シンボル foo2 が存在しないときには、実行時に重大な再配置エラーが発生することになります。この再配置エラーは、プロセス初期設定中またはプロセス実行中に生じる可能性があります。また、アプリケーションの実行パスが関数 foo2 を呼び出さなかった場合には、エラー状態がまったく生じないこともあります。「再配置エラー」を参照してください。
バージョン定義依存関係によって、アプリケーションによって必要なインタフェースが使用可能かどうかがすぐに示されます。
たとえば、libfoo.so.1 がバージョン定義 SUNW_1.1 と SUNW_1.2 だけを含む環境内で、prog を実行するとします。この場合、ウィークでないバージョン定義要件はすべて満たされます。ウィークバージョン定義 SUNW_1.2.1 の不在は、重大ではないエラーと見なされます。この場合、実行時エラー条件は生成されません。
$ pvs -dv libfoo.so.1 libfoo.so.1; SUNW_1.1; SUNW_1.2: {SUNW_1.1}; $ prog string used by foo1() string used by foo2()
ldd(1) を使用すると、検出できないすべてのバージョン定義が表示されます。
$ ldd prog libfoo.so.1 => ./libfoo.so.1 libfoo.so.1 (SUNW_1.2.1) => (version not found) ...........
実行時に依存関係の実装状態にバージョン定義情報が含まれていない場合、依存関係のバージョン検査は暗黙のうちに無視されます。この方針は、非バージョン管理共有オブジェクトからバージョン管理共有オブジェクトへの移行が行われるときに、下位互換性レベルを提供するものです。ldd(1) は、バージョン要件の違いを表示するためにいつでも使用できます。
注 - 環境変数 LD_NOVERSION を使用すると、すべての実行時バージョン検査を抑制できます。
バージョン定義シンボルも、dlopen(3C) によって取得されたオブジェクトのバージョン要件を検査するメカニズムとなるものです。dlopen(3C) を使用してプロセスのアドレス空間に追加されたオブジェクトに対しては、自動バージョン依存関係検査が行われません。このため、dlopen(3C) の呼び出し元が、バージョン管理要件が適合しているかどうかを検査する必要があります。
必要なバージョン定義があるかどうかは、dlsym(3C) を使用して、関連のバージョン定義シンボルを調べることによって検査できます。次の例では、dlopen(3C) を使用して共有オブジェクト libfoo.so.1 をプロセスに追加します。次に、インタフェース SUNW_1.2 が利用可能であることを確認します。
#include <stdio.h> #include <dlfcn.h> main() { void *handle; const char *file = "libfoo.so.1"; const char *vers = "SUNW_1.2"; .... if ((handle = dlopen(file, (RTLD_LAZY | RTLD_FIRST))) == NULL) { (void) printf("dlopen: %s\n", dlerror()); return (1); } if (dlsym(handle, vers) == NULL) { (void) printf("fatal: %s: version `%s' not found\n", file, vers); return (1); } ....
バージョン定義を含む共有オブジェクトに対してリンクされた動的オブジェクトを作成する場合、特定のバージョン定義に対する結合を制限するように、リンカーに指示できます。リンカーを使用すると、特定インタフェースへのオブジェクトの結合を効果的に制御することができます。
オブジェクトの結合要件は DEPEND_VERSIONS mapfile 指令で制御できます。この指令は、リンカーの -M オプションと関連の mapfile を使用して提供されます。DEPEND_VERSIONS 指令は次の構文を使用します。
$mapfile_version 2 DEPEND_VERSIONS objname { ALLOW = version_name; REQUIRE = version_name; ... };
objname は共有オブジェクトの依存関係の名前を表します。この名前は、リンカーによって使用される共有オブジェクトのコンパイル環境名と一致しなければなりません。「ライブラリの命名規約」を 参照してください。
ALLOW 属性を使用して、結合に利用できるようにすべき共有オブジェクト内のバージョン定義名を指定します。複数の ALLOW 属性を指定できます。
REQUIRE 属性を使用すると、記録されるバージョン定義を追加できます。複数の REQUIRE 属性を指定できます。
バージョン結合の制御は、次のような場合に役立ちます。
共有オブジェクトが一意の独立したバージョンを定義するとき。このバージョン管理は、異なる標準インタフェースを定義するときに使用できます。結合制御でオブジェクトを構築することによって、そのオブジェクトが特定のインタフェースだけに結合することを保証できます。
複数世代のソフトウェアリリースにまたがって、共有オブジェクトをバージョン管理するとき。結合制御でオブジェクトを構築することによって、以前のソフトウェアリリースで利用可能だったインタフェースだけに結合するように制限できます。したがって、最新リリースの共有オブジェクトを使用して構築したオブジェクトでも、古いリリースの共有オブジェクトを使用して実行できます。
次に、バージョン制御メカニズムの使用例を示します。この例では、次のバージョンインタフェース定義を含む共有オブジェクト libfoo.so.1 を使用しています。
$ pvs -dsv libfoo.so.1 libfoo.so.1: _end; _GLOBAL_OFFSET_TABLE_; _DYNAMIC; _edata; _PROCEDURE_LINKAGE_TABLE_; _etext; SUNW_1.1: foo1; foo2; SUNW_1.1; SUNW_1.2: {SUNW_1.1}: bar;
バージョン定義 SUNW_1.1 および SUNW_1.2 は、ソフトウェア Release X および Release X+1 で使用可能な libfoo.so.1 内のインタフェースをそれぞれ表します。
アプリケーションは、次のバージョン制御 mapfile 指令を使用して、Release X で使用可能なインタフェースだけに結合するように構築できます。
$ cat mapfile $mapfile_version 2 DEPEND_VERSIONS libfoo.so { ALLOW = SUNW_1.1; }
たとえば、Release X 上で動作するアプリケーション prog を開発するとします。アプリケーションでは、Release X で使用可能なインタフェース以外は使用できません。シンボル bar を間違えて参照すると、アプリケーションは要求されるインタフェースに準拠しなくなります。リンカーはこの状態を未定義のシンボルエラーとして通知します。
$ cat prog.c extern void foo1(); extern void bar(); main() { foo1(); bar(); } $ cc -o prog prog.c -M mapfile -L. -R. -lfoo Undefined first referenced symbol in file bar prog.o (symbol belongs to unavailable \ version ./libfoo.so (SUNW_1.2)) ld: fatal: Symbol referencing errors. No output written to prog
SUNW_1.1 インタフェースに準拠するには、bar への参照を削除する必要があります。これは、アプリケーションを再処理して bar に対する要件を削除するか、または bar の実装をアプリケーションの作成に追加することによって行います。
注 - デフォルトでは、リンク編集の一部として検出された共有オブジェクト依存関係も、すべてのファイル制御指令に対して確認されます。環境変数 LD_NOVERSION を使用して、共有オブジェクト依存関係のバージョン検査を抑制します。
オブジェクトの標準のシンボル結合から作成されるバージョン依存関係よりも多くのバージョン依存関係を記録するには、DEPEND_VERSIONS mapfile 指令に REQUIRE 属性を使用します。次のセクションでは、この追加結合が役に立ついくつかのシナリオについて説明します。
1 つのシナリオは、ISV 固有のインタフェースを公開標準インタフェースで使用します。
libfoo.so.1 の例に続いて、Release X+2 において、バージョン定義 SUNW_1.1 が 2 つの標準リリース STAND_A と STAND_B に分割される場合を想定します。互換性を維持するには、SUNW_1.1 バージョン定義を維持する必要があります。次の例では、このバージョン定義は 2 つの標準定義を継承するものとして表されています。
$ pvs -dsv libfoo.so.1 libfoo.so.1: _end; _GLOBAL_OFFSET_TABLE_; _DYNAMIC; _edata; _PROCEDURE_LINKAGE_TABLE_; _etext; SUNW_1.1: {STAND_A, STAND_B}: SUNW_1.1; SUNW_1.2: {SUNW_1.1}: bar; STAND_A: foo1; STAND_A; STAND_B: foo2; STAND_B;
アプリケーション prog の唯一の要件がインタフェースシンボル foo1 である場合、このアプリケーションはバージョン定義 STAND_A に対して単一の依存関係を持ちます。このことは、libfoo.so.1 が Release X+2 よりも小さいシステムでの prog の実行を阻害します。以前のリリースでは、インタフェース foo1 が存在する場合でも、バージョン定義 STAND_A は存在しませんでした。
アプリケーション prog は、SUNW_1.1 に対する依存関係を作成することによって、その要件を以前のリリースに合わせて構築できます。
$ cat mapfile $mapfile_version 2 DEPEND_VERSIONS libfoo.so { ALLOW = SUNW_1.1; REQURE = SUNW_1.1; }; $ cat prog extern void foo1(); main() { foo1(); } $ cc -M mapfile -o prog prog.c -L. -R. -lfoo $ pvs -r prog libfoo.so.1 (SUNW_1.1);
この明示的な依存関係は、真の依存関係の要件をカプセル化するのに十分です。この依存関係は古いリリースとの互換性も保ちます。
「ウィークバージョン定義の作成」では、ウィークバージョン定義を使用して、内部実装の変更をマークする方法について説明しました。これらのバージョン定義は、オブジェクトに対して行われたバグ修正と性能の改善に適しています。ウィークバージョンの存在が必要である場合、ウィークバージョン定義への明示的な依存関係を作成できます。オブジェクトを正しく機能させるためにバグ修正や性能の改善が重要な場合、このような依存関係の作成も重要になります。
上記の libfoo.so.1 の例で、バグ修正がウィークバージョン定義 SUNW_1.2.1 としてソフトウェア Release X+3 に組み込まれている場合を想定します。
$ pvs -dsv libfoo.so.1 libfoo.so.1: _end; _GLOBAL_OFFSET_TABLE_; _DYNAMIC; _edata; _PROCEDURE_LINKAGE_TABLE_; _etext; SUNW_1.1: {STAND_A, STAND_B}: SUNW_1.1; SUNW_1.2: {SUNW_1.1}: bar; STAND_A: foo1; STAND_A; STAND_B: foo2; STAND_B; SUNW_1.2.1 [WEAK]: {SUNW_1.2}: SUNW_1.2.1;
通常、アプリケーションは、この libfoo.so.1 に対して構築されている場合、バージョン定義 SUNW_1.2.1 に対する弱い依存関係を記録します。この依存関係は情報提供だけを目的とします。実行時に使用される libfoo.so.1 の実装にバージョン定義が見つからなくても、この依存関係によってアプリケーションが強制終了されることはありません。
REQUIRE 属性を DEPEND_VERSIONS mapfile 指令に使用すると、バージョン定義に明示的な依存関係を生成できます。この定義がウィークである場合、この明示的参照によって、バージョン定義が強い依存関係に高められます。
アプリケーション prog は、次のファイル制御指令を使用して、SUNW_1.2.1 インタフェースを実行時に使用できるという要件を実施するように構築できます。
$ cat mapfile $mapfile_version 2 DEPEND_VERSIONS libfoo.so { ALLOW = SUNW_1.1; REQUIRE = SUNW_1.2.1; }; $ cat prog extern void foo1(); main() { foo1(); } $ cc -M mapfile -o prog prog.c -L. -R. -lfoo $ pvs -r prog libfoo.so.1 (SUNW_1.2.1);
prog には、インタフェース STAND_A に対する明示的な依存関係があります。バージョン定義 SUNW_1.2.1 は、強いバージョンに高められているため、依存関係 STAND_A によって正規化されます。実行時にバージョン定義 SUNW_1.2.1 が見つからないと、重大なエラーが生成されます。
注 - 依存関係が少ない場合、リンカーの -u オプションを使用して、バージョン定義に明示的に結合できます。このオプションで、バージョン定義シンボルを参照します。ただし、シンボル参照は非選択的です。類似の名前を持つ複数のバージョン定義を含む可能性がある複数の依存関係を処理する場合は、この手法で明示的な結合を作成できないことがあります。
バージョン定義をオブジェクトの内部に結合することを説明するために、さまざまなモデルについて説明してきました。これらのモデルでは、インタフェースの要件を実行時に検証できます。この検査は、各バージョン定義がオブジェクトの使用期間内に変わらない場合にのみ有効です。
オブジェクトのバージョン定義を作成したら、ほかのオブジェクトが結合することができます。このバージョン定義は、オブジェクトの次のリリースでも存在している必要があります。バージョン名およびバージョンに関連するシンボルは両方とも変更しないでください。これらの要件を適用するために、バージョン定義内で定義されるシンボル名には、ワイルドカードによる拡張はサポートされていません。これは、ワイルドカードに当てはまるシンボルの数が、オブジェクトが発展する過程で異なる場合があるからです。数が一致しない場合には、インタフェースが突然不安定になることがあります。
ここまでのセクションで、バージョン情報を動的オブジェクトの内部に記録する方法を説明してきました。再配置可能オブジェクトは、同様の方法でバージョン管理情報を保持できます。ただし、この情報の使用方法に多少違いがあります。
再配置可能オブジェクトのリンク編集に提供されるバージョン定義はすべて、オブジェクトに記録されます。これらの定義は、動的オブジェクトに記録されるバージョン定義と同じ形式で記録されます。ただしデフォルトにより、作成中の再配置可能オブジェクトに対するシンボル削減は実行されません。再配置可能オブジェクトが動的オブジェクトの生成に使用されると、バージョン情報に定義されているシンボル削減が再配置可能オブジェクトに適用されます。
また、再配置可能オブジェクトで検出されたバージョン定義はすべて、動的オブジェクトに伝達されます。再配置可能オブジェクトでのバージョン処理の例については、「シンボル範囲の縮小」を参照してください。