about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--miasm2/core/sembuilder.py145
-rw-r--r--test/core/sembuilder.py22
-rw-r--r--test/test_all.py1
3 files changed, 168 insertions, 0 deletions
diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py
new file mode 100644
index 00000000..65e951bd
--- /dev/null
+++ b/miasm2/core/sembuilder.py
@@ -0,0 +1,145 @@
+"Helper to quickly build instruction's semantic side effects"
+
+import inspect
+import ast
+import re
+
+
+class MiasmTransformer(ast.NodeTransformer):
+    """AST visitor translating DSL to Miasm expression
+
+    memX[Y]       -> ExprMem(Y, X)
+    iX(Y)         -> ExprIntX(Y)
+    X if Y else Z -> ExprCond(Y, X, Z)
+    """
+
+    # Parsers
+    parse_integer = re.compile("^i([0-9]+)$")
+    parse_mem = re.compile("^mem([0-9]+)$")
+
+    # Visitors
+
+    def visit_Call(self, node):
+        """iX(Y) -> ExprIntX(Y)"""
+        # Match the function name
+        if not isinstance(node.func, ast.Name):
+            # TODO: launch visitor on node
+            return node
+
+        fc_name = node.func.id
+        new_name = fc_name
+        integer = self.parse_integer.search(fc_name)
+
+        # Do replacement
+        if integer is not None:
+            new_name = "ExprInt%s" % integer.groups()[0]
+
+        # Replace in the node
+        node.func.id = new_name
+        # TODO: launch visitor on node
+        return node
+
+    def visit_Subscript(self, node):
+        """memX[Y] -> ExprMem(Y, X)"""
+
+        # Detect the syntax
+        if not isinstance(node.value, ast.Name):
+            return node
+        name = node.value.id
+        mem = self.parse_mem.search(name)
+        if mem is None:
+            # TODO: launch visitor on node
+            return node
+
+        # Do replacement
+        addr = self.visit(node.slice.value)
+        call = ast.Call(func=ast.Name(id='ExprMem', ctx=ast.Load()),
+                        args=[addr, ast.Num(n=int(mem.groups()[0]))],
+                        keywords=[], starargs=None, kwargs=None)
+        return call
+
+    def visit_IfExp(self, node):
+        """X if Y else Z -> ExprCond(Y, X, Z)"""
+        call = ast.Call(func=ast.Name(id='ExprCond', ctx=ast.Load()),
+                        args=[self.visit(node.test),
+                              self.visit(node.body),
+                              self.visit(node.orelse)],
+                        keywords=[], starargs=None, kwargs=None)
+        return call
+
+
+class SemBuilder(object):
+    """Helper for building instruction's semantic side effects method
+
+    This class provides a decorator @parse to use on them.
+    The context in which the function will be parsed must be supplied on
+    instanciation
+    """
+
+    def __init__(self, ctx):
+        """Create a SemBuilder
+        @ctx: context dictionnary used during parsing
+        """
+        self.ctx = dict(ctx)
+        self.transformer = MiasmTransformer()
+
+    def parse(self, func):
+        """Function decorator, returning a correct method from a pseudo-Python
+        one"""
+
+        # Get the function AST
+        parsed = ast.parse(inspect.getsource(func))
+        fc_ast = parsed.body[0]
+        body = []
+
+        # AffBlock of the current instruction
+        new_ast = []
+        for statement in fc_ast.body:
+
+            if isinstance(statement, ast.Assign):
+                src = self.transformer.visit(statement.value)
+                dst = self.transformer.visit(statement.targets[0])
+                dst.ctx = ast.Load()
+
+                res = ast.Call(func=ast.Name(id='ExprAff',
+                                             ctx=ast.Load()),
+                               args=[dst, src],
+                               keywords=[],
+                               starargs=None,
+                               kwargs=None)
+
+                new_ast.append(res)
+
+            elif (isinstance(statement, ast.Expr) and
+                  isinstance(statement.value, ast.Str)):
+                # String (docstring, comment, ...) -> keep it
+                body.append(statement)
+            else:
+                # TODO: real var, +=, /=, -=, <<=, >>=, if/else, ...
+                raise RuntimeError("Unimplemented %s" % statement)
+
+        # Build the new function
+        fc_ast.args.args[0:0] = [ast.Name(id='ir', ctx=ast.Param()),
+                                 ast.Name(id='instr', ctx=ast.Param())]
+        body.append(ast.Return(value=ast.Tuple(elts=[ast.List(elts=new_ast,
+                                                              ctx=ast.Load()),
+                                                     ast.List(elts=[],
+                                                              ctx=ast.Load())],
+                                               ctx=ast.Load())))
+
+        ret = ast.Module([ast.FunctionDef(name=fc_ast.name,
+                                          args=fc_ast.args,
+                                          body=body,
+                                          decorator_list=[])])
+
+        # To display the generated function, use codegen.to_source
+        # codegen: https://github.com/andreif/codegen
+
+        # Compile according to the context
+        fixed = ast.fix_missing_locations(ret)
+        codeobj = compile(fixed, '<string>', 'exec')
+        ctx = self.ctx.copy()
+        eval(codeobj, ctx)
+
+        # Get the function back
+        return ctx[fc_ast.name]
diff --git a/test/core/sembuilder.py b/test/core/sembuilder.py
new file mode 100644
index 00000000..1854fc58
--- /dev/null
+++ b/test/core/sembuilder.py
@@ -0,0 +1,22 @@
+import inspect
+
+from miasm2.core.sembuilder import SemBuilder
+import miasm2.expression.expression as m2_expr
+
+sb = SemBuilder(m2_expr.__dict__)
+
+@sb.parse
+def test(Arg1, Arg2, Arg3):
+    "Test docstring"
+    Arg1 = Arg2
+    mem32[Arg1] = Arg2
+    mem32[Arg2] = Arg3  + i32(4) - mem32[Arg1]
+    Arg3 = Arg3 if Arg2 else i32(0)
+
+a = m2_expr.ExprId('A')
+b = m2_expr.ExprId('B')
+c = m2_expr.ExprId('C')
+ir = None
+instr = None
+print test(ir, instr, a, b, c)
+print test.__doc__
diff --git a/test/test_all.py b/test/test_all.py
index fd8d5ad7..9abbe85b 100644
--- a/test/test_all.py
+++ b/test/test_all.py
@@ -101,6 +101,7 @@ for script in ["interval.py",
                "graph.py",
                "parse_asm.py",
                "utils.py",
+               "sembuilder.py",
                ]:
     testset += RegressionTest([script], base_dir="core")
 ## Expression