From 7c96da8c7544363291f5c80d166fe97507ca9712 Mon Sep 17 00:00:00 2001 From: hakril Date: Fri, 24 Jan 2025 19:11:46 +0100 Subject: [PATCH] Working on simple_arm64.py --- tests/test_simple_arm64.py | 20 ++++++ windows/native_exec/simple_arm64.py | 105 +++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 2 deletions(-) diff --git a/tests/test_simple_arm64.py b/tests/test_simple_arm64.py index 9d4b0ed..cab9de4 100644 --- a/tests/test_simple_arm64.py +++ b/tests/test_simple_arm64.py @@ -94,6 +94,22 @@ class CheckInstr(object): else: raise ValueError("Unknow argument {0} of type {1}".format(op_args, type(op_args))) +def test_shift_parsing(): + assert Shift.parse("LSL #0") + assert Shift.parse("LSL #12") + assert Shift.parse("LSL #1") + assert Shift.parse("LSR #1") + + assert Shift.parse("ROR #0").type == "ROR" + assert Shift.parse("LSL #0").type == "LSL" + assert Shift.parse("LSL #0").value == 0 + assert Shift.parse("LSL #1").type == "LSL" + assert Shift.parse("LSL #1").value == 1 + + assert not Shift.parse("LSX #1") + assert not Shift.parse("LSX ##1") + assert not Shift.parse("LSX #") + def test_assembler(): CheckInstr(Add)('W0', 'W0', 0) CheckInstr(Add)('W1', 'W0', 0) @@ -104,11 +120,15 @@ def test_assembler(): CheckInstr(Add)('X30', 'X12', 0) CheckInstr(Add)('X0', 'X0', 1) CheckInstr(Add)('X11', 'X12', 0x123) + # CheckInstr(Add)('X11', 'X12', 0x123, "LSL #0") + CheckInstr(Add)('X11', 'X12', 0x123, "LSL #12") # Error test todo # CheckInstr(Add)('X11', 'W12', 0x123) with pytest.raises(ValueError): CheckInstr(Add)('BADREG', 'X12', 0) + with pytest.raises(ValueError): + CheckInstr(Add)('X11', 'X12', 0x123, "LSL #1234") with pytest.raises(ValueError): # Immediat too big for encoding diff --git a/windows/native_exec/simple_arm64.py b/windows/native_exec/simple_arm64.py index 4873b7b..6b53634 100644 --- a/windows/native_exec/simple_arm64.py +++ b/windows/native_exec/simple_arm64.py @@ -50,6 +50,48 @@ ALL_REGISTER = XREGISTER | WREGISTER SP = "SP" WSP = "WSP" +# Argument class +class Shift(object): + """Represent a shift parameter of an instruction. + Allow to JIT shift at instruction crafting time without string manipulation for the #XXX""" + def __init__(self, type, value): + assert isinstance(type, str) + assert isinstance(value, int) + type = type.upper() + assert type in ("LSL", "LSR", "ASR", "ROR") + self.type = type + self.value = value + + def __eq__(self, other): + if not isinstance(other, Shift): # Allow compare to tuple & iterable + return ((len(other) == 2) and + (self.type == other[0]) and + (self.value == other[1])) + return (self.type == other.type) and (self.value == other.value) + + def __repr__(self): + return """{0}(type={1}, value={2})""".format(type(self).__name__, self.type, self.value) + + @classmethod + def parse(cls, shiftstr): + if not isinstance(shiftstr, str): + return None + if not shiftstr.count(" ") == 1: + return None + stype, svalue = shiftstr.split(" ", 1) + stype = stype.upper() + if stype not in ("LSL", "LSR", "ASR", "ROR"): + return None + if len(svalue) <= 1: + return None + if not svalue.startswith("#"): + return None + try: + intvalue = int(svalue[1:]) + except ValueError: + return None + return cls(stype, intvalue) +# instruction Encoding class InstructionEncoding(object): # Sub classes can force 32/64 only instrs by setting this to 32 or 64 @@ -80,7 +122,8 @@ class InstructionEncoding(object): @classmethod def is_shift(self, arg): - return True + return (arg is None) or isinstance(arg, Shift) or Shift.parse(arg) + @classmethod def gen(cls, **encoding_array): @@ -164,7 +207,15 @@ class AddSubtractImmediate(DataProcessingImmediate): self.setup_register(self.rn, argsdict[1]) self.setup_immediat(self.imm12, argsdict[2]) - assert argsdict.get(3) is None, "SHIFT NOT IMPLEMENTED YET" + shift = Shift.parse(argsdict.get(3)) + if not shift: + return + + if shift not in [("LSL", 0), ("LSL", 12)]: + raise ValueError("Invalid shift for instruction: {0}".format(shift)) + if shift == ("LSL", 12): + import pdb;pdb.set_trace() + self.sh[:] = bytearray((1,)) @classmethod @@ -175,6 +226,33 @@ class AddSubtractImmediate(DataProcessingImmediate): cls.is_shift(argsdict.get(3))) +class MovWideImmediat(DataProcessingImmediate): + def __init__(self, argsdict): + super(MovWideImmediat, self).__init__() + self.sf = self.bits[31:32] + self.opc = self.bits[29:31] + self.bits[23:29] = bytearray(reversed((1, 0, 0, 1, 0, 1))) + self.hw = self.bits[21:23] + self.imm16 = self.bits[5:21] + self.rd = self.bits[0:5] + + + self.setup_fixed_values() + # Change instruction based of parameter + self.setup_register(self.rd, argsdict[0]) + self.setup_immediat(self.imm16, argsdict[1]) + + assert argsdict.get(3) is None, "SHIFT NOT IMPLEMENTED YET" + + + @classmethod + def accept_arg(cls, argsdict): + return (cls.is_register(argsdict[0], accept_sp=True) and + cls.is_imm12(argsdict[1]) and + cls.is_shift(argsdict.get(2))) + + + ### C4.1.94.13 Unconditional branch (register) class UnconditionalBranchRegister(InstructionEncoding): @@ -209,6 +287,24 @@ class RetEncoding(UnconditionalBranchRegister.gen(opc=0b10, op2=0b11111, op3=0, def accept_arg(cls, argsdict): return not argsdict or cls.is_register(argsdict[0], accept_sp=True) + +# C4.1.95 Data Processing – Register + +class DataProcessingRegister(InstructionEncoding): + def __init__(self): + super(DataProcessingRegister, self).__init__() + self.bits[26:29] = bytearray((0,0,1)) + self.op0 = self.bits[30:31] + self.op1 = self.bits[28:29] + self.bits[25:28] = bytearray(reversed((1, 0, 1))) + self.op2 = self.bits[21:25] + self.op3 = self.bits[10:16] + +# An instruction is a Name that can have multiple encoding +# It's the class we instanciate to assemble instructions +# Add X0, X0, IMM +# Add X0, X0, X0 + class Instruction(object): encoding = [] @@ -248,6 +344,11 @@ class Subs(Instruction): class Ret(Instruction): encoding = [RetEncoding] +# C6.2.254 + +class MovZ(Instruction): + encoding = [MovWideImmediat.gen(opc=0b10)] + class MultipleInstr(object):