From 6b17a65831e3d7f7492c81b3f05388f225ec463e Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:18:48 -0400 Subject: [PATCH 1/2] Initial generic sharing method test --- shared/utils/il2cpp-functions.hpp | 13 ++++ src/utils/il2cpp-functions.cpp | 111 ++++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 6 deletions(-) diff --git a/shared/utils/il2cpp-functions.hpp b/shared/utils/il2cpp-functions.hpp index 43301103..c99d19ce 100644 --- a/shared/utils/il2cpp-functions.hpp +++ b/shared/utils/il2cpp-functions.hpp @@ -467,6 +467,19 @@ class il2cpp_functions { } // COPIES OF FREQUENTLY INLINED NON-API LIBIL2CPP FUNCTIONS: + enum struct GenericParameterRestriction { + GenericParameterRestrictionNone, + GenericParameterRestrictionValueType, + GenericParameterRestrictionReferenceType, + }; + + static bool Type_HasFullGenericSharedParametersOrReturn(const MethodInfo* methodDefinition, const Il2CppType** inflatedParameterTypes); + static bool Type_HasVariableRuntimeSizeWhenFullyShared(const Il2CppType* type); + static bool Type_IsGenericParameter(const Il2CppType* type); + static il2cpp_functions::GenericParameterRestriction MetadataCache_IsReferenceTypeGenericParameter(Il2CppMetadataGenericParameterHandle genericParameter); + static const Il2CppType* MetadataCache_GetGenericParameterConstraintFromIndex(Il2CppMetadataGenericParameterHandle handle, GenericParameterConstraintIndex index); + static il2cpp_functions::GenericParameterRestriction MetadataCache_IsReferenceTypeGenericConstraint(const Il2CppType* constraint); + static const char* MetadataCache_GetStringFromIndex(StringIndex index); static const Il2CppTypeDefinition* MetadataCache_GetTypeDefinitionFromIndex(TypeDefinitionIndex index); static TypeDefinitionIndex MetadataCache_GetExportedTypeFromIndex(TypeDefinitionIndex index); diff --git a/src/utils/il2cpp-functions.cpp b/src/utils/il2cpp-functions.cpp index c7486367..d49b0f9b 100644 --- a/src/utils/il2cpp-functions.cpp +++ b/src/utils/il2cpp-functions.cpp @@ -4,6 +4,9 @@ #include "../../shared/utils/hooking.hpp" #include "../../shared/utils/il2cpp-functions.hpp" #include "../../shared/utils/logging.hpp" + + + #include "capstone/shared/capstone/capstone.h" #define API_INIT(rt, name, ...) rt(*il2cpp_functions::il2cpp_##name) __VA_ARGS__ @@ -326,6 +329,103 @@ const Il2CppDefaults* il2cpp_functions::defaults; bool il2cpp_functions::initialized; // copies of the highly-inlinable functions +bool il2cpp_functions::Type_HasFullGenericSharedParametersOrReturn(const MethodInfo* methodDefinition, const Il2CppType** inflatedParameterTypes) { + // If a method has a variable sized return type, the FGS method will always + // expect the return value to be passed as a by ref parameter + if (il2cpp_functions::Type_HasVariableRuntimeSizeWhenFullyShared(methodDefinition->return_type)) return true; + + for (int i = 0; i < methodDefinition->parameters_count; i++) { + // Value types are passed by ref, but reference types are passed normally, so if the inflated parameter is a + // reference type, we don't have a signature difference. + if (inflatedParameterTypes[i]->valuetype && il2cpp_functions::Type_HasVariableRuntimeSizeWhenFullyShared(methodDefinition->parameters[i])) return true; + } + + return false; +} + +bool il2cpp_functions::Type_HasVariableRuntimeSizeWhenFullyShared(const Il2CppType* type) { + // This needs to align with TypeRuntimeStoage::RuntimeFieldLayout + + // Anything passed by ref is pointer sized + if (type->byref) return false; + + // Any generic parameter that is not constrained to be a reference type would be fully shared + if (type->type == il2cpp_functions::Type_IsGenericParameter(type)) + return il2cpp_functions::MetadataCache_IsReferenceTypeGenericParameter(type->data.genericParameterHandle) != + il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionReferenceType; + + // If we're not a generic instance then we'll be a concrete type + if (type->type != IL2CPP_TYPE_GENERICINST) return false; + + // If a reference type or pointer then we aren't variable sized + if (!il2cpp_functions::class_from_il2cpp_type(type->data.generic_class->type)->byval_arg.valuetype) return false; + + Il2CppClass* klass = il2cpp_functions::class_from_il2cpp_type(type); + Il2CppClass* typeDef = il2cpp_functions::class_from_il2cpp_type(klass->generic_class->type); + FieldInfo* field; + void* iter = NULL; + for (field = il2cpp_functions::class_get_fields(typeDef, &iter); field; field = il2cpp_functions::class_get_fields(typeDef, &iter)) { + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC && il2cpp_functions::Type_HasVariableRuntimeSizeWhenFullyShared(field->type)) return true; + } + + return false; +} + +bool il2cpp_functions::Type_IsGenericParameter(const Il2CppType* type) { + return type->type == IL2CPP_TYPE_VAR || type->type == IL2CPP_TYPE_MVAR; +} + +il2cpp_functions::GenericParameterRestriction il2cpp_functions::MetadataCache_IsReferenceTypeGenericParameter(Il2CppMetadataGenericParameterHandle genericParameterHandle) { + const Il2CppGenericParameter* genericParameter = reinterpret_cast(genericParameterHandle); + + uint16_t flags = genericParameter->flags; + if ((flags & IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) != 0) return il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionReferenceType; + if ((flags & IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_NOT_NULLABLE_VALUE_TYPE_CONSTRAINT) != 0) + return il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionValueType; // Must be a value type + + uint32_t count = genericParameter->constraintsCount; + for (uint32_t constraintIndex = 0; constraintIndex < count; ++constraintIndex) { + const Il2CppType* constraint = il2cpp_functions::MetadataCache_GetGenericParameterConstraintFromIndex(genericParameterHandle, constraintIndex); + GenericParameterRestriction restriction = il2cpp_functions::MetadataCache_IsReferenceTypeGenericConstraint(constraint); + if (restriction != il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionNone) return restriction; + } + + return il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionNone; +} + +const Il2CppType* il2cpp_functions::MetadataCache_GetGenericParameterConstraintFromIndex(Il2CppMetadataGenericParameterHandle handle, GenericParameterConstraintIndex index) { + CheckS_GlobalMetadata(); + const Il2CppGenericParameter* genericParameter = reinterpret_cast(handle); + + IL2CPP_ASSERT(index >= 0 && index < genericParameter->constraintsCount); + + index = genericParameter->constraintsStart + index; + + IL2CPP_ASSERT(index >= 0 && static_cast(index) <= s_GlobalMetadataHeader->genericParameterConstraintsSize / sizeof(TypeIndex)); + const TypeIndex* constraintIndices = (const TypeIndex*)((const char*)s_GlobalMetadata + s_GlobalMetadataHeader->genericParameterConstraintsOffset); + + return il2cpp_functions::s_Il2CppMetadataRegistration->types[constraintIndices[index]]; +} + +il2cpp_functions::GenericParameterRestriction il2cpp_functions::MetadataCache_IsReferenceTypeGenericConstraint(const Il2CppType* constraint) { + // This must match GenericSharingAnalsyis.GetGenericParameterConstraintRestriction() + + if (constraint->type == IL2CPP_TYPE_VAR || constraint->type == IL2CPP_TYPE_MVAR) return il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionNone; + + if (il2cpp_functions::type_equals(constraint, &il2cpp_functions::defaults->enum_class->byval_arg)) + // if (il2cpp::metadata::Il2CppTypeEqualityComparer::AreEqual(constraint, &il2cpp_functions::defaults->enum_class->byval_arg)) + return il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionValueType; + + if (il2cpp_functions::type_equals(constraint, &il2cpp_functions::defaults->value_type_class->byval_arg)) + // if (il2cpp::metadata::Il2CppTypeEqualityComparer::AreEqual(constraint, &il2cpp_functions::defaults->value_type_class->byval_arg)) + return il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionNone; // Not a valid constraint, so consider it unconstrained + else if (il2cpp_functions::class_from_il2cpp_type(constraint)->flags & TYPE_ATTRIBUTE_INTERFACE) + return il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionNone; // Interfaces constraints can be satisfied by reference or value types + + // Any other type constraint e.g. T : SomeType, SomeType must be a reference type + return il2cpp_functions::GenericParameterRestriction::GenericParameterRestrictionReferenceType; +} + const Il2CppTypeDefinition* il2cpp_functions::MetadataCache_GetTypeDefinitionFromIndex(TypeDefinitionIndex index) { CheckS_GlobalMetadata(); if (index == kTypeDefinitionIndexInvalid) return NULL; @@ -457,8 +557,6 @@ void* __wrapper_gc_malloc_uncollectable(size_t sz, [[maybe_unused]] void* desc) } bool il2cpp_functions::trace_GC_AllocFixed(const uint32_t* DomainGetCurrent) { - - // Domain::GetCurrent has a single bl to GarbageCollector::AllocateFixed // MetadataCache::InitializeGCSafe is 3rd bl after first b.ne, which is the 6th b(.lt, .ne), t(bz, nz), c(bz, nz) auto tmp = cs::findNthBl<1>(DomainGetCurrent); @@ -490,7 +588,6 @@ static std::optional loadFind(cs_insn* insn) { return (insn->id == ARM64_INS_LDR || insn->id == ARM64_INS_LDP) ? std::optional(reinterpret_cast(insn->address)) : std::nullopt; } - #define API_SYM(name) \ *(void**)(&il2cpp_##name) = dlsym(imagehandle, "il2cpp_" #name); \ logger.debug("Loaded: " #name ", error: {}", bs_hooks::nullable(dlerror())) @@ -877,8 +974,10 @@ void il2cpp_functions::Init() { logger.debug("GlobalMetadata::GetTypeInfoFromHandle offset: {:X}", reinterpret_cast(il2cpp_GlobalMetadata_GetTypeInfoFromHandle) - getRealOffset(0)); auto GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex_addr = cs::findNthB<1, false, -1, 1024>(reinterpret_cast(il2cpp_GlobalMetadata_GetTypeInfoFromHandle)); if (!GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex_addr) SAFE_ABORT_MSG("Failed to find GlobalMetadata::GetTypeInfoFromTypeDefinitionIndex!"); - il2cpp_GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex = reinterpret_cast(*GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex_addr); - logger.debug("GlobalMetadata::GetTypeInfoFromTypeDefinitionIndex found? offset: {:X}", reinterpret_cast(il2cpp_GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex) - getRealOffset(0)); + il2cpp_GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex = + reinterpret_cast(*GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex_addr); + logger.debug("GlobalMetadata::GetTypeInfoFromTypeDefinitionIndex found? offset: {:X}", + reinterpret_cast(il2cpp_GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex) - getRealOffset(0)); } { @@ -990,4 +1089,4 @@ void il2cpp_functions::Init() { initialized = true; logger.info("il2cpp_functions: Init: Successfully loaded all il2cpp functions!"); -} +} \ No newline at end of file From 1eb9f93cbcc1b4428ba28ef905193766c2b58133 Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:20:50 -0400 Subject: [PATCH 2/2] Fix CI --- .github/workflows/build-ndk.yml | 6 +++--- qpm.json | 2 +- qpm.shared.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-ndk.yml b/.github/workflows/build-ndk.yml index 80fa83be..f68f7d5d 100644 --- a/.github/workflows/build-ndk.yml +++ b/.github/workflows/build-ndk.yml @@ -59,7 +59,7 @@ jobs: # if we don't have a tag, don't do anything special - name: Setup qpm if: ${{ !startsWith(github.ref, 'refs/tags/v') }} - uses: Fernthedev/qpm-action@main + uses: Fernthedev/qpm-action@v1 with: workflow_token: ${{ secrets.GITHUB_TOKEN }} restore: true @@ -69,12 +69,12 @@ jobs: # if we have a tag, we are making a qpm release - name: Setup qpm for release if: startsWith(github.ref, 'refs/tags/v') - uses: Fernthedev/qpm-action@main + uses: Fernthedev/qpm-action@v1 with: workflow_token: ${{ secrets.GITHUB_TOKEN }} restore: true cache: true - publish: true + publish: "late" publish_token: ${{ secrets.QPM_KEY }} version: ${{ steps.version.outputs.VERSION }} tag: ${{ steps.version.outputs.TAG }} diff --git a/qpm.json b/qpm.json index e3a349f3..dff332df 100644 --- a/qpm.json +++ b/qpm.json @@ -24,7 +24,7 @@ "qpm qmod zip" ] }, - "ndk": "^27.1.12297006", + "ndk": "^27.2.12479018", "qmodIncludeDirs": [ "./build", "./extern/libs" diff --git a/qpm.shared.json b/qpm.shared.json index 4d5e316d..b36cf666 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -25,7 +25,7 @@ "qpm qmod zip" ] }, - "ndk": "^27.1.12297006", + "ndk": "^27.2.12479018", "qmodIncludeDirs": [ "./build", "./extern/libs"