2010年09月09日

ARM Debian/Ubuntuの上でAndroidを動かす

先日のCELFテクニカルジャンボリーでは、ARMのUbuntuの上でAndroidを動かすデモを行いました。

これを応用することで、既存のユーザーモードデバイスドライバやそれに付随するデーモンプロセスを無変更のままでAndroidと共存させることができます。

その続報です。

Android側からSDメモリカードが使えるようになりました。



既存のLinux環境の上でまるごとのAndroidを動かす方法

前回のスライドのおさらいです。

  1. そのボード上にAndroidをポーティングする。NFSrootにするとよい。
  2. その時使ったAndroid用にpatchをあてたカーネルを使って既存のLinuxを動かす。
  3. 既存のLinuxのユーザーランドに /androidというディレクトリを作って、そこにandroidのユーザーランドのファイルを全て置く。
  4. 既存のLinuxが動いている状態でそのshellからrootで以下のコマンドを実行する。
# ls -l /android
total 180
drwxr-xr-x  3 root root   4096 2010-09-08 17:00 acct
drwxrwx---  3 user 2001   4096 2010-09-08 17:00 cache
dr-x------  2 root root   4096 2010-09-08 17:00 config
lrwxrwxrwx  1 root root     17 2010-09-08 17:00 d -> /sys/kernel/debug
drwxrwx--x 13 user user   4096 2010-09-08 17:01 data
-rw-r--r--  1 root root    118 2010-09-08 15:48 default.prop
drwxr-xr-x  9 root root   2360 1970-01-01 09:07 dev
lrwxrwxrwx  1 root root     11 2010-09-08 17:00 etc -> /system/etc
-rwxr-x---  1 root root 107420 2010-09-08 15:48 init
-rwxr-x---  1 root root   1677 2010-09-08 15:48 init.goldfish.rc
-rwxr-x---  1 root root  13046 2010-09-08 15:48 init.rc
-rwxr-x---  1 root root  13041 2010-09-08 15:48 init.rc.org
drwxrwxr-x  5 root user   4096 2010-09-08 17:00 mnt
dr-xr-xr-x 79 root root      0 1970-01-01 09:00 proc
drwxr-x---  2 root root   4096 2010-09-08 15:48 sbin
lrwxrwxrwx  1 root root     11 2010-09-08 17:00 sdcard -> /mnt/sdcard
drwxr-xr-x 12 root root      0 1970-01-01 09:00 sys
drwxr-xr-x 10 root root   4096 2010-08-27 10:47 system
#
# chroot /android /init

既存のLinux環境の代表としてDebianとUbuntuで説明しています。

ARM Debian lennyではこれで問題なくAndoridが起動しました。つまりDebian側もAndroid側も修正無しです。

この状態で、Debian側のsshdは生きていてsshでログインが可能です。それと同時にAndroidのプロセスが動いています。Android側からSDメモリカードに読み書きすることも可能です。

pstreeコマンドでこの時のプロセスの状態を見ると以下のようになりました。

pstree

ARM Ubuntu 10.04でやったときには問題が発生して、Android側で回避していました。その詳細と対策を以下で説明します。

Android on Ubuntuで発生していた問題

前回のスライドの通り、UbuntuからchrootでAndroidを起動した場合、voldとnetdがSEGVで落ちていました。

logcatによるログは以下の通り。

I/Vold    ( 3043): Vold 2.1 (the revenge) firing up
D/Vold    ( 3043): Volume sdcard state changing -1 (Initializing) -> 0 (No-Media)
W/Vold    ( 3043): No UMS switch available
I/DEBUG   ( 2999): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   ( 2999): Build fingerprint: 'generic/generic/generic/:2.2/FRF91/eng.koba.20100827.102349:eng/test-keys'
I/DEBUG   ( 2999): pid: 3043, tid: 3044  >>> /system/bin/vold <<<
I/DEBUG   ( 2999): signal 11 (SIGSEGV), fault addr 4001a000
I/DEBUG   ( 2999):  r0 4001a000  r1 ffffeea5  r2 400090ea  r3 00000001
I/DEBUG   ( 2999):  r4 4000902c  r5 aef04178  r6 000162b0  r7 000000be
I/DEBUG   ( 2999):  r8 00100000  r9 aef01cad  10 10000000  fp 40009008
I/DEBUG   ( 2999):  ip 00000000  sp 100ffda8  lr aef0250f  pc aef0267c  cpsr 80000030
I/DEBUG   ( 2999): cannot get registers: I/O error
I/DEBUG   ( 2999):          #00  pc 0000267c  /system/lib/libsysutils.so
I/DEBUG   ( 2999):          #01  pc 0000250a  /system/lib/libsysutils.so
I/DEBUG   ( 2999):          #02  pc 00001b16  /system/lib/libsysutils.so
I/DEBUG   ( 2999):          #03  pc 00001cae  /system/lib/libsysutils.so
I/DEBUG   ( 2999):          #04  pc 00010ec0  /system/lib/libc.so
I/DEBUG   ( 2999):          #05  pc 000109b0  /system/lib/libc.so
I/DEBUG   ( 2999): 

このログで以下の情報がわかります。

  • SEGVを起こしたときのPCは0xaef0267c
  • アクセスしたアドレスは 0x4001a000, 同じ値がr0に入っている。
  • (おそらく r0をベースアドレスにしたロード命令かストア命令)
  • PCのアドレスは /system/lib/libsysutil.soの中。
  • このライブラリは固定アドレスにprelinkされているので、libsysutil.soの中のオフセット 0x0000267c を調べればどの関数かわかる。
$ cd out/target/product/generic/symbols/system/lib
$ nm -n -C libsysutils.so |less
  ...
00002611 T NetlinkEvent::dump()
00002640 t $d
0000264c t $t
0000264d T NetlinkEvent::decode(char*, int)
00002794 t $d
000027bc t $t
000027bd T NetlinkEvent::~NetlinkEvent()
  ...

PCはNetlinkEvent::decode(char*, int)の中のどこかであることまでわかりました。

この関数を見ただけで怪しいところが臭ってきました。

bool NetlinkEvent::decode(char *buffer, int size) {
    char *s = buffer;
    char *end;
    int param_idx = 0;
    int i;
    int first = 1;

    end = s + size;
    while (s < end) {
        if (first) {
            char *p;
            for (p = s; *p != '@'; p++); // <== あれ?! これはマズいでしょう
            p++;
            mPath = strdup(p);
            first = 0;
        } else {
            if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
                char *a = s + strlen("ACTION=");
                if (!strcmp(a, "add"))
                    mAction = NlActionAdd;
                else if (!strcmp(a, "remove"))
                    mAction = NlActionRemove;
                else if (!strcmp(a, "change"))
                    mAction = NlActionChange;
            } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))
                mSeq = atoi(s + strlen("SEQNUM="));
            else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))
                mSubsystem = strdup(s + strlen("SUBSYSTEM="));
            else
                mParams[param_idx++] = strdup(s);
        }
        s+= strlen(s) + 1;
    }
    return true;
}

どうやら、メッセージの先頭から'@'までを読み飛ばそうとしているようですが、その時にデータのサイズをチェックしていません! そのため、もしメッセージに'@'が含まれていない場合にはバッファを突き抜けてSEGVを引き起こします。

2010.12.13 修正

chrootでAndroidを起動するとvoldは元のLinuxのudevdと通信することになるようです。Ubuntu 10.04ではudevdのバージョンが上がっているらしく、Androidでも解釈できる形式のメッセージの後に、同じデバイスに関する情報が異なる(新しい)形式でも送られてきます。

新しい形式のメッセージには'@'が含まれていません。

新しい形式が理解できないならば、単にそれを無視すればいいように新旧両方の形式のメッセージが来るようですが、Androidではこのバグにより新しいメッセージを受けたとたんにvoldが落ちてしまいます。そしてinitがvoldを再起動して、また落ちるというのを繰り返してしまいます。

initとvoldとnetdはカーネルからのメッセージを受け取るためNETLINK_KOBJECT_UEVENTのソケットの受信を行います。通常のLinuxではudevdもこのソケットの受信を行いますが、udevdからのメッセージも同じソケットが使われます。voldとnetdはカーネルからのメッセージのみを処理してudevdからのメッセージは無視すればよいのですが、メッセージ内に必ず'@'が含まれるということを前提にしてコーディングしてしまっていたために、'@'を含まないudevdからのメッセージを受けたときにSEGVを起こしていました。

修正パッチ

system/core/libsysutils/src/NetlinkEvent.cpp

--- NetlinkEvent.cpp.org	2010-09-08 14:25:21.000000000 +0900
+++ NetlinkEvent.cpp	2010-09-08 17:49:30.000000000 +0900
@@ -67,7 +67,9 @@
     while (s < end) {
         if (first) {
             char *p;
-            for (p = s; *p != '@'; p++);
+            for (p = s; *p != '@'; p++) {
+                if (p >= end) return false;
+            }
             p++;
             mPath = strdup(p);
             first = 0;

このように修正することで、voldもnetdも落ちなくなりました。両方ともこの関数を呼んでいました。

前回のスライドでは回避のためにinit.rc, MountService.java, NetworkManagementService.javaを修正しましたが、それを全部元に戻しても大丈夫です。つまり修正が必要なのはこのNetlinkEvent.cppだけです。

voldが正常に機能するようになったので、SDメモリカードの挿入を検出してmountが自動で行われます。これでAndroid側からSDメモリカードの読み書きができるようになりました。

PARTNER-Jetによるデバッグ

このようにDebian/Ubuntuからchrootで起動されたAndroidも、Android単体起動の時と同様にPARTNER-Jetでソースレベルデバッグが可能です。また、同時にDebian/Ubuntu側のプロセスもソースレベルデバッグが可能です。

(ただし製品としての提供の方法はまだ確定していません。ご興味のある方は問い合わせてください。)



トラックバックURL

トラックバック一覧

1. KZM-CA9-01ボードのリンク集  [ KMC Staff Blog ]   2010年09月16日 10:44
4コアのCortex-A9のKZM-CA9-01ボードを使って実験したことを書いたページをまとめてみました。

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード