ライブラリを更新しながらも、そのライブラリの古くて後方互換性のないバージョンを使いたいというプログラムを、引き続きサポートすることができる
特定のプログラムを実行するとき、特定のライブラリ、もしくはライブラリ内の特定の関数でさえもオーバーライドすることができる
加えて、ライブラリ要求時にコンパイラが使用する名前というものもあります (「linker name」と呼ぼうと思います)。 この名前は、単純に一切のバージョン番号を取り除いた soname です。
共有ライブラリはファイルシステム内のどこかに配置しなければなりません。 ほとんどのオープンソースソフトウェアは、GNU 規約に従う傾向があります。 詳細は info:standards#Directory_Variables にある info ファイルドキュメントを参照してください。 GNU 規約は、ソースコードを配布するとき、デフォルト設定時にはライブラリを全て /usr/local/lib にインストールするよう推奨しています (コマンドを全て /usr/local/bin に配置するようにとも推奨しています)。 また、これらのデフォルト設定をオーバーライドしたり、インストールルーチンを呼び出したりする際の約束ごとも定義しています。
ファイルシステム階層規約 (Filesystem Hierarchy Standard; FHS) は、ディストリビューションにおいて何をどこにインストールすべきかを説明しています (http://www.pathname.com/fhs/ を参照してください)。FHS によると、ほとんどのライブラリは /usr/lib にインストールし、起動時に必要とされるライブラリは /lib に、システムの一部ではないライブラリは /usr/local/lib にインストールするのが良い、ということになります。
実際には、これら二つの規約間に矛盾はありません。 GNU 規約は、ソースコード開発者のためのデフォルト設定を推奨しているのであり、一方で FHS は、ディストリビュータ (通常、システムパッケージ管理システムによりソースコードのデフォルト設定を選択的にオーバーライドする人々) のためのデフォルト設定を推奨しているのです。 実際にこれはうまく機能しています。あなたがダウンロードした「最新の」(おそらくバグだらけの!) ソースコードは、自動的に自分自身を「ローカルな」ディレクトリ (/usr/local) にインストールします。 そしてコードが成熟してきたら、ディストリビューション用の標準的な位置にコードを配置するため、パッケージ管理ツールでデフォルト設定を簡単にオーバーライドできます。 あなたのライブラリが、ライブラリ経由でしか呼び出されることのないプログラムを呼び出しているのならば、それらのプログラムを /usr/local/libexec (あるディストリビューションでは /usr/libexec になります) に配置するべきです。 厄介なのは、Red Hat から派生したシステムがデフォルト設定では /usr/local/lib をライブラリ検索対象に含めていないということです。 /etc/ld.so.conf に関する下記の議論を参照してください。 他の標準的なライブラリ配置場所としては、X Window System 用の /usr/X11R6/lib があります。/lib/security は PAM モジュール用に使われますが、通常、PAM モジュール群は動的ライブラリ (これもあとで説明します) としてロードされるので注意してください。
様々な環境変数により、共有ライブラリのロード処理を制御できます。 ロード処理をオーバーライドするための環境変数が存在するのです。
LD_LIBRARY_PATH は開発やテストには便利ですが、一般ユーザに日常的に使用させようとして、インストール処理で変更すべきではありません。 この理由については、http://www.visi.com/~barr/ldpath.html の「Why LD_LIBRARY_PATH is bad」(なぜ LD_LIBRARY_PATH はいけないのか) を参照してください。 とは言うものの、LD_LIBRARY_PATH は、開発やテスト、そして、他の方法では回避できない問題に対処するためには、やはり便利です。 LD_LIBRARY_PATH 環境変数を設定したくない場合、Linux では、プログラムローダを直接起動して引数を与えることもできます。 例えば、次のようにすると、指定の実行ファイルが、環境変数 LD_LIBRARY_PATH の内容ではなく、与えられた PATH を使用して実行されます。
/lib/ld-linux.so.2 --library-path PATH EXECUTABLE |
引数を与えずに ld-linux.so を実行すると、使い方についてのヘルプが表示されます。 しかし、もう一度言いますが、普段はこれを使わないようにしてください。 これらの機能は全てデバッグのためにあるのです。
GNU C ローダのもう一つの便利な環境変数は LD_DEBUG です。 これにより、dl*() 関数群が、実行中の処理に関する情報を大量に出力するようになります。 例えば、次のコマンド
export LD_DEBUG=files command_to_run |
gcc -shared -Wl,-soname,your_soname \ -o library_name file_list library_list |
gcc -fPIC -g -c -Wall a.c gcc -fPIC -g -c -Wall b.c gcc -shared -Wl,-soname,libmystuff.so.1 \ -o libmystuff.so.1.0.1 a.o b.o -lc |
-Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH) |
ldconfig -n directory_with_shared_libraries |
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH my_program |
#!/bin/sh export LD_LIBRARY_PATH=/usr/local/my_lib:$LD_LIBRARY_PATH exec /usr/bin/my_program.orig "$@" |
ldd(1) を使えば、プログラムによって使用されている共有ライブラリのリストを見ることができます。 例えば、次のようにタイプすれば、ls によって使用される共有ライブラリを確認できます。
ldd /bin/ls |
C++ (および、テンプレートやディスパッチされるメソッドをコンパイル時に組み込むという方法でサポートするその他の言語) では、状況はより複雑になります。 上記に述べたことが全て当てはまる上、さらに多くの注意点があります。 幾つかの情報が、コンパイルされるコード内に「隠蔽された状態で」組み込まれるという点にその原因があるのですが、このために、C++ が通常どのように実装されているかを知らない人には理解しにくい依存問題が、引き起こされてしまうのです。 正確に言えば、これは「新しい」問題ではありません。単に、コンパイル済みの C++ コードが、人によっては驚くかもしれない動作をして問題を引き起こす、というだけの話です。 次のリストは、バイナリ互換を維持するために C++ 内でやってはいけない項目のリスト (おそらく完全ではありませんが) であり、Troll Tech 社テクニカル FAQ で公開されているものです。
仮想関数の再実装を追加する (古いバイナリが元の実装を呼び出すのが安全ではない場合)。 コンパイラは SuperClass::virtualFunction() 呼出しをコンパイル時に評価するため (リンク時ではない)。
仮想メンバ関数を追加または削除する。 これにより全サブクラスの仮想関数テーブルのサイズとレイアウトが変更されてしまうため。
インラインメンバ関数経由でアクセス可能なデータメンバを移動させたり、データメンバの型を変更する。
クラス階層を変更する。ただし、リーフ (訳注:下位クラスを持たないクラス) の新規追加を除く。
プライベートデータを追加、または削除する。 これにより全サブクラスのサイズとレイアウトが変更されてしまうため。
public もしくは protected メンバ関数がインライン関数でない場合に、それらを削除する。
public もしくは protected メンバ関数をインライン化する。
インライン関数の挙動を変更する。ただし、古いバージョンが機能し続ける場合を除く。
可搬性を持たせたいプログラム内のメンバ関数のアクセス権 (すなわち、public, protected または private) を変更する。 アクセス権情報を関数名に組み入れるコンパイラもあるため。
この長いリストのことを考えると (訳注:=こんな長いリストの全項目を守り続けることは無理だろうから)、特に C++ ライブラリの開発者は、バイナリ互換性を維持できない更新を頻繁に実施することを計画しておく必要があります。 幸いにして、Unix 系システム (Linux を含みます) では、一つのライブラリの複数のバージョンを同時にロードすることができるので、ディスクスペースを消費することにはなりますが、古いライブラリを必要とする「古い」プログラムを引き続き実行することが可能です。