Andfix&Tinker 热修复方案原理

2022-06-12  本文已影响0人  carlwu_186

Andfix

andfix从native入手修改ArtMethod的字节码地址实现错误方法块的修复。修复的粒度是方法块字节码引用。

Java的内存分布

内存分布
方法区
JVM读取class文件,提取class的类型信息,并将这些信息存储到方法区,同时该类型的类静态变量也会放到方法区,还有方法表,每个类都会有个方法表。
堆区
主要存放的是对象,Java程序在运行时创建的所有类型的对象和数组都储存在堆中。
JVM会根据new的指令在堆中开辟一个确定类型的对象内存空间,但是堆中开辟的对象空间并没有任何人工指令可以回收,而是通过JVM的垃圾回收机制进行回收。
栈区
每启动一个线程,JVM都会为它创建一个JAVA栈,用于存放方法中的局部变量、操作数等。
当线程调用某个方法时,JVM会根据方法区中该方法的字节码组建一个栈帧,并将该栈帧压入到Java栈中,方法执行完毕后,JVM将该栈帧弹出,并释放掉。

我们写的Java文件被编译成class文件到最终加载到内存,所属的内存区域是方法区,所以要做的就是,将差分包中没有bug的方法替换掉已经被加载到内存方法区中有bug方法。

虚拟机运行机制

虚拟机加载执行ActivityThread
  1. 声明一个ApplicationThread类型的成员变量
//这一步不会将Application.class字节码加载到内存的,会在方法区生成一个int类型的符号表量
private Application application

对象只有在主动引用的情况下才会加载到内存,常见的主动引用方式有:new、反射创建、JNI的findCalss()、序列化、调用类的静态成员变量(final除外)和静态方法、初始化一个类如果其父类没有初始化,会先初始化父类。

  1. 通过反射的方式创建application对象,这一步会将application的字节码文件加载到内存,创建的对象存储在堆区。


    APP启动流程

    当通过Application对象调用onCreate()方法时,堆区的application对象指向int符号变量,int符号变量指向方法表,执行onCreate()方法,将onCreate组建成栈帧,压入Java栈,执行完毕后弹出并释放。

方法表

方法表可以理解为是一个数组,数组中存放的是ArtMethod结构体(Android5.0之前为Method)。ArtMethod中存放的方法入口、字节码地址等信息。

扩展JVM DVM ART

ART虚拟机

手动实现Andfix

  1. 找到要修复的方法块,修复bug代码后,对方法加上注解方便后续定位到修改的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
//a
public @interface Replace {
    //  目的
//    地方  在哪里      14号   多少钱
    String clazz();
    String method();
}
  1. 编译修改的Java文件生成.class,使用SDK的DX工具转成.dex文件放入手机中。
  2. APP加载dex文件,遍历出dex中所有的Class后,使用之前的注解找到要修改的function
 DexFile dexFile = DexFile.loadDex()
Enumeration<String> entry= dexFile.entries();
while (entry.hasMoreElements()) {
//                全类名
  String className = entry.nextElement();
  Class realClazz=dexFile.loadClass(className, context.getClassLoader());
  if (realClazz != null) {
    fixClass(realClazz);
  }
}

    private void fixClass(Class realClazz) {
//加载方法 Method
        Method[] methods = realClazz.getMethods();
        for (Method rightMethod : methods) {
            Replace replace = rightMethod.getAnnotation(Replace.class);
            if (replace == null) {
                continue;
            }

            String clazzName = replace.clazz();
            String methodName = replace.method();


            try {
                Class wrongClazz=Class.forName(clazzName);
                //Method     right       wrong
                Method wrongMethod=wrongClazz.getDeclaredMethod(methodName, rightMethod.getParameterTypes());
                replace(wrongMethod,rightMethod);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
//调用到Native层,进行ArtMethod结构体的修改
private native static void replace(Method wrongMethod,Method rightMethod);
  1. 赋值android源码中art_method.h,只保留部分声明即可。
extern "C"
JNIEXPORT void JNICALL
Java_com_dongnao_andfix_DexManager_replace(JNIEnv *env, jobject instance, jint sdk,jobject wrongMethod,
                                           jobject rightMethod) {

//        ArtMethod  ----->

    art::mirror::ArtMethod *wrong= reinterpret_cast<art::mirror::ArtMethod *>(env->FromReflectedMethod(wrongMethod));
    art::mirror::ArtMethod *right= reinterpret_cast<art::mirror::ArtMethod *>(env->FromReflectedMethod(rightMethod));

//    wrong=right;
    wrong->declaring_class_ = right->declaring_class_;
    wrong->dex_cache_resolved_methods_ = right->dex_cache_resolved_methods_;
    wrong->access_flags_ = right->access_flags_;
    wrong->dex_cache_resolved_types_ = right->dex_cache_resolved_types_;
    wrong->dex_code_item_offset_ = right->dex_code_item_offset_;
    wrong->dex_method_index_ = right->dex_method_index_;
    wrong->method_index_ = right->method_index_;
}

它的思想是修改错误ArtMethod结构体中方法入口和字节码地址等,为正确ArtMethod的结构体中对应的内容。这样堆内存对象指向方法时,会去方法区找到ArtMethod,因为ArtMethod的字节码地址修改了,往栈内存压栈的栈帧就会是正确的字节码内容,从而bug得到修复。

Tinker

tinker利用Java层的ClassLoader机制,将修复包中的dex插入到系统ClassLoader的dexElements前端,系统取到了我们替换的class。修复的粒度是整个类替换。
原理和动态加载apk一样。
参考:https://www.jianshu.com/p/fd9ed8b720ef

Andfix和Tinker的差异

由于执行方法时虚拟机层都要读取方法表然后组装栈帧压栈,Andfix可以实现实时热修复。Tinker利用的是ClassLoader机制,但是当一个类已经被加载后,ClassLoader会将其缓存在内存中,不会再去读取dexElement了,所以Tinker不能实现实时热修复。

上一篇下一篇

猜你喜欢

热点阅读