Dalvikバイトコードリファレンスの読み方
description
Transcript of Dalvikバイトコードリファレンスの読み方
Dalvikバイトコード
リファレンスの読み方
発表者 :
僻地からの出稼ぎプログラマkmt-t
自己紹介
Web上での活動
ハンドルネーム : kmt-t
はてなID : kmt-t2
Twitter ID : kmt_t
属性
僻地出身、去年の10月から大阪に出稼ぎ中
組み込みプログラマらしい?
仕事はミドルウェア担当な場合が多いかな?
今後の話の前振り
Dalvik仮想マシン三部作
Dalvik仮想マシンのアーキテクチャ
Dalvikバイトコードリファレンスの読み方
DEXファイルフォーマット
今回は第二部のみを解説
今回は第一部の内容を前提に話をする
http://www.slideshare.net/kmt-t/javadalvik
本日の前振り
Dalvikバイトコードリファレンスの読み方 実際のDalvikバイトコードの命令レイアウト
Dalvikバイトコード命令とそれに対応する実装
Dalvikバイトコードリファレンス Androidソースコードのdalvik/docsディレクトリ以下
dalvik-bytecode.html
instruction-formats.html
opcodeディレクトリ以下
Dalvik仮想マシンインタープリタ 今回はC言語バージョンを参照
dalvik/vm/mterp/out/InterpC-portstd.c
dalvik-bytecode.htmlを
開いてみる
「Summary of Instruction Set」の章に注目
dalvik-bytecode.html (move命令)
列 : Op & Format
「01」はオペコード(命令の種類を示すID)
「12x」は命令フォーマットの種類
instruction-formats.htmlに詳細が記載
実は上記ファイルを読まなくても、列Mnemonic/Syntaxを読めばおおまかな命令フォーマットは分かる
dalvik-bytecode.html (move命令)
列 : Mnemonic/Syntax
「move」は命令の種類
「vA」「vB」は4bitのレジスタ番号のオペランドを2つ持つことを示す
仮に「vAA」とすると、8bitのレジスタ番号、「vAAAA」とすると16bitのレジスタ番号を示す
dalvik-bytecode.html (move命令)
列 : Arguments
「A: destination register (4 bits)」 (レジスタ番号Aは値のコピー先レジスタ)
「B: source register (4 bits)」 (レジスタ番号Aは値のコピー元レジスタ)
オペランドの役割が記述されている
dalvik-bytecode.html (move命令)
列 : Description
命令の解説が書いてある
命令自体のセマンティクス(命令の挙動)については述べられていない場合が多い
セマンティクスはdalvik/docs/opcode以下のディレクトリを参照
命令フォーマット (move命令の例)
命令フォーマットの制約条件
すべての命令は最初の16bitの下位8bitがオペコード
move命令は4bitのレジスタ番号AとBを持つ
命令は必ず16bitでアライメントされている
オペランドは16bit境界ごとにLSB側から順に配置される
レジスタ番号 vB (4bit)
レジスタ番号 vA (4bit)
オペコード = 0x01 (8bit)
LSB MSB
命令長16bit
バイトコード命令の実装を読む
sparse-switch命令の例 (1)
Javaのswitch文に対応した命令
Mnemonic/Syntax
「sparce-switch vAA, +BBBBBBBB」
「vAA」は分岐の判定値が格納されたレジスタ
「+BBBBBBBB」は分岐情報「sparse-switch構造体」への符号付32bitオフセット
バイトコード命令の実装を読む
sparse-switch命令の例 (2)
sparce-switch構造体
バイトコード命令の実装を読む
sparse-switch命令の例 (3)
「ident=0x0200」
バイトコード中に0xXX00(XX≠0)のバイナリで始まる命令が出現する場合をPseudo命令と呼ぶ
Pseudo命令はバイトコードではない
DEXファイル上の都合でバイトコードとsparse-switch構造体を区別する必要があるため
「size」
switch文のcaseの数
「keys」
switch文の「case XX」のXXの値の配列
「targets」
switch文の分岐先バイトコード命令へのオフセットの配列
バイトコード命令の実装を読む
sparse-switch命令の例 (4)
dalvik/vm/mterp/out/InterpC-portstd.c
該当部分の実装コードを簡略化すると以下のとおり
HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/) { u2 vsrc1 = INST_AA(inst); u4 testVal = GET_REGISTER(vsrc1); s4 offset = FETCH(1) | (((s4) FETCH(2)) << 16); const u2* switchData = pc + offset; offset = dvmInterpHandleSparseSwitch(switchData, testVal); FINISH(offset); } OP_END
バイトコード命令の実装を読む
invoke-virtual命令の例 (1)
dalvik/vm/mterp/out/InterpC-portstd.c
「GOTO_TARGET(invokeVirtual, bool
methodCallRange)」マクロが実装コード部分
バイトコード命令の実装を読む
invoke-virtual命令の例 (2)
1. 引数の数をバイトコード命令から取得
2. メソッドIDをバイトコード命令から取得
3. メソッドの引数をバイトコード命令から取得
4. thisポインタのNULLチェック
5. メソッドIDから基底メソッド情報のポインタを取得
DEXファイルから基底メソッド情報を構築する
メソッド構造体はキャッシュされる
キャッシュはクラスIDとメソッドIDの組をキー、メソッド構造体のポインタを値とするハッシュテーブル構造
バイトコード命令の実装を読む
invoke-virtual命令の例 (3)
6. 基底メソッドのインスタンスからメソッドのvtable
インデックスを取得
7. 派生メソッドのインスタンスをvtableから取得
8. 抽象メソッドの呼び出しでないかチェック
9. 命令を詳細にデコードし、引数をひとつずつ取り出し、スタックに積む
10. 新しいフレームポインタとvm-specific-internal-
goopの位置を計算
11. スタックあふれが発生していないかチェック
バイトコード命令の実装を読む
invoke-virtual命令の例 (4)
12. vm-specific-internal-goopに以下の情報を保存
現在のフレームポインタ
現在のプログラムカウンタ
現在のメソッド情報構造体のポインタ
13. (以下ネイティブメソッドでない場合)
14. 実行中のメソッド情報を呼び出し先に切り替え
15. プログラムカウンタとフレームポインタを呼び出し先のものに切り替え
以上で発表は終了
ご静聴ありがとうございました!