從 Native 函數調用 Java 函數

從一個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()進行解釋執行代碼;

流程圖:



發佈了34 篇原創文章 · 獲贊 13 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章