Program Library HOWTO

David A. Wheeler

      dwheeler(a)dwheeler.com
    

川崎貴彦 - 日本語訳

      takahiko(a)hakubi.co.jp
    

日本語訳：2004 年 9 月 17 日 (原文：2003 年 4 月 28 日、バージョン
1.20)

この HOWTO はプログラマ向けであり、Linux におけるプログラムライブラリの
作成方法と使用方法を説明するものです。「静的ライブラリ (static library)
」、「共有ライブラリ (shared library)」、「動的ライブラリ (dynamically
loaded library)」を対象とします。 (訳注：この訳文では、「dynamically
loaded library」のことを「動的ライブラリ」と呼ぶことにします。世の中で
は、shared library のことを指して動的ライブラリと表現する例もありますが
、それとは異なりますので注意してください。)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

Table of Contents
1. はじめに
2. 静的ライブラリ
3. 共有ライブラリ
   
    3.1. 約束ごと
    3.2. ライブラリはどのように使われるか
    3.3. 環境変数
    3.4. 共有ライブラリの作成
    3.5. 共有ライブラリのインストールと使用
    3.6. 互換性のないライブラリ
   
4. 動的ライブラリ
   
    4.1. dlopen()
    4.2. dlerror()
    4.3. dlsym()
    4.4. dlclose()
    4.5. 動的ライブラリの例
   
5. 雑録
   
    5.1. nm コマンド
    5.2. ライブラリのコンストラクタ関数およびデストラクタ関数
    5.3. 共有ライブラリはスクリプト化できる
    5.4. シンボルのバージョン付けとバージョンスクリプト
    5.5. GNU libtool
    5.6. スペースを節約するためにシンボルを削除する
    5.7. 極端に小さな実行可能ファイル
    5.8. C++ vs. C
    5.9. C++ 初期処理の高速化
    5.10. Linux Standard Base (LSB)
    5.11. ライブラリ群を統合して大きな共有ライブラリへ
   
6. 例
   
    6.1. libhello.c ファイル
    6.2. libhello.h ファイル
    6.3. demo_use.c ファイル
    6.4. script_static ファイル
    6.5. script_shared ファイル
    6.6. demo_dynamic.c ファイル
    6.7. script_dynamic ファイル
   
7. その他の情報源
8. 著作権とライセンス
9. 日本語訳について
   
    9.1. 日本語訳履歴
    9.2. 謝辞
   
1. はじめに

この HOWTO はプログラマ向けであり、GNU ツールセットを利用して、Linux 上
でプログラムライブラリを作成する方法、およびその使用方法を説明するもの
です。「プログラムライブラリ」とは、のちのちプログラムに組み込むための
コンパイル済みコード (およびデータ) を含む、単なるファイルのことです。
プログラムライブラリは、プログラムを、よりモジュール化され、より速く再
コンパイルでき、より簡単に更新できるものにします。プログラムライブラリ
は三つのタイプ、すなわち、静的ライブラリ (static library)、共有ライブラ
リ (shared library)、動的ライブラリ (dynamically loaded library)、に分
類することができます。

この文書は、最初に、静的ライブラリ (プログラムが実行される前にその実行
可能プログラムに組み込まれるライブラリ) について説明します。それから、
共有ライブラリ (プログラム実行時にロードされ、かつ複数のプログラム間で
共有されるライブラリ) について説明します。最後に、動的ライブラリ (プロ
グラム実行中の任意の時点でロードして使用することが可能なライブラリ) に
ついて説明します。動的ライブラリのライブラリフォーマットは、実際には別
の種類というわけではありません (静的ライブラリも共有ライブラリも動的ラ
イブラリとして使用することが可能です)。その代わりに、プログラマが動的ラ
イブラリを使用する方法に違いがあります。この HOWTO は、例を挙げている章
と、その他の情報源への参照を挙げている章で締めくくります。

ライブラリを開発しているほとんどの開発者は、共有ライブラリを作成すべき
です。というのは、ユーザが、そのライブラリを使用するアプリケーションと
は別々にライブラリを更新できるようになるからです。動的ライブラリは便利
ですが、使用するには少し手間がかかります。また、多くのプログラムでは、
動的ライブラリが提供する柔軟性を必要とはしません。これとは逆に、静的ラ
イブラリの場合、ライブラリを更新するのは非常に厄介です。そのため、日常
的に利用しようとしているならば、静的ライブラリはお勧めしません。ただし
、各ライブラリにはそれぞれの利点があります。利点については、各ライブラ
リごとの章でそれぞれ説明します。 C++ と動的ライブラリを使用する開発者は
、「C++ dlopen mini HOWTO <http://www.tldp.org/HOWTO/C++-dlopen/>」も参
照してください。

共有ライブラリを指して dynamically linked library (DLL) という用語を使
う人がいること、その DLL という用語を動的ライブラリとして使用される任意
のライブラリを指すために使う人がいること、また、どちらかの条件を満たす
ライブラリを指すために DLL という用語を使う人がいること、には注意したほ
うがよいです。いずれを指すにしても、この HOWTO では Linux における DLL
を扱います。

この HOWTO では、実行可能ファイルおよびライブラリ用の Executable and
Linking Format (ELF) フォーマット (昨今のほとんど全ての Linux ディスト
リビューションで使用されているフォーマット) についてのみ扱います。 GNU
gcc ツールセットは、実際には ELF 以外のライブラリフォーマットを扱うこと
ができます。特に、ほとんどの Linux ディストリビューションでは、旧式の
a.out フォーマットを今なお使用することが可能です。しかしながら、これら
のフォーマットはこの文書の対象外とします。

多くのシステムへと移植する必要のあるアプリケーションを作成しているなら
ば、ライブラリを構築してインストールするとき、Linux ツールを直接使用す
る代わりに、GNU libtool <http://www.gnu.org/software/libtool/
libtool.html> を使用することを考慮したほうがよいかもしれません。 GNU
libtool は、共有ライブラリ使用時の複雑さ (例えば、共有ライブラリの作成
やインストール等) を一貫性のある移植性の高いインターフェースで隠蔽する
、汎用的なライブラリサポートスクリプトです。 Linux 上では、GNU libtool
は、この HOWTO で説明されているツール群と約束ごとの上に構築されています
。動的ライブラリに対する移植性の高いインターフェースとして、様々なポー
タビリティラッパーを使用することができます。 GNU libtool には「libltdl
」と呼ばれるラッパーが含まれています。他の選択肢としては、移植性のある
方法でモジュールの動的ローディングをサポートする glib ライブラリ (glibc
と混同しないでください) を使用することもできます。 glib の詳細について
は http://developer.gnome.org/doc/API/glib/
glib-dynamic-loading-of-modules.html で学習することができます。再度述べ
ますが、Linux 上では、この機能は、この HOWTO 内で説明されている構成物を
利用することによって実装されています。もしもあなたが実際に Linux 上でコ
ードの開発またはデバッグをされているなら、おそらく、この HOWTO 内の情報
をこれから先必要とされることでしょう。

この HOWTO の一次配布場所は http://www.dwheeler.com/program-library/ で
あり (訳注：日本語訳は http://www.linux.or.jp/JF/JFdocs/
Program-Library-HOWTO/ にあります)、Linux Documentation Project (http:/
/www.linuxdoc.org/) に寄贈されています。著作権は David A. Wheeler にあ
り (Copyright (C) 2000)、General Public License (GPL) でライセンスされ
ています。詳細な情報については最後の章を読んでください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

2. 静的ライブラリ

静的ライブラリは、通常のオブジェクトファイルの単なる集合体です。慣習と
して、静的ライブラリは「.a」という拡張子を持ちます。この集合体は、ar
(archiver) プログラムを使用して作成されます。静的ライブラリは以前ほどに
は使われなくなっていますが、これは、共有ライブラリのほうが優れているた
めです (あとで説明します)。それでもまだ、静的ライブラリは折に触れて作成
され (はじめは歴史的な理由で存在していました)、また、説明するのも簡単で
す。

ユーザは、コードを再コンパイルする必要なく静的ライブラリをプログラムに
リンクすることができるので、再コンパイルにかかる時間を節約できます。た
だ、昨今の高速なコンパイラのことを考えると、再コンパイル時間は重要では
なくなってきているので、この理由付けは以前ほど有力ではありません。静的
ライブラリは、その開発者が、ライブラリをリンクすることをプログラマに許
可はしたいがライブラリのソースコードは渡したくはない、という場合にしば
しば役に立ちます (これはライブラリベンダにとっては好都合ですが、そのラ
イブラリを使おうとしているプログラマにとっては、どう考えても好都合とは
言えません)。論理的には、実行可能ファイルにリンクされる静的 ELF ライブ
ラリ内のコードは若干速く (1 〜 5%) 動作するはずですが、実際には、他のご
ちゃごちゃした要因のため、その通りになることは稀のようです。

静的ライブラリを作成する、もしくは既に存在する静的ライブラリにさらにオ
ブジェクトファイルを追加するためには、次のようなコマンドを使用してくだ
さい。

  ar rcs my_library.a file1.o file2.o                                  

このコマンド例では、静的ライブラリ my_library.a にオブジェクトファイル
file1.o と file2.o を追加しています。まだ my_library.a が存在していなけ
れば、作成します。静的ライブラリ作成に関する詳細な情報については ar(1)
を参照してください。

静的ライブラリを作成したら、それを使いたくなることでしょう。実行可能プ
ログラムを作成するときにコンパイルとリンク処理の一部として取り込むこと
で、静的ライブラリを使用できます。実行可能ファイルを作成するときに gcc
(1) を使っているならば、ライブラリを指定するのに -l オプションを使用で
きます。より詳しい情報については info:gcc を参照してください。

gcc を使うときにはパラメータの順番に気をつけてください。-l オプションは
リンカオプションなので、コンパイルするファイル名の「後に」置く必要があ
ります。これは、通常のオプション構文とはだいぶ異なります。-l オプション
をファイル名の前に置いた場合、リンクは完全に失敗し、不可解なエラーにな
ってしまうでしょう。

-l と -L オプションを使ってリンカ ld(1) を直接使用することもできます。
しかしながら、ld(1) のインターフェースは gcc(1) よりも変更されやすいの
で、ほとんどの場合は gcc(1) を使うほうがよいです。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3. 共有ライブラリ

共有ライブラリは、プログラム起動時にロードされるライブラリです。共有ラ
イブラリが適切にインストールされると、その後に起動される全てのプログラ
ムは、自動的にその新しい共有ライブラリを使うことになります。実際には、
これよりもはるかに柔軟で洗練されています。なぜなら、Linux における共有
ライブラリの実現方法のおかげで、次のことが可能となるからです。

 ・ ライブラリを更新しながらも、そのライブラリの古くて後方互換性のない
    バージョンを使いたいというプログラムを、引き続きサポートすることが
    できる
   
 ・ 特定のプログラムを実行するとき、特定のライブラリ、もしくはライブラ
    リ内の特定の関数でさえもオーバーライドすることができる
   
 ・ 既存のライブラリを使用してプログラムが動いている間にも、これら全て
    をおこなうことができる
   
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.1. 約束ごと

これらの望ましい特性すべてを共有ライブラリがサポートするためには、多く
の慣習と指針に従わなければなりません。ライブラリの名前、特に「soname」
と「real name」の違いについて (及びそれらがどのように相互作用するかにつ
いて) 学ばなければなりません。また、それらがファイルシステム内のどの場
所に置かれるべきであるかについても、知っておかなければなりません。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.1.1. 共有ライブラリ名

全ての共有ライブラリは「soname」と呼ばれる特別な名前を持っています。
soname は、「lib」というプレフィックス、ライブラリの名前、「.so」という
語句で構成され、さらに後ろに、ピリオドと、インターフェース変更時に必ず
増加するバージョン番号、が続きます (特別な例外として、最下層の C ライブ
ラリは「lib」では始まりません)。完全記述の soname は、そのライブラリ自
身が含まれるディレクトリをプレフィックスとして含んでいます。実際のシス
テムでは、完全記述の soname は、共有ライブラリの「real name」への単なる
シンボリックリンクになっています。

全ての共有ライブラリには「real name」(実際のライブラリコードを含むファ
イルの名前) があります。real name は、soname に、ピリオド、マイナー番号
、もう一つのピリオド、リリース番号、を加えたものです。最後のピリオドと
リリース番号は任意です。マイナー番号とリリース番号は、ライブラリのどの
バージョンがインストールされているかを正確に示すもので、設定管理に役立
ちます。これらの番号は (同じにしてくれれば話が分かりやすくなるのですが)
文書中でライブラリを説明する際に用いられている番号と同じではないかもし
れない、という点に注意してください。

加えて、ライブラリ要求時にコンパイラが使用する名前というものもあります
(「linker name」と呼ぼうと思います)。この名前は、単純に一切のバージョン
番号を取り除いた soname です。

共有ライブラリを管理する鍵となるのは、これらの名前の使い分けです。必要
とする共有ライブラリの一覧表を内部に持つときには、プログラムは、必要と
する soname のみをリストアップするようにします。逆に、共有ライブラリを
作成するときには、(より詳細なバージョン情報を持つ) 特定のファイル名を持
つライブラリのみを作成するようにします。新しいバージョンのライブラリを
インストールするときには、二、三の特別なディレクトリのうちの一つにそれ
をインストールし、それから ldconfig(8) プログラムを実行します。
ldconfig は、既に存在するファイルを調べ、real name へのシンボリックリン
クとして soname 群を作成します。同様にして、キャッシュファイル /etc/
ld.so.cache も作成します (すぐに説明します)。

ldconfig は linker name を作成しません。通常、この作成作業はライブラリ
インストール時におこない、単純に、「最新の」soname もしくは最新の real
name へのシンボリックリンクとして linker name を作成します。ほとんどの
場合において、ライブラリを更新したら、リンク時にそれを自動的に利用した
いと思うでしょうから、soname へのシンボリックリンクとして linker name
を作っておくことをお勧めします。なぜ ldconfig が自動的に linker name を
作成しないのかを、私は H. J. Lu に尋ねました。彼の説明は、基本的には、
「ライブラリの最新バージョンを使ってコードを実行したいと思われるかも知
れませんが、そうではなく、(おそらく互換性のない) 古いライブラリをリンク
しながらの開発作業を希望されることもありうるでしょう」、というものでし
た。そのため、ldconfig は、プログラムをどのライブラリにリンクさせたいの
かということについては、何の仮定もおこなわないのです。ですから、ライブ
ラリとしてリンカに使わせるものを実際に更新するためには、インストーラが
シンボリックリンクを個別に変更しなければならないのです。

例えば、/usr/lib/libreadline.so.3 は完全記述の soname であり、これは
ldconfig が /usr/lib/libreadline.so.3.0 というような何らかの real name
に対するシンボリックリンクとして作成するものです。 /usr/lib/
libreadline.so という linker name も作成するべきであり、それは、/usr/
lib/libreadline.so.3 を参照するシンボリックリンクとなるでしょう。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.1.2. ファイルシステム配置

共有ライブラリはファイルシステム内のどこかに配置しなければなりません。
ほとんどのオープンソースソフトウェアは、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 モジュール群は動的ライブラリ (これもあとで説明します) としてロー
ドされるので注意してください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.2. ライブラリはどのように使われるか

GNU glibc ベースのシステム (全ての Linux システムが含まれます) では、
ELF バイナリ実行ファイルを起動すると、自動的にプログラムローダがロード
され、実行されます。 Linux システムでは、このローダは /lib/
ld-linux.so.X (X にはバージョン番号が入ります) という名前です。このロー
ダは、プログラムによって使用されるその他の全ての共有ライブラリを順次探
し出し、ロードします。

検索対象となるディレクトリのリストは、/etc/ld.so.conf ファイル内に記述
されています。 Red Hat から派生しているディストリビューションの多くは、
通常 /etc/ld.so.conf ファイル内に /usr/local/lib を含めていません。私は
これをバグだと考えており、また、/usr/local/lib を /etc/ld.so.conf に追
加することは、Red Hat から派生しているシステム上で多くのプログラムを走
らせるのに必要な、共通の「修正」だと思っています。

ライブラリ内の幾つかの関数をオーバーライドしたいだけで、残りはそのまま
にしておきたいならば、オーバーライドするライブラリ (.o ファイル) の名前
を /etc/ld.so.preload に入れることができます。これらの「先行ロード」ラ
イブラリは、標準集合よりも先行します。この先行ロードファイルは、典型的
には緊急用のパッチとして使われます。通常、ディストリビューションが配布
される際、このようなファイルは含まれないでしょう。

プログラム起動時にこれら全てのディレクトリを検索するのは、とても非効率
的なので、実際にはキャッシュ処理がおこなわれます。 ldconfig(8) プログラ
ムはデフォルトで /etc/ld.so.conf ファイルを読み込み、適切なシンボリック
リンクを動的リンクディレクトリ内に作成します (これにより、標準的な慣習
に沿うことになります)。それから、あとで他のプログラムから利用されること
になるキャッシュを /etc/ld.so.cache に書き込みます。これにより、ライブ
ラリへのアクセスが非常に速くなります。結果として言えることは、DLL を追
加したとき、または、DLL を削除したり、DLL ディレクトリのセットを変更し
たりしたときには必ず、ldconfig を実行しなければならない、ということです
。 ldconfig の実行は、ライブラリインストール時にパッケージ管理ツールに
よっておこなわれるステップの一つであることが多いです。 ldconfig 以降、
動的ローダは起動時、実際には /etc/ld.so.cache ファイルを使い、必要とす
るライブラリをロードすることになります。

話は変わりますが、FreeBSD は、このキャッシュ用のファイル名が少し異なり
ます。 FreeBSD では、ELF キャッシュは /var/run/ld-elf.so.hints で、
a.out キャッシュは /var/run/ld.so.hints になります。これらのファイルも
また ldconfig(8) により更新されるので、場所の違いが問題となるのは、幾つ
かの特別な状況においてのみでしょう。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.3. 環境変数

様々な環境変数により、共有ライブラリのロード処理を制御できます。ロード
処理をオーバーライドするための環境変数が存在するのです。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.3.1. LD_LIBRARY_PATH

特定のプログラムを実行する際、一時的に別のライブラリを代替的に使用する
ことができます。Linux では、環境変数 LD_LIBRARY_PATH に、標準的なディレ
クトリ群に先立ってライブラリ検索対象とすべきディレクトリ群を、コロンで
区切って並べます。これは、新しいライブラリをデバッグするときや、特殊な
目的のために非標準的なライブラリを使用するときなどに便利です。環境変数
LD_PRELOAD には、ちょうど /etc/ld.so.preload でやっているのと同じように
、共有ライブラリ群を、標準集合をオーバーライドする関数群と共に列挙しま
す。これらの機能は、ローダ /lib/ld-linux.so により実装されています。
LD_LIBRARY_PATH は多くの Unix 系システムで機能しますが、全てのシステム
において機能するわけではないので注意しましょう。例えば、HP-UX でも同じ
機能を利用できますが、それは SHLIB_PATH ですし、AIX では LIBPATH となり
ます (構文は同じで、コロン区切りのリストです)。

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 を実行すると、使い方についてのヘルプが表示さ
れます。しかし、もう一度言いますが、普段はこれを使わないようにしてくだ
さい。これらの機能は全てデバッグのためにあるのです。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.3.2. LD_DEBUG

GNU C ローダのもう一つの便利な環境変数は LD_DEBUG です。これにより、dl*
() 関数群が、実行中の処理に関する情報を大量に出力するようになります。例
えば、次のコマンド

  export LD_DEBUG=files                                                
  command_to_run                                                       

は、ライブラリを扱うときに処理するファイル群とライブラリ群を表示し、検
出された依存関係、および、どの so ファイルがどの順番でロードされたか、
を教えてくれます。LD_DEBUG を「bindings」にセットすると、シンボルのバイ
ンディングに関する情報を表示します。「libs」にセットすると、ライブラリ
検索パスを表示します。「versions」にセットすると、バージョン依存関係を
表示します。

LD_DEBUG を「help」に設定し、プログラムの実行を試みると、指定可能なオプ
ションがリストアップされます。再度申しますが、LD_DEBUG は日常的に使用す
るためのものではなく、デバッグ時やテスト時に役立つものです。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.3.3. その他の環境変数

実際には、ロード処理を制御する環境変数は他にもたくさん存在します。それ
らの名前は LD_ や RTLD_ ではじまり、そのほとんどが、ローダ処理の低レベ
ルなデバッグや特殊な機能を実装するためのものです。ほとんどのものにはま
だ文書がありません。これらについて調べたいなら、(gcc の一部である) ロー
ダのソースコードを読むのが一番です。

特別な対応をおこなっていない場合、動的にリンクされるライブラリに対する
制御をユーザに許可してしまうと、setuid/setgid プログラムは悲惨なことに
なるでしょう。そのため、(プログラム起動時にプログラムの残りの部分をロー
ドする) GNU ローダは、これらの環境変数 (及び類似の環境変数) を無視する
か、もしくは、これらの環境変数の効果を大幅に制限します。ローダは、プロ
グラムの信任証 (credential) を調べることによって、そのプログラムが
setuid もしくは setgid されているかどうかを確認します。もしも uid (実ユ
ーザ ID) と euid (実効ユーザ ID) が異なるか、もしくは gid (実グループ
ID) と egid (実効グループ ID) が異なるなら、ローダはそのプログラムが
setuid/setgid されている (もしくはそのようなプログラムから起動された)
と推定し、リンク処理を制御する能力を大幅に制限します。 GNU glibc ライブ
ラリのソースコードを読めば、このことを確認することができるでしょう。特
に、elf/rtld.c ファイルと sysdeps/generic/dl-sysdep.c ファイルを参照し
てください。これらのことから、「uid と gid を euid と egid に等しくして
からプログラムを呼べば、これらの環境変数が完全に機能する」、ということ
が分かります。他の Unix 系システムでは、別の方法でこの問題に対処します
が、「setuid/setgid プログラムは、設定されている環境変数群によって過度
に影響を受けるべきではない」、という考え方は同じです。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.4. 共有ライブラリの作成

共有ライブラリの作成は簡単です。まずはじめに、共有ライブラリに組み込む
オブジェクトファイルを、gcc の -fPIC または -fpic フラグを使って作成し
ます。 -fPIC と -fpic オプションにより、共有ライブラリに対する要求事項
である「位置独立コード (position independent code)」を生成することがで
きます。二つのオプションの違いについては、後の説明を参照してください。
soname は、gcc の -Wl オプションを使って指定します。 -Wl オプションは、
オプション群 (今回の例では -soname リンカオプション) をリンカに渡すため
のものです。-Wl の後ろのカンマはタイプミスではありません。 -Wl オプショ
ンには、エスケープされていないスペースを含めてはならないのです (訳注：
-Wl の直後に続く「,-soname,your_soname」という文字列は、カンマの部分で
分割されてからリンカに渡されます。つまり、リンカには「-soname
your_soname」というオプションが渡されます)。次の書式を用いて共有ライブ
ラリを作成してください。

  gcc -shared -Wl,-soname,your_soname \                                
      -o library_name file_list library_list                           

二つのオブジェクトファイル (a.o と b.o) を作成し、これら両方のオブジェ
クトファイルを含む共有ライブラリを作成する例を挙げます。下記のコンパイ
ル処理では、オブジェクトファイルにデバッグ情報 (-g) が含まれることにな
り、また、警告メッセージ (-Wall) も表示されます。これらのオプションは、
共有ライブラリ作成に必須ではありませんが、お勧めします。このコンパイル
処理で、(-c オプションにより) オブジェクトファイルが生成されます。なお
、-fPIC オプションの指定は必須です。

  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                               

注意すべきことが幾つかあります。

 ・ 生成されたライブラリを strip しないでください。また、本当に必要でな
    い限り、コンパイラオプション -fomit-frame-pointer を使わないでくだ
    さい。これらをおこなっても生成されたライブラリは機能しますが、デバ
    ッガがほとんど使い物にならなくなってしまいます。
   
 ・ コードの生成には -fPIC または -fpic を使用してください。コードの生
    成に -fPIC と -fpic のどちらを使うかは、ターゲットに依存します。
    -fPIC を選んでおけば常に動きますが、-fpic よりも大きなコードを生成
    する可能性があります (これを覚えておく方法は、「PIC は大文字だから
    大きなコードを生成する可能性がある」、です)。 -fpic オプションを使
    うと、通常は小さくて速いコードが生成されますが、グローバルで可視に
    できるシンボルの数やコードのサイズといった、プラットフォームに依存
    する制限があります。共有ライブラリを作成するとき、うまくいくかどう
    かをリンカが教えてくれます。疑わしいとき、私は -fPIC の方を選択しま
    す。なぜなら、-fPIC は常に機能するからです。
   
 ・ 場合によっては、gcc を呼び出してオブジェクトファイルを生成するとき
    、「-Wl,-export-dynamic」オプションを含めなければならないこともあり
    ます。通常、動的シンボルテーブルには、動的オブジェクトが使用するシ
    ンボルのみが含まれています。このオプションは、(ELF ファイルを生成す
    る場合) 全てのシンボルを動的シンボルテーブルに追加します (詳細につ
    いては ld(1) を参照してください)。「逆依存関係」が存在するとき、つ
    まり、ライブラリをロードしようとしているプログラムの内部で (慣習に
    より) 宣言しておかなければならないシンボルが、動的ライブラリにおい
    て未解決のとき、このオプションを使う必要があります。「逆依存関係」
    を機能させるには、主プログラムが、シンボル群を動的に利用可能にする
    必要があります。 Linux システムだけで作業するのであれば、「
    -Wl,-export-dynamic」のかわりに「-rdynamic」でもよいです。しかし、
    ELF 文書によると、Linux 以外のシステムにおける gcc では、「
    -rdynamic」フラグは常に機能するわけではありません。
   
開発中は、「他の多くのプログラムからも利用されているライブラリを修正し
てしまうかもしれない」という潜在的な問題があります。開発段階のライブラ
リを対象にしてテストをおこなっている特定のアプリケーション以外には、そ
の開発段階のライブラリを使わせたくないでしょう。使用することになると思
われるリンクオプションは、ld の「-rpath」です。これは、コンパイルする個
々のプログラムの、実行時のライブラリ検索パスを指定するものです。 gcc か
らは、次のように指定することで、-rpath オプションを渡すことができます。

  -Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH)                               

ライブラリのクライアントプログラムをビルドするときにこのオプションを使
えば、そのプログラムが当該ライブラリを隠蔽してしまうような他の技術と競
合していないこと、または、そのような技術を使用していないこと、を確認す
る必要があることを除いて、LD_LIBRARY_PATH (次で説明します) について悩む
必要がなくなります。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.5. 共有ライブラリのインストールと使用

共有ライブラリを作成したら、それをインストールしたくなることでしょう。
簡単な方法は、標準的なディレクトリ (例えば /usr/lib など) の一つに、そ
のライブラリをコピーし、ldconfig(8) を実行することです。

まず、共有ライブラリをどこかに作成する必要があるでしょう。それから、必
要なシンボリックリンク、特に soname から real name へのリンクを作成する
必要もあるでしょう (同様に、バージョン番号を全く指定しないユーザのため
、バージョン番号の無い soname, つまり「.so」で終わる soname からのリン
クも必要です)。最も簡単な方法は、次のとおり実行することです。

  ldconfig -n directory_with_shared_libraries                          

最後に、プログラムをコンパイルするときに、使おうとしている全ての静的ラ
イブラリと共有ライブラリについて、リンカに教えてやる必要があります。こ
れは、-l オプションと -L オプションを使っておこないます。

標準的な場所にライブラリをインストールできない、もしくはインストールし
たくないなら (例えばあなたが /usr/lib を変更する権限を持っていないなど)
、別の手順を踏まなければなりません。このような場合、ライブラリをどこか
にインストールしておき、そのライブラリを見つけるために十分な量の情報を
プログラムに与えなければなりません。これをおこなうには、幾つか方法があ
ります。単純な場合では、gcc の -L フラグを使用できます。また、特に、あ
る特定のプログラム以外は非標準的な場所に置いてあるライブラリを利用しな
いというのであれば、(上で説明した)「-rpath」を使うこともできます。環境
変数を使っていろいろ制御することもできます。特に、LD_LIBRARY_PATH を設
定することができます。 LD_LIBRARY_PATH には、共有ライブラリを検索すると
きに通常の場所に先立って検索対象とするディレクトリ群を、コロンで区切っ
て列挙します。 bash をお使いでしたら、次の方法で my_program を実行でき
ます。

  LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH my_program                        

幾つかの関数を選択的にオーバーライドしたいだけならば、オーバーライドす
るオブジェクトファイルを作成して LD_PRELOAD を設定するだけで実現できま
す。このオブジェクトファイル内の関数により、対象となっている関数だけが
オーバーライドされます (他の関数は元のままです)。

通常、ライブラリの更新は、何も気にせずにおこなうことができます。もしも
API に変更があるならば、ライブラリ作成者により soname も変更されている
ことでしょう。そういうわけで、一つのシステム上に複数のライブラリが存在
でき、各プログラムに対するライブラリが正しく選択されるのです。しかし、
もしも、ライブラリの更新で soname は変更されなかったのにプログラムがう
まく動かなくなってしまった、ということがあるのならば、古いライブラリを
どこかにコピーし、そのプログラムの名称を変更する (古い名前に「.orig」を
付け足すなど) ことによって、そのプログラムが古いバージョンのほうのライ
ブラリを使うように強制することができます。これらの作業をおこなったあと
、利用するライブラリを再設定して実際の (名称変更された) プログラムを呼
び出す、小さな「ラッパー」スクリプトを作成してください。番号付けの約束
ごとのおかげで、同一ディレクトリ内に複数のバージョンのライブラリを置く
こともできますが、お望みなら、古いライブラリをそれ独自の特別な場所に置
くこともできます。ラッパースクリプトは、次のようなものになるでしょう。

  #!/bin/sh                                                            
  export LD_LIBRARY_PATH=/usr/local/my_lib:$LD_LIBRARY_PATH            
  exec /usr/bin/my_program.orig "$@"                                   

自分でプログラムを書くときには、この方法に依存しないようにしてください
。ライブラリの後方互換性を持たせるか、もしくは、互換性の無い変更をおこ
なうときは必ず soname のバージョン番号を増やすよう、気を払ってください
。この方法は、最悪の場合の問題を扱うための「緊急時」対応なのです。

ldd(1) を使えば、プログラムによって使用されている共有ライブラリのリスト
を見ることができます。例えば、次のようにタイプすれば、ls によって使用さ
れる共有ライブラリを確認できます。

  ldd /bin/ls                                                          

通常、プログラムが依存する各 soname について、それぞれどのディレクトリ
に存在するかが特定され、その特定されたディレクトリ名とともに、当該
soname 群のリストが表示されます。実質的に全ての状況において、少なくとも
二つの依存情報が表示されるでしょう。

 ・ /lib/ld-linux.so.N (N は 1 以上。通常は少なくとも 2). これは、他の
    全てのライブラリをロードするためのライブラリです。
   
 ・ libc.so.N (N は 6 以上). これは C ライブラリです。C 言語以外の言語
    でさえ、C ライブラリを使用する傾向があります (少なくともそれら自身
    のライブラリを実装するため)。そのため、ほとんどのプログラムは、少な
    くともこのライブラリを含んでいます。
   
注意：信頼できないプログラムに対して ldd を実行してはいけません。 ldd
(1) のマニュアルで明確に述べられているとおり、ldd は、(幾つかの状況にお
いては) 特別な環境変数 (ELF オブジェクトの場合は
LD_TRACE_LOADED_OBJECTS) を設定してプログラムを実行することにより、動作
しています。信頼できないプログラムが、(ldd 情報を単に表示するかわりに)
任意のコードを ldd ユーザに強制実行させてしまうことも可能かもしれません
。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

3.6. 互換性のないライブラリ

新しいバージョンのライブラリが古いものとのバイナリ互換性を持たないとき
には、soname を変更する必要があります。 C 言語では、ライブラリがバイナ
リ互換ではなくなってしまう四つの基本的な原因があります。

 1. 関数の挙動が変更され、元々の仕様に適合しなくなる
   
 2. エクスポートされているデータが変更される (例外：構造体がライブラリ
    内でのみアロケートされる場合に限り、構造体の末尾に任意のメンバーを
    追加することには問題がない)
   
 3. エクスポートされている関数が削除される
   
 4. エクスポートされている関数のインターフェースが変更される
   
これらの原因を回避できれば、ライブラリをバイナリ互換に保つことができま
す。別の言い方をすると、これらの変更を避ければ、Application Binary
Interface (ABI) 互換を保つことができる、ということです。例えば、古い関
数を削除せずに新しい関数を追加したい、というのは問題ないでしょう。構造
体の末尾にのみメンバーを追加し、その構造体をアロケートするのはライブラ
リ内だけとし (アプリケーションにはアロケートさせない)、追加のメンバーを
オプション扱いにする (ライブラリがそのメンバーを設定する)、などの操作を
おこなって生じる変更が、古いプログラムに対して影響を与えないことが確か
な場合にのみ、メンバーを追加することができます。もしもユーザが構造体を
配列で使っているならば、その構造体を拡張することはほとんどできないので
十分注意してください。

C++ (および、テンプレートやディスパッチされるメソッドをコンパイル時に組
み込むという方法でサポートするその他の言語) では、状況はより複雑になり
ます。上記に述べたことが全て当てはまる上、さらに多くの注意点があります
。幾つかの情報が、コンパイルされるコード内に「隠蔽された状態で」組み込
まれるという点にその原因があるのですが、このために、C++ が通常どのよう
に実装されているかを知らない人には理解しにくい依存問題が、引き起こされ
てしまうのです。正確に言えば、これは「新しい」問題ではありません。単に
、コンパイル済みの C++ コードが、人によっては驚くかもしれない動作をして
問題を引き起こす、というだけの話です。次のリストは、バイナリ互換を維持
するために C++ 内でやってはいけない項目のリスト (おそらく完全ではありま
せんが) であり、Troll Tech 社テクニカル FAQ <http://www.trolltech.com/
developer/faq/tech.html#bincomp> で公開されているものです。

 1. 仮想関数の再実装を追加する (古いバイナリが元の実装を呼び出すのが安
    全ではない場合)。コンパイラは SuperClass::virtualFunction() 呼出し
    をコンパイル時に評価するため (リンク時ではない)。
   
 2. 仮想メンバ関数を追加または削除する。これにより全サブクラスの仮想関
    数テーブルのサイズとレイアウトが変更されてしまうため。
   
 3. インラインメンバ関数経由でアクセス可能なデータメンバを移動させたり
    、データメンバの型を変更する。
   
 4. クラス階層を変更する。ただし、リーフ (訳注：下位クラスを持たないク
    ラス) の新規追加を除く。
   
 5. プライベートデータを追加、または削除する。これにより全サブクラスの
    サイズとレイアウトが変更されてしまうため。
   
 6. public もしくは protected メンバ関数がインライン関数でない場合に、
    それらを削除する。
   
 7. public もしくは protected メンバ関数をインライン化する。
   
 8. インライン関数の挙動を変更する。ただし、古いバージョンが機能し続け
    る場合を除く。
   
 9. 可搬性を持たせたいプログラム内のメンバ関数のアクセス権 (すなわち、
    public, protected または private) を変更する。アクセス権情報を関数
    名に組み入れるコンパイラもあるため。
   
この長いリストのことを考えると (訳注：＝こんな長いリストの全項目を守り
続けることは無理だろうから)、特に C++ ライブラリの開発者は、バイナリ互
換性を維持できない更新を頻繁に実施することを計画しておく必要があります
。幸いにして、Unix 系システム (Linux を含みます) では、一つのライブラリ
の複数のバージョンを同時にロードすることができるので、ディスクスペース
を消費することにはなりますが、古いライブラリを必要とする「古い」プログ
ラムを引き続き実行することが可能です。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

4. 動的ライブラリ

動的ライブラリは、プログラムの起動時以外のときにロードされるライブラリ
です。動的ライブラリは、プラグインやモジュールを実装するときに特に便利
です。というのは、必要になるまで、プラグインのロードを待機させることが
できるからです。例えば、Pluggable Authentication Modules (PAM) システム
では、管理者が認証の設定／再設定をおこなえるよう、動的ライブラリを使用
しています。また、効率を上げるために途中で休止せずにその時々でコードを
マシンコードにコンパイルし、そのコンパイルされたものを使用するというイ
ンタプリタを実装するのにも役に立ちます。この方法は、例えばジャストイン
タイム・コンパイラや、マルチユーザ・ダンジョン (MUD) を実装するときに使
えます。

Linux では、実際のところ、フォーマットという視点から見ると、動的ライブ
ラリは特別なものではありません。今まで述べてきたものと同じで、標準的な
オブジェクトファイルや共有ライブラリとしてビルドされています。主な違い
は、動的ライブラリは、プログラムのリンク時や起動時に自動的にはロードさ
れない、という点です。そのかわりに、ライブラリをオープンし、シンボルを
検索し、エラーを処理し、ライブラリを閉じる、という API が存在します。こ
の API を使用するためには、C プログラマはヘッダファイル <dlfcn.h> をイ
ンクルードする必要があります。

Linux で使用されるインターフェースは、基本的に Solaris のものと同じです
。これを「dlopen()」API と呼ぼうと思います。しかし、このインターフェー
スは全てのプラットフォームでサポートされているわけではありません。
HP-UX では shl_load() という別の仕組みが使用され、Windows プラットフォ
ームでは全く異なるインターフェースである DLL が使用されます。広範な移植
性を最終目標とするならば、おそらく、プラットフォーム間の差違を隠蔽する
ラッパーライブラリの使用を検討したほうがよいでしょう。方法の一つとして
、モジュールの動的ローディングをサポートする glib ライブラリがあります
。 glib ライブラリは、プラットフォーム固有の動的ローディング・ルーチン
群を内部で使用し、動的ローディング用の移植性の高いインターフェースを実
装しています。 glib については、http://developer.gnome.org/doc/API/glib
/glib-dynamic-loading-of-modules.html を参照してください。 glib のイン
ターフェースについては、glib のドキュメントで十分に説明されているので、
ここではこれ以上言及しません。もう一つの方法は、libltdl を使う方法です
。 libltdl は、GNU libtool <http://www.gnu.org/software/libtool/
libtool.html> の一部です。もっと多くの機能が必要ならば、CORBA Object
Request Broker (ORB) を調べてみるのもよいでしょう。 Linux と Solaris で
サポートされるインターフェースを直接使用することにまだ興味をお持ちなら
ば、読み進んでください。

C++ と動的ライブラリを使用する開発者の方は、「C++ dlopen mini HOWTO
<http://www.tldp.org/HOWTO/C++-dlopen/>」も参照してください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

4.1. dlopen()

dlopen(3) 関数は、ライブラリをオープンし、使用前の準備をおこないます。
C のプロトタイプは次のようになります。

  void * dlopen(const char *filename, int flag);                       

ファイル名が「/」で始まるならば (つまり絶対パスならば)、dlopen() は単に
そのファイル名を使おうとします (ライブラリを検索しようとはしません)。そ
れ以外ならば、dlopen() は次の順番でライブラリを検索します。

 1. ユーザの LD_LIBRARY_PATH 環境変数内のコロンで区切られたディレクトリ
    リスト
   
 2. /etc/ld.so.cache 内に指定されたライブラリリスト (/etc/ld.so.cache
    は /etc/ld.so.conf をもとに生成されます)
   
 3. /lib, 次に /usr/lib. この順番に注意してください。この順番は、古い
    a.out ローダで使われていた順番とは逆です。古い a.out ローダは、プロ
    グラムをロードするとき、最初に /usr/lib を検索し、そのあとで /lib
    を検索していました (man ページ ld.so(8) を参照してください)。このこ
    とは、通常は問題にならないはずです。なぜなら、ライブラリはどちらか
    一方のディレクトリのみに置いてあるはずだからです。異なるライブラリ
    に同じ名前を付けると、混乱を招きます。
   
dlopen() では、flag の値は RTLD_LAZY か RTLD_NOW のどちらかでなければな
りません。 RTLD_LAZY は、「動的ライブラリのコードが実行されるときに未定
義シンボルを解決せよ」という意味で、RTLD_NOW は、「dlopen() がリターン
する前に全ての未定義シンボルを解決せよ、それができないようならば失敗せ
よ」という意味です。 RTLD_GLOBAL は、flag のどちらかの値と任意で論理和
結合させることができます。 RTLD_GLOBAL フラグを付けてロードされたライブ
ラリ内で定義されている外部シンボルは、後からロードされるライブラリから
でも使用できるようになります。デバッグ中は、おそらく RTLD_NOW を使いた
くなるでしょう。 RTLD_LAZY を使うと、未解決の参照があったときに不可解な
エラーが生成されます。 RTLD_NOW を使うと、ライブラリのオープンには若干
時間がかかるようになります (しかし、のちのちの検索速度は速くなります)。
このことがユーザインターフェース上問題になるようでしたら、あとで
RTLD_LAZY に切り替えることができます。

ライブラリが他のライブラリに依存しているなら (例えば、X が Y に依存して
いる)、依存されているほうを先にロードしてください (この例で言えば、Y を
先にロードし、それから X をロードします)。

dlopen() の戻り値は、他の動的ライブラリ・ルーチンによって使用される「ハ
ンドル」です (このハンドルの実体は隠蔽されているものとして扱ってくださ
い)。ロードが失敗した場合、dlopen() は NULL を返しますので、戻り値をチ
ェックする必要があります。同じライブラリが dlopen() で二回以上ロードさ
れると、同じファイルハンドルが返されます。

古いシステムでは、ライブラリが _init という名前のルーチンをエクスポート
している場合、dlopen() から戻る前にそのコードが実行されます。この仕組み
を使って、ライブラリの初期化ルーチンを実装することができます。しかし、
ライブラリは、_init や _fini といった名前のルーチンをエクスポートすべき
ではありません。これらの仕組みは古くなっており、望んでいない動作をする
可能性があります。かわりに、ライブラリでは、__attribute__
((constructor)) 関数属性と __attribute__((destructor)) 関数属性を使用し
てルーチンをエクスポートしてください (gcc を使用しているものと仮定して
います)。詳細については Section 5.2 を参照してください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

4.2. dlerror()

dlerror() によりエラーを報告できます。 dlerror() は、最後の dlopen(),
dlsym(), または dlclose() 呼出しのエラーについて説明する文字列を返しま
す。一つ変わっているのは、dlerror() を呼び出すと、以降の dlerror() の呼
出しは他のエラーが発生するまで NULL を返すという点です。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

4.3. dlsym()

動的ライブラリをロードしても、それを使えなければ意味がありません。動的
ライブラリを使用する上で中心となるルーチンは dlsym(3) です。 dlsym() は
、与えられた (オープン済みの) ライブラリ内にあるシンボルの値を検索しま
す。この関数は次のように定義されています。

  void * dlsym(void *handle, char *symbol);                            

handle は dlopen() から返される値で、symbol はヌル文字で終端された文字
列です。 dlsym() の結果を void * ポインタに格納することは、できるだけ避
けてください。なぜなら、そのポインタを使用するたびにキャストしなければ
ならなくなるからです (そのプログラムをメンテナンスしようとする他の方々
が受け取れる情報量も減ってしまいます)。

dlsym() は、シンボルが見つからなければ NULL を返します。シンボルが NULL
もしくはゼロという値をとることはありえないと分かっていれば、問題ありま
せん。しかし、そうでない場合は潜在的に曖昧さが残ります。もしも NULL を
受け取った場合、それは、「そんなシンボルは存在しない」ということを意味
するのでしょうか、それとも「そのシンボルの値は NULL である」ということ
を意味するのでしょうか？標準的な方法は、(存在している可能性のあるエラー
条件をクリアするために) まず最初に dlerror() を呼び、次に、シンボルを要
求するために dlsym() を呼び、最後に、エラーが発生しているかどうかを調べ
るために再度 dlerror() を呼び出す、というものです。コードは次のようにな
るでしょう。

  dlerror(); /* エラーコードをクリアする。*/                           
  s = (actual_type) dlsym(handle, symbol_being_searched_for);          
  if ((err = dlerror()) != NULL) {                                     
      /* ハンドルエラー。シンボルは見つからなかった。*/                
  } else {                                                             
      /* シンボルが見つかった。値は s に格納されている。*/             
  }                                                                    

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

4.4. dlclose()

dlopen() の逆が dlclose() です。dlclose() で動的ライブラリをクローズし
ます。 dl ライブラリは動的なファイルハンドルへのリンク数を管理している
ので、同一の動的ライブラリに対して、dlopen() が成功した回数と同じ数の
dlclose() が呼ばれない限り、当該ライブラリは実際にはメモリ上から削除さ
れません。ですので、同じプログラムが同じライブラリを何回ロードしても、
問題にはなりません。ライブラリの割当てが解除されるとき、古いライブラリ
では、(もしも存在するならば) _fini 関数が呼ばれます。しかし、_fini は古
い仕組みなので、これに依存してはいけません。かわりに、ライブラリでは、
__attribute__((constructor)) 関数属性と __attribute__((destructor)) 関
数属性を使用してルーチンをエクスポートしてください。詳細については 
Section 5.2 を参照してください。注意：dlclose() は、成功ならば 0 を、エ
ラーならば非ゼロを返します。この返り値について言及していない Linux man
ページもあります。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

4.5. 動的ライブラリの例

dlopen(3) の man ページ内の例をここに掲載します。この例では、数学ライブ
ラリをロードし、2.0 のコサインを出力しています。また、全てのステップで
エラーをチェックしています (推奨)。

  #include <stdlib.h>                                                  
  #include <stdio.h>                                                   
  #include <dlfcn.h>                                                   
                                                                       
  int main(int argc, char **argv) {                                    
      void *handle;                                                    
      double (*cosine)(double);                                        
      char *error;                                                     
                                                                       
      handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);                   
      if (!handle) {                                                   
          fputs (dlerror(), stderr);                                   
          exit(1);                                                     
      }                                                                
                                                                       
      cosine = dlsym(handle, "cos");                                   
      if ((error = dlerror()) != NULL)  {                              
          fputs(error, stderr);                                        
          exit(1);                                                     
      }                                                                
                                                                       
      printf ("%f\n", (*cosine)(2.0));                                 
      dlclose(handle);                                                 
  }                                                                    

このプログラムが "foo.c" という名前のファイルだとすると、次のコマンドで
プログラムを作成することができます。

  gcc -o foo foo.c -ldl                                                

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5. 雑録

5.1. nm コマンド

nm(1) コマンドは、指定されたライブラリ内に存在するシンボルのリストを表
示します。静的ライブラリ、共有ライブラリのどちらに対しても機能します。
指定されたライブラリについて、nm(1) は、定義されているシンボルの名前、
それぞれのシンボルの値、シンボルのタイプ、を表示できます。また、そのラ
イブラリ内に情報が存在するならば (-l オプションを参照してください)、シ
ンボルがソースコード内のどこで (ファイル名と行番号) 定義されているかと
いうことも特定できます。

シンボルタイプについては、もう少し説明が必要です。小文字はそのシンボル
がローカルであることを意味し、大文字はそのシンボルがグローバル (外部定
義) であることを意味します。典型的なシンボルタイプは次のとおりです。

 ・ T (コードセクション内の普通の定義)
   
 ・ D (初期化されたデータセクション)
   
 ・ B (初期化されないデータセクション)
   
 ・ U (未定義。シンボルはライブラリによって使われているが、ライブラリ内
    では定義されていない)
   
 ・ W (weak. もしも他のライブラリも同じシンボルを定義している場合、その
    定義によりオーバーライドされる)
   
関数の名前は覚えているけれども、それがどのライブラリで定義されているか
正確には思い出せない場合、nm の「-o」オプションを使い (コマンドラインで
はファイル名より前に置きます)、出力を grep することによって、ライブラリ
名を見つけ出すことができます。 Bourne シェルであれば、/lib, /usr/lib, /
usr/lib 直下のサブディレクトリ、および /usr/local/lib 内の全ライブラリ
を対象にして「cos」を検索するには、次のようにします。

  nm -o /lib/* /usr/lib/* /usr/lib/*/* \                               
        /usr/local/lib/* 2> /dev/null | grep 'cos$'                    

nm に関する詳細な情報は、お手元のマシンにインストールされている nm の「
info」文書 (info:binutils#nm) 内にあります。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.2. ライブラリのコンストラクタ関数およびデストラクタ関数

ライブラリの初期化ルーチンと終了処理ルーチンは、gcc の __attribute__
((constructor)) 関数属性と __attribute__((destructor)) 関数属性を使用し
てエクスポートします。これらの関数属性に関する情報は gcc の info ページ
を参照してください。コンストラクタルーチンは、dlopen() から戻る前に実行
されます (または、ライブラリがプログラム開始時にロードされるならば main
() 開始前)。デストラクタルーチンは dlclose() から戻る前に実行されます
(または、ライブラリがプログラム開始時にロードされたならば、exit() の後
、もしくは main() 完了後)。これらの関数の C 言語プロトタイプは次のよう
になります。

  void __attribute__ ((constructor)) my_init(void);                    
  void __attribute__ ((destructor)) my_fini(void);                     

共有ライブラリをコンパイルするときは、gcc オプションの「-nostartfiles」
や「-nostdlib」を付けてはいけません。これらのオプションが使われると、
(特殊な方法を用いない限り) コンストラクタルーチンおよびデストラクタルー
チンが実行されなくなってしまいます。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.2.1. 特別な関数 _init と _fini (古い仕組み／危険)

歴史的な経緯により、コンストラクタとデストラクタを制御することができる
二つの特別な関数、_init と _fini が存在しています。しかしながら、これら
は古くなっており、使用すると、予測できない結果を招くことがあります。あ
なたのライブラリでは、これらの関数を使用しないようにしてください。かわ
りに、上で説明した関数属性の constructor と destructor を使用してくださ
い。

古いシステムや、_init または _fini を使用しているコードを扱わなければな
らないときのため、_init と _fini の動作をここで説明します。二つの特別な
関数 _init と _fini は、モジュールの初期化と終了処理のために定義されま
した。「_init」関数がライブラリ内でエクスポートされている場合、そのライ
ブラリが初めて (dlopen() により、または単に共有ライブラリとして) オープ
ンされたときに呼び出されます。C プログラムでは、_init という名前の何ら
かの関数を定義しているということを意味します。_init に対応する関数とし
て _fini が存在します。_fini は、(参照カウントをゼロにする dlclose() の
呼出しか、またはプログラムの通常の終了により) クライアントがライブラリ
の使用を終了したときに呼び出されます。これらの関数の C 言語プロトタイプ
は次のようになります。

  void _init(void);                                                    
  void _fini(void);                                                    

この場合は、gcc でファイルを「.o」ファイルにコンパイルするとき、忘れず
に gcc オプション「-nostartfiles」を付けてください。このオプションは
、.so ファイルに対してシステムスタートアップライブラリをリンクしないよ
う、C コンパイラに伝えるものです。このオプションを付けないと、「
multiple-definition (重複定義)」エラーが発生してしまいます。お勧めして
いる関数属性を用いてモジュールをコンパイルする方法と、この方法とは、全
く異なるものなので注意してください。 _init と _fini に関する議論を加え
ることを提案してくれたこと、およびその記述を手伝ってくれたことに対して
、Jim Mischel と Tim Gentry に感謝します。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.3. 共有ライブラリはスクリプト化できる

通常のライブラリフォーマットとは異なる、特別なスクリプト言語で記述され
たテキストファイルを、GNU ローダが共有ライブラリとして扱えることは、注
目に値します。この機能は、他のライブラリを間接的に結合させる際に便利で
す。例えば、私のシステムの一つでは、/usr/lib/libc.so の中身は次のように
なっています。

  /* GNU ld スクリプト                                                 
     共有ライブラリを使うが、幾つかの関数は静的ライブラリ内にしか      
     存在しない。そのため、共有ライブラリ内に関数が見つからなければ、  
     静的ライブラリを検索しにいく。*/                                  
  GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )                   

これに関するより詳しい情報は、ld リンカスクリプト (ld コマンド言語) に
関する texinfo 文書を参照してください。一般的な情報は info:ld#Options
と info:ld#Commands にあり、よく使うコマンドは info:ld#Option Commands
で説明されています。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.4. シンボルのバージョン付けとバージョンスクリプト

通常、外部関数への参照は必要に応じてバインドされます。アプリケーション
の起動時に全てがバインドされるわけではありません。共有ライブラリが古い
ものである場合、必要なインターフェースが存在しないかもしれません。アプ
リケーションがそのようなインターフェースを使おうとしたときになって初め
て、アプリケーションは突然予期せぬエラーを起こします。

この問題に対する一つの解決方法は、バージョンスクリプトによるシンボルの
バージョン付けです。シンボルのバージョン付けにより、アプリケーションに
より使用されるライブラリが古すぎる場合に、そのアプリケーションを起動し
たときに警告が出るようになります。これについては、バージョンスクリプト
に関する ld マニュアルの説明 (http://www.gnu.org/software/binutils/
manual/ld-2.9.1/html_node/ld_25.html) を参照してください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.5. GNU libtool

多くのシステムに移植する必要のあるアプリケーションを作成しているならば
、ライブラリの構築とインストールについて、GNU libtool <http://
www.gnu.org/software/libtool/libtool.html> を使用することを検討したほう
がよいかもしれません。 GNU libtool は、汎用的なライブラリサポートスクリ
プトです。 libtool は、共有ライブラリ使用時の複雑さを、一貫性のある移植
性の高いインターフェースで隠蔽します。 libtool は、オブジェクト作成、ラ
イブラリのリンク (静的および共有)、実行可能ファイルのリンク、実行可能フ
ァイルのデバッグ、ライブラリのインストール、実行可能ファイルのインスト
ール、に対して、移植性の高いインターフェースを提供します。また、libltdl
という、プログラムを動的にロードするための移植性のあるラッパーも、
libtool には含まれています。詳細については、libtool の文書 http://
www.gnu.org/software/libtool/manual.html を参照してください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.6. スペースを節約するためにシンボルを削除する

生成されたファイル内に含まれるシンボルは全て、デバッグの際に役に立ちま
すが、スペースを取ります。スペースを節約する必要があるならば、シンボル
の一部を削除することができます。

一番良い方法は、最初にいつも通りにオブジェクトファイルを生成し、デバッ
グとテストを全て実行してしまうことです (シンボルを含むオブジェクトファ
イルでは、デバッグとテストは非常に簡単です)。その後、プログラムを完全に
テストし終えたら、strip(1) を用いてシンボルを削除します。 strip(1) コマ
ンドでは、どのシンボルを削除するかについて、いろいろと制御することがで
きます。詳細については strip(1) の文書を参照してください。

もう一つの方法は、GNU ld オプションの「-S」と「-s」を使用することです。
「-S」は、出力ファイルから、(全てではないですが) デバッグシンボル情報を
除外します。一方、「-s」は、出力ファイルから全てのシンボル情報を除外し
ます。「-Wl,-S」や「-Wl,-s」とすることにより、これらのオプションを gcc
経由で ld に渡せます。常にシンボルを取り除くことにしていて、これらのオ
プションで十分というのならば、それでよいでしょう。ただし、このやり方は
、strip(1) に比べ柔軟性が下がります。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.7. 極端に小さな実行可能ファイル

「Whirlwind Tutorial on Creating Really Teensy ELF Executables for
Linux <http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html>
(本当に小さな Linux 用 ELF 実行可能ファイル作成についてのすぐ役立つチュ
ートリアル)」という文書が、あなたの役に立つかもしれません。この文書は、
非常に小さな実行可能プログラムの作り方について説明しています。率直に言
えば、一般的な状況下では、この文書で紹介されているトリックのほとんどは
、使わないほうがよいです。しかしこの文書は、ELF が実際にどのように機能
するかを示しているという点において、教育上極めて有益です。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.8. C++ vs. C

C++ プログラムを書いていて、C ライブラリ関数を呼び出すなら、C++ コード
内でその C 関数を extern "C" として宣言する必要があるので注意してくださ
い。そうしないと、リンカがその C 関数を見つけることができなくなってしま
います。内部的に、C++ コンパイラは C++ 関数の名前を「変形 (mangle)」し
ます (例えば、型情報を付加するために)。そのため、C++ コンパイラには、指
定された関数を C 関数として呼び出すことを教えてやる必要があります (これ
により、その C 関数の名前は変形されなくなります)。

C または C++ から呼び出されるプログラムライブラリを作成しているのなら、
ライブラリユーザのため、この処理が自動的におこなわれるよう、ヘッダファ
イルに「extern "C"」を含めることをお勧めします。ヘッダファイルの再読込
みをスキップするためにヘッダファイルの先頭に置く、いつもの #ifndef と組
み合わせて示すとすれば、C および C++ のどちらでも使用可能なヘッダファイ
ルの典型例は、例えば foobar.h だとすると、次のようになります。

  /* foobar が何をするものなのか、ここで説明する。*/                   
                                                                       
  #ifndef FOOBAR_H                                                     
  #define FOOBAR_H                                                     
                                                                       
  #ifdef __cplusplus                                                   
  extern "C" {                                                         
  #endif                                                               
                                                                       
   ... foobar 用ヘッダコードをここに書く ...                           
                                                                       
  #ifdef  __cplusplus                                                  
  }                                                                    
  #endif                                                               
  #endif                                                               

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.9. C++ 初期処理の高速化

KDE 開発者は、大きな GUI C++ アプリケーションの起動に長い時間がかかり、
その理由の一つが、数多くのリロケーションをおこなう必要があることだと気
付いています。この問題に対して、幾つか解決方法があります。詳細について
は、「Making C++ ready for the desktop (by Waldo Bastian) <http://
www.suse.de/~bastian/Export/linking.txt> (C++ コードをデスクトップ向け
にする (Waldo Bastian 著))」を参照してください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.10. Linux Standard Base (LSB)

Linux Standard Base (LSB) プロジェクトの目的は、Linux ディストリビュー
ション間の互換性を高め、LSB に準拠する全ての Linux システム上でソフトウ
ェアアプリケーションの実行を可能とするための標準を開発し、普及させるこ
とです。このプロジェクトのホームページは http://www.linuxbase.org/ です
。

LSB 準拠アプリケーションの開発方法について簡単にまとめた、George Kraft
IV (IBM Linux テクノロジーセンター上級ソフトウェアエンジニア) による素
晴らしい文書、Developing LSB-certified applications: Five steps to
binary-compatible Linux applications <http://www-106.ibm.com/
developerworks/linux/library/l-lsb.html?t=gr,lnxw02=LSBapps> (訳注：日
本語訳はこちら→ LSB 認定アプリケーション開発 <http://www-6.ibm.com/jp/
developerworks/linux/030117/j_l-lsb.html>) が 2002 年 10 月に公開されま
した。当然ながら、自分のコードの移植性を高めたいならば、標準化された移
植層のみを使用してコードを書く必要があります。さらに、LSB では、C/C++
プログラムのアプリケーション作成者が LSB に準拠しているかどうかをチェッ
クするために使えるツールを、幾つか提供しています。これらのツールは、リ
ンカの幾つかの機能と、チェックをおこなうための特別なライブラリを使用し
ています。もちろん、チェックをおこなうためには、ツール群をインストール
する必要があります。ツール群は LSB のウェブサイトから取得できます。取得
後は、C/C++ コンパイラとして単に "lsbcc" コンパイラを使ってください
(lsbcc は、何かしらの LSB 規則が守られていないときにその旨を出力するた
めのリンク用環境変数を、内部的に作成します)。

  $ CC=lsbcc make myapplication                                        
   (または)                                                            
  $ CC=lsbcc ./configure; make myapplication                           

続けて、当該プログラムが LSB で標準化されている関数だけを使用しているこ
とを確認するため、 lsbappchk プログラムを使用します。

  $ lsbappchk myapplication                                            

LSB のパッケージングガイドラインに従う必要もあります (例えば、RPM バー
ジョン 3 を使う、LSB 準拠のパッケージ名を使う、アドオンソフトウェアの場
合はデフォルトで /opt にインストールしなければならない、など)。詳細につ
いては、先に言及した文書と LSB ウェブサイトを参照してください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

5.11. ライブラリ群を統合して大きな共有ライブラリへ

最初に小さいライブラリ群を作成しておき、後からそれらを大きなライブラリ
へとマージしたいという場合、どうしたらよいでしょうか？このようなときは
、ld の "--whole-archive" オプションが便利です。このオプションは、.a フ
ァイル群を強制的に一つの .so ファイルへリンクする際に使用できます。

--whole-archive の使い方の一例を挙げます。

  gcc -shared -Wl,-soname,libmylib.$(VER) -o libmylib.so $(OBJECTS) \  
      -Wl,--whole-archive $(LIBS_TO_LINK) -Wl,--no-whole-archive \     
      $(REGULAR_LIBS)                                                  

ld 文書でも示されているとおり、最後に --no-whole-archive オプションを使
うようにしてください。そうしないと、gcc は標準ライブラリ群もマージしよ
うとしてしまいます。 --whole-archive に関する記述の提案と作成の両方に対
して、Kendall Bennett に感謝します。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6. 例

三つの方法 (静的ライブラリ、共有ライブラリ、動的ライブラリ) 全ての例を
以下に挙げます。 libhello.c ファイルは簡単なライブラリで、そのヘッダフ
ァイルとして libhello.h があります。 demo_use.c ファイルは、libhello ラ
イブラリを呼び出す簡単なプログラムです。続けて、コメント付きのスクリプ
ト (script_static と script_dynamic) を示します。これらのスクリプトは、
libhello ライブラリを静的ライブラリおよび共有ライブラリとして使う方法を
示すものです。さらに demo_dynamic.c と script_dynamic も挙げます。これ
らは、共有ライブラリを動的ライブラリとして使う方法を示すものです。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6.1. libhello.c ファイル

  /* libhello.c - ライブラリ使用方法を示します */                      
                                                                       
  #include <stdio.h>                                                   
                                                                       
  void hello(void) {                                                   
      printf("Hello, library world.\n");                               
  }                                                                    

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6.2. libhello.h ファイル

  /* libhello.h - ライブラリ使用方法を示します */                      
                                                                       
  void hello(void);                                                    

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6.3. demo_use.c ファイル

  /* demo_use.c -- "hello" ルーチンを直接使用する方法を示します */     
                                                                       
  #include "libhello.h"                                                
                                                                       
  int main(void) {                                                     
      hello();                                                         
      return 0;                                                        
  }                                                                    

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6.4. script_static ファイル

  #!/bin/sh                                                            
  # 静的ライブラリのデモ                                               
                                                                       
  # 静的ライブラリのオブジェクトファイル libhello-static.o を          
  # 作成します。静的ライブラリの例と動的ライブラリの例を明確に         
  # 区別するため、libhello-static という名前を使用しています。         
  # しかし、オブジェクトファイルや静的ライブラリの名前に               
  # "-static" を使う必要はありません。                                 
                                                                       
  gcc -Wall -g -c -o libhello-static.o libhello.c                      
                                                                       
  # 静的ライブラリを作成します。                                       
                                                                       
  ar rcs libhello-static.a libhello-static.o                           
                                                                       
  # この段階で、libhello-static.a をどこか他の場所へコピーして         
  # 使用することもできますが、デモが目的なので、libhello-static.a      
  # ライブラリはカレントディレクトリ内に置いたままにします。           
                                                                       
  # demo_use プログラムをコンパイルします。                            
                                                                       
  gcc -Wall -g -c demo_use.c -o demo_use.o                             
                                                                       
  # demo_use プログラムを作成します。-L. オプションにより、プロ        
  # グラム作成中、カレントディレクトリ "." が検索されます。この        
  # コマンドにより、libhello-static 内の関連するオブジェクト           
  # ファイルが demo_use_static に組み込まれます。                      
                                                                       
  gcc -g -o demo_use_static demo_use.o -L. -lhello-static              
                                                                       
  # プログラムを実行します。                                           
                                                                       
  ./demo_use_static                                                    

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6.5. script_shared ファイル

  #!/bin/sh                                                            
  # 共有ライブラリのデモ                                               
                                                                       
  # 共有ライブラリのオブジェクトファイル libhello.o を作成します。     
                                                                       
  gcc -fPIC -Wall -g -c libhello.c                                     
                                                                       
  # 共有ライブラリを作成します。libhello は C ライブラリに依存して     
  # いるので、C ライブラリとリンクさせるために -lc オプションを使い    
  # ます。                                                             
                                                                       
  gcc -g -shared -Wl,-soname,libhello.so.0 \                           
      -o libhello.so.0.0 libhello.o -lc                                
                                                                       
  # この段階で、libhello.so.0.0 を、/usr/local/lib 等のどこかの        
  # ディレクトリへコピーしてもかまいません。                           
                                                                       
  # シンボリックリンクを修正するため、ldconfig を呼び出す必要が        
  # あります。                                                         
                                                                       
  # soname を設定します。単に、                                        
  #                                                                    
  #   ln -sf libhello.so.0.0 libhello.so.0                             
  #                                                                    
  # を実行するだけでもよいですが、ldconfig にやってもらいましょう。    
                                                                       
  /sbin/ldconfig -n .                                                  
                                                                       
  # linker name を設定します。設定手順をより洗練させるならば、既に     
  # linker name が存在するかどうかを調べ、存在する場合には linker      
  # name を残すべきか否かを確認する必要があります。                    
                                                                       
  ln -sf libhello.so.0 libhello.so                                     
                                                                       
  # demo_use プログラムをコンパイルします。                            
                                                                       
  gcc -Wall -g -c demo_use.c -o demo_use.o                             
                                                                       
  # demo_use プログラムを作成します。-L. オプションにより、プロ        
  # グラム作成中、カレントディレクトリ "." が検索されます。これは、    
  # プログラム実行時にカレントディレクトリ "." が検索されるという      
  # 意味ではないので注意してください。                                 
                                                                       
  gcc -g -o demo_use demo_use.o -L. -lhello                            
                                                                       
  # プログラムを実行します。LD_LIBRARY_PATH を使って、どこに共有       
  # ライブラリが存在するかをプログラムに教える必要があることに         
  # 注意してください。                                                 
                                                                       
  LD_LIBRARY_PATH="." ./demo_use                                       

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6.6. demo_dynamic.c ファイル

  /*                                                                                  
   * demo_dynamic.c                                                                   
   *                                                                                  
   * 動的ローディングと "hello" ルーチンの使用方法を示します                          
   */                                                                                 
                                                                                      
  /*                                                                                  
   * 動的にライブラリをロードするルーチン用に dlfcn.h が必要                          
   */                                                                                 
  #include <dlfcn.h>                                                                  
                                                                                      
  #include <stdlib.h>                                                                 
  #include <stdio.h>                                                                  
                                                                                      
  /*                                                                                  
   * "libhello.h" をインクルードする必要がないことに注意して                          
   * ください。但し、関連情報を指定する必要があります。                               
   * dlsym() で取得する値を保持するための型が必要です。                               
   */                                                                                 
                                                                                      
  /*                                                                                  
   * "simple_demo_function" 型は、引数を取らず、何も値を返さない                      
   * 関数を示しています。                                                             
   */                                                                                 
                                                                                      
  typedef void (*simple_demo_function)(void);                                         
                                                                                      
  int main(void) {                                                                    
      const char *error;                                                              
      void *module;                                                                   
      simple_demo_function demo_function;                                             
                                                                                      
      /* 動的ライブラリをロードする */                                                
      module = dlopen("libhello.so", RTLD_LAZY);                                      
      if (!module) {                                                                  
          fprintf(stderr, "libhello.so をオープンできませんでした: %s\n", dlerror()); 
          exit(1);                                                                    
      }                                                                               
                                                                                      
      /* シンボルを取得する */                                                        
      dlerror();                                                                      
      demo_function = dlsym(module, "hello");                                         
      if ((error = dlerror())) {                                                      
          fprintf(stderr, "hello が見つかりませんでした: %s\n", error);               
          exit(1);                                                                    
      }                                                                               
                                                                                      
      /* 動的ライブラリ内の関数を呼び出す */                                          
      (*demo_function)();                                                             
                                                                                      
      /* 全て終了。きちんとクローズする */                                            
      dlclose(module);                                                                
                                                                                      
      return 0;                                                                       
  }                                                                                   

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

6.7. script_dynamic ファイル

  #!/bin/sh                                                            
  # 動的ライブラリのデモ                                               
                                                                       
  # libhello.so とその関連ファイルが既に作成されているものと           
  # 仮定しています (共有ライブラリのデモを参照してください)。          
                                                                       
  # demo_dynamic プログラムファイルをオブジェクトファイルへ            
  # コンパイルします。                                                 
                                                                       
  gcc -Wall -g -c demo_dynamic.c                                       
                                                                       
  # demo_use プログラムを作成します。このプログラムが使用する          
  # 唯一の特別なライブラリは、プログラム起動後までロードされ           
  # ないので、動的ライブラリをどこに探しにいけばよいかを教える         
  # 必要はありません。但し、動的ライブラリをロードするための           
  # ライブラリを含めるのに、-ldl オプションが必要となります。          
                                                                       
  gcc -g -o demo_dynamic demo_dynamic.o -ldl                           
                                                                       
  # プログラムを実行します。LD_LIBRARY_PATH を使って、どこに           
  # 動的ライブラリが存在するかをプログラムに教える必要がある           
  # ことに注意してください。                                           
                                                                       
  LD_LIBRARY_PATH="." ./demo_dynamic                                   

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

7. その他の情報源

ライブラリに関する特に有益な情報源を下記に挙げます。

 ・ Daniel Barlow による「The GCC HOWTO」。特に、この HOWTO では、ライ
    ブラリ作成用のコンパイラオプションと、ライブラリへの問い合わせ方法
    が説明されています。ここでは取り上げていない情報を扱っており、また
    、逆も真です。この HOWTO は、http://www.linuxdoc.org/ の Linux
    Documentation Project から入手できます (訳注：GCC HOWTO は、著者の
    希望により公開が中止されています)。
   
 ・ Tool Interface Standards (TIS) 委員会による「Executable and
    Linkable Format (ELF)」 (実はこの文書は、同委員会による Portable
    Formats Specification Version 1.1 内の一つの章です)。この文書は、
    ELF フォーマット (Linux や GNU gcc に特化したものではありません) に
    関する大量の詳細情報を提供するものです。 ftp://tsx-11.mit.edu/pub/
    linux/packages/GCC/ELF.doc.tar.gz を参照してください。MIT からファ
    イルを取得する場合、フォーマットが一般的なものではないので注意して
    ください。 gunzip して tar で解凍すると、「hps」ファイルができます
    。ファイルの最初と最後の行を削除し、「ps」ファイルに名前を変更すれ
    ば、いつものファイル名の、印字可能な PostScript ファイルを取得でき
    ます。
   
 ・ Hongjui Lu による「ELF: From the Programmer's Perspective」。 Linux
    と GNU gcc に特化した ELF 関連情報があります。 ftp://tsx-11.mit.edu
    /pub/linux/packages/GCC/elf.ps.gz で取得できます。
   
 ・ ld 文書「Using LD, the GNU linker」には、ld についてより詳しい説明
    があります。 http://www.gnu.org/software/binutils/manual/ld-2.9.1/
    で参照できます。
   
 ・ いつもの「info」文書、特に ld と gcc の info 文書も参照してください
    。
   
 ・ (訳者による追加) 佐野武俊さんによる「Linux C Library (libc) につい
    て」。 Linux C Library (libc) の概要について、その役割と歴史などを
    簡単にまとめたものです。 http://www.linux.or.jp/JF/JFdocs/
    libc-intro.html で参照できます。
   
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

8. 著作権とライセンス

この文書の著作権は David A. Wheeler にあり (Copyright (C) 2000)、GNU 一
般公有使用許諾 (GPL) により保護されます。代価なしで再配布しても構いませ
ん。文書の原文を「プログラム」と解釈し、次の条項も守ってください。

   
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
   
    本プログラムはフリー・ソフトウェアです。あなたは、Free Software
    Foundation が公表した GNU 一般公有使用許諾の「バージョン 2」或いは
    それ以降の各バージョンの中からいずれかを選択し、そのバージョンが定
    める条項に従って本プログラムを再頒布または変更することができます。
   
    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
   
    本プログラムは有用とは思いますが、頒布にあたっては、市場性及び特定
    目的適合性についての暗黙の保証を含めて、いかなる保証も行ないません
    。詳細については GNU 一般公有使用許諾書をお読みください。
   
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
    USA
   
    あなたは、本プログラムと一緒に GNU 一般公有使用許諾の写しを受け取っ
    ているはずです。そうでない場合は、Free Software Foundation, Inc.,
    59 Temple Place, Suite 330, Boston, MA 0211-1307, USA へ手紙を書い
    てください。
   
これらの条項は、他のウェブサイトがミラーリングをすることを許可するもの
ですが、

 ・ あなたのミラーサイトがマスターサイトから最新情報を自動的に取得する
    ようにし、
   
 ・ マスターサイトへのハイパーリンクと共にマスターサイトのロケーション 
    http://www.dwheeler.com/program-library/ を明示し、そして
   
 ・ 著者として、私 (David A. Wheeler) に謝辞をお願いします。
   
はじめの二つは、主に、過去のバグについて何度も言及されることを防ぐため
です。あなたが文書を適切にミラーリングしていないというだけの理由で、一
年前に直したバグに関する話を私は聞きたくはありません。マスターサイトへ
リンクを張ることにより、ユーザはあなたのサイトが最新のものであるかどう
かを確認できます。非常に厳しいセキュリティ要求があり、そのためにインタ
ーネットへ通常に接続するリスクを取ることができないサイトの問題を、私は
気にしています。これがあなたの状況に当てはまるならば、少なくとも、他の
ポイントへの接続を試みたり、折に触れてあなたの環境へのスニーカーネット
(sneakernet) アップデート (訳注：スニーカーネット＝ FD 等を持ち運びする
ことにより情報を共有するネットワーク) を試みるなどしてください。

このライセンスによれば、あなたはドキュメントを変更しても構いませんが、
あなたが書いたものではないものをあなたのものであると主張したり (つまり
盗用です)、変更したバージョンが原作であるかのようなふりをすることはでき
ません。著作物の変更は、著作物全体の著作権をあなたに譲渡するものではあ
りません。これは、著作権法の用語でいうところの「パブリックドメイン」の
著作物ではありません。ライセンスを細部に渡って参照してください。特に、
「You must cause the modified files to carry prominent notices stating
that you changed the files and the date of any change. (ファイルを変更
した旨とその変更日とを、変更したファイル上に明確に表示すること)」という
点には注意してください。ライセンスにより何が許可されているかについて質
問がある場合は、私に連絡をください。たいていの場合には、あなたの変更が
他のみなさんの変更と共にマスターコピーへ統合されるよう、あなたの変更を
統合者 (現在は David A. Wheeler) へ送るのがよいでしょう。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

9. 日本語訳について

Program Library HOWTO の日本語訳の維持管理は JF プロジェクト (http://
www.linux.or.jp/JF/) がおこなっています。日本語訳についてのご意見等は
JF プロジェクトのメーリングリスト <JF(a)linux.or.jp> までお願い致します
。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

9.1. 日本語訳履歴

Revision History                                                       
Revision 1.20-ja     2004 年 6 月 30 日      Revised by: 川崎貴彦      
原文バージョン 1.20 の対訳                                             

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

9.2. 謝辞

日本語に翻訳するにあたり、次の方々にご協力していただきました。ここにお
名前を挙げさせていただくとともに、感謝の意を表したいと思います (順不同
、敬称略、1.20-ja 以降)。

 ・ 中谷千絵
   
 ・ Seiji Kaneko
   
 ・ その他 JF プロジェクトのメンバーの方々
   
