Skip to content

Commit

Permalink
Optimise arm64 analysis (4-5x speed boost)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Byass committed Oct 20, 2021
1 parent 5bed6ee commit e7a7b8d
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ protected BaseArm64ConditionalJumpAction(MethodAnalysis<Arm64Instruction> 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++)
{
Expand Down
7 changes: 1 addition & 6 deletions Cpp2IL.Core/Analysis/Actions/Base/BaseAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public abstract class BaseAction<T>
public int IndentLevel;

private List<LocalDefinition> UsedLocals = new();
private List<LocalDefinition> RegisteredLocalsWithoutSideEffects = new();
public readonly List<LocalDefinition> RegisteredLocalsWithoutSideEffects = new();

protected bool is32Bit => LibCpp2IlMain.Binary!.is32Bit;

Expand All @@ -40,11 +40,6 @@ protected void RegisterUsedLocal(LocalDefinition l, MethodAnalysis<T> context)
UsedLocals.Add(l);
context.UnusedLocals.Remove(l);
}

public List<LocalDefinition> GetRegisteredLocalsWithoutSideEffects()
{
return RegisteredLocalsWithoutSideEffects;
}

protected void RegisterDefinedLocalWithoutSideEffects(LocalDefinition l)
{
Expand Down
2 changes: 1 addition & 1 deletion Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public override void PostProcess(MethodAnalysis<T> analysis)
{
foreach (var analysisAction in analysis.Actions)
{
if(analysisAction.GetRegisteredLocalsWithoutSideEffects().Contains(unusedLocal))
if(analysisAction.RegisteredLocalsWithoutSideEffects.Contains(unusedLocal))
toRemove.Add(analysisAction);
}
}
Expand Down
10 changes: 10 additions & 0 deletions Cpp2IL.Core/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ public static void MethodSignatureFullName(this IMethodSignature self, StringBui
builder.Append(")");
}

public static T[] SubArray<T>(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)
{
Expand Down
52 changes: 42 additions & 10 deletions Cpp2IL.Core/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 t)
Expand Down Expand Up @@ -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<Arm64Instruction> GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true)
public static List<Arm64Instruction> GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1)
{
if(_allKnownFunctionStarts == null)
InitArm64Decompilation();
Expand All @@ -1028,25 +1059,26 @@ public static List<Arm64Instruction> 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
var pos = (int) LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtAddress);
var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent();
List<Arm64Instruction> 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;
Expand Down

0 comments on commit e7a7b8d

Please sign in to comment.