リンカーとライブラリ

バージョン定義への結合

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

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

次の例では、シンボル foo1foo2 を参照するアプリケーションを作成しています。アプリケーションに記録されるバージョン管理依存関係に関する情報は、-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.1SUNW_1.2 に結合されています。これらのインタフェースは、それぞれ大域シンボル foo1foo2 を提供しました。

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

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

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


注 –

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


実行時リンカーは、アプリケーションの実行時に結合されたオブジェクトから、記録されたバージョン定義があるかどうかを検査します。この検査は、-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
   ....

注 –

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 の不在は、重大ではないエラーと見なされます。この場合、実行時エラー条件は生成されません。


$ 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());
                exit (1);
        }
 
        if (dlsym(handle, vers) == NULL) {
                (void) printf("fatal: %s: version `%s' not found\n",
                    file, vers);
                exit (1);
        }
        ....

注 –

dlopen(3C) のフラグ RTLD_FIRST を使用すると、dlsym(3C) の検索が libfoo.so.1 に制限されます。