mirror of
https://github.com/LSPosed/LSPlant.git
synced 2025-05-04 20:42:02 +08:00
Fix hook/deoptimize static methods failed on some Andorid 13 devices
Fix #29
This commit is contained in:
parent
c812fb503c
commit
c6cc93ae7e
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -95,6 +95,7 @@ jobs:
|
|||||||
echo 'android.testoptions.manageddevices.emulator.gpu=swiftshader_indirect' >> gradle.properties
|
echo 'android.testoptions.manageddevices.emulator.gpu=swiftshader_indirect' >> gradle.properties
|
||||||
echo 'android.native.buildOutput=verbose' >> gradle.properties
|
echo 'android.native.buildOutput=verbose' >> gradle.properties
|
||||||
echo 'android.sdk.channel=3' >> gradle.properties
|
echo 'android.sdk.channel=3' >> gradle.properties
|
||||||
|
sudo rm -R /Applications/Xcode*
|
||||||
./gradlew :test:allDevicesDebugAndroidTest
|
./gradlew :test:allDevicesDebugAndroidTest
|
||||||
rm -v test/build/outputs/androidTest-results/managedDevice/*/testlog/adb.additional_test_output*
|
rm -v test/build/outputs/androidTest-results/managedDevice/*/testlog/adb.additional_test_output*
|
||||||
- name: Upload outputs
|
- name: Upload outputs
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "art/runtime/art_method.hpp"
|
||||||
|
#include "art/runtime/handle.hpp"
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
namespace lsplant::art {
|
namespace lsplant::art {
|
||||||
|
class Thread;
|
||||||
namespace dex {
|
namespace dex {
|
||||||
class ClassDef {};
|
class ClassDef {};
|
||||||
} // namespace dex
|
} // namespace dex
|
||||||
@ -25,6 +27,75 @@ private:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using BackupMethods = std::list<std::tuple<art::ArtMethod *, void *>>;
|
||||||
|
inline static absl::flat_hash_map<const art::Thread *,
|
||||||
|
absl::flat_hash_map<const dex::ClassDef *, BackupMethods>>
|
||||||
|
backup_methods_;
|
||||||
|
inline static std::mutex backup_methods_lock_;
|
||||||
|
|
||||||
|
static void BackupClassMethods(const dex::ClassDef *class_def, art::Thread *self) {
|
||||||
|
std::list<std::tuple<art::ArtMethod *, void *>> 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) {
|
||||||
|
if (method->IsStatic()) {
|
||||||
|
LOGV("Backup hooked method %s because of initialization",
|
||||||
|
method->PrettyMethod(true).data());
|
||||||
|
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) {
|
||||||
|
if (method->IsStatic()) {
|
||||||
|
LOGV("Backup deoptimized method %s because of initialization",
|
||||||
|
method->PrettyMethod(true).data());
|
||||||
|
out.emplace_back(method, method->GetEntryPoint());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::unique_lock lk(backup_methods_lock_);
|
||||||
|
backup_methods_[self].emplace(class_def, std::move(out));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CREATE_HOOK_STUB_ENTRY(
|
||||||
|
"_ZN3art6mirror5Class9SetStatusENS_6HandleIS1_EENS_11ClassStatusEPNS_6ThreadE", void,
|
||||||
|
SetClassStatus, (Handle<Class> h, uint8_t new_status, Thread *self), {
|
||||||
|
if (new_status == initialized_status) {
|
||||||
|
BackupClassMethods(h->GetClassDef(), self);
|
||||||
|
}
|
||||||
|
return backup(h, new_status, self);
|
||||||
|
});
|
||||||
|
|
||||||
|
CREATE_HOOK_STUB_ENTRY(
|
||||||
|
"_ZN3art6mirror5Class9SetStatusENS_6HandleIS1_EENS1_6StatusEPNS_6ThreadE", void, SetStatus,
|
||||||
|
(Handle<Class> h, int new_status, Thread *self), {
|
||||||
|
if (new_status == static_cast<int>(initialized_status)) {
|
||||||
|
BackupClassMethods(h->GetClassDef(), self);
|
||||||
|
}
|
||||||
|
return backup(h, new_status, self);
|
||||||
|
});
|
||||||
|
|
||||||
|
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art6mirror5Class9SetStatusENS1_6StatusEPNS_6ThreadE", void,
|
||||||
|
ClassSetStatus, (Class * thiz, int new_status, Thread *self), {
|
||||||
|
if (new_status == static_cast<int>(initialized_status)) {
|
||||||
|
BackupClassMethods(thiz->GetClassDef(), self);
|
||||||
|
}
|
||||||
|
return backup(thiz, new_status, self);
|
||||||
|
});
|
||||||
|
|
||||||
|
inline static uint8_t initialized_status = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool Init(const HookHandler &handler) {
|
static bool Init(const HookHandler &handler) {
|
||||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor,
|
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor,
|
||||||
@ -35,6 +106,20 @@ public:
|
|||||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv")) {
|
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!HookSyms(handler, SetClassStatus, SetStatus, ClassSetStatus)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdk_int = GetAndroidApiLevel();
|
||||||
|
if (sdk_int >= __ANDROID_API_P__) {
|
||||||
|
initialized_status = 15;
|
||||||
|
} else if (sdk_int == __ANDROID_API_O_MR1__) {
|
||||||
|
initialized_status = 11;
|
||||||
|
} else {
|
||||||
|
initialized_status = 10;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +139,37 @@ public:
|
|||||||
if (GetClassDefSym) return GetClassDef(this);
|
if (GetClassDefSym) return GetClassDef(this);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto PopBackup(const dex::ClassDef *class_def, art::Thread *self) {
|
||||||
|
BackupMethods methods;
|
||||||
|
if (!backup_methods_.size()) [[likely]] {
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
if (class_def) {
|
||||||
|
std::unique_lock lk(backup_methods_lock_);
|
||||||
|
for (auto it = backup_methods_.begin(); it != backup_methods_.end();) {
|
||||||
|
if (auto found = it->second.find(class_def); found != it->second.end()) {
|
||||||
|
methods.merge(std::move(found->second));
|
||||||
|
it->second.erase(found);
|
||||||
|
}
|
||||||
|
if (it->second.empty()) {
|
||||||
|
backup_methods_.erase(it++);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!class_def && self) {
|
||||||
|
std::unique_lock lk(backup_methods_lock_);
|
||||||
|
if (auto found = backup_methods_.find(self); found != backup_methods_.end()) {
|
||||||
|
for (auto it = found->second.begin(); it != found->second.end();) {
|
||||||
|
methods.merge(std::move(it->second));
|
||||||
|
found->second.erase(it++);
|
||||||
|
}
|
||||||
|
backup_methods_.erase(found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mirror
|
} // namespace mirror
|
||||||
|
@ -64,41 +64,18 @@ private:
|
|||||||
(art::ClassLinker * thiz, art::Thread *self, art::ArtMethod *method),
|
(art::ClassLinker * thiz, art::Thread *self, art::ArtMethod *method),
|
||||||
{ return backup(thiz, self, MayGetBackup(method)); });
|
{ return backup(thiz, self, MayGetBackup(method)); });
|
||||||
|
|
||||||
static auto GetBackupMethods(mirror::Class *mirror_class) {
|
static auto RestoreBackup(const dex::ClassDef *class_def, art::Thread *self) {
|
||||||
std::list<std::tuple<art::ArtMethod *, void *>> out;
|
auto methods = mirror::Class::PopBackup(class_def, self);
|
||||||
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())
|
|
||||||
[[unlikely]] {
|
|
||||||
LOGV("Before fixup %s, backup %zu hooked methods' trampoline",
|
|
||||||
mirror_class->GetDescriptor().c_str(), found->second.size());
|
|
||||||
for (auto method : found->second) {
|
|
||||||
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]] {
|
|
||||||
LOGV("Before fixup %s, backup %zu deoptimized methods' trampoline",
|
|
||||||
mirror_class->GetDescriptor().c_str(), found->second.size());
|
|
||||||
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) {
|
for (const auto &[art_method, old_trampoline] : methods) {
|
||||||
auto new_trampoline = art_method->GetEntryPoint();
|
auto new_trampoline = art_method->GetEntryPoint();
|
||||||
art_method->SetEntryPoint(old_trampoline);
|
art_method->SetEntryPoint(old_trampoline);
|
||||||
if (IsDeoptimized(art_method)) continue;
|
if (IsDeoptimized(art_method)) {
|
||||||
|
if (new_trampoline != old_trampoline) [[unlikely]] {
|
||||||
|
LOGV("prevent deoptimized method %s from being overwritten",
|
||||||
|
art_method->PrettyMethod(true).data());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (auto backup_method = IsHooked(art_method); backup_method) [[likely]] {
|
if (auto backup_method = IsHooked(art_method); backup_method) [[likely]] {
|
||||||
if (new_trampoline != old_trampoline) [[unlikely]] {
|
if (new_trampoline != old_trampoline) [[unlikely]] {
|
||||||
LOGV("propagate entrypoint for %s", backup_method->PrettyMethod(true).data());
|
LOGV("propagate entrypoint for %s", backup_method->PrettyMethod(true).data());
|
||||||
@ -111,28 +88,32 @@ private:
|
|||||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||||
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", void,
|
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", void,
|
||||||
FixupStaticTrampolines, (ClassLinker * thiz, ObjPtr<mirror::Class> mirror_class), {
|
FixupStaticTrampolines, (ClassLinker * thiz, ObjPtr<mirror::Class> mirror_class), {
|
||||||
auto backup_methods = GetBackupMethods(mirror_class);
|
|
||||||
backup(thiz, mirror_class);
|
backup(thiz, mirror_class);
|
||||||
FixTrampoline(backup_methods);
|
RestoreBackup(mirror_class->GetClassDef(), nullptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||||
"_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE",
|
"_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE",
|
||||||
void, FixupStaticTrampolinesWithThread,
|
void, FixupStaticTrampolinesWithThread,
|
||||||
(ClassLinker * thiz, art::Thread *self, ObjPtr<mirror::Class> mirror_class), {
|
(ClassLinker * thiz, art::Thread *self, ObjPtr<mirror::Class> mirror_class), {
|
||||||
auto backup_methods = GetBackupMethods(mirror_class);
|
|
||||||
backup(thiz, self, mirror_class);
|
backup(thiz, self, mirror_class);
|
||||||
FixTrampoline(backup_methods);
|
RestoreBackup(mirror_class->GetClassDef(), self);
|
||||||
});
|
});
|
||||||
|
|
||||||
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6mirror5ClassE",
|
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6mirror5ClassE",
|
||||||
void, FixupStaticTrampolinesRaw,
|
void, FixupStaticTrampolinesRaw,
|
||||||
(ClassLinker * thiz, mirror::Class *mirror_class), {
|
(ClassLinker * thiz, mirror::Class *mirror_class), {
|
||||||
auto backup_methods = GetBackupMethods(mirror_class);
|
|
||||||
backup(thiz, mirror_class);
|
backup(thiz, mirror_class);
|
||||||
FixTrampoline(backup_methods);
|
RestoreBackup(mirror_class->GetClassDef(), nullptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||||
|
"_ZN3art11ClassLinker26VisiblyInitializedCallback29AdjustThreadVisibilityCounterEPNS_6ThreadEl",
|
||||||
|
void, AdjustThreadVisibilityCounter, (void *thiz, art::Thread *self, ssize_t adjustment), {
|
||||||
|
backup(thiz, self, adjustment);
|
||||||
|
RestoreBackup(nullptr, self);
|
||||||
|
});
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool Init(const HookHandler &handler) {
|
static bool Init(const HookHandler &handler) {
|
||||||
if (!HookSyms(handler, FixupStaticTrampolinesWithThread, FixupStaticTrampolines,
|
if (!HookSyms(handler, FixupStaticTrampolinesWithThread, FixupStaticTrampolines,
|
||||||
@ -147,6 +128,9 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fixup static trampoline may have been inlined
|
||||||
|
HookSyms(handler, AdjustThreadVisibilityCounter);
|
||||||
|
|
||||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(
|
if (!RETRIEVE_MEM_FUNC_SYMBOL(
|
||||||
SetEntryPointsToInterpreter,
|
SetEntryPointsToInterpreter,
|
||||||
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"))
|
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"))
|
||||||
|
27
lsplant/src/main/jni/art/runtime/handle.hpp
Normal file
27
lsplant/src/main/jni/art/runtime/handle.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "object_reference.hpp"
|
||||||
|
#include "value_object.hpp"
|
||||||
|
|
||||||
|
namespace lsplant::art {
|
||||||
|
|
||||||
|
namespace mirror {
|
||||||
|
class Class;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Handle : public ValueObject {
|
||||||
|
public:
|
||||||
|
static_assert(std::is_same_v<T, mirror::Class>, "Expected mirror::Class");
|
||||||
|
|
||||||
|
auto operator->() { return Get(); }
|
||||||
|
|
||||||
|
T *Get() { return down_cast<T *>(reference_->AsMirrorPtr()); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
StackReference<T> *reference_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lsplant::art
|
32
lsplant/src/main/jni/art/runtime/object_reference.hpp
Normal file
32
lsplant/src/main/jni/art/runtime/object_reference.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace lsplant::art {
|
||||||
|
template <bool kPoisonReferences, class MirrorType>
|
||||||
|
class alignas(4) [[gnu::packed]] ObjectReference {
|
||||||
|
static MirrorType* Decompress(uint32_t ref) {
|
||||||
|
uintptr_t as_bits = kPoisonReferences ? -ref : ref;
|
||||||
|
return reinterpret_cast<MirrorType*>(as_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t reference_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MirrorType* AsMirrorPtr() const { return Decompress(reference_); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class MirrorType>
|
||||||
|
class alignas(4) [[gnu::packed]] CompressedReference : public ObjectReference<false, MirrorType> {};
|
||||||
|
|
||||||
|
template <class MirrorType>
|
||||||
|
class alignas(4) [[gnu::packed]] StackReference : public CompressedReference<MirrorType> {};
|
||||||
|
|
||||||
|
template <typename To, typename From> // use like this: down_cast<T*>(foo);
|
||||||
|
inline To down_cast(From* f) { // so we only accept pointers
|
||||||
|
static_assert(std::is_base_of_v<From, std::remove_pointer_t<To>>,
|
||||||
|
"down_cast unsafe as To is not a subtype of From");
|
||||||
|
|
||||||
|
return static_cast<To>(f);
|
||||||
|
}
|
||||||
|
} // namespace lsplant::art
|
@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "reflective_reference.hpp"
|
#include "reflective_reference.hpp"
|
||||||
|
#include "value_object.hpp"
|
||||||
|
|
||||||
namespace lsplant::art {
|
namespace lsplant::art {
|
||||||
|
|
||||||
class ArtMethod;
|
class ArtMethod;
|
||||||
class ValueObject {};
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class ReflectiveHandle : public ValueObject {
|
class ReflectiveHandle : public ValueObject {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
namespace lsplant::art {
|
namespace lsplant::art {
|
||||||
template <class ReflectiveType>
|
template <class ReflectiveType>
|
||||||
class ReflectiveReference {
|
class ReflectiveReference {
|
||||||
|
5
lsplant/src/main/jni/art/runtime/value_object.hpp
Normal file
5
lsplant/src/main/jni/art/runtime/value_object.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace lsplant::art {
|
||||||
|
class ValueObject {};
|
||||||
|
} // namespace lsplant::art
|
Loading…
x
Reference in New Issue
Block a user