連載
» 2011年01月13日 00時00分 公開

Androidをデバッグしメモリダンプからデータを復旧する作りながら理解するファイルシステムの仕組み(10)(3/3 ページ)

[森 崇 株式会社 永和システムマネジメント,@IT MONOist]
前のページへ 1|2|3       

(3)gdbでファイルのpageのアドレスを参照する

 (2)で採取した「カーネルメモリダンプ(kernel.dump)」ファイルを直接参照して、対象のpageのアドレスを探すのは不可能ではありませんが、少し骨が折れます。そこで、このメモリダンプファイル内のデータ参照は、gdbの強力なメモリ参照機能を利用することにしました。

 ただし、単純にこのダンプファイルをgdbの引数に与えても、gdbが認識できるフォーマット形式ではないのでアクセスできません。gdbが認識できるフォーマットは「コアダンプファイル(コアファイル)」という形式なのです。そこで、いろいろと思案した結果、あまりエレガントな手法ではありませんが、以下の方法で対処することにしました。

 「自前で作ったユーザープロセスにkernel.dumpファイルをメモリ上に読み込みさせた後、故意に異常終了(SIGSEGV)させて、コアファイルを出力させる」。

 そのイメージを図9に示します(自前のプロセスの名前は「createCore」とします(注9))。

gdbでカーネルメモリダンプを参照する 図9 gdbでカーネルメモリダンプを参照する
※注9:コードの詳細は「createCore.c」を参照ください(リンクを右クリックし、「対象をファイルに保存」でダウンロードして、確認してみてください)。


 こうすれば、カーネルのメモリダンプを含んだコアファイルが自動的に作成されることになり、gdbはこのコアファイルを参照し、カーネルメモリダンプのアクセスが可能になる、という具合です。それでは、実際にカーネルメモリダンプをcreateCoreプロセスに渡して、コアファイルを作成してみましょう。

$ ./createCore kernel.dump  ……kernel.dumpをcreateCoreプロセスで読み込み、SIGSEGVを発生させる
dump_ptr=0xb1d48008  ……kernel.dumpの先頭アドレスはユーザー空間の0xb1d48008からはじまる
now dumping core...
Segmentation fault(コアダンプ)
$ ls -lh core
-rw------- 1 tmori tmori 91M 2010-12-12 11:26 core  ……コアファイルができていることを確認 

 ただし、このやり方には1つ問題があります。それは、カーネルメモリダンプ内に存在するアドレスは、すべてカーネルアドレス空間(0xc0000000以上のアドレス)のものであり、createCoreプロセスのアドレス空間はユーザー空間にあるという点です。つまり、カーネルメモリダンプ内のメモリにアクセスしようとしても、アクセス領域が実際のユーザープロセスのものとは異なるため参照できないのです(図10)。

カーネル空間のアドレスは直接アクセスできない 図10 カーネル空間のアドレスは直接アクセスできない

 そこで、追加対策として、gdbからのメモリアクセス時には、カーネル空間のアドレスをユーザー空間にマッピングすることにしました。

 なお、メモリアクセスのたびに手動でマッピングを行うことは面倒なので、gdbのユーザー定義コマンドを利用することにしました(注10)。今回追加したコマンドで主要なものは表2のとおりです。

※注10:ユーザー定義コマンドは、「.gdbinit」に追加します。詳細は「gdbinit」を参照ください(リンクを右クリックし、「対象をファイルに保存」でダウンロードして、確認してみてください)。


今回追加した主なコマンド 表2 今回追加した主なコマンド
※注11:file_system_type構造体については、連載第7回「仮想ファイルシステムのありがたみを知ろう」を参照ください。


※注12:dentry構造体については、連載第8回「ファイル名を管理するキャッシュdentry」を参照ください。


 これでgdbを使って、シームレスにカーネルのメモリダンプ情報を参照できるようになったわけです。それでは、早速メモリ探索をはじめましょう。

$ gdb  vmlinux  core ……メモリダンプの解析開始 

 まず、最初にtarfsの起点となる「file_system_type」構造体のデータを参照します。

 ここから、fs_supersのメンバをたどることで、マウントしている「super_block」構造体のデータを参照できます。ただし、このアドレスはsuper_block構造体の先頭アドレスではなく、「s_instances」というメンバのアドレスになりますので、以下のコマンドでsuper_block構造体のデータを参照します。

 super_block構造体には「s_root」というメンバがあり、ここにtarfsのルートディレクトリの「dentry」のアドレスが格納されています。今回復旧するinit.rcというファイルは、ルートディレクトリ直下にありますので、「dentry_childs」コマンドを使用すれば、対象ファイルの候補を探すことができます。

 この結果から、ルートディレクトリ直下には3つのファイルが存在することが分かります。それでは、先頭のdentryを参照し、ファイル名を見てみましょう。

 運よく、先頭のdentryがinit.rcであることが分かりました。それでは、「d_inode」のアドレスを参照してみます。

 次に、inodeのi_mappingから「address_space」構造体を参照します。

 そして、rnodeのアドレスを参照すれば基数ツリーノードを見ることができるわけです。ここで、このアドレスが奇数になっていることに気付かれたでしょうか? 通常、奇数のアドレスなど許されません。実は、0ビット目に「1」が設定されている場合は、radix_tree_nodeのデータがあるという意味なのです(そうでない場合はpageが入っています)。とてもトリッキーなルールなのですが、とにかく、1を引いたアドレスをradix_tree_nodeでキャストして、データ参照してみましょう。

 slots配列に4個のアドレスがあります。これがinit.rcに割り当てられているpageなのです。やっと見つかりました!

(4)ファイルデータを復旧する

 init.rcのpageのアドレスが分かりましたので、後はそこから対応するページフレーム番号を求めればよいわけです。

 結果は以下のとおりです(表3)。

index番号 pageアドレス ページフレーム番号
0 0xc0556500 16296
1 0xc0519f00 8568
2 0xc055d2a0 17173
3 0xc05603a0 17565
表3 結果

 ページフレーム番号は、kerne.dumpファイルのオフセット(4kbytes単位)と同じですから、「dd」コマンド(注13)を使って、ページフレーム番号のデータを抽出し、データを再構成します。

※注13:ddコマンドのオプションは以下のとおりです。
・if ……入力ファイルを指定する
・bs ……I/Oサイズ(バイト単位)を指定する
・skip ……ファイルの先頭からスキップするブロック数(ブロックサイズはbsで指定したもの)を指定する
・count ……I/O回数を指定する


$ dd if=tarfs.dump bs=4096 skip=16296 count=1 > tmp.dump
$ dd if=tarfs.dump bs=4096 skip=8568 count=1 >> tmp.dump
$ dd if=tarfs.dump bs=4096 skip=17173 count=1 >> tmp.dump
$ dd if=tarfs.dump bs=4096 skip=17565 count=1 >> tmp.dump 

 これでinit.rcのページフレームデータをすべて抽出できました。ただし、これだとファイルサイズが元のもの(12823bytes)より大きいので、以下のコマンドで、余分な領域を削ったファイルを作成すれば完了です!

$ dd if=tmp.dump  of=init.rc  bs=1 count=12823 

 では、このファイルを早速見てみましょう(図11)。

メモリダンプから復旧したファイルのデータ 図11 メモリダンプから復旧したファイルのデータ

 ちゃんとデータが見えますね〜。

 最後に、Android端末上のinit.rc(ファイル名はandroid_init.rcとして変名)との差分がないか「diff」コマンドで検査してみましょう。

$ diff android_init.rc init.rc
$ echo $?
0 

 ばっちり差分はありません!



 以上、tarfsを実装するために必要となるファイルシステムの基礎的な内容は説明し終わりました。細かなことを書き出すともっと長くなってしまいますが、ここまで読み進められた読者の方ならLinuxカーネルの専門書を片手にLinuxカーネルソースをきっと読破できるものと信じております。

 さて、次回は、tarfsの実装方法について解説します。お楽しみに! (次回に続く)


前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.