diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 94f5acc2b..f82bfd613 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -121,6 +121,15 @@ inline std::pair CompilerDriver::IsFastInstanceField( ArtField* resolved_field, uint16_t field_idx) { DCHECK(!resolved_field->IsStatic()); mirror::Class* fields_class = resolved_field->GetDeclaringClass(); + // Keep these classes in sync with prepareSubclassReplacement() calls in libxposed-art. + mirror::Class* super_class = fields_class->GetSuperClass(); + while (super_class != nullptr) { + if (super_class->DescriptorEquals("Landroid/content/res/TypedArray;")) { + VLOG(compiler) << "Preventing fast access to " << PrettyField(resolved_field); + return std::make_pair(false, false); + } + super_class = super_class->GetSuperClass(); + } bool fast_get = referrer_class != nullptr && referrer_class->CanAccessResolvedField(fields_class, resolved_field, dex_cache, field_idx); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 5cde93cb7..50c0c8ef6 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1647,7 +1647,8 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType gc::Heap* const heap = runtime->GetHeap(); auto* cl = runtime->GetClassLinker(); const auto pointer_size = cl->GetImagePointerSize(); - bool use_dex_cache = GetCompilerOptions().GetCompilePic(); // Off by default + //bool use_dex_cache = GetCompilerOptions().GetCompilePic(); // Off by default + bool use_dex_cache = true; const bool compiling_boot = heap->IsCompilingBoot(); // TODO This is somewhat hacky. We should refactor all of this invoke codepath. const bool force_relocations = (compiling_boot || diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index c9a4bfe98..7dc3ed875 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -403,9 +403,7 @@ static void MaybeRunInliner(HGraph* graph, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, StackHandleScopeCollection* handles) { - const CompilerOptions& compiler_options = driver->GetCompilerOptions(); - bool should_inline = (compiler_options.GetInlineDepthLimit() > 0) - && (compiler_options.GetInlineMaxCodeUnits() > 0); + bool should_inline = false; if (!should_inline) { return; } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 7647ad6e5..c20dd7a68 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -280,13 +280,13 @@ inline const char* ArtMethod::GetDeclaringClassDescriptor() { if (UNLIKELY(dex_method_idx == DexFile::kDexNoIndex)) { return ""; } - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); return dex_file->GetMethodDeclaringClassDescriptor(dex_file->GetMethodId(dex_method_idx)); } inline const char* ArtMethod::GetShorty(uint32_t* out_length) { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), out_length); } @@ -294,7 +294,7 @@ inline const char* ArtMethod::GetShorty(uint32_t* out_length) { inline const Signature ArtMethod::GetSignature() { uint32_t dex_method_idx = GetDexMethodIndex(); if (dex_method_idx != DexFile::kDexNoIndex) { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); return dex_file->GetMethodSignature(dex_file->GetMethodId(dex_method_idx)); } @@ -304,7 +304,7 @@ inline const Signature ArtMethod::GetSignature() { inline const char* ArtMethod::GetName() { uint32_t dex_method_idx = GetDexMethodIndex(); if (LIKELY(dex_method_idx != DexFile::kDexNoIndex)) { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); return dex_file->GetMethodName(dex_file->GetMethodId(dex_method_idx)); } @@ -325,11 +325,14 @@ inline const char* ArtMethod::GetName() { } inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { + if (UNLIKELY(IsXposedHookedMethod())) { + return nullptr; + } return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset()); } inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx, size_t ptr_size) { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); return GetDexCacheResolvedType(type_idx, ptr_size) != nullptr; } @@ -342,13 +345,13 @@ inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) { } inline const DexFile::ProtoId& ArtMethod::GetPrototype() { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); return dex_file->GetMethodPrototype(dex_file->GetMethodId(GetDexMethodIndex())); } inline const DexFile::TypeList* ArtMethod::GetParameterTypeList() { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); const DexFile::ProtoId& proto = dex_file->GetMethodPrototype( dex_file->GetMethodId(GetDexMethodIndex())); @@ -361,17 +364,17 @@ inline const char* ArtMethod::GetDeclaringClassSourceFile() { } inline uint16_t ArtMethod::GetClassDefIndex() { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); return GetDeclaringClass()->GetDexClassDefIndex(); } inline const DexFile::ClassDef& ArtMethod::GetClassDef() { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); return GetDexFile()->GetClassDef(GetClassDefIndex()); } inline const char* ArtMethod::GetReturnTypeDescriptor() { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); @@ -380,27 +383,27 @@ inline const char* ArtMethod::GetReturnTypeDescriptor() { } inline const char* ArtMethod::GetTypeDescriptorFromTypeIdx(uint16_t type_idx) { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); return dex_file->GetTypeDescriptor(dex_file->GetTypeId(type_idx)); } inline mirror::ClassLoader* ArtMethod::GetClassLoader() { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); return GetDeclaringClass()->GetClassLoader(); } inline mirror::DexCache* ArtMethod::GetDexCache() { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); return GetDeclaringClass()->GetDexCache(); } -inline bool ArtMethod::IsProxyMethod() { - return GetDeclaringClass()->IsProxyClass(); +inline bool ArtMethod::IsProxyMethod(bool ignore_xposed) { + return GetDeclaringClass()->IsProxyClass() || (!ignore_xposed && IsXposedHookedMethod()); } inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy(size_t pointer_size) { - if (LIKELY(!IsProxyMethod())) { + if (LIKELY(!IsProxyMethod(true))) { return this; } mirror::Class* klass = GetDeclaringClass(); @@ -425,7 +428,7 @@ inline void ArtMethod::SetDexCacheResolvedTypes(GcRoot* new_dex_c } inline mirror::Class* ArtMethod::GetReturnType(bool resolve, size_t ptr_size) { - DCHECK(!IsProxyMethod()); + DCHECK(!IsProxyMethod(true)); const DexFile* dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); @@ -455,6 +458,9 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this)); interface_method->VisitRoots(visitor, pointer_size); } + if (UNLIKELY(IsXposedHookedMethod())) { + GetXposedOriginalMethod()->VisitRoots(visitor, pointer_size); + } visitor.VisitRoot(declaring_class_.AddressWithoutBarrier()); // We know we don't have profiling information if the class hasn't been verified. Note // that this check also ensures the IsNative call can be made, as IsNative expects a fully @@ -463,7 +469,7 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { // Runtime methods and native methods use the same field as the profiling info for // storing their own data (jni entrypoint for native methods, and ImtConflictTable for // some runtime methods). - if (!IsNative() && !IsRuntimeMethod()) { + if (!IsNative() && !IsRuntimeMethod() && !IsXposedHookedMethod()) { ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size); if (profiling_info != nullptr) { profiling_info->VisitRoots(visitor); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index f86cb1351..b2eb9c8c6 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -33,11 +33,13 @@ #include "jni_internal.h" #include "mirror/abstract_method.h" #include "mirror/class-inl.h" +#include "mirror/method.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "mirror/string.h" #include "oat_file-inl.h" #include "scoped_thread_state_change.h" +#include "thread_list.h" #include "well_known_classes.h" namespace art { @@ -47,6 +49,9 @@ extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, const char*); +jclass ArtMethod::xposed_callback_class = nullptr; +jmethodID ArtMethod::xposed_callback_method = nullptr; + ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) { auto* abstract_method = soa.Decode(jlr_method); @@ -55,7 +60,7 @@ ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnabl } mirror::String* ArtMethod::GetNameAsString(Thread* self) { - CHECK(!IsProxyMethod()); + CHECK(!IsProxyMethod(true)); StackHandleScope<1> hs(self); Handle dex_cache(hs.NewHandle(GetDexCache())); auto* dex_file = dex_cache->GetDexFile(); @@ -134,7 +139,7 @@ ArtMethod* ArtMethod::FindOverriddenMethod(size_t pointer_size) { result = super_class->GetVTableEntry(method_index, pointer_size); } else { // Method didn't override superclass method so search interfaces - if (IsProxyMethod()) { + if (IsProxyMethod(true)) { result = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size), GetDexMethodIndex(), pointer_size); @@ -313,6 +318,10 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* } void ArtMethod::RegisterNative(const void* native_method, bool is_fast) { + if (UNLIKELY(IsXposedHookedMethod())) { + GetXposedOriginalMethod()->RegisterNative(native_method, is_fast); + return; + } CHECK(IsNative()) << PrettyMethod(this); CHECK(!IsFastNative()) << PrettyMethod(this); CHECK(native_method != nullptr) << PrettyMethod(this); @@ -323,6 +332,10 @@ void ArtMethod::RegisterNative(const void* native_method, bool is_fast) { } void ArtMethod::UnregisterNative() { + if (UNLIKELY(IsXposedHookedMethod())) { + GetXposedOriginalMethod()->UnregisterNative(); + return; + } CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this); // restore stub to lookup native pointer via dlsym RegisterNative(GetJniDlsymLookupStub(), false); @@ -388,7 +401,7 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { } if (existing_entry_point == GetQuickProxyInvokeHandler()) { - DCHECK(IsProxyMethod() && !IsConstructor()); + DCHECK(IsProxyMethod(true) && !IsConstructor()); // The proxy entry point does not have any method header. return nullptr; } @@ -418,7 +431,7 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { << ", pc=" << std::hex << pc << ", entry_point=" << std::hex << reinterpret_cast(existing_entry_point) << ", copy=" << std::boolalpha << IsCopied() - << ", proxy=" << std::boolalpha << IsProxyMethod(); + << ", proxy=" << std::boolalpha << IsProxyMethod(true); } } @@ -497,4 +510,85 @@ void ArtMethod::CopyFrom(ArtMethod* src, size_t image_pointer_size) { hotness_count_ = 0; } +static void StackReplaceMethod(Thread* thread, void* arg) SHARED_REQUIRES(Locks::mutator_lock_) { + struct StackReplaceMethodVisitor FINAL : public StackVisitor { + StackReplaceMethodVisitor(Thread* thread_in, ArtMethod* search, ArtMethod* replace) + : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kSkipInlinedFrames), + search_(search), replace_(replace) {}; + + bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) { + if (GetMethod() == search_) { + SetMethod(replace_); + } + return true; + } + + ArtMethod* search_; + ArtMethod* replace_; + }; + + ArtMethod* search = reinterpret_cast(arg); + + // We cannot use GetXposedOriginalMethod() because the access flags aren't modified yet. + auto hook_info = reinterpret_cast(search->GetEntryPointFromJni()); + ArtMethod* replace = hook_info->originalMethod; + + StackReplaceMethodVisitor visitor(thread, search, replace); + visitor.WalkStack(); +} + +void ArtMethod::EnableXposedHook(ScopedObjectAccess& soa, jobject additional_info) { + if (UNLIKELY(IsXposedHookedMethod())) { + // Already hooked + return; + } else if (UNLIKELY(IsXposedOriginalMethod())) { + // This should never happen + ThrowIllegalArgumentException(StringPrintf("Cannot hook the method backup: %s", PrettyMethod(this).c_str()).c_str()); + return; + } + + // Create a backup of the ArtMethod object + auto cl = Runtime::Current()->GetClassLinker(); + //auto allocator = GetClassLoader()->GetAllocator(); SIGSEGV + auto allocator = Runtime::Current()->GetLinearAlloc(); + const size_t method_alignment = ArtMethod::Alignment(cl->GetImagePointerSize()); + const size_t method_size = ArtMethod::Size(cl->GetImagePointerSize()); + LengthPrefixedArray* method_array = cl->AllocArtMethodArray(Thread::Current(), allocator, 1); + ArtMethod* backup_method = &method_array->At(0, method_size, method_alignment); + backup_method->CopyFrom(this, cl->GetImagePointerSize()); + backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod); + + // Create a Method/Constructor object for the backup ArtMethod object + mirror::AbstractMethod* reflect_method; + if (IsConstructor()) { + reflect_method = mirror::Constructor::CreateFromArtMethod(soa.Self(), backup_method); + } else { + reflect_method = mirror::Method::CreateFromArtMethod(soa.Self(), backup_method); + } + reflect_method->SetAccessible(true); + + // Save extra information in a separate structure, stored instead of the native method + XposedHookInfo* hookInfo = reinterpret_cast(calloc(1, sizeof(XposedHookInfo))); + hookInfo->reflectedMethod = soa.Vm()->AddGlobalRef(soa.Self(), reflect_method); + hookInfo->additionalInfo = soa.Env()->NewGlobalRef(additional_info); + hookInfo->originalMethod = backup_method; + SetEntryPointFromJni(reinterpret_cast(hookInfo)); + + ThreadList* tl = Runtime::Current()->GetThreadList(); + soa.Self()->TransitionFromRunnableToSuspended(kSuspended); + tl->SuspendAll("Hooking method"); + { + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); + tl->ForEach(StackReplaceMethod, this); + } + tl->ResumeAll(); + soa.Self()->TransitionFromSuspendedToRunnable(); + + SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); + //SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); + + // Adjust access flags + SetAccessFlags((GetAccessFlags() & ~kAccNative & ~kAccSynchronized) | kAccXposedHookedMethod); +} + } // namespace art diff --git a/runtime/art_method.h b/runtime/art_method.h index b65cb2351..603e0d5ef 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -26,6 +26,7 @@ #include "modifiers.h" #include "mirror/object.h" #include "read_barrier_option.h" +#include "scoped_thread_state_change.h" #include "stack.h" #include "utils.h" @@ -38,6 +39,12 @@ class ScopedObjectAccessAlreadyRunnable; class StringPiece; class ShadowFrame; +struct XposedHookInfo { + jobject reflectedMethod; + jobject additionalInfo; + ArtMethod* originalMethod; +}; + namespace mirror { class Array; class Class; @@ -285,12 +292,15 @@ class ArtMethod FINAL { } // Returns true if the method is static, private, or a constructor. - bool IsDirect() { - return IsDirect(GetAccessFlags()); + bool IsDirect(bool ignore_xposed = false){ + return IsDirect(GetAccessFlags(), ignore_xposed); } - static bool IsDirect(uint32_t access_flags) { - constexpr uint32_t direct = kAccStatic | kAccPrivate | kAccConstructor; + static bool IsDirect(uint32_t access_flags, bool ignore_xposed = false) { + uint32_t direct = kAccStatic | kAccPrivate | kAccConstructor; + if (LIKELY(!ignore_xposed)) { + direct |= kAccXposedOriginalMethod; + } return (access_flags & direct) != 0; } @@ -356,7 +366,7 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccSynthetic) != 0; } - bool IsProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_); + bool IsProxyMethod(bool ignore_xposed = false) SHARED_REQUIRES(Locks::mutator_lock_); bool SkipAccessChecks() { return (GetAccessFlags() & kAccSkipAccessChecks) != 0; @@ -486,6 +496,10 @@ class ArtMethod FINAL { } ALWAYS_INLINE void SetEntryPointFromQuickCompiledCodePtrSize( const void* entry_point_from_quick_compiled_code, size_t pointer_size) { + if (kIsDebugBuild) { + ScopedObjectAccess soa(Thread::Current()); + CHECK(Runtime::Current()->IsAotCompiler() || !IsXposedHookedMethod()); + } SetNativePointer(EntryPointFromQuickCompiledCodeOffset(pointer_size), entry_point_from_quick_compiled_code, pointer_size); } @@ -696,6 +710,29 @@ class ArtMethod FINAL { template ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor, size_t pointer_size); + // Xposed + bool IsXposedHookedMethod() SHARED_REQUIRES(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccXposedHookedMethod) != 0; + } + + bool IsXposedOriginalMethod() SHARED_REQUIRES(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccXposedOriginalMethod) != 0; + } + + void EnableXposedHook(ScopedObjectAccess& soa, jobject additional_info) SHARED_REQUIRES(Locks::mutator_lock_); + + const XposedHookInfo* GetXposedHookInfo() SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsXposedHookedMethod()); + return reinterpret_cast(GetEntryPointFromJni()); + } + + ArtMethod* GetXposedOriginalMethod() SHARED_REQUIRES(Locks::mutator_lock_) { + return GetXposedHookInfo()->originalMethod; + } + + static jclass xposed_callback_class; + static jmethodID xposed_callback_method; + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of. diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index df8a3692a..8ca4872c6 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -185,7 +185,7 @@ class LogMessageData { LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, int error) : data_(new LogMessageData(file, line, severity, error)) { if (PrintDirectly(severity)) { - static constexpr char kLogCharacters[] = { 'N', 'V', 'D', 'I', 'W', 'E', 'F', 'F' }; + static constexpr char kLogCharacters[] = { 'N', 'V', 'D', 'I', 'W', 'E', 'F', 'V', 'D', 'I', 'W', 'E', 'F', 'F' }; static_assert(arraysize(kLogCharacters) == static_cast(INTERNAL_FATAL) + 1, "Wrong character array size"); stream() << ProgramInvocationShortName() << " " << kLogCharacters[static_cast(severity)] @@ -243,6 +243,8 @@ std::ostream& LogMessage::stream() { static const android_LogPriority kLogSeverityToAndroidLogPriority[] = { ANDROID_LOG_VERBOSE, // NONE, use verbose as stand-in, will never be printed. ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, + ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, + ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL }; static_assert(arraysize(kLogSeverityToAndroidLogPriority) == INTERNAL_FATAL + 1, @@ -258,13 +260,16 @@ void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity log_se #ifdef __ANDROID__ const char* tag = ProgramInvocationShortName(); int priority = kLogSeverityToAndroidLogPriority[static_cast(log_severity)]; + if (log_severity >= XPOSED_VERBOSE && log_severity <= XPOSED_FATAL) { + tag = "Xposed"; + } if (priority == ANDROID_LOG_FATAL) { LOG_PRI(priority, tag, "%s:%u] %s", file, line, message); } else { LOG_PRI(priority, tag, "%s", message); } #else - static const char* log_characters = "NVDIWEFF"; + static const char* log_characters = "NVDIWEFVDIWEFF"; CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U); char severity = log_characters[log_severity]; fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", @@ -299,7 +304,7 @@ void LogMessage::LogLineLowStack(const char* file, unsigned int line, LogSeverit android_writeLog(priority, tag, message); } #else - static constexpr char kLogCharacters[] = { 'N', 'V', 'D', 'I', 'W', 'E', 'F', 'F' }; + static constexpr char kLogCharacters[] = { 'N', 'V', 'D', 'I', 'W', 'E', 'F', 'V', 'D', 'I', 'W', 'E', 'F', 'F' }; static_assert(arraysize(kLogCharacters) == static_cast(INTERNAL_FATAL) + 1, "Wrong character array size"); diff --git a/runtime/base/logging.h b/runtime/base/logging.h index 3b5b8b54a..47d40a552 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -31,9 +31,20 @@ enum LogSeverity { WARNING, ERROR, FATAL, + XPOSED_VERBOSE, + XPOSED_DEBUG, + XPOSED_INFO, + XPOSED_WARNING, + XPOSED_ERROR, + XPOSED_FATAL, INTERNAL_FATAL, // For Runtime::Abort. }; +const bool LOG_XPOSED = true; +inline LogSeverity operator|(LogSeverity a, bool log_xposed) { + return log_xposed ? static_cast(static_cast(a) - VERBOSE + XPOSED_VERBOSE) : a; +} + // The members of this struct are the valid arguments to VLOG and VLOG_IS_ON in code, // and the "-verbose:" command line argument. struct LogVerbosity { @@ -93,6 +104,9 @@ extern const char* ProgramInvocationShortName(); // an abort. For example: LOG(FATAL) << "We didn't expect to reach here"; #define LOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, -1).stream() +// Same as LOG(severity), but override the log tag to be "Xposed". +#define XLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity|LOG_XPOSED, -1).stream() + // A variant of LOG that also logs the current errno value. To be used when library calls fail. #define PLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, errno).stream() diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index a88291f28..9a269072f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2707,7 +2707,7 @@ const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, bool* // method for direct methods (or virtual methods made direct). mirror::Class* declaring_class = method->GetDeclaringClass(); size_t oat_method_index; - if (method->IsStatic() || method->IsDirect()) { + if (method->IsStatic() || method->IsDirect(true)) { // Simple case where the oat method index was stashed at load time. oat_method_index = method->GetMethodIndex(); } else { @@ -2742,7 +2742,7 @@ const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, bool* // Special case to get oat code without overwriting a trampoline. const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) { CHECK(method->IsInvokable()) << PrettyMethod(method); - if (method->IsProxyMethod()) { + if (method->IsProxyMethod(true)) { return GetQuickProxyInvokeHandler(); } bool found; @@ -2761,7 +2761,7 @@ const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) { } const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) { - if (method->IsNative() || !method->IsInvokable() || method->IsProxyMethod()) { + if (method->IsNative() || !method->IsInvokable() || method->IsProxyMethod(true)) { return nullptr; } bool found; @@ -2773,7 +2773,7 @@ const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) { } bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) { - if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) { + if (UNLIKELY(method->IsNative() || method->IsProxyMethod(true))) { return false; } @@ -2857,6 +2857,9 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index); quick_code = oat_method.GetQuickCode(); } + if (UNLIKELY(method->IsXposedHookedMethod())) { + method = method->GetXposedOriginalMethod(); + } // Check whether the method is native, in which case it's generic JNI. if (quick_code == nullptr && method->IsNative()) { quick_code = GetQuickGenericJniStub(); @@ -4314,7 +4317,7 @@ std::string ClassLinker::GetDescriptorForProxy(mirror::Class* proxy_class) { ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) { DCHECK(proxy_class->IsProxyClass()); - DCHECK(proxy_method->IsProxyMethod()); + DCHECK(proxy_method->IsProxyMethod(true)); { Thread* const self = Thread::Current(); ReaderMutexLock mu(self, dex_lock_); @@ -4821,7 +4824,7 @@ static void ThrowSignatureCheckResolveReturnTypeException(Handle ArtMethod* m) SHARED_REQUIRES(Locks::mutator_lock_) { DCHECK(Thread::Current()->IsExceptionPending()); - DCHECK(!m->IsProxyMethod()); + DCHECK(!m->IsProxyMethod(true)); const DexFile* dex_file = m->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(m->GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); @@ -4846,7 +4849,7 @@ static void ThrowSignatureCheckResolveArgException(Handle klass, uint32_t arg_type_idx) SHARED_REQUIRES(Locks::mutator_lock_) { DCHECK(Thread::Current()->IsExceptionPending()); - DCHECK(!m->IsProxyMethod()); + DCHECK(!m->IsProxyMethod(true)); const DexFile* dex_file = m->GetDexFile(); std::string arg_type = PrettyType(arg_type_idx, *dex_file); std::string class_loader = PrettyTypeOf(m->GetDeclaringClass()->GetClassLoader()); @@ -5584,7 +5587,7 @@ class MethodNameAndSignatureComparator FINAL : public ValueObject { SHARED_REQUIRES(Locks::mutator_lock_) : dex_file_(method->GetDexFile()), mid_(&dex_file_->GetMethodId(method->GetDexMethodIndex())), name_(nullptr), name_len_(0) { - DCHECK(!method->IsProxyMethod()) << PrettyMethod(method); + DCHECK(!method->IsProxyMethod(true)) << PrettyMethod(method); } const char* GetName() { @@ -5596,7 +5599,7 @@ class MethodNameAndSignatureComparator FINAL : public ValueObject { bool HasSameNameAndSignature(ArtMethod* other) SHARED_REQUIRES(Locks::mutator_lock_) { - DCHECK(!other->IsProxyMethod()) << PrettyMethod(other); + DCHECK(!other->IsProxyMethod(true)) << PrettyMethod(other); const DexFile* other_dex_file = other->GetDexFile(); const DexFile::MethodId& other_mid = other_dex_file->GetMethodId(other->GetDexMethodIndex()); if (dex_file_ == other_dex_file) { diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 197caa187..161ea460c 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -234,6 +234,74 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons } } +JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, + jobject rcvr_jobj, jmethodID method, + std::vector& args) { + // Build argument array possibly triggering GC. + soa.Self()->AssertThreadSuspensionIsAllowable(); + jobjectArray args_jobj = nullptr; + const JValue zero; + int32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion(); + // Do not create empty arrays unless needed to maintain Dalvik bug compatibility. + if (args.size() > 0 || (target_sdk_version > 0 && target_sdk_version <= 21)) { + args_jobj = soa.Env()->NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, nullptr); + if (args_jobj == nullptr) { + CHECK(soa.Self()->IsExceptionPending()); + return zero; + } + for (size_t i = 0; i < args.size(); ++i) { + if (shorty[i + 1] == 'L') { + jobject val = args.at(i).l; + soa.Env()->SetObjectArrayElement(args_jobj, i, val); + } else { + JValue jv; + jv.SetJ(args.at(i).j); + mirror::Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv); + if (val == nullptr) { + CHECK(soa.Self()->IsExceptionPending()); + return zero; + } + soa.Decode* >(args_jobj)->Set(i, val); + } + } + } + + const XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo(); + + // Call XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, + // Object thisObject, Object[] args) + jvalue invocation_args[5]; + invocation_args[0].l = hookInfo->reflectedMethod; + invocation_args[1].i = 1; + invocation_args[2].l = hookInfo->additionalInfo; + invocation_args[3].l = rcvr_jobj; + invocation_args[4].l = args_jobj; + jobject result = + soa.Env()->CallStaticObjectMethodA(ArtMethod::xposed_callback_class, + ArtMethod::xposed_callback_method, + invocation_args); + + + // Unbox the result if necessary and return it. + if (UNLIKELY(soa.Self()->IsExceptionPending())) { + return zero; + } else { + if (shorty[0] == 'V' || (shorty[0] == 'L' && result == nullptr)) { + return zero; + } + // This can cause thread suspension. + size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + mirror::Class* result_type = soa.DecodeMethod(method)->GetReturnType(true, pointer_size); + mirror::Object* result_ref = soa.Decode(result); + JValue result_unboxed; + if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) { + DCHECK(soa.Self()->IsExceptionPending()); + return zero; + } + return result_unboxed; + } +} + bool FillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload) { DCHECK_EQ(payload->ident, static_cast(Instruction::kArrayDataSignature)); if (UNLIKELY(obj == nullptr)) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index a28376fad..f9d9d342d 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -173,6 +173,11 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons std::vector& args) SHARED_REQUIRES(Locks::mutator_lock_); +JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, + jobject rcvr_jobj, jmethodID method, + std::vector& args) + SHARED_REQUIRES(Locks::mutator_lock_); + bool FillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 41033ab0a..7ee5b4d62 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -840,7 +840,9 @@ extern "C" uint64_t artQuickProxyInvokeHandler( ArtMethod* proxy_method, mirror::Object* receiver, Thread* self, ArtMethod** sp) SHARED_REQUIRES(Locks::mutator_lock_) { DCHECK(proxy_method->IsProxyMethod()) << PrettyMethod(proxy_method); - DCHECK(receiver->GetClass()->IsProxyClass()) << PrettyMethod(proxy_method); + const bool is_xposed = proxy_method->IsXposedHookedMethod(); + const bool is_static = proxy_method->IsStatic(); + DCHECK(is_xposed || receiver->GetClass()->IsProxyClass()) << PrettyMethod(proxy_method); // Ensure we don't get thread suspension until the object arguments are safely in jobjects. const char* old_cause = self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments"); @@ -852,20 +854,30 @@ extern "C" uint64_t artQuickProxyInvokeHandler( ScopedObjectAccessUnchecked soa(env); ScopedJniEnvLocalRefState env_state(env); // Create local ref. copies of proxy method and the receiver. - jobject rcvr_jobj = soa.AddLocalReference(receiver); + jobject rcvr_jobj = is_static ? nullptr : soa.AddLocalReference(receiver); // Placing arguments into args vector and remove the receiver. ArtMethod* non_proxy_method = proxy_method->GetInterfaceMethodIfProxy(sizeof(void*)); - CHECK(!non_proxy_method->IsStatic()) << PrettyMethod(proxy_method) << " " + CHECK(is_xposed || !non_proxy_method->IsStatic()) << PrettyMethod(proxy_method) << " " << PrettyMethod(non_proxy_method); std::vector args; uint32_t shorty_len = 0; const char* shorty = non_proxy_method->GetShorty(&shorty_len); - BuildQuickArgumentVisitor local_ref_visitor(sp, false, shorty, shorty_len, &soa, &args); + BuildQuickArgumentVisitor local_ref_visitor(sp, is_static, shorty, shorty_len, &soa, &args); local_ref_visitor.VisitArguments(); - DCHECK_GT(args.size(), 0U) << PrettyMethod(proxy_method); - args.erase(args.begin()); + if (!is_static) { + DCHECK_GT(args.size(), 0U) << PrettyMethod(proxy_method); + args.erase(args.begin()); + } + + if (is_xposed) { + jmethodID proxy_methodid = soa.EncodeMethod(proxy_method); + self->EndAssertNoThreadSuspension(old_cause); + JValue result = InvokeXposedHandleHookedMethod(soa, shorty, rcvr_jobj, proxy_methodid, args); + local_ref_visitor.FixupReferences(); + return result.GetJ(); + } // Convert proxy method into expected interface method. ArtMethod* interface_method = proxy_method->FindOverriddenMethod(sizeof(void*)); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index e9c71b452..871247f57 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3946,7 +3946,7 @@ void Heap::AddModUnionTable(accounting::ModUnionTable* mod_union_table) { void Heap::CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) { CHECK(c == nullptr || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || - (c->IsVariableSize() || c->GetObjectSize() == byte_count)) << c->GetClassFlags(); + (c->IsVariableSize() || c->GetObjectSize() <= byte_count)) << c->GetClassFlags(); CHECK_GE(byte_count, sizeof(mirror::Object)); } diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 61119f849..be4134d30 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -114,6 +114,9 @@ bool Instrumentation::NeedDebugVersionForBootImageCode(ArtMethod* method, const } void Instrumentation::InstallStubsForMethod(ArtMethod* method) { + if (UNLIKELY(method->IsXposedHookedMethod())) { + method = method->GetXposedOriginalMethod(); + } if (!method->IsInvokable() || method->IsProxyMethod()) { // Do not change stubs for these methods. return; diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 7bd85ec04..551baf938 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2191,7 +2191,7 @@ class JNI { c->DumpClass(LOG(return_errors ? ERROR : FATAL), mirror::Class::kDumpClassFullDetail); ThrowNoSuchMethodError(soa, c, name, sig, "static or non-static"); return JNI_ERR; - } else if (!m->IsNative()) { + } else if (!m->IsNative() && !(m->IsXposedHookedMethod() && m->GetXposedOriginalMethod()->IsNative())) { LOG(return_errors ? ERROR : FATAL) << "Failed to register non-native method " << PrettyDescriptor(c) << "." << name << sig << " as native"; @@ -2216,7 +2216,7 @@ class JNI { size_t unregistered_count = 0; auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (auto& m : c->GetMethods(pointer_size)) { - if (m.IsNative()) { + if (m.IsNative() || (m.IsXposedHookedMethod() && m.GetXposedOriginalMethod()->IsNative())) { m.UnregisterNative(); unregistered_count++; } diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 701c60082..85f325531 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -123,20 +123,25 @@ class CopyObjectVisitor { DISALLOW_COPY_AND_ASSIGN(CopyObjectVisitor); }; -Object* Object::Clone(Thread* self) { +Object* Object::Clone(Thread* self, size_t num_target_bytes) { CHECK(!IsClass()) << "Can't clone classes."; // Object::SizeOf gets the right size even if we're an array. Using c->AllocObject() here would // be wrong. gc::Heap* heap = Runtime::Current()->GetHeap(); size_t num_bytes = SizeOf(); + if (LIKELY(num_target_bytes == 0)) { + num_target_bytes = num_bytes; + } else { + CHECK(num_target_bytes >= num_bytes); + } StackHandleScope<1> hs(self); Handle this_object(hs.NewHandle(this)); Object* copy; CopyObjectVisitor visitor(self, &this_object, num_bytes); if (heap->IsMovableObject(this)) { - copy = heap->AllocObject(self, GetClass(), num_bytes, visitor); + copy = heap->AllocObject(self, GetClass(), num_target_bytes, visitor); } else { - copy = heap->AllocNonMovableObject(self, GetClass(), num_bytes, visitor); + copy = heap->AllocNonMovableObject(self, GetClass(), num_target_bytes, visitor); } return copy; } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index e174cbcad..9be3ed932 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -115,7 +115,7 @@ class MANAGED LOCKABLE Object { ReadBarrierOption kReadBarrierOption = kWithReadBarrier> size_t SizeOf() SHARED_REQUIRES(Locks::mutator_lock_); - Object* Clone(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) + Object* Clone(Thread* self, size_t num_target_bytes = 0) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); int32_t IdentityHashCode() const diff --git a/runtime/modifiers.h b/runtime/modifiers.h index fd7a125bc..d22714411 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -76,6 +76,9 @@ static constexpr uint32_t kAccHasDefaultMethod = 0x40000000; // class/ancestor overrides finalize() static constexpr uint32_t kAccClassIsFinalizable = 0x80000000; +static constexpr uint32_t kAccXposedHookedMethod = 0x10000000; // method has been hooked by Xposed +static constexpr uint32_t kAccXposedOriginalMethod = 0x04000000; // method is a backup created by Xposed + // Valid (meaningful) bits for a field. static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum; diff --git a/runtime/stack.h b/runtime/stack.h index e77ab4647..faeacc169 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -331,6 +331,10 @@ class ShadowFrame { return method_; } + void SetMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) { + method_ = method; + } + mirror::Object* GetThisObject() const SHARED_REQUIRES(Locks::mutator_lock_); mirror::Object* GetThisObject(uint16_t num_ins) const SHARED_REQUIRES(Locks::mutator_lock_); @@ -598,6 +602,14 @@ class StackVisitor { return *GetCurrentQuickFrame(); } + void SetMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) { + if (cur_shadow_frame_ != nullptr) { + cur_shadow_frame_->SetMethod(method); + } else if (cur_quick_frame_ != nullptr) { + *cur_quick_frame_ = method; + } + } + bool IsShadowFrame() const { return cur_shadow_frame_ != nullptr; }