angr 9.2.140__py3-none-manylinux2014_x86_64.whl → 9.2.141__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +88 -32
- angr/analyses/calling_convention/fact_collector.py +44 -18
- angr/analyses/calling_convention/utils.py +3 -1
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
- angr/analyses/decompiler/ail_simplifier.py +48 -20
- angr/analyses/decompiler/callsite_maker.py +24 -11
- angr/analyses/decompiler/clinic.py +10 -0
- angr/analyses/decompiler/decompiler.py +1 -0
- angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +76 -1
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +51 -7
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
- angr/analyses/decompiler/region_identifier.py +6 -4
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +32 -18
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -1
- angr/analyses/decompiler/ssailification/rewriting.py +23 -15
- angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
- angr/analyses/decompiler/ssailification/ssailification.py +22 -14
- angr/analyses/decompiler/structured_codegen/c.py +73 -137
- angr/analyses/decompiler/structuring/dream.py +1 -1
- angr/analyses/decompiler/structuring/phoenix.py +6 -1
- angr/analyses/decompiler/structuring/structurer_base.py +2 -1
- angr/analyses/decompiler/utils.py +46 -20
- angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
- angr/analyses/variable_recovery/engine_ail.py +1 -1
- angr/analyses/variable_recovery/engine_vex.py +20 -4
- angr/calling_conventions.py +15 -10
- angr/factory.py +8 -3
- angr/knowledge_plugins/variables/variable_manager.py +7 -5
- angr/simos/simos.py +3 -1
- angr/utils/types.py +48 -0
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/RECORD +40 -39
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
|
@@ -36,19 +36,21 @@ class StatementLocation(LocationBase):
|
|
|
36
36
|
__slots__ = (
|
|
37
37
|
"block_addr",
|
|
38
38
|
"block_idx",
|
|
39
|
+
"phi_stmt",
|
|
39
40
|
"stmt_idx",
|
|
40
41
|
)
|
|
41
42
|
|
|
42
|
-
def __init__(self, block_addr, block_idx, stmt_idx):
|
|
43
|
+
def __init__(self, block_addr, block_idx, stmt_idx, phi_stmt: bool = False):
|
|
43
44
|
self.block_addr = block_addr
|
|
44
45
|
self.block_idx = block_idx
|
|
45
46
|
self.stmt_idx = stmt_idx
|
|
47
|
+
self.phi_stmt = phi_stmt
|
|
46
48
|
|
|
47
49
|
def __repr__(self):
|
|
48
|
-
return f"Loc: Statement@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}"
|
|
50
|
+
return f"Loc: Statement@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}{' phi' if self.phi_stmt else ''}"
|
|
49
51
|
|
|
50
52
|
def __hash__(self):
|
|
51
|
-
return hash((StatementLocation, self.block_addr, self.block_idx, self.stmt_idx))
|
|
53
|
+
return hash((StatementLocation, self.block_addr, self.block_idx, self.stmt_idx, self.phi_stmt))
|
|
52
54
|
|
|
53
55
|
def __eq__(self, other):
|
|
54
56
|
return (
|
|
@@ -56,10 +58,11 @@ class StatementLocation(LocationBase):
|
|
|
56
58
|
and self.block_addr == other.block_addr
|
|
57
59
|
and self.block_idx == other.block_idx
|
|
58
60
|
and self.stmt_idx == other.stmt_idx
|
|
61
|
+
and self.phi_stmt == other.phi_stmt
|
|
59
62
|
)
|
|
60
63
|
|
|
61
64
|
def copy(self):
|
|
62
|
-
return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx)
|
|
65
|
+
return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx, phi_stmt=self.phi_stmt)
|
|
63
66
|
|
|
64
67
|
|
|
65
68
|
class ExpressionLocation(LocationBase):
|
|
@@ -71,23 +74,28 @@ class ExpressionLocation(LocationBase):
|
|
|
71
74
|
"block_addr",
|
|
72
75
|
"block_idx",
|
|
73
76
|
"expr_idx",
|
|
77
|
+
"phi_stmt",
|
|
74
78
|
"stmt_idx",
|
|
75
79
|
)
|
|
76
80
|
|
|
77
|
-
def __init__(self, block_addr, block_idx, stmt_idx, expr_idx):
|
|
81
|
+
def __init__(self, block_addr, block_idx, stmt_idx, expr_idx, phi_stmt: bool = False):
|
|
78
82
|
self.block_addr = block_addr
|
|
79
83
|
self.block_idx = block_idx
|
|
80
84
|
self.stmt_idx = stmt_idx
|
|
81
85
|
self.expr_idx = expr_idx
|
|
86
|
+
self.phi_stmt = phi_stmt
|
|
82
87
|
|
|
83
88
|
def __repr__(self):
|
|
84
|
-
return
|
|
89
|
+
return (
|
|
90
|
+
f"Loc: Expression@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}[{self.expr_idx}]"
|
|
91
|
+
f"{'phi' if self.phi_stmt else ''}"
|
|
92
|
+
)
|
|
85
93
|
|
|
86
94
|
def statement_location(self) -> StatementLocation:
|
|
87
|
-
return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx)
|
|
95
|
+
return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx, phi_stmt=self.phi_stmt)
|
|
88
96
|
|
|
89
97
|
def __hash__(self):
|
|
90
|
-
return hash((ExpressionLocation, self.block_addr, self.block_idx, self.stmt_idx, self.expr_idx))
|
|
98
|
+
return hash((ExpressionLocation, self.block_addr, self.block_idx, self.stmt_idx, self.expr_idx, self.phi_stmt))
|
|
91
99
|
|
|
92
100
|
def __eq__(self, other):
|
|
93
101
|
return (
|
|
@@ -96,6 +104,7 @@ class ExpressionLocation(LocationBase):
|
|
|
96
104
|
and self.block_idx == other.block_idx
|
|
97
105
|
and self.stmt_idx == other.stmt_idx
|
|
98
106
|
and self.expr_idx == other.expr_idx
|
|
107
|
+
and self.phi_stmt == other.phi_stmt
|
|
99
108
|
)
|
|
100
109
|
|
|
101
110
|
|
|
@@ -201,7 +210,18 @@ class ExpressionUseFinder(AILBlockWalker):
|
|
|
201
210
|
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg:
|
|
202
211
|
if not (isinstance(stmt, ailment.Stmt.Assignment) and stmt.dst is expr):
|
|
203
212
|
if block is not None:
|
|
204
|
-
self.uses[expr.varid].add(
|
|
213
|
+
self.uses[expr.varid].add(
|
|
214
|
+
(
|
|
215
|
+
expr,
|
|
216
|
+
ExpressionLocation(
|
|
217
|
+
block.addr,
|
|
218
|
+
block.idx,
|
|
219
|
+
stmt_idx,
|
|
220
|
+
expr_idx,
|
|
221
|
+
phi_stmt=stmt is not None and is_phi_assignment(stmt),
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
)
|
|
205
225
|
else:
|
|
206
226
|
self.uses[expr.varid].add((expr, None))
|
|
207
227
|
return None
|
|
@@ -239,11 +259,7 @@ class ExpressionCounter(SequenceWalker):
|
|
|
239
259
|
if isinstance(stmt, ailment.Stmt.Assignment):
|
|
240
260
|
if is_phi_assignment(stmt):
|
|
241
261
|
return
|
|
242
|
-
if (
|
|
243
|
-
isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
244
|
-
and stmt.dst.was_reg
|
|
245
|
-
and stmt.dst.variable is not None
|
|
246
|
-
):
|
|
262
|
+
if isinstance(stmt.dst, ailment.Expr.VirtualVariable) and stmt.dst.was_reg:
|
|
247
263
|
# dependency
|
|
248
264
|
dependency_finder = ExpressionUseFinder()
|
|
249
265
|
dependency_finder.walk_expression(stmt.src)
|
|
@@ -260,7 +276,6 @@ class ExpressionCounter(SequenceWalker):
|
|
|
260
276
|
isinstance(stmt, ailment.Stmt.Call)
|
|
261
277
|
and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
|
|
262
278
|
and stmt.ret_expr.was_reg
|
|
263
|
-
and stmt.ret_expr.variable is not None
|
|
264
279
|
):
|
|
265
280
|
dependency_finder = ExpressionUseFinder()
|
|
266
281
|
dependency_finder.walk_expression(stmt)
|
|
@@ -279,8 +294,7 @@ class ExpressionCounter(SequenceWalker):
|
|
|
279
294
|
use_finder = ExpressionUseFinder()
|
|
280
295
|
for idx, stmt in enumerate(node.statements):
|
|
281
296
|
self._handle_Statement(idx, stmt, node)
|
|
282
|
-
|
|
283
|
-
use_finder.walk_statement(stmt)
|
|
297
|
+
use_finder.walk_statement(stmt, block=node)
|
|
284
298
|
|
|
285
299
|
for varid, content in use_finder.uses.items():
|
|
286
300
|
if varid not in self.uses:
|
|
@@ -407,7 +421,7 @@ class InterferenceChecker(SequenceWalker):
|
|
|
407
421
|
the_call = None
|
|
408
422
|
if isinstance(stmt, Assignment) and isinstance(stmt.src, ailment.Stmt.Call):
|
|
409
423
|
the_call = stmt.src
|
|
410
|
-
elif isinstance(stmt, ailment.Stmt.Call):
|
|
424
|
+
elif isinstance(stmt, ailment.Stmt.Call) and not isinstance(stmt.target, str):
|
|
411
425
|
the_call = stmt
|
|
412
426
|
if the_call is not None:
|
|
413
427
|
spotter.walk_expression(the_call.target)
|
|
@@ -116,10 +116,13 @@ class RegionSimplifier(Analysis):
|
|
|
116
116
|
for var, uses in expr_counter.uses.items():
|
|
117
117
|
if len(uses) == 1 and var in expr_counter.assignments and len(expr_counter.assignments[var]) == 1:
|
|
118
118
|
definition, deps, loc, has_loads = next(iter(expr_counter.assignments[var]))
|
|
119
|
+
_, use_expr_loc = next(iter(uses))
|
|
120
|
+
if isinstance(use_expr_loc, ExpressionLocation) and use_expr_loc.phi_stmt:
|
|
121
|
+
# we cannot fold expressions that are used in phi statements
|
|
122
|
+
continue
|
|
119
123
|
if has_loads:
|
|
120
124
|
# the definition has at least one load expression. we need to ensure there are no store statements
|
|
121
125
|
# between the definition site and the use site
|
|
122
|
-
_, use_expr_loc = next(iter(uses))
|
|
123
126
|
if isinstance(use_expr_loc, ExpressionLocation):
|
|
124
127
|
use_loc = use_expr_loc.statement_location()
|
|
125
128
|
else:
|
|
@@ -8,21 +8,20 @@ import networkx
|
|
|
8
8
|
|
|
9
9
|
import ailment
|
|
10
10
|
from ailment import Block
|
|
11
|
-
from ailment.expression import
|
|
12
|
-
from ailment.statement import
|
|
11
|
+
from ailment.expression import Phi, VirtualVariable, VirtualVariableCategory
|
|
12
|
+
from ailment.statement import Assignment, Label
|
|
13
13
|
|
|
14
14
|
from angr.code_location import CodeLocation
|
|
15
15
|
from angr.analyses import ForwardAnalysis
|
|
16
|
-
from angr.analyses.forward_analysis.visitors.graph import NodeType
|
|
17
16
|
from angr.analyses.forward_analysis import FunctionGraphVisitor
|
|
18
|
-
from .rewriting_engine import SimEngineSSARewriting
|
|
17
|
+
from .rewriting_engine import SimEngineSSARewriting, DefExprType, AT
|
|
19
18
|
from .rewriting_state import RewritingState
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
l = logging.getLogger(__name__)
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
class RewritingAnalysis(ForwardAnalysis[RewritingState,
|
|
24
|
+
class RewritingAnalysis(ForwardAnalysis[RewritingState, ailment.Block, object, object]):
|
|
26
25
|
"""
|
|
27
26
|
RewritingAnalysis traverses the AIL graph, inserts phi nodes, and rewrites all expression uses to virtual variables
|
|
28
27
|
when necessary.
|
|
@@ -37,7 +36,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
37
36
|
bp_as_gpr: bool,
|
|
38
37
|
udef_to_phiid: dict[tuple, set[int]],
|
|
39
38
|
phiid_to_loc: dict[int, tuple[int, int | None]],
|
|
40
|
-
stackvar_locs: dict[int, int],
|
|
39
|
+
stackvar_locs: dict[int, set[int]],
|
|
41
40
|
rewrite_tmps: bool,
|
|
42
41
|
ail_manager,
|
|
43
42
|
func_args: set[VirtualVariable],
|
|
@@ -75,7 +74,11 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
75
74
|
|
|
76
75
|
self._analyze()
|
|
77
76
|
|
|
78
|
-
self.def_to_vvid: dict[tuple[int, int | None, int,
|
|
77
|
+
self.def_to_vvid: dict[tuple[int, int | None, int, DefExprType, AT], int] = self._engine_ail.def_to_vvid
|
|
78
|
+
# during SSA conversion, we create secondary stack variables because they overlap and are larger than the
|
|
79
|
+
# actual stack variables. these secondary stack variables can be safely eliminated during dead assignment
|
|
80
|
+
# elimination if not used by anything else.
|
|
81
|
+
self.secondary_stackvars: set[int] = self._engine_ail.secondary_stackvars
|
|
79
82
|
self.out_graph = self._make_new_graph(ail_graph)
|
|
80
83
|
|
|
81
84
|
@property
|
|
@@ -177,10 +180,11 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
177
180
|
def _reg_predicate(self, node_, *, reg_offset: int, reg_size: int) -> tuple[bool, Any]:
|
|
178
181
|
out_state: RewritingState = self.out_states[(node_.addr, node_.idx)]
|
|
179
182
|
if reg_offset in out_state.registers and reg_size in out_state.registers[reg_offset]:
|
|
180
|
-
|
|
183
|
+
existing_var = out_state.registers[reg_offset][reg_size]
|
|
184
|
+
if existing_var is None:
|
|
181
185
|
# the vvar is not set. it should never be referenced
|
|
182
186
|
return True, None
|
|
183
|
-
vvar =
|
|
187
|
+
vvar = existing_var.copy()
|
|
184
188
|
vvar.idx = self._ail_manager.next_atom()
|
|
185
189
|
return True, vvar
|
|
186
190
|
return False, None
|
|
@@ -188,10 +192,11 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
188
192
|
def _stack_predicate(self, node_, *, stack_offset: int, stackvar_size: int) -> tuple[bool, Any]:
|
|
189
193
|
out_state: RewritingState = self.out_states[(node_.addr, node_.idx)]
|
|
190
194
|
if stack_offset in out_state.stackvars and stackvar_size in out_state.stackvars[stack_offset]:
|
|
191
|
-
|
|
195
|
+
existing_var = out_state.stackvars[stack_offset][stackvar_size]
|
|
196
|
+
if existing_var is None:
|
|
192
197
|
# the vvar is not set. it should never be referenced
|
|
193
198
|
return True, None
|
|
194
|
-
vvar =
|
|
199
|
+
vvar = existing_var.copy()
|
|
195
200
|
vvar.idx = self._ail_manager.next_atom()
|
|
196
201
|
return True, vvar
|
|
197
202
|
return False, None
|
|
@@ -215,11 +220,14 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
215
220
|
)
|
|
216
221
|
# update state with function arguments
|
|
217
222
|
for func_arg in self._func_args:
|
|
218
|
-
if func_arg.
|
|
219
|
-
reg_offset, reg_size = func_arg.
|
|
223
|
+
if func_arg.parameter_category == VirtualVariableCategory.REGISTER:
|
|
224
|
+
reg_offset, reg_size = func_arg.parameter_reg_offset, func_arg.size
|
|
225
|
+
assert reg_offset is not None and reg_size is not None
|
|
220
226
|
state.registers[reg_offset][reg_size] = func_arg
|
|
221
|
-
elif func_arg.
|
|
222
|
-
|
|
227
|
+
elif func_arg.parameter_category == VirtualVariableCategory.STACK:
|
|
228
|
+
parameter_stack_offset: int = func_arg.oident[1] # type: ignore
|
|
229
|
+
assert parameter_stack_offset is not None and func_arg.size is not None
|
|
230
|
+
state.stackvars[parameter_stack_offset][func_arg.size] = func_arg
|
|
223
231
|
return state
|
|
224
232
|
|
|
225
233
|
def _run_on_node(self, node, state: RewritingState):
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# pylint:disable=no-self-use,unused-argument
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import Literal
|
|
3
4
|
import logging
|
|
4
5
|
|
|
6
|
+
from archinfo import Endness
|
|
5
7
|
from ailment.manager import Manager
|
|
6
8
|
from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement, Jump
|
|
7
9
|
from ailment.expression import (
|
|
@@ -22,6 +24,7 @@ from ailment.expression import (
|
|
|
22
24
|
Reinterpret,
|
|
23
25
|
)
|
|
24
26
|
|
|
27
|
+
from angr.knowledge_plugins.key_definitions import atoms
|
|
25
28
|
from angr.engines.light.engine import SimEngineNostmtAIL
|
|
26
29
|
from angr.utils.ssa import get_reg_offset_base_and_size
|
|
27
30
|
from .rewriting_state import RewritingState
|
|
@@ -30,6 +33,10 @@ from .rewriting_state import RewritingState
|
|
|
30
33
|
_l = logging.getLogger(__name__)
|
|
31
34
|
|
|
32
35
|
|
|
36
|
+
DefExprType = atoms.Register | atoms.Tmp | atoms.MemoryLocation
|
|
37
|
+
AT = Literal["l", "s"] | None
|
|
38
|
+
|
|
39
|
+
|
|
33
40
|
class SimEngineSSARewriting(
|
|
34
41
|
SimEngineNostmtAIL[RewritingState, Expression | None, Statement | tuple[Statement, ...], None]
|
|
35
42
|
):
|
|
@@ -38,6 +45,8 @@ class SimEngineSSARewriting(
|
|
|
38
45
|
copies at each use location.
|
|
39
46
|
"""
|
|
40
47
|
|
|
48
|
+
state: RewritingState
|
|
49
|
+
|
|
41
50
|
def __init__(
|
|
42
51
|
self,
|
|
43
52
|
project,
|
|
@@ -45,7 +54,7 @@ class SimEngineSSARewriting(
|
|
|
45
54
|
sp_tracker,
|
|
46
55
|
udef_to_phiid: dict[tuple, set[int]],
|
|
47
56
|
phiid_to_loc: dict[int, tuple[int, int | None]],
|
|
48
|
-
stackvar_locs: dict[int, int],
|
|
57
|
+
stackvar_locs: dict[int, set[int]],
|
|
49
58
|
ail_manager: Manager,
|
|
50
59
|
vvar_id_start: int = 0,
|
|
51
60
|
bp_as_gpr: bool = False,
|
|
@@ -55,13 +64,15 @@ class SimEngineSSARewriting(
|
|
|
55
64
|
|
|
56
65
|
self.sp_tracker = sp_tracker
|
|
57
66
|
self.bp_as_gpr = bp_as_gpr
|
|
58
|
-
self.def_to_vvid: dict[tuple[int, int | None, int,
|
|
67
|
+
self.def_to_vvid: dict[tuple[int, int | None, int, DefExprType, AT], int] = {}
|
|
59
68
|
self.stackvar_locs = stackvar_locs
|
|
60
69
|
self.udef_to_phiid = udef_to_phiid
|
|
61
70
|
self.phiid_to_loc = phiid_to_loc
|
|
62
71
|
self.rewrite_tmps = rewrite_tmps
|
|
63
72
|
self.ail_manager = ail_manager
|
|
64
73
|
|
|
74
|
+
self.secondary_stackvars: set[int] = set()
|
|
75
|
+
|
|
65
76
|
self._current_vvar_id = vvar_id_start
|
|
66
77
|
|
|
67
78
|
@property
|
|
@@ -103,6 +114,7 @@ class SimEngineSSARewriting(
|
|
|
103
114
|
elif stmt.dst.category == VirtualVariableCategory.STACK:
|
|
104
115
|
self.state.stackvars[stmt.dst.stack_offset][stmt.dst.size] = stmt.dst
|
|
105
116
|
elif stmt.dst.category == VirtualVariableCategory.TMP:
|
|
117
|
+
assert stmt.dst.tmp_idx is not None
|
|
106
118
|
self.state.tmps[stmt.dst.tmp_idx] = stmt.dst
|
|
107
119
|
new_dst = None
|
|
108
120
|
else:
|
|
@@ -134,10 +146,11 @@ class SimEngineSSARewriting(
|
|
|
134
146
|
base_reg_vvar = self._replace_def_expr(
|
|
135
147
|
self.block.addr, self.block.idx, self.stmt_idx, base_reg_expr
|
|
136
148
|
)
|
|
149
|
+
assert base_reg_vvar is not None
|
|
137
150
|
stmt_base_reg = Assignment(
|
|
138
151
|
self.ail_manager.next_atom(),
|
|
139
152
|
base_reg_vvar,
|
|
140
|
-
self.
|
|
153
|
+
self._partial_update_expr(
|
|
141
154
|
existing_base_reg_vvar, base_offset, base_size, new_dst, stmt.dst.reg_offset, stmt.dst.size
|
|
142
155
|
),
|
|
143
156
|
**stmt.tags,
|
|
@@ -160,12 +173,40 @@ class SimEngineSSARewriting(
|
|
|
160
173
|
return new_stmt
|
|
161
174
|
return None
|
|
162
175
|
|
|
163
|
-
def _handle_stmt_Store(self, stmt: Store) -> Store | Assignment | None:
|
|
176
|
+
def _handle_stmt_Store(self, stmt: Store) -> Store | Assignment | tuple[Assignment, ...] | None:
|
|
164
177
|
new_data = self._expr(stmt.data)
|
|
165
178
|
if stmt.guard is None:
|
|
179
|
+
# the variable
|
|
166
180
|
vvar = self._replace_def_store(self.block.addr, self.block.idx, self.stmt_idx, stmt)
|
|
167
181
|
if vvar is not None:
|
|
168
|
-
|
|
182
|
+
assert isinstance(stmt.addr, StackBaseOffset) and isinstance(stmt.addr.offset, int)
|
|
183
|
+
|
|
184
|
+
# remove everything else that overlaps with the full (base) stack variable
|
|
185
|
+
# the full stack variable is kept around because it's always updated immediately and will be used in
|
|
186
|
+
# case of partial stack variable update
|
|
187
|
+
self._clear_overlapping_stackvars(stmt.addr.offset, stmt.size, remove_base_stackvar=False)
|
|
188
|
+
|
|
189
|
+
data = stmt.data if new_data is None else new_data
|
|
190
|
+
vvar_assignment = Assignment(stmt.idx, vvar, data, **stmt.tags)
|
|
191
|
+
|
|
192
|
+
full_size = self._get_stack_var_full_size(stmt)
|
|
193
|
+
assert full_size is not None
|
|
194
|
+
if vvar.size >= full_size:
|
|
195
|
+
return vvar_assignment
|
|
196
|
+
|
|
197
|
+
# update the full variable
|
|
198
|
+
existing_full_vvar = self._replace_use_load(Load(None, stmt.addr, full_size, stmt.endness))
|
|
199
|
+
vvar_full = self._replace_def_store(
|
|
200
|
+
self.block.addr, self.block.idx, self.stmt_idx, stmt, force_size=full_size
|
|
201
|
+
)
|
|
202
|
+
if existing_full_vvar is not None and vvar_full is not None:
|
|
203
|
+
self.secondary_stackvars.add(vvar_full.varid)
|
|
204
|
+
full_data = self._partial_update_expr(
|
|
205
|
+
existing_full_vvar, stmt.addr.offset, full_size, vvar, stmt.addr.offset, stmt.size
|
|
206
|
+
)
|
|
207
|
+
full_assignment = Assignment(stmt.idx, vvar_full, full_data, **stmt.tags)
|
|
208
|
+
return vvar_assignment, full_assignment
|
|
209
|
+
return vvar_assignment
|
|
169
210
|
|
|
170
211
|
# fall back to Store
|
|
171
212
|
new_addr = self._expr(stmt.addr)
|
|
@@ -210,7 +251,7 @@ class SimEngineSSARewriting(
|
|
|
210
251
|
def _handle_stmt_Call(self, stmt: Call) -> Call | None:
|
|
211
252
|
changed = False
|
|
212
253
|
|
|
213
|
-
new_target = self._replace_use_expr(stmt.target)
|
|
254
|
+
new_target = self._replace_use_expr(stmt.target) if not isinstance(stmt.target, str) else None
|
|
214
255
|
new_ret_expr = (
|
|
215
256
|
self._replace_def_expr(self.block.addr, self.block.idx, self.stmt_idx, stmt.ret_expr)
|
|
216
257
|
if stmt.ret_expr is not None
|
|
@@ -275,7 +316,7 @@ class SimEngineSSARewriting(
|
|
|
275
316
|
assert isinstance(dirty, DirtyExpression)
|
|
276
317
|
return DirtyStatement(stmt.idx, dirty, **stmt.tags)
|
|
277
318
|
|
|
278
|
-
def _handle_expr_Register(self, expr: Register) -> VirtualVariable | None:
|
|
319
|
+
def _handle_expr_Register(self, expr: Register) -> VirtualVariable | Expression | None:
|
|
279
320
|
return self._replace_use_reg(expr)
|
|
280
321
|
|
|
281
322
|
def _handle_expr_Tmp(self, expr: Tmp) -> VirtualVariable | None:
|
|
@@ -460,9 +501,9 @@ class SimEngineSSARewriting(
|
|
|
460
501
|
# Expression replacement
|
|
461
502
|
#
|
|
462
503
|
|
|
463
|
-
def
|
|
504
|
+
def _partial_update_expr(
|
|
464
505
|
self,
|
|
465
|
-
existing_vvar:
|
|
506
|
+
existing_vvar: Expression,
|
|
466
507
|
base_offset: int,
|
|
467
508
|
base_size: int,
|
|
468
509
|
new_vvar: VirtualVariable,
|
|
@@ -546,7 +587,7 @@ class SimEngineSSARewriting(
|
|
|
546
587
|
"""
|
|
547
588
|
|
|
548
589
|
# get the virtual variable ID
|
|
549
|
-
vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, expr)
|
|
590
|
+
vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, atoms.Register(expr.reg_offset, expr.size), "s")
|
|
550
591
|
return VirtualVariable(
|
|
551
592
|
expr.idx,
|
|
552
593
|
vvid,
|
|
@@ -578,32 +619,51 @@ class SimEngineSSARewriting(
|
|
|
578
619
|
)
|
|
579
620
|
self.state.registers[base_off][base_size] = vvar
|
|
580
621
|
return vvar
|
|
581
|
-
|
|
622
|
+
existing_var = self.state.registers[base_off][base_size]
|
|
623
|
+
assert existing_var is not None
|
|
624
|
+
return existing_var
|
|
625
|
+
|
|
626
|
+
def _get_stack_var_full_size(self, stmt: Store) -> int | None:
|
|
627
|
+
if (
|
|
628
|
+
isinstance(stmt.addr, StackBaseOffset)
|
|
629
|
+
and isinstance(stmt.addr.offset, int)
|
|
630
|
+
and stmt.addr.offset in self.stackvar_locs
|
|
631
|
+
and stmt.size in self.stackvar_locs[stmt.addr.offset]
|
|
632
|
+
):
|
|
633
|
+
return max(self.stackvar_locs[stmt.addr.offset])
|
|
634
|
+
return None
|
|
582
635
|
|
|
583
636
|
def _replace_def_store(
|
|
584
|
-
self, block_addr: int, block_idx: int | None, stmt_idx: int, stmt: Store
|
|
637
|
+
self, block_addr: int, block_idx: int | None, stmt_idx: int, stmt: Store, force_size: int | None = None
|
|
585
638
|
) -> VirtualVariable | None:
|
|
586
639
|
if (
|
|
587
640
|
isinstance(stmt.addr, StackBaseOffset)
|
|
588
641
|
and isinstance(stmt.addr.offset, int)
|
|
589
642
|
and stmt.addr.offset in self.stackvar_locs
|
|
590
|
-
and stmt.size
|
|
643
|
+
and stmt.size in self.stackvar_locs[stmt.addr.offset]
|
|
591
644
|
):
|
|
592
|
-
|
|
645
|
+
size = stmt.size if force_size is None else force_size
|
|
646
|
+
vvar_id = self.get_vvid_by_def(
|
|
647
|
+
block_addr,
|
|
648
|
+
block_idx,
|
|
649
|
+
stmt_idx,
|
|
650
|
+
atoms.MemoryLocation(stmt.addr.offset, size, Endness(stmt.endness)),
|
|
651
|
+
"s",
|
|
652
|
+
)
|
|
593
653
|
vvar = VirtualVariable(
|
|
594
654
|
self.ail_manager.next_atom(),
|
|
595
655
|
vvar_id,
|
|
596
|
-
|
|
656
|
+
size * self.arch.byte_width,
|
|
597
657
|
category=VirtualVariableCategory.STACK,
|
|
598
658
|
oident=stmt.addr.offset,
|
|
599
659
|
**stmt.tags,
|
|
600
660
|
)
|
|
601
|
-
self.state.stackvars[stmt.addr.offset][
|
|
661
|
+
self.state.stackvars[stmt.addr.offset][size] = vvar
|
|
602
662
|
return vvar
|
|
603
663
|
return None
|
|
604
664
|
|
|
605
665
|
def _replace_def_tmp(self, block_addr: int, block_idx: int | None, stmt_idx: int, expr: Tmp) -> VirtualVariable:
|
|
606
|
-
vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, expr)
|
|
666
|
+
vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, atoms.Tmp(expr.tmp_idx, expr.size), "s")
|
|
607
667
|
vvar = VirtualVariable(
|
|
608
668
|
expr.idx,
|
|
609
669
|
vvid,
|
|
@@ -615,7 +675,7 @@ class SimEngineSSARewriting(
|
|
|
615
675
|
self.state.tmps[expr.tmp_idx] = vvar
|
|
616
676
|
return vvar
|
|
617
677
|
|
|
618
|
-
def _replace_use_expr(self, thing: Expression | Statement) -> VirtualVariable | None:
|
|
678
|
+
def _replace_use_expr(self, thing: Expression | Statement) -> VirtualVariable | Expression | None:
|
|
619
679
|
"""
|
|
620
680
|
Return a new virtual variable for the given defined expression.
|
|
621
681
|
"""
|
|
@@ -670,7 +730,7 @@ class SimEngineSSARewriting(
|
|
|
670
730
|
elif reg_expr.size > existing_size:
|
|
671
731
|
# part of the variable exists... maybe it's a parameter?
|
|
672
732
|
vvar = self.state.registers[reg_expr.reg_offset][existing_size]
|
|
673
|
-
if vvar.category == VirtualVariableCategory.PARAMETER:
|
|
733
|
+
if vvar is not None and vvar.category == VirtualVariableCategory.PARAMETER:
|
|
674
734
|
# just zero-extend it
|
|
675
735
|
return Convert(
|
|
676
736
|
self.ail_manager.next_atom(),
|
|
@@ -698,7 +758,7 @@ class SimEngineSSARewriting(
|
|
|
698
758
|
shift_amount = Const(
|
|
699
759
|
self.ail_manager.next_atom(),
|
|
700
760
|
None,
|
|
701
|
-
(reg_expr.reg_offset - vvar.
|
|
761
|
+
(reg_expr.reg_offset - vvar.reg_offset) * self.arch.byte_width,
|
|
702
762
|
8,
|
|
703
763
|
**reg_expr.tags,
|
|
704
764
|
)
|
|
@@ -729,11 +789,17 @@ class SimEngineSSARewriting(
|
|
|
729
789
|
isinstance(expr.addr, StackBaseOffset)
|
|
730
790
|
and isinstance(expr.addr.offset, int)
|
|
731
791
|
and expr.addr.offset in self.stackvar_locs
|
|
732
|
-
and expr.size
|
|
792
|
+
and expr.size in self.stackvar_locs[expr.addr.offset]
|
|
733
793
|
):
|
|
734
794
|
if expr.size not in self.state.stackvars[expr.addr.offset]:
|
|
735
795
|
# create it on the fly
|
|
736
|
-
vvar_id = self.get_vvid_by_def(
|
|
796
|
+
vvar_id = self.get_vvid_by_def(
|
|
797
|
+
self.block.addr,
|
|
798
|
+
self.block.idx,
|
|
799
|
+
self.stmt_idx,
|
|
800
|
+
atoms.MemoryLocation(expr.addr.offset, expr.size, Endness(expr.endness)),
|
|
801
|
+
"l",
|
|
802
|
+
)
|
|
737
803
|
return VirtualVariable(
|
|
738
804
|
self.ail_manager.next_atom(),
|
|
739
805
|
vvar_id,
|
|
@@ -783,9 +849,9 @@ class SimEngineSSARewriting(
|
|
|
783
849
|
#
|
|
784
850
|
|
|
785
851
|
def get_vvid_by_def(
|
|
786
|
-
self, block_addr: int, block_idx: int | None, stmt_idx: int, thing:
|
|
852
|
+
self, block_addr: int, block_idx: int | None, stmt_idx: int, thing: DefExprType, access_type: AT
|
|
787
853
|
) -> int:
|
|
788
|
-
key = block_addr, block_idx, stmt_idx, thing
|
|
854
|
+
key = block_addr, block_idx, stmt_idx, thing, access_type
|
|
789
855
|
if key in self.def_to_vvid:
|
|
790
856
|
return self.def_to_vvid[key]
|
|
791
857
|
vvid = self.next_vvar_id()
|
|
@@ -802,6 +868,21 @@ class SimEngineSSARewriting(
|
|
|
802
868
|
else:
|
|
803
869
|
del self.state.registers[off]
|
|
804
870
|
|
|
871
|
+
def _clear_overlapping_stackvars(self, stack_offset: int, size: int, remove_base_stackvar: bool = True) -> None:
|
|
872
|
+
for off in range(stack_offset, stack_offset + size):
|
|
873
|
+
if off in self.state.stackvars:
|
|
874
|
+
if (
|
|
875
|
+
not remove_base_stackvar
|
|
876
|
+
and off in self.stackvar_locs
|
|
877
|
+
and off == stack_offset
|
|
878
|
+
and (base_size := max(self.stackvar_locs[off])) == size
|
|
879
|
+
and base_size in self.state.stackvars[off]
|
|
880
|
+
):
|
|
881
|
+
if len(self.state.stackvars[off]) > 1:
|
|
882
|
+
self.state.stackvars[off] = {base_size: self.state.stackvars[off][base_size]}
|
|
883
|
+
else:
|
|
884
|
+
del self.state.stackvars[off]
|
|
885
|
+
|
|
805
886
|
def _unreachable(self, *args, **kwargs):
|
|
806
887
|
assert False
|
|
807
888
|
|
|
@@ -79,7 +79,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
|
|
|
79
79
|
# calculate virtual variables and phi nodes
|
|
80
80
|
self._udef_to_phiid: dict[tuple, set[int]] = None
|
|
81
81
|
self._phiid_to_loc: dict[int, tuple[int, int | None]] = None
|
|
82
|
-
self._stackvar_locs: dict[int, int] = None
|
|
82
|
+
self._stackvar_locs: dict[int, set[int]] = None
|
|
83
83
|
self._calculate_virtual_variables(ail_graph, traversal.def_to_loc, traversal.loc_to_defs)
|
|
84
84
|
|
|
85
85
|
# insert phi variables and rewrite uses
|
|
@@ -97,6 +97,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
|
|
|
97
97
|
self._func_args,
|
|
98
98
|
vvar_id_start=vvar_id_start,
|
|
99
99
|
)
|
|
100
|
+
self.secondary_stackvars = rewriter.secondary_stackvars
|
|
100
101
|
self.out_graph = rewriter.out_graph
|
|
101
102
|
self.max_vvar_id = rewriter.max_vvar_id
|
|
102
103
|
|
|
@@ -130,7 +131,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
|
|
|
130
131
|
if self._func_args:
|
|
131
132
|
for func_arg in self._func_args:
|
|
132
133
|
if func_arg.oident[0] == VirtualVariableCategory.STACK:
|
|
133
|
-
stackvar_locs[func_arg.oident[1]] = func_arg.size
|
|
134
|
+
stackvar_locs[func_arg.oident[1]] = {func_arg.size}
|
|
134
135
|
sorted_stackvar_offs = sorted(stackvar_locs)
|
|
135
136
|
else:
|
|
136
137
|
stackvar_locs = {}
|
|
@@ -157,8 +158,13 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
|
|
|
157
158
|
off = sorted_stackvar_offs[i]
|
|
158
159
|
if off >= def_.addr.offset + def_.size:
|
|
159
160
|
break
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
full_sz = max(stackvar_locs[off])
|
|
162
|
+
udef_to_defs[("stack", off, full_sz)].add(def_)
|
|
163
|
+
udef_to_blockkeys[("stack", off, full_sz)].add((loc.block_addr, loc.block_idx))
|
|
164
|
+
# add a definition for the partial stack variable
|
|
165
|
+
if def_.size in stackvar_locs[off] and def_.size < full_sz:
|
|
166
|
+
udef_to_defs[("stack", off, def_.size)].add(def_)
|
|
167
|
+
udef_to_blockkeys[("stack", off, def_.size)].add((loc.block_addr, loc.block_idx))
|
|
162
168
|
elif isinstance(def_, Tmp):
|
|
163
169
|
# Tmps are local to each block and do not need phi nodes
|
|
164
170
|
pass
|
|
@@ -197,7 +203,15 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
|
|
|
197
203
|
return last_frontier
|
|
198
204
|
|
|
199
205
|
@staticmethod
|
|
200
|
-
def _synthesize_stackvar_locs(defs: list[Store]) -> dict[int, int]:
|
|
206
|
+
def _synthesize_stackvar_locs(defs: list[Store]) -> dict[int, set[int]]:
|
|
207
|
+
"""
|
|
208
|
+
Derive potential locations (in terms of offsets and sizes) for stack variables based on all stack variable
|
|
209
|
+
definitions provided.
|
|
210
|
+
|
|
211
|
+
:param defs: Store definitions.
|
|
212
|
+
:return: A dictionary of stack variable offsets and their sizes.
|
|
213
|
+
"""
|
|
214
|
+
|
|
201
215
|
accesses: defaultdict[int, set[int]] = defaultdict(set)
|
|
202
216
|
offs: set[int] = set()
|
|
203
217
|
|
|
@@ -208,7 +222,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
|
|
|
208
222
|
offs.add(stack_off)
|
|
209
223
|
|
|
210
224
|
sorted_offs = sorted(offs)
|
|
211
|
-
locs: dict[int, int] = {}
|
|
225
|
+
locs: dict[int, set[int]] = {}
|
|
212
226
|
for idx, off in enumerate(sorted_offs):
|
|
213
227
|
sorted_sizes = sorted(accesses[off])
|
|
214
228
|
if idx < len(sorted_offs) - 1:
|
|
@@ -217,14 +231,8 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
|
|
|
217
231
|
else:
|
|
218
232
|
allowed_sizes = sorted_sizes
|
|
219
233
|
|
|
220
|
-
if
|
|
221
|
-
locs[off] = allowed_sizes
|
|
222
|
-
# else:
|
|
223
|
-
# last_off = off
|
|
224
|
-
# for a in allowed_sizes:
|
|
225
|
-
# locs[off + a] = off + a - last_off
|
|
226
|
-
# last_off = off + a
|
|
227
|
-
# TODO: Update locs for sizes beyond allowed_sizes
|
|
234
|
+
if allowed_sizes:
|
|
235
|
+
locs[off] = set(allowed_sizes)
|
|
228
236
|
|
|
229
237
|
return locs
|
|
230
238
|
|