天问

  • Blog
  • 热修复与动态加载
  • python爬虫
  • 专栏
    • 热修复与动态加载
    • NDK开发
    • 设计模式与架构
    • React Native
  • 好文转载
    • 计算机技术
    • 投资理财
    • 摄影与拍照技巧
  • 投稿
  • 关于本站及隐私政策
  • Github
StevenQ
为今天工作,为明天投资,为未来孵化一些东西!
  1. 首页
  2. 计算机技术
  3. 热修复
  4. 正文

【native层方法替换热修复原理】之虚拟机运行时数据结构ClassObject/Class和Method/ArtMethod

2018年10月7日 1142点热度 2人点赞 0条评论

通过前面热修复与动态加载专栏热修复系列文章的讲解我们知道了以下2点重要信息

  • 在安卓java层,描述dex文件的数据结构是DexFile这个类,PathClassLoader对dex文件的加载在java代码中最终都是委托DexFile调用loadDex完成的
  • 在native层,在dalvik模式下描述dex文件中一个类的数据结构是ClassObject这个类

那么这里就有了另外一个问题,在dalvik虚拟机运行时环境中类和方法是如何描述的呢?Art模式下又是如何描述的呢?

根据前面dex中类的加载流程和unexpected DEX异常的分析类加载过程我们已经知道在java层类的加载最终是通过DexFile的loadClass函数完成的。因此我们只需要顺着该函数分析其对应的native函数在dalvik模式和art模式下的实现即可。首先回顾下该函数在java层的调用链:

public Class loadClass(String name, ClassLoader loader) {
        String slashName = name.replace('.', '/');
        return loadClassBinaryName(slashName, loader, null);
}
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, suppressed);
    }

private static Class defineClass(String name, ClassLoader loader, long cookie,
								 List<Throwable> suppressed) {
	Class result = null;
	try {
		result = defineClassNative(name, loader, cookie);
	} catch (NoClassDefFoundError e) {
		if (suppressed != null) {
			suppressed.add(e);
		}
	} catch (ClassNotFoundException e) {
		if (suppressed != null) {
			suppressed.add(e);
		}
	}
	return result;
}
private static native Class defineClassNative(String name, ClassLoader loader, long cookie)
		throws ClassNotFoundException, NoClassDefFoundError;

可以看到在java层类的调用经过层层中转最终是委托给了DexFile的一个native函数defineClassNative来完成的。所以我们只需要分析defineClassNative这个函数在dalvik模式和art模式下分别是如何实现的即可。首先分析下dalvik模式。

dalvik模式下运行时类和方法的描述

在dex中类的加载流程和unexpected DEX异常中已经提到过在dalvik模式下native层加载一个类最终调用的是findClassNoInit函数,该函数返回的是一个ClassObject指针,所以类加载机制的最终目标就是为目标类生成一个ClassObject数据结构,并将其存储在运行时环境中,为解释器的执行提供相应类方法的字节码。所以在运行时类的描述就是ClassObject这个数据结构。而方法在运行时的描述就是ClassObject中定义的一个成员Method数据结构。

弄清楚这2个数据结构对后面我们学习dalvik虚拟机JNI模块实现原理,hook框架Xposed以及阿里的DexPosed AOP框架以及AndFix热修复框架有非常重要的帮助。

注意:虽然现在是安卓默认使用Art运行时,但是对于开发者而言,开发出的应用程序经过编译和打包之后,仍然是一个包含dex字节码的APK文件。也就是仍然适合dalvik虚拟机的那一套指令,只不过Art运行时在apk安装时会将应用的dex字节码翻译成本地机器码,执行时直接执行本地机器码而不再是解释执行。关于dalvik虚拟机和Art运行时区别建议参看:https://blog.csdn.net/luoshengyang/article/details/18006645。

ClassObject数据结构

该数据结构位于dalvk虚拟机源码目录dalvik\vm\oo\Object.h头文件中。完整定义如下:

/*
 * Class objects have many additional fields.  This is used for both
 * classes and interfaces, including synthesized classes (arrays and
 * primitive types).
 *
 * Class objects are unusual in that they have some fields allocated with
 * the system malloc (or LinearAlloc), rather than on the GC heap.  This is
 * handy during initialization, but does require special handling when
 * discarding java.lang.Class objects.
 *
 * The separation of methods (direct vs. virtual) and fields (class vs.
 * instance) used in Dalvik works out pretty well.  The only time it's
 * annoying is when enumerating or searching for things with reflection.
 */
struct ClassObject : Object {
    /* leave space for instance data; we could access fields directly if we
       freeze the definition of java/lang/Class */
    u4              instanceData[CLASS_FIELD_SLOTS];

    /* UTF-8 descriptor for the class; from constant pool, or on heap
       if generated ("[C") */
    const char*     descriptor;
    char*           descriptorAlloc;

    /* access flags; low 16 bits are defined by VM spec */
    u4              accessFlags;

    /* VM-unique class serial number, nonzero, set very early */
    u4              serialNumber;

    /* DexFile from which we came; needed to resolve constant pool entries */
    /* (will be NULL for VM-generated, e.g. arrays and primitive classes) */
    DvmDex*         pDvmDex;

    /* state of class initialization */
    ClassStatus     status;

    /* if class verify fails, we must return same error on subsequent tries */
    ClassObject*    verifyErrorClass;

    /* threadId, used to check for recursive <clinit> invocation */
    u4              initThreadId;

    /*
     * Total object size; used when allocating storage on gc heap.  (For
     * interfaces and abstract classes this will be zero.)
     */
    size_t          objectSize;

    /* arrays only: class object for base element, for instanceof/checkcast
       (for String[][][], this will be String) */
    ClassObject*    elementClass;

    /* arrays only: number of dimensions, e.g. int[][] is 2 */
    int             arrayDim;

    /* primitive type index, or PRIM_NOT (-1); set for generated prim classes */
    PrimitiveType   primitiveType;

    /* superclass, or NULL if this is java.lang.Object */
    ClassObject*    super;

    /* defining class loader, or NULL for the "bootstrap" system loader */
    Object*         classLoader;

    /* initiating class loader list */
    /* NOTE: for classes with low serialNumber, these are unused, and the
       values are kept in a table in gDvm. */
    InitiatingLoaderList initiatingLoaderList;

    /* array of interfaces this class implements directly */
    int             interfaceCount;
    ClassObject**   interfaces;

    /* static, private, and <init> methods */
    int             directMethodCount;
    Method*         directMethods;

    /* virtual methods defined in this class; invoked through vtable */
    int             virtualMethodCount;
    Method*         virtualMethods;

    /*
     * 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.
     */
    int             vtableCount;
    Method**        vtable;

    /*
     * Interface table (iftable), one entry per interface supported by
     * this class.  That means one entry for each interface we support
     * directly, indirectly via superclass, or indirectly via
     * superinterface.  This will be null if neither we nor our superclass
     * implement any interfaces.
     *
     * Why we need this: given "class Foo implements Face", declare
     * "Face faceObj = new Foo()".  Invoke faceObj.blah(), where "blah" is
     * part of the Face interface.  We can't easily use a single vtable.
     *
     * For every interface a concrete class implements, we create a list of
     * virtualMethod indices for the methods in the interface.
     */
    int             iftableCount;
    InterfaceEntry* iftable;

    /*
     * The interface vtable indices for iftable get stored here.  By placing
     * them all in a single pool for each class that implements interfaces,
     * we decrease the number of allocations.
     */
    int             ifviPoolCount;
    int*            ifviPool;

    /* instance fields
     *
     * These describe the layout of the contents of a DataObject-compatible
     * Object.  Note that only the fields directly defined by this class
     * are listed in ifields;  fields defined by a superclass are listed
     * in the superclass's ClassObject.ifields.
     *
     * All instance fields that refer to objects are guaranteed to be
     * at the beginning of the field list.  ifieldRefCount specifies
     * the number of reference fields.
     */
    int             ifieldCount;
    int             ifieldRefCount; // number of fields that are object refs
    InstField*      ifields;

    /* bitmap of offsets of ifields */
    u4 refOffsets;

    /* source file name, if known */
    const char*     sourceFile;

    /* static fields */
    int             sfieldCount;
    StaticField     sfields[0]; /* MUST be last item */
};

ClassObject结构体内包含了大量的其他数据成员,这些成员基本上包含了目标类在运行期间所需要的全部的数据描述,比如类的描述descriptor,类的访问权限等标记accessFlags,直接方法directMethods等。所以说该数据结构是运行时内存中非常重要的一个数据结构。下面对其中比较重要的一些成员做一些说明,

  • const char*     descriptor; 类的描述符,来自于dex文件中的常量池,格式与在JNI开发中使用的类描述符是同一个概念
  • DvmDex*         pDvmDex; DvmDex类型指针。DvmDex数据结构也是一个描述Dex文件的数据结构,但是与前面提到的DexFile的不同在于在DexFile数据结构的基础上增加了一些附加信息用于加快Dex文件解析效果,换而言之就是该数据结构内部是包含一个DexFile数据结构成员的。
  •  ClassObject*    super; ClassObject类型的指针,指向当前类的父类在内存中对应的ClassObject实例,从而能够访问父类的成员,例如父类的方法和字段。
  • Method*         directMethods;Method类型指针,指向该类的直接方法区,从注释上可以看到所谓直接方法就是指静态/私有/构造方法(/* static, private, and <init> methods */)
  • Method*         virtualMethods; 也是Method类型指针,不过指向的时虚方法区。

该类还包括很多其他成员,其余的未做说明的可以看上面源码的注释进行了解。在上面提到的这些成员中我们重点讲解下Method这个数据结构,因为对于一个类而言最重要的就是他所提供的功能,而功能是通过函数以API的形式提供的。

当虚拟机完成类加载工作之后,解释器会对已经装载的代码进行检查,验证符合虚拟机规范格式之后,最终调用dvmInterpret函数初始化解释器斌开始解释执行字节码,dvmInterpret函数是解释器的入口函数。该函数定义如下:

dvmInterpret(Thread* self, const Method* method, JValue* pResult)

可以看到该函数的第二个参数即是Mehtod类型的指针,所以Method对象是dalvik虚拟机解释器的重要输入。引用自《Android Dalvik虚拟机结构及机制剖析》

Method数据结构

Method数据结构同样位于dalvk虚拟机源码目录dalvik\vm\oo\Object.h头文件中,定义如下:

/*
 * A method.  We create one of these for every method in every class
 * we load, so try to keep the size to a minimum.
 *
 * Much of this comes from and could be accessed in the data held in shared
 * memory.  We hold it all together here for speed.  Everything but the
 * pointers could be held in a shared table generated by the optimizer;
 * if we're willing to convert them to offsets and take the performance
 * hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we
 * could move everything but "nativeFunc".
 */
struct Method {
    /* the class we are a part of */
    ClassObject*    clazz;

    /* access flags; low 16 bits are defined by spec (could be u2?) */
    u4              accessFlags;

    /*
     * For concrete virtual methods, this is the offset of the method
     * in "vtable".
     *
     * For abstract methods in an interface class, this is the offset
     * of the method in "iftable[n]->methodIndexArray".
     */
    u2             methodIndex;

    /*
     * Method bounds; not needed for an abstract method.
     *
     * For a native method, we compute the size of the argument list, and
     * set "insSize" and "registerSize" equal to it.
     */
    u2              registersSize;  /* ins + locals */
    u2              outsSize;
    u2              insSize;

    /* method name, e.g. "<init>" or "eatLunch" */
    const char*     name;

    /*
     * Method prototype descriptor string (return and argument types).
     *
     * TODO: This currently must specify the DexFile as well as the proto_ids
     * index, because generated Proxy classes don't have a DexFile.  We can
     * remove the DexFile* and reduce the size of this struct if we generate
     * a DEX for proxies.
     */
    DexProto        prototype;

    /* short-form method descriptor string */
    const char*     shorty;

    /*
     * The remaining items are not used for abstract or native methods.
     * (JNI is currently hijacking "insns" as a function pointer, set
     * after the first call.  For internal-native this stays null.)
     */

    /* the actual code */
    const u2*       insns;          /* instructions, in memory-mapped .dex */

    /* JNI: cached argument and return-type hints */
    int             jniArgInfo;

    /*
     * JNI: native method ptr; could be actual function or a JNI bridge.  We
     * don't currently discriminate between DalvikBridgeFunc and
     * DalvikNativeFunc; the former takes an argument superset (i.e. two
     * extra args) which will be ignored.  If necessary we can use
     * insns==NULL to detect JNI bridge vs. internal native.
     */
    DalvikBridgeFunc nativeFunc;

    /*
     * JNI: true if this static non-synchronized native method (that has no
     * reference arguments) needs a JNIEnv* and jclass/jobject. Libcore
     * uses this.
     */
    bool fastJni;

    /*
     * JNI: true if this method has no reference arguments. This lets the JNI
     * bridge avoid scanning the shorty for direct pointers that need to be
     * converted to local references.
     *
     * TODO: replace this with a list of indexes of the reference arguments.
     */
    bool noRef;

    /*
     * JNI: true if we should log entry and exit. This is the only way
     * developers can log the local references that are passed into their code.
     * Used for debugging JNI problems in third-party code.
     */
    bool shouldTrace;

    /*
     * Register map data, if available.  This will point into the DEX file
     * if the data was computed during pre-verification, or into the
     * linear alloc area if not.
     */
    const RegisterMap* registerMap;

    /* set if method was called during method profiling */
    bool            inProfile;
};

同样的来看下该数据结构中的一些重要成员:

  • ClassObject*    clazz;ClassObject类型指针,用来表示该方法属于哪个类实例。
  • u4              accessFlags;方法访问标记符,如果某个方法被多个访问标记修饰,如public static function(),则是多个访问标记符进行与运算之后的结果。
  • const char*     name;函数名称
  •  const u2*       insns; 从注释可以看到是实际的代码,也就是指向函数的指令部分的起始地址。
  • DalvikBridgeFunc nativeFunc; DalvikBridgeFunc类型的函数指针,从注释上可以看到是JNI本地方法指针,可以是一个真正的native方法或者是一个JNI桥函数(native method ptr; could be actual function or a JNI bridge

从这里可以得到2点重要线索:

  1. dalvik虚拟机对于一个非native方法进行解释执行时,起关键作用的就是方法的指令部分,也就是Method数据结构的insns成员指向的地址的内容。
  2. 对于native方法,Method数据结构有一个nativeFunc成员作为描述执行native方法时的入口,该成员是一个函数指针,定义如下:
    typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult, const Method* method, struct Thread* self);

    为何要定义为函数指针,是因为这样在随着程序运行时可以动态更改该指针的指向即可调用函数声明相同的不同方法。在后面我们深入分析dalvik虚拟机JNI模块的实现原理时可以看到对于不是通过动态注册定义的native方法该函数指针初始指向dvmResolveNativeMethod这个函数。

art模式下类和方法运行时的描述

前面提到过我们只需要分析defineClassNative这个函数在art下是如何实现的即可。该函数在art下的实现函数是DexFile_defineClassNative,该函数位于安卓源码art/runtime/native/dalvik_system_DexFile.cc文件.

static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
                                        jint cookie) {
  ScopedObjectAccess soa(env);
  const DexFile* dex_file = toDexFile(cookie);
  if (dex_file == NULL) {
    VLOG(class_linker) << "Failed to find dex_file";
    return NULL;
  }
  ScopedUtfChars class_name(env, javaName);
  if (class_name.c_str() == NULL) {
    VLOG(class_linker) << "Failed to find class_name";
    return NULL;
  }
  const std::string descriptor(DotToDescriptor(class_name.c_str()));
  const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str());
  if (dex_class_def == NULL) {
    VLOG(class_linker) << "Failed to find dex_class_def";
    return NULL;
  }
  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  class_linker->RegisterDexFile(*dex_file);
  mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
  mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file,
                                                    *dex_class_def);
  VLOG(class_linker) << "DexFile_defineClassNative returning " << result;
  return soa.AddLocalReference<jclass>(result);
}

从这段代码我们可以看到art模式下类的加载是通过ClassLinker这个类调用DefineClass来完成的,加载成功之后返回一个mirror::Class的指针,换而言之就是在art模式下描述一个类运行时的数据结构是mirror::Class,也就是这个类的作用相当于dalvik模式下的ClassObject。

mirror::Class

先看下该类包含哪些重要成员,因为art下的这个类的定义在不同安卓版本下改动较大(这也是为何阿里的AndFix在art模式下的替换逻辑要分不同版本实现的原因),我们选取一个分析即可。这里以安卓源码4.4.4_r1为例(该数据结构定义位于安卓源码的art\runtime\mirror\class.h中):

{
 private:
  void SetVerifyErrorClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);

  bool Implements(const Class* klass) const
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
  bool IsArrayAssignableFromArray(const Class* klass) const
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
  bool IsAssignableFromArray(const Class* klass) const
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);

  // defining class loader, or NULL for the "bootstrap" system loader
  ClassLoader* class_loader_;

  // For array classes, the component class object for instanceof/checkcast
  // (for String[][][], this will be String[][]). NULL for non-array classes.
  Class* component_type_;

  // DexCache of resolved constant pool entries (will be NULL for classes generated by the
  // runtime such as arrays and primitive classes).
  DexCache* dex_cache_;

  // static, private, and <init> methods
  ObjectArray<ArtMethod>* direct_methods_;

  // instance fields
  //
  // These describe the layout of the contents of an Object.
  // Note that only the fields directly declared by this class are
  // listed in ifields; fields declared by a superclass are listed in
  // the superclass's Class.ifields.
  //
  // All instance fields that refer to objects are guaranteed to be at
  // the beginning of the field list.  num_reference_instance_fields_
  // specifies the number of reference fields.
  ObjectArray<ArtField>* ifields_;

  // The interface table (iftable_) contains pairs of a interface class and an array of the
  // interface methods. There is one pair per interface supported by this class.  That means one
  // pair for each interface we support directly, indirectly via superclass, or indirectly via a
  // superinterface.  This will be null if neither we nor our superclass implement any interfaces.
  //
  // Why we need this: given "class Foo implements Face", declare "Face faceObj = new Foo()".
  // Invoke faceObj.blah(), where "blah" is part of the Face interface.  We can't easily use a
  // single vtable.
  //
  // For every interface a concrete class implements, we create an array of the concrete vtable_
  // methods for the methods in the interface.
  IfTable* iftable_;

  // descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName
  String* name_;

  // Static fields
  ObjectArray<ArtField>* sfields_;

  // The superclass, or NULL if this is java.lang.Object, an interface or primitive type.
  Class* super_class_;

  // If class verify fails, we must return same error on subsequent tries.
  Class* verify_error_class_;

  // Virtual methods defined in this class; invoked through vtable.
  ObjectArray<ArtMethod>* virtual_methods_;

  // 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.
  ObjectArray<ArtMethod>* vtable_;

  // Access flags; low 16 bits are defined by VM spec.
  uint32_t access_flags_;

  // Total size of the Class instance; used when allocating storage on gc heap.
  // See also object_size_.
  size_t class_size_;

  // Tid used to check for recursive <clinit> invocation.
  pid_t clinit_thread_id_;

  // ClassDef index in dex file, -1 if no class definition such as an array.
  // TODO: really 16bits
  int32_t dex_class_def_idx_;

  // Type index in dex file.
  // TODO: really 16bits
  int32_t dex_type_idx_;

  // Number of instance fields that are object refs.
  size_t num_reference_instance_fields_;

  // Number of static fields that are object refs,
  size_t num_reference_static_fields_;

  // Total object size; used when allocating storage on gc heap.
  // (For interfaces and abstract classes this will be zero.)
  // See also class_size_.
  size_t object_size_;

  // Primitive type value, or Primitive::kPrimNot (0); set for generated primitive classes.
  Primitive::Type primitive_type_;

  // Bitmap of offsets of ifields.
  uint32_t reference_instance_offsets_;

  // Bitmap of offsets of sfields.
  uint32_t reference_static_offsets_;

  // State of class initialization.
  Status status_;

  // TODO: ?
  // initiating class loader list
  // NOTE: for classes with low serialNumber, these are unused, and the
  // values are kept in a table in gDvm.
  // InitiatingLoaderList initiating_loader_list_;

  // Location of first static field.
  uint32_t fields_[0];

  // java.lang.Class
  static Class* java_lang_Class_;

  friend struct art::ClassOffsets;  // for verifying offset information
  DISALLOW_IMPLICIT_CONSTRUCTORS(Class);
};

这里仅仅只是展示了mirror::Class这个类定义的成员变量,未展示该类定义的成员函数。按照ClassObject的分析方式,对该类中的重要成员做如下说明:

  • ObjectArray<ArtMethod>* direct_methods_;存储该类所有直接方法的数组,从这里其实也可以看到在art模式下描述方法的运行时的数据结构是ArtMethod
  • ObjectArray<ArtMethod>* virtual_methods_; 存储该类所有虚方法的数组
  • uint32_t access_flags_; 描述类访问标志的字段

所以接下来看下ArtMethod成员的定义(该数据结构定义位于安卓源码的art\runtime\mirror\art_method.h)

ArtMethod

{
protected:
  // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
  // The class we are a part of
  Class* declaring_class_;

  // short cuts to declaring_class_->dex_cache_ member for fast compiled code access
  ObjectArray<StaticStorageBase>* dex_cache_initialized_static_storage_;

  // short cuts to declaring_class_->dex_cache_ member for fast compiled code access
  ObjectArray<ArtMethod>* dex_cache_resolved_methods_;

  // short cuts to declaring_class_->dex_cache_ member for fast compiled code access
  ObjectArray<Class>* dex_cache_resolved_types_;

  // short cuts to declaring_class_->dex_cache_ member for fast compiled code access
  ObjectArray<String>* dex_cache_strings_;

  // Access flags; low 16 bits are defined by spec.
  uint32_t access_flags_;

  // Offset to the CodeItem.
  uint32_t code_item_offset_;

  // Architecture-dependent register spill mask
  uint32_t core_spill_mask_;

  // Compiled code associated with this method for callers from managed code.
  // May be compiled managed code or a bridge for invoking a native method.
  // TODO: Break apart this into portable and quick.
  const void* entry_point_from_compiled_code_;

  // Called by the interpreter to execute this method.
  EntryPointFromInterpreter* entry_point_from_interpreter_;

  // Architecture-dependent register spill mask
  uint32_t fp_spill_mask_;

  // Total size in bytes of the frame
  size_t frame_size_in_bytes_;

  // Garbage collection map of native PC offsets (quick) or dex PCs (portable) to reference bitmaps.
  const uint8_t* gc_map_;

  // Mapping from native pc to dex pc
  const uint32_t* mapping_table_;

  // Index into method_ids of the dex file associated with this method
  uint32_t method_dex_index_;

  // For concrete virtual methods, this is the offset of the method in Class::vtable_.
  //
  // For abstract methods in an interface class, this is the offset of the method in
  // "iftable_->Get(n)->GetMethodArray()".
  //
  // For static and direct methods this is the index in the direct methods table.
  uint32_t method_index_;

  // The target native method registered with this method
  const void* native_method_;

  // When a register is promoted into a register, the spill mask holds which registers hold dex
  // registers. The first promoted register's corresponding dex register is vmap_table_[1], the Nth
  // is vmap_table_[N]. vmap_table_[0] holds the length of the table.
  const uint16_t* vmap_table_;

  static Class* java_lang_reflect_ArtMethod_;

 private:
  friend struct art::ArtMethodOffsets;  // for verifying offset information
  DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethod);
};

该数据结构中的几个重要的成员及其意义如下:

  • Class* declaring_class_; 该ArtMethod所属的类,也就是该方法位于哪个类对象中
  • uint32_t access_flags_; ArtMethod代表的方法的访问标志符
  • const void* entry_point_from_compiled_code_; art模式下执行编译过后的本地机器时的实际指令地址,类似前面dalvik模式下Method的insns字段
  • EntryPointFromInterpreter* entry_point_from_interpreter_;EntryPointFromInterpreter类型的函数指针,前面说过Art模式和dalvik模式的一个很重要的区别就是art模式会把字节码先优化未本地机器码然后直接执行,但是art模式下也可以像dalvik模式下解释执行,该函数指针即是用来表面使用哪种方式执行
  • const void* native_method_;java层方法对应的本地方法,作用类似前面dalvik模式下Method的nativeFunc字段

总结

到这里基本上就把安卓framework层Dex文件,类文件以及方法的描述彻底弄清楚了,这里做个小结:

  • 在安卓framework的java层,描述dex文件的数据结构是DexFile这个类,PathClassLoader对dex文件的加载在java代码中最终都是委托DexFile调用loadDex完成的。
  • 在framework的native层,描述dex文件对应的数据结构是c++层DexFile类,描述dex文件中一个类的数据结构在dalvik模式下是ClassObject结构体,在art模式下是mirror::Class;描述方法的数据结构在dalvik模式下是Method结构体,Mehotd类是dalvik虚拟机解释执行时的重要输入,在art模式下是ArtMethod
  • Method结构体中包含了一个insns成员,当该方法为非native方法时,该insns成员指向了该方法实际的指令。同时也包含一个nativeFunc成员,当该方法为native方法时,会执行nativeFunc指向的函数的代码。
  • java层的方法在运行时的表示为Method(dalvik模式下)和ArtMethod(art模式下)

对于热修复而言:

java层的Dex加载以及类加载机制衍生出了基于MultiDex改造的热修复理论,代表作为QQ空间热修复,Tinker以及Nuwa。虽然这他们解决类预校验标志的方法不尽相同,但是他们热修复的理论部分原理是一样的。

native层JNI方法和native方法的执行原理衍生出了native层方法替换的热修复方案,代表做包括Xposed hook框架,Dexposed以及AndFix。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 热修复
最后更新:2018年11月25日

天问

追梦追自由,穷则独善其身,达则兼济天下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2022 天问 ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang