angr 9.2.156__cp310-cp310-manylinux2014_aarch64.whl → 9.2.157__cp310-cp310-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.

Files changed (34) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/cfg/cfg_base.py +87 -71
  3. angr/analyses/cfg/cfg_fast.py +5 -0
  4. angr/analyses/decompiler/clinic.py +182 -104
  5. angr/analyses/decompiler/decompiler.py +11 -0
  6. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +1 -1
  7. angr/analyses/decompiler/structured_codegen/c.py +18 -5
  8. angr/analyses/disassembly.py +5 -11
  9. angr/analyses/s_propagator.py +2 -4
  10. angr/analyses/stack_pointer_tracker.py +3 -7
  11. angr/analyses/typehoon/simple_solver.py +3 -3
  12. angr/analyses/variable_recovery/engine_base.py +2 -8
  13. angr/analyses/variable_recovery/variable_recovery.py +4 -3
  14. angr/calling_conventions.py +3 -3
  15. angr/engines/hook.py +1 -1
  16. angr/engines/icicle.py +229 -0
  17. angr/engines/pcode/behavior.py +1 -4
  18. angr/engines/pcode/emulate.py +1 -4
  19. angr/engines/pcode/lifter.py +2 -10
  20. angr/engines/vex/claripy/irop.py +2 -2
  21. angr/knowledge_plugins/functions/function.py +18 -10
  22. angr/knowledge_plugins/functions/function_manager.py +68 -5
  23. angr/knowledge_plugins/variables/variable_manager.py +15 -3
  24. angr/rustylib.cpython-310-aarch64-linux-gnu.so +0 -0
  25. angr/sim_variable.py +31 -0
  26. angr/storage/memory_mixins/address_concretization_mixin.py +2 -2
  27. angr/storage/memory_mixins/convenient_mappings_mixin.py +1 -1
  28. {angr-9.2.156.dist-info → angr-9.2.157.dist-info}/METADATA +7 -8
  29. {angr-9.2.156.dist-info → angr-9.2.157.dist-info}/RECORD +33 -33
  30. {angr-9.2.156.dist-info → angr-9.2.157.dist-info}/WHEEL +1 -1
  31. angr/rustylib.pyi +0 -165
  32. {angr-9.2.156.dist-info → angr-9.2.157.dist-info}/entry_points.txt +0 -0
  33. {angr-9.2.156.dist-info → angr-9.2.157.dist-info}/licenses/LICENSE +0 -0
  34. {angr-9.2.156.dist-info → angr-9.2.157.dist-info}/top_level.txt +0 -0
@@ -87,6 +87,27 @@ class DataRefDesc:
87
87
  data_type_str: str
88
88
 
89
89
 
90
+ class ClinicStage(enum.IntEnum):
91
+ """
92
+ Different stages of treating an ailment.
93
+ """
94
+
95
+ INITIALIZATION = 0
96
+ AIL_GRAPH_CONVERSION = 1
97
+ MAKE_RETURN_SITES = 2
98
+ MAKE_ARGUMENT_LIST = 3
99
+ PRE_SSA_LEVEL0_FIXUPS = 4
100
+ SSA_LEVEL0_TRANSFORMATION = 5
101
+ CONSTANT_PROPAGATION = 6
102
+ TRACK_STACK_POINTERS = 7
103
+ PRE_SSA_LEVEL1_SIMPLIFICATIONS = 8
104
+ SSA_LEVEL1_TRANSFORMATION = 9
105
+ POST_SSA_LEVEL1_SIMPLIFICATIONS = 10
106
+ MAKE_CALLSITES = 11
107
+ POST_CALLSITES = 12
108
+ RECOVER_VARIABLES = 13
109
+
110
+
90
111
  class Clinic(Analysis):
91
112
  """
92
113
  A Clinic deals with AILments.
@@ -123,6 +144,9 @@ class Clinic(Analysis):
123
144
  force_loop_single_exit: bool = True,
124
145
  complete_successors: bool = False,
125
146
  max_type_constraints: int = 4000,
147
+ ail_graph: networkx.DiGraph | None = None,
148
+ arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
149
+ start_stage: ClinicStage | None = ClinicStage.INITIALIZATION,
126
150
  ):
127
151
  if not func.normalized and mode == ClinicMode.DECOMPILE:
128
152
  raise ValueError("Decompilation must work on normalized function graphs.")
@@ -134,12 +158,16 @@ class Clinic(Analysis):
134
158
  self.unoptimized_graph: networkx.DiGraph | None = None
135
159
  self.arg_list = None
136
160
  self.arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None
161
+ self.func_args = None
137
162
  self.variable_kb = variable_kb
138
163
  self.externs: set[SimMemoryVariable] = set()
139
164
  self.data_refs: dict[int, list[DataRefDesc]] = {} # data address to data reference description
140
165
  self.optimization_scratch = optimization_scratch if optimization_scratch is not None else {}
141
166
 
142
167
  self._func_graph: networkx.DiGraph | None = None
168
+ self._init_ail_graph = ail_graph
169
+ self._init_arg_vvars = arg_vvars
170
+ self._start_stage = start_stage if start_stage is not None else ClinicStage.INITIALIZATION
143
171
  self._blocks_by_addr_and_size = {}
144
172
  self.entry_node_addr: tuple[int, int | None] = self.function.addr, None
145
173
 
@@ -163,6 +191,17 @@ class Clinic(Analysis):
163
191
  # actual stack variables. these secondary stack variables can be safely eliminated if not used by anything.
164
192
  self.secondary_stackvars: set[int] = set()
165
193
 
194
+ #
195
+ # intermediate variables used during decompilation
196
+ #
197
+
198
+ self._ail_graph: networkx.DiGraph = None # type:ignore
199
+ self._spt = None
200
+ # cached block-level reaching definition analysis results and propagator results
201
+ self._block_simplification_cache: dict[ailment.Block, NamedTuple] | None = {}
202
+ self._preserve_vvar_ids: set[int] = set()
203
+ self._type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] = []
204
+
166
205
  # inlining help
167
206
  self._sp_shift = sp_shift
168
207
  self._max_stack_depth = 0
@@ -236,9 +275,14 @@ class Clinic(Analysis):
236
275
  #
237
276
 
238
277
  def _analyze_for_decompiling(self):
239
- if not (ail_graph := self._decompilation_graph_recovery()):
278
+ # initialize the AIL conversion manager
279
+ self._ail_manager = ailment.Manager(arch=self.project.arch)
280
+
281
+ ail_graph = self._init_ail_graph if self._init_ail_graph is not None else self._decompilation_graph_recovery()
282
+ if not ail_graph:
240
283
  return
241
- ail_graph = self._decompilation_fixups(ail_graph)
284
+ if self._start_stage <= ClinicStage.INITIALIZATION:
285
+ ail_graph = self._decompilation_fixups(ail_graph)
242
286
 
243
287
  if self._inline_functions:
244
288
  self._max_stack_depth += self.calculate_stack_depth()
@@ -270,9 +314,6 @@ class Clinic(Analysis):
270
314
  self._update_progress(10.0, text="Recovering calling conventions")
271
315
  self._recover_calling_conventions()
272
316
 
273
- # initialize the AIL conversion manager
274
- self._ail_manager = ailment.Manager(arch=self.project.arch)
275
-
276
317
  # Convert VEX blocks to AIL blocks and then simplify them
277
318
 
278
319
  self._update_progress(20.0, text="Converting VEX to AIL")
@@ -467,34 +508,68 @@ class Clinic(Analysis):
467
508
  return depth
468
509
 
469
510
  def _decompilation_simplifications(self, ail_graph):
470
- # Make returns
511
+ self.arg_vvars = self._init_arg_vvars if self._init_arg_vvars is not None else {}
512
+ self.func_args = {arg_vvar for arg_vvar, _ in self.arg_vvars.values()}
513
+ self._ail_graph = ail_graph
514
+
515
+ stages = {
516
+ ClinicStage.MAKE_RETURN_SITES: self._stage_make_return_sites,
517
+ ClinicStage.MAKE_ARGUMENT_LIST: self._stage_make_function_argument_list,
518
+ ClinicStage.PRE_SSA_LEVEL0_FIXUPS: self._stage_pre_ssa_level0_fixups,
519
+ ClinicStage.SSA_LEVEL0_TRANSFORMATION: self._stage_transform_to_ssa_level0,
520
+ ClinicStage.CONSTANT_PROPAGATION: self._stage_constant_propagation,
521
+ ClinicStage.TRACK_STACK_POINTERS: self._stage_track_stack_pointers,
522
+ ClinicStage.PRE_SSA_LEVEL1_SIMPLIFICATIONS: self._stage_pre_ssa_level1_simplifications,
523
+ ClinicStage.SSA_LEVEL1_TRANSFORMATION: self._stage_transform_to_ssa_level1,
524
+ ClinicStage.POST_SSA_LEVEL1_SIMPLIFICATIONS: self._stage_post_ssa_level1_simplifications,
525
+ ClinicStage.MAKE_CALLSITES: self._stage_make_function_callsites,
526
+ ClinicStage.POST_CALLSITES: self._stage_post_callsite_simplifications,
527
+ ClinicStage.RECOVER_VARIABLES: self._stage_recover_variables,
528
+ }
529
+
530
+ for stage in sorted(stages):
531
+ if stage < self._start_stage:
532
+ continue
533
+ stages[stage]()
534
+
535
+ # remove empty nodes from the graph
536
+ self._ail_graph = self.remove_empty_nodes(self._ail_graph)
537
+ # note that there are still edges to remove before we can structure this graph!
538
+
539
+ self.cc_graph = self.copy_graph(self._ail_graph)
540
+ self.externs = self._collect_externs(self._ail_graph, self.variable_kb)
541
+ return self._ail_graph
542
+
543
+ def _stage_make_return_sites(self) -> None:
471
544
  self._update_progress(30.0, text="Making return sites")
472
545
  if self.function.prototype is None or not isinstance(self.function.prototype.returnty, SimTypeBottom):
473
- ail_graph = self._make_returns(ail_graph)
474
-
475
- ail_graph = self._run_simplification_passes(
476
- ail_graph, stage=OptimizationPassStage.BEFORE_SSA_LEVEL0_TRANSFORMATION
546
+ self._ail_graph = self._make_returns(self._ail_graph)
547
+ self._ail_graph = self._run_simplification_passes(
548
+ self._ail_graph, stage=OptimizationPassStage.BEFORE_SSA_LEVEL0_TRANSFORMATION
477
549
  )
478
550
 
479
- # Make function arguments
551
+ def _stage_make_function_argument_list(self) -> None:
480
552
  self._update_progress(33.0, text="Making argument list")
481
- arg_list = self._make_argument_list()
482
- arg_vvars = self._create_function_argument_vvars(arg_list)
483
- func_args = {arg_vvar for arg_vvar, _ in arg_vvars.values()}
553
+ self.arg_list = self._make_argument_list()
554
+ self.arg_vvars = self._create_function_argument_vvars(self.arg_list)
555
+ self.func_args = {arg_vvar for arg_vvar, _ in self.arg_vvars.values()}
484
556
 
557
+ def _stage_pre_ssa_level0_fixups(self) -> None:
485
558
  # duplicate orphaned conditional jump blocks
486
- ail_graph = self._duplicate_orphaned_cond_jumps(ail_graph)
559
+ self._ail_graph = self._duplicate_orphaned_cond_jumps(self._ail_graph)
487
560
  # rewrite jmp_rax function calls
488
- ail_graph = self._rewrite_jump_rax_calls(ail_graph)
561
+ self._ail_graph = self._rewrite_jump_rax_calls(self._ail_graph)
489
562
 
490
- # Transform the graph into partial SSA form
491
- self._update_progress(35.0, text="Transforming to partial-SSA form")
492
- ail_graph = self._transform_to_ssa_level0(ail_graph, func_args)
563
+ def _stage_transform_to_ssa_level0(self) -> None:
564
+ self._update_progress(35.0, text="Transforming to partial-SSA form (registers)")
565
+ assert self.func_args is not None
566
+ self._ail_graph = self._transform_to_ssa_level0(self._ail_graph, self.func_args)
493
567
 
568
+ def _stage_constant_propagation(self) -> None:
494
569
  # full-function constant-only propagation
495
570
  self._update_progress(36.0, text="Constant propagation")
496
571
  self._simplify_function(
497
- ail_graph,
572
+ self._ail_graph,
498
573
  remove_dead_memdefs=False,
499
574
  unify_variables=False,
500
575
  narrow_expressions=False,
@@ -503,34 +578,34 @@ class Clinic(Analysis):
503
578
  max_iterations=1,
504
579
  )
505
580
 
506
- # cached block-level reaching definition analysis results and propagator results
507
- block_simplification_cache: dict[ailment.Block, NamedTuple] | None = {}
508
-
509
- # Track stack pointers
510
- self._update_progress(37.0, text="Tracking stack pointers")
511
- spt = self._track_stack_pointers()
581
+ def _stage_track_stack_pointers(self) -> None:
582
+ self._spt = self._track_stack_pointers()
512
583
 
513
- preserve_vvar_ids: set[int] = set()
514
- type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] = []
584
+ def _stage_transform_to_ssa_level1(self) -> None:
585
+ self._update_progress(37.0, text="Transforming to partial-SSA form (stack variables)")
586
+ # rewrite (qualified) stack variables into SSA form
587
+ assert self.func_args is not None
588
+ self._ail_graph = self._transform_to_ssa_level1(self._ail_graph, self.func_args)
515
589
 
590
+ def _stage_pre_ssa_level1_simplifications(self) -> None:
516
591
  # Simplify blocks
517
592
  # we never remove dead memory definitions before making callsites. otherwise stack arguments may go missing
518
593
  # before they are recognized as stack arguments.
519
594
  self._update_progress(38.0, text="Simplifying blocks 1")
520
- ail_graph = self._simplify_blocks(
521
- ail_graph,
522
- stack_pointer_tracker=spt,
523
- cache=block_simplification_cache,
524
- preserve_vvar_ids=preserve_vvar_ids,
525
- type_hints=type_hints,
595
+ self._ail_graph = self._simplify_blocks(
596
+ self._ail_graph,
597
+ stack_pointer_tracker=self._spt,
598
+ cache=self._block_simplification_cache,
599
+ preserve_vvar_ids=self._preserve_vvar_ids,
600
+ type_hints=self._type_hints,
526
601
  )
527
- self._rewrite_alloca(ail_graph)
602
+ self._rewrite_alloca(self._ail_graph)
528
603
 
529
604
  # Run simplification passes
530
605
  self._update_progress(40.0, text="Running simplifications 1")
531
- ail_graph = self._run_simplification_passes(
532
- ail_graph,
533
- stack_pointer_tracker=spt,
606
+ self._ail_graph = self._run_simplification_passes(
607
+ self._ail_graph,
608
+ stack_pointer_tracker=self._spt,
534
609
  stack_items=self.stack_items,
535
610
  stage=OptimizationPassStage.AFTER_SINGLE_BLOCK_SIMPLIFICATION,
536
611
  )
@@ -538,160 +613,161 @@ class Clinic(Analysis):
538
613
  # Simplify the entire function for the first time
539
614
  self._update_progress(45.0, text="Simplifying function 1")
540
615
  self._simplify_function(
541
- ail_graph,
616
+ self._ail_graph,
542
617
  remove_dead_memdefs=False,
543
618
  unify_variables=False,
544
619
  narrow_expressions=True,
545
620
  fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
546
- arg_vvars=arg_vvars,
621
+ arg_vvars=self.arg_vvars,
547
622
  )
548
623
 
549
624
  # Run simplification passes again. there might be more chances for peephole optimizations after function-level
550
625
  # simplification
551
626
  self._update_progress(48.0, text="Simplifying blocks 2")
552
- ail_graph = self._simplify_blocks(
553
- ail_graph,
554
- stack_pointer_tracker=spt,
555
- cache=block_simplification_cache,
556
- preserve_vvar_ids=preserve_vvar_ids,
557
- type_hints=type_hints,
627
+ self._ail_graph = self._simplify_blocks(
628
+ self._ail_graph,
629
+ stack_pointer_tracker=self._spt,
630
+ cache=self._block_simplification_cache,
631
+ preserve_vvar_ids=self._preserve_vvar_ids,
632
+ type_hints=self._type_hints,
558
633
  )
559
634
 
560
635
  # Run simplification passes
561
636
  self._update_progress(49.0, text="Running simplifications 2")
562
- ail_graph = self._run_simplification_passes(
563
- ail_graph, stage=OptimizationPassStage.BEFORE_SSA_LEVEL1_TRANSFORMATION
637
+ self._ail_graph = self._run_simplification_passes(
638
+ self._ail_graph, stage=OptimizationPassStage.BEFORE_SSA_LEVEL1_TRANSFORMATION
564
639
  )
565
640
 
566
- # rewrite (qualified) stack variables into SSA form
567
- ail_graph = self._transform_to_ssa_level1(ail_graph, func_args)
568
-
569
- # clear _blocks_by_addr_and_size so no one can use it again
570
- # TODO: Totally remove this dict
571
- self._blocks_by_addr_and_size = None
572
-
641
+ def _stage_post_ssa_level1_simplifications(self) -> None:
573
642
  # Rust-specific; only call this on Rust binaries when we can identify language and compiler
574
- ail_graph = self._rewrite_rust_probestack_call(ail_graph)
643
+ self._ail_graph = self._rewrite_rust_probestack_call(self._ail_graph)
575
644
  # Windows-specific
576
- ail_graph = self._rewrite_windows_chkstk_call(ail_graph)
645
+ self._ail_graph = self._rewrite_windows_chkstk_call(self._ail_graph)
646
+
647
+ def _stage_make_function_callsites(self) -> None:
648
+ assert self.func_args is not None
577
649
 
578
650
  # Make call-sites
579
651
  self._update_progress(50.0, text="Making callsites")
580
652
  _, stackarg_offsets, removed_vvar_ids = self._make_callsites(
581
- ail_graph, func_args, stack_pointer_tracker=spt, preserve_vvar_ids=preserve_vvar_ids
653
+ self._ail_graph, self.func_args, stack_pointer_tracker=self._spt, preserve_vvar_ids=self._preserve_vvar_ids
582
654
  )
583
655
 
584
656
  # Run simplification passes
585
657
  self._update_progress(53.0, text="Running simplifications 2")
586
- ail_graph = self._run_simplification_passes(ail_graph, stage=OptimizationPassStage.AFTER_MAKING_CALLSITES)
658
+ self._ail_graph = self._run_simplification_passes(
659
+ self._ail_graph, stage=OptimizationPassStage.AFTER_MAKING_CALLSITES
660
+ )
587
661
 
588
662
  # Simplify the entire function for the second time
589
663
  self._update_progress(55.0, text="Simplifying function 2")
590
664
  self._simplify_function(
591
- ail_graph,
665
+ self._ail_graph,
592
666
  remove_dead_memdefs=self._remove_dead_memdefs,
593
667
  stack_arg_offsets=stackarg_offsets,
594
668
  unify_variables=True,
595
669
  narrow_expressions=True,
596
670
  fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
597
671
  removed_vvar_ids=removed_vvar_ids,
598
- arg_vvars=arg_vvars,
599
- preserve_vvar_ids=preserve_vvar_ids,
672
+ arg_vvars=self.arg_vvars,
673
+ preserve_vvar_ids=self._preserve_vvar_ids,
600
674
  )
601
675
 
602
676
  # After global optimization, there might be more chances for peephole optimizations.
603
677
  # Simplify blocks for the second time
604
678
  self._update_progress(60.0, text="Simplifying blocks 3")
605
- ail_graph = self._simplify_blocks(
606
- ail_graph,
607
- stack_pointer_tracker=spt,
608
- cache=block_simplification_cache,
609
- preserve_vvar_ids=preserve_vvar_ids,
610
- type_hints=type_hints,
679
+ self._ail_graph = self._simplify_blocks(
680
+ self._ail_graph,
681
+ stack_pointer_tracker=self._spt,
682
+ cache=self._block_simplification_cache,
683
+ preserve_vvar_ids=self._preserve_vvar_ids,
684
+ type_hints=self._type_hints,
611
685
  )
612
686
 
613
687
  # Run simplification passes
614
688
  self._update_progress(65.0, text="Running simplifications 3")
615
- ail_graph = self._run_simplification_passes(
616
- ail_graph, stack_items=self.stack_items, stage=OptimizationPassStage.AFTER_GLOBAL_SIMPLIFICATION
689
+ self._ail_graph = self._run_simplification_passes(
690
+ self._ail_graph, stack_items=self.stack_items, stage=OptimizationPassStage.AFTER_GLOBAL_SIMPLIFICATION
617
691
  )
618
692
 
619
693
  # Simplify the entire function for the third time
620
694
  self._update_progress(70.0, text="Simplifying function 3")
621
695
  self._simplify_function(
622
- ail_graph,
696
+ self._ail_graph,
623
697
  remove_dead_memdefs=self._remove_dead_memdefs,
624
698
  stack_arg_offsets=stackarg_offsets,
625
699
  unify_variables=True,
626
700
  narrow_expressions=True,
627
701
  fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
628
- arg_vvars=arg_vvars,
629
- preserve_vvar_ids=preserve_vvar_ids,
702
+ arg_vvars=self.arg_vvars,
703
+ preserve_vvar_ids=self._preserve_vvar_ids,
630
704
  )
631
705
 
632
706
  self._update_progress(75.0, text="Simplifying blocks 4")
633
- ail_graph = self._simplify_blocks(
634
- ail_graph,
635
- stack_pointer_tracker=spt,
636
- cache=block_simplification_cache,
637
- preserve_vvar_ids=preserve_vvar_ids,
638
- type_hints=type_hints,
707
+ self._ail_graph = self._simplify_blocks(
708
+ self._ail_graph,
709
+ stack_pointer_tracker=self._spt,
710
+ cache=self._block_simplification_cache,
711
+ preserve_vvar_ids=self._preserve_vvar_ids,
712
+ type_hints=self._type_hints,
639
713
  )
640
714
 
641
715
  # Simplify the entire function for the fourth time
642
716
  self._update_progress(78.0, text="Simplifying function 4")
643
717
  self._simplify_function(
644
- ail_graph,
718
+ self._ail_graph,
645
719
  remove_dead_memdefs=self._remove_dead_memdefs,
646
720
  stack_arg_offsets=stackarg_offsets,
647
721
  unify_variables=True,
648
722
  narrow_expressions=True,
649
723
  fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
650
- arg_vvars=arg_vvars,
651
- preserve_vvar_ids=preserve_vvar_ids,
724
+ arg_vvars=self.arg_vvars,
725
+ preserve_vvar_ids=self._preserve_vvar_ids,
652
726
  )
653
727
 
654
728
  self._update_progress(79.0, text="Running simplifications 4")
655
- ail_graph = self._run_simplification_passes(
656
- ail_graph, stack_items=self.stack_items, stage=OptimizationPassStage.BEFORE_VARIABLE_RECOVERY
729
+ self._ail_graph = self._run_simplification_passes(
730
+ self._ail_graph, stack_items=self.stack_items, stage=OptimizationPassStage.BEFORE_VARIABLE_RECOVERY
657
731
  )
658
732
 
733
+ def _stage_post_callsite_simplifications(self) -> None:
734
+ self.arg_list = []
735
+ self.vvar_to_vvar = {}
736
+ self.copied_var_ids = set()
737
+
738
+ assert self.arg_vvars is not None
739
+
659
740
  # update arg_list
660
- arg_list = []
661
- for idx in sorted(arg_vvars):
662
- arg_list.append(arg_vvars[idx][1])
741
+ for idx in sorted(self.arg_vvars):
742
+ self.arg_list.append(self.arg_vvars[idx][1])
663
743
 
664
744
  # Get virtual variable mapping that can de-phi the SSA representation
665
- vvar2vvar, copied_vvar_ids = self._collect_dephi_vvar_mapping_and_rewrite_blocks(ail_graph, arg_vvars)
745
+ self.vvar_to_vvar, self.copied_var_ids = self._collect_dephi_vvar_mapping_and_rewrite_blocks(
746
+ self._ail_graph, self.arg_vvars
747
+ )
748
+
749
+ def _stage_recover_variables(self) -> None:
750
+ assert self.arg_list is not None and self.arg_vvars is not None and self.vvar_to_vvar is not None
666
751
 
667
752
  # Recover variables on AIL blocks
668
753
  self._update_progress(80.0, text="Recovering variables")
669
- variable_kb = self._recover_and_link_variables(ail_graph, arg_list, arg_vvars, vvar2vvar, type_hints)
754
+ variable_kb = self._recover_and_link_variables(
755
+ self._ail_graph, self.arg_list, self.arg_vvars, self.vvar_to_vvar, self._type_hints
756
+ )
670
757
 
671
758
  # Run simplification passes
672
759
  self._update_progress(85.0, text="Running simplifications 4")
673
- ail_graph = self._run_simplification_passes(
674
- ail_graph,
760
+ self._ail_graph = self._run_simplification_passes(
761
+ self._ail_graph,
675
762
  stage=OptimizationPassStage.AFTER_VARIABLE_RECOVERY,
676
- avoid_vvar_ids=copied_vvar_ids,
763
+ avoid_vvar_ids=self.copied_var_ids,
677
764
  )
678
765
 
679
766
  # Make function prototype
680
767
  self._update_progress(90.0, text="Making function prototype")
681
- self._make_function_prototype(arg_list, variable_kb)
768
+ self._make_function_prototype(self.arg_list, variable_kb)
682
769
 
683
- # remove empty nodes from the graph
684
- ail_graph = self.remove_empty_nodes(ail_graph)
685
- # note that there are still edges to remove before we can structure this graph!
686
-
687
- self.arg_list = arg_list
688
- self.arg_vvars = arg_vvars
689
770
  self.variable_kb = variable_kb
690
- self.cc_graph = self.copy_graph(ail_graph)
691
- self.externs = self._collect_externs(ail_graph, variable_kb)
692
- self.vvar_to_vvar = vvar2vvar
693
- self.copied_var_ids = copied_vvar_ids
694
- return ail_graph
695
771
 
696
772
  def _analyze_for_data_refs(self):
697
773
  # Set up the function graph according to configurations
@@ -1813,6 +1889,8 @@ class Clinic(Analysis):
1813
1889
  },
1814
1890
  )
1815
1891
  except Exception: # pylint:disable=broad-except
1892
+ if self._fail_fast:
1893
+ raise
1816
1894
  l.warning(
1817
1895
  "Typehoon analysis failed. Variables will not have types. Please report to GitHub.", exc_info=True
1818
1896
  )
@@ -72,6 +72,9 @@ class Decompiler(Analysis):
72
72
  generate_code: bool = True,
73
73
  use_cache: bool = True,
74
74
  expr_collapse_depth: int = 16,
75
+ clinic_graph=None,
76
+ clinic_arg_vvars=None,
77
+ clinic_start_stage=None,
75
78
  ):
76
79
  if not isinstance(func, Function):
77
80
  func = self.kb.functions[func]
@@ -129,6 +132,9 @@ class Decompiler(Analysis):
129
132
  )
130
133
 
131
134
  self.clinic = None # mostly for debugging purposes
135
+ self._clinic_graph = clinic_graph
136
+ self._clinic_arg_vvars = clinic_arg_vvars
137
+ self._clinic_start_stage = clinic_start_stage
132
138
  self.codegen: CStructuredCodeGenerator | None = None
133
139
  self.cache: DecompilationCache | None = None
134
140
  self.options_by_class = None
@@ -256,6 +262,9 @@ class Decompiler(Analysis):
256
262
  optimization_scratch=self._optimization_scratch,
257
263
  force_loop_single_exit=self._force_loop_single_exit,
258
264
  complete_successors=self._complete_successors,
265
+ ail_graph=self._clinic_graph,
266
+ arg_vvars=self._clinic_arg_vvars,
267
+ start_stage=self._clinic_start_stage,
259
268
  **self.options_to_params(self.options_by_class["clinic"]),
260
269
  )
261
270
  else:
@@ -616,6 +625,8 @@ class Decompiler(Analysis):
616
625
  self.func.prototype.args[:i] + (new_type,) + self.func.prototype.args[i + 1 :]
617
626
  )
618
627
  except Exception: # pylint:disable=broad-except
628
+ if self._fail_fast:
629
+ raise
619
630
  l.warning(
620
631
  "Typehoon analysis failed. Variables will not have types. Please report to GitHub.", exc_info=True
621
632
  )
@@ -158,7 +158,7 @@ class GraphDephicationVVarMapping(Analysis): # pylint:disable=abstract-method
158
158
  for vvar_id in live_outs[src]:
159
159
  interference.add_edge(new_vvar_id, vvar_id)
160
160
 
161
- else: # insertion_type == 1
161
+ else: # insertion_type == 1, i.e. the set has only one element
162
162
  phi_block_loc, old_phi_varid, new_phi_varid = next(iter(new_vvar_ids))
163
163
  self.copied_vvar_ids.add(new_phi_varid)
164
164
 
@@ -1332,10 +1332,13 @@ class CFunctionCall(CStatement, CExpression):
1332
1332
  return False
1333
1333
 
1334
1334
  @staticmethod
1335
- def _is_func_likely_cxx_class_method(func_name: str) -> bool:
1335
+ def _is_func_likely_method(func_name: str, rust: bool) -> bool:
1336
1336
  if "::" not in func_name:
1337
1337
  return False
1338
1338
  chunks = func_name.split("::")
1339
+ if rust and re.match(r"[A-Z][a-zA-Z0-9_]*", chunks[-2]) is None:
1340
+ # let's say that rust structs are always UpperCamelCase
1341
+ return False
1339
1342
  return re.match(r"[a-zA-Z_][a-zA-Z0-9_]*", chunks[-1]) is not None
1340
1343
 
1341
1344
  def c_repr_chunks(self, indent=0, asexpr: bool = False):
@@ -1357,7 +1360,11 @@ class CFunctionCall(CStatement, CExpression):
1357
1360
  func_name = get_cpp_function_name(self.callee_func.demangled_name, specialized=False, qualified=True)
1358
1361
  else:
1359
1362
  func_name = self.callee_func.name
1360
- if self.prettify_thiscall and self.args and self._is_func_likely_cxx_class_method(func_name):
1363
+ if (
1364
+ self.prettify_thiscall
1365
+ and self.args
1366
+ and self._is_func_likely_method(func_name, self.callee_func.is_rust_function())
1367
+ ):
1361
1368
  func_name = self.callee_func.short_name
1362
1369
  yield from self._c_repr_chunks_thiscall(func_name, asexpr=asexpr)
1363
1370
  return
@@ -3571,7 +3578,13 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3571
3578
  return self._variable(SimTemporaryVariable(expr.tmp_idx, expr.bits), expr.size)
3572
3579
 
3573
3580
  def _handle_Expr_Const(
3574
- self, expr: Expr.Const, type_=None, reference_values=None, variable=None, likely_signed=True, **kwargs
3581
+ self,
3582
+ expr: Expr.Const,
3583
+ type_=None,
3584
+ reference_values: dict[SimType | str, str | bytes | int | float | Function | CExpression] | None = None,
3585
+ variable=None,
3586
+ likely_signed=True,
3587
+ **kwargs,
3575
3588
  ):
3576
3589
  inline_string = False
3577
3590
  function_pointer = False
@@ -3581,8 +3594,8 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3581
3594
 
3582
3595
  if type_ is None and reference_values is None and hasattr(expr, "reference_values"):
3583
3596
  reference_values = expr.reference_values.copy()
3584
- if reference_values:
3585
- type_ = next(iter(reference_values))
3597
+ if len(reference_values) == 1: # type: ignore
3598
+ type_ = next(iter(reference_values)) # type: ignore
3586
3599
 
3587
3600
  if reference_values is None:
3588
3601
  reference_values = {}
@@ -6,12 +6,13 @@ from collections import defaultdict
6
6
  from collections.abc import Sequence
7
7
  from typing import Any
8
8
 
9
- import pyvex
10
9
  import archinfo
10
+ import pypcode
11
+ import pyvex
11
12
 
12
13
  from . import Analysis
13
-
14
14
  from angr.analyses import AnalysesHub
15
+ from angr.engines import pcode
15
16
  from angr.errors import AngrTypeError
16
17
  from angr.knowledge_plugins import Function
17
18
  from angr.utils.library import get_cpp_function_name
@@ -20,16 +21,9 @@ from angr.block import DisassemblerInsn, CapstoneInsn, SootBlockNode
20
21
  from angr.codenode import BlockNode
21
22
  from .disassembly_utils import decode_instruction
22
23
 
23
- try:
24
- from angr.engines import pcode
25
- import pypcode
26
24
 
27
- IRSBType = pyvex.IRSB | pcode.lifter.IRSB
28
- IROpObjType = pyvex.stmt.IRStmt | pypcode.PcodeOp
29
- except ImportError:
30
- pcode = None
31
- IRSBType = pyvex.IRSB
32
- IROpObjType = pyvex.stmt
25
+ IRSBType = pyvex.IRSB | pcode.lifter.IRSB
26
+ IROpObjType = pyvex.stmt.IRStmt | pypcode.PcodeOp
33
27
 
34
28
  l = logging.getLogger(name=__name__)
35
29
 
@@ -409,9 +409,8 @@ class SPropagatorAnalysis(Analysis):
409
409
  ][tmp_used] = v
410
410
  continue
411
411
 
412
- if len(tmp_uses) <= 2:
413
- tmp_used, tmp_use_stmtidx = next(iter(tmp_uses))
414
- if is_const_vvar_load_dirty_assignment(stmt):
412
+ if len(tmp_uses) <= 2 and is_const_vvar_load_dirty_assignment(stmt):
413
+ for tmp_used, tmp_use_stmtidx in tmp_uses:
415
414
  same_inst = (
416
415
  block.statements[tmp_def_stmtidx].ins_addr == block.statements[tmp_use_stmtidx].ins_addr
417
416
  )
@@ -426,7 +425,6 @@ class SPropagatorAnalysis(Analysis):
426
425
  replacements[
427
426
  CodeLocation(block_loc.block_addr, tmp_use_stmtidx, block_idx=block_loc.block_idx)
428
427
  ][tmp_used] = stmt.src
429
- continue
430
428
 
431
429
  self.model.replacements = replacements
432
430
 
@@ -7,10 +7,12 @@ import re
7
7
  import logging
8
8
  from collections import defaultdict
9
9
 
10
- from archinfo.arch_arm import is_arm_arch
10
+ import pypcode
11
11
  import pyvex
12
+ from archinfo.arch_arm import is_arm_arch
12
13
 
13
14
  from angr.analyses import ForwardAnalysis, visitors
15
+ from angr.engines import pcode
14
16
  from angr.utils.constants import is_alignment_mask
15
17
  from angr.analyses import AnalysesHub
16
18
  from angr.knowledge_plugins import Function
@@ -21,12 +23,6 @@ from angr.utils.types import dereference_simtype_by_lib
21
23
 
22
24
  from .analysis import Analysis
23
25
 
24
- try:
25
- import pypcode
26
- from angr.engines import pcode
27
- except ImportError:
28
- pypcode = None
29
- pcode = None
30
26
 
31
27
  if TYPE_CHECKING:
32
28
  from angr.block import Block