從一個Native調用java方法的實例開始:
(gdb) bt #0 art_quick_invoke_stub () at art/runtime/arch/arm64/quick_entrypoints_arm64.S:667 #1 0x0000007f8265ae54 in art::ArtMethod::Invoke (this=0x7148dd88, self=0x7f75260c00, args=0x7f7a5fedb0, args_size=28, result=0x7f7a5fed90, shorty=0x73654521 "ZIJJI") at art/runtime/art_method.cc:289 #2 0x0000007f829f2b5c in art::InvokeWithArgArray (soa=..., method=<optimized out>, arg_array=<optimized out>, result=<optimized out>, shorty=<optimized out>) at art/runtime/reflection.cc:439 #3 0x0000007f829f4574 in art::InvokeVirtualOrInterfaceWithVarArgs (soa=..., obj=<optimized out>, mid=<optimized out>, args=...) at art/runtime/reflection.cc:557 #4 0x0000007f828e357c in art::JNI::CallBooleanMethodV (env=<optimized out>, obj=<optimized out>, mid=<optimized out>, args=...) at art/runtime/jni_internal.cc:717 #5 0x0000007f8665d97c in _JNIEnv::CallBooleanMethod (this=this@entry=0x7f75302900, obj=<optimized out>, methodID=<optimized out>) at libnativehelper/include/nativehelper/jni.h:620 #6 0x0000007f866b4454 in JavaBBinder::onTransact (this=0x7f752b0780, code=50, data=..., reply=0x7f7a5ff100, flags=17) at frameworks/base/core/jni/android_util_Binder.cpp:265 #7 0x0000007f85d77174 in android::BBinder::transact (this=0x7f752b0780, code=50, data=..., reply=0x7f7a5ff100, flags=17) at frameworks/native/libs/binder/Binder.cpp:126 #8 0x0000007f85d831a8 in android::IPCThreadState::executeCommand (this=0x7f82d06780, cmd=<optimized out>) at frameworks/native/libs/binder/IPCThreadState.cpp:1115 #9 0x0000007f85d82d04 in android::IPCThreadState::getAndExecuteCommand (this=0x7f82d06780) at frameworks/native/libs/binder/IPCThreadState.cpp:447 #10 0x0000007f85d833fc in android::IPCThreadState::joinThreadPool (this=<optimized out>, isMain=<optimized out>) at frameworks/native/libs/binder/IPCThreadState.cpp:517 #11 0x0000007f85da17e8 in android::PoolThread::threadLoop (this=0x7f77b08b80) at frameworks/native/libs/binder/ProcessState.cpp:63 #12 0x0000007f849f093c in android::Thread::_threadLoop (user=<optimized out>, user@entry=0x7f77b08b80) at system/core/libutils/Threads.cpp:751 #13 0x0000007f86655d1c in android::AndroidRuntime::javaThreadShell (args=<optimized out>) at frameworks/base/core/jni/AndroidRuntime.cpp:1201 #14 0x0000007f851a7c58 in __pthread_start (arg=<optimized out>) at bionic/libc/bionic/pthread_create.cpp:198 #15 0x0000007f85150100 in __start_thread (fn=0x1, arg=0x7148dd88) at bionic/libc/bionic/clone.cpp:41 #16 0x0000000000000000 in ?? () (gdb) f 6 #6 0x0000007f866b4454 in JavaBBinder::onTransact (this=0x7f752b0780, code=50, data=..., reply=0x7f7a5ff100, flags=17) at frameworks/base/core/jni/android_util_Binder.cpp:265 warning: Source file is more recent than executable. 265 code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags); (gdb) list 263,266 263 //printf("\n"); 264 jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, 265 code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags); 266
在 f6:android_util_Binder.cpp:265 Native的 JavaBBinder::onTransact 函數中,通過 CallBooleanMethod 來調用 gBinderOffsets.mExecTransact (methodID,其初始化就不做介紹了)這個java 方法;
從 f6 到 f0,仍然都是Native函數,其遵循的仍然是 ARM調用約定,且目前還沒有真正跳轉到 gBinderOffsets.mExecTransact 這個java函數,
在開始跳轉之前,有一點需要關注的是: 線程的狀態(這裏指該線程在虛擬機中的狀態)的切換;
在 f4 中:
(gdb) down #4 0x0000007f86ae357c in art::JNI::CallBooleanMethodV (env=<optimized out>, obj=<optimized out>, mid=<optimized out>, args=...) at art/runtime/jni_internal.cc:717 warning: Source file is more recent than executable. 717 return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetZ(); (gdb) list 713,718 713 static jboolean CallBooleanMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { 714 CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); 715 CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); 716 ScopedObjectAccess soa(env); 717 return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetZ(); 718 }
在 line 716: ScopedObjectAccess soa(env); 中完成了 Thread 狀態的切換(具體的切換代碼在 ScopedObjectAccess以及其父類的構造函數中完成),
把線程狀態切換成了 Runnable狀態,並且 Shared Hold mutator_lock_; 即線程在開始執行 Java代碼之前已經提前把線程狀態切換成了 Runnable 狀態;
說明:當線程在虛擬機中對應的狀態是 Runnable狀態時,才能執行 java 代碼;當需要執行 Native 代碼時,同樣提前切換線程狀態到 Native狀態;
gdb) down #1 0x0000007f8265ae54 in art::ArtMethod::Invoke (this=0x7148dd88, self=0x7f75260c00, args=0x7f7a5fedb0, args_size=28, result=0x7f7a5fed90, shorty=0x73654521 "ZIJJI") at art/runtime/art_method.cc:289 (gdb) art_get_method_name_by_method_id this android.os.Binder.execTransact "(IJJI)Z" (gdb) list 288,292 288 if (!IsStatic()) { 289 (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty); 290 } else { 291 (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty); 292 } private boolean execTransact(int code, long dataObj, long replyObj, int flags);
非 static的java 函數調用前的準備工作是 art_quick_invoke_stub 來做的,而靜態的調用,其準備工作是 art_quick_invoke_static_stub負責;
看其參數個數及順序,發現並無區別;依次是:art::ArtMethod指針,參數指針,參數大小(byte),art::Thread指針,指向棧上存放返回值的指針 result,java函數對應的 short signature;
我們查一下 args這個參數在哪準備的,因爲這個args裏面保存着 execTransact(int code, long dataObj, long replyObj, int flags);這個java函數的所有參數;
(gdb) f 3 #3 0x0000007f829f4574 in art::InvokeVirtualOrInterfaceWithVarArgs (soa=..., obj=<optimized out>, mid=<optimized out>, args=...) at art/runtime/reflection.cc:557 557 InvokeWithArgArray(soa, method, &arg_array, &result, shorty); (gdb) list 553,557 553 const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len); 554 JValue result; 555 ArgArray arg_array(shorty, shorty_len); 556 arg_array.BuildArgArrayFromVarArgs(soa, receiver, args); 557 InvokeWithArgArray(soa, method, &arg_array, &result, shorty); @art/runtime/reflection.cc void Append(uint32_t value) { arg_array_[num_bytes_ / 4] = value; num_bytes_ += 4; } void BuildArgArrayFromVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver, va_list ap) SHARED_REQUIRES(Locks::mutator_lock_) { // Set receiver if non-null (method is not static) if (receiver != nullptr) { Append(receiver); } for (size_t i = 1; i < shorty_len_; ++i) { switch (shorty_[i]) { case 'Z': case 'B': case 'C': case 'S': case 'I': Append(va_arg(ap, jint)); break; case 'F': AppendFloat(va_arg(ap, jdouble)); break; case 'L': Append(soa.Decode<mirror::Object*>(va_arg(ap, jobject))); break; case 'D': AppendDouble(va_arg(ap, jdouble)); break; case 'J': AppendWide(va_arg(ap, jlong)); break; #ifndef NDEBUG default: LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; #endif } } } 除了 double,long,都是 4byte; // Primitives. case JDWP::JT_BYTE: return 'B'; case JDWP::JT_CHAR: return 'C'; case JDWP::JT_FLOAT: return 'F'; case JDWP::JT_DOUBLE: return 'D'; 8byte case JDWP::JT_INT: return 'I'; case JDWP::JT_LONG: return 'J'; 8byte case JDWP::JT_SHORT: return 'S'; case JDWP::JT_VOID: return 'V'; case JDWP::JT_BOOLEAN: return 'Z'; // Reference types. case JDWP::JT_ARRAY: case JDWP::JT_OBJECT: case JDWP::JT_STRING: case JDWP::JT_THREAD: case JDWP::JT_THREAD_GROUP: case JDWP::JT_CLASS_LOADER: case JDWP::JT_CLASS_OBJECT: return 'L';
參數是通過 ArgArray 的 BuildArgArrayFromVarArgs()這個函數準備的,保存在其成員 arg_array_數組裏,通過AppendXXX()來填充數組;
從這個函數裏,可以看到數據的順序:
非static方法時,傳遞的receiver(java 類對象)不是 nullptr,會把this指針放在 arg_array_的第一個元素裏;
而調用static方法時,參數列表中是沒有this對象的,arg_array_的第一個元素就是對應 static函數的第一個參數;
對於我們正在分析的函數 private boolean execTransact(int code, long dataObj, long replyObj, int flags);爲其構建的 arg_array_的情況是:
this(Binder.java)
|
code | dataObj | replyObj | flags |
---|
在接下來的調用 (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);時, args就是 arg_array_首地址;
我們看下,在art::ArtMethod::Invoke函數跳轉到 art_quick_invoke_stub之前的參數:
0x0000007f8265ae38 <+184>: mov x0, x20 ;this(ArtMethod) 0x0000007f8265ae3c <+188>: mov x1, x22 ; args 0x0000007f8265ae40 <+192>: mov w2, w24 ; args_size 0x0000007f8265ae44 <+196>: mov x3, x21 ; self(art::Thread*) 0x0000007f8265ae48 <+200>: mov x4, x19 ; result 0x0000007f8265ae4c <+204>: mov x5, x23 ; shorty 0x0000007f8265ae50 <+208>: bl 0x7f8264daf0 <art_quick_invoke_stub>
ARM64的調用約定,前面的 6個參數依次存放在 x0~x5;
函數 art_quick_invoke_stub ,是爲了執行 java 函數之前做一個準備工作;
接下來,我們跳轉到 art_quick_invoke_stub:
(gdb) disassemble Dump of assembler code for function art_quick_invoke_stub: => 0x0000007f8264daf0 <+0>: mov x9, sp ;x9保存函數 art::ArtMethod::Invoke 的sp 0x0000007f8264daf4 <+4>: add x10, x2, #0x80 ;x2是 arg_size, x10=x2+0x80 0x0000007f8264daf8 <+8>: sub x10, sp, x10 ;x10=sp - x10 0x0000007f8264dafc <+12>: and x10, x10, #0xfffffffffffffff0 ;x10進行16字節對齊 0x0000007f8264db00 <+16>: mov sp, x10 ;sp移動,相當於開闢了 (x9-x10)byte的棧空間,用來存放:1.args (arg_size byte) 2.ArtMethod(8 byte) 3.保存 callee save register (0x78 byte) 0x0000007f8264db04 <+20>: sub x10, x9, #0x78 ;臨近上一個frame的 0x78的棧空間,下面的關於 x10的操作,都是在保存reg到,這個range的棧上 0x0000007f8264db08 <+24>: str x28, [x10,#112] 0x0000007f8264db0c <+28>: stp x26, x27, [x10,#96] 0x0000007f8264db10 <+32>: stp x24, x25, [x10,#80] 0x0000007f8264db14 <+36>: stp x22, x23, [x10,#64] 0x0000007f8264db18 <+40>: stp x20, x21, [x10,#48] 0x0000007f8264db1c <+44>: stp x9, x19, [x10,#32] 0x0000007f8264db20 <+48>: stp x4, x5, [x10,#16] ;其中x4,存放的是 JValue result的地址,最終java函數的返回值要存入 result中 0x0000007f8264db24 <+52>: stp x29, x30, [x10] ;緊鄰上一個 frame的 0x78的空間填充完成; 0x0000007f8264db28 <+56>: mov x29, x10 ;記錄 frame pointer 0x0000007f8264db2c <+60>: mov x19, x3 ;把 Thread* 放到 x19 來保存 0x0000007f8264db30 <+64>: add x9, sp, #0x8 ;跳過當前frame棧頂的第一個數據,即跳過 ArtMethod,x9指向第二個數據 0x0000007f8264db34 <+68>: cmp w2, #0x0 ; 如果 arg_size 等於 0則跳轉,否則從 arg_array 拷貝數據到棧上,直到arg_array中所有數據拷貝到棧上 0x0000007f8264db38 <+72>: b.eq 0x7f8264db4c <art_quick_invoke_stub+92> 0x0000007f8264db3c <+76>: sub w2, w2, #0x4 ; index = arg_size - 4,index = index -4 0x0000007f8264db40 <+80>: ldr w10, [x1,x2] ; 從 arg_array取出index對應的arg,從後向前獲取數組元素 0x0000007f8264db44 <+84>: str w10, [x9,x2] ; 將 arg存放到棧上的 args區域,每次拷貝 4 byte,先拷貝到高地址,再拷貝到低地址 0x0000007f8264db48 <+88>: b 0x7f8264db34 <art_quick_invoke_stub+68> ; 繼續拷貝 0x0000007f8264db4c <+92>: str xzr, [sp] ; 把棧頂的第一個數據填充0,實際上這個數據對應一個 ArtMethod指針 0x0000007f8264db50 <+96>: adr x11, 0x7f8264dbe0 <art_quick_invoke_stub+240> ; 記錄該函數的幾個入口 0x0000007f8264db54 <+100>: adr x12, 0x7f8264dc28 <art_quick_invoke_stub+312> 0x0000007f8264db58 <+104>: adr x13, 0x7f8264dc70 <art_quick_invoke_stub+384> 0x0000007f8264db5c <+108>: adr x14, 0x7f8264dcd0 <art_quick_invoke_stub+480> 0x0000007f8264db60 <+112>: mov x8, #0x0 // #0 ; integer offset 0x0000007f8264db64 <+116>: mov x15, #0x0 // #0 ; floating offset 0x0000007f8264db68 <+120>: add x10, x5, #0x1 ; short signatur的地址,+0x1是因爲第一個是 return value的shorty,我們是準備參數,所以要跳過: shorty=0x73654521 "ZIJJI" 0x0000007f8264db6c <+124>: ldr w1, [x9],#4 ; x9指向棧上的第一個參數,當函數不是static時,第一個參數是函數對應的類this對象,這裏把 this放到 r1作爲參數傳遞 0x0000007f8264db70 <+128>: ldrb w17, [x10],#1 0x0000007f8264db74 <+132>: cbz w17, 0x7f8264dd30 <art_quick_invoke_stub+576>
在此時,art_quick_invoke_stub 的棧空間情況:
由於 arg_size = 28 = 4(append(this)) + (4+8+8+4)"IJJI" (遍歷shorty時從1開始,忽略返回值);
所以 x2+0x80 = 28+0x80 = 0x1c+0x80 = 0x9c,所以 framesize = 0xa0 = 160 byte;
講一下這裏的對齊,很顯然,x10= sp - 10 過後,要和 #0xfffffffffffffff0 做and運算,運算後會把末尾丟掉,由於棧是從高地址向低地址開闢,所以相當於多開闢了部分空間;
棧空間如下:
已經貼出來的 art_quick_invoke_stub 的這段代碼就已經把棧空間準備好了;
此時的寄存器情況:
x0: ArtMethod*
x1: this (Binder.java)
其餘參數還沒有完成準備;
ARM64 上 art_quick_invoke_stub調用 java 函數的調用約定:
* Outgoing registers:
* x0 - Method*
* x1-x7 - integer parameters.
* d0-d7 - Floating point parameters.
* xSELF = self
* SP = & of ArtMethod*
* x1 = "this" pointer.
*看起來我們纔剛剛準備好 x0,x1 ,xSELF(x19) 和 SP;繼續看剩下的部分代碼:
0x0000007f8264db68 <+120>: add x10, x5, #0x1 ; short signatur的地址,+0x1是因爲第一個是 return value的shorty,我們是準備參數,所以要跳過: shorty=0x73654521 "ZIJJI" 0x0000007f8264db6c <+124>: ldr w1, [x9],#4 ; x9指向棧上的第一個參數,當函數不是static時,第一個參數是函數對應的類this對象,這裏把 this放到 r1作爲參數傳遞,並且x9=x9+4,指向arg1 0x0000007f8264db70 <+128>: ldrb w17, [x10],#1 ; 從shorty signature獲取一個byte到 w17,並且 x10=x10+1,指向signature下一個字節 0x0000007f8264db74 <+132>: cbz w17, 0x7f8264dd30 <art_quick_invoke_stub+576> ; 0,字符串結束,signature結束,參數準備完成,跳轉到 +576位置,準備執行 0x0000007f8264db78 <+136>: cmp w17, #0x46 ; 是否是字符 'F',floating 參數 0x0000007f8264db7c <+140>: b.ne 0x7f8264db90 <art_quick_invoke_stub+160> ; 如果不是字符 'F',進行下一步判斷是否字符 'D' 0x0000007f8264db80 <+144>: cmp x15, #0x60 ; 判斷 floating register是否用完了 0x0000007f8264db84 <+148>: b.eq 0x7f8264dbd0 <art_quick_invoke_stub+224> ; 如 0x0000007f8264db88 <+152>: add x17, x13, x15 0x0000007f8264db8c <+156>: br x17 0x0000007f8264db90 <+160>: cmp w17, #0x44 ; 是否是字符 'D',double 參數 0x0000007f8264db94 <+164>: b.ne 0x7f8264dba8 <art_quick_invoke_stub+184> ; 如果不是字符 'D',進行下一步判斷是否字符 'J' 0x0000007f8264db98 <+168>: cmp x15, #0x60 ; 判斷 floating register是否用完了 0x0000007f8264db9c <+172>: b.eq 0x7f8264dbd8 <art_quick_invoke_stub+232> 0x0000007f8264dba0 <+176>: add x17, x14, x15 0x0000007f8264dba4 <+180>: br x17 0x0000007f8264dba8 <+184>: cmp w17, #0x4a ; 是否是字符 'J',long 參數 0x0000007f8264dbac <+188>: b.ne 0x7f8264dbc0 <art_quick_invoke_stub+208> ; 如果不是字符 'J',進入 else分支 0x0000007f8264dbb0 <+192>: cmp x8, #0x48 ; 判斷 integer register是否用完了 0x0000007f8264dbb4 <+196>: b.eq 0x7f8264dbd8 <art_quick_invoke_stub+232> 0x0000007f8264dbb8 <+200>: add x17, x12, x8 ; 如果是 long參數,跳轉到 x12 + x8(code offset)處,進行load參數;x12= 0x7f8264dc28 <art_quick_invoke_stub+312> 0x0000007f8264dbbc <+204>: br x17 0x0000007f8264dbc0 <+208>: cmp x8, #0x48 ; 不是字符 'F', 'D', 'J',判斷 integer register是否用完了 0x0000007f8264dbc4 <+212>: b.eq 0x7f8264dbd0 <art_quick_invoke_stub+224> 0x0000007f8264dbc8 <+216>: add x17, x11, x8 ; 這個實例中,shorty "IJJI",第一個參數進入else分支, x17=x11+0,而x11在上面初始化過,是 0x7f8264dbe0 <art_quick_invoke_stub+240> 0x0000007f8264dbcc <+220>: br x17 0x0000007f8264dbd0 <+224>: add x9, x9, #0x4 ; x9向棧底移動 4byte,(上一個參數是floating,會走到該分支),移動後,x9指向floating參數後的下一個參數 0x0000007f8264dbd4 <+228>: b 0x7f8264db70 <art_quick_invoke_stub+128> ; 跳轉到 +128處,獲取第二個參數對應的 shorty,並進行過濾 0x0000007f8264dbd8 <+232>: add x9, x9, #0x8 ; x9向棧底移動 8byte,(當上一個參數是 double/long時,會走到該分支),移動後,x9指向 double/long 參數後的下一個參數 0x0000007f8264dbdc <+236>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dbe0 <+240>: ldr w2, [x9],#4 ; load java method的第2個參數, *************當需要load的參數是 4 byte且是integer,對應的分支**************************** 0x0000007f8264dbe4 <+244>: add x8, x8, #0xc ; x8 = x8+12,代碼偏移 0x0000007f8264dbe8 <+248>: b 0x7f8264db70 <art_quick_invoke_stub+128> ; 準備下一個參數 0x0000007f8264dbec <+252>: ldr w3, [x9],#4 0x0000007f8264dbf0 <+256>: add x8, x8, #0xc 0x0000007f8264dbf4 <+260>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dbf8 <+264>: ldr w4, [x9],#4 0x0000007f8264dbfc <+268>: add x8, x8, #0xc 0x0000007f8264dc00 <+272>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc04 <+276>: ldr w5, [x9],#4 ; load java method的第5個參數 0x0000007f8264dc08 <+280>: add x8, x8, #0xc 0x0000007f8264dc0c <+284>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc10 <+288>: ldr w6, [x9],#4 0x0000007f8264dc14 <+292>: add x8, x8, #0xc 0x0000007f8264dc18 <+296>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc1c <+300>: ldr w7, [x9],#4 0x0000007f8264dc20 <+304>: add x8, x8, #0xc 0x0000007f8264dc24 <+308>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc28 <+312>: ldr x2, [x9],#8 ; ****************************當需要load的參數是 8 byte且是integer ,對應的分支**************************** 0x0000007f8264dc2c <+316>: add x8, x8, #0xc 0x0000007f8264dc30 <+320>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc34 <+324>: ldr x3, [x9],#8 ; load java method的第3個參數 0x0000007f8264dc38 <+328>: add x8, x8, #0xc ; x8 = x8+12,代碼偏移 0x0000007f8264dc3c <+332>: b 0x7f8264db70 <art_quick_invoke_stub+128> ; 準備下一個參數 0x0000007f8264dc40 <+336>: ldr x4, [x9],#8 ; load java method的第4個參數 0x0000007f8264dc44 <+340>: add x8, x8, #0xc 0x0000007f8264dc48 <+344>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc4c <+348>: ldr x5, [x9],#8 0x0000007f8264dc50 <+352>: add x8, x8, #0xc 0x0000007f8264dc54 <+356>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc58 <+360>: ldr x6, [x9],#8 0x0000007f8264dc5c <+364>: add x8, x8, #0xc 0x0000007f8264dc60 <+368>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc64 <+372>: ldr x7, [x9],#8 0x0000007f8264dc68 <+376>: add x8, x8, #0xc 0x0000007f8264dc6c <+380>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc70 <+384>: ldr s0, [x9],#4 ;****************************當需要load的參數是 4 byte且是 floating 時的分支**************************** 0x0000007f8264dc74 <+388>: add x15, x15, #0xc 0x0000007f8264dc78 <+392>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc7c <+396>: ldr s1, [x9],#4 0x0000007f8264dc80 <+400>: add x15, x15, #0xc ---Type <return> to continue, or q <return> to quit--- 0x0000007f8264dc84 <+404>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc88 <+408>: ldr s2, [x9],#4 0x0000007f8264dc8c <+412>: add x15, x15, #0xc 0x0000007f8264dc90 <+416>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dc94 <+420>: ldr s3, [x9],#4 0x0000007f8264dc98 <+424>: add x15, x15, #0xc 0x0000007f8264dc9c <+428>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dca0 <+432>: ldr s4, [x9],#4 0x0000007f8264dca4 <+436>: add x15, x15, #0xc 0x0000007f8264dca8 <+440>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dcac <+444>: ldr s5, [x9],#4 0x0000007f8264dcb0 <+448>: add x15, x15, #0xc 0x0000007f8264dcb4 <+452>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dcb8 <+456>: ldr s6, [x9],#4 0x0000007f8264dcbc <+460>: add x15, x15, #0xc 0x0000007f8264dcc0 <+464>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dcc4 <+468>: ldr s7, [x9],#4 0x0000007f8264dcc8 <+472>: add x15, x15, #0xc 0x0000007f8264dccc <+476>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dcd0 <+480>: ldr d0, [x9],#8 ;****************************當需要load的參數是 8 byte,且是 double 時的分支**************************** 0x0000007f8264dcd4 <+484>: add x15, x15, #0xc 0x0000007f8264dcd8 <+488>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dcdc <+492>: ldr d1, [x9],#8 0x0000007f8264dce0 <+496>: add x15, x15, #0xc 0x0000007f8264dce4 <+500>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dce8 <+504>: ldr d2, [x9],#8 0x0000007f8264dcec <+508>: add x15, x15, #0xc 0x0000007f8264dcf0 <+512>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dcf4 <+516>: ldr d3, [x9],#8 0x0000007f8264dcf8 <+520>: add x15, x15, #0xc 0x0000007f8264dcfc <+524>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dd00 <+528>: ldr d4, [x9],#8 0x0000007f8264dd04 <+532>: add x15, x15, #0xc 0x0000007f8264dd08 <+536>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dd0c <+540>: ldr d5, [x9],#8 0x0000007f8264dd10 <+544>: add x15, x15, #0xc 0x0000007f8264dd14 <+548>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dd18 <+552>: ldr d6, [x9],#8 0x0000007f8264dd1c <+556>: add x15, x15, #0xc 0x0000007f8264dd20 <+560>: b 0x7f8264db70 <art_quick_invoke_stub+128> 0x0000007f8264dd24 <+564>: ldr d7, [x9],#8 0x0000007f8264dd28 <+568>: add x15, x15, #0xc 0x0000007f8264dd2c <+572>: b 0x7f8264db70 <art_quick_invoke_stub+128> ;**************** 準備參數的代碼,至此結束,參數已經準備完畢 ***************** 0x0000007f8264dd30 <+576>: ldr x9, [x0,#48] ; x0是 ArtMethod*,48是64bit時METHOD_QUICK_CODE_OFFSET對應的值,這條指令執行結束後,x9的值是 entry_point_from_quick_compiled_code_ 0x0000007f8264dd34 <+580>: blr x9 ; 跳轉到 entry_point_from_quick_compiled_code_ 0x0000007f8264dd38 <+584>: ldp x4, x5, [x29,#16] ; 恢復寄存器,準備返回,其中x4 是 JValue result的地址;x5是shorty addr 0x0000007f8264dd3c <+588>: ldr x28, [x29,#112] 0x0000007f8264dd40 <+592>: ldp x26, x27, [x29,#96] 0x0000007f8264dd44 <+596>: ldp x24, x25, [x29,#80] 0x0000007f8264dd48 <+600>: ldp x22, x23, [x29,#64] 0x0000007f8264dd4c <+604>: ldp x20, x21, [x29,#48] 0x0000007f8264dd50 <+608>: ldrb w10, [x5] ;從x5獲取 shorty第一個字節(代表返回值) 0x0000007f8264dd54 <+612>: cmp w10, #0x56 ;返回值的shorty是否 'V',即void 0x0000007f8264dd58 <+616>: b.eq 0x7f8264dd80 <art_quick_invoke_stub+656> 0x0000007f8264dd5c <+620>: cmp w10, #0x44 ;'D' 是否 double 返回值 0x0000007f8264dd60 <+624>: b.ne 0x7f8264dd6c <art_quick_invoke_stub+636> 0x0000007f8264dd64 <+628>: str d0, [x4] ;保存double值到 result中 0x0000007f8264dd68 <+632>: b 0x7f8264dd80 <art_quick_invoke_stub+656> 0x0000007f8264dd6c <+636>: cmp w10, #0x46 ;'F' 是否 floating 返回值 0x0000007f8264dd70 <+640>: b.ne 0x7f8264dd7c <art_quick_invoke_stub+652> 0x0000007f8264dd74 <+644>: str s0, [x4] ;保存 floating值到 result中 0x0000007f8264dd78 <+648>: b 0x7f8264dd80 <art_quick_invoke_stub+656> 0x0000007f8264dd7c <+652>: str x0, [x4] ;返回值類型不是 'V','D','F', 保存 x0到result中,不關注 32bit/64bit 0x0000007f8264dd80 <+656>: ldp x2, x19, [x29,#32] 0x0000007f8264dd84 <+660>: mov sp, x2 0x0000007f8264dd88 <+664>: ldp x29, x30, [x29] 0x0000007f8264dd8c <+668>: ret
TODO:
1.從Native調用 java method時,總是跳轉到 ArtMethod的 entry_point_from_quick_compiled_code_,假如這個method沒有被編譯,那麼它就沒有對應的 native code,那怎麼執行呢,後續分析 trampoline;
答:在 ClassLinker::LinkCode的時候,如果一個非static,非jni的method 沒有編譯 quick code,那麼其就會通過:
method→SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());把 entry_point_from_quick_compiled_code_ 設置爲 art_quick_to_interpreter_bridge,
最終調用 art_quick_to_interpreter_bridge時會跳轉到 artQuickToInterpreterBridge() → interpreter::EnterInterpreterFromEntryPoint() → interpreter::Execute() → ExecuteMterpImp()進行解釋執行代碼;
流程圖: