diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index b557ae3c9..c7ba456b5 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,24 @@ 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() + 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 +140,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..6853a1f91 100644 --- a/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs +++ b/Cpp2IL.Core/Exceptions/UnsupportedInstructionSetException.cs @@ -1,9 +1,8 @@ -using System; -using LibCpp2IL; +using System; namespace Cpp2IL.Core.Exceptions; -public class UnsupportedInstructionSetException : Exception +public class UnsupportedInstructionSetException(string? instructionSetId = null) : 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."; + 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 19ad5bdbb..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,11 +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) { - 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); + 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 189e9cc95..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(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(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/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..ee42490dc 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.is32Bit); } 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..cba7b22f2 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,13 @@ 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; - public static readonly int VTABLE_OFFSET = LibCpp2IlMain.MetadataVersion >= 24.2 ? V24_2_VTABLE_OFFSET : PRE_24_2_VTABLE_OFFSET; + return metadataVersion >= 24.2f ? v24_2_vtableOffset : pre24_2_vtableOffset; + } public static readonly List UsefulOffsets = [ @@ -37,39 +40,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..e99707268 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, 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, 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..14adca3c1 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.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 5c5bf7dc8..c73e46e2e 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 /// @@ -108,7 +137,7 @@ public ApplicationAnalysisContext(Il2CppBinary binary, Il2CppMetadata metadata) } SystemTypes = new(this); - + 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..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,39 +89,30 @@ public static string GetRegisterNameNew(Arm64RegisterId registerId) return ret; } - private static void InitArm64Decompilation() + public static List GetArm64MethodBodyAtVirtualAddress(ApplicationAnalysisContext appContext, ulong virtAddress, bool managed = true, int count = -1) { - var disassembler = CapstoneDisassembler.CreateArm64Disassembler(LibCpp2IlMain.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) - { - if (_arm64Disassembler == null) - InitArm64Decompilation(); + 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. //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); + var iter = disassembler.Iterate(bytes, (long)virtAddress); if (count > 0) iter = iter.Take(count); @@ -131,14 +121,14 @@ 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)) { //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/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..a48cfcd74 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() - { - _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", LibCpp2IlMain.Binary!.is32Bit ? 4UL : 8UL }, - { "UIntPtr", LibCpp2IlMain.Binary.is32Bit ? 4UL : 8UL }, - }; - } - - internal static string[] GetGenericParams(string input) { if (!input.Contains('<')) @@ -129,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. @@ -146,11 +122,6 @@ public static int GetSlotNum(int offset) return -1; } - public static int GetPointerSizeBytes() - { - return LibCpp2IlMain.Binary!.is32Bit ? 4 : 8; - } - internal static byte[] RawBytes(IConvertible original) => original switch { @@ -182,7 +153,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 +195,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..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) - => Disassemble(bytes.ToArray(), methodBase); + public static InstructionList Disassemble(Memory bytes, ulong methodBase, bool is32Bit) + => Disassemble(bytes.ToArray(), methodBase, is32Bit); - public static InstructionList Disassemble(byte[] bytes, ulong methodBase) + public static InstructionList Disassemble(byte[] bytes, ulong methodBase, bool is32Bit) { var codeReader = new ByteArrayCodeReader(bytes); - var decoder = Decoder.Create(LibCpp2IlMain.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) public static InstructionList Disassemble(MethodAnalysisContext context) { - return Disassemble(context.RawBytes, context.UnderlyingPointer); + return Disassemble(context.RawBytes, context.UnderlyingPointer, context.AppContext.Binary.is32Bit); } - public static IEnumerable Iterate(Memory bytes, ulong methodBase) + public static IEnumerable Iterate(Memory bytes, ulong methodBase, bool is32Bit) { - return Iterate(bytes.AsEnumerable(), methodBase); + return Iterate(bytes.AsEnumerable(), methodBase, is32Bit); } - public static IEnumerable Iterate(IEnumerable bytes, ulong methodBase) + public static IEnumerable Iterate(IEnumerable bytes, ulong methodBase, bool is32Bit) { var codeReader = new EnumerableCodeReader(bytes); - var decoder = Decoder.Create(LibCpp2IlMain.Binary!.is32Bit ? 32 : 64, codeReader); + var decoder = Decoder.Create(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.is32Bit); } - 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.is32Bit); 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..a75b6ceb9 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppGenericClass.cs @@ -5,30 +5,20 @@ 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; - + 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; - public Il2CppTypeDefinition TypeDefinition { get { - if (EffectiveMetadataVersion < 27f) + if (MetadataVersion < 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(); @@ -39,17 +29,17 @@ public Il2CppType? V27BaseType { get { - if (EffectiveMetadataVersion < 27f) + if (MetadataVersion < 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 ??= MetadataVersion >= 27.2f; return t; } } @@ -60,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/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..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; @@ -68,8 +71,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 +550,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..a1b2f8b94 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 + 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..f6b1d456e 100644 --- a/LibCpp2IL/LibCpp2IlContextBuilder.cs +++ b/LibCpp2IL/LibCpp2IlContextBuilder.cs @@ -55,16 +55,19 @@ public void LoadMetadata(byte[] metadataBytes, UnityVersion unityVersion) } _context.Metadata = metadata; + metadata.OwningContext = _context; _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. + // 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; } @@ -76,8 +79,12 @@ 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 LibCpp2IlMain.Binary = bin; +#pragma warning restore CS0618 + + // Set OwningBinary on all metadata structures now that the binary exists. + _context.Metadata.SetOwningBinaryOnAllStructures(bin); _binaryLoaded = true; } @@ -91,8 +98,12 @@ public void LoadBinary(Il2CppBinary binary) _context.Binary = binary; - // Complete legacy/static initialization now that the binary exists. +#pragma warning disable CS0618 LibCpp2IlMain.Binary = binary; +#pragma warning restore CS0618 + + // Set OwningBinary on all metadata structures now that the binary exists. + _context.Metadata.SetOwningBinaryOnAllStructures(binary); _binaryLoaded = true; } @@ -110,7 +121,7 @@ public LibCpp2IlContext Build() { start = DateTime.Now; LibLogger.Info("Mapping Globals..."); - LibCpp2IlGlobalMapper.MapGlobalIdentifiers(_context.Metadata, _context.Binary); + _context.MapGlobalIdentifiers(); LibLogger.InfoNewline($"OK ({(DateTime.Now - start).TotalMilliseconds:F0}ms)"); } @@ -129,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 acaa4f82c..000000000 --- a/LibCpp2IL/LibCpp2IlGlobalMapper.cs +++ /dev/null @@ -1,111 +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)) - .ToList(); - - //More type references - TypeRefs.AddRange(metadata.metadataUsageDic[(uint)MetadataUsageType.Type] - .Select(kvp => new MetadataUsage(MetadataUsageType.Type, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)) - ); - - //Method references - MethodRefs = metadata.metadataUsageDic[(uint)MetadataUsageType.MethodDef] - .Select(kvp => new MetadataUsage(MetadataUsageType.MethodDef, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)) - .ToList(); - - //Field references - FieldRefs = metadata.metadataUsageDic[(uint)MetadataUsageType.FieldInfo] - .Select(kvp => new MetadataUsage(MetadataUsageType.FieldInfo, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)) - .ToList(); - - //Literals - Literals = metadata.metadataUsageDic[(uint)MetadataUsageType.StringLiteral] - .Select(kvp => new MetadataUsage(MetadataUsageType.StringLiteral, cppAssembly.GetRawMetadataUsage(kvp.Key), kvp.Value)).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)); - } - - 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 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); - - if (metadataUsage?.IsValid != true) - return null; - - return metadataUsage; - } -} diff --git a/LibCpp2IL/LibCpp2IlMain.cs b/LibCpp2IL/LibCpp2IlMain.cs index 9f2c579f1..da73960b0 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 . - /// - 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. + /// Initialize the metadata and binary from a pair of byte arrays, returning a context. /// - /// 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,105 @@ 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() + { + 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..38feb4aba 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); } @@ -126,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; @@ -141,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; } @@ -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,8 +239,8 @@ public static Il2CppTypeReflectionData WrapType(Il2CppTypeDefinition what) public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) { - if (LibCpp2IlMain.Binary == null || LibCpp2IlMain.TheMetadata == 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) { @@ -292,12 +290,12 @@ 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; - var genericInst = genericClass.Context.ClassInst; + var genericInst = genericClass.Context.ClassInst!; var genericParams = genericInst.Types .Select(GetTypeReflectionData) //Recursive call here @@ -311,8 +309,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 +324,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,8 +338,8 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) } case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: { - var arrayType = LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(forWhat.Data.Array); - var oriType = arrayType.ElementType; + var arrayType = binary.ReadReadableAtVirtualAddress(forWhat.Data.Array); + var oriType = arrayType.GetElementTypeOrThrow(); return new() { baseType = null, @@ -355,7 +353,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 +371,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..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 (LibCpp2IlMain.TheMetadata == 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 +31,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 => 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 => 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 => 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 => 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..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 ? null : LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex); + 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 4fe06cef6..566ba0ab1 100644 --- a/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppFieldDefinition.cs @@ -13,20 +13,17 @@ 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) - return base.ToString(); - return $"Il2CppFieldDefinition[Name={Name}, FieldType={FieldType}]"; } @@ -41,13 +38,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..43efb61ad 100644 --- a/LibCpp2IL/Metadata/Il2CppImageDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppImageDefinition.cs @@ -19,13 +19,12 @@ 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!.GetStringFromIndex(nameIndex); - public Il2CppTypeDefinition[]? Types => LibCpp2IlMain.TheMetadata == 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(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..98dd5582c 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. + /// + public LibCpp2IlContext? OwningContext { get; internal 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..fa2f47776 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 => LibCpp2ILUtils.GetTypeReflectionData(OwningBinary!.GetType(returnTypeIdx)); - public Il2CppTypeDefinition? DeclaringType => LibCpp2IlMain.TheMetadata == null ? null : LibCpp2IlMain.TheMetadata.GetTypeDefinitionFromIndex(declaringTypeIdx); + public Il2CppTypeDefinition? DeclaringType => 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 (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}. DeclaringType is null"); 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 ? 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 ? 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 (LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == 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] = LibCpp2IlMain.TheMetadata.GetParameterDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(parameterStart.Value + i)); + ret[i] = OwningMetadata!.GetParameterDefinitionFromIndex(Il2CppVariableWidthIndex.MakeTemporaryForFixedWidthUsage(parameterStart.Value + i)); } return ret; @@ -104,7 +101,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 +115,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 +136,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,9 +144,6 @@ public Il2CppParameterReflectionData[]? Parameters public override string? ToString() { - if (LibCpp2IlMain.TheMetadata == 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..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 => LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex); + public object? ContainedDefaultValue => 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..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 (LibCpp2IlMain.TheMetadata == 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 +33,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 => 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 => 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 => 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 => 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..fc8ebafb8 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, OwningBinary!, OwningMetadata!)).ToArray(); } } @@ -125,9 +125,7 @@ public Il2CppImageDefinition? DeclaringAssembly { if (_cachedDeclaringAssembly == null) { - if (LibCpp2IlMain.TheMetadata == null) return null; - - LibCpp2ILUtils.PopulateDeclaringAssemblyCache(); + LibCpp2ILUtils.PopulateDeclaringAssemblyCache(OwningMetadata!); } return _cachedDeclaringAssembly; @@ -135,16 +133,16 @@ public Il2CppImageDefinition? DeclaringAssembly internal set => _cachedDeclaringAssembly = value; } - public Il2CppCodeGenModule? CodeGenModule => LibCpp2IlMain.Binary == null ? null : LibCpp2IlMain.Binary.GetCodegenModuleByName(DeclaringAssembly!.Name!); + public Il2CppCodeGenModule? CodeGenModule => 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 +155,7 @@ public Il2CppRGCTXDefinition[] RgctXs if (rangePair == null) return []; - return LibCpp2IlMain.Binary!.GetRgctxDataForPair(cgm, rangePair); + return OwningBinary!.GetRgctxDataForPair(cgm, rangePair); } } @@ -165,12 +163,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 +184,7 @@ public string? Namespace get { if (_cachedNamespace == null) - _cachedNamespace = LibCpp2IlMain.TheMetadata == null ? null : LibCpp2IlMain.TheMetadata.GetStringFromIndex(NamespaceIndex); + _cachedNamespace = OwningMetadata!.GetStringFromIndex(NamespaceIndex); return _cachedNamespace; } @@ -199,7 +197,7 @@ public string? Name get { if (_cachedName == null) - _cachedName = LibCpp2IlMain.TheMetadata == null ? null : LibCpp2IlMain.TheMetadata.GetStringFromIndex(NameIndex); + _cachedName = OwningMetadata!.GetStringFromIndex(NameIndex); return _cachedName; } @@ -209,9 +207,6 @@ public string? FullName { get { - if (LibCpp2IlMain.TheMetadata == null) - return null; - if (DeclaringType != null) return $"{DeclaringType.FullName}+{Name}"; @@ -219,34 +214,31 @@ 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 ? null : LibCpp2ILUtils.GetTypeReflectionData(OwningBinary!.GetType(ParentIndex)); public Il2CppFieldDefinition[]? Fields { get { - if (LibCpp2IlMain.TheMetadata == 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 +260,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 +272,10 @@ public Il2CppMethodDefinition[]? Methods { get { - if (LibCpp2IlMain.TheMetadata == null) - return null; - if (FirstMethodIdx.IsNull || MethodCount == 0) return []; - return LibCpp2IlMain.TheMetadata.GetMethodDefinitionsFromIndexAndCount(FirstMethodIdx, MethodCount); + return OwningMetadata!.GetMethodDefinitionsFromIndexAndCount(FirstMethodIdx, MethodCount); } } @@ -294,13 +283,10 @@ public Il2CppPropertyDefinition[]? Properties { get { - if (LibCpp2IlMain.TheMetadata == 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,46 +299,37 @@ public Il2CppEventDefinition[]? Events { get { - if (LibCpp2IlMain.TheMetadata == null) - return null; - if (FirstEventId.IsNull || EventCount == 0) return []; - var ret = LibCpp2IlMain.TheMetadata.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 => LibCpp2IlMain.TheMetadata == null - ? null - : LibCpp2IlMain.TheMetadata.GetNestedTypeIndicesFromIndexAndCount(NestedTypesStart, NestedTypeCount) + public Il2CppTypeDefinition[]? NestedTypes => 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 - ? [] - : LibCpp2IlMain.TheMetadata.GetInterfaceIndicesFromIndexAndCount(InterfacesStart, InterfacesCount) - .Select(LibCpp2IlMain.Binary.GetType) + public Il2CppType[] RawInterfaces => OwningMetadata!.GetInterfaceIndicesFromIndexAndCount(InterfacesStart, InterfacesCount) + .Select(OwningBinary!.GetType) .ToArray(); - public Il2CppTypeReflectionData[]? Interfaces => LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null - ? null - : RawInterfaces + public Il2CppTypeReflectionData[]? Interfaces => 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 => DeclaringTypeIndex.IsNull ? null : OwningBinary!.GetType(DeclaringTypeIndex).CoerceToUnderlyingTypeDefinition(); - public Il2CppTypeDefinition? ElementType => LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null || ElementTypeIndex < 0 - ? null - : LibCpp2IlMain.Binary.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 : LibCpp2IlMain.TheMetadata?.GetGenericContainerFromIndex(GenericContainerIndex); + public Il2CppGenericContainer? GenericContainer => GenericContainerIndex.IsNull ? null : OwningMetadata?.GetGenericContainerFromIndex(GenericContainerIndex); public Il2CppType EnumUnderlyingType { @@ -366,15 +343,12 @@ 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) - 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..234bb74ef 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,16 @@ 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, Il2CppMetadata metadata) + { + Type = type; + Offset = offset; + _value = value; + _binary = binary; + _metadata = metadata; + } + + public uint RawValue => _value; public object Value => Type switch @@ -39,11 +52,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 < _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 }; @@ -57,13 +70,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 = _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 {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 {_binary.NumTypes} types", e); } break; @@ -82,7 +95,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 = _metadata.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 +113,7 @@ public Il2CppFieldDefinition AsField() switch (Type) { case MetadataUsageType.FieldInfo: - var fieldRef = LibCpp2IlMain.TheMetadata!.fieldRefs[value]; + var fieldRef = _metadata.fieldRefs[_value]; _cachedField = fieldRef.FieldDefinition; _cachedName = fieldRef.DeclaringTypeDefinition!.FullName + "." + _cachedField!.Name; break; @@ -119,7 +132,7 @@ public string AsLiteral() switch (Type) { case MetadataUsageType.StringLiteral: - _cachedName = _cachedLiteral = LibCpp2IlMain.TheMetadata!.GetStringLiteralFromIndex(value); + _cachedName = _cachedLiteral = _metadata.GetStringLiteralFromIndex(_value); break; default: throw new Exception($"Cannot cast metadata usage of kind {Type} to a String Literal"); @@ -136,7 +149,7 @@ public Cpp2IlMethodRef AsGenericMethodRef() switch (Type) { case MetadataUsageType.MethodRef: - var methodSpec = LibCpp2IlMain.Binary!.GetMethodSpec((int)value); + var methodSpec = _binary.GetMethodSpec((int)_value); _cachedGenericMethod = new Cpp2IlMethodRef(methodSpec); _cachedName = _cachedGenericMethod.ToString(); @@ -154,8 +167,7 @@ public override string ToString() return $"Metadata Usage {{type={Type}, Value={Value}}}"; } - - public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address) + public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address, Il2CppBinary binary, Il2CppMetadata metadata) { var encodedType = encoded & 0xE000_0000; var type = (MetadataUsageType)(encodedType >> 29); @@ -163,17 +175,17 @@ public override string ToString() { var index = (uint)(encoded & 0x1FFF_FFFF); - if (LibCpp2IlMain.MetadataVersion >= 27) + if (metadata.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 > binary.NumTypes) return null; - if (type == MetadataUsageType.MethodDef && index > LibCpp2IlMain.TheMetadata!.MethodDefinitionCount) + if (type == MetadataUsageType.MethodDef && index > metadata.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..ba889ab19 100644 --- a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs +++ b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs @@ -28,7 +28,8 @@ 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 ?? LibCpp2IlMain.DefaultContext?.Metadata)?.GetGenericParameterFromIndex(variableGenericParamIndex); 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 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."); }