LoginSignup
5
2

More than 5 years have passed since last update.

busyboxをスタティックバイナリ化する

Posted at

busyboxは1つのバイナリで多くの標準的なコマンドが実行できるソフトウェアであり、
組み込みLinuxなどのように領域に制限のある環境などで使用されることが多い。

また、busyboxは多くの一般的なプログラム同様にlibcなどの汎用的な機能を使用する動的ライブラリにリンクしています。

今回はbusyboxのビルドオプションを変更し、動的リンクして使用していたコードをバイナリに組み込む、スタティックバイナリ化します。
スタティックバイナリ化の目的は後ほど追って説明します。

今回は、busybox 1.22.1をビルドして使用します。
動作環境はRaspberry Pi(Model B)です。

busyboxを普通にビルドする

busyboxのソースコードをダウンロードしてビルドします。

$ wget http://busybox.net/downloads/busybox-1.22.1.tar.bz2
$ tar xvf busybox-1.22.1.tar.bz2
$ cd busybox-1.22.1
$ make

カレントディレクリにbusyboxのバイナリが出来上がったので、動的ライブラリにリンクしているかlddコマンドで調べてみます。

$ ldd busybox
    /usr/lib/arm-linux-gnueabihf/libcofi_rpi.so (0xb6f4a000)
    libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0xb6ecd000)
    libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6d9e000)
    /lib/ld-linux-armhf.so.3 (0xb6f58000)

libcやlibmに動的ライブラリにリンクしていることが確認できます。

次にstraceを使ってbusyboxが動的ライブラリを呼び出していることを確認します。

$ strace -e trace=open ./busybox > /dev/null
open("/etc/ld.so.preload", O_RDONLY)    = 5
open("/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so", O_RDONLY) = 5
open("/etc/ld.so.cache", O_RDONLY)      = 5
open("/lib/arm-linux-gnueabihf/libm.so.6", O_RDONLY) = 5
open("/lib/arm-linux-gnueabihf/libc.so.6", O_RDONLY) = 5

openシステムコールが動的ライブラリを呼び出していることが確認できました。

busyboxスタティックバイナリでビルドする

$ make menuconfigを実行してbusyboxのビルドオプションを変更します。
「Busybox Settings ---> Build Options --->Build BusyBox as a static binary (no shared libs)」にチェックを入れます。

kobito.1419065806.445939.png

ビルドオプションを変更したら$ makeで再度ビルドします。

出来上がったバイナリが変更通り、動的ライブラリにリンクしていないことを同様にlddstraceを使って確認します。

$ ldd busybox
    not a dynamic executable
$ strace -e trace=open ./busybox > /dev/null

変更前と違い動的ライブラリを呼び出していないことが確認できました。

なぜスタティックバイナリ化するのか?

使用容量の節約

通常のバイナリとスタティックバイナリでのファイルサイズを確認すると、

$ du -k busybox
920 busybox```

```スタティックバイナリ
$ du -k busybox
1840    busybox

スタティックバイナリの方が900K弱ほどがファイルサイズが大きくなっています。
これは本来動的リンクして呼び出していたコードをbusybox自身に組み込んだ(スタティック化)したためです。

しかしながら、組み込み機器やinitrdやinitramfsのようにカーネルイメージにrootfsを組み込む場合のように使用容量に厳しい制限がありbusyboxのみで済ませようとする環境では、libcすら不要なことがあります。

このような環境でスタティックバイナリ化したbusyboxを使用することで、libcを削除することで結果として使用容量を削減することができます。

busyboxにリンクしている動的ライブラリのサイズを確認してみます。

$ du -k /usr/lib/arm-linux-gnueabihf/libcofi_rpi.so
12      /usr/lib/arm-linux-gnueabihf/libcofi_rpi.so
$ du -k /lib/arm-linux-gnueabihf/libm-2.13.so
420 /lib/arm-linux-gnueabihf/libm-2.13.so
$ du -k /lib/arm-linux-gnueabihf/libc-2.13.so
1172    /lib/arm-linux-gnueabihf/libc-2.13.s
$ du -k /lib/arm-linux-gnueabihf/ld-2.13.so
124 /lib/arm-linux-gnueabihf/ld-2.13.so

もちろん、今の環境で上記ファイルを削除すると他のプログラムが動作なくなり大変なことになりますが、
これらの動的ライブラリが不要だとした時の削減量は、busyboxのバイナリサイズの増加量よりも大きくなります。

プログラム実行時間

これまで確認してきた通り、スタティックバイナリ化する前の通常のbusyboxはプログラム実行時に動的ライブラリを呼び出しており、
この呼び出し処理の有無によって非常に些細ですがプログラム実行時間に差がでます。

$ time ./busybox > /dev/null

real    0m0.013s
user    0m0.000s
sys 0m0.010s
$ time ./busybox > /dev/null

real    0m0.009s
user    0m0.000s
sys 0m0.000s

4msecほどスタティックバイナリの方が早くなっています。
些細な差ですが、busyboxはlsやechoからシェル(sh)まで幅広く使用されるので、
busyboxをメインに使用しているシステムでは何千回以上も呼び出されます。

試しに1000回ほど動かしてみると

$ time for n in `seq 1000`;do ./busybox > /dev/null ;done

real    0m12.600s
user    0m2.210s
sys 0m7.540s
$ time for n in `seq 1000`;do ./busybox > /dev/null ;done

real    0m8.355s
user    0m0.600s
sys 0m1.570s

約4.3sほどスタティックバイナリの方が早くなっており、ちょうど1回あたり4msecの差の1000倍ぐらいになっています。

まとめ

スタティックバイナリ化の効果は以下の通りになります。

busybox 動的リンク ファイルサイズ 実行に必要な環境の領域 実行時間
通常
スタティックバイナリ化

スタティックバイナリ化するかどうかはbusyboxの用途次第。
単一のバイナリのみで動くので、システムが壊れたとき非常用として持っておくもあり。

今回はスタティックバイナリ化の効果を確認するためにビルドからしましたが、
「busybox-static」というパッケージもあるのでお手軽に導入することもできます。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2