angr 9.2.122__py3-none-win_amd64.whl → 9.2.123__py3-none-win_amd64.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 CHANGED
@@ -2,7 +2,7 @@
2
2
  # pylint: disable=wrong-import-position
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "9.2.122"
5
+ __version__ = "9.2.123"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
@@ -356,9 +356,12 @@ class CallingConventionAnalysis(Analysis):
356
356
  caller_block_addr: int,
357
357
  call_insn_addr: int,
358
358
  include_preds: bool = False,
359
- ) -> CallSiteFact:
359
+ ) -> CallSiteFact | None:
360
360
  func = self.kb.functions[caller_addr]
361
361
  subgraph = self._generate_callsite_subgraph(func, caller_block_addr, include_preds=include_preds)
362
+ if subgraph is None:
363
+ # failed to generate a subgraph when the caller block cannot be found in the function graph
364
+ return None
362
365
 
363
366
  observation_points: list = [("insn", call_insn_addr, OP_BEFORE), ("node", caller_block_addr, OP_AFTER)]
364
367
 
@@ -440,6 +443,8 @@ class CallingConventionAnalysis(Analysis):
440
443
  call_insn_addr,
441
444
  include_preds=include_callsite_preds,
442
445
  )
446
+ if fact is None:
447
+ continue
443
448
  facts.append(fact)
444
449
 
445
450
  ctr += 1
@@ -22,7 +22,6 @@ from ailment.expression import (
22
22
  Const,
23
23
  BinaryOp,
24
24
  VirtualVariable,
25
- Phi,
26
25
  )
27
26
 
28
27
  from angr.analyses.s_reaching_definitions import SRDAModel
@@ -36,7 +35,7 @@ from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE
36
35
  from angr.errors import AngrRuntimeError
37
36
  from angr.analyses import Analysis, AnalysesHub
38
37
  from .ailgraph_walker import AILGraphWalker
39
- from .expression_narrower import ExpressionNarrowingWalker
38
+ from .expression_narrower import ExprNarrowingInfo, NarrowingInfoExtractor, ExpressionNarrower
40
39
  from .block_simplifier import BlockSimplifier
41
40
  from .ccall_rewriters import CCALL_REWRITERS
42
41
  from .counters.expression_counters import SingleExpressionCounter
@@ -76,26 +75,6 @@ class AILBlockTempCollector(AILBlockWalker):
76
75
  self.temps.add(expr)
77
76
 
78
77
 
79
- class ExprNarrowingInfo:
80
- """
81
- Stores the analysis result of _narrowing_needed().
82
- """
83
-
84
- __slots__ = ("narrowable", "to_size", "use_exprs", "phi_vars")
85
-
86
- def __init__(
87
- self,
88
- narrowable: bool,
89
- to_size: int | None = None,
90
- use_exprs: list[tuple[atoms.VirtualVariable, CodeLocation, tuple[str, tuple[Expression, ...]]]] | None = None,
91
- phi_vars: set[atoms.VirtualVariable] | None = None,
92
- ):
93
- self.narrowable = narrowable
94
- self.to_size = to_size
95
- self.use_exprs = use_exprs
96
- self.phi_vars = phi_vars
97
-
98
-
99
78
  class AILSimplifier(Analysis):
100
79
  """
101
80
  Perform function-level simplifications.
@@ -343,309 +322,26 @@ class AILSimplifier(Analysis):
343
322
  if not repeat:
344
323
  break
345
324
 
346
- replaced_vvar_ids = set()
347
-
348
325
  # let's narrow them (finally)
349
- for def_, narrow_info in narrowables:
350
-
351
- # does any uses involve a previously replaced expressions? if so, we have to skip this one because the use
352
- # expression may no longer exist.
353
- should_skip = False
354
- for _, _, (use_type, use_expr_tpl) in narrow_info.use_exprs:
355
- if use_type == "binop-convert" and self._exprs_contain_vvar(use_expr_tpl, replaced_vvar_ids):
356
- should_skip = True
357
- break
358
- if should_skip:
359
- continue
360
-
361
- # replace the definition
362
- if not isinstance(def_.codeloc, ExternalCodeLocation):
363
- old_block = addr_and_idx_to_block.get((def_.codeloc.block_addr, def_.codeloc.block_idx))
364
- if old_block is None:
365
- # this definition might be inside a callee function, which is why the block does not exist
366
- # ignore it
367
- continue
368
-
369
- the_block = self.blocks.get(old_block, old_block)
370
- stmt = the_block.statements[def_.codeloc.stmt_idx]
371
- r, new_block = False, None
372
- replaced_vvar: VirtualVariable | None = None
373
- if is_phi_assignment(stmt):
374
- new_assignment_dst = VirtualVariable(
375
- stmt.dst.idx,
376
- stmt.dst.varid,
377
- narrow_info.to_size * self.project.arch.byte_width,
378
- category=def_.atom.category,
379
- oident=def_.atom.oident,
380
- **stmt.dst.tags,
381
- )
382
- new_src_and_vvars = []
383
- for src, vvar in stmt.src.src_and_vvars:
384
- if vvar is not None and vvar.varid == stmt.dst.varid:
385
- new_vvar = VirtualVariable(
386
- vvar.idx,
387
- vvar.varid,
388
- narrow_info.to_size * self.project.arch.byte_width,
389
- category=vvar.category,
390
- oident=vvar.oident,
391
- **vvar.tags,
392
- )
393
- else:
394
- new_vvar = vvar
395
- new_src_and_vvars.append((src, new_vvar))
396
- new_assignment_src = Phi(
397
- stmt.src.idx,
398
- narrow_info.to_size * self.project.arch.byte_width,
399
- new_src_and_vvars,
400
- **stmt.src.tags,
401
- )
402
- replaced_vvar = stmt.dst
403
- r, new_block = BlockSimplifier._replace_and_build(
404
- the_block,
405
- {
406
- def_.codeloc: {
407
- stmt.dst: new_assignment_dst,
408
- stmt.src: new_assignment_src,
409
- }
410
- },
411
- replace_assignment_dsts=True,
412
- replace_loads=True,
413
- )
414
- elif isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable) and stmt.dst.was_reg:
415
- new_assignment_dst = VirtualVariable(
416
- stmt.dst.idx,
417
- stmt.dst.varid,
418
- narrow_info.to_size * self.project.arch.byte_width,
419
- category=def_.atom.category,
420
- oident=def_.atom.oident,
421
- **stmt.dst.tags,
422
- )
423
- new_assignment_src = Convert(
424
- stmt.src.idx, # FIXME: This is a hack
425
- stmt.src.bits,
426
- narrow_info.to_size * self.project.arch.byte_width,
427
- False,
428
- stmt.src,
429
- **stmt.src.tags,
430
- )
431
- replaced_vvar = stmt.dst
432
- r, new_block = BlockSimplifier._replace_and_build(
433
- the_block,
434
- {
435
- def_.codeloc: {
436
- stmt.dst: new_assignment_dst,
437
- stmt.src: new_assignment_src,
438
- }
439
- },
440
- replace_assignment_dsts=True,
441
- replace_loads=True,
442
- )
443
- elif isinstance(stmt, Call):
444
- if stmt.ret_expr is not None:
445
- tags = dict(stmt.ret_expr.tags)
446
- tags["reg_name"] = self.project.arch.translate_register_name(
447
- def_.atom.reg_offset, size=narrow_info.to_size
448
- )
449
- replaced_vvar = stmt.ret_expr
450
- new_retexpr = VirtualVariable(
451
- stmt.ret_expr.idx,
452
- stmt.ret_expr.varid,
453
- narrow_info.to_size * self.project.arch.byte_width,
454
- category=def_.atom.category,
455
- oident=def_.atom.oident,
456
- **stmt.ret_expr.tags,
457
- )
458
- r, new_block = BlockSimplifier._replace_and_build(
459
- the_block, {def_.codeloc: {stmt.ret_expr: new_retexpr}}
460
- )
461
- if not r:
462
- # couldn't replace the definition...
463
- continue
326
+ narrower = ExpressionNarrower(self.project, rd, narrowables, addr_and_idx_to_block, self.blocks)
327
+ for old_block in addr_and_idx_to_block.values():
328
+ new_block = self.blocks.get(old_block, old_block)
329
+ new_block = narrower.walk(new_block)
330
+ if narrower.narrowed_any:
331
+ narrowed = True
464
332
  self.blocks[old_block] = new_block
465
- if replaced_vvar is not None:
466
- replaced_vvar_ids.add(replaced_vvar.varid)
467
-
468
- use_exprs = list(narrow_info.use_exprs)
469
- if narrow_info.phi_vars:
470
- for phi_var in narrow_info.phi_vars:
471
- loc = rd.all_vvar_definitions[phi_var]
472
- old_block = addr_and_idx_to_block.get((loc.block_addr, loc.block_idx))
473
- the_block = self.blocks.get(old_block, old_block)
474
- stmt = the_block.statements[loc.stmt_idx]
475
- assert is_phi_assignment(stmt)
476
-
477
- for _, vvar in stmt.src.src_and_vvars:
478
- if vvar is not None and vvar.varid == def_.atom.varid:
479
- use_exprs.append((vvar, loc, ("phi-src-expr", (vvar,))))
480
-
481
- # replace all uses if necessary
482
- for use_atom, use_loc, (use_type, use_expr_tpl) in use_exprs:
483
- if (
484
- isinstance(use_expr_tpl[0], VirtualVariable)
485
- and use_expr_tpl[0].was_reg
486
- and narrow_info.to_size == use_expr_tpl[0].size
487
- ):
488
- # don't replace registers to the same registers
489
- continue
490
- if use_atom.varid != def_.atom.varid:
491
- # don't replace this use - it will be replaced later
492
- continue
493
-
494
- old_block = addr_and_idx_to_block.get((use_loc.block_addr, use_loc.block_idx))
495
- the_block = self.blocks.get(old_block, old_block)
496
333
 
497
- if use_type in {"expr", "mask", "convert"}:
498
- # the first used expr
499
- use_expr_0 = use_expr_tpl[0]
500
- new_use_expr_0 = VirtualVariable(
501
- use_expr_0.idx,
502
- def_.atom.varid,
503
- narrow_info.to_size * self.project.arch.byte_width,
504
- category=def_.atom.category,
505
- oident=def_.atom.oident,
506
- **use_expr_0.tags,
507
- )
508
- new_use_expr = new_use_expr_0
509
-
510
- # the second used expr (if it exists)
511
- if len(use_expr_tpl) == 2:
512
- use_expr_1 = use_expr_tpl[1]
513
- assert isinstance(use_expr_1, BinaryOp)
514
- con = use_expr_1.operands[1]
515
- assert isinstance(con, Const)
516
- new_use_expr_1 = BinaryOp(
517
- use_expr_1.idx,
518
- use_expr_1.op,
519
- [
520
- new_use_expr_0,
521
- Const(con.idx, con.variable, con.value, new_use_expr_0.bits, **con.tags),
522
- ],
523
- use_expr_1.signed,
524
- floating_point=use_expr_1.floating_point,
525
- rounding_mode=use_expr_1.rounding_mode,
526
- **use_expr_1.tags,
527
- )
528
-
529
- if use_expr_1.size > new_use_expr_1.size:
530
- new_use_expr_1 = Convert(
531
- None,
532
- new_use_expr_1.bits,
533
- use_expr_1.bits,
534
- False,
535
- new_use_expr_1,
536
- **new_use_expr_1.tags,
537
- )
538
-
539
- r, new_block = BlockSimplifier._replace_and_build(
540
- the_block, {use_loc: {use_expr_1: new_use_expr_1}}
541
- )
542
- elif len(use_expr_tpl) == 1:
543
- if use_expr_0.size > new_use_expr_0.size:
544
- new_use_expr_0 = Convert(
545
- None,
546
- new_use_expr_0.bits,
547
- use_expr_0.bits,
548
- False,
549
- new_use_expr_0,
550
- **new_use_expr_0.tags,
551
- )
552
-
553
- r, new_block = BlockSimplifier._replace_and_build(
554
- the_block, {use_loc: {use_expr_0: new_use_expr_0}}
555
- )
556
- else:
557
- _l.warning("Nothing to replace at %s.", use_loc)
558
- r = False
559
- new_block = None
560
-
561
- elif use_type == "phi-src-expr":
562
- # the size of the replaced variable will be different from its original size, and it's expected
563
- use_expr = use_expr_tpl[0]
564
- new_use_expr = VirtualVariable(
565
- use_expr.idx,
566
- def_.atom.varid,
567
- narrow_info.to_size * self.project.arch.byte_width,
568
- category=def_.atom.category,
569
- oident=def_.atom.oident,
570
- **use_expr.tags,
571
- )
572
- r, new_block = BlockSimplifier._replace_and_build(the_block, {use_loc: {use_expr: new_use_expr}})
573
-
574
- elif use_type == "binop-convert":
575
- use_expr_0 = use_expr_tpl[0]
576
- new_use_expr_0 = VirtualVariable(
577
- use_expr_0.idx,
578
- def_.atom.varid,
579
- narrow_info.to_size * self.project.arch.byte_width,
580
- category=def_.atom.category,
581
- oident=def_.atom.oident,
582
- **use_expr_0.tags,
583
- )
584
- new_use_expr = new_use_expr_0
585
-
586
- use_expr_1: BinaryOp = use_expr_tpl[1]
587
- # build the new use_expr_1
588
- new_use_expr_1_operands = {}
589
- if use_expr_1.operands[0] is use_expr_0:
590
- new_use_expr_1_operands[0] = new_use_expr_0
591
- other_operand = use_expr_1.operands[1]
592
- else:
593
- new_use_expr_1_operands[1] = new_use_expr_0
594
- other_operand = use_expr_1.operands[0]
595
- use_expr_2: Convert = use_expr_tpl[2]
596
- if other_operand.bits == use_expr_2.from_bits:
597
- new_other_operand = Convert(
598
- None, use_expr_2.from_bits, use_expr_2.to_bits, False, other_operand
599
- )
600
- else:
601
- # Some operations, like Sar and Shl, have operands with different sizes
602
- new_other_operand = other_operand
603
-
604
- if 0 in new_use_expr_1_operands:
605
- new_use_expr_1_operands[1] = new_other_operand
606
- else:
607
- new_use_expr_1_operands[0] = new_other_operand
608
-
609
- # build new use_expr_1
610
- new_use_expr_1 = BinaryOp(
611
- use_expr_1.idx,
612
- use_expr_1.op,
613
- [new_use_expr_1_operands[0], new_use_expr_1_operands[1]],
614
- use_expr_1.signed,
615
- bits=narrow_info.to_size * 8,
616
- floating_point=use_expr_1.floating_point,
617
- rounding_mode=use_expr_1.rounding_mode,
618
- **use_expr_1.tags,
619
- )
620
-
621
- # first remove the old conversion
622
- r, new_block = BlockSimplifier._replace_and_build(
623
- the_block, {use_loc: {use_expr_2: use_expr_2.operand}}
624
- )
625
- # then replace use_expr_1
626
- if r:
627
- r, new_block = BlockSimplifier._replace_and_build(
628
- new_block, {use_loc: {use_expr_1: new_use_expr_1}}
629
- )
630
- else:
631
- raise TypeError(f'Unsupported use_type value "{use_type}"')
632
-
633
- if not r:
634
- _l.warning("Failed to replace use-expr at %s.", use_loc)
635
- else:
636
- # update self._arg_vvars if necessary
637
- if new_use_expr is not None and new_use_expr.was_parameter and self._arg_vvars:
638
- for func_arg_idx in list(self._arg_vvars):
639
- vvar, simvar = self._arg_vvars[func_arg_idx]
640
- if vvar.varid == new_use_expr.varid:
641
- simvar_new = simvar.copy()
642
- simvar_new._hash = None
643
- simvar_new.size = new_use_expr.size
644
- self._arg_vvars[func_arg_idx] = new_use_expr, simvar_new
645
-
646
- self.blocks[old_block] = new_block
647
-
648
- narrowed = True
334
+ # update self._arg_vvars if necessary
335
+ for new_vvars in narrower.replacement_core_vvars.values():
336
+ for new_vvar in new_vvars:
337
+ if new_vvar.was_parameter and self._arg_vvars:
338
+ for func_arg_idx in list(self._arg_vvars):
339
+ vvar, simvar = self._arg_vvars[func_arg_idx]
340
+ if vvar.varid == new_vvar.varid:
341
+ simvar_new = simvar.copy()
342
+ simvar_new._hash = None
343
+ simvar_new.size = new_vvar.size
344
+ self._arg_vvars[func_arg_idx] = new_vvar, simvar_new
649
345
 
650
346
  return narrowed
651
347
 
@@ -669,6 +365,9 @@ class AILSimplifier(Analysis):
669
365
  if len(narrowing_sizes) == 1 and None not in narrowing_sizes:
670
366
  # we can narrow this phi vvar!
671
367
  narrowable_phivarids.add(def_vvarid)
368
+ else:
369
+ # blacklist it for now
370
+ blacklist_varids.add(def_vvarid)
672
371
 
673
372
  # now determine what to narrow!
674
373
  narrowables = []
@@ -834,7 +533,7 @@ class AILSimplifier(Analysis):
834
533
  Determine the effective size of an expression when it's used.
835
534
  """
836
535
 
837
- walker = ExpressionNarrowingWalker(expr)
536
+ walker = NarrowingInfoExtractor(expr)
838
537
  walker.walk_statement(statement)
839
538
  if not walker.operations:
840
539
  if expr is None:
@@ -749,8 +749,9 @@ class Clinic(Analysis):
749
749
  continue
750
750
 
751
751
  call_sites = []
752
- for pred in self.function.transition_graph.predecessors(node):
753
- call_sites.append(pred)
752
+ for pred, _, data in self.function.transition_graph.in_edges(node, data=True):
753
+ if data.get("type", None) != "return":
754
+ call_sites.append(pred)
754
755
  # case 1: calling conventions and prototypes are available at every single call site
755
756
  if call_sites and all(self.kb.callsite_prototypes.has_prototype(callsite.addr) for callsite in call_sites):
756
757
  continue
@@ -126,7 +126,7 @@ class SimEngineDephiRewriting(
126
126
  return None
127
127
 
128
128
  def _handle_Call(self, stmt: Call) -> Call | None:
129
- new_target = self._expr(stmt.target) if stmt.target is not None else None
129
+ new_target = self._expr(stmt.target) if stmt.target is not None and not isinstance(stmt.target, str) else None
130
130
  new_ret_expr = self._expr(stmt.ret_expr) if stmt.ret_expr is not None else None
131
131
  new_fp_ret_expr = self._expr(stmt.fp_ret_expr) if stmt.fp_ret_expr is not None else None
132
132
 
@@ -1,23 +1,52 @@
1
1
  from __future__ import annotations
2
2
  from typing import Any, TYPE_CHECKING
3
- from ailment import AILBlockWalkerBase
3
+ from collections import defaultdict
4
+ import logging
5
+
6
+ from ailment import AILBlockWalkerBase, AILBlockWalker
7
+ from ailment.statement import Assignment, Call
8
+ from ailment.expression import VirtualVariable, Convert, BinaryOp, Phi
9
+
10
+ from angr.knowledge_plugins.key_definitions import atoms
11
+ from angr.code_location import CodeLocation
4
12
 
5
13
  if TYPE_CHECKING:
6
14
  from ailment.expression import (
7
15
  Expression,
8
- BinaryOp,
9
16
  Load,
10
17
  UnaryOp,
11
- Convert,
12
18
  ITE,
13
19
  DirtyExpression,
14
20
  VEXCCallExpression,
15
21
  )
16
- from ailment.statement import Call, Statement
22
+ from ailment.statement import Statement
17
23
  from ailment.block import Block
18
24
 
19
25
 
20
- class ExpressionNarrowingWalker(AILBlockWalkerBase):
26
+ _l = logging.getLogger(__name__)
27
+
28
+
29
+ class ExprNarrowingInfo:
30
+ """
31
+ Stores the analysis result of _narrowing_needed().
32
+ """
33
+
34
+ __slots__ = ("narrowable", "to_size", "use_exprs", "phi_vars")
35
+
36
+ def __init__(
37
+ self,
38
+ narrowable: bool,
39
+ to_size: int | None = None,
40
+ use_exprs: list[tuple[atoms.VirtualVariable, CodeLocation, tuple[str, tuple[Expression, ...]]]] | None = None,
41
+ phi_vars: set[atoms.VirtualVariable] | None = None,
42
+ ):
43
+ self.narrowable = narrowable
44
+ self.to_size = to_size
45
+ self.use_exprs = use_exprs
46
+ self.phi_vars = phi_vars
47
+
48
+
49
+ class NarrowingInfoExtractor(AILBlockWalkerBase):
21
50
  """
22
51
  Walks a statement or an expression and extracts the operations that are applied on the given expression.
23
52
 
@@ -85,3 +114,170 @@ class ExpressionNarrowingWalker(AILBlockWalkerBase):
85
114
  for idx, operand in enumerate(expr.operands):
86
115
  r |= self._handle_expr(idx, operand, stmt_idx, stmt, block)
87
116
  return r
117
+
118
+
119
+ class ExpressionNarrower(AILBlockWalker):
120
+ """
121
+ Narrows an expression regardless of whether the expression is a definition or a use.
122
+ """
123
+
124
+ def __init__(
125
+ self, project, rd, narrowables, addr2blocks: dict[tuple[int, int | None], Block], new_blocks: dict[Block, Block]
126
+ ):
127
+ super().__init__(update_block=False)
128
+
129
+ self.project = project
130
+ self._rd = rd
131
+ self._addr2blocks = addr2blocks
132
+ self._new_blocks = new_blocks
133
+
134
+ self.new_vvar_sizes: dict[int, int] = {}
135
+ self.replacement_core_vvars: dict[int, list[VirtualVariable]] = defaultdict(list)
136
+ self.narrowed_any = False
137
+
138
+ for def_, narrow_info in narrowables:
139
+ self.new_vvar_sizes[def_.atom.varid] = narrow_info.to_size
140
+
141
+ def walk(self, block: Block):
142
+ self.narrowed_any = False
143
+ return super().walk(block)
144
+
145
+ def _handle_Assignment(self, stmt_idx: int, stmt: Assignment, block: Block | None) -> Assignment | None:
146
+
147
+ if isinstance(stmt.src, Phi):
148
+ changed = False
149
+
150
+ src_and_vvars = []
151
+ for src, vvar in stmt.src.src_and_vvars:
152
+ if vvar is None:
153
+ src_and_vvars.append((src, None))
154
+ continue
155
+ if vvar.varid in self.new_vvar_sizes and self.new_vvar_sizes[vvar.varid] != vvar.size:
156
+ self.narrowed_any = True
157
+ changed = True
158
+ new_var = VirtualVariable(
159
+ vvar.idx,
160
+ vvar.varid,
161
+ self.new_vvar_sizes[vvar.varid] * self.project.arch.byte_width,
162
+ category=vvar.category,
163
+ oident=vvar.oident,
164
+ **vvar.tags,
165
+ )
166
+
167
+ self.replacement_core_vvars[new_var.varid].append(new_var)
168
+ else:
169
+ new_var = None
170
+
171
+ src_and_vvars.append((src, new_var))
172
+
173
+ new_src = Phi(stmt.src.idx, stmt.src.bits, src_and_vvars, **stmt.src.tags)
174
+
175
+ else:
176
+ new_src = self._handle_expr(1, stmt.src, stmt_idx, stmt, block)
177
+ if new_src is None:
178
+ changed = False
179
+ new_src = stmt.src
180
+ else:
181
+ changed = True
182
+
183
+ if isinstance(stmt.dst, VirtualVariable) and stmt.dst.varid in self.new_vvar_sizes:
184
+ changed = True
185
+ new_dst = VirtualVariable(
186
+ stmt.dst.idx,
187
+ stmt.dst.varid,
188
+ self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width,
189
+ category=stmt.dst.category,
190
+ oident=stmt.dst.oident,
191
+ **stmt.dst.tags,
192
+ )
193
+
194
+ self.replacement_core_vvars[new_dst.varid].append(new_dst)
195
+
196
+ if isinstance(new_src, Phi):
197
+ new_src.bits = self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width
198
+ else:
199
+ new_src = Convert(
200
+ None,
201
+ stmt.src.bits,
202
+ self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width,
203
+ False,
204
+ new_src,
205
+ **new_src.tags,
206
+ )
207
+ else:
208
+ new_dst = self._handle_expr(0, stmt.dst, stmt_idx, stmt, block)
209
+ if new_dst is not None:
210
+ changed = True
211
+ else:
212
+ new_dst = stmt.dst
213
+
214
+ if changed:
215
+ self.narrowed_any = True
216
+ return Assignment(stmt.idx, new_dst, new_src, **stmt.tags)
217
+
218
+ return None
219
+
220
+ def _handle_VirtualVariable(
221
+ self, expr_idx: int, expr: VirtualVariable, stmt_idx: int, stmt: Statement, block: Block | None
222
+ ) -> Convert | None:
223
+ if expr.varid in self.new_vvar_sizes and self.new_vvar_sizes[expr.varid] != expr.size:
224
+ self.narrowed_any = True
225
+ new_expr = VirtualVariable(
226
+ expr.idx,
227
+ expr.varid,
228
+ self.new_vvar_sizes[expr.varid] * self.project.arch.byte_width,
229
+ category=expr.category,
230
+ oident=expr.oident,
231
+ **expr.tags,
232
+ )
233
+
234
+ self.replacement_core_vvars[expr.varid].append(new_expr)
235
+
236
+ return Convert(
237
+ None,
238
+ new_expr.bits,
239
+ expr.bits,
240
+ False,
241
+ new_expr,
242
+ **new_expr.tags,
243
+ )
244
+ return None
245
+
246
+ def _handle_Call(self, stmt_idx: int, stmt: Call, block: Block | None) -> Call | None:
247
+ new_stmt = super()._handle_Call(stmt_idx, stmt, block)
248
+ if new_stmt is None:
249
+ changed = False
250
+ new_stmt = stmt
251
+ else:
252
+ changed = True
253
+
254
+ if (
255
+ stmt.ret_expr is not None
256
+ and isinstance(stmt.ret_expr, VirtualVariable)
257
+ and stmt.ret_expr.was_reg
258
+ and stmt.ret_expr.varid in self.new_vvar_sizes
259
+ and stmt.ret_expr.size != self.new_vvar_sizes[stmt.ret_expr.varid]
260
+ ):
261
+ changed = True
262
+
263
+ # update reg name
264
+ tags = dict(stmt.ret_expr.tags)
265
+ tags["reg_name"] = self.project.arch.translate_register_name(
266
+ stmt.ret_expr.reg_offset, size=self.new_vvar_sizes[stmt.ret_expr.varid]
267
+ )
268
+ new_ret_expr = VirtualVariable(
269
+ stmt.ret_expr.idx,
270
+ stmt.ret_expr.varid,
271
+ self.new_vvar_sizes[stmt.ret_expr.varid] * self.project.arch.byte_width,
272
+ category=stmt.ret_expr.category,
273
+ oident=stmt.ret_expr.oident,
274
+ **tags,
275
+ )
276
+ self.replacement_core_vvars[new_ret_expr.varid].append(new_ret_expr)
277
+ new_stmt.ret_expr = new_ret_expr
278
+
279
+ if changed:
280
+ self.narrowed_any = True
281
+ return new_stmt
282
+
283
+ return None