Skip to content

Commit 9fda727

Browse files
committed
TYP: infrastructure for importing and displaying type constraints
1 parent 7442115 commit 9fda727

20 files changed

+162
-15
lines changed

chb/app/BasicBlock.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ def to_string(
336336
bytes: bool = False,
337337
opcodetxt: bool = True,
338338
opcodewidth: int = 25,
339+
typingrules: bool = False,
339340
sp: bool = True) -> str:
340341
...
341342

chb/app/CHVersion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
chbversion: str = "0.3.0-20250714"
1+
chbversion: str = "0.3.0-20250721"

chb/app/Function.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# ------------------------------------------------------------------------------
55
# The MIT License (MIT)
66
#
7-
# Copyright (c) 2021-2024 Aarno Labs LLC
7+
# Copyright (c) 2021-2025 Aarno Labs LLC
88
#
99
# Permission is hereby granted, free of charge, to any person obtaining a copy
1010
# of this software and associated documentation files (the "Software"), to deal
@@ -604,6 +604,7 @@ def to_string(
604604
opcodewidth: int = 25, # alignment width for opcode text
605605
sp: bool = True,
606606
proofobligations: bool = False,
607+
typingrules: bool = False,
607608
stacklayout: bool = False) -> str:
608609
...
609610

chb/app/Instruction.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ def to_string(
345345
bytes: bool = False,
346346
opcodetxt: bool = True,
347347
opcodewidth: int = 25,
348+
typingrules: bool = False,
348349
sp: bool = True) -> str:
349350
...
350351

chb/arm/ARMBlock.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# ------------------------------------------------------------------------------
55
# The MIT License (MIT)
66
#
7-
# Copyright (c) 2021-2024 Aarno Labs LLC
7+
# Copyright (c) 2021-2025 Aarno Labs LLC
88
#
99
# Permission is hereby granted, free of charge, to any person obtaining a copy
1010
# of this software and associated documentation files (the "Software"), to deal
@@ -41,6 +41,7 @@
4141
from chb.invariants.XXpr import XXpr
4242

4343
if TYPE_CHECKING:
44+
from chb.app.AppAccess import AppAccess
4445
from chb.arm.ARMFunction import ARMFunction
4546

4647

@@ -55,6 +56,10 @@ def __init__(self, armf: "ARMFunction", xnode: ET.Element) -> None:
5556
def armfunction(self) -> "ARMFunction":
5657
return self._armf
5758

59+
@property
60+
def app(self) -> "AppAccess":
61+
return self.armfunction.app
62+
5863
@property
5964
def armdictionary(self) -> ARMDictionary:
6065
return self.armfunction.armdictionary
@@ -74,13 +79,15 @@ def to_string(
7479
bytes: bool = False,
7580
opcodetxt: bool = True,
7681
opcodewidth: int = 40,
82+
typingrules: bool = False,
7783
sp: bool = True) -> str:
7884
lines: List[str] = []
7985
for (ia, instr) in sorted(self.instructions.items()):
8086
pinstr = instr.to_string(
8187
bytes=bytes,
8288
opcodetxt=opcodetxt,
8389
opcodewidth=opcodewidth,
90+
typingrules=typingrules,
8491
sp=sp)
8592
lines.append(str(ia).rjust(10) + " " + pinstr)
8693
return "\n".join(lines)

chb/arm/ARMFunction.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# ------------------------------------------------------------------------------
55
# The MIT License (MIT)
66
#
7-
# Copyright (c) 2021-2024 Aarno Labs LLC
7+
# Copyright (c) 2021-2025 Aarno Labs LLC
88
#
99
# Permission is hereby granted, free of charge, to any person obtaining a copy
1010
# of this software and associated documentation files (the "Software"), to deal
@@ -59,6 +59,7 @@
5959
import chb.util.fileutil as UF
6060

6161
if TYPE_CHECKING:
62+
from chb.app.AppAccess import AppAccess
6263
from chb.cmdline.PatchResults import PatchEvent
6364

6465

@@ -93,6 +94,10 @@ def __init__(
9394
def armdictionary(self) -> ARMDictionary:
9495
return self._armd
9596

97+
@property
98+
def app(self) -> "AppAccess":
99+
return self.armdictionary.app
100+
96101
@property
97102
def armfunctiondictionary(self) -> FunctionDictionary:
98103
if self._armfnd is None:
@@ -289,6 +294,7 @@ def to_string(
289294
opcodewidth: int = 40,
290295
sp: bool = True,
291296
proofobligations: bool = False,
297+
typingrules: bool = False,
292298
stacklayout: bool = False) -> str:
293299
lines: List[str] = []
294300
if stacklayout:
@@ -311,6 +317,7 @@ def to_string(
311317
bytes=bytes,
312318
opcodetxt=opcodetxt,
313319
opcodewidth=opcodewidth,
320+
typingrules=typingrules,
314321
sp=sp))
315322
lines.append("-" * 80)
316323
if self.has_jumptable(self.blocks[b].lastaddr):

chb/arm/ARMInstruction.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161

6262

6363
if TYPE_CHECKING:
64+
from chb.app.AppAccess import AppAccess
6465
from chb.arm.ARMBlock import ARMBlock
6566
from chb.arm.ARMFunction import ARMFunction
6667
from chb.arm.ARMJumpTable import ARMJumpTable
@@ -88,6 +89,10 @@ def armfunction(self) -> "ARMFunction":
8889
def armdictionary(self) -> ARMDictionary:
8990
return self.armblock.armdictionary
9091

92+
@property
93+
def app(self) -> "AppAccess":
94+
return self.armfunction.app
95+
9196
@property
9297
def armfunctiondictionary(self) -> "FunctionDictionary":
9398
return self.armfunction.armfunctiondictionary
@@ -434,13 +439,24 @@ def to_string(
434439
bytes: bool = False,
435440
opcodetxt: bool = True,
436441
opcodewidth: int = 40,
442+
typingrules: bool = False,
437443
sp: bool = False) -> str:
444+
if typingrules:
445+
lines: List[str] = []
446+
rulesapplied = self.app.type_constraints.rules_applied_to_instruction(
447+
self.armfunction.faddr, self.iaddr)
448+
for r in sorted(str(r) for r in rulesapplied):
449+
lines.append(" " + str(r))
438450
try:
439451
pbytes = self.bytestring.ljust(10) + " " if bytes else ""
440452
pesp = str(self.stackpointer_offset) + " " if sp else ""
441453
popcode = (
442454
self.opcodetext.ljust(opcodewidth) if opcodetxt else "")
443-
return pesp + pbytes + popcode + self.annotation
455+
codeline = pesp + pbytes + popcode + self.annotation
456+
if len(lines) > 0:
457+
return codeline + "\n " + "\n ".join(lines)
458+
else:
459+
return codeline
444460
except Exception as e:
445461
print(
446462
"Error in instruction: "

chb/bctypes/TypeConstraintStore.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2525
# SOFTWARE.
2626
# ------------------------------------------------------------------------------
27+
import xml.etree.ElementTree as ET
2728

2829
from typing import cast, Dict, List, Optional, Set, TYPE_CHECKING
2930

3031
import chb.bctypes.TypeConstraint as TC
3132

33+
import chb.util.fileutil as UF
34+
3235
if TYPE_CHECKING:
3336
from chb.app.AppAccess import AppAccess
3437

@@ -231,18 +234,96 @@ def __str__(self) -> str:
231234
return "\n".join(lines)
232235

233236

237+
class TypingRule:
238+
239+
def __init__(self, tcstore: "TypeConstraintStore", xr: ET.Element) -> None:
240+
self._tcstore = tcstore
241+
self._xr = xr
242+
243+
@property
244+
def tcstore(self) -> "TypeConstraintStore":
245+
return self._tcstore
246+
247+
@property
248+
def app(self) -> "AppAccess":
249+
return self.tcstore.app
250+
251+
@property
252+
def iaddr(self) -> str:
253+
return self._xr.get("loc", "0x0")
254+
255+
@property
256+
def rulename(self) -> str:
257+
return self._xr.get("rule", "?")
258+
259+
@property
260+
def typeconstraint(self) -> TC.TypeConstraint:
261+
ix = self._xr.get("tc-ix")
262+
if ix is not None:
263+
return self.app.tcdictionary.type_constraint(int(ix))
264+
else:
265+
raise UF.CHBError("Type constraint without tc index")
266+
267+
def __str__(self) -> str:
268+
ix = self._xr.get("tc-ix")
269+
return self.rulename + " " + str(ix) + ":" + str(self.typeconstraint)
270+
271+
234272
class TypeConstraintStore:
273+
"""Global store for type constraints.
274+
275+
Note: The use of this object is currently somewhat conflicted. It was
276+
originally intended for global use, to recover function signatures for
277+
an entire binary. More recently, however, it has been mainly used to
278+
perform type construction for all local variables (registers and stack)
279+
within individual functions to support lifting. This latest use in its
280+
present form does not scale to an entire binary. For now the scaling
281+
issue is solved by resetting the store after each function, which
282+
essentially means that the store, when saved, only holds the constraints
283+
for a single function (the latest processed).
284+
"""
235285

236286
def __init__(self, app: "AppAccess") -> None:
237287
self._app = app
238288
self._constraints: Optional[List[TC.TypeConstraint]] = None
239289
self._functionconstraints: Dict[str, FunctionTypeConstraints] = {}
240290
self._functionregconstraints: Dict[str, FunctionRegisterConstraints] = {}
291+
self._rules_applied: Optional[Dict[str, Dict[str, List[TypingRule]]]] = None
241292

242293
@property
243294
def app(self) -> "AppAccess":
244295
return self._app
245296

297+
@property
298+
def rules_applied(self) -> Dict[str, Dict[str, List[TypingRule]]]:
299+
if self._rules_applied is None:
300+
self._rules_applied = {}
301+
tcstore = UF.get_typeconstraint_store_xnode(
302+
self.app.path, self.app.filename)
303+
if tcstore is not None:
304+
rules = tcstore.find("rules-applied")
305+
if rules is not None:
306+
for xf in rules.findall("function"):
307+
faddr = xf.get("faddr", "0x0")
308+
self._rules_applied[faddr] = {}
309+
for xr in xf.findall("rule"):
310+
rule = TypingRule(self, xr)
311+
self._rules_applied[faddr].setdefault(rule.iaddr, [])
312+
self._rules_applied[faddr][rule.iaddr].append(rule)
313+
314+
return self._rules_applied
315+
316+
def rules_applied_to_instruction(
317+
self, faddr: str, iaddr: str) -> List[TypingRule]:
318+
result: List[TypingRule] = []
319+
if faddr in self.rules_applied:
320+
if iaddr in self.rules_applied[faddr]:
321+
for r in self.rules_applied[faddr][iaddr]:
322+
if r.typeconstraint.is_var_constraint:
323+
continue
324+
result.append(r)
325+
return result
326+
246327
@property
247328
def constraints(self) -> List[TC.TypeConstraint]:
248329
if self._constraints is None:

chb/cmdline/chkx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,10 @@ def parse() -> argparse.Namespace:
953953
"--proofobligations",
954954
action="store_true",
955955
help="include proofobligations at the top of the function")
956+
resultsfunction.add_argument(
957+
"--typingrules",
958+
action="store_true",
959+
help="include typing rules applied after each instruction")
956960
resultsfunction.add_argument(
957961
"--loglevel", "-log",
958962
choices=UL.LogLevel.options(),

chb/cmdline/commandutil.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,7 @@ def results_function(args: argparse.Namespace) -> NoReturn:
10991099
txtoutput: bool = not xjson
11001100
stacklayout: bool = args.stacklayout
11011101
proofobligations: bool = args.proofobligations
1102+
typingrules: bool = args.typingrules
11021103
loglevel: str = args.loglevel
11031104
logfilename: Optional[str] = args.logfilename
11041105
logfilemode: str = args.logfilemode
@@ -1161,6 +1162,7 @@ def results_function(args: argparse.Namespace) -> NoReturn:
11611162
sp=True,
11621163
opcodetxt=True,
11631164
proofobligations=proofobligations,
1165+
typingrules=typingrules,
11641166
stacklayout=stacklayout,
11651167
opcodewidth=opcodewidth))
11661168
except UF.CHBError as e:

0 commit comments

Comments
 (0)