前回は「ビルド」という作業の中身を解説しました。今回は、その中でも「リンク」に焦点を当てて、その作業の内容を解き明かします(編集部)
前回はビルドとは一体どういう作業なのかということを解説しました。ざっと復習すると、ビルドとはコンパイルとリンクを実行して、ソースコードから実行可能な形式のファイルを作ることです。コンパイルにはコンパイラ、リンクにはリンカというツールが使われるのでした。
リンカへの入力は、2種類あります。1つはソースコードをコンパイルした結果であるオブジェクトファイル。これは前回解説しました。もう1つはライブラリです。
ライブラリとは一般に、再利用可能なソフトウェアの集合を指します。汎用性の高い処理はライブラリにしておけば、次に必要になったときに、もう一度作らずに済むというわけです。世界には多種多様なライブラリが存在しています。Apache HTTP Server(httpd)やPHPなどのソフトウェアも既存のライブラリをたくさん利用しています。必要なライブラリをそろえることは、ビルド時に解決しなければならない問題の1つです。
OSが提供する基本的なライブラリは、各種のコマンドとともにOSを構成する1つの要素です。特に、C言語の標準ライブラリであるlibcはほとんどのコマンドが利用している最重要ライブラリです。Linuxをインストールすれば、こういった基本的なライブラリも一緒にインストールされます。
また、インストールされるライブラリはパッケージになっています。標準でインストールされないライブラリは、後からパッケージを使ってインストールできます。
これらのライブラリのファイルは、/libや/usr/lib、64bit環境に対応するものは/lib64や/usr/lib64にあります(図1)。
$ ls -lp /lib64 total 20888 drwxr-xr-x 2 root root 4096 Apr 18 19:44 bdevid/ drwxr-xr-x 2 root root 4096 May 15 04:49 dbus-1/ drwxr-xr-x 2 root root 4096 Apr 18 19:43 device-mapper/ drwxr-xr-x 2 root root 4096 Dec 17 17:51 iptables/ -rwxr-xr-x 1 root root 139504 Apr 27 06:15 ld-2.5.so lrwxrwxrwx 1 root root 9 May 14 06:57 ld-linux-x86-64.so.2 -> ld-2.5.so lrwxrwxrwx 1 root root 20 May 14 06:58 ld-lsb-x86-64.so -> ld-linux-x86-64.so.2 lrwxrwxrwx 1 root root 20 Apr 18 19:44 ld-lsb-x86-64.so.3 -> ld-linux-x86-64.so.2 -rwxr-xr-x 1 root root 8904 Apr 27 06:15 libBrokenLocale-2.5.so lrwxrwxrwx 1 root root 22 May 14 06:57 libBrokenLocale.so.1 -> libBrokenLocale-2.5.so -rwxr-xr-x 1 root root 22336 Apr 27 06:15 libSegFault.so lrwxrwxrwx 1 root root 15 Apr 18 19:29 libacl.so.1 -> libacl.so.1.1.0 -rwxr-xr-x 1 root root 27920 Jan 27 2010 libacl.so.1.1.0 -rwxr-xr-x 1 root root 20064 Apr 27 06:15 libanl-2.5.so lrwxrwxrwx 1 root root 13 May 14 06:57 libanl.so.1 -> libanl-2.5.so lrwxrwxrwx 1 root root 18 Apr 18 19:29 libasound.so.2 -> libasound.so.2.0.0 -rwxr-xr-x 1 root root 907552 Jan 21 2009 libasound.so.2.0.0 lrwxrwxrwx 1 root root 16 Apr 18 19:29 libattr.so.1 -> libattr.so.1.1.0 -rwxr-xr-x 1 root root 17888 Jan 6 2007 libattr.so.1.1.0 lrwxrwxrwx 1 root root 17 Apr 18 19:43 libaudit.so.0 -> libaudit.so.0.0.0 (以下略)
ご覧の通り、ライブラリのファイル名は全てlibから始まる規則になっています。「foo」というライブラリであれば「libfoo.拡張子」となるわけです。そして同じライブラリ名でのシンボリックリンクが多数あります。これは後ほど解説します。
ライブラリは、そのリンク方法によって2種類に分けられます。シェアード(共有)ライブラリとスタティック(静的)ライブラリです。シェアードライブラリをリンクすることをダイナミック(動的)リンク、スタティックライブラリではスタティックリンクと呼びます。シェアードライブラリはダイナミックリンクライブラリ(DLL)とも呼びます。
Linuxの各コマンドなどは、ほぼ全てシェアードライブラリを利用しています。図1の例で示したものも全てシェアードライブラリで、拡張子が.soとなっています(Shared Objectの略)。しかしここでは最初に、仕組みが単純なスタティックライブラリから見ていきましょう。
そもそも、ライブラリの実体はコンパイル済みのオブジェクトの集合体です。オブジェクトファイルをarコマンドでアーカイブしたもので、拡張子は.aです。スタティックリンクでは、ライブラリ内から必要なオブジェクトをピックアップしてリンクし、実行可能な形式のファイルを生成します(図2)。つまり、元のライブラリ内からオブジェクトがコピーされて実行形式に統合されるのです。
この場合、元のライブラリがバージョンアップしたとしても、実行形式に統合したライブラリは以前のバージョンのままです。また、同じライブラリを利用している別のソフトウェアがあったとすると、同じライブラリを複数のソフトウェアが別々に組み込んでいることになります。つまり、ディスクを消費するのです。
そこでシェアードライブラリとダイナミックリンクの登場です。ダイナミックリンクは、実行可能な形式のファイルを起動するときに、必要なライブラリとリンクしてしまうというものです。ダイナミックリンクを利用するとき、ビルド時のリンクでは、リンクするシェアードライブラリのチェックと、ライブラリ名を実行可能な形式のファイルに結び付けるところまで処理します。こうすると、シェアードライブラリが更新されれば新しいものを利用できますし、ファイルの使用量にも無駄がなくなります。
実行可能な形式のファイルがどういったシェアードライブラリとリンクしているのかということは、lddコマンドで確認できます(図3)。このコマンドはビルド時のトラブルシュートによく使います。
$ ldd /bin/bash linux-vdso.so.1 => (0x00002b7f0dbe3000) libtermcap.so.2 => /lib64/libtermcap.so.2 (0x0000003290c00000) libdl.so.2 => /lib64/libdl.so.2 (0x0000003290800000) libc.so.6 => /lib64/libc.so.6 (0x0000003290400000) /lib64/ld-linux-x86-64.so.2 (0x0000003290000000)
lddで確認したファイルのうち、libで始まらないものが2つありますが、これは実行可能な形式のファイルには必須もので常にリンクされます。無視しても問題ないでしょう。ちなみにlibcもリンクするように明示しなくてもリンクされます。前回作成したhelloコマンドをlddで表示してみてください。ビルドしたとき、ライブラリは何も明示しませんでしたが、libc含め3つのライブラリがリンクされていることが分かります。
図3の表示の「=>」の左側にはライブラリファイル名、右側にはそのフルパスがあります。このように分かれているのは、実行形式にはライブラリ名だけがリンクされていて、実行時に実際のライブラリファイルを探すようになっているためです。右側は探した結果をフルパスで表示したものというわけです。
実行時にリンクするファイルを探す場所は、設定ファイルや環境変数で設定できます。コマンドが環境変数PATHに従って検索されるのと同じです。この仕組みによって、別の場所にインストールしたライブラリを利用するなど、ライブラリの使い分けができるようになっています。
もしライブラリファイルが見つからない場合、lddコマンドを実行すると「not found」という結果になります。もちろんそのファイルは実行できません。リンク時にはライブラリの存在と内容を確認しますから、本来なら起きないはずの問題ですが、後からライブラリのファイルを削除したなど、実行時に検索パスにライブラリが存在していないときに発生します。
シェアードライブラリは、実行時にリンクされるという特性から、ビルド後のライブラリ側の変化によって問題が起きる可能性があるのです。パッケージシステムを使っている限りは、個々のライブラリパッケージとの依存関係をきちんと設定してあるので、こういった問題は起こりません。独自にビルドするときは念頭に置いておきましょう。
実行可能なファイルとは別に、ライブラリ側だけでもバージョンアップできるというのがシェアードライブラリの利点と言えますが、互換性を考えると問題点にもなります。互換性が損なわれるようなバージョンアップをすると、リンクしている既存の実行可能なファイルが正しく動作しなくなってしまうのです。これを防ぐため、ライブラリのファイル名の方を変更して対処しています。具体的には、実体のファイル名は変えずにシンボリックリンクを利用するのです(図4)。
互換性を保てないようなバージョンアップをするときは、拡張子の直後にあるバージョン番号でライブラリを区別します。図3のlddの結果でもバージョン番号が確認できます。実体のファイル名にはマイナーバージョンまで示されています。
互換性を確保できるバージョンアップであれば、リンク先のライブラリのマイナーバージョンを上げて別名で配置し、実際に切り替えるときはシンボリックリンクを張り直します。バージョンアップ後、仮に問題が起きたとしてもシンボリックリンクを元に戻せば、すぐに以前の問題のない状態に戻せるわけです。
パッケージシステムを使っていると、このあたりは意識する必要がありません。パッケージ配布前に問題が起きないと確かめられている前提ですので、パッケージ更新時には以前のライブラリは自動的に削除されます。
バージョン番号のまったくない.soで終わっているファイルは、ビルド中のリンク時に参照されるものです。このファイルのシンボリックリンク先により、ライブラリのメジャーバージョンが決まる仕組みになっているわけです。
今回はここまでです、次回はmakeについて解説します。
Copyright © ITmedia, Inc. All Rights Reserved.