Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/pyvex/lifting/util/vex_helper.py: 78%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

206 statements  

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)