diff --git a/chb/app/CHVersion.py b/chb/app/CHVersion.py index ec5ca2b0..7ba54b9c 100644 --- a/chb/app/CHVersion.py +++ b/chb/app/CHVersion.py @@ -1 +1 @@ -chbversion: str = "0.3.0-20250210" +chbversion: str = "0.3.0-20250308" diff --git a/chb/arm/ARMCallOpcode.py b/chb/arm/ARMCallOpcode.py index ed4ccd09..50d40634 100644 --- a/chb/arm/ARMCallOpcode.py +++ b/chb/arm/ARMCallOpcode.py @@ -240,7 +240,12 @@ def ast_call_prov( ctinfo = finfo.call_target_info(iaddr) ftype = ctinfo.target_interface.bctype if ftype is not None: - astfntype = ftype.convert(astree.typconverter) + try: + astfntype = ftype.convert(astree.typconverter) + except UF.CHBError as e: + chklogger.logger.warning( + "Type conversion of function type was unsuccessful: %s", + str(e)) if xdata.is_bx_call: # indirect call diff --git a/chb/arm/opcodes/ARMAdd.py b/chb/arm/opcodes/ARMAdd.py index a3c6357c..ede2a66d 100644 --- a/chb/arm/opcodes/ARMAdd.py +++ b/chb/arm/opcodes/ARMAdd.py @@ -237,6 +237,11 @@ def ast_prov( # high-level assignment + def has_cast() -> bool: + return ( + astree.has_register_variable_intro(iaddr) + and astree.get_register_variable_intro(iaddr).has_cast()) + xd = ARMAddXData(xdata) if not xdata.is_ok: chklogger.logger.error("Error value encountered at %s", iaddr) @@ -252,16 +257,10 @@ def ast_prov( defuses = xdata.defuses defuseshigh = xdata.defuseshigh - if rhs3.is_string_reference: - ctype = astree.astree.mk_pointer_type(astree.astree.char_type) - hl_lhs = XU.xvariable_to_ast_lvals( - lhs, - xdata, - astree, - ispointer=True, - ctype=ctype)[0] - else: - hl_lhs = XU.xvariable_to_ast_lval(lhs, xdata, iaddr, astree) + hl_lhs = XU.xvariable_to_ast_lval(lhs, xdata, iaddr, astree) + + def hl_rhs_default () -> AST.ASTExpr: + return XU.xxpr_to_ast_def_expr(rhs3, xdata, iaddr, astree) if str(lhs) == "PC": chklogger.logger.info( @@ -276,11 +275,11 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: hl_rhs2_type = hl_rhs2.ctype(astree.ctyper) if hl_rhs1_type is None and hl_rhs2_type is None: - chklogger.logger.error( + chklogger.logger.info( "Unable to lift pointer arithmetic without type for " + "%s at address %s", str(rhs3), iaddr) - return astree.mk_temp_lval_expression() + return hl_rhs_default() if hl_rhs2_type is not None and hl_rhs2_type.is_pointer: rhs2tgttyp = cast(AST.ASTTypPtr, hl_rhs2_type).tgttyp @@ -290,7 +289,6 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: "Unable to lift pointer arithmetic without size for " + "%s at address %s; set type size to 1", str(hl_rhs2_type), iaddr) - # return astree.mk_temp_lval_expression() tgttypsize = 1 if tgttypsize == 1: @@ -311,11 +309,11 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: rhs1tgttyp = cast(AST.ASTTypPtr, hl_rhs1_type).tgttyp tgttypsize = astree.type_size_in_bytes(rhs1tgttyp) if tgttypsize is None: - chklogger.logger.error( + chklogger.logger.info( "Unable to lift pointer arithmetic without size for " + "%s at address %s", str(hl_rhs1_type), iaddr) - return astree.mk_temp_lval_expression() + return hl_rhs_default() if hl_rhs1.is_ast_startof: arraylval = cast(AST.ASTStartOf, hl_rhs1).lval @@ -345,13 +343,13 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: return astree.mk_binary_op("plus", hl_rhs1, scaled) if hl_rhs2_type is None: - chklogger.logger.error( + chklogger.logger.info( "Unable to lift pointer arithmetic without type for " + "%s at address %s", str(rhs2), iaddr) - return astree.mk_temp_lval_expression() + return hl_rhs_default() - chklogger.logger.error( + chklogger.logger.info( "Second operand pointer variable not yet supported for %s at " + "address %s; rrhs1: %s; hl_rhs1: %s; hl_rhs2: %s; hl_rhs1_type: %s;" + " hl_rhs2_type: %s", @@ -362,8 +360,7 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: str(hl_rhs2), str(hl_rhs1_type), str(hl_rhs2_type)) - return astree.mk_temp_lval_expression() - + return hl_rhs_default() # resulting expression is a stack address if ( @@ -397,7 +394,7 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: if rhs3.is_constant_expression: astree.set_ssa_value(str(hl_lhs), hl_rhs) - elif (hl_lhs_type is not None and hl_lhs_type.is_pointer): + elif (hl_lhs_type is not None and hl_lhs_type.is_pointer and not has_cast()): hl_rhs = pointer_arithmetic_expr() if str(hl_rhs).startswith("astmem_tmp"): chklogger.logger.error( @@ -408,7 +405,7 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: 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) + hl_rhs = hl_rhs_default () hl_assign = astree.mk_assign( hl_lhs, @@ -435,4 +432,9 @@ def pointer_arithmetic_expr() -> AST.ASTExpr: astree.add_lval_defuses(hl_lhs, defuses[0]) astree.add_lval_defuses_high(hl_lhs, defuseshigh[0]) + if astree.has_register_variable_intro(iaddr): + rvintro = astree.get_register_variable_intro(iaddr) + if rvintro.has_cast(): + astree.add_expose_instruction(hl_assign.instrid) + return ([hl_assign], [ll_assign]) diff --git a/chb/arm/opcodes/ARMBitFieldInsert.py b/chb/arm/opcodes/ARMBitFieldInsert.py index c1ac1fae..812de7a5 100644 --- a/chb/arm/opcodes/ARMBitFieldInsert.py +++ b/chb/arm/opcodes/ARMBitFieldInsert.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.arm.ARMPseudoCode as APC @@ -46,6 +46,27 @@ if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMBitFieldInsertXData(ARMOpcodeXData): + """BFI """ + + def __init__(self, xdata: InstrXData) -> None: + ARMOpcodeXData.__init__(self, xdata) + + @property + def vrd(self) -> "XVariable": + return self.var(0, "vrd") + + @property + def xrd(self) -> "XXpr": + return self.xpr(0, "xrd") + + @property + def xrn(self) -> "XXpr": + return self.xpr(1, "xrn") @armregistry.register_tag("BFI", ARMOpcode) @@ -102,26 +123,30 @@ def width(self) -> int: return self.args[2] def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - rhs1 = str(xdata.xprs[0]) - rhs2 = str(xdata.xprs[1]) - assignment = ( - lhs - + " := bit-field-insert(" - + rhs1 - + ", " - + rhs2 - + ", lsb:" - + str(self.lsb) - + ", width:" - + str(self.width)) - 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 = ARMBitFieldInsertXData(xdata) + if xd.is_ok: + lhs = str(xd.vrd) + rhs1 = str(xd.xrd) + rhs2 = str(xd.xrn) + assignment = ( + lhs + + " := bit-field-insert(" + + rhs1 + + ", " + + rhs2 + + ", lsb:" + + str(self.lsb) + + ", width:" + + str(self.width)) + 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 + else: + return assignment else: - return assignment + return "Error value" def ast_prov( self, diff --git a/chb/arm/opcodes/ARMBranch.py b/chb/arm/opcodes/ARMBranch.py index d218f492..e49fba46 100644 --- a/chb/arm/opcodes/ARMBranch.py +++ b/chb/arm/opcodes/ARMBranch.py @@ -90,14 +90,29 @@ def xtgt(self) -> "XXpr": index = 1 if self.is_unconditional else 4 return self.xpr(index, "xtgt") + @property + def is_xtgt_known(self) -> bool: + index = 1 if self.is_unconditional else 4 + return self.xdata.xprs_r[index] is not None + @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) + if self.is_ok: + 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 "?" + elif self.is_xtgt_known: + if self._xdata.has_branch_conditions(): + return "if " + str(self.txpr) + " then goto " + str(self.xtgt) + elif self.is_unconditional: + return "goto " + str(self.xtgt) + else: + return "?" else: - return "?" + return "Error value" @armregistry.register_tag("B", ARMOpcode) @@ -151,7 +166,10 @@ def ft_conditions_basic(self, xdata: InstrXData) -> Sequence[XXpr]: def ft_conditions(self, xdata: InstrXData) -> Sequence[XXpr]: xd = ARMBranchXData(xdata) if xdata.has_branch_conditions(): - return [xd.fcond, xd.tcond] + if xd.is_ok: + return [xd.fcond, xd.tcond] + else: + return [xd.fxpr, xd.txpr] else: return [] @@ -191,7 +209,7 @@ def annotation(self, xdata: InstrXData) -> str: else: return xd.annotation else: - return "Error value" + return xd.annotation def target_expr_ast( self, @@ -264,6 +282,8 @@ def default(condition: XXpr) -> AST.ASTExpr: # case there are no rewritten variables, but this still has to be # validated with more instances. + xd = ARMBranchXData(xdata) + ftconds_basic = self.ft_conditions(xdata) ftconds = self.ft_conditions(xdata) diff --git a/chb/arm/opcodes/ARMByteReverseWord.py b/chb/arm/opcodes/ARMByteReverseWord.py index d96c3695..595dd878 100644 --- a/chb/arm/opcodes/ARMByteReverseWord.py +++ b/chb/arm/opcodes/ARMByteReverseWord.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 @@ -44,6 +44,28 @@ if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMByteReverseWordXData(ARMOpcodeXData): + """REV """ + + 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 xxrn(self) -> "XXpr": + return self.xpr(1, "xxrn") + @armregistry.register_tag("REV", ARMOpcode) @@ -89,16 +111,14 @@ def opargs(self) -> List[ARMOperand]: return [self.armd.arm_operand(i) for i in self.args[:-1]] def annotation(self, xdata: InstrXData) -> str: - lhs = str(xdata.vars[0]) - rhs = str(xdata.xprs[1]) - assignment = lhs + " := __rev(" + str(rhs) + ") intrinsic" - 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 - else: + xd = ARMByteReverseWordXData(xdata) + if xd.is_ok: + lhs = str(xd.vrd) + rhs = str(xd.xxrn) + assignment = lhs + " := __rev(" + str(rhs) + ") intrinsic" return assignment + else: + return "Error value" # -------------------------------------------------------------------------- # Operation diff --git a/chb/arm/opcodes/ARMCompare.py b/chb/arm/opcodes/ARMCompare.py index eb1be2da..291cb8e1 100644 --- a/chb/arm/opcodes/ARMCompare.py +++ b/chb/arm/opcodes/ARMCompare.py @@ -78,7 +78,8 @@ def result(self) -> "XXpr": @property def annotation(self) -> str: ann = "compare " + str(self.xrn) + " and " + str(self.xrm) - ann += " (" + str(self.result) + ")" + if self.is_ok: + ann += " (" + str(self.result) + ")" return self.add_instruction_condition(ann) @@ -118,10 +119,7 @@ def opargs(self) -> List[ARMOperand]: def annotation(self, xdata: InstrXData) -> str: xd = ARMCompareXData(xdata) - if xd.is_ok: - return xd.annotation - else: - return "Error value" + return xd.annotation def ast_prov( self, @@ -149,16 +147,17 @@ def ast_prov( # high-level assignment - 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) + xd = ARMCompareXData(xdata) + if xd.is_ok: + rhs = xd.result + hl_rhs = XU.xxpr_to_ast_def_expr(rhs, xdata, iaddr, astree) + else: + rhs1 = xd.xrn + rhs2 = xd.xrm + hl_rhs1 = XU.xxpr_to_ast_def_expr(rhs1, xdata, iaddr, astree) + hl_rhs2 = XU.xxpr_to_ast_def_expr(rhs2, xdata, iaddr, astree) + hl_rhs = astree.mk_binary_op("minus", hl_rhs1, hl_rhs2) hl_assign = astree.mk_assign( astree.ignoredlhs, diff --git a/chb/arm/opcodes/ARMCountLeadingZeros.py b/chb/arm/opcodes/ARMCountLeadingZeros.py index a248190b..09cf40fd 100644 --- a/chb/arm/opcodes/ARMCountLeadingZeros.py +++ b/chb/arm/opcodes/ARMCountLeadingZeros.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021-2022 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,6 +44,28 @@ if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMCountLeadingZerosXData(ARMOpcodeXData): + """CLZ """ + + 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 xxrn(self) -> "XXpr": + return self.xpr(1, "xxrn") + @armregistry.register_tag("CLZ", ARMOpcode) @@ -82,16 +104,14 @@ 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]) - rhs = str(xdata.xprs[1]) - assignment = lhs + " := __clz(" + rhs + ") (intrinsic)" - 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 - else: + xd = ARMCountLeadingZerosXData(xdata) + if xd.is_ok: + lhs = str(xd.vrd) + rhs = str(xd.xxrn) + assignment = lhs + " := __clz(" + rhs + ") (intrinsic)" return assignment + else: + return "Error value" def ast_prov( self, diff --git a/chb/arm/opcodes/ARMLoadRegisterByte.py b/chb/arm/opcodes/ARMLoadRegisterByte.py index e383c8a4..c4eeb59b 100644 --- a/chb/arm/opcodes/ARMLoadRegisterByte.py +++ b/chb/arm/opcodes/ARMLoadRegisterByte.py @@ -233,12 +233,6 @@ def ast_prov( defuses = xdata.defuses defuseshigh = xdata.defuseshigh - ''' - hl_lhs = XU.xvariable_to_ast_lval(lhs, xdata, iaddr, astree) - hl_rhs = XU.xxpr_to_ast_def_expr( - rhs, xdata, iaddr, astree, size=1, memaddr=xaddr) - ''' - hl_assign = astree.mk_assign( hl_lhs, hl_rhs, @@ -269,8 +263,8 @@ def ast_prov( annotations=annotations) ll_assigns: List[AST.ASTInstruction] = [ll_assign, ll_addr_assign] - basereg = xdata.vars[1] - newaddr = xdata.xprs[4] + basereg = xd.get_base_update_var() + newaddr = xd.get_base_update_xpr() hl_addr_lhs = XU.xvariable_to_ast_lval(basereg, xdata, iaddr, astree) hl_addr_rhs = XU.xxpr_to_ast_def_expr(newaddr, xdata, iaddr, astree) diff --git a/chb/arm/opcodes/ARMMultiplySubtract.py b/chb/arm/opcodes/ARMMultiplySubtract.py index 33dfd60c..4463e46e 100644 --- a/chb/arm/opcodes/ARMMultiplySubtract.py +++ b/chb/arm/opcodes/ARMMultiplySubtract.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021 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,6 +39,47 @@ if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMMultiplySubtractXData(ARMOpcodeXData): + """MLS , , , """ + + 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 xra(self) -> "XXpr": + return self.xpr(2, "xra") + + @property + def xprod(self) -> "XXpr": + return self.xpr(3, "xprod") + + @property + def xxprod(self) -> "XXpr": + return self.xpr(4, "xxprod") + + @property + def xdiff(self) -> "XXpr": + return self.xpr(5, "xdiff") + + @property + def xxdiff(self) -> "XXpr": + return self.xpr(6, "xxdiff") @armregistry.register_tag("MLS", ARMOpcode) @@ -78,11 +119,15 @@ def annotation(self, xdata: InstrXData) -> str: xprs[6]: (rhsra - (rhs1 * rhs2)) (simplified) """ - lhs = str(xdata.vars[0]) - prod = xdata.xprs[3] - rprod = xdata.xprs[4] - xprod = simplify_result(xdata.args[4], xdata.args[5], prod, rprod) - diff = xdata.xprs[5] - rdiff = xdata.xprs[6] - xdiff = simplify_result(xdata.args[6], xdata.args[7], diff, rdiff) - return (lhs + " := " + xdiff) + xd = ARMMultiplySubtractXData(xdata) + if xd.is_ok: + lhs = str(xd.vrd) + xprod = xd.xprod + xxprod = xd.xxprod + rprod = simplify_result(xdata.args[4], xdata.args[5], xprod, xxprod) + xdiff = xd.xdiff + xxdiff = xd.xxdiff + rdiff = simplify_result(xdata.args[6], xdata.args[7], xdiff, xxdiff) + return (lhs + " := " + rdiff) + else: + return "Error value" diff --git a/chb/arm/opcodes/ARMPush.py b/chb/arm/opcodes/ARMPush.py index 3d91caab..66806bf0 100644 --- a/chb/arm/opcodes/ARMPush.py +++ b/chb/arm/opcodes/ARMPush.py @@ -156,7 +156,7 @@ def memory_accesses(self, xdata: InstrXData) -> Sequence[MemoryAccess]: if len(spills) > 0: result: List[RegisterSpill] = [] for (i, spill) in enumerate(spills): - result.append(RegisterSpill(xd.rrhsexprs[i], spill)) + result.append(RegisterSpill(xd.xaddrs[i], spill)) return result else: return [MemoryAccess(xdata.xprs[2], "W", size=4)] diff --git a/chb/arm/opcodes/ARMSignedExtendByte.py b/chb/arm/opcodes/ARMSignedExtendByte.py index b2e3f170..d7c7a6c5 100644 --- a/chb/arm/opcodes/ARMSignedExtendByte.py +++ b/chb/arm/opcodes/ARMSignedExtendByte.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.util.fileutil as UF @@ -39,6 +39,27 @@ if TYPE_CHECKING: import chb.arm.ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMSignedExtendByteXData(ARMOpcodeXData): + """SXTB , """ + + 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 xxrn(self) -> "XXpr": + return self.xpr(1, "xxrn") @armregistry.register_tag("SXTB", ARMOpcode) @@ -77,6 +98,10 @@ def annotation(self, xdata: InstrXData) -> str: xprs[1]: rhs (simplified) """ - lhs = str(xdata.vars[0]) - result = str(xdata.xprs[1]) - return lhs + " := " + result + xd = ARMSignedExtendByteXData(xdata) + if xd.is_ok: + lhs = str(xd.vrd) + result = str(xd.xxrn) + return lhs + " := " + result + else: + return "Error value" diff --git a/chb/arm/opcodes/ARMStoreMultipleIncrementAfter.py b/chb/arm/opcodes/ARMStoreMultipleIncrementAfter.py index 19fdcd9d..f568e76c 100644 --- a/chb/arm/opcodes/ARMStoreMultipleIncrementAfter.py +++ b/chb/arm/opcodes/ARMStoreMultipleIncrementAfter.py @@ -104,6 +104,26 @@ def copysize(self) -> int: else: raise UF.CHBError("Not an ldmstm aggregate") + @property + def regcount(self) -> int: + return len(self.xdata.vars_r) - 1 + + @property + def baselhs(self) -> "XVariable": + return self.var(0, "baselhs") + + @property + def is_baselhs_known(self) -> bool: + return self.xdata.vars_r[0] is not None + + @property + def memlhss(self) -> List["XVariable"]: + return [self.var(i, "memlhs-" + str(i)) for i in range(1, self.regcount + 1)] + + @property + def rhss(self) -> List["XXpr"]: + return [self.xpr(i, "rhs-" + str(i)) for i in range(3, self.regcount + 3)] + @property def annotation(self) -> str: if self.is_ok: @@ -117,7 +137,10 @@ def annotation(self) -> str: + str(self.copysize) + ")") else: - return "not yet supported" + assigns = [] + for (memlhs, rhs) in zip(self.memlhss, self.rhss): + assigns.append(str(memlhs) + " := " + str(rhs)) + return "; ".join(assigns) else: if self.is_ldmstm_aggregate: if self.is_xxdst_unknown and self.is_xxsrc_unknown: diff --git a/chb/arm/opcodes/ARMStoreRegister.py b/chb/arm/opcodes/ARMStoreRegister.py index 80d86eb1..84c18905 100644 --- a/chb/arm/opcodes/ARMStoreRegister.py +++ b/chb/arm/opcodes/ARMStoreRegister.py @@ -65,6 +65,10 @@ def __init__(self, xdata: InstrXData) -> None: def vmem(self) -> "XVariable": return self.var(0, "vmem") + @property + def is_vmem_known(self) -> bool: + return self.xdata.vars_r[0] is not None + @property def is_vmem_unknown(self) -> bool: return self.xdata.vars_r[0] is None @@ -81,6 +85,10 @@ def is_lhsvar_unknown(self) -> bool: def is_lhsvar_known(self) -> bool: return self.xdata.vars_r[1] is not None + @property + def is_xxrtc_known(self) -> bool: + return self.xdata.xprs_r[4] is not None + @property def vrn(self) -> "XVariable": return self.var(1, "vrn") @@ -101,25 +109,30 @@ def xrt(self) -> "XXpr": def xxrt(self) -> "XXpr": return self.xpr(3, "xxrt") + @property + def xxrtc(self) -> "XXpr": + return self.xpr(4, "xxrtc") + @property def xaddr(self) -> "XXpr": - return self.xpr(4, "xaddr") + return self.xpr(5, "xaddr") @property def is_address_known(self) -> bool: - return self.xdata.xprs_r[4] is not None + return self.xdata.xprs_r[5] is not None @property def xaddr_updated(self) -> "XXpr": - return self.xpr(5, "xaddr_updated") + return self.xpr(6, "xaddr_updated") @property def annotation(self) -> str: wbu = self.writeback_update() - if self.is_ok: - assignment = str(self.vmem) + " := " + str(self.xxrt) + rhs = self.xxrtc if self.is_xxrtc_known else self.xxrt + if self.is_ok or self.is_vmem_known: + assignment = str(self.vmem) + " := " + str(rhs) elif self.is_vmem_unknown and self.is_address_known: - assignment = "*(" + str(self.xaddr) + ") := " + str(self.xxrt) + assignment = "*(" + str(self.xaddr) + ") := " + str(rhs) else: assignment = "Error value" return self.add_instruction_condition(assignment + wbu) @@ -240,7 +253,7 @@ def ast_prov( # high-level assignment - if xd.is_ok: + if xd.is_ok or xd.is_vmem_known: lhs = xd.vmem memaddr = xd.xaddr hl_lhs = XU.xvariable_to_ast_lval( @@ -293,6 +306,8 @@ def ast_prov( # assignments must be explicitly forced to appear in the lifting if ( xd.is_vmem_unknown + or lhs.is_memory_variable and cast("VMemoryVariable", + lhs.denotation).base.is_basevar or hl_lhs.offset.is_index_offset or hl_lhs.offset.is_field_offset): astree.add_expose_instruction(hl_assign.instrid) diff --git a/chb/arm/opcodes/ARMStoreRegisterHalfword.py b/chb/arm/opcodes/ARMStoreRegisterHalfword.py index bae3e6ee..58356e30 100644 --- a/chb/arm/opcodes/ARMStoreRegisterHalfword.py +++ b/chb/arm/opcodes/ARMStoreRegisterHalfword.py @@ -79,6 +79,10 @@ def xrt(self) -> "XXpr": def xxrt(self) -> "XXpr": return self.xpr(3, "xxrt") + @property + def is_xxrt_known(self) -> bool: + return self.xdata.xprs_r[3] is not None + @property def xaddr(self) -> "XXpr": return self.xpr(4, "xaddr") @@ -92,6 +96,8 @@ def annotation(self) -> str: wbu = self.writeback_update() if self.is_ok: assignment = str(self.vmem) + " := " + str(self.xxrt) + elif not self.is_xxrt_known: + assignment = "(*" + str(self.xaddr) + ") := " + str(self.xrt) elif self.is_vmem_unknown and self.is_address_known: assignment = "*(" + str(self.xaddr) + ") := " + str(self.xxrt) else: @@ -223,6 +229,8 @@ def ast_prov( # assignments must be explicitly forced to appear in the lifting if ( xd.is_vmem_unknown + or lhs.is_memory_variable and cast("VMemoryVariable", + lhs.denotation).base.is_basevar or hl_lhs.offset.is_index_offset or hl_lhs.offset.is_field_offset): astree.add_expose_instruction(hl_assign.instrid) diff --git a/chb/arm/opcodes/ARMTest.py b/chb/arm/opcodes/ARMTest.py index a3e3c449..449d6922 100644 --- a/chb/arm/opcodes/ARMTest.py +++ b/chb/arm/opcodes/ARMTest.py @@ -87,6 +87,10 @@ def __init__(self, d: "ARMDictionary", ixval: IndexedTableValue) -> None: ARMOpcode.__init__(self, d, ixval) self.check_key(2, 3, "Test") + @property + def opargs(self) -> List[ARMOperand]: + return [self.armd.arm_operand(self.args[i]) for i in [0, 1]] + @property def operands(self) -> List[ARMOperand]: return [self.armd.arm_operand(self.args[i]) for i in [0, 1]] diff --git a/chb/arm/opcodes/ARMUnsignedExtendAddHalfword.py b/chb/arm/opcodes/ARMUnsignedExtendAddHalfword.py index b4cb2d89..f453e9a7 100644 --- a/chb/arm/opcodes/ARMUnsignedExtendAddHalfword.py +++ b/chb/arm/opcodes/ARMUnsignedExtendAddHalfword.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2021 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,6 +39,27 @@ if TYPE_CHECKING: from chb.arm.ARMDictionary import ARMDictionary + from chb.invariants.XVariable import XVariable + from chb.invariants.XXpr import XXpr + + +class ARMUnsignedExtendAddHalfwordXData(ARMOpcodeXData): + """UXTAH , , """ + + 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") @armregistry.register_tag("UXTAH", ARMOpcode) @@ -72,7 +93,11 @@ def annotation(self, xdata: InstrXData) -> str: xprs[1]: rhs2 """ - lhs = str(xdata.vars[0]) - op1 = str(xdata.xprs[0]) - op2 = str(xdata.xprs[1]) - return lhs + ":= extend_add_halfword(" + op1 + ", " + op2 + ")" + xd = ARMUnsignedExtendAddHalfwordXData(xdata) + if xd.is_ok: + lhs = str(xd.vrd) + op1 = str(xd.xrn) + op2 = str(xd.xrm) + return lhs + ":= extend_add_halfword(" + op1 + ", " + op2 + ")" + else: + return "Error value" diff --git a/chb/ast/ASTNode.py b/chb/ast/ASTNode.py index ddfc4185..bc0b2fd5 100644 --- a/chb/ast/ASTNode.py +++ b/chb/ast/ASTNode.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 diff --git a/chb/cmdline/AnalysisManager.py b/chb/cmdline/AnalysisManager.py index 5677ed0a..1aa1d8ee 100644 --- a/chb/cmdline/AnalysisManager.py +++ b/chb/cmdline/AnalysisManager.py @@ -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-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/cmdline/chkx b/chb/cmdline/chkx index 6cad1015..b257e8ea 100755 --- a/chb/cmdline/chkx +++ b/chb/cmdline/chkx @@ -5,7 +5,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 @@ -469,6 +469,16 @@ def parse() -> argparse.Namespace: nargs="*", default=[], help="addresses of function to include in the analysis") + analyzecmd.add_argument( + "--analyze_all_named", + help="include all functions named in userdata in the analysis", + action="store_true") + analyzecmd.add_argument( + "--analyze_range_entry_points", + help=("include all functions whose address is in the given range and " + + "listed in the userdata function entry points"), + nargs="*", + default=[]) analyzecmd.add_argument( "--gc_compact", type=int, @@ -570,6 +580,11 @@ def parse() -> argparse.Namespace: ddatagvars.add_argument("xname", help="name of executable") ddatagvars.set_defaults(func=UCC.ddata_gvars) + # -- ddata md5s -- + ddatamd5s = ddataparsers.add_parser("md5s") + ddatamd5s.add_argument("xname", help="name of executable") + ddatamd5s.set_defaults(func=UCC.ddata_md5s) + # ----------------------------------------------------------------- results -- resultscmd = subparsers.add_parser('results') resultscmd.set_defaults(func=resultscommand) diff --git a/chb/cmdline/commandutil.py b/chb/cmdline/commandutil.py index ab498bd8..a92fbbfa 100644 --- a/chb/cmdline/commandutil.py +++ b/chb/cmdline/commandutil.py @@ -5,7 +5,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 @@ -411,6 +411,8 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: fns_no_lineq: List[str] = args.fns_no_lineq # function hex addresses fns_exclude: List[str] = args.fns_exclude # function hex addresses fns_include: List[str] = args.fns_include # function hex addresses + analyze_all_named: bool = args.analyze_all_named + analyze_range_entry_points: List[str] = args.analyze_range_entry_points gc_compact: int = args.gc_compact construct_all_functions: bool = args.construct_all_functions show_function_timing: bool = args.show_function_timing @@ -447,7 +449,7 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: exit(0) try: - prepare_executable( + userhints = prepare_executable( path, xfile, doreset, @@ -488,6 +490,25 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: print_error("Header file " + f + " not found") exit(1) + if analyze_all_named: + fnnamed_addrs = userhints.rev_function_names().values() + fns_include = fns_include + list(fnnamed_addrs) + chklogger.logger.info("Include %d functions", len(fns_include)) + + if len(analyze_range_entry_points) == 2: + festart = int(analyze_range_entry_points[0], 16) + fefin = int(analyze_range_entry_points[1], 16) + fentrypoints = userhints.function_entry_points() + feincludes: List[str] = [] + for fe in fentrypoints: + eint = int(fe, 16) + if eint >= festart and eint <= fefin: + feincludes.append(fe) + fns_include = fns_include + feincludes + chklogger.logger.info( + "Include %d entry point functions in range %s - %s", + len(feincludes), hex(festart), hex(fefin)) + am = AnalysisManager( path, xfile, @@ -2442,3 +2463,36 @@ def ddata_gvars(args: argparse.Namespace) -> NoReturn: print("Coverage: " + str(coverage) + " (typed: " + str(count) + ")") exit(0) + + +def ddata_md5s(args: argparse.Namespace) -> NoReturn: + + # arguments + xname: str = str(args.xname) + + try: + (path, xfile) = get_path_filename(xname) + except UF.CHBError as e: + print(str(e.wrap())) + exit(1) + + xinfo = XI.XInfo() + xinfo.load(path, xfile) + + app = get_app(path, xfile, xinfo) + md5s = app.function_md5s + + result: Dict[str, int] = {} + + for md5 in md5s.values(): + result.setdefault(md5, 0) + result[md5] += 1 + + print("Number of functions: " + str(len(md5s))) + print("Distinct md5s : " + str(len(result))) + + for (md5, count) in result.items(): + if count > 10: + print(md5 + ": " + str(count)) + + exit(0) diff --git a/chb/invariants/VConstantValueVariable.py b/chb/invariants/VConstantValueVariable.py index b9d62d5c..bcac14e5 100644 --- a/chb/invariants/VConstantValueVariable.py +++ b/chb/invariants/VConstantValueVariable.py @@ -61,8 +61,6 @@ ctxt_iaddress_t * string * bool - | MemoryAddress of - int * memory_offset_t * string option "ma" 1 2 | BridgeVariable of ctxt_iaddress_t * int "bv" 2 1 | FieldValue of string * int * string "fv" 1 3 | SymbolicValue of xpr_t "sv" 1 1 @@ -124,10 +122,6 @@ def is_frozen_test_value(self) -> bool: def is_bridge_variable(self) -> bool: return False - @property - def is_memory_address(self) -> bool: - return False - @property def is_global_value(self) -> bool: return False @@ -739,58 +733,6 @@ def __str__(self) -> str: return self.name -@varregistry.register_tag("ma", VConstantValueVariable) -class MemoryAddress(VConstantValueVariable): - """Address of memory variable. - - args[0]: index of memory base in vardictionary - args[1]: index of memory offset in vardictionary - args[2]: index of optional name or -1 - """ - - def __init__( - self, - vd: "FnVarDictionary", - ixval: IndexedTableValue) -> None: - VConstantValueVariable.__init__(self, vd, ixval) - - @property - def is_memory_address(self) -> bool: - return True - - @property - def base(self) -> VMemoryBase: - return self.vd.memory_base(self.args[0]) - - @property - def offset(self) -> VMemoryOffset: - return self.vd.memory_offset(self.args[1]) - - @property - def name(self) -> Optional[str]: - if self.args[2] == -1: - return None - else: - return self.bd.string(self.args[2]) - - def to_json_result(self) -> JSONResult: - jbase = self.base.to_json_result() - if not jbase.is_ok: - return JSONResult("auxvariable", {}, "fail", jbase.reason) - joffset = self.offset.to_json_result() - if not joffset.is_ok: - return JSONResult("auxvariable", {}, "fail", joffset.reason) - content: Dict[str, Any] = {} - content["kind"] = "ma" - content["base"] = jbase.content - content["offset"] = joffset.content - content["txtrep"] = str(self) - return JSONResult("auxvariable", content, "ok") - - def __str__(self) -> str: - return str(self.base) + " + " + str(self.offset) - - @varregistry.register_tag("fv", VConstantValueVariable) class FieldValue(VConstantValueVariable): """Symbolic representation of a field in a struct. diff --git a/chb/invariants/XVariable.py b/chb/invariants/XVariable.py index 8a0025ff..049cb008 100644 --- a/chb/invariants/XVariable.py +++ b/chb/invariants/XVariable.py @@ -123,13 +123,6 @@ def is_typecast_value(self) -> bool: and self.denotation.is_auxiliary_variable and self.denotation.auxvar.is_typecast_value) - @property - def is_memory_address_value(self) -> bool: - return ( - self.has_denotation() - and self.denotation.is_auxiliary_variable - and self.denotation.auxvar.is_memory_address) - @property def is_symbolic_expr_value(self) -> bool: return ( diff --git a/chb/invariants/XXpr.py b/chb/invariants/XXpr.py index ad1938b9..cbcf4c4d 100644 --- a/chb/invariants/XXpr.py +++ b/chb/invariants/XXpr.py @@ -164,10 +164,6 @@ def is_attr(self) -> bool: def is_four_multiple(self) -> bool: return False - @property - def is_memory_address_value(self) -> bool: - return False - @property def is_stack_base_address(self) -> bool: return False @@ -370,10 +366,6 @@ def is_initial_register_value(self) -> bool: def is_var(self) -> bool: return True - @property - def is_memory_address_value(self) -> bool: - return self.variable.is_memory_address_value - @property def is_tmp_variable(self) -> bool: return self.variable.is_tmp diff --git a/chb/invariants/XXprUtil.py b/chb/invariants/XXprUtil.py index 0320db4a..a5416047 100644 --- a/chb/invariants/XXprUtil.py +++ b/chb/invariants/XXprUtil.py @@ -28,7 +28,63 @@ Note: the boolean argument 'anonymous' is used for lvals: when true no new lvalid is generated for that lval. The argument is primarily used in the - conversion of invariants to available expressions. + conversion of invariants to available expressions. Also, errors are not + generated if anonymous is true. + +Organization: + +Expressions: +- xxpr_to_ast_def_expr: XXpr ==> ASTExpr + + * xxpr_to_ast_expr: XXpr ==> ASTExpr (for constants) + = xconstant_to_ast_expr: XprConstant ==> ASTExpr + + * stack_address_to_ast_expr: XXpr ==> ASTExpr + + * xvariable_to_ast_def_lval_expression: XVariable ==> ASTLvalExpr + = vinitregister_value_to_ast_lval_expression: VInitialRegisterValue ==> + = vinitmemory_value_to_ast_lval_expression: VInitialMemoryValue ==> + + vglobal_variable_value_to_ast_lval_expression: XVariable * VMemoryOffset + + vargument_deref_value_to_ast_lval_expression: XVariable * VMemoryOffset + -> vinitregister_value_to_ast_lval_expression + -> field_pointer_to_ast_memref_expr: ASTExpr * ASTTyp ==> ASTExpr + -> rationalize offset handling? + + + vreturn_deref_value_to_ast_lval_expression: XVariable * VMemoryOffset + + + stack_argument_to_ast_lval_expression: int ==> + + = global_variable_to_lval_expression: VMemoryOffset ==> + ~> rationalize offset handling? + + = memory_variable_to_lval_expression: VMemoryBase * VMemoryOffset ==> + + * xcompound_to_ast_def_expr: XXpr ==> ASTExpr + = xunary_to_ast_def_expr: operator * XXpr ==> ASTExpr + = xbinary_to_ast_def_expr: operator * XXpr * XXpr ==> ASTExpr + + mk_xpointer_expr: operator * ASTExpr * ASTTyp * ASTExpr ==> ASTExpr + +- xmemory_dereference_lval_expr: XXpr ==> ASTExpr + -- conversion of an address dereferenced into an expression + (used primarily for LDR instructions with unresolved memory access as rhs) + + +Variables (left-hand sides, aka lval's) +- xvariable_to_ast_lval: XVariable ==> ASTLval + * stack_variable_to_ast_lval: int ==> ASTLval + * global_variable_to_ast_lval: VMemoryOffset ==> ASTLval + * basevar_variable_to_ast_lval: VMemoryBaseVar * VMemoryOffset ==> ASTLval + * field_pointer_to_ast_memref_lval: ASTExpr * ASTType * int + ~> rationalize offset handling? + +- xmemory_dereference_lval (go via address): XXpr ==> ASTLval + -- conversion of the address of a lhs into an lval + (used primarily for STR instructions with unresolved memory access as lhs) + +Offsets: +- array_offset_to_ast_offset: VMemoryOffsetArrayIndexOffset ==> ASTOffset +- field_offset_to_ast_offset: VMemoryOffsetFieldOffset ==> ASTOffset + """ from codecs import decode @@ -55,7 +111,7 @@ VMemoryVariable, VAuxiliaryVariable, VRegisterVariable) from chb.invariants.VConstantValueVariable import ( VInitialRegisterValue, VInitialMemoryValue, VFunctionReturnValue, - VTypeCastValue, SymbolicValue, MemoryAddress) + VTypeCastValue, SymbolicValue) from chb.invariants.VMemoryBase import ( VMemoryBase, VMemoryBaseBaseVar, @@ -181,44 +237,6 @@ def xxpr_to_ast_expr( return astree.mk_temp_lval_expression() -def xxpr_memory_address_value_to_ast_expr( - xpr: X.XXpr, - xdata: "InstrXData", - iaddr: str, - astree: ASTInterface, - anonymous: bool = False) -> AST.ASTExpr: - - if not xpr.is_memory_address_value: - chklogger.logger.error( - "Encountered expression that is not a memory address value: %s " - + "at address %s", - str(xpr), iaddr) - - xvar = cast(X.XprVariable, xpr).variable - addr = cast("MemoryAddress", xvar.denotation.auxvar) - name = addr.name - if name is None: - chklogger.logger.error( - "AST conversion of unnamed memory address value %s not yet " - + "supported at address %s", - str(xvar), iaddr) - return astree.mk_temp_lval_expression() - - if not astree.globalsymboltable.has_symbol(name): - chklogger.logger.error( - "AST conversion of memory address value %s not in global symbol " - + "table not yet supported at address %s", - str(xvar), iaddr) - return astree.mk_temp_lval_expression() - - vinfo = astree.globalsymboltable.get_symbol(name) - lval = astree.mk_vinfo_lval(vinfo) - if vinfo.vtype is not None and vinfo.vtype.is_array: - return astree.mk_start_of(lval) - else: - return astree.mk_address_of(lval) - - def vinitregister_value_to_ast_lval_expression( vconstvar: "VInitialRegisterValue", xdata: "InstrXData", @@ -384,10 +402,11 @@ def memory_variable_to_lval_expression( if offset.is_field_offset: offset = cast("VMemoryOffsetFieldOffset", offset) astoffset: AST.ASTOffset = field_offset_to_ast_offset( - offset, xdata, iaddr, astree) + offset, xdata, iaddr, astree, anonymous=anonymous) elif offset.is_array_index_offset: offset = cast("VMemoryOffsetArrayIndexOffset", offset) - astoffset = array_offset_to_ast_offset(offset, xdata, iaddr, astree) + astoffset = array_offset_to_ast_offset( + offset, xdata, iaddr, astree, anonymous=anonymous) elif offset.is_constant_value_offset: astoffset = astree.mk_scalar_index_offset(offset.offsetvalue()) else: @@ -396,11 +415,22 @@ def memory_variable_to_lval_expression( astbase, offset=astoffset, anonymous=anonymous) else: - chklogger.logger.error( - "AST conversion of non-typecast basevar %s at address %s " - + "not yet supported", - str(base), iaddr) - return astree.mk_temp_lval_expression() + astlval = xvariable_to_ast_def_lval_expression( + base.basevar, xdata, iaddr, astree, anonymous=anonymous) + if offset.is_field_offset: + offset = cast("VMemoryOffsetFieldOffset", offset) + astoffset = field_offset_to_ast_offset( + offset, xdata, iaddr, astree, anonymous=anonymous) + elif offset.is_array_index_offset: + offset = cast("VMemoryOffsetArrayIndexOffset", offset) + astoffset = array_offset_to_ast_offset( + offset, xdata, iaddr, astree, anonymous=anonymous) + elif offset.is_constant_value_offset: + astoffset = astree.mk_scalar_index_offset(offset.offsetvalue()) + else: + astoffset = nooffset + return astree.mk_memref_expr( + astlval, offset=astoffset, anonymous=anonymous) name = str(base) @@ -414,7 +444,8 @@ def memory_variable_to_lval_expression( vinfo = astree.globalsymboltable.get_symbol(name) if offset.is_field_offset: offset = cast("VMemoryOffsetFieldOffset", offset) - astoffset = field_offset_to_ast_offset(offset, xdata, iaddr, astree) + astoffset = field_offset_to_ast_offset( + offset, xdata, iaddr, astree, anonymous=anonymous) return astree.mk_vinfo_lval_expression( vinfo, astoffset, anonymous=anonymous) @@ -433,7 +464,7 @@ def memory_variable_to_lval_expression( if suboffset.is_field_offset: suboffset = cast("VMemoryOffsetFieldOffset", suboffset) astsuboffset = field_offset_to_ast_offset( - suboffset, xdata, iaddr, astree) + suboffset, xdata, iaddr, astree, anonymous=anonymous) astindexoffset = astree.mk_expr_index_offset( astindex, offset = astsuboffset) return astree.mk_vinfo_lval_expression( @@ -628,10 +659,11 @@ def vargument_deref_value_to_ast_lval_expression( elif tgttype.is_scalar and coff == 0: return astree.mk_memref_expr(xinitarg, anonymous=anonymous) - chklogger.logger.error( - "AST conversion of argument deref value: %s with offset %s and type %s " - + "not yet handled at %s", - str(basevar), str(offset), str(argtype), iaddr) + if not anonymous: + chklogger.logger.error( + "AST conversion of argument deref value: %s with offset %s and type %s " + + "not yet handled at %s", + str(basevar), str(offset), str(argtype), iaddr) return astree.mk_temp_lval_expression() @@ -697,71 +729,14 @@ def vinitmemory_value_to_ast_lval_expression( if avar.is_memory_variable and avar.is_basevar_variable: avar = cast("VMemoryVariable", avar) - if not (avar.offset.is_constant_value_offset or avar.offset.is_no_offset): - chklogger.logger.error( - "Non-constant offset: %s not yet supported at address %s", - str(avar.offset), iaddr) - return astree.mk_temp_lval_expression() - - coff = avar.offset.offsetvalue() - astbase = xvariable_to_ast_def_lval_expression( - avar.basevar, xdata, iaddr, astree, size=size, anonymous=anonymous) - astbasetype = astbase.ctype(astree.ctyper) - - if astbasetype is None: - chklogger.logger.error( - "AST conversion of vinitmemory value %s with basevar %s and " - + "offset %s at address %s unsuccessful because of lack of type " - + "of basevar", - str(vconstvar), str(astbase), str(coff), iaddr) - return astree.mk_temp_lval_expression() - - if astbasetype.is_pointer: - tgttype = cast(AST.ASTTypPtr, astbasetype).tgttyp - if tgttype.is_scalar: - return astree.mk_memref_expr(astbase, anonymous=anonymous) - - if tgttype.is_compound: - - compkey = cast(AST.ASTTypComp, tgttype).compkey - if not astree.has_compinfo(compkey): - chklogger.logger.error( - "Encountered compinfo key without definition in" - + "symbol table: %d", - compkey) - return astree.mk_temp_lval_expression() - - compinfo = astree.compinfo(compkey) - if not compinfo.has_field_offsets(): - chklogger.logger.error( - "No fields are specified for compinfo %s (at address %s)", - compinfo.compname, iaddr) - return astree.mk_temp_lval_expression() - - if not compinfo.has_field_offset(coff): - chklogger.logger.error( - "Compinfo %s does not have a field at offset %d " - + "(at address %s)", - compinfo.compname, coff, iaddr) - return astree.mk_temp_lval_expression() - - (field, restoffset) = compinfo.field_at_offset(coff) - if restoffset > 0: - chklogger.logger.error( - "Rest offset in memory dereference not yet handled at " - + "%s: %s", - iaddr, str(restoffset)) - return astree.mk_temp_lval_expression() - foffset = astree.mk_field_offset(field.fieldname, compkey) - return astree.mk_memref_expr( - astbase, offset=foffset, anonymous=anonymous) - - chklogger.logger.error( - "AST conversion of vinitmemory value %s with astbase %s (type: " - + "%s) and offset %s at address %s", - str(vconstvar), str(astbase), str(astbasetype), str(avar.offset), - iaddr) - return astree.mk_temp_lval_expression() + return memory_variable_to_lval_expression( + avar.base, + avar.offset, + xdata, + iaddr, + astree, + size=size, + anonymous=anonymous) if avar.is_memory_variable and avar.is_stack_argument: return stack_argument_to_ast_lval_expression( @@ -903,25 +878,6 @@ def xvariable_to_ast_def_lval_expression( return memory_variable_to_lval_expression( memvar.base, memvar.offset, xdata, iaddr, astree, anonymous=anonymous) - if xvar.is_memory_address_value: - addr = cast("MemoryAddress", xvar.denotation.auxvar) - name = addr.name - if name is None: - chklogger.logger.error( - "AST def conversion of unnamed memory address variable %s to " - + "lval at address %s not yet supported", - str(xvar), iaddr) - return astree.mk_temp_lval_expression() - - if not astree.globalsymboltable.has_symbol(name): - chklogger.logger.error( - "AST def conversion of memory address % to lval at address %s " - + "not yet supported", - name, iaddr) - - vinfo = astree.globalsymboltable.get_symbol(name) - return astree.mk_vinfo_lval_expression(vinfo, anonymous=anonymous) - if xvar.is_typecast_value: tcvar = cast("VTypeCastValue", xvar.denotation.auxvar) variaddr = tcvar.iaddr @@ -1054,6 +1010,21 @@ def default() -> AST.ASTExpr: subfoffset: AST.ASTOffset compinfo = astree.globalsymboltable.compinfo(compkey) + if not compinfo.has_field_offsets(): + if not anonymous: + chklogger.logger.error( + "No fields are specified for compinfo %s (at address %s)", + compinfo.compname, iaddr) + return astree.mk_temp_lval_expression() + + if not compinfo.has_field_offset(cst2): + if not anonymous: + chklogger.logger.info( + "Compinfo %s does not have a field at offset %d " + + "(at address %s)", + compinfo.compname, cst2, iaddr) + return default() + (field, restoffset) = compinfo.field_at_offset(cst2) if restoffset > 0: @@ -1082,7 +1053,19 @@ def default() -> AST.ASTExpr: subfoffset = astree.mk_field_offset(subfield.fieldname, fcompkey) else: if not anonymous: - chklogger.logger.error( + # an INFO message is issued rather than a WARNING or ERROR + # message, because this may be an intermediate offset that + # is part of a larger offset, where the intermediate offset + # by itself is not meaningful. + # + # Example: + # ADD R0, R0, #0x200 R0 := (R0_in[92]_in + 0x200)) + # LDRH R0, [R0,#0x28] R0 := R0_in[92]_in[552]_in + # + # Here the offset 0x200 is an intermediate to 0x228, which + # denotes a proper offset in the struct, but 0x200 does not + # denote any legal offset. + chklogger.logger.info( "Non-struct type %s for field %s in second-level rest " + "offset not yet " + "handled for %s with offset %s at %s: %d", @@ -1136,7 +1119,7 @@ def default() -> AST.ASTExpr: xvar = cast(X.XprVariable, xpr1).variable astxpr1 = xvariable_to_ast_def_lval_expression( xvar, xdata, iaddr, astree, anonymous=anonymous) - astxpr2 = xxpr_to_ast_expr(xpr2, xdata, iaddr, astree) + astxpr2 = xxpr_to_ast_expr(xpr2, xdata, iaddr, astree, anonymous=anonymous) if operator in ["plus", "minus"]: ty1 = astxpr1.ctype(astree.ctyper) if ty1 is not None: @@ -1353,10 +1336,6 @@ def xxpr_to_ast_def_expr( if xpr.is_constant: return xxpr_to_ast_expr(xpr, xdata, iaddr, astree, anonymous=anonymous) - if xpr.is_memory_address_value: - return xxpr_memory_address_value_to_ast_expr( - xpr, xdata, iaddr, astree, anonymous=anonymous) - if xpr.is_stack_address: return stack_address_to_ast_expr( xpr, xdata, iaddr, astree, anonymous=anonymous) @@ -1439,13 +1418,13 @@ def array_offset_to_ast_offset( if offset.offset.is_field_offset: fsuboffset = cast("VMemoryOffsetFieldOffset", offset.offset) astoffset: AST.ASTOffset = field_offset_to_ast_offset( - fsuboffset, xdata, iaddr, astree) + fsuboffset, xdata, iaddr, astree, anonymous=anonymous) return astree.mk_expr_index_offset(indexxpr, offset=astoffset) if offset.offset.is_array_index_offset: asuboffset = cast("VMemoryOffsetArrayIndexOffset", offset.offset) astoffset = array_offset_to_ast_offset( - asuboffset, xdata, iaddr, astree) + asuboffset, xdata, iaddr, astree, anonymous=anonymous) return astree.mk_expr_index_offset(indexxpr, offset=astoffset) chklogger.logger.error( @@ -1457,7 +1436,8 @@ def field_offset_to_ast_offset( offset: "VMemoryOffsetFieldOffset", xdata: "InstrXData", iaddr: str, - astree: ASTInterface) -> AST.ASTOffset: + astree: ASTInterface, + anonymous: bool = False) -> AST.ASTOffset: if offset.has_no_offset(): return astree.mk_field_offset(offset.fieldname, offset.ckey) @@ -1465,11 +1445,11 @@ def field_offset_to_ast_offset( if offset.offset.is_field_offset: fieldoffset = cast("VMemoryOffsetFieldOffset", offset.offset) suboffset = field_offset_to_ast_offset( - fieldoffset, xdata, iaddr, astree) + fieldoffset, xdata, iaddr, astree, anonymous=anonymous) elif offset.offset.is_array_index_offset: arrayindexoffset = cast("VMemoryOffsetArrayIndexOffset", offset.offset) suboffset = array_offset_to_ast_offset( - arrayindexoffset, xdata, iaddr, astree) + arrayindexoffset, xdata, iaddr, astree, anonymous=anonymous) elif offset.offset.is_constant_value_offset: suboffset = astree.mk_scalar_index_offset(offset.offset.offsetvalue()) else: @@ -1479,93 +1459,6 @@ def field_offset_to_ast_offset( offset.fieldname, offset.ckey, offset=suboffset) -def array_variable_to_ast_lval( - base: "MemoryAddress", - elementtyp: AST.ASTTyp, - offset: "VMemoryOffset", - xdata: "InstrXData", - iaddr: str, - astree: ASTInterface, - anonymous: bool = False) -> AST.ASTLval: - - name = base.name - if name is None: - chklogger.logger.error( - "AST conversion of array variable at %s encountered nameless " - + "base %s", - str(iaddr), str(base)) - return astree.mk_temp_lval() - - if not astree.globalsymboltable.has_symbol(name): - chklogger.logger.error( - "AST conversion of memory address value %s not in global symbol " - + " table not yet supported at address %s", - str(name), iaddr) - return astree.mk_temp_lval() - - vinfo = astree.globalsymboltable.get_symbol(name) - if not offset.is_array_index_offset: - chklogger.logger.error( - "AST conversion of array variable expected to find array index " - + "offset, but found %s at address %s", - str(offset), iaddr) - return astree.mk_temp_lval() - - astoffset = array_offset_to_ast_offset( - cast("VMemoryOffsetArrayIndexOffset", offset), - xdata, - iaddr, - astree) - - return astree.mk_vinfo_lval(vinfo, offset=astoffset, anonymous=anonymous) - - chklogger.logger.error( - "Array variable to astlval still in progress at %s for %s with offset %s", - iaddr, str(base), str(offset)) - return astree.mk_temp_lval() - - -def struct_variable_to_ast_lval( - base: "MemoryAddress", - structtyp: AST.ASTTypComp, - offset: "VMemoryOffset", - xdata: "InstrXData", - iaddr: str, - astree: ASTInterface, - anonymous: bool = False) -> AST.ASTLval: - - name = base.name - if name is None: - chklogger.logger.error( - "AST conversion of struct variable at %s encountered nameless " - + "base %s", - str(iaddr), str(base)) - return astree.mk_temp_lval() - - if not astree.globalsymboltable.has_symbol(name): - chklogger.logger.error( - "AST conversion of memory address value %s not in global symbol " - + " table not yet supported at address %s", - str(name), iaddr) - return astree.mk_temp_lval() - - vinfo = astree.globalsymboltable.get_symbol(name) - if not offset.is_field_offset: - chklogger.logger.error( - "AST conversion of struct variable expected to find field offset " - + "but found %s at address %s", - str(offset), iaddr) - return astree.mk_temp_lval() - - astoffset = field_offset_to_ast_offset( - cast("VMemoryOffsetFieldOffset", offset), - xdata, - iaddr, - astree) - - return astree.mk_vinfo_lval(vinfo, offset=astoffset, anonymous=anonymous) - - def global_variable_to_ast_lval( offset: "VMemoryOffset", xdata: "InstrXData", @@ -1686,7 +1579,7 @@ def xvariable_to_ast_lval( or (rhs.is_constant_value_variable and not rhs.is_function_return_value))): astrhs: Optional[AST.ASTExpr] = xxpr_to_ast_def_expr( - rhs, xdata, iaddr, astree) + rhs, xdata, iaddr, astree, anonymous=anonymous) else: astrhs = None @@ -1706,7 +1599,8 @@ def xvariable_to_ast_lval( astree, size=size, ctype=ctype, - memaddr=memaddr) + memaddr=memaddr, + anonymous=anonymous) elif ( xv.is_memory_variable @@ -1723,61 +1617,14 @@ def xvariable_to_ast_lval( memaddr=memaddr, anonymous=anonymous) - elif ( - xv.is_memory_variable - and cast("VMemoryVariable", - xv.denotation).base.is_basearray): - xvmem = cast("VMemoryVariable", xv.denotation) - base = cast("VMemoryBaseBaseArray", xvmem.base).basearray - if not base.is_memory_address_value: - chklogger.logger.error( - "AST conversion of lval %s encountered invalid BaseArray at %s", - str(xv), iaddr) - return astree.mk_temp_lval() - basevar = cast("MemoryAddress", base.denotation.auxvar) - basetyp = cast( - "VMemoryBaseBaseArray", - xvmem.base).basetyp.convert(astree.typconverter) - return array_variable_to_ast_lval( - basevar, - cast(AST.ASTTypArray, basetyp).tgttyp, - xvmem.offset, - xdata, - iaddr, - astree, - anonymous=anonymous) - - elif ( - xv.is_memory_variable - and cast("VMemoryVariable", - xv.denotation).base.is_basestruct): - xvmem = cast("VMemoryVariable", xv.denotation) - base = cast("VMemoryBaseBaseStruct", xvmem.base).basestruct - if not base.is_memory_address_value: - chklogger.logger.error( - "AST conversion of lval %s encountered invalie BaseStruct at %s", - str(xv), iaddr) - return astree.mk_temp_lval() - basevar = cast("MemoryAddress", base.denotation.auxvar) - basetyp = cast( - "VMemoryBaseBaseStruct", - xvmem.base).basetyp.convert(astree.typconverter) - return struct_variable_to_ast_lval( - basevar, - cast(AST.ASTTypComp, basetyp), - xvmem.offset, - xdata, - iaddr, - astree, - anonymous=anonymous) - + # basevar variable elif ( xv.is_memory_variable and cast("VMemoryVariable", xv.denotation).base.is_basevar): xvmem = cast("VMemoryVariable", xv.denotation) membasevar = cast("VMemoryBaseBaseVar", xvmem.base).basevar - return vargument_deref_value_to_ast_lval( + return basevar_variable_to_ast_lval( membasevar, xvmem.offset, xdata, @@ -1792,7 +1639,7 @@ def xvariable_to_ast_lval( return astree.mk_temp_lval() -def vargument_deref_value_to_ast_lval( +def basevar_variable_to_ast_lval( basevar: "XVariable", offset: "VMemoryOffset", xdata: "InstrXData", @@ -1800,233 +1647,21 @@ def vargument_deref_value_to_ast_lval( astree: ASTInterface, anonymous: bool = False) -> AST.ASTLval: - if offset.is_no_offset: - if basevar.is_initial_register_value: - asmvar = cast("VAuxiliaryVariable", basevar.denotation) - vinitvar = cast("VInitialRegisterValue", asmvar.auxvar) - xinitarg = vinitregister_value_to_ast_lval_expression( - vinitvar, xdata, iaddr, astree, anonymous=anonymous) - argtype = xinitarg.ctype(astree.ctyper) - if argtype is None: - chklogger.logger.error( - "Untyped dereferenced argument value %s not yet supported at " - + "address %s", - str(xinitarg), iaddr) - return astree.mk_temp_lval() - else: - return astree.mk_memref_lval(xinitarg, anonymous=anonymous) - - if basevar.is_function_return_value: - asmvar = cast("VAuxiliaryVariable", basevar.denotation) - frvinitrvar = cast("VFunctionReturnValue", asmvar.auxvar) - callsite = frvinitrvar.callsite - if callsite in astree.ssa_intros: - if len(astree.ssa_intros[callsite]) == 1: - vinfo = list(astree.ssa_intros[callsite].values())[0] - vtype = vinfo.vtype - vexpr = astree.mk_vinfo_lval_expression( - vinfo, anonymous=anonymous) - return astree.mk_memref_lval(vexpr, anonymous=anonymous) - - if not offset.is_constant_value_offset: - if not anonymous: - chklogger.logger.error( - "Non-constant offset: %s with variable %s not yet supported at " - + "address %s", - str(offset), str(basevar), iaddr) - return astree.mk_temp_lval() - - coff = offset.offsetvalue() - if basevar.is_initial_register_value: - asmvar = cast("VAuxiliaryVariable", basevar.denotation) - vinitvar = cast("VInitialRegisterValue", asmvar.auxvar) - xinitarg = vinitregister_value_to_ast_lval_expression( - vinitvar, xdata, iaddr, astree, anonymous=anonymous) - argtype = xinitarg.ctype(astree.ctyper) - if argtype is None: - chklogger.logger.error( - "Untyped dereferenced argument value %s not yet supported at " - + "address %s", - str(xinitarg), iaddr) - return astree.mk_temp_lval() - - if argtype.is_pointer: - tgttype = cast(AST.ASTTypPtr, argtype).tgttyp - if tgttype.is_compound: - - return field_pointer_to_ast_memref_lval( - xinitarg, - tgttype, - coff, - iaddr, - astree, - anonymous=anonymous) - - elif coff == 0: - return astree.mk_memref_lval(xinitarg, anonymous=anonymous) - - chklogger.logger.error( - "AST conversion of initial-register value in argument deref value " - + "%s with offset %s and type %s not yet handled at %s", - str(basevar), str(offset), str(argtype), iaddr) - return astree.mk_temp_lval() - - if basevar.is_initial_memory_value: - asmvar = cast("VAuxiliaryVariable", basevar.denotation) - vinitmemvar = cast("VInitialMemoryValue", asmvar.auxvar) - - if anonymous: - # Silently skip variables from invariants - chklogger.logger.info( - "AST conversion of initial-memory value in argument deref " - + "value %s with offset %s not yet handled at %s", - str(basevar), str(offset), iaddr) - return astree.mk_temp_lval() - else: - chklogger.logger.error( - "AST conversion of initial-memory value in argument deref " - + "value %s with offset %s not yet handled at %s", - str(basevar), str(offset), iaddr) - return astree.mk_temp_lval() - - if basevar.is_function_return_value: - asmvar = cast("VAuxiliaryVariable", basevar.denotation) - vinitrvar = cast("VFunctionReturnValue", asmvar.auxvar) - callsite = vinitrvar.callsite - if callsite in astree.ssa_intros: - if len(astree.ssa_intros[callsite]) == 1: - vinfo = list(astree.ssa_intros[callsite].values())[0] - vtype = vinfo.vtype - vexpr = astree.mk_vinfo_lval_expression( - vinfo, anonymous=anonymous) - if vtype is not None and vtype.is_pointer: - tgttype = cast(AST.ASTTypPtr, vtype).tgttyp - if tgttype.is_compound: - - return field_pointer_to_ast_memref_lval( - vexpr, - tgttype, - coff, - iaddr, - astree, - anonymous=anonymous) - - if coff == 0: - return astree.mk_memref_lval(vexpr, anonymous=anonymous) - - else: - chklogger.logger.error( - "Non-struct pointer type %s not yet handled at %s", - str(vtype), iaddr) - return astree.mk_temp_lval() - else: - chklogger.logger.error( - "AST conversion of return value in argument deref " - + "value %s with offset %s and type %s at address " - + "%s not yet handled", - str(basevar), str(offset), str(vtype), iaddr) - return astree.mk_temp_lval() - - chklogger.logger.error( - "AST conversion of return value in argument deref value %s with " - + "offset %s not yet handled at %s", - str(basevar), str(offset), iaddr) - return astree.mk_temp_lval() - - if basevar.is_typecast_value: - asmvar = cast("VAuxiliaryVariable", basevar.denotation) - vtcv = cast("VTypeCastValue", asmvar.auxvar) - bctype = vtcv.tgttype - vtype = bctype.convert(astree.typconverter) - if vtype is not None and vtype.is_pointer: - tgttype = cast(AST.ASTTypPtr, vtype).tgttyp - castaddr = vtcv.iaddr - if len(astree.ssa_intros[castaddr]) == 1: - vinfo = list(astree.ssa_intros[castaddr].values())[0] - vexpr = astree.mk_vinfo_lval_expression( - vinfo, anonymous=anonymous) - if tgttype.is_compound: - - return field_pointer_to_ast_memref_lval( - vexpr, - tgttype, - coff, - iaddr, - astree, - anonymous=anonymous) - - chklogger.logger.error( - "AST conversion of typecast value %s with type %s not yet handled " - + " at address %s", - str(basevar), str(tgttype), iaddr) - return astree.mk_temp_lval() - - chklogger.logger.error( - "AST conversion of typecast value %s with type %s not yet handled at %s", - str(basevar), str(vtype), iaddr) - return astree.mk_temp_lval() - - chklogger.logger.error( - "AST conversion of argument deref lvalue: %s with offset %s not yet " - + "handled at %s (variable denotation type: %s)", - str(basevar), str(offset), iaddr, basevar.denotation.tags[0]) - return astree.mk_temp_lval() - - -def field_pointer_to_ast_memref_lval( - astexpr: AST.ASTExpr, - asttype: AST.ASTTyp, - offset: int, - iaddr: str, - astree: ASTInterface, - size: int = 4, - anonymous: bool = False) -> AST.ASTLval: - - if not asttype.is_compound: - chklogger.logger.error( - "Expected to see a struct type but received %s at %s", - str(asttype), iaddr) - return astree.mk_temp_lval() - - compkey = cast(AST.ASTTypComp, asttype).compkey - if not astree.has_compinfo(compkey): - chklogger.logger.error( - "Encountered compinfo key without definition in symbol table: %d", - compkey) - return astree.mk_temp_lval() - - subfoffset: AST.ASTOffset = nooffset - compinfo = astree.compinfo(compkey) - (field, restoffset) = compinfo.field_at_offset(offset) - if restoffset > 0: - if field.fieldtype.is_compound: - fcompkey = cast(AST.ASTTypComp, field.fieldtype).compkey - if not astree.has_compinfo(fcompkey): - chklogger.logger.error( - "Encountered field compinfo key without definition in " - + "symbol table: %d", - compkey) - return astree.mk_temp_lval() - fcompinfo = astree.compinfo(fcompkey) - (subfield, subrestoffset) = fcompinfo.field_at_offset(restoffset) - if subrestoffset > 0: - chklogger.logger.error( - "Second-level rest offset in field-pointer memref not yet " - + "handled for %s at %s: %s", - str(astexpr), iaddr, str(subrestoffset)) - return astree.mk_temp_lval() - subfoffset = astree.mk_field_offset(subfield.fieldname, fcompkey) - else: - chklogger.logger.error( - "Non-struct type for second-level rest offset not yet handled " - + " for %s with offset %d at %s", - str(astexpr), offset, iaddr) - return astree.mk_temp_lval() + astbase = xvariable_to_ast_def_lval_expression( + basevar, xdata, iaddr, astree, anonymous=anonymous) + if offset.is_field_offset: + offset = cast("VMemoryOffsetFieldOffset", offset) + astoffset: AST.ASTOffset = field_offset_to_ast_offset( + offset, xdata, iaddr, astree, anonymous=anonymous) + elif offset.is_array_index_offset: + offset = cast("VMemoryOffsetArrayIndexOffset", offset) + astoffset = array_offset_to_ast_offset( + offset, xdata, iaddr, astree, anonymous=anonymous) + elif offset.is_constant_value_offset: + astoffset = astree.mk_scalar_index_offset(offset.offsetvalue()) else: - subfoffset = nooffset - - foffset = astree.mk_field_offset(field.fieldname, compkey, offset=subfoffset) - return astree.mk_memref_lval(astexpr, offset=foffset, anonymous=anonymous) + astoffset = nooffset + return astree.mk_memref_lval(astbase, offset=astoffset, anonymous=anonymous) def xmemory_dereference_lval( @@ -2036,7 +1671,8 @@ def xmemory_dereference_lval( astree: ASTInterface, anonymous: bool = False) -> AST.ASTLval: - xaddr = xxpr_to_ast_def_expr(address, xdata, iaddr, astree) + xaddr = xxpr_to_ast_def_expr( + address, xdata, iaddr, astree, anonymous=anonymous) if xaddr.is_ast_binary_op: xaddr = cast(AST.ASTBinaryOp, xaddr) @@ -2062,72 +1698,12 @@ def xmemory_dereference_lval( return astree.mk_memref_lval(xaddr) -def xmemory_dereference_to_ast_def_expr( - address: X.XXpr, - xdata: "InstrXData", - iaddr: str, - astree: ASTInterface, - anonymous: bool = False) -> AST.ASTExpr: - - hl_addr = xxpr_to_ast_def_expr(address, xdata, iaddr, astree) - hl_addr_type = hl_addr.ctype(astree.ctyper) - if hl_addr_type is None: - return astree.mk_memref_expr(hl_addr, anonymous=anonymous) - - if hl_addr_type.is_pointer: - tgttype = cast(AST.ASTTypPtr, hl_addr_type).tgttyp - if tgttype.is_compound: - - # Identify field offsets - compkey = cast(AST.ASTTypComp, tgttype).compkey - if not astree.has_compinfo(compkey): - chklogger.logger.error( - ("Encountered compinfo key without definition in symbol " - + " table: %d at address %s"), - compkey, iaddr) - return astree.mk_memref_expr(hl_addr, anonymous=anonymous) - - compinfo = astree.compinfo(compkey) - fieldoffset = 0 - baseaddr = hl_addr - if hl_addr.is_ast_binary_op: - hl_addr = cast(AST.ASTBinaryOp, hl_addr) - if not hl_addr.op == "plus": - chklogger.logger.error( - "Encountered address expression with op %s at address %s", - hl_addr.op, iaddr) - return astree.mk_memref_expr(hl_addr, anonymous=anonymous) - - if not hl_addr.exp2.is_integer_constant: - chklogger.logger.warning( - "Non-constant field offset not yet supported: %s " - + "at address %s", - str(hl_addr.exp2), iaddr) - return astree.mk_memref_expr(hl_addr, anonymous=anonymous) - - fieldoffset = cast(AST.ASTIntegerConstant, hl_addr.exp2).cvalue - baseaddr = hl_addr.exp1 - - (field, restoffset) = compinfo.field_at_offset(fieldoffset) - if restoffset > 0: - chklogger.logger.warning( - "Rest offset in memory dereference not yet handled at %s: %s", - iaddr, str(hl_addr)) - foffset = astree.mk_field_offset(field.fieldname, compkey) - return astree.mk_memref_expr( - baseaddr, offset=foffset, anonymous=anonymous) - - chklogger.logger.warning( - "Unexpected type for address in memory dereference: %s at address %s", - str(hl_addr_type), iaddr) - return astree.mk_memref_expr(hl_addr) - - def vfunctionreturn_value_to_ast_lvals( vconstvar: "VFunctionReturnValue", xdata: "InstrXData", astree: ASTInterface, anonymous: bool = False) -> List[AST.ASTLval]: + """Deprecated. Currently only used in Power32.""" chklogger.logger.error( "AST conversion of vfunctionreturn_value %s deprecated", diff --git a/chb/userdata/UserHints.py b/chb/userdata/UserHints.py index 866dd7e9..472e143f 100644 --- a/chb/userdata/UserHints.py +++ b/chb/userdata/UserHints.py @@ -1457,6 +1457,14 @@ def rev_function_names(self) -> Dict[str, str]: else: return {} + def function_entry_points(self) -> List[str]: + if "function-entry-points" in self.astdata: + entry = cast(FunctionEntryPointsHints, + self.astdata["function-entry-points"]) + return entry.entrypoints + else: + return [] + def add_hints(self, hints: Dict[str, Any]) -> None: """Process a user provided dictionary with user hint dictionaries. @@ -1527,6 +1535,11 @@ def add_hints(self, hints: Dict[str, Any]) -> None: self.userdata[tag].update(fepoints) else: self.userdata[tag] = FunctionEntryPointsHints(fepoints) + if tag in self.astdata: + self.astdata[tag].update(fepoints) + else: + self.astdata[tag] = FunctionEntryPointsHints(fepoints) + if "function-annotations" in hints: tag = "function-annotations" @@ -1550,6 +1563,10 @@ def add_hints(self, hints: Dict[str, Any]) -> None: self.userdata[tag].update(fnames) else: self.userdata[tag] = FunctionNamesHints(fnames) + if tag in self.astdata: + self.astdata[tag].update(fnames) + else: + self.astdata[tag] = FunctionNamesHints(fnames) else: if tag in self.astdata: self.astdata[tag].update(fnames)