mirror of
https://github.com/LSPosed/LSPlant.git
synced 2025-05-04 20:42:02 +08:00
181 lines
6.8 KiB
C++
181 lines
6.8 KiB
C++
|
#pragma once
|
||
|
|
||
|
#include "common.hpp"
|
||
|
#include "art/mirror/class.hpp"
|
||
|
|
||
|
namespace lsplant::art {
|
||
|
|
||
|
class ArtMethod {
|
||
|
CREATE_MEM_FUNC_SYMBOL_ENTRY(std::string, PrettyMethod, ArtMethod *thiz, bool with_signature) {
|
||
|
if (thiz == nullptr) [[unlikely]] return "null";
|
||
|
else if (PrettyMethodSym) [[likely]] return PrettyMethodSym(thiz, with_signature);
|
||
|
else return "null sym";
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
void SetNonCompilable() {
|
||
|
auto access_flags = GetAccessFlags();
|
||
|
access_flags |= kAccCompileDontBother;
|
||
|
access_flags &= ~kAccPreCompiled;
|
||
|
SetAccessFlags(access_flags);
|
||
|
}
|
||
|
|
||
|
void SetNonIntrinsic() {
|
||
|
auto access_flags = GetAccessFlags();
|
||
|
access_flags &= ~kAccFastInterpreterToInterpreterInvoke;
|
||
|
SetAccessFlags(access_flags);
|
||
|
}
|
||
|
|
||
|
void SetPrivate() {
|
||
|
auto access_flags = GetAccessFlags();
|
||
|
if (!(access_flags & kAccStatic)) {
|
||
|
access_flags |= kAccPrivate;
|
||
|
access_flags &= ~kAccProtected;
|
||
|
access_flags &= ~kAccPublic;
|
||
|
SetAccessFlags(access_flags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool IsStatic() {
|
||
|
return GetAccessFlags() & kAccStatic;
|
||
|
}
|
||
|
|
||
|
void CopyFrom(const ArtMethod *other) {
|
||
|
memcpy(this, other, art_method_size);
|
||
|
}
|
||
|
|
||
|
void SetEntryPoint(void *entry_point) {
|
||
|
*reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) +
|
||
|
entry_point_offset) = entry_point;
|
||
|
}
|
||
|
|
||
|
void *GetEntryPoint() {
|
||
|
return *reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) +
|
||
|
entry_point_offset);
|
||
|
}
|
||
|
|
||
|
void *GetData() {
|
||
|
return *reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) +
|
||
|
data_offset);
|
||
|
}
|
||
|
|
||
|
uint32_t GetAccessFlags() {
|
||
|
return (reinterpret_cast<const std::atomic<uint32_t> *>(
|
||
|
reinterpret_cast<uintptr_t>(this) + access_flags_offset))->load(
|
||
|
std::memory_order_relaxed);
|
||
|
}
|
||
|
|
||
|
void SetAccessFlags(uint32_t flags) {
|
||
|
return (reinterpret_cast<std::atomic<uint32_t> *>(
|
||
|
reinterpret_cast<uintptr_t>(this) + access_flags_offset))->store(
|
||
|
flags, std::memory_order_relaxed);
|
||
|
}
|
||
|
|
||
|
std::string PrettyMethod(bool with_signature = true) {
|
||
|
return PrettyMethod(this, with_signature);
|
||
|
}
|
||
|
|
||
|
mirror::Class *GetDeclaringClass() {
|
||
|
return *reinterpret_cast<mirror::Class **>(reinterpret_cast<uintptr_t>(this) +
|
||
|
declaring_class_offset);
|
||
|
}
|
||
|
|
||
|
static art::ArtMethod *FromReflectedMethod(JNIEnv *env, jobject method) {
|
||
|
return reinterpret_cast<art::ArtMethod *>(JNI_GetLongField(env, method, art_method_field));
|
||
|
}
|
||
|
|
||
|
static bool Init(JNIEnv *env, const lsplant::InitInfo info) {
|
||
|
auto executable = JNI_FindClass(env, "java/lang/reflect/Executable");
|
||
|
if (!executable) {
|
||
|
LOGE("Failed to found Executable");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (art_method_field = JNI_GetFieldID(env, executable, "artMethod",
|
||
|
"J"); !art_method_field) {
|
||
|
LOGE("Failed to find artMethod field");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
auto throwable = JNI_FindClass(env, "java/lang/Throwable");
|
||
|
if (!throwable) {
|
||
|
LOGE("Failed to found Executable");
|
||
|
return false;
|
||
|
}
|
||
|
auto clazz = JNI_FindClass(env, "java/lang/Class");
|
||
|
static_assert(std::is_same_v<decltype(clazz)::BaseType, jclass>);
|
||
|
jmethodID get_declared_constructors = JNI_GetMethodID(env, clazz, "getDeclaredConstructors",
|
||
|
"()[Ljava/lang/reflect/Constructor;");
|
||
|
auto constructors = JNI_Cast<jobjectArray>(
|
||
|
JNI_CallObjectMethod(env, clazz, get_declared_constructors));
|
||
|
auto length = JNI_GetArrayLength(env, constructors);
|
||
|
if (length < 2) {
|
||
|
LOGE("Throwable has less than 2 constructors");
|
||
|
return false;
|
||
|
}
|
||
|
auto *first = FromReflectedMethod(env,
|
||
|
JNI_GetObjectArrayElement(env, constructors, 0).get());
|
||
|
auto *second = FromReflectedMethod(env,
|
||
|
JNI_GetObjectArrayElement(env, constructors, 0).get());
|
||
|
art_method_size = reinterpret_cast<uintptr_t>(second) - reinterpret_cast<uintptr_t>(first);
|
||
|
LOGD("ArtMethod size: %zu", art_method_size);
|
||
|
|
||
|
if (RoundUpTo(4 * 4 + 2 * 2, kPointerSize) + kPointerSize * 3 < art_method_size) {
|
||
|
LOGW("ArtMethod size exceeds maximum assume. There may be something wrong.");
|
||
|
}
|
||
|
|
||
|
entry_point_offset = art_method_size - sizeof(void *);
|
||
|
LOGD("ArtMethod::entrypoint offset: %zu", entry_point_offset);
|
||
|
|
||
|
data_offset = entry_point_offset - sizeof(void *);
|
||
|
LOGD("ArtMethod::data offset: %zu", data_offset);
|
||
|
|
||
|
declaring_class_offset = 0U;
|
||
|
LOGD("ArtMethod::declaring_class offset: %zu", declaring_class_offset);
|
||
|
|
||
|
access_flags_offset = 4U;
|
||
|
LOGD("ArtMethod::access_flags offset: %zu", access_flags_offset);
|
||
|
auto sdk_int = GetAndroidApiLevel();
|
||
|
|
||
|
if (sdk_int < __ANDROID_API_R__) kAccPreCompiled = 0;
|
||
|
if (sdk_int < __ANDROID_API_Q__) kAccFastInterpreterToInterpreterInvoke = 0;
|
||
|
|
||
|
get_method_shorty_symbol = GetArtSymbol<decltype(get_method_shorty_symbol)>(
|
||
|
info.art_symbol_resolver,
|
||
|
"_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID");
|
||
|
if (!get_method_shorty_symbol) return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static const char *GetMethodShorty(_JNIEnv *env, _jmethodID *method) {
|
||
|
if (get_method_shorty_symbol) [[likely]] return get_method_shorty_symbol(env, method);
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
static size_t GetEntryPointOffset() {
|
||
|
return entry_point_offset;
|
||
|
}
|
||
|
|
||
|
constexpr static uint32_t kAccPublic = 0x0001; // class, field, method, ic
|
||
|
constexpr static uint32_t kAccPrivate = 0x0002; // field, method, ic
|
||
|
constexpr static uint32_t kAccProtected = 0x0004; // field, method, ic
|
||
|
constexpr static uint32_t kAccStatic = 0x0008; // field, method, ic
|
||
|
constexpr static uint32_t kAccNative = 0x0100; // method
|
||
|
|
||
|
private:
|
||
|
inline static jfieldID art_method_field = nullptr;
|
||
|
inline static size_t art_method_size = 0;
|
||
|
inline static size_t entry_point_offset = 0;
|
||
|
inline static size_t data_offset = 0;
|
||
|
inline static size_t declaring_class_offset = 0;
|
||
|
inline static size_t access_flags_offset = 0;
|
||
|
inline static uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||
|
inline static uint32_t kAccPreCompiled = 0x00200000;
|
||
|
inline static uint32_t kAccCompileDontBother = 0x02000000;
|
||
|
|
||
|
inline static const char *
|
||
|
(*get_method_shorty_symbol)(_JNIEnv *env, _jmethodID *method) = nullptr;
|
||
|
};
|
||
|
|
||
|
}
|