2005年12月13日

statifier で動的リンクの実行ファイルを擬似的に静的リンクにする

statifierは動的リンクされた実行ファイルと共有ライブラリを1つのファイルにまとめるための Linux 用のツールです。動的リンクされた実行ファイルを別のホストにコピーして実行したい、というときなどに使えます。

 

statifier は現在のところ Debian パッケージになっていないようなので、ソースコードから make && make install します。

% tar zxf statifier-1.6.7.tar.gz
% cd statifier-1.6.7
% make
% sudo make install

使い方は簡単です。ターゲットの実行ファイルと新しいファイル名をコマンドオプションで指定して statifier を実行するだけで OK です。たとえば /usr/bin/php と共有ライブラリをまとめて php2 という 1つのファイルにまとめるには次のように実行します。

% statifier /usr/bin/php php2

このようにして作成した php2 ファイルは別のホストにコピーして実行できます。試しに PHP がインストールされていないホストに /usr/bin/php と php2 をコピーして実行してみたところ、php2 は無事に実行できました。/usr/bin/php をコピーしただけのファイルは、ライブラリが足りないため、実行できません。

% scp /usr/bin/php php2 foo.example.com:
% slogin foo.example.com

% ./php -v
./php: error while loading shared libraries: libzzip-0.so.12: cannot open shared object file: No such file or directory

% ./php2 -v
PHP 4.3.10-16 (cli) (built: Aug 24 2005 20:25:01)
Copyright (c) 1997-2004 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies

% ldd php |grep 'not found'
        libzzip-0.so.12 => not found
        libgssapi_krb5.so.2 => not found
        libkrb5.so.3 => not found
        libk5crypto.so.3 => not found

まとめ

statifier を使うと、動的リンクされた実行ファイルと共有ライブラリを 1つのファイルにまとめることができます。このようなツールが必要になることはあまりないと思いますが、覚えておくといざというときに便利かもしれません。

statifier と同様のツールにreducebind というものがあり、こちらは ptrace システムコールを使って、たった 311行の Cで実装されています。statifier との比較はRelated Projectsのページにあります。Background, More Details, More Problems といった開発者用文書もなかなか興味深いので興味のある方はどうぞ。

追記

八重樫さんから libnss はどうなるの、と指摘を受けました。glibc のシステムでは DNSなどを用いた名前解決に /lib/libnss*.so を必要とします。これらの共有ライブラリは最初に名前解決を行うときにロードされるため、 依存関係は明白にはわかりません (nm -D で gethost* を探すといった方法で探せるかもしれませんが)。ということで、statifier は生成したファイルに lib/libnss*.so を含めることができません。

試しに getent を statifier で処理して実行してみると、やはり実行時に /lib/libnss* をロードしていました。

% statifier =getent getent2
% LANG=C strace ./getent2 hosts >/dev/null 2>&1 |grep /lib
open("/lib/libnss_files.so.2", O_RDONLY) = 3
open("/lib/libnss_dns.so.2", O_RDONLY)  = 4
open("/lib/libresolv.so.2", O_RDONLY)   = 4

iconv(3) も実行時に /usr/lib/gconv/*.so をロードするため、同様の問題が起きます。glibc の gettext は内部で iconv を呼び出しているため、 gettext化したプログラムを statifier で扱うときは注意が必要にかもしれません。

さらに追記

鵜飼さんから、LD_PRELOAD して statifier を実行するとどうすか、と指摘を受けました。さっそく試したところ、環境変数 LD_PRELOAD に /lib/libnss*.so を含めておけば、生成されるバイナリにこれらを含めることができました。

% LD_PRELOAD="/lib/libnss_files.so.2 /lib/libnss_dns.so.2 /lib/libnss_dns.so.2" statifier =getent getent3
% LANG=C strace ./getent3 hosts >/dev/null 2>&1 |grep /lib
%

% ls -hs getent2 getent3
1.4M getent2  1.5M getent3

LD_PRELOAD なしで生成した getent2 より、LD_PRELOAD で 3つの共有ライブラリを指定した getent3 の方がサイズが大きくなっていることがわかります。

statifier のマニュアルによれば、--set というオプションで任意の環境変数を設定できるようです。というわけで、下のように実行する方が正解です。

statifier --set=LD_PRELOAD="/lib/libnss_files.so.2 /lib/libnss_dns.so.2 /lib/libnss_dns.so.2" =getent getent3

マニュアルには --set=LD_BIND_NOW=1 のようにセットすると生成したバイナリの起動が早くなるという説明も載っていました。