mirror of
https://github.com/LSPosed/LSPlant.git
synced 2025-05-04 20:42:02 +08:00
Modularize codes
This commit is contained in:
parent
386200f0c9
commit
2db8b66b29
@ -1,27 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := lsplant
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/external/dex_builder/include
|
||||
LOCAL_SRC_FILES := lsplant.cc
|
||||
LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH)/include
|
||||
LOCAL_SHARED_LIBRARIES := dex_builder
|
||||
LOCAL_LDLIBS := -llog
|
||||
LOCAL_EXPORT_LDLIBS := $(LOCAL_LDLIBS)
|
||||
LOCAL_CFLAGS := -flto
|
||||
LOCAL_LDFLAGS := -flto
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := lsplant_static
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/external/dex_builder/include
|
||||
LOCAL_SRC_FILES := lsplant.cc
|
||||
LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH)/include
|
||||
LOCAL_STATIC_LIBRARIES := dex_builder_static
|
||||
LOCAL_EXPORT_LDLIBS := -llog
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include jni/external/dex_builder/Android.mk
|
||||
|
@ -1,14 +0,0 @@
|
||||
APP_CFLAGS := -Wall -Wextra
|
||||
APP_CFLAGS += -fno-stack-protector -fomit-frame-pointer
|
||||
APP_CFLAGS += -Wno-builtin-macro-redefined -D__FILE__=__FILE_NAME__ -Wno-gnu-string-literal-operator-template
|
||||
APP_CPPFLAGS := -std=c++20
|
||||
APP_CONLYFLAGS := -std=c18
|
||||
APP_STL := c++_shared
|
||||
|
||||
ifneq ($(NDK_DEBUG),1)
|
||||
APP_CFLAGS += -Oz
|
||||
APP_CFLAGS += -Wno-unused -Wno-unused-parameter -Werror
|
||||
APP_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
|
||||
APP_CFLAGS += -fno-unwind-tables -fno-asynchronous-unwind-tables
|
||||
APP_LDFLAGS += -Wl,--exclude-libs,ALL -Wl,--gc-sections -Wl,--strip-all
|
||||
endif
|
@ -1,8 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
cmake_minimum_required(VERSION 3.28)
|
||||
project(lsplant)
|
||||
|
||||
find_program(CCACHE ccache)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
|
||||
|
||||
if (CCACHE)
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
|
||||
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
|
||||
@ -13,9 +16,8 @@ if (LSPLANT_STANDALONE)
|
||||
link_libraries(cxx::cxx)
|
||||
endif()
|
||||
|
||||
add_definitions(-std=c++20)
|
||||
|
||||
set(SOURCES lsplant.cc)
|
||||
file(GLOB_RECURSE MODULE_SOURCES "*.cxx")
|
||||
|
||||
set(DEX_BUILDER_BUILD_SHARED OFF CACHE INTERNAL "" FORCE)
|
||||
add_subdirectory(external/dex_builder)
|
||||
@ -25,6 +27,7 @@ option(LSPLANT_BUILD_SHARED "If ON, lsplant will also build shared library" ON)
|
||||
if (LSPLANT_BUILD_SHARED)
|
||||
message(STATUS "Building lsplant as a shared library")
|
||||
add_library(${PROJECT_NAME} SHARED ${SOURCES})
|
||||
target_sources(${PROJECT_NAME} PRIVATE FILE_SET CXX_MODULES FILES ${MODULE_SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -flto)
|
||||
@ -40,6 +43,7 @@ if (LSPLANT_BUILD_SHARED)
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME}_static STATIC ${SOURCES})
|
||||
target_sources(${PROJECT_NAME}_static PRIVATE FILE_SET CXX_MODULES FILES ${MODULE_SOURCES})
|
||||
target_include_directories(${PROJECT_NAME}_static PUBLIC include)
|
||||
target_include_directories(${PROJECT_NAME}_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
@ -1,18 +1,20 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "art/runtime/art_method.hpp"
|
||||
#include "art/runtime/handle.hpp"
|
||||
#include "common.hpp"
|
||||
#include <parallel_hashmap/phmap.h>
|
||||
|
||||
namespace lsplant::art {
|
||||
class Thread;
|
||||
namespace dex {
|
||||
class ClassDef {};
|
||||
} // namespace dex
|
||||
#include "logging.hpp"
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
namespace mirror {
|
||||
export module clazz;
|
||||
|
||||
class Class {
|
||||
import common;
|
||||
import art_method;
|
||||
import thread;
|
||||
import handle;
|
||||
|
||||
namespace lsplant::art::mirror {
|
||||
|
||||
export class Class {
|
||||
private:
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(const char *, GetDescriptor, Class *thiz, std::string *storage) {
|
||||
if (GetDescriptorSym) [[likely]]
|
||||
@ -184,5 +186,4 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mirror
|
||||
} // namespace lsplant::art
|
||||
} // namespace lsplant::art::mirror
|
@ -1,11 +1,20 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "art/mirror/class.hpp"
|
||||
#include "common.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
#define JNI_HELPER_NO_NS
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
export module art_method;
|
||||
|
||||
import common;
|
||||
|
||||
namespace lsplant::art {
|
||||
namespace mirror {
|
||||
class Class;
|
||||
}
|
||||
|
||||
class ArtMethod {
|
||||
export class ArtMethod {
|
||||
CREATE_FUNC_SYMBOL_ENTRY(std::string, PrettyMethod, ArtMethod *thiz, bool with_signature) {
|
||||
if (thiz == nullptr) [[unlikely]]
|
||||
return "null";
|
||||
@ -274,7 +283,8 @@ public:
|
||||
LOGE("Failed to find Executable.getName");
|
||||
return false;
|
||||
}
|
||||
RETRIEVE_MEM_FUNC_SYMBOL(ThrowInvocationTimeError, "_ZN3art9ArtMethod24ThrowInvocationTimeErrorEv");
|
||||
RETRIEVE_MEM_FUNC_SYMBOL(ThrowInvocationTimeError,
|
||||
"_ZN3art9ArtMethod24ThrowInvocationTimeErrorEv");
|
||||
auto abstract_method = FromReflectedMethod(
|
||||
env, JNI_ToReflectedMethod(env, executable, executable_get_name, false));
|
||||
uint32_t access_flags = abstract_method->GetAccessFlags();
|
@ -1,12 +1,18 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "art/runtime/art_method.hpp"
|
||||
#include "art/runtime/obj_ptr.hpp"
|
||||
#include "art/runtime/thread.hpp"
|
||||
#include "common.hpp"
|
||||
#include "include/utils/hook_helper.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
export module class_linker;
|
||||
|
||||
import art_method;
|
||||
import thread;
|
||||
import common;
|
||||
import clazz;
|
||||
import handle;
|
||||
|
||||
namespace lsplant::art {
|
||||
class ClassLinker {
|
||||
export class ClassLinker {
|
||||
private:
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetEntryPointsToInterpreter, ClassLinker *thiz,
|
||||
ArtMethod *art_method) {
|
||||
@ -16,14 +22,13 @@ private:
|
||||
}
|
||||
|
||||
CREATE_HOOK_STUB_ENTRY(
|
||||
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv", bool,
|
||||
ShouldUseInterpreterEntrypoint, (ArtMethod * art_method, const void *quick_code), {
|
||||
if (quick_code != nullptr && IsHooked(art_method)) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
return backup(art_method, quick_code);
|
||||
});
|
||||
|
||||
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv", bool,
|
||||
ShouldUseInterpreterEntrypoint, (ArtMethod * art_method, const void *quick_code), {
|
||||
if (quick_code != nullptr && IsHooked(art_method)) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
return backup(art_method, quick_code);
|
||||
});
|
||||
|
||||
CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_to_interpreter_bridge, void *) {}
|
||||
|
||||
@ -130,10 +135,11 @@ private:
|
||||
|
||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||
"_ZN3art11ClassLinker26VisiblyInitializedCallback22MarkVisiblyInitializedEPNS_6ThreadE",
|
||||
void, MarkVisiblyInitialized, (void *thiz, Thread* self), {
|
||||
void, MarkVisiblyInitialized, (void *thiz, Thread *self), {
|
||||
backup(thiz, self);
|
||||
RestoreBackup(nullptr, self);
|
||||
});
|
||||
|
||||
public:
|
||||
static bool Init(const HookHandler &handler) {
|
||||
int sdk_int = GetAndroidApiLevel();
|
||||
@ -155,7 +161,7 @@ public:
|
||||
}
|
||||
|
||||
if (sdk_int >= __ANDROID_API_R__) {
|
||||
if constexpr (GetArch() != Arch::kX86 && GetArch() != Arch::kX86_64) {
|
||||
if constexpr (kArch != Arch::kX86 && kArch != Arch::kX86_64) {
|
||||
// fixup static trampoline may have been inlined
|
||||
HookSyms(handler, AdjustThreadVisibilityCounter, MarkVisiblyInitialized);
|
||||
}
|
@ -1,13 +1,18 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
export module dex_file;
|
||||
|
||||
import common;
|
||||
|
||||
namespace lsplant::art {
|
||||
class DexFile {
|
||||
export class DexFile {
|
||||
struct Header {
|
||||
[[maybe_unused]] uint8_t magic_[8];
|
||||
uint32_t checksum_; // See also location_checksum_
|
@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace lsplant::art::gc {
|
||||
// Which types of collections are able to be performed.
|
||||
enum CollectorType {
|
||||
// No collector selected.
|
||||
kCollectorTypeNone,
|
||||
// Non concurrent mark-sweep.
|
||||
kCollectorTypeMS,
|
||||
// Concurrent mark-sweep.
|
||||
kCollectorTypeCMS,
|
||||
// Semi-space / mark-sweep hybrid, enables compaction.
|
||||
kCollectorTypeSS,
|
||||
// Heap trimming collector, doesn't do any actual collecting.
|
||||
kCollectorTypeHeapTrim,
|
||||
// A (mostly) concurrent copying collector.
|
||||
kCollectorTypeCC,
|
||||
// The background compaction of the concurrent copying collector.
|
||||
kCollectorTypeCCBackground,
|
||||
// Instrumentation critical section fake collector.
|
||||
kCollectorTypeInstrumentation,
|
||||
// Fake collector for adding or removing application image spaces.
|
||||
kCollectorTypeAddRemoveAppImageSpace,
|
||||
// Fake collector used to implement exclusion between GC and debugger.
|
||||
kCollectorTypeDebugger,
|
||||
// A homogeneous space compaction collector used in background transition
|
||||
// when both foreground and background collector are CMS.
|
||||
kCollectorTypeHomogeneousSpaceCompact,
|
||||
// Class linker fake collector.
|
||||
kCollectorTypeClassLinker,
|
||||
// JIT Code cache fake collector.
|
||||
kCollectorTypeJitCodeCache,
|
||||
// Hprof fake collector.
|
||||
kCollectorTypeHprof,
|
||||
// Fake collector for installing/removing a system-weak holder.
|
||||
kCollectorTypeAddRemoveSystemWeakHolder,
|
||||
// Fake collector type for GetObjectsAllocated
|
||||
kCollectorTypeGetObjectsAllocated,
|
||||
// Fake collector type for ScopedGCCriticalSection
|
||||
kCollectorTypeCriticalSection,
|
||||
};
|
||||
} // namespace lsplant::art::gc
|
@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
namespace lsplant::art::gc {
|
||||
// What caused the GC?
|
||||
enum GcCause {
|
||||
// Invalid GC cause used as a placeholder.
|
||||
kGcCauseNone,
|
||||
// GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before
|
||||
// retrying allocation.
|
||||
kGcCauseForAlloc,
|
||||
// A background GC trying to ensure there is free memory ahead of allocations.
|
||||
kGcCauseBackground,
|
||||
// An explicit System.gc() call.
|
||||
kGcCauseExplicit,
|
||||
// GC triggered for a native allocation when NativeAllocationGcWatermark is exceeded.
|
||||
// (This may be a blocking GC depending on whether we run a non-concurrent collector).
|
||||
kGcCauseForNativeAlloc,
|
||||
// GC triggered for a collector transition.
|
||||
kGcCauseCollectorTransition,
|
||||
// Not a real GC cause, used when we disable moving GC (currently for
|
||||
// GetPrimitiveArrayCritical).
|
||||
kGcCauseDisableMovingGc,
|
||||
// Not a real GC cause, used when we trim the heap.
|
||||
kGcCauseTrim,
|
||||
// Not a real GC cause, used to implement exclusion between GC and instrumentation.
|
||||
kGcCauseInstrumentation,
|
||||
// Not a real GC cause, used to add or remove app image spaces.
|
||||
kGcCauseAddRemoveAppImageSpace,
|
||||
// Not a real GC cause, used to implement exclusion between GC and debugger.
|
||||
kGcCauseDebugger,
|
||||
// GC triggered for background transition when both foreground and background collector are CMS.
|
||||
kGcCauseHomogeneousSpaceCompact,
|
||||
// Class linker cause, used to guard filling art methods with special values.
|
||||
kGcCauseClassLinker,
|
||||
// Not a real GC cause, used to implement exclusion between code cache metadata and GC.
|
||||
kGcCauseJitCodeCache,
|
||||
// Not a real GC cause, used to add or remove system-weak holders.
|
||||
kGcCauseAddRemoveSystemWeakHolder,
|
||||
// Not a real GC cause, used to prevent hprof running in the middle of GC.
|
||||
kGcCauseHprof,
|
||||
// Not a real GC cause, used to prevent GetObjectsAllocated running in the middle of GC.
|
||||
kGcCauseGetObjectsAllocated,
|
||||
// GC cause for the profile saver.
|
||||
kGcCauseProfileSaver,
|
||||
};
|
||||
} // namespace lsplant::art::gc
|
@ -0,0 +1,139 @@
|
||||
module;
|
||||
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
export module scope_gc_critical_section;
|
||||
|
||||
import thread;
|
||||
import common;
|
||||
|
||||
namespace lsplant::art::gc {
|
||||
// Which types of collections are able to be performed.
|
||||
enum CollectorType {
|
||||
// No collector selected.
|
||||
kCollectorTypeNone,
|
||||
// Non concurrent mark-sweep.
|
||||
kCollectorTypeMS,
|
||||
// Concurrent mark-sweep.
|
||||
kCollectorTypeCMS,
|
||||
// Semi-space / mark-sweep hybrid, enables compaction.
|
||||
kCollectorTypeSS,
|
||||
// Heap trimming collector, doesn't do any actual collecting.
|
||||
kCollectorTypeHeapTrim,
|
||||
// A (mostly) concurrent copying collector.
|
||||
kCollectorTypeCC,
|
||||
// The background compaction of the concurrent copying collector.
|
||||
kCollectorTypeCCBackground,
|
||||
// Instrumentation critical section fake collector.
|
||||
kCollectorTypeInstrumentation,
|
||||
// Fake collector for adding or removing application image spaces.
|
||||
kCollectorTypeAddRemoveAppImageSpace,
|
||||
// Fake collector used to implement exclusion between GC and debugger.
|
||||
kCollectorTypeDebugger,
|
||||
// A homogeneous space compaction collector used in background transition
|
||||
// when both foreground and background collector are CMS.
|
||||
kCollectorTypeHomogeneousSpaceCompact,
|
||||
// Class linker fake collector.
|
||||
kCollectorTypeClassLinker,
|
||||
// JIT Code cache fake collector.
|
||||
kCollectorTypeJitCodeCache,
|
||||
// Hprof fake collector.
|
||||
kCollectorTypeHprof,
|
||||
// Fake collector for installing/removing a system-weak holder.
|
||||
kCollectorTypeAddRemoveSystemWeakHolder,
|
||||
// Fake collector type for GetObjectsAllocated
|
||||
kCollectorTypeGetObjectsAllocated,
|
||||
// Fake collector type for ScopedGCCriticalSection
|
||||
kCollectorTypeCriticalSection,
|
||||
};
|
||||
|
||||
// What caused the GC?
|
||||
enum GcCause {
|
||||
// Invalid GC cause used as a placeholder.
|
||||
kGcCauseNone,
|
||||
// GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before
|
||||
// retrying allocation.
|
||||
kGcCauseForAlloc,
|
||||
// A background GC trying to ensure there is free memory ahead of allocations.
|
||||
kGcCauseBackground,
|
||||
// An explicit System.gc() call.
|
||||
kGcCauseExplicit,
|
||||
// GC triggered for a native allocation when NativeAllocationGcWatermark is exceeded.
|
||||
// (This may be a blocking GC depending on whether we run a non-concurrent collector).
|
||||
kGcCauseForNativeAlloc,
|
||||
// GC triggered for a collector transition.
|
||||
kGcCauseCollectorTransition,
|
||||
// Not a real GC cause, used when we disable moving GC (currently for
|
||||
// GetPrimitiveArrayCritical).
|
||||
kGcCauseDisableMovingGc,
|
||||
// Not a real GC cause, used when we trim the heap.
|
||||
kGcCauseTrim,
|
||||
// Not a real GC cause, used to implement exclusion between GC and instrumentation.
|
||||
kGcCauseInstrumentation,
|
||||
// Not a real GC cause, used to add or remove app image spaces.
|
||||
kGcCauseAddRemoveAppImageSpace,
|
||||
// Not a real GC cause, used to implement exclusion between GC and debugger.
|
||||
kGcCauseDebugger,
|
||||
// GC triggered for background transition when both foreground and background collector are CMS.
|
||||
kGcCauseHomogeneousSpaceCompact,
|
||||
// Class linker cause, used to guard filling art methods with special values.
|
||||
kGcCauseClassLinker,
|
||||
// Not a real GC cause, used to implement exclusion between code cache metadata and GC.
|
||||
kGcCauseJitCodeCache,
|
||||
// Not a real GC cause, used to add or remove system-weak holders.
|
||||
kGcCauseAddRemoveSystemWeakHolder,
|
||||
// Not a real GC cause, used to prevent hprof running in the middle of GC.
|
||||
kGcCauseHprof,
|
||||
// Not a real GC cause, used to prevent GetObjectsAllocated running in the middle of GC.
|
||||
kGcCauseGetObjectsAllocated,
|
||||
// GC cause for the profile saver.
|
||||
kGcCauseProfileSaver,
|
||||
};
|
||||
|
||||
export class GCCriticalSection {
|
||||
private:
|
||||
[[maybe_unused]] void *self_;
|
||||
[[maybe_unused]] const char *section_name_;
|
||||
};
|
||||
|
||||
export class ScopedGCCriticalSection {
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, ScopedGCCriticalSection *thiz, Thread *self,
|
||||
GcCause cause, CollectorType collector_type) {
|
||||
if (thiz && constructorSym) [[likely]]
|
||||
return constructorSym(thiz, self, cause, collector_type);
|
||||
}
|
||||
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, ScopedGCCriticalSection *thiz) {
|
||||
if (thiz && destructorSym) [[likely]]
|
||||
destructorSym(thiz);
|
||||
}
|
||||
|
||||
public:
|
||||
ScopedGCCriticalSection(Thread *self, GcCause cause, CollectorType collector_type) {
|
||||
constructor(this, self, cause, collector_type);
|
||||
}
|
||||
|
||||
~ScopedGCCriticalSection() { destructor(this); }
|
||||
|
||||
static bool Init(const HookHandler &handler) {
|
||||
// for Android M, it's safe to not found since we have suspendVM & resumeVM
|
||||
auto sdk_int = GetAndroidApiLevel();
|
||||
if (sdk_int >= __ANDROID_API_N__) [[likely]] {
|
||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor,
|
||||
"_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_"
|
||||
"7GcCauseENS0_13CollectorTypeE")) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art2gc23ScopedGCCriticalSectionD2Ev"))
|
||||
[[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
[[maybe_unused]] GCCriticalSection critical_section_;
|
||||
[[maybe_unused]] const char *old_no_suspend_reason_;
|
||||
};
|
||||
} // namespace lsplant::art::gc
|
@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "art/runtime/thread.hpp"
|
||||
#include "collector_type.hpp"
|
||||
#include "common.hpp"
|
||||
#include "gc_cause.hpp"
|
||||
|
||||
namespace lsplant::art::gc {
|
||||
|
||||
class GCCriticalSection {
|
||||
private:
|
||||
[[maybe_unused]] void *self_;
|
||||
[[maybe_unused]] const char *section_name_;
|
||||
};
|
||||
|
||||
class ScopedGCCriticalSection {
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, ScopedGCCriticalSection *thiz, Thread *self,
|
||||
GcCause cause, CollectorType collector_type) {
|
||||
if (thiz && constructorSym) [[likely]]
|
||||
return constructorSym(thiz, self, cause, collector_type);
|
||||
}
|
||||
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, ScopedGCCriticalSection *thiz) {
|
||||
if (thiz && destructorSym) [[likely]]
|
||||
destructorSym(thiz);
|
||||
}
|
||||
|
||||
public:
|
||||
ScopedGCCriticalSection(Thread *self, GcCause cause, CollectorType collector_type) {
|
||||
constructor(this, self, cause, collector_type);
|
||||
}
|
||||
|
||||
~ScopedGCCriticalSection() { destructor(this); }
|
||||
|
||||
static bool Init(const HookHandler &handler) {
|
||||
// for Android M, it's safe to not found since we have suspendVM & resumeVM
|
||||
auto sdk_int = GetAndroidApiLevel();
|
||||
if (sdk_int >= __ANDROID_API_N__) [[likely]] {
|
||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor,
|
||||
"_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_"
|
||||
"7GcCauseENS0_13CollectorTypeE")) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art2gc23ScopedGCCriticalSectionD2Ev"))
|
||||
[[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
[[maybe_unused]] GCCriticalSection critical_section_;
|
||||
[[maybe_unused]] const char *old_no_suspend_reason_;
|
||||
};
|
||||
} // namespace lsplant::art::gc
|
117
lsplant/src/main/jni/art/runtime/handle.cxx
Normal file
117
lsplant/src/main/jni/art/runtime/handle.cxx
Normal file
@ -0,0 +1,117 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
export module handle;
|
||||
|
||||
import art_method;
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
export {
|
||||
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_;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
class ValueObject {};
|
||||
|
||||
template <class ReflectiveType>
|
||||
class ReflectiveReference {
|
||||
public:
|
||||
static_assert(std::is_same_v<ReflectiveType, ArtMethod>, "Unknown type!");
|
||||
|
||||
ReflectiveType *Ptr() { return val_; }
|
||||
|
||||
void Assign(ReflectiveType *r) { val_ = r; }
|
||||
|
||||
private:
|
||||
ReflectiveType *val_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ReflectiveHandle : public ValueObject {
|
||||
public:
|
||||
static_assert(std::is_same_v<T, ArtMethod>, "Expected ArtField or ArtMethod");
|
||||
|
||||
T *Get() { return reference_->Ptr(); }
|
||||
|
||||
void Set(T *val) { reference_->Assign(val); }
|
||||
|
||||
protected:
|
||||
ReflectiveReference<T> *reference_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Handle : public ValueObject {
|
||||
public:
|
||||
Handle(const Handle<T> &handle) : reference_(handle.reference_) {}
|
||||
|
||||
Handle<T> &operator=(const Handle<T> &handle) {
|
||||
reference_ = handle.reference_;
|
||||
return *this;
|
||||
}
|
||||
// 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_;
|
||||
};
|
||||
|
||||
// static_assert(!std::is_trivially_copyable_v<Handle<mirror::Class>>);
|
||||
|
||||
// https://cs.android.com/android/_/android/platform/art/+/38cea84b362a10859580e788e984324f36272817
|
||||
template <typename T>
|
||||
class TrivialHandle : 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
|
@ -1,50 +0,0 @@
|
||||
#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:
|
||||
Handle(const Handle<T>& handle) : reference_(handle.reference_) {}
|
||||
|
||||
Handle<T>& operator=(const Handle<T>& handle) {
|
||||
reference_ = handle.reference_;
|
||||
return *this;
|
||||
}
|
||||
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_;
|
||||
};
|
||||
|
||||
static_assert(!std::is_trivially_copyable_v<Handle<mirror::Class>>);
|
||||
|
||||
// https://cs.android.com/android/_/android/platform/art/+/38cea84b362a10859580e788e984324f36272817
|
||||
template <typename T>
|
||||
class TrivialHandle : 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_;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<TrivialHandle<mirror::Class>>);
|
||||
|
||||
} // namespace lsplant::art
|
@ -1,11 +1,16 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "art_method.hpp"
|
||||
#include "common.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
export module instrumentation;
|
||||
|
||||
import art_method;
|
||||
import common;
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
class Instrumentation {
|
||||
export class Instrumentation {
|
||||
inline static ArtMethod *MaybeUseBackupMethod(ArtMethod *art_method, const void *quick_code) {
|
||||
if (auto backup = IsHooked(art_method); backup && art_method->GetEntryPoint() != quick_code)
|
||||
[[unlikely]] {
|
@ -1,9 +1,16 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "common.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
export module jit_code_cache;
|
||||
|
||||
import art_method;
|
||||
import common;
|
||||
import thread;
|
||||
|
||||
namespace lsplant::art::jit {
|
||||
class JitCodeCache {
|
||||
export class JitCodeCache {
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, MoveObsoleteMethod, JitCodeCache *thiz,
|
||||
ArtMethod *old_method, ArtMethod *new_method) {
|
||||
if (MoveObsoleteMethodSym) [[likely]] {
|
@ -1,12 +1,17 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "art/runtime/art_method.hpp"
|
||||
#include "art/runtime/reflective_handle.hpp"
|
||||
#include "common.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
export module jni_id_manager;
|
||||
|
||||
import art_method;
|
||||
import common;
|
||||
import handle;
|
||||
|
||||
namespace lsplant::art::jni {
|
||||
|
||||
class JniIdManager {
|
||||
export class JniIdManager {
|
||||
private:
|
||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||
"_ZN3art3jni12JniIdManager15EncodeGenericIdINS_9ArtMethodEEEmNS_16ReflectiveHandleIT_EE",
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
} // namespace lsplant::art
|
@ -1,32 +0,0 @@
|
||||
#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,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "reflective_reference.hpp"
|
||||
#include "value_object.hpp"
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
class ArtMethod;
|
||||
|
||||
template <typename T>
|
||||
class ReflectiveHandle : public ValueObject {
|
||||
public:
|
||||
static_assert(std::is_same_v<T, ArtMethod>, "Expected ArtField or ArtMethod");
|
||||
|
||||
T *Get() { return reference_->Ptr(); }
|
||||
|
||||
void Set(T *val) { reference_->Assign(val); }
|
||||
|
||||
protected:
|
||||
ReflectiveReference<T> *reference_;
|
||||
};
|
||||
|
||||
} // namespace lsplant::art
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace lsplant::art {
|
||||
template <class ReflectiveType>
|
||||
class ReflectiveReference {
|
||||
public:
|
||||
static_assert(std::is_same_v<ReflectiveType, ArtMethod>, "Unknown type!");
|
||||
|
||||
ReflectiveType *Ptr() { return val_; }
|
||||
|
||||
void Assign(ReflectiveType *r) { val_ = r; }
|
||||
|
||||
private:
|
||||
ReflectiveType *val_;
|
||||
};
|
||||
|
||||
} // namespace lsplant::art
|
@ -1,29 +1,15 @@
|
||||
/*
|
||||
* This file is part of LSPosed.
|
||||
*
|
||||
* LSPosed is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LSPosed is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2022 LSPosed Contributors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "logging.hpp"
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
export module runtime;
|
||||
|
||||
import common;
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
class Runtime {
|
||||
export class Runtime {
|
||||
public:
|
||||
enum class RuntimeDebugState {
|
||||
// This doesn't support any debug features / method tracing. This is the expected state
|
@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "common.hpp"
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
export module thread;
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
class Thread {
|
||||
export class Thread {
|
||||
CREATE_FUNC_SYMBOL_ENTRY(Thread *, CurrentFromGdb) {
|
||||
if (CurrentFromGdbSym) [[likely]]
|
||||
return CurrentFromGdbSym();
|
@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
module;
|
||||
|
||||
#include "common.hpp"
|
||||
#include "include/utils/hook_helper.hpp"
|
||||
|
||||
export module thread_list;
|
||||
|
||||
namespace lsplant::art::thread_list {
|
||||
|
||||
class ScopedSuspendAll {
|
||||
export class ScopedSuspendAll {
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, ScopedSuspendAll *thiz, const char *cause,
|
||||
bool long_suspend) {
|
||||
if (thiz && constructorSym) [[likely]] {
|
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace lsplant::art {
|
||||
class ValueObject {};
|
||||
} // namespace lsplant::art
|
178
lsplant/src/main/jni/common.cxx
Normal file
178
lsplant/src/main/jni/common.cxx
Normal file
@ -0,0 +1,178 @@
|
||||
module;
|
||||
|
||||
#include <jni.h>
|
||||
#include <parallel_hashmap/phmap.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#include <list>
|
||||
#include <shared_mutex>
|
||||
#include <string_view>
|
||||
|
||||
#include "logging.hpp"
|
||||
#include "utils/jni_helper.hpp"
|
||||
|
||||
export module common;
|
||||
|
||||
namespace lsplant {
|
||||
|
||||
namespace art {
|
||||
class ArtMethod;
|
||||
namespace mirror {
|
||||
class Class;
|
||||
}
|
||||
namespace dex {
|
||||
export class ClassDef {};
|
||||
} // namespace dex
|
||||
|
||||
} // namespace art
|
||||
|
||||
export enum class Arch {
|
||||
kArm,
|
||||
kArm64,
|
||||
kX86,
|
||||
kX86_64,
|
||||
kRiscv64,
|
||||
};
|
||||
|
||||
consteval inline Arch GetArch() {
|
||||
#if defined(__i386__)
|
||||
return Arch::kX86;
|
||||
#elif defined(__x86_64__)
|
||||
return Arch::kX86_64;
|
||||
#elif defined(__arm__)
|
||||
return Arch::kArm;
|
||||
#elif defined(__aarch64__)
|
||||
return Arch::kArm64;
|
||||
#elif defined(__riscv)
|
||||
return Arch::kRiscv64;
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class K, class V, class Hash = phmap::priv::hash_default_hash<K>,
|
||||
class Eq = phmap::priv::hash_default_eq<K>,
|
||||
class Alloc = phmap::priv::Allocator<phmap::priv::Pair<const K, V>>, size_t N = 4>
|
||||
using SharedHashMap = phmap::parallel_flat_hash_map<K, V, Hash, Eq, Alloc, N, std::shared_mutex>;
|
||||
|
||||
template <class T, class Hash = phmap::priv::hash_default_hash<T>,
|
||||
class Eq = phmap::priv::hash_default_eq<T>, class Alloc = phmap::priv::Allocator<T>,
|
||||
size_t N = 4>
|
||||
using SharedHashSet = phmap::parallel_flat_hash_set<T, Hash, Eq, Alloc, N, std::shared_mutex>;
|
||||
|
||||
export {
|
||||
constexpr auto kArch = GetArch();
|
||||
|
||||
template <typename T>
|
||||
constexpr inline auto RoundUpTo(T v, size_t size) {
|
||||
return v + size - 1 - ((v + size - 1) & (size - 1));
|
||||
}
|
||||
|
||||
inline auto GetAndroidApiLevel() {
|
||||
static auto kApiLevel = []() {
|
||||
std::array<char, PROP_VALUE_MAX> prop_value;
|
||||
__system_property_get("ro.build.version.sdk", prop_value.data());
|
||||
int base = atoi(prop_value.data());
|
||||
__system_property_get("ro.build.version.preview_sdk", prop_value.data());
|
||||
return base + atoi(prop_value.data());
|
||||
}();
|
||||
return kApiLevel;
|
||||
}
|
||||
|
||||
inline auto IsJavaDebuggable(JNIEnv * env) {
|
||||
static auto kDebuggable = [&env]() {
|
||||
auto sdk_int = GetAndroidApiLevel();
|
||||
if (sdk_int < __ANDROID_API_P__) {
|
||||
return false;
|
||||
}
|
||||
auto runtime_class = JNI_FindClass(env, "dalvik/system/VMRuntime");
|
||||
if (!runtime_class) {
|
||||
LOGE("Failed to find VMRuntime");
|
||||
return false;
|
||||
}
|
||||
auto get_runtime_method = JNI_GetStaticMethodID(env, runtime_class, "getRuntime",
|
||||
"()Ldalvik/system/VMRuntime;");
|
||||
if (!get_runtime_method) {
|
||||
LOGE("Failed to find VMRuntime.getRuntime()");
|
||||
return false;
|
||||
}
|
||||
auto is_debuggable_method =
|
||||
JNI_GetMethodID(env, runtime_class, "isJavaDebuggable", "()Z");
|
||||
if (!is_debuggable_method) {
|
||||
LOGE("Failed to find VMRuntime.isJavaDebuggable()");
|
||||
return false;
|
||||
}
|
||||
auto runtime = JNI_CallStaticObjectMethod(env, runtime_class, get_runtime_method);
|
||||
if (!runtime) {
|
||||
LOGE("Failed to get VMRuntime");
|
||||
return false;
|
||||
}
|
||||
bool is_debuggable = JNI_CallBooleanMethod(env, runtime, is_debuggable_method);
|
||||
LOGD("java runtime debuggable %s", is_debuggable ? "true" : "false");
|
||||
return is_debuggable;
|
||||
}();
|
||||
return kDebuggable;
|
||||
}
|
||||
|
||||
constexpr auto kPointerSize = sizeof(void *);
|
||||
|
||||
SharedHashMap<art::ArtMethod *, std::pair<jobject, art::ArtMethod *>> hooked_methods_;
|
||||
|
||||
SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>>
|
||||
hooked_classes_;
|
||||
|
||||
SharedHashSet<art::ArtMethod *> deoptimized_methods_set_;
|
||||
|
||||
SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>>
|
||||
deoptimized_classes_;
|
||||
|
||||
std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> jit_movements_;
|
||||
std::shared_mutex jit_movements_lock_;
|
||||
|
||||
inline art::ArtMethod *IsHooked(art::ArtMethod * art_method, bool including_backup = false) {
|
||||
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) {
|
||||
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) {
|
||||
return deoptimized_methods_set_.contains(art_method);
|
||||
}
|
||||
|
||||
inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> GetJitMovements() {
|
||||
std::unique_lock lk(jit_movements_lock_);
|
||||
return std::move(jit_movements_);
|
||||
}
|
||||
|
||||
inline void RecordHooked(art::ArtMethod * target, const art::dex::ClassDef *class_def,
|
||||
jobject reflected_backup, art::ArtMethod *backup) {
|
||||
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<art::ArtMethod *>{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) {
|
||||
{ deoptimized_classes_[class_def].emplace(art_method); }
|
||||
deoptimized_methods_set_.insert(art_method);
|
||||
}
|
||||
|
||||
inline void RecordJitMovement(art::ArtMethod * target, art::ArtMethod * backup) {
|
||||
std::unique_lock lk(jit_movements_lock_);
|
||||
jit_movements_.emplace_back(target, backup);
|
||||
}
|
||||
}
|
||||
} // namespace lsplant
|
@ -1,176 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <parallel_hashmap/phmap.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#include <list>
|
||||
#include <shared_mutex>
|
||||
#include <string_view>
|
||||
|
||||
#include "logging.hpp"
|
||||
#include "lsplant.hpp"
|
||||
#include "utils/hook_helper.hpp"
|
||||
|
||||
namespace lsplant {
|
||||
|
||||
enum class Arch {
|
||||
kArm,
|
||||
kArm64,
|
||||
kX86,
|
||||
kX86_64,
|
||||
kRiscv64,
|
||||
};
|
||||
|
||||
consteval inline Arch GetArch() {
|
||||
#if defined(__i386__)
|
||||
return Arch::kX86;
|
||||
#elif defined(__x86_64__)
|
||||
return Arch::kX86_64;
|
||||
#elif defined(__arm__)
|
||||
return Arch::kArm;
|
||||
#elif defined(__aarch64__)
|
||||
return Arch::kArm64;
|
||||
#elif defined(__riscv)
|
||||
return Arch::kRiscv64;
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
inline static constexpr auto kArch = GetArch();
|
||||
|
||||
template <typename T>
|
||||
constexpr inline auto RoundUpTo(T v, size_t size) {
|
||||
return v + size - 1 - ((v + size - 1) & (size - 1));
|
||||
}
|
||||
|
||||
inline auto GetAndroidApiLevel() {
|
||||
static auto kApiLevel = []() {
|
||||
std::array<char, PROP_VALUE_MAX> prop_value;
|
||||
__system_property_get("ro.build.version.sdk", prop_value.data());
|
||||
int base = atoi(prop_value.data());
|
||||
__system_property_get("ro.build.version.preview_sdk", prop_value.data());
|
||||
return base + atoi(prop_value.data());
|
||||
}();
|
||||
return kApiLevel;
|
||||
}
|
||||
|
||||
inline auto IsJavaDebuggable(JNIEnv *env) {
|
||||
static auto kDebuggable = [&env]() {
|
||||
auto sdk_int = GetAndroidApiLevel();
|
||||
if (sdk_int < __ANDROID_API_P__) {
|
||||
return false;
|
||||
}
|
||||
auto runtime_class = JNI_FindClass(env, "dalvik/system/VMRuntime");
|
||||
if (!runtime_class) {
|
||||
LOGE("Failed to find VMRuntime");
|
||||
return false;
|
||||
}
|
||||
auto get_runtime_method =
|
||||
JNI_GetStaticMethodID(env, runtime_class, "getRuntime", "()Ldalvik/system/VMRuntime;");
|
||||
if (!get_runtime_method) {
|
||||
LOGE("Failed to find VMRuntime.getRuntime()");
|
||||
return false;
|
||||
}
|
||||
auto is_debuggable_method = JNI_GetMethodID(env, runtime_class, "isJavaDebuggable", "()Z");
|
||||
if (!is_debuggable_method) {
|
||||
LOGE("Failed to find VMRuntime.isJavaDebuggable()");
|
||||
return false;
|
||||
}
|
||||
auto runtime = JNI_CallStaticObjectMethod(env, runtime_class, get_runtime_method);
|
||||
if (!runtime) {
|
||||
LOGE("Failed to get VMRuntime");
|
||||
return false;
|
||||
}
|
||||
bool is_debuggable = JNI_CallBooleanMethod(env, runtime, is_debuggable_method);
|
||||
LOGD("java runtime debuggable %s", is_debuggable ? "true" : "false");
|
||||
return is_debuggable;
|
||||
}();
|
||||
return kDebuggable;
|
||||
}
|
||||
|
||||
inline static constexpr auto kPointerSize = sizeof(void *);
|
||||
|
||||
namespace art {
|
||||
class ArtMethod;
|
||||
namespace dex {
|
||||
class ClassDef;
|
||||
}
|
||||
namespace mirror {
|
||||
class Class;
|
||||
}
|
||||
} // namespace art
|
||||
|
||||
namespace {
|
||||
// target, backup
|
||||
template <class K, class V, class Hash = phmap::priv::hash_default_hash<K>,
|
||||
class Eq = phmap::priv::hash_default_eq<K>,
|
||||
class Alloc = phmap::priv::Allocator<phmap::priv::Pair<const K, V>>, size_t N = 4>
|
||||
using SharedHashMap = phmap::parallel_flat_hash_map<K, V, Hash, Eq, Alloc, N, std::shared_mutex>;
|
||||
|
||||
template <class T, class Hash = phmap::priv::hash_default_hash<T>,
|
||||
class Eq = phmap::priv::hash_default_eq<T>, class Alloc = phmap::priv::Allocator<T>,
|
||||
size_t N = 4>
|
||||
using SharedHashSet = phmap::parallel_flat_hash_set<T, Hash, Eq, Alloc, N, std::shared_mutex>;
|
||||
|
||||
inline SharedHashMap<art::ArtMethod *, std::pair<jobject, art::ArtMethod *>> hooked_methods_;
|
||||
|
||||
inline SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>>
|
||||
hooked_classes_;
|
||||
|
||||
inline SharedHashSet<art::ArtMethod *> deoptimized_methods_set_;
|
||||
|
||||
inline SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>>
|
||||
deoptimized_classes_;
|
||||
|
||||
inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> jit_movements_;
|
||||
inline std::shared_mutex jit_movements_lock_;
|
||||
} // namespace
|
||||
|
||||
inline art::ArtMethod *IsHooked(art::ArtMethod *art_method, bool including_backup = false) {
|
||||
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) {
|
||||
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) {
|
||||
return deoptimized_methods_set_.contains(art_method);
|
||||
}
|
||||
|
||||
inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> GetJitMovements() {
|
||||
std::unique_lock lk(jit_movements_lock_);
|
||||
return std::move(jit_movements_);
|
||||
}
|
||||
|
||||
inline void RecordHooked(art::ArtMethod *target, const art::dex::ClassDef *class_def,
|
||||
jobject reflected_backup, art::ArtMethod *backup) {
|
||||
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<art::ArtMethod *>{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) {
|
||||
{ deoptimized_classes_[class_def].emplace(art_method); }
|
||||
deoptimized_methods_set_.insert(art_method);
|
||||
}
|
||||
|
||||
inline void RecordJitMovement(art::ArtMethod *target, art::ArtMethod *backup) {
|
||||
std::unique_lock lk(jit_movements_lock_);
|
||||
jit_movements_.emplace_back(target, backup);
|
||||
}
|
||||
|
||||
} // namespace lsplant
|
@ -15,7 +15,9 @@
|
||||
TypeName(const TypeName &) = delete; \
|
||||
void operator=(const TypeName &) = delete
|
||||
|
||||
#ifndef JNI_HELPER_NO_NS // workaroud for https://github.com/llvm/llvm-project/issues/88400
|
||||
namespace lsplant {
|
||||
#endif
|
||||
template <class, template <class, class...> class>
|
||||
struct is_instance : public std::false_type {};
|
||||
|
||||
@ -1338,7 +1340,9 @@ template <ScopeOrObject Object>
|
||||
env, o, JNI_GetFieldID(env, JNI_GetObjectClass(env, o), field_name, field_class));
|
||||
}
|
||||
|
||||
#ifndef JNI_HELPER_NO_NS
|
||||
} // namespace lsplant
|
||||
#endif
|
||||
|
||||
#undef DISALLOW_COPY_AND_ASSIGN
|
||||
|
||||
|
@ -1,33 +1,40 @@
|
||||
#include "lsplant.hpp"
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <bits/sysconf.h>
|
||||
#include <dex_builder.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bits/sysconf.h>
|
||||
|
||||
#include "art/mirror/class.hpp"
|
||||
#include "art/runtime/art_method.hpp"
|
||||
#include "art/runtime/class_linker.hpp"
|
||||
#include "art/runtime/dex_file.hpp"
|
||||
#include "art/runtime/gc/scoped_gc_critical_section.hpp"
|
||||
#include "art/runtime/instrumentation.hpp"
|
||||
#include "art/runtime/jit/jit_code_cache.hpp"
|
||||
#include "art/runtime/jni/jni_id_manager.h"
|
||||
#include "art/runtime/runtime.hpp"
|
||||
#include "art/runtime/thread.hpp"
|
||||
#include "art/runtime/thread_list.hpp"
|
||||
#include "common.hpp"
|
||||
#include "dex_builder.h"
|
||||
#include "logging.hpp"
|
||||
#include "lsplant.hpp"
|
||||
|
||||
#define JNI_HELPER_NO_NS
|
||||
#include "utils/hook_helper.hpp"
|
||||
#include "utils/jni_helper.hpp"
|
||||
|
||||
import common;
|
||||
import art_method;
|
||||
import clazz;
|
||||
import thread;
|
||||
import instrumentation;
|
||||
import runtime;
|
||||
import thread_list;
|
||||
import class_linker;
|
||||
import scope_gc_critical_section;
|
||||
import jit_code_cache;
|
||||
import jni_id_manager;
|
||||
import dex_file;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
||||
#pragma ide diagnostic ignored "ConstantConditionsOC"
|
||||
#pragma ide diagnostic ignored "Simplify"
|
||||
#pragma ide diagnostic ignored "UnreachableCode"
|
||||
|
||||
namespace lsplant {
|
||||
|
||||
using art::ArtMethod;
|
||||
|
Loading…
x
Reference in New Issue
Block a user