AndFix 的原理—底层如何创建类

石器时代小古董IP属地: 天津
字数 650阅读 501

AndFix 的原理

在 Android 系统中,是无法对一个已经加载的类进行删除的,如果想要替换一个已经加载的类。可以通过 ClassLoader 去加载新的类,做法是在 APP 重启时,优先加载替换的类,这样因为 Android 双亲委派机制的原因,旧的类不会被加载。

AndFix 可以做到不重启,这是因为 AndFix 直接在上层拿到需要替换的方法所对应的 ArtMethod 结构体,并且加载补丁包中的 ArtMethod 结构体。用补丁包中结构体的数据替换掉 bug 方法结构体中数据

每一个 Java 方法在 Art 虚拟机中都对应一个 ArtMethod,它记录了方法的所有信息,包括属性,函数表,访问权限等。

AndroidRuntime

当 AndroidRuntime 被调用时,它会创建 Art 虚拟机 (创建了 env 对象),创建 Zygote 进程。并调用 Zygote 函数的 main 方法

frameworks/base/core/jni/AndroidRuntime.cpp

AndroidRuntime 的 start 函数,这里会调用 ZygoteInit 的 main 函数

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote){
   /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);
    .......
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }

1.加载 class

在 jni 中使用 env->FindClass 可以找到一个类。实际上通过 classLinker 加载类

art/runtime/jni_internal.cc

 static jclass FindClass(JNIEnv* env, const char* name) {
    CHECK_NON_NULL_ARGUMENT(name);
    Runtime* runtime = Runtime::Current();
    ClassLinker* class_linker = runtime->GetClassLinker();
    std::string descriptor(NormalizeJniClassDescriptor(name));
    ScopedObjectAccess soa(env);
    mirror::Class* c = nullptr;
    // 如果AndroidRuntime 已经启动,从当前的 classload 中寻找
    if (runtime->IsStarted()) {
      StackHandleScope<1> hs(soa.Self());
      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa)));
      c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
    } else {
      // 从系统的 classLoader 中寻找
      c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
    }
    return soa.AddLocalReference<jclass>(c);
  }

art/runtime/class_linker.cc

这里调用 LookupClass 通过类的类名,hash 值去查找一个类,如果发现已经加载过,直接返回加载的类

如果没有加载过,判断 classloader 是否为空,如果为空,就通过 BootClassLoader 寻找类。

如果classloader不为空,定义一个类

mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
                                      Handle<mirror::ClassLoader> class_loader) {
  DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
  DCHECK(self != nullptr);
  self->AssertNoPendingException();
  if (descriptor[1] == '\0') {
    // only the descriptors of primitive types should be 1 character long, also avoid class lookup
    // for primitive classes that aren't backed by dex files.
    return FindPrimitiveClass(descriptor[0]);
  }
  const size_t hash = ComputeModifiedUtf8Hash(descriptor);
  // Find the class in the loaded classes table.
  mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get());
  if (klass != nullptr) {
    return EnsureResolved(self, descriptor, klass);
  }
  // Class is not yet loaded.
  if (descriptor[0] == '[') {
    return CreateArrayClass(self, descriptor, hash, class_loader);
  } else if (class_loader.Get() == nullptr) {
    // The boot class loader, search the boot class path.
    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
    if (pair.second != nullptr) {
      return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
                         *pair.second);
    } else {
      // The boot class loader is searched ahead of the application class loader, failures are
      // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
      // trigger the chaining with a proper stack trace.
      mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
      self->SetException(ThrowLocation(), pre_allocated);
      return nullptr;
    }
  } else if (Runtime::Current()->UseCompileTimeClassPath()) {
    // First try with the bootstrap class loader.
    if (class_loader.Get() != nullptr) {
      klass = LookupClass(descriptor, hash, nullptr);
      if (klass != nullptr) {
        return EnsureResolved(self, descriptor, klass);
      }
    }
    // If the lookup failed search the boot class path. We don't perform a recursive call to avoid
    // a NoClassDefFoundError being allocated.
    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
    if (pair.second != nullptr) {
      return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
                         *pair.second);
    }
    // Next try the compile time class path.
    const std::vector<const DexFile*>* class_path;
    {
      ScopedObjectAccessUnchecked soa(self);
      ScopedLocalRef<jobject> jclass_loader(soa.Env(),
                                            soa.AddLocalReference<jobject>(class_loader.Get()));
      class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
    }
    pair = FindInClassPath(descriptor, hash, *class_path);
    if (pair.second != nullptr) {
      return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second);
    }
  } else {
    ScopedObjectAccessUnchecked soa(self);
    mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader);
    if (klass != nullptr) {
      return klass;
    }
    ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                soa.AddLocalReference<jobject>(class_loader.Get()));
    std::string class_name_string(DescriptorToDot(descriptor));
    ScopedLocalRef<jobject> result(soa.Env(), nullptr);
    {
      ScopedThreadStateChange tsc(self, kNative);
      ScopedLocalRef<jobject> class_name_object(soa.Env(),
                                                soa.Env()->NewStringUTF(class_name_string.c_str()));
      if (class_name_object.get() == nullptr) {
        DCHECK(self->IsExceptionPending());  // OOME.
        return nullptr;
      }
      CHECK(class_loader_object.get() != nullptr);
      result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
                                               WellKnownClasses::java_lang_ClassLoader_loadClass,
                                               class_name_object.get()));
    }
  .......
  }

DefineClass

mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
                                        Handle<mirror::ClassLoader> class_loader,
                                        const DexFile& dex_file,
                                        const DexFile::ClassDef& dex_class_def) {
  StackHandleScope<3> hs(self);
  auto klass = hs.NewHandle<mirror::Class>(nullptr);
 .....
   LoadClass(dex_file, dex_class_def, klass, class_loader.Get());

LoadClass

通过 dex 文件中拿到需要的值,赋值给 kclass

void ClassLinker::LoadClass(const DexFile& dex_file,
                            const DexFile::ClassDef& dex_class_def,
                            Handle<mirror::Class> klass,
                            mirror::ClassLoader* class_loader) {
  CHECK(klass.Get() != nullptr);
  CHECK(klass->GetDexCache() != nullptr);
  CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
  const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
  CHECK(descriptor != nullptr);

  klass->SetClass(GetClassRoot(kJavaLangClass));
  if (kUseBakerOrBrooksReadBarrier) {
    klass->AssertReadBarrierPointer();
  }
  uint32_t access_flags = dex_class_def.GetJavaAccessFlags();
  CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U);
  klass->SetAccessFlags(access_flags);
  klass->SetClassLoader(class_loader);
  DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
  klass->SetStatus(mirror::Class::kStatusIdx, nullptr);

  klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
  klass->SetDexTypeIndex(dex_class_def.class_idx_);
  CHECK(klass->GetDexCacheStrings() != nullptr);

  const byte* class_data = dex_file.GetClassData(dex_class_def);
  if (class_data == nullptr) {
    return;  // no fields or methods - for example a marker interface
  }

  OatFile::OatClass oat_class;
  if (Runtime::Current()->IsStarted()
      && !Runtime::Current()->UseCompileTimeClassPath()
      && FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {
    LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);
  } else {
    LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);
  }
}

LoadClassMember

加载 class 类中的成员信息,方法,变量

void ClassLinker::LoadClassMembers(const DexFile& dex_file,
                                   const byte* class_data,
                                   Handle<mirror::Class> klass,
                                   mirror::ClassLoader* class_loader,
                                   const OatFile::OatClass* oat_class) {
  // Load fields.
  ClassDataItemIterator it(dex_file, class_data);
  Thread* self = Thread::Current();
  if (it.NumStaticFields() != 0) {
    mirror::ObjectArray<mirror::ArtField>* statics = AllocArtFieldArray(self, it.NumStaticFields());
    if (UNLIKELY(statics == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetSFields(statics);
  }
  if (it.NumInstanceFields() != 0) {
    mirror::ObjectArray<mirror::ArtField>* fields =
        AllocArtFieldArray(self, it.NumInstanceFields());
    if (UNLIKELY(fields == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetIFields(fields);
  }
  for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
    StackHandleScope<1> hs(self);
    Handle<mirror::ArtField> sfield(hs.NewHandle(AllocArtField(self)));
    if (UNLIKELY(sfield.Get() == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetStaticField(i, sfield.Get());
    LoadField(dex_file, it, klass, sfield);
  }
  for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
    StackHandleScope<1> hs(self);
    Handle<mirror::ArtField> ifield(hs.NewHandle(AllocArtField(self)));
    if (UNLIKELY(ifield.Get() == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetInstanceField(i, ifield.Get());
    LoadField(dex_file, it, klass, ifield);
  }

  // Load methods.
  if (it.NumDirectMethods() != 0) {
    // TODO: append direct methods to class object
    mirror::ObjectArray<mirror::ArtMethod>* directs =
         AllocArtMethodArray(self, it.NumDirectMethods());
    if (UNLIKELY(directs == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetDirectMethods(directs);
  }
  if (it.NumVirtualMethods() != 0) {
    // TODO: append direct methods to class object
    mirror::ObjectArray<mirror::ArtMethod>* virtuals =
        AllocArtMethodArray(self, it.NumVirtualMethods());
    if (UNLIKELY(virtuals == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetVirtualMethods(virtuals);
  }
  size_t class_def_method_index = 0;
  uint32_t last_dex_method_index = DexFile::kDexNoIndex;
  size_t last_class_def_method_index = 0;
  for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
    StackHandleScope<1> hs(self);
    Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
    if (UNLIKELY(method.Get() == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetDirectMethod(i, method.Get());
    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), 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++;
  }
  for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
    StackHandleScope<1> hs(self);
    Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
    if (UNLIKELY(method.Get() == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetVirtualMethod(i, method.Get());
    DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
    class_def_method_index++;
  }
  DCHECK(!it.HasNext());
}

LinkCoder

通过 OatMethod 连接函数

void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
                           const DexFile& dex_file, uint32_t dex_method_index,
                           uint32_t method_index) {
  if (Runtime::Current()->IsCompiler()) {
    // The following code only applies to a non-compiler runtime.
    return;
  }
  // Method shouldn't have already been linked.
  DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
#if defined(ART_USE_PORTABLE_COMPILER)
  DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
#endif
  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(method_index);
    oat_method.LinkMethod(method.Get());
  }

  // Install entry point from interpreter.
  bool enter_interpreter = NeedsInterpreter(method.Get(),
                                            method->GetEntryPointFromQuickCompiledCode(),
#if defined(ART_USE_PORTABLE_COMPILER)
                                            method->GetEntryPointFromPortableCompiledCode());
#else
                                            nullptr);
#endif
  if (enter_interpreter && !method->IsNative()) {
    method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
  } else {
    method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
  }

  if (method->IsAbstract()) {
    method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
#if defined(ART_USE_PORTABLE_COMPILER)
    method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
#endif
    return;
  }

  bool have_portable_code = false;
  if (method->IsStatic() && !method->IsConstructor()) {
    // For static methods excluding the class initializer, install the trampoline.
    // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
    // after initializing class (see ClassLinker::InitializeClass method).
    method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline());
#if defined(ART_USE_PORTABLE_COMPILER)
    method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline());
#endif
  } else if (enter_interpreter) {
    if (!method->IsNative()) {
      // Set entry point from compiled code if there's no code or in interpreter only mode.
      method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
#if defined(ART_USE_PORTABLE_COMPILER)
      method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
#endif
    } else {
      method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniTrampoline());
#if defined(ART_USE_PORTABLE_COMPILER)
      method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
#endif
    }
#if defined(ART_USE_PORTABLE_COMPILER)
  } else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) {
    DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
    have_portable_code = true;
    method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
#endif
  } else {
    DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
#if defined(ART_USE_PORTABLE_COMPILER)
    method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
#endif
  }

  if (method->IsNative()) {
    // Unregistering restores the dlsym lookup stub.
    method->UnregisterNative(Thread::Current());

    if (enter_interpreter) {
      // We have a native method here without code. Then it should have either the GenericJni
      // trampoline as entrypoint (non-static), or the Resolution trampoline (static).
      DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickResolutionTrampoline()
          || method->GetEntryPointFromQuickCompiledCode() == GetQuickGenericJniTrampoline());
    }
  }

  // Allow instrumentation its chance to hijack code.
  Runtime* runtime = Runtime::Current();
  runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
                                                   method->GetEntryPointFromQuickCompiledCode(),
#if defined(ART_USE_PORTABLE_COMPILER)
                                                   method->GetEntryPointFromPortableCompiledCode(),
#else
                                                   nullptr,
#endif
                                                   have_portable_code);
}

art/runtime/oat_file.cc

OatMethod 根据编译的类型,设置方法的入口。

void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
  CHECK(method != NULL);
#if defined(ART_USE_PORTABLE_COMPILER)
  method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
#endif
  method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
}

方法的执行模式

  1. 解释器模式和快速模式(预编译模式)
  2. 预编译模式(AOT)是在安装的时候将文件预先编译成字节码

FindClass 的原理

  1. LookupClass 会进行双亲委托机制的检查。
  2. FindInClass 判断类加载器是否为空,如果是空从系统的库中加载
  3. 如果不为空调用 DefineClass,会这个 Class分配空间并从 Dex 文件中获取值赋值给 Class 对象。
  4. 加载 Class 的成员数据。
  5. 加载来的方法数据
  6. 调用 oat_method 去连接 Method。(Native 方法是没有 Dex 字节码的)

如何判断一个类是否一样

通过类名,包名,类加载器来判断是否是同一个类。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
更多精彩内容,就在简书APP
"小礼物走一走,来简书关注我"
还没有人赞赏,支持一下
总资产55共写了9.5W字获得216个赞共76个粉丝