diff --git a/Cpp2IL.Core/Analysis/MetadataResolver.cs b/Cpp2IL.Core/Analysis/MetadataResolver.cs
index 7265f9490..9316d60ff 100644
--- a/Cpp2IL.Core/Analysis/MetadataResolver.cs
+++ b/Cpp2IL.Core/Analysis/MetadataResolver.cs
@@ -5,7 +5,6 @@
using Cpp2IL.Core.ISIL;
using Cpp2IL.Core.Model.Contexts;
using Cpp2IL.Core.Utils;
-using LibCpp2IL;
namespace Cpp2IL.Core.Analysis;
diff --git a/Cpp2IL.Core/Api/Cpp2IlInstructionSet.cs b/Cpp2IL.Core/Api/Cpp2IlInstructionSet.cs
index cd036485c..c6dd8a6f2 100644
--- a/Cpp2IL.Core/Api/Cpp2IlInstructionSet.cs
+++ b/Cpp2IL.Core/Api/Cpp2IlInstructionSet.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using Cpp2IL.Core.Il2CppApiFunctions;
using Cpp2IL.Core.ISIL;
@@ -15,7 +14,7 @@ public abstract class Cpp2IlInstructionSet
/// The method to get the body for
/// True if this is an attribute generator function, false if it's a managed method
/// A byte array representing the method's body
- public abstract Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator);
+ public abstract BinarySlice GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator);
///
/// Returns the virtual address from which the given method starts. By default, returns the property, but
diff --git a/Cpp2IL.Core/BinarySlice.cs b/Cpp2IL.Core/BinarySlice.cs
new file mode 100644
index 000000000..3304cb1ee
--- /dev/null
+++ b/Cpp2IL.Core/BinarySlice.cs
@@ -0,0 +1,48 @@
+using System;
+using LibCpp2IL;
+
+namespace Cpp2IL.Core;
+
+public readonly struct BinarySlice
+{
+ public static readonly BinarySlice Empty = new([]);
+
+ public readonly int Length;
+
+ private readonly byte[]? _computed;
+
+ private readonly Il2CppBinary? _binary;
+ private readonly int _offset;
+
+ public BinarySlice(byte[] computed)
+ {
+ _computed = computed;
+ Length = computed.Length;
+ }
+
+ public BinarySlice(Il2CppBinary binary, int offset, int length)
+ {
+ _binary = binary;
+ _offset = offset;
+ Length = length;
+ }
+
+ public byte[] ToArray()
+ {
+ if (_computed is not null)
+ return _computed;
+
+ return _binary!.GetRawBinaryContent()
+ .Slice(_offset, Length)
+ .ToArray();
+ }
+
+ public ReadOnlySpan AsSpan()
+ {
+ if (_computed is not null)
+ return _computed;
+
+ return _binary!.GetRawBinaryContent()
+ .Slice(_offset, Length);
+ }
+}
diff --git a/Cpp2IL.Core/Extensions/MiscExtensions.cs b/Cpp2IL.Core/Extensions/MiscExtensions.cs
index 7ab466642..d2a475f28 100644
--- a/Cpp2IL.Core/Extensions/MiscExtensions.cs
+++ b/Cpp2IL.Core/Extensions/MiscExtensions.cs
@@ -160,7 +160,7 @@ public static IEnumerable Peek(this IEnumerable enumerable, Action a
});
}
- public static unsafe uint ReadUInt(this Span span, int start)
+ public static unsafe uint ReadUInt(this ReadOnlySpan span, int start)
{
if (start >= span.Length)
throw new ArgumentOutOfRangeException(nameof(start), $"start=[{start}], mem.Length=[{span.Length}]");
diff --git a/Cpp2IL.Core/ISIL/Instruction.cs b/Cpp2IL.Core/ISIL/Instruction.cs
index e7e89fe42..b56d063a9 100644
--- a/Cpp2IL.Core/ISIL/Instruction.cs
+++ b/Cpp2IL.Core/ISIL/Instruction.cs
@@ -1,6 +1,5 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
-using AsmResolver.DotNet;
using Cpp2IL.Core.Graphs;
using Cpp2IL.Core.Model.Contexts;
diff --git a/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs
index b18a93dea..fc2daaf33 100644
--- a/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs
+++ b/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs
@@ -46,7 +46,7 @@ protected override void Init(ApplicationAnalysisContext context)
var oldLength = primaryExecutableSection.Length;
var toRemove = (int)(attributeGeneratorList[^1] - primaryExecutableSectionVa);
- primaryExecutableSection = primaryExecutableSection.Skip(toRemove).ToArray();
+ primaryExecutableSection = primaryExecutableSection.Slice(toRemove);
primaryExecutableSectionVa = attributeGeneratorList[^1];
@@ -79,7 +79,7 @@ protected override void Init(ApplicationAnalysisContext context)
oldLength = primaryExecutableSection.Length;
toRemove = (int)(startFrom - primaryExecutableSectionVa);
- primaryExecutableSection = primaryExecutableSection.Skip(toRemove).ToArray();
+ primaryExecutableSection = primaryExecutableSection.Slice(toRemove);
primaryExecutableSectionVa = startFrom;
@@ -99,7 +99,7 @@ protected override void Init(ApplicationAnalysisContext context)
oldLength = primaryExecutableSection.Length;
toRemove = (int)(startFrom - primaryExecutableSectionVa);
- primaryExecutableSection = primaryExecutableSection.Skip(toRemove).ToArray();
+ primaryExecutableSection = primaryExecutableSection.Slice(toRemove);
primaryExecutableSectionVa = startFrom;
@@ -115,7 +115,7 @@ protected override void Init(ApplicationAnalysisContext context)
var oldLength = primaryExecutableSection.Length;
var toKeep = (int)(attributeGeneratorList[^1] - primaryExecutableSectionVa);
- primaryExecutableSection = primaryExecutableSection.SubArray(..toKeep);
+ primaryExecutableSection = primaryExecutableSection[..toKeep];
//This doesn't change, we've trimmed the end, not the beginning
// primaryExecutableSectionVa = primaryExecutableSectionVa;
@@ -124,7 +124,7 @@ protected override void Init(ApplicationAnalysisContext context)
}
}
- _allInstructions = disassembler.Disassemble(primaryExecutableSection, (long)primaryExecutableSectionVa).ToList();
+ _allInstructions = disassembler.Disassemble(primaryExecutableSection.ToArray(), (long)primaryExecutableSectionVa).ToList();
}
protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
diff --git a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs
index 424a88232..2c3f9471c 100644
--- a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs
+++ b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -12,23 +11,25 @@ namespace Cpp2IL.Core.InstructionSets;
public class Arm64InstructionSet : Cpp2IlInstructionSet
{
- public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
+ public override BinarySlice GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
{
+ var binary = context.AppContext.Binary;
+
//Avoid use of capstone where possible
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, context.AppContext.Binary) - 1;
+ var startOfNextFunction = (int)MiscUtils.GetAddressOfNextFunctionStart(context.UnderlyingPointer, binary) - 1;
var ptrAsInt = (int)context.UnderlyingPointer;
var count = startOfNextFunction - ptrAsInt;
if (startOfNextFunction > 0)
- return context.AppContext.Binary.GetRawBinaryContent().AsMemory(ptrAsInt, count);
+ return new BinarySlice(binary, ptrAsInt, count);
}
- var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer);
+ var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(binary, context.UnderlyingPointer);
- return instructions.SelectMany(i => i.Bytes).ToArray();
+ return new BinarySlice(instructions.SelectMany(i => i.Bytes).ToArray());
}
public override List GetIsilFromMethod(MethodAnalysisContext context)
diff --git a/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs b/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs
index 64d08e79e..c1b79b21f 100644
--- a/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs
+++ b/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -12,14 +11,15 @@ namespace Cpp2IL.Core.InstructionSets;
public class ArmV7InstructionSet : Cpp2IlInstructionSet
{
- public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
+ public override BinarySlice GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
{
- if (ArmV7Utils.TryGetMethodBodyBytesFast(context.AppContext.Binary, context.UnderlyingPointer, context is AttributeGeneratorMethodAnalysisContext) is { } ret)
- return ret;
+ var slice = ArmV7Utils.TryGetMethodBodyBytesFast(context.AppContext.Binary, context.UnderlyingPointer, isAttributeGenerator);
+ if (slice.Length > 0)
+ return slice;
var instructions = ArmV7Utils.GetArmV7MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer);
- return instructions.SelectMany(i => i.Bytes).ToArray();
+ return new BinarySlice(instructions.SelectMany(i => i.Bytes).ToArray());
}
public override List GetIsilFromMethod(MethodAnalysisContext context)
diff --git a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs b/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs
index 6f5bc2ef8..aa22c7950 100644
--- a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs
+++ b/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs
@@ -8,8 +8,6 @@
using Cpp2IL.Core.Model.Contexts;
using Cpp2IL.Core.Utils;
using Disarm.InternalDisassembly;
-using LibCpp2IL;
-using Cpp2IL.Core.Logging;
namespace Cpp2IL.Core.InstructionSets;
@@ -18,32 +16,34 @@ public class NewArmV8InstructionSet : Cpp2IlInstructionSet
[ThreadStatic]
private static Dictionary adrpOffsets = new();
- public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
+ public override BinarySlice GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
{
+ var binary = context.AppContext.Binary;
+
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, context.AppContext.Binary);
+ var startOfNextFunction = (int)MiscUtils.GetAddressOfNextFunctionStart(context.UnderlyingPointer, binary);
var ptrAsInt = (int)context.UnderlyingPointer;
var count = startOfNextFunction - ptrAsInt;
if (startOfNextFunction > 0)
- return context.AppContext.Binary.GetRawBinaryContent().AsMemory(ptrAsInt, count);
+ return new BinarySlice(binary, ptrAsInt, count);
}
- var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer);
+ var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(binary, context.UnderlyingPointer);
var lastInsn = result.LastValid();
- var start = (int)context.AppContext.Binary.MapVirtualAddressToRaw(context.UnderlyingPointer);
+ var start = (int)binary.MapVirtualAddressToRaw(context.UnderlyingPointer);
// Map the last instruction (always within segment) and add 4 (ARM64 instruction size).
// This avoids mapping endVa which may land exactly at a segment boundary gap.
- var end = (int)context.AppContext.Binary.MapVirtualAddressToRaw(lastInsn.Address) + 4;
+ var end = (int)binary.MapVirtualAddressToRaw(lastInsn.Address) + 4;
//Sanity check
- if (start < 0 || end < 0 || start >= context.AppContext.Binary.RawLength || end >= context.AppContext.Binary.RawLength)
- throw new Exception($"Failed to map virtual address 0x{context.UnderlyingPointer:X} to raw address for method {context!.DeclaringType?.FullName}/{context.Name} - start: 0x{start:X}, end: 0x{end:X} are out of bounds for length {context.AppContext.Binary.RawLength}.");
+ if (start < 0 || end < 0 || start >= binary.RawLength || end >= binary.RawLength)
+ throw new Exception($"Failed to map virtual address 0x{context.UnderlyingPointer:X} to raw address for method {context!.DeclaringType?.FullName}/{context.Name} - start: 0x{start:X}, end: 0x{end:X} are out of bounds for length {binary.RawLength}.");
- return context.AppContext.Binary.GetRawBinaryContent().AsMemory(start, end - start);
+ return new BinarySlice(binary, start, end - start);
}
public override List