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());
}
方法的执行模式
- 解释器模式和快速模式(预编译模式)
- 预编译模式(AOT)是在安装的时候将文件预先编译成字节码
FindClass 的原理
- LookupClass 会进行双亲委托机制的检查。
- FindInClass 判断类加载器是否为空,如果是空从系统的库中加载
- 如果不为空调用 DefineClass,会这个 Class分配空间并从 Dex 文件中获取值赋值给 Class 对象。
- 加载 Class 的成员数据。
- 加载来的方法数据
- 调用 oat_method 去连接 Method。(Native 方法是没有 Dex 字节码的)
如何判断一个类是否一样
通过类名,包名,类加载器来判断是否是同一个类。