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)
|