diff --git a/claripy/operations.py b/claripy/operations.py index 217167c..5a3d108 100644 --- a/claripy/operations.py +++ b/claripy/operations.py @@ -333,6 +333,14 @@ def boolean_reverse_simplifier(body): if body.length == 8: return body + if body.op == 'Extract': + high,low = body.args[:2] + if high % 8 == 7 and low % 8 == 0: + if body.args[2].op == 'Reverse': + bits = body.args[2].args[0].size() + if bits % 8 == 0: + return body.make_like(body.op,[bits - 1 - low,bits - 1 - high,body.args[2].args[0]]) + if body.op == 'Concat': if all(a.op == 'Extract' for a in body.args): first_ast = body.args[0].args[2] @@ -350,6 +358,11 @@ def boolean_reverse_simplifier(body): if all(a.length == 8 for a in body.args): return body.make_like(body.op, body.args[::-1]) + if body.op == 'Concat': + if all(a.op == 'Reverse' or (a.op == 'BVV' and a.args[0] == 0) for a in body.args): + if all(a.length % 8 == 0 for a in body.args): + return body.make_like(body.op,reversed([a.args[0] if a.op == 'Reverse' else a for a in body.args])) + def boolean_and_simplifier(*args): if len(args) == 1: return args[0] @@ -477,6 +490,56 @@ def bitwise_sub_simplifier(a, b): elif a is b or (a == b).is_true(): return ast.all_operations.BVV(0, a.size()) +# recognize b-bit z=signedmax(q,r) from this idiom: +# s=q-r;t=q^r;u=s^q;v=u&t;w=v^s;x=rshift(w,b-1);y=x&t;z=q^y +# and recognize b-bit z=signedmin(q,r) from this idiom: +# s=r-q;t=q^r;u=s^r;v=u&t;w=v^s;x=rshift(w,b-1);y=x&t;z=q^y +def bitwise_xor_simplifier_minmax(a,b): + q,y = a,b + if y.op != '__and__': + q,y = b,a + if y.op != '__and__': return + + x,t = y.args + if t.op != '__xor__': + t,x = y.args + if t.op != '__xor__': return + + if x.op != '__rshift__': return + w,dist = x.args + + bits = a.size() + if dist is not ast.all_operations.BVV(bits - 1,bits): return + if w.op != '__xor__': return + + v,s = w.args + if s.op != '__sub__': + s,v = w.args + if s.op != '__sub__': return + + if v.op != '__and__': return + u,t2 = v.args + if t2 is not t: + t2,u = v.args + if t2 is not t: return + + if u.op != '__xor__': return + + q2,r = t.args + if q2 is not q: + r,q2 = t.args + if q2 is not q: return + + if (u.args[0] is s and u.args[1] is q) or (u.args[0] is q and u.args[1] is s): + if not (s.args[0] is q and s.args[1] is r): return + cond = ast.all_operations.SLE(q,r) + return ast.all_operations.If(cond,r,q) + + if (u.args[0] is s and u.args[1] is r) or (u.args[0] is r and u.args[1] is s): + if not (s.args[0] is r and s.args[1] is q): return + cond = ast.all_operations.SLE(q,r) + return ast.all_operations.If(cond,q,r) + def bitwise_xor_simplifier(a, b): if a is ast.all_operations.BVV(0, a.size()): return b @@ -485,6 +548,14 @@ def bitwise_xor_simplifier(a, b): elif a is b or (a == b).is_true(): return ast.all_operations.BVV(0, a.size()) + result = bitwise_xor_simplifier_minmax(a,b) + if result is not None: return result + + if a.op == 'Concat' and b.op == 'Concat': + if len(a.args) == len(b.args): + if all(x.size() == y.size() for x,y in zip(a.args,b.args)): + return ast.all_operations.Concat(*[x ^ y for x,y in zip(a.args,b.args)]) + def _flattening_filter(args): # since a ^ a == 0, we can safely remove those from args # this procedure is done carefully in order to keep the ordering of arguments @@ -520,6 +591,11 @@ def bitwise_and_simplifier(a, b): elif a is b: return a + if a.op == 'Concat' and b.op == 'Concat': + if len(a.args) == len(b.args): + if all(x.size() == y.size() for x,y in zip(a.args,b.args)): + return ast.all_operations.Concat(*[x & y for x,y in zip(a.args,b.args)]) + def _flattening_filter(args): # a & a == a return tuple(OrderedSet(args)) @@ -583,6 +659,14 @@ def extract_simplifier(high, low, val): if (val.op == 'SignExt' or val.op == 'ZeroExt') and low == 0 and high + 1 == val.args[1].size(): return val.args[1] + # (SignExt(n,v)>>s)[b-1:0] => v>>s if v has b bits + # XXX: do rshift semantics guarantee that shift distance is in range? + if val.op == '__rshift__': + if val.args[0].op == 'SignExt': + bits = val.args[0].args[1].size() + if low == 0 and high == bits - 1 and val.args[0].args[0] + bits < 2**(bits - 1): + return val.args[0].args[1].make_like(val.op,[val.args[0].args[1],val.args[1][bits-1:0]]) + if val.op == 'ZeroExt': extending_bits = val.args[0] if extending_bits == 0: