今年の夏は記録的な暑さで、猛暑日数や連続熱帯夜のありがたくない記録を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ケタの数字はそれぞれの機能を呼び出す際のアドレスで、静的ライブラリを使った実行可能バイナリでは全ての機能にアドレスが割り当てられています。
一方、同じコードを共有ライブラリを使うようにコンパイルすると、実行可能バイナリは4.4KBほど に収まります。
% gcc sample.c -lz
% ls -l a.out
-rwxr-xr-x 1 kojima users 4,447 9月 7日 20:16 a.out*
この実行可能バイナリには、静的ライブラリをリンクした時とは異なり、必要な機能のあちこちにアドレスが割りあてられない未完成な状態 になっています。
% nm a.out
08049554 d _DYNAMIC
08049628 d _GLOBAL_OFFSET_TABLE_
08048538 R _IO_stdin_used
w _Jv_RegisterClasses
08049544 d __CTOR_END__
...
08049648 b completed.5744
U crc32
08049640 W data_start
0804964c b dtor_idx.5746
08048420 t frame_dummy
08048448 T main
一方、この実行可能バイナリには、動作に必要な共有ライブラリ名が記録されており、それらの共有ライブラリを実行時に共にロードすることで、アドレスが未解決だった機能も利用できるようになります。
% ldd a.out
linux-gate.so.1 => (0xffffe000)
libz.so.1 => /usr/lib/libz.so.1 (0xb76f1000)
libc.so.6 => /lib/libc.so.6 (0xb75ae000)
/lib/ld-linux.so.2 (0xb7737000)
このように、静的ライブラリを使う場合は、コンパイル(リンク)時に必要な機能を全て解決して、動作時には完成済みのバイナリファイルを動かすのに対して、共有ライブラリでは、コンパイル時には未解決(アドレスが決まらない)な機能を残しておき、動作時に指定された共有ライブラリをロードして全ての機能のアドレスを解決した上でバイナリファイルを動かします。
例に示したように、共有ライブラリを使えば実行可能バイナリファイルのサイズが大幅に縮小できますが、共有ライブラリのメリットはそれだけではありません。
ライブラリに何らかの問題が見つかって修正版が公開された場合、静的ライブラリでは問題のあるバージョンのオブジェクトファイルを直接バイナリファイルに組み込んでしまっているため、新しいバージョンのライブラリを使うためにはバイナリファイルを再コンパイルしなければならない のに対し、共有ライブラリではライブラリが互換性を保つように考慮されている限り、共有ライブラリを更新するだけで実行時にロードされるライブラリは新しい修正版になる ので、バイナリファイルを再コンパイルする必要はありません。
このようなメリットのため、最近のOSではほぼ全てのバイナリファイルが共有ライブラリを使うようにコンパイルされていますが、共有ライブラリの開発者とそのライブラリを使うソフトウェアの開発者が独立に開発を進めるOSSの世界では、しばしば共有ライブラリの互換性問題が発生します。
先に紹介したように、共有ライブラリでは、そのライブラリを参照するバイナリファイルをコンパイルする際に必要な機能のチェックはするものの、実際にバイナリファイルに必要な機能を組み込むことはしません。そのため、共有ライブラリはそれを参照するバイナリファイルとは独自に更新することができるわけですが、バージョンアップした共有ライブラリで以前存在していた機能が無くなったりすると、それを参照しているバイナリファイルは必要な機能を解決できずにエラーになってしまいます。
このような問題を防ぐために、Linuxの世界では、共有ライブラリのバージョン付けに一定のルールを定めています。
そのルールは、共有ライブラリにはメジャーバージョン、マイナーバージョン、リリースバージョンの3つのバージョン番号を付け、バグフィクスといった小レベルの修正はリリースバージョンの違いで区別する、旧バージョンとの互換性を保ちつつ新機能を追加した、といった中レベルの変更はマイナーバージョンの違いで区別する、旧バージョンとの互換性は捨てて全面的に更新した場合はメジャーバージョンで区別する、というものです。
共有ライブラリがこのバージョン付けのルールに従う限り、そのライブラリを参照するバイナリファイルは、ライブラリのメジャー番号が変らない限り、そのライブラリを使い続けられるはずです。
Linuxの共有ライブラリでは、このルールをシンボリックリックを用いて実現しています。その例を、先に取りあげたlibzを使って紹介しましょう。
先に紹介したlibzの場合、本体の名前はlibz.so.1.2.3で、メジャーバージョンは1、マイナーバージョンは2、リリースバージョンは3、ということになります。
この共有ライブラリに対して/usr/lib/libz.so.1というメジャーバージョンまでのファイル名のシンボリックリンク と、/usr/lib/libz.soというバージョン番号の付かないファイル名のシンボリックリンク が用意されており、前者のメジャーバージョンまでのシンボリックリンクが実行時にローダーによって参照されるライブラリで、後者のバージョン番号の付かないシンボリックリンクはコンパイル時にリンカによって参照されるライブラリになっています。
libz.so.1.2.3という本体に対して、libz.so.1というシンボリックリンクを用意して、バイナリファイルはこのシンボリックリンクを参照する ようにしておくことで、libzの本体が、libz.so.1.2.4とか、libz.so.1.3.1のようにバージョンアップしても、それらが以前に提供していた機能を提供し続けて、バイナリファイルが参照する機能が解決できる限り、libz.so.1というシンボリックリンク経由で利用するバイナリファイルには変更が不要になる、それがLinuxの世界での共有ライブラリのバージョン付けルールになっています。
共有ライブラリのこのバージョン付けルールは、Linuxの世界では広く適用されているものの、それ以外の世界では必ずしも守られているわけではありません。前置きがずいぶん長くなっていましましたが、そのために痛い目に遭った、というのが今回の話題です。
Heimdalパッケージの共有ライブラリ問題
Heimdalは、Kerberos5認証システム のOSSによる実装です。Kerberosは元々、MITのAthenaプロジェクトで開発されたネットワーク経由の認証システムで、認証できたユーザに有効期限を持つチケットを発行し、そのチケットを使う限りKerberosに対応したソフトウェア間では再度ユーザを認証し直す必要がなく、シングルサインオンを可能にする便利な仕組みです。KerberosはRFCとして仕様が広く公開され、誰でも自由に利用できるので、MicrosoftのActive DirectoryやMacOS Xでも採用されています。
Plamo Linuxでは、4.72まではHeimdal-1.2 をパッケージとして提供していましたが、1.3系のHeimdalの開発が進み、今年の春にはHeimdal-1.3.3 が公開されたのに応じて、Heimdalパッケージを1.3.3に更新することにしました。
ところが、Heimdal-1.2系と1.3系では、共有ライブラリlibkrb5 のバージョンが異なっていて、1.2系ではlibkrb5.so.25.0.0、1.3系ではlibkrb5.so.26.0.0になっています。
先に解説したように、共有ライブラリではメジャーバージョン(この例では25と26)が異なれば、シンボリックリンクのレベルで区別できるので、当初はHeimdal-1.3.3パッケージの中に、古いlibkrb5.so.25.0.0も含めておいて、互換性の問題に対応したつもりでした。
先に紹介したように、共有ライブラリの仕組みでは、メジャー番号が異なるlibkrb5.so.25.0.0とlibkrb5.so.26.0.0は別物と見做されて、Heimdal-1.2環境で作成されたバイナリは25.0の方を、新しくHeimdal-1.3.3環境で作成されたバイナリは26.0の方を見るようになるはずでした。ところが、このHeimdal-1.3.3環境ではライブラリのシンボル参照が解決できない例が報告されました。
test.cとして、何もしないこんなソースコードを用意します。
リスト2 テストプログラム(test.c)
int main() {
return 0;
}
このソースコードをビルドする際に、sambaの機能を使うためのsmbclientライブラリ をリンクしてみます。このsmbclientライブラリ(/usr/lib/libsmbclient.so.0)は、Heimdal-1.2環境でビルドしたライブラリなので、Heimdal-1.2のlibkrb5.so.25を参照しています。
% ldd /usr/lib/libsmbclient.so.0
....
libgssapi.so.2 => /usr/heimdal/lib/libgssapi.so.2 (0xb75f1000)
libheimntlm.so.0 => /usr/heimdal/lib/libheimntlm.so.0 (0xb75ec000)
libkrb5.so.25 => /usr/heimdal/lib/libkrb5.so.25 (0xb753f000)
libhx509.so.3 => /usr/heimdal/lib/libhx509.so.3 (0xb7509000)
....
一方、Plamo-4.73で更新したHeimdal-1.3.3環境では、libkrb5.so.25もlibhx509.so.3も残しているので、これらを参照するバイナリも問題なくコンパイルできるはずです。
% ls -l /usr/heimdal/lib/libkrb5*
-rw-r--r-- 1 root root 578,998 6月 17日 16:00 /usr/heimdal/lib/libkrb5.a
-rwxr-xr-x 1 root root 1,155 6月 17日 16:00 /usr/heimdal/lib/libkrb5.la*
lrwxrwxrwx 1 root root 17 9月 7日 23:04 /usr/heimdal/lib/libkrb5.so -> libkrb5.so.26.0.0*
lrwxrwxrwx 1 root root 17 9月 7日 23:04 /usr/heimdal/lib/libkrb5.so.25 -> libkrb5.so.25.0.0*
-rwxr-xr-x 1 root root 860,213 3月 22日 20:10 /usr/heimdal/lib/libkrb5.so.25.0.0*
lrwxrwxrwx 1 root root 17 9月 7日 23:04 /usr/heimdal/lib/libkrb5.so.26 -> libkrb5.so.26.0.0*
-rwxr-xr-x 1 root root 481,470 6月 17日 16:00 /usr/heimdal/lib/libkrb5.so.26.0.0*
ところが、このHeimdal-1.3.3環境でlibsmbclientをリンクしようとすると、シンボルが解決できない旨のエラー になってしまいます。
% gcc test.c -lsmbclient
/usr/heimdal/lib/libkrb5.so.25: undefined reference to `bswap16'
/usr/heimdal/lib/libkrb5.so.25: undefined reference to `socket_get_port'
/usr/heimdal/lib/libkrb5.so.25: undefined reference to `strlcat'
/usr/heimdal/lib/libkrb5.so.25: undefined reference to `bswap32'
...
collect2: ld はステータス 1 で終了しました
あれれ、と思って、見つからないbswap16 というシンボルを調べてみると、これはlibkrb5.so.25自身には存在せず、libkrb5.so.25が依存するライブラリが提供する機能のようです。
% nm /usr/heimdal/lib/libkrb5.so.25 | grep -C3 bswap16
00071200 t bindText
00049700 t bind_principal
00070650 t blobReadWrite
U bswap16
U bswap32
000678b0 t btreeNext
00067a10 t btreePrevious
libkrb5.so.25が参照している共有ライブラリを調べると、以下のような結果になりました。
% ldd /usr/heimdal/lib/libkrb5.so.25
linux-gate.so.1 => (0xffffe000)
libhx509.so.4 => /usr/heimdal/lib/libhx509.so.4 (0xb778f000)
libwind.so.0 => /usr/heimdal/lib/libwind.so.0 (0xb7767000)
libcrypto.so.0.9.8 => /usr/lib/libcrypto.so.0.9.8 (0xb7632000)
libasn1.so.8 => /usr/heimdal/lib/libasn1.so.8 (0xb75b9000)
libcom_err.so.2 => /lib/libcom_err.so.2 (0xb75b5000)
libroken.so.18 => /usr/heimdal/lib/libroken.so.18 (0xb75a3000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0xb7572000)
libdl.so.2 => /lib/libdl.so.2 (0xb756e000)
libresolv.so.2 => /lib/libresolv.so.2 (0xb755a000)
libpthread.so.0 => /lib/libpthread.so.0 (0xb7541000)
libc.so.6 => /lib/libc.so.6 (0xb73fd000)
libcom_err.so.1 => /usr/heimdal/lib/libcom_err.so.1 (0xb73f9000)
/lib/ld-linux.so.2 (0xb78ad000)
これらの共有ライブラリそれぞれについて、bswap16やbswap32というシンボルが無いかを調べたところ、libroken.so.18 でrk_bswap16 というシンボル名を見つけました。
% nm /usr/heimdal/lib/libroken.so.18.1.0 | grep -C3 bswap16
U readdir64@@GLIBC_2.2
U realloc@@GLIBC_2.0
0000b290 T rk_asnprintf
000040c0 T rk_bswap16
00004090 T rk_bswap32
0000e970 T rk_cgetent
0000df30 T rk_cgetstr
あれれ、と思って、古いHeimdal-1.2パッケージを手元で展開し、その中のlibroken.so.18を調べてみると、bswap16というシンボルは確かに存在します。
% nm usr/heimdal/lib/libroken.so.18.1.0 | grep -C3 bswap16
00003b80 T base64_decode
00003cd0 T base64_encode
U bind@@GLIBC_2.0
00003e40 T bswap16
00003e10 T bswap32
000103e0 d bytes_short_units
00010400 d bytes_units
おかしいなぁ、と思って、ソースコードを調べてみると、Heimdal-1.2と1.3.3の間でlibrokenの提供するシンボル名などが変更されたにもかかわらず、バージョン番号は18.1.0のままになっているようです。
Linuxの共有ライブラリの仕組みでは、各共有ライブラリはライブラリ名とメジャーバージョン番号 で区別するので、同じライブラリの複数のバージョンを混在させることも可能ですが、この例のように、同じバージョン番号で異なるシンボル名を提供されてはお手上げになります。
このままでは、Heimdal-1.2との互換性を保つためにわざわざ残したlibkrb5.so.25やlibhx509.so.4が使えないので、「 シンボル名を変更するならライブラリのバージョン番号を変えろよ……」とボヤきながら、対応策を検討しました。
当初は、libkrb5.so.25を参照しているバイナリをコンパイルし直してHeimdal-1.3.3のlibkrb5.so.26を参照するようにしようかと考えましたが、調べてみるとlibkrb5.so.25を参照しているバイナリはかなり多く、全部を作り直すのはかなり時間がかかりそうです。一方、新しいPostgreSQLやhttpdはHeimdal-1.3.3用にビルドされているので、Heimdal-1.2に戻すのも困難です。また、libroken.so.18をイジって、以前と同じシンボル名を提供させる方法も考えましたが、セキュリティに関わるパッケージに、ヘタなローカルパッチをあてるのも気持ちよくありません。
さてどうしたものか…、としばらく悩みましたが、目的はHeimdal-1.2と1.3.3でlibrokenが区別できればいいわけなので、Heimdal-1.2のlibrokenのバージョンを1つ下げて17にして、libkrb5.so.25やlibhx509.so.4はこのlibroken.so.17を参照するようにビルドし直した上で、作り直したライブラリでHeimdal-1.3.3パッケージを更新することにしました。
% ldd /usr/heimdal/lib/libkrb5.so.25
....
libhx509.so.4 => /usr/heimdal/lib/libhx509.so.4 (0xb7638000)
libroken.so.17 => /usr/heimdal/lib/libroken.so.17 (0xb744b000)
....
% ldd /usr/heimdal/lib/libkrb5.so.26
....
libhx509.so.5 => /usr/heimdal/lib/libhx509.so.5 (0xb77fd000)
libroken.so.18 => /usr/heimdal/lib/libroken.so.18 (0xb75ad000)
....
この環境ならば、先にシンボルが解決できなくてエラーになっていたlibsmbclientもリンクできるようになりました。
% gcc test.c -lsmbclient
% ldd a.out
linux-gate.so.1 => (0xffffe000)
libsmbclient.so.0 => /usr/lib/libsmbclient.so.0 (0xb760c000)
...
libkrb5.so.25 => /usr/heimdal/lib/libkrb5.so.25 (0xb7383000)
libroken.so.17 => /usr/heimdal/lib/libroken.so.17 (0xb6fb6000)
....
一方、この方法ではビルドし直したHeimdal-1.2付属のバイナリ以外からlibroken.so.18を参照している場合、Heimdal-1.3.3のlibroken.so.18を見に行ってしまうので、同様のエラーが発生する可能性は残っています。しかしながら、librokenのコードを眺める限り、このライブラリはOSごとに異なる標準Cライブラリ間の差異を吸収して、同じコードで複数のプラットフォームに対応するための機能を提供している、いわばHeimdal内部の賄い用ライブラリなので、恐らくHeimdal以外から直接呼び出されることはないでしょう。
GCCのリンカオプション問題
Plamo-4.73に向けたHeimdalパッケージの更新は前節のような形でまとめたものの、落ちついて考えてみると、そもそものきっかけとなった何もしないコードに -lsmbclient でライブラリをリンクすると、そのライブラリが依存している他のライブラリも芋ヅル式にリンクされていくことがおかしいような気がします。
これはGCCのリンカの仕様の問題で、本来、リンカは静的ライブラリの場合のように、実際にオブジェクトファイルが参照しているシンボルをチェックして、そのシンボルを含むライブラリのみをリンクすべきなのに、共有ライブラリの場合はライブラリに記述されている依存関係をそのまま引きついでしまう ようになっていることが原因です。
この動作はオプションで変更可能で、リンカに--as-needed というオプションを渡せば、実際に参照しているシンボルを含むライブラリのみをリンクさせることができます。
GCCの場合、フロントエンドのgcc コマンドがアセンブラやリンカを自動的に起動するので、リンカに渡すオプションは、gccコマンドに-Wl,--as-needed や-Xlinker --as-needed のように指定します。
先の test.c の場合、このオプションを指定すると、このような結果になります。
% gcc -Wl,--as-needed test.c -lsmbclient
% ldd a.out
linux-gate.so.1 => (0xffffe000)
libc.so.6 => /lib/libc.so.6 (0xb7701000)
/lib/ld-linux.so.2 (0xb7877000)
smbclientライブラリをリンクするように指示されたものの、test.cは実際にはそのライブラリの機能を参照していないので、リンカはlibsmbclientを必要なライブラリとは見做さず、Cの標準ライブラリ(libc.so.6)のみを共有ライブラリとして記録しています。
libc.so.6以外の、linux-gate.so.1は共有ライブラリを使うバイナリファイルを実行するための機能をカーネルが提供するための仮想的なライブラリ、/lib/ld-linux.so.2はバイナリファイルのローダーで、この2つは共有ライブラリを利用する全てのバイナリファイルが利用します。
このオプションを指定するとリンカの仕事が多少増えますが、生成されたバイナリファイルには不要な共有ライブラリは記録されなくなるので、共有ライブラリを更新する際の互換性問題も生じにくくなります。
本来、リンクすべきライブラリはソフトウェアの開発者が把握しておくべきですが、最近の大規模なソフトウェアをautoconfやautomakeでビルドしようとすると、不要なライブラリまでリンク対象になって、依存関係が芋ヅル式に膨れあがってしまうこともよくあるようです。とりあえず、パッケージ作成時にはこのオプションがデフォルトで有効になるよう、Plamoのビルドスクリプトのテンプレートファイルなどは修正してみました。