1import copy
2import re
3
4from pyvex.const import U1, get_type_size, ty_to_const_class, vex_int_class
5from pyvex.enums import IRCallee
6from pyvex.expr import ITE, Binop, CCall, Const, Get, Load, RdTmp, Unop
7from pyvex.stmt import Dirty, Exit, IMark, NoOp, Put, Store, WrTmp
8
9
10class JumpKind:
11 Boring = "Ijk_Boring"
12 Call = "Ijk_Call"
13 Ret = "Ijk_Ret"
14 Segfault = "Ijk_SigSEGV"
15 Exit = "Ijk_Exit"
16 Syscall = "Ijk_Sys_syscall"
17 Sysenter = "Ijk_Sys_sysenter"
18 Invalid = "Ijk_INVALID"
19 NoDecode = "Ijk_NoDecode"
20
21
22class TypeMeta(type):
23 typemeta_re = re.compile(r"int_(?P<size>\d+)$")
24
25 def __getattr__(self, name):
26 match = self.typemeta_re.match(name)
27 if match:
28 width = int(match.group("size"))
29 return vex_int_class(width).type
30 else:
31 return type.__getattr__(name)
32
33
34class Type(metaclass=TypeMeta):
35 __metaclass__ = TypeMeta
36
37 ieee_float_16 = "Ity_F16"
38 ieee_float_32 = "Ity_F32"
39 ieee_float_64 = "Ity_F64"
40 ieee_float_128 = "Ity_F128"
41 decimal_float_32 = "Ity_D32"
42 decimal_float_64 = "Ity_D64"
43 decimal_float_128 = "Ity_D128"
44 simd_vector_128 = "Ity_V128"
45 simd_vector_256 = "Ity_V256"
46
47
48def get_op_format_from_const_ty(ty):
49 return ty_to_const_class(ty).op_format
50
51
52def make_format_op_generator(fmt_string):
53 """
54 Return a function which generates an op format (just a string of the vex instruction)
55
56 Functions by formatting the fmt_string with the types of the arguments
57 """
58
59 def gen(arg_types):
60 converted_arg_types = list(map(get_op_format_from_const_ty, arg_types))
61 op = fmt_string.format(arg_t=converted_arg_types)
62 return op
63
64 return gen
65
66
67def mkbinop(fstring):
68 return lambda self, expr_a, expr_b: self.op_binary(make_format_op_generator(fstring))(expr_a, expr_b)
69
70
71def mkunop(fstring):
72 return lambda self, expr_a: self.op_unary(make_format_op_generator(fstring))(expr_a)
73
74
75def mkcmpop(fstring_fragment, signedness=""):
76 def cmpop(self, expr_a, expr_b):
77 ty = self.get_type(expr_a)
78 fstring = f"Iop_Cmp{fstring_fragment}{{arg_t[0]}}{signedness}"
79 retval = mkbinop(fstring)(self, expr_a, expr_b)
80 return self.cast_to(retval, ty)
81
82 return cmpop
83
84
85class IRSBCustomizer:
86 op_add = mkbinop("Iop_Add{arg_t[0]}")
87 op_sub = mkbinop("Iop_Sub{arg_t[0]}")
88 op_umul = mkbinop("Iop_Mul{arg_t[0]}")
89 op_smul = mkbinop("Iop_MullS{arg_t[0]}")
90 op_sdiv = mkbinop("Iop_DivS{arg_t[0]}")
91 op_udiv = mkbinop("Iop_DivU{arg_t[0]}")
92
93 # Custom operation that does not exist in libVEX
94 op_mod = mkbinop("Iop_Mod{arg_t[0]}")
95
96 op_or = mkbinop("Iop_Or{arg_t[0]}")
97 op_and = mkbinop("Iop_And{arg_t[0]}")
98 op_xor = mkbinop("Iop_Xor{arg_t[0]}")
99
100 op_shr = mkbinop("Iop_Shr{arg_t[0]}") # Shift Right (logical)
101 op_shl = mkbinop("Iop_Shl{arg_t[0]}") # Shift Left (logical)
102
103 op_sar = mkbinop("Iop_Sar{arg_t[0]}") # Shift Arithmetic Right operation
104
105 op_not = mkunop("Iop_Not{arg_t[0]}")
106
107 op_cmp_eq = mkcmpop("EQ")
108 op_cmp_ne = mkcmpop("NE")
109 op_cmp_slt = mkcmpop("LT", "S")
110 op_cmp_sle = mkcmpop("LE", "S")
111 op_cmp_ult = mkcmpop("LT", "U")
112 op_cmp_ule = mkcmpop("LE", "U")
113 op_cmp_sge = mkcmpop("GE", "S")
114 op_cmp_uge = mkcmpop("GE", "U")
115 op_cmp_sgt = mkcmpop("GT", "S")
116 op_cmp_ugt = mkcmpop("GT", "U")
117
118 def __init__(self, irsb):
119 self.arch = irsb.arch
120 self.irsb = irsb
121
122 def get_type(self, rdt):
123 return rdt.result_type(self.irsb.tyenv)
124
125 # Statements (no return value)
126 def _append_stmt(self, stmt):
127 self.irsb.statements += [stmt]
128
129 def imark(self, int_addr, int_length, int_delta=0):
130 self._append_stmt(IMark(int_addr, int_length, int_delta))
131
132 def get_reg(self, regname): # TODO move this into the lifter
133 return self.arch.registers[regname][0]
134
135 def put(self, expr_val, tuple_reg):
136 self._append_stmt(Put(copy.copy(expr_val), tuple_reg))
137
138 def store(self, addr, expr):
139 self._append_stmt(Store(copy.copy(addr), copy.copy(expr), self.arch.memory_endness))
140
141 def noop(self):
142 self._append_stmt(NoOp())
143
144 def add_exit(self, guard, dst, jk, ip):
145 """
146 Add an exit out of the middle of an IRSB.
147 (e.g., a conditional jump)
148 :param guard: An expression, the exit is taken if true
149 :param dst: the destination of the exit (a Const)
150 :param jk: the JumpKind of this exit (probably Ijk_Boring)
151 :param ip: The address of this exit's source
152 """
153 self.irsb.statements.append(Exit(guard, dst.con, jk, ip))
154
155 # end statements
156
157 def goto(self, addr):
158 self.irsb.next = addr
159 self.irsb.jumpkind = JumpKind.Boring
160
161 def ret(self, addr):
162 self.irsb.next = addr
163 self.irsb.jumpkind = JumpKind.Ret
164
165 def call(self, addr):
166 self.irsb.next = addr
167 self.irsb.jumpkind = JumpKind.Call
168
169 def _add_tmp(self, t):
170 return self.irsb.tyenv.add(t)
171
172 def _rdtmp(self, tmp):
173 return RdTmp.get_instance(tmp)
174
175 def _settmp(self, expr):
176 ty = self.get_type(expr)
177 tmp = self._add_tmp(ty)
178 self._append_stmt(WrTmp(tmp, expr))
179 return self._rdtmp(tmp)
180
181 def rdreg(self, reg, ty):
182 return self._settmp(Get(reg, ty))
183
184 def load(self, addr, ty):
185 return self._settmp(Load(self.arch.memory_endness, ty, copy.copy(addr)))
186
187 def op_ccall(self, retty, funcstr, args):
188 return self._settmp(CCall(retty, IRCallee(len(args), funcstr, 0xFFFF), args))
189
190 def dirty(self, retty, funcstr, args):
191 if retty is None:
192 tmp = 0xFFFFFFFF
193 else:
194 tmp = self._add_tmp(retty)
195 self._append_stmt(Dirty(IRCallee(len(args), funcstr, 0xFFFF), Const(U1(1)), args, tmp, None, None, None, None))
196 return self._rdtmp(tmp)
197
198 def ite(self, condrdt, iftruerdt, iffalserdt):
199 return self._settmp(ITE(copy.copy(condrdt), copy.copy(iffalserdt), copy.copy(iftruerdt)))
200
201 def mkconst(self, val, ty):
202 cls = ty_to_const_class(ty)
203 return Const(cls(val))
204
205 # Operations
206 def op_generic(self, Operation, op_generator):
207 def instance(*args): # Note: The args here are all RdTmps
208 for arg in args:
209 assert isinstance(arg, RdTmp) or isinstance(arg, Const)
210 arg_types = [self.get_type(arg) for arg in args]
211 # two operations should never share the same argument instances, copy them here to ensure that
212 args = [copy.copy(a) for a in args]
213 op = Operation(op_generator(arg_types), args)
214 msg = "operation needs to be well typed: " + str(op)
215 assert op.typecheck(self.irsb.tyenv), msg + "\ntypes: " + str(self.irsb.tyenv)
216 return self._settmp(op)
217
218 return instance
219
220 def op_binary(self, op_format_str):
221 return self.op_generic(Binop, op_format_str)
222
223 def op_unary(self, op_format_str):
224 return self.op_generic(Unop, op_format_str)
225
226 def cast_to(self, rdt, tydest, signed=False, high=False):
227 goalwidth = get_type_size(tydest)
228 rdtwidth = self.get_rdt_width(rdt)
229
230 if rdtwidth > goalwidth:
231 return self.op_narrow_int(rdt, tydest, high_half=high)
232 elif rdtwidth < goalwidth:
233 return self.op_widen_int(rdt, tydest, signed=signed)
234 else:
235 return rdt
236
237 def op_to_one_bit(self, rdt):
238 rdtty = self.get_type(rdt)
239 if rdtty not in [Type.int_64, Type.int_32]:
240 rdt = self.op_widen_int_unsigned(rdt, Type.int_32)
241 onebit = self.op_narrow_int(rdt, Type.int_1)
242 return onebit
243
244 def op_narrow_int(self, rdt, tydest, high_half=False):
245 op_name = "{op}{high}to{dest}".format(
246 op="Iop_{arg_t[0]}", high="HI" if high_half else "", dest=get_op_format_from_const_ty(tydest)
247 )
248 return self.op_unary(make_format_op_generator(op_name))(rdt)
249
250 def op_widen_int(self, rdt, tydest, signed=False):
251 op_name = "{op}{sign}to{dest}".format(
252 op="Iop_{arg_t[0]}", sign="S" if signed else "U", dest=get_op_format_from_const_ty(tydest)
253 )
254 return self.op_unary(make_format_op_generator(op_name))(rdt)
255
256 def op_widen_int_signed(self, rdt, tydest):
257 return self.op_widen_int(rdt, tydest, signed=True)
258
259 def op_widen_int_unsigned(self, rdt, tydest):
260 return self.op_widen_int(rdt, tydest, signed=False)
261
262 def get_msb(self, tmp, ty):
263 width = get_type_size(ty)
264 return self.get_bit(tmp, width - 1)
265
266 def get_bit(self, rdt, idx):
267 shifted = self.op_shr(rdt, idx)
268 bit = self.op_extract_lsb(shifted)
269 return bit
270
271 def op_extract_lsb(self, rdt):
272 bitmask = self.mkconst(1, self.get_type(rdt))
273 return self.op_and(bitmask, rdt)
274
275 def set_bit(self, rdt, idx, bval):
276 currbit = self.get_bit(rdt, idx)
277 areequalextrabits = self.op_xor(bval, currbit)
278 one = self.mkconst(1, self.get_type(areequalextrabits))
279 areequal = self.op_and(areequalextrabits, one)
280 shifted = self.op_shl(areequal, idx)
281 return self.op_xor(rdt, shifted)
282
283 def set_bits(self, rdt, idxsandvals):
284 ty = self.get_type(rdt)
285 if all([isinstance(idx, Const) for idx, _ in idxsandvals]):
286 relevantbits = self.mkconst(sum([1 << idx.con.value for idx, _ in idxsandvals]), ty)
287 else:
288 relevantbits = self.mkconst(0, ty)
289 for idx, _ in idxsandvals:
290 shifted = self.op_shl(self.mkconst(1, ty), idx)
291 relevantbits = self.op_or(relevantbits, shifted)
292 setto = self.mkconst(0, ty)
293 for idx, bval in idxsandvals:
294 bvalbit = self.op_extract_lsb(bval)
295 shifted = self.op_shl(bvalbit, idx)
296 setto = self.op_or(setto, shifted)
297 shouldflip = self.op_and(self.op_xor(setto, rdt), relevantbits)
298 return self.op_xor(rdt, shouldflip)
299
300 def get_rdt_width(self, rdt):
301 return rdt.result_size(self.irsb.tyenv)