Android: NDKを使ってみました(3)

今回はNDKを使って、ネイティブなスタンドアロンアプリケーション(アプリケーションフレームワークから独立したアプリ)をクロスコンパイルしてみました。
コンパイルしたのは、NDKのサンプル test-libstdc++、suコマンドのソース、bashのソース、です。

[ NDKのサンプル ]
ndk-buildコマンドでコンパイル

$ cp -r <NDKルート>samples/test-libstdc++ .
$ cd test-libstdc++/jni

$ ls
Android.mk  test-libstl.cpp

$ cat Android.mk
# A simple test for the minimal standard C++ library
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test-libstl
LOCAL_SRC_FILES := test-libstl.cpp
include $(BUILD_EXECUTABLE)

$ cat test-libstl.cpp
#include <cerrno>
#include <cstddef>
int main(void)
{
    return 0;
}

$ <NDKルート>ndk-build
Compile++ thumb  : test-libstl <= test-libstl.cpp
StaticLibrary  : libstdc++.a
Executable     : test-libstl
Install        : test-libstl => libs/armeabi/test-libstl

$ file ../libs/armeabi/test-libstl
../libs/armeabi/test-libstl: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped

コンパイラ arm-linux-androideabi-g++ を直接実行

$ <NDKルート>/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-g++ test-libstl.cpp --sysroot=<NDKルート>/platforms/android-9/arch-arm -I<NDKルート>/sources/cxx-stl/system/include
$ file a.out
a.out: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

[ suのソース ]
Androidのsuコマンドのソースをndk-buildでコンパイル

$ mkdir jni
$ cd jni
$ cp <Androidソースのルート>/system/extras/su/* .
$ ls
Android.mk MODULE_LICENSE_APACHE2 NOTICE su.c

suコマンドをスタティックリンクでコンパイルするために、またソースコードのincludeファイルを参照するために、Android.mkを次のように書き換えました。(Androidソースのルートが/usr/local/mydroidの例)
$ cat Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= su.c
LOCAL_MODULE:= su
LOCAL_C_INCLUDES += /usr/local/mydroid/system/core/include /usr/local/mydroid/system/core/include/private
LOCAL_LDFLAGS:= -static
include $(BUILD_EXECUTABLE)

$ <NDKルート>ndk-build
Compile thumb : su <= su.c
Executable : su
Install : su => libs/armeabi/su
$ file ../libs/armeabi/su
../libs/armeabi/su: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, stripped

なお、オリジナルのsuは実行ユーザのuidをチェックし、rootしか実行できないので、一般ユーザが使えるようにするには、uidチェックの部分を以下のようにコメントにしてから、コンパイルします。

$ vi su.c
...............
 62 //    if (myuid != AID_ROOT && myuid != AID_SHELL) {
 63 //        fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
 64 //        return 1;
 65 //    }
...............

[ bashのソース ]
Scientific Linux 6.1のbashのソースをダウンロードし、Android用にコンパイル
参考:
http://www.compdigitec.com/labs/2011/09/04/compiling-bash-for-android-2-...

以下のURLから bash-4.1.2-8.el6.src.rpm をダウンロードして、インストールし、bash-4.1.tar.gzを展開
ftp://ftp.riken.jp/Linux/scientific/6.1/SRPMS/vendor/

以下のURLからAndroid用のパッチをダウンロード
http://www.compdigitec.com/labs/files/bash-android.patch

$ ls
bash-4.1.tar.gz bash-android.patch
$ tar xvf bash-4.1.tar.gz
$ cd bash-4.1
$ patch -p1 < ../bash-android.patch
$ export CC="/usr/local/android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-gcc --sysroot=/usr/local/android-ndk-r6b/platforms/android-9/arch-arm/"
$ ./configure --host=arm-linux --enable-static-link --without-bash-malloc --disable-rpath --disable-nls
$ make

パッチを当てた後も、以下の2つのファイルでエラーが出ます。
lib/sh/winsize.c → redefinition of 'struct winsize'
 対処:struct winsize の定義部をコメントに
bashline.c → undefined reference to `setgrent', `getgrent', `endgrent'
 対処:NDKのライブラリにこの関数が無いためと思われるので、これを呼び出している関数bash_groupname_completion_function()が単にNULLをreturnするように変更。これは/etc/groupからグループ名を探す関数のようだが、Androidに/etc/groupファイルは無い。

もう1度 makeを実行
$ make
$ file bash
bash: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped

これで出来た。