about summary refs log tree commit diff stats
path: root/miasm/jitter/jitcore_python.py
blob: 269df97360e6bcdf587600040d8d57d3580696d0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
from __future__ import print_function
from builtins import zip
import miasm.jitter.jitcore as jitcore
from miasm.expression.expression import ExprInt, ExprLoc
import miasm.jitter.csts as csts
from miasm.expression.simplifications import expr_simp_explicit
from miasm.jitter.emulatedsymbexec import EmulatedSymbExec

################################################################################
#                              Python jitter Core                              #
################################################################################


class JitCore_Python(jitcore.JitCore):
    "JiT management, using Miasm2 Symbol Execution engine as backend"

    SymbExecClass = EmulatedSymbExec

    def __init__(self, lifter, bin_stream):
        super(JitCore_Python, self).__init__(lifter, bin_stream)
        self.lifter = lifter
        self.ircfg = self.lifter.new_ircfg()

        # CPU & VM (None for now) will be set later

        self.symbexec = self.SymbExecClass(
            None, None,
            self.lifter, {},
            sb_expr_simp=expr_simp_explicit
        )
        self.symbexec.enable_emulated_simplifications()

    def set_cpu_vm(self, cpu, vm):
        self.symbexec.cpu = cpu
        self.symbexec.vm = vm

    def load(self):
        "Preload symbols according to current architecture"
        self.symbexec.reset_regs()

    def arch_specific(self):
        """Return arch specific information for the current architecture"""
        arch = self.lifter.arch
        has_delayslot = False
        if arch.name == "mips32":
            from miasm.arch.mips32.jit import mipsCGen
            cgen_class = mipsCGen
            has_delayslot = True
        elif arch.name == "arm":
            from miasm.arch.arm.jit import arm_CGen
            cgen_class = arm_CGen
        else:
            from miasm.jitter.codegen import CGen
            cgen_class = CGen
        return cgen_class(self.lifter), has_delayslot

    def add_block(self, asmblock):
        """Create a python function corresponding to an AsmBlock
        @asmblock: AsmBlock
        """

        # TODO: merge duplicate code with CGen, llvmconvert
        codegen, has_delayslot = self.arch_specific()
        irblocks_list = codegen.block2assignblks(asmblock)
        instr_offsets = [line.offset for line in asmblock.lines]

        loc_db = self.lifter.loc_db
        local_loc_keys = []
        for irblocks in irblocks_list:
            for irblock in irblocks:
                local_loc_keys.append(irblock.loc_key)

        def myfunc(cpu):
            """Execute the function according to cpu and vmmngr states
            @cpu: JitCpu instance
            """
            # Get virtual memory handler
            vmmngr = cpu.vmmngr

            # Get execution engine (EmulatedSymbExec instance)
            exec_engine = self.symbexec

            # Refresh CPU values according to @cpu instance
            exec_engine.update_engine_from_cpu()

            # Get initial loc_key
            cur_loc_key = asmblock.loc_key

            # Update PC helper
            update_pc = lambda value: setattr(cpu, self.lifter.pc.name, value)

            while True:
                # Retrieve the expected irblock
                for instr, irblocks in zip(asmblock.lines, irblocks_list):
                    for index, irblock in enumerate(irblocks):
                        if irblock.loc_key == cur_loc_key:
                            break
                    else:
                        continue
                    break
                else:
                    raise RuntimeError("Unable to find the block for %r" % cur_loc_key)

                instr_attrib, irblocks_attributes = codegen.get_attributes(
                    instr, irblocks, self.log_mn, self.log_regs
                )
                irblock_attributes = irblocks_attributes[index]

                # Do IRBlock
                new_irblock = self.lifter.irbloc_fix_regs_for_mode(
                    irblock, self.lifter.attrib
                )
                if index == 0:
                    # Pre code
                    if instr_attrib.log_mn:
                        print("%.8X %s" % (
                            instr_attrib.instr.offset,
                            instr_attrib.instr.to_string(loc_db)
                        ))

                # Exec IRBlock
                instr = instr_attrib.instr

                for index, assignblk in enumerate(irblock):
                    attributes = irblock_attributes[index]

                    # Eval current instruction (in IR)
                    exec_engine.eval_updt_assignblk(assignblk)

                    # Check memory access / write exception
                    # TODO: insert a check between memory reads and writes
                    if attributes.mem_read or attributes.mem_write:
                        # Restricted exception
                        flag = ~csts.EXCEPT_CODE_AUTOMOD & csts.EXCEPT_DO_NOT_UPDATE_PC
                        if (vmmngr.get_exception() & flag != 0):
                            # Do not update registers
                            update_pc(instr.offset)
                            return instr.offset

                    # Update registers values
                    exec_engine.update_cpu_from_engine()

                    # Check post assignblk exception flags
                    if attributes.set_exception:
                        # Restricted exception
                        if cpu.get_exception() > csts.EXCEPT_NUM_UPDT_EIP:
                            # Update PC
                            update_pc(instr.offset)
                            return instr.offset

                dst = exec_engine.eval_expr(self.lifter.IRDst)
                if dst.is_int():
                    loc_key = loc_db.get_or_create_offset_location(int(dst))
                    dst = ExprLoc(loc_key, dst.size)

                assert dst.is_loc()
                loc_key = dst.loc_key
                offset = loc_db.get_location_offset(loc_key)
                if offset is None:
                    # Avoid checks on generated label
                    cur_loc_key = loc_key
                    continue

                if instr_attrib.log_regs:
                    update_pc(offset)
                    cpu.dump_gpregs_with_attrib(self.lifter.attrib)

                # Post-instr checks
                if instr_attrib.mem_read | instr_attrib.mem_write:
                    vmmngr.check_memory_breakpoint()
                    vmmngr.check_invalid_code_blocs()
                    if vmmngr.get_exception():
                        update_pc(offset)
                        return offset

                if instr_attrib.set_exception:
                    if cpu.get_exception():
                        update_pc(offset)
                        return offset

                if instr_attrib.mem_read | instr_attrib.mem_write:
                    vmmngr.reset_memory_access()

                # Manage resulting address
                if (loc_key in local_loc_keys and
                    offset > instr.offset):
                    # Forward local jump
                    # Note: a backward local jump has to be promoted to extern,
                    # for max_exec_per_call support
                    cur_loc_key = loc_key
                    continue

                # Delay slot
                if has_delayslot:
                    delay_slot_set = exec_engine.eval_expr(codegen.delay_slot_set)
                    if delay_slot_set.is_int() and int(delay_slot_set) != 0:
                        return int(exec_engine.eval_expr(codegen.delay_slot_dst))

                # Extern of asmblock, must have an offset
                assert offset is not None
                return offset

        # Associate myfunc with current loc_key
        offset = loc_db.get_location_offset(asmblock.loc_key)
        assert offset is not None
        self.offset_to_jitted_func[offset] = myfunc

    def exec_wrapper(self, loc_key, cpu, _offset_to_jitted_func, _stop_offsets,
                     _max_exec_per_call):
        """Call the function @loc_key with @cpu
        @loc_key: function's loc_key
        @cpu: JitCpu instance
        """

        # Get Python function corresponding to @loc_key
        fc_ptr = self.offset_to_jitted_func[loc_key]

        # Execute the function
        return fc_ptr(cpu)