- そのボード上にAndroidをポーティングする。NFSrootにするとよい。
- その時使ったAndroid用にpatchをあてたカーネルを使って既存のLinuxを動かす。
- 既存のLinuxのユーザーランドに /androidというディレクトリを作って、そこにandroidのユーザーランドのファイルを全て置く。
- 既存の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コマンドでこの時のプロセスの状態を見ると以下のようになりました。
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側のプロセスもソースレベルデバッグが可能です。
(ただし製品としての提供の方法はまだ確定していません。ご興味のある方は問い合わせてください。)
0 件のコメント:
コメントを投稿