Core: Some changes to reduce memory usage of call analysis + native method detection

Should help with #306
This commit is contained in:
Sam Byass committed 2024-06-30 22:38:16 +01:00
1 parent 9369c52ba6
commit 97493473de
11 files changed
+150 -19

No files matched your search

+4 -1
View File
@@ -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)
+50
View File
@@ -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
}
}
}
}
+45 -7
View File
@@ -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;
}
}
}
}