本文に進む 日本−日本語
日本HPホーム 製品とサービス お客様サポート/ ダウンロード ソリューション ご購入の方法
≫ お問い合わせ
詳細検索オプション
日本HPホーム
HP-UX リンカー & ライブラリー ユーザーズ・ガイド: HP 9000 コンピュータ > 第5章 ライブラリの作成と使用

共有ライブラリの作成

≫ 

テクニカル ドキュメント

PDF版
フィードバック
ここから本文が始まります

 ≫ 目次

 ≫ 用語集

 ≫ 索引

共有ライブラリを作成するには、次の 2 つのステップを実行する必要があります。

  1. 「位置独立コード (PIC) を作成する」。そのためには、 +z を指定してコンパイルします。

  2. 「ld を使用して共有ライブラリを作成する」。そのためには、 -b を指定してリンクします。

次の例は、共有ライブラリlibunits.sl の作成に使用するコマンドを示しています。

$ cc -Aa -c +z length.c volume.c mass.c
$ ld -b -o libunits.sl length.o volume.o mass.o

共有ライブラリの関連情報:

位置独立コード (PIC) を作成する

共有ライブラリを作成する最初のステップは、位置独立コード (PIC) を含むオブジェクト・ファイルを作成することです。PIC オブジェクト・ファイルを作成するには、次の 2 つの方法があります。

  • 後述の+z または+Z コンパイラ・オプションを使用して、ソース・ファイルをコンパイルします。

  • 第 7 章「位置独立コード」で説明するように、適切なアドレッシング・モードを使用するアセンブリ言語プログラムを記述します。

32 ビット・モードでは、+z および+Z オプションを指定すると、コンパイラによって PIC オブジェクト・ファイルが生成されます。64 ビット・モードでは、+Z オプションがデフォルトです。

+z の使用例

英語と計量単位の換算を実行する C 関数が、length.c にいくつか格納されている場合を考えます。C コンパイラを使用し、これらのルーチンをコンパイルして PIC オブジェクト・ファイルを作成するには、次のコマンドを使用します。

$ cc -Aa -c +z length.c      The +z option creates PIC.

次に、「ld を使用して共有ライブラリを作成する」の項で後述するように、他の PIC オブジェクト・ファイルとリンクして共有ライブラリを作成できます。

+z と +Z の比較

32 ビット・モードでは、+z および+Z オプションの機能は、本質的には同じです。通常は、+z を指定してコンパイルします。ただし、共有ライブラリごとに参照されるシンボル数が事前に決定されている限度を超える場合などは、代わりに+Z オプションを指定して再コンパイルしなければなりません。このような場合は、リンカによってエラー・メッセージが表示され、+Z を指定してライブラリを再コンパイルするように求められます。

64 ビット・モードでは、+Z がデフォルトであり、コンパイラではこのオプションを無視して PIC コードが生成されます。

コンパイラによる +z および +Z のサポート

32 ビット・モードでは、C, C++, FORTRAN, および Pascal のコンパイラで、+z および+Z オプションがサポートされます。

64 ビット・モードでは、+Z は C および C++ コンパイラのデフォルトです。

ld を使用して共有ライブラリを作成する

1 つ以上の PIC オブジェクト・ファイルから共有ライブラリを作成するには、-b オプションを指定してリンカld を使用します。デフォルトでは、ld によってライブラリがa.out と命名されます。この名称は、-o オプションを使用して変更できます。

例えば、3 つの C ソース・ファイルに、長さ、容積、および重量の単位換算を実行するルーチンが含まれている場合を考えます。ファイル名は、それぞれlength.c, volume.c, およびmass.c です。この 3 つのソース・ファイルから共有ライブラリを作成するには、最初に+z オプションを使用して 3 つのファイルをすべてコンパイルし、次に作成された.o ファイルをld で結合します。次の例は、共有ライブラリlibunits.sl の作成に使用するコマンドを示しています。

$ cc -Aa -c +z length.c volume.c mass.c
length.c:
volume.c:
mass.c:
$ ld -b -o libunits.sl length.o volume.o mass.o

ライブラリが作成されたら、そのライブラリを使用するユーザー全員に読み取りおよび実行パーミッションが設定されているかどうかを確認します。例えば、次のchmod コマンドを使用すると、libunits.sl ライブラリのユーザー全員に読み取り/実行パーミッションを与えることができます。

$ chmod +r+x libunits.sl

これで、このライブラリは他のプログラムとリンクできるようになりました。例えば、libunits.sl からのルーチンを呼び出す C のプログラムconvert.c がある場合は、次のようにcc コマンドを使用してコンパイルし、リンクできます。

$ cc -Aa convert.c libunits.sl

32 ビット・モードでは、ライブラリの絶対パス名が実行可能ファイルに格納されるため、実行可能ファイルが作成されたら、ライブラリを移動しないでください (64 ビット・モードでは、./libunit.sl が実行可能ファイルに格納されます)。詳細は、「共有ライブラリの位置」の項を参照してください。

共有ライブラリをプログラムとリンクする方法についての詳細は、第 3 章「リンカのタスク」を参照してください。

注記: C++ のオブジェクト・ファイルをリンクして実行可能ファイルまたは共有ライブラリを作成する場合は、CC コマンドを使用してリンクする必要があります。これにより、c++patch が実行され、ローカルでない静的コンストラクタとデストラクタがチェーン化されます。ld を使用すると、ライブラリや実行可能ファイルが正常に機能せず、エラー・メッセージが表示されない場合があります。詳細は、『『HP C++ Programmer's Guide』』を参照してください。

共有ライブラリの依存関係

共有ライブラリの作成時には、ld コマンド行で共有ライブラリを追加指定できます。この場合に作成された共有ライブラリを、指定したライブラリとの依存関係を持つといい、これらのライブラリを依存ライブラリまたはサポート・ライブラリと呼びます。依存関係を持つライブラリをロードすると、そのすべての依存ライブラリもロードされます。例えば、次のコマンドを使用してライブラリlibdep.sl を作成する場合を考えます。

$ ld -b -o libdep.sl mod1.o mod2.o -lcurses -lcustom

その後、shl_load を使用して明示的に、または実行開始時にダイナミック・ローダーによって暗黙的にlibdep.sl をロードするプログラムによって、依存ライブラリlibcurses.sl およびlibcustom.sl も自動的にロードされます。

その他に、一部の共有ライブラリ開発者にとって重要な問題が 2 つあります。

  • 依存関係を持つ共有ライブラリがロードされる場合に、依存ライブラリはどのようにロードされるか。

  • すべての依存ライブラリが、すでにロード済みの他のライブラリとの相対位置に格納されるかどうか。つまり、ダイナミック・ローダーによって使用されるプロセスの共有ライブラリ検索リストに格納されるかどうか。

ライブラリのロード順 (ロード・グラフ)

依存関係を持つ共有ライブラリのロード時には、ダイナミック・ローダーによってロード・グラフが生成され、依存ライブラリのロード順が決定されます。

例えば、次のld コマンドを使用して 3 つのライブラリ ― libQ, libD, およびlibP を作成する場合を考えます。依存ライブラリとして指定できるのは既存のライブラリだけなので、ライブラリの生成順序が重要になります。

$ ld -b -o libQ.sl modq.o -lB
$ ld -b -o libD.sl modd.o -lQ -lB
$ ld -b -o libP.sl modp.o -lA -lD -lQ

この 3 つのライブラリの依存関係リストは、次のようになります。

  • libQlibB に依存します。

  • libDlibQlibB に依存します。

  • libPlibA, libD, およびlibQ に依存します。

      +-->libA.sl
      |
libP.sl-->libD------+
      |    |        |
      |    v        v
      +-->libB.sl-->libQ.sl
32 ビット・モードの場合

32 ビット・モードの場合、ローダーでは次のアルゴリズムが使用されます。

ライブラリがアクセスされていない場合、ライブラリにアクセス・マークを設定する。ライブラリに依存関係リストがある場合、リストを逆順で検索するライブラリをロード・リストの先頭に配置する。

次の例は、libP のロード時にロード・グラフから実行されるステップを示しています。

  1. P をマークして、Q に移動します。

  2. Q をマークして、B に移動します。

  3. B をマークして、Bロードします。

  4. Qロードします。

  5. D に移動します。

  6. D をマークして、B に移動します。

  7. B はすでにマークされているので、B をスキップしてQ に移動します。

  8. Q はすでにマークされているので、Q をスキップします。

  9. Dロードする。

  10. A をマークして、Aロードします。

  11. Pロードする。

その結果、ロード・グラフは次のようになります。

  libP-->libA-->libD--> libQ--> libB

64 ビット・モードの場合

64 ビット・モードの場合、ダイナミック・ローダーでは次のアルゴリズムが使用されます。

ライブラリがアクセスされていない場合、ライブラリにアクセス・マークを設定するライブラリをリストの最後に追加する。ライブラリに依存関係リストがある場合、リストを順に検索する。

次の例は、libP のロード時にロード・グラフから実行されるステップを示しています。

  1. P をマークして、Pロードします。

  2. P に移動します。

  3. A をマークして、Aロードします。

  4. D をマークして、Dロードします。

  5. Q をマークして、Qロードします。

  6. D に移動します。

  7. D はすでにマークされているので、D をスキップします。

  8. Q に移動します。

  9. Q はすでにマークされているので、Q をスキップします。

  10. Q に移動します。

  11. Q はすでにマークされているので、Q をスキップします。

  12. B に移動します。

  13. B をマークして、Bロードします。

  14. B に移動します。

  15. B はすでにマークされているので、B をスキップします。

この結果、ロード・グラフは、次のようになります。

  libP-->libA-->libD--> libQ--> libB

ロードされたライブラリを検索リストに追加する

ロード・グラフが作成されたら、ライブラリを共有ライブラリ検索リストに追加して、そのシンボルをプログラムにバインドする必要があります。最初のライブラリが暗黙的にロードされたライブラリ (つまり、プログラムの実行開始時に自動的にロードされるライブラリ) であれば、ロード・グラフ内のライブラリはライブラリ検索リストに追加されます。例えば、libP が暗黙的にロードされる場合、ライブラリ検索リストは次のようになります。

  <current search list>--> libP--> libA--> libD--> libQ--> libB

これと同じ動作は、shl_load を使用し、BIND_FIRST 修飾キーを指定しないで明示的にロードされるライブラリにも発生します (詳細は、「BIND_FIRST 修飾キー」の項を参照)。BIND_FIRSTshl_load の呼び出しで指定されている場合、ロード・グラフ内のライブラリは既存の検索リストのに挿入されます。例えば、この呼び出しによってlibP がロードされる場合を考えます。

lib_handle = shl_load("libP.sl", BIND_IMMEDIATE | BIND_FIRST, 0);

この場合、挿入後のライブラリ検索リストは次のようになります。

   libP--> libA--> libD--> libQ--> libB--><current search list>

共有ライブラリを更新する

ld コマンドでは、共有ライブラリ内のオブジェクト・モジュールを置換または削除することはできません。従って、共有ライブラリを更新するには、ライブラリに組み込むすべてのオブジェクト・ファイルとライブラリを再リンクする必要があります。例えば、不正な結果を示した length.c (前項の例) 内の一部のルーチンを修正する場合を考えます。libunits.sl ライブラリを更新してこれらの変更を組み込むには、次の一連のコマンドを使用します。

$ cc -Aa -c +z length.c
$ ld -b -o libunits.sl length.o volume.o mass.o

このライブラリを使用するプログラムでは、ルーチンの新バージョンが使用されるようになります。つまり、この共有ライブラリを使用するプログラムを再リンクする必要はありません。これは、ライブラリ内のルーチンが実行時にプログラムに結び付けられるからです。

これは、共有ライブラリがアーカイブ・ライブラリより優れている点の 1 つです。アーカイブ・ライブラリを変更する場合は、それを使用するプログラムを再リンクする必要があります。共有ライブラリの場合は、そのライブラリを再作成するだけですみます。

共有ライブラリに対する互換性のない変更

共有ライブラリに互換性のない変更を加える場合は、ライブラリのバージョン化を使用して新旧両方のルーチンを提供し、旧ルーチンとリンクされたプログラムを引き続き機能させることができます。共有ライブラリのバージョン管理についての詳細は、「共有ライブラリによるバージョン管理」の項を参照してください。

共有ライブラリの位置

共有ライブラリは、アーカイブ・ライブラリと同じ位置に配置できます (「アーカイブ・ライブラリの位置」の項を参照)。また、通常、この位置は、アプリケーション・ライブラリの場合は /usr/local/lib および/usr/contrib/lib (32 ビット・モード) または/usr/local/lib/pa20_64 および/usr/contrib/lib/pa20_64 (64 ビット・モード) であり、システム・ライブラリの場合は /usr/lib (32 ビット・モード) または/usr/lib/pa20_64 (64 ビット・モード) です。ただし、これはあくまでも推奨位置です。

HP-UX 9.0 リリースまでは、共有ライブラリを移動すると、そのライブラリとリンクされていたプログラムはライブラリをロードできなくなりました。従って、9.0 までは、ライブラリを別のディレクトリに移動した場合は、そのライブラリを使用するすべてのアプリケーションを再リンクする必要がありました。

HP-UX 9.0 リリースからは、プログラムを使用して、実行時に必要なライブラリをディレクトリ・リスト内で検索できます。従って、アプリケーションをリンクした後も、ライブラリを移動できます。実行時にライブラリを検索するには、プログラムに検索対象のディレクトリを認識させる必要があります。このディレクトリ検索情報を指定するには、次の 2 つの方法があります。

  • リンカ・オプション+b path_list を使用して、ディレクトリ・パス・リストをプログラムに格納します。

  • +s を使用してプログラムをリンクします。これにより、プログラムでは、 SHLIB_PATH 環境変数によって定義されたパス・リストを実行時に使用できるようになります。

64 ビット・プログラムの場合は、LD_LIBRARY_PATH 環境変数を使用することもできます。この場合、+s オプションはデフォルトで使用可能になります。

これらのオプションの使用法についての詳細は、「+b を使用してリンクした後にライブラリを移動する」の項と「+s と SHLIB_PATH を使用してリンク後にライブラリを移動する」の項を参照してください。

共有ライブラリのパフォーマンスを改善する

この項では、共有ライブラリの実行時パフォーマンスを改善する方法について説明します。後述の方法を使用しても、共有ライブラリを使用するプログラムのパフォーマンスに満足できない場合は、代わりにアーカイブ・ライブラリとリンクして、パフォーマンスが改善されるかどうかを調べてください。ただし、通常は、アーカイブ・ライブラリを使用しても、共有ライブラリの使用時に比べてあまりパフォーマンスは改善されません。

LD_PRELOAD 環境変数によって共有ライブラリをロードする

注記: LD_PRELOAD 機能は、passwd などの seteuid/setegid プログラムに対して無効です。詳細は、ld(1) を参照してください。この機能は、完全バインド静的実行可能プログラムでは使用できません。

LD_PRELOAD 環境変数によって、プログラム起動時に追加の共有ライブラリをロードできます。LD_PRELOAD には、ダイナミック・ローダーが解釈可能な共有ライブラリをコロン区切りかスペース区切りで指定します。ダイナミック・ローダーdld.sl は、プログラムがその他の依存ライブラリの前にLD_PRELOAD の共有ライブラリと明示的にリンクされているかのように、指定した共有ライブラリをロードします。

起動時に、LD_PRELOAD 環境変数に指定したライブラリがある場合、ダイナミック・ローダーは暗黙的に 1 つ以上のライブラリをロードします。実行可能プログラム・ビルド時のこのライブラリのロード順とシンボル解決順は、リンク行の最初のライブラリとして明示的にリンクされるときと同じになります。例えば、次のようなリンク行で生成される実行可能プログラムがあるとします。

$ ld ... lib2.sl lib3.sl lib4.sl

LD_PRELOAD="/var/tmp/lib1.sl" を指定した場合は、ダイナミック・ローダーは、次のように、リンク行の最初のライブラリとして指定したときと同じロード順とシンボル解決順でlib1.sl をロードします。

$ ld ... /var/tmp/lib1.sl lib2.sl lib3.sl lib4.sl

一般的に、/bin/sh を使用したコマンド行では、LD_PRELOAD は次のように定義されます。

$ LD_PRELOAD=mysl.sl application

ダイナミック・ローダーでは、application$PATH に従って検索されます。mysl.sl は、SHLIB_PATHLD_LIBRARY_PATH、または文字式のパス (有効な場合) に従って検索されます。

LD_PRELOAD 環境変数を使用してスレッド・ローカル記憶領域を格納する共有ライブラリをロードすると、ライブラリを動的にロードするときに発生する、次に示すエラーを避けることができます。

 /usr/lib/dld.sl: Can't shl_load() a library containing Thread Local Storage: /usr/lib/libpthread.1

ロード順とシンボル解決順は、PA64 プログラムと PA32 プログラムでは異なります。ダイナミック・ローダーは、PA32 モードでは深さ優先の検索順序を使用し、PA64 モードでは幅優先の検索順序を使用するためです。

ダイナミック・ローダーは、リンク行で+noenvvar を使用した場合もLD_PRELOAD 環境変数を使用します。これによって、+compat リンクの場合もLD_PRELOAD は確実に有効になります。LD_PRELOAD 変数は、setuid および setgid プログラムの場合を除いて常に有効です。

注記: 共有ライブラリとアーカイブ・ライブラリが混在するアプリケーションで使用されており、特に、共有ライブラリとアプリケーションの両方が aC++ でビルドされているかlibc を使用している場合にLD_PRELOAD を使用すると、コア・ダンプが発生することがあります。

LD_PRELOAD 環境変数の一部として複数のライブラリを指定できます。LD_LIBRARY_PATH の場合と同様に、ライブラリはスペースまたはコロンで区切ってください (マルチバイト・サポートはLD_PRELOAD ライブラリ・リストの構文解析機能の一部として提供されていません)。LD_PRELOAD ライブラリは、絶対パスまたは相対パスで指定できます。LD_PRELOAD ライブラリはライブラリ名だけで構成することもできます。この場合は、ダイナミック・ローダーはLD_LIBRARY_PATHSHLIB_PATH、または文字式のパス (有効な場合) のディレクトリ・パス・リストを使用してライブラリを検索します。

ダイナミック・ローダーは、LD_PRELOAD で指定したライブラリが見つからない場合にエラーや警告メッセージを発行しません。ただし、LD_PRELOAD ライブラリの依存ライブラリが見つからない場合は、LD_PRELOAD ライブラリがリンク行で指定されている場合と同様のエラー・メッセージを発行します。

LD_PRELOAD の例

A64 リンカ・ツールセットは、シンボル解決のために依存ライブラリを幅優先順で検索します。PA32 リンカ・ツールセットは、深さ優先順で検索します。従って、ライブラリのロード順とシンボル解決順は、使用モードによって異なります。a.out に次の依存ライブラリがある場合を想定してください。

               a.out
               /   \
          libA.sl  libB.sl
                /  \
          libC.sl  libD.sl

つまり、a.out は次のコマンドによってビルドされたものになります。

$ cc +DA2.0W -c +z ?.c$ ld -b -o libB.sl b.o$ ld -b -o libC.sl c.o$ ld -b -o libD.sl d.o$ ld -b -o libA.sl a.o -L. -lC -lD$ cc foo.c -L. -lA -lB

64 ビットでの動作

64 ビット・モードでは、次のように、ldd(1) によって共有ライブラリがロードされる順番が表示されます (最初に兄弟ライブラリ、次にその依存ライプラリ)。

$ ldd a.out
    libA.sl =>       ./libA.sl
    libB.sl =>       ./libB.sl
    libc.2 =>        /usr/lib/pa20_64/libc.2
    libC.sl =>      ./libC.sl
    libD.sl =>      ./libD.sl
    libdl.1 =>        /usr/lib/pa20_64/libdl.1

従って、LD_PRELOAD を設定しないと、64 ビット・モードでのユーザー・ライブラリのシンボル解決順は、次のようになります。

a.out- -> libA.sl --> libB.sl --> libC.sl -->libD.sl

例 (i):

LD_PRELOAD="./libB.sl"

64 ビット・モードでは、シンボル解決順は次のようになります。

$ export LD_PRELOAD="./libB.sl"
$ ldd a.out
    ./libB.sl =>    ./libB.sl
    ./libB.sl =>    ./libB.sl
    libA.sl =>      ./libA.sl
    libB.sl =>      ./libB.sl
    libc.2 =>       /usr/lib/pa20_64/libc.2
    libC.sl =>      ./libC.sl
    libD.sl =>      ./libD.sl
    libdl.1 =>      /usr/lib/pa20_64/libdl.1

a.out --> libD.sl --> libA.sl --> libB.sl --> libC.sl

例 (ii):

LD_PRELOAD = "./libD.sl"

64 ビット・モードでは、シンボル解決順は次のようになります。

$ export LD_PRELOAD="./libD.sl"
$ ldd a.out
    ./libD.sl =>    ./libD.sl
    ./libD.sl =>    ./libD.sl
    libA.sl =>      ./libA.sl
    libB.sl =>      ./libB.sl
    libc.2 =>       /usr/lib/pa20_64/libc.2
    libC.sl =>      ./libC.sl
    libD.sl =>      ./libD.sl
    libdl.1 =>       /usr/lib/pa20_64/libdl.1

a.out --> libD.sl --> libA.sl --> libB.sl- -> libC.sl

同じシンボルがlibA.sl およびlibD.sl で定義されている場合は、64 ビット・リンカ・ツールセットはlibD.sl に定義されたシンボルを使用します。これは、LD_PRELOAD="./libD.sl"の場合は、libD.sllibA.sl の前にロードされて検索されるからです。

32 ビットでの動作

LD_PRELOAD を設定していないと、32 モードでは、ダイナミック・ローダーは次のようなロード・グラフが生成されます。

  1. B をマークし、B をロードします。

  2. A をマークして D に移動し、D をロードします。

  3. C をロードします。

  4. A をロードします。

このロード・グラフによって、シンボル解決順は、32 ビット・モードでは逆順に生成されます。

a.out --> libA.sl --> libC.sl --> libD.sl --> libB.sl

例 (i):

LD_PRELOAD="./libB.sl"

32 ビット・モードでは、a.out の依存ライブラリは、libB.sl, libA.sl, libB.sl として処理されます。2 番目のlibB.sl は、重複するため、ダイナミック・ローダーによって無視されます。従って、a.out の有効依存ライブラリはlibB.sl, libA.sl として処理されます。ロード・グラフは次の通りです。

  1. A をマークして D に移動し、D をロードします。

  2. C をロードします。

  3. A をロードします。

  4. B をロードします。

シンボル解決順は次の通りです。

a.out --> libB.sl--> libA.sl --> libC.sl --> libD.sl

例 (ii):

LD_PRELOAD = "./libD.sl"

32 ビット・モードでは、ロード・グラフは次の通りです。

  1. B をマークし、B をロードします。

  2. A をマークして D に移動し、D をロードします。

  3. C をロードします。

  4. A をロードします。

シンボル解決順は次の通りです。

a.out --> libA.sl--> libC.sl --> libD.sl --> libB.sl

共有ライブラリに対してプロファイル・ベースの最適化を使用する

共有ライブラリに対してプロファイル・ベースの最適化を実行し、そのパフォーマンスを改善できます。詳細は、「プロファイル・ベースの最適化」の項を参照してください。

必要なシンボルのみをエクスポートする

通常は、すべてのグローバル変数とプロシージャの定義が共有ライブラリからエクスポートされます。つまり、共有ライブラリ内で定義されたプロシージャや変数は、このライブラリを使用するコードに対して可視になります。また、コンパイラでは、エクスポートされる「内部」シンボルが生成されます。共有ライブラリでは、そのライブラリを使用するコードが必要とするより、はるかに多数のシンボルがエクスポートされることに驚くことがあります。このように余分なシンボルがエクスポートされると、ライブラリのシンボル・テーブルが大きくなり、パフォーマンスまで低下することがあります (ダイナミック・ローダーが必要以上に多数のシンボルを検索せざるを得ないため)。

共有ライブラリのパフォーマンスを改善する方法の 1 つとして、必要なシンボルだけをライブラリからエクスポートすることが考えられます。どのシンボルがエクスポートされるかを制御するには、ld コマンドで+e または-h オプションを指定します。+e オプションを指定すると、リンカでは、このオプションで指定したシンボルのみがエクスポートされます。-h オプションを指定すると、指定したシンボルは非表示になります (この 2 つのオプションの使用法についての詳細は、「-h を使用してシンボルを非表示にする」の項と「+e を使用してシンボルをエクスポートする」の項を参照)。

例えば、プロシージャinit_prog およびquit_prog と、グローバル変数prog_state を定義する共有ライブラリを作成した場合を考えます。これらのシンボルのみをライブラリからエクスポートさせるには、ライブラリの作成時に次のオプションを指定します。

+e init_prog +e quit_prog +e prog_state

多数のシンボルをエクスポートする必要がある場合は、-c file オプションを使用すると便利な場合があります。これにより、file でリンカ・オプションを指定できます。例えば、前述のオプションをファイルexport_opts 内で次のように指定できます。

+e init_prog
+e quit_prog
+e prog_state

その後に、リンカ・コマンド行で次のオプションを指定します。

-c export_opts

(-c オプションについての詳細は、「-c を使用してファイル内のリンカ・オプションを渡す」の項を参照)。

頻繁に呼び出されるルーチンをまとめて格納する

リンカでは、共有ライブラリの作成時に、PIC オブジェクト・モジュールがリンカ・コマンド行で指定した順序でライブラリに格納されます。モジュールを指定する順序は、パフォーマンスに大きく影響することがあります。例えば、次のモジュールを考えます。

a.o 

c.o 内のルーチンを頻繁に呼び出し、そのルーチンは c.o によって頻繁に呼び出されます。

b.o 

大型モジュールですが、呼び出し頻度の低いエラー・ルーチンしか入っていません。

c.o 

a.o によって頻繁に呼び出され、a.o 内のルーチンを頻繁に呼び出すルーチンが入っています。

次のコマンド行を使用して共有ライブラリを作成すると、モジュールはライブラリにアルファベット順に挿入されます。

$ ld -b -o libabc.sl *.o

この順序付けには、a.o およびc.o 内のルーチンがライブラリ内で離れた位置に配置されるという問題が発生する可能性があります。モジュールa.oc.o を共有ライブラリにまとめて挿入し、その後にモジュールb.o を挿入すると、仮想メモリのパフォーマンスを改善できる可能性があります。そのためには、次のコマンドを使用します。

$ ld -b -o libabc.sl a.o c.o b.o

オブジェクト・ファイルについて最も適切な指定順序を決定する方法の 1 つは、オブジェクト・モジュールのプロファイル・データを収集することです。頻繁に呼び出されるモジュールは、コマンド行でグループ化する必要があります。

もう 1 つの方法は、『lorder』(1) コマンドおよび『tsort』(1) コマンドを使用することです。この 2 つのコマンドを一連のオブジェクト・モジュールに対して一括して使用すると、リンカが一度でモジュール間の参照を解決できるように、モジュールの順序が決定されます。この方法には、相互に呼び出すモジュールを、そうでないモジュールより近い位置に配置するという副作用もあります。例えば、次のオブジェクト・モジュールを定義した場合を考えます。

モジュール 

モジュール内のルーチンの呼び出し元

a.o 

x.o y.o

b.o 

x.o y.o

d.o 

なし

e.o 

なし

x.o 

d.o

y.o 

d.o

この場合、次のコマンドを使用すると、単一パスのリンク順が決定されます。

$ lorder ?.o | tsort       Pipe lorder's output to tsort.a.o
b.o
e.o
x.o
y.o
d.o

これで、d.o は呼び出し元のx.oy.o に近い位置に配置されたことに注目してください。ただし、a.o およびb.oどのモジュールからも呼び出されないモジュールe.o によってx.o およびy.o から区切られているため、まだ最善の情報とはいえません。実際の最適順は、次のようになります。

a.o b.o x.o y.o d.o e.o

この場合も、lorder およびtsort を使用する方法は完全ではありませんが、モジュールを最適の順序で設定する手がかりにはなります。どのような順序にすれば最大のパフォーマンスが得られるかは、さまざまな順序を試して調べる必要があります。

共有ライブラリの書き込み禁止

共有ライブラリに書き込みパーミッションが設定されないようにすると、さらにパフォーマンスを改善できる場合があります。複数の書き込み可能ライブラリを使用するプログラムの場合は、ロード時間がわずかに長くなることがあります。次のchmod コマンドを使用すると、共有ライブラリにはロード時のパフォーマンスが最大になるように、正しいパーミッションが与えられます。

$ chmod 555 libname

cc コマンドの +ESlit オプションを使用する

通常、C コンパイラでは、データ・スペースに定数データが配置されます。この種のデータが共有ライブラリ内で使用される場合は、データは一定であり変化しないにもかかわらず、各プロセスはデータの専用コピーを取得します。このため、パフォーマンスが低下することがあります。

この問題を回避するには、C コンパイラの+ESlit オプションを使用します。これにより、定数データはデータ・スペースの代わりに$LIT$ テキスト・スペース (または、64 ビット・モードの場合は.text テキスト・セグメント) に配置されます。また、データの単一コピーが、ライブラリを使用する全プロセス間で共有されます。

注記: このオプションを使用するには、プログラムで定数文字列とデータに書き込まないようにする必要があります。また、ポインタは読み取り専用の$TEXT$ スペースに配置され、再配置できないため、初期化済みのポインタが埋め込まれている構造体は機能しません。この場合、リンカではエラー・メッセージ "Invalid loader fixup needed" が出力されます。

フィルタ処理された共有ライブラリを使用する (32 ビット・モードのみ)

フィルタ処理された共有ライブラリを使用すると、開発者は共有ライブラリの遅延ローディング (バインド時ロード) により、共有ライブラリのメモリ・フットプリントを減らすことができます。フィルタ処理によって、大きなライブラリを 1 つのフィルタ・ライブラリと複数の処理系ライブラリに分割します。

フィルタ・ライブラリ 

シンボルをエクスポートするライブラリ。ただし、シンボルの実装または記憶領域は含まない。

処理系ライブラリ 

シンボルの実装または記憶領域を含むライブラリ。

ユーザーはフィルタ・ライブラリとのリンクを行いますが、データと関数の実定義は処理系ライブラリにあります。実行時には、実際に使用される処理系ライブラリのみがロードされます。フィルタ処理されたライブラリはネストできます。処理系ライブラリがフィルタ処理されたライブラリとなり、他の処理系ライブラリを含むこともできます。

リンカで、-b オプションと 32 ビットの +filter オプションを併用すると、この機構を使用できます。

$ld -b...+filter shared_library_pathname

フィルタ処理された共有ライブラリを、独立した処理系ライブラリに分割すると、アプリケーションがライブラリの一部のみを使用する場合にメモリの総消費量が大幅に減少します。このメモリ消費の減少は、共有ライブラリに、一部のアプリケーションのみが使用している大量の静的データが含まれている場合に最も有意となります。

共有ライブラリを処理系ライブラリに分割する際に、各処理系ライブラリを独立したライブラリにすることが重要です。処理系ライブラリ間に依存があると、メモリ消費を減らすことはできません。フィルタ処理された共有ライブラリは、アプリケーションに対して単一の構成要素として認識されるため、互換性があります。分割された処理系ライブラリの数に関係なく、フィルタ・ライブラリだけにリンクする必要があります。

フィルタ処理された共有ライブラリを生成する

次の手順で、フィルタ処理された共有ライブラリを生成します。

  1. フィルタ・セットを構成する個々の処理系ライブラリを生成します。処理系ライブラリには、コードとデータ・シンボルの実定義が含まれます。 -b リンカ・オプションを使用して、通常の共有ライブラリと同様にこれらのライブラリを生成します。

  2. +filter リンカ・オプションを使用して、フィルタ・ライブラリを生成します。フィルタ・セットの一部となる各処理系ライブラリを、 +filter オプションを使用して指定します。例えば、次のように入力します。

    $ld -b impl1.o -o libimpl1.sl 
    $ld -b impl2.o -o libimpl2.sl 
    $ld -b +filter libimpl1.sl +filter libimpl2.sl\ 
    -o libfilt.sl

    これにより、フィルタ処理された共有ライブラリ libfilt.sl と 2 つの処理系ライブラリ libimpl1.sl および libimpl2.sl が生成されます。

  3. どの処理系ライブラリがフィルタ・セットに含まれるのか確認するには、odump を使用してフィルタ・ライブラリの内容をリストにします。

    $odump -filtertable libfilt.sl

    libfilt.sl のフィルタ処理された共有ライブラリのリスト・テーブルは次の通りです。

                Index  String Table Offset  Name
     
                0                    11  ./libimpl1.sl
                1                     24  ./libimpl2.sl
注記: +filter オプションは、-b オプションを使用した場合にのみ効果があります。

+filter オプションは、-L リンカ・オプションまたは LPATH 環境変数と併用できます。例えば、次のように使用します。

$ld -b -L. +filter impl1 +filter impl2 -o libfilt.sl

または

$export LPATH=. $ld -b -L. +filter impl1 +filter impl2 -o libfilt.sl

フィルタ処理された共有ライブラリ自身に、「遅延ロードする」必要のないシンボル (通常、例えば、libc の場合のexit(2) など、常に使用されるシンボル) の定義を含めることもできます。例えば、次のように入力します。

$ld -b a.o b.o...+filter libimpl1.sl\ +filter libimpl2.sl -o libfilt.sl

フィルタ処理された共有ライブラリにリンクするアプリケーション・プログラムを生成する

アプリケーションがフィルタ処理された共有ライブラリを使用する際に、アプリケーションはフィルタ・ライブラリにのみリンクし、各処理系ライブラリにはリンクしません。リンク時に、処理系ライブラリはフィルタにリンクするアプリケーションに対して透過になります。実行時に、ダイナミック・ローダーはシンボル定義を検索します。フィルタ・ライブラリのシンボルとの一致が見つかった場合は、必要なシンボルの実定義を含む適切な処理系ライブラリがロードされます。次の例は、フィルタ処理されたライブラリでのアプリケーション・プログラムのコンパイルを示しています。

$cc prog.c libfilt.sl -o prog

ユーザーが作成したプログラムを、処理系ライブラリや上位のフィルタ処理されたライブラリと明示的にリンクさせることができますが、フィルタ・ライブラリ機能の利点は失われます。処理系ライブラリはプログラムの実行時にロードされます。

フィルタ処理された共有ライブラリの実行時の動作

処理系ライブラリで定義されるシンボルは、アプリケーションに直接使用できません。上位のフィルタ・ライブラリを介してのみアクセスできます。shl_* および dl* API のユーザーは、フィルタ・ライブラリ用のハンドルを使用してシンボルを照会または定義する必要があります。既存のアプリケーションと互換性を確保するために、このような処理を行います。

イニシャライザ。

処理系ライブラリの一部として宣言された場合、イニシャライザは処理系ライブラリのロード時にのみ (シンボル・バインディングの一部として) 呼び出されます。イニシャライザのハンドル引き数は、フィルタ・ライブラリを指定し、shl_findsym(3X) または dlsym(3C) で使用できるようになっています。例えば、イニシャライザで次のように宣言します。

#include <dlfcn.h>
void initializer(void *handle, int loading)
{
        ...
        ptr = dlsym(handle, "symname");
        ...
}
ダイナミック・パス参照。

フィルタ処理された共有ライブラリを +b リンカ・オプションを使用して生成する最中に、途中にあるパスを指定する場合、ダイナミック・ローダーはロードする処理系ライブラリの検索時に、このダイナミック・パスを使用しようとします。例えば、次のようになります。

$ld -b impl1.o -o libimpl1.sl
$ld -b +b /path/to/implementation/libs +filter\ ./libimpl1.sl -o libfilt.sl
$chatr +b enable libfilt.sl
$cc prog.c libfilt.sl -o prog
$mv libimpl1.sl /path/to/implementation/libs
$./prog
スレッド非公開データ (TLS)。

「遅延ロード」の場合でも、処理系ライブラリで TLS を使用できます。フィルタ・ライブラリのロード時に、ロード前でも処理系ライブラリの TLS 用にスペースが予約されます。スペースが予約されるため、フィルタ処理は TLS の消費を減らすのに適した方法ではありません。

フィルタ処理された共有ライブラリを保守する

フィルタ処理された共有ライブラリを保守するには、次の変更を行ったときにライブラリを再生成する必要があります。

  • 処理系ライブラリからエクスポートされたシンボルのセットにシンボルを追加、またはセットからシンボルを削除。

  • シンボルの種類の変更。例えば、初期化されていないデータ・シンボル (BSS シンボル) が初期化されたデータ・シンボルに変更されたとき (またはその反対)、またはテキスト・シンボルがデータ・シンボルに変更されたとき。

  • 処理系ライブラリの TLS (スレッド・ローカル記憶領域) の大きさの変更。新規に追加した TLS シンボルが処理系ライブラリからエクスポートできなくても、フィルタ・ライブラリを再生成して正しい量の TLS スペースが予約されるようにする必要がある。

    TLS の大きさを調べるには、次のコマンドを使用します。

    $odump -sldlheader shared_library_pathname\ | fgrep tdsize

印刷用画面へ
プライバシー 本サイト利用時の合意事項
© Hewlett-Packard Development Company, L.P.