Dalvik VM内でのJavaずCの友情はどれほど匷力ですか

この蚘事では、AndroidコヌドずDalvik VMでの実行を怜蚌する際に、手順を非垞に詳现に説明しようずしたした。 質問に察する答えを知りたいず思いたした。





したがっお、この蚘事は3぀のパヌトに分かれおいたす。



私はそのような質問を投げかけおそれらを研究するように思えたす-アンドロむドはすでにかかずを螏んでいお、それを知らずにそのお気に入りのツヌルの1぀たずえばCが正しくないので、コヌドのその埌の執筆の重芁なポむントです。





それ以前は、仮想マシンを実際には分析しおいたせんでしたが、今はDalvik VMに興味がありたす。 したがっお、説明党䜓がこのVMに適甚され、ナレヌションを芳察できたす。ナレヌションのコヌスは数回倉曎されたす。



゚ントリヌ



この蚘事を理解するには、ARMアヌキテクチャのアセンブラヌの基本的な知識が必芁です。 これを理解するための蚘事にはほずんど情報がありたせんが、読者がこれを知らないずいう事実に重点が眮かれおいたす。 したがっお、「あなた自身の蚀葉で」倚くの説明がありたすが、それ以䞊はありたせん。



たた、Androidアプリケヌションを最初から䜜成するスキルを持っおいる必芁がありたすディレクトリ構造ずメむンファむルを知っおいる。



各蚘事の結果を芋お、読むべきかどうかを確認するこずもできたす。



3぀の郚分はすべお荷重が倧きく異なり、匷床がほが同じです。 たずえば、3番目の郚分に移動するず、最初の郚分に関連しお2番目の郚分ず2番目の郚分のように耇雑なように芋えたす。 そのため、アむテムを所有しおいない堎合は泚意しおください。最終的には負荷が倧きくなる可胜性がありたす。



誰が蚘事に興味を持ちたすか


冒険者だけ。



優れた開発者は、圌がこれを知らない堎合、簡単に芋぀けたす優れた開発者に察する私の理解がそれほど高くないこずを望みたすが、䜕かを始めお詊しおみたいが、方法を知らない人は、圌にずっお非垞に難しいでしょう。



Dalvik VMのような鬱denseずした森の䞭で、自分自身に質問しお答えを芋぀けるこずは珟実的であり、同じ倧胆な行動をずるこずを決めた人にこの方法を瀺したいず思いたした。



最初の目暙


私の䞻なタスクは、Cコヌドを比范するこずです。

int sum(int a, int b) { return a + b; }
      
      







同じJavaコヌドの堎合

  public class Summator { int sum(int a, int b) { return a + b; } static int staticSum(int a, int b) { return a + b; } }
      
      







  public class NativeSummator { native int sum(int a, int b); static native int staticSum(int a, int b); }
      
      







いく぀かの仮定がありたすが、私は本圓にそれが実際にどのように起こるかを芋たいです。



予備知識


ここで、JNIを介しお、JavaでのCコヌドの統合がどのように芋えるかを思い出したす。



Javaコヌドで倖郚ネむティブ関数を䜿甚するには、nativeキヌワヌドが䜿甚されたす。



 package com.m039.study; public class Summator { native int sum(int a, int b); }
      
      







この堎合、Cコヌドは次のようになりたす。



 int Java_com_m039_study_Summator_sum(JNIEnv* env, jobject thiz, int a, int b) { return a + b; }
      
      







Javaが関数を衚瀺するには、特定のルヌルメモリから䞎えられたに埓っお構成する必芁がありたす。





コンパむルには、 ndk-build



コマンドが䜿甚されたす。 このコマンドは、CたたはC ++ファむルが./jniディレクトリにあるこずを想定しおいたす。 ディレクトリにはAndroid.mkファむルが含たれおいる必芁があり、Application.mkである堎合がありたす。



コンパむル埌、libNAME.soラむブラリは./libs/armebiディレクトリに䜜成されたすが、armebiは異なる堎合がありたす。



より詳现な手順が必芁な堎合は、別のドキュメントを参照する必芁がありたす。 NDKに添付されおいるドキュメントずサンプルのカタログをご芧ください 。



パヌトI. Cによっお生成されたコヌドはどのように芋えたすか



質問Cコヌドは正確に䜕をしたすか



原則ずしお、これは理解できたすが、ARMアヌキテクチャの芳点から芋るず、 ほずんどすべおのモバむルデバむスがこの特定のアヌキテクチャを䜿甚しおいたすか



これを理解するには、Cコヌドがアセンブラヌ生圢匏でどのように芋えるかを理解する必芁がありたす。



分解する


䞻な質問は、CずJavaの盞互䜜甚です。これに基づいお、5぀の䟋を遞択したした。



1.暙準

 int sum(int a, int b) { return a + b; }
      
      







2. JNIを介したバむンド静的ではない

  int Java_com_m039_study_Summator_sum(JNIEnv* env, jobject thiz, int a, int b) { return a + b; }
      
      







3. JNI経由のバむンディング静的

  int Java_com_m039_study_StaticSummator_sum(JNIEnv* env, jclass thiz, int a, int b) { return a + b; }
      
      







4. JNIを介したバむンド静的ではなく、関数を䜿甚

  int Java_com_m039_study_Summator_sum(JNIEnv* env, jobject thiz, int a, int b) { return sum(a, b); }
      
      







5. JNIを介したバむンド静的、関数を䜿甚

  int Java_com_m039_study_StaticSummator_sum(JNIEnv* env, jclass thiz, int a, int b) { return sum(a, b); }
      
      







少し考えおみたしょう。 4ず5は、完党ではないにしおも、ほずんど䌌おいるように思えたす。 1-3は、送信されるパラメヌタが異なる必芁がありたす。 これは私が確認したいものです。



ビルドシステム


Androidアプリケヌションのビルドシステムは非垞に単玔化されおおり、Androidのコンパむル枈みコヌドをチェックするなどの目的で、コンパむルフラグを知る必芁がありたす。 特に、最適化フラグ。



Application.mkファむルでは、 APP_OPTIM



倉数を倉曎しお、リリヌスたたはデバッグの2぀の倀のいずれかに割り圓おるこずができたす。



なぜなら この蚘事の目的は、2぀の蚀語の統合を比范するこずであり、1぀の蚀語をよく理解するこずではありたせん。その埌、掚論を簡単にするために、倀 'debug'を䜿甚したす。 これにより、理解が深たり、゚ラヌが発生する可胜性が䜎くなりたすこの蚘事を曞く機䌚が増えたすが、ずにかく、「リリヌス」モヌドのコヌドの違いを調べる必芁がありたす。



今、もう少し詳现。



APPLICATION-MK.htmlファむルには、 APP_OPTIM



フラグのAPP_OPTIM



説明されおおり、 アプリケヌションタグの属性をandroid:debuggable="true"



倉曎でき、 APP_OPTIM



倉数APP_OPTIM



'debug'に蚭定APP_OPTIM



れるこずも瀺されおいたす。 属性がfalseに蚭定されおいる堎合、 APP_OPTIM



'release'に割り圓おられたす。



したがっお、 android:debuggable



属性がAndroidManifest.xmlで「true」にandroid:debuggable



おいるずさらに想定されおいたす。



たた、倉数APP_OPTIM



を䜿甚するずきにコンパむラヌに枡される倀を逃すこずはできたせん。同じファむルで以䞋のように衚瀺されたす 。



 ifeq ($(APP_OPTIM),debug) APP_CFLAGS := -O0 -g $(APP_CFLAGS) else APP_CFLAGS := -O2 -DNDEBUG -g $(APP_CFLAGS) endif
      
      







次に、5぀のオプションすべおを怜蚎し始めたす。



「デバッグ」モヌド


泚 すべおのリストは、コマンドarm-linux-androideabi-objdump -d " "



取埗されたしたarm-linux-androideabi-objdump -d " "







オプション1


 00000804 <sum>: 804: b082 sub sp, #8 806: 9001 str r0, [sp, #4] 808: 9100 str r1, [sp, #0] 80a: 9a01 ldr r2, [sp, #4] 80c: 9b00 ldr r3, [sp, #0] 80e: 18d3 adds r3, r2, r3 810: 1c18 adds r0, r3, #0 812: b002 add sp, #8 814: 4770 bx lr 816: 46c0 nop (mov r8, r8)
      
      







このリストには倚くの䜙分なものがありたすが、これはすべお呌び出し芏玄、より具䜓的にはARM呌び出し芏玄のためです。



私はこの䟋を翻蚳したす

-042぀のロヌカル倉数甚にメモリを予玄したす。

-06-08関数に枡された匕数を保存したす。

-0a-0c同じ匕数が他のレゞスタr2およびr3にそれぞれ枡されたす。

-0er3 = r2 + r3ず同等

-10結果をレゞスタr0に保存したす

-12スタックポむンタヌを初期状態に戻す

-14元の堎所に戻る

-16nopコマンドを䜿甚しおファむル内の関数を敎列する



远加 2぀のポむントは明確ではありたせんでしたサフィックスsずコマンドbx。 最初は、実行時にステヌタスフラグも曎新されるこずを意味したす。 2番目はthumbコマンドで、blコマンドず同等です。



コヌドを噛んだ埌、さらに倚くのものがあるこずが明らかになりたした。 もちろん、最適化を䜿甚する堎合、そのようなものはありたせんでした。



このリストには、匕数が関数にどのように枡され、䞻芁郚分がどのように実行されるかを瀺す2぀のポむントしかありたせん。



匕数はレゞスタを介しお枡され、メむン郚分にはコマンドが1぀だけ含たれたす。 ここから、コヌドadds r0, r1, r2; bx lr



するこずを確認できたしたadds r0, r1, r2; bx lr



adds r0, r1, r2; bx lr



は存圚する暩利がありたす。



ただし、このようなコヌドが完成したアプリケヌションにあるずは蚀えたせん誰かが「デバッグ」バヌゞョンを垂堎にリリヌスするこずを決定しない限り。 そしお、Javaではすべおが悪化しおいるずいう仮定がありたすが、私はそれを確認したいず思いたす。 したがっお、残りのリストをすばやく確認する必芁がありたす。



オプション2


 000007ec <Java_com_m039_study_Summator_sum>: 7ec: b084 sub sp, #16 7ee: 9003 str r0, [sp, #12] 7f0: 9102 str r1, [sp, #8] 7f2: 9201 str r2, [sp, #4] 7f4: 9300 str r3, [sp, #0] 7f6: 9a01 ldr r2, [sp, #4] 7f8: 9b00 ldr r3, [sp, #0] 7fa: 18d3 adds r3, r2, r3 7fc: 1c18 adds r0, r3, #0 7fe: b004 add sp, #16 800: 4770 bx lr 802: 46c0 nop (mov r8, r8)
      
      







はい、はい、同じこず、そしお予想どおり、匕数の数だけが増えたした。 そしお、䜕回の䞍必芁な操䜜、恐怖。 しかし同時に、最適化されたバヌゞョンを芋たいずいう欲求ははるかに倧きくなりたす。



泚 倚くのJNI関数远加の匕数が枡されるを䜿甚するこずは、単玔な関数より少ない匕数が枡されるよりも悪いずいう仮定が確認されおいたす。 今、この蚘事を曞き盎しおいるずき、この声明は非垞に玠朎に聞こえたす



オプション3


 00000850 <Java_com_m039_study_StaticSummator_sum>: 850: b084 sub sp, #16 852: 9003 str r0, [sp, #12] 854: 9102 str r1, [sp, #8] 856: 9201 str r2, [sp, #4] 858: 9300 str r3, [sp, #0] 85a: 9a01 ldr r2, [sp, #4] 85c: 9b00 ldr r3, [sp, #0] 85e: 18d3 adds r3, r2, r3 860: 1c18 adds r0, r3, #0 862: b004 add sp, #16 864: 4770 bx lr 866: 46c0 nop (mov r8, r8)
      
      







仮定は正圓化され、コヌドはオプション2ず同じです。



オプション4


 000008e0 <Java_com_m039_study_Summator_sum>: 8e0: b500 push {lr} 8e2: b085 sub sp, #20 8e4: 9003 str r0, [sp, #12] 8e6: 9102 str r1, [sp, #8] 8e8: 9201 str r2, [sp, #4] 8ea: 9300 str r3, [sp, #0] 8ec: 9a01 ldr r2, [sp, #4] 8ee: 9b00 ldr r3, [sp, #0] 8f0: 1c10 adds r0, r2, #0 8f2: 1c19 adds r1, r3, #0 8f4: f7ff ffde bl 8b4 <sum> 8f8: 1c03 adds r3, r0, #0 8fa: 1c18 adds r0, r3, #0 8fc: b005 add sp, #20 8fe: bd00 pop {pc}
      
      







なぜこのオプションを怜蚎する必芁があるのですか JNI関数ですべおを曞く方が良いず思った堎合に備えお。 䞀方、実際には、JNIをより耇雑な蚭蚈のラッパヌずしお䜿甚するこずをお勧めしたす。 たた、小さなC関数では、䜕でもできたす。  泚 この蚘事を曞いた埌、この声明はすでに私には明らかなようです



このコヌドはもう少し耇雑です

-e0返信先アドレスを保存したす

-e2ロヌカル倉数甚にメモリを予玄したす

-e4-ea匕数をロヌカル倉数に保存したす

-ec-eeロヌカル倉数の倀を取埗したす

-f0-f2関数呌び出しのためにレゞスタの倀を準備したす

-f4関数呌び出し自䜓

-f8-fa結果をレゞスタに保存し、戻り倀のレゞスタに曞き蟌みたす

-fcスタックポむンタヌを初期䜍眮に戻したす

-fe呌び出した堎所に移動したす



レゞスタlrがbl呜什を介しお曞き換えられるこずを考えるず、文字列「e0」は戻りアドレスを栌玍するために䜿甚されたす。



ご芧のずおり、䜙分なコヌドがたくさんありたす。 そのようなコヌドには存圚する暩利があるず思いたす

 push {lr} sub sp, #20 adds r0, r2, #0 adds r1, r3, #0 bl 8b4 <sum> add sp, #20 pop {pc}
      
      







オプション5


怜蚎する䟡倀はないず思いたす。



リリヌスモヌドで


最適化されたバヌゞョンに含たれるコヌドに぀いおの掚枬をテストしたいず思いたす。

オプション1


 0000894 <sum>: 894: 1808 adds r0, r1, r0 896: 4770 bx lr
      
      





オプション2


 00000890 <Java_com_m039_study_Summator_sum>: 890: 1898 adds r0, r3, r2 892: 4770 bx lr
      
      





オプション3


 00000898 <Java_com_m039_study_StaticSummator_SUM>: 898: 1898 adds r0, r3, r2 89a: 4770 bx lr
      
      





オプション4


 0000089c <Java_com_m039_study_Summator_SUM3>: 89c: b510 push {r4, lr} 89e: 1c10 adds r0, r2, #0 8a0: 1c19 adds r1, r3, #0 8a2: f7ff fff7 bl 894 <sum> 8a6: bd10 pop {r4, pc}
      
      





印象


私は少し驚いおいたす。 その前に䞀臎するはずだったすべおのもの。 最適化されたコヌドは優れおおり、私が自分で曞き蟌もうずしたコヌドに非垞に近いものです。



パヌトI.たずめ


このパヌトでは、おそらくARMでのアセンブラヌを玹介したす。 他の蚀語構成芁玠を分析しお理解するこずもできたす。 たた、あなたの掚枬に驚くかもしれたせん。



しかし、最も重芁なこずは、あなたがアンドロむド開発者であれば、デフォルトのコンパむル蚭定がどのように䜿甚され、必芁なずきにどこを芋るべきかを理解できるようになりたした。



最初は、JavaコヌドよりもCコヌドの方がどれだけ優れおいるかを知りたいず思いたした。 最初は蚘事を曞きたしたが、この結果を曞いおいたす。 今のずころ、Cの問題を解決するこずがいかに゚レガントで、分解された圢でどれだけコンパクトになったかに泚意を払うこずができたす。 Javaオペコヌドでは、これも芋られたすが、すでに少なくなっおいたす。 そしお、オペコヌドがCず混圚しおいる3番目の蚘事では、これは実際にはありたせん。



そしお今、Javaオペコヌドに぀いお少しだけ説明したす。



パヌトII Javaによっお生成されたコヌドはどのように芋えたすか



最初に、JVMたたはDalvik VMがどのように機胜するかを理解する必芁がありたす。 これを行うには、Dalvik VMのバむトコヌドファむルである* .dexファむルを逆アセンブルする必芁がありたす。



Summatorクラスはどのようになりたすか元の目暙を参照 これを行うには、プログラムdexdump -d classes.dex



蚭定したす。 コマンドが発行したもの

  #5 : (in Lcom/m039/study/Summator;) name : 'sum' type : '(II)I' access : 0x0000 () code - registers : 4 ins : 3 outs : 0 insns size : 3 16-bit code units 00226c: |[00226c] com.m039.study.Summator.sum:(II)I 00227c: 9000 0203 |0000: add-int v0, v2, v3 002280: 0f00 |0002: return v0 catches : (none) positions : 0x0000 line=73 locals : 0x0000 - 0x0003 reg=1 this com/m039/study/Summator; 0x0000 - 0x0003 reg=2 a I 0x0000 - 0x0003 reg=3 b I
      
      







  name : 'staticSum' type : '(II)I' access : 0x0008 (STATIC) code - registers : 3 ins : 2 outs : 0 insns size : 3 16-bit code units 002100: |[002100] com.m039.study.Summator.staticSum:(II)I 002110: 9000 0102 |0000: add-int v0, v1, v2 002114: 0f00 |0002: return v0 catches : (none) positions : 0x0000 line=77 locals : 0x0000 - 0x0003 reg=1 a I 0x0000 - 0x0003 reg=2 b I
      
      







ここではadd-int v0, v1, v2



、およびオペコヌド「9000 0102」の倀を考慮する必芁がありたす。 これで、内郚からこれらのオペコヌドがどのように芋えるかに進むこずができたす。



監芖するオペコヌドは䜕ですか


ARMアヌキテクチャにもっず興味がありたす。 このアヌキテクチャはデフォルトで䜿甚されるため、オペコヌドが適切です。 しかし、倚くのARMがあり、デフォルトでどれが䜿甚されたすか



これを行うには、Application.mkドキュメント、より具䜓的には倉数APP_ABIの説明を参照しおください。 デフォルトではarmv5teが䜿甚されるず蚘茉されおいたす。 ここで探玢したす



どこから芖聎を始めたすか


オペコヌド自䜓はarmv5teディレクトリにありたす。 このディレクトリにあるファむルを怜蚎するのは正しいでしょうが、私はそれをたったく正しくない方法でやりたいず思っおいたしたが、既に生成されたファむルを怜蚎するためにも機胜したす。 より具䜓的には、 InterpAsm-armv5te.S。File 。 ずおも面癜くお実甚的です。



ドキュメント


すべおの䞻芁なドキュメントはdocsディレクトリにありたすが、必芁に応じお、生のHTMLよりも読みやすい圢匏ぞのリンクを提䟛したす。



远加オペコヌドの怜査0x90


オペコヌド0x90 、そのコヌドを考えおみたしょう

 .L_OP_ADD_INT: /* 0x90 */ /* File: armv5te/OP_ADD_INT.S */ /* File: armv5te/binop.S */ /* * Generic 32-bit binary operation. Provide an "instr" line that * specifies an instruction that performs "result = r0 op r1". * This could be an ARM instruction or a function call. (If the result * comes back in a register other than r0, you can override "result".) * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (r1). Useful for integer division and modulus. Note that we * *don't* check for (INT_MIN / -1) here, because the ARM math lib * handles it correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float, * mul-float, div-float, rem-float */ /* binop vAA, vBB, vCC */ FETCH(r0, 1) @ r0<- CCBB mov r9, rINST, lsr #8 @ r9<- AA mov r3, r0, lsr #8 @ r3<- CC and r2, r0, #255 @ r2<- BB GET_VREG(r1, r3) @ r1<- vCC GET_VREG(r0, r2) @ r0<- vBB .if 0 cmp r1, #0 @ is second operand zero? beq common_errDivideByZero .endif FETCH_ADVANCE_INST(2) @ advance rPC, load rINST @ optional op; may set condition codes add r0, r0, r1 @ r0<- op, r0-r3 changed GET_INST_OPCODE(ip) @ extract opcode from rINST SET_VREG(r0, r9) @ vAA<- r0 GOTO_OPCODE(ip) @ jump to next instruction /* 11-14 instructions */
      
      







コメントを芋るず、すべおが明確になりたすが、コヌド内でこれらのコメントを自分で芋たいず思いたす。 したがっお、このコヌドの䞀郚をさらに分析したす。



オペコヌド0x90は加算呜什に察応し、その圢匏は23xです。 これは、呜什サむズが2バむトであり、3぀のレゞスタを䜿甚するこずを意味したす。「x」は、これ以倖に䜕もないこずを意味したす。



したがっお、このオペコヌドのコヌドは、これらの3぀のレゞスタを抜出し、23x圢匏で送信し、加算に䜿甚する必芁がありたす。 しかし、ここではすべおがそれほど単玔ではないこずがわかりたす。 レゞスタの接頭蟞「v」、たずえば「vCC」は、仮想を意味したす。 そしお、オペコヌドがレゞスタ番号を枡し、呜什が指定されたレゞスタに含たれる倀を取埗するこずがわかりたす。  泚 レゞスタヌには垞に接頭蟞「v」が付いおいるわけではありたせん



次のようになりたす。

-オペコヌドがありたす00 | 90 02 | 01AA | op CC | BB*

-90はオペコヌドを远加するこずを意味したす

-レゞスタ番号を抜出r9 = 0、r2 = 1、r3 = 2

-レゞスタの内容を抜出したすr1 = REGr3、r0 = REGr2**

-「add r0、r0、r1」ずいう呜什を実行したす

-戻り倀REGr9= r0を保存したす



*認識を容易にするために、ドキュメントず同様に远加|

** REG-擬䌌コヌドです



このオペコヌドには、別のオペコヌドに切り替えるために必芁なコマンドも含たれおいたす。 少し分析するので、今は泚意を払っおはいけたせん。



そしお今、それはすべおアセンブラヌたたはより厳しい説明でどのように芋えたすか

1. FETCH r0、1。 このオペコヌド0x90は、r4ずr5に察応する2぀のレゞスタrPCずrINST同䞊を䜿甚したす。 少し䞋を芋るず、rINST FETCH_INST がrPCの倀であるこずがわかりたす。 したがっお、rINSTはFETCHコマンドの倀rINST、0ず等しいず蚀えたす。



2.レゞスタ番号は、暙準の方法を䜿甚しお取埗されたす。 写真を参照



3. GET_VREG r1、r3。 新しいrFPレゞスタが衚瀺されたす。 このレゞスタは、ロヌカル倉数ず匕数のメモリ領域を指したす。 ぀たり これにより、匕数の倀を抜出し、戻り倀を曞き蟌むこずができたす。 VMの内郚レゞスタヌぞのポむンタヌずしお衚すこずができたす。



4. .if 0 ... .endifはわかりにくいですが、InterpAsm-armv5te.Sファむルで調査を開始したためです。 binop.Sファむルを芋るず、このオペコヌドの2番目の匕数であるれロのチェックが無効になっおいるこずが明らかになりたす。



5. FETCH_ADVANCE_INST2の詳现



6.远加が進行䞭です



7. GET_INST_OPCODEipに぀いおさらに説明したす。



8. SET_VREG r0、r9は、戻り倀を察応するレゞスタに曞き蟌みたす。このレゞスタの番号はr0で指定されたす。



9.埌で説明するGOTO_OPCODEip



私は芖芚的な私は願っおいたす写真を描きたした





オペコヌドは同じアセンブラコヌドに䌌おいる「r0、r1、r0を远加する」だけで、仮想レゞスタの凊理ず次のオペコヌドぞの移動ずいう2぀の远加だけであるず結論付けるこずができたす。



審査前の郚分


仮想レゞスタの凊理が考慮されたした。 オペコヌドの他のチヌムが䜕をしおいるのかを理解する必芁もありたす。圌らがあたりにも遠くないこずを願っおいたす。 たずえば、rFPやrPCなどのレゞスタの倀が初期化される堎合。 興味深いこずではありたすが、目暙からの匷い分岐です。



次に、残りのコマンドを怜蚎したす。

1. FETCH_ADVANCE_INST 2次のオペコヌドがrINSTレゞスタに曞き蟌たれたす

2. ipレゞスタのGET_INST_OPCODE ipにオペコヌド番号を入力したすすでに次

3. GOTO_OPCODE ip次のオペコヌドのコヌドに移動したす



これら3぀のコマンドでは、rIBASEレゞスタが衚瀺されたす。 番号が0x00のオペコヌドかどうかを確認する必芁がありたすか どうやらそうだ。 はい、そうです。 倚くの堎合、コヌドには次の行がありたす。

 adr rIBASE, dvmAsmInstructionStart @ set rIBASE
      
      







たた、 dvmAsmInstructionStartの倀は.L_OP_NOPです。 OP_NOPコヌドはどうですか 本圓に0x00です。



考慮しなかったが、私はしたい


このセクションでは、他の初期rPC、rFPレゞスタの内容のみを考慮したせんでした。 おそらく私たちはそれらをさらに怜蚎したす。 なぜそれが「可胜」なのか、これたでのずころrPCではなくrFPが倧きな䟡倀があるからです。



远加オペコヌドを調べる正しいバヌゞョン


オペコヌドの怜査方法は完党に正しいずは思えたせんでしたが、私にずっおはより快適に思えたした。 しかし、今床はこれをどのように改善できるか、぀たりarm5vteフォルダヌ内のファむルはどのように構成されおいるのかを芋おみたしょう。



これを行うには、 README.txtドキュメントを参照しおください。



少し驚いおいたすが、研究に䜿甚した方法はREADME.txtファむルの芳点からは正しいものでした。 匕甚はここにありたす



「むンタヌプリタヌに慣れる最善の方法は、armv5teのさたざたなコンポヌネントを調べるのではなく、out / InterpC-portstd.cなどのoutディレクトリで生成されたファむルを調べるこずです。 」



ここで、䟋ずしお、ファむルOP_ADD_INS.Sを考えたす。

 %verify "executed" %include "armv5te/binop.S" {"instr":"add r0, r0, r1"}
      
      







このコヌドは、このファむルが条件付きでbinop.Sファむルに眮き換えられ、$ instrの倀にadd r0, r0, r1



がadd r0, r0, r1



されるこずadd r0, r0, r1



意味したす。



binop.Sファむルを怜蚎する必芁がありたす。



 %default {"preinstr":"", "result":"r0", "chkzero":"0"} /* * Generic 32-bit binary operation. Provide an "instr" line that * specifies an instruction that performs "result = r0 op r1". * This could be an ARM instruction or a function call. (If the result * comes back in a register other than r0, you can override "result".) * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (r1). Useful for integer division and modulus. Note that we * *don't* check for (INT_MIN / -1) here, because the ARM math lib * handles it correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float, * mul-float, div-float, rem-float */ /* binop vAA, vBB, vCC */ FETCH(r0, 1) @ r0<- CCBB mov r9, rINST, lsr #8 @ r9<- AA mov r3, r0, lsr #8 @ r3<- CC and r2, r0, #255 @ r2<- BB GET_VREG(r1, r3) @ r1<- vCC GET_VREG(r0, r2) @ r0<- vBB .if $chkzero cmp r1, #0 @ is second operand zero? beq common_errDivideByZero .endif FETCH_ADVANCE_INST(2) @ advance rPC, load rINST $preinstr @ optional op; may set condition codes $instr @ $result<- op, r0-r3 changed GET_INST_OPCODE(ip) @ extract opcode from rINST SET_VREG($result, r9) @ vAA<- $result GOTO_OPCODE(ip) @ jump to next instruction
      
      







ご芧のずおり、コヌドは以前ず非垞によく䌌おいたす。 今しか理解できない瞬間は残っおいたせん。 たずえば、「@ optional op ..」ずいうコメントがありたしたが、その理由は明らかではありたせん。 今では明らかです。



このオペコヌドを分解するこずには意味がありたせん。すべおがすでに考慮されおいたす。 ここで、最も重芁な質問にすばやく進む必芁がありたす。ネむティブ関数ず非ネむティブ関数の匕数はどのように取り蟌たれたすか



パヌトII たずめ


それはすでに最初の郚分ずは異なるレベルでした、私は倚くを孊び、芚えおいなければなりたせんでしたが、オペコヌドの構造は明確になりたした 楜しめたす。 はい、これに喜びを感じるこずほど倧きな意味はありたせん。 ただし、同時に、すでにオペコヌドを䜜成できたす。



この郚分から、ファむルを生成するために䜿甚されるアヌキテクチャを孊習できたす。 適切なオペコヌドを芋぀ける堎所ず方法。



たた、倚くの質問があるかもしれたせん。開発者のDalvik VMのプレれンテヌションを芋るこずをお勧めしたす。このプレれンテヌションでは、GOTO_OPCODEなどのコマンドがどのように䜿甚されおいるか、たたはバむトコヌドキュヌがどのように線成されおいるかを非垞によく瀺しおいたす。



目暙に぀いおすぐに結論を出す぀もりはありたせん。すべおの結論は蚘事の最埌になりたす。そしお今、Dalvik VMの内郚に飛び蟌んで、このコヌドが実行される堎所を理解するこずを提案したすが、レベルがさらに高くなるず譊告したすが、挿入するコヌドは少なくしたす。



パヌトIII。コヌドはどのように、どこで実行されたすか



第3郚では、Dalvik VMで関数を抜出するプロセスに぀いお説明したす。以前の知識は倧いに圹立ちたすが、飛躍は第1郚ず第2郚の間ず同じであるず仮定するのは怖いです。さあ始めたしょう



泚すぐにリンクをたどるのではなく、最初に読むこずをお勧めしたす。そのため、資料は埐々に衚瀺され、混乱する可胜性は䜎くなりたす。



分解する


オペコヌドがどのように読み蟌たれるかを理解する必芁がありたす。そのため、このようなセクショナルコヌドを芋おいきたす。

  public class Summator { void test() { sum(44, 43); staticSum(42, 41); nSum(44, 43); nStaticSum(42, 41); } int sum(int a, int b) { return a + b; } static int staticSum(int a, int b) { return a + b; } native int nSum(int a, int b); native static int nStaticSum(int a, int b); }
      
      







  name : 'test' type : '()V' access : 0x0000 () code - registers : 5 ins : 1 outs : 3 insns size : 21 16-bit code units outs : 3 insns size : 21 16-bit code units 0022a8: |[0022a8] com.m039.study.Summator.test:()V 0022b8: 1303 2c00 |0000: const/16 v3, #int 44 // #2c 0022bc: 1302 2b00 |0002: const/16 v2, #int 43 // #2b 0022c0: 1301 2a00 |0004: const/16 v1, #int 42 // #2a 0022c4: 1300 2900 |0006: const/16 v0, #int 41 // #29 0022c8: 6e30 2e00 3402|0008: invoke-virtual {v4, v3, v2}, Lcom/m039/study/Summator;.sum:(II)I // method@002e 0022ce: 7120 2d00 0100|000b: invoke-static {v1, v0}, Lcom/m039/study/Summator;.staticSum:(II)I // method@002d 0022d4: 6e30 2600 3402|000e: invoke-virtual {v4, v3, v2}, Lcom/m039/study/Summator;.nSum:(II)I // method@0026 0022da: 7120 2500 0100|0011: invoke-static {v1, v0}, Lcom/m039/study/Summator;.nStaticSum:(II)I // method@0025 0022e0: 0e00 |0014: return-void catches : (none) positions : 0x0008 line=29 0x000b line=30 0x000e line=31 0x0011 line=32 0x0014 line=33 locals : 0x0000 - 0x0015 reg=4 this Lcom/m039/study/Summator;
      
      







私は本圓にarmv5teディレクトリ内のすべおのファむルを解析および蚘事にコピヌしたくありたせん。したがっお、これらのファむルからリンクず抜粋を提䟛しようずしたす。



䞊蚘のリストを芋るず、ネむティブメ゜ッドに違いはないこずがわかりたす。どうしお実際にはそうであるはずですが、ずにかく、リストにはネむティブメ゜ッドがどのように抜出されるかのヒントはありたせん。



しかし、最初に、呌び出し自䜓invoke-virtualを考慮せず、すぐにそこにあるコヌドが小さくないず仮定しおください。したがっお、私たちはそこに芋぀けたいものを事前に圢成する

仮想静的のコントラスト-

-自己解凍機胜

のアクションが抜出するために、次にどのような可胜性が、どのくらいの、そしお-



あなたが興味を持っおいるずの研究を損なうべきではないたで、残りを。



メ゜ッド実行


リストを䞊から䞋に芋おいきたしょう。



sumメ゜ッドを入力および取埗するコヌドの䞀郚

 0022b8: 1303 2c00 |0000: const/16 v3, #int 44 // #2c 0022bc: 1302 2b00 |0002: const/16 v2, #int 43 // #2b 0022c8: 6e30 2e00 3402 |0008: invoke-virtual {v4, v3, v2}, Lcom/m039/study/Summator;.sum:(II)I // method@002e
      
      







䞀芋、すべおが非垞に簡単です。適切なレゞスタが入力され、メ゜ッドが取埗されたす。



この方法が内郚からどのように芋えるかを提案しようず思いたす。



const/16



-おそらく数倀0で倀44を仮想レゞスタに入れ、次に倀43を入れたす泚v1が仮想レゞスタの指定であるず仮定した堎合、疑いはありたせん



invoke-vritual



-この倀をv4に入れお関数を呌び出したす。



const / 16


  %verify "executed" /* const/16 vAA, #+BBBB */ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA FETCH_ADVANCE_INST(2) @ advance rPC, load rINST SET_VREG(r0, r3) @ vAA<- r0 GET_INST_OPCODE(ip) @ extract opcode from rINST GOTO_OPCODE(ip) @ jump to next instruction
      
      







このコヌドは、2番目のバむトで送信された倀を読み取り、その倀を仮想レゞスタに曞き蟌みたす。再びすべおが生成されたCコヌド最適化されたバヌゞョンず同じように芋えたすが、2぀の远加だけがありたす曞き蟌み/読み取りごずに䞭間仮想レゞスタが䜿甚され、毎回新しいオペコヌドがロヌドされ、遷移が行われたす。そうでなければ、すべおが非垞に透明です。



呌び出す皮類


2぀の仮想レゞスタがいっぱいです。ファむルOP_INVOKE_VIRTUAL.Sに移動しおください。そしお、深い恐怖がありたす。そのため、仮想ファむルOP_INVOKE_VIRTUAL.Sず静的OP_INVOKE_STATIC.Sの違いをすぐに理解しようずしたす。



コヌドによるず、違いは2぀の堎所にありたす。静的メ゜ッドではなくdvmResolveMethod関数に枡される倀には、远加の「.L $ {opcode} _continue」が远加されたす。



dvmResolveMethod関数の機胜を怜蚎しおください。



この関数を呌び出す前に、レゞスタは次のように埋められたす。 コメントからの抜粋

-r0 <-method-> clazz

-r1 <

-Priv * -r2 <-METHOD_VIRTUALたたはMETHOD_STATICmethod type

-r3 <-glue-> method



*文曞による呜什圢匏 Priv、BBBBはコメントに蚘述され



おいたすが、今床はdvmResolveMethod関数のコヌドに戻りたす。関数の宣蚀を芋るず、4番目の匕数䞊蚘を参照が䞍芁であるこずが明らかになりたす。



  Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx, MethodType methodType)
      
      







この関数はMethod構造䜓ぞのポむンタヌを返したす。この関数に぀いお詳しく知る必芁はありたせん。この構造を芋おください。



興味深く有甚な分野がたくさんありたすが、特別な関心を提䟛する玠晎らしい分野がありたす- nativeFunc



。したがっお、この構造にはネむティブ関数ぞのポむンタも含たれたす。



ここで、この構造たたはこのメ゜ッドが「実行䞭」の堎所を芋぀けおおくずいいでしょう。



OP_INVOKE_STATIC.SファむルずOP_INVOKE_VIRTUAL.Sの䞡方で、関数は最埌に呌び出されbl common_invokeMethod${routine}



たす。ほずんどの堎合、これはMethod構造のメむンハンドラヌであり、そのコヌドはfooter.Sファむルにありたす。プレフィックス「NoRange」は、ファむルの最初の行のために衚瀺されたした- %default { ... , "routine" : "NoRange" }







しかし、footer.Sを芋る前に、タむプの意味を確認できたすDalvikBridgeFunc



フィヌルドでnativeMethod



 VMコヌドを実行するずnativeMethod



、dvmResolveNativeMethod 関数がフィヌルドに割り圓おられおいるこずがわかりたす。



この関数のコメントから、ネむティブメ゜ッドラむブラリlibNAME.so内を怜玢し、最も重芁な実行に䜿甚されるこずが明らかになりたす。それで圌女の蚀葉を聞いおみたしょう。その堎合はそれを信じるのが唯䞀の堎合です。



もう1぀質問がありたすが、ネむティブメ゜ッドを実行するナヌザヌず堎所が明確になりたした。しかし、それでも、どこで完党に明確ではありたせん。結局、footer.Sファむルはレビュヌされたせんでした。common_invokeMethodNoRange



関数に戻る。非垞に恐ろしく、最埌の郚分で明確ではなかったもの、぀たりrFPやrPCなどのレゞスタヌを蚭定しおいるのは誰なのかずいう答えが含たれおいるこずに泚意しおください。ご芧のずおり、この関数はそれらを埋めたす。



ほんの少しの忍耐、そしお、たずえば、rINST、rPC、r2が䜕で満たされおいるかを理解できたすか method-> instフィヌルドぞのポむンタヌ、぀たり呜什バむトコヌド。そしお、そのような瞬間がたくさんあるので、それらを省略したす。



しかし、ネむティブは興味があり、すべおがさらに簡単です。メ゜ッドがネむティブの堎合察応するフラグがチェックされおいる堎合、既に凊理したのず同じnativeFunc関数が実行されたす。



それ以倖の堎合、common_invokeMethodNoRangeは以前に調べた暙準オペコヌドず非垞によく䌌おいたす。远加のチェックは倚くありたす。



しかし、静的たたは非静的メ゜ッドはどうでしょうかfooter.Sは、どうやらそれらずは䜕の関係もありたせん。それらに぀いお蚀えるこずはすべお、察応するオペコヌドファむルinvoke-virtualおよびinvoke-staticですでに述べられおいたす。



これに぀いお、誰がどこで䜕を䜜成したかを明確にするこずができたす。そうでない堎合、さらに理解するこずは難しくありたせん。私の仕事は、質問をするこず元の目暙を参照を瀺し、これを実際よりもよく理解するこずです。



パヌトIII。 たずめ




この郚分では、ネむティブメ゜ッドの取埗を担圓する関数が芋぀かりたした。たた、この関数が取埗される堎所ず保存される堎所も远跡したした。この知識があれば、Java蚀語の他の郚分を芋お、Dalvik VMコヌドでそれらを芳察できたす。



このパヌトの埌、バむトコヌドは倚くのオペコヌドであり、それぞれが盞互に䜜甚するこずが明らかになりたす。そしお、この郚分では、この盞互䜜甚のごく䞀郚のみが考慮されたした。



このトピックに興味がある堎合は、将来的には逆アセンブルされた圢匏のJava蚀語の暙準構造を怜蚎し、プレれンテヌションで開発者のDalvik VMがAndroid甚のJava蚀語でプログラミングするずきに行うべきたたはすべきでない䟋を瀺した理由を理解できたす。



たずめ



ここでは、この蚘事で芋぀けるこずができるず思われるものから抜粋しようずしたす。



しかし、最初に、蚘事がどのようにコンパむルされるかに぀いおのいく぀かの機胜を匷調したいず思いたす。



テキストで「備考」ずいう蚀葉に出くわしたかもしれたせん。これは、ドラフトで蚘事党䜓を曞いた埌に远加されたした。そしお、これらのコメントを远加する䟡倀があるように思えたした。



この蚘事の私の目暙は、あいたいなコヌドを実際に研究するために必芁な考え方を瀺すこずです。私は非垞に簡単な質問ず、このために興味のあるオブゞェクトを遞択したした。したがっお、倚くの支郚に䌚っお、䞻題に察する態床がどのように倉化するかに気付くこずができたす。



そしお今、私にずっお興味深いず思われるこず、そしお研究の結果に぀いお少し説明したす。 Java蚀語仕様には倚くのこずが曞かれおいたすが、この蚘事の埌では、これらの構造をそのたた理解する方がはるかに簡単です。



䞻なポむントは次のずおり



です。1.メ゜ッド呌び出しは、静的、仮想クむックプレフィックスを含むが䜕であれ、メ゜ッド呌び出しであり、単玔なCおよびC ++ず比范した堎合、それ自䜓非垞に耇雑です。たずえば、Box2Dのラッパヌがあるずしたす。オブゞェクトが亀差するかどうかをチェックするために毎回無限ルヌプでメ゜ッドがこのラッパヌから呌び出されるず、察応する質問が発生したす-なぜですかこれはCで行う必芁がありたすが、Javaでワヌルドを䜜成しおオブゞェクトを初期化できたす。



2.オペコヌドの機胜はアセンブラヌの挿入に非垞に近いため、オペコヌドはアセンブラヌを䜿甚しお実装されたす。圓然、これらはJNIを介しおサヌドパヌティ関数を呌び出すよりも優れおいたすが、Cでそれを行うよりも悪いです



。2぀の基準によっお悪化したす



。1呌び出しに仮想レゞスタを介しおラッパヌを䜿甚したす。モヌド。オペコヌドの速床はCの最適化されおいないバヌゞョンず非垞に䌌おいるずさえ蚀えたす



。2各オペコヌドには次のオペコヌドを取埗するコヌドがありたすが、それほど倧きくはありたせん。抜出埌、すでに簡単な指瀺のように芋える次のものぞの移行が行われbl



たす。



私は興味を倱い、私が望んでいたこずを知り、私の掚枬はほずんど正圓化されたした。あなたがそれを奜きで、あなたが私ず䞀緒にこのように行ったなら、私は非垞に嬉しく思いたす。興味深い考えず、すべおがどのように機胜するかに䞻な関心があるこずを願っおいたす。



開発者はステレオタむプJavaずC / C ++に関しおを非垞に頻繁に䜿甚し、それらを砎壊するためのわずかなアクションをどこにも衚瀺しないこずにしばしば気づきたす。



あなたは远加の材料に興味がある堎合は、ファむルのdocsフォルダに芋るこずができたすJNI-tips.htmlずのプレれンテヌション 2008デベロッパヌDavik VM。興味深いsmaliプロゞェクトもありたすが、私の手には届きたせんでした。



PSすぐに謝眪したす。間違えた堎合は修正しおください。すぐに倉曎を加えたす。



All Articles