From 34adcff8308d2fce92424dcf817e5e3152dbce8c Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:21:07 +0000 Subject: [PATCH] GP-4782 Refactor RulePtrsubUndo --- .../src/decompile/cpp/coreaction.cc | 7 +- .../Decompiler/src/decompile/cpp/funcdata.hh | 5 +- .../src/decompile/cpp/ruleaction.cc | 266 ++++++++++++++++-- .../src/decompile/cpp/ruleaction.hh | 10 +- .../Decompiler/src/decompile/cpp/type.cc | 114 ++++++-- .../Decompiler/src/decompile/cpp/type.hh | 7 +- .../Decompiler/src/decompile/cpp/typeop.cc | 26 +- 7 files changed, 355 insertions(+), 80 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index a79c3030c3..22a461bf85 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -2723,7 +2723,7 @@ int4 ActionSetCasts::apply(Funcdata &data) data.opUndoPtradd(op,true); } else if (opc == CPUI_PTRSUB) { // Check for PTRSUB that no longer fits pointer - if (!op->getIn(0)->getHighTypeReadFacing(op)->isPtrsubMatching(op->getIn(1)->getOffset())) { + if (!op->getIn(0)->getTypeReadFacing(op)->isPtrsubMatching(op->getIn(1)->getOffset(),0,1)) { if (op->getIn(1)->getOffset() == 0) { data.opRemoveInput(op, 1); data.opSetOpcode(op, CPUI_COPY); @@ -5232,6 +5232,7 @@ int4 ActionInferTypes::apply(Funcdata &data) if (localcount >= 7) { // This constant arrived at empirically if (localcount == 7) { data.warningHeader("Type propagation algorithm not settling"); + data.setTypeRecoveryExceeded(); localcount += 1; } return 0; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 23aafec62a..840c8c1978 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -68,7 +68,8 @@ class Funcdata { restart_pending = 0x400, ///< Analysis must be restarted (because of new override info) unimplemented_present = 0x800, ///< Set if function contains unimplemented instructions baddata_present = 0x1000, ///< Set if function flowed into bad data - double_precis_on = 0x2000 ///< Set if we are performing double precision recovery + double_precis_on = 0x2000, ///< Set if we are performing double precision recovery + typerecovery_exceeded= 0x4000 ///< Set if data-type propagation passes reached maximum }; uint4 flags; ///< Boolean properties associated with \b this function uint4 clean_up_index; ///< Creation index of first Varnode created after start of cleanup @@ -151,6 +152,7 @@ public: bool hasUnreachableBlocks(void) const { return ((flags&blocks_unreachable)!=0); } ///< Did this function exhibit unreachable code bool isTypeRecoveryOn(void) const { return ((flags&typerecovery_on)!=0); } ///< Will data-type analysis be performed bool hasTypeRecoveryStarted(void) const { return ((flags&typerecovery_start)!=0); } ///< Has data-type recovery processes started + bool isTypeRecoveryExceeded(void) const { return ((flags&typerecovery_exceeded)!=0); } ///< Has maximum propagation passes been reached bool hasNoCode(void) const { return ((flags & no_code)!=0); } ///< Return \b true if \b this function has no code body void setNoCode(bool val) { if (val) flags |= no_code; else flags &= ~no_code; } ///< Toggle whether \b this has a body void setLanedRegGenerated(void) { minLanedSize = 1000000; } ///< Mark that laned registers have been collected @@ -180,6 +182,7 @@ public: /// /// \param val is \b true if data-type analysis is enabled void setTypeRecovery(bool val) { flags = val ? (flags | typerecovery_on) : (flags & ~typerecovery_on); } + void setTypeRecoveryExceeded(void) { flags |= typerecovery_exceeded; } ///< Mark propagation passes have reached maximum void startCastPhase(void) { cast_phase_index = vbank.getCreateIndex(); } ///< Start the \b cast insertion phase uint4 getCastPhaseIndex(void) const { return cast_phase_index; } ///< Get creation index at the start of \b cast insertion uint4 getHighLevelIndex(void) const { return high_level_index; } ///< Get creation index at the start of HighVariable creation diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index 658a3b45ca..e897fcf390 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -5920,30 +5920,30 @@ bool AddTreeState::spanAddTree(PcodeOp *op,uint8 treeCoeff) void AddTreeState::calcSubtype(void) { - if (size == 0 || nonmultsum < size) - offset = nonmultsum; + uint8 tmpoff = (multsum + nonmultsum) & ptrmask; + if (size == 0 || tmpoff < size) + offset = tmpoff; else { // For a sum that falls completely outside the data-type, there is presumably some // type of constant term added to an array index either at the current level or lower. // If we knew here whether an array of the baseType was possible we could make a slightly // better decision. - intb snonmult = sign_extend(nonmultsum,ptrsize*8-1); - snonmult = snonmult % size; - if (snonmult >= 0) + intb stmpoff = sign_extend(tmpoff,ptrsize*8-1); + stmpoff = stmpoff % size; + if (stmpoff >= 0) // We assume the sum is big enough it represents an array index at this level - offset = (uint8)snonmult; + offset = (uint8)stmpoff; else { // For a negative sum, if the baseType is a structure and there is array hints, // we assume the sum is an array index at a lower level - if (baseType->getMetatype() == TYPE_STRUCT && biggestNonMultCoeff != 0) - offset = nonmultsum; + if (baseType->getMetatype() == TYPE_STRUCT && biggestNonMultCoeff != 0 && multsum == 0) + offset = tmpoff; else - offset = (uint8)(snonmult + size); + offset = (uint8)(stmpoff + size); } } - correct = nonmultsum - offset; - nonmultsum = offset; - multsum = (multsum + correct) & ptrmask; // Some extra multiples of size + correct = nonmultsum; // Non-multiple constants are double counted, correct in final sum + multsum = (tmpoff - offset) & ptrmask; // Some extra multiples of size if (nonmult.empty()) { if ((multsum == 0) && multiple.empty()) { // Is there anything at all valid = false; @@ -5952,31 +5952,33 @@ void AddTreeState::calcSubtype(void) isSubtype = false; // There are no offsets INTO the pointer } else if (baseType->getMetatype() == TYPE_SPACEBASE) { - int8 nonmultbytes = AddrSpace::addressToByteInt(nonmultsum,ct->getWordSize()); // Convert to bytes + int8 offsetbytes = AddrSpace::addressToByteInt(offset,ct->getWordSize()); // Convert to bytes int8 extra; // Get offset into mapped variable - if (!hasMatchingSubType(nonmultbytes, biggestNonMultCoeff, &extra)) { + if (!hasMatchingSubType(offsetbytes, biggestNonMultCoeff, &extra)) { valid = false; // Cannot find mapped variable but nonmult is non-empty return; } extra = AddrSpace::byteToAddress(extra, ct->getWordSize()); // Convert back to address units - offset = (nonmultsum - extra) & ptrmask; + offset = (offset - extra) & ptrmask; + correct = (correct - extra) & ptrmask; isSubtype = true; } else if (baseType->getMetatype() == TYPE_STRUCT) { - intb snonmult = sign_extend(nonmultsum,ptrsize*8-1); - int8 nonmultbytes = AddrSpace::addressToByteInt(snonmult,ct->getWordSize()); // Convert to bytes + intb soffset = sign_extend(offset,ptrsize*8-1); + int8 offsetbytes = AddrSpace::addressToByteInt(soffset,ct->getWordSize()); // Convert to bytes int8 extra; // Get offset into field in structure - if (!hasMatchingSubType(nonmultbytes, biggestNonMultCoeff, &extra)) { - if (nonmultbytes < 0 || nonmultbytes >= baseType->getSize()) { // Compare as bytes! not address units + if (!hasMatchingSubType(offsetbytes, biggestNonMultCoeff, &extra)) { + if (offsetbytes < 0 || offsetbytes >= baseType->getSize()) { // Compare as bytes! not address units valid = false; // Out of structure's bounds return; } extra = 0; // No field, but pretend there is something there } extra = AddrSpace::byteToAddressInt(extra, ct->getWordSize()); // Convert back to address units - offset = (nonmultsum - extra) & ptrmask; + offset = (offset - extra) & ptrmask; + correct = (correct - extra) & ptrmask; if (pRelType != (TypePointerRel *)0 && offset == pRelType->getPointerOffset()) { // offset falls within basic ptrto if (!pRelType->evaluateThruParent(0)) { // If we are not representing offset 0 through parent @@ -5988,12 +5990,31 @@ void AddTreeState::calcSubtype(void) } else if (baseType->getMetatype() == TYPE_ARRAY) { isSubtype = true; + correct = (correct - offset) & ptrmask; offset = 0; } else { // No struct or array, but nonmult is non-empty valid = false; // There is substructure we don't know about } + if (pRelType != (const TypePointerRel *)0) { + int4 ptrOff = ((TypePointerRel *)ct)->getPointerOffset(); + offset = (offset - ptrOff) & ptrmask; + correct = (correct - ptrOff) & ptrmask; + } +} + +/// The data-type from the pointer input (of either a PTRSUB or PTRADD) is propagated to the +/// output of the PcodeOp. +/// \param op is the given PcodeOp +void AddTreeState::assignPropagatedType(PcodeOp *op) + +{ + Varnode *vn = op->getIn(0); + Datatype *inType = vn->getTypeReadFacing(op); + Datatype *newType = op->getOpcode()->propagateType(inType, op, vn, op->getOut(), 0, -1); + if (newType != (Datatype *)0) + op->getOut()->updateType(newType, false, false); } /// Construct part of the tree that sums to a multiple of the base data-type size. @@ -6037,7 +6058,6 @@ Varnode *AddTreeState::buildMultiples(void) Varnode *AddTreeState::buildExtra(void) { - correct = correct+offset; // Total correction that needs to be made Varnode *resNode = (Varnode *)0; for(int4 i=0;igetPointerOffset(); - offset -= ptrOff; - offset &= ptrmask; - } Varnode *multNode = buildMultiples(); Varnode *extraNode = buildExtra(); PcodeOp *newop = (PcodeOp *)0; @@ -6152,6 +6167,8 @@ void AddTreeState::buildTree(void) newop = data.newOpBefore(baseOp,CPUI_PTRADD,ptr,multNode,data.newConstant(ptrsize,size)); if (ptr->getType()->needsResolution()) data.inheritResolution(ptr->getType(),newop, 0, baseOp, baseSlot); + if (data.isTypeRecoveryExceeded()) + assignPropagatedType(newop); multNode = newop->getOut(); } else @@ -6162,6 +6179,8 @@ void AddTreeState::buildTree(void) newop = data.newOpBefore(baseOp,CPUI_PTRSUB,multNode,data.newConstant(ptrsize,offset)); if (multNode->getType()->needsResolution()) data.inheritResolution(multNode->getType(),newop, 0, baseOp, baseSlot); + if (data.isTypeRecoveryExceeded()) + assignPropagatedType(newop); if (size != 0) newop->setStopTypePropagation(); multNode = newop->getOut(); @@ -6555,6 +6574,8 @@ int4 RulePtraddUndo::applyOp(PcodeOp *op,Funcdata &data) return 1; } +const int4 RulePtrsubUndo::DEPTH_LIMIT = 8; + /// \class RulePtrsubUndo /// \brief Remove PTRSUB operations with mismatched data-type information /// @@ -6567,17 +6588,204 @@ void RulePtrsubUndo::getOpList(vector &oplist) const oplist.push_back(CPUI_PTRSUB); } +/// \brief Recursively search for additive constants and multiplicative constants +/// +/// Walking backward from the given Varnode, search for constants being added in and return +/// the sum of all the constants. Additionally pass back the biggest constant coefficient, for any term +/// formed with INT_MULT. +/// \param vn is the given root Varnode of the additive tree +/// \param multiplier will hold the biggest constant coefficient +/// \param maxLevel is the maximum depth to search in the tree +/// \return the sum of all constants in the additive expression +int8 RulePtrsubUndo::getConstOffsetBack(Varnode *vn,int8 &multiplier,int4 maxLevel) + +{ + multiplier = 1; + int8 submultiplier; + if (vn->isConstant()) + return vn->getOffset(); + if (!vn->isWritten()) + return 0; + maxLevel -= 1; + if (maxLevel < 0) + return 0; + PcodeOp *op = vn->getDef(); + OpCode opc = op->code(); + int8 retval = 0; + if (opc == CPUI_INT_ADD) { + retval += getConstOffsetBack(op->getIn(0),submultiplier,maxLevel); + if (submultiplier > multiplier) + multiplier = submultiplier; + retval += getConstOffsetBack(op->getIn(1), submultiplier, maxLevel); + if (submultiplier > multiplier) + multiplier = submultiplier; + } + else if (opc == CPUI_INT_MULT) { + Varnode *cvn = op->getIn(1); + if (!cvn->isConstant()) return 0; + multiplier = cvn->getOffset(); + getConstOffsetBack(op->getIn(0), submultiplier, maxLevel); + multiplier *= submultiplier; // Only contribute to the multiplier + } + return retval; +} + +/// \brief Collect constants and the biggest multiplier in the given PTRSUB expression. +/// +/// Walking the additive expression (INT_ADD, PTRADD, and other PTRSUBs) and calculate any additional +/// constant value being added to the PTRSUB. Additionally pass back the biggest constant coefficient of any +/// multiplicative term in the expression. +/// \param op is the given PTRSUB +/// \param multiplier will hold the biggest multiplicative coefficient +int8 RulePtrsubUndo::getExtraOffset(PcodeOp *op,int8 &multiplier) + +{ + int8 extra = 0; + multiplier = 1; + int8 submultiplier; + Varnode *outvn = op->getOut(); + op = outvn->loneDescend(); + while(op != (PcodeOp *)0) { + OpCode opc = op->code(); + if (opc == CPUI_INT_ADD) { + int4 slot = op->getSlot(outvn); + extra += getConstOffsetBack(op->getIn(1-slot),submultiplier,DEPTH_LIMIT); // Get any constants from other input + if (submultiplier > multiplier) + multiplier = submultiplier; + } + else if (opc == CPUI_PTRSUB) { + extra += op->getIn(1)->getOffset(); + } + else if (opc == CPUI_PTRADD) { + if (op->getIn(0) != outvn) break; + int8 ptraddmult = op->getIn(2)->getOffset(); + Varnode *invn = op->getIn(1); + if (invn->isConstant()) // Only contribute to the extra + extra += ptraddmult * (int8)invn->getOffset(); // if the index is constant + getConstOffsetBack(invn,submultiplier,DEPTH_LIMIT); // otherwise just contribute to multiplier + submultiplier *= ptraddmult; + if (submultiplier > multiplier) + multiplier = submultiplier; + } + else { + break; + } + outvn = op->getOut(); + op = outvn->loneDescend(); + } + extra = sign_extend(extra, 8*outvn->getSize()-1); + extra &= calc_mask(outvn->getSize()); + return extra; +} + +/// \brief Remove any constants in the additive expression rooted at the given PcodeOp +/// +/// Walking recursively through the expression, any INT_ADD with a constant input is converted to +/// a COPY. The INT_ADD must only contribute to the root expression. +/// \param op is the given root PcodeOp +/// \param slot is the input slot to walk back from +/// \param maxLevel is the maximum depth to recurse +/// \param data is the function containing the expression +/// \return the sum of all constants that are removed +int8 RulePtrsubUndo::removeLocalAddRecurse(PcodeOp *op,int4 slot,int4 maxLevel,Funcdata &data) + +{ + Varnode *vn = op->getIn(slot); + if (!vn->isWritten()) + return 0; + if (vn->loneDescend() != op) + return 0; // Varnode must not be used anywhere else + maxLevel -= 1; + if (maxLevel < 0) + return 0; + op = vn->getDef(); + int8 retval = 0; + if (op->code() == CPUI_INT_ADD) { + if (op->getIn(1)->isConstant()) { + retval += (int8)op->getIn(1)->getOffset(); + data.opRemoveInput(op, 1); + data.opSetOpcode(op, CPUI_COPY); + } + else { + retval += removeLocalAddRecurse(op, 0, maxLevel, data); + retval += removeLocalAddRecurse(op, 1, maxLevel, data); + } + } + return retval; +} + +/// \brief Remove constants in the additive expression involving the given Varnode +/// +/// Any additional PTRADD, PTRSUB, or INT_ADD that uses the Varnode and adds a constant is converted +/// to a COPY. Additionally any other INT_ADD involved in the expression that adds a constant is +/// also converted to COPY. +/// \param vn is the given Varnode +/// \param data is the function containing the expression +/// \return the sum of all constants that are removed +int8 RulePtrsubUndo::removeLocalAdds(Varnode *vn,Funcdata &data) + +{ + int8 extra = 0; + PcodeOp *op = vn->loneDescend(); + while(op != (PcodeOp *)0) { + OpCode opc = op->code(); + if (opc == CPUI_INT_ADD) { + int4 slot = op->getSlot(vn); + if (slot == 0 && op->getIn(1)->isConstant()) { + extra += (int8)op->getIn(1)->getOffset(); + data.opRemoveInput(op, 1); + data.opSetOpcode(op, CPUI_COPY); + } + else { + extra += removeLocalAddRecurse(op,1-slot,DEPTH_LIMIT, data); // Get any constants from other input + } + } + else if (opc == CPUI_PTRSUB) { + extra += op->getIn(1)->getOffset(); + op->clearStopTypePropagation(); + data.opRemoveInput(op, 1); + data.opSetOpcode(op, CPUI_COPY); + } + else if (opc == CPUI_PTRADD) { + if (op->getIn(0) != vn) break; + int8 ptraddmult = op->getIn(2)->getOffset(); + Varnode *invn = op->getIn(1); + if (invn->isConstant()) { + extra += ptraddmult * (int8)invn->getOffset(); + data.opRemoveInput(op,2); + data.opRemoveInput(op,1); + data.opSetOpcode(op, CPUI_COPY); + } + } + else { + break; + } + vn = op->getOut(); + op = vn->loneDescend(); + } + return extra; +} + int4 RulePtrsubUndo::applyOp(PcodeOp *op,Funcdata &data) { if (!data.hasTypeRecoveryStarted()) return 0; Varnode *basevn = op->getIn(0); - if (basevn->getTypeReadFacing(op)->isPtrsubMatching(op->getIn(1)->getOffset())) + Varnode *cvn = op->getIn(1); + int8 val = cvn->getOffset(); + int8 multiplier; + int8 extra = getExtraOffset(op,multiplier); + if (basevn->getTypeReadFacing(op)->isPtrsubMatching(val,extra,multiplier)) return 0; data.opSetOpcode(op,CPUI_INT_ADD); op->clearStopTypePropagation(); + extra = removeLocalAdds(op->getOut(),data); + if (extra != 0) { + val = val + extra; // Lump extra into additive offset + data.opSetInput(op,data.newConstant(cvn->getSize(), val & calc_mask(cvn->getSize())),1); + } return 1; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh index b38af07623..97c299a04b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -71,6 +71,7 @@ class AddTreeState { bool checkTerm(Varnode *vn,uint8 treeCoeff); ///< Accumulate details of given term and continue tree traversal bool spanAddTree(PcodeOp *op,uint8 treeCoeff); ///< Walk the given sub-tree accumulating details void calcSubtype(void); ///< Calculate final sub-type offset + void assignPropagatedType(PcodeOp *op); ///< Assign a data-type propagated through the given PcodeOp Varnode *buildMultiples(void); ///< Build part of tree that is multiple of base size Varnode *buildExtra(void); ///< Build part of tree not accounted for by multiples or \e offset bool buildDegenerate(void); ///< Transform ADD into degenerate PTRADD @@ -1079,6 +1080,11 @@ public: virtual int4 applyOp(PcodeOp *op,Funcdata &data); }; class RulePtrsubUndo : public Rule { + static const int4 DEPTH_LIMIT; ///< The maximum depth of the additive expression to check + static int8 getConstOffsetBack(Varnode *vn,int8 &multiplier,int4 maxLevel); + static int8 getExtraOffset(PcodeOp *op,int8 &multiplier); + static int8 removeLocalAdds(Varnode *vn,Funcdata &data); + static int8 removeLocalAddRecurse(PcodeOp *op,int4 slot,int4 maxLevel,Funcdata &data); public: RulePtrsubUndo(const string &g) : Rule(g, 0, "ptrsubundo") {} ///< Constructor virtual Rule *clone(const ActionGroupList &grouplist) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index 7862028213..38376a0b26 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -528,8 +528,10 @@ void Datatype::calcAlignSize(void) /// A CPUI_PTRSUB must act on a pointer data-type where the given offset addresses a component. /// Perform this check. /// \param off is the given offset +/// \param extra is any additional constant being added to the pointer +/// \param multiplier is the size of any index multiplier being added to the pointer /// \return \b true if \b this is a suitable PTRSUB data-type -bool Datatype::isPtrsubMatching(uintb off) const +bool Datatype::isPtrsubMatching(int8 off,int8 extra,int8 multiplier) const { return false; @@ -897,14 +899,14 @@ void TypePointer::printRaw(ostream &s) const Datatype *TypePointer::getSubType(int8 off,int8 *newoff) const { - if (truncate == (TypePointer *)0) - return truncate; - int8 min = ((flags & truncate_bigendian) != 0) ? size - truncate->getSize() : 0; - if (off >= min && off < min + truncate->getSize()) { - *newoff = off - min; - return truncate; + if (truncate != (TypePointer *)0) { + int8 min = ((flags & truncate_bigendian) != 0) ? size - truncate->getSize() : 0; + if (off >= min && off < min + truncate->getSize()) { + *newoff = off - min; + return truncate; + } } - return (Datatype *)0; + return Datatype::getSubType(off, newoff); } int4 TypePointer::compare(const Datatype &op,int4 level) const @@ -960,6 +962,27 @@ void TypePointer::encode(Encoder &encoder) const encoder.closeElement(ELEM_TYPE); } +/// If the given data-type is an array, or has an arrayed component, return \b true. +/// \param dt is the given data-type to check +/// \param off is the out-of-bounds offset +/// \return \b true is an array is present +bool TypePointer::testForArraySlack(Datatype *dt,int8 off) + +{ + int8 newoff; + int8 elSize; + if (dt->getMetatype() == TYPE_ARRAY) + return true; + Datatype *compType; + if (off < 0) { + compType = dt->nearestArrayedComponentForward(off, &newoff, &elSize); + } + else { + compType = dt->nearestArrayedComponentBackward(off, &newoff, &elSize); + } + return (compType != (Datatype *)0); +} + /// Parse a \ element with a child describing the data-type being pointed to /// \param decoder is the stream decoder /// \param typegrp is the factory owning \b this data-type @@ -1070,19 +1093,49 @@ TypePointer *TypePointer::downChain(int8 &off,TypePointer *&par,int8 &parOff,boo return typegrp.getTypePointer(size,pt,wordsize); } -bool TypePointer::isPtrsubMatching(uintb off) const +bool TypePointer::isPtrsubMatching(int8 off,int8 extra,int8 multiplier) const { - if (ptrto->getMetatype()==TYPE_SPACEBASE) { + type_metatype meta = ptrto->getMetatype(); + if (meta==TYPE_SPACEBASE) { int8 newoff = AddrSpace::addressToByteInt(off,wordsize); - ptrto->getSubType(newoff,&newoff); - if (newoff != 0) + Datatype *subType = ptrto->getSubType(newoff,&newoff); + if (subType == (Datatype *)0 || newoff != 0) + return false; + extra = AddrSpace::addressToByteInt(extra,wordsize); + if (extra < 0 || extra >= subType->getSize()) { + if (!testForArraySlack(subType, extra)) + return false; + } + } + else if (meta == TYPE_ARRAY) { + if (off != 0) + return false; + multiplier = AddrSpace::addressToByteInt(multiplier,wordsize); + if (multiplier >= ptrto->getAlignSize()) return false; } - else if (ptrto->getMetatype() == TYPE_ARRAY || ptrto->getMetatype() == TYPE_STRUCT) { + else if (meta == TYPE_STRUCT) { int4 typesize = ptrto->getSize(); - if ((typesize <= AddrSpace::addressToByteInt(off,wordsize))&&(typesize!=0)) + multiplier = AddrSpace::addressToByteInt(multiplier,wordsize); + if (multiplier >= ptrto->getAlignSize()) return false; + int8 newoff = AddrSpace::addressToByteInt(off,wordsize); + extra = AddrSpace::addressToByteInt(extra, wordsize); + Datatype *subType = ptrto->getSubType(newoff,&newoff); + if (subType != (Datatype *)0) { + if (newoff != 0) + return false; + if (extra < 0 || extra >= subType->getSize()) { + if (!testForArraySlack(subType, extra)) + return false; + } + } + else { + extra += newoff; + if ((extra < 0 || extra >= typesize)&&(typesize!=0)) + return false; + } } else if (ptrto->getMetatype() == TYPE_UNION) { // A PTRSUB reaching here cannot be used for a union field resolution @@ -1154,6 +1207,8 @@ int4 TypeArray::compareDependency(const Datatype &op) const Datatype *TypeArray::getSubType(int8 off,int8 *newoff) const { // Go down exactly one level, to type of element + if (off >= size) + return Datatype::getSubType(off, newoff); *newoff = off % arrayof->getAlignSize(); return arrayof; } @@ -2465,13 +2520,14 @@ TypePointer *TypePointerRel::downChain(int8 &off,TypePointer *&par,int8 &parOff, return origPointer->downChain(off,par,parOff,allowArrayWrap,typegrp); } -bool TypePointerRel::isPtrsubMatching(uintb off) const +bool TypePointerRel::isPtrsubMatching(int8 off,int8 extra,int8 multiplier) const { if (stripped != (TypePointer *)0) - return TypePointer::isPtrsubMatching(off); + return TypePointer::isPtrsubMatching(off,extra,multiplier); int4 iOff = AddrSpace::addressToByteInt(off,wordsize); - iOff += offset; + extra = AddrSpace::addressToByteInt(extra, wordsize); + iOff += offset + extra; return (iOff >= 0 && iOff <= parent->getSize()); } @@ -3874,27 +3930,27 @@ TypePointer *TypeFactory::resizePointer(TypePointer *ptr,int4 newSize) Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size) { - if (offset + size > ct->getSize()) - return (Datatype *)0; Datatype *lastType = (Datatype *)0; int8 lastOff = 0; int8 curOff = offset; do { - if (ct->getSize() <= size) { - if (ct->getSize() == size) - return ct; // Perfect size match - break; + if (ct->getSize() < size + curOff) { // Range is beyond end of current data-type + break; // Construct partial around last data-type } - else if (ct->getMetatype() == TYPE_UNION) { + if (ct->getSize() == size) + return ct; // Perfect size match + if (ct->getMetatype() == TYPE_UNION) { return getTypePartialUnion((TypeUnion *)ct, curOff, size); } lastType = ct; lastOff = curOff; ct = ct->getSubType(curOff,&curOff); } while(ct != (Datatype *)0); - // If we reach here, lastType is bigger than size - if (lastType->getMetatype() == TYPE_STRUCT || lastType->getMetatype() == TYPE_ARRAY) - return getTypePartialStruct(lastType, lastOff, size); + if (lastType != (Datatype *)0) { + // If we reach here, lastType is bigger than size + if (lastType->getMetatype() == TYPE_STRUCT || lastType->getMetatype() == TYPE_ARRAY) + return getTypePartialStruct(lastType, lastOff, size); + } return (Datatype *)0; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index 5fa888190e..32cf96dbb0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -268,7 +268,7 @@ public: virtual int4 compare(const Datatype &op,int4 level) const; ///< Order types for propagation virtual int4 compareDependency(const Datatype &op) const; ///< Compare for storage in tree structure virtual void encode(Encoder &encoder) const; ///< Encode the data-type to a stream - virtual bool isPtrsubMatching(uintb off) const; ///< Is this data-type suitable as input to a CPUI_PTRSUB op + virtual bool isPtrsubMatching(int8 off,int8 extra,int8 multiplier) const; ///< Is this data-type suitable as input to a CPUI_PTRSUB op virtual Datatype *getStripped(void) const; ///< Get a stripped version of \b this for formal use in formal declarations virtual Datatype *resolveInFlow(PcodeOp *op,int4 slot); ///< Tailor data-type propagation based on Varnode use virtual Datatype* findResolve(const PcodeOp *op,int4 slot); ///< Find a previously resolved sub-type @@ -393,6 +393,7 @@ protected: AddrSpace *spaceid; ///< If non-null, the address space \b this is intented to point into TypePointer *truncate; ///< Truncated form of the pointer (if not null) uint4 wordsize; ///< What size unit does the pointer address + static bool testForArraySlack(Datatype *dt,int8 off); ///< Test if an \e out-of-bounds offset makes sense as array slack void decode(Decoder &decoder,TypeFactory &typegrp); ///< Restore \b this pointer data-type from a stream void calcSubmeta(void); ///< Calculate specific submeta for \b this pointer void calcTruncate(TypeFactory &typegrp); // Assign a truncated pointer subcomponent if necessary @@ -420,7 +421,7 @@ public: virtual Datatype *clone(void) const { return new TypePointer(*this); } virtual void encode(Encoder &encoder) const; virtual TypePointer *downChain(int8 &off,TypePointer *&par,int8 &parOff,bool allowArrayWrap,TypeFactory &typegrp); - virtual bool isPtrsubMatching(uintb off) const; + virtual bool isPtrsubMatching(int8 off,int8 extra,int8 multiplier) const; virtual Datatype *resolveInFlow(PcodeOp *op,int4 slot); virtual Datatype* findResolve(const PcodeOp *op,int4 slot); }; @@ -633,7 +634,7 @@ public: virtual Datatype *clone(void) const { return new TypePointerRel(*this); } virtual void encode(Encoder &encoder) const; virtual TypePointer *downChain(int8 &off,TypePointer *&par,int8 &parOff,bool allowArrayWrap,TypeFactory &typegrp); - virtual bool isPtrsubMatching(uintb off) const; + virtual bool isPtrsubMatching(int8 off,int8 extra,int8 multiplier) const; virtual Datatype *getStripped(void) const { return stripped; } ///< Get the plain form of the pointer static Datatype *getPtrToFromParent(Datatype *base,int4 off,TypeFactory &typegrp); }; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc index 3fb7fa9bb6..3264c3fd7d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc @@ -411,15 +411,15 @@ Datatype *TypeOpLoad::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn, Datatype *newtype; if (inslot == -1) { // Propagating output to input (value to ptr) AddrSpace *spc = op->getIn(0)->getSpaceFromConst(); - newtype = tlst->getTypePointerNoDepth(outvn->getTempType()->getSize(),alttype,spc->getWordSize()); + newtype = tlst->getTypePointerNoDepth(outvn->getSize(),alttype,spc->getWordSize()); } else if (alttype->getMetatype()==TYPE_PTR) { newtype = ((TypePointer *)alttype)->getPtrTo(); - if (newtype->getSize() != outvn->getTempType()->getSize() || newtype->isVariableLength()) // Size must be appropriate - newtype = outvn->getTempType(); + if (newtype->getSize() != outvn->getSize() || newtype->isVariableLength()) // Size must be appropriate + newtype = (Datatype *)0; } else - newtype = outvn->getTempType(); // Don't propagate anything + newtype = (Datatype *)0; // Don't propagate anything return newtype; } @@ -486,15 +486,15 @@ Datatype *TypeOpStore::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn Datatype *newtype; if (inslot==2) { // Propagating value to ptr AddrSpace *spc = op->getIn(0)->getSpaceFromConst(); - newtype = tlst->getTypePointerNoDepth(outvn->getTempType()->getSize(),alttype,spc->getWordSize()); + newtype = tlst->getTypePointerNoDepth(outvn->getSize(),alttype,spc->getWordSize()); } else if (alttype->getMetatype()==TYPE_PTR) { newtype = ((TypePointer *)alttype)->getPtrTo(); - if (newtype->getSize() != outvn->getTempType()->getSize() || newtype->isVariableLength()) - newtype = outvn->getTempType(); + if (newtype->getSize() != outvn->getSize() || newtype->isVariableLength()) + newtype = (Datatype *)0; } else - newtype = outvn->getTempType(); // Don't propagate anything + newtype = (Datatype *)0; // Don't propagate anything return newtype; } @@ -1106,7 +1106,7 @@ Datatype *TypeOpIntAdd::propagateType(Datatype *alttype,PcodeOp *op,Varnode *inv if (outvn->isConstant() && (alttype->getMetatype() != TYPE_PTR)) newtype = alttype; else if (inslot == -1) // Propagating output to input - newtype = op->getIn(outslot)->getTempType(); // Don't propagate pointer types this direction + newtype = (Datatype *)0; // Don't propagate pointer types this direction else newtype = propagateAddIn2Out(alttype,tlst,op,inslot); return newtype; @@ -1130,7 +1130,7 @@ Datatype *TypeOpIntAdd::propagateAddIn2Out(Datatype *alttype,TypeFactory *typegr TypePointer *pointer = (TypePointer *)alttype; uintb offset; int4 command = propagateAddPointer(offset,op,inslot,pointer->getPtrTo()->getAlignSize()); - if (command == 2) return op->getOut()->getTempType(); // Doesn't look like a good pointer add + if (command == 2) return (Datatype *)0; // Doesn't look like a good pointer add TypePointer *parent = (TypePointer *)0; int8 parentOff; if (command != 3) { @@ -1155,7 +1155,7 @@ Datatype *TypeOpIntAdd::propagateAddIn2Out(Datatype *alttype,TypeFactory *typegr if (pointer == (TypePointer *)0) { if (command == 0) return alttype; - return op->getOut()->getTempType(); + return (Datatype *)0; } if (op->getIn(inslot)->isSpacebase()) { if (pointer->getPtrTo()->getMetatype() == TYPE_SPACEBASE) @@ -2112,7 +2112,7 @@ Datatype *TypeOpPtradd::propagateType(Datatype *alttype,PcodeOp *op,Varnode *inv if (metain != TYPE_PTR) return (Datatype *)0; Datatype *newtype; if (inslot == -1) // Propagating output to input - newtype = op->getIn(outslot)->getTempType(); // Don't propagate pointer types this direction + newtype = (Datatype *)0; // Don't propagate pointer types this direction else newtype = TypeOpIntAdd::propagateAddIn2Out(alttype,tlst,op,inslot); return newtype; @@ -2192,7 +2192,7 @@ Datatype *TypeOpPtrsub::propagateType(Datatype *alttype,PcodeOp *op,Varnode *inv if (metain != TYPE_PTR) return (Datatype *)0; Datatype *newtype; if (inslot == -1) // Propagating output to input - newtype = op->getIn(outslot)->getTempType(); // Don't propagate pointer types this direction + newtype = (Datatype *)0; // Don't propagate pointer types this direction else newtype = TypeOpIntAdd::propagateAddIn2Out(alttype,tlst,op,inslot); return newtype;