Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cpp2IL.Core/Analysis/MetadataResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Cpp2IL.Core.ISIL;
using Cpp2IL.Core.Model.Contexts;
using Cpp2IL.Core.Utils;
using LibCpp2IL;

namespace Cpp2IL.Core.Analysis;

Expand Down
3 changes: 1 addition & 2 deletions Cpp2IL.Core/Api/Cpp2IlInstructionSet.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using Cpp2IL.Core.Il2CppApiFunctions;
using Cpp2IL.Core.ISIL;
Expand All @@ -15,7 +14,7 @@ public abstract class Cpp2IlInstructionSet
/// <param name="context">The method to get the body for</param>
/// <param name="isAttributeGenerator">True if this is an attribute generator function, false if it's a managed method</param>
/// <returns>A byte array representing the method's body</returns>
public abstract Memory<byte> GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator);
public abstract BinarySlice GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator);

/// <summary>
/// Returns the virtual address from which the given method starts. By default, returns the <see cref="Il2CppMethodDefinition.MethodPointer"/> property, but
Expand Down
48 changes: 48 additions & 0 deletions Cpp2IL.Core/BinarySlice.cs
Original file line number Diff line number Diff line change
@@ -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<byte> AsSpan()
{
if (_computed is not null)
return _computed;

return _binary!.GetRawBinaryContent()
.Slice(_offset, Length);
}
}
2 changes: 1 addition & 1 deletion Cpp2IL.Core/Extensions/MiscExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public static IEnumerable<T> Peek<T>(this IEnumerable<T> enumerable, Action<T> a
});
}

public static unsafe uint ReadUInt(this Span<byte> span, int start)
public static unsafe uint ReadUInt(this ReadOnlySpan<byte> span, int start)
{
if (start >= span.Length)
throw new ArgumentOutOfRangeException(nameof(start), $"start=[{start}], mem.Length=[{span.Length}]");
Expand Down
3 changes: 1 addition & 2 deletions Cpp2IL.Core/ISIL/Instruction.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
10 changes: 5 additions & 5 deletions Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand Down Expand Up @@ -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;

Expand All @@ -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;

Expand All @@ -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;
Expand All @@ -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<ulong> FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
Expand Down
13 changes: 7 additions & 6 deletions Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Expand All @@ -12,23 +11,25 @@ namespace Cpp2IL.Core.InstructionSets;

public class Arm64InstructionSet : Cpp2IlInstructionSet
{
public override Memory<byte> 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<Instruction> GetIsilFromMethod(MethodAnalysisContext context)
Expand Down
10 changes: 5 additions & 5 deletions Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Expand All @@ -12,14 +11,15 @@ namespace Cpp2IL.Core.InstructionSets;

public class ArmV7InstructionSet : Cpp2IlInstructionSet
{
public override Memory<byte> 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<Instruction> GetIsilFromMethod(MethodAnalysisContext context)
Expand Down
32 changes: 16 additions & 16 deletions Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -18,32 +16,34 @@ public class NewArmV8InstructionSet : Cpp2IlInstructionSet
[ThreadStatic]
private static Dictionary<Arm64Register, ulong> adrpOffsets = new();

public override Memory<byte> 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<object> GetParameterOperandsFromMethod(MethodAnalysisContext context)
Expand All @@ -56,10 +56,10 @@ public override List<Instruction> GetIsilFromMethod(MethodAnalysisContext contex
{
var insns = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.AppContext.Binary, context.UnderlyingPointer);

if (adrpOffsets == null!) //Null suppress because thread static weirdness
if (adrpOffsets == null!) // initializers for ThreadStatic fields only run on the first thread
adrpOffsets = new();

adrpOffsets.Clear();
else
adrpOffsets.Clear();

var instructions = new List<Instruction>();
var addresses = new List<ulong>();
Expand Down Expand Up @@ -267,7 +267,7 @@ void AddCall(MethodAnalysisContext context, object? returnRegister2, ulong addre
//Unconditional branch to outside the method, treat as call (tail-call, specifically) followed by return
var returnRegister2 = GetReturnRegisterForContext(context);
AddCall(context, returnRegister2, address, target);

if (returnRegister2 == null)
Add(address, OpCode.Return);
else
Expand Down Expand Up @@ -517,7 +517,7 @@ private object ConvertOperand(Arm64Instruction instruction, int operand)

public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance() => new NewArm64KeyFunctionAddresses();

public override string PrintAssembly(MethodAnalysisContext context) => context.RawBytes.Span.Length <= 0 ? "" : string.Join("\n", Disassembler.Disassemble(context.RawBytes.Span, context.UnderlyingPointer, new Disassembler.Options(true, true, false)).ToList());
public override string PrintAssembly(MethodAnalysisContext context) => context.RawBytes.Length <= 0 ? "" : string.Join("\n", Disassembler.Disassemble(context.RawBytes.AsSpan(), context.UnderlyingPointer, new Disassembler.Options(true, true, false)).ToList());

private object? GetReturnRegisterForContext(MethodAnalysisContext context)
{
Expand Down
9 changes: 4 additions & 5 deletions Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using Cpp2IL.Core.Api;
using Cpp2IL.Core.Il2CppApiFunctions;
Expand All @@ -12,7 +11,7 @@ namespace Cpp2IL.Core.InstructionSets;

public class WasmInstructionSet : Cpp2IlInstructionSet
{
public override Memory<byte> GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
public override BinarySlice GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
{
if (context.Definition is { } methodDefinition)
{
Expand All @@ -21,16 +20,16 @@ public override Memory<byte> GetRawBytesForMethod(MethodAnalysisContext context,
if (wasmDef == null)
{
Logger.WarnNewline($"Could not find WASM definition for method {methodDefinition.HumanReadableSignature} in {methodDefinition.DeclaringType?.FullName}, probably incorrect signature calculation (signature was {WasmUtils.BuildSignature(context)}, index is {context.UnderlyingPointer})", "WasmInstructionSet");
return Array.Empty<byte>();
return BinarySlice.Empty;
}

if (wasmDef.AssociatedFunctionBody == null)
throw new($"WASM definition {wasmDef}, resolved from MethodAnalysisContext {context.Definition.HumanReadableSignature} in {context.DeclaringType?.FullName} has no associated function body (signature was {WasmUtils.BuildSignature(context)}, index is {context.UnderlyingPointer})");

return wasmDef.AssociatedFunctionBody.Instructions;
return new BinarySlice(wasmDef.AssociatedFunctionBody.Instructions);
}

return Array.Empty<byte>();
return BinarySlice.Empty;
}

public override List<Instruction> GetIsilFromMethod(MethodAnalysisContext context)
Expand Down
4 changes: 2 additions & 2 deletions Cpp2IL.Core/InstructionSets/X86InstructionSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ public static string FormatInstruction(Instruction instruction)
}
}

public override Memory<byte> GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) => X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, isAttributeGenerator, context.AppContext.Binary);
public override BinarySlice GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) => X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, isAttributeGenerator, context.AppContext.Binary);

public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance() => new X86KeyFunctionAddresses();

public override string PrintAssembly(MethodAnalysisContext context)
{
lock (Formatter)
{
var insns = X86Utils.Iterate(X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, false, context.AppContext.Binary), context.UnderlyingPointer, context.AppContext.Binary.is32Bit);
var insns = X86Utils.Iterate(context);

return string.Join("\n", insns.Select(FormatInstructionInternal));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

namespace Cpp2IL.Core.Model.Contexts;

public class AttributeGeneratorMethodAnalysisContext : MethodAnalysisContext
public class AttributeGeneratorMethodAnalysisContext(ulong pointer, ApplicationAnalysisContext context, HasCustomAttributes associatedMember)
: MethodAnalysisContext(context)
{
public override ulong UnderlyingPointer { get; }
public override ulong UnderlyingPointer { get; } = pointer;

protected override bool IsInjected => true;
public override string DefaultName => "<AttributeGenerator>";
Expand All @@ -13,12 +14,5 @@ public class AttributeGeneratorMethodAnalysisContext : MethodAnalysisContext
public override TypeAnalysisContext DefaultReturnType => AppContext.SystemTypes.SystemVoidType;
protected override int CustomAttributeIndex => -1;

public readonly HasCustomAttributes AssociatedMember;

public AttributeGeneratorMethodAnalysisContext(ulong pointer, ApplicationAnalysisContext context, HasCustomAttributes associatedMember) : base(context)
{
UnderlyingPointer = pointer;
AssociatedMember = associatedMember;
rawMethodBody = AppContext.InstructionSet.GetRawBytesForMethod(this, true);
}
public readonly HasCustomAttributes AssociatedMember = associatedMember;
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,6 @@ private ConcreteGenericMethodAnalysisContext(Cpp2IlMethodRef? methodRef, MethodA
}

DefaultReturnType = GenericInstantiation.Instantiate(BaseMethodContext.ReturnType, typeGenericParameters, methodGenericParameters);

if (UnderlyingPointer != 0)
rawMethodBody = AppContext.InstructionSet.GetRawBytesForMethod(this, false);
}

private static AssemblyAnalysisContext ResolveDeclaringAssembly(Cpp2IlMethodRef methodRef, ApplicationAnalysisContext context)
Expand Down
Loading
Loading