シェアード・ライブラリは,省略時のシステム・ライブラリです。 C コンパイラがコンパイルとリンクを行う際,省略時の設定ではシェアード・ライブラリを使用します。
この章では,次の内容について説明します。
シェアード・ライブラリの概要 (4.1 節)
シンボルの解決 (4.2 節)
シェアード・ライブラリとのリンク (4.3 節)
シェアード・ライブラリの指定解除 (4.4 節)
シェアード・ライブラリの作成 (4.5 節)
プライベートなシェアード・ライブラリの使用 (4.6 節)
クイックスタートの使用 (4.7 節)
シェアード・ライブラリとリンクしたプログラムのデバッグ (4.8 節)
シェアード・ライブラリの実行時のロード (4.9 節)
シェアード・ライブラリ・ファイルの保護 (4.10 節)
シェアード・ライブラリのバージョン管理 (4.11 節)
シンボル割り当て (4.12 節)
シェアード・ライブラリの制限事項 (4.13 節)
シェアード・ライブラリは,メモリ内のどのアドレスへも配置できる実行可能ファイル・コードから構成されています。 シェアード・ライブラリの命令のコピーは 1 つだけロードされます。 アーカイブ (静的) ライブラリのようにライブラリを使用する各プログラムがライブラリのコピーをロードするのではなく,複数のプログラム間で 1 つのコピーを共用します。
シェアード・ライブラリを使用するプログラムは,次の点でアーカイブ・ライブラリを使用するプログラムよりもはるかに有利です。
シェアード・ライブラリとリンクしたプログラムは,そのシェアード・ライブラリに変更が行われても,再コンパイルおよび再リンクする必要はありません。
アーカイブ・ライブラリにリンクしたプログラムとは異なり,シェアード・ライブラリにリンクしたプログラムの場合,その実行可能プログラム・ファイルにライブラリ・ルーチンは含まれません。 シェアード・ライブラリにリンクしたプログラムには,シェアード・ライブラリをロードするための情報,ロード時にそのルーチンやデータにアクセスするための情報が含まれています。
つまり,シェアード・ライブラリを使用すると,メモリやディスクの専有領域が少なくなります。 複数のプログラムが 1 つのシェアード・ライブラリにリンクされる場合には,それぞれのプロセスで使用される物理メモリの量は,大幅に減少します。
シェアード・ライブラリの使用はユーザからは透過です。 さらに,プログラムをアーカイブ・ライブラリとリンクさせる方法を採用することもできます。 また,ユーザ独自のシェアード・ライブラリを作成し,他のユーザに使用させることもできます。 大部分のオブジェクト・ファイルおよびアーカイブ・ライブラリは,シェアード・ライブラリにすることができます。 シェアード・ライブラリにできるファイルについては,4.5 節を参照してください。
シェアード・ライブラリは,次の点でアーカイブ・ライブラリと異なります。
シェアード・ライブラリは
ld
コマンドに適切なオプションを付けて作成するが,アーカイブ・ライブラリは,ar
コマンドで作成する。
ld
コマンドの詳細については,
ld
(1)
シェアード・ライブラリは,リンクされると使用可能な任意のアドレスに置かれる。
実行時に,ローダ (/sbin/loader
) がメモリ内のプライベートな仮想アドレス空間をシェアード・ライブラリに割り当てます。
アーカイブ・ライブラリは,実行可能ファイルの一部としてリンクされるとメモリ内の定位置に置かれます。
シェアード・ライブラリは
/usr/shlib
ディレクトリに常駐し,アーカイブ・ライブラリは
/usr/lib
ディレクトリに常駐する。
シェアード・ライブラリの命名規則により,シェアード・ライブラリ名は接頭語
lib
で始まり,接尾語
.so
で終わる。
たとえば,共通の C 言語機能を含むライブラリ名は,libc.so
となります。
アーカイブ・ライブラリ名も接頭語
lib
で始まりますが,接尾語
.a
で終わります。
図 4-1
に,アーカイブ・ライブラリとシェアード・ライブラリの違いを示します。
図 4-1: アーカイブ・ライブラリとシェアード・ライブラリ
シンボルの解決とは,プログラムまたはシェアード・ライブラリによりインポートされた未解決のシンボルを,そのシンボルをエクスポートするシェアード・ライブラリのパス名にマップする処理です。 アーカイブ・ライブラリおよびシェアード・ライブラリのシンボル解決は,シェアード・オブジェクトのシンボルの最終的な解決がプログラム起動時まで行われないという点を除いて,ほとんど同じ方法で行なわれます。
次の各項では,以下の項目について説明します。
リンカの探索パス (ld
) (4.2.1 項)
実行時ローダの探索パス (/sbin/loader
) (4.2.2 項)
名前の解決 (4.2.3 項)
未解決の外部シンボルの動作を解決するため,ld
コマンドに付けるオプション (4.2.4 項)
リンカ (ld
) は
-l
オプションによって指定されたファイルを探索する場合,次に示した順序で各ディレクトリで探索します。
リンカは,最初にシェアード・ライブラリ (.so
) を探索します。
/usr/shlib
/usr/ccs/lib
/usr/lib/cmplrs/cc
/usr/lib
/usr/local/lib
/var/shlib
シェアード・ライブラリがない場合は,リンカはすべてのディレクトリでアーカイブ (.a
) ライブラリを探索します。
-no_archive
オプションを使用すると,リンカによるアーカイブ・ライブラリの探索を禁止することができます。
4.2.2 実行時ローダの探索パス
特に指示がない限り,実行時ローダ (/sbin/loader
) は,リンカと同じ探索パスを経由します。
実行時ローダに次のように指示して,省略時の探索パスで指定されているディレクトリ以外のディレクトリを探索することができます。
ld
コマンドを
-rpath string
オプションとともに使用し,string
を探索するディレクトリのリストに設定して,ディレクトリ・パスを指定する。
プログラムを実行する前に,環境変数
LD_LIBRARY_PATH
がプライベートなシェアード・ライブラリを格納しているディレクトリを指すように設定する。
この環境変数は,プログラムの実行時に実行時ローダによって検証されます。
このとおり設定されると,ローダは4.2.1 項で説明されている一連のディレクトリを探索する前に,LD_LIBRARY_PATH
によって定義されたパスを探索します。
次のいずれかの方法で,LD_LIBRARY_PATH
変数を設定することができます。
シェル・プロンプトが表示されているときに,環境変数として設定する。
C シェルの場合は,次に示すように,コロンで区切ったパスを指定して
setenv
コマンドを実行します。
% setenv LD_LIBRARY_PATH .:$HOME/testdir
Korn シェルおよび Bourne シェルの場合は,次に示すように,変数を設定した後エクスポートする必要があります。
$ LD_LIBRARY_PATH=.:$HOME/testdir $ export LD_LIBRARY_PATH
これらの例では,ローダが最初に現在のディレクトリを探索し,続いて
$HOME/testdir
ディレクトリを探索するようにパスを設定しています。
ログイン・ファイルまたはシェル・スタートアップ・ファイルに,変数の定義を追加する。
たとえば,C シェルを使用している場合,次の行を
.login
ファイルまたは
.cshrc
ファイルに追加することができます。
setenv LD_LIBRARY_PATH .:$HOME/testdir:/usr/shlib
これらの手順で定義されたパスの中で必要とするライブラリをローダが探索できない場合,ローダは4.2.1 項で説明されている省略時のパスの中のディレクトリを探索します。
さらに,_RLD_ROOT
環境変数を使用して,実行時ローダの探索パスを変更することもできます。
詳細については,
loader
(5)4.2.3 名前の解決
シンボル名の解決の意味規則は,シンボルが入っているオブジェクト・ファイルまたは共用オブジェクトがリンク・コマンド行に現れる順序に基づいています。 通常,リンカは一番左側の定義を解決しなければならないシンボルと見なします。
リンク・コマンド行が実行可能ファイルに保存されているかのように,名前が順番に解決されます。 プログラムを実行する場合には,実行時にアクセスされるすべてのシンボルが解決されなければなりません。 シンボルが解決されないと,ローダはプログラムの実行を打ち切ります。
未解決のシンボルに関してシステムの動作を判断する方法の詳細については,4.2.4 項を参照してください。
次の順序でメイン・プログラムまたはライブラリへのシンボルの参照が解決されます。
メイン実行可能ファイルを生成する元になったオブジェクト・ライブラリまたはアーカイブ・ライブラリでシンボルが定義されている場合,そのシンボルはメイン・プログラム・ファイルおよびそれが使用するすべてのシェアード・ライブラリで使用される。
シンボルが手順 1 で定義されないで,リンクされている 1 つまたは複数の共用オブジェクトで定義される場合には,定義を指定するリンク・コマンド行の一番左端のライブラリが使用される。
リンク・コマンド行に指定したライブラリが他のライブラリに依存するようにリンクされた場合には,ライブラリの依存性は,深さ優先の方法で探索されるのではなく,広さ優先の方法で探索される。
たとえば,次の図のように,実行可能プログラム A がシェアード・ライブラリ B および D にリンクされ,シェアード・ライブラリ B がライブラリ C にリンクされているとします。
A / \ B D / C
この場合,探索順序は A→B→D→C です。 幅優先探索では,孫にあたるノードはすべての子ノードが探索された後に探索されます。
これまでの手順でシンボルが解決されない場合には,シンボルが解決されないまま残る。
シンボル解決では必ずメイン・オブジェクトのほうが優先されるため,メイン・オブジェクト内で定義されているシンボルにコールバックされるようにシェアード・ライブラリを設定できることに注意してください。
さらに,メイン・オブジェクトは,シェアード・ライブラリの定義を指定変更 (強制排除またはフック) するシンボルを定義することができます。
4.2.4 未解決の外部シンボルの処理のオプション
実行可能プログラムを作成する場合とシェアード・ライブラリを作成する場合では,リンカの省略時の動作は次のように異なります。
実行可能プログラム -- 実行可能プログラムを作成する場合,省略時の設定では未解決のシンボルによりエラーが生成されます (-error_unresolved オプション)。
シェアード・ライブラリ -- シェアード・ライブラリを作成する場合,省略時の設定では未解決のシンボルは警告メッセージのみを作成します (-warning_unresolved オプション)。
-error_unresolved
を指定してシェアード・ライブラリを構築すると,未解決のシンボルがある場合でも出力ファイル
.so
ファイル) が生成され,同名の
.so
があればそのファイルは上書きされます。
リンカ出力ファイルの上書きについての詳細は 『プログラミング・ガイド』 を参照してください。
リンカが未解決のシンボルをどのように処理するかは,ld
コマンドで次のオプションを使用して制御することができます。
-expect_unresolved
pattern
-warning_unresolved
-error_unresolved
これらのオプションについての詳細は,
ld
(1)4.3 シェアード・ライブラリとのリンク
プログラムのコンパイルおよびリンクは,シェアード・ライブラリを使用しても,スタティック・ライブラリを使用しても同じ結果になります。
たとえば,次のコマンドは,プログラム
hello.c
をコンパイルして,省略時のシステムの共用 C ライブラリ
libc.so
にリンクします。
% cc -o hello hello.c
特定の
ld
コマンドのオプションを
cc
コマンドに渡すことによって,シェアード・ライブラリ用の探索パスを自由に指定することができます。
たとえば,次に示すように,cc
コマンドに
-Ldir
オプションを使用して,省略時のディレクトリより先に
dir
を見るように探索パスを変更することができます。
% cc -o hello hello.c -L/usr/person -lmylib
省略時のディレクトリを探索から除外し,特定のディレクトリおよび特定のライブラリに探索を限定する場合には,次のように指定します。
まず,引数を付けずに
-L
オプションを指定し,次に,探索するディレクトリを付けて
-L
オプションを指定し,その後に探索するライブラリ名を付けて
-l
オプションを指定します。
たとえば,探索パスを
/usr/person
とプライベートなライブラリ (libmylib.so
) に限定するには,次のコマンドを入力してください。
% cc -o hello hello.c -L -L/usr/person -lmylib
cc
コマンドは常に暗黙に C ライブラリにリンクしているため,前述の例では,/usr/person
ディレクトリ内に
libc.so
または
libc.a
のコピーが必要であることに注意してください。
4.4 シェアード・ライブラリの指定解除
アプリケーションをリンクする場合は,省略時の設定としてシェアード・ライブラリが使用されます。
シェアード・ライブラリを使用しないアプリケーションをリンクする場合には,そのアプリケーションをリンクするときに,cc
コマンドまたは
ld
コマンドに
-non_shared
オプションを使用しなければなりません。
たとえば,次のように入力します。
% cc -non_shared -o hello hello.c
大部分のアプリケーションではシェアード・ライブラリは省略時の設定ですが,アプリケーションの中にはシェアード・ライブラリを使用できないものがあります。
シングルユーザ・モードで実行する必要があるアプリケーションは,シェアード・ライブラリとリンクすることができない。
これは,シェアード・ライブラリにアクセスできるようにするためには,/usr/shlib
ディレクトリをマウントしなければならないためです。
シングルユーザ・ベンチマークが唯一の用途であるアプリケーションを,シェアード・ライブラリとリンクしてはならない。
シェアード・ライブラリは,-shared
オプション付きで
ld
コマンドを使用して作成します。
オブジェクト・ファイルまたは既存のアーカイブ・ライブラリからシェアード・ライブラリを作成することもできます。
4.5.1 オブジェクト・ファイルからのシェアード・ライブラリの作成
オブジェクト・ファイル
bigmod1.o
および
bigmod2.o
から,シェアード・ライブラリ
libbig.so
を作成するには,次のコマンドを入力します。
% ld -shared -no_archive -o libbig.so bigmod1.o bigmod2.o -lc
-no_archive
オプションは,シェアード・ライブラリだけを使用して,すべてのシンボルを解決するようにリンカに指示します。
-lc
オプションは未解決のシンボルがないかどうか,システムの C ライブラリを探索するようにリンカに指示します。
シェアード・ライブラリを
/usr/shlib
ディレクトリにコピーして,システム・レベルでの利用を可能にするには,ルート・ディレクトリに関する特権が必要です。
システムで共用するライブラリは,/usr/shlib
ディレクトリまたは複数ある省略時のディレクトリのうちいずれかに格納し,各ユーザが
LD_LIBRARY_PATH
変数を省略時のパス以外にあるディレクトリに設定しなくても,実行時ローダ (/sbin/loader
) がシェアード・ライブラリの場所を探索できるようにしなければなりません。
4.5.2 アーカイブ・ライブラリからのシェアード・ライブラリの作成
ld
コマンドを使用して,既存のアーカイブ・ライブラリからシェアード・ライブラリを作成することができます。
次に,スタティック・ライブラリ
old.a
をシェアード・ライブラリ
libold.so
に変換する例を示します。
% ld -shared -no_archive -o libold.so -all old.a -none -lc
この例では,-all
オプションは,アーカイブ・ライブラリ
old.a
にあるすべてのオブジェクトをリンクするように,リンカに指示しています。
-none
オプションは,-all
オプションを取り消すようにリンカに指示しています。
-no_archive
オプションは,-lc
オプションの解決には適用されますが,old.a
の解決には適用されない点に注意してください (old.a
オプションが明示的に記述されているため)。
4.6 プライベートなシェアード・ライブラリの使用
システムのシェアード・ライブラリだけでなく,どのユーザもプライベートなシェアード・ライブラリを作成および使用することができます。
たとえば,ある共通のコードを共用する 3 つのアプリケーションがあるとします。
これらのアプリケーションは,user
,db
,および
admin
という名称であるとします。
共用ファイル
io_util.c
,defines.c
,および
network.c
に定義されているすべてのシンボルが含まれる共通のシェアード・ライブラリ
libcommon.so
を作成するには,次の手順に従ってください。
ライブラリの一部となる各 C ファイルをコンパイルする。
% cc -c io_util.c % cc -c defines.c % cc -c network.c
ld
コマンドを使用してシェアード・ライブラリ
libcommon.so
を作成する。
% ld -shared -no_archive \ -o libcommon.so io_util.o defines.o network.o -lc
アプリケーションの一部となる C ファイルをコンパイルする。
% cc -c user.c % cc -o user user.o -L. -lcommon
この手順の 2 番目のコマンドは,現在のディレクトリを見てからライブラリ
libcommon.so
を使用するように,リンカに指示していることに注意してください。
同じ方法で,db.c
と
admin.c
をコンパイルします。
% cc -c db.c % cc -o db db.o -L. -lcommon % cc -c admin.c % cc -o admin admin.o -L. -lcommon
libcommon.so
が
LD_LIBRARY_PATH
で示されているディレクトリ内にない場合には,libcommon.so
をそのディレクトリにコピーする。
コンパイル済みのプログラム (user
,db
,および
admin
) をそれぞれ実行する。
シェアード・ライブラリの 1 つの利点は,すべての実行イメージをリンクした後にライブラリを変更できるため,ライブラリのバグを修正できることです。 この機能は,製品の開発期間中に非常に役立ちます。
しかし,製品出荷サイクルでは,通常,開発および修正したシェアード・ライブラリとアプリケーションは,次のリリースまで変更されません。 その場合には,クイックスタートを活用することができます。 これは,ユーザのプログラムとライブラリにあるすべてのシンボルに,あらかじめアドレスを設定する方法です。
アプリケーションをクイックスタートさせるための特別なリンク・オプションは必要ありませんが,満たさなければならない一連の条件があります。 クイックスタートできない場合でもオブジェクトを実行することはできますが,スタートアップ時間は長くなります。
リンカが共用オブジェクト,すなわちシェアード・ライブラリまたはシェアード・ライブラリを使用するメイン実行可能ファイルを作成する場合には,オブジェクトのテキストおよびデータ部分にアドレスを割り当てます。 このアドレスが,いわゆるクイックスタート・アドレスと呼ばれているものです。 オブジェクトがクイックスタート・アドレスでロードされるかのように,リンカは事前にすべての動的再配置を実行します。
依存するすべてのオブジェクトは,クイックスタート用アドレスにあるとみなされます。 元のオブジェクトから依存先のオブジェクトを参照すると,それに応じて依存先のオブジェクトのアドレスが設定されます。
クイックスタートを使用するためには,オブジェクトは次の条件を満たしていなければなりません。
オブジェクトの実行時の実際のメモリ位置は,クイックスタート・アドレスと一致しなければならない。
実行時ローダがクイックスタート・アドレスを使用しようとしても,すでに別のライブラリがそのアドレスを占有している場合には使用できません。
依存するすべてのオブジェクトは,クイックスタートしなければならない。
依存するすべてのオブジェクトは,リンク後に変更があってはならない。
変更があった場合には,ライブラリ内の関数のアドレスが移動し,新しいシンボルの追加によってリンクに影響する可能性があります。
リンク後に変更されたオブジェクトに対して
fixso
ユーティリティを実行することによって,そのオブジェクトをクイックスタートすることができるようになります。
詳細については,
fixso
(1)
オペレーティング・システムは,これらの条件を,チェックサムとタイムスタンプによって確認します。
ライブラリを作成する場合,それらにはクイックスタート・アドレスが与えられます。 クイックスタートの制約条件を満たすためには,アプリケーションが使用するすべてのライブラリのクイックスタート・アドレスをそれぞれ一意な値に設定する必要があります。 アプリケーション側でアドレスの心配をするよりも,作成した各シェアード・ライブラリに一意なクイックスタート・アドレスを割り当て,すべてのオブジェクトをクイックスタート・アドレスでロードできるようにしてください。
リンカは,ライブラリ作成時にクイックスタート・アドレスが登録される
so_locations
データベースを保守します。
新しいライブラリに対してクイックスタート・アドレスを選択する際,リンカは,すでに
so_locations
ファイルに存在するアドレスはクイックスタート・アドレスとして使用しません。
省略時の設定では,-update_registry ./so_locations
オプションが選択されたものとして,ld
が実行されます。
この結果,作成したライブラリのディレクトリ内の
so_locations
ファイルが,必要に応じて更新されるかまたは作成されます。
ライブラリがユーザのシステムのシェアード・ライブラリと競合しないよう,次のコマンドを入力してください。
% cd <directory_of_build> % cp /usr/shlib/so_locations . % chmod +w so_locations
ここまでの手順により,ライブラリを作成することができます。
複数のディレクトリにライブラリを作成した場合には,ld
コマンドを
-update_registry
オプションとともに使用して,共通の
so_locations
ファイルの位置を明示的に指定します。
たとえば,次のように入力します。
% ld -shared -update_registry /common/directory/so_locations ...
システムのすべてのユーザのために,グローバルにシェアード・ライブラリをインストールする場合には,システム全体で使用する
so_locations
ファイルを更新します。
shared_library.so
に実際のシェアード・ライブラリを指定して,ルート・アカウントで次のコマンドを入力します。
# cp shared_library.so /usr/shlib # mv /usr/shlib/so_locations /usr/shlib/so_locations.old # cp so_locations /usr/shlib
複数の人がシェアード・ライブラリを作成している場合には,他の共用データベースと同様に,共通の
so_locations
ファイルを管理する必要があります。
任意のプロセスで使用される各シェアード・ライブラリには,ファイルに一意のクイックスタート・アドレスを指定しなければなりません。
リンカがメイン実行可能ファイルに割り当てる省略時の開始アドレスの範囲は,共用オブジェクトに対して作成するクイックスタート・アドレスとは競合しません。
1 つのメイン実行可能ファイルだけがプロセスにロードされるので,メインの実行可能ファイルとその共用オブジェクトの間でアドレスの競合は起こりません。
既存のシェアード・ライブラリを使用しているだけで,ユーザ独自のライブラリを使用しない場合には,特に何もする必要はありません。 ライブラリが前に説明した条件に合っている限り,ライブラリ自身をクイックスタートしない場合を除いて,ユーザのプログラムはクイックスタートされます。 オペレーティング・システムに付属するほとんどのライブラリは,クイックスタートするようになっています。
新規にシェアード・ライブラリを作成している場合には,すでに説明しているように,最初に
so_locations
ファイルをコピーしなければなりません。
次に,so_locations
ファイルを使用して,すべてのシェアード・ライブラリをボトムアップ式の順番で作成しなければなりません。
依存するすべてのライブラリをリンク行に指定してください。
すべてのライブラリを作成した後に,ユーザのアプリケーションを作成することができます。
4.7.1 オブジェクトのクイックスタートの確認
アプリケーションの実行可能プログラムがクイックスタートであるかどうかを調べるには,_RLD_ARGS
環境変数に
-quickstart_only
を設定して,プログラムを実行します。
たとえば,次のように設定します。
%
setenv _RLD_ARGS -quickstart_only
%
foo
クイックスタートでない場合には,次のような出力になります。
21887:foo: /sbin/loader: Fatal Error: NON-QUICKSTART detected \ -- QUICKSTART must be enforced
プログラムが正常に終了すればクイックスタートです。
ロード・エラー・メッセージが表示された場合には,そのプログラムはクイックスタートではありません。
4.7.2 手動によるクイックスタート問題の解決
実行可能プログラムがクイックスタートでない原因を判断するには,4.7.3 項で説明する
fixso
ユーティリティを使用するか,あるいは次の条件を調べてみてください。
fixso
を使用すると簡単ですが,ここで説明する手順を理解しておくことも役に立ちます。
実行可能プログラムはクイックスタート可能でなければならない。
動的ヘッダのクイックスタート・オプションを調べてください。 クイックスタート・オプションの値は 0x00000001 です。
%
odump -D foo | grep FLAGS
クイックスタートでない場合の出力は,次のようになります。
FLAGS: 0x00000000
クイックスタートの場合の出力は,次のようになります。
FLAGS: 0x00000001
クイックスタート・オプションが設定されていない場合には,次のことが考えられます。
実行可能プログラムが未解決のシンボルにリンクされている。
実行可能プログラムをリンクするときに,ld
オプションの
-warning_unresolved
と
-expect_unresolved
が使用されていないことを確認してください。
実行可能プログラムのリンク時に生じるすべての未解決シンボル・エラーを修正してください。
実行可能プログラムが,実行時に使用するすべてのライブラリに直接リンクされていなかった。
実行可能プログラムを作成するときに,使用している
ld
オプションに
-transitive_link
オプションを追加してください。
実行可能プログラムの依存するライブラリはクイックスタートでなければならない。
実行可能プログラムの依存するライブラリのリストを表示するには,次のコマンドを実行します。
%
odump -Dl foo
クイックスタートの場合の出力は,次のようになります。
***LIBRARY LIST SECTION*** Name Time-Stamp CheckSum Flags Version foo: libX11.so Sep 17 00:51:19 1993 0x78c81c78 NONE libc.so Sep 16 22:29:50 1993 0xba22309c NONE osf.1 libdnet_stub.so Sep 16 22:56:51 1993 0x1d568a0c NONE osf.1
各依存ライブラリの動的ヘッダのクイックスタート・フラグを調べます。
%
cd /usr/shlib
%
odump -D libX11.so libc.so libdnet_stub.so | grep FLAGS
クイックスタートの場合には,次のような出力になります。
FLAGS: 0x00000001 FLAGS: 0x00000001 FLAGS: 0x00000001
依存ライブラリのいずれかがクイックスタートできないとき,シェアード・ライブラリをユーザが作成し直せる場合には,手順 1 で説明した方法で解決してください。
すべての依存ライブラリのタイムスタンプとチェックサム情報が一致していなければならない。
手順 2 の依存ライブラリ・リストには,foo
の各依存ライブラリのタイムスタンプとチェックサムのフィールドに期待値が表示されています。
これらの値と,各ライブラリの現在の値を照合するには,次のようにします。
%
cd /usr/shlib
%
odump -D libX11.so libc.so libdnet_stub.so | \
grep TIME_STAMP
クイックスタートの場合は,次のような出力になります。
TIME_STAMP: (0x2c994247) Fri Sep 17 00:51:19 1993 TIME_STAMP: (0x2c99211e) Thu Sep 16 22:29:50 1993 TIME_STAMP: (0x2c992773) Thu Sep 16 22:56:51 1993
%
odump -D libX11.so libc.so libdnet_stub.so | grep CHECKSUM
クイックスタートの場合には,次のような出力になります。
ICHECKSUM: 0x78c81c78 ICHECKSUM: 0xba22309c ICHECKSUM: 0x1d568a0c
どちらかのテストで,タイムスタンプまたはチェックサムの不一致があった場合には,プログラムを再リンクすることにより問題は解決されます。
バージョン・フィールドを使用すると,実行時に正しいライブラリがロードされたことを確認することができます。
依存ライブラリのバージョンを調べるには,次の例に示すように
odump
コマンドを使用します。
%
odump -D libX11.so | grep IVERSION
%
odump -D libc.so | grep IVERSION
IVERSION: osf.1%
odump -D libdnet_stub.so | grep IVERSION
IVERSION: osf.1
依存情報のエントリが空白の場合には,一致する
IVERSION
エントリはありません。
また,特殊なバージョン
_null
に一致するエントリもありません。
バージョンの不一致が見つかった場合には,通常は,依存リストのバージョン識別子を追加するか,または
_null
をパス
/usr/shlib
に追加することによって,シェアード・ライブラリの正しいバージョンを見つけることができます。
実行可能プログラムの各依存ライブラリも,一致するタイムスタンプとチェックサム情報を示す依存リストを含んでいなければならない。
実行可能プログラムの依存リストに表示されている各シェアード・ライブラリに対して,手順 3 を繰り返して実行します。
%
odump -Dl libX11.so
クイックスタートの場合には,次のような出力になります。
***LIBRARY LIST SECTION*** Name Time-Stamp CheckSum Flags Version libX11.so: libdnet_stub.so Sep 16 22:56:51 1993 0x1d568a0c NONE osf.1 libc.so Sep 16 22:29:50 1993 0xba22309c NONE osf.1%
odump -D libdnet_stub.so libc.so | grep TIME_STAMP
TIME_STAMP: (0x2c992773) Thu Sep 16 22:56:51 1993 TIME_STAMP: (0x2c99211e) Thu Sep 16 22:29:50 1993%
odump -D libdnet_stub.so libc.so | grep CHECKSUM
ICHECKSUM: 0x1d568a0c ICHECKSUM: 0xba22309c
タイムスタンプまたはチェックサム情報が一致していない場合には,シェアード・ライブラリを再作成して問題を解決しなければなりません。 シェアード・ライブラリを再作成すると,タイムスタンプが変更され,場合によってはチェックサムも変更されます。 依存ライブラリをボトムアップ式に再作成してから,実行可能プログラムやシェアード・ライブラリを再作成します。
4.7.3 fixso ユーティリティによるクイックスタート問題の解決
fixso
ユーティリティは,タイムスタンプおよびチェックサムの不一致が原因で発生するクイックスタートの問題を識別し修復します。
このユーティリティは,プログラムが依存するシェアード・ライブラリと同様に,プログラム自体も修復できます。
ただし,シンボルの変更が必要となるようなプログラムの修復はできません。
fixso
ユーティリティは,次に示す制限に該当するプログラムあるいはシェアード・ライブラリは修復できません。
プログラムあるいはシェアード・ライブラリがクイックスタートできない他のシェアード・ライブラリに依存している場合。
この制限は,シェアード・ライブラリに対して逆の順序で
fixso
を使用することによって回避することができます。
プログラムあるいはシェアード・ライブラリを作成した後に新しい名前の矛盾が発生した場合。
名前の矛盾は,2 つ以上の依存するシェアード・ライブラリ,あるいはプログラムおよびそれが依存するシェアード・ライブラリによって同じグローバル・シンボル名がエクスポートされている場合に発生します。
プログラムが依存するシェアード・ライブラリのいずれかが,それらのクイックスタート位置にロードされない場合。
同じクイックスタート位置に他のシェアード・ライブラリがロードされ,既に使用されている場合は,シェアード・ライブラリをクイックスタート位置にロードできません。
この規則は個々のプロセスに対してだけでなく,システム全体に適用されます。
この制限を回避するには,シェアード・ライブラリに対してユニークなアドレスを登録するための共通
so_locations
ファイルを使用します。
プログラムあるいはシェアード・ライブラリが,互換性のないバージョンのシェアード・ライブラリに依存している場合。
この制限は,互換性のあるバージョンのシェアード・ライブラリを探すよう
fixso
に指示することによって回避できます。
fixso
ユーティリティを使用すると,次の例に示すような形でクイックスタート問題を識別することができます。
%
fixso -n hello.so
fixso: Warning: found '/usr/shlib/libc.so' (0x2d93b353) which does not match timestamp 0x2d6ae076 in liblist of hello.so, will fix fixso: Warning: found '/usr/shlib/libc.so' (0xc777ff16) which does not match checksum 0x70e62eeb in liblist of hello.so, will fix
-n
オプションを指定すると出力ファイルの生成を行いません。
この例では,不一致が報告されていますが問題の修復は行われていません。
次の例では,fixso
を使ってクイックスタート問題を修復する方法を説明します。
%
fixso -o ./fixed/main main
fixso: Warning: found '/usr/shlib/libc.so' (0x2d93b353) which does not match timestamp 0x2d7149c9 in liblist of main, will fix%
chmod +x fixed/main
-o
オプションは出力ファイルを指定します。
出力ファイル名が指定されない場合は
a.out
が使用されます。
fixso
が作成する出力ファイルは,ファイル保護コードが実行可能ではないことに注意してください。
chmod
コマンドで出力ファイルの保護コードを変更しています。
この変更処理は,実行可能プログラムに対してのみ必要になります。
fixso
を使用してシェアード・ライブラリを修復する場合は省略できます。
プログラムあるいはシェアード・ライブラリの修復が不要な場合,fixso
は,次の例に示すように指摘します。
%
fixso -n /bin/ls
no fixup needed for /bin/ls
4.8 シェアード・ライブラリとリンクしているプログラムのデバッグ
シェアード・ライブラリを使用しているプログラムをデバッグする方法は,アーカイブ・ライブラリを使用しているプログラムをデバッグする方法と基本的には同じです。
dbx
デバッガの
listobj
コマンドは,実行プログラムの名前とデバッガが認識できるすべてのシェアード・ライブラリを表示します。
dbx
についての詳細は,第 5 章を参照してください。
4.9 シェアード・ライブラリの実行時のロード
プログラムからシェアード・ライブラリをロードする場合があります。 この節では,2 つの短い C プログラムと makefile を例に,実行時にシェアード・ライブラリをロードする方法を示します。
簡単なメッセージを表示する C プログラムのソース・ファイルの例 (pr.c
) を,次に示します。
printmsg() { printf("Hello world from printmsg!\n"); }
シンボルを定義し,dlopen
関数を使用する方法を示す C プログラムのソース・ファイルの例 (used1.c
) を,次に示します。
#include <stdio.h> #include <dlfcn.h> /* dl* ルーチンからのエラーはすべて NULL で戻される。*/ #define BAD(x) ((x) == NULL) main(int argc, char *argv[]) { void *handle; void (*fp)(); /* ./ 接頭語を使用すると,dlpen は現在の * ディレクトリを探索する。それ以外の場合には, * LD_LIBRARY_PATH などを使用する。 */ handle = dlopen("./pr.so", RTLD_LAZY); if (!BAD(handle)) { fp = dlsym(handle, "printmsg"); if (!BAD(fp)) { /* * ここで,探索していた関数が呼び出される。 */ (*fp)(); } else { perror("dlsym"); fprintf(stderr, "%s\n", dlerror()); } } else { perror("dlopen"); fprintf(stderr, "%s\n", dlerror()); } dlclose(handle); }
次に,pr.o
,pr.so
,so_locations
,usedl.o
を生成する makefile を示します。
# これは例を検証する makefile である。 all: runit runit: usedl pr.so ./usedl usedl: usedl.c $(CC) -o usedl usedl.c pr.so: pr.o $(LD) -o pr.so -shared pr.o -lc
シェアード・ライブラリに共用のためのメカニズムが使用されているため,通常のファイル・システムのための保護機能では,不正な読み取りを防ぐことができません。 たとえば,シェアード・ライブラリがプログラムの中で使用されている場合,そのライブラリのテキスト部分は,次に示すような状況においても他のプロセスから読み取ることができます。
ライブラリの保護コードが 600 に指定されている
ライブラリの所有者ではない,あるいはライブラリ所有者に設定されている UID を使用していない
この場合,テキスト部分だけが共用され,データ・セグメントは共用されません。
必要以外の共用を行わないために,保護する必要のあるシェアード・ライブラリは
-T
オプションや
-D
オプションを使用してリンクし,データ・セクションを次のセクションと同じ 8 MB のセグメントに格納してください。
たとえば,次の例のようなコマンドを入力してください。
% ld -shared -o libfoo.so -T 30000000000 \ -D 30000400000 object_files
さらに,マップされたアドレスが
mmap
を使用している他のファイルと同じメモリ・セグメントを参照している限り,PROT_WRITE
オプションを使用せずに
mmap
システム・コールを使用しているどのようなファイルにもセグメントの共用が発生する可能性があります。
mmap
を使用して厳重に保護されている可能性のあるファイルを検査するプログラムを使用すると,mmap
の前またはその途中セグメントに書き込み可能なページを挿入して,セグメントの共用を防ぐことができます。
シェアード・ライブラリを最も簡単に保護するには,保護機能の実行の際に使用可能になる
PROT_WRITE
が使用されているファイルで
mmap
システム・コールを使用し,mprotect
システム・コールを使用してマップされたメモリを読み取りのみ可能にしてください。
別の方法として,すべてのセグメント化を使用不能にし,権限のない共用を防ぐには,構成ファイルに次の行を入力します。
segmentation 0
シェアード・ライブラリを使用することの利点の 1 つは,シェアード・ライブラリが変更されても,そのライブラリとリンクしているプログラムを再作成する必要がないということです。 変更されたライブラリがインストールされると,アプリケーションは古いライブラリを使用していたときと同様に新しいライブラリを使用して動作します。
注意
新しいバージョンのシェアード・ライブラリがインストールされると,古いバージョンのシェアード・ライブラリを使用する既存のアプリケーションは,追加アドレスの決定が必要になるため,ロード時間が長くなる場合があります。 アプリケーションを新しいライブラリにリンクし直すと,このようなアドレスの決定を避けて,ロード時間を短縮することができます。
まれに,シェアード・ライブラリを変更すると,そのライブラリと変更前にリンクしていたアプリケーションとの互換性がなくなることがあります。 この種の変更をバイナリ非互換といいます。 シェアード・ライブラリの新しいバージョンでバイナリ非互換が起こっても,古いバージョンのライブラリに依存するアプリケーションは,必ずしもエラーになる (つまり,ライブラリの下位互換性に違反する) わけではありません。 システムではシェアード・ライブラリのバージョン管理を行うことによって,ライブラリでバイナリ非互換が起こった場合に,シェアード・ライブラリの下位互換性を維持する処置がとれるようにしています。
シェアード・ライブラリで非互換を起こす修正には,次のような種類があります。
文書化されているインタフェースの削除
たとえば,libc.so
の
malloc()
関数を (_ _malloc
) という関数と置き換えると,古い関数に依存するプログラムは,シンボル
malloc
がないために異常終了します。
文書化されているインタフェースの修正
たとえば,libc.so
の
malloc()
関数に 2 番目の引数を追加すると,古い関数に依存するプログラムが,2 番目の引数を未定義の値のままにして,1 つの引数だけに値を引き渡したときに,新しい
malloc()
は失敗する可能性が高くなります。
グローバル・データ定義の修正
たとえば,libc.so
のシンボル
errno
の型を
int
から
long
に変更すると,古いライブラリとリンクされていたプログラムは,新しく拡張された 64 ビットのデータ項目との間で 32 ビット値の読み取りや書き込みを行います。
この場合には,無効なエラー・コードと不定なプログラム動作が生じます。
もちろん,これだけがバイナリ非互換を引き起こす変更のすべてではありません。
シェアード・ライブラリの開発者は常識を働かせて,どのような変更を行うと,変更前のライブラリとリンクしていたアプリケーションに障害が生じるかを判断してください。
4.11.2 シェアード・ライブラリのバージョン管理
非互換変更の影響を受けたシェアード・ライブラリの下位互換性は,ライブラリの複数バージョンを使用することにより維持できます。 各シェアード・ライブラリはバージョン識別子によってマークされます。 ライブラリの新しいバージョンを省略時のディレクトリにインストールし,そのライブラリのバージョン識別子と一致する名前を持つ,古いバイナリ互換バージョンをサブディレクトリにインストールします。
たとえば,libc.so
に非互換の変更が行われた場合,新しいライブラリ (/usr/shlib/libc.so
) は,変更前のライブラリのインスタンス (/usr/shlib/osf.1/libc.so
) を伴っていなければなりません。
この例では,libc.so
の古いバイナリ互換バージョンは
osf.1
バージョンです。
変更が適用されると,新しい
libc.so
が新しいバージョン識別子を使用して作成されます。
シェアード・ライブラリのバージョン識別子は,そのライブラリを使用するプログラムのシェアード・ライブラリ従属レコードにリストされているため,ローダはアプリケーションで必要とするシェアード・ライブラリのバージョンを識別できます (4.11.6 項を参照)。
前述の例では,バイナリ非互換変更の前に
libc.so
を使用して作成したプログラムは,ライブラリの
osf.1
バージョンを必要とします。
/usr/shlib/libc.so
のバージョンはプログラムのシェアード・ライブラリ従属レコードにリストされているバージョン識別子と一致しないため,ローダは
/usr/shlib/osf.1
で一致するバージョンを探します。
非互換変更後に作成されるアプリケーションは,/usr/shlib/libc.so
を使用して作成され,ライブラリの新しいバージョンに依存します。
ローダは,別のバイナリ非互換変更が起こるまで,/usr/shlib/libc.so
を使用してこれらのアプリケーションをロードします。
表 4-1
は,シェアード・ライブラリのバージョン管理を有効にするためのリンカ・オプションの説明です。
表 4-1: シェアード・ライブラリのバージョンを管理するリンカ・オプション
オプション | 説明 |
-set_version
version-string |
シェアード・ライブラリに関連するバージョン識別子を設定する。 文字列 version-string は,単一のバージョン識別子またはコロンで区切ったバージョン識別子のリストである。 バージョン識別子の名前に関する制約はないが,UNIX のディレクトリ命名規則に従うのがよい。 シェアード・ライブラリがこのオプションを使用して作成されると,そのライブラリに対して作成されたプログラムは,指定されたバージョン,またはバージョン識別子のリストが指定されている場合には,そのリストに指定されている一番右端のバージョンの従属が記録される。 シェアード・ライブラリがバージョン識別子のリストを使用して作成されると,実行時ローダは,リストされている任意のバージョンに関するシェアード・ライブラリの従属を持つプログラムはどれでも実行できるようにする。 このオプションはシェアード・ライブラリの作成時のみに ( |
-exact_version |
このオプションは,動的実行可能ファイル ( |
odump
コマンドを使用すると,シェアード・ライブラリのバージョン文字列を確認することができます。
この文字列は,そのライブラリを作成した
ld
コマンドの
-set_version version-string
オプションで設定されるものです。
次のように入力します。
% odump -D library-name
IVERSION
フィールドに表示される値が,ライブラリの作成時に指定されたバージョン文字列です。
-set_version
オプションを指定しないでライブラリを作成した場合には,IVERSION
フィールドは表示されません。
このようなシェアード・ライブラリは,バージョン識別子として
_null
が指定された場合と同様に扱われます。
ld
が共用オブジェクトをリンクするときには,各シェアード・ライブラリの従属のバージョンを記録します。
コロンで区切ったリストの一番右端にあるバージョン識別子だけが記録されます。
任意の共用されている実行可能ファイルまたはライブラリのこれらの依存関係を確認するには,次のコマンドを使用します。
% odump -Dl shared-object-name
Tru64 UNIX では,シェアード・ライブラリのメジャー・バージョンとマイナー・バージョンを区別しません。
メジャー・バージョンはシェアード・ライブラリの非互換バージョンを区別するために使用されます。
マイナー・バージョンは通常,異なっているけれど互換性のあるライブラリ・バージョンを区別します。 マイナー・バージョンは,修正版用の識別子として使用されたり,下位互換のシェアード・ライブラリの使用を制限するために使用されます。
Tru64 UNIX のシェアード・ライブラリは,コロンで区切ったバージョン識別子のリストを使用することによって,通常はマイナー・バージョンを使用して行うバージョン管理機能を提供しています。
次に示すライブラリ・バージョンのシーケンスは,シェアード・ライブラリの互換性に影響を与えることなく,修正版用の識別子をシェアード・ライブラリのバージョン・リストに追加する方法を示しています。
シェアード・ライブラリ | バージョン |
libminor.so |
3.0 |
libminor.so |
3.1:3.0 |
libminor.so |
3.2:3.1:3.0 |
libminor.so
の新しいリリースはそれぞれ,バージョン・リストの先頭に新しい識別子を追加します。
この新しい識別子により,前のバージョンと最新バージョンを区別します。
libminor.so
の任意のバージョンとリンクする実行可能ファイルは,必須バージョンとして
3.0
を記録するため,互換ライブラリと区別がつきません。
追加のバージョン識別子だけが情報を提供します。
次のライブラリ・バージョンのシーケンスは,下位互換のシェアード・ライブラリの使用を制限する方法を示しています。
シェアード・ライブラリ | バージョン |
libminor2.so |
3.0 |
libminor2.so |
3.0:3.1 |
libminor2.so |
3.0:3.1:3.2 |
この例では,libminor2.so
の古いバージョンとリンクされるプログラムは,ライブラリの新しいバージョンで実行できますが,libminor2.so
の新しいバージョンとリンクされるプログラムは,古いバージョンでは実行できません。
4.11.4 シェアード・ライブラリの完全バージョンと部分バージョン
シェアード・ライブラリのバイナリ互換バージョンは,2 つの方法でインプリメントできます。 つまり,完全で独立したオブジェクトと,直接または間接的に完全で独立したオブジェクトに依存する部分オブジェクトとしてインプリメントできます。 完全に重複したシェアード・ライブラリは部分バージョンより多くのディスク・スペースを必要としますが,従属処理が単純で,使用するスワップ領域が少なくてすみます。 使用するディスク・スペースを削減できることが,シェアード・ライブラリの部分バージョンの唯一の利点です。
部分シェアード・ライブラリは,ライブラリの新しいバージョンでバイナリ非互換変更が行われる前にリンクされたアプリケーションに対して,下位互換を維持するために最低限必要なモジュールのサブセットを含みます。 これは,ライブラリ・モジュールの完全なセットを持つ同じライブラリの 1 つまたは複数の以前のバージョンとリンクしています。 このように,シェアード・ライブラリの複数のバージョンを連結すると,シェアード・ライブラリの任意のインスタンスは,通常はライブラリでエクスポートされるすべてのシンボルを,間接的に提供できるようになります。
たとえば,libxyz.so
のバージョン
osf.1
には,モジュール
x.o
,y.o
,z.o
が含まれています。
これは,次のコマンドを使用して作成され,インストールされました。
% ld -shared -o libxyz.so -set_version osf.1 \ x.o y.o z.o -lc % mv libxyz.so /usr/shlib/libxyz.so
将来,libxyz.so
において,モジュール
z.o
だけに影響を及ぼす非互換変更が必要になった場合には,osf.2
と呼ばれる新しいバージョンと,osf.1
と呼ばれる部分バージョンは,次のように作成することができます。
% ld -shared -o libxyz.so -set_version osf.2 x.o \ y.o new_z.o -lc % mv libxyz.so /usr/shlib/libxyz.so % ld -shared -o libxyz.so -set_version osf.1 z.o \ -lxyz -lc % mv libxyz.so /usr/shlib/osf.1/libxyz.so
4.11.5 シェアード・ライブラリの複数バージョンとのリンク
一般に,アプリケーションはシェアード・ライブラリの最新バージョンとリンクされます。
しかし,アプリケーションやシェアード・ライブラリを,シェアード・ライブラリの古い,バイナリ互換バージョンとリンクしたいことがあります。
そのような場合には,ld
コマンドの
-L
オプションを使用して,アプリケーションが使用するシェアード・ライブラリの古いバージョンを識別します。
アプリケーションを同じシェアード・ライブラリの複数のバージョンとリンクすると,リンカは警告メッセージを出します。 アプリケーションまたはシェアード・ライブラリの複数バージョンの従属は,実行のためにロードされるまで通知されない場合があります。
ld
コマンドは,省略時には,リンクするように指示されているライブラリのみの複数バージョンの従属を調べます。
考えられるすべての複数バージョンの従属を識別するには,ld
コマンドの
-transitive_link
オプションを使用して,リンクのステップに間接シェアード・ライブラリの従属を含めます。
アプリケーションを部分シェアード・ライブラリとリンクする場合には,部分シェアード・ライブラリのインプリメンテーションで生じた複数バージョンの従属を注意深く区別しなければなりません。 リンカは,受け入れ可能な複数バージョンの従属と受け入れ不可能なものの区別ができない場合,複数バージョンの警告メッセージを表示します。
場合によっては,実行時にシェアード・ライブラリの複数バージョンを使用しないアプリケーションに対し,リンク時に複数バージョンの従属が通知されます。
図 4-2
および次の表に示すライブラリと従属について考えてみてください。
図 4-2: シェアード・ライブラリの複数バージョンとのリンク
ライブラリ | バージョン | 従属 | 依存するバージョン |
libA.so |
v1 | libcommon.so |
v1 |
libB.so |
v2 | libcommon.so |
v2 |
libcommon.so |
v1:v2 |
-- |
-- |
この表で,libA.so
は,右端のバージョン識別子に v1 を含む
libcommon.so
にリンクされています。
libB.so
は,右端のバージョン識別子に v2 を含む
libcommon.so
にリンクされています。
この表に示す
libcommon.so
は,バージョン文字列に v1 と v2 の両方を含むため,libA.so
および
libB.so
の従属はともに
libcommon.so
という 1 つのインスタンスで満足できます。
a.out
がリンクされるとき,libA.so
および
libB.so
だけがリンクのコマンド行に指定されます。
ただし,ld
は
libA.so
および
libB.so
の従属を調べ,libcommon.so
の考えられる複数バージョンの従属を承認して警告メッセージを出します。
a.out
を
libcommon.so
にもリンクすると,この間違った警告を回避することができます。
4.11.6 ロード時におけるバージョン・チェック
ローダは,シェアード・ライブラリがサポートするバージョン・リストと,シェアード・ライブラリの従属レコードに記録されているバージョンの照合を実行します。
共用オブジェクトがリンクのコマンド行で
-exact_match
オプションを使用してリンクされている場合には,ローダはシェアード・ライブラリのタイムスタンプとチェックサムも従属レコードに記録されている値と比較します。
ローダはバージョン照合検査が失敗したシェアード・ライブラリでマップした後,RPATH
,LD_LIBRARY_PATH
,または省略時の探索パスに指定されている他のディレクトリの探索を続行して,シェアード・ライブラリの正しいバージョンを見つけようとします。
これらのディレクトリをすべて探索しても見つからない場合には,ローダは,従属に記録されているバージョン文字列を,最初に一致しないライブラリのバージョンが見つかったディレクトリ・パスに付加して,一致するバージョンを探索しようとします。
たとえば,シェアード・ライブラリ
libfoo.so
はバージョン
osf.2
を使用してディレクトリ
/usr/local/lib
にロードされますが,このライブラリの従属はバージョン
osf.1
を必要とします。
ローダは組み合わせた次のパスを使用して,ライブラリの正しいバージョンの探索を試みます。
/usr/local/lib/osf.1/libfoo.so
この組み合わせたパスでも正しいライブラリの探索に失敗した場合,あるいは,省略時の探索ディレクトリまたはユーザ指定の探索ディレクトリでもライブラリのバージョンが探索できなかった場合,ローダは,必須のバージョン文字列を標準システムのシェアード・ライブラリのディレクトリ (/usr/shlib
) に付加して,最後の探索を試みます。
前述の例を使用すると,libfoo.so
を探索するローダの最後の試みでは,次のような組み合わせたパスを使用します。
/usr/shlib/osf.1/libfoo.so
ローダがシェアード・ライブラリの一致するバージョンを見つけられない場合,ロードは打ち切られて,探索できなかった従属とシェアード・ライブラリのバージョンを示す詳細なエラー・メッセージが報告されます。
setuid
関数を使用してインストールされなかったプログラムのバージョン・チェックは,次の C シェルの例に示すように,ローダの環境変数を設定することにより,禁止することができます。
% setenv _RLD_ARGS -ignore_all_versions
次の例に示すように,特定のシェアード・ライブラリに対するバージョン・チェックを禁止することも可能です。
% setenv _RLD_ARGS -ignore_version libDXm.so
リンカと同様,ローダもシェアード・ライブラリの複数バージョンの有効な使用と無効な使用を区別しなければなりません。
複数バージョンの有効な使用は,同じライブラリの他のバージョンに依存する部分シェアード・ライブラリがロードされるときに起こります。 場合によっては,これらの部分シェアード・ライブラリは異なる部分シェアード・ライブラリに依存しているため,ローダは誤ったエラーを報告しないように注意深く解釈しなければならないような,複雑な従属図式になることがあります。
複数バージョンの無効な使用は,2 つの異なる共用オブジェクトが別の共用オブジェクトの異なるバージョンに依存する場合に起こります。 部分シェアード・ライブラリの連鎖はこのルールの例外です。 バージョン・チェックのため,連結の最初の部分シェアード・ライブラリは,その連結内の他のメンバの同じ従属を上書きする 1 組の従属を定義します。
次の図は,複数従属エラーになる共用オブジェクトの従属図式を示しています。 バージョン識別子はカッコ内に示しています。
図 4-3
では,アプリケーションは,基本システムの非互換バージョンで作成された 2 つのレイヤード・プロダクトを使用しています。
図 4-3: 共用オブジェクト間の無効な複数バージョンの従属: 例 1
図 4-4
では,アプリケーションは,基本システムの非互換バージョンを使用して作成されたレイヤード・プロダクトとリンクしています。
図 4-4: 共用オブジェクト間の無効な複数バージョンの従属: 例 2
図 4-5
では,アプリケーションは,部分シェアード・ライブラリとしてインプリメントされた下位互換の不完全なライブラリとリンクしています。
図 4-5: 共用オブジェクト間の無効な複数バージョンの従属: 例 3
次の例は,シェアード・ライブラリの複数バージョンの有効な使用を示しています。
図 4-6
では,アプリケーションは,部分シェアード・ライブラリとしてインプリメントされた下位互換のライブラリを使用しています。
図 4-6: シェアード・ライブラリの複数バージョンの有効な使用: 例 1
図 4-7
では,アプリケーションは,2 つの下位互換のライブラリを使用しています。
そのうちの 1 つは,もう一方に依存しています。
図 4-7: シェアード・ライブラリの複数バージョンの有効な使用: 例 2
ローダによるシンボル解決の方法には,即時割り当ておよび遅延割り当ての 2 つの方法があります。 即時割り当ての場合は,実行可能プログラムあるいはシェアード・ライブラリがロードされるときにシンボルが解決されます。 遅延割り当ての場合,テキスト・シンボルは実行時に解決されます。 遅延テキスト・シンボルは,プログラムで最初に参照される際に解決されます。
省略時の設定では,プログラムは遅延割り当てでロードされます。
LD_BIND_NOW
環境変数にヌル以外の値に設定すると,その後のプログラムの実行は即時割り当てによって行われます。
即時割り当ては,解決できないシンボルを識別するのに便利です。 遅延割り当ての場合は,その部分のコードが実行されるまで,解決できないシンボルを検出できません。
また,即時割り当てを使用するとシンボル解決のオーバヘッドを軽減できます。
4.13 シェアード・ライブラリの制約事項
シェアード・ライブラリを使用する際には,次の制約事項を満たす必要があります。
シェアード・ライブラリには未定義シンボルがあってはならない。
シェアード・ライブラリが参照するシンボルを定義している他のシェアード・ライブラリと,そのシェアード・ライブラリを明示的にリンクしなければなりません。
実行可能プログラム内のシンボルを参照するシェアード・ライブラリのような場合には,未定義のシンボル参照を回避することは困難です。 シェアード・ライブラリにおける未解決の外部シンボルの処理方法については,4.2.4 項を参照してください。
アセンブラ・ファイルや,レベル
03
で最適化されている古いオブジェクト・ファイルおよび C ファイルは,シェアード・ライブラリでは動作しない場合がある。
シェアード・ライブラリでは,Tru64 UNIX C コンパイラを使用し,最適化レベル
02
以下でコンパイルされた C モジュールが使用可能です。
シェアード・ライブラリにリンクされている実行プログラムは,最適化レベル
03
以下でコンパイルすることができます。
setuid
または
setgid
サブルーチンを使用してインストールしているプログラムでは,ライブラリ探索を制御するさまざまな環境変数 (LD_LIBRARY_PATH
,_RLD_ARGS
,_RLD_LIST
,_RLD_ROOT
など) を使用せず,システム・インストールによるライブラリ (/usr/shlib
にあるライブラリ) だけを使用する。
これにより,プログラムのセキュリティに対する潜在的な危険が取り除かれます。
これは,実行時ローダ (/sbin/loader
) により行われます。