Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions chb/api/FormatStringSpec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# ------------------------------------------------------------------------------
# CodeHawk Binary Analyzer
# Author: Henny Sipma
# ------------------------------------------------------------------------------
# The MIT License (MIT)
#
# Copyright (c) 2026 Aarno Labs LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ------------------------------------------------------------------------------

from dataclasses import dataclass
from typing import List, Optional, TYPE_CHECKING

from chb.api.InterfaceDictionaryRecord import InterfaceDictionaryRecord

import chb.util.fileutil as UF

from chb.util.IndexedTable import IndexedTableValue

if TYPE_CHECKING:
from chb.api.InterfaceDictionary import InterfaceDictionary


@dataclass
class FmtArgFieldWidth:
s: str

def has_fieldwidth(self) -> bool:
return self.s.startswith("fwc:")

def has_fieldwidth_argument(self) -> bool:
return self.s == "fwa"

def fieldwidth(self) -> Optional[int]:
if self.has_fieldwidth():
return int(self.s[4:])
return None

def __str__(self) -> str:
if self.has_fieldwidth():
return self.s[4:]
elif self.has_fieldwidth_argument():
return "*"
else:
return ""

@dataclass
class FmtArgPrecision:
s: str

def has_precision(self) -> bool:
return self.s.startswith("pc:")

def has_precision_argument(self) -> bool:
return self.s == "pa"

def precision(self) -> Optional[int]:
if self.has_precision():
return int(self.s[3:])
return None

def __str__(self) -> str:
if self.has_precision():
return "." + self.s[3:]
elif self.has_precision_argument():
return ".*"
else:
return ""


class FormatArgSpec(InterfaceDictionaryRecord):

def __init__(
self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None:
InterfaceDictionaryRecord.__init__(self, ixd, ixval)

@property
def fieldwidth(self) -> FmtArgFieldWidth:
return FmtArgFieldWidth(self.tags[0])

@property
def precision(self) -> FmtArgPrecision:
return FmtArgPrecision(self.tags[1])

@property
def lengthmodifier(self) -> str:
if self.tags[2] == "none":
return ""
else:
return self.tags[2]

@property
def conversion(self) -> str:
return self.tags[3]

@property
def flags(self) -> str:
return "".join(chr(i) for i in self.args)

def __str__(self):
return (
"%"
+ self.flags
+ str(self.fieldwidth)
+ str(self.precision)
+ str(self.lengthmodifier)
+ self.conversion)


class FormatStringSpec(InterfaceDictionaryRecord):

def __init__(
self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None:
InterfaceDictionaryRecord.__init__(self, ixd, ixval)

@property
def argspecs(self) -> List[FormatArgSpec]:
return [self.id.formatarg_spec(ix) for ix in self.args[1:]]

@property
def literal_length(self) -> int:
return self.args[0]

def __str__(self) -> str:
return ", ".join(str(s) for s in self.argspecs)
13 changes: 12 additions & 1 deletion chb/api/InterfaceDictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#
# Copyright (c) 2016-2020 Kestrel Technology LLC
# Copyright (c) 2020 Henny Sipma
# Copyright (c) 2021-2023 Aarno Labs LLC
# Copyright (c) 2021-2026 Aarno Labs LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand All @@ -33,6 +33,7 @@
from typing import List, TYPE_CHECKING

from chb.api.BTerm import BTerm
from chb.api.FormatStringSpec import FormatStringSpec, FormatArgSpec
from chb.api.FormatStringType import FormatStringType
from chb.api.FtsParameter import FtsParameter
from chb.api.FtsParameterLocation import FtsParameterLocation
Expand Down Expand Up @@ -62,6 +63,8 @@ def __init__(
app: "AppAccess",
xnode: ET.Element) -> None:
self._app = app
self.formatarg_spec_table = IT.IndexedTable("formatarg-spec-table")
self.formatstring_spec_table = IT.IndexedTable("formatstring-spec-table")
self.function_stub_table = IT.IndexedTable("function-stub-table")
self.call_target_table = IT.IndexedTable("call-target-table")
self.formatstring_type_table = IT.IndexedTable("formatstring-type-table")
Expand All @@ -79,6 +82,8 @@ def __init__(
self.bterm_table = IT.IndexedTable("bterm-table")
self.tables: List[IT.IndexedTable] = [
self.formatstring_type_table,
self.formatarg_spec_table,
self.formatstring_spec_table,
self.parameter_location_table,
self.parameter_location_list_table,
self.function_stub_table,
Expand Down Expand Up @@ -113,6 +118,12 @@ def formatstring_type(self, ix: int) -> FormatStringType:
return apiregistry.mk_instance(
self, self.formatstring_type_table.retrieve(ix), FormatStringType)

def formatarg_spec(self, ix: int) -> FormatArgSpec:
return FormatArgSpec(self, self.formatarg_spec_table.retrieve(ix))

def formatstring_spec(self, ix: int) -> FormatStringSpec:
return FormatStringSpec(self, self.formatstring_spec_table.retrieve(ix))

def parameter_location(self, ix: int) -> FtsParameterLocation:
return apiregistry.mk_instance(
self, self.parameter_location_table.retrieve(ix), FtsParameterLocation)
Expand Down
2 changes: 1 addition & 1 deletion chb/app/AppAccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ def function_info(self, faddr: str) -> FunctionInfo:
if faddr not in self._functioninfos:
xnode = UF.get_function_info_xnode(self.path, self.filename, faddr)
self._functioninfos[faddr] = FunctionInfo(
self.interfacedictionary, faddr, xnode)
self.bdictionary, self.interfacedictionary, faddr, xnode)
return self._functioninfos[faddr]

# Instructions -----------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions chb/app/CHVersion.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
chbversion: str = "0.3.0-20260614"
chbversion: str = "0.3.0-20260617"

minimum_required_chb_version = "0.6.0_20260614"
minimum_required_chb_version = "0.6.0_20260617"
5 changes: 5 additions & 0 deletions chb/app/Function.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@


if TYPE_CHECKING:
from chb.api.FormatStringSpec import FormatStringSpec
from chb.app.AppAccess import AppAccess
from chb.app.FunctionStackframe import FunctionStackframe
from chb.app.GlobalMemoryMap import (
Expand Down Expand Up @@ -409,6 +410,10 @@ def register_lhs_type(self, iaddr: str, reg: str) -> Optional["BCTyp"]:

return None

@property
def formatstrings(self) -> Mapping[str, Tuple[str, "FormatStringSpec"]]:
return self.finfo.formatstrings

@property
def lhs_names(self) -> Dict[str, str]:
return self.finfo.lhs_names
Expand Down
30 changes: 28 additions & 2 deletions chb/app/FunctionInfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#
# Copyright (c) 2016-2020 Kestrel Technology LLC
# Copyright (c) 2020 Henny Sipma
# Copyright (c) 2021-2023 Aarno Labs LLC
# Copyright (c) 2021-2026 Aarno Labs LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand All @@ -30,35 +30,44 @@

import xml.etree.ElementTree as ET

from typing import Dict, Mapping, Optional, TYPE_CHECKING
from typing import Dict, Mapping, Optional, Tuple, TYPE_CHECKING

from chb.api.AppSummary import AppSummary
from chb.api.CallTarget import CallTarget
from chb.api.CallTargetInfo import CallTargetInfo
import chb.util.fileutil as UF

if TYPE_CHECKING:
from chb.api.FormatStringSpec import FormatStringSpec
from chb.api.InterfaceDictionary import InterfaceDictionary
from chb.app.BDictionary import BDictionary


class FunctionInfo:

def __init__(
self,
bd: "BDictionary",
ixd: "InterfaceDictionary",
faddr: str,
xnode: ET.Element) -> None:
self._bd = bd
self._ixd = ixd
self._faddr = faddr
self.xnode = xnode
self._calltargets: Dict[str, CallTarget] = {}
self._variablenames: Dict[int, str] = {}
self._calltargetinfos: Dict[str, CallTargetInfo] = {}
self._formatstringspecs: Optional[Dict[str, Tuple[str,FormatStringSpec]]] = None

@property
def faddr(self) -> str:
return self._faddr

@property
def bd(self) -> "BDictionary":
return self._bd

@property
def ixd(self) -> "InterfaceDictionary":
return self._ixd
Expand Down Expand Up @@ -115,6 +124,23 @@ def calltargetinfos(self) -> Mapping[str, CallTargetInfo]:
ctgt, fintf, fsem)
return self._calltargetinfos

@property
def formatstrings(self) -> Mapping[str, Tuple[str, "FormatStringSpec"]]:
if self._formatstringspecs is None:
self._formatstringspecs = {}
fsnode = self.xnode.find("format-strings")
if fsnode is not None:
for fs in fsnode.findall("fs"):
xaddr = fs.get("a")
ixs = int(fs.get("ixs", "-1"))
ixc = int(fs.get("ixc", "-1"))
kind = fs.get("kind", "printf")
if xaddr is not None and ixs > 0 and ixc > 0:
formatstring = self.bd.string(ixs)
formatspec = self.ixd.formatstring_spec(ixc)
self._formatstringspecs[xaddr] = (formatstring, formatspec)
return self._formatstringspecs

@property
def lhs_names(self) -> Dict[str, str]:
result: Dict[str, str] = {}
Expand Down