ART运行时之methodfield加载

    xiaoxiao2021-03-25  122

    先简单介绍下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.cc class 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.h class 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.cc static 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.cc void 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.cc mirror::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.cc void 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.cc ImageSpace* 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.cc ArtMethod* Runtime::CreateResolutionMethod() { auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod(); ... method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); return method; }

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

    File: quick_entrypoints_x86_64.S DEFINE_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_EXCEPTION END_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.cc extern "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.cc 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()) { 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.h class 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.cc mirror::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.cc void 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.cc void 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.cc ArtMethod* 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.S DEFINE_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_EXCEPTION END_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.h class 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.h class 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()获得实例的Class called = 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.cc bool 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.h class 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 访问过程

    const变量, dex2oat会在代码优化中写死它的值,而不会去class中找一遍。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.cc void 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.cc void 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.h extern "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.S DEFINE_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
    转载请注明原文地址: https://ju.6miu.com/read-5374.html

    最新回复(0)