angr 9.2.175__cp310-abi3-macosx_11_0_arm64.whl → 9.2.176__cp310-abi3-macosx_11_0_arm64.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 +12 -0
- angr/analyses/complete_calling_conventions.py +39 -26
- angr/analyses/decompiler/ail_simplifier.py +13 -11
- angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +5 -1
- angr/analyses/decompiler/clinic.py +54 -40
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +3 -3
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
- angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy.py → inlined_wcscpy.py} +16 -8
- angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy_consolidation.py → inlined_wcscpy_consolidation.py} +13 -13
- angr/analyses/decompiler/ssailification/rewriting_engine.py +14 -1
- angr/analyses/decompiler/structured_codegen/c.py +6 -5
- angr/analyses/decompiler/structuring/dream.py +2 -2
- angr/analyses/decompiler/structuring/phoenix.py +101 -23
- angr/analyses/stack_pointer_tracker.py +4 -3
- angr/analyses/typehoon/lifter.py +29 -18
- angr/analyses/typehoon/simple_solver.py +157 -50
- angr/analyses/typehoon/translator.py +34 -34
- angr/analyses/typehoon/typeconsts.py +33 -15
- angr/analyses/typehoon/typevars.py +9 -2
- angr/analyses/variable_recovery/engine_ail.py +4 -2
- angr/analyses/variable_recovery/engine_base.py +4 -1
- angr/analyses/variable_recovery/variable_recovery_fast.py +3 -1
- angr/engines/icicle.py +4 -4
- angr/engines/vex/claripy/ccall.py +3 -3
- angr/knowledge_plugins/functions/function.py +17 -0
- angr/procedures/posix/pthread.py +4 -4
- angr/procedures/stubs/format_parser.py +3 -3
- angr/rustylib.abi3.so +0 -0
- angr/sim_type.py +11 -6
- angr/simos/windows.py +1 -1
- angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -1
- angr/unicornlib.dylib +0 -0
- angr/utils/constants.py +1 -1
- angr/utils/strings.py +20 -0
- {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/METADATA +5 -5
- {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/RECORD +42 -41
- {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/WHEEL +0 -0
- {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/entry_points.txt +0 -0
- {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
|
@@ -95,6 +95,8 @@ class CallingConventionAnalysis(Analysis):
|
|
|
95
95
|
calling convention and arguments. This can be time-consuming if there are many call
|
|
96
96
|
sites to analyze.
|
|
97
97
|
:ivar cc: The recovered calling convention for the function.
|
|
98
|
+
:ivar _collect_facts: True if we should run FunctionFactCollector to collect input arguments and return
|
|
99
|
+
value size. False if input arguments and return value size are provided by the user.
|
|
98
100
|
"""
|
|
99
101
|
|
|
100
102
|
def __init__(
|
|
@@ -108,6 +110,7 @@ class CallingConventionAnalysis(Analysis):
|
|
|
108
110
|
func_graph: networkx.DiGraph | None = None,
|
|
109
111
|
input_args: list[SimRegArg | SimStackArg] | None = None,
|
|
110
112
|
retval_size: int | None = None,
|
|
113
|
+
collect_facts: bool = False,
|
|
111
114
|
):
|
|
112
115
|
if func is not None and not isinstance(func, Function):
|
|
113
116
|
func = self.kb.functions[func]
|
|
@@ -121,6 +124,7 @@ class CallingConventionAnalysis(Analysis):
|
|
|
121
124
|
self._func_graph = func_graph
|
|
122
125
|
self._input_args = input_args
|
|
123
126
|
self._retval_size = retval_size
|
|
127
|
+
self._collect_facts = collect_facts
|
|
124
128
|
|
|
125
129
|
if self._retval_size is not None and self._input_args is None:
|
|
126
130
|
# retval size will be ignored if input_args is not specified - user error?
|
|
@@ -132,6 +136,7 @@ class CallingConventionAnalysis(Analysis):
|
|
|
132
136
|
self.cc: SimCC | None = None
|
|
133
137
|
self.prototype: SimTypeFunction | None = None
|
|
134
138
|
self.prototype_libname: str | None = None
|
|
139
|
+
self.proto_from_symbol: bool = False
|
|
135
140
|
|
|
136
141
|
if self._cfg is None and "CFGFast" in self.kb.cfgs:
|
|
137
142
|
self._cfg = self.kb.cfgs["CFGFast"]
|
|
@@ -168,6 +173,7 @@ class CallingConventionAnalysis(Analysis):
|
|
|
168
173
|
r_demangled = self._analyze_demangled_name(demangled_name)
|
|
169
174
|
if r_demangled is not None:
|
|
170
175
|
self.cc, self.prototype, self.prototype_libname = r_demangled
|
|
176
|
+
self.proto_from_symbol = True
|
|
171
177
|
return
|
|
172
178
|
|
|
173
179
|
if self._function.is_simprocedure:
|
|
@@ -242,6 +248,12 @@ class CallingConventionAnalysis(Analysis):
|
|
|
242
248
|
self.cc, self.prototype, self.prototype_libname = r_plt
|
|
243
249
|
return
|
|
244
250
|
|
|
251
|
+
# we gotta analyze the function properly
|
|
252
|
+
if self._collect_facts and self._input_args is None and self._retval_size is None:
|
|
253
|
+
facts = self.project.analyses.FunctionFactCollector(self._function, kb=self.kb)
|
|
254
|
+
self._input_args = facts.input_args
|
|
255
|
+
self._retval_size = facts.retval_size
|
|
256
|
+
|
|
245
257
|
r = self._analyze_function()
|
|
246
258
|
if r is None:
|
|
247
259
|
l.warning("Cannot determine calling convention for %r.", self._function)
|
|
@@ -17,7 +17,7 @@ from angr.utils.graph import GraphUtils
|
|
|
17
17
|
from angr.simos import SimWindows
|
|
18
18
|
from angr.utils.mp import mp_context, Initializer
|
|
19
19
|
from angr.knowledge_plugins.cfg import CFGModel
|
|
20
|
-
from . import Analysis, register_analysis, VariableRecoveryFast, CallingConventionAnalysis,
|
|
20
|
+
from . import Analysis, register_analysis, VariableRecoveryFast, CallingConventionAnalysis, CFGFast
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from angr.calling_conventions import SimCC
|
|
@@ -191,10 +191,15 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
191
191
|
self._prioritize_func_addrs = None # no longer useful
|
|
192
192
|
|
|
193
193
|
def _set_function_prototype(
|
|
194
|
-
self,
|
|
194
|
+
self,
|
|
195
|
+
func: Function,
|
|
196
|
+
prototype: SimTypeFunction | None,
|
|
197
|
+
prototype_libname: str | None,
|
|
198
|
+
prototype_guessed: bool | None,
|
|
195
199
|
) -> None:
|
|
196
200
|
if func.prototype is None or func.is_prototype_guessed or self._force:
|
|
197
|
-
|
|
201
|
+
if prototype_guessed is not None:
|
|
202
|
+
func.is_prototype_guessed = prototype_guessed
|
|
198
203
|
func.prototype = prototype
|
|
199
204
|
func.prototype_libname = prototype_libname
|
|
200
205
|
|
|
@@ -204,12 +209,12 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
204
209
|
if self._workers == 0:
|
|
205
210
|
self._update_progress(0)
|
|
206
211
|
for idx, func_addr in enumerate(self._func_addrs):
|
|
207
|
-
cc, proto, proto_libname, _ = self._analyze_core(func_addr)
|
|
212
|
+
cc, proto, proto_libname, proto_guessed, _ = self._analyze_core(func_addr)
|
|
208
213
|
|
|
209
214
|
func = self.kb.functions.get_by_addr(func_addr)
|
|
210
215
|
if cc is not None or proto is not None:
|
|
211
216
|
func.calling_convention = cc
|
|
212
|
-
self._set_function_prototype(func, proto, proto_libname)
|
|
217
|
+
self._set_function_prototype(func, proto, proto_libname, proto_guessed)
|
|
213
218
|
if proto_libname is not None:
|
|
214
219
|
self.prototype_libnames.add(proto_libname)
|
|
215
220
|
|
|
@@ -269,10 +274,13 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
269
274
|
# update progress
|
|
270
275
|
self._update_progress(0)
|
|
271
276
|
idx = 0
|
|
277
|
+
assert self._results_lock is not None
|
|
272
278
|
while idx < total_funcs:
|
|
273
279
|
try:
|
|
274
280
|
with self._results_lock:
|
|
275
|
-
func_addr, cc, proto, proto_libname, varman = self._results.get(
|
|
281
|
+
func_addr, cc, proto, proto_libname, proto_guessed, varman = self._results.get(
|
|
282
|
+
True, timeout=0.01
|
|
283
|
+
)
|
|
276
284
|
except queue.Empty:
|
|
277
285
|
time.sleep(0.1)
|
|
278
286
|
continue
|
|
@@ -280,7 +288,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
280
288
|
func = self.kb.functions.get_by_addr(func_addr)
|
|
281
289
|
if cc is not None or proto is not None:
|
|
282
290
|
func.calling_convention = cc
|
|
283
|
-
self._set_function_prototype(func, proto, proto_libname)
|
|
291
|
+
self._set_function_prototype(func, proto, proto_libname, proto_guessed)
|
|
284
292
|
if proto_libname is not None:
|
|
285
293
|
self.prototype_libnames.add(proto_libname)
|
|
286
294
|
|
|
@@ -317,6 +325,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
317
325
|
def _worker_routine(self, worker_id: int, initializer: Initializer):
|
|
318
326
|
initializer.initialize()
|
|
319
327
|
idx = 0
|
|
328
|
+
assert self._remaining_funcs is not None and self._func_queue is not None and self._results_lock is not None
|
|
320
329
|
while self._remaining_funcs.value > 0:
|
|
321
330
|
try:
|
|
322
331
|
with self._func_queue_lock:
|
|
@@ -327,33 +336,39 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
327
336
|
continue
|
|
328
337
|
|
|
329
338
|
if callee_info is not None:
|
|
330
|
-
callee_info: dict[int, tuple[SimCC | None, SimTypeFunction | None, str | None]]
|
|
331
|
-
for callee, (
|
|
339
|
+
callee_info: dict[int, tuple[SimCC | None, SimTypeFunction | None, str | None, bool | None]]
|
|
340
|
+
for callee, (
|
|
341
|
+
callee_cc,
|
|
342
|
+
callee_proto,
|
|
343
|
+
callee_proto_libname,
|
|
344
|
+
callee_proto_guessed,
|
|
345
|
+
) in callee_info.items():
|
|
332
346
|
callee_func = self.kb.functions.get_by_addr(callee)
|
|
333
347
|
callee_func.calling_convention = callee_cc
|
|
334
|
-
self._set_function_prototype(callee_func, callee_proto, callee_proto_libname)
|
|
348
|
+
self._set_function_prototype(callee_func, callee_proto, callee_proto_libname, callee_proto_guessed)
|
|
335
349
|
|
|
336
350
|
idx += 1
|
|
337
351
|
if self._low_priority and idx % 3 == 0:
|
|
338
352
|
time.sleep(0.1)
|
|
339
353
|
|
|
340
354
|
try:
|
|
341
|
-
cc, proto, proto_libname, varman = self._analyze_core(func_addr)
|
|
355
|
+
cc, proto, proto_libname, proto_guessed, varman = self._analyze_core(func_addr)
|
|
342
356
|
except Exception: # pylint:disable=broad-except
|
|
343
357
|
_l.error("Worker %d: Exception occurred during _analyze_core().", worker_id, exc_info=True)
|
|
344
|
-
cc, proto, proto_libname, varman = None, None, None, None
|
|
358
|
+
cc, proto, proto_libname, proto_guessed, varman = None, None, None, None, None
|
|
345
359
|
with self._results_lock:
|
|
346
|
-
self._results.put((func_addr, cc, proto, proto_libname, varman))
|
|
360
|
+
self._results.put((func_addr, cc, proto, proto_libname, proto_guessed, varman))
|
|
347
361
|
|
|
348
362
|
def _analyze_core(
|
|
349
363
|
self, func_addr: int
|
|
350
|
-
) -> tuple[SimCC | None, SimTypeFunction | None, str | None, VariableManagerInternal | None]:
|
|
364
|
+
) -> tuple[SimCC | None, SimTypeFunction | None, str | None, bool | None, VariableManagerInternal | None]:
|
|
351
365
|
func = self.kb.functions.get_by_addr(func_addr)
|
|
352
366
|
if func.ran_cca:
|
|
353
367
|
return (
|
|
354
368
|
func.calling_convention,
|
|
355
369
|
func.prototype,
|
|
356
370
|
func.prototype_libname,
|
|
371
|
+
func.is_prototype_guessed,
|
|
357
372
|
self.kb.variables.get_function_manager(func_addr),
|
|
358
373
|
)
|
|
359
374
|
|
|
@@ -365,7 +380,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
365
380
|
# special case: we don't have a PCode-engine variable recovery analysis for PCode architectures!
|
|
366
381
|
if ":" in self.project.arch.name and self._func_graphs and func.addr in self._func_graphs:
|
|
367
382
|
# this is a pcode architecture
|
|
368
|
-
return None, None, None, None
|
|
383
|
+
return None, None, None, None, None
|
|
369
384
|
|
|
370
385
|
_l.info("Performing variable recovery on %r...", func)
|
|
371
386
|
try:
|
|
@@ -378,17 +393,14 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
378
393
|
func.addr,
|
|
379
394
|
exc_info=True,
|
|
380
395
|
)
|
|
381
|
-
return None, None, None, None
|
|
382
|
-
|
|
383
|
-
kwargs = {}
|
|
384
|
-
if self.mode == CallingConventionAnalysisMode.FAST:
|
|
385
|
-
facts = self.project.analyses[FactCollector].prep(kb=self.kb)(func)
|
|
386
|
-
kwargs["input_args"] = facts.input_args
|
|
387
|
-
kwargs["retval_size"] = facts.retval_size
|
|
396
|
+
return None, None, None, None, None
|
|
388
397
|
|
|
389
398
|
# determine the calling convention of each function
|
|
390
399
|
cc_analysis = self.project.analyses[CallingConventionAnalysis].prep(kb=self.kb)(
|
|
391
|
-
func,
|
|
400
|
+
func,
|
|
401
|
+
cfg=self._cfg,
|
|
402
|
+
analyze_callsites=self._analyze_callsites,
|
|
403
|
+
collect_facts=self.mode == CallingConventionAnalysisMode.FAST,
|
|
392
404
|
)
|
|
393
405
|
|
|
394
406
|
if cc_analysis.cc is not None:
|
|
@@ -397,10 +409,11 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
397
409
|
cc_analysis.cc,
|
|
398
410
|
cc_analysis.prototype,
|
|
399
411
|
cc_analysis.prototype_libname if cc_analysis.prototype_libname is not None else func.prototype_libname,
|
|
412
|
+
not cc_analysis.proto_from_symbol,
|
|
400
413
|
self.kb.variables.get_function_manager(func_addr),
|
|
401
414
|
)
|
|
402
415
|
_l.info("Cannot determine calling convention for %r.", func)
|
|
403
|
-
return None, None, None, self.kb.variables.get_function_manager(func_addr)
|
|
416
|
+
return None, None, None, None, self.kb.variables.get_function_manager(func_addr)
|
|
404
417
|
|
|
405
418
|
def prioritize_functions(self, func_addrs_to_prioritize: Iterable[int]):
|
|
406
419
|
"""
|
|
@@ -424,12 +437,12 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
424
437
|
|
|
425
438
|
def _get_callees_cc_prototypes(
|
|
426
439
|
self, caller_func_addr: int
|
|
427
|
-
) -> dict[int, tuple[SimCC | None, SimTypeFunction | None, str | None]]:
|
|
440
|
+
) -> dict[int, tuple[SimCC | None, SimTypeFunction | None, str | None, bool | None]]:
|
|
428
441
|
d = {}
|
|
429
442
|
for callee in self.kb.functions.callgraph.successors(caller_func_addr):
|
|
430
443
|
if callee != caller_func_addr and callee not in d:
|
|
431
444
|
func = self.kb.functions.get_by_addr(callee)
|
|
432
|
-
tpl = func.calling_convention, func.prototype, func.prototype_libname
|
|
445
|
+
tpl = func.calling_convention, func.prototype, func.prototype_libname, func.is_prototype_guessed
|
|
433
446
|
d[callee] = tpl
|
|
434
447
|
return d
|
|
435
448
|
|
|
@@ -168,6 +168,7 @@ class AILSimplifier(Analysis):
|
|
|
168
168
|
fold_callexprs_into_conditions=False,
|
|
169
169
|
use_callee_saved_regs_at_return=True,
|
|
170
170
|
rewrite_ccalls=True,
|
|
171
|
+
rename_ccalls=True,
|
|
171
172
|
removed_vvar_ids: set[int] | None = None,
|
|
172
173
|
arg_vvars: dict[int, tuple[VirtualVariable, SimVariable]] | None = None,
|
|
173
174
|
avoid_vvar_ids: set[int] | None = None,
|
|
@@ -188,6 +189,7 @@ class AILSimplifier(Analysis):
|
|
|
188
189
|
self._fold_callexprs_into_conditions = fold_callexprs_into_conditions
|
|
189
190
|
self._use_callee_saved_regs_at_return = use_callee_saved_regs_at_return
|
|
190
191
|
self._should_rewrite_ccalls = rewrite_ccalls
|
|
192
|
+
self._should_rename_ccalls = rename_ccalls
|
|
191
193
|
self._removed_vvar_ids = removed_vvar_ids if removed_vvar_ids is not None else set()
|
|
192
194
|
self._arg_vvars = arg_vvars
|
|
193
195
|
self._avoid_vvar_ids = avoid_vvar_ids if avoid_vvar_ids is not None else set()
|
|
@@ -1992,20 +1994,20 @@ class AILSimplifier(Analysis):
|
|
|
1992
1994
|
|
|
1993
1995
|
v = False
|
|
1994
1996
|
|
|
1995
|
-
def
|
|
1996
|
-
expr_idx: int, expr:
|
|
1997
|
+
def _handle_VEXCCallExpression(
|
|
1998
|
+
expr_idx: int, expr: VEXCCallExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
1997
1999
|
) -> Expression | None:
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
return
|
|
2000
|
+
r_expr = AILBlockWalker._handle_VEXCCallExpression(walker, expr_idx, expr, stmt_idx, stmt, block)
|
|
2001
|
+
if r_expr is None:
|
|
2002
|
+
r_expr = expr
|
|
2003
|
+
rewriter = rewriter_cls(r_expr, self.project.arch, rename_ccalls=self._should_rename_ccalls)
|
|
2004
|
+
if rewriter.result is not None:
|
|
2005
|
+
_any_update.v = True
|
|
2006
|
+
return rewriter.result
|
|
2007
|
+
return r_expr if r_expr is not expr else None
|
|
2006
2008
|
|
|
2007
2009
|
blocks_by_addr_and_idx = {(node.addr, node.idx): node for node in self.func_graph.nodes()}
|
|
2008
|
-
walker.
|
|
2010
|
+
walker.expr_handlers[VEXCCallExpression] = _handle_VEXCCallExpression
|
|
2009
2011
|
|
|
2010
2012
|
updated = False
|
|
2011
2013
|
for block in blocks_by_addr_and_idx.values():
|
|
@@ -12,9 +12,13 @@ class CCallRewriterBase:
|
|
|
12
12
|
"result",
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
def __init__(self, ccall: ailment.Expr.VEXCCallExpression, arch):
|
|
15
|
+
def __init__(self, ccall: ailment.Expr.VEXCCallExpression, arch, rename_ccalls: bool = False):
|
|
16
16
|
self.arch = arch
|
|
17
17
|
self.result: ailment.Expr.Expression | None = self._rewrite(ccall)
|
|
18
|
+
if rename_ccalls and self.result is None and ccall.callee != "_ccall":
|
|
19
|
+
renamed = ccall.copy()
|
|
20
|
+
renamed.callee = "_ccall"
|
|
21
|
+
self.result = renamed
|
|
18
22
|
|
|
19
23
|
def _rewrite(self, ccall: ailment.Expr.VEXCCallExpression) -> ailment.Expr.Expression | None:
|
|
20
24
|
raise NotImplementedError
|
|
@@ -1231,52 +1231,62 @@ class Clinic(Analysis):
|
|
|
1231
1231
|
@timethis
|
|
1232
1232
|
def _replace_tail_jumps_with_calls(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
1233
1233
|
"""
|
|
1234
|
-
|
|
1234
|
+
Rewrite tail jumps to functions as call statements.
|
|
1235
1235
|
"""
|
|
1236
1236
|
for block in list(ail_graph.nodes()):
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
if out_degree != 0:
|
|
1237
|
+
if ail_graph.out_degree[block] > 1:
|
|
1240
1238
|
continue
|
|
1241
1239
|
|
|
1242
1240
|
last_stmt = block.statements[-1]
|
|
1243
1241
|
if isinstance(last_stmt, ailment.Stmt.Jump):
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1242
|
+
targets = [last_stmt.target]
|
|
1243
|
+
replace_last_stmt = True
|
|
1244
|
+
elif isinstance(last_stmt, ailment.Stmt.ConditionalJump):
|
|
1245
|
+
targets = [last_stmt.true_target, last_stmt.false_target]
|
|
1246
|
+
replace_last_stmt = False
|
|
1247
|
+
else:
|
|
1248
|
+
continue
|
|
1249
|
+
|
|
1250
|
+
for target in targets:
|
|
1251
|
+
if not isinstance(target, ailment.Const) or not self.kb.functions.contains_addr(target.value):
|
|
1252
|
+
continue
|
|
1253
|
+
|
|
1254
|
+
target_func = self.kb.functions.get_by_addr(target.value)
|
|
1255
|
+
|
|
1256
|
+
ret_reg_offset = self.project.arch.ret_offset
|
|
1257
|
+
if target_func.returning and ret_reg_offset is not None:
|
|
1258
|
+
ret_expr = ailment.Expr.Register(
|
|
1259
|
+
None,
|
|
1260
|
+
None,
|
|
1261
|
+
ret_reg_offset,
|
|
1262
|
+
self.project.arch.bits,
|
|
1263
|
+
reg_name=self.project.arch.translate_register_name(ret_reg_offset, size=self.project.arch.bits),
|
|
1264
|
+
**target.tags,
|
|
1265
|
+
)
|
|
1266
|
+
else:
|
|
1267
|
+
ret_expr = None
|
|
1268
|
+
|
|
1269
|
+
call_stmt = ailment.Stmt.Call(
|
|
1270
|
+
None,
|
|
1271
|
+
target.copy(),
|
|
1272
|
+
calling_convention=None, # target_func.calling_convention,
|
|
1273
|
+
prototype=None, # target_func.prototype,
|
|
1274
|
+
ret_expr=ret_expr,
|
|
1275
|
+
**last_stmt.tags,
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
if replace_last_stmt:
|
|
1279
|
+
call_block = block
|
|
1280
|
+
block.statements[-1] = call_stmt
|
|
1281
|
+
else:
|
|
1282
|
+
call_block = ailment.Block(self.new_block_addr(), 1, statements=[call_stmt])
|
|
1283
|
+
ail_graph.add_edge(block, call_block)
|
|
1284
|
+
target.value = call_block.addr
|
|
1285
|
+
|
|
1286
|
+
if target_func.returning:
|
|
1287
|
+
ret_stmt = ailment.Stmt.Return(None, [], **last_stmt.tags)
|
|
1288
|
+
ret_block = ailment.Block(self.new_block_addr(), 1, statements=[ret_stmt])
|
|
1289
|
+
ail_graph.add_edge(call_block, ret_block, type="fake_return")
|
|
1280
1290
|
|
|
1281
1291
|
return ail_graph
|
|
1282
1292
|
|
|
@@ -1443,6 +1453,7 @@ class Clinic(Analysis):
|
|
|
1443
1453
|
only_consts=False,
|
|
1444
1454
|
fold_callexprs_into_conditions=False,
|
|
1445
1455
|
rewrite_ccalls=True,
|
|
1456
|
+
rename_ccalls=True,
|
|
1446
1457
|
removed_vvar_ids: set[int] | None = None,
|
|
1447
1458
|
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
|
|
1448
1459
|
preserve_vvar_ids: set[int] | None = None,
|
|
@@ -1462,6 +1473,7 @@ class Clinic(Analysis):
|
|
|
1462
1473
|
only_consts=only_consts,
|
|
1463
1474
|
fold_callexprs_into_conditions=fold_callexprs_into_conditions,
|
|
1464
1475
|
rewrite_ccalls=rewrite_ccalls,
|
|
1476
|
+
rename_ccalls=rename_ccalls,
|
|
1465
1477
|
removed_vvar_ids=removed_vvar_ids,
|
|
1466
1478
|
arg_vvars=arg_vvars,
|
|
1467
1479
|
preserve_vvar_ids=preserve_vvar_ids,
|
|
@@ -1480,6 +1492,7 @@ class Clinic(Analysis):
|
|
|
1480
1492
|
only_consts=False,
|
|
1481
1493
|
fold_callexprs_into_conditions=False,
|
|
1482
1494
|
rewrite_ccalls=True,
|
|
1495
|
+
rename_ccalls=True,
|
|
1483
1496
|
removed_vvar_ids: set[int] | None = None,
|
|
1484
1497
|
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
|
|
1485
1498
|
preserve_vvar_ids: set[int] | None = None,
|
|
@@ -1504,6 +1517,7 @@ class Clinic(Analysis):
|
|
|
1504
1517
|
fold_callexprs_into_conditions=fold_callexprs_into_conditions,
|
|
1505
1518
|
use_callee_saved_regs_at_return=not self._register_save_areas_removed,
|
|
1506
1519
|
rewrite_ccalls=rewrite_ccalls,
|
|
1520
|
+
rename_ccalls=rename_ccalls,
|
|
1507
1521
|
removed_vvar_ids=removed_vvar_ids,
|
|
1508
1522
|
arg_vvars=arg_vvars,
|
|
1509
1523
|
secondary_stackvars=self.secondary_stackvars,
|
|
@@ -296,7 +296,7 @@ class ITERegionConverter(OptimizationPass):
|
|
|
296
296
|
)
|
|
297
297
|
|
|
298
298
|
if len(new_src_and_vvars) == 1:
|
|
299
|
-
|
|
299
|
+
new_stmt = Assignment(
|
|
300
300
|
stmt.idx,
|
|
301
301
|
stmt.dst,
|
|
302
302
|
new_src_and_vvars[0][1],
|
|
@@ -309,13 +309,13 @@ class ITERegionConverter(OptimizationPass):
|
|
|
309
309
|
new_src_and_vvars,
|
|
310
310
|
**stmt.src.tags,
|
|
311
311
|
)
|
|
312
|
-
|
|
312
|
+
new_stmt = Assignment(
|
|
313
313
|
stmt.idx,
|
|
314
314
|
stmt.dst,
|
|
315
315
|
new_phi,
|
|
316
316
|
**stmt.tags,
|
|
317
317
|
)
|
|
318
|
-
stmts.append(
|
|
318
|
+
stmts.append(new_stmt)
|
|
319
319
|
new_region_tail = Block(region_tail.addr, region_tail.original_size, statements=stmts, idx=region_tail.idx)
|
|
320
320
|
|
|
321
321
|
#
|
|
@@ -864,7 +864,7 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
|
|
|
864
864
|
next_node_addr = last_block.addr
|
|
865
865
|
|
|
866
866
|
while next_node_addr is not None and next_node_addr in ca_others:
|
|
867
|
-
onode,
|
|
867
|
+
onode, _value, target, target_idx, next_node_addr = ca_others[next_node_addr]
|
|
868
868
|
onode: Block
|
|
869
869
|
|
|
870
870
|
if first_nonlabel_nonphi_statement(onode) is not onode.statements[-1]:
|
|
@@ -893,7 +893,7 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
|
|
|
893
893
|
|
|
894
894
|
# default nodes
|
|
895
895
|
if ca_default:
|
|
896
|
-
onode,
|
|
896
|
+
onode, _value, target, target_idx, _ = ca_default[0]
|
|
897
897
|
default_target = next(
|
|
898
898
|
iter(
|
|
899
899
|
nn
|
|
@@ -46,8 +46,8 @@ from .rol_ror import RolRorRewriter
|
|
|
46
46
|
from .inlined_memcpy import InlinedMemcpy
|
|
47
47
|
from .inlined_strcpy import InlinedStrcpy
|
|
48
48
|
from .inlined_strcpy_consolidation import InlinedStrcpyConsolidation
|
|
49
|
-
from .
|
|
50
|
-
from .
|
|
49
|
+
from .inlined_wcscpy import InlinedWcscpy
|
|
50
|
+
from .inlined_wcscpy_consolidation import InlinedWcscpyConsolidation
|
|
51
51
|
from .cmpord_rewriter import CmpORDRewriter
|
|
52
52
|
from .coalesce_adjacent_shrs import CoalesceAdjacentShiftRights
|
|
53
53
|
from .a_mul_const_sub_a import AMulConstSubA
|
|
@@ -104,8 +104,8 @@ ALL_PEEPHOLE_OPTS: list[type[PeepholeOptimizationExprBase]] = [
|
|
|
104
104
|
InlinedMemcpy,
|
|
105
105
|
InlinedStrcpy,
|
|
106
106
|
InlinedStrcpyConsolidation,
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
InlinedWcscpy,
|
|
108
|
+
InlinedWcscpyConsolidation,
|
|
109
109
|
CmpORDRewriter,
|
|
110
110
|
CoalesceAdjacentShiftRights,
|
|
111
111
|
ShlToMul,
|
|
@@ -17,17 +17,20 @@ ASCII_PRINTABLES = {ord(x) for x in string.printable}
|
|
|
17
17
|
ASCII_DIGITS = {ord(x) for x in string.digits}
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class
|
|
20
|
+
class InlinedWcscpy(PeepholeOptimizationStmtBase):
|
|
21
21
|
"""
|
|
22
|
-
Simplifies inlined wide string copying logic into calls to
|
|
22
|
+
Simplifies inlined wide string copying logic into calls to wcscpy.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
__slots__ = ()
|
|
26
26
|
|
|
27
|
-
NAME = "Simplifying inlined
|
|
27
|
+
NAME = "Simplifying inlined wcscpy"
|
|
28
28
|
stmt_classes = (Assignment, Store)
|
|
29
29
|
|
|
30
30
|
def optimize(self, stmt: Assignment | Store, stmt_idx: int | None = None, block=None, **kwargs):
|
|
31
|
+
assert self.project is not None
|
|
32
|
+
assert self.kb is not None
|
|
33
|
+
|
|
31
34
|
if (
|
|
32
35
|
isinstance(stmt, Assignment)
|
|
33
36
|
and isinstance(stmt.dst, VirtualVariable)
|
|
@@ -48,11 +51,12 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
48
51
|
r, s = self.is_integer_likely_a_wide_string(value, value_size, self.project.arch.memory_endness)
|
|
49
52
|
if r:
|
|
50
53
|
# replace it with a call to strncpy
|
|
54
|
+
assert s is not None
|
|
51
55
|
str_id = self.kb.custom_strings.allocate(s)
|
|
52
56
|
wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
|
|
53
57
|
return Call(
|
|
54
58
|
stmt.idx,
|
|
55
|
-
"
|
|
59
|
+
"wcsncpy",
|
|
56
60
|
args=[
|
|
57
61
|
dst,
|
|
58
62
|
Const(None, None, str_id, self.project.arch.bits, custom_string=True, type=wstr_type),
|
|
@@ -83,6 +87,7 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
83
87
|
integer, size = self.stride_to_int(stride)
|
|
84
88
|
r, s = self.is_integer_likely_a_wide_string(integer, size, Endness.BE, min_length=3)
|
|
85
89
|
if r:
|
|
90
|
+
assert s is not None
|
|
86
91
|
# we remove all involved statements whose statement IDs are greater than the current one
|
|
87
92
|
for _, stmt_idx_, _ in reversed(stride):
|
|
88
93
|
if stmt_idx_ <= stmt_idx:
|
|
@@ -94,7 +99,7 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
94
99
|
wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
|
|
95
100
|
return Call(
|
|
96
101
|
stmt.idx,
|
|
97
|
-
"
|
|
102
|
+
"wcsncpy",
|
|
98
103
|
args=[
|
|
99
104
|
dst,
|
|
100
105
|
Const(None, None, str_id, self.project.arch.bits, custom_string=True, type=wstr_type),
|
|
@@ -112,11 +117,14 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
112
117
|
size = 0
|
|
113
118
|
for _, _, v in stride:
|
|
114
119
|
size += v.size
|
|
120
|
+
assert isinstance(v.value, int)
|
|
115
121
|
n <<= v.bits
|
|
116
122
|
n |= v.value
|
|
117
123
|
return n, size
|
|
118
124
|
|
|
119
125
|
def collect_constant_stores(self, block, starting_stmt_idx: int) -> dict[int, tuple[int, Const | None]]:
|
|
126
|
+
assert self.project is not None
|
|
127
|
+
|
|
120
128
|
r = {}
|
|
121
129
|
expected_store_varid: int | None = None
|
|
122
130
|
starting_stmt = block.statements[starting_stmt_idx]
|
|
@@ -224,7 +232,7 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
224
232
|
# unsupported endness
|
|
225
233
|
return False, None
|
|
226
234
|
|
|
227
|
-
if not (
|
|
235
|
+
if not (InlinedWcscpy.even_offsets_are_zero(chars) or InlinedWcscpy.odd_offsets_are_zero(chars)):
|
|
228
236
|
return False, None
|
|
229
237
|
|
|
230
238
|
if chars and len(chars) >= 2 and chars[-1] == 0 and chars[-2] == 0:
|
|
@@ -236,11 +244,11 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
236
244
|
return False, None
|
|
237
245
|
|
|
238
246
|
@staticmethod
|
|
239
|
-
def
|
|
247
|
+
def is_inlined_wcsncpy(stmt: Statement) -> bool:
|
|
240
248
|
return (
|
|
241
249
|
isinstance(stmt, Call)
|
|
242
250
|
and isinstance(stmt.target, str)
|
|
243
|
-
and stmt.target == "
|
|
251
|
+
and stmt.target == "wcsncpy"
|
|
244
252
|
and stmt.args is not None
|
|
245
253
|
and len(stmt.args) == 3
|
|
246
254
|
and isinstance(stmt.args[1], Const)
|