この章では、ライブラリの構築方法を説明します。
ライブラリには 2 つの利点があります。まず、ライブラリを使えば、コードをいくつかのアプリケーションで共有できます。共有するコードがある場合は、そのコードを含むライブラリを作成し、コードを必要とするアプリケーションとリンクできます。次に、ライブラリを使えば、非常に大きなアプリケーションの複雑さを軽減できます。アプリケーションの中の、比較的独立した部分をライブラリとして構築および保守することで、プログラマはほかの部分の作業により専念できるようになるためです。
ライブラリの構築とは、.o ファイルを作成し (コードを -c オプションでコンパイルし)、これらの .o ファイルを CC コマンドでライブラリに結合することです。ライブラリには、静的 (アーカイブ) ライブラリと動的 (共有) ライブラリがあります。
静的 (アーカイブ) ライブラリの場合は、ライブラリのオブジェクトがリンク時にプログラムの実行可能ファイルにリンクされます。アプリケーションにとって必要な .o ファイルだけがライブラリから実行可能ファイルにリンクされます。静的 (アーカイブ) ライブラリの名前には、通常、接尾辞 .a が付きます。
動的 (共有) ライブラリの場合は、 ライブラリのオブジェクトはプログラムの実行可能ファイルにリンクされません。 その代わりに、プログラムがこのライブラリに依存することをリンカーが実行可能ファイルに記録します。プログラムが実行されるとき、システムは、プログラムに必要な動的ライブラリを読み込みます。同じ動的ライブラリを使用する 2 つのプログラムが同時に実行されると、ライブラリはこれらのプログラムによって共有されます。動的 (共有) ライブラリの名前には、接尾辞として .so が付きます。
共有ライブラリを動的にリンクすることは、アーカイブライブラリを静的にリンクすることに比べていくつかの利点があります。
実行可能ファイルのサイズが小さくなる
実行時にコードのかなりの部分をプログラム間で共有できるため、メモリーの使用量が少なくなる
アプリケーションをリンクし直さずに、実行時にライブラリを置き換えることができる (これは、プログラムの再リンクおよび再配布の必要なしに、Solaris オペレーティングシステムの多くの改良をプログラムで利用できる主要機構である)
ただし、動的ライブラリには短所もあります。
実行時のリンクに時間がかかる
動的ライブラリを使用するプログラムを配布する場合には、それらのライブラリも同時に配布しなければならないことがある
共有ライブラリを異なる場所に移動すると、システムがライブラリを見つけられず、プログラムを実行できないことがある (この問題は、環境変数 LD_LIBRARY_PATH で解決できる)
静的 (アーカイブ) ライブラリを構築する仕組みは、実行可能ファイルを構築することに似ています。一連のオブジェクト (.o) ファイルは、CC で -xar オプションを使うことで 1 つのライブラリに結合できます。
静的 (アーカイブ) ライブラリを構築する場合は、ar コマンドを直接使用せずに CC -xar を使用してください。C++ 言語では一般に、従来の .o ファイルに収容できる情報より多くの情報 (特に、テンプレートインスタンス) をコンパイラが持たなければなりません。-xar オプションを使用すると、テンプレートインスタンスを含め、すべての必要な情報がライブラリに組み込まれます。make ではどのテンプレートファイルが実際に作成され、参照されているのかがわからないため、通常のプログラミング環境でこのようにすることは困難です。CC -xar を指定しないと、参照に必要なテンプレートインスタンスがライブラリに組み込まれないことがあります。次に例を示します。
% CC -c foo.cc # main を含むファイルをコンパイルし、テンプレートオブジェクトを作成する % CC -xar -o foo.a foo.o # すべてのオブジェクトを 1 つのライブラリに集める |
-xar フラグによって、CC が静的 (アーカイブ) ライブラリを作成します。-o 命令は、新しく作成するライブラリの名前を指定するために必要です。コンパイラは、コマンド行のオブジェクトファイルを調べ、これらのオブジェクトファイルと、テンプレートリポジトリで認識されているオブジェクトファイルとを相互参照します。 そして、ユーザーのオブジェクトファイルに必要なテンプレートを (本体のオブジェクトファイルとともに) アーカイブに追加します。
既存のアーカイブのみを作成または更新するには、-xar フラグを使用します。このフラグをアーカイブの保守に使用しないでください。-xar オプションは ar -cr を実行するのと同じことです。
1 つの .o ファイルには 1 つの関数を入れることをお勧めします。アーカイブとリンクする場合、特定の .o ファイルのシンボルが必要になると、.o ファイル全体がアーカイブからアプリケーションにリンクされます。.o ファイルに 1 つの関数を入れておけば、アプリケーションにとって必要なシンボルだけがアーカイブからリンクされます。
動的 (共有) ライブラリの構築方法は、コマンド行に -xar の代わりに -G を指定することを除けば、 静的 (アーカイブ) ライブラリの場合と同じです。
ld は直接使用しないでください。静的ライブラリの場合と同じように、CC コマンドを使用すると、必要なすべてのテンプレートインスタンスがテンプレートリポジトリからライブラリに組み込まれます (テンプレートを使用している場合)。アプリケーションにリンクされている動的ライブラリでは、すべての静的コンストラクタは main() が実行される前に呼び出され、すべての静的デストラクタは main() が終了したあとに呼び出されます。dlopen() で共有ライブラリを開いた場合、すべての静的コンストラクタは dlopen() で実行され、すべての静的デストラクタは dlclose() で実行されます。
動的ライブラリを構築するには、必ず CC に -G を使用します。ld (リンクエディタ) または cc (C コンパイラ) を使用して動的ライブラリを構築すると、例外が機能しない場合があり、ライブラリに定義されている大域変数が初期化されません。
動的 (共有) ライブラリを構築するには、 CC の -Kpic や -KPIC オプションで各オブジェクトをコンパイルして、再配置可能なオブジェクトファイルを作成する必要があります。次に、これらの再配置可能オブジェクトファイルから動的ライブラリを構築します。原因不明のリンクエラーがいくつも出る場合は、-Kpic や -KPIC でコンパイルしていないオブジェクトがある可能性があります。
ソースファイル lsrc1.cc と lsrc2.cc から作成するオブジェクトファイルから C++ 動的ライブラリ libfoo.so を構築するには、次のようにします。
% CC -G -o libfoo.so -h libfoo.so -Kpic lsrc1.cc lsrc2.cc |
-G オプションは、動的ライブラリの構築を指定しています。-o オプションは、ライブラリのファイル名を指定しています。-h オプションは、共有ライブラリの名前を指定しています。-Kpic オプションは、オブジェクトファイルが位置に依存しないことを指定しています。
CC -G コマンドは -l オプションを ld に渡しません。共有ライブラリにほかの共有ライブラリとの依存関係を持たせる場合、 必要な -l オプションをコマンド行に指定する必要があります。たとえば、共有ライブラリに libCrun.so との依存関係を持たせる場合、-lCrunをコマンド行に指定する必要があります。
C++ コードが含まれているプログラムでは、-Bsymbolic を使用せずに、リンカーのマップファイルを使用してください。-Bsymbolic を使用すると、異なるモジュール内の参照が、本来 1 つの大域オブジェクトの複数の異なる複製に結合されてしまう可能性があります。
例外メカニズムは、アドレスの比較によって機能します。オブジェクトの複製が 2 つある場合は、アドレスが同一であると評価されず、本来一意のアドレスを比較することで機能する例外メカニズムで問題が発生することがあります。
dlopen() を使用して共有ライブラリを開いている場合は、 例外メカニズムが機能するには RTLD_GLOBAL を使用する必要があります。
ある組織の内部でしか使用しないライブラリを構築する場合には、一般的な使用には適さないオプションを使ってライブラリを構築することもできます。具体的には、ライブラリはシステムのアプリケーションバイナリインタフェース (ABI) に準拠していなくてもかまいません。たとえば、ライブラリを -fast オプションでコンパイルして、特定のアーキテクチャー上でのパフォーマンスを向上させることができます。同じように、-xregs=float オプションでコンパイルして、パフォーマンスを向上させることもできます。
ほかの組織からも使用できるライブラリを構築する場合は、ライブラリの管理やプラットフォームの汎用性などの問題が重要になります。ライブラリを公開にするかどうかを決める簡単な基準は、アプリケーションのプログラマがライブラリを簡単に再コンパイルできるかどうかということです。公開ライブラリは、システムの ABI に準拠して構築しなければなりません。一般に、これはプロセッサ固有のオプションを使用しないということを意味します。たとえば、-fast や -xtarget は使用しないようにします。
SPARC ABI では、いくつかのレジスタがアプリケーション専用で使用されます。V7 と V8 では、これらのレジスタは %g2、%g3、%g4 です。V9 では、これらのレジスタは %g2 と %g3 です。ほとんどのコンパイルはアプリケーション用に行われるので、C++ コンパイラは、デフォルトでこれらのレジスタを一時レジスタに使用して、プログラムのパフォーマンスを向上しようとします。しかし、公開ライブラリでこれらのレジスタを使用することは、SPARC ABI に適合しないことになります。公開ライブラリを構築するときには、アプリケーションレジスタを使用しないようにするために、すべてのオブジェクトを -xregs=no%appl オプションで コンパイルしてください。
C++ で作成されたライブラリを C プログラムから使用できるようにするには、C API を作成する必要があります 。そのためには、エクスポートされるすべての関数を extern "C" にします。ただし、これができるのは大域関数だけで、メンバー関数にはできません。
C インタフェースライブラリで C++ の実行時サポートを必要とし、しかも cc とリンクしている場合は、C インタフェースライブラリを使用するときにアプリケーションも libC (互換モード) または libCrun (標準モード) にリンクする必要があります。C インタフェースライブラリで C++ 実行時サポートが不要の場合は、libC や libCrun とリンクする必要はありません。リンク手順は、アーカイブされたライブラリと共有ライブラリでは異なります。
アーカイブされた C インタフェースライブラリを提供するときは、ライブラリの使用方法を説明する必要があります。
C インタフェースライブラリが CC を標準モード (デフォルト) で構築している場合は、C インタフェースライブラリを使用するときに -lCrun を cc コマンド行に追加します。
C インタフェースライブラリが CC を互換モード (-compat) で構築している場合は、C インタフェースライブラリを使用するときに -lC を cc コマンド行に追加します。
共有 C インタフェースライブラリを提供するときは、ライブラリの構築時に libC または libCrun と依存関係をつくる必要があります。共有ライブラリの依存関係が正しければ、ライブラリを使用するときに -lC または -lCrun をコマンド行に追加する必要はありません。
C インタフェースライブラリを互換モード (-compat) で構築している場合は、-lC インタフェースライブラリを使用するときに -lC を cc コマンド行に追加します。ライブラリ構築時に -lC を CC コマンド行に追加します。
C インタフェースライブラリを標準モード (デフォルト) で構築している場合は、C インタフェースライブラリを使用するときに -lCrun を cc ではなく CC コマンド行に追加します。
さらに、C++ 実行時ライブラリにもまったく依存しないようにするには、 ライブラリソースに対して次のコーディング規則を適用する必要があります。
どのような形式の new も delete も使用しない (独自の new または delete を定義する場合は除く)
例外を使用しない
実行時の型識別機構 (RunTime Type Information、RTTI) を使用しない
C プログラムから dlopen で C++ 共有ライブラリを開く場合は、共有ライブラリが適切な C++ 実行時ライブラリ (-compat=4 の場合は libC.so.5、-compat=5 の場合は libCrun.so.1) に依存していることを確認してください。
そのためには、共有ライブラリを構築するときに、-compat=4 の場合は -lC、-compat=5 の場合は -lCrun を次のようにコマンド行に追加します。次に例を示します。
example% CC -G -compat=4... -lC example% CC -G -compat=5... -lCrun |
共有ライブラリが例外を使用している場合には、ライブラリが C++ 共有ライブラリに依存していないと、C プログラムが正しく動作しないことがあります。
共有ライブラリを dlopen() で開く場合は、RTLD_GLOBAL を使用しないと、例外は機能しません。