diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index 2a833581c8..5a59a8eaea 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -39,6 +39,7 @@ src/decompile/datatests/noforloop_globcall.xml||GHIDRA||||END| src/decompile/datatests/noforloop_iterused.xml||GHIDRA||||END| src/decompile/datatests/offsetarray.xml||GHIDRA||||END| src/decompile/datatests/packstructaccess.xml||GHIDRA||||END| +src/decompile/datatests/partialmerge.xml||GHIDRA||||END| src/decompile/datatests/partialunion.xml||GHIDRA||||END| src/decompile/datatests/pointercmp.xml||GHIDRA||||END| src/decompile/datatests/pointerrel.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 3b9ebb4981..ccdcd8ae38 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -356,6 +356,10 @@ public: VarnodeLocSet::const_iterator endLoc(int4 s,const Address &addr,const Address &pc,uintm uniq=~((uintm)0)) const { return vbank.endLoc(s,addr,pc,uniq); } + /// \brief Given start, return maximal range of overlapping Varnodes + uint4 overlapLoc(VarnodeLocSet::const_iterator iter,vector &bounds) const { + return vbank.overlapLoc(iter,bounds); } + /// \brief Start of all Varnodes sorted by definition address VarnodeDefSet::const_iterator beginDef(void) const { return vbank.beginDef(); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc index cb41351e25..26458054c9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc @@ -112,6 +112,10 @@ bool Merge::mergeTestRequired(HighVariable *high_out,HighVariable *high_in) return false; // Map to different parts of same symbol } + // Currently don't allow merging of variables that are in separate overlapping collections + if (high_out->piece != (VariablePiece *)0 && high_in->piece != (VariablePiece *)0) + return false; + return true; } @@ -433,6 +437,7 @@ void Merge::eliminateIntersect(Varnode *vn,const vector &blocksort map::const_iterator iter,enditer; Varnode *vn2; int4 boundtype; + int4 overlaptype; bool insertop; for(oiter=vn->beginDescend();oiter!=vn->endDescend();++oiter) { @@ -456,6 +461,13 @@ void Merge::eliminateIntersect(Varnode *vn,const vector &blocksort if (vn2 == vn) continue; boundtype = single.containVarnodeDef(vn2); if (boundtype == 0) continue; + overlaptype = vn->characterizeOverlap(*vn2); + if (overlaptype == 0) continue; // No overlap in storage + if (overlaptype == 1) { // Partial overlap + int4 off = (int4)(vn->getOffset() - vn2->getOffset()); + if (vn->partialCopyShadow(vn2,off)) + continue; // SUBPIECE shadow, not a new value + } if (boundtype == 2) { // We have to resolve things defined at same place if (vn2->getDef() == (PcodeOp *)0) { if (vn->getDef() == (PcodeOp *)0) { @@ -483,7 +495,13 @@ void Merge::eliminateIntersect(Varnode *vn,const vector &blocksort if (indop->code() != CPUI_INDIRECT) continue; // The vn2 INDIRECT must be linked to the read op if (op != PcodeOp::getOpFromConst(indop->getIn(1)->getAddr())) continue; - if (vn->copyShadow(indop->getIn(0))) continue; // If INDIRECT input shadows vn, don't consider as intersection + if (overlaptype != 1) { + if (vn->copyShadow(indop->getIn(0))) continue; // If INDIRECT input shadows vn, don't consider as intersection + } + else { + int4 off = (int4)(vn->getOffset() - vn2->getOffset()); + if (vn->partialCopyShadow(indop->getIn(0),off)) continue; + } } insertop = true; break; // No need to continue iterating through varnodes in block @@ -500,7 +518,7 @@ void Merge::eliminateIntersect(Varnode *vn,const vector &blocksort /// /// The list of Varnodes to be merged is provided as a range in the main location sorted /// container. Any discovered intersection is \b snipped by splitting data-flow for one of -/// the Varnodes into two or more flows, which involves insert new COPY ops and temporaries. +/// the Varnodes into two or more flows, which involves inserting new COPY ops and temporaries. /// \param startiter is the beginning of the range of Varnodes with the same storage address /// \param enditer is the end of the range void Merge::unifyAddress(VarnodeLocSet::const_iterator startiter,VarnodeLocSet::const_iterator enditer) @@ -513,14 +531,14 @@ void Merge::unifyAddress(VarnodeLocSet::const_iterator startiter,VarnodeLocSet:: for(iter=startiter;iter!=enditer;++iter) { vn = *iter; + if (vn->isFree()) continue; isectlist.push_back(vn); } blocksort.resize(isectlist.size()); for(int4 i=0;i bounds; for(startiter=data.beginLoc();startiter!=data.endLoc();) { - addrtied = false; - enditer = data.endLoc((*startiter)->getSize(),(*startiter)->getAddr(),Varnode::written); - for(iter=startiter;iter!=enditer;++iter) { - if ((*iter)->isAddrTied()) { - addrtied = true; - break; + AddrSpace *spc = (*startiter)->getSpace(); + spacetype type = spc->getType(); + if (type != IPTR_PROCESSOR && type != IPTR_SPACEBASE) { + startiter = data.endLoc(spc); // Skip over the whole space + continue; + } + VarnodeLocSet::const_iterator finaliter = data.endLoc(spc); + while(startiter != finaliter) { + Varnode *vn = *startiter; + if (vn->isFree()) { + startiter = data.endLoc(vn->getSize(),vn->getAddr(),0); // Skip over any free Varnodes + continue; } + bounds.clear(); + uint4 flags = data.overlapLoc(startiter,bounds); // Collect maximally overlapping range of Varnodes + int4 max = bounds.size() - 1; // Index of last iterator + if ((flags & Varnode::addrtied) != 0) { + unifyAddress(startiter,bounds[max]); + for(int4 i=0;i 2) { + Varnode *vn1 = *bounds[0]; + for(int4 i=2;igetOffset() - vn1->getOffset()); + vn2->getHigh()->groupWith(off, vn1->getHigh()); + } + } + } + startiter = bounds[max]; } - if (addrtied) { - unifyAddress(startiter,enditer); // unify_address may stick varnodes in our range - enditer = data.endLoc((*startiter)->getSize(),(*startiter)->getAddr(),Varnode::written); - mergeRangeMust(startiter,enditer); - } - startiter = data.endLoc((*startiter)->getSize(),(*startiter)->getAddr(),0); } } @@ -1396,23 +1432,15 @@ void Merge::markInternalCopies(void) #endif } -/// \brief Perform low-level details of merging two HighVariables if possible +/// \brief Translate any intersection tests for \e high2 into tests for \e high1 /// -/// This routine only fails (returning \b false) if there is a Cover intersection between -/// the two variables. Otherwise, all the Varnode instances from the second HighVariable -/// are merged into the first and its Cover is updated. The cached intersection tests are -/// also updated to reflect the merge. -/// \param high1 is the first HighVariable being merged -/// \param high2 is the second -/// \param isspeculative is \b true if the desired merge is speculative -/// \return \b true if the merge was successful -bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative) +/// The two variables will be merged and \e high2, as an object, will be freed. +/// We update the cached intersection tests for \e high2 so that they will now apply to new merged \e high1 +/// \param high1 is the variable object being kept +/// \param high2 is the variable object being eliminated +void Merge::moveIntersectTests(HighVariable *high1,HighVariable *high2) { - if (high1 == high2) return true; // Already merged - if (intersection(high1,high2)) return false; - - // Translate any tests for high2 into tests for high1 vector yesinter; // Highs that high2 intersects vector nointer; // Highs that high2 does not intersect map::iterator iterfirst = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)0) ); @@ -1420,7 +1448,7 @@ bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative) map::iterator iter; for(iter=iterfirst;iter!=iterlast;++iter) { - HighVariable *b = (*iter).first.b; + HighVariable *b = (*iter).first.b; if (b == high1) continue; if ((*iter).second) // Save all high2's intersections yesinter.push_back(b); // as they are still valid for the merge @@ -1436,7 +1464,7 @@ bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative) highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) ); highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) ); ++iterlast; // Restore original range (with possibly new open endpoint) - + highedgemap.erase(iterfirst,iterlast); } @@ -1454,13 +1482,33 @@ bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative) vector::iterator titer; for(titer=nointer.begin();titer!=nointer.end();++titer) (*titer)->clearMark(); - // Reinsert high2's intersection==true tests for high1 now + + // Reinsert high2's intersection==true tests for high1 now for(titer=yesinter.begin();titer!=yesinter.end();++titer) { highedgemap[ HighEdge(high1,*titer) ] = true; highedgemap[ HighEdge(*titer,high1) ] = true; } +} + +/// \brief Perform low-level details of merging two HighVariables if possible +/// +/// This routine only fails (returning \b false) if there is a Cover intersection between +/// the two variables. Otherwise, all the Varnode instances from the second HighVariable +/// are merged into the first and its Cover is updated. The second variable is deleted. +/// The cached intersection tests are also updated to reflect the merge. +/// \param high1 is the first HighVariable being merged +/// \param high2 is the second +/// \param isspeculative is \b true if the desired merge is speculative +/// \return \b true if the merge was successful +bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative) + +{ + if (high1 == high2) return true; // Already merged + if (intersection(high1,high2)) return false; + + moveIntersectTests(high1, high2); high1->merge(high2,isspeculative); // Do the actual merge - high1->updateCover(); + high1->updateCover(); // Update cover now so that updateHigh won't purge cached tests return true; } @@ -1473,10 +1521,8 @@ bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative) bool Merge::updateHigh(HighVariable *a) { - if ((a->highflags&HighVariable::coverdirty)==0) return true; + if (!a->isCoverDirty()) return true; - for(int4 i=0;inumInstances();++i) - a->getInstance(i)->updateCover(); a->updateCover(); purgeHigh(a); return false; @@ -1525,7 +1571,7 @@ bool Merge::intersection(HighVariable *a,HighVariable *b) bool res = false; int4 blk; vector blockisect; - a->wholecover.intersectList(blockisect,b->wholecover,2); + a->getCover().intersectList(blockisect,b->getCover(),2); for(blk=0;blk &res) + +{ + for(int4 i=0;inumInstances();++i) { + Varnode *vn = a->getInstance(i); + if (1getCover()->intersectByBlock(blk,cover)) + res.push_back(vn); + } +} + +/// \brief Test instances of a the given HighVariable for intersection on a specific block with a cover +/// +/// A list of Varnodes has already been determined to intersect on the block. For an instance that does as +/// well, a final test of copy shadowing is performed with the Varnode list. If there is no shadowing, +/// a merging intersection has been found and \b true is returned. +/// \param a is the given HighVariable +/// \param blk is the specific block number +/// \param cover is the Cover to test for intersection +/// \param relOff is the relative byte offset of the HighVariable to the Varnodes +/// \param blist is the list of Varnodes for copy shadow testing +/// \return \b true if there is an intersection preventing merging +bool Merge::testBlockIntersection(HighVariable *a,int4 blk,const Cover &cover,int4 relOff,const vector &blist) + +{ + for(int4 i=0;inumInstances();++i) { + Varnode *vn = a->getInstance(i); + if (2>vn->getCover()->intersectByBlock(blk,cover)) continue; + for(int4 j=0;jgetCover()->intersectByBlock(blk,*vn->getCover())) { + if (vn->getSize() == vn2->getSize()) { + if (!vn->copyShadow(vn2)) + return true; + } + else { + if (!vn->partialCopyShadow(vn2,relOff)) + return true; + } + } + } + } + return false; +} + /// \brief Test if two HighVariables intersect on a given BlockBasic /// /// Intersections are checked only on the specified block. @@ -1549,19 +1645,40 @@ bool Merge::blockIntersection(HighVariable *a,HighVariable *b,int4 blk) { vector blist; - for(int4 i=0;inumInstances();++i) { - Varnode *vn = b->getInstance(i); - if (1getCover()->intersectByBlock(blk,a->wholecover)) - blist.push_back(vn); + const Cover &aCover(a->getCover()); + const Cover &bCover(b->getCover()); + gatherBlockVarnodes(b,blk,aCover,blist); + if (testBlockIntersection(a, blk, bCover, 0, blist)) + return true; + if (a->piece != (VariablePiece *)0) { + int4 baseOff = a->piece->getOffset(); + for(int4 i=0;ipiece->numIntersection();++i) { + const VariablePiece *interPiece = a->piece->getIntersection(i); + int4 off = interPiece->getOffset() - baseOff; + if (testBlockIntersection(interPiece->getHigh(), blk, bCover, off, blist)) + return true; + } } - for(int4 i=0;inumInstances();++i) { - Varnode *vn = a->getInstance(i); - if (2>vn->getCover()->intersectByBlock(blk,b->wholecover)) continue; - for(int4 j=0;jgetCover()->intersectByBlock(blk,*vn->getCover())) - if (!vn->copyShadow(vn2)) - return true; + if (b->piece != (VariablePiece *)0) { + int4 bBaseOff = b->piece->getOffset(); + for(int4 i=0;ipiece->numIntersection();++i) { + blist.clear(); + const VariablePiece *bPiece = b->piece->getIntersection(i); + int4 bOff = bPiece->getOffset() - bBaseOff; + gatherBlockVarnodes(bPiece->getHigh(),blk,aCover,blist); + if (testBlockIntersection(a, blk, bCover, -bOff, blist)) + return true; + if (a->piece != (VariablePiece *)0) { + int4 baseOff = a->piece->getOffset(); + for(int4 j=0;jpiece->numIntersection();++j) { + const VariablePiece *interPiece = a->piece->getIntersection(j); + int4 off = (interPiece->getOffset() - baseOff) - bOff; + if (off > 0 && off >= bPiece->getSize()) continue; // Do a piece and b piece intersect at all + if (off < 0 && -off >= interPiece->getSize()) continue; + if (testBlockIntersection(interPiece->getHigh(), blk, bCover, off, blist)) + return true; + } + } } } return false; @@ -1595,24 +1712,38 @@ void Merge::inflate(Varnode *a,HighVariable *high) /// Varnode is not part of the HighVariable. /// \param a is the given Varnode to inflate /// \param high is the HighVariable being propagated -/// \return \b true if the Varnode can be inflated without intersection +/// \return \b true if inflating the Varnode causes an intersection bool Merge::inflateTest(Varnode *a,HighVariable *high) { HighVariable *ahigh = a->getHigh(); - bool res = false; updateHigh(high); + const Cover &highCover( high->internalCover ); // Only check for intersections with cover contributing to inflate for(int4 i=0;inumInstances();++i) { Varnode *b = ahigh->getInstance(i); - if (b->copyShadow(a)) continue; - if (2==b->getCover()->intersect( high->wholecover )) { - res = true; - break; + if (b->copyShadow(a)) continue; // Intersection with a or shadows of a is allowed + if (2==b->getCover()->intersect( highCover )) { + return true; } } - return res; + VariablePiece *piece = ahigh->piece; + if (piece != (VariablePiece *)0) { + piece->updateIntersections(); + for(int4 i=0;inumIntersection();++i) { + const VariablePiece *otherPiece = piece->getIntersection(i); + HighVariable *otherHigh = otherPiece->getHigh(); + int4 off = otherPiece->getOffset() - piece->getOffset(); + for(int4 i=0;inumInstances();++i) { + Varnode *b = otherHigh->getInstance(i); + if (b->partialCopyShadow(a, off)) continue; // Intersection with partial shadow of a is allowed + if (2==b->getCover()->intersect( highCover )) + return true; + } + } + } + return false; } /// \brief Test for intersections between a given HighVariable and a list of other HighVariables diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh index 18c9e0b158..65349af178 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh @@ -83,6 +83,8 @@ class Merge { vector copyTrims; ///< COPY ops inserted to facilitate merges bool updateHigh(HighVariable *a); ///< Make sure given HighVariable's Cover is up-to-date void purgeHigh(HighVariable *high); ///< Remove cached intersection tests for a given HighVariable + static void gatherBlockVarnodes(HighVariable *a,int4 blk,const Cover &cover,vector &res); + static bool testBlockIntersection(HighVariable *a,int4 blk,const Cover &cover,int4 relOff,const vector &blist); bool blockIntersection(HighVariable *a,HighVariable *b,int4 blk); static bool mergeTestRequired(HighVariable *high_out,HighVariable *high_in); static bool mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in); @@ -96,6 +98,7 @@ class Merge { void collectCovering(vector &vlist,HighVariable *high,PcodeOp *op); bool collectCorrectable(const vector &vlist,list &oplist,vector &slotlist, PcodeOp *op); + void moveIntersectTests(HighVariable *high1,HighVariable *high2); PcodeOp *allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trimOp); void snipReads(Varnode *vn,list &markedop); void snipIndirect(PcodeOp *indop); @@ -148,7 +151,7 @@ public: inline bool Merge::compareHighByBlock(const HighVariable *a,const HighVariable *b) { - int4 result = a->wholecover.compareTo(b->wholecover); + int4 result = a->getCover().compareTo(b->getCover()); if ( result == 0 ) { Varnode *v1 = a->getInstance( 0 ); Varnode *v2 = b->getInstance( 0 ); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc index b8fdd62c94..0155b2a882 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc @@ -23,6 +23,170 @@ AttributeId ATTRIB_SYMREF = AttributeId("symref",68); ElementId ELEM_HIGH = ElementId("high",82); +/// Compare by offset within the group, then by size. +/// \param op2 is the other piece to compare with \b this +/// \return \b true if \b this should be ordered before the other piece +bool VariableGroup::PieceCompareByOffset::operator()(const VariablePiece *a,const VariablePiece *b) const + +{ + if (a->getOffset() != b->getOffset()) + return (a->getOffset() < b->getOffset()); + return (a->getSize() < b->getSize()); +} + +/// The VariablePiece takes partial ownership of \b this, via refCount. +/// \param piece is the new piece to add +void VariableGroup::addPiece(VariablePiece *piece) + +{ + piece->group = this; + if (!pieceSet.insert(piece).second) + throw LowlevelError("Duplicate VariablePiece"); +} + +void VariableGroup::removePiece(VariablePiece *piece) + +{ + pieceSet.erase(piece); +} + +/// Construct piece given a HighVariable and its position within the whole. +/// If \b this is the first piece in the group, allocate a new VariableGroup object. +/// \param h is the given HighVariable to treat as a piece +/// \param offset is the byte offset of the piece within the whole +/// \param grp is another HighVariable in the whole, or null if \b this is the first piece +VariablePiece::VariablePiece(HighVariable *h,int4 offset,HighVariable *grp) + +{ + high = h; + groupOffset = offset; + size = h->getInstance(0)->getSize(); + if (grp != (HighVariable *)0) + group = grp->piece->getGroup(); + else + group = new VariableGroup(); + group->addPiece(this); +} + +VariablePiece::~VariablePiece(void) + +{ + group->removePiece(this); + if (group->empty()) + delete group; + else + markIntersectionDirty(); +} + +void VariablePiece::markIntersectionDirty(void) const + +{ + set::const_iterator iter; + + for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter) + (*iter)->high->highflags |= (HighVariable::intersectdirty | HighVariable::extendcoverdirty); +} + +void VariablePiece::markExtendCoverDirty(void) const + +{ + if ((high->highflags & HighVariable::intersectdirty)!=0) + return; // intersection list itself is dirty, extended covers will be recomputed anyway + for(int4 i=0;ihigh->highflags |= HighVariable::extendcoverdirty; + } + high->highflags |= HighVariable::extendcoverdirty; +} + +/// Compute list of exactly the HighVariable pieces that intersect with \b this. +void VariablePiece::updateIntersections(void) const + +{ + if ((high->highflags & HighVariable::intersectdirty)==0) return; + set::const_iterator iter; + + int4 endOffset = groupOffset + size; + intersection.clear(); + for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter) { + VariablePiece *otherPiece = *iter; + if (otherPiece == this) continue; + if (endOffset <= otherPiece->groupOffset) continue; + int4 otherEndOffset = otherPiece->groupOffset + otherPiece->size; + if (groupOffset >= otherEndOffset) continue; + intersection.push_back(otherPiece); + } + high->highflags &= ~(uint4)HighVariable::intersectdirty; +} + +/// Union internal covers of all pieces intersecting with \b this. +void VariablePiece::updateCover(void) const + +{ + if ((high->highflags & (HighVariable::coverdirty | HighVariable::extendcoverdirty))==0) return; + high->updateInternalCover(); + cover = high->internalCover; + for(int4 i=0;ihigh; + high->updateInternalCover(); + cover.merge(high->internalCover); + } + high->highflags &= ~(uint4)HighVariable::extendcoverdirty; +} + +/// \param amt is the given amout to add to offset +void VariablePiece::adjustOffset(int4 amt) + +{ + set::iterator iter; + + for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter) { + (*iter)->groupOffset += amt; + } +} + +/// If there are no remaining references to the old VariableGroup it is deleted. +/// \param newGropu is the new VariableGroup to transfer \b this to +void VariablePiece::transferGroup(VariableGroup *newGroup) + +{ + group->removePiece(this); + if (group->empty()) + delete group; + newGroup->addPiece(this); +} + +/// Combine the VariableGroup associated with the given other VariablePiece and the VariableGroup of \b this +/// into one group. Combining in this way requires pieces of the same size and offset to be merged. This +/// method does not do the merging but passes back a list of HighVariable pairs that need to be merged. +/// The first element in the pair will have its VariablePiece in the new group, and the second element +/// will have its VariablePiece freed in preparation for the merge. +/// Offsets are adjusted so that \b this and the given other piece have the same offset; +/// \param op2 is the given other VariablePiece +/// \param mergePairs passes back the collection of HighVariable pairs that must be merged +void VariablePiece::combineOtherGroup(VariablePiece *op2,vector &mergePairs) + +{ + int4 diff = groupOffset - op2->groupOffset; // Add to op2, or subtract from this + if (diff > 0) + op2->adjustOffset(diff); + else if (diff < 0) + adjustOffset(-diff); + set::iterator iter = op2->group->pieceSet.begin(); + set::iterator enditer = op2->group->pieceSet.end(); + while(iter != enditer) { + VariablePiece *piece = *iter; + set::iterator matchiter = group->pieceSet.find(piece); + if (matchiter != group->pieceSet.end()) { + mergePairs.push_back((*matchiter)->high); + mergePairs.push_back(piece->high); + piece->high->piece = (VariablePiece *)0; // Detach HighVariable from its original VariablePiece + delete piece; + } + else + piece->transferGroup(group); + } +} + /// The new instance starts off with no associate Symbol and all properties marked as \e dirty. /// \param vn is the single Varnode member HighVariable::HighVariable(Varnode *vn) @@ -32,6 +196,7 @@ HighVariable::HighVariable(Varnode *vn) highflags = flagsdirty | namerepdirty | typedirty | coverdirty; flags = 0; type = (Datatype *)0; + piece = (VariablePiece *)0; symbol = (Symbol *)0; nameRepresentative = (Varnode *)0; symboloffset = -1; @@ -41,6 +206,13 @@ HighVariable::HighVariable(Varnode *vn) setSymbol(vn); } +HighVariable::~HighVariable(void) + +{ + if (piece != (VariablePiece *)0) + delete piece; +} + /// The given Varnode \b must be a member and \b must have a non-null SymbolEntry void HighVariable::setSymbol(Varnode *vn) const @@ -82,19 +254,41 @@ void HighVariable::setSymbolReference(Symbol *sym,int4 off) highflags &= ~((uint4)symboldirty); } +void HighVariable::transferPiece(HighVariable *tv2) + +{ + piece = tv2->piece; + tv2->piece = (VariablePiece *)0; + piece->setHigh(this); + highflags |= (tv2->highflags & (intersectdirty | extendcoverdirty)); + tv2->highflags &= ~(uint4)(intersectdirty | extendcoverdirty); +} + /// Only update if the cover is marked as \e dirty. /// Merge the covers of all Varnode instances. +void HighVariable::updateInternalCover(void) const + +{ + if ((highflags & coverdirty) != 0) { + internalCover.clear(); + if (inst[0]->hasCover()) { + for(int4 i = 0;i < inst.size();++i) + internalCover.merge(*inst[i]->getCover()); + } + highflags &= ~coverdirty; + } +} + /// This is \b only called by the Merge class which knows when to call it properly. void HighVariable::updateCover(void) const { - if ((highflags & coverdirty)==0) return; // Cover info is upto date - highflags &= ~coverdirty; - - wholecover.clear(); - if (!inst[0]->hasCover()) return; - for(int4 i=0;igetCover()); + if (piece == (VariablePiece *)0) + updateInternalCover(); + else { + piece->updateIntersections(); + piece->updateCover(); + } } /// Only update if flags are marked as \e dirty. @@ -275,6 +469,8 @@ void HighVariable::remove(Varnode *vn) highflags |= (flagsdirty|namerepdirty|coverdirty|typedirty); if (vn->getSymbolEntry() != (SymbolEntry *)0) highflags |= symboldirty; + if (piece != (VariablePiece *)0) + piece->markExtendCoverDirty(); return; } } @@ -304,16 +500,49 @@ void HighVariable::finalizeDatatype(Datatype *tp) highflags |= type_finalized; } +/// If one of the HighVariables is already in a group, the other HighVariable is added to this group. +/// \param off is the relative byte offset of \b this with the other HighVariable +/// \param hi2 is the other HighVariable +void HighVariable::groupWith(int4 off,HighVariable *hi2) + +{ + if (piece == (VariablePiece *)0 && hi2->piece == (VariablePiece *)0) { + hi2->piece = new VariablePiece(hi2,0); + piece = new VariablePiece(this,off,hi2); + hi2->piece->markIntersectionDirty(); + return; + } + if (piece == (VariablePiece *)0) { + if ((hi2->highflags & intersectdirty) == 0) + hi2->piece->markIntersectionDirty(); + highflags |= intersectdirty | extendcoverdirty; + off += hi2->piece->getOffset(); + piece = new VariablePiece(this,off,hi2); + } + else if (hi2->piece == (VariablePiece *)0) { + int4 hi2Off = piece->getOffset() - off; + if (hi2Off < 0) { + piece->adjustOffset(-hi2Off); + hi2Off = 0; + } + if ((highflags & intersectdirty) == 0) + piece->markIntersectionDirty(); + hi2->highflags |= intersectdirty | extendcoverdirty; + hi2->piece = new VariablePiece(hi2,hi2Off,this); + } + else { + throw LowlevelError("Cannot group HighVariables that are already grouped"); + } +} + /// The lists of members are merged and the other HighVariable is deleted. /// \param tv2 is the other HighVariable to merge into \b this /// \param isspeculative is \b true to keep the new members in separate \e merge classes -void HighVariable::merge(HighVariable *tv2,bool isspeculative) +void HighVariable::mergeInternal(HighVariable *tv2,bool isspeculative) { int4 i; - if (tv2 == this) return; - highflags |= (flagsdirty|namerepdirty|typedirty); if (tv2->symbol != (Symbol *)0) { // Check if we inherit a Symbol if ((tv2->highflags & symboldirty)==0) { @@ -344,13 +573,54 @@ void HighVariable::merge(HighVariable *tv2,bool isspeculative) tv2->inst.clear(); if (((highflags&coverdirty)==0)&&((tv2->highflags&coverdirty)==0)) - wholecover.merge(tv2->wholecover); + internalCover.merge(tv2->internalCover); else highflags |= coverdirty; delete tv2; } +/// The HighVariables are merged internally as with mergeInternal. If \b this is part of a VariableGroup, +/// extended covers of the group may be affected. If both HighVariables are part of separate groups, +/// the groups are combined into one, which may induce additional HighVariable pairs within the group to be merged. +/// In all cases, the other HighVariable is deleted. +/// \param tv2 is the other HighVariable to merge into \b this +/// \param isspeculative is \b true to keep the new members in separate \e merge classes +void HighVariable::merge(HighVariable *tv2,bool isspeculative) + +{ + if (tv2 == this) return; + + if (piece == (VariablePiece *)0 && tv2->piece == (VariablePiece *)0) { + mergeInternal(tv2,isspeculative); + return; + } + if (tv2->piece == (VariablePiece *)0) { + // Keep group that this is already in + piece->markExtendCoverDirty(); + mergeInternal(tv2,isspeculative); + return; + } + if (piece == (VariablePiece *)0) { + // Move ownership of the VariablePiece object from the HighVariable that will be freed + transferPiece(tv2); + piece->markExtendCoverDirty(); + mergeInternal(tv2,isspeculative); + return; + } + // Reaching here both HighVariables are part of a group + throw LowlevelError("Merging variables in separate groups not supported"); +// vector mergePairs; +// piece->combineOtherGroup(tv2->piece, mergePairs); +// for(int4 i=0;imergeInternal(high2, isspeculative); +// } +// piece->markIntersectionDirty(); +} + /// All Varnode objects are assigned a HighVariable, including those that don't get names like /// indirect variables, constants, and annotations. Determine if \b this, as inherited from its /// member Varnodes, can have a name. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh index 21e5479190..f8fb0e4335 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh @@ -29,6 +29,63 @@ extern AttributeId ATTRIB_SYMREF; ///< Marshaling attribute "symref" extern ElementId ELEM_HIGH; ///< Marshaling element \ +class HighVariable; ///< Forward declaration +class VariablePiece; ///< Forward declaration + +/// \brief A collection of HighVariable objects that overlap +/// +/// A HighVariable represents a variable or partial variable that is manipulated as a unit by the (de)compiler. +/// A formal Symbol may be manipulated using multiple HighVariables that in principal can overlap. For a set of +/// HighVariable objects that mutually overlap, a VariableGroup is a central access point for information about +/// the intersections. The information is used in particular to extend HighVariable Cover objects to take into +/// account the intersections. +class VariableGroup { + friend class VariablePiece; + + /// \brief Compare two VariablePiece pointers by offset then by size + struct PieceCompareByOffset { + bool operator()(const VariablePiece *a,const VariablePiece *b) const; ///< Comparison operator + }; + + set pieceSet; ///< The set of VariablePieces making up \b this group +public: + bool empty(void) const { return pieceSet.empty(); } ///< Return \b true if \b this group has no pieces + void addPiece(VariablePiece *piece); ///< Add a new piece to \b this group + void removePiece(VariablePiece *piece); ///< Remove a piece from \b this group +}; + +/// \brief Information about how a HighVariable fits into a larger group or Symbol +/// +/// This is an extension to a HighVariable object that is assigned if the HighVariable is part of a +/// group of mutually overlapping HighVariables. It describes the overlaps and how they affect the HighVariable Cover. +class VariablePiece { + friend class VariableGroup; + VariableGroup *group; ///< Group to which \b this piece belongs + HighVariable *high; ///< HighVariable owning \b this piece + int4 groupOffset; ///< Byte offset of \b this piece within the group + int4 size; ///< Number of bytes in \b this piece + mutable vector intersection; ///< List of VariablePieces \b this piece intersects with + mutable Cover cover; ///< Extended cover for the piece, taking into account intersections +public: + VariablePiece(HighVariable *h,int4 offset,HighVariable *grp=(HighVariable *)0); + ~VariablePiece(void); ///< Destructor + HighVariable *getHigh(void) const { return high; } ///< Get the HighVariable associate with \b this piece + VariableGroup *getGroup(void) const { return group; } ///< Get the central group + int4 getOffset(void) const { return groupOffset; } ///< Get the offset of \b this within its group + int4 getSize(void) const { return size; } ///< Return the number of bytes in \b this piece. + const Cover &getCover(void) const { return cover; } ///< Get the cover associated with \b this piece. + int4 numIntersection(void) const { return intersection.size(); } ///< Get number of pieces \b this intersects with + const VariablePiece *getIntersection(int4 i) const { return intersection[i]; } ///< Get i-th piece \b this intersects with + void markIntersectionDirty(void) const; ///< Mark all pieces as needing intersection recalculation + void markExtendCoverDirty(void) const; ///< Mark all intersecting pieces as having a dirty extended cover + void updateIntersections(void) const; ///< Calculate intersections with other pieces in the group + void updateCover(void) const; ///< Calculate extended cover based on intersections + void adjustOffset(int4 amt); ///< Adjust every piece's offset by the given amount + void transferGroup(VariableGroup *newGroup); ///< Transfer \b this piece to another VariableGroup + void setHigh(HighVariable *newHigh) { high = newHigh; } ///< Move ownership of \b this to another HighVariable + void combineOtherGroup(VariablePiece *op2,vector &mergePairs); ///< Combine two VariableGroups +}; + /// \brief A high-level variable modeled as a list of low-level variables, each written once /// /// In the Static Single Assignment (SSA) representation of a function's data-flow, the Varnode @@ -57,23 +114,28 @@ public: copy_in1 = 0x20, ///< There exists at least 1 COPY into \b this HighVariable from other HighVariables copy_in2 = 0x40, ///< There exists at least 2 COPYs into \b this HighVariable from other HighVariables type_finalized = 0x80, ///< Set if a final data-type is locked in and dirtying is disabled - unmerged = 0x100 ///< Set if part of a multi-entry Symbol but did not get merged with other SymbolEntrys + unmerged = 0x100, ///< Set if part of a multi-entry Symbol but did not get merged with other SymbolEntrys + intersectdirty = 0x200, ///< Set if intersections with other HighVariables needs to be recomputed + extendcoverdirty = 0x400 ///< Set if extended cover needs to be recomputed }; private: friend class Varnode; friend class Merge; + friend class VariablePiece; vector inst; ///< The member Varnode objects making up \b this HighVariable int4 numMergeClasses; ///< Number of different speculative merge classes in \b this mutable uint4 highflags; ///< Dirtiness flags mutable uint4 flags; ///< Boolean properties inherited from Varnode members mutable Datatype *type; ///< The data-type for \b this mutable Varnode *nameRepresentative; ///< The storage location used to generate a Symbol name - mutable Cover wholecover; ///< The ranges of code addresses covered by this HighVariable + mutable Cover internalCover; ///< The ranges of code addresses covered by this HighVariable + mutable VariablePiece *piece; ///< Additional info about intersections with other pieces (if non-null) mutable Symbol *symbol; ///< The Symbol \b this HighVariable is tied to mutable int4 symboloffset; ///< -1=perfect symbol match >=0, offset int4 instanceIndex(const Varnode *vn) const; ///< Find the index of a specific Varnode member void updateFlags(void) const; ///< (Re)derive boolean properties of \b this from the member Varnodes - void updateCover(void) const; ///< (Re)derive the cover of \b this from the member Varnodes + void updateInternalCover(void) const; ///< (Re)derive the internal cover of \b this from the member Varnodes + void updateCover(void) const; ///< (Re)derive the external cover of \b this, as a union of internal covers void updateType(void) const; ///< (Re)derive the data-type for \b this from the member Varnodes void updateSymbol(void) const; ///< (Re)derive the Symbol and offset for \b this from member Varnodes void setCopyIn1(void) const { highflags |= copy_in1; } ///< Mark the existence of one COPY into \b this @@ -82,28 +144,34 @@ private: bool hasCopyIn1(void) const { return ((highflags©_in1)!=0); } ///< Is there at least one COPY into \b this bool hasCopyIn2(void) const { return ((highflags©_in2)!=0); } ///< Is there at least two COPYs into \b this void remove(Varnode *vn); ///< Remove a member Varnode from \b this - void merge(HighVariable *tv2,bool isspeculative); ///< Merge another HighVariable into \b this + void mergeInternal(HighVariable *tv2,bool isspeculative); ///< Merge another HighVariable into \b this + void merge(HighVariable *tv2,bool isspeculative); ///< Merge with another HighVariable taking into account groups void setSymbol(Varnode *vn) const; ///< Update Symbol information for \b this from the given member Varnode void setSymbolReference(Symbol *sym,int4 off); ///< Attach a reference to a Symbol to \b this + void transferPiece(HighVariable *tv2); ///< Transfer ownership of another's VariablePiece to \b this void flagsDirty(void) const { highflags |= flagsdirty | namerepdirty; } ///< Mark the boolean properties as \e dirty - void coverDirty(void) const { highflags |= coverdirty; } ///< Mark the cover as \e dirty + void coverDirty(void) const; ///< Mark the cover as \e dirty void typeDirty(void) const { highflags |= typedirty; } ///< Mark the data-type as \e dirty void symbolDirty(void) const { highflags |= symboldirty; } ///< Mark the symbol as \e dirty void setUnmerged(void) const { highflags |= unmerged; } ///< Mark \b this as having merge problems + bool isCoverDirty(void) const; ///< Is the cover returned by getCover() up-to-date public: HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode + ~HighVariable(void); ///< Destructor Datatype *getType(void) const { updateType(); return type; } ///< Get the data-type + const Cover &getCover(void) const; ///< Get cover data for \b this variable Symbol *getSymbol(void) const { updateSymbol(); return symbol; } ///< Get the Symbol associated with \b this or null SymbolEntry *getSymbolEntry(void) const; /// Get the SymbolEntry mapping to \b this or null int4 getSymbolOffset(void) const { return symboloffset; } ///< Get the Symbol offset associated with \b this int4 numInstances(void) const { return inst.size(); } ///< Get the number of member Varnodes \b this has Varnode *getInstance(int4 i) const { return inst[i]; } ///< Get the i-th member Varnode void finalizeDatatype(Datatype *tp); ///< Set a final datatype for \b this variable + void groupWith(int4 off,HighVariable *hi2); ///< Put \b this and another HighVariable in the same intersection group /// \brief Print details of the cover for \b this (for debug purposes) /// /// \param s is the output stream - void printCover(ostream &s) const { if ((highflags&HighVariable::coverdirty)==0) wholecover.print(s); else s << "Cover dirty"; } + void printCover(ostream &s) const { if ((highflags&HighVariable::coverdirty)==0) internalCover.print(s); else s << "Cover dirty"; } void printInfo(ostream &s) const; ///< Print information about \b this HighVariable to stream bool hasName(void) const; ///< Check if \b this HighVariable can be named @@ -147,4 +215,33 @@ public: static int4 markExpression(Varnode *vn,vector &highList); ///< Mark and collect variables in expression }; +/// The internal cover is marked as dirty. If \b this is a piece of a VariableGroup, it and all the other +/// HighVariables it intersects with are marked as having a dirty extended cover. +inline void HighVariable::coverDirty(void) const + +{ + highflags |= coverdirty; + if (piece != (VariablePiece *)0) + piece->markExtendCoverDirty(); +} + +/// The cover could either by the internal one or the extended one if \b this is part of a Variable Group. +/// \return \b true if the cover needs to be recomputed. +inline bool HighVariable::isCoverDirty(void) const + +{ + return ((highflags & (coverdirty | extendcoverdirty)) != 0); +} + +/// The returns the internal cover unless \b this is part of a VariableGroup, in which case the +/// extended cover is returned. +/// \return the cover associated with \b this variable +inline const Cover &HighVariable::getCover(void) const + +{ + if (piece == (VariablePiece *)0) + return internalCover; + return piece->getCover(); +} + #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc index 36203c8d62..7d9acb78fb 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc @@ -878,6 +878,135 @@ bool Varnode::copyShadow(const Varnode *op2) const return false; } +/// \brief Try to find a SUBPIECE operation producing the value in \b this from the given \b whole Varnode +/// +/// The amount of truncation producing \b this must be known apriori. Allow for COPY and MULTIEQUAL operations +/// in the flow path from \b whole to \b this. This method will search recursively through branches +/// of MULTIEQUAL up to a maximum depth. +/// \param leastByte is the number of least significant bytes being truncated from \b whole to get \b this +/// \param whole is the given whole Varnode +/// \param recurse is the current depth of recursion +/// \return \b true if \b this and \b whole have the prescribed SUBPIECE relationship +bool Varnode::findSubpieceShadow(int4 leastByte,const Varnode *whole,int4 recurse) const + +{ + const Varnode *vn = this; + while( vn->isWritten() && vn->getDef()->code() == CPUI_COPY) + vn = vn->getDef()->getIn(0); + if (!vn->isWritten()) { + if (vn->isConstant()) { + while( whole->isWritten() && whole->getDef()->code() == CPUI_COPY) + whole = whole->getDef()->getIn(0); + if (!whole->isConstant()) return false; + uintb off = whole->getOffset() >> leastByte*8; + off &= calc_mask(vn->getSize()); + return (off == vn->getOffset()); + } + return false; + } + OpCode opc = vn->getDef()->code(); + if (opc == CPUI_SUBPIECE) { + const Varnode *tmpvn = vn->getDef()->getIn(0); + int4 off = (int4)vn->getDef()->getIn(1)->getOffset(); + if (off != leastByte || tmpvn->getSize() != whole->getSize()) + return false; + if (tmpvn == whole) return true; + while(tmpvn->isWritten() && tmpvn->getDef()->code() == CPUI_COPY) { + tmpvn = tmpvn->getDef()->getIn(0); + if (tmpvn == whole) return true; + } + } + else if (opc == CPUI_MULTIEQUAL) { + recurse += 1; + if (recurse > 1) return false; // Truncate the recursion at maximum depth + while( whole->isWritten() && whole->getDef()->code() == CPUI_COPY) + whole = whole->getDef()->getIn(0); + if (!whole->isWritten()) return false; + const PcodeOp *bigOp = whole->getDef(); + if (bigOp->code() != CPUI_MULTIEQUAL) return false; + const PcodeOp *smallOp = vn->getDef(); + if (bigOp->getParent() != smallOp->getParent()) return false; + // Recurse search through all branches of the two MULTIEQUALs + for(int4 i=0;inumInput();++i) { + if (!smallOp->getIn(i)->findSubpieceShadow(leastByte, bigOp->getIn(i), recurse)) + return false; + } + return true; // All branches were copy shadows + } + return false; +} + +bool Varnode::findPieceShadow(int4 leastByte,const Varnode *piece) const + +{ + const Varnode *vn = this; + while( vn->isWritten() && vn->getDef()->code() == CPUI_COPY) + vn = vn->getDef()->getIn(0); + if (!vn->isWritten()) return false; + OpCode opc = vn->getDef()->code(); + if (opc == CPUI_PIECE) { + const Varnode *tmpvn = vn->getDef()->getIn(1); // Least significant part + if (leastByte >= tmpvn->getSize()) { + leastByte -= tmpvn->getSize(); + tmpvn = vn->getDef()->getIn(0); + } + else { + if (piece->getSize() + leastByte > tmpvn->getSize()) return false; + } + if (leastByte == 0 && tmpvn->getSize() == piece->getSize()) { + if (tmpvn == piece) return true; + while(tmpvn->isWritten() && tmpvn->getDef()->code() == CPUI_COPY) { + tmpvn = tmpvn->getDef()->getIn(0); + if (tmpvn == piece) return true; + } + return false; + } + // CPUI_PIECE input is too big, recursively search for another CPUI_PIECE + return tmpvn->findPieceShadow(leastByte, piece); + } + return false; +} + +/// For \b this and another Varnode, establish that either: +/// - bigger = CONCAT(smaller,..) or +/// - smaller = SUBPIECE(bigger) +/// +/// Check through COPY chains and verify that the form of the CONCAT or SUBPIECE matches +/// a given relative offset between the Varnodes. +/// \param op2 is the Varnode to compare to \b this +/// \param relOff is the putative relative byte offset of \b this to \b op2 +/// \return \b true if one Varnode is contained, as a value, in the other +bool Varnode::partialCopyShadow(const Varnode *op2,int4 relOff) const + +{ + const Varnode *vn; + + if (size < op2->size) { + vn = this; + } + else if (size > op2->size) { + vn = op2; + op2 = this; + relOff = -relOff; + } + else + return false; + if (relOff < 0) + return false; // Not proper containment + if (relOff + vn->getSize() > op2->getSize()) + return false; // Not proper containment + + bool bigEndian = getSpace()->isBigEndian(); + int4 leastByte = bigEndian ? (op2->getSize() - vn->getSize()) - relOff : relOff; + if (vn->findSubpieceShadow(leastByte, op2, 0)) + return true; + + if (op2->findPieceShadow(leastByte, vn)) + return true; + + return false; +} + /// Compare term order of two Varnodes. Used in Term Rewriting strategies to order operands of commutative ops /// \param op is the Varnode to order against \b this /// \return -1 if \b this comes before \b op, 1 if op before this, or 0 @@ -1487,6 +1616,44 @@ VarnodeLocSet::const_iterator VarnodeBank::endLoc(int4 s,const Address &addr, return iter; } +/// \brief Given start, return maximal range of overlapping Varnodes +/// +/// Advance the iterator until no Varnodes after the iterator intersect any Varnodes +/// from the initial Varnode through the current iterator. The range is returned as pairs +/// of iterators to subranges. One subrange for each set of Varnodes with the same size and starting address. +/// A final iterator to the next Varnode after the overlapping set is also passed back. +/// \param iter is an iterator to the given start Varnode +/// \param bounds holds the array of iterator pairs passed back +/// \return the union of Varnode flags across the range +uint4 VarnodeBank::overlapLoc(VarnodeLocSet::const_iterator iter,vector &bounds) const + +{ + Varnode *vn = *iter; + AddrSpace *spc = vn->getSpace(); + uintb off = vn->getOffset(); + uintb maxoff = off + (vn->getSize() - 1); + uint4 flags = vn->getFlags(); + bounds.push_back(iter); + iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written); + bounds.push_back(iter); + while(iter != loc_tree.end()) { + vn = *iter; + if (vn->getSpace() != spc || vn->getOffset() > maxoff) + break; + if (vn->isFree()) { + iter = endLoc(vn->getSize(),vn->getAddr(),0); + continue; + } + maxoff = vn->getOffset() + (vn->getSize() - 1); + flags |= vn->getFlags(); + bounds.push_back(iter); + iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written); + bounds.push_back(iter); + } + bounds.push_back(iter); + return flags; +} + /// \brief Beginning of varnodes with set definition property /// /// Get an iterator to Varnodes in definition order restricted with the diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh index 30a49cb754..9f25983017 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh @@ -331,6 +331,9 @@ public: Datatype *getLocalType(bool &blockup) const; ///< Calculate type of Varnode based on local information bool isBooleanValue(bool useAnnotation) const; ///< Does \b this Varnode hold a formal boolean value bool copyShadow(const Varnode *op2) const; ///< Are \b this and \b op2 copied from the same source? + bool findSubpieceShadow(int4 leastByte,const Varnode *whole,int4 recurse) const; + bool findPieceShadow(int4 leastByte,const Varnode *piece) const; + bool partialCopyShadow(const Varnode *op2,int4 relOff) const; ///< Is one of \b this or \b op2 a partial copy of the other? void encode(Encoder &encoder) const; ///< Encode a description of \b this to a stream static bool comparePointers(const Varnode *a,const Varnode *b) { return (*a < *b); } ///< Compare Varnodes as pointers static void printRaw(ostream &s,const Varnode *vn); ///< Print raw info about a Varnode to stream @@ -387,6 +390,7 @@ public: VarnodeLocSet::const_iterator endLoc(int4 s,const Address &addr,uint4 fl) const; VarnodeLocSet::const_iterator beginLoc(int4 s,const Address &addr,const Address &pc,uintm uniq) const; VarnodeLocSet::const_iterator endLoc(int4 s,const Address &addr,const Address &pc,uintm uniq) const; + uint4 overlapLoc(VarnodeLocSet::const_iterator iter,vector &bounds) const; VarnodeDefSet::const_iterator beginDef(void) const { return def_tree.begin(); } ///< Beginning of Varnodes sorted by definition VarnodeDefSet::const_iterator endDef(void) const { return def_tree.end(); } ///< End of Varnodes sorted by definition VarnodeDefSet::const_iterator beginDef(uint4 fl) const; diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/partialmerge.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/partialmerge.xml new file mode 100644 index 0000000000..c52f3ac7a4 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/partialmerge.xml @@ -0,0 +1,64 @@ + + + + + 8b1dc3ffffff48893d +bcffffff83c30a89d8c300008b05aeff +ffff01f048893da5ffffff83c00ac3 + + + 4883ec104889e766e893 +008b1c244889e766e8890089d8c30000 +8b1d6affffff488b3d63ffffff66e873 +0089d8c34883ec10488b0551ffffff48 +8904244889e766e85a008b5c24044889 +e766e84f0089d84883c410c348893d2d +ffffff83fe0a7f0c66e838008b051eff +ffffeb0a66e8b8fd8b0512ffffff83c0 +07c3 + + + + + + + + +a_simple = glob1\.a; +return a_simple \+ 10; +return glob1\.a \+ 10; +param_2 = glob1\.a \+ param_2; +return param_2 \+ 10; +a_call = glob1\.a; +return a_call; +return a_call; +b_between = hilo_stack\.b; +return b_between; +return glob1\.a \+ 7; +