angr 9.2.141__py3-none-manylinux2014_x86_64.whl → 9.2.142__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 +17 -3
- angr/analyses/cfg/cfg_base.py +38 -4
- angr/analyses/cfg/cfg_fast.py +23 -7
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +4 -0
- angr/analyses/class_identifier.py +8 -7
- angr/analyses/complete_calling_conventions.py +1 -1
- angr/analyses/decompiler/ail_simplifier.py +61 -46
- angr/analyses/decompiler/clinic.py +73 -5
- angr/analyses/decompiler/condition_processor.py +7 -7
- angr/analyses/decompiler/decompilation_cache.py +2 -1
- angr/analyses/decompiler/decompiler.py +10 -2
- angr/analyses/decompiler/dephication/graph_vvar_mapping.py +4 -6
- angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +8 -2
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +63 -34
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +2 -0
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +29 -7
- angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +6 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +9 -1
- angr/analyses/decompiler/region_identifier.py +70 -47
- angr/analyses/decompiler/ssailification/rewriting.py +47 -17
- angr/analyses/decompiler/ssailification/rewriting_engine.py +13 -0
- angr/analyses/decompiler/stack_item.py +36 -0
- angr/analyses/decompiler/structured_codegen/c.py +14 -9
- angr/analyses/decompiler/structuring/phoenix.py +3 -3
- angr/analyses/find_objects_static.py +2 -1
- angr/analyses/reaching_definitions/engine_vex.py +13 -0
- angr/analyses/reaching_definitions/function_handler.py +24 -10
- angr/analyses/reaching_definitions/function_handler_library/stdio.py +1 -0
- angr/analyses/reaching_definitions/function_handler_library/stdlib.py +45 -12
- angr/analyses/reaching_definitions/function_handler_library/string.py +77 -21
- angr/analyses/reaching_definitions/function_handler_library/unistd.py +21 -1
- angr/analyses/reaching_definitions/rd_state.py +11 -7
- angr/analyses/s_liveness.py +44 -6
- angr/analyses/s_reaching_definitions/s_rda_model.py +4 -2
- angr/analyses/typehoon/simple_solver.py +35 -8
- angr/analyses/typehoon/typehoon.py +3 -1
- angr/calling_conventions.py +2 -2
- angr/knowledge_plugins/functions/function.py +5 -10
- angr/knowledge_plugins/variables/variable_manager.py +27 -0
- angr/procedures/definitions/__init__.py +3 -10
- angr/procedures/definitions/wdk_ntoskrnl.py +2 -0
- angr/procedures/win32_kernel/__fastfail.py +15 -0
- angr/sim_procedure.py +2 -2
- angr/simos/simos.py +14 -10
- angr/simos/windows.py +42 -1
- angr/utils/ail.py +41 -1
- angr/utils/cpp.py +17 -0
- angr/utils/doms.py +142 -0
- angr/utils/library.py +1 -1
- angr/utils/types.py +12 -1
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/METADATA +7 -7
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/RECORD +58 -54
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/LICENSE +0 -0
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/WHEEL +0 -0
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/entry_points.txt +0 -0
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
|
@@ -165,6 +165,19 @@ class CallingConventionAnalysis(Analysis):
|
|
|
165
165
|
):
|
|
166
166
|
return
|
|
167
167
|
|
|
168
|
+
if (
|
|
169
|
+
hooker is not None
|
|
170
|
+
and hooker.cc is not None
|
|
171
|
+
and hooker.is_function
|
|
172
|
+
and not hooker.guessed_prototype
|
|
173
|
+
and hooker.prototype is not None
|
|
174
|
+
):
|
|
175
|
+
# copy the calling convention and prototype from the SimProcedure instance
|
|
176
|
+
self.cc = hooker.cc
|
|
177
|
+
self.prototype = hooker.prototype
|
|
178
|
+
self.prototype_libname = hooker.library_name
|
|
179
|
+
return
|
|
180
|
+
|
|
168
181
|
if self._function.prototype is None:
|
|
169
182
|
# try our luck
|
|
170
183
|
# we set ignore_binary_name to True because the binary name SimProcedures is "cle##externs" and does not
|
|
@@ -309,9 +322,10 @@ class CallingConventionAnalysis(Analysis):
|
|
|
309
322
|
if self.project.is_hooked(real_func.addr):
|
|
310
323
|
# prioritize the hooker
|
|
311
324
|
hooker = self.project.hooked_by(real_func.addr)
|
|
312
|
-
if hooker is not None and
|
|
313
|
-
|
|
314
|
-
|
|
325
|
+
if hooker is not None and hooker.is_function and not hooker.guessed_prototype:
|
|
326
|
+
# we only take the prototype from the SimProcedure if
|
|
327
|
+
# - the SimProcedure is a function
|
|
328
|
+
# - the prototype of the SimProcedure is not guessed
|
|
315
329
|
return cc, hooker.prototype
|
|
316
330
|
if real_func.prototype is not None:
|
|
317
331
|
return cc, real_func.prototype
|
angr/analyses/cfg/cfg_base.py
CHANGED
|
@@ -11,7 +11,7 @@ import pyvex
|
|
|
11
11
|
from cle import ELF, PE, Blob, TLSObject, MachO, ExternObject, KernelObject, FunctionHintSource, Hex, Coff, SRec, XBE
|
|
12
12
|
from cle.backends import NamedRegion
|
|
13
13
|
import archinfo
|
|
14
|
-
from archinfo.arch_soot import SootAddressDescriptor
|
|
14
|
+
from archinfo.arch_soot import SootAddressDescriptor, SootMethodDescriptor
|
|
15
15
|
from archinfo.arch_arm import is_arm_arch, get_real_address_if_arm
|
|
16
16
|
|
|
17
17
|
from angr.knowledge_plugins.functions.function_manager import FunctionManager
|
|
@@ -129,7 +129,7 @@ class CFGBase(Analysis):
|
|
|
129
129
|
|
|
130
130
|
# Store all the functions analyzed before the set is cleared
|
|
131
131
|
# Used for performance optimization
|
|
132
|
-
self._updated_nonreturning_functions: set[int] | None = None
|
|
132
|
+
self._updated_nonreturning_functions: set[int | SootMethodDescriptor] | None = None
|
|
133
133
|
|
|
134
134
|
self._normalize = normalize
|
|
135
135
|
|
|
@@ -246,7 +246,7 @@ class CFGBase(Analysis):
|
|
|
246
246
|
)
|
|
247
247
|
|
|
248
248
|
self._regions_size = sum((end - start) for start, end in regions)
|
|
249
|
-
self._regions:
|
|
249
|
+
self._regions: SortedDict = SortedDict(regions)
|
|
250
250
|
|
|
251
251
|
l.debug("CFG recovery covers %d regions:", len(self._regions))
|
|
252
252
|
for start, end in self._regions.items():
|
|
@@ -1556,6 +1556,7 @@ class CFGBase(Analysis):
|
|
|
1556
1556
|
self.kb.functions[func_addr].alignment = True
|
|
1557
1557
|
continue
|
|
1558
1558
|
node = function.get_node(block.addr)
|
|
1559
|
+
assert node is not None
|
|
1559
1560
|
successors = list(function.graph.successors(node))
|
|
1560
1561
|
if len(successors) == 1 and successors[0].addr == node.addr:
|
|
1561
1562
|
# self loop. mark this function as a function alignment
|
|
@@ -2151,6 +2152,11 @@ class CFGBase(Analysis):
|
|
|
2151
2152
|
f = self.kb.functions.function(addr=addr)
|
|
2152
2153
|
assert f is not None
|
|
2153
2154
|
|
|
2155
|
+
# copy over existing metadata
|
|
2156
|
+
if known_functions.contains_addr(addr):
|
|
2157
|
+
kf = known_functions.get_by_addr(addr)
|
|
2158
|
+
f.is_plt = kf.is_plt
|
|
2159
|
+
|
|
2154
2160
|
blockaddr_to_function[addr] = f
|
|
2155
2161
|
|
|
2156
2162
|
function_is_returning = False
|
|
@@ -2532,6 +2538,34 @@ class CFGBase(Analysis):
|
|
|
2532
2538
|
# Other functions
|
|
2533
2539
|
#
|
|
2534
2540
|
|
|
2541
|
+
@staticmethod
|
|
2542
|
+
def _is_noop_jump_block(block) -> bool:
|
|
2543
|
+
"""
|
|
2544
|
+
Check if the block does nothing but jumping to a constant address.
|
|
2545
|
+
|
|
2546
|
+
:param block: The block instance. We assume the block is already optimized.
|
|
2547
|
+
:return: True if the entire block is a jump to a constant address, False otherwise.
|
|
2548
|
+
"""
|
|
2549
|
+
|
|
2550
|
+
vex = block.vex
|
|
2551
|
+
if vex.jumpkind != "Ijk_Boring":
|
|
2552
|
+
return False
|
|
2553
|
+
if isinstance(vex.next, pyvex.expr.Const):
|
|
2554
|
+
return all(isinstance(stmt, pyvex.stmt.IMark) for stmt in vex.statements)
|
|
2555
|
+
if isinstance(vex.next, pyvex.expr.RdTmp):
|
|
2556
|
+
next_tmp = vex.next.tmp
|
|
2557
|
+
return all(
|
|
2558
|
+
isinstance(stmt, pyvex.stmt.IMark)
|
|
2559
|
+
or (
|
|
2560
|
+
isinstance(stmt, pyvex.stmt.WrTmp)
|
|
2561
|
+
and stmt.tmp == next_tmp
|
|
2562
|
+
and isinstance(stmt.data, pyvex.expr.Load)
|
|
2563
|
+
and isinstance(stmt.data.addr, pyvex.expr.Const)
|
|
2564
|
+
)
|
|
2565
|
+
for stmt in vex.statements
|
|
2566
|
+
)
|
|
2567
|
+
return False
|
|
2568
|
+
|
|
2535
2569
|
@staticmethod
|
|
2536
2570
|
def _is_noop_block(arch: archinfo.Arch, block) -> bool:
|
|
2537
2571
|
"""
|
|
@@ -2755,7 +2789,7 @@ class CFGBase(Analysis):
|
|
|
2755
2789
|
cfg_node: CFGNode,
|
|
2756
2790
|
irsb: pyvex.IRSB,
|
|
2757
2791
|
func_addr: int,
|
|
2758
|
-
stmt_idx: int
|
|
2792
|
+
stmt_idx: int = DEFAULT_STATEMENT,
|
|
2759
2793
|
) -> tuple[bool, set[int], IndirectJump | None]:
|
|
2760
2794
|
"""
|
|
2761
2795
|
Called when we encounter an indirect jump. We will try to resolve this indirect jump using timeless (fast)
|
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -1782,7 +1782,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1782
1782
|
self.project.loader.discard_ro_memview()
|
|
1783
1783
|
|
|
1784
1784
|
# Clean up
|
|
1785
|
-
self._traced_addresses = None
|
|
1785
|
+
self._traced_addresses = None # type: ignore
|
|
1786
1786
|
self._lifter_deregister_readonly_regions()
|
|
1787
1787
|
self._function_returns = None
|
|
1788
1788
|
|
|
@@ -1838,6 +1838,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1838
1838
|
xrefs = self.kb.xrefs.get_xrefs_by_dst(security_cookie_addr)
|
|
1839
1839
|
tested_func_addrs = set()
|
|
1840
1840
|
for xref in xrefs:
|
|
1841
|
+
assert xref.block_addr is not None
|
|
1841
1842
|
cfg_node = self.model.get_any_node(xref.block_addr)
|
|
1842
1843
|
if cfg_node is None:
|
|
1843
1844
|
continue
|
|
@@ -2081,13 +2082,20 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
2081
2082
|
|
|
2082
2083
|
if (
|
|
2083
2084
|
cfg_job.src_node is not None
|
|
2084
|
-
and self.functions.contains_addr(cfg_job.src_node.addr)
|
|
2085
|
-
and self.functions[cfg_job.src_node.addr].is_default_name
|
|
2086
2085
|
and cfg_job.src_node.addr not in self.kb.labels
|
|
2087
2086
|
and cfg_job.jumpkind == "Ijk_Boring"
|
|
2087
|
+
and self._is_noop_jump_block(cfg_job.src_node.block)
|
|
2088
2088
|
):
|
|
2089
|
-
#
|
|
2090
|
-
self.functions
|
|
2089
|
+
# the caller node is very likely to be a PLT stub
|
|
2090
|
+
if not self.functions.contains_addr(cfg_job.src_node.addr):
|
|
2091
|
+
src_func = self.functions.function(addr=cfg_job.src_node.addr, create=True)
|
|
2092
|
+
else:
|
|
2093
|
+
src_func = self.functions.get_by_addr(cfg_job.src_node.addr)
|
|
2094
|
+
if len(src_func.block_addrs_set) <= 1 and src_func.is_default_name:
|
|
2095
|
+
# assign a name to the caller function that jumps to this procedure
|
|
2096
|
+
src_func.name = procedure.display_name
|
|
2097
|
+
# mark it as PLT
|
|
2098
|
+
src_func.is_plt = True
|
|
2091
2099
|
|
|
2092
2100
|
if procedure.ADDS_EXITS:
|
|
2093
2101
|
# Get two blocks ahead
|
|
@@ -3714,7 +3722,12 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
3714
3722
|
#
|
|
3715
3723
|
|
|
3716
3724
|
def _graph_add_edge(
|
|
3717
|
-
self,
|
|
3725
|
+
self,
|
|
3726
|
+
cfg_node: CFGNode,
|
|
3727
|
+
src_node: CFGNode | None,
|
|
3728
|
+
src_jumpkind: str,
|
|
3729
|
+
src_ins_addr: int | None,
|
|
3730
|
+
src_stmt_idx: int | None,
|
|
3718
3731
|
):
|
|
3719
3732
|
"""
|
|
3720
3733
|
Add edge between nodes, or add node if entry point
|
|
@@ -4584,6 +4597,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4584
4597
|
elif (
|
|
4585
4598
|
lifted_block is not None
|
|
4586
4599
|
and is_x86_x64_arch
|
|
4600
|
+
and lifted_block.bytes is not None
|
|
4587
4601
|
and len(lifted_block.bytes) - irsb_size > 2
|
|
4588
4602
|
and lifted_block.bytes[irsb_size : irsb_size + 2]
|
|
4589
4603
|
in {
|
|
@@ -4659,7 +4673,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4659
4673
|
self._seg_list.occupy(real_addr + irsb_size, nodecode_size, "nodecode")
|
|
4660
4674
|
|
|
4661
4675
|
# Occupy the block in segment list
|
|
4662
|
-
if irsb.size > 0:
|
|
4676
|
+
if irsb is not None and irsb.size > 0:
|
|
4663
4677
|
self._seg_list.occupy(real_addr, irsb.size, "code")
|
|
4664
4678
|
|
|
4665
4679
|
# Create a CFG node, and add it to the graph
|
|
@@ -4969,6 +4983,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4969
4983
|
|
|
4970
4984
|
for assumption_addr in to_remove:
|
|
4971
4985
|
# remove this assumption from the graph (since we may have new relationships formed later)
|
|
4986
|
+
assert self._decoding_assumption_relations is not None
|
|
4972
4987
|
if assumption_addr in self._decoding_assumption_relations:
|
|
4973
4988
|
self._decoding_assumption_relations.remove_node(assumption_addr)
|
|
4974
4989
|
|
|
@@ -5159,6 +5174,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
5159
5174
|
target_func = edges[0][1]
|
|
5160
5175
|
if isinstance(target_func, (HookNode, Function)) and self.project.is_hooked(target_func.addr):
|
|
5161
5176
|
hooker = self.project.hooked_by(target_func.addr)
|
|
5177
|
+
assert hooker is not None
|
|
5162
5178
|
if hooker.DYNAMIC_RET:
|
|
5163
5179
|
return self._is_call_returning(callsite_cfgnode, target_func.addr)
|
|
5164
5180
|
|
|
@@ -183,7 +183,11 @@ class ConstantValueManager:
|
|
|
183
183
|
# determine blocks to run FCP on
|
|
184
184
|
|
|
185
185
|
# - include at most three levels of superblock successors from the entrypoint
|
|
186
|
+
self.mapping = {}
|
|
186
187
|
startpoint = self.func.startpoint
|
|
188
|
+
if startpoint is None:
|
|
189
|
+
return
|
|
190
|
+
|
|
187
191
|
blocks = set()
|
|
188
192
|
succ_and_levels = [(startpoint, 0)]
|
|
189
193
|
while succ_and_levels:
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
from angr.sim_type import SimCppClass, SimTypeCppFunction
|
|
3
4
|
from angr.analyses import AnalysesHub
|
|
5
|
+
from angr.utils.cpp import is_cpp_funcname_ctor
|
|
4
6
|
from . import Analysis, CFGFast, VtableFinder
|
|
5
7
|
|
|
6
8
|
|
|
@@ -33,17 +35,13 @@ class ClassIdentifier(Analysis):
|
|
|
33
35
|
class_name = class_name.removeprefix("non-virtual thunk for ")
|
|
34
36
|
if col_ind != -1:
|
|
35
37
|
if class_name not in self.classes:
|
|
36
|
-
ctor =
|
|
37
|
-
if func.demangled_name.find("{ctor}"):
|
|
38
|
-
ctor = True
|
|
38
|
+
ctor = is_cpp_funcname_ctor(func.demangled_name)
|
|
39
39
|
function_members = {func.addr: SimTypeCppFunction([], None, label=func.demangled_name, ctor=ctor)}
|
|
40
40
|
new_class = SimCppClass(name=class_name, function_members=function_members)
|
|
41
41
|
self.classes[class_name] = new_class
|
|
42
42
|
|
|
43
43
|
else:
|
|
44
|
-
ctor =
|
|
45
|
-
if func.demangled_name.find("{ctor}"):
|
|
46
|
-
ctor = True
|
|
44
|
+
ctor = is_cpp_funcname_ctor(func.demangled_name)
|
|
47
45
|
cur_class = self.classes[class_name]
|
|
48
46
|
cur_class.function_members[func.addr] = SimTypeCppFunction(
|
|
49
47
|
[], None, label=func.demangled_name, ctor=ctor
|
|
@@ -55,7 +53,10 @@ class ClassIdentifier(Analysis):
|
|
|
55
53
|
vtable_calling_func = self.project.kb.functions.floor_func(ref.ins_addr)
|
|
56
54
|
tmp_col_ind = vtable_calling_func.demangled_name.rfind("::")
|
|
57
55
|
possible_constructor_class_name = vtable_calling_func.demangled_name[:tmp_col_ind]
|
|
58
|
-
if
|
|
56
|
+
if (
|
|
57
|
+
is_cpp_funcname_ctor(vtable_calling_func.demangled_name)
|
|
58
|
+
and possible_constructor_class_name in self.classes
|
|
59
|
+
):
|
|
59
60
|
self.classes[possible_constructor_class_name].vtable_ptrs.append(vtable.vaddr)
|
|
60
61
|
|
|
61
62
|
|
|
@@ -383,7 +383,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
383
383
|
return (
|
|
384
384
|
cc_analysis.cc,
|
|
385
385
|
cc_analysis.prototype,
|
|
386
|
-
func.prototype_libname,
|
|
386
|
+
cc_analysis.prototype_libname if cc_analysis.prototype_libname is not None else func.prototype_libname,
|
|
387
387
|
self.kb.variables.get_function_manager(func_addr),
|
|
388
388
|
)
|
|
389
389
|
_l.info("Cannot determine calling convention for %r.", func)
|
|
@@ -118,7 +118,7 @@ class AILSimplifier(Analysis):
|
|
|
118
118
|
self._should_rewrite_ccalls = rewrite_ccalls
|
|
119
119
|
self._removed_vvar_ids = removed_vvar_ids if removed_vvar_ids is not None else set()
|
|
120
120
|
self._arg_vvars = arg_vvars
|
|
121
|
-
self._avoid_vvar_ids = avoid_vvar_ids
|
|
121
|
+
self._avoid_vvar_ids = avoid_vvar_ids if avoid_vvar_ids is not None else set()
|
|
122
122
|
self._propagator_dead_vvar_ids: set[int] = set()
|
|
123
123
|
self._secondary_stackvars: set[int] = secondary_stackvars if secondary_stackvars is not None else set()
|
|
124
124
|
|
|
@@ -132,12 +132,10 @@ class AILSimplifier(Analysis):
|
|
|
132
132
|
def _simplify(self):
|
|
133
133
|
if self._narrow_expressions:
|
|
134
134
|
_l.debug("Removing dead assignments before narrowing expressions")
|
|
135
|
-
r = self.
|
|
135
|
+
r = self._iteratively_remove_dead_assignments()
|
|
136
136
|
if r:
|
|
137
137
|
_l.debug("... dead assignments removed")
|
|
138
138
|
self.simplified = True
|
|
139
|
-
self._rebuild_func_graph()
|
|
140
|
-
self._clear_cache()
|
|
141
139
|
|
|
142
140
|
_l.debug("Narrowing expressions")
|
|
143
141
|
narrowed_exprs = self._narrow_exprs()
|
|
@@ -170,12 +168,10 @@ class AILSimplifier(Analysis):
|
|
|
170
168
|
|
|
171
169
|
if self._unify_vars:
|
|
172
170
|
_l.debug("Removing dead assignments")
|
|
173
|
-
r = self.
|
|
171
|
+
r = self._iteratively_remove_dead_assignments()
|
|
174
172
|
if r:
|
|
175
173
|
_l.debug("... dead assignments removed")
|
|
176
174
|
self.simplified = True
|
|
177
|
-
self._rebuild_func_graph()
|
|
178
|
-
self._clear_cache()
|
|
179
175
|
|
|
180
176
|
_l.debug("Unifying local variables")
|
|
181
177
|
r = self._unify_local_variables()
|
|
@@ -194,11 +190,10 @@ class AILSimplifier(Analysis):
|
|
|
194
190
|
self._clear_cache()
|
|
195
191
|
|
|
196
192
|
_l.debug("Removing dead assignments")
|
|
197
|
-
r = self.
|
|
193
|
+
r = self._iteratively_remove_dead_assignments()
|
|
198
194
|
if r:
|
|
199
195
|
_l.debug("... dead assignments removed")
|
|
200
196
|
self.simplified = True
|
|
201
|
-
self._rebuild_func_graph()
|
|
202
197
|
|
|
203
198
|
def _rebuild_func_graph(self):
|
|
204
199
|
def _handler(node):
|
|
@@ -1319,14 +1314,27 @@ class AILSimplifier(Analysis):
|
|
|
1319
1314
|
|
|
1320
1315
|
return False, None
|
|
1321
1316
|
|
|
1317
|
+
def _iteratively_remove_dead_assignments(self) -> bool:
|
|
1318
|
+
anything_removed = False
|
|
1319
|
+
while True:
|
|
1320
|
+
r = self._remove_dead_assignments()
|
|
1321
|
+
if not r:
|
|
1322
|
+
return anything_removed
|
|
1323
|
+
self._rebuild_func_graph()
|
|
1324
|
+
self._clear_cache()
|
|
1325
|
+
|
|
1322
1326
|
def _remove_dead_assignments(self) -> bool:
|
|
1323
1327
|
|
|
1324
1328
|
# keeping tracking of statements to remove and statements (as well as dead vvars) to keep allows us to handle
|
|
1325
|
-
# cases where a statement defines more than one
|
|
1329
|
+
# cases where a statement defines more than one atom, e.g., a call statement that defines both the return
|
|
1326
1330
|
# value and the floating-point return value.
|
|
1327
1331
|
stmts_to_remove_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
|
|
1328
1332
|
stmts_to_keep_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
|
|
1329
1333
|
dead_vvar_ids: set[int] = set()
|
|
1334
|
+
dead_vvar_codelocs: set[CodeLocation] = set()
|
|
1335
|
+
blocks: dict[tuple[int, int | None], Block] = {
|
|
1336
|
+
(node.addr, node.idx): self.blocks.get(node, node) for node in self.func_graph.nodes()
|
|
1337
|
+
}
|
|
1330
1338
|
|
|
1331
1339
|
# Find all statements that should be removed
|
|
1332
1340
|
mask = (1 << self.project.arch.bits) - 1
|
|
@@ -1335,59 +1343,64 @@ class AILSimplifier(Analysis):
|
|
|
1335
1343
|
stackarg_offsets = (
|
|
1336
1344
|
{(tpl[1] & mask) for tpl in self._stack_arg_offsets} if self._stack_arg_offsets is not None else None
|
|
1337
1345
|
)
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
if isinstance(def_.atom, atoms.VirtualVariable):
|
|
1345
|
-
if def_.atom.varid in self._propagator_dead_vvar_ids:
|
|
1346
|
+
while True:
|
|
1347
|
+
new_dead_vars_found = False
|
|
1348
|
+
for vvar, codeloc in rd.all_vvar_definitions.items():
|
|
1349
|
+
if vvar.varid in dead_vvar_ids:
|
|
1350
|
+
continue
|
|
1351
|
+
if vvar.varid in self._propagator_dead_vvar_ids:
|
|
1346
1352
|
# we are definitely removing this variable if it has no uses
|
|
1347
|
-
uses = rd.
|
|
1348
|
-
elif
|
|
1353
|
+
uses = rd.all_vvar_uses[vvar]
|
|
1354
|
+
elif vvar.was_stack:
|
|
1349
1355
|
if not self._remove_dead_memdefs:
|
|
1350
|
-
if rd.is_phi_vvar_id(
|
|
1356
|
+
if rd.is_phi_vvar_id(vvar.varid):
|
|
1351
1357
|
# we always remove unused phi variables
|
|
1352
1358
|
pass
|
|
1353
|
-
elif
|
|
1359
|
+
elif vvar.varid in self._secondary_stackvars:
|
|
1354
1360
|
# secondary stack variables are potentially removable
|
|
1355
1361
|
pass
|
|
1356
1362
|
elif stackarg_offsets is not None:
|
|
1357
1363
|
# we always remove definitions for stack arguments
|
|
1358
|
-
assert
|
|
1359
|
-
if (
|
|
1364
|
+
assert vvar.stack_offset is not None
|
|
1365
|
+
if (vvar.stack_offset & mask) not in stackarg_offsets:
|
|
1360
1366
|
continue
|
|
1361
1367
|
else:
|
|
1362
1368
|
continue
|
|
1363
|
-
uses = rd.
|
|
1369
|
+
uses = rd.all_vvar_uses[vvar]
|
|
1364
1370
|
|
|
1365
|
-
elif
|
|
1366
|
-
uses = rd.
|
|
1371
|
+
elif vvar.was_tmp or vvar.was_reg or vvar.was_parameter:
|
|
1372
|
+
uses = rd.all_vvar_uses[vvar]
|
|
1367
1373
|
|
|
1368
1374
|
else:
|
|
1369
1375
|
uses = set()
|
|
1370
1376
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
+
# remove uses where vvars are going to be removed
|
|
1378
|
+
filtered_uses_count = 0
|
|
1379
|
+
for _, loc in uses:
|
|
1380
|
+
if loc in dead_vvar_codelocs and loc.block_addr is not None and loc.stmt_idx is not None:
|
|
1381
|
+
stmt = blocks[(loc.block_addr, loc.block_idx)].statements[loc.stmt_idx]
|
|
1382
|
+
if not self._statement_has_call_exprs(stmt) and not isinstance(stmt, (DirtyStatement, Call)):
|
|
1383
|
+
continue
|
|
1384
|
+
filtered_uses_count += 1
|
|
1385
|
+
|
|
1386
|
+
if filtered_uses_count == 0:
|
|
1387
|
+
new_dead_vars_found = True
|
|
1388
|
+
dead_vvar_ids.add(vvar.varid)
|
|
1389
|
+
dead_vvar_codelocs.add(codeloc)
|
|
1390
|
+
if not isinstance(codeloc, ExternalCodeLocation):
|
|
1391
|
+
assert codeloc.block_addr is not None
|
|
1392
|
+
assert codeloc.stmt_idx is not None
|
|
1393
|
+
stmts_to_remove_per_block[(codeloc.block_addr, codeloc.block_idx)].add(codeloc.stmt_idx)
|
|
1394
|
+
stmts_to_keep_per_block[(codeloc.block_addr, codeloc.block_idx)].discard(codeloc.stmt_idx)
|
|
1395
|
+
else:
|
|
1396
|
+
if not isinstance(codeloc, ExternalCodeLocation):
|
|
1397
|
+
assert codeloc.block_addr is not None
|
|
1398
|
+
assert codeloc.stmt_idx is not None
|
|
1399
|
+
stmts_to_keep_per_block[(codeloc.block_addr, codeloc.block_idx)].add(codeloc.stmt_idx)
|
|
1377
1400
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
stmts_to_remove_per_block[(def_.codeloc.block_addr, def_.codeloc.block_idx)].add(
|
|
1382
|
-
def_.codeloc.stmt_idx
|
|
1383
|
-
)
|
|
1384
|
-
else:
|
|
1385
|
-
if not isinstance(def_.codeloc, ExternalCodeLocation):
|
|
1386
|
-
assert def_.codeloc.block_addr is not None
|
|
1387
|
-
assert def_.codeloc.stmt_idx is not None
|
|
1388
|
-
stmts_to_keep_per_block[(def_.codeloc.block_addr, def_.codeloc.block_idx)].add(
|
|
1389
|
-
def_.codeloc.stmt_idx
|
|
1390
|
-
)
|
|
1401
|
+
if not new_dead_vars_found:
|
|
1402
|
+
# nothing more is found. let's end the loop
|
|
1403
|
+
break
|
|
1391
1404
|
|
|
1392
1405
|
# find all phi variables that rely on variables that no longer exist
|
|
1393
1406
|
all_removed_var_ids = self._removed_vvar_ids.copy()
|
|
@@ -1462,6 +1475,7 @@ class AILSimplifier(Analysis):
|
|
|
1462
1475
|
if codeloc in self._assignments_to_remove:
|
|
1463
1476
|
# it should be removed
|
|
1464
1477
|
simplified = True
|
|
1478
|
+
self._assignments_to_remove.discard(codeloc)
|
|
1465
1479
|
continue
|
|
1466
1480
|
|
|
1467
1481
|
if self._statement_has_call_exprs(stmt):
|
|
@@ -1489,6 +1503,7 @@ class AILSimplifier(Analysis):
|
|
|
1489
1503
|
codeloc = CodeLocation(block.addr, idx, ins_addr=stmt.ins_addr, block_idx=block.idx)
|
|
1490
1504
|
if codeloc in self._calls_to_remove:
|
|
1491
1505
|
# this call can be removed
|
|
1506
|
+
self._calls_to_remove.discard(codeloc)
|
|
1492
1507
|
simplified = True
|
|
1493
1508
|
continue
|
|
1494
1509
|
|