angr 9.2.172__cp310-abi3-win_amd64.whl → 9.2.173__cp310-abi3-win_amd64.whl

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

Potentially problematic release.


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

@@ -4,15 +4,17 @@ import string
4
4
 
5
5
  from archinfo import Endness
6
6
 
7
+ from angr.ailment import BinaryOp
7
8
  from angr.ailment.expression import Const, StackBaseOffset, VirtualVariable
8
- from angr.ailment.statement import Call, Assignment
9
+ from angr.ailment.statement import Call, Assignment, Statement, Store
9
10
 
11
+ from angr.sim_type import SimTypePointer, SimTypeWideChar
10
12
  from angr.utils.endness import ail_const_to_be
11
13
  from .base import PeepholeOptimizationStmtBase
12
14
 
13
15
 
14
- ASCII_PRINTABLES = set(string.printable)
15
- ASCII_DIGITS = set(string.digits)
16
+ ASCII_PRINTABLES = {ord(x) for x in string.printable}
17
+ ASCII_DIGITS = {ord(x) for x in string.digits}
16
18
 
17
19
 
18
20
  class InlinedWstrcpy(PeepholeOptimizationStmtBase):
@@ -23,70 +25,83 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
23
25
  __slots__ = ()
24
26
 
25
27
  NAME = "Simplifying inlined wstrcpy"
26
- stmt_classes = (Assignment,)
28
+ stmt_classes = (Assignment, Store)
27
29
 
28
- def optimize(self, stmt: Assignment, stmt_idx: int | None = None, block=None, **kwargs):
30
+ def optimize(self, stmt: Assignment | Store, stmt_idx: int | None = None, block=None, **kwargs):
29
31
  if (
30
- isinstance(stmt.dst, VirtualVariable)
32
+ isinstance(stmt, Assignment)
33
+ and isinstance(stmt.dst, VirtualVariable)
31
34
  and stmt.dst.was_stack
32
35
  and isinstance(stmt.src, Const)
33
36
  and isinstance(stmt.src.value, int)
34
37
  ):
35
- r, s = self.is_integer_likely_a_wide_string(stmt.src.value, stmt.src.size, self.project.arch.memory_endness)
36
- if r:
37
- # replace it with a call to strncpy
38
- str_id = self.kb.custom_strings.allocate(s.encode("ascii"))
39
- return Call(
40
- stmt.idx,
41
- "wstrncpy",
42
- args=[
43
- StackBaseOffset(None, self.project.arch.bits, stmt.dst.stack_offset),
44
- Const(None, None, str_id, self.project.arch.bits, custom_string=True),
45
- Const(None, None, len(s), self.project.arch.bits),
46
- ],
47
- **stmt.tags,
48
- )
49
-
50
- # scan forward in the current block to find all consecutive constant stores
51
- if block is not None and stmt_idx is not None:
52
- all_constant_stores: dict[int, tuple[int, Const | None]] = self.collect_constant_stores(block, stmt_idx)
53
- if all_constant_stores:
54
- offsets = sorted(all_constant_stores.keys())
55
- next_offset = min(offsets)
56
- stride = []
57
- for offset in offsets:
58
- if next_offset is not None and offset != next_offset:
59
- next_offset = None
60
- stride = []
61
- stmt_idx_, v = all_constant_stores[offset]
62
- if v is not None:
63
- stride.append((offset, stmt_idx_, v))
64
- next_offset = offset + v.size
65
- else:
66
- next_offset = None
67
- stride = []
68
-
69
- integer, size = self.stride_to_int(stride)
70
- r, s = self.is_integer_likely_a_wide_string(integer, size, Endness.BE)
71
- if r:
72
- # we remove all involved statements whose statement IDs are greater than the current one
73
- for _, stmt_idx_, _ in reversed(stride):
74
- if stmt_idx_ <= stmt_idx:
75
- continue
76
- block.statements[stmt_idx_] = None
77
- block.statements = [ss for ss in block.statements if ss is not None]
78
-
79
- str_id = self.kb.custom_strings.allocate(s.encode("ascii"))
80
- return Call(
81
- stmt.idx,
82
- "wstrncpy",
83
- args=[
84
- StackBaseOffset(None, self.project.arch.bits, stmt.dst.stack_offset),
85
- Const(None, None, str_id, self.project.arch.bits, custom_string=True),
86
- Const(None, None, len(s), self.project.arch.bits),
87
- ],
88
- **stmt.tags,
89
- )
38
+ dst = StackBaseOffset(None, self.project.arch.bits, stmt.dst.stack_offset)
39
+ value_size = stmt.src.size
40
+ value = stmt.src.value
41
+ elif isinstance(stmt, Store) and isinstance(stmt.data, Const) and isinstance(stmt.data.value, int):
42
+ dst = stmt.addr
43
+ value_size = stmt.data.size
44
+ value = stmt.data.value
45
+ else:
46
+ return None
47
+
48
+ r, s = self.is_integer_likely_a_wide_string(value, value_size, self.project.arch.memory_endness)
49
+ if r:
50
+ # replace it with a call to strncpy
51
+ str_id = self.kb.custom_strings.allocate(s)
52
+ wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
53
+ return Call(
54
+ stmt.idx,
55
+ "wstrncpy",
56
+ args=[
57
+ dst,
58
+ Const(None, None, str_id, self.project.arch.bits, custom_string=True, type=wstr_type),
59
+ Const(None, None, len(s) // 2, self.project.arch.bits),
60
+ ],
61
+ **stmt.tags,
62
+ )
63
+
64
+ # scan forward in the current block to find all consecutive constant stores
65
+ if block is not None and stmt_idx is not None:
66
+ all_constant_stores: dict[int, tuple[int, Const | None]] = self.collect_constant_stores(block, stmt_idx)
67
+ if all_constant_stores:
68
+ offsets = sorted(all_constant_stores.keys())
69
+ next_offset = min(offsets)
70
+ stride = []
71
+ for offset in offsets:
72
+ if next_offset is not None and offset != next_offset:
73
+ next_offset = None
74
+ stride = []
75
+ stmt_idx_, v = all_constant_stores[offset]
76
+ if v is not None:
77
+ stride.append((offset, stmt_idx_, v))
78
+ next_offset = offset + v.size
79
+ else:
80
+ next_offset = None
81
+ stride = []
82
+
83
+ integer, size = self.stride_to_int(stride)
84
+ r, s = self.is_integer_likely_a_wide_string(integer, size, Endness.BE, min_length=3)
85
+ if r:
86
+ # we remove all involved statements whose statement IDs are greater than the current one
87
+ for _, stmt_idx_, _ in reversed(stride):
88
+ if stmt_idx_ <= stmt_idx:
89
+ continue
90
+ block.statements[stmt_idx_] = None
91
+ block.statements = [ss for ss in block.statements if ss is not None]
92
+
93
+ str_id = self.kb.custom_strings.allocate(s)
94
+ wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
95
+ return Call(
96
+ stmt.idx,
97
+ "wstrncpy",
98
+ args=[
99
+ dst,
100
+ Const(None, None, str_id, self.project.arch.bits, custom_string=True, type=wstr_type),
101
+ Const(None, None, len(s) // 2, self.project.arch.bits),
102
+ ],
103
+ **stmt.tags,
104
+ )
90
105
 
91
106
  return None
92
107
 
@@ -103,68 +118,131 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
103
118
 
104
119
  def collect_constant_stores(self, block, starting_stmt_idx: int) -> dict[int, tuple[int, Const | None]]:
105
120
  r = {}
121
+ expected_store_varid: int | None = None
122
+ starting_stmt = block.statements[starting_stmt_idx]
123
+ if (
124
+ isinstance(starting_stmt, Assignment)
125
+ and isinstance(starting_stmt.dst, VirtualVariable)
126
+ and starting_stmt.dst.was_stack
127
+ and isinstance(starting_stmt.dst.stack_offset, int)
128
+ ):
129
+ expected_type = "stack"
130
+ elif isinstance(starting_stmt, Store):
131
+ if isinstance(starting_stmt.addr, VirtualVariable):
132
+ expected_store_varid = starting_stmt.addr.varid
133
+ elif (
134
+ isinstance(starting_stmt.addr, BinaryOp)
135
+ and starting_stmt.addr.op == "Add"
136
+ and isinstance(starting_stmt.addr.operands[0], VirtualVariable)
137
+ and isinstance(starting_stmt.addr.operands[1], Const)
138
+ ):
139
+ expected_store_varid = starting_stmt.addr.operands[0].varid
140
+ else:
141
+ expected_store_varid = None
142
+ expected_type = "store"
143
+ else:
144
+ return r
145
+
106
146
  for idx, stmt in enumerate(block.statements):
107
147
  if idx < starting_stmt_idx:
108
148
  continue
109
149
  if (
110
- isinstance(stmt, Assignment)
150
+ expected_type == "stack"
151
+ and isinstance(stmt, Assignment)
111
152
  and isinstance(stmt.dst, VirtualVariable)
112
153
  and stmt.dst.was_stack
113
154
  and isinstance(stmt.dst.stack_offset, int)
114
155
  ):
115
- if isinstance(stmt.src, Const):
116
- r[stmt.dst.stack_offset] = idx, ail_const_to_be(stmt.src, self.project.arch.memory_endness)
156
+ offset = stmt.dst.stack_offset
157
+ value = (
158
+ ail_const_to_be(stmt.src, self.project.arch.memory_endness) if isinstance(stmt.src, Const) else None
159
+ )
160
+ elif expected_type == "store" and isinstance(stmt, Store):
161
+ if isinstance(stmt.addr, VirtualVariable) and stmt.addr.varid == expected_store_varid:
162
+ offset = 0
163
+ elif (
164
+ isinstance(stmt.addr, BinaryOp)
165
+ and stmt.addr.op == "Add"
166
+ and isinstance(stmt.addr.operands[0], VirtualVariable)
167
+ and isinstance(stmt.addr.operands[1], Const)
168
+ and stmt.addr.operands[0].varid == expected_store_varid
169
+ ):
170
+ offset = stmt.addr.operands[1].value
117
171
  else:
118
- r[stmt.dst.stack_offset] = idx, None
172
+ offset = None
173
+ value = (
174
+ ail_const_to_be(stmt.data, self.project.arch.memory_endness)
175
+ if isinstance(stmt.data, Const)
176
+ else None
177
+ )
178
+ else:
179
+ continue
180
+
181
+ if offset is not None:
182
+ r[offset] = idx, value
119
183
 
120
184
  return r
121
185
 
122
186
  @staticmethod
123
- def even_offsets_are_zero(lst: list[str]) -> bool:
124
- return all(ch == "\x00" for i, ch in enumerate(lst) if i % 2 == 0)
187
+ def even_offsets_are_zero(lst: list[int]) -> bool:
188
+ if len(lst) >= 2 and lst[-1] == 0 and lst[-2] == 0:
189
+ lst = lst[:-2]
190
+ return all((ch == 0 if i % 2 == 0 else ch != 0) for i, ch in enumerate(lst))
125
191
 
126
192
  @staticmethod
127
- def odd_offsets_are_zero(lst: list[str]) -> bool:
128
- return all(ch == "\x00" for i, ch in enumerate(lst) if i % 2 == 1)
193
+ def odd_offsets_are_zero(lst: list[int]) -> bool:
194
+ if len(lst) >= 2 and lst[-1] == 0 and lst[-2] == 0:
195
+ lst = lst[:-2]
196
+ return all((ch == 0 if i % 2 == 1 else ch != 0) for i, ch in enumerate(lst))
129
197
 
130
198
  @staticmethod
131
199
  def is_integer_likely_a_wide_string(
132
200
  v: int, size: int, endness: Endness, min_length: int = 4
133
- ) -> tuple[bool, str | None]:
201
+ ) -> tuple[bool, bytes | None]:
134
202
  # we need at least four bytes of printable characters
135
203
 
136
- chars = []
204
+ chars: list[int] = []
137
205
  if endness == Endness.LE:
138
206
  while v != 0:
139
207
  byt = v & 0xFF
140
- if byt != 0 and chr(byt) not in ASCII_PRINTABLES:
208
+ if byt != 0 and byt not in ASCII_PRINTABLES:
141
209
  return False, None
142
- chars.append(chr(byt))
210
+ chars.append(byt)
143
211
  v >>= 8
212
+ if len(chars) % 2 == 1:
213
+ chars.append(0)
144
214
 
145
215
  elif endness == Endness.BE:
146
216
  for _ in range(size):
147
217
  byt = v & 0xFF
148
218
  v >>= 8
149
- if byt != 0 and chr(byt) not in ASCII_PRINTABLES:
219
+ if byt != 0 and byt not in ASCII_PRINTABLES:
150
220
  return False, None
151
- chars.append(chr(byt))
221
+ chars.append(byt)
152
222
  chars.reverse()
153
223
  else:
154
224
  # unsupported endness
155
225
  return False, None
156
226
 
157
- if InlinedWstrcpy.even_offsets_are_zero(chars):
158
- chars = [ch for i, ch in enumerate(chars) if i % 2 == 1]
159
- elif InlinedWstrcpy.odd_offsets_are_zero(chars):
160
- chars = [ch for i, ch in enumerate(chars) if i % 2 == 0]
161
- else:
227
+ if not (InlinedWstrcpy.even_offsets_are_zero(chars) or InlinedWstrcpy.odd_offsets_are_zero(chars)):
162
228
  return False, None
163
229
 
164
- if chars and chars[-1] == "\x00":
230
+ if chars and len(chars) >= 2 and chars[-1] == 0 and chars[-2] == 0:
165
231
  chars = chars[:-1]
166
- if len(chars) >= min_length and all(ch in ASCII_PRINTABLES for ch in chars):
167
- if len(chars) <= 4 and all(ch in ASCII_DIGITS for ch in chars):
232
+ if len(chars) >= min_length * 2 and all((ch == 0 or ch in ASCII_PRINTABLES) for ch in chars):
233
+ if len(chars) <= 4 * 2 and all((ch == 0 or ch in ASCII_DIGITS) for ch in chars):
168
234
  return False, None
169
- return True, "".join(chars)
235
+ return True, bytes(chars)
170
236
  return False, None
237
+
238
+ @staticmethod
239
+ def is_inlined_wstrncpy(stmt: Statement) -> bool:
240
+ return (
241
+ isinstance(stmt, Call)
242
+ and isinstance(stmt.target, str)
243
+ and stmt.target == "wstrncpy"
244
+ and stmt.args is not None
245
+ and len(stmt.args) == 3
246
+ and isinstance(stmt.args[1], Const)
247
+ and hasattr(stmt.args[1], "custom_string")
248
+ )
@@ -0,0 +1,113 @@
1
+ # pylint:disable=arguments-differ
2
+ from __future__ import annotations
3
+
4
+ from angr.ailment.expression import Expression, BinaryOp, Const, Register, StackBaseOffset, UnaryOp, VirtualVariable
5
+ from angr.ailment.statement import Call, Store
6
+
7
+ from angr.sim_type import SimTypePointer, SimTypeWideChar
8
+ from .base import PeepholeOptimizationMultiStmtBase
9
+ from .inlined_wstrcpy import InlinedWstrcpy
10
+
11
+
12
+ class InlinedWstrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
13
+ """
14
+ Consolidate multiple inlined wstrcpy/wstrncpy calls.
15
+ """
16
+
17
+ __slots__ = ()
18
+
19
+ NAME = "Consolidate multiple inlined wstrncpy calls"
20
+ stmt_classes = ((Call, Call), (Call, Store))
21
+
22
+ def optimize( # type:ignore
23
+ self, stmts: list[Call], stmt_idx: int | None = None, block=None, **kwargs
24
+ ): # pylint:disable=unused-argument
25
+ last_stmt, stmt = stmts
26
+ if InlinedWstrcpy.is_inlined_wstrncpy(last_stmt):
27
+ assert last_stmt.args is not None
28
+ assert self.kb is not None
29
+ s_last: bytes = self.kb.custom_strings[last_stmt.args[1].value]
30
+ addr_last = last_stmt.args[0]
31
+ new_str = None # will be set if consolidation should happen
32
+
33
+ if isinstance(stmt, Call) and InlinedWstrcpy.is_inlined_wstrncpy(stmt):
34
+ assert stmt.args is not None
35
+ # consolidating two calls
36
+ s_curr: bytes = self.kb.custom_strings[stmt.args[1].value]
37
+ addr_curr = stmt.args[0]
38
+ # determine if the two addresses are consecutive
39
+ delta = self._get_delta(addr_last, addr_curr)
40
+ if delta is not None and delta == len(s_last):
41
+ # consolidate both calls!
42
+ new_str = s_last + s_curr
43
+ elif isinstance(stmt, Store) and isinstance(stmt.data, Const) and isinstance(stmt.data.value, int):
44
+ # consolidating a call and a store, in case the store statement is storing the suffix of a string (but
45
+ # the suffix is too short to qualify an inlined strcpy optimization)
46
+ addr_curr = stmt.addr
47
+ delta = self._get_delta(addr_last, addr_curr)
48
+ if delta is not None and delta == len(s_last):
49
+ if stmt.size == 2 and stmt.data.value == 0:
50
+ # it's probably the terminating null byte
51
+ r, s = True, b"\x00\x00"
52
+ else:
53
+ r, s = InlinedWstrcpy.is_integer_likely_a_wide_string(
54
+ stmt.data.value, stmt.size, stmt.endness, min_length=1 # type:ignore
55
+ )
56
+ if r and s is not None:
57
+ new_str = s_last + s
58
+
59
+ if new_str is not None:
60
+ assert self.project is not None
61
+ wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
62
+ if new_str.endswith(b"\x00\x00"):
63
+ call_name = "wstrcpy"
64
+ new_str_idx = self.kb.custom_strings.allocate(new_str[:-2])
65
+ args = [
66
+ last_stmt.args[0],
67
+ Const(None, None, new_str_idx, last_stmt.args[0].bits, custom_string=True, type=wstr_type),
68
+ ]
69
+ prototype = None
70
+ else:
71
+ call_name = "wstrncpy"
72
+ new_str_idx = self.kb.custom_strings.allocate(new_str)
73
+ args = [
74
+ last_stmt.args[0],
75
+ Const(None, None, new_str_idx, last_stmt.args[0].bits, custom_string=True, type=wstr_type),
76
+ Const(None, None, len(new_str) // 2, self.project.arch.bits),
77
+ ]
78
+ prototype = None
79
+
80
+ return [Call(stmt.idx, call_name, args=args, prototype=prototype, **stmt.tags)]
81
+
82
+ return None
83
+
84
+ @staticmethod
85
+ def _parse_addr(addr: Expression) -> tuple[Expression, int]:
86
+ if isinstance(addr, Register):
87
+ return addr, 0
88
+ if isinstance(addr, StackBaseOffset):
89
+ return StackBaseOffset(None, addr.bits, 0), addr.offset
90
+ if (
91
+ isinstance(addr, UnaryOp)
92
+ and addr.op == "Reference"
93
+ and isinstance(addr.operand, VirtualVariable)
94
+ and addr.operand.was_stack
95
+ ):
96
+ return StackBaseOffset(None, addr.bits, 0), addr.operand.stack_offset
97
+ if isinstance(addr, BinaryOp):
98
+ if addr.op == "Add" and isinstance(addr.operands[1], Const) and isinstance(addr.operands[1].value, int):
99
+ base_0, offset_0 = InlinedWstrcpyConsolidation._parse_addr(addr.operands[0])
100
+ return base_0, offset_0 + addr.operands[1].value
101
+ if addr.op == "Sub" and isinstance(addr.operands[1], Const) and isinstance(addr.operands[1].value, int):
102
+ base_0, offset_0 = InlinedWstrcpyConsolidation._parse_addr(addr.operands[0])
103
+ return base_0, offset_0 - addr.operands[1].value
104
+
105
+ return addr, 0
106
+
107
+ @staticmethod
108
+ def _get_delta(addr_0: Expression, addr_1: Expression) -> int | None:
109
+ base_0, offset_0 = InlinedWstrcpyConsolidation._parse_addr(addr_0)
110
+ base_1, offset_1 = InlinedWstrcpyConsolidation._parse_addr(addr_1)
111
+ if base_0.likes(base_1):
112
+ return offset_1 - offset_0
113
+ return None
@@ -2230,52 +2230,68 @@ class CConstant(CExpression):
2230
2230
  return f'{prefix}"{base_str}"'
2231
2231
 
2232
2232
  def c_repr_chunks(self, indent=0, asexpr=False):
2233
+
2234
+ def _default_output(v) -> str | None:
2235
+ if isinstance(v, MemoryData) and v.sort == MemoryDataSort.String:
2236
+ return CConstant.str_to_c_str(v.content.decode("utf-8"))
2237
+ if isinstance(v, Function):
2238
+ return get_cpp_function_name(v.demangled_name)
2239
+ if isinstance(v, str):
2240
+ return CConstant.str_to_c_str(v)
2241
+ if isinstance(v, bytes):
2242
+ return CConstant.str_to_c_str(v.replace(b"\x00", b"").decode("utf-8"))
2243
+ return None
2244
+
2233
2245
  if self.collapsed:
2234
2246
  yield "...", self
2235
2247
  return
2236
2248
 
2237
- # default priority: string references -> variables -> other reference values
2238
2249
  if self.reference_values is not None:
2239
- for _ty, v in self.reference_values.items(): # pylint:disable=unused-variable
2240
- if isinstance(v, MemoryData) and v.sort == MemoryDataSort.String:
2241
- yield CConstant.str_to_c_str(v.content.decode("utf-8")), self
2242
- return
2243
- elif isinstance(v, Function):
2244
- yield get_cpp_function_name(v.demangled_name), self
2250
+ if self._type is not None and self._type in self.reference_values:
2251
+ if isinstance(self._type, SimTypeInt):
2252
+ if isinstance(self.reference_values[self._type], int):
2253
+ yield self.fmt_int(self.reference_values[self._type]), self
2254
+ return
2255
+ yield hex(self.reference_values[self._type]), self
2245
2256
  return
2246
- elif isinstance(v, str):
2257
+ elif isinstance(self._type, SimTypePointer) and isinstance(self._type.pts_to, SimTypeChar):
2258
+ refval = self.reference_values[self._type]
2259
+ if isinstance(refval, MemoryData):
2260
+ v = refval.content.decode("utf-8")
2261
+ elif isinstance(refval, bytes):
2262
+ v = refval.decode("latin1")
2263
+ else:
2264
+ # it must be a string
2265
+ v = refval
2266
+ assert isinstance(v, str)
2247
2267
  yield CConstant.str_to_c_str(v), self
2248
2268
  return
2249
- elif isinstance(v, bytes):
2250
- yield CConstant.str_to_c_str(v.replace(b"\x00", b"").decode("utf-8")), self
2269
+ elif isinstance(self._type, SimTypePointer) and isinstance(self._type.pts_to, SimTypeWideChar):
2270
+ refval = self.reference_values[self._type]
2271
+ v = (
2272
+ refval.content.decode("utf_16_le")
2273
+ if isinstance(refval, MemoryData)
2274
+ else refval.decode("utf_16_le")
2275
+ ) # it's a string
2276
+ yield CConstant.str_to_c_str(v, prefix="L"), self
2251
2277
  return
2252
-
2253
- if self.reference_values is not None and self._type is not None and self._type in self.reference_values:
2254
- if isinstance(self._type, SimTypeInt):
2255
- if isinstance(self.reference_values[self._type], int):
2256
- yield self.fmt_int(self.reference_values[self._type]), self
2257
- return
2258
- yield hex(self.reference_values[self._type]), self
2259
- elif isinstance(self._type, SimTypePointer) and isinstance(self._type.pts_to, SimTypeChar):
2260
- refval = self.reference_values[self._type]
2261
- if isinstance(refval, MemoryData):
2262
- v = refval.content.decode("utf-8")
2263
2278
  else:
2264
- # it's a string
2265
- v = refval
2266
- assert isinstance(v, str)
2267
- yield CConstant.str_to_c_str(v), self
2268
- elif isinstance(self._type, SimTypePointer) and isinstance(self._type.pts_to, SimTypeWideChar):
2269
- refval = self.reference_values[self._type]
2270
- v = refval.content.decode("utf_16_le") if isinstance(refval, MemoryData) else refval # it's a string
2271
- yield CConstant.str_to_c_str(v, prefix="L"), self
2272
- else:
2273
- if isinstance(self.reference_values[self._type], int):
2274
- yield self.fmt_int(self.reference_values[self._type]), self
2279
+ if isinstance(self.reference_values[self._type], int):
2280
+ yield self.fmt_int(self.reference_values[self._type]), self
2281
+ return
2282
+ o = _default_output(self.reference_values[self.type])
2283
+ if o is not None:
2284
+ yield o, self
2285
+ return
2286
+
2287
+ # default priority: string references -> variables -> other reference values
2288
+ for _ty, v in self.reference_values.items(): # pylint:disable=unused-variable
2289
+ o = _default_output(v)
2290
+ if o is not None:
2291
+ yield o, self
2275
2292
  return
2276
- yield self.reference_values[self.type], self
2277
2293
 
2278
- elif isinstance(self.value, int) and self.value == 0 and isinstance(self.type, SimTypePointer):
2294
+ if isinstance(self.value, int) and self.value == 0 and isinstance(self.type, SimTypePointer):
2279
2295
  # print NULL instead
2280
2296
  yield "NULL", self
2281
2297
 
@@ -3622,10 +3638,10 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3622
3638
  if type_ is None and hasattr(expr, "type"):
3623
3639
  type_ = expr.type
3624
3640
 
3625
- if type_ is None and reference_values is None and hasattr(expr, "reference_values"):
3641
+ if reference_values is None and hasattr(expr, "reference_values"):
3626
3642
  reference_values = expr.reference_values.copy()
3627
- if len(reference_values) == 1: # type: ignore
3628
- type_ = next(iter(reference_values)) # type: ignore
3643
+ if type_ is None and reference_values is not None and len(reference_values) == 1: # type: ignore
3644
+ type_ = next(iter(reference_values)) # type: ignore
3629
3645
 
3630
3646
  if reference_values is None:
3631
3647
  reference_values = {}
@@ -3714,10 +3730,10 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3714
3730
  offset = getattr(expr, "reference_variable_offset", 0)
3715
3731
  var_access = self._access_constant_offset_reference(self._get_variable_reference(cvar), offset, None)
3716
3732
 
3717
- if var_access is not None and expr.value >= self.min_data_addr:
3718
- return var_access
3719
-
3720
- reference_values["offset"] = var_access
3733
+ if var_access is not None:
3734
+ if expr.value >= self.min_data_addr:
3735
+ return var_access
3736
+ reference_values["offset"] = var_access
3721
3737
  return CConstant(expr.value, type_, reference_values=reference_values, tags=expr.tags, codegen=self)
3722
3738
 
3723
3739
  def _handle_Expr_UnaryOp(self, expr, **kwargs):
@@ -30,6 +30,9 @@ class RegVVarPredicate:
30
30
  self.arch = arch
31
31
 
32
32
  def _get_call_clobbered_regs(self, stmt: Call) -> set[int]:
33
+ if isinstance(stmt.target, str):
34
+ # pseudo calls do not clobber any registers
35
+ return set()
33
36
  cc = stmt.calling_convention
34
37
  if cc is None:
35
38
  # get the default calling convention