玩式草子─ソフトウェアとたわむれる日々

第14回 Plamo-4.73と共有ライブラリのトラブル

この記事を読むのに必要な時間:およそ 8.5 分

今年の夏は記録的な暑さで,猛暑日数や連続熱帯夜のありがたくない記録を9月になっても更新し続けているようです。

人間さまは9月に入っても続く猛暑でバテバテですが,前回紹介した物置部屋に置い出したサーバ機は特に熱暴走することもなく8月を越し,無事,9月2日にPlamo Linux 4.73をリリースすることができました。4.73というバージョン番号が示すように,今回も4.7系のメンテナンスリリースで,新機能よりも過去との互換性を重視したバージョンアップになっています。

互換性を重視したバージョンアップの際に特に気を使うのが共有ライブラリ(shared library)の扱いです。共有ライブラリは現在のOSにとっては必須の便利な機能ですが,便利な反面,名称や機能,使用法(API)⁠バージョン番号などを正しく管理しておく必要があります。

WindowsやMacOSのような,1つの企業がシステム用のライブラリを一元管理している商用OSとは異なり,世界中で分散開発されているオープンソースソフトウェアの世界では,大まかな合意はあるものの,それぞれの開発者が独自の方針で共有ライブラリを開発,提供しているため,ライブラリのバージョンや提供されている機能の擦り合せに注意が必要になることがあります。

今回は,Plamo-4.73の準備中に経験した,共有ライブラリに関するトラブル例を紹介しましょう。

共有ライブラリの基礎知識

実際のトラブル例を紹介するまえに,共有ライブラリについて簡単に紹介しておきましょう。⁠ライブラリ」というのは,複数のソフトウェアが利用するであろう機能をあらかじめコンパイルして1つにまとめたファイルです。その機能を使いたいソフトウェアは,コンパイル時(厳密にはリンク時)や実行時にそのファイルを参照して,必要な機能を利用します。

Linuxの場合,ライブラリには静的ライブラリ(static library)共有ライブラリ(shared library)という2種類のライブラリが存在します。

共有ライブラリは「動的ライブラリ(dynamic library)⁠と呼ばれることもあります。

これらライブラリは/libディレクトリや/usr/libディレクトリに配置され,"lib"を接頭辞にしてライブラリの名前をつなげ,静的ライブラリには ".a"(Archive)という拡張子,共有ライブラリには ".so"(Shared Object)とバージョン番号を合わせた拡張子が付くことになっています。

たとえば,gzip圧縮/展開機能を提供するzlibライブラリでは,libz.aという静的ライブラリとlibz.so.1.2.3という共有ライブラリを用意しています。

% ls /usr/lib/libz.*
/usr/lib/libz.a*  /usr/lib/libz.so@  /usr/lib/libz.so.1@  /usr/lib/libz.so.1.2.3*

上記のうち,libz.soとlibz.so.1はそれぞれlibz.so.1.2.3を参照するシンボリックリンクです(詳細は後述)⁠

静的ライブラリは,コンパイル済みのオブジェクトファイルをarコマンドで単純にまとめた形式で,libz.a には以下の12のオブジェクトファイルが含まれていました。

% ar t /usr/lib/libz.a
adler32.o
compress.o
crc32.o
gzio.o
uncompr.o
deflate.o
trees.o
zutil.o
inflate.o
infback.o
inftrees.o
inffast.o

それぞれのオブジェクトファイルが提供する機能はnmコマンドで調べることができます。

% nm -A /usr/lib/libz.a
/usr/lib/libz.a:adler32.o:00000000 T adler32
/usr/lib/libz.a:adler32.o:00000270 T adler32_combine
/usr/lib/libz.a:compress.o:000000b0 T compress
/usr/lib/libz.a:compress.o:00000000 T compress2
/usr/lib/libz.a:compress.o:00000160 T compressBound
/usr/lib/libz.a:compress.o:         U deflate
/usr/lib/libz.a:compress.o:         U deflateEnd
/usr/lib/libz.a:compress.o:         U deflateInit_
/usr/lib/libz.a:crc32.o:00000010 T crc32
/usr/lib/libz.a:crc32.o:000002a0 T crc32_combine
/usr/lib/libz.a:crc32.o:00000000 r crc_table
...

静的ライブラリは,ソースコードをコンパイルする最終段階(リンク時)にリンカからチェックされます。リンカは,ソースコードをコンパイルしたオブジェクトファイルが参照している機能を調べ,指定されたライブラリからその機能を含むオブジェクトファイルを取り出して,ソースコードをコンパイルしたオブジェクトファイルと結びつけて実行可能なバイナリファイルを生成します。

上記リストで00000000や00000270と表示されているのは,その機能を呼び出すためのアドレスになります。

静的ライブラリが参照されるのはソースコードのコンパイル時であるのに対して,共有ライブラリが参照されるのは,コンパイルされたバイナリファイルの実行時です。

共有ライブラリでは,静的ライブラリのように各機能がオブジェクトファイルごとに分割されてはおらず,全体が1つのアドレス空間に配置されるようになっています。

% nm -A /usr/lib/libz.so.1.2.3
/usr/lib/libz.so.1.2.3:000098a5 t .L147
/usr/lib/libz.so.1.2.3:00009929 t .L155
/usr/lib/libz.so.1.2.3:0000bb96 t .L156
...
/usr/lib/libz.so.1.2.3:000017a0 T adler32
/usr/lib/libz.so.1.2.3:00001a30 T adler32_combine
/usr/lib/libz.so.1.2.3:0000ff20 r base_dist
/usr/lib/libz.so.1.2.3:00010020 r base_length
...

共有ライブラリを利用する場合,リンカはオブジェクトファイルが参照している機能が指定されたライブラリに存在することは調べるものの,静的ライブラリの場合のように,その機能を取り出してオブジェクトファイルとリンクすることはせず,必要となる共有ライブラリの名前のみを記録して実行可能なバイナリファイルを生成します。

このように作成されたバイナリファイルは,実行時にローダーによって必要な共有ライブラリと共にメモリ上に読み込まれ,共有ライブラリ上に存在する機能が使えるようになった上で実行されます。

言葉だけで説明するのはわかりにくいと思うので,実際にサンプルコードを使って説明してみましょう。

libz が提供するcrc32()という機能を使う,こんなコード(sample.c)を用意します。

リスト1 sample.c

 #include <zlib.h>
 
 int main() {
     crc32(0,0,0);
     return 0;
 }

このコードを静的ライブラリを使うようにコンパイルするには,gcc に-staticオプションを指定します。

% gcc -static sample.c -lz

この結果,作成された実行可能バイナリ(a.out)670KBほどになります。

% ls -l a.out
-rwxr-xr-x 1 kojima users 671,785  9月  7日  20:11 a.out*

作成されたバイナリファイルには,crc32() の機能も含まれています。

% nm a.out
080a581c t .L100
080a179e t .L101
080a5809 t .L101
...
0806a470 t compute_offset
080506f0 W connect
080c8840 b connected
08048270 T crc32
08048500 T crc32_combine
080a6820 r crc_table
080689d0 t critical_factorization
....

それぞれの機能に付いている8ケタの数字はそれぞれの機能を呼び出す際のアドレスで,静的ライブラリを使った実行可能バイナリでは全ての機能にアドレスが割り当てられています。

著者プロフィール

こじまみつひろ

Plamo Linuxとりまとめ役。もともとは人類学的にハッカー文化を研究しようとしていたものの,いつの間にかミイラ取りがミイラになってOSSの世界にどっぷりと漬かってしまいました。最近は田舎に隠棲して半農半自営な生活をしながらソフトウェアと戯れています。

URLhttp://www.linet.gr.jp/~kojima/Plamo/index.html

コメント

コメントの記入