Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pyvex/stmt.py: 66%
447 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
1import logging
2from typing import Iterator, Optional
4from archinfo import RegisterOffset, TmpVar
6from . import expr
7from .const import IRConst
8from .enums import IRCallee, IRRegArray, VEXObject, get_enum_from_int, get_int_from_enum
9from .errors import PyVEXError
10from .expr import Const, Get, IRExpr
11from .native import ffi, pvc
13log = logging.getLogger("pyvex.stmt")
16class IRStmt(VEXObject):
17 """
18 IR statements in VEX represents operations with side-effects.
19 """
21 tag: Optional[str] = None
22 tag_int = 0 # set automatically at bottom of file
24 __slots__ = []
26 def pp(self):
27 print(self.__str__())
29 @property
30 def child_expressions(self) -> Iterator["IRExpr"]:
31 for k in self.__slots__:
32 v = getattr(self, k)
33 if isinstance(v, IRExpr):
34 # return itself
35 yield v
36 # return all the child expressions
37 yield from v.child_expressions
39 # ???
40 @property
41 def expressions(self):
42 return self.child_expressions
44 @property
45 def constants(self):
46 return sum((e.constants for e in self.expressions), [])
48 @staticmethod
49 def _from_c(c_stmt):
50 if c_stmt[0] == ffi.NULL:
51 return None
53 try:
54 stmt_class = enum_to_stmt_class(c_stmt.tag)._from_c(c_stmt)
55 except KeyError:
56 raise PyVEXError("Unknown/unsupported IRStmtTag %s.\n" % get_enum_from_int(c_stmt.tag))
57 return stmt_class._from_c(c_stmt)
59 def typecheck(self, tyenv): # pylint: disable=unused-argument,no-self-use
60 return True
62 def replace_expression(self, replacements):
63 """
64 Replace child expressions in-place.
66 :param Dict[IRExpr, IRExpr] replacements: A mapping from expression-to-find to expression-to-replace-with
67 :return: None
68 """
70 for k in self.__slots__:
71 v = getattr(self, k)
72 if isinstance(v, IRExpr) and v in replacements:
73 setattr(self, k, replacements.get(v))
74 elif isinstance(v, IRExpr):
75 v.replace_expression(replacements)
76 elif type(v) is tuple:
77 # Rebuild the tuple
78 _lst = []
79 replaced = False
80 for expr_ in v:
81 if isinstance(expr_, IRExpr) and expr_ in replacements:
82 _lst.append(replacements.get(expr_))
83 replaced = True
84 else:
85 _lst.append(expr_)
86 if replaced:
87 setattr(self, k, tuple(_lst))
89 def __str__(self, reg_name=None, arch=None, tyenv=None):
90 raise NotImplementedError()
93class NoOp(IRStmt):
94 """
95 A no-operation statement. It is usually the result of an IR optimization.
96 """
98 __slots__ = []
100 tag = "Ist_NoOp"
102 def __str__(self, reg_name=None, arch=None, tyenv=None):
103 return "IR-NoOp"
105 @staticmethod
106 def _from_c(c_stmt):
107 return NoOp()
110class IMark(IRStmt):
111 """
112 An instruction mark. It marks the start of the statements that represent a single machine instruction (the end of
113 those statements is marked by the next IMark or the end of the IRSB). Contains the address and length of the
114 instruction.
115 """
117 __slots__ = ["addr", "len", "delta"]
119 tag = "Ist_IMark"
121 def __init__(self, addr: int, length: int, delta: int):
122 self.addr = addr
123 self.len = length
124 self.delta = delta
126 def __str__(self, reg_name=None, arch=None, tyenv=None):
127 return "------ IMark(0x%x, %d, %d) ------" % (self.addr, self.len, self.delta)
129 @staticmethod
130 def _from_c(c_stmt):
131 return IMark(c_stmt.Ist.IMark.addr, c_stmt.Ist.IMark.len, c_stmt.Ist.IMark.delta)
134class AbiHint(IRStmt):
135 """
136 An ABI hint, provides specific information about this platform's ABI.
137 """
139 __slots__ = ["base", "len", "nia"]
141 tag = "Ist_AbiHint"
143 def __init__(self, base, length, nia):
144 self.base = base
145 self.len = length
146 self.nia = nia
148 def __str__(self, reg_name=None, arch=None, tyenv=None):
149 return "====== AbiHint(0x%s, %d, %s) ======" % (self.base, self.len, self.nia)
151 @staticmethod
152 def _from_c(c_stmt):
153 return AbiHint(
154 IRExpr._from_c(c_stmt.Ist.AbiHint.base), c_stmt.Ist.AbiHint.len, IRExpr._from_c(c_stmt.Ist.AbiHint.nia)
155 )
158class Put(IRStmt):
159 """
160 Write to a guest register, at a fixed offset in the guest state.
161 """
163 __slots__ = ["data", "offset"]
165 tag = "Ist_Put"
167 def __init__(self, data: "IRExpr", offset: RegisterOffset):
168 self.data = data
169 self.offset = offset
171 ## TODO: Check if result_size and arch are available before looking of arch register name
172 def __str__(self, reg_name=None, arch=None, tyenv=None):
173 if arch is not None and tyenv is not None:
174 reg_name = arch.translate_register_name(self.offset, self.data.result_size(tyenv) // 8)
176 if reg_name is not None:
177 return f"PUT({reg_name}) = {self.data}"
178 else:
179 return f"PUT(offset={self.offset}) = {self.data}"
181 @staticmethod
182 def _from_c(c_stmt):
183 return Put(IRExpr._from_c(c_stmt.Ist.Put.data), c_stmt.Ist.Put.offset)
185 def typecheck(self, tyenv):
186 return self.data.typecheck(tyenv)
189class PutI(IRStmt):
190 """
191 Write to a guest register, at a non-fixed offset in the guest state.
192 """
194 __slots__ = ["descr", "ix", "data", "bias"]
196 tag = "Ist_PutI"
198 def __init__(self, descr, ix, data, bias):
199 self.descr = descr
200 self.ix = ix
201 self.data = data
202 self.bias = bias
204 def __str__(self, reg_name=None, arch=None, tyenv=None):
205 return "PutI(%s)[%s,%d] = %s" % (self.descr, self.ix, self.bias, self.data)
207 @staticmethod
208 def _from_c(c_stmt):
209 return PutI(
210 IRRegArray._from_c(c_stmt.Ist.PutI.details.descr),
211 IRExpr._from_c(c_stmt.Ist.PutI.details.ix),
212 IRExpr._from_c(c_stmt.Ist.PutI.details.data),
213 c_stmt.Ist.PutI.details.bias,
214 )
216 def typecheck(self, tyenv):
217 dataty = self.data.typecheck(tyenv)
218 if dataty is None:
219 return False
220 if dataty != self.descr.elemTy:
221 log.debug("Expression doesn't match RegArray type")
222 return False
223 return True
226class WrTmp(IRStmt):
227 """
228 Assign a value to a temporary. Note that SSA rules require each tmp is only assigned to once. IR sanity checking
229 will reject any block containing a temporary which is not assigned to exactly once.
230 """
232 __slots__ = ["data", "tmp"]
234 tag = "Ist_WrTmp"
236 def __init__(self, tmp: TmpVar, data: "IRExpr"):
237 self.tmp = tmp
238 self.data = data
240 def __str__(self, reg_name=None, arch=None, tyenv=None):
241 # Support for named register in string representation of expr.Get
243 if arch is not None and tyenv is not None and isinstance(self.data, Get):
244 reg_name = arch.translate_register_name(self.data.offset, self.data.result_size(tyenv) // 8)
246 if reg_name is not None and isinstance(self.data, expr.Get):
247 return "t%d = %s" % (self.tmp, self.data.__str__(reg_name=reg_name))
248 else:
249 return "t%d = %s" % (self.tmp, self.data)
251 @staticmethod
252 def _from_c(c_stmt):
253 return WrTmp(c_stmt.Ist.WrTmp.tmp, IRExpr._from_c(c_stmt.Ist.WrTmp.data))
255 def typecheck(self, tyenv):
256 dataty = self.data.typecheck(tyenv)
257 if dataty is None:
258 return False
259 if dataty != tyenv.lookup(self.tmp):
260 log.debug("Expression doesn't match tmp type")
261 return False
262 return True
265class Store(IRStmt):
266 """
267 Write a value to memory..
268 """
270 __slots__ = ["addr", "data", "end"]
272 tag = "Ist_Store"
274 def __init__(self, addr: "IRExpr", data: "IRExpr", end: str):
275 self.addr = addr
276 self.data = data
277 self.end = end
279 @property
280 def endness(self):
281 return self.end
283 def __str__(self, reg_name=None, arch=None, tyenv=None):
284 return f"ST{self.endness[-2:].lower()}({self.addr}) = {self.data}"
286 @staticmethod
287 def _from_c(c_stmt):
288 return Store(
289 IRExpr._from_c(c_stmt.Ist.Store.addr),
290 IRExpr._from_c(c_stmt.Ist.Store.data),
291 get_enum_from_int(c_stmt.Ist.Store.end),
292 )
294 def typecheck(self, tyenv):
295 dataty = self.data.typecheck(tyenv)
296 if dataty is None:
297 return False
298 addrty = self.addr.typecheck(tyenv)
299 if addrty is None:
300 return False
301 if addrty != tyenv.wordty:
302 log.debug("addr must be full word for arch")
303 return False
304 if self.end not in ("Iend_LE", "Iend_BE"):
305 log.debug("invalid endness enum")
306 return False
307 return True
310class CAS(IRStmt):
311 """
312 an atomic compare-and-swap operation.
313 """
315 __slots__ = ["addr", "dataLo", "dataHi", "expdLo", "expdHi", "oldLo", "oldHi", "end"]
317 tag = "Ist_CAS"
319 def __init__(self, addr, dataLo, dataHi, expdLo, expdHi, oldLo, oldHi, end):
320 self.addr = addr
321 self.dataLo = dataLo
322 self.dataHi = dataHi
323 self.expdLo = expdLo
324 self.expdHi = expdHi
325 self.oldLo = oldLo
326 self.oldHi = oldHi
327 self.end = end
329 @property
330 def endness(self):
331 return self.end
333 def __str__(self, reg_name=None, arch=None, tyenv=None):
334 return "t({},{}) = CAS{}({} :: ({},{})->({},{}))".format(
335 self.oldLo, self.oldHi, self.end[-2:].lower(), self.addr, self.expdLo, self.expdHi, self.dataLo, self.dataHi
336 )
338 @staticmethod
339 def _from_c(c_stmt):
340 return CAS(
341 IRExpr._from_c(c_stmt.Ist.CAS.details.addr),
342 IRExpr._from_c(c_stmt.Ist.CAS.details.dataLo),
343 IRExpr._from_c(c_stmt.Ist.CAS.details.dataHi),
344 IRExpr._from_c(c_stmt.Ist.CAS.details.expdLo),
345 IRExpr._from_c(c_stmt.Ist.CAS.details.expdHi),
346 c_stmt.Ist.CAS.details.oldLo,
347 c_stmt.Ist.CAS.details.oldHi,
348 get_enum_from_int(c_stmt.Ist.CAS.details.end),
349 )
351 def typecheck(self, tyenv):
352 addrty = self.addr.typecheck(tyenv)
353 if addrty is None:
354 return False
355 if addrty != tyenv.wordty:
356 log.debug("addr must be full word for arch")
357 return False
358 if self.end not in ("Iend_LE", "Iend_BE"):
359 log.debug("invalid endness enum")
360 return False
362 if self.oldHi == 0xFFFFFFFF:
363 # single-element case
364 if self.expdHi is not None or self.dataHi is not None:
365 log.debug("expdHi and dataHi must be None")
366 return False
367 expdLoTy = self.expdLo.typecheck(tyenv)
368 dataLoTy = self.dataLo.typecheck(tyenv)
369 if expdLoTy is None or dataLoTy is None:
370 return False
371 if tyenv.lookup(self.oldLo) != expdLoTy or expdLoTy != dataLoTy:
372 log.debug("oldLo, expdL, dataLo must all have the same type")
373 return False
374 else:
375 # double-element case
376 expdLoTy = self.expdLo.typecheck(tyenv)
377 dataLoTy = self.dataLo.typecheck(tyenv)
378 expdHiTy = self.expdHi.typecheck(tyenv)
379 dataHiTy = self.dataHi.typecheck(tyenv)
380 if expdLoTy is None or dataLoTy is None or expdHiTy is None or dataHiTy is None:
381 return False
382 if (
383 tyenv.lookup(self.oldLo) != expdLoTy
384 or expdLoTy != dataLoTy
385 or tyenv.lookup(self.oldHi) != expdHiTy
386 or expdHiTy != dataHiTy
387 or expdLoTy != expdHiTy
388 ):
389 log.debug("oldLo, expdLo, dataLo, oldHi, expdHi, dataHi must all have the same type")
390 return False
392 return True
395class LLSC(IRStmt):
396 """
397 Either Load-Linked or Store-Conditional, depending on STOREDATA. If STOREDATA is NULL then this is a Load-Linked,
398 else it is a Store-Conditional.
399 """
401 __slots__ = ["addr", "storedata", "result", "end"]
403 tag = "Ist_LLSC"
405 def __init__(self, addr, storedata, result, end):
406 self.addr = addr
407 self.storedata = storedata
408 self.result = result
409 self.end = end
411 @property
412 def endness(self):
413 return self.end
415 def __str__(self, reg_name=None, arch=None, tyenv=None):
416 if self.storedata is None:
417 return "t%d = LD%s-Linked(%s)" % (self.result, self.end[-2:].lower(), self.addr)
418 else:
419 return "t%d = ( ST%s-Cond(%s) = %s )" % (self.result, self.end[-2:].lower(), self.addr, self.storedata)
421 @staticmethod
422 def _from_c(c_stmt):
423 return LLSC(
424 IRExpr._from_c(c_stmt.Ist.LLSC.addr),
425 IRExpr._from_c(c_stmt.Ist.LLSC.storedata),
426 c_stmt.Ist.LLSC.result,
427 get_enum_from_int(c_stmt.Ist.LLSC.end),
428 )
430 def typecheck(self, tyenv):
431 addrty = self.addr.typecheck(tyenv)
432 if addrty is None:
433 return False
434 if addrty != tyenv.wordty:
435 log.debug("addr must be full word for arch")
436 return False
437 if self.end not in ("Iend_LE", "Iend_BE"):
438 log.debug("invalid endness enum")
439 return False
441 if self.storedata is not None:
442 # load-linked
443 storety = self.storedata.typecheck(tyenv)
444 if storety is None:
445 return False
447 if tyenv.lookup(self.result) != "Ity_I1":
448 log.debug("result tmp must be Ity_I1")
449 return False
451 return True
454class MBE(IRStmt):
455 __slots__ = ["event"]
457 tag = "Ist_MBE"
459 def __init__(self, event):
460 self.event = event
462 def __str__(self, reg_name=None, arch=None, tyenv=None):
463 return "MBusEvent-" + self.event
465 @staticmethod
466 def _from_c(c_stmt):
467 return MBE(get_enum_from_int(c_stmt.Ist.MBE.event))
470class Dirty(IRStmt):
471 __slots__ = ["cee", "guard", "args", "tmp", "mFx", "mAddr", "mSize", "nFxState"]
473 tag = "Ist_Dirty"
475 def __init__(self, cee, guard, args, tmp, mFx, mAddr, mSize, nFxState):
476 self.cee = cee
477 self.guard = guard
478 self.args = tuple(args)
479 self.tmp = tmp
480 self.mFx = mFx
481 self.mAddr = mAddr
482 self.mSize = mSize
483 self.nFxState = nFxState
485 def __str__(self, reg_name=None, arch=None, tyenv=None):
486 return "t{} = DIRTY {} {} ::: {}({})".format(
487 self.tmp, self.guard, "TODO(effects)", self.cee, ",".join(str(a) for a in self.args)
488 )
490 @property
491 def child_expressions(self):
492 expressions = sum((a.child_expressions for a in self.args), [])
493 expressions.extend(self.args)
494 expressions.append(self.guard)
495 expressions.extend(self.guard.child_expressions)
496 return expressions
498 @staticmethod
499 def _from_c(c_stmt):
500 args = []
501 for i in range(20):
502 a = c_stmt.Ist.Dirty.details.args[i]
503 if a == ffi.NULL:
504 break
506 args.append(IRExpr._from_c(a))
508 return Dirty(
509 IRCallee._from_c(c_stmt.Ist.Dirty.details.cee),
510 IRExpr._from_c(c_stmt.Ist.Dirty.details.guard),
511 tuple(args),
512 c_stmt.Ist.Dirty.details.tmp,
513 get_enum_from_int(c_stmt.Ist.Dirty.details.mFx),
514 IRExpr._from_c(c_stmt.Ist.Dirty.details.mAddr),
515 c_stmt.Ist.Dirty.details.mSize,
516 c_stmt.Ist.Dirty.details.nFxState,
517 )
520class Exit(IRStmt):
521 """
522 A conditional exit from the middle of an IRSB.
523 """
525 __slots__ = ["guard", "dst", "offsIP", "jk"]
527 tag = "Ist_Exit"
529 def __init__(self, guard, dst, jk, offsIP):
530 self.guard = guard
531 self.dst = dst
532 self.offsIP = offsIP
533 self.jk = jk
535 @property
536 def jumpkind(self):
537 return self.jk
539 def __str__(self, reg_name=None, arch=None, tyenv=None):
540 if arch is not None and tyenv is not None:
541 reg_name = arch.translate_register_name(self.offsIP, arch.bits // 8)
543 if reg_name is None:
544 return "if (%s) { PUT(offset=%d) = %#x; %s }" % (self.guard, self.offsIP, self.dst.value, self.jumpkind)
545 else:
546 return f"if ({self.guard}) {{ PUT({reg_name}) = {self.dst.value:#x}; {self.jumpkind} }}"
548 @property
549 def child_expressions(self):
550 return [self.guard] + self.guard.child_expressions + [Const(self.dst)]
552 @staticmethod
553 def _from_c(c_stmt):
554 return Exit(
555 IRExpr._from_c(c_stmt.Ist.Exit.guard),
556 IRConst._from_c(c_stmt.Ist.Exit.dst),
557 get_enum_from_int(c_stmt.Ist.Exit.jk),
558 c_stmt.Ist.Exit.offsIP,
559 )
561 def typecheck(self, tyenv):
562 if not self.jk.startswith("Ijk_"):
563 log.debug("Jumpkind is not a jumpkind enum")
564 return False
565 guardty = self.guard.typecheck(tyenv)
566 if guardty is None:
567 return False
568 if guardty != "Ity_I1":
569 log.debug("guard must be Ity_I1")
570 return False
571 return True
574class LoadG(IRStmt):
575 """
576 A guarded load.
577 """
579 __slots__ = ["addr", "alt", "guard", "dst", "cvt", "end", "cvt_types"]
581 tag = "Ist_LoadG"
583 def __init__(self, end, cvt, dst, addr, alt, guard):
584 self.addr = addr
585 self.alt = alt
586 self.guard = guard
587 self.dst = dst
588 self.cvt = cvt
589 self.end = end
591 type_in = ffi.new("IRType *") # TODO separate this from the pyvex C implementation
592 type_out = ffi.new("IRType *")
593 pvc.typeOfIRLoadGOp(get_int_from_enum(self.cvt), type_out, type_in)
594 type_in = ffi.cast("int *", type_in)[0]
595 type_out = ffi.cast("int *", type_out)[0]
596 self.cvt_types = (get_enum_from_int(type_in), get_enum_from_int(type_out))
598 @property
599 def endness(self):
600 return self.end
602 def __str__(self, reg_name=None, arch=None, tyenv=None):
603 return "t%d = if (%s) %s(LD%s(%s)) else %s" % (
604 self.dst,
605 self.guard,
606 self.cvt,
607 self.end[-2:].lower(),
608 self.addr,
609 self.alt,
610 )
612 @staticmethod
613 def _from_c(c_stmt):
614 return LoadG(
615 get_enum_from_int(c_stmt.Ist.LoadG.details.end),
616 get_enum_from_int(c_stmt.Ist.LoadG.details.cvt),
617 c_stmt.Ist.LoadG.details.dst,
618 IRExpr._from_c(c_stmt.Ist.LoadG.details.addr),
619 IRExpr._from_c(c_stmt.Ist.LoadG.details.alt),
620 IRExpr._from_c(c_stmt.Ist.LoadG.details.guard),
621 )
623 def typecheck(self, tyenv):
624 addrty = self.addr.typecheck(tyenv)
625 if addrty is None:
626 return False
627 if addrty != tyenv.wordty:
628 log.debug("addr must be full word for arch")
629 return False
630 if self.end not in ("Iend_LE", "Iend_BE"):
631 log.debug("invalid endness enum")
632 return False
634 dstty = tyenv.lookup(self.dst)
635 guardty = self.guard.typecheck(tyenv)
636 altty = self.alt.typecheck(tyenv)
638 if guardty is None or altty is None:
639 return False
640 if dstty != "Ity_I32" or altty != "Ity_I32":
641 log.debug("dst and alt must be Ity_I32")
642 return False
643 if guardty != "Ity_I1":
644 log.debug("guard must be Ity_I1")
645 return False
646 if not self.cvt.startswith("ILGop_"):
647 log.debug("Invalid cvt enum")
648 return False
649 return True
652class StoreG(IRStmt):
653 """
654 A guarded store.
655 """
657 __slots__ = ["addr", "data", "guard", "end"]
659 tag = "Ist_StoreG"
661 def __init__(self, end, addr, data, guard):
662 self.addr = addr
663 self.data = data
664 self.guard = guard
665 self.end = end
667 @property
668 def endness(self):
669 return self.end
671 def __str__(self, reg_name=None, arch=None, tyenv=None):
672 return f"if ({self.guard}) ST{self.end[-2:].lower()}({self.addr}) = {self.data}"
674 @staticmethod
675 def _from_c(c_stmt):
676 return StoreG(
677 get_enum_from_int(c_stmt.Ist.StoreG.details.end),
678 IRExpr._from_c(c_stmt.Ist.StoreG.details.addr),
679 IRExpr._from_c(c_stmt.Ist.StoreG.details.data),
680 IRExpr._from_c(c_stmt.Ist.StoreG.details.guard),
681 )
683 def typecheck(self, tyenv):
684 addrty = self.addr.typecheck(tyenv)
685 if addrty is None:
686 return False
687 if addrty != tyenv.wordty:
688 log.debug("addr must be full word for arch")
689 return False
690 if self.end not in ("Iend_LE", "Iend_BE"):
691 log.debug("invalid endness enum")
692 return False
694 guardty = self.guard.typecheck(tyenv)
695 dataty = self.data.typecheck(tyenv)
697 if guardty is None or dataty is None:
698 return False
699 if guardty != "Ity_I1":
700 log.debug("guard must be Ity_I1")
701 return False
702 return True
705_globals = globals().copy()
706#
707# Mapping from tag strings/enums to IRStmt classes
708#
709tag_to_stmt_mapping = {}
710enum_to_stmt_mapping = {}
711tag_count = 0
712cls = None
713for cls in _globals.values():
714 if type(cls) is type and issubclass(cls, IRStmt) and cls is not IRStmt:
715 tag_to_stmt_mapping[cls.tag] = cls
716 enum_to_stmt_mapping[get_int_from_enum(cls.tag)] = cls
717 cls.tag_int = tag_count
718 tag_count += 1
719del cls
722def tag_to_stmt_class(tag):
723 try:
724 return tag_to_stmt_mapping[tag]
725 except KeyError:
726 raise KeyError("No statement class for tag %s." % tag)
729def enum_to_stmt_class(tag_enum):
730 try:
731 return enum_to_stmt_mapping[tag_enum]
732 except KeyError:
733 raise KeyError("No statement class for tag %s." % get_enum_from_int(tag_enum))