angr 9.2.102__py3-none-manylinux2014_x86_64.whl → 9.2.104__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.

Files changed (239) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +7 -6
  3. angr/analyses/calling_convention.py +33 -35
  4. angr/analyses/cdg.py +2 -4
  5. angr/analyses/cfg/cfb.py +4 -3
  6. angr/analyses/cfg/cfg_base.py +14 -14
  7. angr/analyses/cfg/cfg_emulated.py +3 -4
  8. angr/analyses/cfg/cfg_fast.py +46 -46
  9. angr/analyses/cfg/cfg_fast_soot.py +1 -2
  10. angr/analyses/cfg/cfg_job_base.py +2 -2
  11. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +14 -13
  12. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +5 -5
  13. angr/analyses/cfg_slice_to_sink/cfg_slice_to_sink.py +3 -3
  14. angr/analyses/complete_calling_conventions.py +13 -12
  15. angr/analyses/data_dep/data_dependency_analysis.py +24 -24
  16. angr/analyses/data_dep/dep_nodes.py +3 -3
  17. angr/analyses/ddg.py +1 -2
  18. angr/analyses/decompiler/ail_simplifier.py +35 -34
  19. angr/analyses/decompiler/block_io_finder.py +20 -20
  20. angr/analyses/decompiler/block_similarity.py +4 -6
  21. angr/analyses/decompiler/block_simplifier.py +17 -16
  22. angr/analyses/decompiler/callsite_maker.py +25 -10
  23. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -3
  24. angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +2 -4
  25. angr/analyses/decompiler/clinic.py +250 -45
  26. angr/analyses/decompiler/condition_processor.py +15 -8
  27. angr/analyses/decompiler/decompilation_cache.py +7 -7
  28. angr/analyses/decompiler/decompilation_options.py +4 -4
  29. angr/analyses/decompiler/decompiler.py +19 -15
  30. angr/analyses/decompiler/expression_counters.py +10 -9
  31. angr/analyses/decompiler/goto_manager.py +2 -4
  32. angr/analyses/decompiler/graph_region.py +9 -9
  33. angr/analyses/decompiler/jump_target_collector.py +1 -2
  34. angr/analyses/decompiler/optimization_passes/__init__.py +4 -3
  35. angr/analyses/decompiler/optimization_passes/code_motion.py +5 -6
  36. angr/analyses/decompiler/optimization_passes/const_derefs.py +4 -4
  37. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +73 -0
  38. angr/analyses/decompiler/optimization_passes/engine_base.py +25 -3
  39. angr/analyses/decompiler/optimization_passes/expr_op_swapper.py +6 -5
  40. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -2
  41. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +3 -0
  42. angr/analyses/decompiler/optimization_passes/ite_expr_converter.py +2 -2
  43. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +17 -17
  44. angr/analyses/decompiler/optimization_passes/optimization_pass.py +12 -13
  45. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +25 -21
  46. angr/analyses/decompiler/optimization_passes/ret_addr_save_simplifier.py +3 -3
  47. angr/analyses/decompiler/optimization_passes/ret_deduplicator.py +1 -2
  48. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +7 -7
  49. angr/analyses/decompiler/optimization_passes/spilled_register_finder.py +18 -0
  50. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +2 -3
  51. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +1 -2
  52. angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py +2 -2
  53. angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -3
  54. angr/analyses/decompiler/peephole_optimizations/base.py +13 -15
  55. angr/analyses/decompiler/peephole_optimizations/bswap.py +1 -3
  56. angr/analyses/decompiler/peephole_optimizations/cmpord_rewriter.py +72 -0
  57. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +1 -2
  58. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +1 -1
  59. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +5 -10
  60. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +3 -4
  61. angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy.py +7 -10
  62. angr/analyses/decompiler/peephole_optimizations/rewrite_bit_extractions.py +2 -3
  63. angr/analyses/decompiler/peephole_optimizations/sar_to_signed_div.py +1 -2
  64. angr/analyses/decompiler/peephole_optimizations/tidy_stack_addr.py +4 -4
  65. angr/analyses/decompiler/redundant_label_remover.py +4 -5
  66. angr/analyses/decompiler/region_identifier.py +4 -5
  67. angr/analyses/decompiler/region_simplifiers/cascading_cond_transformer.py +1 -2
  68. angr/analyses/decompiler/region_simplifiers/expr_folding.py +19 -20
  69. angr/analyses/decompiler/region_simplifiers/goto.py +2 -3
  70. angr/analyses/decompiler/region_simplifiers/loop.py +1 -2
  71. angr/analyses/decompiler/region_simplifiers/node_address_finder.py +1 -2
  72. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +1 -3
  73. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +19 -19
  74. angr/analyses/decompiler/return_maker.py +1 -2
  75. angr/analyses/decompiler/structured_codegen/base.py +5 -6
  76. angr/analyses/decompiler/structured_codegen/c.py +39 -38
  77. angr/analyses/decompiler/structuring/__init__.py +1 -1
  78. angr/analyses/decompiler/structuring/dream.py +17 -16
  79. angr/analyses/decompiler/structuring/phoenix.py +45 -46
  80. angr/analyses/decompiler/structuring/recursive_structurer.py +4 -4
  81. angr/analyses/decompiler/structuring/structurer_base.py +16 -15
  82. angr/analyses/decompiler/structuring/structurer_nodes.py +10 -9
  83. angr/analyses/decompiler/utils.py +17 -16
  84. angr/analyses/disassembly.py +7 -6
  85. angr/analyses/flirt.py +9 -9
  86. angr/analyses/forward_analysis/forward_analysis.py +15 -14
  87. angr/analyses/forward_analysis/visitors/function_graph.py +1 -2
  88. angr/analyses/forward_analysis/visitors/graph.py +16 -15
  89. angr/analyses/propagator/engine_ail.py +30 -26
  90. angr/analyses/propagator/outdated_definition_walker.py +8 -7
  91. angr/analyses/propagator/propagator.py +11 -13
  92. angr/analyses/proximity_graph.py +21 -21
  93. angr/analyses/reaching_definitions/__init__.py +3 -3
  94. angr/analyses/reaching_definitions/call_trace.py +3 -6
  95. angr/analyses/reaching_definitions/dep_graph.py +41 -48
  96. angr/analyses/reaching_definitions/engine_ail.py +11 -5
  97. angr/analyses/reaching_definitions/engine_vex.py +9 -8
  98. angr/analyses/reaching_definitions/function_handler.py +51 -34
  99. angr/analyses/reaching_definitions/heap_allocator.py +3 -4
  100. angr/analyses/reaching_definitions/rd_initializer.py +8 -8
  101. angr/analyses/reaching_definitions/rd_state.py +57 -58
  102. angr/analyses/reaching_definitions/reaching_definitions.py +18 -17
  103. angr/analyses/reaching_definitions/subject.py +2 -3
  104. angr/analyses/stack_pointer_tracker.py +15 -6
  105. angr/analyses/typehoon/dfa.py +4 -4
  106. angr/analyses/typehoon/simple_solver.py +48 -52
  107. angr/analyses/typehoon/translator.py +3 -6
  108. angr/analyses/typehoon/typeconsts.py +13 -14
  109. angr/analyses/typehoon/typehoon.py +9 -9
  110. angr/analyses/typehoon/typevars.py +18 -17
  111. angr/analyses/variable_recovery/engine_ail.py +5 -5
  112. angr/analyses/variable_recovery/engine_base.py +25 -21
  113. angr/analyses/variable_recovery/irsb_scanner.py +8 -9
  114. angr/analyses/variable_recovery/variable_recovery.py +1 -2
  115. angr/analyses/variable_recovery/variable_recovery_base.py +14 -13
  116. angr/analyses/variable_recovery/variable_recovery_fast.py +8 -8
  117. angr/analyses/veritesting.py +1 -2
  118. angr/analyses/vfg.py +57 -56
  119. angr/analyses/xrefs.py +1 -2
  120. angr/angrdb/db.py +7 -7
  121. angr/angrdb/serializers/kb.py +16 -13
  122. angr/angrdb/serializers/loader.py +1 -2
  123. angr/angrdb/serializers/structured_code.py +2 -2
  124. angr/annocfg.py +1 -2
  125. angr/block.py +16 -6
  126. angr/calling_conventions.py +28 -27
  127. angr/code_location.py +8 -8
  128. angr/codenode.py +1 -2
  129. angr/concretization_strategies/max.py +1 -3
  130. angr/distributed/server.py +1 -3
  131. angr/distributed/worker.py +1 -2
  132. angr/engines/engine.py +2 -3
  133. angr/engines/light/engine.py +4 -4
  134. angr/engines/pcode/behavior.py +20 -2
  135. angr/engines/pcode/emulate.py +1 -1
  136. angr/engines/pcode/engine.py +7 -7
  137. angr/engines/pcode/lifter.py +78 -77
  138. angr/engines/vex/claripy/ccall.py +1 -2
  139. angr/engines/vex/claripy/datalayer.py +1 -2
  140. angr/engines/vex/light/light.py +1 -2
  141. angr/exploration_techniques/tracer.py +4 -4
  142. angr/factory.py +12 -15
  143. angr/flirt/__init__.py +8 -8
  144. angr/flirt/build_sig.py +2 -3
  145. angr/keyed_region.py +2 -2
  146. angr/knowledge_base/knowledge_base.py +3 -3
  147. angr/knowledge_plugins/callsite_prototypes.py +4 -6
  148. angr/knowledge_plugins/cfg/cfg_manager.py +19 -6
  149. angr/knowledge_plugins/cfg/cfg_model.py +26 -27
  150. angr/knowledge_plugins/cfg/cfg_node.py +2 -2
  151. angr/knowledge_plugins/cfg/indirect_jump.py +6 -8
  152. angr/knowledge_plugins/cfg/memory_data.py +8 -9
  153. angr/knowledge_plugins/custom_strings.py +1 -3
  154. angr/knowledge_plugins/debug_variables.py +2 -2
  155. angr/knowledge_plugins/functions/function.py +21 -22
  156. angr/knowledge_plugins/functions/function_manager.py +5 -5
  157. angr/knowledge_plugins/indirect_jumps.py +1 -3
  158. angr/knowledge_plugins/key_definitions/atoms.py +7 -7
  159. angr/knowledge_plugins/key_definitions/definition.py +14 -14
  160. angr/knowledge_plugins/key_definitions/environment.py +5 -7
  161. angr/knowledge_plugins/key_definitions/heap_address.py +1 -3
  162. angr/knowledge_plugins/key_definitions/key_definition_manager.py +3 -2
  163. angr/knowledge_plugins/key_definitions/live_definitions.py +60 -59
  164. angr/knowledge_plugins/key_definitions/liveness.py +16 -16
  165. angr/knowledge_plugins/key_definitions/rd_model.py +15 -15
  166. angr/knowledge_plugins/key_definitions/uses.py +11 -11
  167. angr/knowledge_plugins/patches.py +4 -8
  168. angr/knowledge_plugins/propagations/prop_value.py +10 -9
  169. angr/knowledge_plugins/propagations/propagation_manager.py +3 -5
  170. angr/knowledge_plugins/propagations/propagation_model.py +9 -9
  171. angr/knowledge_plugins/propagations/states.py +52 -22
  172. angr/knowledge_plugins/structured_code/manager.py +2 -2
  173. angr/knowledge_plugins/sync/sync_controller.py +3 -3
  174. angr/knowledge_plugins/variables/variable_access.py +4 -4
  175. angr/knowledge_plugins/variables/variable_manager.py +39 -39
  176. angr/knowledge_plugins/xrefs/xref.py +9 -11
  177. angr/knowledge_plugins/xrefs/xref_manager.py +3 -4
  178. angr/misc/ansi.py +1 -2
  179. angr/misc/autoimport.py +3 -3
  180. angr/misc/plugins.py +9 -9
  181. angr/procedures/definitions/__init__.py +16 -16
  182. angr/procedures/definitions/linux_kernel.py +1 -1
  183. angr/procedures/definitions/parse_win32json.py +1 -1
  184. angr/procedures/java_jni/__init__.py +1 -1
  185. angr/procedures/java_jni/array_operations.py +1 -2
  186. angr/procedures/java_jni/method_calls.py +1 -2
  187. angr/procedures/posix/inet_ntoa.py +1 -2
  188. angr/procedures/stubs/format_parser.py +3 -3
  189. angr/project.py +13 -11
  190. angr/sim_manager.py +12 -12
  191. angr/sim_procedure.py +7 -3
  192. angr/sim_state.py +2 -2
  193. angr/sim_type.py +60 -45
  194. angr/sim_variable.py +5 -5
  195. angr/simos/simos.py +1 -2
  196. angr/simos/userland.py +1 -2
  197. angr/state_plugins/callstack.py +3 -2
  198. angr/state_plugins/history.py +1 -2
  199. angr/state_plugins/solver.py +34 -34
  200. angr/storage/memory_mixins/__init__.py +4 -3
  201. angr/storage/memory_mixins/actions_mixin.py +1 -3
  202. angr/storage/memory_mixins/address_concretization_mixin.py +1 -3
  203. angr/storage/memory_mixins/convenient_mappings_mixin.py +3 -4
  204. angr/storage/memory_mixins/default_filler_mixin.py +1 -1
  205. angr/storage/memory_mixins/label_merger_mixin.py +2 -2
  206. angr/storage/memory_mixins/multi_value_merger_mixin.py +4 -3
  207. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +9 -8
  208. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +12 -11
  209. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +8 -8
  210. angr/storage/memory_mixins/paged_memory/pages/history_tracking_mixin.py +2 -3
  211. angr/storage/memory_mixins/paged_memory/pages/list_page.py +10 -11
  212. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +11 -10
  213. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +18 -17
  214. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +12 -11
  215. angr/storage/memory_mixins/regioned_memory/abstract_address_descriptor.py +3 -3
  216. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +3 -2
  217. angr/storage/memory_mixins/regioned_memory/region_data.py +1 -2
  218. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +2 -2
  219. angr/storage/memory_mixins/regioned_memory/regioned_address_concretization_mixin.py +3 -3
  220. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +18 -21
  221. angr/storage/memory_mixins/size_resolution_mixin.py +1 -2
  222. angr/storage/memory_mixins/symbolic_merger_mixin.py +3 -2
  223. angr/storage/memory_mixins/top_merger_mixin.py +3 -2
  224. angr/storage/memory_object.py +2 -4
  225. angr/utils/algo.py +3 -2
  226. angr/utils/dynamic_dictlist.py +5 -5
  227. angr/utils/formatting.py +4 -4
  228. angr/utils/funcid.py +1 -2
  229. angr/utils/graph.py +5 -6
  230. angr/utils/library.py +5 -5
  231. angr/utils/mp.py +5 -4
  232. angr/utils/segment_list.py +3 -4
  233. angr/utils/typing.py +3 -2
  234. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/METADATA +9 -11
  235. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/RECORD +239 -236
  236. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/LICENSE +0 -0
  237. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/WHEEL +0 -0
  238. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/entry_points.txt +0 -0
  239. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  # decompilation options
2
- from typing import Optional, List, Callable
2
+ from collections.abc import Callable
3
3
  from collections import defaultdict
4
4
 
5
5
  from .structuring import structurer_class_from_name
@@ -22,8 +22,8 @@ class DecompilationOption:
22
22
  category="General",
23
23
  default_value=None,
24
24
  clears_cache=True,
25
- candidate_values: Optional[List] = None,
26
- convert: Optional[Callable] = None,
25
+ candidate_values: list | None = None,
26
+ convert: Callable | None = None,
27
27
  ):
28
28
  self.NAME = name
29
29
  self.DESCRIPTION = description
@@ -267,7 +267,7 @@ for o in options:
267
267
  #
268
268
 
269
269
 
270
- def get_structurer_option() -> Optional[DecompilationOption]:
270
+ def get_structurer_option() -> DecompilationOption | None:
271
271
  for opt in options:
272
272
  if opt.cls == "recursive_structurer" and opt.param == "structurer_cls":
273
273
  return opt
@@ -1,7 +1,8 @@
1
1
  # pylint:disable=unused-import
2
2
  import logging
3
3
  from collections import defaultdict
4
- from typing import List, Tuple, Optional, Iterable, Union, Type, Set, Dict, Any, TYPE_CHECKING
4
+ from typing import Optional, Union, Any, TYPE_CHECKING
5
+ from collections.abc import Iterable
5
6
 
6
7
  import networkx
7
8
  from cle import SymbolType
@@ -33,7 +34,7 @@ if TYPE_CHECKING:
33
34
  l = logging.getLogger(name=__name__)
34
35
 
35
36
  _PEEPHOLE_OPTIMIZATIONS_TYPE = Optional[
36
- Iterable[Union[Type["PeepholeOptimizationStmtBase"], Type["PeepholeOptimizationExprBase"]]]
37
+ Iterable[Union[type["PeepholeOptimizationStmtBase"], type["PeepholeOptimizationExprBase"]]]
37
38
  ]
38
39
 
39
40
 
@@ -47,14 +48,14 @@ class Decompiler(Analysis):
47
48
 
48
49
  def __init__(
49
50
  self,
50
- func: Union[Function, str, int],
51
- cfg: Optional[Union["CFGFast", "CFGModel"]] = None,
51
+ func: Function | str | int,
52
+ cfg: Union["CFGFast", "CFGModel"] | None = None,
52
53
  options=None,
53
54
  optimization_passes=None,
54
55
  sp_tracker_track_memory=True,
55
56
  variable_kb=None,
56
57
  peephole_optimizations: _PEEPHOLE_OPTIMIZATIONS_TYPE = None,
57
- vars_must_struct: Optional[Set[str]] = None,
58
+ vars_must_struct: set[str] | None = None,
58
59
  flavor="pseudocode",
59
60
  expr_comments=None,
60
61
  stmt_comments=None,
@@ -62,6 +63,7 @@ class Decompiler(Analysis):
62
63
  binop_operators=None,
63
64
  decompile=True,
64
65
  regen_clinic=True,
66
+ inline_functions=frozenset(),
65
67
  update_memory_data: bool = True,
66
68
  generate_code: bool = True,
67
69
  ):
@@ -87,14 +89,15 @@ class Decompiler(Analysis):
87
89
  self._regen_clinic = regen_clinic
88
90
  self._update_memory_data = update_memory_data
89
91
  self._generate_code = generate_code
92
+ self._inline_functions = inline_functions
90
93
 
91
94
  self.clinic = None # mostly for debugging purposes
92
95
  self.codegen: Optional["CStructuredCodeGenerator"] = None
93
- self.cache: Optional[DecompilationCache] = None
96
+ self.cache: DecompilationCache | None = None
94
97
  self.options_by_class = None
95
98
  self.seq_node: Optional["SequenceNode"] = None
96
- self.unoptimized_ail_graph: Optional[networkx.DiGraph] = None
97
- self.ail_graph: Optional[networkx.DiGraph] = None
99
+ self.unoptimized_ail_graph: networkx.DiGraph | None = None
100
+ self.ail_graph: networkx.DiGraph | None = None
98
101
 
99
102
  if decompile:
100
103
  self._decompile()
@@ -171,6 +174,7 @@ class Decompiler(Analysis):
171
174
  must_struct=self._vars_must_struct,
172
175
  cache=cache,
173
176
  progress_callback=progress_callback,
177
+ inline_functions=self._inline_functions,
174
178
  **self.options_to_params(self.options_by_class["clinic"]),
175
179
  )
176
180
  else:
@@ -295,8 +299,8 @@ class Decompiler(Analysis):
295
299
  :param reaching_defenitions: ReachingDefenitionAnalysis
296
300
  :return: The possibly new AIL DiGraph and RegionIdentifier
297
301
  """
298
- addr_and_idx_to_blocks: Dict[Tuple[int, Optional[int]], ailment.Block] = {}
299
- addr_to_blocks: Dict[int, Set[ailment.Block]] = defaultdict(set)
302
+ addr_and_idx_to_blocks: dict[tuple[int, int | None], ailment.Block] = {}
303
+ addr_to_blocks: dict[int, set[ailment.Block]] = defaultdict(set)
300
304
 
301
305
  # update blocks_map to allow node_addr to node lookup
302
306
  def _updatedict_handler(node):
@@ -346,8 +350,8 @@ class Decompiler(Analysis):
346
350
  :param reaching_defenitions: ReachingDefenitionAnalysis
347
351
  :return: The possibly new AIL DiGraph and RegionIdentifier
348
352
  """
349
- addr_and_idx_to_blocks: Dict[Tuple[int, Optional[int]], ailment.Block] = {}
350
- addr_to_blocks: Dict[int, Set[ailment.Block]] = defaultdict(set)
353
+ addr_and_idx_to_blocks: dict[tuple[int, int | None], ailment.Block] = {}
354
+ addr_to_blocks: dict[int, set[ailment.Block]] = defaultdict(set)
351
355
 
352
356
  # update blocks_map to allow node_addr to node lookup
353
357
  def _updatedict_handler(node):
@@ -415,7 +419,7 @@ class Decompiler(Analysis):
415
419
  SimMemoryVariable(symbol.rebased_addr, 1, name=symbol.name, ident=ident),
416
420
  )
417
421
 
418
- def reflow_variable_types(self, type_constraints: Set, func_typevar, var_to_typevar: Dict, codegen):
422
+ def reflow_variable_types(self, type_constraints: set, func_typevar, var_to_typevar: dict, codegen):
419
423
  """
420
424
  Re-run type inference on an existing variable recovery result, then rerun codegen to generate new results.
421
425
 
@@ -485,7 +489,7 @@ class Decompiler(Analysis):
485
489
  return codegen
486
490
 
487
491
  def find_data_references_and_update_memory_data(self, seq_node: "SequenceNode"):
488
- const_values: Set[int] = set()
492
+ const_values: set[int] = set()
489
493
 
490
494
  def _handle_Const(expr_idx: int, expr: ailment.Expr.Const, *args, **kwargs): # pylint:disable=unused-argument
491
495
  const_values.add(expr.value)
@@ -520,7 +524,7 @@ class Decompiler(Analysis):
520
524
  )
521
525
 
522
526
  @staticmethod
523
- def options_to_params(options: List[Tuple[DecompilationOption, Any]]) -> Dict[str, Any]:
527
+ def options_to_params(options: list[tuple[DecompilationOption, Any]]) -> dict[str, Any]:
524
528
  """
525
529
  Convert decompilation options to a dict of params.
526
530
 
@@ -1,4 +1,5 @@
1
- from typing import Optional, Any, DefaultDict, Tuple, Union, Iterable, Set, TYPE_CHECKING
1
+ from typing import Any, DefaultDict, TYPE_CHECKING
2
+ from collections.abc import Iterable
2
3
  from collections import defaultdict
3
4
 
4
5
  from ailment.expression import Expression, Register
@@ -22,7 +23,7 @@ class SingleExpressionCounter(AILBlockWalkerBase):
22
23
  self.walk_statement(stmt)
23
24
 
24
25
  def _handle_expr(
25
- self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Optional[Statement], block: Optional[Block]
26
+ self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: Block | None
26
27
  ) -> Any:
27
28
  if expr == self.subexpr:
28
29
  self.count += 1
@@ -34,9 +35,9 @@ class RegisterExpressionCounter(AILBlockWalkerBase):
34
35
  Count the occurrence of all register expressions in expr
35
36
  """
36
37
 
37
- def __init__(self, expr_or_stmt: Union[Expression, Statement]):
38
+ def __init__(self, expr_or_stmt: Expression | Statement):
38
39
  super().__init__()
39
- self.counts: DefaultDict[Tuple[int, int], int] = defaultdict(int)
40
+ self.counts: DefaultDict[tuple[int, int], int] = defaultdict(int)
40
41
  if isinstance(expr_or_stmt, Expression):
41
42
  self.walk_expression(expr_or_stmt)
42
43
  elif isinstance(expr_or_stmt, Statement):
@@ -44,7 +45,7 @@ class RegisterExpressionCounter(AILBlockWalkerBase):
44
45
  else:
45
46
  raise TypeError(f"Unsupported argument type {type(expr_or_stmt)}")
46
47
 
47
- def _handle_Register(self, expr_idx: int, expr: Register, stmt_idx: int, stmt: Statement, block: Optional[Block]):
48
+ def _handle_Register(self, expr_idx: int, expr: Register, stmt_idx: int, stmt: Statement, block: Block | None):
48
49
  self.counts[expr.reg_offset, expr.size] += 1
49
50
 
50
51
 
@@ -53,10 +54,10 @@ class OperatorCounter(AILBlockWalkerBase):
53
54
  Count the occurrence of a given expression operator.
54
55
  """
55
56
 
56
- def __init__(self, operator: Union[str, Iterable[str]], expr_or_stmt: Union[Expression, Statement]):
57
+ def __init__(self, operator: str | Iterable[str], expr_or_stmt: Expression | Statement):
57
58
  super().__init__()
58
59
  self.count = 0
59
- self.operators: Set[str] = {operator} if isinstance(operator, str) else set(operator)
60
+ self.operators: set[str] = {operator} if isinstance(operator, str) else set(operator)
60
61
  if isinstance(expr_or_stmt, Expression):
61
62
  self.walk_expression(expr_or_stmt)
62
63
  elif isinstance(expr_or_stmt, Statement):
@@ -64,12 +65,12 @@ class OperatorCounter(AILBlockWalkerBase):
64
65
  else:
65
66
  raise TypeError(f"Unsupported argument type {type(expr_or_stmt)}")
66
67
 
67
- def _handle_BinaryOp(self, expr_idx: int, expr: "BinaryOp", stmt_idx: int, stmt: Statement, block: Optional[Block]):
68
+ def _handle_BinaryOp(self, expr_idx: int, expr: "BinaryOp", stmt_idx: int, stmt: Statement, block: Block | None):
68
69
  if expr.op in self.operators:
69
70
  self.count += 1
70
71
  return super()._handle_BinaryOp(expr_idx, expr, stmt_idx, stmt, block)
71
72
 
72
- def _handle_UnaryOp(self, expr_idx: int, expr: "UnaryOp", stmt_idx: int, stmt: Statement, block: Optional[Block]):
73
+ def _handle_UnaryOp(self, expr_idx: int, expr: "UnaryOp", stmt_idx: int, stmt: Statement, block: Block | None):
73
74
  if expr.op in self.operators:
74
75
  self.count += 1
75
76
  return super()._handle_UnaryOp(expr_idx, expr, stmt_idx, stmt, block)
@@ -1,5 +1,3 @@
1
- from typing import Set
2
-
3
1
  import ailment
4
2
 
5
3
 
@@ -40,7 +38,7 @@ class GotoManager:
40
38
 
41
39
  def __init__(self, func, gotos=None):
42
40
  self.func = func
43
- self.gotos: Set[Goto] = gotos or set()
41
+ self.gotos: set[Goto] = gotos or set()
44
42
 
45
43
  self._gotos_by_addr = None
46
44
 
@@ -50,7 +48,7 @@ class GotoManager:
50
48
  def __repr__(self):
51
49
  return self.__str__()
52
50
 
53
- def gotos_in_block(self, block: ailment.Block) -> Set[Goto]:
51
+ def gotos_in_block(self, block: ailment.Block) -> set[Goto]:
54
52
  gotos_found = set()
55
53
  for goto in self.gotos:
56
54
  if goto.src_addr == block.addr:
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Any, Optional, List, Set, Tuple
2
+ from typing import Any
3
3
  from collections import defaultdict
4
4
 
5
5
  import networkx
@@ -37,10 +37,10 @@ class GraphRegion:
37
37
  self,
38
38
  head,
39
39
  graph,
40
- successors: Optional[Set],
41
- graph_with_successors: Optional[networkx.DiGraph],
40
+ successors: set | None,
41
+ graph_with_successors: networkx.DiGraph | None,
42
42
  cyclic,
43
- full_graph: Optional[networkx.DiGraph],
43
+ full_graph: networkx.DiGraph | None,
44
44
  cyclic_ancestor: bool = False,
45
45
  ):
46
46
  self.head = head
@@ -61,7 +61,7 @@ class GraphRegion:
61
61
  self._replaced_regions = {}
62
62
 
63
63
  def __repr__(self):
64
- addrs: List[int] = []
64
+ addrs: list[int] = []
65
65
  s = ""
66
66
  if self.graph is None:
67
67
  # only head is available
@@ -197,7 +197,7 @@ class GraphRegion:
197
197
  sub_region: "GraphRegion",
198
198
  updated_sub_region: "GraphRegion",
199
199
  replace_with,
200
- virtualized_edges: Set[Tuple[Any, Any]],
200
+ virtualized_edges: set[tuple[Any, Any]],
201
201
  ):
202
202
  if sub_region not in self.graph:
203
203
  l.error("The sub-region to replace must be in the current region. Note that this method is not recursive.")
@@ -299,7 +299,7 @@ class GraphRegion:
299
299
  )
300
300
 
301
301
  @staticmethod
302
- def _replace_node_in_graph(graph: networkx.DiGraph, node, replace_with, removed_edges: Set):
302
+ def _replace_node_in_graph(graph: networkx.DiGraph, node, replace_with, removed_edges: set):
303
303
  in_edges = [(src, dst) for src, dst in graph.in_edges(node) if (src, dst) not in removed_edges]
304
304
  out_edges = [(src, dst) for src, dst in graph.out_edges(node) if (src, dst) not in removed_edges]
305
305
 
@@ -323,8 +323,8 @@ class GraphRegion:
323
323
  def _replace_node_in_graph_with_subgraph(
324
324
  self,
325
325
  graph: networkx.DiGraph,
326
- known_successors: Optional[List],
327
- reference_full_graph: Optional[networkx.DiGraph],
326
+ known_successors: list | None,
327
+ reference_full_graph: networkx.DiGraph | None,
328
328
  node,
329
329
  sub_graph: networkx.DiGraph,
330
330
  sub_graph_head,
@@ -1,5 +1,4 @@
1
1
  # pylint:disable=unused-argument
2
- from typing import Set, Tuple, Optional
3
2
 
4
3
  import ailment
5
4
 
@@ -13,7 +12,7 @@ class JumpTargetCollector:
13
12
 
14
13
  def __init__(self, node):
15
14
  self.root = node
16
- self.jump_targets: Set[Tuple[int, Optional[int]]] = set()
15
+ self.jump_targets: set[tuple[int, int | None]] = set()
17
16
 
18
17
  handlers = {
19
18
  ailment.Block: self._handle_Block,
@@ -17,6 +17,7 @@ from .return_duplicator_low import ReturnDuplicatorLow
17
17
  from .return_duplicator_high import ReturnDuplicatorHigh
18
18
  from .const_derefs import ConstantDereferencesSimplifier
19
19
  from .register_save_area_simplifier import RegisterSaveAreaSimplifier
20
+ from .spilled_register_finder import SpilledRegisterFinder
20
21
  from .ret_addr_save_simplifier import RetAddrSaveSimplifier
21
22
  from .x86_gcc_getpc_simplifier import X86GccGetPcSimplifier
22
23
  from .flip_boolean_cmp import FlipBooleanCmp
@@ -25,6 +26,7 @@ from .win_stack_canary_simplifier import WinStackCanarySimplifier
25
26
  from .cross_jump_reverter import CrossJumpReverter
26
27
  from .code_motion import CodeMotionOptimization
27
28
  from .switch_default_case_duplicator import SwitchDefaultCaseDuplicator
29
+ from .deadblock_remover import DeadblockRemover
28
30
  from .inlined_string_transformation_simplifier import InlinedStringTransformationSimplifier
29
31
 
30
32
  # order matters!
@@ -43,6 +45,7 @@ _all_optimization_passes = [
43
45
  (ITEExprConverter, True),
44
46
  (ExprOpSwapper, True),
45
47
  (ReturnDuplicatorHigh, True),
48
+ (DeadblockRemover, True),
46
49
  (SwitchDefaultCaseDuplicator, True),
47
50
  (LoweredSwitchSimplifier, False),
48
51
  (ReturnDuplicatorLow, True),
@@ -78,9 +81,7 @@ def get_optimization_passes(arch, platform):
78
81
  return passes
79
82
 
80
83
 
81
- def get_default_optimization_passes(
82
- arch: Union[Arch, str], platform: Optional[str], enable_opts=None, disable_opts=None
83
- ):
84
+ def get_default_optimization_passes(arch: Arch | str, platform: str | None, enable_opts=None, disable_opts=None):
84
85
  if isinstance(arch, Arch):
85
86
  arch = arch.name
86
87
 
@@ -1,5 +1,4 @@
1
1
  import itertools
2
- from typing import Tuple, List, Optional, Dict
3
2
  import logging
4
3
 
5
4
  from ailment import Block
@@ -79,7 +78,7 @@ class CodeMotionOptimization(OptimizationPass):
79
78
 
80
79
  @staticmethod
81
80
  def update_graph_with_super_edits(
82
- original_graph: nx.DiGraph, super_graph: nx.DiGraph, updated_blocks: Dict[Block, Block]
81
+ original_graph: nx.DiGraph, super_graph: nx.DiGraph, updated_blocks: dict[Block, Block]
83
82
  ) -> bool:
84
83
  """
85
84
  This function updates an graph when doing block edits on a supergraph version of that same graph.
@@ -123,7 +122,7 @@ class CodeMotionOptimization(OptimizationPass):
123
122
 
124
123
  return False
125
124
 
126
- def _move_common_code(self, graph) -> Tuple[bool, Optional[Dict[Block, Block]]]:
125
+ def _move_common_code(self, graph) -> tuple[bool, dict[Block, Block] | None]:
127
126
  """
128
127
  Does two things at a high level:
129
128
  1. rearrange code in blocks to maximize the number of similar statements at the end of the block
@@ -220,7 +219,7 @@ class CodeMotionOptimization(OptimizationPass):
220
219
 
221
220
  def _make_stmts_end_similar(
222
221
  self, b0: Block, b1: Block, up=False, down=False
223
- ) -> Tuple[bool, Optional[Block], Optional[Block]]:
222
+ ) -> tuple[bool, Block | None, Block | None]:
224
223
  """
225
224
  This algorithm attempts to rearrange two blocks to have the longest common sequence of statements
226
225
  at either ends of the blocks. It is flawed in that it currently only attempts to do this rearrangement
@@ -321,7 +320,7 @@ class CodeMotionOptimization(OptimizationPass):
321
320
 
322
321
  def _maximize_ends(
323
322
  self, b0_stmts, b1_stmts, up=False, down=False
324
- ) -> Tuple[bool, Tuple[List[Statement], List[Statement]]]:
323
+ ) -> tuple[bool, tuple[list[Statement], list[Statement]]]:
325
324
  self._assert_up_or_down(up, down)
326
325
 
327
326
  similar_stmt = b0_stmts[0] if up else b0_stmts[-1]
@@ -333,7 +332,7 @@ class CodeMotionOptimization(OptimizationPass):
333
332
  success, new_b1_stmts = self._move_to_end(target_stmt, b1_stmts, up=up, down=down)
334
333
  return (success and (b1_stmts != new_b1_stmts)), (b0_stmts, new_b1_stmts)
335
334
 
336
- def _move_to_end(self, stmt, stmts, up=False, down=False) -> Tuple[bool, List[Statement]]:
335
+ def _move_to_end(self, stmt, stmts, up=False, down=False) -> tuple[bool, list[Statement]]:
337
336
  """
338
337
  Attempts to move a stmt to either the top or the bottom of stmts.
339
338
  It does this by attempting to swap, 1 by 1, in either direction it is targeting.
@@ -1,5 +1,5 @@
1
1
  # pylint:disable=unused-argument
2
- from typing import Optional, TYPE_CHECKING
2
+ from typing import TYPE_CHECKING
3
3
  import logging
4
4
 
5
5
  from ailment import Block, AILBlockWalker
@@ -20,7 +20,7 @@ class BlockWalker(AILBlockWalker):
20
20
  def __init__(self, project: "Project"):
21
21
  super().__init__()
22
22
  self._project = project
23
- self._new_block: Optional[Block] = None # output
23
+ self._new_block: Block | None = None # output
24
24
 
25
25
  def walk(self, block: Block):
26
26
  self._new_block = None
@@ -68,7 +68,7 @@ class BlockWalker(AILBlockWalker):
68
68
  return new_stmt
69
69
  return None
70
70
 
71
- def _handle_CallExpr(self, expr_idx: int, expr: Call, stmt_idx: int, stmt: Statement, block: Optional[Block]):
71
+ def _handle_CallExpr(self, expr_idx: int, expr: Call, stmt_idx: int, stmt: Statement, block: Block | None):
72
72
  new_target = self._handle_expr(-1, expr.target, stmt_idx, stmt, block)
73
73
 
74
74
  new_args = None
@@ -260,6 +260,6 @@ class ConstantDereferencesSimplifier(OptimizationPass):
260
260
  walker = AILGraphWalker(self._graph, handler=self._walk_block, replace_nodes=True)
261
261
  walker.walk()
262
262
 
263
- def _walk_block(self, block: Block) -> Optional[Block]:
263
+ def _walk_block(self, block: Block) -> Block | None:
264
264
  new_block = self._block_walker.walk(block)
265
265
  return new_block
@@ -0,0 +1,73 @@
1
+ # pylint:disable=too-many-boolean-expressions
2
+ import logging
3
+
4
+ import networkx
5
+
6
+ import claripy
7
+
8
+ from ailment.statement import Jump
9
+ from ailment.expression import Const
10
+ from angr.utils.graph import to_acyclic_graph
11
+ from angr.analyses.decompiler.condition_processor import ConditionProcessor
12
+ from .optimization_pass import OptimizationPass, OptimizationPassStage
13
+
14
+
15
+ _l = logging.getLogger(name=__name__)
16
+
17
+
18
+ class DeadblockRemover(OptimizationPass):
19
+ """
20
+ Removes condition-unreachable blocks from the graph.
21
+ """
22
+
23
+ ARCHES = None
24
+ PLATFORMS = None
25
+ STAGE = OptimizationPassStage.BEFORE_REGION_IDENTIFICATION
26
+ NAME = "Remove blocks with unsatisfiable conditions"
27
+ DESCRIPTION = __doc__.strip()
28
+
29
+ def __init__(self, func, **kwargs):
30
+ super().__init__(func, **kwargs)
31
+ self.analyze()
32
+
33
+ def _check(self):
34
+ cond_proc = ConditionProcessor(self.project.arch)
35
+ if networkx.is_directed_acyclic_graph(self._graph):
36
+ acyclic_graph = self._graph
37
+ else:
38
+ acyclic_graph = to_acyclic_graph(self._graph)
39
+ cond_proc.recover_reaching_conditions(region=None, graph=acyclic_graph)
40
+
41
+ if not any(claripy.is_false(c) for c in cond_proc.reaching_conditions.values()):
42
+ return False, None
43
+
44
+ cache = {"cond_proc": cond_proc}
45
+ return True, cache
46
+
47
+ def _analyze(self, cache=None):
48
+ cond_proc = cache["cond_proc"]
49
+ to_remove = {
50
+ blk
51
+ for blk in self._graph.nodes()
52
+ if blk.addr != self._func.addr
53
+ and self._graph.in_degree(blk) == 0
54
+ or claripy.is_false(cond_proc.reaching_conditions[blk])
55
+ }
56
+
57
+ # fix up predecessors
58
+ for b in to_remove:
59
+ for p in self._graph.predecessors(b):
60
+ if self._graph.out_degree(p) != 2:
61
+ continue
62
+ other_successor = next(s for s in self._graph.successors(p) if s != b)
63
+ p.statements[-1] = Jump(
64
+ None,
65
+ Const(None, None, other_successor.addr, self.project.arch.bits),
66
+ other_successor.idx,
67
+ **p.statements[-1].tags,
68
+ )
69
+
70
+ for n in to_remove:
71
+ self._graph.remove_node(n)
72
+
73
+ self.out_graph = self._graph
@@ -209,9 +209,21 @@ class SimplifierAILEngine(
209
209
  return operand_expr.operand
210
210
  else:
211
211
  return Expr.Convert(
212
- expr.idx, operand_expr.from_bits, expr.to_bits, expr.is_signed, operand_expr.operand, **expr.tags
212
+ expr.idx,
213
+ operand_expr.from_bits,
214
+ expr.to_bits,
215
+ expr.is_signed,
216
+ operand_expr.operand,
217
+ from_type=operand_expr.from_type,
218
+ to_type=expr.to_type,
219
+ rounding_mode=expr.rounding_mode,
220
+ **expr.tags,
213
221
  )
214
- elif type(operand_expr) is Expr.Const:
222
+ elif (
223
+ type(operand_expr) is Expr.Const
224
+ and expr.from_type == Expr.Convert.TYPE_INT
225
+ and expr.to_type == Expr.Convert.TYPE_INT
226
+ ):
215
227
  # do the conversion right away
216
228
  value = operand_expr.value
217
229
  mask = (2**expr.to_bits) - 1
@@ -277,5 +289,15 @@ class SimplifierAILEngine(
277
289
  **operand_expr.tags,
278
290
  )
279
291
 
280
- converted = Expr.Convert(expr.idx, expr.from_bits, expr.to_bits, expr.is_signed, operand_expr, **expr.tags)
292
+ converted = Expr.Convert(
293
+ expr.idx,
294
+ expr.from_bits,
295
+ expr.to_bits,
296
+ expr.is_signed,
297
+ operand_expr,
298
+ from_type=expr.from_type,
299
+ to_type=expr.to_type,
300
+ rounding_mode=expr.rounding_mode,
301
+ **expr.tags,
302
+ )
281
303
  return converted
@@ -1,5 +1,6 @@
1
1
  import logging
2
- from typing import Optional, Dict, Any, Callable, TYPE_CHECKING
2
+ from typing import Any, TYPE_CHECKING
3
+ from collections.abc import Callable
3
4
 
4
5
  from ailment.block import Block as AILBlock
5
6
  from ailment.statement import Statement
@@ -23,7 +24,7 @@ class OuterWalker(SequenceWalker):
23
24
 
24
25
  def __init__(self, desc):
25
26
  super().__init__()
26
- self.desc: Dict[OpDescriptor, str] = desc
27
+ self.desc: dict[OpDescriptor, str] = desc
27
28
 
28
29
  def _handle_Condition(self, node: "ConditionNode", **kwargs):
29
30
  for desc, new_op in self.desc.items():
@@ -56,7 +57,7 @@ class OuterWalker(SequenceWalker):
56
57
  return super()._handle_ConditionalBreak(node, **kwargs)
57
58
 
58
59
  @staticmethod
59
- def _swap_expr_op(new_op: str, atom: Expression) -> Optional[Expression]:
60
+ def _swap_expr_op(new_op: str, atom: Expression) -> Expression | None:
60
61
  # swap
61
62
  new_expr = BinaryOp(
62
63
  atom.idx, new_op, (atom.operands[1], atom.operands[0]), atom.signed, bits=atom.bits, **atom.tags
@@ -76,7 +77,7 @@ class ExpressionReplacer(AILBlockWalker):
76
77
  self._callback = callback
77
78
 
78
79
  def _handle_expr(
79
- self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Optional[Statement], block: Optional[AILBlock]
80
+ self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: AILBlock | None
80
81
  ) -> Any:
81
82
  if self._target_expr_predicate(expr):
82
83
  new_expr = self._callback(self._block_addr, expr)
@@ -119,7 +120,7 @@ class ExprOpSwapper(SequenceOptimizationPass):
119
120
  NAME = "Swap operands of expressions as requested"
120
121
  DESCRIPTION = __doc__.strip()
121
122
 
122
- def __init__(self, func, binop_operators: Optional[Dict[OpDescriptor, str]] = None, **kwargs):
123
+ def __init__(self, func, binop_operators: dict[OpDescriptor, str] | None = None, **kwargs):
123
124
  super().__init__(func, **kwargs)
124
125
  self._expr_operators = {} if binop_operators is None else binop_operators
125
126
 
@@ -1,5 +1,5 @@
1
1
  # pylint:disable=arguments-renamed,too-many-boolean-expressions
2
- from typing import List, Tuple, Any
2
+ from typing import Any
3
3
 
4
4
  import ailment
5
5
  from ailment.expression import Op
@@ -29,7 +29,7 @@ class FlipBooleanWalker(SequenceWalker):
29
29
  # Type 2:
30
30
  # if (cond) { ... } return; --> if (!cond) return; ...
31
31
  type1_condition_nodes = [node for node in seq_node.nodes if isinstance(node, ConditionNode) and node.false_node]
32
- type2_condition_nodes: List[Tuple[int, ConditionNode, Any]] = []
32
+ type2_condition_nodes: list[tuple[int, ConditionNode, Any]] = []
33
33
 
34
34
  if len(seq_node.nodes) >= 2:
35
35
  idx = len(seq_node.nodes) - 2
@@ -234,6 +234,9 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
234
234
  def _handle_Call(self, stmt):
235
235
  pass
236
236
 
237
+ def _ail_handle_CallExpr(self, expr):
238
+ pass
239
+
237
240
 
238
241
  class InlineStringTransformationDescriptor:
239
242
  """
@@ -59,7 +59,7 @@ class ExpressionReplacer(AILBlockWalker):
59
59
  self._callback = callback
60
60
 
61
61
  def _handle_expr(
62
- self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Optional[Statement], block: Optional["AILBlock"]
62
+ self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: Optional["AILBlock"]
63
63
  ) -> Any:
64
64
  if expr == self._target_expr:
65
65
  new_expr = self._callback(self._block_addr, stmt_idx, stmt.ins_addr, expr)
@@ -101,7 +101,7 @@ class ITEExprConverter(OptimizationPass):
101
101
  block_walker = ExpressionReplacer(block_addr, expr, self._convert_expr)
102
102
  block_walker.walk(block)
103
103
 
104
- def _convert_expr(self, block_addr: int, stmt_idx: int, ins_addr: int, atom: Expression) -> Optional[Expression]:
104
+ def _convert_expr(self, block_addr: int, stmt_idx: int, ins_addr: int, atom: Expression) -> Expression | None:
105
105
  rda = self.project.analyses[ReachingDefinitionsAnalysis].prep()(subject=self._func, func_graph=self._graph)
106
106
 
107
107
  # find the corresponding definition