From 74e1bbdb57788c05d3598a4740327d5878d0fc63 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Tue, 28 Feb 2023 12:40:24 -0500 Subject: [PATCH] GP-3148 More join space overlaps --- .../Decompiler/src/decompile/cpp/address.cc | 26 -------- .../Decompiler/src/decompile/cpp/address.hh | 20 +++++- .../Decompiler/src/decompile/cpp/block.cc | 5 +- .../src/decompile/cpp/coreaction.cc | 3 + .../Decompiler/src/decompile/cpp/flow.cc | 9 ++- .../src/decompile/cpp/funcdata_block.cc | 10 ++- .../Decompiler/src/decompile/cpp/jumptable.cc | 2 +- .../Decompiler/src/decompile/cpp/space.cc | 61 ++++++++++++++----- .../Decompiler/src/decompile/cpp/space.hh | 4 +- .../Decompiler/src/decompile/cpp/translate.cc | 30 ++++++--- .../ghidra/program/model/lang/ParamEntry.java | 58 +++++++++++++++++- .../program/model/lang/ParamListStandard.java | 9 ++- 12 files changed, 168 insertions(+), 69 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc index 08ac817926..26694b558f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc @@ -162,32 +162,6 @@ int4 Address::overlap(int4 skip,const Address &op,int4 size) const return (int4) dist; } -/// This method is equivalent to Address::overlap, but a range in the \e join space can be -/// considered overlapped with its constituent pieces. -/// If \e this + \e skip falls in the range, \e op to \e op + \e size, then a non-negative integer is -/// returned indicating where in the interval it falls. Otherwise -1 is returned. -/// \param skip is an adjust to \e this address -/// \param op is the start of the range to check -/// \param size is the size of the range -/// \return an integer indicating how overlap occurs -int4 Address::overlapJoin(int4 skip,const Address &op,int4 size) const - -{ - if (base != op.base) { - if (op.base->getType()==IPTR_JOIN) { - uintb dist = base->wrapOffset(offset + skip); - return ((JoinSpace *)op.base)->pieceOverlap(op.offset,size,base,dist); - } - return -1; - } - if (base->getType()==IPTR_CONSTANT) return -1; // Must not be constants - - uintb dist = base->wrapOffset(offset+skip-op.offset); - - if (dist >= size) return -1; // but must fall before op+size - return (int4) dist; -} - /// Does the location \e this, \e sz form a contiguous region to \e loaddr, \e losz, /// where \e this forms the most significant piece of the logical whole /// \param sz is the size of \e this hi region diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh index c3bea29ea8..8ca8477d51 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh @@ -87,9 +87,9 @@ public: friend ostream &operator<<(ostream &s,const Address &addr); ///< Write out an address to stream bool containedBy(int4 sz,const Address &op2,int4 sz2) const; ///< Determine if \e op2 range contains \b this range int4 justifiedContain(int4 sz,const Address &op2,int4 sz2,bool forceleft) const; ///< Determine if \e op2 is the least significant part of \e this. - int4 overlap(int4 skip,const Address &op,int4 size) const; ///< Determine how two address ranges overlap - int4 overlapJoin(int4 skip,const Address &op,int4 size) const; ///< Determine how two ranges overlap, when one might be in the \e join space - bool isContiguous(int4 sz,const Address &loaddr,int4 losz) const; ///< Does \e this form a contigous range with \e loaddr + int4 overlap(int4 skip,const Address &op,int4 size) const; ///< Determine how \b this address falls in a given address range + int4 overlapJoin(int4 skip,const Address &op,int4 size) const; ///< Determine how \b this falls in a possible \e join space address range + bool isContiguous(int4 sz,const Address &loaddr,int4 losz) const; ///< Does \e this form a contiguous range with \e loaddr bool isConstant(void) const; ///< Is this a \e constant \e value void renormalize(int4 size); ///< Make sure there is a backing JoinRecord if \b this is in the \e join space bool isJoin(void) const; ///< Is this a \e join \e value @@ -432,6 +432,20 @@ inline Address Address::operator-(int4 off) const { return Address(base,base->wrapOffset(offset-off)); } +/// This method is equivalent to Address::overlap, but a range in the \e join space can be +/// considered overlapped with its constituent pieces. +/// If \e this + \e skip falls in the range, \e op to \e op + \e size, then a non-negative integer is +/// returned indicating where in the interval it falls. Otherwise -1 is returned. +/// \param skip is an adjust to \e this address +/// \param op is the start of the range to check +/// \param size is the size of the range +/// \return an integer indicating how overlap occurs +inline int4 Address::overlapJoin(int4 skip,const Address &op,int4 size) const + +{ + return op.getSpace()->overlapJoin(op.getOffset(), size, base, offset, skip); +} + /// Determine if this address is from the \e constant \e space. /// All constant values are represented as an offset into /// the \e constant \e space. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc index a79e17dedc..eef4a58b97 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc @@ -2616,10 +2616,9 @@ bool BlockBasic::noInterveningStatement(PcodeOp *first,int4 path,PcodeOp *last) return false; } -/// If there exists a CPUI_MULTIEQUAL PcodeOp in the given basic block that takes this exact list of Varnodes +/// If there exists a CPUI_MULTIEQUAL PcodeOp in \b this basic block that takes the given exact list of Varnodes /// as its inputs, return that PcodeOp. Otherwise return null. /// \param varArray is the exact list of Varnodes -/// \param bl is the basic block /// \return the MULTIEQUAL or null PcodeOp *BlockBasic::findMultiequal(const vector &varArray) @@ -2646,7 +2645,7 @@ PcodeOp *BlockBasic::findMultiequal(const vector &varArray) /// with the input Varnode in the indicated slot. /// \param varArray is the given array of Varnodes /// \param slot is the indicated slot -/// \return \true if all the Varnodes are defined in the same way +/// \return \b true if all the Varnodes are defined in the same way bool BlockBasic::liftVerifyUnroll(vector &varArray,int4 slot) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 5efffd2de4..0c28dbb954 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -3946,6 +3946,9 @@ int4 ActionDeadCode::apply(Funcdata &data) return 0; } +/// \brief Clear all marks on the given list of PcodeOps +/// +/// \param opList is the given list void ActionConditionalConst::clearMarks(const vector &opList) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc index f688535a6d..9235d71c7c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc @@ -1380,9 +1380,14 @@ void FlowInfo::checkMultistageJumptables(void) } } -/// \brief Recover jumptables for current set of BRANCHIND ops using existing flow +/// \brief Recover jumptables for the current set of BRANCHIND ops using existing flow /// -/// \param newTables will hold one JumpTable pointer for each BRANCHIND in \b tablelist +/// This method passes back a list of JumpTable objects, one for each BRANCHIND in the current +/// \b tablelist where the jumptable can be recovered. If a particular BRANCHIND cannot be recovered +/// because the current partial control flow cannot legally reach it, the BRANCHIND is passed back +/// in a separate list. +/// \param newTables will hold the list of recovered JumpTables +/// \param notreached will hold the list of BRANCHIND ops that could not be reached void FlowInfo::recoverJumpTables(vector &newTables,vector ¬reached) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc index b5e311dd70..d67f5252ca 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc @@ -623,13 +623,11 @@ bool Funcdata::earlyJumpTableFail(PcodeOp *op) return false; } -/// \brief Recover destinations for a BRANCHIND by analyzing nearby data and control-flow +/// \brief Recover control-flow destinations for a BRANCHIND /// -/// This is the high-level entry point for jump-table/switch recovery. In short, a -/// copy of the current state of data-flow is made, simplification transformations are applied -/// to the copy, and the resulting data-flow tree is examined to enumerate possible values -/// of the input Varnode to the given BRANCHIND PcodeOp. This information is stored in a -/// JumpTable object. +/// If an existing and complete JumpTable exists for the BRANCHIND, it is returned immediately. +/// Otherwise an attempt is made to analyze the current partial function and recover the set of destination +/// addresses, which if successful will be returned as a new JumpTable object. /// \param partial is the Funcdata copy to perform analysis on if necessary /// \param op is the given BRANCHIND PcodeOp /// \param flow is current flow information for \b this function diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc index 1c032adad8..47d220df0a 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc @@ -606,7 +606,7 @@ static bool matching_constants(Varnode *vn1,Varnode *vn2) /// \param path is the specific branch to take from the CBRANCH to reach the switch /// \param rng is the range of values causing the switch path to be taken /// \param v is the Varnode holding the value controlling the CBRANCH -/// \param unroll is \b true if the guard is duplicated across multiple blocks +/// \param unr is \b true if the guard is duplicated across multiple blocks GuardRecord::GuardRecord(PcodeOp *bOp,PcodeOp *rOp,int4 path,const CircleRange &rng,Varnode *v,bool unr) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc index fcb309e1f8..c40c2599e8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc @@ -126,6 +126,30 @@ void AddrSpace::truncateSpace(uint4 newsize) calcScaleMask(); } +/// \brief Determine if a given point is contained in an address range in \b this address space +/// +/// The point is specified as an address space and offset pair plus an additional number of bytes to "skip". +/// A non-negative value is returned if the point falls in the address range. +/// If the point falls on the first byte of the range, 0 is returned. For the second byte, 1 is returned, etc. +/// Otherwise -1 is returned. +/// \param offset is the starting offset of the address range within \b this space +/// \param size is the size of the address range in bytes +/// \param pointSpace is the address space of the given point +/// \param pointOff is the offset of the given point +/// \param pointSkip is the additional bytes to skip +/// \return a non-negative value indicating where the point falls in the range, or -1 +int4 AddrSpace::overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOff,int4 pointSkip) const + +{ + if (this != pointSpace) + return -1; + + uintb dist = wrapOffset(pointOff+pointSkip-offset); + + if (dist >= size) return -1; // but must fall before op+size + return (int4) dist; +} + /// Write the main attributes for an address within \b this space. /// The caller provides only the \e offset, and this routine fills /// in other details pertaining to this particular space. @@ -363,6 +387,12 @@ ConstantSpace::ConstantSpace(AddrSpaceManager *m,const Translate *t) setFlags(big_endian); } +int4 ConstantSpace::overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOff,int4 pointSkip) const + +{ + return -1; +} + /// Constants are always printed as hexidecimal values in /// the debugger and console dumps void ConstantSpace::printRaw(ostream &s,uintb offset) const @@ -471,21 +501,22 @@ JoinSpace::JoinSpace(AddrSpaceManager *m,const Translate *t,int4 ind) clearFlags(heritaged); // This space is never heritaged, but does dead-code analysis } -/// \brief Calculate where a given address falls inside a range in the \e join space -/// -/// The given address, specified as an (AddrSpace, offset) pair is assumed to not be in the \e join space, -/// but is assumed to be in one of the pieces of the join. A final non-negative offset is returned if the -/// address falls in one of the pieces. The offset is in data order, i.e. the offset is 0 if the given Address -/// matches the address of the first element in an array overlaid on the \e join range. -/// -1 is returned if the given Address does not lie in any piece. -/// \param offset is the offset into the \e join space, specifying the range -/// \param size is the size of the range, which may be smaller than the size of the JoinRecord -/// \param pieceSpace is the AddrSpace of the given address -/// \param pieceOffset is the offset of the given address -/// \return a non-negative offset indicating where in the range the Address lies, or -1 -int4 JoinSpace::pieceOverlap(uintb offset,int4 size,AddrSpace *pieceSpace,uintb pieceOffset) const +int4 JoinSpace::overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOffset,int4 pointSkip) const { + if (this == pointSpace) { + // If the point is in the join space, translate the point into the piece address space + JoinRecord *pieceRecord = getManager()->findJoin(pointOffset); + int4 pos; + Address addr = pieceRecord->getEquivalentAddress(pointOffset + pointSkip, pos); + pointSpace = addr.getSpace(); + pointOffset = addr.getOffset(); + } + else { + if (pointSpace->getType() == IPTR_CONSTANT) + return -1; + pointOffset = pointSpace->wrapOffset(pointOffset + pointSkip); + } JoinRecord *joinRecord = getManager()->findJoin(offset); // Set up so we traverse pieces in data order int4 startPiece,endPiece,dir; @@ -502,8 +533,8 @@ int4 JoinSpace::pieceOverlap(uintb offset,int4 size,AddrSpace *pieceSpace,uintb int4 bytesAccum = 0; for(int4 i=startPiece;i!=endPiece;i += dir) { const VarnodeData &vData(joinRecord->getPiece(i)); - if (vData.space == pieceSpace && pieceOffset >= vData.offset && pieceOffset <= vData.offset + (vData.size-1)) { - int4 res = (int4)(pieceOffset - vData.offset) + bytesAccum; + if (vData.space == pointSpace && pointOffset >= vData.offset && pointOffset <= vData.offset + (vData.size-1)) { + int4 res = (int4)(pointOffset - vData.offset) + bytesAccum; if (res >= size) return -1; return res; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh index 4f201209b2..362fbe42e8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh @@ -163,6 +163,7 @@ public: virtual const VarnodeData &getSpacebaseFull(int4 i) const; ///< Return original spacebase register before truncation virtual bool stackGrowsNegative(void) const; ///< Return \b true if a stack in this space grows negative virtual AddrSpace *getContain(void) const; ///< Return this space's containing space (if any) + virtual int4 overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOff,int4 pointSkip) const; virtual void encodeAttributes(Encoder &encoder,uintb offset) const; ///< Encode address attributes to a stream virtual void encodeAttributes(Encoder &encoder,uintb offset,int4 size) const; ///< Encode an address and size attributes to a stream virtual uintb decodeAttributes(Decoder &decoder,uint4 &size) const; ///< Recover an offset and size @@ -193,6 +194,7 @@ public: class ConstantSpace : public AddrSpace { public: ConstantSpace(AddrSpaceManager *m,const Translate *t); ///< Only constructor + virtual int4 overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOff,int4 pointSkip) const; virtual void printRaw(ostream &s,uintb offset) const; virtual void saveXml(ostream &s) const; virtual void decode(Decoder &decoder); @@ -240,7 +242,7 @@ public: class JoinSpace : public AddrSpace { public: JoinSpace(AddrSpaceManager *m,const Translate *t,int4 ind); - int4 pieceOverlap(uintb offset,int4 size,AddrSpace *pieceSpace,uintb pieceOffset) const; + virtual int4 overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOff,int4 pointSkip) const; virtual void encodeAttributes(Encoder &encoder,uintb offset) const; virtual void encodeAttributes(Encoder &encoder,uintb offset,int4 size) const; virtual uintb decodeAttributes(Decoder &decoder,uint4 &size) const; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc index 6e379031af..61b8cc108f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc @@ -849,18 +849,32 @@ void AddrSpaceManager::renormalizeJoinAddress(Address &addr,int4 size) return; } vector newPieces; - newPieces.push_back(joinRecord->pieces[pos1]); int4 sizeTrunc1 = (int4)(addr1.getOffset() - joinRecord->pieces[pos1].offset); - pos1 += 1; - while(pos1 <= pos2) { + int4 sizeTrunc2 = joinRecord->pieces[pos2].size - (int4)(addr2.getOffset() - joinRecord->pieces[pos2].offset) - 1; + + if (pos2 < pos1) { // Little endian + newPieces.push_back(joinRecord->pieces[pos2]); + pos2 += 1; + while(pos2 <= pos1) { + newPieces.push_back(joinRecord->pieces[pos2]); + pos2 += 1; + } + newPieces.back().offset = addr1.getOffset(); + newPieces.back().size -= sizeTrunc1; + newPieces.front().size -= sizeTrunc2; + } + else { newPieces.push_back(joinRecord->pieces[pos1]); pos1 += 1; + while(pos1 <= pos2) { + newPieces.push_back(joinRecord->pieces[pos1]); + pos1 += 1; + } + newPieces.front().offset = addr1.getOffset(); + newPieces.front().size -= sizeTrunc1; + newPieces.back().size -= sizeTrunc2; } - int4 sizeTrunc2 = joinRecord->pieces[pos2].size - (int4)(addr2.getOffset() - joinRecord->pieces[pos2].offset) - 1; - newPieces.front().offset = addr1.getOffset(); - newPieces.front().size -= sizeTrunc1; - newPieces.back().size -= sizeTrunc2; - JoinRecord *newJoinRecord = findAddJoin(newPieces, size); + JoinRecord *newJoinRecord = findAddJoin(newPieces, 0); addr = Address(newJoinRecord->unified.space,newJoinRecord->unified.offset); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamEntry.java index ab85ddee19..5e58ed87be 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamEntry.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamEntry.java @@ -124,8 +124,62 @@ public class ParamEntry { return spaceid; } - public Varnode[] getJoinRecord() { - return joinrec; + /** + * Collect pieces from the join list, in endian order, until the given size is covered. + * The last piece is trimmed to match the size exactly. If the size is too big to be + * covered by this ParamEntry, null is returned. + * @param sz is the given size + * @return the collected array of Varnodes or null + */ + public Varnode[] getJoinPieces(int sz) { + int num = 0; + int first, replace; + Varnode vn = null; + Varnode[] res; + if (isBigEndian()) { + first = 0; + while (sz > 0) { + if (num >= joinrec.length) { + return null; + } + vn = joinrec[num]; + if (vn.getSize() > sz) { + num += 1; + break; + } + sz -= vn.getSize(); + num += 1; + } + replace = num - 1; + } + else { + while (sz > 0) { + if (num >= joinrec.length) { + return null; + } + vn = joinrec[joinrec.length - 1 - num]; + if (vn.getSize() > sz) { + num += 1; + break; + } + sz -= vn.getSize(); + num += 1; + } + first = joinrec.length - num; + replace = first; + } + if (sz == 0 && num == joinrec.length) { + return joinrec; + } + res = new Varnode[num]; + for (int i = 0; i < num; ++i) { + res[i] = joinrec[first + i]; + } + if (sz > 0) { + res[replace] = new Varnode(vn.getAddress(), sz); + } + + return res; } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamListStandard.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamListStandard.java index 56665b0010..fdb0b6e24e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamListStandard.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamListStandard.java @@ -118,8 +118,13 @@ public class ParamListStandard implements ParamList { VariableStorage store; try { if (res.space.getType() == AddressSpace.TYPE_JOIN) { - Varnode[] pieces = element.getJoinRecord(); - store = new DynamicVariableStorage(program, false, pieces); + Varnode[] pieces = element.getJoinPieces(sz); + if (pieces != null) { + store = new DynamicVariableStorage(program, false, pieces); + } + else { + store = DynamicVariableStorage.getUnassignedDynamicStorage(false); + } } else { Address addr = res.space.getAddress(res.offset);