DalvikVM
昨日の高速化云々な記事にて DalvikVM の中身が云々という記述があったので中身を見てやれ、と言いつつ em1 な dalvik ディレクトリ配下を gtags -v したりしてたんですが、どうもとっかかりが無くって困ってました。
dalvik/dalvikvm/Main.c とか果てし無いカンジですし。
で
色々探していたらようやくエントリポイントを発見。dalvik/vm/interp 配下な模様。README.txt には以下な記述があります。
Dalvik interpreter entry point. The "mterp" directory now holds the interpreter implementation.
dalvik/vm/interp/interp.c あたりから掘っていけば良いのかな。
中身確認ちう
なんですが、存在しない手続きを呼び出しているのかどうなのか。定義を見つけきれない。
と思ったら dvmMterpStdRun な手続きはアセンブラな手続きとして定義されているのを発見。でも dvmInterpretStd というソレは無いッス。
とりあえず dvmMterpStd が _"standard" mterp entry point._ って事なんで
しばらく
中でさまよっておりましたが、ざっくりベースでまとめます。
dalvik/vm/interp/interp.c の dvmInterpret という手続きから。手続きの最初らへんをざっくり以下に引用。
void dvmInterpret(Thread* self, const Method* method, JValue* pResult) { InterpState interpState; bool change; /* snip */ /* * Initialize working state. * * No need to initialize "retval". */ interpState.method = method; interpState.fp = (u4*) self->curFrame; interpState.pc = method->insns; interpState.entryPoint = kInterpEntryInstr;
interpState という変数は行ってらっしゃい、な手続きに渡しています。
change = (*stdInterp)(self, &interpState);
データ構造をちょっとだけ見ておくと、Method 型は dalvik/vm/oo/Object.h で定義されてて insns という属性の定義だけ控えておくと以下。
const u2* insns; /* instructions, in memory-mapped .dex */
u2 は unsigned short (2 byte) って事な模様。unsigned short な命令を格納した配列がどこかにあるはず。
あるいは InterpState 型については dalvik/vm/interp/InterpDefs.h で定義されてます。定義のアタマの部分のみ以下に引用。
typedef struct InterpState { /* * To make some mterp state updates easier, "pc" and "fp" MUST come * first and MUST appear in this order. */ const u2* pc; // program counter u4* fp; // frame pointer JValue retval; // return value -- "out" only const Method* method; // method being executed
適度に初期化をして関数ポインタの初期化をして手続き呼び出ししてるんですが、
typedef bool (*Interpreter)(Thread*, InterpState*); Interpreter stdInterp; if (gDvm.executionMode == kExecutionModeInterpFast) stdInterp = dvmMterpStd; else stdInterp = dvmInterpretStd;
上記の通り dvmInterpretStd が解決できてません。とりあえず dvmMterpStd という手続きを掘っております。詳細は略しますが、以下が核心。
changeInterp = dvmMterpStdRun(glue);
で、この svmMterpStdRun というソレがアセンブラになります。gtags では見つかりませんでしたので、必殺 find|xargs grep で発見。
とりあえず、x86 なアセンブラな記述がしてあるソレ (dalvik/vm/mterp/x86/entry.S) を確認。とその前に vm の中で使ってるレジスタの定義が以下かな。。
#define rPC %edx #define rFP %esi #define rIBASE %edi #define rINST_FULL %ebx #define rINST %bx #define rINST_HI %bh #define rINST_LO %bl #define rOPCODE %bl
で、手続きの中としてはレジスタの初期設定をして
movl IN_ARG0(%ebp),%ecx movl %ecx,rGLUE_SPILL(%ebp) LOAD_PC_FROM_GLUE(%ecx) LOAD_FP_FROM_GLUE(%ecx) movl $$dvmAsmInstructionStart,rIBASE
詳細は略しますが、以下が eval な部分かな。。。
FETCH_INST() GOTO_NEXT
マクロ定義がそれぞれ以下。
#define FETCH_INST() movzwl (rPC),rINST_FULL
movzwl って何だったか。。。
最近 z80 とか 65c02 とかのアセンブラをナニしてたので、x86 云々って微妙。あと、GOTO_NEXT の定義が以下かな。
/* For computed next version */ movzx rOPCODE,%eax sall $6,%eax addl rIBASE,%eax jmp *%eax
FETCH_NEXT でプログラムカウンタの指してる命令が rINST_FULL レジスタ (ebx レジスタ) に格納されるんですが、次の GOTO_NEXT なマクロの中で bl レジスタからという形でオペコードを取り出しています。
オペコードはドキュメントによれば 5bit 表現になってます。が 6bit 左シフトさせて rIBASE レジスタと加算したソレに jmp している模様。
そりゃいいんですが、行ってらっしゃい、というだけでポインタ++ はしてませんな。あらら? と言いつつ実際の命令の定義を見てみたらその末端でやっておりました。以下は dalvik/vm/mterp/x86/OP_AGET.S の中身です。
%default { "load":"movl", "shift":"4" } %verify "executed" /* * Array get, 32 bits or less. vAA <- vBB[vCC]. * * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short */ /* op vAA, vBB, vCC */ movzbl 2(rPC),%eax # eax<- BB movzbl 3(rPC),%ecx # ecx<- CC movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA GET_VREG(%eax,%eax) # eax<- vBB (array object) GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index) testl %eax,%eax # null array object? je common_errNullObject # bail if so cmpl offArrayObject_length(%eax),%ecx jae common_errArrayIndex # index >= length, bail $load offArrayObject_contents(%eax,%ecx,$shift),%eax movl rINST_FULL,%ecx FETCH_INST_WORD(2) SET_VREG(%eax,%ecx) ADVANCE_PC(2) GOTO_NEXT
ただ、ADVANCE_PC なソレの定義が若干微妙。
#define ADVANCE_PC(_count) leal 2*_count(rPC),rPC
とりあえず微妙なカンジではありますがエントリ投入。
追記
げげ。opcode ですが 8bit 使っておりますな。6bit シフトさせてる根拠は何だろ。