Workaround UpdateMethodsCode inlined (#9)

This commit is contained in:
LoveSy 2022-03-14 00:07:14 +08:00 committed by GitHub
parent 3d54cde6aa
commit e49bdad9b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 181 additions and 106 deletions

View File

@ -1,69 +0,0 @@
#pragma once
#include "common.hpp"
#include "runtime/art_method.hpp"
namespace lsplant::art {
class Instrumentation {
inline static ArtMethod *MaybeUseBackupMethod(ArtMethod *art_method, const void *quick_code) {
std::shared_lock lk(hooked_methods_lock_);
if (auto found = hooked_methods_.find(art_method);
found != hooked_methods_.end() && art_method->GetEntryPoint() != quick_code)
[[unlikely]] {
LOGD("Propagate update method code %p for hooked method %s to its backup", quick_code,
art_method->PrettyMethod().c_str());
return found->second.second;
}
return art_method;
}
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art15instrumentation15Instrumentation21UpdateMethodsCodeImplEPNS_9ArtMethodEPKv", void,
UpdateMethodsCodeImpl,
(Instrumentation * thiz, ArtMethod *art_method, const void *quick_code),
{ backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code); });
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art15instrumentation15Instrumentation17UpdateMethodsCodeEPNS_9ArtMethodEPKv", void,
UpdateMethodsCode, (Instrumentation * thiz, ArtMethod *art_method, const void *quick_code),
{
if (UpdateMethodsCodeImpl.backup) {
UpdateMethodsCodeImpl.backup(thiz, MaybeUseBackupMethod(art_method, quick_code),
quick_code);
} else {
backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code);
}
});
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art15instrumentation15Instrumentation17UpdateMethodsCodeEPNS_6mirror9ArtMethodEPKvS6_b",
void, UpdateMethodsCodeWithProtableCode,
(Instrumentation * thiz, ArtMethod *art_method, const void *quick_code,
const void *portable_code, bool have_portable_code),
{
backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code, portable_code,
have_portable_code);
});
public:
static bool Init(const HookHandler &handler) {
int sdk_int = GetAndroidApiLevel();
if (sdk_int < __ANDROID_API_M__) [[unlikely]] {
if (!HookSyms(handler, UpdateMethodsCodeWithProtableCode)) {
return false;
}
return true;
}
if (!HookSyms(handler, UpdateMethodsCode)) {
return false;
}
if (sdk_int >= __ANDROID_API_N__) [[likely]] {
if (!HookSyms(handler, UpdateMethodsCodeImpl)) {
return false;
}
}
return true;
}
};
} // namespace lsplant::art

View File

@ -0,0 +1,60 @@
#pragma once
#include "common.hpp"
namespace lsplant::art {
namespace dex {
class ClassDef {};
} // namespace dex
namespace mirror {
class Class {
private:
CREATE_MEM_FUNC_SYMBOL_ENTRY(const char *, GetDescriptor, Class *thiz, std::string *storage) {
if (GetDescriptorSym) [[likely]]
return GetDescriptorSym(thiz, storage);
else
return "";
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(const dex::ClassDef *, GetClassDef, Class *thiz) {
if (GetClassDefSym) [[likely]]
return GetClassDefSym(thiz);
return nullptr;
}
public:
static bool Init(const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor,
"_ZN3art6mirror5Class13GetDescriptorEPNSt3__112"
"basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE")) {
return false;
}
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv")) {
return false;
}
return true;
}
const char *GetDescriptor(std::string *storage) {
if (GetDescriptorSym) {
return GetDescriptor(this, storage);
}
return "";
}
std::string GetDescriptor() {
std::string storage;
return GetDescriptor(&storage);
}
const dex::ClassDef *GetClassDef() {
if (GetClassDefSym) return GetClassDef(this);
return nullptr;
}
};
} // namespace mirror
} // namespace lsplant::art

View File

@ -1,5 +1,6 @@
#pragma once
#include "art/mirror/class.hpp"
#include "common.hpp"
namespace lsplant::art {
@ -125,6 +126,11 @@ public:
return PrettyMethod(this, with_signature);
}
mirror::Class *GetDeclaringClass() {
return reinterpret_cast<mirror::Class *>(*reinterpret_cast<uint32_t *>(
reinterpret_cast<uintptr_t>(this) + declaring_class_offset));
}
static art::ArtMethod *FromReflectedMethod(JNIEnv *env, jobject method) {
if (art_method_field) [[likely]] {
return reinterpret_cast<art::ArtMethod *>(
@ -221,6 +227,8 @@ public:
field_offset);
};
access_flags_offset = get_offset_from_art_method("accessFlags", "I");
declaring_class_offset =
get_offset_from_art_method("declaringClass", "Ljava/lang/Class;");
if (sdk_int == __ANDROID_API_L__) {
entry_point_offset =
get_offset_from_art_method("entryPointFromQuickCompiledCode", "J");
@ -229,6 +237,7 @@ public:
data_offset = get_offset_from_art_method("entryPointFromJni", "J");
}
}
LOGD("ArtMethod::declaring_class offset: %zu", declaring_class_offset);
LOGD("ArtMethod::entrypoint offset: %zu", entry_point_offset);
LOGD("ArtMethod::data offset: %zu", data_offset);
LOGD("ArtMethod::access_flags offset: %zu", access_flags_offset);
@ -279,7 +288,7 @@ public:
kAccCompileDontBother = kAccDefaultConflict;
}
}
if (sdk_int <= __ANDROID_API_N__) {
if (sdk_int < __ANDROID_API_N__) {
kAccCompileDontBother = 0;
}
if (sdk_int <= __ANDROID_API_M__) [[unlikely]] {
@ -312,6 +321,7 @@ private:
inline static size_t interpreter_entry_point_offset = 0;
inline static size_t data_offset = 0;
inline static size_t access_flags_offset = 0;
inline static size_t declaring_class_offset = 0;
inline static uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
inline static uint32_t kAccPreCompiled = 0x00200000;
inline static uint32_t kAccCompileDontBother = 0x02000000;

View File

@ -1,6 +1,7 @@
#pragma once
#include "art/runtime/art_method.hpp"
#include "art/runtime/obj_ptr.h"
#include "art/thread.hpp"
#include "common.hpp"
@ -35,6 +36,59 @@ private:
return backup(art_method);
});
static auto GetBackupMethods(mirror::Class *mirror_class) {
std::list<std::tuple<art::ArtMethod *, void *>> out;
auto class_def = mirror_class->GetClassDef();
if (!class_def) return out;
std::shared_lock lk(hooked_classes_lock_);
if (auto found = hooked_classes_.find(class_def); found != hooked_classes_.end()) {
LOGD("Before fixup %s, backup hooked methods' trampoline",
mirror_class->GetDescriptor().c_str());
for (auto method : found->second) {
out.emplace_back(method, method->GetEntryPoint());
}
}
return out;
}
static void FixTrampoline(const std::list<std::tuple<art::ArtMethod *, void *>> &methods) {
std::shared_lock lk(hooked_methods_lock_);
for (const auto &[art_method, old_trampoline] : methods) {
if (auto found = hooked_methods_.find(art_method); found != hooked_methods_.end()) {
if (auto new_trampoline = art_method->GetEntryPoint();
new_trampoline != old_trampoline) {
found->second.second->SetEntryPoint(new_trampoline);
art_method->SetEntryPoint(old_trampoline);
}
}
}
}
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", void,
FixupStaticTrampolines, (ClassLinker * thiz, ObjPtr<mirror::Class> mirror_class), {
auto backup_methods = GetBackupMethods(mirror_class);
backup(thiz, mirror_class);
FixTrampoline(backup_methods);
});
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE",
void, FixupStaticTrampolinesWithThread,
(ClassLinker * thiz, art::Thread *self, ObjPtr<mirror::Class> mirror_class), {
auto backup_methods = GetBackupMethods(mirror_class);
backup(thiz, self, mirror_class);
FixTrampoline(backup_methods);
});
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6mirror5ClassE",
void, FixupStaticTrampolinesRaw,
(ClassLinker * thiz, mirror::Class *mirror_class), {
auto backup_methods = GetBackupMethods(mirror_class);
backup(thiz, mirror_class);
FixTrampoline(backup_methods);
});
public:
static bool Init(const HookHandler &handler) {
int sdk_int = GetAndroidApiLevel();
@ -46,6 +100,11 @@ public:
}
}
if (!HookSyms(handler, FixupStaticTrampolinesWithThread, FixupStaticTrampolines,
FixupStaticTrampolinesRaw)) {
return false;
}
if (!RETRIEVE_MEM_FUNC_SYMBOL(
SetEntryPointsToInterpreter,
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"))

View File

@ -0,0 +1,12 @@
#pragma once
template <typename MirrorType>
class ObjPtr {
public:
inline MirrorType* operator->() const { return Ptr(); }
inline MirrorType* Ptr() const { return reference_; }
inline operator MirrorType*() const { return Ptr(); }
private:
MirrorType* reference_;
};

View File

@ -5,17 +5,6 @@
namespace lsplant::art {
class Thread {
struct ObjPtr {
void *data;
};
CREATE_MEM_FUNC_SYMBOL_ENTRY(ObjPtr, DecodeJObject, Thread *thiz, jobject obj) {
if (DecodeJObjectSym) [[likely]]
return DecodeJObjectSym(thiz, obj);
else
return {.data = nullptr};
}
CREATE_FUNC_SYMBOL_ENTRY(Thread *, CurrentFromGdb) {
if (CurrentFromGdbSym) [[likely]]
return CurrentFromGdbSym();
@ -27,22 +16,11 @@ public:
static Thread *Current() { return CurrentFromGdb(); }
static bool Init(const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(DecodeJObject, "_ZNK3art6Thread13DecodeJObjectEP8_jobject"))
[[unlikely]] {
return false;
}
if (!RETRIEVE_FUNC_SYMBOL(CurrentFromGdb, "_ZN3art6Thread14CurrentFromGdbEv"))
[[unlikely]] {
return false;
}
return true;
}
void *DecodeJObject(jobject obj) {
if (DecodeJObjectSym) [[likely]] {
return DecodeJObject(this, obj).data;
}
return nullptr;
}
};
} // namespace lsplant::art

View File

@ -6,6 +6,7 @@
#include <shared_mutex>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include "logging.hpp"
#include "lsplant.hpp"
@ -56,6 +57,12 @@ inline static constexpr auto kPointerSize = sizeof(void *);
namespace art {
class ArtMethod;
namespace dex {
class ClassDef;
}
namespace mirror {
class Class;
}
} // namespace art
namespace {
@ -65,6 +72,10 @@ inline std::shared_mutex hooked_methods_lock_;
inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> jit_movements_;
inline std::shared_mutex jit_movements_lock_;
inline std::unordered_map<const art::dex::ClassDef *, std::unordered_set<art::ArtMethod *>>
hooked_classes_;
inline std::shared_mutex hooked_classes_lock_;
} // namespace
inline bool IsHooked(art::ArtMethod *art_method) {
@ -77,9 +88,16 @@ inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> GetJitMovements(
return std::move(jit_movements_);
}
inline void RecordHooked(art::ArtMethod *target, jobject reflected_backup, art::ArtMethod *backup) {
std::unique_lock lk(hooked_methods_lock_);
hooked_methods_.emplace(target, std::make_pair(reflected_backup, backup));
inline void RecordHooked(art::ArtMethod *target, const art::dex::ClassDef *class_def,
jobject reflected_backup, art::ArtMethod *backup) {
{
std::unique_lock lk(hooked_methods_lock_);
hooked_methods_[target] = {reflected_backup, backup};
}
{
std::unique_lock lk(hooked_classes_lock_);
hooked_classes_[class_def].emplace(target);
}
}
inline void RecordJitMovement(art::ArtMethod *target, art::ArtMethod *backup) {

View File

@ -7,7 +7,7 @@
#include <array>
#include <atomic>
#include "art/instrumentation.hpp"
#include "art/mirror/class.hpp"
#include "art/runtime/art_method.hpp"
#include "art/runtime/class_linker.hpp"
#include "art/runtime/dex_file.hpp"
@ -29,10 +29,10 @@ namespace lsplant {
using art::ArtMethod;
using art::ClassLinker;
using art::DexFile;
using art::Instrumentation;
using art::Thread;
using art::gc::ScopedGCCriticalSection;
using art::jit::JitCodeCache;
using art::mirror::Class;
using art::thread_list::ScopedSuspendAll;
namespace {
@ -226,8 +226,8 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) {
LOGE("Failed to init class linker");
return false;
}
if (!Instrumentation::Init(handler)) {
LOGE("Failed to init instrumentation");
if (!Class::Init(handler)) {
LOGE("Failed to init mirror class");
return false;
}
if (!ScopedSuspendAll::Init(handler)) {
@ -580,7 +580,7 @@ using ::lsplant::IsHooked;
if (DoHook(target, hook, backup)) {
jobject global_backup = JNI_NewGlobalRef(env, reflected_backup);
RecordHooked(target, global_backup, backup);
RecordHooked(target, target->GetDeclaringClass()->GetClassDef(), global_backup, backup);
if (!is_proxy) [[likely]] {
RecordJitMovement(target, backup);
}
@ -605,6 +605,16 @@ using ::lsplant::IsHooked;
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 (reflected_backup == nullptr) {
LOGE("Unable to unhook a method that is not hooked");
return false;
@ -619,11 +629,8 @@ using ::lsplant::IsHooked;
return false;
}
auto *art_method = ArtMethod::FromReflectedMethod(env, method);
if (std::shared_lock lk(hooked_methods_lock_); hooked_methods_.contains(art_method)) {
return true;
}
return false;
std::shared_lock lk(hooked_methods_lock_);
return hooked_methods_.contains(art_method);
}
[[maybe_unused]] bool Deoptimize(JNIEnv *env, jobject method) {

View File

@ -107,7 +107,7 @@ public class UnitTest {
Hooker hooker = Hooker.hook(staticMethod, staticMethodReplacement, null);
Assert.assertNotNull(hooker);
for (int i = 0; i < 1000; ++i) {
for (int i = 0; i < 5000; ++i) {
Assert.assertTrue("Iter " + i, (Boolean) callStaticMethod.invoke(null));
Assert.assertFalse("Iter " + i, (boolean) hooker.backup.invoke(null));
}