mirror of
https://github.com/AssetRipper/Cpp2IL.git
synced 2026-11-12 18:07:39 -10:00
Core: Some changes to reduce memory usage of call analysis + native method detection
Should help with #306
This commit is contained in:
1 parent
9369c52ba6
commit
97493473de
11 files changed
+150
-19
No files matched your search
+4
-1
@@ -1,11 +1,14 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
# top-most EditorConfig file
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# ReSharper properties
|
||||
resharper_indent_inside_namespace = true
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[*.{cs,yml,config}]
|
||||
|
||||
@@ -19,7 +19,7 @@ public class InstructionSetIndependentInstruction : IsilOperandData
|
||||
OpCode.Validate(this);
|
||||
}
|
||||
|
||||
public override string ToString() => $"{InstructionIndex:000} {OpCode} {string.Join(", ", (IEnumerable<InstructionSetIndependentOperand>) Operands)}";
|
||||
public override string ToString() => $"{InstructionIndex:000} {OpCode} {string.Join(", ", Operands)}";
|
||||
|
||||
/// <summary>
|
||||
/// Marks the instruction as <see cref="IsilMnemonic.Invalid"/>.
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Cpp2IL.Core.Il2CppApiFunctions
|
||||
Logger.VerboseNewline(ptr == 0 ? "Not found" : $"Found at 0x{ptr:X}");
|
||||
}
|
||||
|
||||
public void Find(ApplicationAnalysisContext applicationAnalysisContext)
|
||||
public virtual void Find(ApplicationAnalysisContext applicationAnalysisContext)
|
||||
{
|
||||
_appContext = applicationAnalysisContext;
|
||||
Init(applicationAnalysisContext);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cpp2IL.Core.Logging;
|
||||
using Cpp2IL.Core.Model.Contexts;
|
||||
using Cpp2IL.Core.Utils;
|
||||
using Iced.Intel;
|
||||
using LibCpp2IL;
|
||||
@@ -24,6 +25,13 @@ namespace Cpp2IL.Core.Il2CppApiFunctions
|
||||
return _cachedDisassembledBytes;
|
||||
}
|
||||
|
||||
public override void Find(ApplicationAnalysisContext applicationAnalysisContext)
|
||||
{
|
||||
base.Find(applicationAnalysisContext);
|
||||
|
||||
_cachedDisassembledBytes = null; //Clean up once we're done finding everything
|
||||
}
|
||||
|
||||
protected override IEnumerable<ulong> FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
|
||||
{
|
||||
//Disassemble .text
|
||||
@@ -132,4 +140,4 @@ namespace Cpp2IL.Core.Il2CppApiFunctions
|
||||
return allInstructions.Count(i => i.Mnemonic == Mnemonic.Jmp || i.Mnemonic == Mnemonic.Call && i.NearBranchTarget == toWhere);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,22 @@ namespace Cpp2IL.Core.InstructionSets;
|
||||
|
||||
public class X86InstructionSet : Cpp2IlInstructionSet
|
||||
{
|
||||
private static readonly MasmFormatter Formatter = new();
|
||||
private static readonly StringOutput Output = new();
|
||||
|
||||
private static string FormatInstructionInternal(Instruction instruction)
|
||||
{
|
||||
Formatter.Format(instruction, Output);
|
||||
return Output.ToStringAndReset();
|
||||
}
|
||||
|
||||
public static string FormatInstruction(Instruction instruction)
|
||||
{
|
||||
lock (Formatter)
|
||||
{
|
||||
return FormatInstructionInternal(instruction);
|
||||
}
|
||||
}
|
||||
|
||||
public override Memory<byte> GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) => X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, isAttributeGenerator);
|
||||
|
||||
@@ -20,18 +36,19 @@ public class X86InstructionSet : Cpp2IlInstructionSet
|
||||
|
||||
public override string PrintAssembly(MethodAnalysisContext context)
|
||||
{
|
||||
var insns = X86Utils.Disassemble(X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, false), context.UnderlyingPointer);
|
||||
lock (Formatter)
|
||||
{
|
||||
var insns = X86Utils.Iterate(X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, false), context.UnderlyingPointer);
|
||||
|
||||
return string.Join("\n", insns);
|
||||
return string.Join("\n", insns.Select(FormatInstructionInternal));
|
||||
}
|
||||
}
|
||||
|
||||
public override List<InstructionSetIndependentInstruction> GetIsilFromMethod(MethodAnalysisContext context)
|
||||
{
|
||||
var insns = X86Utils.Disassemble(context.RawBytes, context.UnderlyingPointer);
|
||||
|
||||
var builder = new IsilBuilder();
|
||||
|
||||
foreach (var instruction in insns)
|
||||
foreach (var instruction in X86Utils.Iterate(context.RawBytes, context.UnderlyingPointer))
|
||||
{
|
||||
ConvertInstructionStatement(instruction, builder, context);
|
||||
}
|
||||
@@ -163,7 +180,7 @@ public class X86InstructionSet : Cpp2IlInstructionSet
|
||||
|
||||
ret++; //For MethodInfo arg
|
||||
return ret;
|
||||
}).ToArray();
|
||||
});
|
||||
|
||||
// if (parameterCounts.Max() != parameterCounts.Min())
|
||||
// throw new("Cannot handle call to address with multiple managed methods of different parameter counts");
|
||||
@@ -296,7 +313,7 @@ public class X86InstructionSet : Cpp2IlInstructionSet
|
||||
builder.Interrupt(instruction.IP); // We'll add it but eliminate later
|
||||
break;
|
||||
default:
|
||||
builder.NotImplemented(instruction.IP, instruction.ToString());
|
||||
builder.NotImplemented(instruction.IP, FormatInstruction(instruction));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,12 @@ public class MethodAnalysisContext : HasCustomAttributesAndName, IMethodInfoProv
|
||||
// ControlFlowGraph.Run();
|
||||
// InstructionSetIndependentNodes = AppContext.InstructionSet.ControlFlowGraphToISIL(ControlFlowGraph, this);
|
||||
}
|
||||
|
||||
public void ReleaseAnalysisData()
|
||||
{
|
||||
ConvertedIsil = null;
|
||||
ControlFlowGraph = null;
|
||||
}
|
||||
|
||||
public override string ToString() => $"Method: {Definition?.DeclaringType!.Name}::{Definition?.Name ?? "No definition"}";
|
||||
|
||||
|
||||
@@ -48,12 +48,13 @@ public class IsilDumpOutputFormat : Cpp2IlOutputFormat
|
||||
|
||||
try
|
||||
{
|
||||
method.Analyze();
|
||||
|
||||
typeDump.AppendLine("Disassembly:");
|
||||
typeDump.Append('\t').AppendLine(context.InstructionSet.PrintAssembly(method).Replace("\n", "\n\t"));
|
||||
|
||||
typeDump.AppendLine().AppendLine("ISIL:");
|
||||
|
||||
method.Analyze();
|
||||
|
||||
if (method.ConvertedIsil == null || method.ConvertedIsil.Count == 0)
|
||||
{
|
||||
@@ -65,6 +66,8 @@ public class IsilDumpOutputFormat : Cpp2IlOutputFormat
|
||||
{
|
||||
typeDump.Append('\t').Append(isilInsn).AppendLine();
|
||||
}
|
||||
|
||||
method.ReleaseAnalysisData();
|
||||
|
||||
typeDump.AppendLine();
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ public class CallAnalysisProcessingLayer : Cpp2IlProcessingLayer
|
||||
{
|
||||
AttributeInjectionUtils.AddZeroParameterAttribute(m, analysisNotSupportedConstructor);
|
||||
}
|
||||
m.ReleaseAnalysisData();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -124,6 +125,8 @@ public class CallAnalysisProcessingLayer : Cpp2IlProcessingLayer
|
||||
unknownCalls[m] = unknownCalls.GetOrDefault(m, 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
m.ReleaseAnalysisData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ public class NativeMethodDetectionProcessingLayer : Cpp2IlProcessingLayer
|
||||
|
||||
if (m.ConvertedIsil is { Count: 0 })
|
||||
{
|
||||
m.ReleaseAnalysisData();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,6 +73,8 @@ public class NativeMethodDetectionProcessingLayer : Cpp2IlProcessingLayer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.ReleaseAnalysisData();
|
||||
}
|
||||
|
||||
private static bool TryGetAddressFromInstruction(InstructionSetIndependentInstruction instruction, out ulong address)
|
||||
|
||||
@@ -317,5 +317,55 @@ namespace Cpp2IL.Core.Utils
|
||||
|
||||
return InvalidPathElements.Contains(input) ? $"__invalidwin32name_{input}__" : input;
|
||||
}
|
||||
|
||||
public static MemoryEnumerator<T> GetEnumerator<T>(this Memory<T> memory) => new(memory);
|
||||
|
||||
public static MemoryEnumerable<T> AsEnumerable<T>(this Memory<T> memory) => new(memory);
|
||||
|
||||
public class MemoryEnumerable<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly Memory<T> _memory;
|
||||
|
||||
public MemoryEnumerable(Memory<T> memory)
|
||||
{
|
||||
_memory = memory;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => new MemoryEnumerator<T>(_memory);
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
public class MemoryEnumerator<T> : IEnumerator<T>
|
||||
{
|
||||
private readonly Memory<T> _memory;
|
||||
|
||||
private int _index = -1;
|
||||
|
||||
public MemoryEnumerator(Memory<T> memory)
|
||||
{
|
||||
_memory = memory;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
return _index < _memory.Length;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
public T Current => _memory.Span[_index];
|
||||
|
||||
object? IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Nothing to dispose
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Cpp2IL.Core.Utils
|
||||
var decoder = Decoder.Create(LibCpp2IlMain.Binary!.is32Bit ? 32 : 64, codeReader);
|
||||
decoder.IP = methodBase;
|
||||
var instructions = new InstructionList();
|
||||
var endRip = decoder.IP + (uint) bytes.Length;
|
||||
var endRip = decoder.IP + (uint)bytes.Length;
|
||||
|
||||
while (decoder.IP < endRip)
|
||||
instructions.Add(decoder.Decode());
|
||||
@@ -34,6 +34,25 @@ namespace Cpp2IL.Core.Utils
|
||||
return instructions;
|
||||
}
|
||||
|
||||
public static IEnumerable<Instruction> Iterate(Memory<byte> bytes, ulong methodBase)
|
||||
{
|
||||
return Iterate(bytes.AsEnumerable(), methodBase);
|
||||
}
|
||||
|
||||
public static IEnumerable<Instruction> Iterate(IEnumerable<byte> bytes, ulong methodBase)
|
||||
{
|
||||
var codeReader = new EnumerableCodeReader(bytes);
|
||||
var decoder = Decoder.Create(LibCpp2IlMain.Binary!.is32Bit ? 32 : 64, codeReader);
|
||||
decoder.IP = methodBase;
|
||||
|
||||
decoder.Decode(out var instruction);
|
||||
while (!instruction.IsInvalid)
|
||||
{
|
||||
yield return instruction;
|
||||
decoder.Decode(out instruction);
|
||||
}
|
||||
}
|
||||
|
||||
public static Memory<byte> GetRawManagedOrCaCacheGenMethodBody(ulong ptr, bool isCaGen)
|
||||
{
|
||||
var rawAddr = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(ptr, false);
|
||||
@@ -80,8 +99,8 @@ namespace Cpp2IL.Core.Utils
|
||||
|
||||
while (rawArray[lastPos] == 0xCC && lastPos > rawAddr)
|
||||
lastPos--;
|
||||
var memArray = rawArray.AsMemory((int) rawAddr, (int) (lastPos - rawAddr + 1));
|
||||
|
||||
var memArray = rawArray.AsMemory((int)rawAddr, (int)(lastPos - rawAddr + 1));
|
||||
|
||||
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?
|
||||
@@ -93,8 +112,8 @@ namespace Cpp2IL.Core.Utils
|
||||
*/
|
||||
memArray = memArray.Slice(0, startIndex);
|
||||
}
|
||||
return memArray;
|
||||
|
||||
return memArray;
|
||||
}
|
||||
|
||||
private static bool TryFindJumpTableStart(Memory<byte> methodBytes, ulong methodPtr, ulong nextMethodPtr, out int startIndex, out List<ulong> jumpTableElements)
|
||||
@@ -102,9 +121,9 @@ namespace Cpp2IL.Core.Utils
|
||||
bool foundTable = false;
|
||||
startIndex = 0;
|
||||
jumpTableElements = new List<ulong>();
|
||||
for (int i = (int) (methodPtr % 4); i < methodBytes.Length; i += 4)
|
||||
for (int i = (int)(methodPtr % 4); i < methodBytes.Length; i += 4)
|
||||
{
|
||||
var result = (ulong) methodBytes.Span.ReadUInt(i);
|
||||
var result = (ulong)methodBytes.Span.ReadUInt(i);
|
||||
var possibleJumpAddress = result + 0x180000000; // image base
|
||||
if (possibleJumpAddress > methodPtr && possibleJumpAddress < nextMethodPtr)
|
||||
{
|
||||
@@ -114,6 +133,7 @@ namespace Cpp2IL.Core.Utils
|
||||
startIndex = i;
|
||||
foundTable = true;
|
||||
}
|
||||
|
||||
jumpTableElements.Add(result);
|
||||
}
|
||||
}
|
||||
@@ -144,7 +164,7 @@ namespace Cpp2IL.Core.Utils
|
||||
if (addr >= startOfNextFunc)
|
||||
break;
|
||||
|
||||
buff.Add(LibCpp2IlMain.Binary.GetByteAtRawAddress((ulong) rawAddr));
|
||||
buff.Add(LibCpp2IlMain.Binary.GetByteAtRawAddress((ulong)rawAddr));
|
||||
|
||||
ret = X86Utils.Disassemble(buff.ToArray(), functionStart);
|
||||
|
||||
@@ -229,5 +249,23 @@ namespace Cpp2IL.Core.Utils
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private class EnumerableCodeReader : CodeReader
|
||||
{
|
||||
private readonly IEnumerator<byte> _enumerator;
|
||||
|
||||
public EnumerableCodeReader(IEnumerable<byte> bytes)
|
||||
{
|
||||
_enumerator = bytes.GetEnumerator();
|
||||
}
|
||||
|
||||
public override int ReadByte()
|
||||
{
|
||||
if (_enumerator.MoveNext())
|
||||
return _enumerator.Current;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user