diff --git a/chb/api/AppFunctionSignature.py b/chb/api/AppFunctionSignature.py index 206f2cdf..90cd8838 100644 --- a/chb/api/AppFunctionSignature.py +++ b/chb/api/AppFunctionSignature.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2023 Aarno Labs LLC +# Copyright (c) 2023-2024 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 @@ -54,7 +54,9 @@ class AppFunctionSignature(InterfaceDictionaryRecord): args[6..]: indices of registers preserved (not normally preserved) """ - def __init__(self, ixd: "InterfaceDictionary", ixval: IndexedTableValue) -> None: + def __init__( + self, ixd: "InterfaceDictionary", ixval: IndexedTableValue + ) -> None: InterfaceDictionaryRecord.__init__(self, ixd, ixval) @property @@ -69,16 +71,22 @@ def parameter_list(self) -> List["FtsParameter"]: def returntype(self) -> "BCTyp": return self.bcd.typ(self.args[3]) - def index_of_register_parameter_location(self, r: "Register") -> Optional[int]: + def index_of_register_parameter_location( + self, r: "Register") -> Optional[int]: for p in self.parameter_list: if p.is_register_parameter_location(r): return p.argindex return None + def index_of_stack_parameter_location(self, offset: int) -> Optional[int]: + for p in self.parameter_list: + if p.is_stack_parameter_location(offset): + return p.argindex + return None + def __str__(self) -> str: return ( str(self.returntype) + " (" + ", ".join(str(p) for p in self.parameter_list) + ")") - diff --git a/chb/api/FtsParameter.py b/chb/api/FtsParameter.py index 4c38dcf1..45f310f5 100644 --- a/chb/api/FtsParameter.py +++ b/chb/api/FtsParameter.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2023 Aarno Labs LLC +# Copyright (c) 2023-2024 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 @@ -80,5 +80,10 @@ def is_register_parameter_location(self, r: "Register") -> bool: return self.parameter_location_list[0].is_register_parameter_location_of(r) return False + def is_stack_parameter_location(self, offset: int) -> bool: + if len(self.parameter_location_list) == 1: + return self.parameter_location_list[0].is_stack_parameter_location_of(offset) + return False + def __str__(self) -> str: return str(self.typ) + " " + str(self.name) diff --git a/chb/api/FtsParameterLocation.py b/chb/api/FtsParameterLocation.py index 9e4b3a25..44255588 100644 --- a/chb/api/FtsParameterLocation.py +++ b/chb/api/FtsParameterLocation.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2023 Aarno Labs LLC +# Copyright (c) 2023-2024 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 @@ -81,6 +81,9 @@ def is_unknown_location(self) -> bool: def is_register_parameter_location_of(self, r: "Register") -> bool: return False + def is_stack_parameter_location_of(self, offset: int) -> bool: + return False + @apiregistry.register_tag("s", FtsParameterLocation) class FtsStackParameter(FtsParameterLocation): @@ -98,9 +101,12 @@ def is_stack_parameter(self) -> bool: return True @property - def index(self) -> int: + def offset(self) -> int: return self.args[0] + def is_stack_parameter_location_of(self, offset: int) -> bool: + return offset == self.offset + def __str__(self) -> str: return "stack-parameter " + str(self.index) diff --git a/chb/app/AppAccess.py b/chb/app/AppAccess.py index 5197ebeb..9f93b535 100644 --- a/chb/app/AppAccess.py +++ b/chb/app/AppAccess.py @@ -63,6 +63,7 @@ from chb.app.Function import Function from chb.app.FunctionInfo import FunctionInfo from chb.app.FunctionsData import FunctionsData +from chb.app.GlobalMemoryMap import GlobalMemoryMap from chb.app.JumpTables import JumpTables from chb.app.SystemInfo import SystemInfo from chb.app.StringXRefs import StringsXRefs @@ -128,6 +129,7 @@ def __init__( self._bcfiles: Optional[BCFiles] = None self._typeconstraints = TypeConstraintStore(self) + self._globalmemorymap: Optional[GlobalMemoryMap] = None self._systeminfo: Optional[SystemInfo] = None @property @@ -190,6 +192,16 @@ def tcdictionary(self) -> TypeConstraintDictionary: self._tcdictionary = TypeConstraintDictionary(self, None) return self._tcdictionary + @property + def globalmemorymap(self) -> GlobalMemoryMap: + if self._globalmemorymap is None: + if UF.has_global_locations_file(self.path, self.filename): + x = UF.get_global_locations_xnode(self.path, self.filename) + self._globalmemorymap = GlobalMemoryMap(self, x) + else: + self._globalmemorymap = GlobalMemoryMap(self, None) + return self._globalmemorymap + @property def bdictionary(self) -> BDictionary: if self._bdictionary is None: diff --git a/chb/app/CHVersion.py b/chb/app/CHVersion.py index 74e2d98f..34d0dc97 100644 --- a/chb/app/CHVersion.py +++ b/chb/app/CHVersion.py @@ -1 +1 @@ -chbversion: str = "0.3.0-20241111" +chbversion: str = "0.3.0-20250203" diff --git a/chb/app/Function.py b/chb/app/Function.py index 26821970..77fc74ab 100644 --- a/chb/app/Function.py +++ b/chb/app/Function.py @@ -59,6 +59,8 @@ from chb.app.FnProofObligations import FnProofObligations from chb.app.FnXPODictionary import FnXPODictionary from chb.app.FunctionInfo import FunctionInfo +from chb.app.GlobalMemoryMap import ( + GlobalLoad, GlobalStore, GlobalAddressArgument) from chb.app.Instruction import Instruction from chb.app.JumpTables import JumpTable from chb.app.StackLayout import StackLayout @@ -84,9 +86,14 @@ import chb.util.fileutil as UF from chb.util.graphutil import coalesce_lists +from chb.util.loggingutil import chklogger + if TYPE_CHECKING: + from chb.app.AppAccess import AppAccess from chb.app.FnStackFrame import FnStackFrame + from chb.app.GlobalMemoryMap import ( + GlobalMemoryMap, GlobalLocation, GlobalReference) from chb.bctypes.BCTyp import BCTyp @@ -119,6 +126,7 @@ def __init__( self._invariants: Dict[str, List[InvariantFact]] = {} self._varinvariants: Dict[str, List[VarInvariantFact]] = {} self._stacklayout: Optional[StackLayout] = None + self._globalrefs: Optional[Dict[str, List["GlobalReference"]]] = None self._proofobligations: Optional[FnProofObligations] = None @property @@ -129,6 +137,10 @@ def path(self) -> str: def filename(self) -> str: return self._filename + @property + def app(self) -> "AppAccess": + return self.ixd.app + @property def bd(self) -> BDictionary: return self._bd @@ -544,6 +556,44 @@ def stacklayout(self) -> StackLayout: self._stacklayout = stacklayout return self._stacklayout + def globalrefs(self) -> Dict[str, List["GlobalReference"]]: + if self._globalrefs is None: + self._globalrefs = {} + gnode = self.xnode.find("global-references") + if gnode is not None: + glnode = gnode.find("location-references") + if glnode is not None: + for rnode in glnode.findall("gref"): + gaddr = rnode.get("g") + if gaddr is None: + chklogger.logger.error( + "Global address is missing in xml gref") + continue + gloc = self.app.globalmemorymap.get_location(gaddr) + if gloc is None: + chklogger.logger.error( + "Global location is missing for %s", gaddr) + continue + gt = rnode.get("t") + if gt is None: + chklogger.logger.error( + "Global reference type is missing for %s", gaddr) + continue + if gt == "L": + gref: "GlobalReference" = GlobalLoad(self, gloc, rnode) + elif gt == "S": + gref = GlobalStore(self, gloc, rnode) + elif gt == "CA": + gref = GlobalAddressArgument(self, gloc, rnode) + else: + chklogger.logger.error( + "Global reference type %s not recognized for %s", + gt, gaddr) + continue + self._globalrefs.setdefault(gaddr, []) + self._globalrefs[gaddr].append(gref) + return self._globalrefs + @abstractmethod def to_string( self, diff --git a/chb/app/GlobalMemoryMap.py b/chb/app/GlobalMemoryMap.py new file mode 100644 index 00000000..ebef1d2a --- /dev/null +++ b/chb/app/GlobalMemoryMap.py @@ -0,0 +1,276 @@ +# ------------------------------------------------------------------------------ +# CodeHawk Binary Analyzer +# Author: Henny Sipma +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2024-2025 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. +# ------------------------------------------------------------------------------ + +import xml.etree.ElementTree as ET + +from typing import Dict, List, Optional, Tuple, TYPE_CHECKING + +import chb.util.fileutil as UF +from chb.util.loggingutil import chklogger + +if TYPE_CHECKING: + from chb.app.AppAccess import AppAccess + from chb.app.Function import Function + from chb.app.Instruction import Instruction + from chb.bctypes.BCDictionary import BCDictionary + from chb.bctypes.BCTyp import BCTyp + from chb.invariants.FnVarDictionary import FnVarDictionary + from chb.invariants.FnXprDictionary import FnXprDictionary + from chb.invariants.VMemoryOffset import VMemoryOffset + from chb.invariants.XXpr import XXpr + + +class GlobalReference: + + def __init__(self, + function: "Function", + gloc: "GlobalLocation", + xnode: ET.Element) -> None: + self._function = function + self._gloc = gloc + self._xnode = xnode + + @property + def xnode(self) -> ET.Element: + return self._xnode + + @property + def mmap(self) -> "GlobalMemoryMap": + return self._gloc.mmap + + @property + def app(self) -> "AppAccess": + return self.mmap.app + + @property + def function(self) -> "Function": + return self._function + + @property + def vardictionary(self) -> "FnVarDictionary": + return self.function.vardictionary + + @property + def xprdictionary(self) -> "FnXprDictionary": + return self.vardictionary.xd + + @property + def gloc(self) -> "GlobalLocation": + return self._gloc + + @property + def gaddr(self) -> str: + return self.gloc.addr + + @property + def faddr(self) -> str: + return self.function.faddr + + @property + def iaddr(self) -> str: + iaddr = self._xnode.get("i") + if iaddr is None: + raise UF.CHBError("Attribute i is missing for " + self.gaddr) + return iaddr + + @property + def grefvalue(self) -> "XXpr": + xix = self._xnode.get("xix") + if xix is None: + raise UF.CHBError("Attribute xix is missing for " + self.gaddr) + return self.xprdictionary.xpr(int(xix)) + + def __str__(self) -> str: + return "Ref: " + self.gaddr + ": " + str(self.grefvalue) + + +class GlobalLoad(GlobalReference): + + def __init__( + self, + function: "Function", + gloc: "GlobalLocation", + xnode: ET.Element) -> None: + GlobalReference.__init__(self, function, gloc, xnode) + + @property + def size(self) -> int: + return int(self.xnode.get("s", "0")) + + def __str__(self) -> str: + return "Load: " + str(self.grefvalue) + " (" + self.gaddr + ")" + + +class GlobalStore(GlobalReference): + + def __init__( + self, + function: "Function", + gloc: "GlobalLocation", + xnode: ET.Element) -> None: + GlobalReference.__init__(self, function, gloc, xnode) + + @property + def size(self) -> int: + return int(self.xnode.get("s", "0")) + + @property + def value(self) -> Optional[int]: + optval = self._xnode.get("v") + if optval is not None: + return int(optval) + else: + return None + + def __str__(self) -> str: + return "Store: " + str(self.grefvalue) + " (" + self.gaddr + ")" + + +class GlobalAddressArgument(GlobalReference): + + def __init__( + self, + function: "Function", + gloc: "GlobalLocation", + xnode: ET.Element) -> None: + GlobalReference.__init__(self, function, gloc, xnode) + + @property + def argindex(self) -> int: + return int(self.xnode.get("aix", "-1")) + + @property + def memory_offset(self) -> Optional["VMemoryOffset"]: + mix = self.xnode.get("mix") + if mix is not None: + return self.vardictionary.memory_offset(int(mix)) + return None + + def __str__(self) -> str: + return ( + "Argument: " + + str(self.grefvalue) + + " with offset " + + str(self.memory_offset) + + " (" + + self.gaddr + + ")") + + +class GlobalLocation: + + def __init__(self, mmap: "GlobalMemoryMap", xnode: ET.Element) -> None: + self._mmap = mmap + self._xnode = xnode + + @property + def mmap(self) -> "GlobalMemoryMap": + return self._mmap + + @property + def name(self) -> str: + return self._xnode.get("name", "?") + + @property + def addr(self) -> str: + return self._xnode.get("a", "0x0") + + @property + def gtype(self) -> Optional["BCTyp"]: + tix = self._xnode.get("tix", None) + if tix is not None: + return self.mmap.bcdictionary.typ(int(tix)) + return None + + @property + def size(self) -> Optional[int]: + s = self._xnode.get("size", None) + if s is not None: + return int(s) + else: + return None + + +class GlobalMemoryMap: + + def __init__(self, app: "AppAccess", xnode: Optional[ET.Element]) -> None: + self._app = app + self._xnode = xnode + self._locations: Optional[Dict[str, GlobalLocation]] = None + + @property + def app(self) -> "AppAccess": + return self._app + + @property + def bcdictionary(self) -> "BCDictionary": + return self.app.bcdictionary + + @property + def deflocnode(self) -> Optional[ET.Element]: + if self._xnode is not None: + return self._xnode.find("locations") + return None + + @property + def refaddrnode(self) -> Optional[ET.Element]: + if self._xnode is not None: + return self._xnode.find("no-locations") + return None + + @property + def locations(self) -> Dict[str, GlobalLocation]: + if self._locations is None: + self._locations = {} + if self.deflocnode is not None: + for xgloc in self.deflocnode.findall("gloc"): + addr = xgloc.get("a", "0x0") + self._locations[addr] = GlobalLocation(self, xgloc) + return self._locations + + def get_location_by_name(self, name: str) -> Optional[GlobalLocation]: + for gloc in self.locations.values(): + if gloc.name == name: + return gloc + else: + return None + + def get_location(self, addr: str) -> Optional[GlobalLocation]: + if addr in self.locations: + return self.locations[addr] + else: + return None + + def coverage(self) -> Tuple[int, int]: + loccoverage: int = 0 + loccount: int = 0 + for gloc in self.locations.values(): + size = gloc.size + if size is not None: + loccoverage += size + loccount += 1 + return (loccount, loccoverage) diff --git a/chb/app/InstrXData.py b/chb/app/InstrXData.py index deaeb8d4..06bee162 100644 --- a/chb/app/InstrXData.py +++ b/chb/app/InstrXData.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -73,8 +73,10 @@ def __init__( self.expanded = False self._ssavals: List[XVariable] = [] self._vars: List[XVariable] = [] + self._vars_r: List[Optional[XVariable]] = [] self._types: List["BCTyp"] = [] self._xprs: List[XXpr] = [] + self._xprs_r: List[Optional[XXpr]] = [] self._intervals: List[XInterval] = [] self._strs: List[str] = [] self._ints: List[int] = [] @@ -121,6 +123,12 @@ def vars(self) -> List[XVariable]: self._expand() return self._vars + @property + def vars_r(self) -> List[Optional[XVariable]]: + if not self.expanded: + self._expand() + return self._vars_r + @property def types(self) -> List["BCTyp"]: if not self.expanded: @@ -161,12 +169,29 @@ def get_var(self, index: int) -> XVariable: + str(len(self.vars)) + ")") + def get_var_x(self, index: int) -> Optional[XVariable]: + if index < len(self.vars_r): + return self.vars_r[index] + else: + raise UF.CHBError( + "xdata: var-index out-of-bound: " + + str(index) + + " (length is " + + str(len(self.vars)) + + ")") + @property def xprs(self) -> List[XXpr]: if not self.expanded: self._expand() return self._xprs + @property + def xprs_r(self) -> List[Optional[XXpr]]: + if not self.expanded: + self._expand() + return self._xprs_r + @property def intervals(self) -> List[XInterval]: if not self.expanded: @@ -223,6 +248,32 @@ def reachingdeflocs_for_s(self, var: str) -> Sequence[XSymbol]: return rdef.deflocations return [] + @property + def is_ok(self) -> bool: + """Returns false if the data key is ar: and one or more of the + variables and/or expressions are error values (None). Returns true + otherwise.""" + + key = self.tags[0] + if key.startswith("ar:"): + return all(self.vars_r) and all(self.xprs_r) + else: + return True + + @property + def error_values(self) -> Tuple[List[int], List[int]]: + + key = self.tags[0] + if key.startswith("ar:"): + vars_e: List[int] = [ + i for i in range(0, len(self.vars_r)) if self.vars_r[i] is None] + xprs_e: List[int] = [ + i for i in range(0, len(self.xprs_r)) if self.xprs_r[i] is None] + return (vars_e, xprs_e) + else: + return ([], []) + + def _expand(self) -> None: """Expand the arguments based on the argument string in the keys. @@ -240,49 +291,72 @@ def _expand(self) -> None: key = self.tags[0] if key.startswith("a:"): keyletters = key[2:] - for (i, c) in enumerate(keyletters): - arg = self.args[i] - xd = self.xprdictionary - bd = self.bdictionary - bcd = self.bcdictionary - if c == "v": + use_result = False + elif key.startswith("ar:"): + keyletters = key[3:] + use_result = True + else: + chklogger.logger.error( + "InstrXData tag: %s not recognized", key) + return + + for (i, c) in enumerate(keyletters): + arg = self.args[i] + xd = self.xprdictionary + bd = self.bdictionary + bcd = self.bcdictionary + if c == "v": + if use_result: + if arg == -2: + self._vars_r.append(None) + else: + self._vars_r.append(xd.variable(arg)) + else: self._vars.append(xd.variable(arg)) - elif c == "x": - self._xprs.append(xd.xpr(arg)) - elif c == "a": - self._xprs.append(xd.xpr(arg)) - elif c == "s": - self._strs.append(bd.string(arg)) - elif c == "i": - self._intervals.append(xd.interval(arg)) - elif c == "l": - self._ints.append(arg) - elif c == "t": - self._types.append(bcd.typ(arg)) - elif c == "r": - varinvd = self.varinvdictionary - rdef = varinvd.var_invariant_fact(arg) if arg >= 0 else None - rdef = cast(Optional[ReachingDefFact], rdef) - self._reachingdefs.append(rdef) - elif c == "d": - varinvd = self.varinvdictionary - use = varinvd.var_invariant_fact(arg) if arg >= 0 else None - use = cast(Optional[DefUse], use) - self._defuses.append(use) - elif c == "h": - varinvd = self.varinvdictionary - usehigh = varinvd.var_invariant_fact(arg) if arg > 0 else None - usehigh = cast(Optional[DefUseHigh], usehigh) - self._defuseshigh.append(usehigh) - elif c == "f": - varinvd = self.varinvdictionary - flagrdef = varinvd.var_invariant_fact(arg) if arg >= 0 else None - flagrdef = cast(Optional[FlagReachingDefFact], flagrdef) - self._flagreachingdefs.append(flagrdef) - elif c == "c": - self._ssavals.append(xd.variable(arg)) + + elif c == "x": + if use_result: + if arg == -2: + self._xprs_r.append(None) + else: + self._xprs_r.append(xd.xpr(arg)) else: - raise UF.CHBError("Key letter not recognized: " + c) + self._xprs.append(xd.xpr(arg)) + + elif c == "a": + self._xprs.append(xd.xpr(arg)) + elif c == "s": + self._strs.append(bd.string(arg)) + elif c == "i": + self._intervals.append(xd.interval(arg)) + elif c == "l": + self._ints.append(arg) + elif c == "t": + self._types.append(bcd.typ(arg)) + elif c == "r": + varinvd = self.varinvdictionary + rdef = varinvd.var_invariant_fact(arg) if arg >= 0 else None + rdef = cast(Optional[ReachingDefFact], rdef) + self._reachingdefs.append(rdef) + elif c == "d": + varinvd = self.varinvdictionary + use = varinvd.var_invariant_fact(arg) if arg >= 0 else None + use = cast(Optional[DefUse], use) + self._defuses.append(use) + elif c == "h": + varinvd = self.varinvdictionary + usehigh = varinvd.var_invariant_fact(arg) if arg > 0 else None + usehigh = cast(Optional[DefUseHigh], usehigh) + self._defuseshigh.append(usehigh) + elif c == "f": + varinvd = self.varinvdictionary + flagrdef = varinvd.var_invariant_fact(arg) if arg >= 0 else None + flagrdef = cast(Optional[FlagReachingDefFact], flagrdef) + self._flagreachingdefs.append(flagrdef) + elif c == "c": + self._ssavals.append(xd.variable(arg)) + else: + raise UF.CHBError("Key letter not recognized: " + c) @property def is_function_argument(self) -> bool: @@ -313,6 +387,11 @@ def is_bx_call(self) -> bool: return "bx-call" in self.tags def call_target_argument_count(self) -> Optional[int]: + if any(s.startswith("argcount:") for s in self.tags): + tag = next(s for s in self.tags if s.startswith("argcount:")) + argcount = int(tag[9:]) + return argcount + if len(self.tags) >= 3: if self.tags[1] == "call": try: @@ -325,7 +404,6 @@ def call_target_argument_count(self) -> Optional[int]: return None - def has_inlined_call_target(self) -> bool: return len(self.tags) >= 3 and self.tags[2] == "inlined" @@ -378,7 +456,19 @@ def get_branch_conditions(self) -> Sequence[XXpr]: "XData does not have branch conditions: " + str(self)) def has_instruction_condition(self) -> bool: - return "ic" in self.tags + return any(s.startswith("ic:") for s in self.tags) + + def get_instruction_condition(self) -> XXpr: + for t in self.tags: + if t.startswith("ic:"): + index = int(t[3:]) + argval = self.args[index] + if argval == -2: + raise UF.CHBError( + "Unexpected error value in instruction condition") + return self.xprdictionary.xpr(argval) + else: + raise UF.CHBError("No instruction condition index found") def has_condition_block_condition(self) -> bool: return "TF" in self.tags @@ -387,7 +477,44 @@ def has_unknown_instruction_condition(self) -> bool: return "uc" in self.tags def has_base_update(self) -> bool: - return "bu" in self.tags + return any(s.startswith("vbu:") for s in self.tags) + + def get_base_update_var(self) -> XVariable: + vbutag = next(t for t in self.tags if t.startswith("vbu:")) + vix = int(vbutag[4:]) + vbuval = self.args[vix] + if vbuval == -2: + raise UF.CHBError( + "Unexpected error value in base-update variable") + return self.xprdictionary.variable(vbuval) + + def get_base_update_xpr(self) -> XXpr: + xbutag = next(t for t in self.tags if t.startswith("xbu:")) + xix = int(xbutag[4:]) + xbuval = self.args[xix] + if xbuval == -2: + raise UF.CHBError( + "Unexpected error value in base-update expression") + return self.xprdictionary.xpr(xbuval) + + def has_return_xpr(self) -> bool: + return any(s.startswith("return:") for s in self.tags) + + def get_return_xpr(self) -> XXpr: + rvtag = next(t for t in self.tags if t.startswith("return:")) + rvix = int(rvtag[7:]) + rval = self.args[rvix] + if rval == -2: + raise UF.CHBError("Unexpected error in return value") + return self.xprdictionary.xpr(rval) + + def get_return_xxpr(self) -> XXpr: + rvtag = next(t for t in self.tags if t.startswith("return:")) + rvix = int(rvtag[7:]) + rval = self.args[rvix + 1] + if rval == -2: + raise UF.CHBError("Unexpected error in rewritten return value") + return self.xprdictionary.xpr(rval) @property def is_aggregate_jumptable(self) -> bool: diff --git a/chb/arm/ARMCallOpcode.py b/chb/arm/ARMCallOpcode.py index 11555499..ed4ccd09 100644 --- a/chb/arm/ARMCallOpcode.py +++ b/chb/arm/ARMCallOpcode.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2022-2024 Aarno Labs LLC +# Copyright (c) 2022-2025 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 @@ -32,7 +32,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand from chb.arm.ARMOperandKind import ARMOperandKind, ARMAbsoluteOp @@ -54,20 +54,18 @@ if TYPE_CHECKING: from chb.api.CallTarget import CallTarget, AppTarget, StaticStubTarget + from chb.api.InterfaceDictionary import InterfaceDictionary from chb.arm.ARMDictionary import ARMDictionary from chb.bctypes.BCTyp import BCTypFun from chb.invariants.VarInvariantFact import ReachingDefFact from chb.invariants.VAssemblyVariable import VRegisterVariable from chb.invariants.VConstantValueVariable import VFunctionReturnValue - from chb.invariants.XXpr import XprConstant, XprVariable + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr, XprConstant, XprVariable -class ARMCallOpcode(ARMOpcode): - """Generic call functionality, covers BL and BLX. - - tags[1]: - args[0]: index of target operand in armdictionary - +class ARMCallOpcodeXData(ARMOpcodeXData): + """ xdata format: a:x[2n]xr[n]dh, call (n arguments) ------------------------------------------------- xprs[0..2n-1]: (arg location expr, arg value expr) * n @@ -75,8 +73,68 @@ class ARMCallOpcode(ARMOpcode): rdefs[0..n-1]: arg location reaching definitions uses[0]: lhs useshigh[0]: lhs + """ + + def __init__( + self, + xdata: InstrXData, + ixd: "InterfaceDictionary") -> None: + ARMOpcodeXData.__init__(self, xdata) + self._ixd = ixd + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def argument_count(self) -> int: + argcount = self._xdata.call_target_argument_count() + if argcount is None: + chklogger.logger.error( + "No argument count found for call") + return 0 + return argcount + + @property + def arguments(self) -> List["XXpr"]: + argcount = self.argument_count + arguments: List["XXpr"] = [] + for i in range(argcount): + x = self._xdata.xprs_r[i] + if x is None: + x = self._xdata.xprs_r[i + argcount] + if x is None: + raise UF.CHBError( + "Unexpected None-value call argument at index " + + str(i)) + arguments.append(x) + return arguments + + @property + def argumentxvars(self) -> List["XXpr"]: + argcount = self.argument_count + return [x for x in self._xdata.xprs_r[argcount:2 * argcount] + if x is not None] - or (if call target is not known): + @property + def calltarget(self) -> "CallTarget": + return self._xdata.call_target(self._ixd) + + @property + def annotation(self) -> str: + tgt = str(self.calltarget) + args = ", ".join(str(x) for x in self.arguments) + call = "call " + str(tgt) + "(" + args + ")" + return self.add_instruction_condition(call) + + +class ARMCallOpcode(ARMOpcode): + """Generic call functionality, covers BL and BLX. + + tags[1]: + args[0]: index of target operand in armdictionary + + (if call target is not known): xdata format: a:xxxxx --------------------- vars[0]: return value variable @@ -85,10 +143,7 @@ class ARMCallOpcode(ARMOpcode): rdefs[0]: target reaching definition """ - def __init__( - self, - d: "ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) @property @@ -100,19 +155,10 @@ def opargs(self) -> List[ARMOperand]: return [self.armd.arm_operand(self.args[0])] def lhs(self, xdata: InstrXData) -> List[XVariable]: - if len(xdata.vars) > 0: - return [xdata.vars[0]] - else: - return [] + return [ARMCallOpcodeXData(xdata, self.ixd).vrd] def argument_count(self, xdata: InstrXData) -> int: - if self.is_call_instruction(xdata): - argcount = xdata.call_target_argument_count() - if argcount is not None: - return argcount - chklogger.logger.warning( - "Call instruction does not have argument count") - return 0 + return ARMCallOpcodeXData(xdata, self.ixd).argument_count def has_string_arguments(self, xdata: InstrXData) -> bool: return any([x.is_string_reference for x in self.arguments(xdata)]) @@ -125,7 +171,7 @@ def annotated_call_arguments( return [x.to_annotated_value() for x in self.arguments(xdata)] def arguments(self, xdata: InstrXData) -> Sequence[XXpr]: - return xdata.xprs[:self.argument_count(xdata)] + return ARMCallOpcodeXData(xdata, self.ixd).arguments def is_call(self, xdata: InstrXData) -> bool: return len(xdata.tags) >= 2 and xdata.tags[1] == "call" @@ -134,13 +180,8 @@ def is_call_instruction(self, xdata: InstrXData) -> bool: return xdata.has_call_target() def annotation(self, xdata: InstrXData) -> str: - if self.is_call(xdata) and xdata.has_call_target(): - tgt = xdata.call_target(self.ixd) - args = ", ".join(str(x) for x in self.arguments(xdata)) - return "call " + str(tgt) + "(" + args + ")" - - ctgt = str(xdata.xprs[0]) - return "call " + ctgt + xd = ARMCallOpcodeXData(xdata, self.ixd) + return xd.annotation def call_target(self, xdata: InstrXData) -> "CallTarget": if self.is_call(xdata): @@ -161,11 +202,13 @@ def ast_call_prov( chklogger.logger.info("Inlined call omitted at %s", iaddr) return ([], []) + xd = ARMCallOpcodeXData(xdata, self.ixd) + annotations: List[str] = [iaddr, "BL"] # low-level call data - lhs = xdata.vars[0] + lhs = xd.vrd tgt = self.opargs[0] if not lhs.is_register_variable: @@ -179,7 +222,7 @@ def ast_call_prov( ll_lhs = astree.mk_register_variable_lval(str(lhsreg)) (ll_tgt, _, _) = tgt.ast_rvalue(astree) - xprs = xdata.xprs + xprs = xd.arguments rdefs = xdata.reachingdefs defuses = xdata.defuses defuseshigh = xdata.defuseshigh @@ -239,7 +282,7 @@ def ast_call_prov( # argument data - argcount = self.argument_count(xdata) + argcount = xd.argument_count ll_args: List[AST.ASTExpr] = [] hl_args: List[AST.ASTExpr] = [] @@ -259,8 +302,8 @@ def ast_call_prov( if len(astargtypes) < argcount: astargtypes += ([None] * (argcount - len(astargtypes))) - xargs = xdata.xprs[:argcount] - xvarargs = xdata.xprs[argcount:(2 * argcount)] + xargs = xd.arguments + xvarargs = xd.argumentxvars if len(rdefs) >= argcount: llrdefs = rdefs[:argcount] # x represents the (invariant-enhanced) argument value. @@ -271,7 +314,8 @@ def ast_call_prov( # in bytes (note: not the stackpointer at function entry). # rdef represents the reaching definition for the argument # location. - for (x, xv, rdef, argtype) in zip(xargs, xvarargs, llrdefs, astargtypes): + for (x, xv, rdef, argtype) in zip( + xargs, xvarargs, llrdefs, astargtypes): # low-level argument @@ -351,20 +395,6 @@ def ast_call_prov( argtype = cast(AST.ASTTypInt, argtype) hl_arg = astree.mk_integer_constant( x.constant.value, argtype.ikind) - - elif ( - argtype is not None - and (argtype.is_function - or (argtype.is_pointer - and cast( - AST.ASTTypPtr, - argtype).tgttyp.is_function))): - hexaddr = hex(x.constant.value) - argname = "sub_" + hexaddr[2:] - hl_arg = astree.mk_global_variable_expr( - argname, - vtype=argtype, - globaladdress=x.constant.value) else: hexgaddr = hex(x.constant.value) if hexgaddr in astree.global_addresses: @@ -377,6 +407,20 @@ def ast_call_prov( else: hl_arg = astree.mk_address_of( astree.mk_vinfo_lval(vinfo)) + elif ( + argtype is not None + and (argtype.is_function + or (argtype.is_pointer + and cast( + AST.ASTTypPtr, + argtype).tgttyp.is_function))): + hexaddr = hex(x.constant.value) + argname = "sub_" + hexaddr[2:] + hl_arg = astree.mk_global_variable_expr( + argname, + vtype=argtype, + globaladdress=x.constant.value) + else: chklogger.logger.warning( ("Type of global address %s at instr. " diff --git a/chb/arm/ARMInstruction.py b/chb/arm/ARMInstruction.py index 1dc89e18..60806350 100644 --- a/chb/arm/ARMInstruction.py +++ b/chb/arm/ARMInstruction.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -249,11 +249,7 @@ def has_condition_block_condition(self) -> bool: return self.xdata.has_condition_block_condition() def get_instruction_condition(self) -> XXpr: - if self.has_instruction_condition(): - return self.xdata.xprs[2] - else: - raise UF.CHBError( - "Instruction does not have an instruction condition") + return self.xdata.get_instruction_condition() @property def memory_accesses(self) -> Sequence[MemoryAccess]: @@ -313,17 +309,22 @@ def ast_switch_condition_prov(self, astree: ASTInterface) -> Tuple[ jumptable = cast( "ARMJumpTable", self.armfunction.get_jumptable(self.iaddr)) indexop = jumptable.index_operand - condition = self.xdata.xprs[1] - (ll_cond, _, _) = indexop.ast_rvalue(astree) - hl_cond = XU.xxpr_to_ast_def_expr( - condition, self.xdata, self.iaddr, astree) - - astree.add_expr_mapping(hl_cond, ll_cond) - astree.add_expr_reachingdefs(hl_cond, self.xdata.reachingdefs) - astree.add_condition_address( - ll_cond, (self.xdata.subsumes() + [self.iaddr])) - - return (hl_cond, ll_cond) + condition = self.xdata.xprs_r[1] + if condition is not None: + (ll_cond, _, _) = indexop.ast_rvalue(astree) + hl_cond = XU.xxpr_to_ast_def_expr( + condition, self.xdata, self.iaddr, astree) + + astree.add_expr_mapping(hl_cond, ll_cond) + astree.add_expr_reachingdefs(hl_cond, self.xdata.reachingdefs) + astree.add_condition_address( + ll_cond, (self.xdata.subsumes() + [self.iaddr])) + + return (hl_cond, ll_cond) + else: + chklogger.logger.error( + "Error value encountered at address %s", self.iaddr) + raise UF.CHBError("Jumptable without condition") else: return self.opcode.ast_switch_condition_prov( @@ -392,7 +393,7 @@ def rdef_locations(self) -> Dict[str, List[List[str]]]: def lhs_types(self) -> Dict[str, "BCTyp"]: result: Dict[str, "BCTyp"] = {} - vars = self.xdata.vars + vars = self.xdata.vars_r types = self.xdata.types if len(vars) == len(types): for (v, ty) in zip(vars, types): diff --git a/chb/arm/ARMOpcode.py b/chb/arm/ARMOpcode.py index dc683eb3..de93cd1e 100644 --- a/chb/arm/ARMOpcode.py +++ b/chb/arm/ARMOpcode.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -26,6 +26,8 @@ # ------------------------------------------------------------------------------ """ARM opcodes.""" +import inspect + from typing import List, Optional, Sequence, Tuple, TYPE_CHECKING from chb.api.CallTarget import CallTarget @@ -55,6 +57,8 @@ if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr from chb.simulation.SimulationState import SimulationState @@ -101,6 +105,77 @@ def get_extension(e: str) -> str: return e +class ARMOpcodeXData: + + def __init__(self, xdata: InstrXData) -> None: + self._xdata = xdata + + @property + def xdata(self) -> InstrXData: + return self._xdata + + @property + def is_ok(self) -> bool: + return self.xdata.is_ok + + def var(self, index: int, name: str) -> "XVariable": + if index >= len(self.xdata.vars_r): + raise UF.CHBError( + self.__class__.__name__ + ":" + + name + " index out of bounds: " + str(index)) + v = self.xdata.vars_r[index] + if v is None: + raise UF.CHBError( + self.__class__.__name__ + ":" + name + " has an error value") + return v + + def xpr(self, index: int, name: str) -> "XXpr": + if index >= len(self.xdata.xprs_r): + raise UF.CHBError( + self.__class__.__name__ + ":" + + name + " index out of bounds: " + str(index)) + x = self.xdata.xprs_r[index] + if x is None: + raise UF.CHBError( + self.__class__.__name__ + ":" + name + " has an error value") + return x + + def add_instruction_condition(self, s: str) -> str: + if self.xdata.has_unknown_instruction_condition(): + return "if ? then " + s + elif self.xdata.has_instruction_condition(): + c = str(self.xdata.get_instruction_condition()) + return "if " + c + " then " + s + else: + return s + + @property + def is_writeback(self) -> bool: + return self.xdata.has_base_update() + + def get_base_update_var(self) -> "XVariable": + if self.is_writeback: + return self.xdata.get_base_update_var() + else: + raise UF.CHBError( + self.__class__.__name__ + " does not have writeback") + + def get_base_update_xpr(self) -> "XXpr": + if self.is_writeback: + return self.xdata.get_base_update_xpr() + else: + raise UF.CHBError( + self.__class__.__name__ + " does not have writeback") + + def writeback_update(self) -> str: + if self.xdata.has_base_update(): + vbu = self.get_base_update_var() + xbu = self.get_base_update_xpr() + return "; " + str(vbu) + " := " + str(xbu) + else: + return "" + + class ARMOpcode(ARMDictionaryRecord): def __init__( diff --git a/chb/arm/opcodes/ARMAdd.py b/chb/arm/opcodes/ARMAdd.py index c707cd4a..92d00e84 100644 --- a/chb/arm/opcodes/ARMAdd.py +++ b/chb/arm/opcodes/ARMAdd.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -44,8 +44,88 @@ if TYPE_CHECKING: - import chb.arm.ARMDictionary - from chb.invariants.XXpr import XprCompound, XprConstant + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.VarInvariantFact import ReachingDefFact + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XprCompound, XprConstant, XXpr + + +class ARMAddXData(ARMOpcodeXData): + """Add ==> result + + xdata format: a:vxxxxxxrrdh + ------------------------- + vars[0]: vrd (Rd) + xprs[0]: xrn (Rn) + xprs[1]: xrm (Rm) + xprs[2]: result: xrn + xrm + xprs[3]: rresult: xrn + xrm (rewritten) + xprs[4]: xxrn (xrn rewritten) + xprs[5]: xxrm (xrm rewritten) + xprs[6]: tcond (optional) + xprs[7]: fcond (optional) + rdefs[0]: xrn + rdefs[1]: xrm + rdefs[2:..]: reaching definitions for simplified result expression + uses[0]: vrd + useshigh[0]: vrd + """ + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def xrm(self) -> "XXpr": + return self.xpr(1, "xrm") + + @property + def jt_xxrn(self) -> "XXpr": + """Part of jumptable.""" + return self.xpr(1, "xxrn") + + @property + def result(self) -> "XXpr": + return self.xpr(2, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(3, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[3], self.xdata.args[4], self.result, self.rresult) + + @property + def xxrn(self) -> "XXpr": + return self.xpr(4, "xxrn") + + @property + def xxrm(self) -> "XXpr": + return self.xpr(5, "xxrm") + + @property + def rn_rdef(self) -> Optional["ReachingDefFact"]: + return self._xdata.reachingdefs[0] + + @property + def rm_rdef(self) -> Optional["ReachingDefFact"]: + return self._xdata.reachingdefs[1] + + @property + def annotation(self) -> str: + if self.xdata.is_aggregate_jumptable: + return "jump-table: " + str(self.jt_xxrn) + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("ADD", ARMOpcode) @@ -69,26 +149,9 @@ class ARMAdd(ARMOpcode): args[3]: index of op3 in armdictionary args[4]: is-wide (thumb) - xdata format: a:vxxxxrrdh - ------------------------- - vars[0]: lhs (Rd) - xprs[0]: rhs1 (Rn) - xprs[1]: rhs2 (Rm) - xprs[2]: rhs1 + rhs2 - xprs[3]: rhs1 + rhs2 (simplified) - xprs[4]: rhs1 (simplified) - xprs[5]: rhs2 (simplified) - rdefs[0]: rhs1 - rdefs[1]: rhs2 - rdefs[2:..]: reaching definitions for simplified result expression - uses[0]: lhs - useshigh[0]: lhs """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 5, "Add") @@ -111,18 +174,11 @@ def mnemonic_extension(self) -> str: return wb + cc + wide def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[2] - rresult = xdata.xprs[3] - xresult = simplify_result(xdata.args[3], xdata.args[4], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMAddXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error Value" # -------------------------------------------------------------------------- # AddWithCarry() @@ -181,12 +237,17 @@ def ast_prov( # high-level assignment - lhs = xdata.vars[0] - rhs1 = xdata.xprs[0] - rhs2 = xdata.xprs[1] - rhs3 = xdata.xprs[3] - rrhs1 = xdata.xprs[4] - rrhs2 = xdata.xprs[5] + xd = ARMAddXData(xdata) + if not xdata.is_ok: + chklogger.logger.error("Error value encountered at %s", iaddr) + return ([], []) + + lhs = xd.vrd + rhs1 = xd.xrn + rhs2 = xd.xrm + rhs3 = xd.rresult + rrhs1 = xd.xxrn + rrhs2 = xd.xxrm defuses = xdata.defuses defuseshigh = xdata.defuseshigh @@ -292,8 +353,15 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: chklogger.logger.error( "Second operand pointer variable not yet supported for %s at " - + "address %s", - str(rhs3), iaddr) + + "address %s; rrhs1: %s; hl_rhs1: %s; hl_rhs2: %s; hl_rhs1_type: %s;" + + " hl_rhs2_type: %s", + str(rhs3), + iaddr, + str(rrhs1), + str(hl_rhs1), + str(hl_rhs2), + str(hl_rhs1_type), + str(hl_rhs2_type)) return astree.mk_temp_lval_expression() @@ -331,8 +399,14 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: elif (hl_lhs_type is not None and hl_lhs_type.is_pointer): hl_rhs = pointer_arithmetic_expr() - if rhs3.is_constant_expression: - astree.set_ssa_value(str(hl_lhs), hl_rhs) + if str(hl_rhs).startswith("astmem_tmp"): + chklogger.logger.error( + "Unable to compute pointer arithmetic expression for %s " + "at address %s", + str(rhs3), iaddr) + else: + if rhs3.is_constant_expression: + astree.set_ssa_value(str(hl_lhs), hl_rhs) else: hl_rhs = XU.xxpr_to_ast_def_expr(rhs3, xdata, iaddr, astree) diff --git a/chb/arm/opcodes/ARMAddCarry.py b/chb/arm/opcodes/ARMAddCarry.py index add0a16a..c6bcc010 100644 --- a/chb/arm/opcodes/ARMAddCarry.py +++ b/chb/arm/opcodes/ARMAddCarry.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2023 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -40,11 +40,49 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger if TYPE_CHECKING: - import chb.arm.ARMDictionary + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMAddCarryXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def xrm(self) -> "XXpr": + return self.xpr(1, "xrm") + + @property + def result(self) -> "XXpr": + return self.xpr(2, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(3, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[3], self.xdata.args[4], self.result, self.rresult) + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("ADC", ARMOpcode) @@ -73,10 +111,7 @@ class ARMAddCarry(ARMOpcode): useshigh[0]: lhs """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 5, "AddCarry") @@ -99,18 +134,11 @@ def mnemonic_extension(self) -> str: return wb + cc + wide def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[2] - rresult = xdata.xprs[3] - xresult = simplify_result(xdata.args[3], xdata.args[4], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMAddCarryXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -131,6 +159,8 @@ def ast_prov( defuses = xdata.defuses defuseshigh = xdata.defuseshigh + # low-level assignment + (ll_lhs, _, _) = self.operands[0].ast_lvalue(astree) (ll_op1, _, _) = self.operands[1].ast_rvalue(astree) (ll_op2, _, _) = self.operands[2].ast_rvalue(astree) @@ -143,27 +173,29 @@ def ast_prov( bytestring=bytestring, annotations=annotations) - lhsasts = XU.xvariable_to_ast_lvals(lhs, xdata, astree) - if len(lhsasts) == 0: - raise UF.CHBError("Adc: no lval found") + rdefs = xdata.reachingdefs + + astree.add_expr_reachingdefs(ll_op1, [rdefs[0]]) + astree.add_expr_reachingdefs(ll_op2, [rdefs[1]]) - if len(lhsasts) > 1: - raise UF.CHBError( - "Adc: multiple lvals in ast: " - + ", ".join(str(v) for v in lhsasts)) + # high-level assignment - hl_lhs = lhsasts[0] + xd = ARMAddCarryXData(xdata) + if not xdata.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) - rhsasts = XU.xxpr_to_ast_def_exprs(rhs3, xdata, iaddr, astree) - if len(rhsasts) == 0: - raise UF.CHBError("Adc: no rhs value found") + lhs = xd.vrd + rhs1 = xd.xrn + rhs2 = xd.xrm + rhs3 = xd.rresult - if len(rhsasts) > 1: - raise UF.CHBError( - "Multiple rhs values found for Adc: " - + ", ".join(str(v) for v in rhsasts)) + defuses = xdata.defuses + defuseshigh = xdata.defuseshigh - hl_rhs = rhsasts[0] + hl_lhs = XU.xvariable_to_ast_lval(lhs, xdata, iaddr, astree) + hl_rhs = XU.xxpr_to_ast_def_expr(rhs3, xdata, iaddr, astree) hl_assign = astree.mk_assign( hl_lhs, @@ -172,14 +204,12 @@ def ast_prov( bytestring=bytestring, annotations=annotations) - astree.add_reg_definition(iaddr, hl_lhs, hl_rhs) astree.add_instr_mapping(hl_assign, ll_assign) astree.add_instr_address(hl_assign, [iaddr]) astree.add_expr_mapping(hl_rhs, ll_rhs) astree.add_lval_mapping(hl_lhs, ll_lhs) - astree.add_expr_reachingdefs(ll_rhs, [rdefs[0], rdefs[1]]) - astree.add_expr_reachingdefs(ll_op1, [rdefs[0]]) - astree.add_expr_reachingdefs(ll_op2, [rdefs[1]]) + astree.add_expr_reachingdefs(hl_rhs, rdefs[2:]) + astree.add_expr_reachingdefs(ll_rhs, rdefs[:2]) astree.add_lval_defuses(hl_lhs, defuses[0]) astree.add_lval_defuses_high(hl_lhs, defuseshigh[0]) diff --git a/chb/arm/opcodes/ARMAdr.py b/chb/arm/opcodes/ARMAdr.py index 46d78398..311d151f 100644 --- a/chb/arm/opcodes/ARMAdr.py +++ b/chb/arm/opcodes/ARMAdr.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -39,11 +39,32 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger if TYPE_CHECKING: - import chb.arm.ARMDictionary + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMAdrXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def ximm(self) -> "XXpr": + return self.xpr(0, "ximm") + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + str(self.ximm) + return self.add_instruction_condition(assignment) @armregistry.register_tag("ADR", ARMOpcode) @@ -65,10 +86,7 @@ class ARMAdr(ARMOpcode): useshigh[0]: lhs """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 2, "Adr") @@ -81,16 +99,11 @@ def opargs(self) -> List[ARMOperand]: return [self.armd.arm_operand(i) for i in self.args] def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = str(xdata.xprs[0]) - assignment = lhs + " := " + result - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMAdrXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -116,8 +129,14 @@ def ast_prov( # high-level assignment - lhs = xdata.vars[0] - rhs = xdata.xprs[0] + xd = ARMAdrXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) + + lhs = xd.vrd + rhs = xd.ximm defuses = xdata.defuses defuseshigh = xdata.defuseshigh diff --git a/chb/arm/opcodes/ARMArithmeticShiftRight.py b/chb/arm/opcodes/ARMArithmeticShiftRight.py index cb0cf62f..70e5fbcb 100644 --- a/chb/arm/opcodes/ARMArithmeticShiftRight.py +++ b/chb/arm/opcodes/ARMArithmeticShiftRight.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -39,11 +39,49 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger if TYPE_CHECKING: - import chb.arm.ARMDictionary + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMArithmeticShiftRightXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def xrm(self) -> "XXpr": + return self.xpr(1, "xrm") + + @property + def result(self) -> "XXpr": + return self.xpr(2, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(3, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[3], self.xdata.args[4], self.result, self.rresult) + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("ASR", ARMOpcode) @@ -74,10 +112,7 @@ class ARMArithmeticShiftRight(ARMOpcode): useshigh[0]: lhs """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 5, "ArithmeticShiftRight") @@ -100,18 +135,11 @@ def mnemonic_extension(self) -> str: return wb + cc + wide def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[1] - rresult = xdata.xprs[2] - xresult = simplify_result(xdata.args[3], xdata.args[4], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMArithmeticShiftRightXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -139,10 +167,16 @@ def ast_prov( # high-level assignment - lhs = xdata.vars[0] - rhs1 = xdata.xprs[0] - rhs2 = xdata.xprs[1] - rhs3 = xdata.xprs[3] + xd = ARMArithmeticShiftRightXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) + + lhs = xd.vrd + rhs1 = xd.xrn + rhs2 = xd.xrm + rhs3 = xd.rresult rdefs = xdata.reachingdefs defuses = xdata.defuses defuseshigh = xdata.defuseshigh diff --git a/chb/arm/opcodes/ARMBitwiseAnd.py b/chb/arm/opcodes/ARMBitwiseAnd.py index 595b2a12..16b94378 100644 --- a/chb/arm/opcodes/ARMBitwiseAnd.py +++ b/chb/arm/opcodes/ARMBitwiseAnd.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -39,11 +39,50 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger + if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMBitwiseAndXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def xrm(self) -> "XXpr": + return self.xpr(1, "xrm") + + @property + def result(self) -> "XXpr": + return self.xpr(2, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(3, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[3], self.xdata.args[4], self.result, self.rresult) + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("AND", ARMOpcode) @@ -96,18 +135,11 @@ def mnemonic_extension(self) -> str: return cc + wide def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[2] - rresult = xdata.xprs[3] - xresult = simplify_result(xdata.args[3], xdata.args[4], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMBitwiseAndXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -140,8 +172,14 @@ def ast_prov( # high-level assignment - lhs = xdata.vars[0] - rhs = xdata.xprs[3] + xd = ARMBitwiseAndXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) + + lhs = xd.vrd + rhs = xd.rresult defuses = xdata.defuses defuseshigh = xdata.defuseshigh diff --git a/chb/arm/opcodes/ARMBitwiseBitClear.py b/chb/arm/opcodes/ARMBitwiseBitClear.py index ee0b5038..027fd80a 100644 --- a/chb/arm/opcodes/ARMBitwiseBitClear.py +++ b/chb/arm/opcodes/ARMBitwiseBitClear.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -39,11 +39,45 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMBitwiseBitClearXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrm(self) -> "XXpr": + return self.xpr(0, "xrm") + + @property + def result(self) -> "XXpr": + return self.xpr(1, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(2, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[3], self.xdata.args[4], self.result, self.rresult) + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("BIC", ARMOpcode) @@ -73,10 +107,7 @@ class ARMBitwiseBitClear(ARMOpcode): useshigh[0]: lhs """ - def __init__( - self, - d: "ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 5, "BitwiseBitClear") @@ -89,18 +120,11 @@ def opargs(self) -> List[ARMOperand]: return [self.armd.arm_operand(i) for i in self.args[1: -1]] def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[2] - rresult = xdata.xprs[3] - xresult = simplify_result(xdata.args[3], xdata.args[4], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMBitwiseBitClearXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -134,8 +158,14 @@ def ast_prov( # high-level assignment - lhs = xdata.vars[0] - rhs = xdata.xprs[3] + xd = ARMBitwiseBitClearXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) + + lhs = xd.vrd + rhs = xd.rresult defuses = xdata.defuses defuseshigh = xdata.defuseshigh @@ -149,11 +179,12 @@ def ast_prov( bytestring=bytestring, annotations=annotations) - astree.add_reg_definition(iaddr, hl_lhs, hl_rhs) astree.add_instr_mapping(hl_assign, ll_assign) astree.add_instr_address(hl_assign, [iaddr]) astree.add_expr_mapping(hl_rhs, ll_rhs) astree.add_lval_mapping(hl_lhs, ll_lhs) + astree.add_expr_reachingdefs(hl_rhs, rdefs[1:]) + astree.add_expr_reachingdefs(ll_rhs, [rdefs[0]]) astree.add_lval_defuses(hl_lhs, defuses[0]) astree.add_lval_defuses_high(hl_lhs, defuseshigh[0]) diff --git a/chb/arm/opcodes/ARMBitwiseExclusiveOr.py b/chb/arm/opcodes/ARMBitwiseExclusiveOr.py index b3235cfc..ef0a399e 100644 --- a/chb/arm/opcodes/ARMBitwiseExclusiveOr.py +++ b/chb/arm/opcodes/ARMBitwiseExclusiveOr.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.util.fileutil as UF @@ -39,11 +39,49 @@ from chb.astinterface.ASTInterface import ASTInterface import chb.invariants.XXprUtil as XU - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger if TYPE_CHECKING: - import chb.arm.ARMDictionary + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMBitwiseExclusiveOrXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def xrm(self) -> "XXpr": + return self.xpr(1, "xrm") + + @property + def result(self) -> "XXpr": + return self.xpr(2, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(3, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[3], self.xdata.args[4], self.result, self.rresult) + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("EOR", ARMOpcode) @@ -74,10 +112,7 @@ class ARMBitwiseExclusiveOr(ARMOpcode): useshigh[0]: lhs """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 5, "BitwiseExclusiveOr") @@ -95,18 +130,11 @@ def opargs(self) -> List[ARMOperand]: return [self.armd.arm_operand(i) for i in self.args[1: -1]] def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[2] - rresult = xdata.xprs[3] - xresult = simplify_result(xdata.args[3], xdata.args[4], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMBitwiseExclusiveOrXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -137,8 +165,14 @@ def ast_prov( # high-level assignment - lhs = xdata.vars[0] - rhs = xdata.xprs[3] + xd = ARMBitwiseExclusiveOrXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) + + lhs = xd.vrd + rhs = xd.rresult defuses = xdata.defuses defuseshigh = xdata.defuseshigh diff --git a/chb/arm/opcodes/ARMBitwiseNot.py b/chb/arm/opcodes/ARMBitwiseNot.py index 2e4a3d0a..01aa3efd 100644 --- a/chb/arm/opcodes/ARMBitwiseNot.py +++ b/chb/arm/opcodes/ARMBitwiseNot.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -39,15 +39,50 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger + if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMBitwiseNotXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrm(self) -> "XXpr": + return self.xpr(0, "xrm") + + @property + def result(self) -> "XXpr": + return self.xpr(1, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(2, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[2], self.xdata.args[3], self.result, self.rresult) + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("MVN", ARMOpcode) -class ARMBitwiseBitwiseNot(ARMOpcode): +class ARMBitwiseNot(ARMOpcode): """Bitwise inverse of a register or immediate value. MVN{S} , {, } @@ -70,10 +105,7 @@ class ARMBitwiseBitwiseNot(ARMOpcode): useshigh[0]: lhs """ - def __init__( - self, - d: "ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 4, "BitwiseNot") @@ -96,18 +128,11 @@ def is_writeback(self) -> bool: return self.args[0] == 1 def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[1] - rresult = xdata.xprs[2] - xresult = simplify_result(xdata.args[2], xdata.args[1], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMBitwiseNotXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -134,8 +159,15 @@ def ast_prov( # high-level assignment - lhs = xdata.vars[0] - rhs = xdata.xprs[2] + xd = ARMBitwiseNotXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) + + lhs = xd.vrd + rhs = xd.rresult + rdefs = xdata.reachingdefs defuses = xdata.defuses defuseshigh = xdata.defuseshigh diff --git a/chb/arm/opcodes/ARMBitwiseOr.py b/chb/arm/opcodes/ARMBitwiseOr.py index 32395ab6..d493a08e 100644 --- a/chb/arm/opcodes/ARMBitwiseOr.py +++ b/chb/arm/opcodes/ARMBitwiseOr.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -39,11 +39,49 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger if TYPE_CHECKING: - import chb.arm.ARMDictionary + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMBitwiseOrXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def xrm(self) -> "XXpr": + return self.xpr(1, "xrm") + + @property + def result(self) -> "XXpr": + return self.xpr(2, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(3, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[3], self.xdata.args[4], self.result, self.rresult) + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("ORR", ARMOpcode) @@ -74,10 +112,7 @@ class ARMBitwiseOr(ARMOpcode): useshigh[0]: lhs """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 5, "BitwiseOr") @@ -100,18 +135,11 @@ def opargs(self) -> List[ARMOperand]: return [self.armd.arm_operand(i) for i in self.args[1:-1]] def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[2] - rresult = xdata.xprs[3] - xresult = simplify_result(xdata.args[3], xdata.args[4], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[4]) - return "if " + c + " then " + assignment + xd = ARMBitwiseOrXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -144,24 +172,17 @@ def ast_prov( # high-level assignment - lhs = xdata.vars[0] - rhs1 = xdata.xprs[0] - rhs2 = xdata.xprs[1] - rhs3 = xdata.xprs[3] + xd = ARMBitwiseOrXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) + lhs = xd.vrd + rhs3 = xd.rresult defuses = xdata.defuses defuseshigh = xdata.defuseshigh - astree.add_expr_reachingdefs(ll_op1, [rdefs[0]]) - astree.add_expr_reachingdefs(ll_op2, [rdefs[1]]) - - ll_assign = astree.mk_assign( - ll_lhs, - ll_rhs, - iaddr=iaddr, - bytestring=bytestring, - annotations=annotations) - hl_lhs = XU.xvariable_to_ast_lval(lhs, xdata, iaddr, astree) hl_rhs = XU.xxpr_to_ast_def_expr(rhs3, xdata, iaddr, astree) diff --git a/chb/arm/opcodes/ARMBitwiseOrNot.py b/chb/arm/opcodes/ARMBitwiseOrNot.py index a24793fb..10cec56c 100644 --- a/chb/arm/opcodes/ARMBitwiseOrNot.py +++ b/chb/arm/opcodes/ARMBitwiseOrNot.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2023 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -39,11 +39,54 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger + if TYPE_CHECKING: - import chb.arm.ARMDictionary + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMBitwiseOrNotXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def xrm(self) -> "XXpr": + return self.xpr(1, "xrm") + + @property + def xrmn(self) -> "XXpr": + return self.xpr(2, "xrmn") + + @property + def result(self) -> "XXpr": + return self.xpr(3, "result") + + @property + def rresult(self) -> "XXpr": + return self.xpr(4, "rresult") + + @property + def result_simplified(self) -> str: + return simplify_result( + self.xdata.args[4], self.xdata.args[5], self.result, self.rresult) + + @property + def annotation(self) -> str: + assignment = str(self.vrd) + " := " + self.result_simplified + return self.add_instruction_condition(assignment) @armregistry.register_tag("ORN", ARMOpcode) @@ -74,10 +117,7 @@ class ARMBitwiseOrNot(ARMOpcode): useshigh[0]: lhs """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 4, "BitwiseOrNot") @@ -99,18 +139,11 @@ def is_writeback(self) -> bool: return self.args[0] == 1 def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - result = xdata.xprs[3] - rresult = xdata.xprs[4] - xresult = simplify_result(xdata.args[4], xdata.args[5], result, rresult) - assignment = lhs + " := " + xresult - if xdata.has_unknown_instruction_condition(): - return "if ? then " + assignment - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + assignment + xd = ARMBitwiseOrNotXData(xdata) + if xd.is_ok: + return xd.annotation else: - return assignment + return "Error value" def ast_prov( self, @@ -122,14 +155,6 @@ def ast_prov( annotations: List[str] = [iaddr, "ORN"] - lhs = xdata.vars[0] - rhs1 = xdata.xprs[0] - rhs2 = xdata.xprs[1] - rhs4 = xdata.xprs[4] - rdefs = xdata.reachingdefs - defuses = xdata.defuses - defuseshigh = xdata.defuseshigh - (ll_lhs, _, _) = self.opargs[0].ast_lvalue(astree) (ll_op1, _, _) = self.opargs[1].ast_rvalue(astree) (ll_op2, _, _) = self.opargs[2].ast_rvalue(astree) @@ -143,27 +168,21 @@ def ast_prov( bytestring=bytestring, annotations=annotations) - lhsasts = XU.xvariable_to_ast_lvals(lhs, xdata, astree) - if len(lhsasts) == 0: - raise UF.CHBError("BitwiseOrNot (ORN): no lval found") - - if len(lhsasts) > 1: - raise UF.CHBError( - "BitwiseOrNot (ORN): multiple lvals found: " - + ", ".join(str(v) for v in lhsasts)) - - hl_lhs = lhsasts[0] + xd = ARMBitwiseOrNotXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Encountered error value at address %s", iaddr) + return ([], []) - rhsasts = XU.xxpr_to_ast_def_exprs(rhs4, xdata, iaddr, astree) - if len(rhsasts) == 0: - raise UF.CHBError("BitwiseOrNot (ORN): no lval found") + lhs = xd.vrd + rhs = xd.rresult - if len(rhsasts) > 1: - raise UF.CHBError( - "BitwiseOrNot (ORN): multiple rhs values found: " - + ", ".join(str(v) for v in rhsasts)) + rdefs = xdata.reachingdefs + defuses = xdata.defuses + defuseshigh = xdata.defuseshigh - hl_rhs = rhsasts[0] + hl_lhs = XU.xvariable_to_ast_lval(lhs, xdata, iaddr, astree) + hl_rhs = XU.xxpr_to_ast_def_expr(rhs, xdata, iaddr, astree) hl_assign = astree.mk_assign( hl_lhs, diff --git a/chb/arm/opcodes/ARMBranch.py b/chb/arm/opcodes/ARMBranch.py index f8a78aac..d218f492 100644 --- a/chb/arm/opcodes/ARMBranch.py +++ b/chb/arm/opcodes/ARMBranch.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -32,7 +32,7 @@ from chb.arm.ARMCallOpcode import ARMCallOpcode from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand from chb.arm.ARMOperandKind import ARMOperandKind, ARMAbsoluteOp @@ -57,6 +57,47 @@ from chb.api.CallTarget import CallTarget, AppTarget, StaticStubTarget from chb.arm.ARMDictionary import ARMDictionary from chb.invariants.VConstantValueVariable import VFunctionReturnValue + from chb.invariants.XXpr import XXpr + + +class ARMBranchXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def is_unconditional(self) -> bool: + return len(self._xdata.xprs_r) == 2 + + @property + def txpr(self) -> "XXpr": + return self.xpr(0, "txpr") + + @property + def fxpr(self) -> "XXpr": + return self.xpr(1, "fxpr") + + @property + def tcond(self) -> "XXpr": + return self.xpr(2, "tcond") + + @property + def fcond(self) -> "XXpr": + return self.xpr(3, "fcond") + + @property + def xtgt(self) -> "XXpr": + index = 1 if self.is_unconditional else 4 + return self.xpr(index, "xtgt") + + @property + def annotation(self) -> str: + if self._xdata.has_branch_conditions(): + return "if " + str(self.tcond) + " then goto " + str(self.xtgt) + elif self.is_unconditional: + return "goto " + str(self.xtgt) + else: + return "?" @armregistry.register_tag("B", ARMOpcode) @@ -84,10 +125,7 @@ class ARMBranch(ARMCallOpcode): xprs[0]: target address (absolute) """ - def __init__( - self, - d: "ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMCallOpcode.__init__(self, d, ixval) self.check_key(2, 2, "Branch") @@ -111,8 +149,9 @@ def ft_conditions_basic(self, xdata: InstrXData) -> Sequence[XXpr]: return [] def ft_conditions(self, xdata: InstrXData) -> Sequence[XXpr]: + xd = ARMBranchXData(xdata) if xdata.has_branch_conditions(): - return [xdata.xprs[3], xdata.xprs[2]] + return [xd.fcond, xd.tcond] else: return [] @@ -143,16 +182,16 @@ def jump_target(self, xdata: InstrXData) -> Optional["XXpr"]: return xdata.xprs[0] def annotation(self, xdata: InstrXData) -> str: - if self.is_call_instruction(xdata): - tgt = xdata.call_target(self.ixd) - args = ", ".join(str(x) for x in self.arguments(xdata)) - return "call " + str(tgt) + "(" + args + ")" - elif xdata.has_branch_conditions(): - return "if " + str(xdata.xprs[2]) + " then goto " + str(xdata.xprs[4]) - elif self.tags[1] in ["a", "unc"]: - return "goto " + str(xdata.xprs[0]) + xd = ARMBranchXData(xdata) + if xd.is_ok: + if self.is_call_instruction(xdata): + tgt = xdata.call_target(self.ixd) + args = ", ".join(str(x) for x in self.arguments(xdata)) + return "call " + str(tgt) + "(" + args + ")" + else: + return xd.annotation else: - return "if ? goto " + str(xdata.xprs[0]) + return "Error value" def target_expr_ast( self, diff --git a/chb/arm/opcodes/ARMBranchExchange.py b/chb/arm/opcodes/ARMBranchExchange.py index 830b4686..5149ede4 100644 --- a/chb/arm/opcodes/ARMBranchExchange.py +++ b/chb/arm/opcodes/ARMBranchExchange.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -31,7 +31,7 @@ from chb.arm.ARMCallOpcode import ARMCallOpcode from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -42,12 +42,47 @@ import chb.util.fileutil as UF from chb.util.loggingutil import chklogger - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger + if TYPE_CHECKING: from chb.api.CallTarget import CallTarget, AppTarget, StaticStubTarget - import chb.arm.ARMDictionary + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMBranchExchangeXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def xtgt(self) -> "XXpr": + return self.xpr(0, "xtgt") + + @property + def xxtgt(self) -> "XXpr": + return self.xpr(1, "xxtgt") + + def has_return_xpr(self) -> bool: + return self.xdata.has_return_xpr() + + def returnval(self) -> "XXpr": + return self.xdata.get_return_xpr() + + def rreturnval(self) -> "XXpr": + return self.xdata.get_return_xxpr() + + @property + def annotation(self) -> str: + if self.xdata.is_bx_call: + return "bx-call" + if self.has_return_xpr(): + return "return " + str(self.rreturnval()) + else: + return "Not supported yet" @armregistry.register_tag("BX", ARMOpcode) @@ -58,10 +93,7 @@ class ARMBranchExchange(ARMCallOpcode): args[0]: index of target operand in armdictionary """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 1, "BranchExchange") @@ -83,8 +115,9 @@ def is_return_instruction(self, xdata: InstrXData) -> bool: return False def return_value(self, xdata: InstrXData) -> Optional[XXpr]: - if self.is_return_instruction(xdata): - return xdata.xprs[1] + xd = ARMBranchExchangeXData(xdata) + if xd.has_return_xpr(): + return xd.rreturnval() else: return None @@ -92,7 +125,8 @@ def is_call(self, xdata: InstrXData) -> bool: return len(xdata.tags) >= 2 and xdata.tags[1] == "call" def is_call_instruction(self, xdata: InstrXData) -> bool: - return xdata.has_call_target() + # return xdata.has_call_target() + return False def call_target(self, xdata: InstrXData) -> "CallTarget": if self.is_call_instruction(xdata): @@ -124,11 +158,13 @@ def annotation(self, xdata: InstrXData) -> str: args = ", ".join(str(x) for x in self.arguments(xdata)) return "call " + str(tgt) + "(" + args + ")" - tgtop = str(xdata.xprs[0]) - if tgtop == "LR": - return "return" + xd = ARMBranchExchangeXData(xdata) + if xd.has_return_xpr and xd.is_ok: + return xd.annotation + elif xd.is_ok: + return "goto " + str(xd.xxtgt) else: - return "goto " + tgtop + return "Error value" def assembly_ast( self, diff --git a/chb/arm/opcodes/ARMBranchLink.py b/chb/arm/opcodes/ARMBranchLink.py index cc9c80df..435ed547 100644 --- a/chb/arm/opcodes/ARMBranchLink.py +++ b/chb/arm/opcodes/ARMBranchLink.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 diff --git a/chb/arm/opcodes/ARMCompare.py b/chb/arm/opcodes/ARMCompare.py index c384f1e8..eb1be2da 100644 --- a/chb/arm/opcodes/ARMCompare.py +++ b/chb/arm/opcodes/ARMCompare.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -39,16 +39,52 @@ import chb.invariants.XXprUtil as XU import chb.util.fileutil as UF - from chb.util.IndexedTable import IndexedTableValue +from chb.util.loggingutil import chklogger + if TYPE_CHECKING: - import chb.arm.ARMDictionary + from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XXpr import XXpr + + +class ARMCompareXData(ARMOpcodeXData): + """ + xdata format: a:xxxrr + --------------------- + xprs[0]: xrn + xprs[1]: xrm + xprs[2]: xrn - xrm (simplified) + rdefs[0]: rn + rdefs[1]: rm + rdefs[2..]: xrn - xrm (simplified) + """ + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def xrm(self) -> "XXpr": + return self.xpr(1, "xrm") + + @property + def result(self) -> "XXpr": + return self.xpr(2, "result") + + @property + def annotation(self) -> str: + ann = "compare " + str(self.xrn) + " and " + str(self.xrm) + ann += " (" + str(self.result) + ")" + return self.add_instruction_condition(ann) @armregistry.register_tag("CMP", ARMOpcode) class ARMCompare(ARMOpcode): - """Subtracts a register or immediate value from a register value and sets flags. + """Subtracts a register or imm. value from a register value and sets flags. CMP , # CMP.W , # @@ -61,21 +97,9 @@ class ARMCompare(ARMOpcode): args[0]: index of rn in armdictionary args[1]: index of rm in armdictionary args[2]: is-wide (thumb) - - xdata format: a:xxxrr - --------------------- - xprs[0]: xrn - xprs[1]: xrm - xprs[2]: xrn - xrm (simplified) - rdefs[0]: rn - rdefs[1]: rm - rdefs[2..]: xrn - xrm (simplified) """ - def __init__( - self, - d: "chb.arm.ARMDictionary.ARMDictionary", - ixval: IndexedTableValue) -> None: + def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 3, "Compare") @@ -93,17 +117,11 @@ def opargs(self) -> List[ARMOperand]: return [self.armd.arm_operand(i) for i in self.args[:-1]] def annotation(self, xdata: InstrXData) -> str: - rhs1 = str(xdata.xprs[0]) - rhs2 = str(xdata.xprs[1]) - result = str(xdata.xprs[2]) - ann = "compare " + str(rhs1) + " and " + str(rhs2) + " (" + result + ")" - if xdata.has_unknown_instruction_condition(): - return "if ? then " + ann - elif xdata.has_instruction_condition(): - c = str(xdata.xprs[1]) - return "if " + c + " then " + ann + xd = ARMCompareXData(xdata) + if xd.is_ok: + return xd.annotation else: - return ann + return "Error value" def ast_prov( self, @@ -131,7 +149,13 @@ def ast_prov( # high-level assignment - rhs = xdata.xprs[2] + xd = ARMCompareXData(xdata) + if not xd.is_ok: + chklogger.logger.error( + "Error value encountered at address %s", iaddr) + return ([], []) + + rhs = xd.result rdefs = xdata.reachingdefs hl_rhs = XU.xxpr_to_ast_def_expr(rhs, xdata, iaddr, astree) diff --git a/chb/arm/opcodes/ARMCompareBranchNonzero.py b/chb/arm/opcodes/ARMCompareBranchNonzero.py index 73abaf3f..5e678f60 100644 --- a/chb/arm/opcodes/ARMCompareBranchNonzero.py +++ b/chb/arm/opcodes/ARMCompareBranchNonzero.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2024 Aarno Labs LLC +# Copyright (c) 2021-2025 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 @@ -30,7 +30,7 @@ from chb.app.InstrXData import InstrXData from chb.arm.ARMDictionaryRecord import armregistry -from chb.arm.ARMOpcode import ARMOpcode, simplify_result +from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result from chb.arm.ARMOperand import ARMOperand import chb.ast.ASTNode as AST @@ -48,10 +48,46 @@ if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XXpr import XXpr + + +class ARMCompareBranchNonZeroXData(ARMOpcodeXData): + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def xrn(self) -> "XXpr": + return self.xpr(0, "xrn") + + @property + def txpr(self) -> "XXpr": + return self.xpr(1, "txpr") + + @property + def fxpr(self) -> "XXpr": + return self.xpr(2, "fxpr") + + @property + def tcond(self) -> "XXpr": + return self.xpr(3, "tcond") + + @property + def fcond(self) -> "XXpr": + return self.xpr(4, "fcond") + + @property + def xtgt(self) -> "XXpr": + return self.xpr(5, "xtgt") + + @property + def annotation(self) -> str: + return "if " + str(self.tcond) + " goto " + str(self.xtgt) + @armregistry.register_tag("CBNZ", ARMOpcode) -class ARMCompareBranchZero(ARMOpcode): +class ARMCompareBranchNonZero(ARMOpcode): """Compares the value in a register with zero and conditionally branches forward. CBNZ ,