Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pyvex/expr.py: 72%

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

614 statements  

1from __future__ import annotations 

2 

3import logging 

4import re 

5from typing import TYPE_CHECKING 

6 

7from .const import U8, U16, U32, U64, IRConst, get_type_size 

8from .enums import IRCallee, IRRegArray, VEXObject, get_enum_from_int, get_int_from_enum 

9from .errors import PyVEXError 

10from .native import ffi, pvc 

11 

12if TYPE_CHECKING: 

13 from .block import IRTypeEnv 

14 

15log = logging.getLogger("pyvex.expr") 

16 

17 

18class IRExpr(VEXObject): 

19 """ 

20 IR expressions in VEX represent operations without side effects. 

21 """ 

22 

23 __slots__ = [] 

24 

25 tag: str | None = None 

26 tag_int = 0 # set automatically at bottom of file 

27 

28 def pp(self): 

29 print(str(self)) 

30 

31 def __str__(self): 

32 return self._pp_str() 

33 

34 def _pp_str(self) -> str: 

35 raise NotImplementedError 

36 

37 @property 

38 def child_expressions(self) -> list[IRExpr]: 

39 """ 

40 A list of all of the expressions that this expression ends up evaluating. 

41 """ 

42 expressions = [] 

43 for k in self.__slots__: 

44 v = getattr(self, k) 

45 if isinstance(v, IRExpr): 

46 expressions.append(v) 

47 expressions.extend(v.child_expressions) 

48 return expressions 

49 

50 @property 

51 def constants(self): 

52 """ 

53 A list of all of the constants that this expression ends up using. 

54 """ 

55 constants = [] 

56 for k in self.__slots__: 

57 v = getattr(self, k) 

58 if isinstance(v, IRExpr): 

59 constants.extend(v.constants) 

60 elif isinstance(v, IRConst): 

61 constants.append(v) 

62 return constants 

63 

64 def result_size(self, tyenv: IRTypeEnv): 

65 return get_type_size(self.result_type(tyenv)) 

66 

67 def result_type(self, tyenv: IRTypeEnv): 

68 raise NotImplementedError() 

69 

70 def replace_expression(self, replacements): 

71 """ 

72 Replace child expressions in-place. 

73 

74 :param Dict[IRExpr, IRExpr] replacements: A mapping from expression-to-find to expression-to-replace-with 

75 :return: None 

76 """ 

77 

78 for k in self.__slots__: 

79 v = getattr(self, k) 

80 if isinstance(v, IRExpr) and v in replacements: 

81 setattr(self, k, replacements.get(v)) 

82 elif isinstance(v, list): 

83 # Replace the instance in the list 

84 for i, expr_ in enumerate(v): 

85 if isinstance(expr_, IRExpr) and expr_ in replacements: 

86 v[i] = replacements.get(expr_) 

87 elif type(v) is tuple: 

88 # Rebuild the tuple 

89 _lst = [] 

90 replaced = False 

91 for i, expr_ in enumerate(v): 

92 if isinstance(expr_, IRExpr) and expr_ in replacements: 

93 _lst.append(replacements.get(expr_)) 

94 replaced = True 

95 else: 

96 _lst.append(expr_) 

97 if replaced: 

98 setattr(self, k, tuple(_lst)) 

99 elif isinstance(v, IRExpr): 

100 v.replace_expression(replacements) 

101 

102 @staticmethod 

103 def _from_c(c_expr) -> IRExpr | None: 

104 if c_expr == ffi.NULL or c_expr[0] == ffi.NULL: 

105 return None 

106 

107 try: 

108 return enum_to_expr_class(c_expr.tag)._from_c(c_expr) 

109 except KeyError: 

110 raise PyVEXError("Unknown/unsupported IRExprTag %s\n" % get_enum_from_int(c_expr.tag)) 

111 

112 _translate = _from_c 

113 

114 @staticmethod 

115 def _to_c(expr): 

116 try: 

117 return tag_to_expr_class(expr.tag)._to_c(expr) 

118 except KeyError: 

119 raise PyVEXError("Unknown/unsupported IRExprTag %s\n" % expr.tag) 

120 

121 def typecheck(self, tyenv): 

122 return self.result_type(tyenv) 

123 

124 

125class Binder(IRExpr): 

126 """ 

127 Used only in pattern matching within Vex. Should not be seen outside of Vex. 

128 """ 

129 

130 __slots__ = ["binder"] 

131 

132 tag = "Iex_Binder" 

133 

134 def __init__(self, binder): 

135 self.binder = binder 

136 

137 def _pp_str(self): 

138 return "Binder" 

139 

140 @staticmethod 

141 def _from_c(c_expr): 

142 return Binder(c_expr.iex.Binder.binder) 

143 

144 @staticmethod 

145 def _to_c(expr): 

146 return pvc.IRExpr_Binder(expr.binder) 

147 

148 def result_type(self, tyenv): 

149 return "Ity_INVALID" 

150 

151 

152class VECRET(IRExpr): 

153 tag = "Iex_VECRET" 

154 

155 __slots__ = [] 

156 

157 def _pp_str(self): 

158 return "VECRET" 

159 

160 @staticmethod 

161 def _from_c(c_expr): 

162 return VECRET() 

163 

164 @staticmethod 

165 def _to_c(expr): 

166 return pvc.IRExpr_VECRET() 

167 

168 def result_type(self, tyenv): 

169 return "Ity_INVALID" 

170 

171 

172class GSPTR(IRExpr): 

173 __slots__ = [] 

174 

175 tag = "Iex_GSPTR" 

176 

177 def _pp_str(self): 

178 return "GSPTR" 

179 

180 @staticmethod 

181 def _from_c(c_expr): 

182 return GSPTR() 

183 

184 @staticmethod 

185 def _to_c(expr): 

186 return pvc.IRExpr_GSPTR() 

187 

188 def result_type(self, tyenv): 

189 return "Ity_INVALID" 

190 

191 

192class GetI(IRExpr): 

193 """ 

194 Read a guest register at a non-fixed offset in the guest state. 

195 """ 

196 

197 __slots__ = ["descr", "ix", "bias"] 

198 

199 tag = "Iex_GetI" 

200 

201 def __init__(self, descr, ix, bias): 

202 self.descr = descr 

203 self.ix = ix 

204 self.bias = bias 

205 

206 @property 

207 def description(self): 

208 return self.descr 

209 

210 @property 

211 def index(self): 

212 return self.ix 

213 

214 def _pp_str(self): 

215 return f"GetI({self.descr})[{self.ix},{self.bias}]" 

216 

217 @staticmethod 

218 def _from_c(c_expr): 

219 descr = IRRegArray._from_c(c_expr.Iex.GetI.descr) 

220 ix = IRExpr._from_c(c_expr.Iex.GetI.ix) 

221 bias = c_expr.Iex.GetI.bias 

222 return GetI(descr, ix, bias) 

223 

224 @staticmethod 

225 def _to_c(expr): 

226 return pvc.IRExpr_GetI(IRRegArray._to_c(expr.descr), IRExpr._to_c(expr.ix), expr.bias) 

227 

228 def result_type(self, tyenv): 

229 return self.descr.elemTy 

230 

231 

232class RdTmp(IRExpr): 

233 """ 

234 Read the value held by a temporary. 

235 """ 

236 

237 __slots__ = ["_tmp"] 

238 

239 tag = "Iex_RdTmp" 

240 

241 def __init__(self, tmp): 

242 self._tmp = tmp 

243 

244 def _pp_str(self): 

245 return "t%d" % self.tmp 

246 

247 @property 

248 def tmp(self): 

249 return self._tmp 

250 

251 @staticmethod 

252 def _from_c(c_expr): 

253 tmp = c_expr.Iex.RdTmp.tmp 

254 return RdTmp.get_instance(tmp) 

255 

256 @staticmethod 

257 def _to_c(expr): 

258 return pvc.IRExpr_RdTmp(expr.tmp) 

259 

260 @staticmethod 

261 def get_instance(tmp): 

262 if tmp < 1024: 

263 # for small tmp reads, they are cached and are only created once globally 

264 return _RDTMP_POOL[tmp] 

265 return RdTmp(tmp) 

266 

267 def replace_expression(self, replacements): 

268 # RdTmp is one of the terminal IRExprs, which cannot be replaced. 

269 pass 

270 

271 def result_type(self, tyenv): 

272 return tyenv.lookup(self.tmp) 

273 

274 def __hash__(self): 

275 return 133700 + self._tmp 

276 

277 

278_RDTMP_POOL = list(RdTmp(i) for i in range(0, 1024)) 

279 

280 

281class Get(IRExpr): 

282 """ 

283 Read a guest register, at a fixed offset in the guest state. 

284 """ 

285 

286 __slots__ = ["offset", "ty_int"] 

287 

288 tag = "Iex_Get" 

289 

290 def __init__(self, offset: int, ty: str, ty_int: int | None = None): 

291 self.offset = offset 

292 if ty_int is None: 

293 self.ty_int = get_int_from_enum(ty) 

294 else: 

295 self.ty_int = ty_int 

296 

297 @property 

298 def ty(self): 

299 return get_enum_from_int(self.ty_int) 

300 

301 @property 

302 def type(self): 

303 return get_enum_from_int(self.ty_int) 

304 

305 def _pp_str(self): 

306 return f"GET:{self.ty[4:]}(offset={self.offset})" 

307 

308 def pp_str_with_name(self, reg_name: str): 

309 """pp_str_with_name is used to print the expression with the name of the 

310 register instead of the offset""" 

311 return f"GET:{self.ty[4:]}({reg_name})" 

312 

313 @staticmethod 

314 def _from_c(c_expr): 

315 return Get(c_expr.Iex.Get.offset, get_enum_from_int(c_expr.Iex.Get.ty)) 

316 

317 @staticmethod 

318 def _to_c(expr): 

319 return pvc.IRExpr_Get(expr.offset, expr.ty_int) 

320 

321 def result_type(self, tyenv): 

322 return self.ty 

323 

324 def __hash__(self): 

325 return (self.offset << 8) | self.ty_int 

326 

327 

328class Qop(IRExpr): 

329 """ 

330 A quaternary operation (4 arguments). 

331 """ 

332 

333 __slots__ = ["op", "args"] 

334 

335 tag = "Iex_Qop" 

336 

337 def __init__(self, op, args): 

338 self.op = op 

339 self.args = args 

340 

341 def _pp_str(self): 

342 return "{}({})".format(self.op[4:], ",".join(str(a) for a in self.args)) 

343 

344 @property 

345 def child_expressions(self): 

346 expressions = sum((a.child_expressions for a in self.args), []) 

347 expressions.extend(self.args) 

348 return expressions 

349 

350 @staticmethod 

351 def _from_c(c_expr): 

352 return Qop( 

353 get_enum_from_int(c_expr.Iex.Qop.details.op), 

354 [ 

355 IRExpr._from_c(arg) 

356 for arg in [ 

357 c_expr.Iex.Qop.details.arg1, 

358 c_expr.Iex.Qop.details.arg2, 

359 c_expr.Iex.Qop.details.arg3, 

360 c_expr.Iex.Qop.details.arg4, 

361 ] 

362 ], 

363 ) 

364 

365 @staticmethod 

366 def _to_c(expr): 

367 return pvc.IRExpr_Qop(get_int_from_enum(expr.op), *[IRExpr._to_c(arg) for arg in expr.args]) 

368 

369 def result_type(self, tyenv): 

370 return get_op_retty(self.op) 

371 

372 def typecheck(self, tyenv): # TODO change all this to use PyvexTypeErrorException 

373 resty, (arg1ty, arg2ty, arg3ty, arg4ty) = op_arg_types(self.op) 

374 arg1ty_real = self.args[0].typecheck(tyenv) 

375 arg2ty_real = self.args[1].typecheck(tyenv) 

376 arg3ty_real = self.args[2].typecheck(tyenv) 

377 arg4ty_real = self.args[3].typecheck(tyenv) 

378 if arg1ty_real is None or arg2ty_real is None or arg3ty_real is None or arg4ty_real is None: 

379 return None 

380 

381 if arg1ty_real != arg1ty: 

382 log.debug("First arg of %s must be %s", self.op, arg1ty) 

383 return None 

384 if arg2ty_real != arg2ty: 

385 log.debug("Second arg of %s must be %s", self.op, arg2ty) 

386 return None 

387 if arg3ty_real != arg3ty: 

388 log.debug("Third arg of %s must be %s", self.op, arg3ty) 

389 return None 

390 if arg4ty_real != arg4ty: 

391 log.debug("Fourth arg of %s must be %s", self.op, arg4ty) 

392 return None 

393 

394 return resty 

395 

396 

397class Triop(IRExpr): 

398 """ 

399 A ternary operation (3 arguments) 

400 """ 

401 

402 __slots__ = ["op", "args"] 

403 

404 tag = "Iex_Triop" 

405 

406 def __init__(self, op, args): 

407 self.op = op 

408 self.args = args 

409 

410 def _pp_str(self): 

411 return "{}({})".format(self.op[4:], ",".join(str(a) for a in self.args)) 

412 

413 @property 

414 def child_expressions(self): 

415 expressions = sum((a.child_expressions for a in self.args), []) 

416 expressions.extend(self.args) 

417 return expressions 

418 

419 @staticmethod 

420 def _from_c(c_expr): 

421 return Triop( 

422 get_enum_from_int(c_expr.Iex.Triop.details.op), 

423 [ 

424 IRExpr._from_c(arg) 

425 for arg in [c_expr.Iex.Triop.details.arg1, c_expr.Iex.Triop.details.arg2, c_expr.Iex.Triop.details.arg3] 

426 ], 

427 ) 

428 

429 @staticmethod 

430 def _to_c(expr): 

431 return pvc.IRExpr_Triop(get_int_from_enum(expr.op), *[IRExpr._to_c(arg) for arg in expr.args]) 

432 

433 def result_type(self, tyenv): 

434 return get_op_retty(self.op) 

435 

436 def typecheck(self, tyenv): 

437 resty, (arg1ty, arg2ty, arg3ty) = op_arg_types(self.op) 

438 arg1ty_real = self.args[0].typecheck(tyenv) 

439 arg2ty_real = self.args[1].typecheck(tyenv) 

440 arg3ty_real = self.args[2].typecheck(tyenv) 

441 if arg1ty_real is None or arg2ty_real is None or arg3ty_real is None: 

442 return None 

443 

444 if arg1ty_real != arg1ty: 

445 log.debug("First arg of %s must be %s", self.op, arg1ty) 

446 return None 

447 if arg2ty_real != arg2ty: 

448 log.debug("Second arg of %s must be %s", self.op, arg2ty) 

449 return None 

450 if arg3ty_real != arg3ty: 

451 log.debug("Third arg of %s must be %s", self.op, arg3ty) 

452 return None 

453 

454 return resty 

455 

456 

457class Binop(IRExpr): 

458 """ 

459 A binary operation (2 arguments). 

460 """ 

461 

462 __slots__ = ["_op", "op_int", "args"] 

463 

464 tag = "Iex_Binop" 

465 

466 def __init__(self, op, args, op_int=None): 

467 self.op_int = op_int 

468 self.args = args 

469 self._op = op if op is not None else None 

470 

471 def _pp_str(self): 

472 return "{}({})".format(self.op[4:], ",".join(str(a) for a in self.args)) 

473 

474 @property 

475 def op(self): 

476 if self._op is None: 

477 self._op = get_enum_from_int(self.op_int) 

478 return self._op 

479 

480 @property 

481 def child_expressions(self): 

482 expressions = sum((a.child_expressions for a in self.args), []) 

483 expressions.extend(self.args) 

484 return expressions 

485 

486 @staticmethod 

487 def _from_c(c_expr): 

488 return Binop( 

489 None, 

490 [IRExpr._from_c(arg) for arg in [c_expr.Iex.Binop.arg1, c_expr.Iex.Binop.arg2]], 

491 op_int=c_expr.Iex.Binop.op, 

492 ) 

493 

494 @staticmethod 

495 def _to_c(expr): 

496 return pvc.IRExpr_Binop(get_int_from_enum(expr.op), *[IRExpr._to_c(arg) for arg in expr.args]) 

497 

498 def result_type(self, tyenv): 

499 return get_op_retty(self.op) 

500 

501 def typecheck(self, tyenv): 

502 arg1ty_real = self.args[0].typecheck(tyenv) 

503 arg2ty_real = self.args[1].typecheck(tyenv) 

504 

505 resty, (arg1ty, arg2ty) = op_arg_types(self.op) 

506 if arg1ty_real is None or arg2ty_real is None: 

507 return None 

508 

509 if arg1ty_real != arg1ty: 

510 log.debug("First arg of %s must be %s", self.op, arg1ty) 

511 return None 

512 if arg2ty_real != arg2ty: 

513 log.debug("Second arg of %s must be %s", self.op, arg2ty) 

514 return None 

515 

516 return resty 

517 

518 

519class Unop(IRExpr): 

520 """ 

521 A unary operation (1 argument). 

522 """ 

523 

524 __slots__ = ["op", "args"] 

525 

526 tag = "Iex_Unop" 

527 

528 def __init__(self, op: str, args: list[IRExpr]): 

529 self.op = op 

530 self.args = args 

531 

532 def _pp_str(self): 

533 return "{}({})".format(self.op[4:], ",".join(str(a) for a in self.args)) 

534 

535 @property 

536 def child_expressions(self): 

537 expressions = sum((a.child_expressions for a in self.args), []) 

538 expressions.extend(self.args) 

539 return expressions 

540 

541 @staticmethod 

542 def _from_c(c_expr): 

543 return Unop(get_enum_from_int(c_expr.Iex.Unop.op), [IRExpr._from_c(c_expr.Iex.Unop.arg)]) 

544 

545 @staticmethod 

546 def _to_c(expr): 

547 return pvc.IRExpr_Unop(get_int_from_enum(expr.op), IRExpr._to_c(expr.args[0])) 

548 

549 def result_type(self, tyenv): 

550 return get_op_retty(self.op) 

551 

552 def typecheck(self, tyenv): 

553 resty, (arg1ty,) = op_arg_types(self.op) 

554 arg1ty_real = self.args[0].typecheck(tyenv) 

555 if arg1ty_real is None: 

556 return None 

557 

558 if arg1ty_real != arg1ty: 

559 log.debug("First arg of %s must be %s", self.op, arg1ty) 

560 return None 

561 

562 return resty 

563 

564 

565class Load(IRExpr): 

566 """ 

567 A load from memory. 

568 """ 

569 

570 __slots__ = ["end", "ty", "addr"] 

571 

572 tag = "Iex_Load" 

573 

574 def __init__(self, end, ty, addr): 

575 self.end = end 

576 self.ty = ty 

577 self.addr = addr 

578 

579 @property 

580 def endness(self): 

581 return self.end 

582 

583 @property 

584 def type(self): 

585 return self.ty 

586 

587 def _pp_str(self): 

588 return f"LD{self.end[-2:].lower()}:{self.ty[4:]}({self.addr})" 

589 

590 @staticmethod 

591 def _from_c(c_expr): 

592 return Load( 

593 get_enum_from_int(c_expr.Iex.Load.end), 

594 get_enum_from_int(c_expr.Iex.Load.ty), 

595 IRExpr._from_c(c_expr.Iex.Load.addr), 

596 ) 

597 

598 @staticmethod 

599 def _to_c(expr): 

600 return pvc.IRExpr_Load(get_int_from_enum(expr.end), get_int_from_enum(expr.ty), IRExpr._to_c(expr.addr)) 

601 

602 def result_type(self, tyenv): 

603 return self.ty 

604 

605 def typecheck(self, tyenv): 

606 addrty = self.addr.typecheck(tyenv) 

607 if addrty is None: 

608 return None 

609 if addrty != tyenv.wordty: 

610 log.debug("Address must be word-sized") 

611 return None 

612 return self.ty 

613 

614 

615class Const(IRExpr): 

616 """ 

617 A constant expression. 

618 """ 

619 

620 __slots__ = ["_con"] 

621 

622 tag = "Iex_Const" 

623 

624 def __init__(self, con: IRConst): 

625 self._con = con 

626 

627 def _pp_str(self): 

628 return str(self.con) 

629 

630 @property 

631 def con(self) -> IRConst: 

632 return self._con 

633 

634 @staticmethod 

635 def _from_c(c_expr): 

636 con = IRConst._from_c(c_expr.Iex.Const.con) 

637 return Const.get_instance(con) 

638 

639 @staticmethod 

640 def _to_c(expr): 

641 return pvc.IRExpr_Const(IRConst._to_c(expr.con)) 

642 

643 @staticmethod 

644 def get_instance(con): 

645 if con.value < 1024 and con.__class__ in _CONST_POOL: 

646 return _CONST_POOL[con.__class__][con.value] 

647 return Const(con) 

648 

649 def result_type(self, tyenv): 

650 return self.con.type 

651 

652 

653_CONST_POOL = { 

654 U8: [Const(U8(i)) for i in range(0, 1024)], 

655 U16: [Const(U16(i)) for i in range(0, 1024)], 

656 U32: [Const(U32(i)) for i in range(0, 1024)], 

657 U64: [Const(U64(i)) for i in range(0, 1024)], 

658} 

659 

660 

661class ITE(IRExpr): 

662 """ 

663 An if-then-else expression. 

664 """ 

665 

666 __slots__ = ["cond", "iffalse", "iftrue"] 

667 

668 tag = "Iex_ITE" 

669 

670 def __init__(self, cond, iffalse, iftrue): 

671 self.cond = cond 

672 self.iffalse = iffalse 

673 self.iftrue = iftrue 

674 

675 def _pp_str(self): 

676 return f"ITE({self.cond},{self.iftrue},{self.iffalse})" 

677 

678 @staticmethod 

679 def _from_c(c_expr): 

680 return ITE( 

681 IRExpr._from_c(c_expr.Iex.ITE.cond), 

682 IRExpr._from_c(c_expr.Iex.ITE.iffalse), 

683 IRExpr._from_c(c_expr.Iex.ITE.iftrue), 

684 ) 

685 

686 @staticmethod 

687 def _to_c(expr): 

688 return pvc.IRExpr_ITE(IRExpr._to_c(expr.cond), IRExpr._to_c(expr.iftrue), IRExpr._to_c(expr.iffalse)) 

689 

690 def result_type(self, tyenv): 

691 return self.iftrue.result_type(tyenv) 

692 

693 def typecheck(self, tyenv): 

694 condty = self.cond.typecheck(tyenv) 

695 falsety = self.iffalse.typecheck(tyenv) 

696 truety = self.iftrue.typecheck(tyenv) 

697 

698 if condty is None or falsety is None or truety is None: 

699 return None 

700 

701 if condty != "Ity_I1": 

702 log.debug("guard must be Ity_I1") 

703 return None 

704 

705 if falsety != truety: 

706 log.debug("false condition must be same type as true condition") 

707 return None 

708 

709 return falsety 

710 

711 

712class CCall(IRExpr): 

713 """ 

714 A call to a pure (no side-effects) helper C function. 

715 """ 

716 

717 __slots__ = ["retty", "cee", "args"] 

718 

719 tag = "Iex_CCall" 

720 

721 def __init__(self, retty, cee, args): 

722 self.retty = retty 

723 self.cee = cee 

724 self.args = tuple(args) 

725 

726 @property 

727 def ret_type(self): 

728 return self.retty 

729 

730 @property 

731 def callee(self): 

732 return self.cee 

733 

734 def _pp_str(self): 

735 return "{}({}):{}".format(self.cee, ",".join(str(a) for a in self.args), self.retty) 

736 

737 @property 

738 def child_expressions(self): 

739 expressions = sum((a.child_expressions for a in self.args), []) 

740 expressions.extend(self.args) 

741 return expressions 

742 

743 @staticmethod 

744 def _from_c(c_expr): 

745 i = 0 

746 args = [] 

747 while True: 

748 arg = c_expr.Iex.CCall.args[i] 

749 if arg == ffi.NULL: 

750 break 

751 args.append(IRExpr._from_c(arg)) 

752 i += 1 

753 

754 return CCall(get_enum_from_int(c_expr.Iex.CCall.retty), IRCallee._from_c(c_expr.Iex.CCall.cee), tuple(args)) 

755 

756 @staticmethod 

757 def _to_c(expr): 

758 args = [IRExpr._to_c(arg) for arg in expr.args] 

759 mkIRExprVec = getattr(pvc, "mkIRExprVec_%d" % len(args)) 

760 return pvc.IRExpr_CCall(IRCallee._to_c(expr.cee), get_int_from_enum(expr.retty), mkIRExprVec(*args)) 

761 

762 def result_type(self, tyenv): 

763 return self.retty 

764 

765 

766def get_op_retty(op): 

767 return op_arg_types(op)[0] 

768 

769 

770op_signatures: dict[str, tuple[str, tuple[str, ...]]] = {} 

771 

772 

773def _request_op_type_from_cache(op): 

774 return op_signatures[op] 

775 

776 

777def _request_op_type_from_libvex(op): 

778 Ity_INVALID = 0x1100 # as defined in enum IRType in VEX 

779 

780 res_ty = ffi.new("IRType *") 

781 arg_tys = [ffi.new("IRType *") for _ in range(4)] 

782 # initialize all IRTypes to Ity_INVALID 

783 for arg in arg_tys: 

784 arg[0] = Ity_INVALID 

785 pvc.typeOfPrimop(get_int_from_enum(op), res_ty, *arg_tys) 

786 arg_ty_vals = [a[0] for a in arg_tys] 

787 

788 try: 

789 numargs = arg_ty_vals.index(Ity_INVALID) 

790 except ValueError: 

791 numargs = 4 

792 args_tys_list = [get_enum_from_int(arg_ty_vals[i]) for i in range(numargs)] 

793 

794 op_ty_sig = (get_enum_from_int(res_ty[0]), tuple(args_tys_list)) 

795 op_signatures[op] = op_ty_sig 

796 return op_ty_sig 

797 

798 

799class PyvexOpMatchException(Exception): 

800 pass 

801 

802 

803class PyvexTypeErrorException(Exception): 

804 pass 

805 

806 

807def int_type_for_size(size): 

808 return "Ity_I%d" % size 

809 

810 

811# precompiled regexes 

812unop_signature_re = re.compile(r"Iop_(Not|Ctz|Clz)(?P<size>\d+)$") 

813binop_signature_re = re.compile(r"Iop_(Add|Sub|Mul|Xor|Or|And|Div[SU]|Mod)(?P<size>\d+)$") 

814shift_signature_re = re.compile(r"Iop_(Shl|Shr|Sar)(?P<size>\d+)$") 

815cmp_signature_re_1 = re.compile(r"Iop_Cmp(EQ|NE)(?P<size>\d+)$") 

816cmp_signature_re_2 = re.compile(r"Iop_Cmp(GT|GE|LT|LE)(?P<size>\d+)[SU]$") 

817mull_signature_re = re.compile(r"Iop_Mull[SU](?P<size>\d+)$") 

818half_signature_re = re.compile(r"Iop_DivMod[SU](?P<fullsize>\d+)to(?P<halfsize>\d+)$") 

819cast_signature_re = re.compile(r"Iop_(?P<srcsize>\d+)(U|S|HI|HL)?to(?P<dstsize>\d+)") 

820 

821 

822def unop_signature(op): 

823 m = unop_signature_re.match(op) 

824 if m is None: 

825 raise PyvexOpMatchException() 

826 size = int(m.group("size")) 

827 size_type = int_type_for_size(size) 

828 return size_type, (size_type,) 

829 

830 

831def binop_signature(op): 

832 m = binop_signature_re.match(op) 

833 if m is None: 

834 raise PyvexOpMatchException() 

835 size = int(m.group("size")) 

836 size_type = int_type_for_size(size) 

837 return (size_type, (size_type, size_type)) 

838 

839 

840def shift_signature(op): 

841 m = shift_signature_re.match(op) 

842 if m is None: 

843 raise PyvexOpMatchException() 

844 size = int(m.group("size")) 

845 if size > 255: 

846 raise PyvexTypeErrorException("Cannot apply shift operation to %d size int because shift index is 8-bit" % size) 

847 size_type = int_type_for_size(size) 

848 return (size_type, (size_type, int_type_for_size(8))) 

849 

850 

851def cmp_signature(op): 

852 m = cmp_signature_re_1.match(op) 

853 m2 = cmp_signature_re_2.match(op) 

854 if (m is None) == (m2 is None): 

855 raise PyvexOpMatchException() 

856 mfound = m if m is not None else m2 

857 assert mfound is not None 

858 size = int(mfound.group("size")) 

859 size_type = int_type_for_size(size) 

860 return (int_type_for_size(1), (size_type, size_type)) 

861 

862 

863def mull_signature(op): 

864 m = mull_signature_re.match(op) 

865 if m is None: 

866 raise PyvexOpMatchException() 

867 size = int(m.group("size")) 

868 size_type = int_type_for_size(size) 

869 doubled_size_type = int_type_for_size(2 * size) 

870 return (doubled_size_type, (size_type, size_type)) 

871 

872 

873def half_signature(op): 

874 m = half_signature_re.match(op) 

875 if m is None: 

876 raise PyvexOpMatchException() 

877 fullsize = int(m.group("fullsize")) 

878 halfsize = int(m.group("halfsize")) 

879 if halfsize * 2 != fullsize: 

880 raise PyvexTypeErrorException("Invalid Instruction %s: Type 1 must be twice the size of type 2" % op) 

881 fullsize_type = int_type_for_size(fullsize) 

882 halfsize_type = int_type_for_size(halfsize) 

883 return (fullsize_type, (fullsize_type, halfsize_type)) 

884 

885 

886def cast_signature(op): 

887 m = cast_signature_re.match(op) 

888 if m is None: 

889 raise PyvexOpMatchException() 

890 src_type = int_type_for_size(int(m.group("srcsize"))) 

891 dst_type = int_type_for_size(int(m.group("dstsize"))) 

892 return (dst_type, (src_type,)) 

893 

894 

895polymorphic_op_processors = [ 

896 unop_signature, 

897 binop_signature, 

898 shift_signature, 

899 cmp_signature, 

900 mull_signature, 

901 half_signature, 

902 cast_signature, 

903] 

904 

905 

906def _request_polymorphic_op_type(op): 

907 for polymorphic_signature in polymorphic_op_processors: 

908 try: 

909 op_ty_sig = polymorphic_signature(op) 

910 break 

911 except PyvexOpMatchException: 

912 continue 

913 else: 

914 raise PyvexOpMatchException("Op %s not recognized" % op) 

915 return op_ty_sig 

916 

917 

918_request_funcs = [_request_op_type_from_cache, _request_op_type_from_libvex, _request_polymorphic_op_type] 

919 

920 

921def op_arg_types(op): 

922 for _request_func in _request_funcs: 

923 try: 

924 return _request_func(op) 

925 except KeyError: 

926 continue 

927 raise ValueError("Cannot find type of op %s" % op) 

928 

929 

930_globals = globals().copy() 

931# 

932# Mapping from tag strings/enums to IRExpr classes 

933# 

934tag_to_expr_mapping = {} 

935enum_to_expr_mapping = {} 

936tag_count = 0 

937cls = None 

938for cls in _globals.values(): 

939 if type(cls) is type and issubclass(cls, IRExpr) and cls is not IRExpr: 

940 tag_to_expr_mapping[cls.tag] = cls 

941 enum_to_expr_mapping[get_int_from_enum(cls.tag)] = cls 

942 cls.tag_int = tag_count 

943 tag_count += 1 

944del cls 

945 

946 

947def tag_to_expr_class(tag): 

948 """ 

949 Convert a tag string to the corresponding IRExpr class type. 

950 

951 :param str tag: The tag string. 

952 :return: A class. 

953 :rtype: type 

954 """ 

955 

956 try: 

957 return tag_to_expr_mapping[tag] 

958 except KeyError: 

959 raise KeyError("Cannot find expression class for type %s." % tag) 

960 

961 

962def enum_to_expr_class(tag_enum): 

963 """ 

964 Convert a tag enum to the corresponding IRExpr class. 

965 

966 :param int tag_enum: The tag enum. 

967 :return: A class. 

968 :rtype: type 

969 """ 

970 

971 try: 

972 return enum_to_expr_mapping[tag_enum] 

973 except KeyError: 

974 raise KeyError("Cannot find expression class for type %s." % get_enum_from_int(tag_enum))