Android NDKでautotoolsベースのライブラリをビルド出来ないかと何日か試していたのだけど、けっこう上手く行っていなかった。昨日何とかそれらしい共有ライブラリのビルドが出来たので、まだ動作確認出来ていないけどとりあえず載せておこうと思う。基本部分は先日書いた簡単なビルドの紹介文から。
export ANDROID_ROOT=/cygdrive/c/android-ndk-r4b export PATH=$PATH:$ANDROID_ROOT/build/prebuilt/windows/arm-eabi-4.2.1/bin export ANDROID_VER=8 # could be 3, 4, 5 or 8 echo RUN: ./configure --host=arm-eabi CC=arm-eabi-gcc CPPFLAGS="-I$ANDROID_ROOT/build/platforms/android-$ANDROID_VER/arch-arm/usr/include/" CFLAGS="-nostdlib" LDFLAGS="-Wl,-rpath-link=$ANDROID_ROOT/build/platforms/android-$ANDROID_VER/arch-arm/usr/lib/ -L$ANDROID_ROOT/build/platforms/android-$ANDROID_VER/arch-arm/usr/lib/" LIBS="-lc " echo THEN RUN: make echo NOTE: to link with libc, use: -L $ANDROID_ROOT/build/platforms/android-X/arch-arm/usr/lib -lc echo NOTE: to generate shared library from static library, run: echo $ arm-eabi-gcc -nostdlib -shared -s -o YOUR_LIBRARY.so --whole-archive -Wl,-whole-archive YOUR_PROJECT/src/.libs/YOUR_LIBRARY.a -Wl,-no-whole-archive --no-whole-archive -L /cygdrive/c/android-ndk-r4b/build/platforms/android-$ANDROID_VER/arch-arm/usr/lib -lc
これは実際には環境変数などを適当に設定しているだけで、コマンドの実行は使う人に見せているだけだ。その内容も、configure, makeした後、手動でldを呼び出して、静的ライブラリ(.aファイル)から共有ライブラリ(.soファイル)を生成する、というだけの手順だ。ビルドした.soファイルは手動で jni/../libs にコピーする必要がある(JNIを使う場合)。
ld(コマンドライン上はarm-eabi-gcc)の呼び出しで行っているのは、autotoolsに基づくconfigureとmakeでビルドした静的ライブラリから、jniで使用するような動的ライブラリを生成するということだ。
このオプションで重要なのは、-Wl,-whole-archive と -Wl,-no-whole-archive で、これは静的ライブラリで参照されていないコードを削除するというldの機能を制御することにある。
通常のhello worldをビルドする場合などを例として考えれば分かると思うけど、hello worldではprintfで使用されるコードだけがlibcの静的ライブラリ(libc.a)から取り出されて、実行ファイル上にリンクされて埋め込まれる。libc全体をリンクしたら、hello worldの実行プログラムのサイズはlibcを上回るものになるはずだけど、そうならないのは、libcで使われていないコードをstripしてリンクしているからだ。
でも、このstripする動作は、ビルドされた静的ライブラリの機能全体を共有ライブラリに取り込みたい場合には適合しない。ライブラリの機能を全て参照するコードなんて(通常は)書かれていないし、自分でも書かない(いずれにしろ自動化できない)からだ。でも、-Wl,-whole-archiveを指定すると、このstrippingを行わないようにしてくれる(ちなみに "-Wl,-whole-archive" で1つのオプションになっていることに注意)。
stripが適正に行われていないと、共有ライブラリの生成はエラーなしで成功しているかのように見えるけど、生成されたファイルは1kb前後しか無く、リンクが正しく行われていないことが分かる。
あと標準ライブラリは自動的に取り込まず、-nostdlibでいったん無効にした後、-lc で明示的にlibcを指定している(-Lでライブラリのロードパスを指定してリンクするbionic libcを選んでいる)。
本当はこの辺の事はndk-buildで出来ればよいのだけど、後述する参考サイトにある通り、どうやら途中までしか出来ていないようだ。
最後に、基本情報として使用したサイト以外の参考情報を載せておこうと思う:
- GNU リンカldの使い方(マニュアル)。これを全部読んだわけではなく、静的ライブラリのリンクに関する引数を探した。
- このフォーラムエントリでは、ビルドオプションの指定で何が足りないとどういうエラーになるかというのを、詳細に説明している。とても教育的でエラーフレンドリだ。
- このstackoverflowのエントリでは、NDK r4bで静的ライブラリをAndroid.mkでビルドに取り込みつつ不要なコードをstripしない方法について説明してある。どうやらNDK r4bでちゃんと実装されていない部分のようだ。