Skip to content

Commit 383ec0b

Browse files
committed
APP: make formatstring specs available
1 parent 2b0370c commit 383ec0b

6 files changed

Lines changed: 190 additions & 6 deletions

File tree

chb/api/FormatStringSpec.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# ------------------------------------------------------------------------------
2+
# CodeHawk Binary Analyzer
3+
# Author: Henny Sipma
4+
# ------------------------------------------------------------------------------
5+
# The MIT License (MIT)
6+
#
7+
# Copyright (c) 2026 Aarno Labs LLC
8+
#
9+
# Permission is hereby granted, free of charge, to any person obtaining a copy
10+
# of this software and associated documentation files (the "Software"), to deal
11+
# in the Software without restriction, including without limitation the rights
12+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
# copies of the Software, and to permit persons to whom the Software is
14+
# furnished to do so, subject to the following conditions:
15+
#
16+
# The above copyright notice and this permission notice shall be included in all
17+
# copies or substantial portions of the Software.
18+
#
19+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
# SOFTWARE.
26+
# ------------------------------------------------------------------------------
27+
28+
from dataclasses import dataclass
29+
from typing import List, Optional, TYPE_CHECKING
30+
31+
from chb.api.InterfaceDictionaryRecord import InterfaceDictionaryRecord
32+
33+
import chb.util.fileutil as UF
34+
35+
from chb.util.IndexedTable import IndexedTableValue
36+
37+
if TYPE_CHECKING:
38+
from chb.api.InterfaceDictionary import InterfaceDictionary
39+
40+
41+
@dataclass
42+
class FmtArgFieldWidth:
43+
s: str
44+
45+
def has_fieldwidth(self) -> bool:
46+
return self.s.startswith("fwc:")
47+
48+
def has_fieldwidth_argument(self) -> bool:
49+
return self.s == "fwa"
50+
51+
def fieldwidth(self) -> Optional[int]:
52+
if self.has_fieldwidth():
53+
return int(self.s[4:])
54+
return None
55+
56+
def __str__(self) -> str:
57+
if self.has_fieldwidth():
58+
return self.s[4:]
59+
elif self.has_fieldwidth_argument():
60+
return "*"
61+
else:
62+
return ""
63+
64+
@dataclass
65+
class FmtArgPrecision:
66+
s: str
67+
68+
def has_precision(self) -> bool:
69+
return self.s.startswith("pc:")
70+
71+
def has_precision_argument(self) -> bool:
72+
return self.s == "pa"
73+
74+
def precision(self) -> Optional[int]:
75+
if self.has_precision():
76+
return int(self.s[3:])
77+
return None
78+
79+
def __str__(self) -> str:
80+
if self.has_precision():
81+
return "." + self.s[3:]
82+
elif self.has_precision_argument():
83+
return ".*"
84+
else:
85+
return ""
86+
87+
88+
class FormatArgSpec(InterfaceDictionaryRecord):
89+
90+
def __init__(
91+
self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None:
92+
InterfaceDictionaryRecord.__init__(self, ixd, ixval)
93+
94+
@property
95+
def fieldwidth(self) -> FmtArgFieldWidth:
96+
return FmtArgFieldWidth(self.tags[0])
97+
98+
@property
99+
def precision(self) -> FmtArgPrecision:
100+
return FmtArgPrecision(self.tags[1])
101+
102+
@property
103+
def lengthmodifier(self) -> str:
104+
if self.tags[2] == "none":
105+
return ""
106+
else:
107+
return self.tags[2]
108+
109+
@property
110+
def conversion(self) -> str:
111+
return self.tags[3]
112+
113+
@property
114+
def flags(self) -> str:
115+
return "".join(chr(i) for i in self.args)
116+
117+
def __str__(self):
118+
return (
119+
"%"
120+
+ self.flags
121+
+ str(self.fieldwidth)
122+
+ str(self.precision)
123+
+ str(self.lengthmodifier)
124+
+ self.conversion)
125+
126+
127+
class FormatStringSpec(InterfaceDictionaryRecord):
128+
129+
def __init__(
130+
self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None:
131+
InterfaceDictionaryRecord.__init__(self, ixd, ixval)
132+
133+
@property
134+
def argspecs(self) -> List[FormatArgSpec]:
135+
return [self.id.formatarg_spec(ix) for ix in self.args[1:]]
136+
137+
@property
138+
def literal_length(self) -> int:
139+
return self.args[0]
140+
141+
def __str__(self) -> str:
142+
return ", ".join(str(s) for s in self.argspecs)

chb/api/InterfaceDictionary.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#
77
# Copyright (c) 2016-2020 Kestrel Technology LLC
88
# Copyright (c) 2020 Henny Sipma
9-
# Copyright (c) 2021-2023 Aarno Labs LLC
9+
# Copyright (c) 2021-2026 Aarno Labs LLC
1010
#
1111
# Permission is hereby granted, free of charge, to any person obtaining a copy
1212
# of this software and associated documentation files (the "Software"), to deal
@@ -33,6 +33,7 @@
3333
from typing import List, TYPE_CHECKING
3434

3535
from chb.api.BTerm import BTerm
36+
from chb.api.FormatStringSpec import FormatStringSpec, FormatArgSpec
3637
from chb.api.FormatStringType import FormatStringType
3738
from chb.api.FtsParameter import FtsParameter
3839
from chb.api.FtsParameterLocation import FtsParameterLocation
@@ -62,6 +63,8 @@ def __init__(
6263
app: "AppAccess",
6364
xnode: ET.Element) -> None:
6465
self._app = app
66+
self.formatarg_spec_table = IT.IndexedTable("formatarg-spec-table")
67+
self.formatstring_spec_table = IT.IndexedTable("formatstring-spec-table")
6568
self.function_stub_table = IT.IndexedTable("function-stub-table")
6669
self.call_target_table = IT.IndexedTable("call-target-table")
6770
self.formatstring_type_table = IT.IndexedTable("formatstring-type-table")
@@ -79,6 +82,8 @@ def __init__(
7982
self.bterm_table = IT.IndexedTable("bterm-table")
8083
self.tables: List[IT.IndexedTable] = [
8184
self.formatstring_type_table,
85+
self.formatarg_spec_table,
86+
self.formatstring_spec_table,
8287
self.parameter_location_table,
8388
self.parameter_location_list_table,
8489
self.function_stub_table,
@@ -113,6 +118,12 @@ def formatstring_type(self, ix: int) -> FormatStringType:
113118
return apiregistry.mk_instance(
114119
self, self.formatstring_type_table.retrieve(ix), FormatStringType)
115120

121+
def formatarg_spec(self, ix: int) -> FormatArgSpec:
122+
return FormatArgSpec(self, self.formatarg_spec_table.retrieve(ix))
123+
124+
def formatstring_spec(self, ix: int) -> FormatStringSpec:
125+
return FormatStringSpec(self, self.formatstring_spec_table.retrieve(ix))
126+
116127
def parameter_location(self, ix: int) -> FtsParameterLocation:
117128
return apiregistry.mk_instance(
118129
self, self.parameter_location_table.retrieve(ix), FtsParameterLocation)

chb/app/AppAccess.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ def function_info(self, faddr: str) -> FunctionInfo:
343343
if faddr not in self._functioninfos:
344344
xnode = UF.get_function_info_xnode(self.path, self.filename, faddr)
345345
self._functioninfos[faddr] = FunctionInfo(
346-
self.interfacedictionary, faddr, xnode)
346+
self.bdictionary, self.interfacedictionary, faddr, xnode)
347347
return self._functioninfos[faddr]
348348

349349
# Instructions -----------------------------------------------------------

chb/app/CHVersion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
chbversion: str = "0.3.0-20260614"
1+
chbversion: str = "0.3.0-20260617"
22

3-
minimum_required_chb_version = "0.6.0_20260614"
3+
minimum_required_chb_version = "0.6.0_20260617"

chb/app/Function.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989

9090

9191
if TYPE_CHECKING:
92+
from chb.api.FormatStringSpec import FormatStringSpec
9293
from chb.app.AppAccess import AppAccess
9394
from chb.app.FunctionStackframe import FunctionStackframe
9495
from chb.app.GlobalMemoryMap import (
@@ -409,6 +410,10 @@ def register_lhs_type(self, iaddr: str, reg: str) -> Optional["BCTyp"]:
409410

410411
return None
411412

413+
@property
414+
def formatstrings(self) -> Mapping[str, Tuple[str, "FormatStringSpec"]]:
415+
return self.finfo.formatstrings
416+
412417
@property
413418
def lhs_names(self) -> Dict[str, str]:
414419
return self.finfo.lhs_names

chb/app/FunctionInfo.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#
77
# Copyright (c) 2016-2020 Kestrel Technology LLC
88
# Copyright (c) 2020 Henny Sipma
9-
# Copyright (c) 2021-2023 Aarno Labs LLC
9+
# Copyright (c) 2021-2026 Aarno Labs LLC
1010
#
1111
# Permission is hereby granted, free of charge, to any person obtaining a copy
1212
# of this software and associated documentation files (the "Software"), to deal
@@ -30,35 +30,44 @@
3030

3131
import xml.etree.ElementTree as ET
3232

33-
from typing import Dict, Mapping, Optional, TYPE_CHECKING
33+
from typing import Dict, Mapping, Optional, Tuple, TYPE_CHECKING
3434

3535
from chb.api.AppSummary import AppSummary
3636
from chb.api.CallTarget import CallTarget
3737
from chb.api.CallTargetInfo import CallTargetInfo
3838
import chb.util.fileutil as UF
3939

4040
if TYPE_CHECKING:
41+
from chb.api.FormatStringSpec import FormatStringSpec
4142
from chb.api.InterfaceDictionary import InterfaceDictionary
43+
from chb.app.BDictionary import BDictionary
4244

4345

4446
class FunctionInfo:
4547

4648
def __init__(
4749
self,
50+
bd: "BDictionary",
4851
ixd: "InterfaceDictionary",
4952
faddr: str,
5053
xnode: ET.Element) -> None:
54+
self._bd = bd
5155
self._ixd = ixd
5256
self._faddr = faddr
5357
self.xnode = xnode
5458
self._calltargets: Dict[str, CallTarget] = {}
5559
self._variablenames: Dict[int, str] = {}
5660
self._calltargetinfos: Dict[str, CallTargetInfo] = {}
61+
self._formatstringspecs: Optional[Dict[str, Tuple[str,FormatStringSpec]]] = None
5762

5863
@property
5964
def faddr(self) -> str:
6065
return self._faddr
6166

67+
@property
68+
def bd(self) -> "BDictionary":
69+
return self._bd
70+
6271
@property
6372
def ixd(self) -> "InterfaceDictionary":
6473
return self._ixd
@@ -115,6 +124,23 @@ def calltargetinfos(self) -> Mapping[str, CallTargetInfo]:
115124
ctgt, fintf, fsem)
116125
return self._calltargetinfos
117126

127+
@property
128+
def formatstrings(self) -> Mapping[str, Tuple[str, "FormatStringSpec"]]:
129+
if self._formatstringspecs is None:
130+
self._formatstringspecs = {}
131+
fsnode = self.xnode.find("format-strings")
132+
if fsnode is not None:
133+
for fs in fsnode.findall("fs"):
134+
xaddr = fs.get("a")
135+
ixs = int(fs.get("ixs", "-1"))
136+
ixc = int(fs.get("ixc", "-1"))
137+
kind = fs.get("kind", "printf")
138+
if xaddr is not None and ixs > 0 and ixc > 0:
139+
formatstring = self.bd.string(ixs)
140+
formatspec = self.ixd.formatstring_spec(ixc)
141+
self._formatstringspecs[xaddr] = (formatstring, formatspec)
142+
return self._formatstringspecs
143+
118144
@property
119145
def lhs_names(self) -> Dict[str, str]:
120146
result: Dict[str, str] = {}

0 commit comments

Comments
 (0)