Skip to content
Closed
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
135 changes: 2 additions & 133 deletions src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,137 +5,6 @@

namespace BenchmarkDotNet.Disassemblers
{
internal struct RegisterValueAccumulator
{
private enum State
{
LookingForPattern,
ExpectingMovk,
ExpectingAdd,
LookingForPossibleLdr
}

private State _state;
private long _value;
private int _expectedMovkShift;
private Arm64RegisterId _registerId;
private ClrRuntime _runtime;

public void Init(ClrRuntime runtime)
{
_state = State.LookingForPattern;
_expectedMovkShift = 0;
_value = 0;
_registerId = Arm64RegisterId.Invalid;
_runtime = runtime;
}

public void Feed(Arm64Instruction instruction)
{
Arm64InstructionDetail details = instruction.Details;

switch (_state)
{
case State.LookingForPattern:
if (instruction.Id == Arm64InstructionId.ARM64_INS_MOVZ)
{
_registerId = details.Operands[0].Register.Id;
_value = details.Operands[1].Immediate;
_state = State.ExpectingMovk;
_expectedMovkShift = 16;
}
else if (instruction.Id == Arm64InstructionId.ARM64_INS_ADRP)
{
_registerId = details.Operands[0].Register.Id;
_value = details.Operands[1].Immediate;
_state = State.ExpectingAdd;
}
break;
case State.ExpectingMovk:
if (instruction.Id == Arm64InstructionId.ARM64_INS_MOVK &&
details.Operands[0].Register.Id == _registerId &&
details.Operands[1].ShiftOperation == Arm64ShiftOperation.ARM64_SFT_LSL &&
details.Operands[1].ShiftValue == _expectedMovkShift)
{
_value = _value | (instruction.Details.Operands[1].Immediate << details.Operands[1].ShiftValue);
_expectedMovkShift += 16;
break;
}
_state = State.LookingForPossibleLdr;
goto case State.LookingForPossibleLdr;
case State.ExpectingAdd:
if (instruction.Id == Arm64InstructionId.ARM64_INS_ADD &&
details.Operands[0].Register.Id == _registerId &&
details.Operands[1].Register.Id == _registerId &&
details.Operands[2].Type == Arm64OperandType.Immediate)
{
_value = _value | instruction.Details.Operands[2].Immediate;
_state = State.LookingForPossibleLdr;
}
break;
case State.LookingForPossibleLdr:
if (instruction.Id == Arm64InstructionId.ARM64_INS_LDR &&
details.Operands[1].Type == Arm64OperandType.Memory &&
details.Operands[1].Memory.Base.Id == _registerId && // The source address is in the register we are tracking
details.Operands[1].Memory.Displacement == 0 && // There is no displacement
details.Operands[1].Memory.Index == null) // And there is no extra index register
{
// Simulate the LDR instruction.
long newValue = (long)_runtime.DataTarget.DataReader.ReadPointer((ulong)_value);
_value = newValue;
if (_value == 0)
{
_state = State.LookingForPattern;
}
else
{
// The LDR might have loaded the result in another register
_registerId = details.Operands[0].Register.Id;
}
}
else if (instruction.Id == Arm64InstructionId.ARM64_INS_CBZ ||
instruction.Id == Arm64InstructionId.ARM64_INS_CBNZ ||
instruction.Id == Arm64InstructionId.ARM64_INS_B && details.ConditionCode != Arm64ConditionCode.Invalid)
{
// ignore conditional branches
}
else if (details.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_BRANCH_RELATIVE) ||
details.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_CALL) ||
details.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_JUMP))
{
// We've encountered an unconditional jump or call, the accumulated registers value is not valid anymore
_state = State.LookingForPattern;
}
else if (instruction.Id == Arm64InstructionId.ARM64_INS_MOVZ)
{
// Another constant loading is starting
_state = State.LookingForPattern;
goto case State.LookingForPattern;
}
else
{
// Finally check if the current instruction modified the register that was accumulating the constant
// and reset the state machine in case it did.
foreach (Arm64Register reg in details.AllWrittenRegisters)
{
// Some unexpected instruction overwriting the accumulated register
if (reg.Id == _registerId)
{
_state = State.LookingForPattern;
}
}
}
break;
}
}

public bool HasValue => _state == State.ExpectingMovk || _state == State.LookingForPossibleLdr;

public long Value { get { return _value; } }

public Arm64RegisterId RegisterId { get { return _registerId; } }
}

internal class Arm64Disassembler : ClrMdDisassembler
{
protected override IEnumerable<Asm> Decode(byte[] code, ulong startAddress, State state, int depth, ClrMethod currentMethod, DisassemblySyntax syntax)
Expand All @@ -147,7 +16,7 @@ protected override IEnumerable<Asm> Decode(byte[] code, ulong startAddress, Stat
// disassembled binary code.
disassembler.EnableInstructionDetails = true;
disassembler.DisassembleSyntax = Map(syntax);
RegisterValueAccumulator accumulator = new RegisterValueAccumulator();
Arm64RegisterValueAccumulator accumulator = new();
accumulator.Init(state.Runtime);

Arm64Instruction[] instructions = disassembler.Disassemble(code, (long)startAddress);
Expand Down Expand Up @@ -324,7 +193,7 @@ private static bool IsLdrLiteral64(uint instr, out int rt, out int offsetBytes)
return true;
}

private static bool TryGetReferencedAddress(Arm64Instruction instruction, RegisterValueAccumulator accumulator, uint pointerSize, out ulong referencedAddress, out bool isReferencedAddressIndirect)
private static bool TryGetReferencedAddress(Arm64Instruction instruction, Arm64RegisterValueAccumulator accumulator, uint pointerSize, out ulong referencedAddress, out bool isReferencedAddressIndirect)
{
if ((instruction.Id == Arm64InstructionId.ARM64_INS_BR || instruction.Id == Arm64InstructionId.ARM64_INS_BLR) && instruction.Details.Operands[0].Register.Id == accumulator.RegisterId && accumulator.HasValue)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ internal static string Format(Arm64Asm asm, FormatterOptions formatterOptions,
FormatInstructionPointer(instruction, formatterOptions, pointerSize, output);
}

output.Append(instruction.Mnemonic.ToString().PadRight(formatterOptions.FirstOperandCharIndex));
var padRight = Math.Max(formatterOptions.FirstOperandCharIndex, instruction.Mnemonic.Length + 1);
output.Append(instruction.Mnemonic.PadRight(padRight));

if (asm.ReferencedAddress.HasValue && !asm.IsReferencedAddressIndirect && symbols.TryGetValue(asm.ReferencedAddress.Value, out var name))
{
Expand Down
135 changes: 135 additions & 0 deletions src/BenchmarkDotNet/Disassemblers/Arm64RegisterValueAccumulator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using Gee.External.Capstone.Arm64;
using Microsoft.Diagnostics.Runtime.Interfaces;

namespace BenchmarkDotNet.Disassemblers;

internal struct Arm64RegisterValueAccumulator
{
private enum State
{
LookingForPattern,
ExpectingMovk,
ExpectingAdd,
LookingForPossibleLdr
}

private State _state;
private long _value;
private int _expectedMovkShift;
private Arm64RegisterId _registerId;
private IClrRuntime _runtime;

public void Init(IClrRuntime runtime)
{
_state = State.LookingForPattern;
_expectedMovkShift = 0;
_value = 0;
_registerId = Arm64RegisterId.Invalid;
_runtime = runtime;
}

public void Feed(Arm64Instruction instruction)
{
Arm64InstructionDetail details = instruction.Details;

switch (_state)
{
case State.LookingForPattern:
if (instruction.Id == Arm64InstructionId.ARM64_INS_MOVZ)
{
_registerId = details.Operands[0].Register.Id;
_value = details.Operands[1].Immediate;
_state = State.ExpectingMovk;
_expectedMovkShift = 16;
}
else if (instruction.Id == Arm64InstructionId.ARM64_INS_ADRP)
{
_registerId = details.Operands[0].Register.Id;
_value = details.Operands[1].Immediate;
_state = State.ExpectingAdd;
}
break;
case State.ExpectingMovk:
if (instruction.Id == Arm64InstructionId.ARM64_INS_MOVK &&
details.Operands[0].Register.Id == _registerId &&
details.Operands[1].ShiftOperation == Arm64ShiftOperation.ARM64_SFT_LSL &&
details.Operands[1].ShiftValue == _expectedMovkShift)
{
_value = _value | (instruction.Details.Operands[1].Immediate << details.Operands[1].ShiftValue);
_expectedMovkShift += 16;
break;
}
_state = State.LookingForPossibleLdr;
goto case State.LookingForPossibleLdr;
case State.ExpectingAdd:
if (instruction.Id == Arm64InstructionId.ARM64_INS_ADD &&
details.Operands[0].Register.Id == _registerId &&
details.Operands[1].Register.Id == _registerId &&
details.Operands[2].Type == Arm64OperandType.Immediate)
{
_value = _value | instruction.Details.Operands[2].Immediate;
_state = State.LookingForPossibleLdr;
}
break;
case State.LookingForPossibleLdr:
if (instruction.Id == Arm64InstructionId.ARM64_INS_LDR &&
details.Operands[1].Type == Arm64OperandType.Memory &&
details.Operands[1].Memory.Base.Id == _registerId && // The source address is in the register we are tracking
details.Operands[1].Memory.Displacement == 0 && // There is no displacement
details.Operands[1].Memory.Index == null) // And there is no extra index register
{
// Simulate the LDR instruction.
long newValue = (long)_runtime.DataTarget.DataReader.ReadPointer((ulong)_value);
_value = newValue;
if (_value == 0)
{
_state = State.LookingForPattern;
}
else
{
// The LDR might have loaded the result in another register
_registerId = details.Operands[0].Register.Id;
}
}
else if (instruction.Id == Arm64InstructionId.ARM64_INS_CBZ ||
instruction.Id == Arm64InstructionId.ARM64_INS_CBNZ ||
instruction.Id == Arm64InstructionId.ARM64_INS_B && details.ConditionCode != Arm64ConditionCode.Invalid)
{
// ignore conditional branches
}
else if (details.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_BRANCH_RELATIVE) ||
details.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_CALL) ||
details.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_JUMP))
{
// We've encountered an unconditional jump or call, the accumulated registers value is not valid anymore
_state = State.LookingForPattern;
}
else if (instruction.Id == Arm64InstructionId.ARM64_INS_MOVZ)
{
// Another constant loading is starting
_state = State.LookingForPattern;
goto case State.LookingForPattern;
}
else
{
// Finally check if the current instruction modified the register that was accumulating the constant
// and reset the state machine in case it did.
foreach (Arm64Register reg in details.AllWrittenRegisters)
{
// Some unexpected instruction overwriting the accumulated register
if (reg.Id == _registerId)
{
_state = State.LookingForPattern;
}
}
}
break;
}
}

public bool HasValue => _state == State.ExpectingMovk || _state == State.LookingForPossibleLdr;

public long Value { get { return _value; } }

public Arm64RegisterId RegisterId { get { return _registerId; } }
}