angr 9.2.175__cp310-abi3-macosx_11_0_arm64.whl → 9.2.176__cp310-abi3-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (42) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +12 -0
  3. angr/analyses/complete_calling_conventions.py +39 -26
  4. angr/analyses/decompiler/ail_simplifier.py +13 -11
  5. angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +5 -1
  6. angr/analyses/decompiler/clinic.py +54 -40
  7. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +3 -3
  8. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +2 -2
  9. angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
  10. angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy.py → inlined_wcscpy.py} +16 -8
  11. angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy_consolidation.py → inlined_wcscpy_consolidation.py} +13 -13
  12. angr/analyses/decompiler/ssailification/rewriting_engine.py +14 -1
  13. angr/analyses/decompiler/structured_codegen/c.py +6 -5
  14. angr/analyses/decompiler/structuring/dream.py +2 -2
  15. angr/analyses/decompiler/structuring/phoenix.py +101 -23
  16. angr/analyses/stack_pointer_tracker.py +4 -3
  17. angr/analyses/typehoon/lifter.py +29 -18
  18. angr/analyses/typehoon/simple_solver.py +157 -50
  19. angr/analyses/typehoon/translator.py +34 -34
  20. angr/analyses/typehoon/typeconsts.py +33 -15
  21. angr/analyses/typehoon/typevars.py +9 -2
  22. angr/analyses/variable_recovery/engine_ail.py +4 -2
  23. angr/analyses/variable_recovery/engine_base.py +4 -1
  24. angr/analyses/variable_recovery/variable_recovery_fast.py +3 -1
  25. angr/engines/icicle.py +4 -4
  26. angr/engines/vex/claripy/ccall.py +3 -3
  27. angr/knowledge_plugins/functions/function.py +17 -0
  28. angr/procedures/posix/pthread.py +4 -4
  29. angr/procedures/stubs/format_parser.py +3 -3
  30. angr/rustylib.abi3.so +0 -0
  31. angr/sim_type.py +11 -6
  32. angr/simos/windows.py +1 -1
  33. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -1
  34. angr/unicornlib.dylib +0 -0
  35. angr/utils/constants.py +1 -1
  36. angr/utils/strings.py +20 -0
  37. {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/METADATA +5 -5
  38. {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/RECORD +42 -41
  39. {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/WHEEL +0 -0
  40. {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/entry_points.txt +0 -0
  41. {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/licenses/LICENSE +0 -0
  42. {angr-9.2.175.dist-info → angr-9.2.176.dist-info}/top_level.txt +0 -0
@@ -179,8 +179,11 @@ class RecursiveRefNode(SketchNodeBase):
179
179
  This is equivalent to sketches.LabelNode in the reference implementation of retypd.
180
180
  """
181
181
 
182
- def __init__(self, target: DerivedTypeVariable):
183
- self.target: DerivedTypeVariable = target
182
+ def __init__(self, target: TypeVariable | DerivedTypeVariable):
183
+ self.target: TypeVariable | DerivedTypeVariable = target
184
+
185
+ def __repr__(self):
186
+ return f"Ref({self.target})"
184
187
 
185
188
  def __hash__(self):
186
189
  return hash((RecursiveRefNode, self.target))
@@ -265,7 +268,7 @@ class Sketch:
265
268
  basetype = supertype
266
269
  assert basetype.size is not None
267
270
  max_size = self.solver.stackvar_max_sizes.get(subtype, None)
268
- if max_size not in {0, None} and max_size // basetype.size > 0: # type: ignore
271
+ if max_size not in {0, None} and basetype.size > 0 and max_size // basetype.size > 0: # type: ignore
269
272
  supertype = Array(element=basetype, count=max_size // basetype.size) # type: ignore
270
273
 
271
274
  if SimpleSolver._typevar_inside_set(subtype, PRIMITIVE_TYPES) and not SimpleSolver._typevar_inside_set(
@@ -328,7 +331,7 @@ class ConstraintGraphNode:
328
331
 
329
332
  def __init__(
330
333
  self,
331
- typevar: TypeVariable | DerivedTypeVariable,
334
+ typevar: TypeVariable | DerivedTypeVariable | TypeConstant,
332
335
  variance: Variance,
333
336
  tag: ConstraintGraphTag,
334
337
  forgotten: FORGOTTEN,
@@ -512,10 +515,21 @@ class SimpleSolver:
512
515
  typevars.add(repl)
513
516
 
514
517
  constraints = set()
518
+
515
519
  for tv in typevars:
516
520
  if tv in self._constraints:
517
521
  constraints |= self._constraints[tv]
518
522
 
523
+ equiv_classes, sketches = self.infer_shapes(typevars, constraints)
524
+
525
+ # only create sketches for the type variables representing their equivalence classes
526
+ tv_to_reptvs = {}
527
+ for tv_or_dtv, reptv in equiv_classes.items():
528
+ if not isinstance(tv_or_dtv, DerivedTypeVariable) and tv_or_dtv is not reptv:
529
+ tv_to_reptvs[tv_or_dtv] = reptv
530
+ # rewrite constraints to only use representative type variables
531
+ constraints = self._rewrite_constraints_with_replacements(constraints, tv_to_reptvs)
532
+
519
533
  # collect typevars used in the constraint set
520
534
  constrained_typevars = set()
521
535
  for constraint in constraints:
@@ -527,7 +541,6 @@ class SimpleSolver:
527
541
  elif isinstance(t, TypeVariable) and t in typevars:
528
542
  constrained_typevars.add(t)
529
543
 
530
- _, sketches = self.infer_shapes(typevars, constraints)
531
544
  constraintset2tvs = defaultdict(set)
532
545
  tvs_seen = set()
533
546
  for idx, tv in enumerate(constrained_typevars):
@@ -579,6 +592,10 @@ class SimpleSolver:
579
592
  filtered_constraint_subset, tvs | PRIMITIVE_TYPES
580
593
  )
581
594
  primitive_constraints = self._generate_primitive_constraints(tvs, base_constraint_graph)
595
+ if len(tvs) > 1:
596
+ primitive_constraints |= self._generate_transitive_subtype_constraints(
597
+ tvs, filtered_constraint_subset, primitive_constraints
598
+ )
582
599
  tvs_with_primitive_constraints = set()
583
600
  for primitive_constraint in primitive_constraints:
584
601
  tv = self._typevar_from_primitive_constraint(primitive_constraint)
@@ -586,7 +603,9 @@ class SimpleSolver:
586
603
  assert tv is not None, f"Cannot find type variable in primitive constraint {primitive_constraint}"
587
604
  sketches[tv].add_constraint(primitive_constraint)
588
605
  solutions = {}
589
- self.determine(sketches, tvs_with_primitive_constraints, solutions)
606
+ self.determine(
607
+ sketches, sorted(tvs_with_primitive_constraints, key=lambda x: x.idx), equiv_classes, solutions
608
+ )
590
609
  _l.debug("Determined solutions for %d type variable(s).", len(tvs_with_primitive_constraints))
591
610
 
592
611
  leaf_solutions = 0
@@ -614,7 +633,12 @@ class SimpleSolver:
614
633
  constraint_subset = self._filter_constraints(new_constraint_subset)
615
634
 
616
635
  # set the solution for missing type vars to TOP
617
- self.determine(sketches, set(sketches).difference(set(self.solution)), self.solution)
636
+ self.determine(sketches, set(sketches).difference(set(self.solution)), equiv_classes, self.solution)
637
+
638
+ # set solutions for non-representative type variables
639
+ for tv, reptv in equiv_classes.items():
640
+ if reptv in self.solution and tv not in self.solution:
641
+ self.solution[tv] = self.solution[reptv]
618
642
 
619
643
  def infer_shapes(
620
644
  self, typevars: set[TypeVariable], constraints: set[TypeConstraint]
@@ -627,14 +651,19 @@ class SimpleSolver:
627
651
 
628
652
  sketches: dict[TypeVariable, Sketch] = {}
629
653
  for tv in typevars:
654
+ if tv in equivalence_classes and equivalence_classes[tv] != tv:
655
+ # skip non-representative type variables
656
+ continue
630
657
  sketches[tv] = Sketch(self, tv)
631
658
 
632
659
  for tv, sketch in sketches.items():
633
660
  sketch_node = sketch.lookup(tv)
634
661
  graph_node = equivalence_classes.get(tv, None)
635
662
  # assert graph_node is not None
636
- if graph_node is None:
663
+ if graph_node is None or sketch_node is None:
637
664
  continue
665
+ assert isinstance(graph_node, TypeVariable)
666
+ assert isinstance(sketch_node, SketchNode)
638
667
  visited = {graph_node: sketch_node}
639
668
  self._get_all_paths(quotient_graph, sketch, graph_node, visited)
640
669
  return equivalence_classes, sketches
@@ -654,6 +683,7 @@ class SimpleSolver:
654
683
  last_node = tv
655
684
  prefix = tv
656
685
  while isinstance(prefix, DerivedTypeVariable) and prefix.labels:
686
+ assert isinstance(last_node, DerivedTypeVariable)
657
687
  prefix = prefix.longest_prefix()
658
688
  if prefix is None:
659
689
  continue
@@ -683,8 +713,8 @@ class SimpleSolver:
683
713
  out_graph = networkx.MultiDiGraph() # there can be multiple edges between two nodes, each edge is associated
684
714
  # with a different label
685
715
  for src, dst, data in g.edges(data=True):
686
- src_cls = equivalence_classes[src]
687
- dst_cls = equivalence_classes[dst]
716
+ src_cls = equivalence_classes.get(src, src)
717
+ dst_cls = equivalence_classes.get(dst, dst)
688
718
  label = None if not data else data["label"]
689
719
  if label is not None and out_graph.has_edge(src_cls, dst_cls):
690
720
  # do not add the same edge twice
@@ -708,6 +738,41 @@ class SimpleSolver:
708
738
  constraints_1 = self._solve_constraints_between(constraint_graph, PRIMITIVE_TYPES, non_primitive_endpoints)
709
739
  return constraints_0 | constraints_1
710
740
 
741
+ @staticmethod
742
+ def _generate_transitive_subtype_constraints(
743
+ typevars: set[TypeVariable | DerivedTypeVariable],
744
+ constraints: set[TypeConstraint],
745
+ primitive_constraints: set[TypeConstraint],
746
+ ) -> set[TypeConstraint]:
747
+ """
748
+ Handling multiple type variables at once means we may miss some subtyping relationships between a type variable
749
+ and a primitive type (e.g., tv_1 <: tv_2 and tv_2 <: int, we may not see tv_1 <: int). This method attempts to
750
+ recover some of these missing constraints.
751
+ """
752
+
753
+ tv_supertype = defaultdict(set)
754
+ for constraint in primitive_constraints:
755
+ if isinstance(constraint, Subtype) and SimpleSolver._typevar_inside_set(
756
+ constraint.super_type, PRIMITIVE_TYPES
757
+ ):
758
+ tv_supertype[constraint.sub_type].add(constraint.super_type)
759
+
760
+ additional_constraints = set()
761
+ for constraint in constraints:
762
+ if (
763
+ isinstance(constraint, Subtype)
764
+ and isinstance(constraint.sub_type, TypeVariable)
765
+ and (
766
+ (isinstance(constraint.sub_type, DerivedTypeVariable) and constraint.sub_type.type_var in typevars)
767
+ or (not isinstance(constraint.sub_type, DerivedTypeVariable) and constraint.sub_type in typevars)
768
+ )
769
+ and constraint.super_type in tv_supertype
770
+ ):
771
+ for supertype in tv_supertype[constraint.super_type]:
772
+ additional_constraints.add(Subtype(constraint.sub_type, supertype))
773
+
774
+ return additional_constraints
775
+
711
776
  @staticmethod
712
777
  def _typevars_from_constraints(constraints: set[TypeConstraint]) -> set[TypeVariable | DerivedTypeVariable]:
713
778
  """
@@ -717,8 +782,10 @@ class SimpleSolver:
717
782
  typevars: set[TypeVariable | DerivedTypeVariable] = set()
718
783
  for constraint in constraints:
719
784
  if isinstance(constraint, Subtype):
720
- typevars.add(constraint.sub_type)
721
- typevars.add(constraint.super_type)
785
+ if not isinstance(constraint.sub_type, TypeConstant):
786
+ typevars.add(constraint.sub_type)
787
+ if not isinstance(constraint.super_type, TypeConstant):
788
+ typevars.add(constraint.super_type)
722
789
  # TODO: Other types of constraints?
723
790
  return typevars
724
791
 
@@ -745,7 +812,7 @@ class SimpleSolver:
745
812
  def _get_all_paths(
746
813
  graph: networkx.DiGraph[TypeVariable | DerivedTypeVariable],
747
814
  sketch: Sketch,
748
- node: DerivedTypeVariable,
815
+ node: TypeVariable,
749
816
  visited: dict[TypeVariable | DerivedTypeVariable, SketchNode],
750
817
  ):
751
818
  if node not in graph:
@@ -779,29 +846,45 @@ class SimpleSolver:
779
846
 
780
847
  @staticmethod
781
848
  def _unify(
782
- equivalence_classes: dict, cls0: DerivedTypeVariable, cls1: DerivedTypeVariable, graph: networkx.DiGraph
849
+ equivalence_classes: dict,
850
+ cls0: TypeConstant | TypeVariable,
851
+ cls1: TypeConstant | TypeVariable,
852
+ graph: networkx.DiGraph,
783
853
  ) -> None:
784
854
  # first convert cls0 and cls1 to their equivalence classes
785
- cls0 = equivalence_classes[cls0]
786
- cls1 = equivalence_classes[cls1]
855
+ cls0 = equivalence_classes.get(cls0, cls0)
856
+ cls1 = equivalence_classes.get(cls1, cls1)
787
857
 
788
858
  # unify if needed
789
859
  if cls0 != cls1:
790
860
  # MakeEquiv
791
- existing_elements = {key for key, item in equivalence_classes.items() if item in {cls0, cls1}}
792
- rep_cls = cls0
861
+ cls0_elems = {key for key, item in equivalence_classes.items() if item is cls0}
862
+ cls1_elems = {key for key, item in equivalence_classes.items() if item is cls1}
863
+ existing_elements = cls0_elems | cls1_elems
864
+ # pick a representative type variable and prioritize non-derived type variables
865
+ if not isinstance(cls0, DerivedTypeVariable):
866
+ rep_cls = cls0
867
+ elif not isinstance(cls1, DerivedTypeVariable):
868
+ rep_cls = cls1
869
+ else:
870
+ rep_cls = next(
871
+ iter(elem for elem in existing_elements if not isinstance(elem, DerivedTypeVariable)), cls0
872
+ )
793
873
  for elem in existing_elements:
794
874
  equivalence_classes[elem] = rep_cls
795
875
  # the logic below refers to the retypd reference implementation. it is different from Algorithm E.1
796
876
  # note that graph is used read-only in this method, so we do not need to make copy of edges
797
- for _, dst0, data0 in graph.out_edges(cls0, data=True):
877
+ for _, dst0, data0 in graph.out_edges(cls0_elems, data=True):
798
878
  if "label" in data0 and data0["label"] is not None:
799
- for _, dst1, data1 in graph.out_edges(cls1, data=True):
879
+ for _, dst1, data1 in graph.out_edges(cls1_elems, data=True):
800
880
  if data0["label"] == data1["label"] or (
801
881
  isinstance(data0["label"], Load) and isinstance(data1["label"], Store)
802
882
  ):
803
883
  SimpleSolver._unify(
804
- equivalence_classes, equivalence_classes[dst0], equivalence_classes[dst1], graph
884
+ equivalence_classes,
885
+ equivalence_classes.get(dst0, dst0),
886
+ equivalence_classes.get(dst1, dst1),
887
+ graph,
805
888
  )
806
889
 
807
890
  def _eq_constraints_from_add(self, typevar: TypeVariable):
@@ -898,9 +981,12 @@ class SimpleSolver:
898
981
 
899
982
  @staticmethod
900
983
  def _rewrite_constraints_with_replacements(
901
- constraints: set[TypeConstraint], replacements: dict[TypeVariable, TypeVariable]
984
+ constraints: set[TypeConstraint], replacements: dict[TypeVariable, TypeVariable | TypeConstant]
902
985
  ) -> set[TypeConstraint]:
903
986
  # replace constraints according to a dictionary of type variable replacements
987
+ if not replacements:
988
+ return constraints
989
+
904
990
  replaced_constraints = set()
905
991
  for constraint in constraints:
906
992
  if isinstance(constraint, Existence):
@@ -966,9 +1052,9 @@ class SimpleSolver:
966
1052
  if isinstance(constraint.sub_type, TypeVariable):
967
1053
  sub_typevars[constraint.sub_type].add(constraint.super_type)
968
1054
  for tv in [constraint.sub_type, constraint.super_type]:
969
- if isinstance(tv, DerivedTypeVariable):
1055
+ if isinstance(tv, DerivedTypeVariable) and not isinstance(constraint.sub_type, TypeConstant):
970
1056
  tv_to_dtvs[tv.type_var].add(constraint.sub_type)
971
- elif isinstance(tv, TypeVariable):
1057
+ elif isinstance(tv, TypeVariable) and not isinstance(constraint.sub_type, TypeConstant):
972
1058
  tv_to_dtvs[tv].add(constraint.sub_type)
973
1059
 
974
1060
  ub_subtypes: dict[TypeVariable, TypeVariable] = {}
@@ -980,8 +1066,11 @@ class SimpleSolver:
980
1066
 
981
1067
  filtered_constraints = set()
982
1068
  for constraint in constraints:
983
- if isinstance(constraint, Subtype) and constraint.sub_type in ub_subtypes:
984
- continue
1069
+ if isinstance(constraint, Subtype):
1070
+ if constraint.sub_type in ub_subtypes:
1071
+ continue
1072
+ if constraint.sub_type == constraint.super_type:
1073
+ continue
985
1074
  filtered_constraints.add(constraint)
986
1075
 
987
1076
  return filtered_constraints, ub_subtypes
@@ -1136,9 +1225,9 @@ class SimpleSolver:
1136
1225
  def _constraint_graph_add_edges(
1137
1226
  self,
1138
1227
  graph: networkx.DiGraph,
1139
- subtype: TypeVariable | DerivedTypeVariable,
1140
- supertype: TypeVariable | DerivedTypeVariable,
1141
- interesting_variables: set[DerivedTypeVariable],
1228
+ subtype: TypeVariable | DerivedTypeVariable | TypeConstant,
1229
+ supertype: TypeVariable | DerivedTypeVariable | TypeConstant,
1230
+ interesting_variables: set[TypeVariable | DerivedTypeVariable | TypeConstant],
1142
1231
  ):
1143
1232
  # left and right tags
1144
1233
  if self._typevar_inside_set(self._to_typevar_or_typeconst(subtype), interesting_variables):
@@ -1173,7 +1262,7 @@ class SimpleSolver:
1173
1262
 
1174
1263
  # initialize the reaching-push sets R(x)
1175
1264
  for x, y, data in graph.edges(data=True):
1176
- if "label" in data and data.get("label")[1] == "forget":
1265
+ if "label" in data and data.get("label")[1] == "forget": # type:ignore
1177
1266
  d = data["label"][0], x
1178
1267
  R[y].add(d)
1179
1268
 
@@ -1191,7 +1280,7 @@ class SimpleSolver:
1191
1280
  lbl = data.get("label")
1192
1281
  if lbl and lbl[1] == "recall":
1193
1282
  for _label, z in R[x]:
1194
- if not graph.has_edge(z, y):
1283
+ if lbl[0] == _label and not graph.has_edge(z, y):
1195
1284
  changed = True
1196
1285
  graph.add_edge(z, y)
1197
1286
  v_contravariant = []
@@ -1376,6 +1465,7 @@ class SimpleSolver:
1376
1465
  self,
1377
1466
  sketches,
1378
1467
  tvs,
1468
+ equivalence_classes: dict[TypeVariable, TypeVariable],
1379
1469
  solution: dict,
1380
1470
  nodes: set[SketchNode] | None = None,
1381
1471
  ) -> None:
@@ -1389,13 +1479,21 @@ class SimpleSolver:
1389
1479
  """
1390
1480
 
1391
1481
  for typevar in tvs:
1392
- self._determine(typevar, sketches[typevar], solution, nodes=nodes)
1482
+ self._solution_cache = {}
1483
+ self._determine(typevar, sketches[typevar], equivalence_classes, solution, nodes=nodes)
1393
1484
 
1394
1485
  for v, eq in self._equivalence.items():
1395
1486
  if v not in solution and eq in solution:
1396
1487
  solution[v] = solution[eq]
1397
1488
 
1398
- def _determine(self, the_typevar, sketch, solution: dict, nodes: set[SketchNode] | None = None):
1489
+ def _determine(
1490
+ self,
1491
+ the_typevar,
1492
+ sketch,
1493
+ equivalence_classes: dict[TypeVariable, TypeVariable],
1494
+ solution: dict,
1495
+ nodes: set[SketchNode] | None = None,
1496
+ ):
1399
1497
  """
1400
1498
  Return the solution from sketches
1401
1499
  """
@@ -1409,8 +1507,9 @@ class SimpleSolver:
1409
1507
  # consult the cache
1410
1508
  cached_results = set()
1411
1509
  for node in nodes:
1412
- if node.typevar in self._solution_cache:
1413
- cached_results.add(self._solution_cache[node.typevar])
1510
+ repr_tv = equivalence_classes.get(node.typevar, node.typevar)
1511
+ if repr_tv in self._solution_cache:
1512
+ cached_results.add(self._solution_cache[repr_tv])
1414
1513
  if len(cached_results) == 1:
1415
1514
  return next(iter(cached_results))
1416
1515
  if len(cached_results) > 1:
@@ -1434,7 +1533,8 @@ class SimpleSolver:
1434
1533
  func_type = Function([], [])
1435
1534
  result = self._pointer_class()(basetype=func_type)
1436
1535
  for node in nodes:
1437
- self._solution_cache[node.typevar] = result
1536
+ repr_tv = equivalence_classes.get(node.typevar, node.typevar)
1537
+ self._solution_cache[repr_tv] = result
1438
1538
 
1439
1539
  # this is a function variable
1440
1540
  func_inputs = defaultdict(set)
@@ -1455,7 +1555,7 @@ class SimpleSolver:
1455
1555
  for vals, out in [(func_inputs, input_args), (func_outputs, output_values)]:
1456
1556
  for idx in range(max(vals) + 1):
1457
1557
  if idx in vals:
1458
- sol = self._determine(the_typevar, sketch, solution, nodes=vals[idx])
1558
+ sol = self._determine(the_typevar, sketch, equivalence_classes, solution, nodes=vals[idx])
1459
1559
  out.append(sol)
1460
1560
  else:
1461
1561
  out.append(None)
@@ -1483,15 +1583,19 @@ class SimpleSolver:
1483
1583
  else the_node.upper_bound
1484
1584
  )
1485
1585
  for node in nodes:
1586
+ repr_tv = equivalence_classes.get(node.typevar, node.typevar)
1587
+ self._solution_cache[repr_tv] = result
1486
1588
  solution[node.typevar] = result
1487
- self._solution_cache[node.typevar] = result
1488
1589
  return result
1489
1590
 
1490
1591
  # create a dummy result and shove it into the cache
1491
1592
  struct_type = Struct(fields={})
1492
1593
  result = self._pointer_class()(struct_type)
1594
+ # print(f"Creating a struct type: {struct_type} for {the_typevar}")
1493
1595
  for node in nodes:
1494
- self._solution_cache[node.typevar] = result
1596
+ # print(f"... assigned it to {node.typevar}")
1597
+ repr_tv = equivalence_classes.get(node.typevar, node.typevar)
1598
+ self._solution_cache[repr_tv] = result
1495
1599
 
1496
1600
  # this might be a struct
1497
1601
  fields = {}
@@ -1528,21 +1632,21 @@ class SimpleSolver:
1528
1632
  offset_to_maxsize[base] = max(offset_to_maxsize[base], (last_label.offset - base) + access_size)
1529
1633
  offset_to_sizes[base].add(access_size)
1530
1634
 
1531
- idx_to_base = {}
1635
+ array_idx_to_base = {}
1532
1636
 
1533
1637
  for idx, (labels, _) in enumerate(path_and_successors):
1534
1638
  last_label = labels[-1] if labels else None
1535
- if isinstance(last_label, HasField):
1639
+ if isinstance(last_label, HasField) and last_label.elem_count > 1:
1536
1640
  prev_offset = next(offset_to_base.irange(maximum=last_label.offset, reverse=True))
1537
- idx_to_base[idx] = offset_to_base[prev_offset]
1641
+ array_idx_to_base[idx] = offset_to_base[prev_offset]
1538
1642
 
1539
1643
  node_by_offset = defaultdict(set)
1540
1644
 
1541
1645
  for idx, (labels, succ) in enumerate(path_and_successors):
1542
1646
  last_label = labels[-1] if labels else None
1543
1647
  if isinstance(last_label, HasField):
1544
- if idx in idx_to_base:
1545
- node_by_offset[idx_to_base[idx]].add(succ)
1648
+ if idx in array_idx_to_base:
1649
+ node_by_offset[array_idx_to_base[idx]].add(succ)
1546
1650
  else:
1547
1651
  node_by_offset[last_label.offset].add(succ)
1548
1652
 
@@ -1551,8 +1655,8 @@ class SimpleSolver:
1551
1655
  offset = sorted_offsets[i]
1552
1656
 
1553
1657
  child_nodes = node_by_offset[offset]
1554
- sol = self._determine(the_typevar, sketch, solution, nodes=child_nodes)
1555
- if isinstance(sol, TopType):
1658
+ sol = self._determine(the_typevar, sketch, equivalence_classes, solution, nodes=child_nodes)
1659
+ if isinstance(sol, TopType) and offset in offset_to_sizes:
1556
1660
  # make it an array if possible
1557
1661
  elem_size = min(offset_to_sizes[offset])
1558
1662
  array_size = offset_to_maxsize[offset]
@@ -1566,12 +1670,14 @@ class SimpleSolver:
1566
1670
  if not fields:
1567
1671
  result = Top_
1568
1672
  for node in nodes:
1569
- self._solution_cache[node.typevar] = result
1673
+ repr_tv = equivalence_classes.get(node.typevar, node.typevar)
1674
+ self._solution_cache[repr_tv] = result
1570
1675
  solution[node.typevar] = result
1571
- elif any(off < 0 for off in fields):
1676
+ elif any(off < 0 for off in fields) or any(fld is Bottom_ for fld in fields.values()):
1572
1677
  result = self._pointer_class()(Bottom_)
1573
1678
  for node in nodes:
1574
- self._solution_cache[node.typevar] = result
1679
+ repr_tv = equivalence_classes.get(node.typevar, node.typevar)
1680
+ self._solution_cache[repr_tv] = result
1575
1681
  solution[node.typevar] = result
1576
1682
  else:
1577
1683
  # back-patch
@@ -1599,8 +1705,9 @@ class SimpleSolver:
1599
1705
  result = lower_bound if not isinstance(lower_bound, BottomType) else upper_bound
1600
1706
 
1601
1707
  for node in nodes:
1708
+ repr_tv = equivalence_classes.get(node.typevar, node.typevar)
1709
+ self._solution_cache[repr_tv] = result
1602
1710
  solution[node.typevar] = result
1603
- self._solution_cache[node.typevar] = result
1604
1711
 
1605
1712
  # import pprint
1606
1713
 
@@ -85,7 +85,7 @@ class TypeTranslator:
85
85
  internal = sim_type.SimTypeBottom(label="void").with_arch(self.arch)
86
86
  else:
87
87
  internal = self._tc2simtype(tc.basetype)
88
- return sim_type.SimTypePointer(internal).with_arch(self.arch)
88
+ return sim_type.SimTypePointer(internal, label=tc.name).with_arch(self.arch)
89
89
 
90
90
  def _translate_Pointer32(self, tc):
91
91
  if isinstance(tc.basetype, typeconsts.BottomType):
@@ -93,11 +93,11 @@ class TypeTranslator:
93
93
  internal = sim_type.SimTypeBottom(label="void").with_arch(self.arch)
94
94
  else:
95
95
  internal = self._tc2simtype(tc.basetype)
96
- return sim_type.SimTypePointer(internal).with_arch(self.arch)
96
+ return sim_type.SimTypePointer(internal, label=tc.name).with_arch(self.arch)
97
97
 
98
98
  def _translate_Array(self, tc: typeconsts.Array) -> sim_type.SimTypeArray:
99
99
  elem_type = self._tc2simtype(tc.element)
100
- return sim_type.SimTypeArray(elem_type, length=tc.count).with_arch(self.arch)
100
+ return sim_type.SimTypeArray(elem_type, length=tc.count, label=tc.name).with_arch(self.arch)
101
101
 
102
102
  def _translate_Struct(self, tc: typeconsts.Struct):
103
103
  if tc in self.structs:
@@ -136,26 +136,26 @@ class TypeTranslator:
136
136
 
137
137
  return s
138
138
 
139
- def _translate_Int8(self, tc): # pylint:disable=unused-argument
140
- return sim_type.SimTypeChar(signed=False).with_arch(self.arch)
139
+ def _translate_Int8(self, tc):
140
+ return sim_type.SimTypeChar(signed=False, label=tc.name).with_arch(self.arch)
141
141
 
142
- def _translate_Int16(self, tc): # pylint:disable=unused-argument
143
- return sim_type.SimTypeShort(signed=False).with_arch(self.arch)
142
+ def _translate_Int16(self, tc):
143
+ return sim_type.SimTypeShort(signed=False, label=tc.name).with_arch(self.arch)
144
144
 
145
- def _translate_Int32(self, tc): # pylint:disable=unused-argument
146
- return sim_type.SimTypeInt(signed=False).with_arch(self.arch)
145
+ def _translate_Int32(self, tc):
146
+ return sim_type.SimTypeInt(signed=False, label=tc.name).with_arch(self.arch)
147
147
 
148
- def _translate_Int64(self, tc): # pylint:disable=unused-argument
149
- return sim_type.SimTypeLongLong(signed=False).with_arch(self.arch)
148
+ def _translate_Int64(self, tc):
149
+ return sim_type.SimTypeLongLong(signed=False, label=tc.name).with_arch(self.arch)
150
150
 
151
- def _translate_Int128(self, tc): # pylint:disable=unused-argument
152
- return sim_type.SimTypeInt128(signed=False).with_arch(self.arch)
151
+ def _translate_Int128(self, tc):
152
+ return sim_type.SimTypeInt128(signed=False, label=tc.name).with_arch(self.arch)
153
153
 
154
- def _translate_Int256(self, tc): # pylint:disable=unused-argument
155
- return sim_type.SimTypeInt256(signed=False).with_arch(self.arch)
154
+ def _translate_Int256(self, tc):
155
+ return sim_type.SimTypeInt256(signed=False, label=tc.name).with_arch(self.arch)
156
156
 
157
- def _translate_Int512(self, tc): # pylint:disable=unused-argument
158
- return sim_type.SimTypeInt512(signed=False).with_arch(self.arch)
157
+ def _translate_Int512(self, tc):
158
+ return sim_type.SimTypeInt512(signed=False, label=tc.name).with_arch(self.arch)
159
159
 
160
160
  def _translate_TypeVariableReference(self, tc):
161
161
  if tc.typevar in self.translated:
@@ -164,11 +164,11 @@ class TypeTranslator:
164
164
  self._has_nonexistent_ref = True
165
165
  return SimTypeTempRef(tc.typevar)
166
166
 
167
- def _translate_Float32(self, tc: typeconsts.Float32) -> sim_type.SimTypeFloat: # pylint:disable=unused-argument
168
- return sim_type.SimTypeFloat().with_arch(self.arch)
167
+ def _translate_Float32(self, tc: typeconsts.Float32) -> sim_type.SimTypeFloat:
168
+ return sim_type.SimTypeFloat(label=tc.name).with_arch(self.arch)
169
169
 
170
- def _translate_Float64(self, tc: typeconsts.Float64) -> sim_type.SimTypeDouble: # pylint:disable=unused-argument
171
- return sim_type.SimTypeDouble().with_arch(self.arch)
170
+ def _translate_Float64(self, tc: typeconsts.Float64) -> sim_type.SimTypeDouble:
171
+ return sim_type.SimTypeDouble(label=tc.name).with_arch(self.arch)
172
172
 
173
173
  #
174
174
  # Backpatching
@@ -197,25 +197,25 @@ class TypeTranslator:
197
197
  #
198
198
 
199
199
  def _translate_SimTypeInt128(self, st: sim_type.SimTypeChar) -> typeconsts.Int128:
200
- return typeconsts.Int128()
200
+ return typeconsts.Int128(name=st.label)
201
201
 
202
202
  def _translate_SimTypeInt256(self, st: sim_type.SimTypeChar) -> typeconsts.Int256:
203
- return typeconsts.Int256()
203
+ return typeconsts.Int256(name=st.label)
204
204
 
205
205
  def _translate_SimTypeInt512(self, st: sim_type.SimTypeChar) -> typeconsts.Int512:
206
- return typeconsts.Int512()
206
+ return typeconsts.Int512(name=st.label)
207
207
 
208
208
  def _translate_SimTypeInt(self, st: sim_type.SimTypeInt) -> typeconsts.Int32:
209
- return typeconsts.Int32()
209
+ return typeconsts.Int32(name=st.label)
210
210
 
211
211
  def _translate_SimTypeLong(self, st: sim_type.SimTypeLong) -> typeconsts.Int32:
212
- return typeconsts.Int32()
212
+ return typeconsts.Int32(name=st.label)
213
213
 
214
214
  def _translate_SimTypeLongLong(self, st: sim_type.SimTypeLongLong) -> typeconsts.Int64:
215
- return typeconsts.Int64()
215
+ return typeconsts.Int64(name=st.label)
216
216
 
217
217
  def _translate_SimTypeChar(self, st: sim_type.SimTypeChar) -> typeconsts.Int8:
218
- return typeconsts.Int8()
218
+ return typeconsts.Int8(name=st.label)
219
219
 
220
220
  def _translate_SimStruct(self, st: sim_type.SimStruct) -> typeconsts.Struct:
221
221
  fields = {}
@@ -224,25 +224,25 @@ class TypeTranslator:
224
224
  offset = offsets[name]
225
225
  fields[offset] = self._simtype2tc(ty)
226
226
 
227
- return typeconsts.Struct(fields=fields)
227
+ return typeconsts.Struct(fields=fields, name=st.label)
228
228
 
229
229
  def _translate_SimTypeArray(self, st: sim_type.SimTypeArray) -> typeconsts.Array:
230
230
  elem_type = self._simtype2tc(st.elem_type)
231
- return typeconsts.Array(elem_type, count=st.length)
231
+ return typeconsts.Array(elem_type, count=st.length, name=st.label)
232
232
 
233
233
  def _translate_SimTypePointer(self, st: sim_type.SimTypePointer) -> typeconsts.Pointer32 | typeconsts.Pointer64:
234
234
  base = self._simtype2tc(st.pts_to)
235
235
  if self.arch.bits == 32:
236
- return typeconsts.Pointer32(base)
236
+ return typeconsts.Pointer32(base, name=st.label)
237
237
  if self.arch.bits == 64:
238
- return typeconsts.Pointer64(base)
238
+ return typeconsts.Pointer64(base, name=st.label)
239
239
  raise TypeError(f"Unsupported pointer size {self.arch.bits}")
240
240
 
241
241
  def _translate_SimTypeFloat(self, st: sim_type.SimTypeFloat) -> typeconsts.Float32:
242
- return typeconsts.Float32()
242
+ return typeconsts.Float32(name=st.label)
243
243
 
244
244
  def _translate_SimTypeDouble(self, st: sim_type.SimTypeDouble) -> typeconsts.Float64:
245
- return typeconsts.Float64()
245
+ return typeconsts.Float64(name=st.label)
246
246
 
247
247
 
248
248
  TypeConstHandlers = {