diff --git a/lsplant/src/main/jni/art/runtime/art_method.cxx b/lsplant/src/main/jni/art/runtime/art_method.cxx index 91e8ad2..854a02e 100644 --- a/lsplant/src/main/jni/art/runtime/art_method.cxx +++ b/lsplant/src/main/jni/art/runtime/art_method.cxx @@ -2,6 +2,7 @@ module; #include #include +#include #include "logging.hpp" @@ -98,6 +99,18 @@ public: SetAccessFlags(access_flags); } + void SetNative() { + auto access_flags = GetAccessFlags(); + access_flags |= kAccNative; + SetAccessFlags(access_flags); + } + + void SetNonNative() { + auto access_flags = GetAccessFlags(); + access_flags &= ~kAccNative; + SetAccessFlags(access_flags); + } + bool IsPrivate() { return GetAccessFlags() & kAccPrivate; } bool IsProtected() { return GetAccessFlags() & kAccProtected; } bool IsPublic() { return GetAccessFlags() & kAccPublic; } @@ -155,6 +168,12 @@ public: reinterpret_cast(this) + declaring_class_offset)); } + std::unique_ptr Clone() { + auto *method = reinterpret_cast(::operator new(art_method_size)); + method->CopyFrom(this); + return std::unique_ptr(method); + } + void BackupTo(ArtMethod *backup) { SetNonCompilable(); diff --git a/lsplant/src/main/jni/art/runtime/class_linker.cxx b/lsplant/src/main/jni/art/runtime/class_linker.cxx index 8d9a105..167fc26 100644 --- a/lsplant/src/main/jni/art/runtime/class_linker.cxx +++ b/lsplant/src/main/jni/art/runtime/class_linker.cxx @@ -12,6 +12,7 @@ import common; import clazz; import handle; import hook_helper; +import runtime; namespace lsplant::art { export class ClassLinker { @@ -35,6 +36,9 @@ private: inline static Function<"art_quick_generic_jni_trampoline", void(void *)> art_quick_generic_jni_trampoline_; + inline static Function<"_ZN3art15instrumentationL19GetOptimizedCodeForEPNS_9ArtMethodE", + void *(ArtMethod *)> GetOptimizedCodeFor_; + inline static art::ArtMethod *MayGetBackup(art::ArtMethod *method) { if (auto backup = IsHooked(method); backup) [[unlikely]] { method = backup; @@ -161,7 +165,7 @@ private: }; public: - static bool Init(const HookHandler &handler) { + static bool Init(JNIEnv *env, const HookHandler &handler) { int sdk_int = GetAndroidApiLevel(); if (sdk_int >= __ANDROID_API_N__ && sdk_int < __ANDROID_API_T__) { @@ -187,16 +191,32 @@ public: } } - if (!handler.dlsym(SetEntryPointsToInterpreter_)) [[unlikely]] { + if (handler.dlsym(GetOptimizedCodeFor_, true)) [[likely]] { + auto obj = JNI_FindClass(env, "java/lang/Object"); + if (!obj) { + return false; + } + auto method = JNI_GetMethodID(env, obj, "equals", "(Ljava/lang/Object;)Z"); + if (!method) { + return false; + } + auto dummy = ArtMethod::FromReflectedMethod( + env, JNI_ToReflectedMethod(env, obj, method, false).get())->Clone(); + JavaDebuggableGuard guard; + dummy->SetNonNative(); + art_quick_to_interpreter_bridge_ = GetOptimizedCodeFor_(dummy.get()); + dummy->SetNative(); + art_quick_generic_jni_trampoline_ = GetOptimizedCodeFor_(dummy.get()); + } else if (!handler.dlsym(SetEntryPointsToInterpreter_)) [[unlikely]] { if (!handler.dlsym(art_quick_to_interpreter_bridge_)) [[unlikely]] { return false; } if (!handler.dlsym(art_quick_generic_jni_trampoline_)) [[unlikely]] { return false; } - LOGD("art_quick_to_interpreter_bridge = %p", &art_quick_to_interpreter_bridge_); - LOGD("art_quick_generic_jni_trampoline = %p", &art_quick_generic_jni_trampoline_); } + LOGD("art_quick_to_interpreter_bridge = %p", &art_quick_to_interpreter_bridge_); + LOGD("art_quick_generic_jni_trampoline = %p", &art_quick_generic_jni_trampoline_); return true; } diff --git a/lsplant/src/main/jni/art/runtime/runtime.cxx b/lsplant/src/main/jni/art/runtime/runtime.cxx index 521a3ce..c46b8e4 100644 --- a/lsplant/src/main/jni/art/runtime/runtime.cxx +++ b/lsplant/src/main/jni/art/runtime/runtime.cxx @@ -1,6 +1,7 @@ module; #include +#include #include "logging.hpp" @@ -87,4 +88,56 @@ public: return true; } }; + +export struct JavaDebuggableGuard { + JavaDebuggableGuard() { + while (true) { + size_t expected = 0; + if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel, + std::memory_order_acquire)) { + Runtime::Current()->SetJavaDebuggable( + Runtime::RuntimeDebugState::kJavaDebuggableAtInit); + count.fetch_add(1, std::memory_order_release); + count.notify_all(); + break; + } + if (expected == 1) { + count.wait(expected, std::memory_order_acquire); + continue; + } + if (count.compare_exchange_strong(expected, expected + 1, std::memory_order_acq_rel, + std::memory_order_relaxed)) { + break; + } + } + } + + ~JavaDebuggableGuard() { + while (true) { + size_t expected = 2; + if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel, + std::memory_order_acquire)) { + Runtime::Current()->SetJavaDebuggable( + Runtime::RuntimeDebugState::kNonJavaDebuggable); + count.fetch_sub(1, std::memory_order_release); + count.notify_all(); + break; + } + if (expected == 1) { + count.wait(expected, std::memory_order_acquire); + continue; + } + if (count.compare_exchange_strong(expected, expected - 1, std::memory_order_acq_rel, + std::memory_order_relaxed)) { + break; + } + } + } + +private: + inline static std::atomic_size_t count{0}; + static_assert(std::atomic_size_t::is_always_lock_free, "Unsupported architecture"); + static_assert(std::is_same_v, + "Unsupported architecture"); +}; } // namespace lsplant::art diff --git a/lsplant/src/main/jni/include/utils/hook_helper.hpp b/lsplant/src/main/jni/include/utils/hook_helper.hpp index 98da471..ad148a4 100644 --- a/lsplant/src/main/jni/include/utils/hook_helper.hpp +++ b/lsplant/src/main/jni/include/utils/hook_helper.hpp @@ -129,6 +129,10 @@ struct Function { [[gnu::always_inline]] constexpr Ret operator()(Args... args) { return function_(args...); } [[gnu::always_inline]] operator bool() { return function_ != nullptr; } auto operator&() const { return function_; } + Function &operator=(void *function) { + function_ = reinterpret_cast(function); + return *this; + } private: friend struct HookHandler; diff --git a/lsplant/src/main/jni/lsplant.cc b/lsplant/src/main/jni/lsplant.cc index 3851511..0d25d08 100644 --- a/lsplant/src/main/jni/lsplant.cc +++ b/lsplant/src/main/jni/lsplant.cc @@ -50,6 +50,7 @@ using art::jit::JitCodeCache; using art::jni::JniIdManager; using art::mirror::Class; using art::thread_list::ScopedSuspendAll; +using art::JavaDebuggableGuard; using namespace std::string_view_literals; @@ -272,7 +273,11 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) { LOGE("Failed to init mirror class"); return false; } - if (!ClassLinker::Init(handler)) { + if (!Runtime::Init(handler)) { + LOGE("Failed to init runtime"); + return false; + } + if (!ClassLinker::Init(env, handler)) { LOGE("Failed to init class linker"); return false; } @@ -304,10 +309,6 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) { LOGE("Failed to init jni id manager"); return false; } - if (!Runtime::Init(handler)) { - LOGE("Failed to init runtime"); - return false; - } // This should always be the last one if (IsJavaDebuggable(env)) { @@ -318,58 +319,6 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) { return true; } -struct JavaDebuggableGuard { - JavaDebuggableGuard() { - while (true) { - size_t expected = 0; - if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel, - std::memory_order_acquire)) { - Runtime::Current()->SetJavaDebuggable( - Runtime::RuntimeDebugState::kJavaDebuggableAtInit); - count.fetch_add(1, std::memory_order_release); - count.notify_all(); - break; - } - if (expected == 1) { - count.wait(expected, std::memory_order_acquire); - continue; - } - if (count.compare_exchange_strong(expected, expected + 1, std::memory_order_acq_rel, - std::memory_order_relaxed)) { - break; - } - } - } - - ~JavaDebuggableGuard() { - while (true) { - size_t expected = 2; - if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel, - std::memory_order_acquire)) { - Runtime::Current()->SetJavaDebuggable( - Runtime::RuntimeDebugState::kNonJavaDebuggable); - count.fetch_sub(1, std::memory_order_release); - count.notify_all(); - break; - } - if (expected == 1) { - count.wait(expected, std::memory_order_acquire); - continue; - } - if (count.compare_exchange_strong(expected, expected - 1, std::memory_order_acq_rel, - std::memory_order_relaxed)) { - break; - } - } - } - -private: - inline static std::atomic_size_t count{0}; - static_assert(std::atomic_size_t::is_always_lock_free, "Unsupported architecture"); - static_assert(std::is_same_v, - "Unsupported architecture"); -}; - std::tuple BuildDex(JNIEnv *env, jobject class_loader, std::string_view shorty, bool is_static, std::string_view method_name,