From b383ae3129607d7e2e15e4215de68ce7f0269411 Mon Sep 17 00:00:00 2001 From: Lucas <31241699+nitrog0d@users.noreply.github.com> Date: Thu, 9 Apr 2026 07:36:20 -0300 Subject: [PATCH 1/4] Remove static mutable state from LibCpp2IL Migrate all metadata/binary structures from static LibCpp2IlMain fields to per-instance OwningBinary/OwningMetadata on ReadableClass. Move global mapper state into LibCpp2IlContext. Update Cpp2IL.Core to pass Il2CppBinary explicitly. Legacy static API kept as [Obsolete] wrappers. --- Cpp2IL.Core/Cpp2IlApi.cs | 52 +--- .../UnsupportedInstructionSetException.cs | 16 +- .../Arm64KeyFunctionAddresses.cs | 4 +- .../BaseKeyFunctionAddresses.cs | 4 +- .../NewArm64KeyFunctionAddresses.cs | 21 +- .../X86KeyFunctionAddresses.cs | 21 +- Cpp2IL.Core/Il2CppArrayUtils.cs | 16 +- Cpp2IL.Core/Il2CppClassUsefulOffsets.cs | 54 ++-- .../Il2CppMethodDefinitionUsefulOffsets.cs | 12 +- Cpp2IL.Core/Il2CppMethodInfoUsefulOffsets.cs | 10 +- .../InstructionSets/Arm64InstructionSet.cs | 9 +- .../InstructionSets/ArmV7InstructionSet.cs | 6 +- .../InstructionSets/NewArmV8InstructionSet.cs | 9 +- .../InstructionSets/X86InstructionSet.cs | 4 +- .../Contexts/ApplicationAnalysisContext.cs | 3 +- .../Model/Contexts/MethodAnalysisContext.cs | 2 +- .../Contexts/NativeMethodAnalysisContext.cs | 3 +- .../OutputFormats/WasmMappingOutputFormat.cs | 2 +- .../WasmNameSectionOutputFormat.cs | 2 +- Cpp2IL.Core/Utils/Arm64Utils.cs | 22 +- Cpp2IL.Core/Utils/ArmV7Utils.cs | 28 +- Cpp2IL.Core/Utils/MiscUtils.cs | 14 +- Cpp2IL.Core/Utils/NewArm64Utils.cs | 16 +- Cpp2IL.Core/Utils/WasmUtils.cs | 8 +- Cpp2IL.Core/Utils/X86Utils.cs | 56 ++-- .../StrippedCodeRegSupportPlugin.cs | 2 +- LibCpp2IL/BinarySearcher.cs | 28 +- LibCpp2IL/BinaryStructures/Il2CppArrayType.cs | 5 +- .../BinaryStructures/Il2CppCodeGenModule.cs | 6 +- .../BinaryStructures/Il2CppGenericClass.cs | 18 +- .../BinaryStructures/Il2CppGenericContext.cs | 7 +- .../BinaryStructures/Il2CppGenericInst.cs | 7 +- .../BinaryStructures/Il2CppMethodSpec.cs | 12 +- .../BinaryStructures/Il2CppRGCTXDefinition.cs | 11 +- LibCpp2IL/BinaryStructures/Il2CppType.cs | 28 +- LibCpp2IL/ClassReadingBinaryReader.cs | 7 + LibCpp2IL/Elf/ElfFile.cs | 2 +- LibCpp2IL/Il2CppBinary.cs | 9 +- LibCpp2IL/LibCpp2IlContext.cs | 86 +++++- LibCpp2IL/LibCpp2IlContextBuilder.cs | 9 + LibCpp2IL/LibCpp2IlMain.cs | 285 +++++++----------- LibCpp2IL/LibCpp2IlUtils.cs | 45 +-- .../Metadata/Il2CppAssemblyDefinition.cs | 6 +- .../Metadata/Il2CppAssemblyNameDefinition.cs | 10 +- LibCpp2IL/Metadata/Il2CppEventDefinition.cs | 14 +- LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs | 2 +- LibCpp2IL/Metadata/Il2CppFieldDefinition.cs | 12 +- LibCpp2IL/Metadata/Il2CppFieldRef.cs | 6 +- LibCpp2IL/Metadata/Il2CppGenericContainer.cs | 6 +- LibCpp2IL/Metadata/Il2CppGenericParameter.cs | 8 +- LibCpp2IL/Metadata/Il2CppImageDefinition.cs | 6 +- LibCpp2IL/Metadata/Il2CppInterfaceOffset.cs | 2 +- LibCpp2IL/Metadata/Il2CppMetadata.cs | 73 ++++- LibCpp2IL/Metadata/Il2CppMethodDefinition.cs | 40 +-- .../Metadata/Il2CppParameterDefaultValue.cs | 2 +- .../Metadata/Il2CppParameterDefinition.cs | 2 +- .../Metadata/Il2CppPropertyDefinition.cs | 12 +- LibCpp2IL/Metadata/Il2CppTypeDefinition.cs | 86 +++--- LibCpp2IL/MetadataUsage.cs | 64 ++-- LibCpp2IL/ReadableClass.cs | 6 +- .../Reflection/Il2CppTypeReflectionData.cs | 2 +- LibCpp2ILTests/Tests.cs | 9 +- 62 files changed, 710 insertions(+), 619 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index b557ae3c9..a51cd3af4 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -51,7 +51,7 @@ public static void ConfigureLib(bool allowUserToInputAddresses) LibCpp2IlMain.Settings.MetadataFixupFunc = Cpp2IlPluginManager.MetadataFixupFuncs is { } funcs ? (originalBytes, version) => { Logger.InfoNewline("Received request for metadata fixup from LibCpp2Il. Calling registered plugin fixup functions..."); - + foreach (var func in funcs) { try @@ -68,7 +68,7 @@ public static void ConfigureLib(bool allowUserToInputAddresses) Logger.ErrorNewline($"Metadata fixup function threw an exception: {e}. Ignoring and trying next fixup function, if any..."); } } - + //only get here if every fixup function returns null or throws. return null; } : null; @@ -89,8 +89,8 @@ public static void InitializeLibCpp2Il(string assemblyPath, string metadataPath, try { #endif - if (!LibCpp2IlMain.LoadFromFile(assemblyPath, metadataPath, unityVersion)) - throw new Exception("Initialization with LibCpp2Il failed"); + var context = LibCpp2IlMain.LoadFromFileAsContext(assemblyPath, metadataPath, unityVersion); + OnLibInitialized(context); #if !DEBUG } catch (Exception e) @@ -98,7 +98,6 @@ public static void InitializeLibCpp2Il(string assemblyPath, string metadataPath, throw new LibCpp2ILInitializationException("Fatal Exception initializing LibCpp2IL!", e); } #endif - OnLibInitialized(); } [MemberNotNull(nameof(CurrentAppContext))] @@ -112,27 +111,25 @@ public static void InitializeLibCpp2Il(byte[] assemblyData, byte[] metadataData, try { - if (!LibCpp2IlMain.Initialize(assemblyData, metadataData, unityVersion)) - throw new Exception("Initialization with LibCpp2Il failed"); + var context = LibCpp2IlMain.InitializeAsContext(assemblyData, metadataData, unityVersion); + OnLibInitialized(context); } catch (Exception e) { throw new LibCpp2ILInitializationException("Fatal Exception initializing LibCpp2IL!", e); } - - OnLibInitialized(); } [MemberNotNull(nameof(CurrentAppContext))] - private static void OnLibInitialized() + private static void OnLibInitialized(LibCpp2IlContext libContext) { - MiscUtils.Init(); - LibCpp2IlMain.Binary!.AllCustomAttributeGenerators.ToList() + MiscUtils.Init(libContext.Binary); + libContext.Binary.AllCustomAttributeGenerators.ToList() .ForEach(ptr => SharedState.AttributeGeneratorStarts.Add(ptr)); var start = DateTime.Now; Logger.InfoNewline("Creating application model..."); - CurrentAppContext = new(LibCpp2IlMain.Binary, LibCpp2IlMain.TheMetadata!); + CurrentAppContext = new(libContext.Binary, libContext.Metadata); Logger.InfoNewline($"Application model created in {(DateTime.Now - start).TotalMilliseconds}ms"); } @@ -144,38 +141,11 @@ public static void ResetInternalState() AsmResolverUtils.Reset(); - LibCpp2IlMain.Reset(); - CurrentAppContext = null; } - // public static void PopulateConcreteImplementations() - // { - // CheckLibInitialized(); - // - // Logger.InfoNewline("Populating Concrete Implementation Table..."); - // - // foreach (var def in LibCpp2IlMain.TheMetadata!.typeDefs) - // { - // if (def.IsAbstract) - // continue; - // - // var baseTypeReflectionData = def.BaseType; - // while (baseTypeReflectionData != null) - // { - // if (baseTypeReflectionData.baseType == null) - // break; - // - // if (baseTypeReflectionData.isType && baseTypeReflectionData.baseType.IsAbstract && !SharedState.ConcreteImplementations.ContainsKey(baseTypeReflectionData.baseType)) - // SharedState.ConcreteImplementations[baseTypeReflectionData.baseType] = def; - // - // baseTypeReflectionData = baseTypeReflectionData.baseType.BaseType; - // } - // } - // } - private static bool IsLibInitialized() { - return LibCpp2IlMain.Binary != null && LibCpp2IlMain.TheMetadata != null; + return CurrentAppContext != null; } } diff --git a/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs b/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs index bcf9402ef..d6b84a1fb 100644 --- a/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs +++ b/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs @@ -1,9 +1,19 @@ -using System; -using LibCpp2IL; +using System; namespace Cpp2IL.Core.Exceptions; public class UnsupportedInstructionSetException : Exception { - public override string Message => $"This action is not supported on the {LibCpp2IlMain.Binary?.InstructionSetId} instruction set yet. If running the CLI, try adding the --skip-analysis argument."; + private readonly string? _instructionSetId; + + public UnsupportedInstructionSetException() + { + } + + public UnsupportedInstructionSetException(string instructionSetId) + { + _instructionSetId = instructionSetId; + } + + public override string Message => $"This action is not supported on the {_instructionSetId ?? "unknown"} instruction set yet. If running the CLI, try adding the --skip-analysis argument."; } diff --git a/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs index 189e9cc95..0d71e06fa 100644 --- a/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs @@ -208,7 +208,7 @@ protected override ulong GetObjectIsInstFromSystemType() //The last call is to Object::IsInst Logger.Verbose($"IsInstanceOfType found at 0x{typeIsInstanceOfType.MethodPointer:X}..."); - var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(typeIsInstanceOfType.MethodPointer, false); + var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(_appContext.Binary, typeIsInstanceOfType.MethodPointer, false); var lastCall = instructions.LastOrDefault(i => i.Mnemonic == "bl"); @@ -258,7 +258,7 @@ protected override void AttemptInstructionAnalysisToFillGaps() if (arrayTypeDef.Methods!.FirstOrDefault(m => m.Name == "GetEnumerator") is { } methodDef) { var ptr = methodDef.MethodPointer; - var body = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(ptr); + var body = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(_appContext.Binary, ptr); //Looking for adrp, ldr, ldr, bl. Probably more than one - the first will be initializing the method, second will be the constructor call var probableResult = 0L; diff --git a/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs index 5fac4f97b..d3e2e9272 100644 --- a/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs @@ -52,7 +52,7 @@ public abstract class BaseKeyFunctionAddresses public IEnumerable> Pairs => resolvedAddressMap; - private ApplicationAnalysisContext _appContext = null!; //Always initialized before used + protected ApplicationAnalysisContext _appContext = null!; //Always initialized before used private readonly Dictionary resolvedAddressMap = []; private readonly HashSet resolvedAddressSet = []; @@ -131,7 +131,7 @@ protected void TryGetInitMetadataFromException() { Logger.VerboseNewline($"\t\tTarget Method Located at {targetMethod.MethodPointer}. Taking first CALL as the (version-specific) metadata initialization function..."); - var disasm = X86Utils.GetMethodBodyAtVirtAddressNew(targetMethod.MethodPointer, false); + var disasm = X86Utils.GetMethodBodyAtVirtAddressNew(targetMethod.MethodPointer, false, _appContext.Binary); var calls = disasm.Where(i => i.Mnemonic == Mnemonic.Call).ToList(); if (calls.Count == 0) diff --git a/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs index 96a1ce65f..aa6cc0957 100644 --- a/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs @@ -4,7 +4,6 @@ using Disarm; using Cpp2IL.Core.Logging; using Cpp2IL.Core.Utils; -using LibCpp2IL; using LibCpp2IL.Reflection; namespace Cpp2IL.Core.Il2CppApiFunctions; @@ -17,8 +16,9 @@ private List DisassembleTextSection() { if (_cachedDisassembledBytes == null) { - var toDisasm = LibCpp2IlMain.Binary!.GetEntirePrimaryExecutableSection(); - _cachedDisassembledBytes = Disassembler.Disassemble(toDisasm, LibCpp2IlMain.Binary.GetVirtualAddressOfPrimaryExecutableSection(), new(true, true, false)).ToList(); + var binary = _appContext.Binary; + var toDisasm = binary.GetEntirePrimaryExecutableSection(); + _cachedDisassembledBytes = Disassembler.Disassemble(toDisasm, binary.GetVirtualAddressOfPrimaryExecutableSection(), new(true, true, false)).ToList(); } return _cachedDisassembledBytes; @@ -37,13 +37,14 @@ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint max if (addressesToIgnore.Contains(matchingJmp.Address)) continue; //Find this instruction in the raw file - var offsetInPe = (ulong)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(matchingJmp.Address); - if (offsetInPe == 0 || offsetInPe == (ulong)(LibCpp2IlMain.Binary.RawLength - 1)) + var binary = _appContext.Binary; + var offsetInPe = (ulong)binary.MapVirtualAddressToRaw(matchingJmp.Address); + if (offsetInPe == 0 || offsetInPe == (ulong)(binary.RawLength - 1)) continue; //get next and previous bytes - var previousByte = LibCpp2IlMain.Binary.GetByteAtRawAddress(offsetInPe - 1); - var nextByte = LibCpp2IlMain.Binary.GetByteAtRawAddress(offsetInPe + 4); + var previousByte = binary.GetByteAtRawAddress(offsetInPe - 1); + var nextByte = binary.GetByteAtRawAddress(offsetInPe + 4); //Double-cc = thunk if (previousByte == 0xCC && nextByte == 0xCC) @@ -60,7 +61,7 @@ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint max //Move to next jmp break; - if (LibCpp2IlMain.Binary.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC) + if (binary.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC) { yield return matchingJmp.Address - (backtrack - 1); break; @@ -86,7 +87,7 @@ protected override ulong GetObjectIsInstFromSystemType() //The last call is to Object::IsInst Logger.Verbose($"IsInstanceOfType found at 0x{typeIsInstanceOfType.MethodPointer:X}..."); - var instructions = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(typeIsInstanceOfType.MethodPointer, true); + var instructions = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(_appContext.Binary, typeIsInstanceOfType.MethodPointer, true); var lastCall = instructions.LastOrDefault(i => i.Mnemonic == Arm64Mnemonic.BL); @@ -102,7 +103,7 @@ protected override ulong GetObjectIsInstFromSystemType() protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false) { - var instructions = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(thunkPtr, true); + var instructions = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(_appContext.Binary, thunkPtr, true); var target = prioritiseCall ? Arm64Mnemonic.BL : Arm64Mnemonic.B; var matchingCall = instructions.FirstOrDefault(i => i.Mnemonic == target); diff --git a/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs index d8deb5439..a95b153c6 100644 --- a/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs @@ -5,7 +5,6 @@ using Cpp2IL.Core.Model.Contexts; using Cpp2IL.Core.Utils; using Iced.Intel; -using LibCpp2IL; using LibCpp2IL.Reflection; namespace Cpp2IL.Core.Il2CppApiFunctions; @@ -18,8 +17,9 @@ private InstructionList DisassembleTextSection() { if (_cachedDisassembledBytes == null) { - var toDisasm = LibCpp2IlMain.Binary!.GetEntirePrimaryExecutableSection(); - _cachedDisassembledBytes = X86Utils.Disassemble(toDisasm, LibCpp2IlMain.Binary.GetVirtualAddressOfPrimaryExecutableSection()); + var binary = _appContext.Binary; + var toDisasm = binary.GetEntirePrimaryExecutableSection(); + _cachedDisassembledBytes = X86Utils.Disassemble(toDisasm, binary.GetVirtualAddressOfPrimaryExecutableSection(), binary); } return _cachedDisassembledBytes; @@ -45,13 +45,14 @@ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint max if (addressesToIgnore.Contains(matchingJmp.IP)) continue; //Find this instruction in the raw file - var offsetInPe = (ulong)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(matchingJmp.IP); - if (offsetInPe == 0 || offsetInPe == (ulong)(LibCpp2IlMain.Binary!.RawLength - 1)) + var binary = _appContext.Binary; + var offsetInPe = (ulong)binary.MapVirtualAddressToRaw(matchingJmp.IP); + if (offsetInPe == 0 || offsetInPe == (ulong)(binary.RawLength - 1)) continue; //get next and previous bytes - var previousByte = LibCpp2IlMain.Binary.GetByteAtRawAddress(offsetInPe - 1); - var nextByte = LibCpp2IlMain.Binary.GetByteAtRawAddress(offsetInPe + (ulong)matchingJmp.Length); + var previousByte = binary.GetByteAtRawAddress(offsetInPe - 1); + var nextByte = binary.GetByteAtRawAddress(offsetInPe + (ulong)matchingJmp.Length); //Double-cc = thunk if (previousByte == 0xCC && nextByte == 0xCC) @@ -68,7 +69,7 @@ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint max //Move to next jmp break; - if (LibCpp2IlMain.Binary!.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC) + if (binary.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC) { yield return matchingJmp.IP - (backtrack - 1); break; @@ -94,7 +95,7 @@ protected override ulong GetObjectIsInstFromSystemType() //The last call is to Object::IsInst Logger.Verbose($"IsInstanceOfType found at 0x{typeIsInstanceOfType.MethodPointer:X}..."); - var instructions = X86Utils.GetMethodBodyAtVirtAddressNew(typeIsInstanceOfType.MethodPointer, true); + var instructions = X86Utils.GetMethodBodyAtVirtAddressNew(typeIsInstanceOfType.MethodPointer, true, _appContext.Binary); var lastCall = instructions.LastOrDefault(i => i.Mnemonic == Mnemonic.Call); @@ -110,7 +111,7 @@ protected override ulong GetObjectIsInstFromSystemType() protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false) { - var instructions = X86Utils.GetMethodBodyAtVirtAddressNew(thunkPtr, true); + var instructions = X86Utils.GetMethodBodyAtVirtAddressNew(thunkPtr, true, _appContext.Binary); var target = prioritiseCall ? Mnemonic.Call : Mnemonic.Jmp; var matchingCall = instructions.FirstOrDefault(i => i.Mnemonic == target); diff --git a/Cpp2IL.Core/Il2CppArrayUtils.cs b/Cpp2IL.Core/Il2CppArrayUtils.cs index 1880e0b4b..7577fa498 100644 --- a/Cpp2IL.Core/Il2CppArrayUtils.cs +++ b/Cpp2IL.Core/Il2CppArrayUtils.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using LibCpp2IL; @@ -7,7 +7,7 @@ namespace Cpp2IL.Core; public static class Il2CppArrayUtils { - public static uint FirstItemOffset = (uint)(LibCpp2IlMain.Binary!.is32Bit ? 0x10 : 0x20); + public static uint GetFirstItemOffset(Il2CppBinary binary) => (uint)(binary.is32Bit ? 0x10 : 0x20); //32-bit: //0x0: klass ptr //0x4: monitor ptr @@ -23,21 +23,21 @@ public static class Il2CppArrayUtils new UsefulOffset("length", 0x18, typeof(int), false) ]; - public static string? GetOffsetName(uint offset) + public static string? GetOffsetName(uint offset, Il2CppBinary binary) { - var is32Bit = LibCpp2IlMain.Binary!.is32Bit; + var is32Bit = binary.is32Bit; return UsefulOffsets.FirstOrDefault(o => o.is32Bit == is32Bit && o.offset == offset)?.name; } - public static bool IsIl2cppLengthAccessor(uint offset) + public static bool IsIl2cppLengthAccessor(uint offset, Il2CppBinary binary) { - return GetOffsetName(offset) == "length"; + return GetOffsetName(offset, binary) == "length"; } - public static bool IsAtLeastFirstItemPtr(uint offset) + public static bool IsAtLeastFirstItemPtr(uint offset, Il2CppBinary binary) { - return offset >= FirstItemOffset; + return offset >= GetFirstItemOffset(binary); } public class UsefulOffset(string name, uint offset, Type type, bool is32Bit) diff --git a/Cpp2IL.Core/Il2CppClassUsefulOffsets.cs b/Cpp2IL.Core/Il2CppClassUsefulOffsets.cs index b1f9088c0..c90ed3999 100644 --- a/Cpp2IL.Core/Il2CppClassUsefulOffsets.cs +++ b/Cpp2IL.Core/Il2CppClassUsefulOffsets.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using LibCpp2IL; @@ -10,10 +10,28 @@ public static class Il2CppClassUsefulOffsets public const int X86_INTERFACE_OFFSETS_OFFSET = 0x50; public const int X86_64_INTERFACE_OFFSETS_OFFSET = 0xB0; - private static readonly int V24_2_VTABLE_OFFSET = LibCpp2IlMain.Binary!.is32Bit ? 0x999 /*TODO*/ : 0x138; - private static readonly int PRE_24_2_VTABLE_OFFSET = LibCpp2IlMain.Binary.is32Bit ? 0x999 /*TODO*/ : 0x128; + public static int GetVtableOffset(Il2CppBinary binary, float metadataVersion) + { + var v24_2_vtableOffset = binary.is32Bit ? 0x999 /*TODO*/ : 0x138; + var pre24_2_vtableOffset = binary.is32Bit ? 0x999 /*TODO*/ : 0x128; + + return metadataVersion >= 24.2f ? v24_2_vtableOffset : pre24_2_vtableOffset; + } - public static readonly int VTABLE_OFFSET = LibCpp2IlMain.MetadataVersion >= 24.2 ? V24_2_VTABLE_OFFSET : PRE_24_2_VTABLE_OFFSET; + // Keep VTABLE_OFFSET as a convenience for code that still needs a static reference. + // This is initialized lazily from the first call site that has context. + private static int? _vtableOffset; + + public static int VTABLE_OFFSET + { + get => _vtableOffset ?? throw new InvalidOperationException("VTABLE_OFFSET has not been initialized. Call InitVtableOffset first."); + private set => _vtableOffset = value; + } + + public static void InitVtableOffset(Il2CppBinary binary, float metadataVersion) + { + VTABLE_OFFSET = GetVtableOffset(binary, metadataVersion); + } public static readonly List UsefulOffsets = [ @@ -37,39 +55,39 @@ public static class Il2CppClassUsefulOffsets new UsefulOffset("vtable", 0x138, typeof(IntPtr), false) ]; - public static bool IsStaticFieldsPtr(uint offset) + public static bool IsStaticFieldsPtr(uint offset, Il2CppBinary binary) { - return GetOffsetName(offset) == "static_fields"; + return GetOffsetName(offset, binary) == "static_fields"; } - public static bool IsInterfaceOffsetsPtr(uint offset) + public static bool IsInterfaceOffsetsPtr(uint offset, Il2CppBinary binary) { - return GetOffsetName(offset) == "interfaceOffsets"; + return GetOffsetName(offset, binary) == "interfaceOffsets"; } - public static bool IsInterfaceOffsetsCount(uint offset) + public static bool IsInterfaceOffsetsCount(uint offset, Il2CppBinary binary) { - return GetOffsetName(offset) == "interface_offsets_count"; + return GetOffsetName(offset, binary) == "interface_offsets_count"; } - public static bool IsRGCTXDataPtr(uint offset) + public static bool IsRGCTXDataPtr(uint offset, Il2CppBinary binary) { - return GetOffsetName(offset) == "rgctx_data"; + return GetOffsetName(offset, binary) == "rgctx_data"; } - public static bool IsElementTypePtr(uint offset) + public static bool IsElementTypePtr(uint offset, Il2CppBinary binary) { - return GetOffsetName(offset) == "elementType"; + return GetOffsetName(offset, binary) == "elementType"; } - public static bool IsPointerIntoVtable(uint offset) + public static bool IsPointerIntoVtable(uint offset, Il2CppBinary binary, float metadataVersion) { - return offset >= VTABLE_OFFSET; + return offset >= GetVtableOffset(binary, metadataVersion); } - public static string? GetOffsetName(uint offset) + public static string? GetOffsetName(uint offset, Il2CppBinary binary) { - var is32Bit = LibCpp2IlMain.Binary!.is32Bit; + var is32Bit = binary.is32Bit; return UsefulOffsets.FirstOrDefault(o => o.is32Bit == is32Bit && o.offset == offset)?.name; } diff --git a/Cpp2IL.Core/Il2CppMethodDefinitionUsefulOffsets.cs b/Cpp2IL.Core/Il2CppMethodDefinitionUsefulOffsets.cs index 75cb57f6f..ac825b338 100644 --- a/Cpp2IL.Core/Il2CppMethodDefinitionUsefulOffsets.cs +++ b/Cpp2IL.Core/Il2CppMethodDefinitionUsefulOffsets.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using LibCpp2IL; @@ -22,13 +22,13 @@ public static class Il2CppMethodDefinitionUsefulOffsets new UsefulOffset("methodPtr", 0x30, typeof(IntPtr), false) ]; - public static bool IsSlotOffset(uint offset) => GetOffsetName(offset) == "slot"; - public static bool IsKlassPtr(uint offset) => GetOffsetName(offset) == "klass"; - public static bool IsMethodPtr(uint offset) => GetOffsetName(offset) == "methodPtr"; + public static bool IsSlotOffset(uint offset, Il2CppBinary binary) => GetOffsetName(offset, binary) == "slot"; + public static bool IsKlassPtr(uint offset, Il2CppBinary binary) => GetOffsetName(offset, binary) == "klass"; + public static bool IsMethodPtr(uint offset, Il2CppBinary binary) => GetOffsetName(offset, binary) == "methodPtr"; - public static string? GetOffsetName(uint offset) + public static string? GetOffsetName(uint offset, Il2CppBinary binary) { - var is32Bit = LibCpp2IlMain.Binary!.is32Bit; + var is32Bit = binary.is32Bit; return UsefulOffsets.FirstOrDefault(o => o.is32Bit == is32Bit && o.offset == offset)?.name; } diff --git a/Cpp2IL.Core/Il2CppMethodInfoUsefulOffsets.cs b/Cpp2IL.Core/Il2CppMethodInfoUsefulOffsets.cs index 5ecd18204..6a5529733 100644 --- a/Cpp2IL.Core/Il2CppMethodInfoUsefulOffsets.cs +++ b/Cpp2IL.Core/Il2CppMethodInfoUsefulOffsets.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using LibCpp2IL; @@ -18,14 +18,14 @@ public static class Il2CppMethodInfoUsefulOffsets new UsefulOffset("klass", X86_64_KLASS_OFFSET, typeof(IntPtr), false) ]; - public static bool IsKlassPtr(uint offset) + public static bool IsKlassPtr(uint offset, Il2CppBinary binary) { - return GetOffsetName(offset) == "klass"; + return GetOffsetName(offset, binary) == "klass"; } - public static string? GetOffsetName(uint offset) + public static string? GetOffsetName(uint offset, Il2CppBinary binary) { - var is32Bit = LibCpp2IlMain.Binary!.is32Bit; + var is32Bit = binary.is32Bit; return UsefulOffsets.FirstOrDefault(o => o.is32Bit == is32Bit && o.offset == offset)?.name; } diff --git a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs index 74dafcaea..8d70ddfb2 100644 --- a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs @@ -8,7 +8,6 @@ using Cpp2IL.Core.ISIL; using Cpp2IL.Core.Model.Contexts; using Cpp2IL.Core.Utils; -using LibCpp2IL; namespace Cpp2IL.Core.InstructionSets; @@ -20,15 +19,15 @@ public override Memory GetRawBytesForMethod(MethodAnalysisContext context, if (true || context is not ConcreteGenericMethodAnalysisContext) { //Managed method or attr gen => grab raw byte range between a and b - var startOfNextFunction = (int)MiscUtils.GetAddressOfNextFunctionStart(context.UnderlyingPointer) - 1; + var startOfNextFunction = (int)MiscUtils.GetAddressOfNextFunctionStart(context.UnderlyingPointer, context.AppContext.Binary) - 1; var ptrAsInt = (int)context.UnderlyingPointer; var count = startOfNextFunction - ptrAsInt; if (startOfNextFunction > 0) - return LibCpp2IlMain.Binary!.GetRawBinaryContent().AsMemory(ptrAsInt, count); + return context.AppContext.Binary.GetRawBinaryContent().AsMemory(ptrAsInt, count); } - var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); + var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer); return instructions.SelectMany(i => i.Bytes).ToArray(); } @@ -44,7 +43,7 @@ public override string PrintAssembly(MethodAnalysisContext context) { var sb = new StringBuilder(); - var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); + var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer); var first = true; foreach (var instruction in instructions) diff --git a/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs b/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs index 8f621dc7f..fce601348 100644 --- a/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs @@ -15,10 +15,10 @@ public class ArmV7InstructionSet : Cpp2IlInstructionSet { public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) { - if (ArmV7Utils.TryGetMethodBodyBytesFast(context.UnderlyingPointer, context is AttributeGeneratorMethodAnalysisContext) is { } ret) + if (ArmV7Utils.TryGetMethodBodyBytesFast(context.AppContext.Binary, context.UnderlyingPointer, context is AttributeGeneratorMethodAnalysisContext) is { } ret) return ret; - var instructions = ArmV7Utils.GetArmV7MethodBodyAtVirtualAddress(context.UnderlyingPointer); + var instructions = ArmV7Utils.GetArmV7MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer); return instructions.SelectMany(i => i.Bytes).ToArray(); } @@ -38,7 +38,7 @@ public override string PrintAssembly(MethodAnalysisContext context) { var sb = new StringBuilder(); - var instructions = ArmV7Utils.GetArmV7MethodBodyAtVirtualAddress(context.UnderlyingPointer); + var instructions = ArmV7Utils.GetArmV7MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer); var first = true; foreach (var instruction in instructions) diff --git a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs b/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs index f2ac95c45..9833a38bf 100644 --- a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs @@ -9,7 +9,6 @@ using Cpp2IL.Core.Model.Contexts; using Cpp2IL.Core.Utils; using Disarm.InternalDisassembly; -using LibCpp2IL; namespace Cpp2IL.Core.InstructionSets; @@ -23,15 +22,15 @@ public override Memory GetRawBytesForMethod(MethodAnalysisContext context, if (context is not ConcreteGenericMethodAnalysisContext) { //Managed method or attr gen => grab raw byte range between a and b - var startOfNextFunction = (int)MiscUtils.GetAddressOfNextFunctionStart(context.UnderlyingPointer); + var startOfNextFunction = (int)MiscUtils.GetAddressOfNextFunctionStart(context.UnderlyingPointer, context.AppContext.Binary); var ptrAsInt = (int)context.UnderlyingPointer; var count = startOfNextFunction - ptrAsInt; if (startOfNextFunction > 0) - return LibCpp2IlMain.Binary!.GetRawBinaryContent().AsMemory(ptrAsInt, count); + return context.AppContext.Binary.GetRawBinaryContent().AsMemory(ptrAsInt, count); } - var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); + var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer); var lastInsn = result.LastValid(); var start = (int)context.AppContext.Binary.MapVirtualAddressToRaw(context.UnderlyingPointer); @@ -48,7 +47,7 @@ public override Memory GetRawBytesForMethod(MethodAnalysisContext context, public override List GetIsilFromMethod(MethodAnalysisContext context) { - var insns = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); + var insns = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer); var builder = new IsilBuilder(); diff --git a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs b/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs index 0484b2ce7..7f2b8dfab 100644 --- a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs @@ -33,7 +33,7 @@ public static string FormatInstruction(Instruction instruction) } } - public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) => X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, isAttributeGenerator); + public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) => X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, isAttributeGenerator, context.AppContext.Binary); public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance() => new X86KeyFunctionAddresses(); @@ -41,7 +41,7 @@ public override string PrintAssembly(MethodAnalysisContext context) { lock (Formatter) { - var insns = X86Utils.Iterate(X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, false), context.UnderlyingPointer); + var insns = X86Utils.Iterate(X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, false, context.AppContext.Binary), context.UnderlyingPointer, context.AppContext.Binary); return string.Join("\n", insns.Select(FormatInstructionInternal)); } diff --git a/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs index 5c5bf7dc8..fbe9b16a5 100644 --- a/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs @@ -108,7 +108,8 @@ public ApplicationAnalysisContext(Il2CppBinary binary, Il2CppMetadata metadata) } SystemTypes = new(this); - + + Il2CppClassUsefulOffsets.InitVtableOffset(binary, MetadataVersion); MiscUtils.InitFunctionStarts(this); PopulateMethodsByAddressTable(); diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index 6422da0d3..fd7d50b04 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -36,7 +36,7 @@ public class MethodAnalysisContext : HasGenericParameters, IMethodInfoProvider /// public virtual ulong UnderlyingPointer => Definition?.MethodPointer ?? throw new("Subclasses of MethodAnalysisContext should override UnderlyingPointer"); - public ulong Rva => UnderlyingPointer == 0 || LibCpp2IlMain.Binary == null ? 0 : LibCpp2IlMain.Binary.GetRva(UnderlyingPointer); + public ulong Rva => UnderlyingPointer == 0 ? 0 : AppContext.Binary.GetRva(UnderlyingPointer); /// /// The raw method body as machine code in the active instruction set. diff --git a/Cpp2IL.Core/Model/Contexts/NativeMethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/NativeMethodAnalysisContext.cs index b918f3551..3f6be677d 100644 --- a/Cpp2IL.Core/Model/Contexts/NativeMethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/NativeMethodAnalysisContext.cs @@ -1,6 +1,5 @@ using System; using System.Reflection; -using LibCpp2IL; namespace Cpp2IL.Core.Model.Contexts; @@ -29,7 +28,7 @@ public NativeMethodAnalysisContext(TypeAnalysisContext parent, ulong address, bo isVoid = voidReturn; UnderlyingPointer = address; - if (LibCpp2IlMain.Binary?.TryGetExportedFunctionName(UnderlyingPointer, out var name) ?? false) + if (AppContext.Binary.TryGetExportedFunctionName(UnderlyingPointer, out var name)) { DefaultName = name; } diff --git a/Cpp2IL.Core/OutputFormats/WasmMappingOutputFormat.cs b/Cpp2IL.Core/OutputFormats/WasmMappingOutputFormat.cs index 71bff8b2c..34071515e 100644 --- a/Cpp2IL.Core/OutputFormats/WasmMappingOutputFormat.cs +++ b/Cpp2IL.Core/OutputFormats/WasmMappingOutputFormat.cs @@ -45,7 +45,7 @@ public override void DoOutput(ApplicationAnalysisContext context, string outputR try { var wasmDef = WasmUtils.GetWasmDefinition(methodAnalysisContext); - var ghidraName = WasmUtils.GetGhidraFunctionName(wasmDef); + var ghidraName = WasmUtils.GetGhidraFunctionName(context.Binary, wasmDef); output.AppendLine(ghidraName); } diff --git a/Cpp2IL.Core/OutputFormats/WasmNameSectionOutputFormat.cs b/Cpp2IL.Core/OutputFormats/WasmNameSectionOutputFormat.cs index 9e6a58da8..98abd2231 100644 --- a/Cpp2IL.Core/OutputFormats/WasmNameSectionOutputFormat.cs +++ b/Cpp2IL.Core/OutputFormats/WasmNameSectionOutputFormat.cs @@ -52,7 +52,7 @@ public override void DoOutput(ApplicationAnalysisContext context, string outputR .Where(v => v.definition is not null) .Select(v => { - var trueParamCount = v.definition!.GetType((WasmFile)LibCpp2IlMain.Binary!).ParamTypes.Length; + var trueParamCount = v.definition!.GetType((WasmFile)context.Binary).ParamTypes.Length; // Also see WasmUtils.BuildSignature var parameters = v.method.Parameters.Select(param => param.Name).ToList(); diff --git a/Cpp2IL.Core/Utils/Arm64Utils.cs b/Cpp2IL.Core/Utils/Arm64Utils.cs index 044c4e4ab..1f5be03d1 100644 --- a/Cpp2IL.Core/Utils/Arm64Utils.cs +++ b/Cpp2IL.Core/Utils/Arm64Utils.cs @@ -90,37 +90,37 @@ public static string GetRegisterNameNew(Arm64RegisterId registerId) return ret; } - private static void InitArm64Decompilation() + private static void InitArm64Decompilation(Il2CppBinary binary) { - var disassembler = CapstoneDisassembler.CreateArm64Disassembler(LibCpp2IlMain.Binary!.IsBigEndian ? Arm64DisassembleMode.BigEndian : Arm64DisassembleMode.LittleEndian); + var disassembler = CapstoneDisassembler.CreateArm64Disassembler(binary.IsBigEndian ? Arm64DisassembleMode.BigEndian : Arm64DisassembleMode.LittleEndian); disassembler.EnableInstructionDetails = true; disassembler.EnableSkipDataMode = true; disassembler.DisassembleSyntax = DisassembleSyntax.Intel; _arm64Disassembler = disassembler; } - public static List GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1) + public static List GetArm64MethodBodyAtVirtualAddress(Il2CppBinary binary, ulong virtAddress, bool managed = true, int count = -1) { if (_arm64Disassembler == null) - InitArm64Decompilation(); + InitArm64Decompilation(binary); //We can't use CppMethodBodyBytes to get the byte array, because ARMv7 doesn't have filler bytes like x86 does. //So we can't work out the end of the method. //But we can find the start of the next one! (If managed) if (managed) { - var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress); + var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress, binary); //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end if (startOfNext > 0) { - var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext); + var rawStartOfNextMethod = binary.MapVirtualAddressToRaw(startOfNext); - var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtAddress); + var rawStart = binary.MapVirtualAddressToRaw(virtAddress); if (rawStartOfNextMethod < rawStart) - rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength; + rawStartOfNextMethod = binary.RawLength; - byte[] bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod); + byte[] bytes = binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod); var iter = _arm64Disassembler!.Iterate(bytes, (long)virtAddress); if (count > 0) @@ -131,8 +131,8 @@ public static List GetArm64MethodBodyAtVirtualAddress(ulong vi } //Unmanaged function, look for first b or bl - var pos = (int)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtAddress); - var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent(); + var pos = (int)binary.MapVirtualAddressToRaw(virtAddress); + var allBytes = binary.GetRawBinaryContent(); List ret = []; while (!ret.Any(i => i.Mnemonic is "b" or ".byte") && (count == -1 || ret.Count < count)) diff --git a/Cpp2IL.Core/Utils/ArmV7Utils.cs b/Cpp2IL.Core/Utils/ArmV7Utils.cs index 06a11a594..d93eed0a6 100644 --- a/Cpp2IL.Core/Utils/ArmV7Utils.cs +++ b/Cpp2IL.Core/Utils/ArmV7Utils.cs @@ -20,9 +20,9 @@ private static void InitArmDecompilation() _armDisassembler = disassembler; } - public static byte[]? TryGetMethodBodyBytesFast(ulong virtAddress, bool isCAGen) + public static byte[]? TryGetMethodBodyBytesFast(Il2CppBinary binary, ulong virtAddress, bool isCAGen) { - var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress); + var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress, binary); var length = (startOfNext - virtAddress); if (isCAGen && length > 50_000) @@ -32,16 +32,16 @@ private static void InitArmDecompilation() //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end return null; - var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext); + var rawStartOfNextMethod = binary.MapVirtualAddressToRaw(startOfNext); - var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtAddress); + var rawStart = binary.MapVirtualAddressToRaw(virtAddress); if (rawStartOfNextMethod < rawStart) - rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength; + rawStartOfNextMethod = binary.RawLength; - return LibCpp2IlMain.Binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod); + return binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod); } - public static List GetArmV7MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1) + public static List GetArmV7MethodBodyAtVirtualAddress(Il2CppBinary binary, ulong virtAddress, bool managed = true, int count = -1) { if (_armDisassembler == null) InitArmDecompilation(); @@ -51,18 +51,18 @@ public static List GetArmV7MethodBodyAtVirtualAddress(ulong virt //But we can find the start of the next one! (If managed) if (managed) { - var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress); + var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress, binary); //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end if (startOfNext > 0) { - var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext); + var rawStartOfNextMethod = binary.MapVirtualAddressToRaw(startOfNext); - var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtAddress); + var rawStart = binary.MapVirtualAddressToRaw(virtAddress); if (rawStartOfNextMethod < rawStart) - rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength; + rawStartOfNextMethod = binary.RawLength; - byte[] bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod); + byte[] bytes = binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod); var iter = _armDisassembler!.Iterate(bytes, (long)virtAddress); if (count > 0) @@ -73,8 +73,8 @@ public static List GetArmV7MethodBodyAtVirtualAddress(ulong virt } //Unmanaged function, look for first b or bl - var pos = (int)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtAddress); - var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent(); + var pos = (int)binary.MapVirtualAddressToRaw(virtAddress); + var allBytes = binary.GetRawBinaryContent(); List ret = []; while (!ret.Any(i => i.Mnemonic is "b" or ".byte") && (count == -1 || ret.Count < count)) diff --git a/Cpp2IL.Core/Utils/MiscUtils.cs b/Cpp2IL.Core/Utils/MiscUtils.cs index c720ede44..d34215f4d 100644 --- a/Cpp2IL.Core/Utils/MiscUtils.cs +++ b/Cpp2IL.Core/Utils/MiscUtils.cs @@ -47,7 +47,7 @@ internal static void Reset() _allKnownFunctionStarts = null; } - internal static void Init() + internal static void Init(Il2CppBinary binary) { _primitiveSizes = new(14) { @@ -63,8 +63,8 @@ internal static void Init() { "Int64", 8 }, { "UInt64", 8 }, { "Double", 8 }, - { "IntPtr", LibCpp2IlMain.Binary!.is32Bit ? 4UL : 8UL }, - { "UIntPtr", LibCpp2IlMain.Binary.is32Bit ? 4UL : 8UL }, + { "IntPtr", binary.is32Bit ? 4UL : 8UL }, + { "UIntPtr", binary.is32Bit ? 4UL : 8UL }, }; } @@ -146,9 +146,9 @@ public static int GetSlotNum(int offset) return -1; } - public static int GetPointerSizeBytes() + public static int GetPointerSizeBytes(Il2CppBinary binary) { - return LibCpp2IlMain.Binary!.is32Bit ? 4 : 8; + return binary.is32Bit ? 4 : 8; } internal static byte[] RawBytes(IConvertible original) => @@ -182,7 +182,7 @@ internal static void InitFunctionStarts(ApplicationAnalysisContext appContext) } //TODO: End - public static ulong GetAddressOfNextFunctionStart(ulong current) + public static ulong GetAddressOfNextFunctionStart(ulong current, Il2CppBinary binary) { if (_allKnownFunctionStarts == null) throw new("Function starts not initialized!"); @@ -224,7 +224,7 @@ public static ulong GetAddressOfNextFunctionStart(ulong current) if (ret <= current && upper == _allKnownFunctionStarts.Count - 1) return 0; - if (!LibCpp2IlMain.Binary!.TryMapVirtualAddressToRaw(ret, out _)) + if (!binary.TryMapVirtualAddressToRaw(ret, out _)) return 0; return ret; diff --git a/Cpp2IL.Core/Utils/NewArm64Utils.cs b/Cpp2IL.Core/Utils/NewArm64Utils.cs index 9a5397ba9..76ae5f899 100644 --- a/Cpp2IL.Core/Utils/NewArm64Utils.cs +++ b/Cpp2IL.Core/Utils/NewArm64Utils.cs @@ -8,30 +8,30 @@ namespace Cpp2IL.Core.Utils; public static class NewArm64Utils { - public static List GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1) + public static List GetArm64MethodBodyAtVirtualAddress(Il2CppBinary binary, ulong virtAddress, bool managed = true, int count = -1) { if (managed) { - var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress); + var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress, binary); //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end if (startOfNext > 0) { - var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext); + var rawStartOfNextMethod = binary.MapVirtualAddressToRaw(startOfNext); - var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtAddress); + var rawStart = binary.MapVirtualAddressToRaw(virtAddress); if (rawStartOfNextMethod < rawStart) - rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength; + rawStartOfNextMethod = binary.RawLength; - var bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().AsSpan((int)rawStart, (int)(rawStartOfNextMethod - rawStart)); + var bytes = binary.GetRawBinaryContent().AsSpan((int)rawStart, (int)(rawStartOfNextMethod - rawStart)); return Disassemble(bytes, virtAddress); } } //Unmanaged function, look for first b - var pos = (int)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtAddress); - var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent(); + var pos = (int)binary.MapVirtualAddressToRaw(virtAddress); + var allBytes = binary.GetRawBinaryContent(); var span = allBytes.AsSpan(pos, 4); List ret = []; diff --git a/Cpp2IL.Core/Utils/WasmUtils.cs b/Cpp2IL.Core/Utils/WasmUtils.cs index 117645a99..f0686f7df 100644 --- a/Cpp2IL.Core/Utils/WasmUtils.cs +++ b/Cpp2IL.Core/Utils/WasmUtils.cs @@ -76,10 +76,10 @@ private static string GetSignatureLetter(TypeAnalysisContext type, bool isRefOrO }; } - public static string GetGhidraFunctionName(WasmFunctionDefinition functionDefinition) + public static string GetGhidraFunctionName(Il2CppBinary binary, WasmFunctionDefinition functionDefinition) { var index = functionDefinition.IsImport - ? ((WasmFile)LibCpp2IlMain.Binary!).FunctionTable.IndexOf(functionDefinition) + ? ((WasmFile)binary).FunctionTable.IndexOf(functionDefinition) : functionDefinition.FunctionTableIndex; return $"unnamed_function_{index}"; @@ -101,12 +101,12 @@ public static WasmFunctionDefinition GetWasmDefinition(MethodAnalysisContext con { if (context.Definition == null) throw new($"Attempted to get wasm definition for probably-injected method context: {context}"); - + //First, we have to calculate the signature var signature = BuildSignature(context); try { - return ((WasmFile)LibCpp2IlMain.Binary!).GetFunctionFromIndexAndSignature(context.Definition.MethodPointer, signature); + return ((WasmFile)context.AppContext.Binary).GetFunctionFromIndexAndSignature(context.Definition.MethodPointer, signature); } catch (Exception e) { diff --git a/Cpp2IL.Core/Utils/X86Utils.cs b/Cpp2IL.Core/Utils/X86Utils.cs index 8f20aff9f..ae31a3473 100644 --- a/Cpp2IL.Core/Utils/X86Utils.cs +++ b/Cpp2IL.Core/Utils/X86Utils.cs @@ -18,13 +18,13 @@ public static class X86Utils private static readonly ConcurrentDictionary CachedX86RegNamesNew = new(); //TODO Consider implementing a CodeReader for Memory - public static InstructionList Disassemble(Memory bytes, ulong methodBase) - => Disassemble(bytes.ToArray(), methodBase); + public static InstructionList Disassemble(Memory bytes, ulong methodBase, Il2CppBinary binary) + => Disassemble(bytes.ToArray(), methodBase, binary); - public static InstructionList Disassemble(byte[] bytes, ulong methodBase) + public static InstructionList Disassemble(byte[] bytes, ulong methodBase, Il2CppBinary binary) { var codeReader = new ByteArrayCodeReader(bytes); - var decoder = Decoder.Create(LibCpp2IlMain.Binary!.is32Bit ? 32 : 64, codeReader); + var decoder = Decoder.Create(binary.is32Bit ? 32 : 64, codeReader); decoder.IP = methodBase; var instructions = new InstructionList(); var endRip = decoder.IP + (uint)bytes.Length; @@ -37,18 +37,18 @@ public static InstructionList Disassemble(byte[] bytes, ulong methodBase) public static InstructionList Disassemble(MethodAnalysisContext context) { - return Disassemble(context.RawBytes, context.UnderlyingPointer); + return Disassemble(context.RawBytes, context.UnderlyingPointer, context.AppContext.Binary); } - public static IEnumerable Iterate(Memory bytes, ulong methodBase) + public static IEnumerable Iterate(Memory bytes, ulong methodBase, Il2CppBinary binary) { - return Iterate(bytes.AsEnumerable(), methodBase); + return Iterate(bytes.AsEnumerable(), methodBase, binary); } - public static IEnumerable Iterate(IEnumerable bytes, ulong methodBase) + public static IEnumerable Iterate(IEnumerable bytes, ulong methodBase, Il2CppBinary binary) { var codeReader = new EnumerableCodeReader(bytes); - var decoder = Decoder.Create(LibCpp2IlMain.Binary!.is32Bit ? 32 : 64, codeReader); + var decoder = Decoder.Create(binary.is32Bit ? 32 : 64, codeReader); decoder.IP = methodBase; decoder.Decode(out var instruction); @@ -61,30 +61,30 @@ public static IEnumerable Iterate(IEnumerable bytes, ulong me public static IEnumerable Iterate(MethodAnalysisContext context) { - return Iterate(context.RawBytes, context.UnderlyingPointer); + return Iterate(context.RawBytes, context.UnderlyingPointer, context.AppContext.Binary); } - public static Memory GetRawManagedOrCaCacheGenMethodBody(ulong ptr, bool isCaGen) + public static Memory GetRawManagedOrCaCacheGenMethodBody(ulong ptr, bool isCaGen, Il2CppBinary binary) { - var rawAddr = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(ptr, false); + var rawAddr = binary.MapVirtualAddressToRaw(ptr, false); if (rawAddr <= 0) return Memory.Empty; - var virtStartNextFunc = MiscUtils.GetAddressOfNextFunctionStart(ptr); + var virtStartNextFunc = MiscUtils.GetAddressOfNextFunctionStart(ptr, binary); if (virtStartNextFunc == 0 || (isCaGen && virtStartNextFunc - ptr > 50000)) { - GetMethodBodyAtVirtAddressNew(ptr, false, out var ret); + GetMethodBodyAtVirtAddressNew(ptr, false, binary, out var ret); return ret; } - var ra2 = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtStartNextFunc, false); + var ra2 = binary.MapVirtualAddressToRaw(virtStartNextFunc, false); if (ra2 <= 0) { //Don't have a known end point => fall back - GetMethodBodyAtVirtAddressNew(ptr, false, out var ret); + GetMethodBodyAtVirtAddressNew(ptr, false, binary, out var ret); return ret; } @@ -93,18 +93,18 @@ public static Memory GetRawManagedOrCaCacheGenMethodBody(ulong ptr, bool i if (startOfNextFunc < rawAddr) { Logger.WarnNewline($"StartOfNextFunc returned va 0x{virtStartNextFunc:X}, raw address 0x{startOfNextFunc:X}, for raw address 0x{rawAddr:X}. It should be more than raw address. Falling back to manual, slow, decompiler-based approach."); - GetMethodBodyAtVirtAddressNew(ptr, false, out var ret); + GetMethodBodyAtVirtAddressNew(ptr, false, binary, out var ret); return ret; } - var rawArray = LibCpp2IlMain.Binary.GetRawBinaryContent(); + var rawArray = binary.GetRawBinaryContent(); var lastPos = startOfNextFunc - 1; if (lastPos >= rawArray.Length) { Logger.WarnNewline($"StartOfNextFunc returned va 0x{virtStartNextFunc:X}, raw address 0x{startOfNextFunc:X}, for raw address 0x{rawAddr:X}. LastPos should be less than the raw array length. Falling back to manual, slow, decompiler-based approach."); - GetMethodBodyAtVirtAddressNew(ptr, false, out var ret); + GetMethodBodyAtVirtAddressNew(ptr, false, binary, out var ret); return ret; } @@ -115,7 +115,7 @@ public static Memory GetRawManagedOrCaCacheGenMethodBody(ulong ptr, bool i if (TryFindJumpTableStart(memArray, ptr, virtStartNextFunc, out var startIndex, out var jumpTableElements)) { // TODO: Figure out what to do with jumpTableElements, how do we handle returning it from this function? - // we might need to return the address it was found at in TryFindJumpTableStart function too + // we might need to return the address it was found at in TryFindJumpTableStart function too // Should clean up the way we handle the bytes array too /* foreach (var element in jumpTableElements) @@ -152,18 +152,18 @@ private static bool TryFindJumpTableStart(Memory methodBytes, ulong method return foundTable; } - public static InstructionList GetMethodBodyAtVirtAddressNew(ulong addr, bool peek) => GetMethodBodyAtVirtAddressNew(addr, peek, out _); + public static InstructionList GetMethodBodyAtVirtAddressNew(ulong addr, bool peek, Il2CppBinary binary) => GetMethodBodyAtVirtAddressNew(addr, peek, binary, out _); - public static InstructionList GetMethodBodyAtVirtAddressNew(ulong addr, bool peek, out byte[] rawBytes) + public static InstructionList GetMethodBodyAtVirtAddressNew(ulong addr, bool peek, Il2CppBinary binary, out byte[] rawBytes) { var functionStart = addr; var ret = new InstructionList(); var con = true; var buff = new List(); - var rawAddr = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(addr); - var startOfNextFunc = MiscUtils.GetAddressOfNextFunctionStart(addr); + var rawAddr = binary.MapVirtualAddressToRaw(addr); + var startOfNextFunc = MiscUtils.GetAddressOfNextFunctionStart(addr, binary); - if (rawAddr < 0 || rawAddr >= LibCpp2IlMain.Binary.RawLength) + if (rawAddr < 0 || rawAddr >= binary.RawLength) { Logger.ErrorNewline($"Invalid call to GetMethodBodyAtVirtAddressNew, virt addr {addr} resolves to raw {rawAddr} which is out of bounds"); rawBytes = []; @@ -175,9 +175,9 @@ public static InstructionList GetMethodBodyAtVirtAddressNew(ulong addr, bool pee if (addr >= startOfNextFunc) break; - buff.Add(LibCpp2IlMain.Binary.GetByteAtRawAddress((ulong)rawAddr)); + buff.Add(binary.GetByteAtRawAddress((ulong)rawAddr)); - ret = X86Utils.Disassemble(buff.ToArray(), functionStart); + ret = X86Utils.Disassemble(buff.ToArray(), functionStart, binary); if (ret.All(i => i.Mnemonic != Mnemonic.INVALID) && ret.Any(i => i.Code == Code.Int3)) con = false; @@ -189,7 +189,7 @@ public static InstructionList GetMethodBodyAtVirtAddressNew(ulong addr, bool pee addr++; rawAddr++; - if (rawAddr >= LibCpp2IlMain.Binary.RawLength) + if (rawAddr >= binary.RawLength) con = false; } diff --git a/Cpp2IL.Plugin.StrippedCodeRegSupport/StrippedCodeRegSupportPlugin.cs b/Cpp2IL.Plugin.StrippedCodeRegSupport/StrippedCodeRegSupportPlugin.cs index 8d7b2dbb2..58c53f886 100644 --- a/Cpp2IL.Plugin.StrippedCodeRegSupport/StrippedCodeRegSupportPlugin.cs +++ b/Cpp2IL.Plugin.StrippedCodeRegSupport/StrippedCodeRegSupportPlugin.cs @@ -38,7 +38,7 @@ private void OnReadFail(Il2CppBinary binary, Il2CppMetadata metadata, ref Il2Cpp //All we NEED to find is pCodegenModules - the rest of the CodeRegistration struct isn't critical to a successful dump. //We can piggyback off BinarySearcher: - var searcher = new BinarySearcher(binary, metadata.MethodDefinitionCount, metadata.TypeDefinitionCount); + var searcher = new BinarySearcher(binary, metadata, metadata.MethodDefinitionCount, metadata.TypeDefinitionCount); var mscorlibs = searcher.FindAllStrings("mscorlib.dll\0").Select(idx => binary.MapRawAddressToVirtual(idx)).ToList(); diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index a003194c8..087464e30 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -12,7 +12,7 @@ namespace LibCpp2IL; -public class BinarySearcher(Il2CppBinary binary, int methodCount, int typeDefinitionsCount) +public class BinarySearcher(Il2CppBinary binary, Il2CppMetadata metadata, int methodCount, int typeDefinitionsCount) { private readonly byte[] _binaryBytes = binary.GetRawBinaryContent(); @@ -115,10 +115,10 @@ public ulong FindCodeRegistrationPre2019() LibLogger.VerboseNewline($"\t\t\tChecking for CodeRegistration at virtual address 0x{va:x}..."); var cr = binary.ReadReadableAtVirtualAddress(va); - if ((long)cr.customAttributeCount == LibCpp2IlMain.TheMetadata!.attributeTypeRanges!.Count) + if ((long)cr.customAttributeCount == metadata.attributeTypeRanges!.Count) return va; - LibLogger.VerboseNewline($"\t\t\t\tNot a valid CodeRegistration - custom attribute count is {cr.customAttributeCount}, expecting {LibCpp2IlMain.TheMetadata!.attributeTypeRanges.Count}"); + LibLogger.VerboseNewline($"\t\t\t\tNot a valid CodeRegistration - custom attribute count is {cr.customAttributeCount}, expecting {metadata.attributeTypeRanges.Count}"); } return 0; @@ -161,9 +161,9 @@ internal ulong FindCodeRegistrationPost2019(Il2CppMetadata metadata) else { //but in v27 it's close to the LAST codegen module (winrt.dll is an exception), so we need to work back until we find an xref. - var sanityCheckNumberOfModules = Math.Min(400, LibCpp2IlMain.TheMetadata!.imageDefinitions.Length); + var sanityCheckNumberOfModules = Math.Min(400, metadata.imageDefinitions.Length); var pSomewhereInCodegenModules = pMscorlibCodegenEntryInCodegenModulesList.AsEnumerable(); - var numModuleDefs = LibCpp2IlMain.TheMetadata!.imageDefinitions.Length; + var numModuleDefs = metadata.imageDefinitions.Length; var initialBacktrack = numModuleDefs - 10; pSomewhereInCodegenModules = pSomewhereInCodegenModules.Select(va => va - ptrSize * (ulong)initialBacktrack); @@ -183,7 +183,7 @@ internal ulong FindCodeRegistrationPost2019(Il2CppMetadata metadata) if (moduleCount < 0 || moduleCount > sanityCheckNumberOfModules) pCodegenModules = []; else - LibLogger.VerboseNewline($"\t\t\tFound valid address for pCodegenModules after a backtrack of {backtrack}, module count is {LibCpp2IlMain.TheMetadata!.imageDefinitions.Length}"); + LibLogger.VerboseNewline($"\t\t\tFound valid address for pCodegenModules after a backtrack of {backtrack}, module count is {metadata.imageDefinitions.Length}"); } else if (pCodegenModules.Count > 1) { @@ -230,9 +230,9 @@ internal ulong FindCodeRegistrationPost2019(Il2CppMetadata metadata) LibLogger.Verbose($"\t\t\tConsidering potential code registration at 0x{address:X}..."); - var codeReg = LibCpp2IlMain.Binary!.ReadReadableAtVirtualAddress(address); + var codeReg = binary.ReadReadableAtVirtualAddress(address); - var success = ValidateCodeRegistration(codeReg, fieldsByName); + var success = ValidateCodeRegistration(codeReg, fieldsByName, binary); if (success) { @@ -244,7 +244,7 @@ internal ulong FindCodeRegistrationPost2019(Il2CppMetadata metadata) return 0; } - public static bool ValidateCodeRegistration(Il2CppCodeRegistration codeReg, Dictionary fieldsByName) + public static bool ValidateCodeRegistration(Il2CppCodeRegistration codeReg, Dictionary fieldsByName, Il2CppBinary binaryToValidate) { var success = true; foreach (var keyValuePair in fieldsByName) @@ -267,7 +267,7 @@ public static bool ValidateCodeRegistration(Il2CppCodeRegistration codeReg, Dict else { //Pointer - if (!LibCpp2IlMain.Binary!.TryMapVirtualAddressToRaw(fieldValue, out _)) + if (!binaryToValidate.TryMapVirtualAddressToRaw(fieldValue, out _)) { LibLogger.VerboseNewline($"Rejected due to invalid pointer 0x{fieldValue:X} for field {keyValuePair.Key}"); success = false; @@ -287,9 +287,9 @@ public ulong FindMetadataRegistrationPre24_5() var bytesToSubtract = sizeOfMr - ptrSize * 4; - var potentialMetaRegPointers = MapOffsetsToVirt(FindAllBytes(BitConverter.GetBytes(LibCpp2IlMain.TheMetadata!.TypeDefinitionCount), 1)).ToList(); + var potentialMetaRegPointers = MapOffsetsToVirt(FindAllBytes(BitConverter.GetBytes(metadata.TypeDefinitionCount), 1)).ToList(); - LibLogger.VerboseNewline($"\t\t\tFound {potentialMetaRegPointers.Count} instances of the number of type defs, {LibCpp2IlMain.TheMetadata.TypeDefinitionCount}"); + LibLogger.VerboseNewline($"\t\t\tFound {potentialMetaRegPointers.Count} instances of the number of type defs, {metadata.TypeDefinitionCount}"); potentialMetaRegPointers = potentialMetaRegPointers.Select(p => p - bytesToSubtract).ToList(); @@ -297,13 +297,13 @@ public ulong FindMetadataRegistrationPre24_5() { var mr = binary.ReadReadableAtVirtualAddress(potentialMetaRegPointer); - if (mr.metadataUsagesCount == (ulong)LibCpp2IlMain.TheMetadata!.metadataUsageLists!.Length) + if (mr.metadataUsagesCount == (ulong)metadata.metadataUsageLists!.Length) { LibLogger.VerboseNewline($"\t\t\tFound and selected probably valid metadata registration at 0x{potentialMetaRegPointer:X}."); return potentialMetaRegPointer; } else - LibLogger.VerboseNewline($"\t\t\tSkipping 0x{potentialMetaRegPointer:X} as the metadata reg, metadata usage count was 0x{mr.metadataUsagesCount:X}, expecting 0x{LibCpp2IlMain.TheMetadata.metadataUsageLists.Length:X}"); + LibLogger.VerboseNewline($"\t\t\tSkipping 0x{potentialMetaRegPointer:X} as the metadata reg, metadata usage count was 0x{mr.metadataUsagesCount:X}, expecting 0x{metadata.metadataUsageLists.Length:X}"); } return 0; diff --git a/LibCpp2IL/BinaryStructures/Il2CppArrayType.cs b/LibCpp2IL/BinaryStructures/Il2CppArrayType.cs index e6d262403..9feddd228 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppArrayType.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppArrayType.cs @@ -4,9 +4,6 @@ namespace LibCpp2IL.BinaryStructures; public class Il2CppArrayType : ReadableClass { - // Populated by the caller after reading. - internal Il2CppBinary? OwningBinary { get; set; } - public ulong etype; public byte rank; public byte numsizes; @@ -18,7 +15,7 @@ public Il2CppType? ElementType { get { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; return binary == null ? null : binary.GetIl2CppTypeFromPointer(etype); } } diff --git a/LibCpp2IL/BinaryStructures/Il2CppCodeGenModule.cs b/LibCpp2IL/BinaryStructures/Il2CppCodeGenModule.cs index 89ace72c2..9cba1dd3a 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppCodeGenModule.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppCodeGenModule.cs @@ -34,15 +34,13 @@ public class Il2CppCodeGenModule : ReadableClass private string? _cachedName; - internal Il2CppBinary? OwningBinary { get; set; } - public string Name { get { if (_cachedName == null) { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) throw new InvalidOperationException("No binary context available to resolve Il2CppCodeGenModule.Name"); @@ -57,7 +55,7 @@ public Il2CppTokenRangePair[] RGCTXRanges { get { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) throw new InvalidOperationException("No binary context available to resolve Il2CppCodeGenModule.RGCTXRanges"); diff --git a/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs b/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs index b01e58297..e166610b1 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs @@ -5,10 +5,6 @@ namespace LibCpp2IL.BinaryStructures; public class Il2CppGenericClass : ReadableClass { - // Populated by the caller after reading. - internal Il2CppBinary? OwningBinary { get; set; } - internal Il2CppMetadata? OwningMetadata { get; set; } - [Version(Max = 24.5f)] public long TypeDefinitionIndex; /* the generic type definition */ [Version(Min = 27.0f)] public ulong V27TypePointer; @@ -16,7 +12,7 @@ public class Il2CppGenericClass : ReadableClass public Il2CppGenericContext Context = null!; /* a context that contains the type instantiation doesn't contain any method instantiation */ public ulong CachedClass; /* if present, the Il2CppClass corresponding to the instantiation. */ - private float EffectiveMetadataVersion => OwningMetadata?.MetadataVersion ?? LibCpp2IlMain.MetadataVersion; + private float EffectiveMetadataVersion => OwningMetadata?.MetadataVersion ?? MetadataVersion; public Il2CppTypeDefinition TypeDefinition { @@ -24,11 +20,7 @@ public Il2CppTypeDefinition TypeDefinition { if (EffectiveMetadataVersion < 27f) { - var md = OwningMetadata ?? LibCpp2IlMain.TheMetadata; - if (md == null) - throw new InvalidOperationException("No metadata context available for generic class type definition resolution."); - - return md.GetTypeDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int)TypeDefinitionIndex)); + return OwningMetadata!.GetTypeDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int)TypeDefinitionIndex)); } return V27BaseType!.AsClass(); @@ -42,14 +34,14 @@ public Il2CppType? V27BaseType if (EffectiveMetadataVersion < 27f) return null; - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) return null; var t = binary.ReadReadableAtVirtualAddress(V27TypePointer); t.OwningBinary = binary; - t.OwningMetadata = OwningMetadata ?? LibCpp2IlMain.TheMetadata; - t.Il2CppTypeHasNumMods5Bits ??= (OwningMetadata?.MetadataVersion ?? LibCpp2IlMain.MetadataVersion) >= 27.2f; + t.OwningMetadata = OwningMetadata; + t.Il2CppTypeHasNumMods5Bits ??= (OwningMetadata?.MetadataVersion ?? MetadataVersion) >= 27.2f; return t; } } diff --git a/LibCpp2IL/BinaryStructures/Il2CppGenericContext.cs b/LibCpp2IL/BinaryStructures/Il2CppGenericContext.cs index e8093c384..6fd096005 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppGenericContext.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppGenericContext.cs @@ -2,9 +2,6 @@ namespace LibCpp2IL.BinaryStructures; public class Il2CppGenericContext : ReadableClass { - // Populated by the caller after reading. - internal Il2CppBinary? OwningBinary { get; set; } - /* The instantiation corresponding to the class generic parameters */ public ulong class_inst; @@ -16,7 +13,7 @@ public Il2CppGenericInst? ClassInst get { if (class_inst == 0) return null; - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) return null; var inst = binary.ReadReadableAtVirtualAddress(class_inst); inst.OwningBinary = binary; @@ -29,7 +26,7 @@ public Il2CppGenericInst? MethodInst get { if (method_inst == 0) return null; - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) return null; var inst = binary.ReadReadableAtVirtualAddress(method_inst); inst.OwningBinary = binary; diff --git a/LibCpp2IL/BinaryStructures/Il2CppGenericInst.cs b/LibCpp2IL/BinaryStructures/Il2CppGenericInst.cs index 55265b22d..f5fef8cbb 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppGenericInst.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppGenericInst.cs @@ -4,9 +4,6 @@ namespace LibCpp2IL.BinaryStructures; public class Il2CppGenericInst : ReadableClass { - // Populated by the caller after reading. - internal Il2CppBinary? OwningBinary { get; set; } - public ulong pointerCount; public ulong pointerStart; @@ -14,7 +11,7 @@ public ulong[] Pointers { get { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; return binary == null ? [] : binary.ReadNUintArrayAtVirtualAddress(pointerStart, (long)pointerCount); } } @@ -23,7 +20,7 @@ public Il2CppType[] Types { get { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; return binary == null ? [] : Pointers.Select(binary.GetIl2CppTypeFromPointer).ToArray(); } } diff --git a/LibCpp2IL/BinaryStructures/Il2CppMethodSpec.cs b/LibCpp2IL/BinaryStructures/Il2CppMethodSpec.cs index cfdacfd46..5c6c7d736 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppMethodSpec.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppMethodSpec.cs @@ -8,23 +8,19 @@ namespace LibCpp2IL.BinaryStructures; public class Il2CppMethodSpec : ReadableClass { - // Populated by Il2CppBinary.Init for per-context usage. - internal Il2CppBinary? OwningBinary { get; set; } - internal Il2CppMetadata? OwningMetadata { get; set; } - public int methodDefinitionIndex; public int classIndexIndex; public int methodIndexIndex; - public Il2CppMethodDefinition? MethodDefinition - => (OwningMetadata ?? LibCpp2IlMain.TheMetadata) + public Il2CppMethodDefinition? MethodDefinition + => OwningMetadata ?.GetMethodDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(methodDefinitionIndex)); //DynWidth: Il2CppMethodSpec is in-binary, dynamic widths weren't applied here. public Il2CppGenericInst? GenericClassInst { get { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) return null; if (classIndexIndex < 0) return null; var inst = binary.GetGenericInst(classIndexIndex); @@ -37,7 +33,7 @@ public Il2CppGenericInst? GenericMethodInst { get { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) return null; if (methodIndexIndex < 0) return null; var inst = binary.GetGenericInst(methodIndexIndex); diff --git a/LibCpp2IL/BinaryStructures/Il2CppRGCTXDefinition.cs b/LibCpp2IL/BinaryStructures/Il2CppRGCTXDefinition.cs index 3995999d7..bef3fd42b 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppRGCTXDefinition.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppRGCTXDefinition.cs @@ -5,10 +5,6 @@ namespace LibCpp2IL.BinaryStructures; public class Il2CppRGCTXDefinition : ReadableClass { - // Populated by Il2CppBinary.Init (codegen module init) for per-context usage. - internal Il2CppBinary? OwningBinary { get; set; } - internal Il2CppMetadata? OwningMetadata { get; set; } - public Il2CppRGCTXDataType type; public int _rawIndex; @@ -20,8 +16,7 @@ public Il2CppMethodSpec? MethodSpec { get { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; - return binary?.GetMethodSpec(MethodIndex); + return OwningBinary?.GetMethodSpec(MethodIndex); } } @@ -29,7 +24,7 @@ public Il2CppTypeReflectionData? Type { get { - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) return null; var t = binary.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(TypeIndex)); return LibCpp2ILUtils.GetTypeReflectionData(t); @@ -79,7 +74,7 @@ public override void Read(ClassReadingBinaryReader reader) var va = reader.ReadNUint(); var bakPosition = reader.Position; - var binary = OwningBinary ?? LibCpp2IlMain.Binary; + var binary = OwningBinary; if (binary == null) { // Can't resolve VA -> raw without a binary context. Leave as-is. diff --git a/LibCpp2IL/BinaryStructures/Il2CppType.cs b/LibCpp2IL/BinaryStructures/Il2CppType.cs index 75f5e89ce..446359652 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppType.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppType.cs @@ -7,9 +7,6 @@ namespace LibCpp2IL.BinaryStructures; public class Il2CppType : ReadableClass { - // Populated by Il2CppBinary.Init for per-context usage. - internal Il2CppBinary? OwningBinary { get; set; } - internal Il2CppMetadata? OwningMetadata { get; set; } internal bool? Il2CppTypeHasNumMods5Bits { get; set; } public ulong Datapoint; @@ -28,7 +25,7 @@ private void InitUnionAndFlags() Type = (Il2CppTypeEnum)((Bits >> 16) & 0b1111_1111); //Bits 16-23 Data = new Union { Dummy = Datapoint }; - var hasNumMods5Bits = Il2CppTypeHasNumMods5Bits ?? LibCpp2IlMain.Il2CppTypeHasNumMods5Bits; + var hasNumMods5Bits = Il2CppTypeHasNumMods5Bits ?? (MetadataVersion >= 27.2f); if (hasNumMods5Bits) { //Unity 2021 (v27.2) changed num_mods to be 5 bits not 6 @@ -68,8 +65,7 @@ private Il2CppTypeDefinition? Class if (Type is not Il2CppTypeEnum.IL2CPP_TYPE_CLASS and not Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE) return null; - var metadata = OwningMetadata ?? LibCpp2IlMain.TheMetadata; - return metadata!.GetTypeDefinitionFromIndex(Data.ClassIndex); + return OwningMetadata!.GetTypeDefinitionFromIndex(Data.ClassIndex); } } @@ -85,8 +81,7 @@ private Il2CppType? EncapsulatedType if (Type is not Il2CppTypeEnum.IL2CPP_TYPE_PTR and not Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) return null; - var binary = OwningBinary ?? LibCpp2IlMain.Binary; - return binary!.GetIl2CppTypeFromPointer(Data.Type); + return OwningBinary!.GetIl2CppTypeFromPointer(Data.Type); } } @@ -102,9 +97,8 @@ private Il2CppArrayType? ArrayType if (Type is not Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) return null; - var binary = OwningBinary ?? LibCpp2IlMain.Binary; - var at = binary!.ReadReadableAtVirtualAddress(Data.Array); - at.OwningBinary = binary; + var at = OwningBinary!.ReadReadableAtVirtualAddress(Data.Array); + at.OwningBinary = OwningBinary; return at; } } @@ -125,8 +119,7 @@ private Il2CppGenericParameter? GenericParameter if (Type is not Il2CppTypeEnum.IL2CPP_TYPE_VAR and not Il2CppTypeEnum.IL2CPP_TYPE_MVAR) return null; - var metadata = OwningMetadata ?? LibCpp2IlMain.TheMetadata; - return metadata!.GetGenericParameterFromIndex(Data.GenericParameterIndex); + return OwningMetadata!.GetGenericParameterFromIndex(Data.GenericParameterIndex); } } @@ -144,11 +137,10 @@ private Il2CppGenericClass? GenericClass if (Type is not Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) return null; - var binary = OwningBinary ?? LibCpp2IlMain.Binary; - var gc = binary!.ReadReadableAtVirtualAddress(Data.GenericClass); - gc.OwningBinary = binary; - gc.OwningMetadata = OwningMetadata ?? LibCpp2IlMain.TheMetadata; - gc.Context.OwningBinary = binary; + var gc = OwningBinary!.ReadReadableAtVirtualAddress(Data.GenericClass); + gc.OwningBinary = OwningBinary; + gc.OwningMetadata = OwningMetadata; + gc.Context.OwningBinary = OwningBinary; return gc; } } diff --git a/LibCpp2IL/ClassReadingBinaryReader.cs b/LibCpp2IL/ClassReadingBinaryReader.cs index 6815d3c19..141233306 100644 --- a/LibCpp2IL/ClassReadingBinaryReader.cs +++ b/LibCpp2IL/ClassReadingBinaryReader.cs @@ -177,6 +177,7 @@ protected internal int ReadUnityCompressedIntAtRawAddr(long position, bool doLoc private T InternalReadReadableClass() where T : ReadableClass, new() { var t = new T { MetadataVersion = MetadataVersion }; + OnReadableCreated(t); if (!_inReadableRead) { @@ -192,6 +193,12 @@ protected internal int ReadUnityCompressedIntAtRawAddr(long position, bool doLoc return t; } + /// + /// Called after a ReadableClass instance is created but before Read is called. + /// Override in subclasses to set OwningBinary/OwningMetadata on newly created instances. + /// + protected internal virtual void OnReadableCreated(ReadableClass instance) { } + private object InternalReadClass(Type type, bool overrideArchCheck = false) { if (type.IsPrimitive) diff --git a/LibCpp2IL/Elf/ElfFile.cs b/LibCpp2IL/Elf/ElfFile.cs index 0dae7bad4..3941f545f 100644 --- a/LibCpp2IL/Elf/ElfFile.cs +++ b/LibCpp2IL/Elf/ElfFile.cs @@ -689,7 +689,7 @@ public override (ulong pCodeRegistration, ulong pMetadataRegistration) FindCodeA var typeDefinitionsCount = metadata.TypeDefinitionCount; LibLogger.VerboseNewline("Searching for il2cpp structures in an ELF binary using non-arch-specific method..."); - var searcher = new BinarySearcher(this, methodCount, typeDefinitionsCount); + var searcher = new BinarySearcher(this, metadata, methodCount, typeDefinitionsCount); LibLogger.VerboseNewline("\tLooking for code reg (this might take a while)..."); var codeReg = metadata.MetadataVersion >= 24.2f ? searcher.FindCodeRegistrationPost2019(metadata) : searcher.FindCodeRegistrationPre2019(); diff --git a/LibCpp2IL/Il2CppBinary.cs b/LibCpp2IL/Il2CppBinary.cs index 734b34385..1bbf473f9 100644 --- a/LibCpp2IL/Il2CppBinary.cs +++ b/LibCpp2IL/Il2CppBinary.cs @@ -68,8 +68,15 @@ public abstract class Il2CppBinary(MemoryStream input) : ClassReadingBinaryReade private Il2CppMetadata? _metadata; + protected internal override void OnReadableCreated(ReadableClass instance) + { + instance.OwningBinary = this; + instance.OwningMetadata = _metadata; + } + public void Init(Il2CppMetadata metadata) { + _metadata = metadata; _metadataVersion = metadata.MetadataVersion; var start = DateTime.Now; @@ -540,7 +547,7 @@ public virtual (ulong pCodeRegistration, ulong pMetadataRegistration) FindCodeAn var methodCount = metadata.methodDefs.Count(x => x.methodIndex >= 0); var typeDefinitionsCount = metadata.TypeDefinitionCount; - var plusSearch = new BinarySearcher(this, methodCount, typeDefinitionsCount); + var plusSearch = new BinarySearcher(this, metadata, methodCount, typeDefinitionsCount); LibLogger.VerboseNewline("\t\t-Searching for MetadataReg..."); diff --git a/LibCpp2IL/LibCpp2IlContext.cs b/LibCpp2IL/LibCpp2IlContext.cs index bc72908c3..3ec46a970 100644 --- a/LibCpp2IL/LibCpp2IlContext.cs +++ b/LibCpp2IL/LibCpp2IlContext.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using LibCpp2IL.Metadata; using LibCpp2IL.Reflection; @@ -22,6 +23,17 @@ public sealed class LibCpp2IlContext public LibCpp2IlReflectionCache ReflectionCache { get; } = new(); + // Global mapper state (was static on LibCpp2IlGlobalMapper) + internal List TypeRefs = []; + internal List MethodRefs = []; + internal List FieldRefs = []; + internal List Literals = []; + + internal Dictionary TypeRefsByAddress = new(); + internal Dictionary MethodRefsByAddress = new(); + internal Dictionary FieldRefsByAddress = new(); + internal Dictionary LiteralsByAddress = new(); + internal LibCpp2IlContext(LibCpp2IlMain.LibCpp2IlSettings settings) { Settings = settings; @@ -30,10 +42,74 @@ internal LibCpp2IlContext(LibCpp2IlMain.LibCpp2IlSettings settings) public List? GetManagedMethodImplementationsAtAddress(ulong addr) => MethodsByPtr.TryGetValue(addr, out var ret) ? ret : null; + internal void MapGlobalIdentifiers() + { + if (MetadataVersion < 27f) + MapGlobalIdentifiersPre27(); + // Post-27 is a no-op - globals are decoded on demand + } + + private void MapGlobalIdentifiersPre27() + { + var metadata = Metadata; + var cppAssembly = Binary; + + //We non-null assert here because this function is only called pre-27, when this is guaranteed to be non-null + TypeRefs = metadata.metadataUsageDic![(uint)MetadataUsageType.TypeInfo] + .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, Binary, Metadata)) + .ToList(); + + TypeRefs.AddRange(metadata.metadataUsageDic[(uint)MetadataUsageType.Type] + .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, Binary, Metadata)) + ); + + MethodRefs = metadata.metadataUsageDic[(uint)MetadataUsageType.MethodDef] + .Select(kvp => new MetadataUsage(MetadataUsageType.MethodDef, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, Binary, Metadata)) + .ToList(); + + FieldRefs = metadata.metadataUsageDic[(uint)MetadataUsageType.FieldInfo] + .Select(kvp => new MetadataUsage(MetadataUsageType.FieldInfo, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, Binary, Metadata)) + .ToList(); + + Literals = metadata.metadataUsageDic[(uint)MetadataUsageType.StringLiteral] + .Select(kvp => new MetadataUsage(MetadataUsageType.StringLiteral, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, Binary, Metadata)).ToList(); + + foreach (var (metadataUsageIdx, methodSpecIdx) in metadata.metadataUsageDic[(uint)MetadataUsageType.MethodRef]) + { + MethodRefs.Add(new MetadataUsage(MetadataUsageType.MethodRef, cppAssembly.GetRawMetadataUsage(metadataUsageIdx), methodSpecIdx, Binary, Metadata)); + } + + foreach (var globalIdentifier in TypeRefs) + TypeRefsByAddress[globalIdentifier.Offset] = globalIdentifier; + + foreach (var globalIdentifier in MethodRefs) + MethodRefsByAddress[globalIdentifier.Offset] = globalIdentifier; + + foreach (var globalIdentifier in FieldRefs) + FieldRefsByAddress[globalIdentifier.Offset] = globalIdentifier; + + foreach (var globalIdentifier in Literals) + LiteralsByAddress[globalIdentifier.Offset] = globalIdentifier; + } + + public MetadataUsage? CheckForPost27GlobalAt(ulong address) + { + if (!Binary.TryMapVirtualAddressToRaw(address, out var raw) || raw >= Binary.RawLength) + return null; + + var encoded = Binary.ReadPointerAtVirtualAddress(address); + var metadataUsage = MetadataUsage.DecodeMetadataUsage(encoded, address, Binary, Metadata); + + if (metadataUsage?.IsValid != true) + return null; + + return metadataUsage; + } + public MetadataUsage? GetAnyGlobalByAddress(ulong address) { if (MetadataVersion >= 27f) - return LibCpp2IlGlobalMapper.CheckForPost27GlobalAt(address); + return CheckForPost27GlobalAt(address); var glob = GetLiteralGlobalByAddress(address); glob ??= GetMethodGlobalByAddress(address); @@ -44,7 +120,7 @@ internal LibCpp2IlContext(LibCpp2IlMain.LibCpp2IlSettings settings) } public MetadataUsage? GetLiteralGlobalByAddress(ulong address) - => MetadataVersion < 27f ? LibCpp2IlGlobalMapper.LiteralsByAddress.GetOrDefault(address) : GetAnyGlobalByAddress(address); + => MetadataVersion < 27f ? LiteralsByAddress.GetOrDefault(address) : GetAnyGlobalByAddress(address); public string? GetLiteralByAddress(ulong address) { @@ -56,7 +132,7 @@ internal LibCpp2IlContext(LibCpp2IlMain.LibCpp2IlSettings settings) } public MetadataUsage? GetRawTypeGlobalByAddress(ulong address) - => MetadataVersion < 27f ? LibCpp2IlGlobalMapper.TypeRefsByAddress.GetOrDefault(address) : GetAnyGlobalByAddress(address); + => MetadataVersion < 27f ? TypeRefsByAddress.GetOrDefault(address) : GetAnyGlobalByAddress(address); public Il2CppTypeReflectionData? GetTypeGlobalByAddress(ulong address) { @@ -69,13 +145,13 @@ internal LibCpp2IlContext(LibCpp2IlMain.LibCpp2IlSettings settings) } public MetadataUsage? GetRawFieldGlobalByAddress(ulong address) - => MetadataVersion < 27f ? LibCpp2IlGlobalMapper.FieldRefsByAddress.GetOrDefault(address) : GetAnyGlobalByAddress(address); + => MetadataVersion < 27f ? FieldRefsByAddress.GetOrDefault(address) : GetAnyGlobalByAddress(address); public Il2CppFieldDefinition? GetFieldGlobalByAddress(ulong address) => GetRawFieldGlobalByAddress(address)?.AsField(); public MetadataUsage? GetMethodGlobalByAddress(ulong address) - => MetadataVersion < 27f ? LibCpp2IlGlobalMapper.MethodRefsByAddress.GetOrDefault(address) : GetAnyGlobalByAddress(address); + => MetadataVersion < 27f ? MethodRefsByAddress.GetOrDefault(address) : GetAnyGlobalByAddress(address); public Il2CppMethodDefinition? GetMethodDefinitionByGlobalAddress(ulong address) { diff --git a/LibCpp2IL/LibCpp2IlContextBuilder.cs b/LibCpp2IL/LibCpp2IlContextBuilder.cs index db07db13c..9420bf874 100644 --- a/LibCpp2IL/LibCpp2IlContextBuilder.cs +++ b/LibCpp2IL/LibCpp2IlContextBuilder.cs @@ -55,6 +55,7 @@ public void LoadMetadata(byte[] metadataBytes, UnityVersion unityVersion) } _context.Metadata = metadata; + metadata.OwningContext = _context; _context.Il2CppTypeHasNumMods5Bits = metadata.MetadataVersion >= 27.2f; @@ -79,6 +80,9 @@ public void LoadBinary(byte[] binaryBytes) // Complete legacy/static initialization now that the binary exists. LibCpp2IlMain.Binary = bin; + // Set OwningBinary on all metadata structures now that the binary exists. + _context.Metadata.SetOwningBinaryOnAllStructures(bin); + _binaryLoaded = true; } @@ -94,6 +98,9 @@ public void LoadBinary(Il2CppBinary binary) // Complete legacy/static initialization now that the binary exists. LibCpp2IlMain.Binary = binary; + // Set OwningBinary on all metadata structures now that the binary exists. + _context.Metadata.SetOwningBinaryOnAllStructures(binary); + _binaryLoaded = true; } @@ -110,6 +117,8 @@ public LibCpp2IlContext Build() { start = DateTime.Now; LibLogger.Info("Mapping Globals..."); + _context.MapGlobalIdentifiers(); + // Also populate legacy static mapper for backwards compatibility during transition LibCpp2IlGlobalMapper.MapGlobalIdentifiers(_context.Metadata, _context.Binary); LibLogger.InfoNewline($"OK ({(DateTime.Now - start).TotalMilliseconds:F0}ms)"); } diff --git a/LibCpp2IL/LibCpp2IlMain.cs b/LibCpp2IL/LibCpp2IlMain.cs index 9f2c579f1..f15c2bbf2 100644 --- a/LibCpp2IL/LibCpp2IlMain.cs +++ b/LibCpp2IL/LibCpp2IlMain.cs @@ -1,20 +1,16 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Text; using System.Text.RegularExpressions; using AssetRipper.Primitives; using LibCpp2IL.Logging; -using LibCpp2IL.Metadata; -using LibCpp2IL.Reflection; namespace LibCpp2IL; public static class LibCpp2IlMain { public delegate byte[]? MetadataFixupFunc(byte[] originalBytes, UnityVersion unityVersion); - + private static readonly Regex UnityVersionRegex = new Regex(@"^[0-9]+\.[0-9]+\.[0-9]+[abcfxp][0-9]+$", RegexOptions.Compiled); public class LibCpp2IlSettings @@ -28,181 +24,16 @@ public class LibCpp2IlSettings public static readonly LibCpp2IlSettings Settings = new(); /// - /// Backwards-compatible default context. Existing static APIs are effectively wrappers around this. - /// New code should prefer creating and passing around a . + /// Initialize the metadata and binary from a pair of byte arrays, returning a context. /// - public static LibCpp2IlContext? DefaultContext { get; set; } - - public static bool Il2CppTypeHasNumMods5Bits; - public static float MetadataVersion => DefaultContext?.MetadataVersion ?? TheMetadata!.MetadataVersion; - - public static Il2CppBinary? Binary; - public static Il2CppMetadata? TheMetadata; - - public static readonly Dictionary> MethodsByPtr = new(); - - public static void Reset() - { - LibCpp2IlGlobalMapper.Reset(); - MethodsByPtr.Clear(); - - DefaultContext = null; - Binary = null; - TheMetadata = null; - - // Note: reflection caches are per-context now; legacy static caches are not reset here. - } - - public static List? GetManagedMethodImplementationsAtAddress(ulong addr) - { - MethodsByPtr.TryGetValue(addr, out var ret); - - return ret; - } - - public static MetadataUsage? GetAnyGlobalByAddress(ulong address) - { - if (MetadataVersion >= 27f) - return LibCpp2IlGlobalMapper.CheckForPost27GlobalAt(address); - - //Pre-27 - var glob = GetLiteralGlobalByAddress(address); - glob ??= GetMethodGlobalByAddress(address); - glob ??= GetRawFieldGlobalByAddress(address); - glob ??= GetRawTypeGlobalByAddress(address); - - return glob; - } - - public static MetadataUsage? GetLiteralGlobalByAddress(ulong address) - { - if (MetadataVersion < 27f) - return LibCpp2IlGlobalMapper.LiteralsByAddress.GetOrDefault(address); - - return GetAnyGlobalByAddress(address); - } - - public static string? GetLiteralByAddress(ulong address) - { - var literal = GetLiteralGlobalByAddress(address); - if (literal?.Type != MetadataUsageType.StringLiteral) - return null; - - return literal.AsLiteral(); - } - - public static MetadataUsage? GetRawTypeGlobalByAddress(ulong address) - { - if (MetadataVersion < 27f) - return LibCpp2IlGlobalMapper.TypeRefsByAddress.GetOrDefault(address); - - return GetAnyGlobalByAddress(address); - } - - public static Il2CppTypeReflectionData? GetTypeGlobalByAddress(ulong address) - { - if (TheMetadata == null) return null; - - var typeGlobal = GetRawTypeGlobalByAddress(address); - - if (typeGlobal?.Type is not (MetadataUsageType.Type or MetadataUsageType.TypeInfo)) - return null; - - return typeGlobal.AsType(); - } - - public static MetadataUsage? GetRawFieldGlobalByAddress(ulong address) - { - if (MetadataVersion < 27f) - return LibCpp2IlGlobalMapper.FieldRefsByAddress.GetOrDefault(address); - return GetAnyGlobalByAddress(address); - } - - public static Il2CppFieldDefinition? GetFieldGlobalByAddress(ulong address) - { - if (TheMetadata == null) return null; - - var typeGlobal = GetRawFieldGlobalByAddress(address); - - return typeGlobal?.AsField(); - } - - public static MetadataUsage? GetMethodGlobalByAddress(ulong address) - { - if (TheMetadata == null) return null; - - if (MetadataVersion < 27f) - return LibCpp2IlGlobalMapper.MethodRefsByAddress.GetOrDefault(address); - - return GetAnyGlobalByAddress(address); - } - - public static Il2CppMethodDefinition? GetMethodDefinitionByGlobalAddress(ulong address) - { - var global = GetMethodGlobalByAddress(address); - - if (global?.Type == MetadataUsageType.MethodRef) - return global.AsGenericMethodRef().BaseMethod; - - return global?.AsMethod(); - } - - /// - /// Initialize the metadata and PE from a pair of byte arrays. - /// - /// The content of the GameAssembly.dll file. - /// The content of the global-metadata.dat file - /// The unity version - /// True if the initialize succeeded, else false - /// if the metadata is invalid (bad magic number, bad version), or if the PE is invalid (bad header signature, bad magic number)
- /// if the PE file specifies it is neither for AMD64 or i386 architecture - public static bool Initialize(byte[] binaryBytes, byte[] metadataBytes, UnityVersion unityVersion) - { - DefaultContext = LibCpp2IlContextBuilder.Build(binaryBytes, metadataBytes, unityVersion); - - // Preserve legacy static fields for existing consumers. - TheMetadata = DefaultContext.Metadata; - Binary = DefaultContext.Binary; - Il2CppTypeHasNumMods5Bits = DefaultContext.Il2CppTypeHasNumMods5Bits; - - // Keep legacy MethodsByPtr populated for old call sites. - MethodsByPtr.Clear(); - foreach (var kvp in DefaultContext.MethodsByPtr) - MethodsByPtr[kvp.Key] = kvp.Value; - - return true; - } - public static LibCpp2IlContext InitializeAsContext(byte[] binaryBytes, byte[] metadataBytes, UnityVersion unityVersion) => LibCpp2IlContextBuilder.Build(binaryBytes, metadataBytes, unityVersion); - public static LibCpp2IlContext LoadFromFileAsContext(string pePath, string metadataPath, UnityVersion unityVersion) - => LibCpp2IlContextBuilder.BuildFromFiles(pePath, metadataPath, unityVersion); - /// - /// Initialize the metadata and PE from their respective file locations. + /// Initialize the metadata and binary from file paths, returning a context. /// - /// The path to the GameAssembly.dll file - /// The path to the global-metadata.dat file - /// The unity version, split on periods, with the patch version (e.g. f1) stripped out. For example, [2018, 2, 0] - /// True if the initialize succeeded, else false - /// if the metadata is invalid (bad magic number, bad version), or if the PE is invalid (bad header signature, bad magic number)
- /// if the PE file specifies it is neither for AMD64 or i386 architecture - public static bool LoadFromFile(string pePath, string metadataPath, UnityVersion unityVersion) - { - var ctx = LibCpp2IlContextBuilder.BuildFromFiles(pePath, metadataPath, unityVersion); - DefaultContext = ctx; - - TheMetadata = ctx.Metadata; - Binary = ctx.Binary; - Il2CppTypeHasNumMods5Bits = ctx.Il2CppTypeHasNumMods5Bits; - - MethodsByPtr.Clear(); - foreach (var kvp in ctx.MethodsByPtr) - MethodsByPtr[kvp.Key] = kvp.Value; - - return true; - } + public static LibCpp2IlContext LoadFromFileAsContext(string pePath, string metadataPath, UnityVersion unityVersion) + => LibCpp2IlContextBuilder.BuildFromFiles(pePath, metadataPath, unityVersion); /// /// Attempts to determine the Unity version from the given binary path and game data path @@ -240,11 +71,11 @@ public static UnityVersion DetermineUnityVersion(string? unityPlayerPath, string LibLogger.VerboseNewline($"DetermineUnityVersion: No globalgamemanagers or data.unity3d found in game data path."); } - + if (Environment.OSVersion.Platform == PlatformID.Win32NT && !string.IsNullOrEmpty(unityPlayerPath)) { LibLogger.VerboseNewline($"DetermineUnityVersion: Running on windows so have FileVersionInfo, trying to pull version from unity player {unityPlayerPath}"); - var unityVer = FileVersionInfo.GetVersionInfo(unityPlayerPath); + var unityVer = System.Diagnostics.FileVersionInfo.GetVersionInfo(unityPlayerPath); if (unityVer.FileMajorPart > 0) return new UnityVersion((ushort)unityVer.FileMajorPart, (ushort)unityVer.FileMinorPart, (ushort)unityVer.FileBuildPart); @@ -328,4 +159,106 @@ public static UnityVersion GetVersionFromDataUnity3D(Stream fileStream) return UnityVersion.Parse(unityVer); } + + #region Legacy static API — kept for backwards compatibility during migration + + // These fields exist solely to allow external consumers (Cpp2IL.Core, plugins) to keep working + // during the transition to context-based APIs. New code should use LibCpp2IlContext directly. + + [Obsolete("Use LibCpp2IlContext.Binary instead.")] + public static Il2CppBinary? Binary; + + [Obsolete("Use LibCpp2IlContext.Metadata instead.")] + public static Metadata.Il2CppMetadata? TheMetadata; + + [Obsolete("Use LibCpp2IlContext instead.")] + public static LibCpp2IlContext? DefaultContext; + + [Obsolete("Use context.Il2CppTypeHasNumMods5Bits instead.")] + public static bool Il2CppTypeHasNumMods5Bits; + + [Obsolete("Use LibCpp2IlContext.MetadataVersion instead.")] + public static float MetadataVersion => DefaultContext?.MetadataVersion ?? TheMetadata?.MetadataVersion ?? 0; + + [Obsolete("Use LibCpp2IlContext.MethodsByPtr instead.")] + public static readonly System.Collections.Generic.Dictionary> MethodsByPtr = new(); + + [Obsolete("Use LibCpp2IlContextBuilder directly.")] + public static bool Initialize(byte[] binaryBytes, byte[] metadataBytes, UnityVersion unityVersion) + { + var ctx = LibCpp2IlContextBuilder.Build(binaryBytes, metadataBytes, unityVersion); + DefaultContext = ctx; + TheMetadata = ctx.Metadata; + Binary = ctx.Binary; + Il2CppTypeHasNumMods5Bits = ctx.Il2CppTypeHasNumMods5Bits; + + MethodsByPtr.Clear(); + foreach (var kvp in ctx.MethodsByPtr) + MethodsByPtr[kvp.Key] = kvp.Value; + + return true; + } + + [Obsolete("Use LibCpp2IlContextBuilder directly.")] + public static bool LoadFromFile(string pePath, string metadataPath, UnityVersion unityVersion) + { + var ctx = LibCpp2IlContextBuilder.BuildFromFiles(pePath, metadataPath, unityVersion); + DefaultContext = ctx; + TheMetadata = ctx.Metadata; + Binary = ctx.Binary; + Il2CppTypeHasNumMods5Bits = ctx.Il2CppTypeHasNumMods5Bits; + + MethodsByPtr.Clear(); + foreach (var kvp in ctx.MethodsByPtr) + MethodsByPtr[kvp.Key] = kvp.Value; + + return true; + } + + [Obsolete("No longer needed — context is garbage collected.")] + public static void Reset() + { + LibCpp2IlGlobalMapper.Reset(); + MethodsByPtr.Clear(); + + DefaultContext = null; + Binary = null; + TheMetadata = null; + } + + [Obsolete("Use context.GetManagedMethodImplementationsAtAddress instead.")] + public static System.Collections.Generic.List? GetManagedMethodImplementationsAtAddress(ulong addr) + { + MethodsByPtr.TryGetValue(addr, out var ret); + return ret; + } + + [Obsolete("Use context.GetAnyGlobalByAddress instead.")] + public static MetadataUsage? GetAnyGlobalByAddress(ulong address) => DefaultContext?.GetAnyGlobalByAddress(address); + + [Obsolete("Use context.GetLiteralGlobalByAddress instead.")] + public static MetadataUsage? GetLiteralGlobalByAddress(ulong address) => DefaultContext?.GetLiteralGlobalByAddress(address); + + [Obsolete("Use context.GetLiteralByAddress instead.")] + public static string? GetLiteralByAddress(ulong address) => DefaultContext?.GetLiteralByAddress(address); + + [Obsolete("Use context.GetRawTypeGlobalByAddress instead.")] + public static MetadataUsage? GetRawTypeGlobalByAddress(ulong address) => DefaultContext?.GetRawTypeGlobalByAddress(address); + + [Obsolete("Use context.GetTypeGlobalByAddress instead.")] + public static Reflection.Il2CppTypeReflectionData? GetTypeGlobalByAddress(ulong address) => DefaultContext?.GetTypeGlobalByAddress(address); + + [Obsolete("Use context.GetRawFieldGlobalByAddress instead.")] + public static MetadataUsage? GetRawFieldGlobalByAddress(ulong address) => DefaultContext?.GetRawFieldGlobalByAddress(address); + + [Obsolete("Use context.GetFieldGlobalByAddress instead.")] + public static Metadata.Il2CppFieldDefinition? GetFieldGlobalByAddress(ulong address) => DefaultContext?.GetFieldGlobalByAddress(address); + + [Obsolete("Use context.GetMethodGlobalByAddress instead.")] + public static MetadataUsage? GetMethodGlobalByAddress(ulong address) => DefaultContext?.GetMethodGlobalByAddress(address); + + [Obsolete("Use context.GetMethodDefinitionByGlobalAddress instead.")] + public static Metadata.Il2CppMethodDefinition? GetMethodDefinitionByGlobalAddress(ulong address) => DefaultContext?.GetMethodDefinitionByGlobalAddress(address); + + #endregion } diff --git a/LibCpp2IL/LibCpp2IlUtils.cs b/LibCpp2IL/LibCpp2IlUtils.cs index b0537e671..45f6ba112 100644 --- a/LibCpp2IL/LibCpp2IlUtils.cs +++ b/LibCpp2IL/LibCpp2IlUtils.cs @@ -71,9 +71,9 @@ internal static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAsse ret += metadata.GetStringFromIndex(typeDef.NameIndex); var names = new List(); - if (typeDef.GenericContainer is not {} genericContainer) + if (typeDef.GenericContainer is not {} genericContainer) return ret; - + foreach (var parameter in genericContainer.GenericParameters) { names.Add(metadata.GetStringFromIndex(parameter.nameIndex)); @@ -87,13 +87,14 @@ internal static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAsse internal static Il2CppTypeReflectionData[]? GetGenericTypeParams(Il2CppGenericInst genericInst) { - if (LibCpp2IlMain.Binary == null || LibCpp2IlMain.TheMetadata == null) return null; + var binary = genericInst.OwningBinary; + if (binary == null) return null; var types = new Il2CppTypeReflectionData[genericInst.pointerCount]; - var pointers = LibCpp2IlMain.Binary.ReadNUintArrayAtVirtualAddress(genericInst.pointerStart, (long)genericInst.pointerCount); + var pointers = binary.ReadNUintArrayAtVirtualAddress(genericInst.pointerStart, (long)genericInst.pointerCount); for (uint i = 0; i < genericInst.pointerCount; ++i) { - var oriType = LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(pointers[i]); + var oriType = binary.GetIl2CppTypeFromPointer(pointers[i]); types[i] = GetTypeReflectionData(oriType); } @@ -165,18 +166,15 @@ public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssemb return ret; } - internal static object? GetDefaultValue(Il2CppVariableWidthIndex dataIndex, Il2CppVariableWidthIndex typeIndex) + internal static object? GetDefaultValue(Il2CppVariableWidthIndex dataIndex, Il2CppVariableWidthIndex typeIndex, Il2CppMetadata metadata, Il2CppBinary binary) { - var metadata = LibCpp2IlMain.TheMetadata!; - var theDll = LibCpp2IlMain.Binary!; - if (dataIndex.IsNull) return null; //Literally null. var pointer = metadata.GetDefaultValueFromIndex(dataIndex); if (pointer <= 0) return null; - var defaultValueType = theDll.GetType(typeIndex); + var defaultValueType = binary.GetType(typeIndex); metadata.GetLockOrThrow(); metadata.Position = pointer; try @@ -196,11 +194,11 @@ public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssemb case Il2CppTypeEnum.IL2CPP_TYPE_I2: return metadata.ReadInt16(); case Il2CppTypeEnum.IL2CPP_TYPE_U4: - if (LibCpp2IlMain.MetadataVersion < 29) + if (metadata.MetadataVersion < 29) return metadata.ReadUInt32(); return metadata.ReadUnityCompressedUIntAtRawAddrNoLock(pointer, out _); case Il2CppTypeEnum.IL2CPP_TYPE_I4: - if (LibCpp2IlMain.MetadataVersion < 29) + if (metadata.MetadataVersion < 29) return metadata.ReadInt32(); return metadata.ReadUnityCompressedIntAtRawAddr(pointer, false, out _); case Il2CppTypeEnum.IL2CPP_TYPE_U8: @@ -214,7 +212,7 @@ public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssemb case Il2CppTypeEnum.IL2CPP_TYPE_STRING: int len; var lenLen = 4; - if (LibCpp2IlMain.MetadataVersion < 29) + if (metadata.MetadataVersion < 29) len = metadata.ReadInt32(); else len = metadata.ReadUnityCompressedIntAtRawAddr(pointer, false, out lenLen); @@ -241,7 +239,10 @@ public static Il2CppTypeReflectionData WrapType(Il2CppTypeDefinition what) public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) { - if (LibCpp2IlMain.Binary == null || LibCpp2IlMain.TheMetadata == null) + var binary = forWhat.OwningBinary ?? LibCpp2IlMain.Binary; + var metadata = forWhat.OwningMetadata ?? LibCpp2IlMain.TheMetadata; + + if (binary == null || metadata == null) throw new Exception("Can't get type reflection data when not initialized. How did you even get the type?"); switch (forWhat.Type) @@ -292,7 +293,7 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: { //Generic type - var genericClass = LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(forWhat.Data.GenericClass); + var genericClass = binary.ReadReadableAtVirtualAddress(forWhat.Data.GenericClass); //CHANGED IN v27: typeDefinitionIndex is a ptr to the type in the file. var typeDefinition = genericClass.TypeDefinition; @@ -311,8 +312,8 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: { - var param = LibCpp2IlMain.TheMetadata.GetGenericParameterFromIndex(forWhat.Data.GenericParameterIndex); - var genericName = LibCpp2IlMain.TheMetadata.GetStringFromIndex(param.nameIndex); + var param = metadata.GetGenericParameterFromIndex(forWhat.Data.GenericParameterIndex); + var genericName = metadata.GetStringFromIndex(param.nameIndex); return new() { @@ -326,7 +327,7 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) } case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: { - var oriType = LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(forWhat.Data.Type); + var oriType = binary.GetIl2CppTypeFromPointer(forWhat.Data.Type); return new() { baseType = null, @@ -340,7 +341,7 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) } case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: { - var arrayType = LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(forWhat.Data.Array); + var arrayType = binary.ReadReadableAtVirtualAddress(forWhat.Data.Array); var oriType = arrayType.ElementType; return new() { @@ -355,7 +356,7 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) } case Il2CppTypeEnum.IL2CPP_TYPE_PTR: { - var oriType = LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(forWhat.Data.Type); + var oriType = binary.GetIl2CppTypeFromPointer(forWhat.Data.Type); var ret = GetTypeReflectionData(oriType); ret.isPointer = true; return ret; @@ -373,9 +374,9 @@ internal static IEnumerable Range(int start, int count) } } - internal static void PopulateDeclaringAssemblyCache() + internal static void PopulateDeclaringAssemblyCache(Il2CppMetadata metadata) { - foreach (var assembly in LibCpp2IlMain.TheMetadata!.imageDefinitions) + foreach (var assembly in metadata.imageDefinitions) { foreach (var il2CppTypeDefinition in assembly.Types!) { diff --git a/LibCpp2IL/Metadata/Il2CppAssemblyDefinition.cs b/LibCpp2IL/Metadata/Il2CppAssemblyDefinition.cs index e09df4d47..12e2e2481 100644 --- a/LibCpp2IL/Metadata/Il2CppAssemblyDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppAssemblyDefinition.cs @@ -13,12 +13,12 @@ public class Il2CppAssemblyDefinition : ReadableClass public int ReferencedAssemblyCount; public Il2CppAssemblyNameDefinition AssemblyName = null!; //Late-read - public Il2CppImageDefinition Image => LibCpp2IlMain.TheMetadata!.imageDefinitions[ImageIndex]; + public Il2CppImageDefinition Image => OwningMetadata!.imageDefinitions[ImageIndex]; public Il2CppAssemblyDefinition[] ReferencedAssemblies => ReferencedAssemblyStart < 0 ? [] - : LibCpp2IlMain.TheMetadata!.referencedAssemblies.SubArray(ReferencedAssemblyStart, ReferencedAssemblyCount) - .Select(idx => LibCpp2IlMain.TheMetadata.AssemblyDefinitions[idx]) + : OwningMetadata!.referencedAssemblies.SubArray(ReferencedAssemblyStart, ReferencedAssemblyCount) + .Select(idx => OwningMetadata.AssemblyDefinitions[idx]) .ToArray(); public override string ToString() => AssemblyName.ToString(); diff --git a/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs b/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs index 8d613a3b0..352229a52 100644 --- a/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs @@ -23,8 +23,8 @@ public class Il2CppAssemblyNameDefinition : ReadableClass public int revision; public ulong publicKeyToken; - public string Name => LibCpp2IlMain.TheMetadata!.GetStringFromIndex(nameIndex); - public string Culture => LibCpp2IlMain.TheMetadata!.GetStringFromIndex(cultureIndex); + public string Name => OwningMetadata!.GetStringFromIndex(nameIndex); + public string Culture => OwningMetadata!.GetStringFromIndex(cultureIndex); public byte[]? PublicKey { @@ -40,12 +40,12 @@ public byte[]? PublicKey // 2019.4.15 until 2020 (24.4) // 2020.1.11 until 2020.2.0 (24.4) // 2020.2.0b7 and later (27+) - var result = LibCpp2IlMain.TheMetadata!.GetByteArrayFromIndex(publicKeyIndex); + var result = OwningMetadata!.GetByteArrayFromIndex(publicKeyIndex); return result.Length == 0 ? null : result; } else { - var str = LibCpp2IlMain.TheMetadata!.GetStringFromIndex(publicKeyIndex); + var str = OwningMetadata!.GetStringFromIndex(publicKeyIndex); if (str is "NULL") return null; // No public key @@ -141,7 +141,7 @@ public byte[]? PublicKeyToken } } - public string HashValue => LibCpp2IlMain.MetadataVersion > 24.3f ? "NULL" : LibCpp2IlMain.TheMetadata!.GetStringFromIndex(hashValueIndex); + public string HashValue => MetadataVersion > 24.3f ? "NULL" : OwningMetadata!.GetStringFromIndex(hashValueIndex); public override string ToString() { diff --git a/LibCpp2IL/Metadata/Il2CppEventDefinition.cs b/LibCpp2IL/Metadata/Il2CppEventDefinition.cs index 598991b77..7eb440f62 100644 --- a/LibCpp2IL/Metadata/Il2CppEventDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppEventDefinition.cs @@ -23,9 +23,9 @@ public Il2CppTypeDefinition? DeclaringType get { if (_type != null) return _type; - if (LibCpp2IlMain.TheMetadata == null) return null; + if (OwningMetadata == null) return null; - _type = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault(t => t.Events!.Contains(this)); + _type = OwningMetadata.typeDefs.FirstOrDefault(t => t.Events!.Contains(this)); return _type; } internal set => _type = value; @@ -33,17 +33,17 @@ public Il2CppTypeDefinition? DeclaringType public string? Name { get; private set; } - public Il2CppType? RawType => LibCpp2IlMain.Binary?.GetType(typeIndex); + public Il2CppType? RawType => OwningBinary?.GetType(typeIndex); - public Il2CppTypeReflectionData? EventType => LibCpp2IlMain.Binary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(RawType!); + public Il2CppTypeReflectionData? EventType => OwningBinary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(RawType!); public EventAttributes EventAttributes => (EventAttributes)RawType!.Attrs; - public Il2CppMethodDefinition? Adder => LibCpp2IlMain.TheMetadata == null || add.IsNull || DeclaringType == null ? null : LibCpp2IlMain.TheMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + add); + public Il2CppMethodDefinition? Adder => OwningMetadata == null || add.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + add); - public Il2CppMethodDefinition? Remover => LibCpp2IlMain.TheMetadata == null || remove.IsNull || DeclaringType == null ? null : LibCpp2IlMain.TheMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + remove); + public Il2CppMethodDefinition? Remover => OwningMetadata == null || remove.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + remove); - public Il2CppMethodDefinition? Invoker => LibCpp2IlMain.TheMetadata == null || raise.IsNull || DeclaringType == null ? null : LibCpp2IlMain.TheMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + raise); + public Il2CppMethodDefinition? Invoker => OwningMetadata == null || raise.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + raise); public bool IsStatic { diff --git a/LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs b/LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs index 53a19ca09..ea74578fd 100644 --- a/LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs +++ b/LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs @@ -8,7 +8,7 @@ public class Il2CppFieldDefaultValue : ReadableClass public Il2CppVariableWidthIndex typeIndex; public Il2CppVariableWidthIndex dataIndex; - public object? Value => dataIndex.IsNull ? null : LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex); + public object? Value => dataIndex.IsNull || OwningMetadata == null || OwningBinary == null ? null : LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex, OwningMetadata, OwningBinary); public override void Read(ClassReadingBinaryReader reader) { diff --git a/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs b/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs index 4fe06cef6..a7d31a869 100644 --- a/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs @@ -13,18 +13,18 @@ public class Il2CppFieldDefinition : ReadableClass public string? Name { get; private set; } - public Il2CppType? RawFieldType => LibCpp2IlMain.Binary?.GetType(typeIndex); + public Il2CppType? RawFieldType => OwningBinary?.GetType(typeIndex); public Il2CppTypeReflectionData? FieldType => RawFieldType == null ? null : LibCpp2ILUtils.GetTypeReflectionData(RawFieldType); public Il2CppVariableWidthIndex FieldIndex => LibCpp2IlReflection.GetFieldIndexFromField(this); - public Il2CppFieldDefaultValue? DefaultValue => LibCpp2IlMain.TheMetadata?.GetFieldDefaultValue(this); + public Il2CppFieldDefaultValue? DefaultValue => OwningMetadata?.GetFieldDefaultValue(this); public Il2CppTypeDefinition DeclaringType => LibCpp2IlReflection.GetDeclaringTypeFromField(this); public override string? ToString() { - if (LibCpp2IlMain.TheMetadata == null) + if (OwningMetadata == null) return base.ToString(); return $"Il2CppFieldDefinition[Name={Name}, FieldType={FieldType}]"; @@ -41,13 +41,13 @@ public byte[] StaticArrayInitialValue return []; var length = int.Parse(FieldType.baseType!.Name.Replace("__StaticArrayInitTypeSize=", "")); - var (dataIndex, _) = LibCpp2IlMain.TheMetadata!.GetFieldDefaultValue(FieldIndex); + var (dataIndex, _) = OwningMetadata!.GetFieldDefaultValue(FieldIndex); - var pointer = LibCpp2IlMain.TheMetadata!.GetDefaultValueFromIndex(dataIndex); + var pointer = OwningMetadata!.GetDefaultValueFromIndex(dataIndex); if (pointer <= 0) return []; - var results = LibCpp2IlMain.TheMetadata.ReadByteArrayAtRawAddress(pointer, length); + var results = OwningMetadata.ReadByteArrayAtRawAddress(pointer, length); return results; } diff --git a/LibCpp2IL/Metadata/Il2CppFieldRef.cs b/LibCpp2IL/Metadata/Il2CppFieldRef.cs index 2dc3082c3..4e7020366 100644 --- a/LibCpp2IL/Metadata/Il2CppFieldRef.cs +++ b/LibCpp2IL/Metadata/Il2CppFieldRef.cs @@ -10,11 +10,11 @@ public class Il2CppFieldRef : ReadableClass public Il2CppVariableWidthIndex typeIndex; public Il2CppVariableWidthIndex fieldIndex; // local offset into type fields - public Il2CppType? DeclaringType => LibCpp2IlMain.Binary?.GetType(typeIndex); + public Il2CppType? DeclaringType => OwningBinary?.GetType(typeIndex); - public Il2CppTypeDefinition? DeclaringTypeDefinition => LibCpp2IlMain.TheMetadata?.GetTypeDefinitionFromIndex(DeclaringType!.Data.ClassIndex); + public Il2CppTypeDefinition? DeclaringTypeDefinition => OwningMetadata?.GetTypeDefinitionFromIndex(DeclaringType!.Data.ClassIndex); - public Il2CppFieldDefinition? FieldDefinition => LibCpp2IlMain.TheMetadata?.GetFieldDefinitionFromIndex(DeclaringTypeDefinition!.FirstFieldIdx + fieldIndex); + public Il2CppFieldDefinition? FieldDefinition => OwningMetadata?.GetFieldDefinitionFromIndex(DeclaringTypeDefinition!.FirstFieldIdx + fieldIndex); public override void Read(ClassReadingBinaryReader reader) { diff --git a/LibCpp2IL/Metadata/Il2CppGenericContainer.cs b/LibCpp2IL/Metadata/Il2CppGenericContainer.cs index f4773a64f..0142bb4ba 100644 --- a/LibCpp2IL/Metadata/Il2CppGenericContainer.cs +++ b/LibCpp2IL/Metadata/Il2CppGenericContainer.cs @@ -27,7 +27,7 @@ public IEnumerable GenericParameters for (var i = 0; i < genericParameterCount; i++) { var index = Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(genericParameterStart.Value + i); //DynWidth: computed, not read, so temp is fine - var p = LibCpp2IlMain.TheMetadata!.GetGenericParameterFromIndex(index); + var p = OwningMetadata!.GetGenericParameterFromIndex(index); p.Index = index; Debug.Assert(p.genericParameterIndexInOwner == i); yield return p; @@ -36,10 +36,10 @@ public IEnumerable GenericParameters } //DynWidth: ownerIndex is always int, so making temp is ok - public Il2CppTypeDefinition? TypeOwner => isGenericMethod ? null : LibCpp2IlMain.TheMetadata!.GetTypeDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ownerIndex)); + public Il2CppTypeDefinition? TypeOwner => isGenericMethod ? null : OwningMetadata!.GetTypeDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ownerIndex)); //DynWidth: ownerIndex is always int, so making temp is ok - public Il2CppMethodDefinition? MethodOwner => isGenericMethod ? LibCpp2IlMain.TheMetadata!.GetMethodDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ownerIndex)) : null; + public Il2CppMethodDefinition? MethodOwner => isGenericMethod ? OwningMetadata!.GetMethodDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ownerIndex)) : null; public Il2CppTypeDefinition? DeclaringType => TypeOwner ?? MethodOwner?.DeclaringType; diff --git a/LibCpp2IL/Metadata/Il2CppGenericParameter.cs b/LibCpp2IL/Metadata/Il2CppGenericParameter.cs index a093e343e..7ec3bdd17 100644 --- a/LibCpp2IL/Metadata/Il2CppGenericParameter.cs +++ b/LibCpp2IL/Metadata/Il2CppGenericParameter.cs @@ -15,14 +15,14 @@ public class Il2CppGenericParameter : ReadableClass public GenericParameterAttributes Attributes => (GenericParameterAttributes)flags; - public string? Name => LibCpp2IlMain.TheMetadata?.GetStringFromIndex(nameIndex); + public string? Name => OwningMetadata?.GetStringFromIndex(nameIndex); public Il2CppType[] ConstraintTypes => constraintsCount == 0 ? [] - : LibCpp2IlMain.TheMetadata!.constraintIndices + : OwningMetadata!.constraintIndices .Skip(constraintsStart) .Take(constraintsCount) - .Select(LibCpp2IlMain.Binary!.GetType) + .Select(OwningBinary!.GetType) .ToArray(); /// @@ -30,7 +30,7 @@ public class Il2CppGenericParameter : ReadableClass /// public Il2CppVariableWidthIndex Index { get; internal set; } - public Il2CppGenericContainer Owner => LibCpp2IlMain.TheMetadata!.GetGenericContainerFromIndex(ownerIndex); + public Il2CppGenericContainer Owner => OwningMetadata!.GetGenericContainerFromIndex(ownerIndex); public Il2CppTypeEnum Type => Owner.isGenericMethod ? Il2CppTypeEnum.IL2CPP_TYPE_MVAR : Il2CppTypeEnum.IL2CPP_TYPE_VAR; diff --git a/LibCpp2IL/Metadata/Il2CppImageDefinition.cs b/LibCpp2IL/Metadata/Il2CppImageDefinition.cs index 02acdb77b..56f819d38 100644 --- a/LibCpp2IL/Metadata/Il2CppImageDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppImageDefinition.cs @@ -19,13 +19,13 @@ public class Il2CppImageDefinition : ReadableClass [Version(Min = 24.1f)] public int customAttributeStart; [Version(Min = 24.1f)] public uint customAttributeCount; - public string? Name => LibCpp2IlMain.TheMetadata == null ? null : LibCpp2IlMain.TheMetadata.GetStringFromIndex(nameIndex); + public string? Name => OwningMetadata == null ? null : OwningMetadata.GetStringFromIndex(nameIndex); - public Il2CppTypeDefinition[]? Types => LibCpp2IlMain.TheMetadata == null ? null + public Il2CppTypeDefinition[]? Types => OwningMetadata == null ? null : Enumerable .Range(firstTypeIndex.Value, (int)typeCount) .Select(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage) // DynWidth: using Enumerable.Range, not read from file, so making temp is ok - .Select(LibCpp2IlMain.TheMetadata.GetTypeDefinitionFromIndex) + .Select(OwningMetadata.GetTypeDefinitionFromIndex) .ToArray(); public override string ToString() diff --git a/LibCpp2IL/Metadata/Il2CppInterfaceOffset.cs b/LibCpp2IL/Metadata/Il2CppInterfaceOffset.cs index 7a5a3c86a..e367fd4a1 100644 --- a/LibCpp2IL/Metadata/Il2CppInterfaceOffset.cs +++ b/LibCpp2IL/Metadata/Il2CppInterfaceOffset.cs @@ -8,7 +8,7 @@ public class Il2CppInterfaceOffset : ReadableClass public Il2CppVariableWidthIndex typeIndex; public int offset; - public Il2CppTypeReflectionData Type => LibCpp2ILUtils.GetTypeReflectionData(LibCpp2IlMain.Binary!.GetType(typeIndex)); + public Il2CppTypeReflectionData Type => LibCpp2ILUtils.GetTypeReflectionData(OwningBinary!.GetType(typeIndex)); public override string ToString() { diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index 2830e42b4..1028ceb43 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -55,6 +55,11 @@ public class Il2CppMetadata : ClassReadingBinaryReader public int[] referencedAssemblies; + /// + /// Set by after construction. + /// + internal LibCpp2IlContext? OwningContext { get; set; } + private readonly Dictionary, Il2CppFieldDefaultValue> _fieldDefaultValueLookup = new(); private readonly Dictionary _fieldDefaultLookupNew = new(); @@ -429,6 +434,9 @@ private Il2CppMetadata(MemoryStream stream, UnityVersion unityVersion, float met } LibLogger.VerboseNewline($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)"); + + SetOwningMetadataOnAllStructures(); + _hasFinishedInitialRead = true; } finally @@ -451,6 +459,69 @@ private Il2CppMetadata(MemoryStream stream, UnityVersion unityVersion, float met } #pragma warning restore 8618 + private void SetOwningMetadataOnAllStructures() + { + SetOwningMetadata(imageDefinitions); + SetOwningMetadata(AssemblyDefinitions); + SetOwningMetadata(typeDefs); + SetOwningMetadata(interfaceOffsets); + SetOwningMetadata(methodDefs); + SetOwningMetadata(parameterDefs); + SetOwningMetadata(fieldDefs); + SetOwningMetadata(fieldDefaultValues); + SetOwningMetadata(parameterDefaultValues); + SetOwningMetadata(propertyDefs); + SetOwningMetadata(eventDefs); + SetOwningMetadata(genericContainers); + SetOwningMetadata(genericParameters); + SetOwningMetadata(stringLiterals); + SetOwningMetadata(fieldRefs); + + if (RgctxDefinitions != null) + SetOwningMetadata(RgctxDefinitions); + + // Set on sub-objects not directly in arrays + foreach (var asm in AssemblyDefinitions) + asm.AssemblyName.OwningMetadata = this; + } + + private void SetOwningMetadata(T[] items) where T : ReadableClass + { + foreach (var item in items) + item.OwningMetadata = this; + } + + internal void SetOwningBinaryOnAllStructures(Il2CppBinary binary) + { + SetOwningBinary(imageDefinitions, binary); + SetOwningBinary(AssemblyDefinitions, binary); + SetOwningBinary(typeDefs, binary); + SetOwningBinary(interfaceOffsets, binary); + SetOwningBinary(methodDefs, binary); + SetOwningBinary(parameterDefs, binary); + SetOwningBinary(fieldDefs, binary); + SetOwningBinary(fieldDefaultValues, binary); + SetOwningBinary(parameterDefaultValues, binary); + SetOwningBinary(propertyDefs, binary); + SetOwningBinary(eventDefs, binary); + SetOwningBinary(genericContainers, binary); + SetOwningBinary(genericParameters, binary); + SetOwningBinary(stringLiterals, binary); + SetOwningBinary(fieldRefs, binary); + + if (RgctxDefinitions != null) + SetOwningBinary(RgctxDefinitions, binary); + + foreach (var asm in AssemblyDefinitions) + asm.AssemblyName.OwningBinary = binary; + } + + private static void SetOwningBinary(T[] items, Il2CppBinary binary) where T : ReadableClass + { + foreach (var item in items) + item.OwningBinary = binary; + } + private T[] ReadMetadataClassArray(Il2CppGlobalMetadataSectionHeader section) where T : ReadableClass, new() { //First things first, we're going to be moving the position around a lot, so we need to lock. @@ -551,7 +622,7 @@ private uint GetDecodedMethodIndex(uint index) public (Il2CppVariableWidthIndex ptr, Il2CppVariableWidthIndex type) GetFieldDefaultValue(Il2CppVariableWidthIndex fieldIdx) { var fieldDef = GetFieldDefinitionFromIndex(fieldIdx); - var fieldType = LibCpp2IlMain.Binary!.GetType(fieldDef.typeIndex); + var fieldType = OwningContext!.Binary.GetType(fieldDef.typeIndex); if ((fieldType.Attrs & (int)FieldAttributes.HasFieldRVA) != 0) { var fieldDefault = GetFieldDefaultValueFromIndex(fieldIdx); diff --git a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs index aa059fb1d..7bf7f59d9 100644 --- a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs @@ -38,11 +38,11 @@ public class Il2CppMethodDefinition : ReadableClass public string? GlobalKey => DeclaringType == null ? null : DeclaringType.Name + "." + Name + "()"; - public Il2CppType? RawReturnType => LibCpp2IlMain.Binary?.GetType(returnTypeIdx); + public Il2CppType? RawReturnType => OwningBinary?.GetType(returnTypeIdx); - public Il2CppTypeReflectionData? ReturnType => LibCpp2IlMain.Binary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(LibCpp2IlMain.Binary.GetType(returnTypeIdx)); + public Il2CppTypeReflectionData? ReturnType => OwningBinary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(OwningBinary.GetType(returnTypeIdx)); - public Il2CppTypeDefinition? DeclaringType => LibCpp2IlMain.TheMetadata == null ? null : LibCpp2IlMain.TheMetadata.GetTypeDefinitionFromIndex(declaringTypeIdx); + public Il2CppTypeDefinition? DeclaringType => OwningMetadata == null ? null : OwningMetadata.GetTypeDefinitionFromIndex(declaringTypeIdx); private ulong? _methodPointer = null; @@ -52,32 +52,32 @@ public ulong MethodPointer { if (!_methodPointer.HasValue) { - if (LibCpp2IlMain.Binary == null || LibCpp2IlMain.TheMetadata == null || DeclaringType == null) + if (OwningBinary == null || OwningMetadata == null || DeclaringType == null) { - LibLogger.WarnNewline($"Couldn't get method pointer for {Name}. Binary is {LibCpp2IlMain.Binary}, Meta is {LibCpp2IlMain.TheMetadata}, DeclaringType is {DeclaringType}"); + LibLogger.WarnNewline($"Couldn't get method pointer for {Name}. Binary is {OwningBinary}, Meta is {OwningMetadata}, DeclaringType is {DeclaringType}"); return 0; } var asmIdx = 0; //Not needed pre-24.2 - if (LibCpp2IlMain.MetadataVersion >= 27) + if (MetadataVersion >= 27) { - asmIdx = LibCpp2IlMain.Binary.GetCodegenModuleIndexByName(DeclaringType!.DeclaringAssembly!.Name!); + asmIdx = OwningBinary.GetCodegenModuleIndexByName(DeclaringType!.DeclaringAssembly!.Name!); } - else if (LibCpp2IlMain.MetadataVersion >= 24.2f) + else if (MetadataVersion >= 24.2f) { asmIdx = DeclaringType!.DeclaringAssembly!.assemblyIndex; } - _methodPointer = LibCpp2IlMain.Binary.GetMethodPointer(methodIndex, MethodIndex, asmIdx, token); + _methodPointer = OwningBinary.GetMethodPointer(methodIndex, MethodIndex, asmIdx, token); } return _methodPointer.Value; } } - public long MethodOffsetInFile => MethodPointer == 0 || LibCpp2IlMain.Binary == null ? 0 : LibCpp2IlMain.Binary.TryMapVirtualAddressToRaw(MethodPointer, out var ret) ? ret : 0; + public long MethodOffsetInFile => MethodPointer == 0 || OwningBinary == null ? 0 : OwningBinary.TryMapVirtualAddressToRaw(MethodPointer, out var ret) ? ret : 0; - public ulong Rva => MethodPointer == 0 || LibCpp2IlMain.Binary == null ? 0 : LibCpp2IlMain.Binary.GetRva(MethodPointer); + public ulong Rva => MethodPointer == 0 || OwningBinary == null ? 0 : OwningBinary.GetRva(MethodPointer); public string? HumanReadableSignature => ReturnType == null || Parameters == null || Name == null ? null : $"{ReturnType} {Name}({string.Join(", ", Parameters.AsEnumerable())})"; @@ -85,7 +85,7 @@ public Il2CppParameterDefinition[]? InternalParameterData { get { - if (LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null) + if (OwningMetadata == null || OwningBinary == null) return null; if (parameterStart.IsNull || parameterCount == 0) @@ -95,7 +95,7 @@ public Il2CppParameterDefinition[]? InternalParameterData for (var i = 0; i < parameterCount; i++) { - ret[i] = LibCpp2IlMain.TheMetadata.GetParameterDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(parameterStart.Value + i)); + ret[i] = OwningMetadata.GetParameterDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(parameterStart.Value + i)); } return ret; @@ -104,7 +104,7 @@ public Il2CppParameterDefinition[]? InternalParameterData public Il2CppType[]? InternalParameterTypes => InternalParameterData == null ? null - : InternalParameterData.Select(paramDef => LibCpp2IlMain.Binary!.GetType(paramDef.typeIndex)) + : InternalParameterData.Select(paramDef => OwningBinary!.GetType(paramDef.typeIndex)) .ToArray(); private Il2CppParameterReflectionData[]? _cachedParameters; @@ -118,18 +118,18 @@ public Il2CppParameterReflectionData[]? Parameters _cachedParameters = InternalParameterData .Select((paramDef, idx) => { - var paramType = LibCpp2IlMain.Binary!.GetType(paramDef.typeIndex); + var paramType = OwningBinary!.GetType(paramDef.typeIndex); var paramFlags = (ParameterAttributes)paramType.Attrs; var paramDefaultData = (paramFlags & ParameterAttributes.HasDefault) != 0 - ? LibCpp2IlMain.TheMetadata!.GetParameterDefaultValueFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(parameterStart.Value + idx)) //DynamicWidth: value is computed so temp usage is ok + ? OwningMetadata!.GetParameterDefaultValueFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(parameterStart.Value + idx)) //DynamicWidth: value is computed so temp usage is ok : null; return new Il2CppParameterReflectionData { Type = LibCpp2ILUtils.GetTypeReflectionData(paramType)!, - ParameterName = LibCpp2IlMain.TheMetadata!.GetStringFromIndex(paramDef.nameIndex), + ParameterName = OwningMetadata!.GetStringFromIndex(paramDef.nameIndex), Attributes = paramFlags, RawType = paramType, - DefaultValue = paramDefaultData == null ? null : LibCpp2ILUtils.GetDefaultValue(paramDefaultData.dataIndex, paramDefaultData.typeIndex), + DefaultValue = paramDefaultData == null ? null : LibCpp2ILUtils.GetDefaultValue(paramDefaultData.dataIndex, paramDefaultData.typeIndex, OwningMetadata!, OwningBinary!), ParameterIndex = idx, }; }).ToArray(); @@ -139,7 +139,7 @@ public Il2CppParameterReflectionData[]? Parameters } } - public Il2CppGenericContainer? GenericContainer => genericContainerIndex.IsNull ? null : LibCpp2IlMain.TheMetadata?.GetGenericContainerFromIndex(genericContainerIndex); + public Il2CppGenericContainer? GenericContainer => genericContainerIndex.IsNull ? null : OwningMetadata?.GetGenericContainerFromIndex(genericContainerIndex); public bool IsUnmanagedCallersOnly => (iflags & 0xF000) != 0; @@ -147,7 +147,7 @@ public Il2CppParameterReflectionData[]? Parameters public override string? ToString() { - if (LibCpp2IlMain.TheMetadata == null) + if (OwningMetadata == null) return base.ToString(); return $"Il2CppMethodDefinition[Name='{Name}', ReturnType={ReturnType}, DeclaringType={DeclaringType}]"; diff --git a/LibCpp2IL/Metadata/Il2CppParameterDefaultValue.cs b/LibCpp2IL/Metadata/Il2CppParameterDefaultValue.cs index f03063a44..d7a24636a 100644 --- a/LibCpp2IL/Metadata/Il2CppParameterDefaultValue.cs +++ b/LibCpp2IL/Metadata/Il2CppParameterDefaultValue.cs @@ -8,7 +8,7 @@ public class Il2CppParameterDefaultValue : ReadableClass public Il2CppVariableWidthIndex typeIndex; public Il2CppVariableWidthIndex dataIndex; - public object? ContainedDefaultValue => LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex); + public object? ContainedDefaultValue => OwningMetadata == null || OwningBinary == null ? null : LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex, OwningMetadata, OwningBinary); public override void Read(ClassReadingBinaryReader reader) { diff --git a/LibCpp2IL/Metadata/Il2CppParameterDefinition.cs b/LibCpp2IL/Metadata/Il2CppParameterDefinition.cs index e48ffee92..97ae4b435 100644 --- a/LibCpp2IL/Metadata/Il2CppParameterDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppParameterDefinition.cs @@ -11,7 +11,7 @@ public class Il2CppParameterDefinition : ReadableClass, IIl2CppTokenProvider public uint Token => token; - public Il2CppType? RawType => LibCpp2IlMain.Binary?.GetType(typeIndex); + public Il2CppType? RawType => OwningBinary?.GetType(typeIndex); public string? Name { get; private set; } diff --git a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs index 2d1b598d4..1d1ea067a 100644 --- a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs @@ -25,9 +25,9 @@ public Il2CppTypeDefinition? DeclaringType if (_type != null) return _type; - if (LibCpp2IlMain.TheMetadata == null) return null; + if (OwningMetadata == null) return null; - _type = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault(t => t.Properties!.Contains(this)); + _type = OwningMetadata.typeDefs.FirstOrDefault(t => t.Properties!.Contains(this)); return _type; } internal set => _type = value; @@ -35,13 +35,13 @@ public Il2CppTypeDefinition? DeclaringType public string? Name { get; private set; } - public Il2CppMethodDefinition? Getter => LibCpp2IlMain.TheMetadata == null || get.IsNull || DeclaringType == null ? null : LibCpp2IlMain.TheMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + get); + public Il2CppMethodDefinition? Getter => OwningMetadata == null || get.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + get); - public Il2CppMethodDefinition? Setter => LibCpp2IlMain.TheMetadata == null || set.IsNull || DeclaringType == null ? null : LibCpp2IlMain.TheMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + set); + public Il2CppMethodDefinition? Setter => OwningMetadata == null || set.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + set); - public Il2CppTypeReflectionData? PropertyType => LibCpp2IlMain.TheMetadata == null ? null : Getter == null ? Setter!.Parameters![0].Type : Getter!.ReturnType; + public Il2CppTypeReflectionData? PropertyType => OwningMetadata == null ? null : Getter == null ? Setter!.Parameters![0].Type : Getter!.ReturnType; - public Il2CppType? RawPropertyType => LibCpp2IlMain.TheMetadata == null ? null : Getter == null ? Setter!.Parameters![0].RawType : Getter!.RawReturnType; + public Il2CppType? RawPropertyType => OwningMetadata == null ? null : Getter == null ? Setter!.Parameters![0].RawType : Getter!.RawReturnType; public bool IsStatic => Getter == null ? Setter!.IsStatic : Getter!.IsStatic; public uint Token => token; diff --git a/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs b/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs index c63b128b5..8cde449b5 100644 --- a/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs @@ -78,14 +78,14 @@ public class Il2CppTypeDefinition : ReadableClass public TypeAttributes Attributes => (TypeAttributes)Flags; - public Il2CppType RawType => LibCpp2IlMain.Binary!.GetType(ByvalTypeIndex); + public Il2CppType RawType => OwningBinary!.GetType(ByvalTypeIndex); public Il2CppTypeDefinitionSizes RawSizes { get { - var sizePtr = LibCpp2IlMain.Binary!.TypeDefinitionSizePointers[TypeIndex.Value]; - return LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(sizePtr); + var sizePtr = OwningBinary!.TypeDefinitionSizePointers[TypeIndex.Value]; + return OwningBinary.ReadReadableAtVirtualAddress(sizePtr); } } @@ -97,7 +97,7 @@ public Il2CppInterfaceOffset[] InterfaceOffsets { if (InterfaceOffsetsStart.IsNull) return []; - return LibCpp2IlMain.TheMetadata!.GetInterfaceOffsetsFromIndexAndCount(InterfaceOffsetsStart, InterfaceOffsetsCount); + return OwningMetadata!.GetInterfaceOffsetsFromIndexAndCount(InterfaceOffsetsStart, InterfaceOffsetsCount); } } @@ -107,7 +107,7 @@ public MetadataUsage?[] VTable { if (VtableStart < 0) return []; - return LibCpp2IlMain.TheMetadata!.VTableMethodIndices.SubArray(VtableStart, VtableCount).Select(v => MetadataUsage.DecodeMetadataUsage(v, 0)).ToArray(); + return OwningMetadata!.VTableMethodIndices.SubArray(VtableStart, VtableCount).Select(v => MetadataUsage.DecodeMetadataUsage(v, 0)).ToArray(); } } @@ -125,9 +125,9 @@ public Il2CppImageDefinition? DeclaringAssembly { if (_cachedDeclaringAssembly == null) { - if (LibCpp2IlMain.TheMetadata == null) return null; + if (OwningMetadata == null) return null; - LibCpp2ILUtils.PopulateDeclaringAssemblyCache(); + LibCpp2ILUtils.PopulateDeclaringAssemblyCache(OwningMetadata); } return _cachedDeclaringAssembly; @@ -135,16 +135,16 @@ public Il2CppImageDefinition? DeclaringAssembly internal set => _cachedDeclaringAssembly = value; } - public Il2CppCodeGenModule? CodeGenModule => LibCpp2IlMain.Binary == null ? null : LibCpp2IlMain.Binary.GetCodegenModuleByName(DeclaringAssembly!.Name!); + public Il2CppCodeGenModule? CodeGenModule => OwningBinary == null ? null : OwningBinary.GetCodegenModuleByName(DeclaringAssembly!.Name!); public Il2CppRGCTXDefinition[] RgctXs { get { - if (LibCpp2IlMain.MetadataVersion < 24.2f) + if (MetadataVersion < 24.2f) { //No codegen modules here. - return LibCpp2IlMain.TheMetadata!.RgctxDefinitions!.Skip(RgctxStartIndex).Take(RgctxCount).ToArray(); + return OwningMetadata!.RgctxDefinitions!.Skip(RgctxStartIndex).Take(RgctxCount).ToArray(); } var cgm = CodeGenModule; @@ -157,7 +157,7 @@ public Il2CppRGCTXDefinition[] RgctXs if (rangePair == null) return []; - return LibCpp2IlMain.Binary!.GetRgctxDataForPair(cgm, rangePair); + return OwningBinary!.GetRgctxDataForPair(cgm, rangePair); } } @@ -165,12 +165,12 @@ public ulong[] RgctxMethodPointers { get { - var index = LibCpp2IlMain.Binary!.GetCodegenModuleIndexByName(DeclaringAssembly!.Name!); + var index = OwningBinary!.GetCodegenModuleIndexByName(DeclaringAssembly!.Name!); if (index < 0) return []; - var pointers = LibCpp2IlMain.Binary!.GetCodegenModuleMethodPointers(index); + var pointers = OwningBinary!.GetCodegenModuleMethodPointers(index); return RgctXs .Where(r => r.type == Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_METHOD) @@ -186,7 +186,7 @@ public string? Namespace get { if (_cachedNamespace == null) - _cachedNamespace = LibCpp2IlMain.TheMetadata == null ? null : LibCpp2IlMain.TheMetadata.GetStringFromIndex(NamespaceIndex); + _cachedNamespace = OwningMetadata == null ? null : OwningMetadata.GetStringFromIndex(NamespaceIndex); return _cachedNamespace; } @@ -199,7 +199,7 @@ public string? Name get { if (_cachedName == null) - _cachedName = LibCpp2IlMain.TheMetadata == null ? null : LibCpp2IlMain.TheMetadata.GetStringFromIndex(NameIndex); + _cachedName = OwningMetadata == null ? null : OwningMetadata.GetStringFromIndex(NameIndex); return _cachedName; } @@ -209,7 +209,7 @@ public string? FullName { get { - if (LibCpp2IlMain.TheMetadata == null) + if (OwningMetadata == null) return null; if (DeclaringType != null) @@ -219,34 +219,34 @@ public string? FullName } } - public Il2CppType? RawBaseType => ParentIndex.IsNull ? null : LibCpp2IlMain.Binary!.GetType(ParentIndex); + public Il2CppType? RawBaseType => ParentIndex.IsNull ? null : OwningBinary!.GetType(ParentIndex); - public Il2CppTypeReflectionData? BaseType => ParentIndex.IsNull || LibCpp2IlMain.Binary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(LibCpp2IlMain.Binary!.GetType(ParentIndex)); + public Il2CppTypeReflectionData? BaseType => ParentIndex.IsNull || OwningBinary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(OwningBinary!.GetType(ParentIndex)); public Il2CppFieldDefinition[]? Fields { get { - if (LibCpp2IlMain.TheMetadata == null) + if (OwningMetadata == null) return null; if (FirstFieldIdx.IsNull || FieldCount == 0) return []; - return LibCpp2IlMain.TheMetadata.GetFieldDefinitionsFromIndexAndCount(FirstFieldIdx, FieldCount); + return OwningMetadata.GetFieldDefinitionsFromIndexAndCount(FirstFieldIdx, FieldCount); } } public FieldAttributes[]? FieldAttributes => Fields? .Select(f => f.typeIndex) - .Select(idx => LibCpp2IlMain.Binary!.GetType(idx)) + .Select(idx => OwningBinary!.GetType(idx)) .Select(t => (FieldAttributes)t.Attrs) .ToArray(); public object?[]? FieldDefaults => Fields? .Select((f, idx) => (f.FieldIndex, FieldAttributes![idx])) - .Select(tuple => (tuple.Item2 & System.Reflection.FieldAttributes.HasDefault) != 0 ? LibCpp2IlMain.TheMetadata!.GetFieldDefaultValueFromIndex(tuple.FieldIndex) : null) - .Select(def => def == null ? null : LibCpp2ILUtils.GetDefaultValue(def.dataIndex, def.typeIndex)) + .Select(tuple => (tuple.Item2 & System.Reflection.FieldAttributes.HasDefault) != 0 ? OwningMetadata!.GetFieldDefaultValueFromIndex(tuple.FieldIndex) : null) + .Select(def => def == null ? null : LibCpp2ILUtils.GetDefaultValue(def.dataIndex, def.typeIndex, OwningMetadata!, OwningBinary!)) .ToArray(); public Il2CppFieldReflectionData[]? FieldInfos @@ -268,7 +268,7 @@ public Il2CppFieldReflectionData[]? FieldInfos attributes![i], defaults![i], i, - LibCpp2IlMain.Binary!.GetFieldOffsetFromIndex(TypeIndex, i, fields[i].FieldIndex, IsValueType, attributes[i].HasFlag(System.Reflection.FieldAttributes.Static)) + OwningBinary!.GetFieldOffsetFromIndex(TypeIndex, i, fields[i].FieldIndex, IsValueType, attributes[i].HasFlag(System.Reflection.FieldAttributes.Static)) ); } @@ -280,13 +280,13 @@ public Il2CppMethodDefinition[]? Methods { get { - if (LibCpp2IlMain.TheMetadata == null) + if (OwningMetadata == null) return null; if (FirstMethodIdx.IsNull || MethodCount == 0) return []; - return LibCpp2IlMain.TheMetadata.GetMethodDefinitionsFromIndexAndCount(FirstMethodIdx, MethodCount); + return OwningMetadata.GetMethodDefinitionsFromIndexAndCount(FirstMethodIdx, MethodCount); } } @@ -294,13 +294,13 @@ public Il2CppPropertyDefinition[]? Properties { get { - if (LibCpp2IlMain.TheMetadata == null) + if (OwningMetadata == null) return null; if (FirstPropertyId.IsNull || PropertyCount == 0) return []; - var ret = LibCpp2IlMain.TheMetadata.GetPropertyDefinitionsFromIndexAndCount(FirstPropertyId, PropertyCount); + var ret = OwningMetadata.GetPropertyDefinitionsFromIndexAndCount(FirstPropertyId, PropertyCount); foreach (var definition in ret) definition.DeclaringType = this; @@ -313,13 +313,13 @@ public Il2CppEventDefinition[]? Events { get { - if (LibCpp2IlMain.TheMetadata == null) + if (OwningMetadata == null) return null; if (FirstEventId.IsNull || EventCount == 0) return []; - var ret = LibCpp2IlMain.TheMetadata.GetEventDefinitionsFromIndexAndCount(FirstEventId, EventCount); + var ret = OwningMetadata.GetEventDefinitionsFromIndexAndCount(FirstEventId, EventCount); foreach (var def in ret) def.DeclaringType = this; @@ -327,32 +327,32 @@ public Il2CppEventDefinition[]? Events } } - public Il2CppTypeDefinition[]? NestedTypes => LibCpp2IlMain.TheMetadata == null + public Il2CppTypeDefinition[]? NestedTypes => OwningMetadata == null ? null - : LibCpp2IlMain.TheMetadata.GetNestedTypeIndicesFromIndexAndCount(NestedTypesStart, NestedTypeCount) + : OwningMetadata.GetNestedTypeIndicesFromIndexAndCount(NestedTypesStart, NestedTypeCount) .Select(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage) //DynWidth: nestedTypeIndices is always int, so making temp is ok - .Select(LibCpp2IlMain.TheMetadata.GetTypeDefinitionFromIndex) + .Select(OwningMetadata.GetTypeDefinitionFromIndex) .ToArray(); - public Il2CppType[] RawInterfaces => LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null + public Il2CppType[] RawInterfaces => OwningMetadata == null || OwningBinary == null ? [] - : LibCpp2IlMain.TheMetadata.GetInterfaceIndicesFromIndexAndCount(InterfacesStart, InterfacesCount) - .Select(LibCpp2IlMain.Binary.GetType) + : OwningMetadata.GetInterfaceIndicesFromIndexAndCount(InterfacesStart, InterfacesCount) + .Select(OwningBinary.GetType) .ToArray(); - public Il2CppTypeReflectionData[]? Interfaces => LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null + public Il2CppTypeReflectionData[]? Interfaces => OwningMetadata == null || OwningBinary == null ? null : RawInterfaces .Select(LibCpp2ILUtils.GetTypeReflectionData) .ToArray(); - public Il2CppTypeDefinition? DeclaringType => LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null || DeclaringTypeIndex.IsNull ? null : LibCpp2IlMain.Binary.GetType(DeclaringTypeIndex).CoerceToUnderlyingTypeDefinition(); + public Il2CppTypeDefinition? DeclaringType => OwningMetadata == null || OwningBinary == null || DeclaringTypeIndex.IsNull ? null : OwningBinary.GetType(DeclaringTypeIndex).CoerceToUnderlyingTypeDefinition(); - public Il2CppTypeDefinition? ElementType => LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null || ElementTypeIndex < 0 + public Il2CppTypeDefinition? ElementType => OwningMetadata == null || OwningBinary == null || ElementTypeIndex < 0 ? null - : LibCpp2IlMain.Binary.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ElementTypeIndex)).CoerceToUnderlyingTypeDefinition(); //DynWidth: ElementTypeIndex was removed in v35, so it's never dynamic + : OwningBinary.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ElementTypeIndex)).CoerceToUnderlyingTypeDefinition(); //DynWidth: ElementTypeIndex was removed in v35, so it's never dynamic - public Il2CppGenericContainer? GenericContainer => GenericContainerIndex.IsNull ? null : LibCpp2IlMain.TheMetadata?.GetGenericContainerFromIndex(GenericContainerIndex); + public Il2CppGenericContainer? GenericContainer => GenericContainerIndex.IsNull ? null : OwningMetadata?.GetGenericContainerFromIndex(GenericContainerIndex); public Il2CppType EnumUnderlyingType { @@ -366,13 +366,13 @@ public Il2CppType EnumUnderlyingType return RawBaseType!; //pre-v35: ElementTypeIndex is used for enums to store the underlying type, so we need to get the type from there instead of the parent index (which is just System.Enum) - return LibCpp2IlMain.Binary!.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ElementTypeIndex)); + return OwningBinary!.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ElementTypeIndex)); } } public override string? ToString() { - if (LibCpp2IlMain.TheMetadata == null) + if (OwningMetadata == null) return base.ToString(); return $"Il2CppTypeDefinition[namespace='{Namespace}', name='{Name}', parentType={BaseType?.ToString() ?? "null"}, assembly={DeclaringAssembly}]"; diff --git a/LibCpp2IL/MetadataUsage.cs b/LibCpp2IL/MetadataUsage.cs index 87a1fe3a1..31428c3da 100644 --- a/LibCpp2IL/MetadataUsage.cs +++ b/LibCpp2IL/MetadataUsage.cs @@ -1,14 +1,18 @@ -using System; +using System; using LibCpp2IL.BinaryStructures; using LibCpp2IL.Metadata; using LibCpp2IL.Reflection; namespace LibCpp2IL; -public class MetadataUsage(MetadataUsageType type, ulong offset, uint value) +public class MetadataUsage { - public readonly MetadataUsageType Type = type; - public readonly ulong Offset = offset; + public readonly MetadataUsageType Type; + public readonly ulong Offset; + + private readonly uint _value; + private readonly Il2CppBinary? _binary; + private readonly Il2CppMetadata? _metadata; private string? _cachedName; @@ -23,7 +27,19 @@ public class MetadataUsage(MetadataUsageType type, ulong offset, uint value) private Cpp2IlMethodRef? _cachedGenericMethod; - public uint RawValue => value; + public MetadataUsage(MetadataUsageType type, ulong offset, uint value, Il2CppBinary? binary = null, Il2CppMetadata? metadata = null) + { + Type = type; + Offset = offset; + _value = value; + _binary = binary; + _metadata = metadata; + } + + private Il2CppBinary EffectiveBinary => _binary ?? LibCpp2IlMain.Binary!; + private Il2CppMetadata EffectiveMetadata => _metadata ?? LibCpp2IlMain.TheMetadata!; + + public uint RawValue => _value; public object Value => Type switch @@ -39,11 +55,11 @@ public class MetadataUsage(MetadataUsageType type, ulong offset, uint value) public bool IsValid => Type switch { - MetadataUsageType.Type or MetadataUsageType.TypeInfo => value < LibCpp2IlMain.Binary!.NumTypes, - MetadataUsageType.MethodDef => value < LibCpp2IlMain.TheMetadata!.MethodDefinitionCount, - MetadataUsageType.FieldInfo => value < LibCpp2IlMain.TheMetadata!.fieldRefs.Length, - MetadataUsageType.StringLiteral => value < LibCpp2IlMain.TheMetadata!.stringLiterals.Length, - MetadataUsageType.MethodRef => value < LibCpp2IlMain.Binary!.AllGenericMethodSpecs.Length, + MetadataUsageType.Type or MetadataUsageType.TypeInfo => _value < EffectiveBinary.NumTypes, + MetadataUsageType.MethodDef => _value < EffectiveMetadata.MethodDefinitionCount, + MetadataUsageType.FieldInfo => _value < EffectiveMetadata.fieldRefs.Length, + MetadataUsageType.StringLiteral => _value < EffectiveMetadata.stringLiterals.Length, + MetadataUsageType.MethodRef => _value < EffectiveBinary.AllGenericMethodSpecs.Length, _ => false }; @@ -57,13 +73,13 @@ public Il2CppTypeReflectionData AsType() case MetadataUsageType.TypeInfo: try { - _cachedType = LibCpp2IlMain.Binary!.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int) value)); //DynWidth: value is always masked out of 32-bits, ok for temp usage + _cachedType = EffectiveBinary.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int) _value)); //DynWidth: value is always masked out of 32-bits, ok for temp usage _cachedTypeReflectionData = LibCpp2ILUtils.GetTypeReflectionData(_cachedType); _cachedName = _cachedTypeReflectionData?.ToString(); } catch (Exception e) { - throw new Exception($"Failed to convert this metadata usage to a type, but it is of type {Type}, with a value of {value} (0x{value:X}). There are {LibCpp2IlMain.Binary!.NumTypes} types", e); + throw new Exception($"Failed to convert this metadata usage to a type, but it is of type {Type}, with a value of {_value} (0x{_value:X}). There are {EffectiveBinary.NumTypes} types", e); } break; @@ -82,7 +98,7 @@ public Il2CppMethodDefinition AsMethod() switch (Type) { case MetadataUsageType.MethodDef: - _cachedMethod = LibCpp2IlMain.TheMetadata!.GetMethodDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int)value)); //DynWidth: value is always masked out of 32-bits, ok for temp usage + _cachedMethod = EffectiveMetadata.GetMethodDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int)_value)); //DynWidth: value is always masked out of 32-bits, ok for temp usage _cachedName = _cachedMethod.GlobalKey; break; default: @@ -100,7 +116,7 @@ public Il2CppFieldDefinition AsField() switch (Type) { case MetadataUsageType.FieldInfo: - var fieldRef = LibCpp2IlMain.TheMetadata!.fieldRefs[value]; + var fieldRef = EffectiveMetadata.fieldRefs[_value]; _cachedField = fieldRef.FieldDefinition; _cachedName = fieldRef.DeclaringTypeDefinition!.FullName + "." + _cachedField!.Name; break; @@ -119,7 +135,7 @@ public string AsLiteral() switch (Type) { case MetadataUsageType.StringLiteral: - _cachedName = _cachedLiteral = LibCpp2IlMain.TheMetadata!.GetStringLiteralFromIndex(value); + _cachedName = _cachedLiteral = EffectiveMetadata.GetStringLiteralFromIndex(_value); break; default: throw new Exception($"Cannot cast metadata usage of kind {Type} to a String Literal"); @@ -136,7 +152,7 @@ public Cpp2IlMethodRef AsGenericMethodRef() switch (Type) { case MetadataUsageType.MethodRef: - var methodSpec = LibCpp2IlMain.Binary!.GetMethodSpec((int)value); + var methodSpec = EffectiveBinary.GetMethodSpec((int)_value); _cachedGenericMethod = new Cpp2IlMethodRef(methodSpec); _cachedName = _cachedGenericMethod.ToString(); @@ -155,25 +171,31 @@ public override string ToString() } - public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address) + public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address) => + DecodeMetadataUsage(encoded, address, null, null); + + public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address, Il2CppBinary? binary, Il2CppMetadata? metadata) { + var effectiveBinary = binary ?? LibCpp2IlMain.Binary!; + var effectiveMetadata = metadata ?? LibCpp2IlMain.TheMetadata!; + var encodedType = encoded & 0xE000_0000; var type = (MetadataUsageType)(encodedType >> 29); if (type <= MetadataUsageType.MethodRef && type >= MetadataUsageType.TypeInfo) { var index = (uint)(encoded & 0x1FFF_FFFF); - if (LibCpp2IlMain.MetadataVersion >= 27) + if (effectiveMetadata.MetadataVersion >= 27) index >>= 1; - if (type is MetadataUsageType.Type or MetadataUsageType.TypeInfo && index > LibCpp2IlMain.Binary!.NumTypes) + if (type is MetadataUsageType.Type or MetadataUsageType.TypeInfo && index > effectiveBinary.NumTypes) return null; - if (type == MetadataUsageType.MethodDef && index > LibCpp2IlMain.TheMetadata!.MethodDefinitionCount) + if (type == MetadataUsageType.MethodDef && index > effectiveMetadata.MethodDefinitionCount) return null; - return new MetadataUsage(type, address, index); + return new MetadataUsage(type, address, index, binary, metadata); } return null; diff --git a/LibCpp2IL/ReadableClass.cs b/LibCpp2IL/ReadableClass.cs index 7af15787a..9fe71187a 100644 --- a/LibCpp2IL/ReadableClass.cs +++ b/LibCpp2IL/ReadableClass.cs @@ -1,11 +1,15 @@ using System; +using LibCpp2IL.Metadata; namespace LibCpp2IL; public abstract class ReadableClass { internal float MetadataVersion { get; set; } - + + internal Il2CppBinary? OwningBinary { get; set; } + internal Il2CppMetadata? OwningMetadata { get; set; } + protected bool IsAtLeast(float vers) => MetadataVersion >= vers; protected bool IsLessThan(float vers) => MetadataVersion < vers; protected bool IsAtMost(float vers) => MetadataVersion <= vers; diff --git a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs index 1e741c9c3..9a87cbf70 100644 --- a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs +++ b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs @@ -28,7 +28,7 @@ public class Il2CppTypeReflectionData public bool isPointer; #pragma warning restore 8618 - public Il2CppGenericParameter? GenericParameter => isArray || isType ? null : LibCpp2IlMain.TheMetadata?.GetGenericParameterFromIndex(variableGenericParamIndex); + public Il2CppGenericParameter? GenericParameter => isArray || isType ? null : baseType?.OwningMetadata?.GetGenericParameterFromIndex(variableGenericParamIndex) ?? LibCpp2IlMain.TheMetadata?.GetGenericParameterFromIndex(variableGenericParamIndex); private string GetPtrSuffix() { diff --git a/LibCpp2ILTests/Tests.cs b/LibCpp2ILTests/Tests.cs index bc386584a..a2a8bd2bd 100644 --- a/LibCpp2ILTests/Tests.cs +++ b/LibCpp2ILTests/Tests.cs @@ -33,12 +33,11 @@ private async void CheckFiles(string metadataUrl, string binaryUrl, UnityVersion LibCpp2IlMain.Settings.DisableMethodPointerMapping = true; LibCpp2IlMain.Settings.AllowManualMetadataAndCodeRegInput = false; - //Clean up any previous runs - LibCpp2IlMain.TheMetadata = null; - LibCpp2IlMain.Binary = null; - _outputHelper.WriteLine("Invoking LibCpp2IL..."); - Assert.True(LibCpp2IlMain.Initialize(binaryBytes, metadataBytes, unityVer)); + var context = LibCpp2IlMain.InitializeAsContext(binaryBytes, metadataBytes, unityVer); + Assert.NotNull(context); + Assert.NotNull(context.Binary); + Assert.NotNull(context.Metadata); _outputHelper.WriteLine("Done."); } From e0c2e9de1483f1a5ed53e51c41a4b1a7df7b23c3 Mon Sep 17 00:00:00 2001 From: Lucas <31241699+nitrog0d@users.noreply.github.com> Date: Thu, 9 Apr 2026 07:53:23 -0300 Subject: [PATCH 2/4] Fix warnings --- Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs | 2 ++ LibCpp2IL/LibCpp2IlContextBuilder.cs | 6 ++++++ LibCpp2IL/LibCpp2IlGlobalMapper.cs | 2 ++ LibCpp2IL/LibCpp2IlUtils.cs | 10 ++++++---- LibCpp2IL/MetadataUsage.cs | 4 ++++ LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs | 2 ++ LibCpp2IL/Reflection/LibCpp2IlReflection.cs | 2 ++ 7 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs b/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs index 19ad5bdbb..76cc7b35f 100644 --- a/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs +++ b/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs @@ -26,11 +26,13 @@ public void Process(MethodAnalysisContext methodAnalysisContext, Block block) var memoryOp = (IsilMemoryOperand)instruction.Operands[1].Data; if (memoryOp.Base == null && memoryOp.Index == null && memoryOp.Scale == 0) { +#pragma warning disable CS0618 // Deprecated static API - TODO: migrate to context-based API when available from Cpp2IL.Core var val = LibCpp2IlMain.GetLiteralByAddress((ulong)memoryOp.Addend); if (val == null) { // Try instead check if its type metadata usage var metadataUsage = LibCpp2IlMain.GetTypeGlobalByAddress((ulong)memoryOp.Addend); +#pragma warning restore CS0618 if (metadataUsage != null && methodAnalysisContext.DeclaringType is not null) { var typeAnalysisContext = metadataUsage.ToContext(methodAnalysisContext.DeclaringType!.DeclaringAssembly); diff --git a/LibCpp2IL/LibCpp2IlContextBuilder.cs b/LibCpp2IL/LibCpp2IlContextBuilder.cs index 9420bf874..544b8aa04 100644 --- a/LibCpp2IL/LibCpp2IlContextBuilder.cs +++ b/LibCpp2IL/LibCpp2IlContextBuilder.cs @@ -63,9 +63,11 @@ public void LoadMetadata(byte[] metadataBytes, UnityVersion unityVersion) // Legacy/static API compatibility: some in-binary structures still resolve via LibCpp2IlMain.Binary/TheMetadata // during binary initialization, so we must set metadata defaults before initializing the binary. +#pragma warning disable CS0618 // Intentional writes to legacy static fields for backwards compatibility LibCpp2IlMain.TheMetadata = metadata; LibCpp2IlMain.DefaultContext = _context; LibCpp2IlMain.Il2CppTypeHasNumMods5Bits = _context.Il2CppTypeHasNumMods5Bits; +#pragma warning restore CS0618 _metadataLoaded = true; } @@ -78,7 +80,9 @@ public void LoadBinary(byte[] binaryBytes) var bin = _context.Binary = LibCpp2IlBinaryRegistry.CreateAndInit(binaryBytes, _context.Metadata); // Complete legacy/static initialization now that the binary exists. +#pragma warning disable CS0618 // Intentional write to legacy static field for backwards compatibility LibCpp2IlMain.Binary = bin; +#pragma warning restore CS0618 // Set OwningBinary on all metadata structures now that the binary exists. _context.Metadata.SetOwningBinaryOnAllStructures(bin); @@ -96,7 +100,9 @@ public void LoadBinary(Il2CppBinary binary) _context.Binary = binary; // Complete legacy/static initialization now that the binary exists. +#pragma warning disable CS0618 // Intentional write to legacy static field for backwards compatibility LibCpp2IlMain.Binary = binary; +#pragma warning restore CS0618 // Set OwningBinary on all metadata structures now that the binary exists. _context.Metadata.SetOwningBinaryOnAllStructures(binary); diff --git a/LibCpp2IL/LibCpp2IlGlobalMapper.cs b/LibCpp2IL/LibCpp2IlGlobalMapper.cs index acaa4f82c..24525fc3d 100644 --- a/LibCpp2IL/LibCpp2IlGlobalMapper.cs +++ b/LibCpp2IL/LibCpp2IlGlobalMapper.cs @@ -95,6 +95,7 @@ private static void MapGlobalIdentifiersPre27(Il2CppMetadata metadata, Il2CppBin LiteralsByAddress[globalIdentifier.Offset] = globalIdentifier; } +#pragma warning disable CS0618 // Intentional use of legacy static LibCpp2IlMain.Binary for backwards compatibility public static MetadataUsage? CheckForPost27GlobalAt(ulong address) { if (!LibCpp2IlMain.Binary!.TryMapVirtualAddressToRaw(address, out var raw) || raw >= LibCpp2IlMain.Binary.RawLength) @@ -108,4 +109,5 @@ private static void MapGlobalIdentifiersPre27(Il2CppMetadata metadata, Il2CppBin return metadataUsage; } +#pragma warning restore CS0618 } diff --git a/LibCpp2IL/LibCpp2IlUtils.cs b/LibCpp2IL/LibCpp2IlUtils.cs index 45f6ba112..5cc93cb18 100644 --- a/LibCpp2IL/LibCpp2IlUtils.cs +++ b/LibCpp2IL/LibCpp2IlUtils.cs @@ -127,7 +127,7 @@ public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssemb var genericClass = cppAssembly.ReadReadableAtVirtualAddress(type.Data.GenericClass); var typeDef = genericClass.TypeDefinition; ret = typeDef.Name!; - var genericInst = genericClass.Context.ClassInst; + var genericInst = genericClass.Context.ClassInst!; ret = ret.Replace($"`{genericInst.pointerCount}", ""); ret += GetGenericTypeParamNames(metadata, cppAssembly, genericInst); break; @@ -142,7 +142,7 @@ public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssemb case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: { var arrayType = cppAssembly.ReadReadableAtVirtualAddress(type.Data.Array); - var oriType = arrayType.ElementType; + var oriType = arrayType.GetElementTypeOrThrow(); ret = $"{GetTypeName(metadata, cppAssembly, oriType)}[{new string(',', arrayType.rank - 1)}]"; break; } @@ -239,8 +239,10 @@ public static Il2CppTypeReflectionData WrapType(Il2CppTypeDefinition what) public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) { +#pragma warning disable CS0618 // Fallback to legacy statics for backwards compatibility var binary = forWhat.OwningBinary ?? LibCpp2IlMain.Binary; var metadata = forWhat.OwningMetadata ?? LibCpp2IlMain.TheMetadata; +#pragma warning restore CS0618 if (binary == null || metadata == null) throw new Exception("Can't get type reflection data when not initialized. How did you even get the type?"); @@ -298,7 +300,7 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) //CHANGED IN v27: typeDefinitionIndex is a ptr to the type in the file. var typeDefinition = genericClass.TypeDefinition; - var genericInst = genericClass.Context.ClassInst; + var genericInst = genericClass.Context.ClassInst!; var genericParams = genericInst.Types .Select(GetTypeReflectionData) //Recursive call here @@ -342,7 +344,7 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: { var arrayType = binary.ReadReadableAtVirtualAddress(forWhat.Data.Array); - var oriType = arrayType.ElementType; + var oriType = arrayType.GetElementTypeOrThrow(); return new() { baseType = null, diff --git a/LibCpp2IL/MetadataUsage.cs b/LibCpp2IL/MetadataUsage.cs index 31428c3da..437f9b2c1 100644 --- a/LibCpp2IL/MetadataUsage.cs +++ b/LibCpp2IL/MetadataUsage.cs @@ -36,8 +36,10 @@ public MetadataUsage(MetadataUsageType type, ulong offset, uint value, Il2CppBin _metadata = metadata; } +#pragma warning disable CS0618 // Fallback to legacy statics for backwards compatibility private Il2CppBinary EffectiveBinary => _binary ?? LibCpp2IlMain.Binary!; private Il2CppMetadata EffectiveMetadata => _metadata ?? LibCpp2IlMain.TheMetadata!; +#pragma warning restore CS0618 public uint RawValue => _value; @@ -176,8 +178,10 @@ public override string ToString() public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address, Il2CppBinary? binary, Il2CppMetadata? metadata) { +#pragma warning disable CS0618 // Fallback to legacy statics for backwards compatibility var effectiveBinary = binary ?? LibCpp2IlMain.Binary!; var effectiveMetadata = metadata ?? LibCpp2IlMain.TheMetadata!; +#pragma warning restore CS0618 var encodedType = encoded & 0xE000_0000; var type = (MetadataUsageType)(encodedType >> 29); diff --git a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs index 9a87cbf70..c4d9a90bc 100644 --- a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs +++ b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs @@ -28,7 +28,9 @@ public class Il2CppTypeReflectionData public bool isPointer; #pragma warning restore 8618 +#pragma warning disable CS0618 // Fallback to legacy static LibCpp2IlMain.TheMetadata for backwards compatibility public Il2CppGenericParameter? GenericParameter => isArray || isType ? null : baseType?.OwningMetadata?.GetGenericParameterFromIndex(variableGenericParamIndex) ?? LibCpp2IlMain.TheMetadata?.GetGenericParameterFromIndex(variableGenericParamIndex); +#pragma warning restore CS0618 private string GetPtrSuffix() { diff --git a/LibCpp2IL/Reflection/LibCpp2IlReflection.cs b/LibCpp2IL/Reflection/LibCpp2IlReflection.cs index 1baee2201..7283fd933 100644 --- a/LibCpp2IL/Reflection/LibCpp2IlReflection.cs +++ b/LibCpp2IL/Reflection/LibCpp2IlReflection.cs @@ -3,6 +3,8 @@ using LibCpp2IL.BinaryStructures; using LibCpp2IL.Metadata; +#pragma warning disable CS0618 // This static facade intentionally uses LibCpp2IlMain.DefaultContext for backwards compatibility + namespace LibCpp2IL.Reflection; public static class LibCpp2IlReflection From 58ed29305640c55220870e6359f31482ff89b0a5 Mon Sep 17 00:00:00 2001 From: Lucas <31241699+nitrog0d@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:03:51 -0300 Subject: [PATCH 3/4] Address PR review feedback Remove unused _primitiveSizes from MiscUtils. Use primary constructor for UnsupportedInstructionSetException. Remove redundant EffectiveMetadataVersion from Il2CppGenericClass. Make MetadataUsage binary/metadata params non-nullable and remove static fallbacks. Move PointerSizeBytes to Il2CppBinary instance property. Expose LibCpp2IlContext on ApplicationAnalysisContext and migrate MetadataProcessor. Move Arm64 disassembler from static Arm64Utils to ApplicationAnalysisContext. Remove unnecessary OwningBinary/OwningMetadata null checks across metadata structures. --- Cpp2IL.Core/Cpp2IlApi.cs | 1 - .../UnsupportedInstructionSetException.cs | 15 +---- .../Graphs/Processors/MetadataProcessor.cs | 7 +- .../Arm64KeyFunctionAddresses.cs | 4 +- .../InstructionSets/Arm64InstructionSet.cs | 4 +- .../Contexts/ApplicationAnalysisContext.cs | 29 +++++++++ Cpp2IL.Core/Utils/Arm64Utils.cs | 22 ++----- Cpp2IL.Core/Utils/MiscUtils.cs | 29 --------- .../BinaryStructures/Il2CppGenericClass.cs | 12 ++-- LibCpp2IL/Il2CppBinary.cs | 3 + LibCpp2IL/LibCpp2IlGlobalMapper.cs | 16 ++--- LibCpp2IL/Metadata/Il2CppEventDefinition.cs | 12 ++-- LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs | 2 +- LibCpp2IL/Metadata/Il2CppFieldDefinition.cs | 3 - LibCpp2IL/Metadata/Il2CppImageDefinition.cs | 5 +- LibCpp2IL/Metadata/Il2CppMetadata.cs | 2 +- LibCpp2IL/Metadata/Il2CppMethodDefinition.cs | 24 +++---- .../Metadata/Il2CppParameterDefaultValue.cs | 2 +- .../Metadata/Il2CppPropertyDefinition.cs | 12 ++-- LibCpp2IL/Metadata/Il2CppTypeDefinition.cs | 64 ++++++------------- LibCpp2IL/MetadataUsage.cs | 50 ++++++--------- 21 files changed, 120 insertions(+), 198 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index a51cd3af4..c7ba456b5 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -123,7 +123,6 @@ public static void InitializeLibCpp2Il(byte[] assemblyData, byte[] metadataData, [MemberNotNull(nameof(CurrentAppContext))] private static void OnLibInitialized(LibCpp2IlContext libContext) { - MiscUtils.Init(libContext.Binary); libContext.Binary.AllCustomAttributeGenerators.ToList() .ForEach(ptr => SharedState.AttributeGeneratorStarts.Add(ptr)); diff --git a/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs b/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs index d6b84a1fb..6853a1f91 100644 --- a/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs +++ b/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs @@ -2,18 +2,7 @@ namespace Cpp2IL.Core.Exceptions; -public class UnsupportedInstructionSetException : Exception +public class UnsupportedInstructionSetException(string? instructionSetId = null) : Exception { - private readonly string? _instructionSetId; - - public UnsupportedInstructionSetException() - { - } - - public UnsupportedInstructionSetException(string instructionSetId) - { - _instructionSetId = instructionSetId; - } - - public override string Message => $"This action is not supported on the {_instructionSetId ?? "unknown"} instruction set yet. If running the CLI, try adding the --skip-analysis argument."; + public override string Message => $"This action is not supported on the {instructionSetId ?? "unknown"} instruction set yet. If running the CLI, try adding the --skip-analysis argument."; } diff --git a/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs b/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs index 76cc7b35f..36c06989f 100644 --- a/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs +++ b/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs @@ -2,7 +2,6 @@ using Cpp2IL.Core.ISIL; using Cpp2IL.Core.Model.Contexts; using Cpp2IL.Core.Utils; -using LibCpp2IL; namespace Cpp2IL.Core.Graphs.Processors; @@ -26,13 +25,11 @@ public void Process(MethodAnalysisContext methodAnalysisContext, Block block) var memoryOp = (IsilMemoryOperand)instruction.Operands[1].Data; if (memoryOp.Base == null && memoryOp.Index == null && memoryOp.Scale == 0) { -#pragma warning disable CS0618 // Deprecated static API - TODO: migrate to context-based API when available from Cpp2IL.Core - var val = LibCpp2IlMain.GetLiteralByAddress((ulong)memoryOp.Addend); + var val = methodAnalysisContext.AppContext.LibCpp2IlContext?.GetLiteralByAddress((ulong)memoryOp.Addend); if (val == null) { // Try instead check if its type metadata usage - var metadataUsage = LibCpp2IlMain.GetTypeGlobalByAddress((ulong)memoryOp.Addend); -#pragma warning restore CS0618 + var metadataUsage = methodAnalysisContext.AppContext.LibCpp2IlContext?.GetTypeGlobalByAddress((ulong)memoryOp.Addend); if (metadataUsage != null && methodAnalysisContext.DeclaringType is not null) { var typeAnalysisContext = metadataUsage.ToContext(methodAnalysisContext.DeclaringType!.DeclaringAssembly); diff --git a/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs index 0d71e06fa..7bcf0b6ab 100644 --- a/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs @@ -208,7 +208,7 @@ protected override ulong GetObjectIsInstFromSystemType() //The last call is to Object::IsInst Logger.Verbose($"IsInstanceOfType found at 0x{typeIsInstanceOfType.MethodPointer:X}..."); - var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(_appContext.Binary, typeIsInstanceOfType.MethodPointer, false); + var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(_appContext, typeIsInstanceOfType.MethodPointer, false); var lastCall = instructions.LastOrDefault(i => i.Mnemonic == "bl"); @@ -258,7 +258,7 @@ protected override void AttemptInstructionAnalysisToFillGaps() if (arrayTypeDef.Methods!.FirstOrDefault(m => m.Name == "GetEnumerator") is { } methodDef) { var ptr = methodDef.MethodPointer; - var body = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(_appContext.Binary, ptr); + var body = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(_appContext, ptr); //Looking for adrp, ldr, ldr, bl. Probably more than one - the first will be initializing the method, second will be the constructor call var probableResult = 0L; diff --git a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs index 8d70ddfb2..e99707268 100644 --- a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs @@ -27,7 +27,7 @@ public override Memory GetRawBytesForMethod(MethodAnalysisContext context, return context.AppContext.Binary.GetRawBinaryContent().AsMemory(ptrAsInt, count); } - var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer); + var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext, context.UnderlyingPointer); return instructions.SelectMany(i => i.Bytes).ToArray(); } @@ -43,7 +43,7 @@ public override string PrintAssembly(MethodAnalysisContext context) { var sb = new StringBuilder(); - var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer); + var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext, context.UnderlyingPointer); var first = true; foreach (var instruction in instructions) diff --git a/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs index fbe9b16a5..91f2bc803 100644 --- a/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs @@ -10,6 +10,8 @@ using Cpp2IL.Core.Il2CppApiFunctions; using Cpp2IL.Core.Logging; using Cpp2IL.Core.Utils; +using Gee.External.Capstone; +using Gee.External.Capstone.Arm64; using LibCpp2IL; using LibCpp2IL.Metadata; @@ -40,6 +42,11 @@ public class ApplicationAnalysisContext : ContextWithDataStorage /// public UnityVersion UnityVersion => Metadata.UnityVersion; + /// + /// The that owns the metadata file this application was loaded from, if available. + /// + public LibCpp2IlContext? LibCpp2IlContext => Metadata.OwningContext; + /// /// The instruction set helper class associated with the instruction set that this application was compiled with. /// @@ -70,6 +77,28 @@ public class ApplicationAnalysisContext : ContextWithDataStorage ///
public readonly Dictionary ConcreteGenericMethodsByRef = new(); + /// + /// Capstone ARM64 disassembler, lazily initialized on first use. + /// + private CapstoneArm64Disassembler? _arm64Disassembler; + + /// + /// Gets or creates a Capstone ARM64 disassembler configured for this binary. + /// + internal CapstoneArm64Disassembler GetOrCreateArm64Disassembler() + { + if (_arm64Disassembler == null) + { + var disassembler = CapstoneDisassembler.CreateArm64Disassembler(Binary.IsBigEndian ? Arm64DisassembleMode.BigEndian : Arm64DisassembleMode.LittleEndian); + disassembler.EnableInstructionDetails = true; + disassembler.EnableSkipDataMode = true; + disassembler.DisassembleSyntax = DisassembleSyntax.Intel; + _arm64Disassembler = disassembler; + } + + return _arm64Disassembler; + } + /// /// Key Function Addresses for the binary file. Populated on-demand /// diff --git a/Cpp2IL.Core/Utils/Arm64Utils.cs b/Cpp2IL.Core/Utils/Arm64Utils.cs index 1f5be03d1..7bf6bc552 100644 --- a/Cpp2IL.Core/Utils/Arm64Utils.cs +++ b/Cpp2IL.Core/Utils/Arm64Utils.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using Cpp2IL.Core.Extensions; -using Gee.External.Capstone; +using Cpp2IL.Core.Model.Contexts; using Gee.External.Capstone.Arm64; using LibCpp2IL; @@ -11,7 +11,6 @@ namespace Cpp2IL.Core.Utils; public static class Arm64Utils { private static readonly ConcurrentDictionary CachedArm64RegNamesNew = new(); - private static CapstoneArm64Disassembler? _arm64Disassembler; public static string GetRegisterNameNew(Arm64RegisterId registerId) { @@ -90,19 +89,10 @@ public static string GetRegisterNameNew(Arm64RegisterId registerId) return ret; } - private static void InitArm64Decompilation(Il2CppBinary binary) + public static List GetArm64MethodBodyAtVirtualAddress(ApplicationAnalysisContext appContext, ulong virtAddress, bool managed = true, int count = -1) { - var disassembler = CapstoneDisassembler.CreateArm64Disassembler(binary.IsBigEndian ? Arm64DisassembleMode.BigEndian : Arm64DisassembleMode.LittleEndian); - disassembler.EnableInstructionDetails = true; - disassembler.EnableSkipDataMode = true; - disassembler.DisassembleSyntax = DisassembleSyntax.Intel; - _arm64Disassembler = disassembler; - } - - public static List GetArm64MethodBodyAtVirtualAddress(Il2CppBinary binary, ulong virtAddress, bool managed = true, int count = -1) - { - if (_arm64Disassembler == null) - InitArm64Decompilation(binary); + var binary = appContext.Binary; + var disassembler = appContext.GetOrCreateArm64Disassembler(); //We can't use CppMethodBodyBytes to get the byte array, because ARMv7 doesn't have filler bytes like x86 does. //So we can't work out the end of the method. @@ -122,7 +112,7 @@ public static List GetArm64MethodBodyAtVirtualAddress(Il2CppBi byte[] bytes = binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod); - var iter = _arm64Disassembler!.Iterate(bytes, (long)virtAddress); + var iter = disassembler.Iterate(bytes, (long)virtAddress); if (count > 0) iter = iter.Take(count); @@ -138,7 +128,7 @@ public static List GetArm64MethodBodyAtVirtualAddress(Il2CppBi while (!ret.Any(i => i.Mnemonic is "b" or ".byte") && (count == -1 || ret.Count < count)) { //All arm64 instructions are 4 bytes - ret.AddRange(_arm64Disassembler!.Iterate(allBytes.SubArray(pos..(pos + 4)), (long)virtAddress)); + ret.AddRange(disassembler.Iterate(allBytes.SubArray(pos..(pos + 4)), (long)virtAddress)); virtAddress += 4; pos += 4; } diff --git a/Cpp2IL.Core/Utils/MiscUtils.cs b/Cpp2IL.Core/Utils/MiscUtils.cs index d34215f4d..7236be21c 100644 --- a/Cpp2IL.Core/Utils/MiscUtils.cs +++ b/Cpp2IL.Core/Utils/MiscUtils.cs @@ -12,8 +12,6 @@ public static class MiscUtils { private static List? _allKnownFunctionStarts; - private static Dictionary _primitiveSizes = new(); - public static readonly List InvalidPathChars = ['<', '>', ':', '"', '/', '\\', '|', '?', '*']; public static readonly HashSet InvalidPathElements = @@ -47,28 +45,6 @@ internal static void Reset() _allKnownFunctionStarts = null; } - internal static void Init(Il2CppBinary binary) - { - _primitiveSizes = new(14) - { - { "Byte", 1 }, - { "SByte", 1 }, - { "Boolean", 1 }, - { "Int16", 2 }, - { "UInt16", 2 }, - { "Char", 2 }, - { "Int32", 4 }, - { "UInt32", 4 }, - { "Single", 4 }, - { "Int64", 8 }, - { "UInt64", 8 }, - { "Double", 8 }, - { "IntPtr", binary.is32Bit ? 4UL : 8UL }, - { "UIntPtr", binary.is32Bit ? 4UL : 8UL }, - }; - } - - internal static string[] GetGenericParams(string input) { if (!input.Contains('<')) @@ -146,11 +122,6 @@ public static int GetSlotNum(int offset) return -1; } - public static int GetPointerSizeBytes(Il2CppBinary binary) - { - return binary.is32Bit ? 4 : 8; - } - internal static byte[] RawBytes(IConvertible original) => original switch { diff --git a/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs b/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs index e166610b1..a75b6ceb9 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs @@ -8,17 +8,15 @@ public class Il2CppGenericClass : ReadableClass [Version(Max = 24.5f)] public long TypeDefinitionIndex; /* the generic type definition */ [Version(Min = 27.0f)] public ulong V27TypePointer; - + public Il2CppGenericContext Context = null!; /* a context that contains the type instantiation doesn't contain any method instantiation */ public ulong CachedClass; /* if present, the Il2CppClass corresponding to the instantiation. */ - private float EffectiveMetadataVersion => OwningMetadata?.MetadataVersion ?? MetadataVersion; - public Il2CppTypeDefinition TypeDefinition { get { - if (EffectiveMetadataVersion < 27f) + if (MetadataVersion < 27f) { return OwningMetadata!.GetTypeDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int)TypeDefinitionIndex)); } @@ -31,7 +29,7 @@ public Il2CppType? V27BaseType { get { - if (EffectiveMetadataVersion < 27f) + if (MetadataVersion < 27f) return null; var binary = OwningBinary; @@ -41,7 +39,7 @@ public Il2CppType? V27BaseType var t = binary.ReadReadableAtVirtualAddress(V27TypePointer); t.OwningBinary = binary; t.OwningMetadata = OwningMetadata; - t.Il2CppTypeHasNumMods5Bits ??= (OwningMetadata?.MetadataVersion ?? MetadataVersion) >= 27.2f; + t.Il2CppTypeHasNumMods5Bits ??= MetadataVersion >= 27.2f; return t; } } @@ -52,7 +50,7 @@ public override void Read(ClassReadingBinaryReader reader) V27TypePointer = reader.ReadNUint(); else TypeDefinitionIndex = reader.ReadNInt(); - + Context = reader.ReadReadableHereNoLock(); Context.OwningBinary = OwningBinary; CachedClass = reader.ReadNUint(); diff --git a/LibCpp2IL/Il2CppBinary.cs b/LibCpp2IL/Il2CppBinary.cs index 1bbf473f9..9a6a49014 100644 --- a/LibCpp2IL/Il2CppBinary.cs +++ b/LibCpp2IL/Il2CppBinary.cs @@ -52,6 +52,9 @@ public abstract class Il2CppBinary(MemoryStream input) : ClassReadingBinaryReade private readonly Dictionary _typesByAddress = new(); public abstract long RawLength { get; } + + public int PointerSizeBytes => is32Bit ? 4 : 8; + public int NumTypes => _types.Length; public Il2CppType[] AllTypes => _types; diff --git a/LibCpp2IL/LibCpp2IlGlobalMapper.cs b/LibCpp2IL/LibCpp2IlGlobalMapper.cs index 24525fc3d..a25b0a532 100644 --- a/LibCpp2IL/LibCpp2IlGlobalMapper.cs +++ b/LibCpp2IL/LibCpp2IlGlobalMapper.cs @@ -54,32 +54,32 @@ private static void MapGlobalIdentifiersPre27(Il2CppMetadata metadata, Il2CppBin //We non-null assert here because this function is only called pre-27, when this is guaranteed to be non-null TypeRefs = metadata.metadataUsageDic![(uint)MetadataUsageType.TypeInfo] - .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)) + .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)) .ToList(); //More type references TypeRefs.AddRange(metadata.metadataUsageDic[(uint)MetadataUsageType.Type] - .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)) + .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)) ); //Method references MethodRefs = metadata.metadataUsageDic[(uint)MetadataUsageType.MethodDef] - .Select(kvp => new MetadataUsage(MetadataUsageType.MethodDef, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)) + .Select(kvp => new MetadataUsage(MetadataUsageType.MethodDef, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)) .ToList(); //Field references FieldRefs = metadata.metadataUsageDic[(uint)MetadataUsageType.FieldInfo] - .Select(kvp => new MetadataUsage(MetadataUsageType.FieldInfo, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)) + .Select(kvp => new MetadataUsage(MetadataUsageType.FieldInfo, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)) .ToList(); //Literals Literals = metadata.metadataUsageDic[(uint)MetadataUsageType.StringLiteral] - .Select(kvp => new MetadataUsage(MetadataUsageType.StringLiteral, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)).ToList(); + .Select(kvp => new MetadataUsage(MetadataUsageType.StringLiteral, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)).ToList(); //Generic method references foreach (var (metadataUsageIdx, methodSpecIdx) in metadata.metadataUsageDic[(uint)MetadataUsageType.MethodRef]) //kIl2CppMetadataUsageMethodRef { - MethodRefs.Add(new MetadataUsage(MetadataUsageType.MethodRef, cppAssembly.GetRawMetadataUsage(metadataUsageIdx), methodSpecIdx)); + MethodRefs.Add(new MetadataUsage(MetadataUsageType.MethodRef, cppAssembly.GetRawMetadataUsage(metadataUsageIdx), methodSpecIdx, cppAssembly, metadata)); } foreach (var globalIdentifier in TypeRefs) @@ -95,14 +95,14 @@ private static void MapGlobalIdentifiersPre27(Il2CppMetadata metadata, Il2CppBin LiteralsByAddress[globalIdentifier.Offset] = globalIdentifier; } -#pragma warning disable CS0618 // Intentional use of legacy static LibCpp2IlMain.Binary for backwards compatibility +#pragma warning disable CS0618 // Intentional use of legacy static LibCpp2IlMain.Binary/TheMetadata for backwards compatibility public static MetadataUsage? CheckForPost27GlobalAt(ulong address) { if (!LibCpp2IlMain.Binary!.TryMapVirtualAddressToRaw(address, out var raw) || raw >= LibCpp2IlMain.Binary.RawLength) return null; var encoded = LibCpp2IlMain.Binary.ReadPointerAtVirtualAddress(address); - var metadataUsage = MetadataUsage.DecodeMetadataUsage(encoded, address); + var metadataUsage = MetadataUsage.DecodeMetadataUsage(encoded, address, LibCpp2IlMain.Binary, LibCpp2IlMain.TheMetadata!); if (metadataUsage?.IsValid != true) return null; diff --git a/LibCpp2IL/Metadata/Il2CppEventDefinition.cs b/LibCpp2IL/Metadata/Il2CppEventDefinition.cs index 7eb440f62..b71004a78 100644 --- a/LibCpp2IL/Metadata/Il2CppEventDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppEventDefinition.cs @@ -23,9 +23,7 @@ public Il2CppTypeDefinition? DeclaringType get { if (_type != null) return _type; - if (OwningMetadata == null) return null; - - _type = OwningMetadata.typeDefs.FirstOrDefault(t => t.Events!.Contains(this)); + _type = OwningMetadata!.typeDefs.FirstOrDefault(t => t.Events!.Contains(this)); return _type; } internal set => _type = value; @@ -35,15 +33,15 @@ public Il2CppTypeDefinition? DeclaringType public Il2CppType? RawType => OwningBinary?.GetType(typeIndex); - public Il2CppTypeReflectionData? EventType => OwningBinary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(RawType!); + public Il2CppTypeReflectionData? EventType => LibCpp2ILUtils.GetTypeReflectionData(RawType!); public EventAttributes EventAttributes => (EventAttributes)RawType!.Attrs; - public Il2CppMethodDefinition? Adder => OwningMetadata == null || add.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + add); + public Il2CppMethodDefinition? Adder => add.IsNull || DeclaringType == null ? null : OwningMetadata!.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + add); - public Il2CppMethodDefinition? Remover => OwningMetadata == null || remove.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + remove); + public Il2CppMethodDefinition? Remover => remove.IsNull || DeclaringType == null ? null : OwningMetadata!.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + remove); - public Il2CppMethodDefinition? Invoker => OwningMetadata == null || raise.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + raise); + public Il2CppMethodDefinition? Invoker => raise.IsNull || DeclaringType == null ? null : OwningMetadata!.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + raise); public bool IsStatic { diff --git a/LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs b/LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs index ea74578fd..83077c136 100644 --- a/LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs +++ b/LibCpp2IL/Metadata/Il2CppFieldDefaultValue.cs @@ -8,7 +8,7 @@ public class Il2CppFieldDefaultValue : ReadableClass public Il2CppVariableWidthIndex typeIndex; public Il2CppVariableWidthIndex dataIndex; - public object? Value => dataIndex.IsNull || OwningMetadata == null || OwningBinary == null ? null : LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex, OwningMetadata, OwningBinary); + public object? Value => dataIndex.IsNull ? null : LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex, OwningMetadata!, OwningBinary!); public override void Read(ClassReadingBinaryReader reader) { diff --git a/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs b/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs index a7d31a869..566ba0ab1 100644 --- a/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs @@ -24,9 +24,6 @@ public class Il2CppFieldDefinition : ReadableClass public override string? ToString() { - if (OwningMetadata == null) - return base.ToString(); - return $"Il2CppFieldDefinition[Name={Name}, FieldType={FieldType}]"; } diff --git a/LibCpp2IL/Metadata/Il2CppImageDefinition.cs b/LibCpp2IL/Metadata/Il2CppImageDefinition.cs index 56f819d38..43efb61ad 100644 --- a/LibCpp2IL/Metadata/Il2CppImageDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppImageDefinition.cs @@ -19,10 +19,9 @@ public class Il2CppImageDefinition : ReadableClass [Version(Min = 24.1f)] public int customAttributeStart; [Version(Min = 24.1f)] public uint customAttributeCount; - public string? Name => OwningMetadata == null ? null : OwningMetadata.GetStringFromIndex(nameIndex); + public string? Name => OwningMetadata!.GetStringFromIndex(nameIndex); - public Il2CppTypeDefinition[]? Types => OwningMetadata == null ? null - : Enumerable + public Il2CppTypeDefinition[]? Types => Enumerable .Range(firstTypeIndex.Value, (int)typeCount) .Select(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage) // DynWidth: using Enumerable.Range, not read from file, so making temp is ok .Select(OwningMetadata.GetTypeDefinitionFromIndex) diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index 1028ceb43..98dd5582c 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -58,7 +58,7 @@ public class Il2CppMetadata : ClassReadingBinaryReader /// /// Set by after construction. /// - internal LibCpp2IlContext? OwningContext { get; set; } + public LibCpp2IlContext? OwningContext { get; internal set; } private readonly Dictionary, Il2CppFieldDefaultValue> _fieldDefaultValueLookup = new(); private readonly Dictionary _fieldDefaultLookupNew = new(); diff --git a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs index 7bf7f59d9..fa2f47776 100644 --- a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs @@ -40,9 +40,9 @@ public class Il2CppMethodDefinition : ReadableClass public Il2CppType? RawReturnType => OwningBinary?.GetType(returnTypeIdx); - public Il2CppTypeReflectionData? ReturnType => OwningBinary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(OwningBinary.GetType(returnTypeIdx)); + public Il2CppTypeReflectionData? ReturnType => LibCpp2ILUtils.GetTypeReflectionData(OwningBinary!.GetType(returnTypeIdx)); - public Il2CppTypeDefinition? DeclaringType => OwningMetadata == null ? null : OwningMetadata.GetTypeDefinitionFromIndex(declaringTypeIdx); + public Il2CppTypeDefinition? DeclaringType => OwningMetadata!.GetTypeDefinitionFromIndex(declaringTypeIdx); private ulong? _methodPointer = null; @@ -52,32 +52,32 @@ public ulong MethodPointer { if (!_methodPointer.HasValue) { - if (OwningBinary == null || OwningMetadata == null || DeclaringType == null) + if (DeclaringType == null) { - LibLogger.WarnNewline($"Couldn't get method pointer for {Name}. Binary is {OwningBinary}, Meta is {OwningMetadata}, DeclaringType is {DeclaringType}"); + LibLogger.WarnNewline($"Couldn't get method pointer for {Name}. DeclaringType is null"); return 0; } var asmIdx = 0; //Not needed pre-24.2 if (MetadataVersion >= 27) { - asmIdx = OwningBinary.GetCodegenModuleIndexByName(DeclaringType!.DeclaringAssembly!.Name!); + asmIdx = OwningBinary!.GetCodegenModuleIndexByName(DeclaringType!.DeclaringAssembly!.Name!); } else if (MetadataVersion >= 24.2f) { asmIdx = DeclaringType!.DeclaringAssembly!.assemblyIndex; } - _methodPointer = OwningBinary.GetMethodPointer(methodIndex, MethodIndex, asmIdx, token); + _methodPointer = OwningBinary!.GetMethodPointer(methodIndex, MethodIndex, asmIdx, token); } return _methodPointer.Value; } } - public long MethodOffsetInFile => MethodPointer == 0 || OwningBinary == null ? 0 : OwningBinary.TryMapVirtualAddressToRaw(MethodPointer, out var ret) ? ret : 0; + public long MethodOffsetInFile => MethodPointer == 0 ? 0 : OwningBinary!.TryMapVirtualAddressToRaw(MethodPointer, out var ret) ? ret : 0; - public ulong Rva => MethodPointer == 0 || OwningBinary == null ? 0 : OwningBinary.GetRva(MethodPointer); + public ulong Rva => MethodPointer == 0 ? 0 : OwningBinary!.GetRva(MethodPointer); public string? HumanReadableSignature => ReturnType == null || Parameters == null || Name == null ? null : $"{ReturnType} {Name}({string.Join(", ", Parameters.AsEnumerable())})"; @@ -85,9 +85,6 @@ public Il2CppParameterDefinition[]? InternalParameterData { get { - if (OwningMetadata == null || OwningBinary == null) - return null; - if (parameterStart.IsNull || parameterCount == 0) return []; @@ -95,7 +92,7 @@ public Il2CppParameterDefinition[]? InternalParameterData for (var i = 0; i < parameterCount; i++) { - ret[i] = OwningMetadata.GetParameterDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(parameterStart.Value + i)); + ret[i] = OwningMetadata!.GetParameterDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(parameterStart.Value + i)); } return ret; @@ -147,9 +144,6 @@ public Il2CppParameterReflectionData[]? Parameters public override string? ToString() { - if (OwningMetadata == null) - return base.ToString(); - return $"Il2CppMethodDefinition[Name='{Name}', ReturnType={ReturnType}, DeclaringType={DeclaringType}]"; } diff --git a/LibCpp2IL/Metadata/Il2CppParameterDefaultValue.cs b/LibCpp2IL/Metadata/Il2CppParameterDefaultValue.cs index d7a24636a..4feb5e1df 100644 --- a/LibCpp2IL/Metadata/Il2CppParameterDefaultValue.cs +++ b/LibCpp2IL/Metadata/Il2CppParameterDefaultValue.cs @@ -8,7 +8,7 @@ public class Il2CppParameterDefaultValue : ReadableClass public Il2CppVariableWidthIndex typeIndex; public Il2CppVariableWidthIndex dataIndex; - public object? ContainedDefaultValue => OwningMetadata == null || OwningBinary == null ? null : LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex, OwningMetadata, OwningBinary); + public object? ContainedDefaultValue => LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex, OwningMetadata!, OwningBinary!); public override void Read(ClassReadingBinaryReader reader) { diff --git a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs index 1d1ea067a..eaf344554 100644 --- a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs @@ -25,9 +25,7 @@ public Il2CppTypeDefinition? DeclaringType if (_type != null) return _type; - if (OwningMetadata == null) return null; - - _type = OwningMetadata.typeDefs.FirstOrDefault(t => t.Properties!.Contains(this)); + _type = OwningMetadata!.typeDefs.FirstOrDefault(t => t.Properties!.Contains(this)); return _type; } internal set => _type = value; @@ -35,13 +33,13 @@ public Il2CppTypeDefinition? DeclaringType public string? Name { get; private set; } - public Il2CppMethodDefinition? Getter => OwningMetadata == null || get.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + get); + public Il2CppMethodDefinition? Getter => get.IsNull || DeclaringType == null ? null : OwningMetadata!.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + get); - public Il2CppMethodDefinition? Setter => OwningMetadata == null || set.IsNull || DeclaringType == null ? null : OwningMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + set); + public Il2CppMethodDefinition? Setter => set.IsNull || DeclaringType == null ? null : OwningMetadata!.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + set); - public Il2CppTypeReflectionData? PropertyType => OwningMetadata == null ? null : Getter == null ? Setter!.Parameters![0].Type : Getter!.ReturnType; + public Il2CppTypeReflectionData? PropertyType => Getter == null ? Setter!.Parameters![0].Type : Getter!.ReturnType; - public Il2CppType? RawPropertyType => OwningMetadata == null ? null : Getter == null ? Setter!.Parameters![0].RawType : Getter!.RawReturnType; + public Il2CppType? RawPropertyType => Getter == null ? Setter!.Parameters![0].RawType : Getter!.RawReturnType; public bool IsStatic => Getter == null ? Setter!.IsStatic : Getter!.IsStatic; public uint Token => token; diff --git a/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs b/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs index 8cde449b5..fc8ebafb8 100644 --- a/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs @@ -107,7 +107,7 @@ public MetadataUsage?[] VTable { if (VtableStart < 0) return []; - return OwningMetadata!.VTableMethodIndices.SubArray(VtableStart, VtableCount).Select(v => MetadataUsage.DecodeMetadataUsage(v, 0)).ToArray(); + return OwningMetadata!.VTableMethodIndices.SubArray(VtableStart, VtableCount).Select(v => MetadataUsage.DecodeMetadataUsage(v, 0, OwningBinary!, OwningMetadata!)).ToArray(); } } @@ -125,9 +125,7 @@ public Il2CppImageDefinition? DeclaringAssembly { if (_cachedDeclaringAssembly == null) { - if (OwningMetadata == null) return null; - - LibCpp2ILUtils.PopulateDeclaringAssemblyCache(OwningMetadata); + LibCpp2ILUtils.PopulateDeclaringAssemblyCache(OwningMetadata!); } return _cachedDeclaringAssembly; @@ -135,7 +133,7 @@ public Il2CppImageDefinition? DeclaringAssembly internal set => _cachedDeclaringAssembly = value; } - public Il2CppCodeGenModule? CodeGenModule => OwningBinary == null ? null : OwningBinary.GetCodegenModuleByName(DeclaringAssembly!.Name!); + public Il2CppCodeGenModule? CodeGenModule => OwningBinary!.GetCodegenModuleByName(DeclaringAssembly!.Name!); public Il2CppRGCTXDefinition[] RgctXs { @@ -186,7 +184,7 @@ public string? Namespace get { if (_cachedNamespace == null) - _cachedNamespace = OwningMetadata == null ? null : OwningMetadata.GetStringFromIndex(NamespaceIndex); + _cachedNamespace = OwningMetadata!.GetStringFromIndex(NamespaceIndex); return _cachedNamespace; } @@ -199,7 +197,7 @@ public string? Name get { if (_cachedName == null) - _cachedName = OwningMetadata == null ? null : OwningMetadata.GetStringFromIndex(NameIndex); + _cachedName = OwningMetadata!.GetStringFromIndex(NameIndex); return _cachedName; } @@ -209,9 +207,6 @@ public string? FullName { get { - if (OwningMetadata == null) - return null; - if (DeclaringType != null) return $"{DeclaringType.FullName}+{Name}"; @@ -221,19 +216,16 @@ public string? FullName public Il2CppType? RawBaseType => ParentIndex.IsNull ? null : OwningBinary!.GetType(ParentIndex); - public Il2CppTypeReflectionData? BaseType => ParentIndex.IsNull || OwningBinary == null ? null : LibCpp2ILUtils.GetTypeReflectionData(OwningBinary!.GetType(ParentIndex)); + public Il2CppTypeReflectionData? BaseType => ParentIndex.IsNull ? null : LibCpp2ILUtils.GetTypeReflectionData(OwningBinary!.GetType(ParentIndex)); public Il2CppFieldDefinition[]? Fields { get { - if (OwningMetadata == null) - return null; - if (FirstFieldIdx.IsNull || FieldCount == 0) return []; - return OwningMetadata.GetFieldDefinitionsFromIndexAndCount(FirstFieldIdx, FieldCount); + return OwningMetadata!.GetFieldDefinitionsFromIndexAndCount(FirstFieldIdx, FieldCount); } } @@ -280,13 +272,10 @@ public Il2CppMethodDefinition[]? Methods { get { - if (OwningMetadata == null) - return null; - if (FirstMethodIdx.IsNull || MethodCount == 0) return []; - return OwningMetadata.GetMethodDefinitionsFromIndexAndCount(FirstMethodIdx, MethodCount); + return OwningMetadata!.GetMethodDefinitionsFromIndexAndCount(FirstMethodIdx, MethodCount); } } @@ -294,13 +283,10 @@ public Il2CppPropertyDefinition[]? Properties { get { - if (OwningMetadata == null) - return null; - if (FirstPropertyId.IsNull || PropertyCount == 0) return []; - var ret = OwningMetadata.GetPropertyDefinitionsFromIndexAndCount(FirstPropertyId, PropertyCount); + var ret = OwningMetadata!.GetPropertyDefinitionsFromIndexAndCount(FirstPropertyId, PropertyCount); foreach (var definition in ret) definition.DeclaringType = this; @@ -313,44 +299,35 @@ public Il2CppEventDefinition[]? Events { get { - if (OwningMetadata == null) - return null; - if (FirstEventId.IsNull || EventCount == 0) return []; - var ret = OwningMetadata.GetEventDefinitionsFromIndexAndCount(FirstEventId, EventCount); - foreach (var def in ret) + var ret = OwningMetadata!.GetEventDefinitionsFromIndexAndCount(FirstEventId, EventCount); + foreach (var def in ret) def.DeclaringType = this; return ret; } } - public Il2CppTypeDefinition[]? NestedTypes => OwningMetadata == null - ? null - : OwningMetadata.GetNestedTypeIndicesFromIndexAndCount(NestedTypesStart, NestedTypeCount) + public Il2CppTypeDefinition[]? NestedTypes => OwningMetadata!.GetNestedTypeIndicesFromIndexAndCount(NestedTypesStart, NestedTypeCount) .Select(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage) //DynWidth: nestedTypeIndices is always int, so making temp is ok .Select(OwningMetadata.GetTypeDefinitionFromIndex) .ToArray(); - public Il2CppType[] RawInterfaces => OwningMetadata == null || OwningBinary == null - ? [] - : OwningMetadata.GetInterfaceIndicesFromIndexAndCount(InterfacesStart, InterfacesCount) - .Select(OwningBinary.GetType) + public Il2CppType[] RawInterfaces => OwningMetadata!.GetInterfaceIndicesFromIndexAndCount(InterfacesStart, InterfacesCount) + .Select(OwningBinary!.GetType) .ToArray(); - public Il2CppTypeReflectionData[]? Interfaces => OwningMetadata == null || OwningBinary == null - ? null - : RawInterfaces + public Il2CppTypeReflectionData[]? Interfaces => RawInterfaces .Select(LibCpp2ILUtils.GetTypeReflectionData) .ToArray(); - public Il2CppTypeDefinition? DeclaringType => OwningMetadata == null || OwningBinary == null || DeclaringTypeIndex.IsNull ? null : OwningBinary.GetType(DeclaringTypeIndex).CoerceToUnderlyingTypeDefinition(); + public Il2CppTypeDefinition? DeclaringType => DeclaringTypeIndex.IsNull ? null : OwningBinary!.GetType(DeclaringTypeIndex).CoerceToUnderlyingTypeDefinition(); - public Il2CppTypeDefinition? ElementType => OwningMetadata == null || OwningBinary == null || ElementTypeIndex < 0 - ? null - : OwningBinary.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ElementTypeIndex)).CoerceToUnderlyingTypeDefinition(); //DynWidth: ElementTypeIndex was removed in v35, so it's never dynamic + public Il2CppTypeDefinition? ElementType => ElementTypeIndex < 0 + ? null + : OwningBinary!.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(ElementTypeIndex)).CoerceToUnderlyingTypeDefinition(); //DynWidth: ElementTypeIndex was removed in v35, so it's never dynamic public Il2CppGenericContainer? GenericContainer => GenericContainerIndex.IsNull ? null : OwningMetadata?.GetGenericContainerFromIndex(GenericContainerIndex); @@ -372,9 +349,6 @@ public Il2CppType EnumUnderlyingType public override string? ToString() { - if (OwningMetadata == null) - return base.ToString(); - return $"Il2CppTypeDefinition[namespace='{Namespace}', name='{Name}', parentType={BaseType?.ToString() ?? "null"}, assembly={DeclaringAssembly}]"; } diff --git a/LibCpp2IL/MetadataUsage.cs b/LibCpp2IL/MetadataUsage.cs index 437f9b2c1..234bb74ef 100644 --- a/LibCpp2IL/MetadataUsage.cs +++ b/LibCpp2IL/MetadataUsage.cs @@ -11,8 +11,8 @@ public class MetadataUsage public readonly ulong Offset; private readonly uint _value; - private readonly Il2CppBinary? _binary; - private readonly Il2CppMetadata? _metadata; + private readonly Il2CppBinary _binary; + private readonly Il2CppMetadata _metadata; private string? _cachedName; @@ -27,7 +27,7 @@ public class MetadataUsage private Cpp2IlMethodRef? _cachedGenericMethod; - public MetadataUsage(MetadataUsageType type, ulong offset, uint value, Il2CppBinary? binary = null, Il2CppMetadata? metadata = null) + public MetadataUsage(MetadataUsageType type, ulong offset, uint value, Il2CppBinary binary, Il2CppMetadata metadata) { Type = type; Offset = offset; @@ -36,11 +36,6 @@ public MetadataUsage(MetadataUsageType type, ulong offset, uint value, Il2CppBin _metadata = metadata; } -#pragma warning disable CS0618 // Fallback to legacy statics for backwards compatibility - private Il2CppBinary EffectiveBinary => _binary ?? LibCpp2IlMain.Binary!; - private Il2CppMetadata EffectiveMetadata => _metadata ?? LibCpp2IlMain.TheMetadata!; -#pragma warning restore CS0618 - public uint RawValue => _value; public object Value => @@ -57,11 +52,11 @@ public MetadataUsage(MetadataUsageType type, ulong offset, uint value, Il2CppBin public bool IsValid => Type switch { - MetadataUsageType.Type or MetadataUsageType.TypeInfo => _value < EffectiveBinary.NumTypes, - MetadataUsageType.MethodDef => _value < EffectiveMetadata.MethodDefinitionCount, - MetadataUsageType.FieldInfo => _value < EffectiveMetadata.fieldRefs.Length, - MetadataUsageType.StringLiteral => _value < EffectiveMetadata.stringLiterals.Length, - MetadataUsageType.MethodRef => _value < EffectiveBinary.AllGenericMethodSpecs.Length, + MetadataUsageType.Type or MetadataUsageType.TypeInfo => _value < _binary.NumTypes, + MetadataUsageType.MethodDef => _value < _metadata.MethodDefinitionCount, + MetadataUsageType.FieldInfo => _value < _metadata.fieldRefs.Length, + MetadataUsageType.StringLiteral => _value < _metadata.stringLiterals.Length, + MetadataUsageType.MethodRef => _value < _binary.AllGenericMethodSpecs.Length, _ => false }; @@ -75,13 +70,13 @@ public Il2CppTypeReflectionData AsType() case MetadataUsageType.TypeInfo: try { - _cachedType = EffectiveBinary.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int) _value)); //DynWidth: value is always masked out of 32-bits, ok for temp usage + _cachedType = _binary.GetType(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int) _value)); //DynWidth: value is always masked out of 32-bits, ok for temp usage _cachedTypeReflectionData = LibCpp2ILUtils.GetTypeReflectionData(_cachedType); _cachedName = _cachedTypeReflectionData?.ToString(); } catch (Exception e) { - throw new Exception($"Failed to convert this metadata usage to a type, but it is of type {Type}, with a value of {_value} (0x{_value:X}). There are {EffectiveBinary.NumTypes} types", e); + throw new Exception($"Failed to convert this metadata usage to a type, but it is of type {Type}, with a value of {_value} (0x{_value:X}). There are {_binary.NumTypes} types", e); } break; @@ -100,7 +95,7 @@ public Il2CppMethodDefinition AsMethod() switch (Type) { case MetadataUsageType.MethodDef: - _cachedMethod = EffectiveMetadata.GetMethodDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int)_value)); //DynWidth: value is always masked out of 32-bits, ok for temp usage + _cachedMethod = _metadata.GetMethodDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage((int)_value)); //DynWidth: value is always masked out of 32-bits, ok for temp usage _cachedName = _cachedMethod.GlobalKey; break; default: @@ -118,7 +113,7 @@ public Il2CppFieldDefinition AsField() switch (Type) { case MetadataUsageType.FieldInfo: - var fieldRef = EffectiveMetadata.fieldRefs[_value]; + var fieldRef = _metadata.fieldRefs[_value]; _cachedField = fieldRef.FieldDefinition; _cachedName = fieldRef.DeclaringTypeDefinition!.FullName + "." + _cachedField!.Name; break; @@ -137,7 +132,7 @@ public string AsLiteral() switch (Type) { case MetadataUsageType.StringLiteral: - _cachedName = _cachedLiteral = EffectiveMetadata.GetStringLiteralFromIndex(_value); + _cachedName = _cachedLiteral = _metadata.GetStringLiteralFromIndex(_value); break; default: throw new Exception($"Cannot cast metadata usage of kind {Type} to a String Literal"); @@ -154,7 +149,7 @@ public Cpp2IlMethodRef AsGenericMethodRef() switch (Type) { case MetadataUsageType.MethodRef: - var methodSpec = EffectiveBinary.GetMethodSpec((int)_value); + var methodSpec = _binary.GetMethodSpec((int)_value); _cachedGenericMethod = new Cpp2IlMethodRef(methodSpec); _cachedName = _cachedGenericMethod.ToString(); @@ -172,30 +167,21 @@ public override string ToString() return $"Metadata Usage {{type={Type}, Value={Value}}}"; } - - public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address) => - DecodeMetadataUsage(encoded, address, null, null); - - public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address, Il2CppBinary? binary, Il2CppMetadata? metadata) + public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address, Il2CppBinary binary, Il2CppMetadata metadata) { -#pragma warning disable CS0618 // Fallback to legacy statics for backwards compatibility - var effectiveBinary = binary ?? LibCpp2IlMain.Binary!; - var effectiveMetadata = metadata ?? LibCpp2IlMain.TheMetadata!; -#pragma warning restore CS0618 - var encodedType = encoded & 0xE000_0000; var type = (MetadataUsageType)(encodedType >> 29); if (type <= MetadataUsageType.MethodRef && type >= MetadataUsageType.TypeInfo) { var index = (uint)(encoded & 0x1FFF_FFFF); - if (effectiveMetadata.MetadataVersion >= 27) + if (metadata.MetadataVersion >= 27) index >>= 1; - if (type is MetadataUsageType.Type or MetadataUsageType.TypeInfo && index > effectiveBinary.NumTypes) + if (type is MetadataUsageType.Type or MetadataUsageType.TypeInfo && index > binary.NumTypes) return null; - if (type == MetadataUsageType.MethodDef && index > effectiveMetadata.MethodDefinitionCount) + if (type == MetadataUsageType.MethodDef && index > metadata.MethodDefinitionCount) return null; From 6e5ab4de0c1532abee35bd41213e7ee5fb048c75 Mon Sep 17 00:00:00 2001 From: Lucas <31241699+nitrog0d@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:40:51 -0300 Subject: [PATCH 4/4] Address remaining PR review feedback Remove LibCpp2IlGlobalMapper (logic already in LibCpp2IlContext). Remove static fallbacks from LibCpp2IlUtils, Il2CppTypeReflectionData, and MetadataUsage. Remove static VTABLE_OFFSET from Il2CppClassUsefulOffsets. Change X86Utils to take is32Bit param instead of full binary. Move Arm64 disassembler to ApplicationAnalysisContext. Expose LibCpp2IlContext on ApplicationAnalysisContext and migrate MetadataProcessor. Keep legacy static field writes in LibCpp2IlContextBuilder since LibCpp2IlReflection still depends on DefaultContext. --- .../X86KeyFunctionAddresses.cs | 2 +- Cpp2IL.Core/Il2CppClassUsefulOffsets.cs | 15 --- .../InstructionSets/X86InstructionSet.cs | 2 +- .../Contexts/ApplicationAnalysisContext.cs | 1 - Cpp2IL.Core/Utils/MiscUtils.cs | 4 +- Cpp2IL.Core/Utils/X86Utils.cs | 22 ++-- LibCpp2IL/LibCpp2IlContext.cs | 2 +- LibCpp2IL/LibCpp2IlContextBuilder.cs | 23 ++-- LibCpp2IL/LibCpp2IlGlobalMapper.cs | 113 ------------------ LibCpp2IL/LibCpp2IlMain.cs | 1 - LibCpp2IL/LibCpp2IlUtils.cs | 9 +- .../Reflection/Il2CppTypeReflectionData.cs | 5 +- 12 files changed, 32 insertions(+), 167 deletions(-) delete mode 100644 LibCpp2IL/LibCpp2IlGlobalMapper.cs diff --git a/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs index a95b153c6..ee42490dc 100644 --- a/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs @@ -19,7 +19,7 @@ private InstructionList DisassembleTextSection() { var binary = _appContext.Binary; var toDisasm = binary.GetEntirePrimaryExecutableSection(); - _cachedDisassembledBytes = X86Utils.Disassemble(toDisasm, binary.GetVirtualAddressOfPrimaryExecutableSection(), binary); + _cachedDisassembledBytes = X86Utils.Disassemble(toDisasm, binary.GetVirtualAddressOfPrimaryExecutableSection(), binary.is32Bit); } return _cachedDisassembledBytes; diff --git a/Cpp2IL.Core/Il2CppClassUsefulOffsets.cs b/Cpp2IL.Core/Il2CppClassUsefulOffsets.cs index c90ed3999..cba7b22f2 100644 --- a/Cpp2IL.Core/Il2CppClassUsefulOffsets.cs +++ b/Cpp2IL.Core/Il2CppClassUsefulOffsets.cs @@ -18,21 +18,6 @@ public static int GetVtableOffset(Il2CppBinary binary, float metadataVersion) return metadataVersion >= 24.2f ? v24_2_vtableOffset : pre24_2_vtableOffset; } - // Keep VTABLE_OFFSET as a convenience for code that still needs a static reference. - // This is initialized lazily from the first call site that has context. - private static int? _vtableOffset; - - public static int VTABLE_OFFSET - { - get => _vtableOffset ?? throw new InvalidOperationException("VTABLE_OFFSET has not been initialized. Call InitVtableOffset first."); - private set => _vtableOffset = value; - } - - public static void InitVtableOffset(Il2CppBinary binary, float metadataVersion) - { - VTABLE_OFFSET = GetVtableOffset(binary, metadataVersion); - } - public static readonly List UsefulOffsets = [ new UsefulOffset("cctor_finished", 0x74, typeof(uint), true), diff --git a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs b/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs index 7f2b8dfab..14adca3c1 100644 --- a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs @@ -41,7 +41,7 @@ public override string PrintAssembly(MethodAnalysisContext context) { lock (Formatter) { - var insns = X86Utils.Iterate(X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, false, context.AppContext.Binary), context.UnderlyingPointer, context.AppContext.Binary); + var insns = X86Utils.Iterate(X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, false, context.AppContext.Binary), context.UnderlyingPointer, context.AppContext.Binary.is32Bit); return string.Join("\n", insns.Select(FormatInstructionInternal)); } diff --git a/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs index 91f2bc803..c73e46e2e 100644 --- a/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/ApplicationAnalysisContext.cs @@ -138,7 +138,6 @@ public ApplicationAnalysisContext(Il2CppBinary binary, Il2CppMetadata metadata) SystemTypes = new(this); - Il2CppClassUsefulOffsets.InitVtableOffset(binary, MetadataVersion); MiscUtils.InitFunctionStarts(this); PopulateMethodsByAddressTable(); diff --git a/Cpp2IL.Core/Utils/MiscUtils.cs b/Cpp2IL.Core/Utils/MiscUtils.cs index 7236be21c..a48cfcd74 100644 --- a/Cpp2IL.Core/Utils/MiscUtils.cs +++ b/Cpp2IL.Core/Utils/MiscUtils.cs @@ -105,9 +105,9 @@ internal static string[] GetGenericParams(string input) return null; } - public static int GetSlotNum(int offset) + public static int GetSlotNum(int offset, Il2CppBinary binary, float metadataVersion) { - var offsetInVtable = offset - Il2CppClassUsefulOffsets.VTABLE_OFFSET; //0x128 being the address of the vtable in an Il2CppClass + var offsetInVtable = offset - Il2CppClassUsefulOffsets.GetVtableOffset(binary, metadataVersion); //0x128 being the address of the vtable in an Il2CppClass if (offsetInVtable % 0x10 != 0 && offsetInVtable % 0x8 == 0) offsetInVtable -= 0x8; //Handle read of the second pointer in the struct. diff --git a/Cpp2IL.Core/Utils/X86Utils.cs b/Cpp2IL.Core/Utils/X86Utils.cs index ae31a3473..f8debf279 100644 --- a/Cpp2IL.Core/Utils/X86Utils.cs +++ b/Cpp2IL.Core/Utils/X86Utils.cs @@ -18,13 +18,13 @@ public static class X86Utils private static readonly ConcurrentDictionary CachedX86RegNamesNew = new(); //TODO Consider implementing a CodeReader for Memory - public static InstructionList Disassemble(Memory bytes, ulong methodBase, Il2CppBinary binary) - => Disassemble(bytes.ToArray(), methodBase, binary); + public static InstructionList Disassemble(Memory bytes, ulong methodBase, bool is32Bit) + => Disassemble(bytes.ToArray(), methodBase, is32Bit); - public static InstructionList Disassemble(byte[] bytes, ulong methodBase, Il2CppBinary binary) + public static InstructionList Disassemble(byte[] bytes, ulong methodBase, bool is32Bit) { var codeReader = new ByteArrayCodeReader(bytes); - var decoder = Decoder.Create(binary.is32Bit ? 32 : 64, codeReader); + var decoder = Decoder.Create(is32Bit ? 32 : 64, codeReader); decoder.IP = methodBase; var instructions = new InstructionList(); var endRip = decoder.IP + (uint)bytes.Length; @@ -37,18 +37,18 @@ public static InstructionList Disassemble(byte[] bytes, ulong methodBase, Il2Cpp public static InstructionList Disassemble(MethodAnalysisContext context) { - return Disassemble(context.RawBytes, context.UnderlyingPointer, context.AppContext.Binary); + return Disassemble(context.RawBytes, context.UnderlyingPointer, context.AppContext.Binary.is32Bit); } - public static IEnumerable Iterate(Memory bytes, ulong methodBase, Il2CppBinary binary) + public static IEnumerable Iterate(Memory bytes, ulong methodBase, bool is32Bit) { - return Iterate(bytes.AsEnumerable(), methodBase, binary); + return Iterate(bytes.AsEnumerable(), methodBase, is32Bit); } - public static IEnumerable Iterate(IEnumerable bytes, ulong methodBase, Il2CppBinary binary) + public static IEnumerable Iterate(IEnumerable bytes, ulong methodBase, bool is32Bit) { var codeReader = new EnumerableCodeReader(bytes); - var decoder = Decoder.Create(binary.is32Bit ? 32 : 64, codeReader); + var decoder = Decoder.Create(is32Bit ? 32 : 64, codeReader); decoder.IP = methodBase; decoder.Decode(out var instruction); @@ -61,7 +61,7 @@ public static IEnumerable Iterate(IEnumerable bytes, ulong me public static IEnumerable Iterate(MethodAnalysisContext context) { - return Iterate(context.RawBytes, context.UnderlyingPointer, context.AppContext.Binary); + return Iterate(context.RawBytes, context.UnderlyingPointer, context.AppContext.Binary.is32Bit); } public static Memory GetRawManagedOrCaCacheGenMethodBody(ulong ptr, bool isCaGen, Il2CppBinary binary) @@ -177,7 +177,7 @@ public static InstructionList GetMethodBodyAtVirtAddressNew(ulong addr, bool pee buff.Add(binary.GetByteAtRawAddress((ulong)rawAddr)); - ret = X86Utils.Disassemble(buff.ToArray(), functionStart, binary); + ret = X86Utils.Disassemble(buff.ToArray(), functionStart, binary.is32Bit); if (ret.All(i => i.Mnemonic != Mnemonic.INVALID) && ret.Any(i => i.Code == Code.Int3)) con = false; diff --git a/LibCpp2IL/LibCpp2IlContext.cs b/LibCpp2IL/LibCpp2IlContext.cs index 3ec46a970..a1b2f8b94 100644 --- a/LibCpp2IL/LibCpp2IlContext.cs +++ b/LibCpp2IL/LibCpp2IlContext.cs @@ -23,7 +23,7 @@ public sealed class LibCpp2IlContext public LibCpp2IlReflectionCache ReflectionCache { get; } = new(); - // Global mapper state (was static on LibCpp2IlGlobalMapper) + // Global mapper state internal List TypeRefs = []; internal List MethodRefs = []; internal List FieldRefs = []; diff --git a/LibCpp2IL/LibCpp2IlContextBuilder.cs b/LibCpp2IL/LibCpp2IlContextBuilder.cs index 544b8aa04..f6b1d456e 100644 --- a/LibCpp2IL/LibCpp2IlContextBuilder.cs +++ b/LibCpp2IL/LibCpp2IlContextBuilder.cs @@ -59,16 +59,16 @@ public void LoadMetadata(byte[] metadataBytes, UnityVersion unityVersion) _context.Il2CppTypeHasNumMods5Bits = metadata.MetadataVersion >= 27.2f; - LibLogger.InfoNewline($"Initialized Metadata in {(DateTime.Now - start).TotalMilliseconds:F0}ms"); - - // Legacy/static API compatibility: some in-binary structures still resolve via LibCpp2IlMain.Binary/TheMetadata - // during binary initialization, so we must set metadata defaults before initializing the binary. -#pragma warning disable CS0618 // Intentional writes to legacy static fields for backwards compatibility + // Set legacy static fields during initialization so that code running during + // binary init (e.g. BinarySearcher) and LibCpp2IlReflection can access them. +#pragma warning disable CS0618 LibCpp2IlMain.TheMetadata = metadata; LibCpp2IlMain.DefaultContext = _context; LibCpp2IlMain.Il2CppTypeHasNumMods5Bits = _context.Il2CppTypeHasNumMods5Bits; #pragma warning restore CS0618 + LibLogger.InfoNewline($"Initialized Metadata in {(DateTime.Now - start).TotalMilliseconds:F0}ms"); + _metadataLoaded = true; } @@ -79,8 +79,7 @@ public void LoadBinary(byte[] binaryBytes) var bin = _context.Binary = LibCpp2IlBinaryRegistry.CreateAndInit(binaryBytes, _context.Metadata); - // Complete legacy/static initialization now that the binary exists. -#pragma warning disable CS0618 // Intentional write to legacy static field for backwards compatibility +#pragma warning disable CS0618 LibCpp2IlMain.Binary = bin; #pragma warning restore CS0618 @@ -99,8 +98,7 @@ public void LoadBinary(Il2CppBinary binary) _context.Binary = binary; - // Complete legacy/static initialization now that the binary exists. -#pragma warning disable CS0618 // Intentional write to legacy static field for backwards compatibility +#pragma warning disable CS0618 LibCpp2IlMain.Binary = binary; #pragma warning restore CS0618 @@ -124,8 +122,6 @@ public LibCpp2IlContext Build() start = DateTime.Now; LibLogger.Info("Mapping Globals..."); _context.MapGlobalIdentifiers(); - // Also populate legacy static mapper for backwards compatibility during transition - LibCpp2IlGlobalMapper.MapGlobalIdentifiers(_context.Metadata, _context.Binary); LibLogger.InfoNewline($"OK ({(DateTime.Now - start).TotalMilliseconds:F0}ms)"); } @@ -144,6 +140,11 @@ public LibCpp2IlContext Build() LibLogger.InfoNewline($"Processed {_context.Metadata.methodDefs.Length} OK ({(DateTime.Now - start).TotalMilliseconds:F0}ms)"); } + // Set DefaultContext so that LibCpp2IlReflection (static facade) works during context usage. +#pragma warning disable CS0618 + LibCpp2IlMain.DefaultContext = _context; +#pragma warning restore CS0618 + _context.ReflectionCache.Init(_context); return _context; diff --git a/LibCpp2IL/LibCpp2IlGlobalMapper.cs b/LibCpp2IL/LibCpp2IlGlobalMapper.cs deleted file mode 100644 index a25b0a532..000000000 --- a/LibCpp2IL/LibCpp2IlGlobalMapper.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using LibCpp2IL.Metadata; - -namespace LibCpp2IL; - -public static class LibCpp2IlGlobalMapper -{ - internal static List TypeRefs = []; - internal static List MethodRefs = []; - internal static List FieldRefs = []; - internal static List Literals = []; - - internal static Dictionary TypeRefsByAddress = new(); - internal static Dictionary MethodRefsByAddress = new(); - internal static Dictionary FieldRefsByAddress = new(); - internal static Dictionary LiteralsByAddress = new(); - - internal static void Reset() - { - TypeRefs.Clear(); - MethodRefs.Clear(); - FieldRefs.Clear(); - Literals.Clear(); - TypeRefsByAddress.Clear(); - MethodRefsByAddress.Clear(); - FieldRefsByAddress.Clear(); - LiteralsByAddress.Clear(); - } - - internal static void MapGlobalIdentifiers(Il2CppMetadata metadata, Il2CppBinary cppAssembly) - { - if (metadata.MetadataVersion < 27f) - MapGlobalIdentifiersPre27(metadata, cppAssembly); - else - MapGlobalIdentifiersPost27(metadata, cppAssembly); - } - - private static void MapGlobalIdentifiersPost27(Il2CppMetadata metadata, Il2CppBinary cppAssembly) - { - //No-op - } - - private static void MapGlobalIdentifiersPre27(Il2CppMetadata metadata, Il2CppBinary cppAssembly) - { - //Type 1 => TypeInfo - //Type 2 => Il2CppType - //Type 3 => MethodDef - //Type 4 => FieldInfo - //Type 5 => StringLiteral - //Type 6 => MethodRef - - //Type references - - //We non-null assert here because this function is only called pre-27, when this is guaranteed to be non-null - TypeRefs = metadata.metadataUsageDic![(uint)MetadataUsageType.TypeInfo] - .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)) - .ToList(); - - //More type references - TypeRefs.AddRange(metadata.metadataUsageDic[(uint)MetadataUsageType.Type] - .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)) - ); - - //Method references - MethodRefs = metadata.metadataUsageDic[(uint)MetadataUsageType.MethodDef] - .Select(kvp => new MetadataUsage(MetadataUsageType.MethodDef, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)) - .ToList(); - - //Field references - FieldRefs = metadata.metadataUsageDic[(uint)MetadataUsageType.FieldInfo] - .Select(kvp => new MetadataUsage(MetadataUsageType.FieldInfo, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)) - .ToList(); - - //Literals - Literals = metadata.metadataUsageDic[(uint)MetadataUsageType.StringLiteral] - .Select(kvp => new MetadataUsage(MetadataUsageType.StringLiteral, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value, cppAssembly, metadata)).ToList(); - - //Generic method references - foreach (var (metadataUsageIdx, methodSpecIdx) in metadata.metadataUsageDic[(uint)MetadataUsageType.MethodRef]) //kIl2CppMetadataUsageMethodRef - { - MethodRefs.Add(new MetadataUsage(MetadataUsageType.MethodRef, cppAssembly.GetRawMetadataUsage(metadataUsageIdx), methodSpecIdx, cppAssembly, metadata)); - } - - foreach (var globalIdentifier in TypeRefs) - TypeRefsByAddress[globalIdentifier.Offset] = globalIdentifier; - - foreach (var globalIdentifier in MethodRefs) - MethodRefsByAddress[globalIdentifier.Offset] = globalIdentifier; - - foreach (var globalIdentifier in FieldRefs) - FieldRefsByAddress[globalIdentifier.Offset] = globalIdentifier; - - foreach (var globalIdentifier in Literals) - LiteralsByAddress[globalIdentifier.Offset] = globalIdentifier; - } - -#pragma warning disable CS0618 // Intentional use of legacy static LibCpp2IlMain.Binary/TheMetadata for backwards compatibility - public static MetadataUsage? CheckForPost27GlobalAt(ulong address) - { - if (!LibCpp2IlMain.Binary!.TryMapVirtualAddressToRaw(address, out var raw) || raw >= LibCpp2IlMain.Binary.RawLength) - return null; - - var encoded = LibCpp2IlMain.Binary.ReadPointerAtVirtualAddress(address); - var metadataUsage = MetadataUsage.DecodeMetadataUsage(encoded, address, LibCpp2IlMain.Binary, LibCpp2IlMain.TheMetadata!); - - if (metadataUsage?.IsValid != true) - return null; - - return metadataUsage; - } -#pragma warning restore CS0618 -} diff --git a/LibCpp2IL/LibCpp2IlMain.cs b/LibCpp2IL/LibCpp2IlMain.cs index f15c2bbf2..da73960b0 100644 --- a/LibCpp2IL/LibCpp2IlMain.cs +++ b/LibCpp2IL/LibCpp2IlMain.cs @@ -218,7 +218,6 @@ public static bool LoadFromFile(string pePath, string metadataPath, UnityVersion [Obsolete("No longer needed — context is garbage collected.")] public static void Reset() { - LibCpp2IlGlobalMapper.Reset(); MethodsByPtr.Clear(); DefaultContext = null; diff --git a/LibCpp2IL/LibCpp2IlUtils.cs b/LibCpp2IL/LibCpp2IlUtils.cs index 5cc93cb18..38feb4aba 100644 --- a/LibCpp2IL/LibCpp2IlUtils.cs +++ b/LibCpp2IL/LibCpp2IlUtils.cs @@ -239,13 +239,8 @@ public static Il2CppTypeReflectionData WrapType(Il2CppTypeDefinition what) public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) { -#pragma warning disable CS0618 // Fallback to legacy statics for backwards compatibility - var binary = forWhat.OwningBinary ?? LibCpp2IlMain.Binary; - var metadata = forWhat.OwningMetadata ?? LibCpp2IlMain.TheMetadata; -#pragma warning restore CS0618 - - if (binary == null || metadata == null) - throw new Exception("Can't get type reflection data when not initialized. How did you even get the type?"); + var binary = forWhat.OwningBinary ?? throw new Exception("Can't get type reflection data: OwningBinary is not set on the Il2CppType."); + var metadata = forWhat.OwningMetadata ?? throw new Exception("Can't get type reflection data: OwningMetadata is not set on the Il2CppType."); switch (forWhat.Type) { diff --git a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs index c4d9a90bc..ba889ab19 100644 --- a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs +++ b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs @@ -28,9 +28,8 @@ public class Il2CppTypeReflectionData public bool isPointer; #pragma warning restore 8618 -#pragma warning disable CS0618 // Fallback to legacy static LibCpp2IlMain.TheMetadata for backwards compatibility - public Il2CppGenericParameter? GenericParameter => isArray || isType ? null : baseType?.OwningMetadata?.GetGenericParameterFromIndex(variableGenericParamIndex) ?? LibCpp2IlMain.TheMetadata?.GetGenericParameterFromIndex(variableGenericParamIndex); -#pragma warning restore CS0618 + public Il2CppGenericParameter? GenericParameter => isArray || isType ? null + : (baseType?.OwningMetadata ?? LibCpp2IlMain.DefaultContext?.Metadata)?.GetGenericParameterFromIndex(variableGenericParamIndex); private string GetPtrSuffix() {