リンカーとライブラリ

バージョン定義への結合

動的実行可能ファイルまたは共有オブジェクトが、他の共有オブジェクトに対して構築される場合、これらの依存関係は結果オブジェクトに記録されます。詳細は、共有オブジェクトの処理共有オブジェクト名の記録を参照してください。これらの共有オブジェクトの依存関係にバージョン定義も含まれる場合、関連のバージョン依存関係は構築されたオブジェクトに記録されます。

次の例は、前述のデータファイルを取り上げて、コンパイル時環境に適した共有オブジェクトを生成しています。この共有オブジェクト 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 つの公開インタフェースがあります。これらのインタフェースのうち 4 つ (SUNW_1.1SUNW_1.2SUNW_1.3a SUNW_1.3b) はエクスポートされたシンボル名を定義します。1 つのインタフェース (SUNW_1.2.1) は共有オブジェクトに対する内部実装の変更を記述し、もう 1 つのインタフェース (libfoo.so.1 ) はいくつかの予約ラベルを定義します。この共有オブジェクトによって依存関係として作成される動的オブジェクトは、その動的オブジェクトが結合するインタフェースのバージョン名を記録します。

次の例では、シンボル foo1foo2 を参照するアプリケーションを作成しています。アプリケーションに記録されるバージョン管理依存関係に関する情報は、pvs(1)-r オプションを付けて使用して調べることができます。


$ 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.1SUNW_1.2 に結合されています。これらのインタフェースは、それぞれ大域シンボル foo1foo2 を提供しました。

バージョン定義 SUNW_1.1 はバージョン定義 SUNW_1.2 から継承されたものとして libfoo.so.1 内に定義されているため、後者のバージョン依存関係も記録する必要があります。バージョン定義依存関係のこの正規化によって、オブジェクト内に保持されているバージョン情報の量は削減され、実行時に必要な処理が縮小されます。

アプリケーション prog は、ウィークバージョン定義 SUNW_1.2.1 を含む共有オブジェクトの実装状態に対して構築されるため、この依存関係も記録されます。このバージョン定義は、バージョン定義 SUNW_1.2 を継承するように定義されていますが、バージョンのウィーク性は SUNW_1.1 によるその正規化を阻害するため、依存関係は別々に記録されます。

相互に継承される複数のウィークバージョン定義がある場合、これらの定義は、ウィークでないバージョン定義と同じ方法で正規化されます。


注 –

バージョン依存関係の記録は、リンカーの -z noversion オプションによって抑制できます。


これらのバージョン定義依存関係の記録を終えると、実行時リンカーは、アプリケーションの実行時に結合されたオブジェクト内に必要なバージョン定義があるかどうかを検査します。この検査は、ldd(1)-v オプションを付けて使用して表示できます。たとえば、アプリケーション 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
   ....

注 –

ldd(1)-v オプションを付けると、詳細出力が暗黙のうちに指定されます。 この出力では、すべての依存関係の再帰的なリストが、すべてのバージョン管理条件とともに生成されます。


ウィークでないバージョン定義依存関係を検出できないと、アプリケーションの初期設定中に重大なエラーが起こります。検出できないウィークバージョン定義依存関係は、暗黙の内に無視されます。たとえば、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.1SUNW_1.2 だけを含む環境内でアプリケーション prog が実行された場合、ウィークでないバージョン定義条件はすべて満たされます。ウィークバージョン定義 SUNW_1.2.1 の不在は重大ではないエラーと見なされるため、実行時エラー条件は生成されません。ただし、ldd(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 prog
        libfoo.so.1 =>   ./libfoo.so.1
        libfoo.so.1 (SUNW_1.2.1) =>          (version not found)
        ...........

注 –

オブジェクトが指定の依存関係からのバージョン定義を必要としている場合、実行時にその依存関係の実装状態にバージョン定義情報が含まれていないことがわかると、依存関係のバージョン検査は暗黙の内に無視されます。この方針は、非バージョン管理共有オブジェクトからバージョン管理共有オブジェクトへの移行が行われるときに、下位互換性レベルを提供するものです。ただし、ldd(1) は、バージョン条件の違いを表示するために引き続き使用できます。環境変数 LD_NOVERSION を使用すると、すべての実行時バージョン検査を抑制できます。


追加オブジェクトのバージョンの検査

バージョン定義シンボルも、dlopen(3DL) によって取得されたオブジェクトのバージョン条件を検査するメカニズムとなるものです。この関数を使用してプロセスのアドレス空間に追加されたオブジェクトに対しては、実行時リンカーによる自動バージョン依存関係検査が行われません。このため、この関数の呼び出し元が、バージョン管理条件が適合しているかどうかを検査する必要があります。

必要なバージョン定義があるかどうかは、dlsym(3DL) を使用して、関連のバージョン定義シンボルを調べることによって検査できます。次の例では dlopen(3DL) を使用して共有オブジェクト 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());
                exit (1);
        }
 
        if (dlsym(handle, vers) == NULL) {
                (void) printf("fatal: %s: version `%s' not found\n",
                    file, vers);
                exit (1);
        }
        ....