ART运行时之method/field加载

来源:互联网 发布:超融合软件下载 编辑:程序博客网 时间:2024/05/16 17:41

先简单介绍下OAT的文件格式,后面针对Method/Field/JNI方法加载做些代码分析。

OAT文件格式


在虚拟机以ART模式运行的Android手机上,apk安装时,系统通过installed调用dex2oat将apk中的classes.dex优化成目标机器的本地机器指令,优化的oat文件放在/data/dalvik-cache目录下。
OAT文件是ELF格式文件,其包含2个重要的Section,一个是oatdata段,保存了被优化的原.dex文件(Dex File Content),以及一个用来找到方法的索引(OatClass)。通过OatClass段,可以找到本地机器指令的偏移。例如,给定一个类A,那么从Dex文件中可以拿到这个类的所有信息,包括父类,成员变量,函数等。给定一个类及其某一个方法的签名后,就可以通过dex文件内容和OatClass结构体找到在oatexec段的本地机器指令,然后就可以调用这个方法的本地指令了。
一个OAT文件可以包括1到多个DEX文件。

方法调用“狸猫换太子”

这里先介绍下caller/callee方法在同一个dex中的方法/field访问,跨dex调用相对简单很多,后面会介绍下。以下是ART访问method/field用到的主要结构体:

Class是类在内存中的一个映射,包括类中包含的instant field, static fields, virtual methods table, direct methods table, interface methods tables等。
DexFile类是Dex文件在内存中的一个映射,其实就是parse Dex的结果。通过它可以找到DexFile中各个成员的名字和在各个段中的偏移。(可以参考Dex文件结构)
DexCache类是一个buffer,用来保存已经访问过的fields, methods等。
ArtMethod保存了任何一个方法时的入口(native code或者“绷床”函数“)
ArtField类似ArtMethod。
每个Class包含了一个ifTable数组,一个ifTable对应Class直接implement的一个interface,或者通过super class间接implement的一个interface。一个ifTable包含了一个ArtMethod数组,每个ArtMethod对应一个implement 的方法,这个方法可能是当前类实现的,也可以是super类implement的。
每个Class包含了一个vTable,这个vTable也有一个ArtMethod数组,包含了所有的Public方法以及从直接父类或者间接父类继承过来的public方法。
direct methods也有一个ArtMethod数组,包含了constructor函数和private函数。
ClassLoader是Load当前类的ClassLoader(native层)。

ART调用方法时用到的最重要的2个结构体就是DEX Cache和ART Method。

File:dex_cache.ccclass MANAGED DexCache FINAL : public Object {..... private:  HeapReference<Object> dex_;  HeapReference<String> location_;  // Either an int array or long array based on runtime ISA since these arrays hold pointers.  HeapReference<PointerArray> resolved_fields_;  HeapReference<PointerArray> resolved_methods_;  HeapReference<ObjectArray<Class>> resolved_types_;  HeapReference<ObjectArray<String>> strings_;  uint64_t dex_file_;}

每个Dex File在内存中都会对应有一个DEX Cache,例如蘑菇街app有5个multidex,那么虚拟机内存中就会有5个Dex Cache。Dex Cache中保存了对应的DEX中的域(fields), 方法(methods), 类型(types), 字符串(String)。
Dex Cache其实就是一个buffer用来保存已经解析过的fields, methods, types, String。

File:art_method.hclass ArtMethod FINAL {protected:...    struct PACKED(4) PtrSizedFields {           // 解释模式时调用此函数的入口        void* entry_point_from_interpreter_;        // 此函数是一个JNI方法时的调用入口        void* entry_point_from_jni_;        // 以本地代码调用时的函数入口        void* entry_point_from_quick_compiled_code_;    }ptr_sized_fields_      ...} 

ArtMethod最重要的就是这3个entry_point了,分别表示解释模式时调用此函数的入口,此函数是一个JNI方法时的调用入口,以本地代码调用时的函数入口。
在查找方法时,比如A调用B方法,假设A, B在同一个dex中,经过dex2oat优化后,本地代码将会根据B在DEX中的method_id,索引到DEX_Cache中的resolved_method,然后调用B方法的ArtMethod的entry_point_from_quick_compiled_code指针指向的方法入口。

好,接下来看看DexCache的初始化过程。当classloader load一个尚未加载的类A时,findClass->loadClass->defineClass A的时候,会去ClassLinker(单例)中查找A类所在的DEXFile,然后调用ClassLinker->RegisterDexFile()。

File:dalvik_system_file.ccstatic jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,                                        jobject cookie) {  ...  const std::string descriptor(DotToDescriptor(class_name.c_str()));  const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));  for (auto& dex_file : *dex_files) {    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);    if (dex_class_def != nullptr) {    ...      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();      class_linker->RegisterDexFile(*dex_file);      Handle<mirror::ClassLoader> class_loader(          hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));      mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,                                                        class_loader, *dex_file, *dex_class_def);      ...    }  }  return nullptr;}

在ClassLinker->RegisterDexFile()中,如果所在DEX还没有在ClassLinker中创建了对应DEX的dex_cache,将会创建一个dexcache并初始化, 然后注册到ClassLinker中,方便后续查找。

File:class_linker.ccvoid ClassLinker::RegisterDexFile(const DexFile& dex_file) {  ...  Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));  {    ...    if (IsDexFileRegisteredLocked(dex_file)) {      return;    }    RegisterDexFileLocked(dex_file, dex_cache);  }}

下面看看DexCache初始化了些啥?
DexCache为该Dex中的String, type, method, field都分配了访问指针和数组,分配的空间大小来源于Dex文件里面的记录,然后调用dex_cache->init()进行初始化。

File: Class_linker.ccmirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) {  StackHandleScope<6> hs(self);  auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(      GetClassRoot(kJavaLangDexCache)->AllocObject(self))));...  auto location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str())));...  auto strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds())));...  auto types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds())));...  auto methods(hs.NewHandle(AllocPointerArray(self, dex_file.NumMethodIds())));...  auto fields(hs.NewHandle(AllocPointerArray(self, dex_file.NumFieldIds())));...  dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(),                  fields.Get(), image_pointer_size_);  return dex_cache.Get();

init()函数中初始化了String, field, types的访问变量,最最最重要的是初始化了该Dex中所有的ArtMethod,初始化的值是从runtime中拿出来的ResolutionMethod, 就是Runtime method。

File: Dex_cache.ccvoid DexCache::Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings,                    ObjectArray<Class>* resolved_types, PointerArray* resolved_methods,                    PointerArray* resolved_fields, size_t pointer_size) {  SetDexFile(dex_file);  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);  SetFieldObject<false>(StringsOffset(), strings);  SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields);  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types);  SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods);  Runtime* const runtime = Runtime::Current();  if (runtime->HasResolutionMethod()) {    // Initialize the resolve methods array to contain trampolines for resolution.    Fixup(runtime->GetResolutionMethod(), pointer_size);  }}void DexCache::Fixup(ArtMethod* trampoline, size_t pointer_size) {  // 将ArtMethod都初始化成Runtime method。  auto* resolved_methods = GetResolvedMethods();  for (size_t i = 0, length = resolved_methods->GetLength(); i < length; i++) {    if (resolved_methods->GetElementPtrSize<ArtMethod*>(i, pointer_size) == nullptr) {      resolved_methods->SetElementPtrSize(i, trampoline, pointer_size);    }  }}

而Runtime的ResolutionMethod是从oat文件头里面取出来的,也就是说,这个ResulutionMethod是dex2oat写到oat文件里面去的。

File: Image.ccImageSpace* ImageSpace::Init(const char* image_filename, const char* image_location,                             bool validate_oat_file, std::string* error_msg) {.....  runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));....}

dex2oat写到文件头里面的ResulutionMethod就是下面这个ARTMethod,它的entry_point_from_quick_compiled_code_被初始化为一段汇编指令DEFINE_FUNCTION art_quick_resolution_trampoline,这段汇编代码通过宏GetQuickResolutionStub()获取到。其实这段汇编代码最初就是放在boot.oat中的。

File: Runtime.ccArtMethod* Runtime::CreateResolutionMethod() {  auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();  ...    method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());  return method;}

辗转,这段汇编代码最终通过一个汇编的跳转Symbol指令跳回到C函数的artQuickResolutionTrampoline()方法中。以下是一段X86 64位汇编,针对每种平台,都会有对应的一个trampoline汇编函数。

File: quick_entrypoints_x86_64.SDEFINE_FUNCTION art_quick_resolution_trampoline    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME    movq %gs:THREAD_SELF_OFFSET, %rdx    movq %rsp, %rcx    call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)    movq %rax, %r10               // Remember returned code pointer in R10.    movq (%rsp), %rdi             // Load called method into RDI.    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME    testq %r10, %r10              // If code pointer is null goto deliver pending exception.    jz 1f    jmp *%r10                     // Tail call into method.1:    DELIVER_PENDING_EXCEPTIONEND_FUNCTION art_quick_resolution_trampoline

阶段小结

一个dexfile中的类如果从来没有被加载过,那么defineClass会为这个dexFile创建一个DexCache,这个DexCache包含一个指针数组resolved_methods_,数组的大小就是当前dexFile中所有的method方法数:

HeapReference<PointerArray> resolved_methods_;

并且初始化所有的指针都指向了一个叫做Runtime Method的ArtMethod,这个Runtime Method就是个“狸猫”,他只是一个代理,后面会将真正的“太子”方法请回来,好,这个Runtime Method也是一个ArtMethod, 它有三个跳转指针变量,其中最重要的entry_point_from_quick_compiled_code_指向一段汇编指令,最后回调到C函数artQuickResolutionTrampoline()

void* entry_point_from_quick_compiled_code_;

好了,如果是同一个dex内部跳转,在类首次被调用时,那么它的函数调用接口最终都走到了这个artQuickResolutionTrampoline().
比如类A的fA()调用到了类B的fB(),A.fA()->B.fB(),dex2oat在生成A.f()的native code时,其调用B.fA()最后就偏移到了Class B所在的dexcache中fB对应的ArtMethod的entry_point_from_quick_compiled_code_地址。dex2oat如何知道fB对应的ArtMethod呢?因为Dex文件里面保存了fB在该Dex中的信息,包括签名和methodId,而dexcache中的ArtMethods数组就是按照这个methodId来对应到各个method的。所以, dex2oat在解析了Dex文件后根据methodId就知道到哪个ArtMethod去调用entry_point_from_quick_compiled_code_了。

下面是一个汇编代码的例子:

函数

com.mogujie.www.hotpatchtest.HotPatchInvoker.<init>()

对应的汇编代码如下,eax+63808就是根据methodId偏移找到对应method在dexcache中的ArtMethod指针,然后通过偏移44找到entry_point_from_quick_compiled_code_地址。

      0x003bb482:           8B8040F90000        mov     eax, [eax + 63808]      0x003bb488:                 FF502C        call    [eax + 44]

请回“太子”

在上面分析完类首次调用的过程后,我们需要看下artQuickResolutionTrampoline()里面的处理,以及各个不同方法的调用流程。

方法调用分为五种指令:

invoke_virtual   调用一个虚方法,即非private, static, final, constructor方法invoke_super     调用最近的父类的virtual方法, 对应的method_id和calling clsss一致invoke_direct    调用一个非static的direct方法,也就是private, constructor方法invoke_static    调用一个static的direct方法invoke_interface 调用一个interface方法,当操作一个不知道实体类的object时使用,method_id指向的是一个interface

以下是artQuickResolutionTrampoline()的全部代码体,是整个Runtime的核心代码,对于同一个dex中的相互跳转,以上五种类型的方法调用都会走到这个函数中(dex2oat优化的native代码最终都会调用到函数artQuickResolutionTrampoline),第一个参数called表示被调用的类方法,第二个参数receiver表示被调用的对象,也就是接收消息的对象,第三个参数thread表示当前线程,第四个参数sp指向调用栈顶。

先大致浏览一下artQuickResolutionTrampoline()方法,后面针对5中invoke_方法做独立代码分析。

File: Quick_trampoline_entry.ccextern "C" const void* artQuickResolutionTrampoline(    ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {  ...  // 获取被调用方法的一些信息  ClassLinker* linker = Runtime::Current()->GetClassLinker();  ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);  InvokeType invoke_type;  MethodReference called_method(nullptr, 0);  // 是否一个“狸猫”Artmethod?如果是,表示方法的真正ArtMethod还没创建好。  const bool called_method_known_on_entry = !called->IsRuntimeMethod();  if (!called_method_known_on_entry) {    uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));    const DexFile::CodeItem* code;    called_method.dex_file = caller->GetDexFile();    code = caller->GetCodeItem();    CHECK_LT(dex_pc, code->insns_size_in_code_units_);    const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);    Instruction::Code instr_code = instr->Opcode();    bool is_range;    switch (instr_code) {      case Instruction::INVOKE_DIRECT:        invoke_type = kDirect;        is_range = false;        break;      case Instruction::INVOKE_DIRECT_RANGE:        invoke_type = kDirect;        is_range = true;        break;      case Instruction::INVOKE_STATIC:        invoke_type = kStatic;        is_range = false;        break;      case Instruction::INVOKE_STATIC_RANGE:        invoke_type = kStatic;        is_range = true;        break;      case Instruction::INVOKE_SUPER:        invoke_type = kSuper;        is_range = false;        break;      case Instruction::INVOKE_SUPER_RANGE:        invoke_type = kSuper;        is_range = true;        break;      case Instruction::INVOKE_VIRTUAL:        invoke_type = kVirtual;        is_range = false;        break;      case Instruction::INVOKE_VIRTUAL_RANGE:        invoke_type = kVirtual;        is_range = true;        break;      case Instruction::INVOKE_INTERFACE:        invoke_type = kInterface;        is_range = false;        break;      case Instruction::INVOKE_INTERFACE_RANGE:        invoke_type = kInterface;        is_range = true;        break;      default:        LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);        UNREACHABLE();    }    //从Dex的instr代码中获取dex_method_index。    called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();  } else {    //这个地方有点绕,对于Static方法来说,被调用的时候可能所在类尚未被初始化(<clinit>())。这个时候就需要在这里block Static    //方法的调用,直到类初始化完成。但是类初始化可能其它线程在做了,这时候artQuickResolutionTrampoline就会仍然返回“狸    //猫”Artmethod, 这不,又进来了这里了,好吧,继续等待初始化完成。    invoke_type = kStatic;    called_method.dex_file = called->GetDexFile();    called_method.dex_method_index = called->GetDexMethodIndex();  }  ....  //是否一个invoke_virtual/invoke_interface方法  const bool virtual_or_interface = invoke_type == kVirtual || invoke_type == kInterface;  // Resolve method filling in dex cache.  if (!called_method_known_on_entry) {    ....    //调用class_linker将calledMethod所在的类加载进来,为每个method生成一个ArtMethod,    //并将entry_point_from_quick_compiled_code_设置为native code.    //这里将得到“太子”ArtMethod。    called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);  }  const void* code = nullptr;  if (LIKELY(!self->IsExceptionPending())) {    // 兼容性检查    CHECK(!called->CheckIncompatibleClassChange(invoke_type))        << PrettyMethod(called) << " " << invoke_type;    if (virtual_or_interface) {      ...       //如果是一个invoke_virtual/invoke_interface方法,那么当前找到的方法可能是一个父类的ArtMethod或者一个interface类的       //ArtMethod,这时候就需要从receiver实例中找到receiver的类,并根据vtable/ifTable找到真正需要的ArtMethod。       // vtable/ifTable是存在于Class对象里面的用来查找本类virtual method/interface method的索引表      ArtMethod* orig_called = called;      if (invoke_type == kVirtual) {        called = receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*));      } else {        called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*));      }      ...    }    ...    // 确保调用的类已经初始化好了    linker->EnsureInitialized(soa.Self(), called_class, true, true);    if (LIKELY(called_class->IsInitialized())) {      ...        //从called的ArtMethod中拿出entry_point_from_quick_compiled_code_指针        code = called->GetEntryPointFromQuickCompiledCode();      }    } else if (called_class->IsInitializing()) {     ...     else if (invoke_type == kStatic) {        // 类还在初始化,那么继续走trampling吧,咱再等等。        code = linker->GetQuickOatCodeFor(called);      } else {        // 非static方法,直接调用native code。        code = called->GetEntryPointFromQuickCompiledCode();      }    }  }  ...  return code;}

invoke_direct调用流程

分析artQuickResolutionTrampoline()代码,我们先看看invoke_direct xxx调用方法指令的流程。

const bool called_method_known_on_entry = !called->IsRuntimeMethod();
inline bool ArtMethod::IsRuntimeMethod() {  return dex_method_index_ == DexFile::kDexNoIndex;}

called_method_known_on_entry是一个flag用来标记当前的called变量(ArtMethod类型)是否是一个运行时的ArtMethod,如果是,那么表示这个ArtMethod只是一个“狸猫“ArtMethod,如上分析,他就是DexCache初始化的时候指向的Resolution Method,知道这是一个占位替身后,需要在artQuickResolutionTrampoline()方法中去找到真正的那个ArtMethod。如何找到这个真的ArtMethod呢?首先通过字节码获取到要查找的method一个dex_method_index。

  called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();

然后在ClassLinker里面去查找/构造这个ArtMethod。ART虚拟机在被调用的方法所在类还没有加载进来时,对所有的dex方法的调用都会用一个替身ArtMethod进行“虚假”调用,在这个“虚假”调用里面,找到真正的ArtMethod后,回填dexcache中的ArtMethod指针, 这样下次native代码就找到真的那个ArtMethod进行调用了。

  if (!called_method_known_on_entry) {    ...    called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);  }

如下,分析ResolvedMethod()代码,首先在dex_cache中查找,找到则直接返回,当然得满足条件非空且非Runtime method。当然如果类尚未加载,当前dex_cache中还是那个替身ArtMethod,那么resolved为空。好,继续往下,调用ResolveType()函数对类进行解析。

File: Class_liner.ccArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx,                                      Handle<mirror::DexCache> dex_cache,                                      Handle<mirror::ClassLoader> class_loader,                                      ArtMethod* referrer, InvokeType type) {  DCHECK(dex_cache.Get() != nullptr);  // 检查Cache是否有  ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);  if (resolved != nullptr && !resolved->IsRuntimeMethod()) {    DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();    return resolved;  }  // 获取调用类  const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);  mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);  if (klass == nullptr) {    return nullptr;  }  // 先尝试在本dex的dex_cache找,输入参数是method_idx.  switch (type) {    case kDirect:  // Fall-through.    case kStatic:      resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_);      break;...  }...  // 兼容性检查,返回找到的ArtMethod  if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) {    // Be a good citizen and update the dex cache to speed subsequent calls.    dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);    return resolved;  } else {    .....  }}

在ResolveType()中,会调用pathClassLoader加载需要的类,这里会对每个类创建一个nativie层的Class对象,Class对象就是java类在虚拟机内存中的一个印象,如下,这个Class对象会保存所在的dex的dex_cache, 接口表(interface table), 父类(super class), 虚函数表(virtual table), direct methods指针, fields指针, virtual methods指针等。这些变量都会在Class_linker的DefineClass()函数里面被赋值。

File: Class.hclass MANAGED Class FINAL : public Object {// Defining class loader, or null for the "bootstrap" system loader.  HeapReference<ClassLoader> class_loader_;  HeapReference<DexCache> dex_cache_;  HeapReference<IfTable> iftable_;  // The superclass, or null if this is java.lang.Object, an interface or primitive type.  HeapReference<Class> super_class_;  // virtual table, 存储当前类及父类的virtual methods  HeapReference<PointerArray> vtable_;  // static, private, and <init> methods. Pointer to an ArtMethod array.  uint64_t direct_methods_;  // instance fields  uint64_t ifields_;  // Static fields  uint64_t sfields_;  // Virtual methods defined in this class; invoked through vtable. Pointer to an ArtMethod array.  uint64_t virtual_methods_;};

创建好这个class的native对象以后,这个class对象又会被放入dex_cache中。以后访问这个类的时候就可以直接从dex_cache中拿了。

File: class_linker.ccmirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx,                                        Handle<mirror::DexCache> dex_cache,                                        Handle<mirror::ClassLoader> class_loader) {    ......    resolved = FindClass(self, descriptor, class_loader);    if (resolved != nullptr) {      dex_cache->SetResolvedType(type_idx, resolved);    }}

OK, 看看class对象的赋值过程,流程是这样的:

artQuickResolutionTrampoline()->ResolveMethod()->ResolveType()->FindClass()->DefineClass()->LoadClass()->LoadClassMemebers()

由于代码太多,这里直接跳入LoadClassMemebers()函数, LoadClassMemebers()函数对新创建的class对象进行了赋值,包括:
1. Static fields数组赋值
2. Instant fields数组赋值
3. direct methods的ArtMethod数组赋值
4. virtual methods的ArtMethod数组赋值

File: Class_linker.ccvoid ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,                                   const uint8_t* class_data,                                   Handle<mirror::Class> klass,                                   const OatFile::OatClass* oat_class) {  {    ...    /** 初始化Static fields **/    const size_t num_sfields = it.NumStaticFields();    ArtField* sfields = num_sfields != 0 ? AllocArtFieldArray(self, num_sfields) : nullptr;    for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {      LoadField(it, klass, &sfields[i]);    }    klass->SetSFields(sfields);    klass->SetNumStaticFields(num_sfields);    // 加载intant fields **/    const size_t num_ifields = it.NumInstanceFields();    ArtField* ifields = num_ifields != 0 ? AllocArtFieldArray(self, num_ifields) : nullptr;    for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {      LoadField(it, klass, &ifields[i]);    }    klass->SetIFields(ifields);    klass->SetNumInstanceFields(num_ifields);    // 给Direct methods分配空间    if (it.NumDirectMethods() != 0) {      klass->SetDirectMethodsPtr(AllocArtMethodArray(self, it.NumDirectMethods()));    }    klass->SetNumDirectMethods(it.NumDirectMethods());    // 给Virtual methods分配空间    if (it.NumVirtualMethods() != 0) {      klass->SetVirtualMethodsPtr(AllocArtMethodArray(self, it.NumVirtualMethods()));    }    klass->SetNumVirtualMethods(it.NumVirtualMethods());    size_t class_def_method_index = 0;    uint32_t last_dex_method_index = DexFile::kDexNoIndex;    size_t last_class_def_method_index = 0;    // 初始化Direct methods    for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {      ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);      LoadMethod(self, dex_file, it, klass, method);      LinkCode(method, oat_class, class_def_method_index);      uint32_t it_method_index = it.GetMemberIndex();      if (last_dex_method_index == it_method_index) {        // duplicate case        method->SetMethodIndex(last_class_def_method_index);      } else {        method->SetMethodIndex(class_def_method_index);        last_dex_method_index = it_method_index;        last_class_def_method_index = class_def_method_index;      }      class_def_method_index++;    }    // 初始化Virtual methods    for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {      ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);      LoadMethod(self, dex_file, it, klass, method);      DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);      LinkCode(method, oat_class, class_def_method_index);      class_def_method_index++;    }  }}

好,终于可以看到“太子”ArtMethod如何创建被回填了,狸猫换太子的游戏可以结束了。

void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,....    for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {      ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);      LoadMethod(self, dex_file, it, klass, method);      LinkCode(method, oat_class, class_def_method_index);

先从kclass的native class对象中拿出需要构造的ArtMethod对象,然后调用LoadMethod对其进行赋值,包括method_id, method_name等。

void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,                             Handle<mirror::Class> klass, ArtMethod* dst) {  uint32_t dex_method_idx = it.GetMemberIndex();  const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);  const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);  dst->SetDexMethodIndex(dex_method_idx);  dst->SetDeclaringClass(klass.Get());  dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());  dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());  dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());

然后ClassLoadMembers()调用LinkCode()方法对ArtMethod的entry_point_from_quick_compiled_code_进行赋值。

void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,                           uint32_t class_def_method_index) {...  if (oat_class != nullptr) {    // Every kind of method should at least get an invoke stub from the oat_method.    // non-abstract methods also get their code pointers.    const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);    oat_method.LinkMethod(method);  } ...}

对于invoke_direct方法来说,ArtMethod的entry_point_from_quick_compiled_code_是通过oat_method.LinkMethod(method);赋值的, 最终被赋值为oat_method的code,也就是这个method在oat文件中的native地址。好了,通过这样回填dexcache中的ArtMethod以后,下次这个method被调用到的时候找到的就是“太子”而不是“狸猫”了。“太子”ArtMethod的entry_point_from_quick_compiled_code_指针指向的是当前method的native code ptr。

File: Oat_file.ccvoid OatFile::OatMethod::LinkMethod(ArtMethod* method) const {  CHECK(method != nullptr);  method->SetEntryPointFromQuickCompiledCode(GetQuickCode());}
File: Oat_file.h...  class OatMethod FINAL {  ...    const void* GetQuickCode() const {      return GetOatPointer<const void*>(code_offset_);    }

我们继续回退到ResolveMethod()中,在创建好“太子”ArtMethod后,将其放入dex_cache中,方便下次调用。

artQuickResolutionTrampoline()->ResolveMethod()->ResolveType()->FindClass()->DefineClass()->LoadClass()->LoadClassMemebers()
File: Class_linker.ccArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx,...  // 对类进行兼容性检查  if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) {    // 将dex_cache中的ResolvedMethod(“狸猫”)换成“太子”,下次调用就直接走native code了。    dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);    return resolved;  }

最后到artQuickResolutionTrampoline(),将找到的CalledMethod塞入callee的栈帧中,当作第一个参数,然后返回native code地址。art_quick_resolution_trampoline汇编代码直接调用code:call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)

File: Quick_trampoline_entrypoints.cc// Lazily resolve a method for quick. Called by stub code.extern "C" const void* artQuickResolutionTrampoline(    ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp){...      if (!called_method_known_on_entry) {    ...    // 调用ResolveMethod找到“太子”    called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);  }  ...  code = called->GetEntryPointFromQuickCompiledCode();  ...    // 将找到的CalledMethod塞入callee的栈帧中,当作第一个参数   *sp = called;   // 返回native code   return code}
File: quick_entrypoints_x86_64.SDEFINE_FUNCTION art_quick_resolution_trampoline    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME    movq %gs:THREAD_SELF_OFFSET, %rdx    movq %rsp, %rcx    call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)    movq %rax, %r10               // Remember returned code pointer in R10.    movq (%rsp), %rdi             // Load called method into RDI.    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME    testq %r10, %r10              // If code pointer is null goto deliver pending exception.    jz 1f    jmp *%r10                     // Tail call into method.1:    DELIVER_PENDING_EXCEPTIONEND_FUNCTION art_quick_resolution_trampoline

invoke_static调用流程

基本类似invoke_direct方法,invoke_static方法稍微有点特殊,就是在调用static方法时,其所在的类可能尚未被初始化(\

File: Quick_trampoline_entrypoints.cc// Lazily resolve a method for quick. Called by stub code.extern "C" const void* artQuickResolutionTrampoline(    ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {...  const bool called_method_known_on_entry = !called->IsRuntimeMethod();  if (!called_method_known_on_entry) {    uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));    const DexFile::CodeItem* code;    called_method.dex_file = caller->GetDexFile();    code = caller->GetCodeItem();    const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);    Instruction::Code instr_code = instr->Opcode();    bool is_range;    switch (instr_code) {    ...      case Instruction::INVOKE_STATIC:        invoke_type = kStatic;        is_range = false;        break;      case Instruction::INVOKE_STATIC_RANGE:        invoke_type = kStatic;        is_range = true;        break;    }    called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();  } else {     /*      ** 上次调用EnsureInitialized()时,另外一个线程正在初始化,且未初始化好,     ** 继续调用artQuickResolutionTrampoline      */    invoke_type = kStatic;    called_method.dex_file = called->GetDexFile();    called_method.dex_method_index = called->GetDexMethodIndex();  }....  const bool virtual_or_interface = invoke_type == kVirtual || invoke_type == kInterface;  // Resolve method filling in dex cache.  if (!called_method_known_on_entry) {  ...    /* 调用ResolveMethod来解析Class并创建ArtMethod */    called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);  }  const void* code = nullptr;  if (LIKELY(!self->IsExceptionPending())) {  ...    if (virtual_or_interface) {      ...    } else if (invoke_type == kStatic) {      const auto called_dex_method_idx = called->GetDexMethodIndex();      // For static invokes, we may dispatch to the static method in the superclass but resolve      // using the subclass. To prevent getting slow paths on each invoke, we force set the      // resolved method for the super class dex method index if we are in the same dex file.      // b/19175856      if (called->GetDexFile() == called_method.dex_file &&          called_method.dex_method_index != called_dex_method_idx) {        called->GetDexCache()->SetResolvedMethod(called_dex_method_idx, called, sizeof(void*));      }    }    /*    ** 确认类初始化成功,static方法的ArtMethod的art_quick_resolution_trampoline    ** 变量赋值也在EnsureInitialized()在类初始化好之后赋值为native code的    */    ...    linker->EnsureInitialized(soa.Self(), called_class, true, true);    if (LIKELY(called_class->IsInitialized())) {      if (UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self, called))) {       ...       } else {        code = called->GetEntryPointFromQuickCompiledCode();      }    } else if (called_class->IsInitializing()) {      if (UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self, called))) {        ...      } else if (invoke_type == kStatic) {        // 类还在初始化中,那么继续调用artQuickResolutionTrampoline方法。        code = linker->GetQuickOatCodeFor(called);      } else {        // No trampoline for non-static methods.        code = called->GetEntryPointFromQuickCompiledCode();      }    } else {      DCHECK(called_class->IsErroneous());    }  }  // 将找到的CalledMethod塞入callee的栈帧中,当作第一个参数  *sp = called;  return code;}

invoke_virtual调用

Class类中vtable_和virtual_methods_用来访问类中的virtual methods。

File: Class.hclass MANAGED Class FINAL : public Object {...  // Virtual method table (vtable), for use by "invoke-virtual".  The vtable from the superclass is  // copied in, and virtual methods from our class either replace those from the super or are  // appended. For abstract classes, methods may be created in the vtable that aren't in  // virtual_ methods_ for miranda methods.  HeapReference<PointerArray> vtable_;  // Virtual methods defined in this class; invoked through vtable. Pointer to an ArtMethod array.  uint64_t virtual_methods_;...}

同样类似invoke_direct,只是在找到ArtMethod后,这个ArtMethod可能是父类的,之后需要从实例(receiver)中获取到实例的类(可能是一个子类,也可能就是父类本身),然后通过一个FindVirtualMethodForVirtual方法到vtable(virtual table)中找到实例类的ArtMethod。

// Lazily resolve a method for quick. Called by stub code.extern "C" const void* artQuickResolutionTrampoline(    ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {  ...  const void* code = nullptr;  if (LIKELY(!self->IsExceptionPending())) {    if (virtual_or_interface) {        /*        ** dex2oat一个virtual方法时,当时指向的类可能是super类,这时候就需要找到实例receiver,获取其        ** 真正的sub类,然后通过FindVirtualMethodForVirtual找到sub类的实现,对于interfacel类似。        */      ArtMethod* orig_called = called;      if (invoke_type == kVirtual) {        called = receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*));      } else {        called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*));      }        ...    }    // Ensure that the called method's class is initialized.    linker->EnsureInitialized(soa.Self(), called_class, true, true);    if (LIKELY(called_class->IsInitialized())) {     ...     //获取native code的入口地址        code = called->GetEntryPointFromQuickCompiledCode();    }   }...  return code;}

我们来看下下面这个代码片段的例子,ExtendsCase3Sub是ExtendsCase3Super的一个子类。dex2oat在优化testExtendsCase3()的代码时,并不知道extends的实际类型,所以只能到ExtendsCase3Super类中查找对应的ArtMethod。

void main(){....ExtendsCase3SuperPatch extends3Sub = new ExtendsCase3Sub();ExtendsCase3SuperPatch extends3Super = new ExtendsCase3Super();result = testExtendsCase3(extends3Sub);result = testExtendsCase3(extends3Super);}public boolean testExtendsCase3(ExtendsCase3SuperPatch extends3) throws IOException {    return extends3.method2();}

testExtendsCase3()方法对应的Dex代码如下,代码里找super类,即ExtendsCase3Super.method2()。

boolean com.mogujie.www.hotpatchtest.cases.CasesTest.testExtendsCase3(com.mogujie.www.hotpatchtest.cases.Extends.ExtendsCase3Super) (dex_method_idx=15974)    DEX CODE:      0x0000: invoke-virtual {v2}, boolean com.mogujie.www.hotpatchtest.cases.Extends.ExtendsCase3Super.method2() // method@15985      0x0003: move-result v0      0x0004: return v0

好了,我们看看FindVirtualMethodForVirtual()的实现, receiver->GetClass()获取到实例的Class,然后调用FindVirtualMethodForVirtual()通过Super类的method_index_到当前类的vtable中找到对应的ArtMethod。子类如果覆盖了父类的一个方法,那么其method_index_在父类和子类中是一样的。method_index_是在vtable中的一个偏移。

File: Art_method.hclass ArtMethod FINAL {...  // Entry within a dispatch table for this method. For static/direct methods the index is into  // the declaringClass.directMethods, for virtual methods the vtable and for interface methods the  // ifTable.  uint32_t method_index_;}
// receiver->GetClass()获得实例的Classcalled = receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*));
inline ArtMethod* Class::FindVirtualMethodForVirtual(ArtMethod* method, size_t pointer_size) { // The argument method may from a super class.  // Use the index to a potentially overridden one for this instance's class.  return GetVTableEntry(method->GetMethodIndex(), pointer_size);}inline ArtMethod* Class::GetVTableEntry(uint32_t i, size_t pointer_size) {  if (ShouldHaveEmbeddedImtAndVTable()) {    return GetEmbeddedVTableEntry(i, pointer_size);  }  auto* vtable = GetVTable();  //从实例的类的vtable中找到实例类中该方法的ArtMethod,i就是method_index_  return vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);}

那么Art虚拟机是如何确保子类覆盖父类的方法后,其method_index_是一样的呢?秘密在LinkVirtualMethods()里,DefineClass()的时候,会为当前被define的类生成一个vtable(vtable是一个用来保存当前类的所有virtual方法的Artmethod的一个指针数组),然后找到其直接父类。
1. 将直接父类的vtable拷贝到当前类的vtable中。
2. 查找vtable中是否有跟当前类一样签名的方法,如果有,那么用当前类的artmethod直接覆盖vtable中的父类的artmethod
3. 将存在于当前类的virtual method而不存在于父类的append到vtable尾部。

ResolveMethod()->ResolveType()->FindClass()->FindClassInPathClassLoader()->DefineClass()->LinkClass()->LinkMethods()->LinkVirtualMethods()
File: Class_linker.ccbool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) {  const size_t num_virtual_methods = klass->NumVirtualMethods();  if (klass->HasSuperClass()) {    const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();    const size_t max_count = num_virtual_methods + super_vtable_length;    ...    MutableHandle<mirror::PointerArray> vtable;    if (super_class->ShouldHaveEmbeddedImtAndVTable()) {      /*      ** 父类是一个abstract类,那么vtable已经嵌入到了父类的字节码中。      */      vtable = hs.NewHandle(AllocPointerArray(self, max_count));      for (size_t i = 0; i < super_vtable_length; i++) {        vtable->SetElementPtrSize(            i, super_class->GetEmbeddedVTableEntry(i, image_pointer_size_), image_pointer_size_);      }      if (num_virtual_methods == 0) {        klass->SetVTable(vtable.Get());        return true;      }    } else {      /*      ** 父类在被define的时候创建了vtable,拷贝父类的vtable到当前类的vtable中。      */      auto* super_vtable = super_class->GetVTable();      if (num_virtual_methods == 0) {        klass->SetVTable(super_vtable);        return true;      }      vtable = hs.NewHandle(down_cast<mirror::PointerArray*>(          super_vtable->CopyOf(self, max_count)));    }    // How the algorithm works:    // 1. Populate hash table by adding num_virtual_methods from klass. The values in the hash    // table are: invalid_index for unused slots, index super_vtable_length + i for a virtual    // method which has not been matched to a vtable method, and j if the virtual method at the    // index overrode the super virtual method at index j.    // 2. Loop through super virtual methods, if they overwrite, update hash table to j    // (j < super_vtable_length) to avoid redundant checks. (TODO maybe use this info for reducing    // the need for the initial vtable which we later shrink back down).    // 3. Add non overridden methods to the end of the vtable.    static constexpr size_t kMaxStackHash = 250;    const size_t hash_table_size = num_virtual_methods * 3;    uint32_t* hash_table_ptr;    std::unique_ptr<uint32_t[]> hash_heap_storage;    if (hash_table_size <= kMaxStackHash) {      hash_table_ptr = reinterpret_cast<uint32_t*>(          alloca(hash_table_size * sizeof(*hash_table_ptr)));    }     LinkVirtualHashTable hash_table(klass, hash_table_size, hash_table_ptr, image_pointer_size_);    // Add virtual methods to the hash table.    for (size_t i = 0; i < num_virtual_methods; ++i) {      hash_table.Add(i);    }    // Loop through each super vtable method and see if they are overriden by a method we added to    // the hash table.    for (size_t j = 0; j < super_vtable_length; ++j) {      // Search the hash table to see if we are overidden by any method.      ArtMethod* super_method = vtable->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);      MethodNameAndSignatureComparator super_method_name_comparator(          super_method->GetInterfaceMethodIfProxy(image_pointer_size_));      uint32_t hash_index = hash_table.FindAndRemove(&super_method_name_comparator);      if (hash_index != hash_table.GetNotFoundIndex()) {        ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(            hash_index, image_pointer_size_);        if (klass->CanAccessMember(super_method->GetDeclaringClass(),                                   super_method->GetAccessFlags())) {          vtable->SetElementPtrSize(j, virtual_method, image_pointer_size_);          virtual_method->SetMethodIndex(j);        }      }    }    // Add the non overridden methods at the end.    size_t actual_count = super_vtable_length;    for (size_t i = 0; i < num_virtual_methods; ++i) {      ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);      size_t method_idx = local_method->GetMethodIndexDuringLinking();      if (method_idx < super_vtable_length &&          local_method == vtable->GetElementPtrSize<ArtMethod*>(method_idx, image_pointer_size_)) {        continue;      }      vtable->SetElementPtrSize(actual_count, local_method, image_pointer_size_);      local_method->SetMethodIndex(actual_count);      ++actual_count;    }    // Shrink vtable if possible    if (actual_count < max_count) {      vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, actual_count)));    }    klass->SetVTable(vtable.Get());  } else {    /*    ** 没有父类,那么将当前类的所有virtual methods的Artmethods加进vtable。    */    auto* vtable = AllocPointerArray(self, num_virtual_methods);    for (size_t i = 0; i < num_virtual_methods; ++i) {      ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);      vtable->SetElementPtrSize(i, virtual_method, image_pointer_size_);      virtual_method->SetMethodIndex(i & 0xFFFF);    }    klass->SetVTable(vtable);  }  return true;}

invoke_super 调用过程

跟invoke_direct流程一样,其实也就是访问另外一个类的方法,只不过这个类有点特殊,是一个自己的super类而已。

invoke_interface 调用过程

类似invoke_virtual,interface接口是通过iftable_来访问。ifTable是一个指针数组,指向implements的各个接口类。

File: Class.hclass MANAGED Class FINAL : public Object {...  HeapReference<IfTable> iftable_;...}

同样类似invoke_virutal, 调用invoke_interface的时候,刚开始找到的called(ArtMethod类型)是隶属于interface类的,仍然需要调用receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*))来获取receiver实例的那个类的ArtMethod。

// Lazily resolve a method for quick. Called by stub code.extern "C" const void* artQuickResolutionTrampoline(    ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {  ...        called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*));        code = called->GetEntryPointFromQuickCompiledCode();  ...  return code;}

来看看FindVirtualMethodForInterface()的实现,可以看到每一个Class都有一个interface table数组,由于一个类可以implement多个interface接口类,每个接口类在这个interface table中都有一个MethodArray()。

inline ArtMethod* Class::FindVirtualMethodForInterface(ArtMethod* method, size_t pointer_size) {  Class* declaring_class = method->GetDeclaringClass();  const int32_t iftable_count = GetIfTableCount();  IfTable* iftable = GetIfTable();  for (int32_t i = 0; i < iftable_count; i++) {    //找到interface table中的接口类的方法数组    if (iftable->GetInterface(i) == declaring_class) {      //从方法数组中找到需要的ArtMethod      return iftable->GetMethodArray(i)->GetElementPtrSize<ArtMethod*>(          method->GetMethodIndex(), pointer_size);    }  }  return nullptr;}

再看看interface table数组是如何被赋值的?调用过程类似virtual table的生成。

ResolveMethod()->ResolveType()->FindClass()->FindClassInPathClassLoader()->DefineClass()->LinkClass()->LinkMethods()-> LinkInterfaceMethods()

相比invoke_virtual, invoke_interface相对复杂很多,因为接口类可以实现另外一个接口类,同时还能继承其它的super类。大致流程如下:
1. 计算总的接口类数量,如果总的数量为0则直接返回
2. 如果当前类没有实现接口类,且父类只是实现了marker接口类(即serializalbe, clonable),则直接复制父类的ifTable,返回
3. 将父类的ifTable拷贝到当前类的ifTable。
4. 平铺ifTable,就是将直接接口类append到ifTable,如果有重复则过滤,然后再将直接接口类的接口类依次放入ifTable中。
5. 从vTable找真正实现的Artmthod: 从当前类的vTable中找跟ifTable签名一样的方法,如果有,则将vTable中的ArtMethod放入该ifTable中。这样,从ifTable中找方法时,就可以直接找到需要的ArtMethod了。


具体见以下代码分析:

bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,                                       Handle<mirror::ObjectArray<mirror::Class>> interfaces,                                       ArtMethod** out_imt) {...  const bool has_superclass = klass->HasSuperClass();  //父类可能也实现了接口类  const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;  //当前类是否实现了接口类  const bool have_interfaces = interfaces.Get() != nullptr;  const size_t num_interfaces =      have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();  const size_t method_size = ArtMethod::ObjectSize(image_pointer_size_);  if (num_interfaces == 0) {    if (super_ifcount == 0) {        //当前类和父类没有实现接口则直接返回      return true;    }    // 当前类没有实现接口,父类有实现的接口类的情况下,has_non_marker_interface    // 这个标记位检查是否有非marker interface(如serializable, clonable等)的接口。    bool has_non_marker_interface = false;    mirror::IfTable* super_iftable = klass->GetSuperClass()->GetIfTable();    for (size_t i = 0; i < super_ifcount; ++i) {      if (super_iftable->GetMethodArrayCount(i) > 0) {        has_non_marker_interface = true;        break;      }    }    // 父类有marker interface(如serializable, clonable等), 那么直接用父类的interface table即可    if (!has_non_marker_interface) {      klass->SetIfTable(super_iftable);      return true;    }  }  // 计算接口类的数量。  size_t ifcount = super_ifcount + num_interfaces;  for (size_t i = 0; i < num_interfaces; i++) {    mirror::Class* interface = have_interfaces ?        interfaces->GetWithoutChecks(i) : mirror::Class::GetDirectInterface(self, klass, i);   ...    ifcount += interface->GetIfTableCount();  }  MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));  //拷贝父类的接口类到当前类的iftable中  if (super_ifcount != 0) {    mirror::IfTable* super_iftable = klass->GetSuperClass()->GetIfTable();    for (size_t i = 0; i < super_ifcount; i++) {      mirror::Class* super_interface = super_iftable->GetInterface(i);      iftable->SetInterface(i, super_interface);    }  }  // 平铺iftable  size_t idx = super_ifcount;  for (size_t i = 0; i < num_interfaces; i++) {    mirror::Class* interface = have_interfaces ? interfaces->Get(i) :        mirror::Class::GetDirectInterface(self, klass, i);    // 检查下当前类直接继承的接口类是否已经放入iftable中    bool duplicate = false;    for (size_t j = 0; j < idx; j++) {      mirror::Class* existing_interface = iftable->GetInterface(j);      if (existing_interface == interface) {        duplicate = true;        break;      }    }    // 还没放置的话直接append到后面,否则continue..    if (!duplicate) {      // 将interface类加到iftable后面      iftable->SetInterface(idx++, interface);      // 直接接口类可能继承了其它接口类,那么将其它接口类添加到iftable后面      for (int32_t j = 0; j < interface->GetIfTableCount(); j++) {        mirror::Class* super_interface = interface->GetIfTable()->GetInterface(j);        bool super_duplicate = false;        for (size_t k = 0; k < idx; k++) {          mirror::Class* existing_interface = iftable->GetInterface(k);          if (existing_interface == super_interface) {            super_duplicate = true;            break;          }        }        if (!super_duplicate) {          iftable->SetInterface(idx++, super_interface);        }      }    }  }  ...  // 压缩iftable,因为可能会有dupliate接口类,也就是前面申请的内存多了,现在根据实际情况释放一些。  if (idx < ifcount) {    iftable.Assign(down_cast<mirror::IfTable*>(        iftable->CopyOf(self, idx * mirror::IfTable::kMax)));    ifcount = idx;  }  klass->SetIfTable(iftable.Get());  // 接下来代码处理virtual table,如何当前类就是一个接口类,那么它不需要virtual table的函数指针,直接返回  if (klass->IsInterface()) {    return true;  }  ...  MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));  ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();  ArtMethod* const conflict_method = runtime->GetImtConflictMethod();  bool extend_super_iftable = false;  ...  // 为iftable分配内存  for (size_t i = 0; i < ifcount; ++i) {    size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();    if (num_methods > 0) {      const bool is_super = i < super_ifcount;      const bool super_interface = is_super && extend_super_iftable;      mirror::PointerArray* method_array;      if (super_interface) {        mirror::IfTable* if_table = klass->GetSuperClass()->GetIfTable();        // 如果当前if_table数组元素指向的是一个super类的接口类,那么直接扩展这个元素,深拷贝。        method_array = down_cast<mirror::PointerArray*>(if_table->GetMethodArray(i)->Clone(self));      } else {        //当前类直接implements的接口类,那么申请num_methods个ArtMethod指针即可。        method_array = AllocPointerArray(self, num_methods);      }      iftable->SetMethodArray(i, method_array);    }  }  ...  // 以下代码去填充iftable中的各个ArtMethod元素,当然是从vtable里面找咯,找到了就填进去。  for (size_t i = 0; i < ifcount; ++i) {    size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();    if (num_methods > 0) {      StackHandleScope<2> hs2(self);      const bool is_super = i < super_ifcount;      const bool super_interface = is_super && extend_super_iftable;      auto method_array(hs2.NewHandle(iftable->GetMethodArray(i)));      ArtMethod* input_virtual_methods = nullptr;      Handle<mirror::PointerArray> input_vtable_array = NullHandle<mirror::PointerArray>();      int32_t input_array_length = 0;      if (super_interface) {        // 如果当前interface方法是父类的方法,那么就说明我们正在覆盖父类的一个方法,这时候只需要        // 把当前类的Virtual methods拿出来做输入        input_virtual_methods = klass->GetVirtualMethodsPtr();        input_array_length = klass->NumVirtualMethods();      } else {        // 当前类直接implement的一个接口类方法,这时候就需要把整个vtable都拿出来做输入        // 因为这个接口类的方法可能存在于任何一个super类中        input_vtable_array = vtable;        input_array_length = input_vtable_array->GetLength();      }      if (input_array_length == 0) {        // 没有输入的virtual methods,没必要往下走了,直接continue..        continue;      }      for (size_t j = 0; j < num_methods; ++j) {        auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(            j, image_pointer_size_);        MethodNameAndSignatureComparator interface_name_comparator(            interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));        int32_t k;        // 现在从输入的input_vtable_array或者input_virtual_methods去找if_table        // 中匹配到的方法,如果找到就覆盖        for (k = input_array_length - 1; k >= 0; --k) {          ArtMethod* vtable_method = input_virtual_methods != nullptr ?              reinterpret_cast<ArtMethod*>(                  reinterpret_cast<uintptr_t>(input_virtual_methods) + method_size * k) :              input_vtable_array->GetElementPtrSize<ArtMethod*>(k, image_pointer_size_);          ArtMethod* vtable_method_for_name_comparison =              vtable_method->GetInterfaceMethodIfProxy(image_pointer_size_);          if (interface_name_comparator.HasSameNameAndSignature(              vtable_method_for_name_comparison)) {            if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {              // 如果找到的匹配的方法非abstract且不是public那么就抛异常              self->EndAssertNoThreadSuspension(old_cause);              ThrowIllegalAccessError(klass.Get(),                  "Method '%s' implementing interface method '%s' is not public",                  PrettyMethod(vtable_method).c_str(), PrettyMethod(interface_method).c_str());              return false;            }            method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_);            break;          }        }        ...      }    }  }  ...  return true;}

Field 访问过程

  1. const变量, dex2oat会在代码优化中写死它的值,而不会去class中找一遍。
  2. instant&static变量,dex2oat会在代码优化过程中将instant变量的访问以其类中的偏移来访问,并写到native code中。如:
    public boolean testVariable() {          mFieldCase1InvokeePatch = new FieldCase1InvokeePatch();        return mFieldCase1InvokeePatch.flag_var;    }    public class FieldCase1InvokeePatch {        public boolean flag_var = false;    }

以下是testVariable()被dex2oat优化后对应的native代码,8是flag_var在类FieldCase1InvokeePatch的偏移

      0x003c187c:                 83EC1C        sub     esp, 28      0x003c187f:               896C2414        mov     [esp + 20], ebp      0x003c1883:               89742418        mov     [esp + 24], esi      0x003c1887:                 890424        mov     [esp], eax      0x003c188a:                   8BF1        mov     esi, ecx      0x003c188c:                 8B6E08        mov     ebp, [esi + 8]       0x003c188f:                 8B6D0C        mov     ebp, [ebp + 8] //flag_var

JNI方法加载过程

在LinkCode()的时候,如果发现method是一个native method,那么就调用UnregisterNative()。

File: Class_liner.ccvoid ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,                           uint32_t class_def_method_index) {   ...     if (method->IsNative()) {        // Unregistering restores the dlsym lookup stub.        method->UnregisterNative();    }

展开UnregisterNative(). 最后发现ArtMethod的entry_point_from_jni_入口被设置成了art_jni_dlsym_lookup_stub的汇编FUNCTION,所以首次调用这个JNI方法时会走到art_jni_dlsym_lookup_stub中。这个汇编代码会调用artFindNativeMethod()的C方法。

File: Art_method.ccvoid ArtMethod::UnregisterNative() {  // restore stub to lookup native pointer via dlsym  RegisterNative(GetJniDlsymLookupStub(), false);}void ArtMethod::RegisterNative(const void* native_method, bool is_fast) {  ...  SetEntryPointFromJni(native_method);}
File: Runtime_asm_entrypoints.hextern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject);static inline const void* GetJniDlsymLookupStub() {  return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub);}
File: jni_entrypoints_x86.SDEFINE_FUNCTION art_jni_dlsym_lookup_stub    subl LITERAL(8), %esp         // align stack    CFI_ADJUST_CFA_OFFSET(8)    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()    CFI_ADJUST_CFA_OFFSET(4)    call SYMBOL(artFindNativeMethod)  // (Thread*)    addl LITERAL(12), %esp        // remove argument & padding    CFI_ADJUST_CFA_OFFSET(-12)    testl %eax, %eax              // check if returned method code is null    jz .Lno_native_code_found     // if null, jump to return to handle    jmp *%eax                     // otherwise, tail call to intended method.Lno_native_code_found:    retEND_FUNCTION art_jni_dlsym_lookup_stub

在artFindNativeMethod()方法中,先通过全局的Vm线程找到对应的method的native 地址,然后将其注册到Jni method的ArtMethod中,也就是将ArtMethod的entry_point_from_jni_入口设置成native代码,下次就直接跳转JNI方法的native代码了。

File: Jni_entrypoints.ccextern "C" void* artFindNativeMethod() {...  Thread* self = Thread::Current();  ArtMethod* method = self->GetCurrentMethod(nullptr);  // Lookup symbol address for method, on failure we'll return null with an exception set,  // otherwise we return the address of the method we found.  void* native_code = soa.Vm()->FindCodeForNativeMethod(method);  ...    // 注册一下,以后jni跳转就不用再走一遍artFindNativeMethod了,直接跳转native代码    method->RegisterNative(native_code, false);    return native_code;  }}

接下来看看FindCodeForNativeMethod()如何工作的,原来是通过一个libraries_来查找到native 代码的,这个libraries_是一个记录了所有SharedLibrary的一个map,每个SharedLibrary是一个so在内存中的一个映射,最后轮训所有的SharedLibrary的dlsym()函数将so中的JNI方法load到内存中,并返回其指针。而每次调用LoadNativeLibrary()来注册so时都会在libraries_中新增一个SharedLibrary。

File: Java_vm_ext.ccvoid* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) {  ...    native_method = libraries_->FindNativeMethod(m, detail);  ...  return native_method;}/** Libraries 包含了当前VM中所有load过的so **/class Libraries {... private:  AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibraries> libraries_;};/** 最终FindSymbol查找JNI func都会走dlsym **/class SharedLibrary {  void* FindSymbol(const std::string& symbol_name) {    return dlsym(handle_, symbol_name.c_str());  }};/** 每次调用LoadNativeLibrary()都会在libraries_中注册一个SharedLibrary。**/bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader,                                  std::string* error_msg) {    ...    library = libraries_->Get(path);    if (library == nullptr) {  // We won race to get libraries_lock.      library = new_library.release();      libraries_->Put(path, library);      created_library = true;    }    ...}

反射调用

没怎么看懂,有时间再看吧。写了个demo,跨dex间反射调用field和method都没有问题。

跨dex的method访问

对于跨dex的方法访问,5种方法调用类型都对应一个”跳板”函数。

  1. invoke_static: artInvokeStaticTrampolineWithAccessCheck
  2. invoke_direct: artInvokeDirectTrampolineWithAccessCheck
  3. invoke_super: artInvokeSuperTrampolineWithAccessCheck
  4. invoke_virtual: artInvokeVirtualTrampolineWithAccessCheck
  5. invoke_interface: artInvokeInterfaceTrampolineWithAccessCheck

以下是一个代码片段例子,

public boolean invokeStatic2(){    return !AcrossDexCase1StaticMethod.invokeStatic2();}

invokeStatic2()方法被dex2oat优化的时候生成的native code如下,15892是invokeStatic2()在caller方法所在的dex中的一个methodId,也就是说即使AcrossDexCase1StaticMethod类在另外一个dex中,它的方法在caller dex的dex_cache中也是有一个ArtMethod指针的。

0x0039b787:             B8143E0000        mov     eax, 158920x0039b78c:         64FF1524020000        call    fs:[0x224]  ; pInvokeStaticTrampolineWithAccessCheck

以invoke_static为例, 看看artInvokeStaticTrampolineWithAccessCheck方法的实现:

extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck(    uint32_t method_idx, mirror::Object* this_object,    ArtMethod* caller_method, Thread* self, ArtMethod** sp)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {  return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method,                                        self, sp);}

最终以上5个“跳板”函数都会走到artInvokeCommon中,method_idx是native 代码中写死的(例子中的15892),表示其在当前caller的dex_cache中的method偏移,this_object是被操作的对象,caller_method是调用方法(例子中的invokeStatic2), self是虚拟机线程,sp是栈指针。大致流程如下:
1. 尝试通过调用方法FindMethodFast来快速找到method_idx对应的ArtMethod, 找到则直接返回这个ArtMethod的native_code,否则进入2.
2. 调用FindMethodFromCode()继续查找。

template<InvokeType type, bool access_check>static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,                                     ArtMethod* caller_method, Thread* self, ArtMethod** sp) {  ...  ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type);  if (UNLIKELY(method == nullptr)) {     ...    {      ...      method = FindMethodFromCode<type, access_check>(method_idx, &this_object, &caller_method,                                                      self);    }  }  const void* code = method->GetEntryPointFromQuickCompiledCode();...}

首次调用method时FindMethodFast()并不能在dex_cache中命中ArtMethod。那么将走到FindMethodFromCode()中,可以看到最后也是通过调用class_linker的ResolveMethod来将ArtMethod加载到Class和dex_cache中的。后面的逻辑就是处理各种类型的method调用,上面已经介绍过很多,这里就不赘述了。

template<InvokeType type, bool access_check>inline ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object** this_object,                                     ArtMethod** referrer, Thread* self) {  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();  ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, *referrer);  if (resolved_method == nullptr) {    ...    resolved_method = class_linker->ResolveMethod(self, method_idx, *referrer, type);  }  ...  switch (type) {    case kStatic:    case kDirect:      return resolved_method;    case kVirtual: {      mirror::Class* klass = (*this_object)->GetClass();      uint16_t vtable_index = resolved_method->GetMethodIndex();      ...      return klass->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize());    }    case kSuper: {      mirror::Class* super_class = (*referrer)->GetDeclaringClass()->GetSuperClass();      uint16_t vtable_index = resolved_method->GetMethodIndex();      ...      return super_class->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize());    }    case kInterface: {      uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;      ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(          imt_index, class_linker->GetImagePointerSize());      if (!imt_method->IsImtConflictMethod() && !imt_method->IsImtUnimplementedMethod()) {        ...        return imt_method;      } else {        ArtMethod* interface_method = (*this_object)->GetClass()->FindVirtualMethodForInterface(            resolved_method, class_linker->GetImagePointerSize());        ...        return interface_method;      }    }    ...  }}

以下是ResolveMethod的逻辑:

  1. 先走Caller所在dex的dex_cache找
  2. 尝试使用method_idx从load到的target method所在的Class中查找,这个地方有点tricky,klass->FindxxxMethod在看到Class所在的dex和caller 方法所在的dex不一致时,就直接返回空了。
  3. 所以使用方法签名来找,这个应该可以找到,除非这个Class里面没有定义此方法。
  4. 将找到的ArtMethod存入caller所在dex的dex_cache中,方便下次查找。
ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx,                                      Handle<mirror::DexCache> dex_cache,                                      Handle<mirror::ClassLoader> class_loader,                                      ArtMethod* referrer, InvokeType type) {  DCHECK(dex_cache.Get() != nullptr);  // Cache是否命中?  ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);  if (resolved != nullptr && !resolved->IsRuntimeMethod()) {    ...    return resolved;  }  // Cache中没有,那么从method所在的Class中找  const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);  mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);  ...  // 从Caller所在的dex的dex_cache中尝试去找这个ArtMethod,如果是跨dex,那么返回就是空。  switch (type) {    case kDirect:  // Fall-through.    case kStatic:      resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_);      break;    case kInterface:      resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);      break;    case kSuper:  // Fall-through.    case kVirtual:      resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);      break;    ...  }  if (resolved == nullptr) {    // 只能使用签名从load进来的Class中查找了,肯定能命中,除非这个Class没有定义要找的method。    const char* name = dex_file.StringDataByIdx(method_id.name_idx_);    const Signature signature = dex_file.GetMethodSignature(method_id);    switch (type) {      case kDirect:  // Fall-through.      case kStatic:        resolved = klass->FindDirectMethod(name, signature, image_pointer_size_);        break;      case kInterface:        resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);        break;      case kSuper:  // Fall-through.      case kVirtual:        resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);        break;    }  }  // 找到方法,那么检查兼容性  if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) {    // 存入Caller所在的dex的dex\_cache中    dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);    return resolved;  }  ...}
ArtMethod* Class::FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx,                                            size_t pointer_size) {  //Class所在的dex和caller 方法所在的dex不一致时,直接返回空  if (GetDexCache() == dex_cache) {    for (auto& method : GetVirtualMethods(pointer_size)) {      ...      if (method.GetDexMethodIndex() == dex_method_idx && !method.IsMiranda()) {        return &method;      }    }  }  return nullptr;}

可以看到,跨dex访问时方法的查找是通过签名找到的。

跨dex的Field访问

  1. const变量, dex2oat会在代码优化中写死它的值,而不会去class中找一遍。
  2. instance&static变量,不同于同dex的访问,跨dex访问非const变量是走的“quick_entry” points接口。

比如下面的访问:

AcrossDexCase2Fields mAcrossDexCase2Fields = new AcrossDexCase2Fields();public boolean accessStaticBoolean(){    return mAcrossDexCase2Fields.S_NUM;}public class AcrossDexCase2Fields {    public static boolean S_NUM = true;    ...}

mAcrossDexCase2Fields.S_NUM;汇编dex2oat编译为以下native code, 4790是S_NUM的field_idx。之后调用pGet32Static方法。

  0x0039bb85:             B86A130000        mov     eax, 4970  0x0039bb8a:         64FF1570010000        call    fs:[0x170]  ; pGet32Static

之后调用到artGet32StaticFromCode这个绷床函数,也就是说对field的跨dex访问,ART虚拟机也会在caller所在的dex_cache中为其分配一个ARTField。FindFieldFromCode()函数里面的逻辑这里就不贴了,跟跨dex的method访问逻辑几乎一样,就是当发现field所在类的dex和caller的dex不一致时,直接用field的名字和class的名字进行查找。找到以后填入caller的ARTField中。

extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx,                                           ArtMethod* referrer,                                           Thread* self)    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {  ...  ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int32_t));  if (LIKELY(field != nullptr)) {    return field->Get32(field->GetDeclaringClass());  }  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int32_t));  if (LIKELY(field != nullptr)) {    return field->Get32(field->GetDeclaringClass());  }  return 0;  // Will throw exception by checking with Thread::Current.}

这样,ART虚拟机的field/method的访问流程已经分析完毕。

0 0