-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathengine_types.py
More file actions
200 lines (156 loc) · 5.6 KB
/
engine_types.py
File metadata and controls
200 lines (156 loc) · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
from __future__ import annotations
from dataclasses import dataclass
from enum import IntEnum
from typing import Any, Mapping, TypeAlias
import operator
from binary_function import BinaryFunction, Loop
from project import Project
LoopsDict: TypeAlias = Mapping[int, list[Loop]]
def repr_or_hexint(val: Any) -> str:
if isinstance(val, int):
return hex(val)
return repr(val)
@dataclass(frozen=True)
class Register:
offset: int
address: int
project: Project
def __repr__(self):
return f"{{{self.project.arch_regs.names[self.offset]}@{hex(self.address)}}}"
@dataclass(frozen=True)
class Arg:
index: int
def __repr__(self):
return f"arg{self.index}"
@dataclass(frozen=True)
class UnaryOp:
obj: Any
op: str
def __repr__(self):
return f"{self.op}{repr_or_hexint(self.obj)}"
@dataclass(frozen=True)
class BinaryOp:
left: Any
right: Any
op: str
signed: bool = False
def __repr__(self):
return f"({repr_or_hexint(self.left)} {self.op} {repr_or_hexint(self.right)})"
@staticmethod
def _eval_numeric_expression(left: int, right: int, op: str):
return BinaryOp._INTEGER_OPS[op](left, right) & BinaryOp._BITNESS_TO_MASK[BinaryOp._PTR_SIZE]
_BITNESS_TO_MASK = {
32: 0xFFFFFFFF,
64: 0xFFFFFFFFFFFFFFFF,
}
_PTR_SIZE = 32
_INTEGER_OPS = {
"+": operator.add,
"-": operator.sub,
"*": operator.mul,
"/": operator.floordiv,
"%": operator.mod,
"&": operator.and_,
"|": operator.or_,
"^": operator.xor,
"<<": operator.lshift,
">>": operator.rshift,
"==": lambda a, b: int(operator.eq(a, b)),
"!=": lambda a, b: int(operator.ne(a, b)),
"<": lambda a, b: int(operator.lt(a, b)),
"<=": lambda a, b: int(operator.le(a, b)),
">": lambda a, b: int(operator.gt(a, b)),
">=": lambda a, b: int(operator.ge(a, b)),
}
_ASSOCIATIVE_OPS = {"+", "*", "&", "|", "^"}
_COMPERISON_OPS = {"==", "!=", "<", "<=", ">", ">="}
_MONOID = {"+": 0, "*": 1, "|": 0, "^": 0, "-": 0}
@staticmethod
def create_binop(left: Any, right: Any, op: str, signed: bool = False) -> BinaryOp | int:
if isinstance(left, int) and isinstance(right, int):
return BinaryOp._eval_numeric_expression(left, right, op)
elif isinstance(left, BinaryOp) and isinstance(right, int):
if left.op == op and op in BinaryOp._ASSOCIATIVE_OPS and isinstance(left.right, int):
return BinaryOp(left.left, BinaryOp._eval_numeric_expression(left.right, right, op), op)
elif left.op in BinaryOp._COMPERISON_OPS and right == 0:
if op == "!=":
return left
elif op == "==":
return left.negate()
elif left.op == "^" and right == 1 and op == "<" and not signed:
return BinaryOp(left.left, left.right, "==")
elif right == BinaryOp._MONOID.get(op, None):
return left
elif left == BinaryOp._MONOID.get(op, None):
return right
return BinaryOp(left, right, op, signed)
def negate(self) -> "BinaryOp":
neg_op_map = {
"==": "!=",
"!=": "==",
"<": ">=",
"<=": ">",
">": "<=",
">=": "<",
}
if self.op not in neg_op_map:
raise ValueError(f"Cannot negate binary operation with operator '{self.op}'")
return BinaryOp(self.left, self.right, neg_op_map[self.op])
@dataclass(frozen=True)
class ConditionalSite:
addr: int
condition: BinaryOp
iftrue: int
iffalse: int
def __repr__(self):
return f"goto {repr_or_hexint(self.iftrue)} if {self.condition!r} else {hex(self.iffalse)}"
@dataclass(frozen=True)
class CallSite:
addr: int
target: int
args: Mapping[int, Any]
def __repr__(self):
return f"({repr_or_hexint(self.target)})({', '.join(repr(self.args[arg_idx]) for arg_idx in sorted(self.args.keys()))})"
@dataclass(frozen=True)
class ConditionalExpression:
condsite: ConditionalSite
iftrue: Any
iffalse: Any
def __repr__(self):
return f"({repr_or_hexint(self.iftrue)} if({self.condsite.condition!r} at {hex(self.condsite.addr)}) else {repr_or_hexint(self.iffalse)})"
def collect_values(self) -> list[Any]:
values = []
if isinstance(self.iftrue, ConditionalExpression):
values.extend(self.iftrue.collect_values())
else:
values.append(self.iftrue)
if isinstance(self.iffalse, ConditionalExpression):
values.extend(self.iffalse.collect_values())
else:
values.append(self.iffalse)
return values
class MemoryAccessType(IntEnum):
LOAD = 0
STORE = 1
@dataclass(frozen=True)
class MemoryAccess:
addr: int
base: Any
offset: Any
access_type: MemoryAccessType
stored_value: Any | None = None
def __repr__(self):
addition_str = "" if self.offset == 0 else f" + {repr_or_hexint(self.offset)}"
if self.access_type == MemoryAccessType.LOAD:
return f"*({repr_or_hexint(self.base)}{addition_str})"
else:
return f"*({repr_or_hexint(self.base)}{addition_str}) = {repr_or_hexint(self.stored_value)}"
def __eq__(self, other):
if not isinstance(other, MemoryAccess):
return False
return (
self.base == other.base
and self.offset == other.offset
and self.access_type == other.access_type
and self.stored_value == other.stored_value
)