From e7a7b8d6f3e32df4e363ab338c71d82967d5df75 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 20 Oct 2021 14:17:16 +0100 Subject: [PATCH] Optimise arm64 analysis (4-5x speed boost) --- .../ARM64/BaseArm64ConditionalJumpAction.cs | 2 +- .../Analysis/Actions/Base/BaseAction.cs | 7 +-- Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs | 2 +- .../0RemovedUnusedLocalsPostProcessor.cs | 2 +- Cpp2IL.Core/Extensions.cs | 10 ++++ Cpp2IL.Core/Utils.cs | 52 +++++++++++++++---- 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/BaseArm64ConditionalJumpAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/BaseArm64ConditionalJumpAction.cs index 204d3ecb..da9afecf 100644 --- a/Cpp2IL.Core/Analysis/Actions/ARM64/BaseArm64ConditionalJumpAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/BaseArm64ConditionalJumpAction.cs @@ -14,7 +14,7 @@ protected BaseArm64ConditionalJumpAction(MethodAnalysis contex protected sealed override bool IsImplicitNRE() { - var body = Utils.GetArm64MethodBodyAtVirtualAddress(JumpTarget); + var body = Utils.GetArm64MethodBodyAtVirtualAddress(JumpTarget, true, 3); for (var i = 0; i < Math.Min(3, body.Count); i++) { diff --git a/Cpp2IL.Core/Analysis/Actions/Base/BaseAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/BaseAction.cs index 1b3aa45e..71d72485 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/BaseAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/BaseAction.cs @@ -14,7 +14,7 @@ public abstract class BaseAction public int IndentLevel; private List UsedLocals = new(); - private List RegisteredLocalsWithoutSideEffects = new(); + public readonly List RegisteredLocalsWithoutSideEffects = new(); protected bool is32Bit => LibCpp2IlMain.Binary!.is32Bit; @@ -40,11 +40,6 @@ protected void RegisterUsedLocal(LocalDefinition l, MethodAnalysis context) UsedLocals.Add(l); context.UnusedLocals.Remove(l); } - - public List GetRegisteredLocalsWithoutSideEffects() - { - return RegisteredLocalsWithoutSideEffects; - } protected void RegisterDefinedLocalWithoutSideEffects(LocalDefinition l) { diff --git a/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs b/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs index 356b2fd7..4242aa58 100644 --- a/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs +++ b/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs @@ -131,7 +131,7 @@ public StringBuilder BuildILToString() } catch (InvalidOperationException) { - Logger.WarnNewline($"Skipping IL Generation for {MethodDefinition}, as one of its locals, {localDefinition.Name}, has a type, {varType}, which is invalid for use in a variable.", "Analysis"); + // Logger.WarnNewline($"Skipping IL Generation for {MethodDefinition}, as one of its locals, {localDefinition.Name}, has a type, {varType}, which is invalid for use in a variable.", "Analysis"); builder.Append($"IL Generation Skipped due to invalid local {localDefinition.Name} of type {localDefinition.Type}\n\t"); success = false; break; diff --git a/Cpp2IL.Core/Analysis/PostProcessActions/0RemovedUnusedLocalsPostProcessor.cs b/Cpp2IL.Core/Analysis/PostProcessActions/0RemovedUnusedLocalsPostProcessor.cs index 6b3d540c..93e581ea 100644 --- a/Cpp2IL.Core/Analysis/PostProcessActions/0RemovedUnusedLocalsPostProcessor.cs +++ b/Cpp2IL.Core/Analysis/PostProcessActions/0RemovedUnusedLocalsPostProcessor.cs @@ -21,7 +21,7 @@ public override void PostProcess(MethodAnalysis analysis) { foreach (var analysisAction in analysis.Actions) { - if(analysisAction.GetRegisteredLocalsWithoutSideEffects().Contains(unusedLocal)) + if(analysisAction.RegisteredLocalsWithoutSideEffects.Contains(unusedLocal)) toRemove.Add(analysisAction); } } diff --git a/Cpp2IL.Core/Extensions.cs b/Cpp2IL.Core/Extensions.cs index 659ce23d..d9833727 100644 --- a/Cpp2IL.Core/Extensions.cs +++ b/Cpp2IL.Core/Extensions.cs @@ -116,6 +116,16 @@ public static void MethodSignatureFullName(this IMethodSignature self, StringBui builder.Append(")"); } + public static T[] SubArray(this T[] source, Range range) + { + var (offset, len) = range.GetOffsetAndLength(source.Length); + var dest = new T[len]; + + Array.Copy(source, offset, dest, 0, len); + + return dest; + } + [return: NotNullIfNotNull("unmanaged")] public static MethodDefinition? AsManaged(this Il2CppMethodDefinition? unmanaged) { diff --git a/Cpp2IL.Core/Utils.cs b/Cpp2IL.Core/Utils.cs index ac941c4e..e8ebe4c9 100644 --- a/Cpp2IL.Core/Utils.cs +++ b/Cpp2IL.Core/Utils.cs @@ -954,7 +954,8 @@ public static void CoerceUnknownGlobalValue(TypeReference targetType, UnknownGlo else return; - destinationConstant.Type = Type.GetType(targetType.FullName!)!; + if(destinationConstant.Type != typeof(byte[]) || targetType.IsArray) + destinationConstant.Type = Type.GetType(targetType.FullName!)!; } public static ulong GetAddressOfInstruction(T t) @@ -1009,11 +1010,41 @@ public static ulong GetAddressOfNextFunctionStart(ulong current) { if(_allKnownFunctionStarts == null) InitArm64Decompilation(); + + //Binary-search-like approach + var lower = 0; + var upper = _allKnownFunctionStarts!.Count; - return _allKnownFunctionStarts!.FirstOrDefault(a => a > current); + var ret = ulong.MaxValue; + while (upper - lower >= 1) + { + var pos = (upper - lower) / 2 + lower; + + if (upper - lower == 1) + pos = upper; + + var ptr = _allKnownFunctionStarts[pos]; + if (ptr > current) + { + //This matches what we want to look for + if (ptr < ret) + //This is a better "next method" pointer + ret = ptr; + + //Either way, we're above our current address now, so search lower in the list + upper = pos - 1; + } + else + { + //Not what we want, so move up in the list + lower = pos + 1; + } + } + + return ret; } - public static List GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true) + public static List GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1) { if(_allKnownFunctionStarts == null) InitArm64Decompilation(); @@ -1028,9 +1059,13 @@ public static List GetArm64MethodBodyAtVirtualAddress(ulong vi if (rawStartOfNextMethod < rawStart) rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength; - byte[] bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().Skip((int)rawStart).Take((int)(rawStartOfNextMethod - rawStart)).ToArray(); + byte[] bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().SubArray((int) rawStart..(int)rawStartOfNextMethod); + + var iter = _arm64Disassembler!.Iterate(bytes, (long)virtAddress); + if (count > 0) + iter = iter.Take(count); - return _arm64Disassembler!.Disassemble(bytes, (long)virtAddress).ToList(); + return iter.ToList(); } //Unmanaged function, look for first b or bl @@ -1038,15 +1073,12 @@ public static List GetArm64MethodBodyAtVirtualAddress(ulong vi var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent(); List ret = new(); - var keepGoing = true; - while (keepGoing) + while (!ret.Any(i => i.Mnemonic is "b" or "bl") && (count == -1 || ret.Count < count)) { //All arm64 instructions are 4 bytes - ret.AddRange(_arm64Disassembler!.Disassemble(allBytes.Skip(pos).Take(4).ToArray(), (long)virtAddress)); + ret.AddRange(_arm64Disassembler!.Iterate(allBytes.SubArray(pos..(pos+4)), (long)virtAddress)); virtAddress += 4; pos += 4; - - keepGoing = !ret.Any(i => i.Mnemonic is "b" or "bl"); } return ret;