Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/pyvex/stmt.py: 66%
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
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
1import logging
2from typing import Iterator, Optional
4from . import expr
5from .const import IRConst
6from .enums import IRCallee, IRRegArray, VEXObject, get_enum_from_int, get_int_from_enum
7from .errors import PyVEXError
8from .expr import Const, Get, IRExpr
9from .native import ffi, pvc
11log = logging.getLogger("pyvex.stmt")
14class IRStmt(VEXObject):
15 """
16 IR statements in VEX represents operations with side-effects.
17 """
19 tag: Optional[str] = None
20 tag_int = 0 # set automatically at bottom of file
22 __slots__ = []
24 def pp(self):
25 print(str(self))
27 @property
28 def child_expressions(self) -> Iterator["IRExpr"]:
29 for k in self.__slots__:
30 v = getattr(self, k)
31 if isinstance(v, IRExpr):
32 # return itself
33 yield v
34 # return all the child expressions
35 yield from v.child_expressions
37 # ???
38 @property
39 def expressions(self):
40 return self.child_expressions
42 @property
43 def constants(self):
44 return sum((e.constants for e in self.expressions), [])
46 @staticmethod
47 def _from_c(c_stmt):
48 if c_stmt[0] == ffi.NULL:
49 return None
51 try:
52 stmt_class = enum_to_stmt_class(c_stmt.tag)._from_c(c_stmt)
53 except KeyError:
54 raise PyVEXError("Unknown/unsupported IRStmtTag %s.\n" % get_enum_from_int(c_stmt.tag))
55 return stmt_class._from_c(c_stmt)
57 def typecheck(self, tyenv): # pylint: disable=unused-argument,no-self-use
58 return True
60 def replace_expression(self, replacements):
61 """
62 Replace child expressions in-place.
64 :param Dict[IRExpr, IRExpr] replacements: A mapping from expression-to-find to expression-to-replace-with
65 :return: None
66 """
68 for k in self.__slots__:
69 v = getattr(self, k)
70 if isinstance(v, IRExpr) and v in replacements:
71 setattr(self, k, replacements.get(v))
72 elif isinstance(v, IRExpr):
73 v.replace_expression(replacements)
74 elif type(v) is tuple:
75 # Rebuild the tuple
76 _lst = []
77 replaced = False
78 for expr_ in v:
79 if isinstance(expr_, IRExpr) and expr_ in replacements:
80 _lst.append(replacements.get(expr_))
81 replaced = True
82 else:
83 _lst.append(expr_)
84 if replaced:
85 setattr(self, k, tuple(_lst))
87 def __str__(self):
88 return self.pp_str(None, None, None)
90 def pp_str(self, reg_name=None, arch=None, tyenv=None) -> str:
91 raise NotImplementedError()
94class NoOp(IRStmt):
95 """
96 A no-operation statement. It is usually the result of an IR optimization.
97 """
99 __slots__ = []
101 tag = "Ist_NoOp"
103 def pp_str(self, reg_name=None, arch=None, tyenv=None):
104 return "IR-NoOp"
106 @staticmethod
107 def _from_c(c_stmt):
108 return NoOp()
111class IMark(IRStmt):
112 """
113 An instruction mark. It marks the start of the statements that represent a single machine instruction (the end of
114 those statements is marked by the next IMark or the end of the IRSB). Contains the address and length of the
115 instruction.
116 """
118 __slots__ = ["addr", "len", "delta"]
120 tag = "Ist_IMark"
122 def __init__(self, addr: int, length: int, delta: int):
123 self.addr = addr
124 self.len = length
125 self.delta = delta
127 def pp_str(self, reg_name=None, arch=None, tyenv=None):
128 return "------ IMark(0x%x, %d, %d) ------" % (self.addr, self.len, self.delta)
130 @staticmethod
131 def _from_c(c_stmt):
132 return IMark(c_stmt.Ist.IMark.addr, c_stmt.Ist.IMark.len, c_stmt.Ist.IMark.delta)
135class AbiHint(IRStmt):
136 """
137 An ABI hint, provides specific information about this platform's ABI.
138 """
140 __slots__ = ["base", "len", "nia"]
142 tag = "Ist_AbiHint"
144 def __init__(self, base, length, nia):
145 self.base = base
146 self.len = length
147 self.nia = nia
149 def pp_str(self, reg_name=None, arch=None, tyenv=None):
150 return "====== AbiHint(0x%s, %d, %s) ======" % (self.base, self.len, self.nia)
152 @staticmethod
153 def _from_c(c_stmt):
154 return AbiHint(
155 IRExpr._from_c(c_stmt.Ist.AbiHint.base), c_stmt.Ist.AbiHint.len, IRExpr._from_c(c_stmt.Ist.AbiHint.nia)
156 )
159class Put(IRStmt):
160 """
161 Write to a guest register, at a fixed offset in the guest state.
162 """
164 __slots__ = ["data", "offset"]
166 tag = "Ist_Put"
168 def __init__(self, data: "IRExpr", offset):
169 self.data = data
170 self.offset = offset
172 ## TODO: Check if result_size and arch are available before looking of arch register name
173 def pp_str(self, reg_name=None, arch=None, tyenv=None):
174 if arch is not None and tyenv is not None:
175 reg_name = arch.translate_register_name(self.offset, self.data.result_size(tyenv) // 8)
177 if reg_name is not None:
178 return f"PUT({reg_name}) = {self.data}"
179 else:
180 return f"PUT(offset={self.offset}) = {self.data}"
182 @staticmethod
183 def _from_c(c_stmt):
184 return Put(IRExpr._from_c(c_stmt.Ist.Put.data), c_stmt.Ist.Put.offset)
186 def typecheck(self, tyenv):
187 return self.data.typecheck(tyenv)
190class PutI(IRStmt):
191 """
192 Write to a guest register, at a non-fixed offset in the guest state.
193 """
195 __slots__ = ["descr", "ix", "data", "bias"]
197 tag = "Ist_PutI"
199 def __init__(self, descr, ix, data, bias):
200 self.descr = descr
201 self.ix = ix
202 self.data = data
203 self.bias = bias
205 def pp_str(self, reg_name=None, arch=None, tyenv=None):
206 return "PutI(%s)[%s,%d] = %s" % (self.descr, self.ix, self.bias, self.data)
208 @staticmethod
209 def _from_c(c_stmt):
210 return PutI(
211 IRRegArray._from_c(c_stmt.Ist.PutI.details.descr),
212 IRExpr._from_c(c_stmt.Ist.PutI.details.ix),
213 IRExpr._from_c(c_stmt.Ist.PutI.details.data),
214 c_stmt.Ist.PutI.details.bias,
215 )
217 def typecheck(self, tyenv):
218 dataty = self.data.typecheck(tyenv)
219 if dataty is None:
220 return False
221 if dataty != self.descr.elemTy:
222 log.debug("Expression doesn't match RegArray type")
223 return False
224 return True
227class WrTmp(IRStmt):
228 """
229 Assign a value to a temporary. Note that SSA rules require each tmp is only assigned to once. IR sanity checking
230 will reject any block containing a temporary which is not assigned to exactly once.
231 """
233 __slots__ = ["data", "tmp"]
235 tag = "Ist_WrTmp"
237 def __init__(self, tmp, data: "IRExpr"):
238 self.tmp = tmp
239 self.data = data
241 def pp_str(self, reg_name=None, arch=None, tyenv=None):
242 # Support for named register in string representation of expr.Get
244 if arch is not None and tyenv is not None and isinstance(self.data, Get):
245 reg_name = arch.translate_register_name(self.data.offset, self.data.result_size(tyenv) // 8)
247 if reg_name is not None and isinstance(self.data, expr.Get):
248 return "t%d = %s" % (self.tmp, self.data.pp_str_with_name(reg_name))
249 else:
250 return "t%d = %s" % (self.tmp, self.data)
252 @staticmethod
253 def _from_c(c_stmt):
254 return WrTmp(c_stmt.Ist.WrTmp.tmp, IRExpr._from_c(c_stmt.Ist.WrTmp.data))
256 def typecheck(self, tyenv):
257 dataty = self.data.typecheck(tyenv)
258 if dataty is None:
259 return False
260 if dataty != tyenv.lookup(self.tmp):
261 log.debug("Expression doesn't match tmp type")
262 return False
263 return True
266class Store(IRStmt):
267 """
268 Write a value to memory..
269 """
271 __slots__ = ["addr", "data", "end"]
273 tag = "Ist_Store"
275 def __init__(self, addr: "IRExpr", data: "IRExpr", end: str):
276 self.addr = addr
277 self.data = data
278 self.end = end
280 @property
281 def endness(self):
282 return self.end
284 def pp_str(self, reg_name=None, arch=None, tyenv=None):
285 return f"ST{self.endness[-2:].lower()}({self.addr}) = {self.data}"
287 @staticmethod
288 def _from_c(c_stmt):
289 return Store(
290 IRExpr._from_c(c_stmt.Ist.Store.addr),
291 IRExpr._from_c(c_stmt.Ist.Store.data),
292 get_enum_from_int(c_stmt.Ist.Store.end),
293 )
295 def typecheck(self, tyenv):
296 dataty = self.data.typecheck(tyenv)
297 if dataty is None:
298 return False
299 addrty = self.addr.typecheck(tyenv)
300 if addrty is None:
301 return False
302 if addrty != tyenv.wordty:
303 log.debug("addr must be full word for arch")
304 return False
305 if self.end not in ("Iend_LE", "Iend_BE"):
306 log.debug("invalid endness enum")
307 return False
308 return True
311class CAS(IRStmt):
312 """
313 an atomic compare-and-swap operation.
314 """
316 __slots__ = ["addr", "dataLo", "dataHi", "expdLo", "expdHi", "oldLo", "oldHi", "end"]
318 tag = "Ist_CAS"
320 def __init__(self, addr, dataLo, dataHi, expdLo, expdHi, oldLo, oldHi, end):
321 self.addr = addr
322 self.dataLo = dataLo
323 self.dataHi = dataHi
324 self.expdLo = expdLo
325 self.expdHi = expdHi
326 self.oldLo = oldLo
327 self.oldHi = oldHi
328 self.end = end
330 @property
331 def endness(self):
332 return self.end
334 def pp_str(self, reg_name=None, arch=None, tyenv=None):
335 return "t({},{}) = CAS{}({} :: ({},{})->({},{}))".format(
336 self.oldLo, self.oldHi, self.end[-2:].lower(), self.addr, self.expdLo, self.expdHi, self.dataLo, self.dataHi
337 )
339 @staticmethod
340 def _from_c(c_stmt):
341 return CAS(
342 IRExpr._from_c(c_stmt.Ist.CAS.details.addr),
343 IRExpr._from_c(c_stmt.Ist.CAS.details.dataLo),
344 IRExpr._from_c(c_stmt.Ist.CAS.details.dataHi),
345 IRExpr._from_c(c_stmt.Ist.CAS.details.expdLo),
346 IRExpr._from_c(c_stmt.Ist.CAS.details.expdHi),
347 c_stmt.Ist.CAS.details.oldLo,
348 c_stmt.Ist.CAS.details.oldHi,
349 get_enum_from_int(c_stmt.Ist.CAS.details.end),
350 )
352 def typecheck(self, tyenv):
353 addrty = self.addr.typecheck(tyenv)
354 if addrty is None:
355 return False
356 if addrty != tyenv.wordty:
357 log.debug("addr must be full word for arch")
358 return False
359 if self.end not in ("Iend_LE", "Iend_BE"):
360 log.debug("invalid endness enum")
361 return False
363 if self.oldHi == 0xFFFFFFFF:
364 # single-element case
365 if self.expdHi is not None or self.dataHi is not None:
366 log.debug("expdHi and dataHi must be None")
367 return False
368 expdLoTy = self.expdLo.typecheck(tyenv)
369 dataLoTy = self.dataLo.typecheck(tyenv)
370 if expdLoTy is None or dataLoTy is None:
371 return False
372 if tyenv.lookup(self.oldLo) != expdLoTy or expdLoTy != dataLoTy:
373 log.debug("oldLo, expdL, dataLo must all have the same type")
374 return False
375 else:
376 # double-element case
377 expdLoTy = self.expdLo.typecheck(tyenv)
378 dataLoTy = self.dataLo.typecheck(tyenv)
379 expdHiTy = self.expdHi.typecheck(tyenv)
380 dataHiTy = self.dataHi.typecheck(tyenv)
381 if expdLoTy is None or dataLoTy is None or expdHiTy is None or dataHiTy is None:
382 return False
383 if (
384 tyenv.lookup(self.oldLo) != expdLoTy
385 or expdLoTy != dataLoTy
386 or tyenv.lookup(self.oldHi) != expdHiTy
387 or expdHiTy != dataHiTy
388 or expdLoTy != expdHiTy
389 ):
390 log.debug("oldLo, expdLo, dataLo, oldHi, expdHi, dataHi must all have the same type")
391 return False
393 return True
396class LLSC(IRStmt):
397 """
398 Either Load-Linked or Store-Conditional, depending on STOREDATA. If STOREDATA is NULL then this is a Load-Linked,
399 else it is a Store-Conditional.
400 """
402 __slots__ = ["addr", "storedata", "result", "end"]
404 tag = "Ist_LLSC"
406 def __init__(self, addr, storedata, result, end):
407 self.addr = addr
408 self.storedata = storedata
409 self.result = result
410 self.end = end
412 @property
413 def endness(self):
414 return self.end
416 def pp_str(self, reg_name=None, arch=None, tyenv=None):
417 if self.storedata is None:
418 return "t%d = LD%s-Linked(%s)" % (self.result, self.end[-2:].lower(), self.addr)
419 else:
420 return "t%d = ( ST%s-Cond(%s) = %s )" % (self.result, self.end[-2:].lower(), self.addr, self.storedata)
422 @staticmethod
423 def _from_c(c_stmt):
424 return LLSC(
425 IRExpr._from_c(c_stmt.Ist.LLSC.addr),
426 IRExpr._from_c(c_stmt.Ist.LLSC.storedata),
427 c_stmt.Ist.LLSC.result,
428 get_enum_from_int(c_stmt.Ist.LLSC.end),
429 )
431 def typecheck(self, tyenv):
432 addrty = self.addr.typecheck(tyenv)
433 if addrty is None:
434 return False
435 if addrty != tyenv.wordty:
436 log.debug("addr must be full word for arch")
437 return False
438 if self.end not in ("Iend_LE", "Iend_BE"):
439 log.debug("invalid endness enum")
440 return False
442 if self.storedata is not None:
443 # load-linked
444 storety = self.storedata.typecheck(tyenv)
445 if storety is None:
446 return False
448 if tyenv.lookup(self.result) != "Ity_I1":
449 log.debug("result tmp must be Ity_I1")
450 return False
452 return True
455class MBE(IRStmt):
456 __slots__ = ["event"]
458 tag = "Ist_MBE"
460 def __init__(self, event):
461 self.event = event
463 def pp_str(self, reg_name=None, arch=None, tyenv=None):
464 return "MBusEvent-" + self.event
466 @staticmethod
467 def _from_c(c_stmt):
468 return MBE(get_enum_from_int(c_stmt.Ist.MBE.event))
471class Dirty(IRStmt):
472 __slots__ = ["cee", "guard", "args", "tmp", "mFx", "mAddr", "mSize", "nFxState"]
474 tag = "Ist_Dirty"
476 def __init__(self, cee, guard, args, tmp, mFx, mAddr, mSize, nFxState):
477 self.cee = cee
478 self.guard = guard
479 self.args = tuple(args)
480 self.tmp = tmp
481 self.mFx = mFx
482 self.mAddr = mAddr
483 self.mSize = mSize
484 self.nFxState = nFxState
486 def pp_str(self, reg_name=None, arch=None, tyenv=None):
487 return "t{} = DIRTY {} {} ::: {}({})".format(
488 self.tmp, self.guard, "TODO(effects)", self.cee, ",".join(str(a) for a in self.args)
489 )
491 @property
492 def child_expressions(self):
493 expressions = sum((a.child_expressions for a in self.args), [])
494 expressions.extend(self.args)
495 expressions.append(self.guard)
496 expressions.extend(self.guard.child_expressions)
497 return expressions
499 @staticmethod
500 def _from_c(c_stmt):
501 args = []
502 for i in range(20):
503 a = c_stmt.Ist.Dirty.details.args[i]
504 if a == ffi.NULL:
505 break
507 args.append(IRExpr._from_c(a))
509 return Dirty(
510 IRCallee._from_c(c_stmt.Ist.Dirty.details.cee),
511 IRExpr._from_c(c_stmt.Ist.Dirty.details.guard),
512 tuple(args),
513 c_stmt.Ist.Dirty.details.tmp,
514 get_enum_from_int(c_stmt.Ist.Dirty.details.mFx),
515 IRExpr._from_c(c_stmt.Ist.Dirty.details.mAddr),
516 c_stmt.Ist.Dirty.details.mSize,
517 c_stmt.Ist.Dirty.details.nFxState,
518 )
521class Exit(IRStmt):
522 """
523 A conditional exit from the middle of an IRSB.
524 """
526 __slots__ = ["guard", "dst", "offsIP", "jk"]
528 tag = "Ist_Exit"
530 def __init__(self, guard, dst, jk, offsIP):
531 self.guard = guard
532 self.dst = dst
533 self.offsIP = offsIP
534 self.jk = jk
536 @property
537 def jumpkind(self):
538 return self.jk
540 def pp_str(self, reg_name=None, arch=None, tyenv=None):
541 if arch is not None and tyenv is not None:
542 reg_name = arch.translate_register_name(self.offsIP, arch.bits // 8)
544 if reg_name is None:
545 return "if (%s) { PUT(offset=%d) = %#x; %s }" % (self.guard, self.offsIP, self.dst.value, self.jumpkind)
546 else:
547 return f"if ({self.guard}) {{ PUT({reg_name}) = {self.dst.value:#x}; {self.jumpkind} }}"
549 @property
550 def child_expressions(self):
551 return [self.guard] + self.guard.child_expressions + [Const(self.dst)]
553 @staticmethod
554 def _from_c(c_stmt):
555 return Exit(
556 IRExpr._from_c(c_stmt.Ist.Exit.guard),
557 IRConst._from_c(c_stmt.Ist.Exit.dst),
558 get_enum_from_int(c_stmt.Ist.Exit.jk),
559 c_stmt.Ist.Exit.offsIP,
560 )
562 def typecheck(self, tyenv):
563 if not self.jk.startswith("Ijk_"):
564 log.debug("Jumpkind is not a jumpkind enum")
565 return False
566 guardty = self.guard.typecheck(tyenv)
567 if guardty is None:
568 return False
569 if guardty != "Ity_I1":
570 log.debug("guard must be Ity_I1")
571 return False
572 return True
575class LoadG(IRStmt):
576 """
577 A guarded load.
578 """
580 __slots__ = ["addr", "alt", "guard", "dst", "cvt", "end", "cvt_types"]
582 tag = "Ist_LoadG"
584 def __init__(self, end, cvt, dst, addr, alt, guard):
585 self.addr = addr
586 self.alt = alt
587 self.guard = guard
588 self.dst = dst
589 self.cvt = cvt
590 self.end = end
592 type_in = ffi.new("IRType *") # TODO separate this from the pyvex C implementation
593 type_out = ffi.new("IRType *")
594 pvc.typeOfIRLoadGOp(get_int_from_enum(self.cvt), type_out, type_in)
595 type_in = ffi.cast("int *", type_in)[0]
596 type_out = ffi.cast("int *", type_out)[0]
597 self.cvt_types = (get_enum_from_int(type_in), get_enum_from_int(type_out))
599 @property
600 def endness(self):
601 return self.end
603 def pp_str(self, reg_name=None, arch=None, tyenv=None):
604 return "t%d = if (%s) %s(LD%s(%s)) else %s" % (
605 self.dst,
606 self.guard,
607 self.cvt,
608 self.end[-2:].lower(),
609 self.addr,
610 self.alt,
611 )
613 @staticmethod
614 def _from_c(c_stmt):
615 return LoadG(
616 get_enum_from_int(c_stmt.Ist.LoadG.details.end),
617 get_enum_from_int(c_stmt.Ist.LoadG.details.cvt),
618 c_stmt.Ist.LoadG.details.dst,
619 IRExpr._from_c(c_stmt.Ist.LoadG.details.addr),
620 IRExpr._from_c(c_stmt.Ist.LoadG.details.alt),
621 IRExpr._from_c(c_stmt.Ist.LoadG.details.guard),
622 )
624 def typecheck(self, tyenv):
625 addrty = self.addr.typecheck(tyenv)
626 if addrty is None:
627 return False
628 if addrty != tyenv.wordty:
629 log.debug("addr must be full word for arch")
630 return False
631 if self.end not in ("Iend_LE", "Iend_BE"):
632 log.debug("invalid endness enum")
633 return False
635 dstty = tyenv.lookup(self.dst)
636 guardty = self.guard.typecheck(tyenv)
637 altty = self.alt.typecheck(tyenv)
639 if guardty is None or altty is None:
640 return False
641 if dstty != "Ity_I32" or altty != "Ity_I32":
642 log.debug("dst and alt must be Ity_I32")
643 return False
644 if guardty != "Ity_I1":
645 log.debug("guard must be Ity_I1")
646 return False
647 if not self.cvt.startswith("ILGop_"):
648 log.debug("Invalid cvt enum")
649 return False
650 return True
653class StoreG(IRStmt):
654 """
655 A guarded store.
656 """
658 __slots__ = ["addr", "data", "guard", "end"]
660 tag = "Ist_StoreG"
662 def __init__(self, end, addr, data, guard):
663 self.addr = addr
664 self.data = data
665 self.guard = guard
666 self.end = end
668 @property
669 def endness(self):
670 return self.end
672 def pp_str(self, reg_name=None, arch=None, tyenv=None):
673 return f"if ({self.guard}) ST{self.end[-2:].lower()}({self.addr}) = {self.data}"
675 @staticmethod
676 def _from_c(c_stmt):
677 return StoreG(
678 get_enum_from_int(c_stmt.Ist.StoreG.details.end),
679 IRExpr._from_c(c_stmt.Ist.StoreG.details.addr),
680 IRExpr._from_c(c_stmt.Ist.StoreG.details.data),
681 IRExpr._from_c(c_stmt.Ist.StoreG.details.guard),
682 )
684 def typecheck(self, tyenv):
685 addrty = self.addr.typecheck(tyenv)
686 if addrty is None:
687 return False
688 if addrty != tyenv.wordty:
689 log.debug("addr must be full word for arch")
690 return False
691 if self.end not in ("Iend_LE", "Iend_BE"):
692 log.debug("invalid endness enum")
693 return False
695 guardty = self.guard.typecheck(tyenv)
696 dataty = self.data.typecheck(tyenv)
698 if guardty is None or dataty is None:
699 return False
700 if guardty != "Ity_I1":
701 log.debug("guard must be Ity_I1")
702 return False
703 return True
706_globals = globals().copy()
707#
708# Mapping from tag strings/enums to IRStmt classes
709#
710tag_to_stmt_mapping = {}
711enum_to_stmt_mapping = {}
712tag_count = 0
713cls = None
714for cls in _globals.values():
715 if type(cls) is type and issubclass(cls, IRStmt) and cls is not IRStmt:
716 tag_to_stmt_mapping[cls.tag] = cls
717 enum_to_stmt_mapping[get_int_from_enum(cls.tag)] = cls
718 cls.tag_int = tag_count
719 tag_count += 1
720del cls
723def tag_to_stmt_class(tag):
724 try:
725 return tag_to_stmt_mapping[tag]
726 except KeyError:
727 raise KeyError("No statement class for tag %s." % tag)
730def enum_to_stmt_class(tag_enum):
731 try:
732 return enum_to_stmt_mapping[tag_enum]
733 except KeyError:
734 raise KeyError("No statement class for tag %s." % get_enum_from_int(tag_enum))