diff --git a/build.gradle.kts b/build.gradle.kts index bd91eba..f18e6ff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,9 +2,9 @@ plugins { alias(libs.plugins.lsplugin.publish) } -val androidTargetSdkVersion by extra(33) +val androidTargetSdkVersion by extra(34) val androidMinSdkVersion by extra(21) -val androidBuildToolsVersion by extra("33.0.2") -val androidCompileSdkVersion by extra(33) -val androidNdkVersion by extra("25.2.9519653") +val androidBuildToolsVersion by extra("34.0.0") +val androidCompileSdkVersion by extra(34) +val androidNdkVersion by extra("26.0.10792818") val androidCmakeVersion by extra("3.22.1+") diff --git a/lsplant/src/main/jni/art/mirror/class.hpp b/lsplant/src/main/jni/art/mirror/class.hpp index f499459..6dce48c 100644 --- a/lsplant/src/main/jni/art/mirror/class.hpp +++ b/lsplant/src/main/jni/art/mirror/class.hpp @@ -28,8 +28,8 @@ private: } using BackupMethods = std::list>; - inline static absl::flat_hash_map> + inline static phmap::flat_hash_map> backup_methods_; inline static std::mutex backup_methods_lock_; @@ -37,28 +37,24 @@ private: std::list> out; if (!class_def) return; { - std::shared_lock lk(hooked_classes_lock_); - if (auto found = hooked_classes_.find(class_def); found != hooked_classes_.end()) - [[unlikely]] { - for (auto method : found->second) { + hooked_classes_.if_contains(class_def, [&out](const auto &it) { + for (auto method : it.second) { if (method->IsStatic()) { LOGV("Backup hooked method %p because of initialization", method); out.emplace_back(method, method->GetEntryPoint()); } } - } + }); } { - std::shared_lock lk(deoptimized_methods_lock_); - if (auto found = deoptimized_classes_.find(class_def); - found != deoptimized_classes_.end()) [[unlikely]] { - for (auto method : found->second) { + deoptimized_classes_.if_contains(class_def, [&out](const auto &it) { + for (auto method : it.second) { if (method->IsStatic()) { LOGV("Backup deoptimized method %p because of initialization", method); out.emplace_back(method, method->GetEntryPoint()); } } - } + }); } if (!out.empty()) [[unlikely]] { std::unique_lock lk(backup_methods_lock_); diff --git a/lsplant/src/main/jni/common.hpp b/lsplant/src/main/jni/common.hpp index 893db92..3e68747 100644 --- a/lsplant/src/main/jni/common.hpp +++ b/lsplant/src/main/jni/common.hpp @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #include #include @@ -104,44 +103,47 @@ class Class; namespace { // target, backup -inline absl::flat_hash_map> hooked_methods_; -inline std::shared_mutex hooked_methods_lock_; +template , + class Eq = phmap::priv::hash_default_eq, + class Alloc = phmap::priv::Allocator>, size_t N = 4> +using SharedHashMap = phmap::parallel_flat_hash_map; -inline absl::flat_hash_map> +template , + class Eq = phmap::priv::hash_default_eq, class Alloc = phmap::priv::Allocator, + size_t N = 4> +using SharedHashSet = phmap::parallel_flat_hash_set; + +inline SharedHashMap> hooked_methods_; + +inline SharedHashMap> hooked_classes_; -inline std::shared_mutex hooked_classes_lock_; -inline absl::flat_hash_set deoptimized_methods_set_; -inline std::shared_mutex deoptimized_methods_lock_; +inline SharedHashSet deoptimized_methods_set_; -inline absl::flat_hash_map> +inline SharedHashMap> deoptimized_classes_; -inline std::shared_mutex deoptimized_classes_lock_; inline std::list> jit_movements_; inline std::shared_mutex jit_movements_lock_; } // namespace inline art::ArtMethod *IsHooked(art::ArtMethod *art_method, bool including_backup = false) { - std::shared_lock lk(hooked_methods_lock_); - if (auto it = hooked_methods_.find(art_method); - it != hooked_methods_.end() && (!including_backup || it->second.first)) { - return it->second.second; - } - return nullptr; + art::ArtMethod *backup = nullptr; + hooked_methods_.if_contains(art_method, [&backup, &including_backup](const auto &it) { + if (!including_backup || it.second.first) backup = it.second.second; + }); + return backup; } inline art::ArtMethod *IsBackup(art::ArtMethod *art_method) { - std::shared_lock lk(hooked_methods_lock_); - if (auto it = hooked_methods_.find(art_method); - it != hooked_methods_.end() && !it->second.first) { - return it->second.second; - } + art::ArtMethod *backup = nullptr; + hooked_methods_.if_contains(art_method, [&backup](const auto &it) { + if (!it.second.first) backup = it.second.second; + }); return nullptr; } inline bool IsDeoptimized(art::ArtMethod *art_method) { - std::shared_lock lk(deoptimized_methods_lock_); return deoptimized_methods_set_.contains(art_method); } @@ -152,26 +154,18 @@ inline std::list> GetJitMovements( inline void RecordHooked(art::ArtMethod *target, const art::dex::ClassDef *class_def, jobject reflected_backup, art::ArtMethod *backup) { - { - std::unique_lock lk(hooked_classes_lock_); - hooked_classes_[class_def].emplace(target); - } - { - std::unique_lock lk(hooked_methods_lock_); - hooked_methods_[target] = {reflected_backup, backup}; - hooked_methods_[backup] = {nullptr, target}; - } + hooked_classes_.lazy_emplace_l( + class_def, [&target](auto &it) { it.second.emplace(target); }, + [&class_def, &target](const auto &ctor) { + ctor(class_def, phmap::flat_hash_set{target}); + }); + hooked_methods_.insert({std::make_pair(target, std::make_pair(reflected_backup, backup)), + std::make_pair(backup, std::make_pair(nullptr, target))}); } inline void RecordDeoptimized(const art::dex::ClassDef *class_def, art::ArtMethod *art_method) { - { - std::unique_lock lk(deoptimized_classes_lock_); - deoptimized_classes_[class_def].emplace(art_method); - } - { - std::unique_lock lk(deoptimized_methods_lock_); - deoptimized_methods_set_.insert(art_method); - } + { deoptimized_classes_[class_def].emplace(art_method); } + deoptimized_methods_set_.insert(art_method); } inline void RecordJitMovement(art::ArtMethod *target, art::ArtMethod *backup) { diff --git a/lsplant/src/main/jni/external/dex_builder b/lsplant/src/main/jni/external/dex_builder index 60251dd..b5a00f2 160000 --- a/lsplant/src/main/jni/external/dex_builder +++ b/lsplant/src/main/jni/external/dex_builder @@ -1 +1 @@ -Subproject commit 60251dde8d7dcb49b2a6249a0214437836cc8991 +Subproject commit b5a00f2ea94ad4c3b92054fd53896dbb429298f9 diff --git a/lsplant/src/main/jni/lsplant.cc b/lsplant/src/main/jni/lsplant.cc index 81338b2..53c2388 100644 --- a/lsplant/src/main/jni/lsplant.cc +++ b/lsplant/src/main/jni/lsplant.cc @@ -158,7 +158,8 @@ bool InitJNI(JNIEnv *env) { return false; } if (method_get_return_type = - JNI_GetMethodID(env, JNI_FindClass(env, "java/lang/reflect/Method"), "getReturnType", "()Ljava/lang/Class;"); + JNI_GetMethodID(env, JNI_FindClass(env, "java/lang/reflect/Method"), "getReturnType", + "()Ljava/lang/Class;"); !method_get_return_type) { LOGE("Failed to find getReturnType method"); return false; @@ -620,7 +621,7 @@ struct JavaDebuggableGuard { if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel, std::memory_order_acquire)) { Runtime::Current()->SetJavaDebuggable( - Runtime::RuntimeDebugState::kJavaDebuggableAtInit); + Runtime::RuntimeDebugState::kJavaDebuggableAtInit); count.fetch_add(1, std::memory_order_release); count.notify_all(); break; @@ -642,7 +643,7 @@ struct JavaDebuggableGuard { if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel, std::memory_order_acquire)) { Runtime::Current()->SetJavaDebuggable( - Runtime::RuntimeDebugState::kNonJavaDebuggable); + Runtime::RuntimeDebugState::kNonJavaDebuggable); count.fetch_sub(1, std::memory_order_release); count.notify_all(); break; @@ -776,28 +777,19 @@ using ::lsplant::IsHooked; auto *target = ArtMethod::FromReflectedMethod(env, target_method); jobject reflected_backup = nullptr; art::ArtMethod *backup = nullptr; - { - std::unique_lock lk(hooked_methods_lock_); - if (auto it = hooked_methods_.find(target); it != hooked_methods_.end()) [[likely]] { - std::tie(reflected_backup, backup) = it->second; - if (reflected_backup == nullptr) { - LOGE("Unable to unhook a method that is not hooked"); - return false; - } - hooked_methods_.erase(it->second.second); - hooked_methods_.erase(it); - } - } - { - std::unique_lock lk(hooked_classes_lock_); - if (auto it = hooked_classes_.find(target->GetDeclaringClass()->GetClassDef()); - it != hooked_classes_.end()) { - it->second.erase(target); - if (it->second.empty()) { - hooked_classes_.erase(it); - } - } + if (!hooked_methods_.erase_if(target, [&reflected_backup, &backup](const auto &it) { + std::tie(reflected_backup, backup) = it.second; + return reflected_backup != nullptr; + })) { + LOGE("Unable to unhook a method that is not hooked"); + return false; } + // FIXME: not atomic, but should be fine + hooked_methods_.erase(backup); + hooked_classes_.erase_if(target->GetDeclaringClass()->GetClassDef(), [&target](auto &it) { + it.second.erase(target); + return it.second.empty(); + }); auto *backup_method = env->FromReflectedMethod(reflected_backup); env->DeleteGlobalRef(reflected_backup); if (DoUnHook(target, backup)) {