about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorwoni <81616747+W0ni@users.noreply.github.com>2023-09-06 18:19:56 +0200
committerwoni <81616747+W0ni@users.noreply.github.com>2023-09-06 18:19:56 +0200
commit18a93f4049f6f9a72eb52aaeac13571f7b18c41a (patch)
tree7cb990363ecb45af4c11914e6527f5e8cc4e18bd
parente9dee4eb5e8b8dc0b81e22598b754c8df29f1674 (diff)
downloadmiasm-18a93f4049f6f9a72eb52aaeac13571f7b18c41a.tar.gz
miasm-18a93f4049f6f9a72eb52aaeac13571f7b18c41a.zip
Add shifters for instructions that use shifter carry
-rw-r--r--miasm/arch/arm/sem.py230
1 files changed, 154 insertions, 76 deletions
diff --git a/miasm/arch/arm/sem.py b/miasm/arch/arm/sem.py
index e507a045..0c623475 100644
--- a/miasm/arch/arm/sem.py
+++ b/miasm/arch/arm/sem.py
@@ -2,6 +2,7 @@ from builtins import range
 from future.utils import viewitems, viewvalues
 
 from miasm.expression.expression import *
+from miasm.expression.simplifications import expr_simp
 from miasm.ir.ir import Lifter, IRBlock, AssignBlock
 from miasm.arch.arm.arch import mn_arm, mn_armt
 from miasm.arch.arm.regs import *
@@ -253,6 +254,15 @@ def update_flag_zn(a):
     return e
 
 
+# Instructions which use shifter's carry flag: ANDS, BICS, EORS, MOVS/RRX, MVNS, ORNS (TODO), ORRS, TEQ, TST
+def compute_rrx_carry(operation):
+    """
+    Returns a tuple (result, carry) corresponding to the RRX computation
+    @operation: The ExprOp operation
+    """
+    new_cf = operation.args[0][:1]
+    res = ExprCompose(operation.args[0][1:], cf)
+    return res, new_cf
 
 # XXX TODO: set cf if ROT imm in argument
 
@@ -272,12 +282,12 @@ def update_flag_add_of(op1, op2):
 
 
 def update_flag_sub_cf(op1, op2):
-    "Compote CF in @op1 - @op2"
+    "Compute CF in @op1 - @op2"
     return [ExprAssign(cf, ExprOp("FLAG_SUB_CF", op1, op2) ^ ExprInt(1, 1))]
 
 
 def update_flag_sub_of(op1, op2):
-    "Compote OF in @op1 - @op2"
+    "Compute OF in @op1 - @op2"
     return [ExprAssign(of, ExprOp("FLAG_SUB_OF", op1, op2))]
 
 
@@ -424,11 +434,18 @@ def add(ir, instr, a, b, c=None):
 
 
 def l_and(ir, instr, a, b, c=None):
-    e = []
+    setflags = (instr.name == 'ANDS') and a != PC
     if c is None:
         b, c = a, b
+    if c.is_op():
+        e, extra_ir = _shift_rotate_tpl(ir, instr, a, c, onlyCarry=setflags)
+        # get back the result
+        c = e.pop(0).src
+    else:
+        e = []
+        extra_ir = []
     r = b & c
-    if instr.name == 'ANDS' and a != PC:
+    if setflags:
         e += [ExprAssign(zf, ExprOp('FLAG_EQ_AND', b, c))]
         e += update_flag_nf(r)
 
@@ -436,8 +453,7 @@ def l_and(ir, instr, a, b, c=None):
     dst = get_dst(a)
     if dst is not None:
         e.append(ExprAssign(ir.IRDst, r))
-    return e, []
-
+    return e, extra_ir
 
 def sub(ir, instr, a, b, c=None):
     e = []
@@ -479,9 +495,16 @@ def eor(ir, instr, a, b, c=None):
 
 
 def eors(ir, instr, a, b, c=None):
-    e = []
+    setflags = a != PC
     if c is None:
         b, c = a, b
+    if c.is_op():
+        e, extra_ir = _shift_rotate_tpl(ir, instr, a, c, onlyCarry=setflags)
+        # get back the result
+        c = e.pop(0).src
+    else:
+        e = []
+        extra_ir = []
     arg1, arg2 = b, c
     r = arg1 ^ arg2
 
@@ -492,7 +515,7 @@ def eors(ir, instr, a, b, c=None):
     dst = get_dst(a)
     if dst is not None:
         e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return e, extra_ir
 
 
 def rsb(ir, instr, a, b, c=None):
@@ -585,18 +608,32 @@ def rscs(ir, instr, a, b, c=None):
 
 
 def tst(ir, instr, a, b):
-    e = []
+    setflags = a != PC
+    if b.is_op():
+        e, extra_ir = _shift_rotate_tpl(ir, instr, a, b, onlyCarry=setflags)
+        # get back the result
+        b = e.pop(0).src
+    else:
+        e = []
+        extra_ir = []
     arg1, arg2 = a, b
     r = arg1 & arg2
 
     e += [ExprAssign(zf, ExprOp('FLAG_EQ_AND', arg1, arg2))]
     e += update_flag_nf(r)
 
-    return e, []
+    return e, extra_ir
 
 
 def teq(ir, instr, a, b, c=None):
-    e = []
+    setflags = a != PC
+    if b.is_op():
+        e, extra_ir = _shift_rotate_tpl(ir, instr, a, b, onlyCarry=setflags)
+        # get back the result
+        b = e.pop(0).src
+    else:
+        e = []
+        extra_ir = []
     if c is None:
         b, c = a, b
     arg1, arg2 = b, c
@@ -605,7 +642,7 @@ def teq(ir, instr, a, b, c=None):
     e += [ExprAssign(zf, ExprOp('FLAG_EQ_CMP', arg1, arg2))]
     e += update_flag_nf(r)
 
-    return e, []
+    return e, extra_ir
 
 
 def l_cmp(ir, instr, a, b, c=None):
@@ -653,9 +690,16 @@ def orn(ir, instr, a, b, c=None):
 
 
 def orrs(ir, instr, a, b, c=None):
-    e = []
+    setflags = a != PC
     if c is None:
         b, c = a, b
+    if c.is_op():
+        e, extra_ir = _shift_rotate_tpl(ir, instr, a, c, onlyCarry=setflags)
+        # get back the result
+        c = e.pop(0).src
+    else:
+        e = []
+        extra_ir = []
     arg1, arg2 = b, c
     r = arg1 | arg2
 
@@ -666,10 +710,13 @@ def orrs(ir, instr, a, b, c=None):
     dst = get_dst(a)
     if dst is not None:
         e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return e, extra_ir
 
 
 def mov(ir, instr, a, b):
+    if b.is_op():
+        return _shift_rotate_tpl(ir, instr, a, b, setflags=False)
+    # TODO handle cf
     e = [ExprAssign(a, b)]
     dst = get_dst(a)
     if dst is not None:
@@ -686,10 +733,78 @@ def movt(ir, instr, a, b):
     return e, []
 
 
+def _shift_rotate_tpl(ir, instr, dst, shift_operation, setflags=False, is_not=False, onlyCarry=False):
+    """
+    Template to generate a shift/rotate
+    A temporary basic block is generated to handle 0-shift
+    @dst: destination
+    @shift_operation: the shift/rotate operation (ExprOp)
+    @setflags: (optional) if set, flags are updated (ZNC)
+    @onlyCarry: (optional) if set, Z and N flags won't be updated except if setflags is set.
+    @is_not: (optional) if set, behaves as MVN/MVNS
+    """
+    op = shift_operation.op
+    # Compute carry (+ result for rrx)
+    if op == 'rrx':
+        res, new_cf = compute_rrx_carry(shift_operation)
+        shifter = ExprInt(1, 8)
+    elif op in ['<<', '>>', 'a>>']:
+        shifter = shift_operation.args[1]
+        if setflags or onlyCarry:
+            new_cf = ExprOp(op, shift_operation.args[0], shifter - ExprInt(1, size=shifter.size))
+            left = op[-1] == '<'
+            new_cf = new_cf.msb() if left else new_cf[:1]
+        res = shift_operation
+    elif op == '>>>':
+        shifter = shift_operation.args[1]
+        if setflags or onlyCarry:
+            new_cf = shift_operation.msb()
+        res = shift_operation
+    else:
+        raise NotImplementedError(f"Unknown shift / rotate operation : {op}")
+
+    # NOT the result and use it for ZN flags computations
+    if is_not:
+        res ^= ExprInt(-1, res.size)
+    # Build basic blocks
+    e_do = []
+    e = [ExprAssign(dst, res)]
+    if setflags:
+        e += update_flag_zn(res)
+    if setflags or onlyCarry:
+        e_do += [ExprAssign(cf, expr_simp(new_cf))]
+    # Don't generate conditional shifter on constant
+    if shifter.is_int():
+        if shifter.is_int(0):
+            # assignement + flags if setflags except cf
+            return (e, [])
+        else:
+            # assignement + flags if setflags
+            return (e + e_do, [])
+
+    loc_do, loc_do_expr = ir.gen_loc_key_and_expr(ir.IRDst.size)
+    loc_skip = ir.get_next_loc_key(instr)
+    loc_skip_expr = ExprLoc(loc_skip, ir.IRDst.size)
+    isPC = get_dst(dst)
+    if isPC is not None:
+        # Not really a Loc in this case
+        loc_skip_expr = res
+    e_do.append(ExprAssign(ir.IRDst, loc_skip_expr))
+    e.append(ExprAssign(
+        ir.IRDst, ExprCond(shifter, loc_do_expr, loc_skip_expr)))
+    return (e, [IRBlock(ir.loc_db, loc_do, [AssignBlock(e_do, instr)])])
+
+
+
 def movs(ir, instr, a, b):
     e = []
+    # handle shift / rotate
+    if b.is_op():
+        return _shift_rotate_tpl(ir, instr, a, b, setflags=a != PC)
+
+
     e.append(ExprAssign(a, b))
-    # XXX TODO check
+    # TODO handle cf
     e += [ExprAssign(zf, ExprOp('FLAG_EQ', b))]
     e += update_flag_nf(b)
 
@@ -700,7 +815,10 @@ def movs(ir, instr, a, b):
 
 
 def mvn(ir, instr, a, b):
+    if b.is_op():
+        return _shift_rotate_tpl(ir, instr, a, b, setflags=False, is_not=True)
     r = b ^ ExprInt(-1, 32)
+    # TODO handle cf
     e = [ExprAssign(a, r)]
     dst = get_dst(a)
     if dst is not None:
@@ -709,10 +827,12 @@ def mvn(ir, instr, a, b):
 
 
 def mvns(ir, instr, a, b):
+    if b.is_op():
+        return _shift_rotate_tpl(ir, instr, a, b, setflags= a != PC, is_not=True)
     e = []
     r = b ^ ExprInt(-1, 32)
     e.append(ExprAssign(a, r))
-    # XXX TODO check
+    # TODO handle cf
     e += [ExprAssign(zf, ExprOp('FLAG_EQ', r))]
     e += update_flag_nf(r)
 
@@ -774,9 +894,16 @@ def bic(ir, instr, a, b, c=None):
 
 
 def bics(ir, instr, a, b, c=None):
-    e = []
+    setflags = a != PC
     if c is None:
         b, c = a, b
+    if c.is_op():
+        e, extra_ir = _shift_rotate_tpl(ir, instr, a, c, onlyCarry=setflags)
+        # get back the result
+        c = e.pop(0).src
+    else:
+        e = []
+        extra_ir = []
     tmp1, tmp2 = b, ~c
     r = tmp1 & tmp2
 
@@ -787,7 +914,7 @@ def bics(ir, instr, a, b, c=None):
     dst = get_dst(a)
     if dst is not None:
         e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return e, extra_ir
 
 
 def sdiv(ir, instr, a, b, c=None):
@@ -1169,100 +1296,53 @@ def und(ir, instr, a, b):
     e = []
     return e, []
 
-# TODO XXX implement correct CF for shifters
 def lsr(ir, instr, a, b, c=None):
     e = []
     if c is None:
         b, c = a, b
-    r = b >> c
-    e.append(ExprAssign(a, r))
-    dst = get_dst(a)
-    if dst is not None:
-        e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return _shift_rotate_tpl(ir, instr, a, b >> c, setflags=False)
 
 
 def lsrs(ir, instr, a, b, c=None):
     e = []
     if c is None:
         b, c = a, b
-    r = b >> c
-    e.append(ExprAssign(a, r))
-
-    e += [ExprAssign(zf, ExprOp('FLAG_EQ', r))]
-    e += update_flag_nf(r)
-
-    dst = get_dst(a)
-    if dst is not None:
-        e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return _shift_rotate_tpl(ir, instr, a, b >> c, setflags= a != PC)
 
 def asr(ir, instr, a, b, c=None):
     e = []
     if c is None:
         b, c = a, b
     r = ExprOp("a>>", b, c)
-    e.append(ExprAssign(a, r))
-    dst = get_dst(a)
-    if dst is not None:
-        e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return _shift_rotate_tpl(ir, instr, a, r, setflags=False)
 
 def asrs(ir, instr, a, b, c=None):
     e = []
     if c is None:
         b, c = a, b
     r = ExprOp("a>>", b, c)
-    e.append(ExprAssign(a, r))
-
-    e += [ExprAssign(zf, ExprOp('FLAG_EQ', r))]
-    e += update_flag_nf(r)
-
-    dst = get_dst(a)
-    if dst is not None:
-        e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return _shift_rotate_tpl(ir, instr, a, r, setflags= a != PC)
 
 def lsl(ir, instr, a, b, c=None):
     e = []
     if c is None:
         b, c = a, b
-    r = b << c
-    e.append(ExprAssign(a, r))
-    dst = get_dst(a)
-    if dst is not None:
-        e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return _shift_rotate_tpl(ir, instr, a, b << c, setflags=False)
+
 
 
 def lsls(ir, instr, a, b, c=None):
     e = []
     if c is None:
         b, c = a, b
-    r = b << c
-    e.append(ExprAssign(a, r))
-
-    e += [ExprAssign(zf, ExprOp('FLAG_EQ', r))]
-    e += update_flag_nf(r)
-
-    dst = get_dst(a)
-    if dst is not None:
-        e.append(ExprAssign(ir.IRDst, r))
-    return e, []
+    return _shift_rotate_tpl(ir, instr, a, b << c, setflags= a != PC)
 
 
 def rors(ir, instr, a, b):
     e = []
     r = ExprOp(">>>", a, b)
-    e.append(ExprAssign(a, r))
-
-    e += [ExprAssign(zf, ExprOp('FLAG_EQ', r))]
-    e += update_flag_nf(r)
+    return _shift_rotate_tpl(ir, instr, a, r, setflags= a != PC)
 
-    dst = get_dst(a)
-    if dst is not None:
-        e.append(ExprAssign(ir.IRDst, r))
-    return e, []
 
 
 def push(ir, instr, a):
@@ -1962,9 +2042,7 @@ class Lifter_Arml(Lifter):
         args = instr.args
         # ir = get_mnemo_expr(self, self.name.lower(), *args)
         if len(args) and isinstance(args[-1], ExprOp):
-            if args[-1].op == 'rrx':
-                args[-1] = ExprCompose(args[-1].args[0][1:], cf)
-            elif (args[-1].op in ['<<', '>>', '<<a', 'a>>', '<<<', '>>>'] and
+            if (args[-1].op in ['<<', '>>', '<<a', 'a>>', '<<<', '>>>'] and
                   isinstance(args[-1].args[-1], ExprId)):
                 args[-1] = ExprOp(args[-1].op,
                                   args[-1].args[0],