angr 9.2.139__py3-none-manylinux2014_aarch64.whl → 9.2.141__py3-none-manylinux2014_aarch64.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 +136 -53
- angr/analyses/calling_convention/fact_collector.py +44 -18
- angr/analyses/calling_convention/utils.py +3 -1
- angr/analyses/cfg/cfg_base.py +13 -0
- angr/analyses/cfg/cfg_fast.py +11 -0
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
- angr/analyses/decompiler/ail_simplifier.py +115 -72
- angr/analyses/decompiler/callsite_maker.py +24 -11
- angr/analyses/decompiler/clinic.py +78 -43
- angr/analyses/decompiler/decompiler.py +18 -7
- angr/analyses/decompiler/expression_narrower.py +1 -1
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
- 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/ite_region_converter.py +21 -13
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +92 -11
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +53 -9
- 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 +287 -122
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +31 -13
- 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 +22 -18
- angr/analyses/decompiler/structuring/phoenix.py +158 -41
- angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
- angr/analyses/decompiler/structuring/structurer_base.py +37 -10
- angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
- angr/analyses/decompiler/utils.py +106 -21
- angr/analyses/deobfuscator/api_obf_finder.py +8 -5
- angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
- angr/analyses/deobfuscator/string_obf_finder.py +105 -18
- angr/analyses/forward_analysis/forward_analysis.py +1 -1
- angr/analyses/propagator/top_checker_mixin.py +6 -6
- angr/analyses/reaching_definitions/__init__.py +2 -1
- angr/analyses/reaching_definitions/dep_graph.py +1 -12
- angr/analyses/reaching_definitions/engine_vex.py +36 -31
- angr/analyses/reaching_definitions/function_handler.py +15 -2
- angr/analyses/reaching_definitions/rd_state.py +1 -37
- angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
- angr/analyses/s_propagator.py +6 -41
- angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
- angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
- angr/analyses/stack_pointer_tracker.py +36 -22
- angr/analyses/typehoon/simple_solver.py +45 -7
- angr/analyses/typehoon/typeconsts.py +18 -5
- angr/analyses/variable_recovery/engine_ail.py +1 -1
- angr/analyses/variable_recovery/engine_base.py +7 -5
- angr/analyses/variable_recovery/engine_vex.py +20 -4
- angr/block.py +69 -107
- angr/callable.py +14 -7
- angr/calling_conventions.py +30 -11
- angr/distributed/__init__.py +1 -1
- angr/engines/__init__.py +7 -8
- angr/engines/engine.py +1 -120
- angr/engines/failure.py +2 -2
- angr/engines/hook.py +2 -2
- angr/engines/light/engine.py +2 -2
- angr/engines/pcode/engine.py +2 -14
- angr/engines/procedure.py +2 -2
- angr/engines/soot/engine.py +2 -2
- angr/engines/soot/statements/switch.py +1 -1
- angr/engines/successors.py +124 -11
- angr/engines/syscall.py +2 -2
- angr/engines/unicorn.py +3 -3
- angr/engines/vex/heavy/heavy.py +3 -15
- angr/factory.py +12 -22
- angr/knowledge_plugins/key_definitions/atoms.py +8 -4
- angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
- angr/knowledge_plugins/variables/variable_manager.py +7 -5
- angr/sim_type.py +19 -17
- angr/simos/simos.py +3 -1
- angr/state_plugins/plugin.py +19 -4
- angr/storage/memory_mixins/memory_mixin.py +1 -1
- angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
- angr/utils/ssa/__init__.py +119 -4
- angr/utils/types.py +48 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/RECORD +87 -86
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
|
@@ -12,7 +12,13 @@ from .if_ import IfSimplifier
|
|
|
12
12
|
from .cascading_ifs import CascadingIfsRemover
|
|
13
13
|
from .ifelse import IfElseFlattener
|
|
14
14
|
from .loop import LoopSimplifier
|
|
15
|
-
from .expr_folding import
|
|
15
|
+
from .expr_folding import (
|
|
16
|
+
ExpressionCounter,
|
|
17
|
+
ExpressionFolder,
|
|
18
|
+
StoreStatementFinder,
|
|
19
|
+
ExpressionLocation,
|
|
20
|
+
InterferenceChecker,
|
|
21
|
+
)
|
|
16
22
|
from .cascading_cond_transformer import CascadingConditionTransformer
|
|
17
23
|
from .switch_expr_simplifier import SwitchExpressionSimplifier
|
|
18
24
|
from .switch_cluster_simplifier import SwitchClusterFinder, simplify_switch_clusters, simplify_lowered_switches
|
|
@@ -23,10 +29,17 @@ class RegionSimplifier(Analysis):
|
|
|
23
29
|
Simplifies a given region.
|
|
24
30
|
"""
|
|
25
31
|
|
|
26
|
-
def __init__(
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
func,
|
|
35
|
+
region,
|
|
36
|
+
arg_vvars: set[int] | None = None,
|
|
37
|
+
simplify_switches: bool = True,
|
|
38
|
+
simplify_ifelse: bool = True,
|
|
39
|
+
):
|
|
27
40
|
self.func = func
|
|
28
41
|
self.region = region
|
|
29
|
-
self.
|
|
42
|
+
self.arg_vvars = arg_vvars
|
|
30
43
|
self._simplify_switches = simplify_switches
|
|
31
44
|
self._should_simplify_ifelses = simplify_ifelse
|
|
32
45
|
|
|
@@ -54,7 +67,7 @@ class RegionSimplifier(Analysis):
|
|
|
54
67
|
# Remove empty nodes again
|
|
55
68
|
r = self._remove_empty_nodes(r)
|
|
56
69
|
|
|
57
|
-
if self.
|
|
70
|
+
if self.arg_vvars is not None:
|
|
58
71
|
# Fold expressions that are only used once into their use sites
|
|
59
72
|
r = self._fold_oneuse_expressions(r)
|
|
60
73
|
r = self._remove_empty_nodes(r)
|
|
@@ -88,12 +101,8 @@ class RegionSimplifier(Analysis):
|
|
|
88
101
|
#
|
|
89
102
|
|
|
90
103
|
def _fold_oneuse_expressions(self, region):
|
|
91
|
-
# Disabled until https://github.com/angr/angr/issues/5110 and related folding issues fixed
|
|
92
|
-
return region
|
|
93
|
-
|
|
94
104
|
# pylint:disable=unreachable
|
|
95
|
-
|
|
96
|
-
expr_counter = ExpressionCounter(region, variable_manager)
|
|
105
|
+
expr_counter = ExpressionCounter(region)
|
|
97
106
|
|
|
98
107
|
variable_assignments = {}
|
|
99
108
|
variable_uses = {}
|
|
@@ -107,10 +116,13 @@ class RegionSimplifier(Analysis):
|
|
|
107
116
|
for var, uses in expr_counter.uses.items():
|
|
108
117
|
if len(uses) == 1 and var in expr_counter.assignments and len(expr_counter.assignments[var]) == 1:
|
|
109
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
|
|
110
123
|
if has_loads:
|
|
111
124
|
# the definition has at least one load expression. we need to ensure there are no store statements
|
|
112
125
|
# between the definition site and the use site
|
|
113
|
-
_, use_expr_loc = next(iter(uses))
|
|
114
126
|
if isinstance(use_expr_loc, ExpressionLocation):
|
|
115
127
|
use_loc = use_expr_loc.statement_location()
|
|
116
128
|
else:
|
|
@@ -130,7 +142,7 @@ class RegionSimplifier(Analysis):
|
|
|
130
142
|
# make sure all variables that var depends on has been assigned at most once
|
|
131
143
|
fail = False
|
|
132
144
|
for dep_var in deps:
|
|
133
|
-
if dep_var.
|
|
145
|
+
if self.arg_vvars is not None and dep_var in self.arg_vvars:
|
|
134
146
|
continue
|
|
135
147
|
if dep_var in expr_counter.assignments and len(expr_counter.assignments[dep_var]) > 1:
|
|
136
148
|
fail = True
|
|
@@ -155,8 +167,14 @@ class RegionSimplifier(Analysis):
|
|
|
155
167
|
del variable_assignments[var]
|
|
156
168
|
del variable_uses[var]
|
|
157
169
|
|
|
158
|
-
#
|
|
159
|
-
|
|
170
|
+
# ensure there is no interference between the call site and the use site
|
|
171
|
+
checker = InterferenceChecker(variable_assignments, variable_uses, region)
|
|
172
|
+
for varid in checker.interfered_assignments:
|
|
173
|
+
if varid in variable_assignments:
|
|
174
|
+
del variable_assignments[varid]
|
|
175
|
+
del variable_uses[varid]
|
|
176
|
+
# fold these expressions if possible
|
|
177
|
+
ExpressionFolder(variable_assignments, variable_uses, region)
|
|
160
178
|
return region
|
|
161
179
|
|
|
162
180
|
@staticmethod
|
|
@@ -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
|
|