Skip to content

AArch64

Mahdi Safsafi edited this page Aug 24, 2017 · 1 revision

Quick start

The example bellow shows how to use Azote to decode opcode.

var
  Insn:TA64Instruction;
begin
  FillChar(Insn, SizeOf(TA64Instruction), #00);
  Insn.OpCode := OpCode;
  if(DecodeInstruction(Insn))then
  begin
    // Do something with Insn here.
  end;
end;

Before calling DecodeInstruction function, it's necessary to initialize the Insn structure to 0. After initialization, you set your opcode that you want to decode and optionally set decoding options. After decoding opcode, Insn will be filled with valid information that applies to that opcode.

Instruction fields

TA64Instruction = record

    /// <summary>Instruction identifier (ID).</summary>
    IID: TA64InstructionIID;

    /// <summary>Instruction opcode value.</summary>
    OpCode: UInt32;

    /// <summary>Instruction conditional execution.</summary>
    Condition: TA64Condition;

    /// <summary>Instruction class.</summary>
    IClass: TA64Class;

    /// <summary>Instruction syntax.</summary>
    Syntax: AnsiString;

    /// <summary>Number of operand.</summary>
    OperandCount: Integer;

    /// <summary>A list of operand used by the instruction.</summary>
    Operands: array [0 .. { OperandCount } A64_MAX_OPERANDS - 1] of TA64Operand;

    Options: TAzoteOptions;
    /// <summary>Storable field.</summary>
    /// <para>This field is only used during the process.
    /// You can store here what you want.
    /// </para>
    case Boolean of
      True: (UserTag: NativeInt);
      False: (Internal: Pointer);
  end;
  • IID: Instruction ID that allows to us to distinguish between instructions.
if(Insn.IID = INSN_ADD) then
  // It's add instruction.
else if(Insn.IID = INSN_MOV) then
 // It's mov instruction.
// and so on ...
  • OpCode: The opcode that you want to decode.
  • Condition: Instruction conditional execution.
if (Insn.Condition = cdEQ) then
  // The instruction executes only when (Z = 1).
  • IClass: Instruction class.
  • Syntax: Instruction syntax (string).
  • OperandCount: Number of operand used by the instruction.
  • Operands: An array [0 .. OperandCount -1] of operand representing instruction arguments.
  • Options: Options for decoding.
    • optLittleEndian: By default, Azote assumes that OpCode you provided is Big-Endian. If your opcode's endianness is Little-Endian, you need to specify that by including optLittleEndian in Insn.Options.
  • UserTag: Here's your space, you can store what you want here.

Operand fields

You should carefully access operand info. Because they are structured in union. Only access info if OperandType and Flags indicate that info is valid.

TA64Operand = record
    /// <summary>Argument Index.</summary>
    ID: Integer;

    /// <summary>Argument flags.</summary>
    Flags: TOperandFlags;

    /// <summary>Argument type.</summary>
    /// <para>Accessing operand info depends on OperandType and Flags.</para>
    OperandType: TOperandType;
    case TOperandType { OperandType } of
      otList: (List: TA64ListInfo);
      otCondition: (Condition: TA64Condition);
      otRegister: (Register: TA64Register);
      otLabel: (RelOffset: Int64);
      otMemory: (Memory: TA64MemoryInfo);
      otHint: (Hint: TA64HintOption);
      otImmediate: (Immediate: TA64ImmediateInfo;
          case TOperandFlag { Flags } of
            ofArrangement: (Arrangement: TA64Arrangement; Index: Integer);
            ofShifter: (Shifter: TA64Shifter; Amount: Integer);
          );
  end;
  • ID: Operand index.
  • Flags: Operand flags. It could be a combination of:
    • ofShifter: Operand has a shifter. Shifter and Amount info are valid.
    • ofArrangement: Operand has arrangement. Arrangement info is valid. And Index is valid only if(Index > -1).
    • ofDestination: Operand will be modified after executing the instruction.
  • OperandType: indicates operand type (register, immediate, list, memory, ...).

Examples

Finding all 'Add/Sub' instructions.

{ This example demonstrates how to find all 'Add/Sub' instructions }

const
  OpCodes: array [0 .. 7] of UInt32 = (
    $8B348331, // add x17, x25, w20, sxtb.
    $B203295F, // orr sp, x10, #0xe00000ffe00000ff.
    $0B33A23A, // add w26, w17, w19, sxth.
    $CB250882, // sub x2, x4, w5, uxtb #2.
    $CB3333F4, // sub x20, sp, w19, uxth #4.
    $8B23C05F, // add sp, x2, w3, sxtw.
    $4B23C05F, // sub wsp, w2, w3, sxtw.
    $AB3333FF // cmn sp, w19, uxth #4.
    );

var
  Insn: TA64Instruction;
  I: Integer;
begin
  try
    Writeln('Add/Sub Instructions:');
    for I := 0 to Length(OpCodes) - 1 do
    begin
      FillChar(Insn, SizeOf(TA64Instruction), #00);
      Insn.OpCode := OpCodes[I];
      if (DecodeInstruction(Insn)) then
      begin
        if (Insn.IID = INSN_ADD) or (Insn.IID = INSN_SUB) then
        begin
          Writeln(Insn.Syntax);
        end;
      end;
    end;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

Output:

Add/Sub Instructions:
add x17, x25, w20, sxtb
add w26, w17, w19, sxth
sub x2, x4, w5, uxtb #2
sub x20, sp, w19, uxth #4
add sp, x2, w3, sxtw
sub wsp, w2, w3, sxtw

Finding all Instructions that modify stack pointer.

{ This example demonstrates how to find all instructions
  that may change the stack pointer register value. }

const
  OpCodes: array [0 .. 7] of UInt32 = (
    $8B348331, // add x17, x25, w20, sxtb.
    $B203295F, // orr sp, x10, #0xe00000ffe00000ff.
    $0B33A23A, // add w26, w17, w19, sxth.
    $CB250882, // sub x2, x4, w5, uxtb #2.
    $CB3333F4, // sub x20, sp, w19, uxth #4.
    $8B23C05F, // add sp, x2, w3, sxtw.
    $4B23C05F, // sub wsp, w2, w3, sxtw.
    $AB3333FF // cmn sp, w19, uxth #4.
    );

var
  Insn: TA64Instruction;
  I: Integer;
  J: Integer;
  Operand: TA64Operand;

begin
  try
    Writeln('Instructions that modify stack pointer register are:');
    for I := 0 to Length(OpCodes) - 1 do
    begin
      FillChar(Insn, SizeOf(TA64Instruction), #00);
      Insn.OpCode := OpCodes[I];
      if (DecodeInstruction(Insn)) then
      begin
        { Iterate through instruction operands. }
        for J := 0 to Insn.OperandCount - 1 do
        begin
          Operand := Insn.Operands[J];
          if ((Operand.OperandType = otRegister) and (ofDestination in Operand.Flags { register is not source. } ) and
            ((Operand.Register = REG_SP) or (Operand.Register = REG_WSP))) then
          begin
            Writeln(Insn.Syntax);
            Break;
          end;
        end;
      end;
    end;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

Output:

Instructions that modify stack pointer register are:
orr sp, x10, #-2305841910238936833
add sp, x2, w3, sxtw
sub wsp, w2, w3, sxtw