From 2591c17f220c3d0f1b9cdfe28b77d29979ed7276 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Tue, 14 Mar 2023 13:25:39 -0400 Subject: [PATCH] GP-2627 TypePartialStruct --- .../Decompiler/certification.manifest | 1 + .../src/decompile/cpp/coreaction.cc | 20 +- .../Decompiler/src/decompile/cpp/funcdata.hh | 1 - .../src/decompile/cpp/funcdata_varnode.cc | 22 +- .../Decompiler/src/decompile/cpp/merge.cc | 337 +++------------- .../Decompiler/src/decompile/cpp/merge.hh | 24 +- .../src/decompile/cpp/ruleaction.cc | 38 ++ .../src/decompile/cpp/ruleaction.hh | 10 + .../Decompiler/src/decompile/cpp/type.cc | 96 ++++- .../Decompiler/src/decompile/cpp/type.hh | 55 ++- .../Decompiler/src/decompile/cpp/variable.cc | 361 +++++++++++++++--- .../Decompiler/src/decompile/cpp/variable.hh | 53 ++- .../Decompiler/src/decompile/cpp/varmap.cc | 4 + .../Decompiler/src/decompile/cpp/varnode.cc | 8 +- .../src/decompile/datatests/piecestruct.xml | 39 ++ 15 files changed, 636 insertions(+), 433 deletions(-) create mode 100644 Ghidra/Features/Decompiler/src/decompile/datatests/piecestruct.xml diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index ec9ff53bb4..2e905c5fbe 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -44,6 +44,7 @@ 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/piecestruct.xml||GHIDRA||||END| src/decompile/datatests/pointercmp.xml||GHIDRA||||END| src/decompile/datatests/pointerrel.xml||GHIDRA||||END| src/decompile/datatests/pointersub.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 1cbb3c039a..225573699a 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -4732,16 +4732,19 @@ void ActionInferTypes::buildLocaltypes(Funcdata &data) Datatype *ct; Varnode *vn; VarnodeLocSet::const_iterator iter; + TypeFactory *typegrp = data.getArch()->types; for(iter=data.beginLoc();iter!=data.endLoc();++iter) { vn = *iter; if (vn->isAnnotation()) continue; if ((!vn->isWritten())&&(vn->hasNoDescend())) continue; bool needsBlock = false; - if (vn->getSymbolEntry() != (SymbolEntry *)0) { - ct = data.checkSymbolType(vn); - if (ct == (Datatype *)0) - ct = vn->getLocalType(needsBlock); + SymbolEntry *entry = vn->getSymbolEntry(); + if (entry != (SymbolEntry *)0 && !vn->isTypeLock() && entry->getSymbol()->isTypeLocked()) { + int4 curOff = (vn->getAddr().getOffset() - entry->getAddr().getOffset()) + entry->getOffset(); + ct = typegrp->getExactPiece(entry->getSymbol()->getType(), curOff, vn->getSize()); + if (ct == (Datatype *)0 || ct->getMetatype() == TYPE_UNKNOWN) // If we can't resolve, or resolve to UNKNOWN + ct = vn->getLocalType(needsBlock); // Let data-type float, even though parent symbol is type-locked } else ct = vn->getLocalType(needsBlock); @@ -4956,13 +4959,9 @@ void ActionInferTypes::propagateRef(Funcdata &data,Varnode *vn,const Address &ad if ((cursize!=lastsize)||(curoff!=lastoff)) { lastoff = curoff; lastsize = cursize; - Datatype *cur = ct; - do { - lastct = cur; - cur = cur->getSubType(curoff,&curoff); - } while(cur != (Datatype *)0); + lastct = typegrp->getExactPiece(ct, curoff, cursize); } - if (lastct->getSize() != cursize) continue; + if (lastct == (Datatype *)0) continue; // Try to propagate the reference type into a varnode that is pointed to by that reference if (0>lastct->typeOrder(*curvn->getTempType())) { @@ -5314,6 +5313,7 @@ void ActionDatabase::universalAction(Architecture *conf) actprop->addRule( new RuleAndDistribute("analysis") ); actprop->addRule( new RuleAndCommute("analysis") ); actprop->addRule( new RuleAndPiece("analysis") ); + actprop->addRule( new RuleAndZext("analysis") ); actprop->addRule( new RuleAndCompare("analysis") ); actprop->addRule( new RuleDoubleSub("analysis") ); actprop->addRule( new RuleDoubleShift("analysis") ); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 3397f986a8..fe3841beda 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -390,7 +390,6 @@ public: bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial,uint4 mainFlags) const; bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial,int4 offset,uint4 mainFlags) const; bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool updateDatatypes,bool unmappedAliasCheck); - Datatype *checkSymbolType(Varnode *vn); ///< Check for any delayed symbol data-type information on the given Varnode void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset); bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image bool replaceVolatile(Varnode *vn); ///< Replace accesses of the given Varnode with \e volatile operations diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc index 0a641091b4..e5b85187db 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc @@ -869,24 +869,6 @@ bool Funcdata::syncVarnodesWithSymbols(const ScopeLocal *lm,bool updateDatatypes return updateoccurred; } -/// If the Varnode is a partial of a Symbol with a \e union data-type component, we assign -/// a partial union data-type (TypePartialUnion) to the Varnode, so that facing resolutions -/// can be provided. -/// \param vn is the given Varnode -/// \return the partial data-type or null -Datatype *Funcdata::checkSymbolType(Varnode *vn) - -{ - if (vn->isTypeLock()) return vn->getType(); - SymbolEntry *entry = vn->getSymbolEntry(); - Symbol *sym = entry->getSymbol(); - Datatype *curType = sym->getType(); - if (curType->getSize() == vn->getSize()) - return (Datatype *)0; - int4 curOff = (vn->getAddr().getOffset() - entry->getAddr().getOffset()) + entry->getOffset(); - return glb->types->getExactPiece(curType, curOff, vn->getSize()); -} - /// A Varnode overlaps the given SymbolEntry. Make sure the Varnode is part of the variable /// underlying the Symbol. If not, remap things so that the Varnode maps to a distinct Symbol. /// In either case, attach the appropriate Symbol to the Varnode @@ -1037,9 +1019,11 @@ void Funcdata::linkProtoPartial(Varnode *vn) Varnode *rootVn = PieceNode::findRoot(vn); if (rootVn == vn) return; - Varnode *nameRep = rootVn->getHigh()->getNameRepresentative(); + HighVariable *rootHigh = rootVn->getHigh(); + Varnode *nameRep = rootHigh->getNameRepresentative(); Symbol *sym = linkSymbol(nameRep); if (sym == (Symbol *)0) return; + rootHigh->establishGroupSymbolOffset(); SymbolEntry *entry = sym->getFirstWholeMap(); vn->setSymbolEntry(entry); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc index 096fbbf537..1fa81812de 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc @@ -114,6 +114,15 @@ bool Merge::mergeTestRequired(HighVariable *high_out,HighVariable *high_in) if (high_in->isAddrTied()) return false; if (high_in->isPersist()) return false; } + if (high_in->piece != (VariablePiece *)0 && high_out->piece != (VariablePiece *)0) { + VariableGroup *groupIn = high_in->piece->getGroup(); + VariableGroup *groupOut = high_out->piece->getGroup(); + if (groupIn == groupOut) + return false; + // At least one of the pieces must represent its whole group + if (high_in->piece->getSize() != groupIn->getSize() && high_out->piece->getSize() != groupOut->getSize()) + return false; + } Symbol *symbolIn = high_in->getSymbol(); Symbol *symbolOut = high_out->getSymbol(); @@ -123,20 +132,6 @@ bool Merge::mergeTestRequired(HighVariable *high_out,HighVariable *high_in) if (high_in->getSymbolOffset() != high_out->getSymbolOffset()) return false; // Map to different parts of same symbol } - - if (high_out->piece != (VariablePiece *)0 || high_in->piece != (VariablePiece *)0) { - // 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; - if (symbolIn != symbolOut) { // If we know symbols are involved, and not both the same symbol - // Treat piece as if it were a separate symbol - if (symbolIn != (Symbol *)0 && high_out->piece != (VariablePiece *)0) - return false; // effectively different symbols - if (symbolOut != (Symbol *)0 && high_in->piece != (VariablePiece *)0) - return false; // effectively different symbols - } - } - return true; } @@ -178,6 +173,10 @@ bool Merge::mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in) if (symbol != (Symbol *)0) if (symbol->isIsolated()) return false; + + // Currently don't allow speculative merging of variables that are in separate overlapping collections + if (high_out->piece != (VariablePiece *)0 && high_in->piece != (VariablePiece *)0) + return false; return true; } @@ -249,7 +248,7 @@ void Merge::mergeLinear(vector &highvec) if (highvec.size() <= 1) return; for(initer=highvec.begin();initer!=highvec.end();++initer) - updateHigh(*initer); + testCache.updateHigh(*initer); sort(highvec.begin(),highvec.end(),compareHighByBlock); for(initer=highvec.begin();initer!=highvec.end();++initer) { high = *initer; @@ -927,11 +926,11 @@ void Merge::mergeMultiEntry(void) } if (mergeList.empty()) continue; HighVariable *high = mergeList[0]->getHigh(); - updateHigh(high); + testCache.updateHigh(high); for(int4 i=0;igetHigh(); if (newHigh == high) continue; // Varnodes already merged - updateHigh(newHigh); + testCache.updateHigh(newHigh); if (!mergeTestRequired(high, newHigh)) { symbol->setMergeProblems(); newHigh->setUnmerged(); @@ -1005,7 +1004,7 @@ void Merge::mergeAdjacent(void) high_in = vn2->getHigh(); if (!mergeTestAdjacent(high_out,high_in)) continue; - if (!intersection(high_in,high_out)) // If no interval intersection + if (!testCache.intersection(high_in,high_out)) // If no interval intersection merge(high_out,high_in,true); } } @@ -1232,7 +1231,7 @@ void Merge::buildDominantCopy(HighVariable *high,vector ©,int4 po } } if (count > 0 && domCopyIsNew) { - high->merge(domVn->getHigh(),true); + high->merge(domVn->getHigh(),(HighIntersectTest *)0,true); } } @@ -1437,8 +1436,9 @@ void Merge::markInternalCopies(void) vector multiCopy; list::const_iterator iter; PcodeOp *op; - HighVariable *h1,*h2,*h3; + HighVariable *h1; Varnode *v1,*v2,*v3; + VariablePiece *p1,*p2,*p3; int4 val; for(iter=data.beginOpAlive();iter!=data.endOpAlive();++iter) { @@ -1465,41 +1465,42 @@ void Merge::markInternalCopies(void) } break; case CPUI_PIECE: // Check if output is built out of pieces of itself - h1 = op->getOut()->getHigh(); - h2 = op->getIn(0)->getHigh(); - h3 = op->getIn(1)->getHigh(); - if (!h2->isPartialOrAddrTied()) break; - if (!h3->isPartialOrAddrTied()) break; - v2 = h2->getPartialOrAddrTied(); - v3 = h3->getPartialOrAddrTied(); - if (v2->isAddrTied()) { - if (!h1->isAddrTied()) break; - v1 = h1->getTiedVarnode(); + v1 = op->getOut(); + v2 = op->getIn(0); + v3 = op->getIn(1); + p1 = v1->getHigh()->piece; + p2 = v2->getHigh()->piece; + p3 = v3->getHigh()->piece; + if (p1 == (VariablePiece *)0) break; + if (p2 == (VariablePiece *)0) break; + if (p3 == (VariablePiece *)0) break; + if (p1->getGroup() != p2->getGroup()) break; + if (p1->getGroup() != p3->getGroup()) break; + if (v1->getSpace()->isBigEndian()) { + if (p2->getOffset() != p1->getOffset()) break; + if (p3->getOffset() != p1->getOffset() + v2->getSize()) break; } else { - if (op->getIn(0) != v2) break; - if (op->getIn(1) != v3) break; - v1 = op->getOut(); + if (p3->getOffset() != p1->getOffset()) break; + if (p2->getOffset() != p1->getOffset() + v3->getSize()) break; } - if (v3->overlapJoin(*v1) != 0) break; - if (v2->overlapJoin(*v1) != v3->getSize()) break; data.opMarkNonPrinting(op); break; case CPUI_SUBPIECE: - h1 = op->getOut()->getHigh(); - h2 = op->getIn(0)->getHigh(); - if (!h1->isPartialOrAddrTied()) break; - v1 = h1->getPartialOrAddrTied(); - if (v1->isAddrTied()) { - if (!h2->isAddrTied()) break; - v2 = h2->getTiedVarnode(); + v1 = op->getOut(); + v2 = op->getIn(0); + p1 = v1->getHigh()->piece; + p2 = v2->getHigh()->piece; + if (p1 == (VariablePiece *)0) break; + if (p2 == (VariablePiece *)0) break; + if (p1->getGroup() != p2->getGroup()) break; + val = op->getIn(1)->getOffset(); + if (v1->getSpace()->isBigEndian()) { + if (p2->getOffset() + (v2->getSize() - v1->getSize() - val) != p1->getOffset()) break; } else { - if (!h1->sameGroup(h2)) break; - v2 = op->getIn(0); + if (p2->getOffset() + val != p1->getOffset()) break; } - val = op->getIn(1)->getOffset(); - if (v1->overlapJoin(*v2) != val) break; data.opMarkNonPrinting(op); break; default: @@ -1528,64 +1529,6 @@ void Merge::registerProtoPartialRoot(Varnode *vn) protoPartial.push_back(vn->getDef()); } -/// \brief Translate any intersection tests for \e high2 into tests for \e high1 -/// -/// 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) - -{ - vector yesinter; // Highs that high2 intersects - vector nointer; // Highs that high2 does not intersect - map::iterator iterfirst = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)0) ); - map::iterator iterlast = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)~((uintp)0)) ); - map::iterator iter; - - for(iter=iterfirst;iter!=iterlast;++iter) { - 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 - else { - nointer.push_back(b); - b->setMark(); // Mark that high2 did not intersect - } - } - // Do a purge of all high2's tests - if (iterfirst != iterlast) { // Delete all the high2 tests - --iterlast; // Move back 1 to prevent deleting under the iterator - for(iter=iterfirst;iter!=iterlast;++iter) - 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); - } - - iter = highedgemap.lower_bound( HighEdge(high1,(HighVariable *)0) ); - while((iter!=highedgemap.end())&&((*iter).first.a == high1)) { - if (!(*iter).second) { // If test is intersection==false - if (!(*iter).first.b->isMark()) // and there was no test with high2 - highedgemap.erase( iter++ ); // Delete the test - else - ++iter; - } - else // Keep any intersection==true tests - ++iter; - } - vector::iterator titer; - for(titer=nointer.begin();titer!=nointer.end();++titer) - (*titer)->clearMark(); - - // 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 @@ -1600,197 +1543,25 @@ bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative) { if (high1 == high2) return true; // Already merged - if (intersection(high1,high2)) return false; + if (testCache.intersection(high1,high2)) return false; - moveIntersectTests(high1, high2); - high1->merge(high2,isspeculative); // Do the actual merge + high1->merge(high2,&testCache,isspeculative); // Do the actual merge high1->updateCover(); // Update cover now so that updateHigh won't purge cached tests return true; } -/// As manipulations are made, Cover information gets out of date. A \e dirty flag is used to -/// indicate a particular HighVariable Cover is out-of-date. This routine checks the \e dirty -/// flag and updates the Cover information if it is set. -/// \param a is the HighVariable to update -/// \return \b true if the HighVariable was not originally dirty -bool Merge::updateHigh(HighVariable *a) - -{ - if (!a->isCoverDirty()) return true; - - a->updateCover(); - purgeHigh(a); - return false; -} - -/// All tests for pairs where either the first or second HighVariable matches the given one -/// are removed. -/// \param high is the given HighVariable to purge -void Merge::purgeHigh(HighVariable *high) - -{ - map::iterator iterfirst = highedgemap.lower_bound( HighEdge(high,(HighVariable *)0) ); - map::iterator iterlast = highedgemap.lower_bound( HighEdge(high,(HighVariable *)~((uintp)0)) ); - - if (iterfirst == iterlast) return; - --iterlast; // Move back 1 to prevent deleting under the iterator - map::iterator iter; - for(iter=iterfirst;iter!=iterlast;++iter) - 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); -} - /// \brief Clear the any cached data from the last merge process /// /// Free up resources used by cached intersection tests etc. void Merge::clear(void) { - highedgemap.clear(); + testCache.clear(); copyTrims.clear(); protoPartial.clear(); } -/// \brief Test the intersection of two HighVariables and cache the result -/// -/// If the Covers of the two variables intersect, this routine returns \b true. To avoid -/// expensive computation on the Cover objects themselves, the test result associated with -/// the pair of HighVariables is cached. -/// \param a is the first HighVariable -/// \param b is the second HighVariable -/// \return \b true if the variables intersect -bool Merge::intersection(HighVariable *a,HighVariable *b) - -{ - if (a==b) return false; - bool ares = updateHigh(a); - bool bres = updateHigh(b); - if (ares && bres) { // If neither high was dirty - map::iterator iter = highedgemap.find( HighEdge(a,b) ); - if (iter != highedgemap.end()) // If previous test is present - return (*iter).second; // Use it - } - - bool res = false; - int4 blk; - vector blockisect; - 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. -/// \param a is the first HighVariable -/// \param b is the second HighVariable -/// \param blk is the index of the BlockBasic on which to test intersection -/// \return \b true if an intersection occurs in the specified block -bool Merge::blockIntersection(HighVariable *a,HighVariable *b,int4 blk) - -{ - vector blist; - - 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; - } - } - 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; -} - /// \brief Inflate the Cover of a given Varnode with a HighVariable /// /// An expression involving a HighVariable can be propagated to all the read sites of the @@ -1802,8 +1573,8 @@ bool Merge::blockIntersection(HighVariable *a,HighVariable *b,int4 blk) void Merge::inflate(Varnode *a,HighVariable *high) { - updateHigh(a->getHigh()); - updateHigh(high); + testCache.updateHigh(a->getHigh()); + testCache.updateHigh(high); for(int4 i=0;inumInstances();++i) { Varnode *b = high->getInstance(i); a->cover->merge(*b->cover); @@ -1825,7 +1596,7 @@ bool Merge::inflateTest(Varnode *a,HighVariable *high) { HighVariable *ahigh = a->getHigh(); - updateHigh(high); + testCache.updateHigh(high); const Cover &highCover( high->internalCover ); // Only check for intersections with cover contributing to inflate for(int4 i=0;inumInstances();++i) { @@ -1868,7 +1639,7 @@ bool Merge::mergeTest(HighVariable *high,vector &tmplist) for(int4 i=0;i highedgemap; ///< A cache of intersection tests, sorted by HighVariable pair + HighIntersectTest testCache; ///< Cached intersection tests vector copyTrims; ///< COPY ops inserted to facilitate merges vector protoPartial; ///< Roots of unmapped CONCAT trees - 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); static bool mergeTestSpeculative(HighVariable *high_out,HighVariable *high_in); @@ -100,7 +80,6 @@ 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); @@ -122,7 +101,6 @@ class Merge { public: Merge(Funcdata &fd) : data(fd) {} ///< Construct given a specific function void clear(void); - bool intersection(HighVariable *a,HighVariable *b); bool inflateTest(Varnode *a,HighVariable *high); void inflate(Varnode *a,HighVariable *high); bool mergeTest(HighVariable *high,vector &tmplist); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index 9068f2a811..b26487af6f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -1685,6 +1685,44 @@ int4 RuleAndPiece::applyOp(PcodeOp *op,Funcdata &data) return 1; } +/// \class RuleAndZext +/// \brief Convert INT_AND to INT_ZEXT where appropriate: `sext(X) & 0xffff => zext(X)` +/// +/// Similarly `concat(Y,X) & 0xffff => zext(X)` +void RuleAndZext::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_INT_AND); +} + +int4 RuleAndZext::applyOp(PcodeOp *op,Funcdata &data) + +{ + Varnode *cvn1 = op->getIn(1); + if (!cvn1->isConstant()) return 0; + if (!op->getIn(0)->isWritten()) return 0; + PcodeOp *otherop = op->getIn(0)->getDef(); + OpCode opc = otherop->code(); + Varnode *rootvn; + if (opc == CPUI_INT_SEXT) + rootvn = otherop->getIn(0); + else if (opc == CPUI_PIECE) + rootvn = otherop->getIn(1); + else + return 0; + uintb mask = calc_mask(rootvn->getSize()); + if (mask != cvn1->getOffset()) + return 0; + if (rootvn->isFree()) + return 0; + if (rootvn->getSize() > sizeof(uintb)) // FIXME: Should be arbitrary precision + return 0; + data.opSetOpcode(op, CPUI_INT_ZEXT); + data.opRemoveInput(op, 1); + data.opSetInput(op, rootvn, 0); + return 1; +} + /// \class RuleAndCompare /// \brief Simplify INT_ZEXT and SUBPIECE in masked comparison: `zext(V) & c == 0 => V & (c & mask) == 0` /// diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh index 325c62987f..9e357deca3 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh @@ -374,6 +374,16 @@ public: virtual void getOpList(vector &oplist) const; virtual int4 applyOp(PcodeOp *op,Funcdata &data); }; +class RuleAndZext : public Rule { +public: + RuleAndZext(const string &g) : Rule(g, 0, "andzext") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleAndZext(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; class RuleAndCompare : public Rule { public: RuleAndCompare(const string &g) : Rule(g, 0, "andcompare") {} ///< Constructor diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index 9fc48aa7d5..b35dcc3397 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -1894,6 +1894,67 @@ int4 TypeUnion::findCompatibleResolve(Datatype *ct) const return -1; } +TypePartialStruct::TypePartialStruct(const TypePartialStruct &op) + : Datatype(op) +{ + stripped = op.stripped; + container = op.container; + offset = op.offset; +} + +TypePartialStruct::TypePartialStruct(Datatype *contain,int4 off,int4 sz,Datatype *strip) + : Datatype(sz,TYPE_PARTIALSTRUCT) +{ +#ifdef CPUI_DEBUG + if (contain->getMetatype() != TYPE_STRUCT && contain->getMetatype() != TYPE_ARRAY) + throw new LowlevelError("Parent of partial struct is not a struture or array"); +#endif + flags |= has_stripped; + stripped = strip; + container = contain; + offset = off; +} + +void TypePartialStruct::printRaw(ostream &s) const + +{ + container->printRaw(s); + s << "[off=" << dec << offset << ",sz=" << size << ']'; +} + +Datatype *TypePartialStruct::getSubType(uintb off,uintb *newoff) const + +{ + off += offset; + return container->getSubType(off, newoff); +} + +int4 TypePartialStruct::compare(const Datatype &op,int4 level) const + +{ + int4 res = Datatype::compare(op,level); + if (res != 0) return res; + // Both must be partial + TypePartialStruct *tp = (TypePartialStruct *) &op; + if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1; + level -= 1; + if (level < 0) { + if (id == op.getId()) return 0; + return (id < op.getId()) ? -1 : 1; + } + return container->compare(*tp->container,level); // Compare the underlying union +} + +int4 TypePartialStruct::compareDependency(const Datatype &op) const + +{ + if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1; + TypePartialStruct *tp = (TypePartialStruct *) &op; // Both must be partial + if (container != tp->container) return (container < tp->container) ? -1 : 1; // Compare absolute pointers + if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1; + return (op.getSize()-size); +} + TypePartialUnion::TypePartialUnion(const TypePartialUnion &op) : Datatype(op) { @@ -3370,6 +3431,14 @@ TypeStruct *TypeFactory::getTypeStruct(const string &n) return (TypeStruct *) findAdd(tmp); } +TypePartialStruct *TypeFactory::getTypePartialStruct(Datatype *contain,int4 off,int4 sz) + +{ + Datatype *strip = getBase(sz, TYPE_UNKNOWN); + TypePartialStruct tps(contain,off,sz,strip); + return (TypePartialStruct *) findAdd(tps); +} + /// The created union will be incomplete and have no fields. They must be added later. /// \param n is the name of the union /// \return the TypeUnion object @@ -3495,16 +3564,23 @@ TypePointer *TypeFactory::getTypePointerWithSpace(Datatype *ptrTo,AddrSpace *spc Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size) { - uintb newOff = offset; - while(ct != (Datatype *)0 && ct->getSize() > size && ct->getMetatype() != TYPE_UNION) { - ct = ct->getSubType(newOff, &newOff); - } - if (ct == (Datatype *)0 || ct->getSize() < size) - return (Datatype *)0; - if (ct->getSize() == size) - return ct; - if (ct->getMetatype() == TYPE_UNION) // If we hit a containing union - return getTypePartialUnion((TypeUnion *)ct, newOff, size); + Datatype *lastType = (Datatype *)0; + uintb curOff = offset; + do { + if (ct->getSize() <= size) { + if (ct->getSize() == size) + return ct; // Perfect size match + break; + } + else if (ct->getMetatype() == TYPE_UNION) { + return getTypePartialUnion((TypeUnion *)ct, curOff, size); + } + lastType = ct; + 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, curOff, 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 6703c74b24..89900734a0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -99,23 +99,23 @@ enum sub_metatype { SUB_VOID = 22, ///< Compare as a TYPE_VOID SUB_SPACEBASE = 21, ///< Compare as a TYPE_SPACEBASE SUB_UNKNOWN = 20, ///< Compare as a TYPE_UNKNOWN - SUB_INT_CHAR = 19, ///< Signed 1-byte character, sub-type of TYPE_INT - SUB_UINT_CHAR = 18, ///< Unsigned 1-byte character, sub-type of TYPE_UINT - SUB_INT_PLAIN = 17, ///< Compare as a plain TYPE_INT - SUB_UINT_PLAIN = 16, ///< Compare as a plain TYPE_UINT - SUB_INT_ENUM = 15, ///< Signed enum, sub-type of TYPE_INT - SUB_UINT_ENUM = 14, ///< Unsigned enum, sub-type of TYPE_UINT - SUB_INT_UNICODE = 13, ///< Signed wide character, sub-type of TYPE_INT - SUB_UINT_UNICODE = 12, ///< Unsigned wide character, sub-type of TYPE_UINT - SUB_BOOL = 11, ///< Compare as TYPE_BOOL - SUB_CODE = 10, ///< Compare as TYPE_CODE - SUB_FLOAT = 9, ///< Compare as TYPE_FLOAT - SUB_PTRREL_UNK = 8, ///< Pointer to unknown field of struct, sub-type of TYPE_PTR - SUB_PTR = 7, ///< Compare as TYPE_PTR - SUB_PTRREL = 6, ///< Pointer relative to another data-type, sub-type of TYPE_PTR - SUB_PTR_STRUCT = 5, ///< Pointer into struct, sub-type of TYPE_PTR - SUB_ARRAY = 4, ///< Compare as TYPE_ARRAY - SUB_PARTIALSTRUCT = 3, ///< Compare as TYPE_PARTIALSTRUCT + SUB_PARTIALSTRUCT = 19, ///< Compare as TYPE_PARTIALSTRUCT + SUB_INT_CHAR = 18, ///< Signed 1-byte character, sub-type of TYPE_INT + SUB_UINT_CHAR = 17, ///< Unsigned 1-byte character, sub-type of TYPE_UINT + SUB_INT_PLAIN = 16, ///< Compare as a plain TYPE_INT + SUB_UINT_PLAIN = 15, ///< Compare as a plain TYPE_UINT + SUB_INT_ENUM = 14, ///< Signed enum, sub-type of TYPE_INT + SUB_UINT_ENUM = 13, ///< Unsigned enum, sub-type of TYPE_UINT + SUB_INT_UNICODE = 12, ///< Signed wide character, sub-type of TYPE_INT + SUB_UINT_UNICODE = 11, ///< Unsigned wide character, sub-type of TYPE_UINT + SUB_BOOL = 10, ///< Compare as TYPE_BOOL + SUB_CODE = 9, ///< Compare as TYPE_CODE + SUB_FLOAT = 8, ///< Compare as TYPE_FLOAT + SUB_PTRREL_UNK = 7, ///< Pointer to unknown field of struct, sub-type of TYPE_PTR + SUB_PTR = 6, ///< Compare as TYPE_PTR + SUB_PTRREL = 5, ///< Pointer relative to another data-type, sub-type of TYPE_PTR + SUB_PTR_STRUCT = 4, ///< Pointer into struct, sub-type of TYPE_PTR + SUB_ARRAY = 3, ///< Compare as TYPE_ARRAY SUB_STRUCT = 2, ///< Compare as TYPE_STRUCT SUB_UNION = 1, ///< Compare as TYPE_UNION SUB_PARTIALUNION = 0 ///< Compare as a TYPE_PARTIALUNION @@ -484,6 +484,24 @@ public: virtual const TypeField *resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff); }; +/// \brief A data-type that holds \e part of a TypeStruct or TypeArray +class TypePartialStruct : public Datatype { + friend class TypeFactory; + Datatype *stripped; ///< The \e undefined data-type to use if a formal data-type is required. + Datatype *container; ///< Parent structure or array of which \b this is a part + int4 offset; ///< Byte offset within the parent where \b this starts +public: + TypePartialStruct(const TypePartialStruct &op); ///< Construct from another TypePartialStruct + TypePartialStruct(Datatype *contain,int4 off,int4 sz,Datatype *strip); ///< Constructor + Datatype *getParent(void) const { return container; } ///< Get the data-type containing \b this piece + virtual void printRaw(ostream &s) const; + virtual Datatype *getSubType(uintb off,uintb *newoff) const; + virtual int4 compare(const Datatype &op,int4 level) const; + virtual int4 compareDependency(const Datatype &op) const; + virtual Datatype *clone(void) const { return new TypePartialStruct(*this); } + virtual Datatype *getStripped(void) const { return stripped; } +}; + /// \brief An internal data-type for holding information about a variable's relative position within a union data-type /// /// This is a data-type that can be assigned to a Varnode offset into a Symbol, where either the Symbol itself or @@ -500,7 +518,7 @@ public: TypePartialUnion(const TypePartialUnion &op); ///< Construct from another TypePartialUnion TypePartialUnion(TypeUnion *contain,int4 off,int4 sz,Datatype *strip); ///< Constructor TypeUnion *getParentUnion(void) const { return container; } ///< Get the union which \b this is part of - virtual void printRaw(ostream &s) const; ///< Print a description of the type to stream + virtual void printRaw(ostream &s) const; virtual const TypeField *findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const; virtual int4 numDepend(void) const; virtual Datatype *getDepend(int4 index) const; @@ -681,6 +699,7 @@ public: TypePointer *getTypePointerNoDepth(int4 s,Datatype *pt,uint4 ws); ///< Construct a depth limited pointer data-type TypeArray *getTypeArray(int4 as,Datatype *ao); ///< Construct an array data-type TypeStruct *getTypeStruct(const string &n); ///< Create an (empty) structure + TypePartialStruct *getTypePartialStruct(Datatype *contain,int4 off,int4 sz); ///< Create a partial structure TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc index c3b89df4c1..c8e3205afc 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc @@ -43,12 +43,29 @@ void VariableGroup::addPiece(VariablePiece *piece) piece->group = this; if (!pieceSet.insert(piece).second) throw LowlevelError("Duplicate VariablePiece"); + int4 pieceMax = piece->getOffset() + piece->getSize(); + if (pieceMax > size) + size = pieceMax; +} + +/// The adjustment amount must be positive, and this effectively increases the size of the group. +/// \param amt is the given amount to add to offsets +void VariableGroup::adjustOffsets(int4 amt) + +{ + set::iterator iter; + + for(iter=pieceSet.begin();iter!=pieceSet.end();++iter) { + (*iter)->groupOffset += amt; + } + size += amt; } void VariableGroup::removePiece(VariablePiece *piece) { pieceSet.erase(piece); + // We currently don't adjust size here as removePiece is currently only called during clean up } /// Construct piece given a HighVariable and its position within the whole. @@ -134,17 +151,6 @@ void VariablePiece::updateCover(void) const 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 newGroup is the new VariableGroup to transfer \b this to void VariablePiece::transferGroup(VariableGroup *newGroup) @@ -169,13 +175,14 @@ void VariablePiece::combineOtherGroup(VariablePiece *op2,vector { int4 diff = groupOffset - op2->groupOffset; // Add to op2, or subtract from this if (diff > 0) - op2->adjustOffset(diff); + op2->group->adjustOffsets(diff); else if (diff < 0) - adjustOffset(-diff); + group->adjustOffsets(-diff); set::iterator iter = op2->group->pieceSet.begin(); set::iterator enditer = op2->group->pieceSet.end(); while(iter != enditer) { VariablePiece *piece = *iter; + ++iter; set::iterator matchiter = group->pieceSet.find(piece); if (matchiter != group->pieceSet.end()) { mergePairs.push_back((*matchiter)->high); @@ -228,15 +235,8 @@ void HighVariable::setSymbol(Varnode *vn) const } } symbol = entry->getSymbol(); - if (vn->isProtoPartial()) { - Varnode *rootVn = PieceNode::findRoot(vn); - if (rootVn == vn) - throw LowlevelError("Partial varnode does not match symbol"); - - symboloffset = vn->getAddr().overlapJoin(0,rootVn->getAddr(),rootVn->getSize()); - SymbolEntry *entry = rootVn->getSymbolEntry(); - if (entry != (SymbolEntry *)0) - symboloffset += entry->getOffset(); + if (vn->isProtoPartial() && piece != (VariablePiece *)0) { + symboloffset = piece->getOffset() + piece->getGroup()->getSymbolOffset(); } else if (entry->isDynamic()) // Dynamic symbols (that aren't partials) match whole variable symboloffset = -1; @@ -249,6 +249,8 @@ void HighVariable::setSymbol(Varnode *vn) const symboloffset = vn->getAddr().overlapJoin(0,entry->getAddr(),symbol->getType()->getSize()) + entry->getOffset(); } + if (type != (Datatype *)0 && type->getMetatype() == TYPE_PARTIALUNION) + highflags |= typedirty; highflags &= ~((uint4)symboldirty); // We are no longer dirty } @@ -365,9 +367,17 @@ void HighVariable::updateType(void) const vn = getTypeRepresentative(); type = vn->getType(); - if (type->hasStripped() && type->getMetatype() != TYPE_PARTIALUNION) - type = type->getStripped(); - + if (type->hasStripped()) { + if (type->getMetatype() == TYPE_PARTIALUNION) { + if (symbol != (Symbol *)0 && symboloffset != -1) { + type_metatype meta = symbol->getType()->getMetatype(); + if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol + type = type->getStripped(); // strip the partial union + } + } + else + type = type->getStripped(); + } // Update lock flags flags &= ~Varnode::typelock; if (vn->isTypeLock()) @@ -469,21 +479,6 @@ Varnode *HighVariable::getNameRepresentative(void) const return nameRepresentative; } -/// Find the first member that is either address tied or marked as a proto partial. -/// \return a member Varnode acting as partial storage or null if none exist -Varnode *HighVariable::getPartialOrAddrTied(void) const - -{ - int4 i; - - for(i=0;iisAddrTied() || vn->isProtoPartial()) - return vn; - } - return (Varnode *)0; -} - /// Search for the given Varnode and cut it out of the list, marking all properties as \e dirty. /// \param vn is the given Varnode member to remove void HighVariable::remove(Varnode *vn) @@ -551,7 +546,7 @@ void HighVariable::groupWith(int4 off,HighVariable *hi2) else if (hi2->piece == (VariablePiece *)0) { int4 hi2Off = piece->getOffset() - off; if (hi2Off < 0) { - piece->adjustOffset(-hi2Off); + piece->getGroup()->adjustOffsets(-hi2Off); hi2Off = 0; } if ((highflags & intersectdirty) == 0) @@ -564,6 +559,22 @@ void HighVariable::groupWith(int4 off,HighVariable *hi2) } } +/// If \b this is part of a larger group and has had its \b symboloffset set, it can be used +/// to calculate the \b symboloffset of other HighVariables in the same group, by writing it +/// to the common VariableGroup object. +void HighVariable::establishGroupSymbolOffset(void) + +{ + VariableGroup *group = piece->getGroup(); + int4 off = symboloffset; + if (off < 0) + off = 0; + off -= piece->getOffset(); + if (off < 0) + throw LowlevelError("Symbol offset is incompatible with VariableGroup"); + group->setSymbolOffset(off); +} + /// 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 @@ -614,12 +625,15 @@ void HighVariable::mergeInternal(HighVariable *tv2,bool isspeculative) /// 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 testCache if non-null is a cache of intersection tests that must be updated to reflect the merge /// \param isspeculative is \b true to keep the new members in separate \e merge classes -void HighVariable::merge(HighVariable *tv2,bool isspeculative) +void HighVariable::merge(HighVariable *tv2,HighIntersectTest *testCache,bool isspeculative) { if (tv2 == this) return; + if (testCache != (HighIntersectTest *)0) + testCache->moveIntersectTests(this,tv2); if (piece == (VariablePiece *)0 && tv2->piece == (VariablePiece *)0) { mergeInternal(tv2,isspeculative); return; @@ -638,16 +652,18 @@ void HighVariable::merge(HighVariable *tv2,bool 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(); + if (isspeculative) + throw LowlevelError("Trying speculatively merge variables in separate groups"); + vector mergePairs; + piece->combineOtherGroup(tv2->piece, mergePairs); + for(int4 i=0;imoveIntersectTests(high1, high2); + high1->mergeInternal(high2, isspeculative); + } + piece->markIntersectionDirty(); } /// All Varnode objects are assigned a HighVariable, including those that don't get names like @@ -755,15 +771,6 @@ int4 HighVariable::instanceIndex(const Varnode *vn) const return -1; } -/// \param op2 is the other HighVariable to compare with \b this -/// \return \b true if they are in the same group -bool HighVariable::sameGroup(const HighVariable *op2) const - -{ - if (piece == (VariablePiece *)0 || op2->piece == (VariablePiece *)0) return false; - return (piece->getGroup() == op2->piece->getGroup()); -} - /// \param encoder is the stream encoder void HighVariable::encode(Encoder &encoder) const @@ -780,6 +787,8 @@ void HighVariable::encode(Encoder &encoder) const else if (!isPersist() && (symbol != (Symbol *)0)) { if (symbol->getCategory() == Symbol::function_parameter) encoder.writeString(ATTRIB_CLASS, "param"); + else if (symbol->getScope()->isGlobal()) + encoder.writeString(ATTRIB_CLASS, "global"); else encoder.writeString(ATTRIB_CLASS, "local"); } @@ -883,3 +892,233 @@ void HighVariable::verifyCover(void) const } } #endif + +/// \brief Gather Varnode instances of the given HighVariable that intersect a cover on a specific block +/// +/// \param a is the given HighVariable +/// \param blk is the specific block number +/// \param cover is the Cover to test for intersection +/// \param res will hold the resulting intersecting Varnodes +void HighIntersectTest::gatherBlockVarnodes(HighVariable *a,int4 blk,const Cover &cover,vector &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 HighIntersectTest::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. +/// \param a is the first HighVariable +/// \param b is the second HighVariable +/// \param blk is the index of the BlockBasic on which to test intersection +/// \return \b true if an intersection occurs in the specified block +bool HighIntersectTest::blockIntersection(HighVariable *a,HighVariable *b,int4 blk) + +{ + vector blist; + + 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; + } + } + 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; +} + +/// All tests for pairs where either the first or second HighVariable matches the given one +/// are removed. +/// \param high is the given HighVariable to purge +void HighIntersectTest::purgeHigh(HighVariable *high) + +{ + map::iterator iterfirst = highedgemap.lower_bound( HighEdge(high,(HighVariable *)0) ); + map::iterator iterlast = highedgemap.lower_bound( HighEdge(high,(HighVariable *)~((uintp)0)) ); + + if (iterfirst == iterlast) return; + --iterlast; // Move back 1 to prevent deleting under the iterator + map::iterator iter; + for(iter=iterfirst;iter!=iterlast;++iter) + 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); +} + +/// \brief Translate any intersection tests for \e high2 into tests for \e high1 +/// +/// 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 HighIntersectTest::moveIntersectTests(HighVariable *high1,HighVariable *high2) + +{ + vector yesinter; // Highs that high2 intersects + vector nointer; // Highs that high2 does not intersect + map::iterator iterfirst = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)0) ); + map::iterator iterlast = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)~((uintp)0)) ); + map::iterator iter; + + for(iter=iterfirst;iter!=iterlast;++iter) { + 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 + else { + nointer.push_back(b); + b->setMark(); // Mark that high2 did not intersect + } + } + // Do a purge of all high2's tests + if (iterfirst != iterlast) { // Delete all the high2 tests + --iterlast; // Move back 1 to prevent deleting under the iterator + for(iter=iterfirst;iter!=iterlast;++iter) + 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); + } + + iter = highedgemap.lower_bound( HighEdge(high1,(HighVariable *)0) ); + while((iter!=highedgemap.end())&&((*iter).first.a == high1)) { + if (!(*iter).second) { // If test is intersection==false + if (!(*iter).first.b->isMark()) // and there was no test with high2 + highedgemap.erase( iter++ ); // Delete the test + else + ++iter; + } + else // Keep any intersection==true tests + ++iter; + } + vector::iterator titer; + for(titer=nointer.begin();titer!=nointer.end();++titer) + (*titer)->clearMark(); + + // 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; + } +} + +/// As manipulations are made, Cover information gets out of date. A \e dirty flag is used to +/// indicate a particular HighVariable Cover is out-of-date. This routine checks the \e dirty +/// flag and updates the Cover information if it is set. +/// \param a is the HighVariable to update +/// \return \b true if the HighVariable was not originally dirty +bool HighIntersectTest::updateHigh(HighVariable *a) + +{ + if (!a->isCoverDirty()) return true; + + a->updateCover(); + purgeHigh(a); + return false; +} + +/// \brief Test the intersection of two HighVariables and cache the result +/// +/// If the Covers of the two variables intersect, this routine returns \b true. To avoid +/// expensive computation on the Cover objects themselves, the test result associated with +/// the pair of HighVariables is cached. +/// \param a is the first HighVariable +/// \param b is the second HighVariable +/// \return \b true if the variables intersect +bool HighIntersectTest::intersection(HighVariable *a,HighVariable *b) + +{ + if (a==b) return false; + bool ares = updateHigh(a); + bool bres = updateHigh(b); + if (ares && bres) { // If neither high was dirty + map::iterator iter = highedgemap.find( HighEdge(a,b) ); + if (iter != highedgemap.end()) // If previous test is present + return (*iter).second; // Use it + } + + bool res = false; + int4 blk; + vector blockisect; + a->getCover().intersectList(blockisect,b->getCover(),2); + for(blk=0;blk pieceSet; ///< The set of VariablePieces making up \b this group + int4 size; ///< Number of contiguous bytes covered by the whole group + int4 symbolOffset; ///< Byte offset of \b this group within its containing Symbol public: + VariableGroup(void) { size = 0; symbolOffset = 0; } 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 adjustOffsets(int4 amt); ///< Adjust offset for every piece by the given amount void removePiece(VariablePiece *piece); ///< Remove a piece from \b this group + int4 getSize(void) const { return size; } ///< Get the number of bytes \b this group covers + void setSymbolOffset(int4 val) { symbolOffset = val; } ///< Cache the symbol offset for the group + int4 getSymbolOffset(void) const { return symbolOffset; } ///< Get offset of \b this group within its Symbol }; /// \brief Information about how a HighVariable fits into a larger group or Symbol @@ -80,12 +87,13 @@ public: 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 }; +class HighIntersectTest; + /// \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 @@ -122,6 +130,7 @@ private: friend class Varnode; friend class Merge; friend class VariablePiece; + friend class HighIntersectTest; 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 @@ -145,7 +154,7 @@ private: 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 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 merge(HighVariable *tv2,HighIntersectTest *testCache,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 @@ -167,6 +176,7 @@ public: 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 + void establishGroupSymbolOffset(void); ///< Transfer \b symbol offset of \b this to the VariableGroup /// \brief Print details of the cover for \b this (for debug purposes) /// @@ -179,7 +189,6 @@ public: Varnode *getInputVarnode(void) const; ///< Find (the) input member Varnode Varnode *getTypeRepresentative(void) const; ///< Get a member Varnode with the strongest data-type Varnode *getNameRepresentative(void) const; ///< Get a member Varnode that dictates the naming of \b this HighVariable - Varnode *getPartialOrAddrTied(void) const; ///< Find the first member that can act as partial symbol storage int4 getNumMergeClasses(void) const { return numMergeClasses; } ///< Get the number of speculative merges for \b this bool isMapped(void) const { updateFlags(); return ((flags&Varnode::mapped)!=0); } ///< Return \b true if \b this is mapped bool isPersist(void) const { updateFlags(); return ((flags&Varnode::persist)!=0); } ///< Return \b true if \b this is a global variable @@ -191,7 +200,6 @@ public: bool isUnaffected(void) const { updateFlags(); return ((flags&Varnode::unaffected)!=0); } ///< Return \b true if \b this is an \e unaffected register bool isExtraOut(void) const { updateFlags(); return ((flags&(Varnode::indirect_creation|Varnode::addrtied))==Varnode::indirect_creation); } ///< Return \b true if \b this is an extra output bool isProtoPartial(void) const { updateFlags(); return ((flags&Varnode::proto_partial)!=0); } ///< Return \b true if \b this is a piece concatenated into a larger whole - bool isPartialOrAddrTied(void) const { updateFlags(); return ((flags&(Varnode::addrtied|Varnode::proto_partial))!=0); } ///< Return \b true if \b this is potential partial symbol void setMark(void) const { flags |= Varnode::mark; } ///< Set the mark on this variable void clearMark(void) const { flags &= ~Varnode::mark; } ///< Clear the mark on this variable bool isMark(void) const { return ((flags&Varnode::mark)!=0); } ///< Return \b true if \b this is marked @@ -208,7 +216,6 @@ public: bool isUnattached(void) const { return inst.empty(); } ///< Return \b true if \b this has no member Varnode bool isTypeLock(void) const { updateType(); return ((flags & Varnode::typelock)!=0); } ///< Return \b true if \b this is \e typelocked bool isNameLock(void) const { updateFlags(); return ((flags & Varnode::namelock)!=0); } ///< Return \b true if \b this is \e namelocked - bool sameGroup(const HighVariable *op2) const; ///< Return \b true if \b and other variable are parts of the same variable void encode(Encoder &encoder) const; ///< Encode \b this variable to stream as a \ element #ifdef MERGEMULTI_DEBUG void verifyCover(void) const; @@ -219,6 +226,42 @@ public: static int4 markExpression(Varnode *vn,vector &highList); ///< Mark and collect variables in expression }; +/// \brief A record for caching a Cover intersection test between two HighVariable objects +/// +/// This is just a pair of HighVariable objects that can be used as a map key. The HighIntersectTest +/// class uses it to cache intersection test results between the two variables in a map. +class HighEdge { + friend class HighIntersectTest; + HighVariable *a; ///< First HighVariable of the pair + HighVariable *b; ///< Second HighVariable of the pair +public: + /// \brief Comparator + bool operator<(const HighEdge &op2) const { if (a==op2.a) return (b highedgemap; ///< A cache of intersection tests, sorted by HighVariable pair + 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); + void purgeHigh(HighVariable *high); ///< Remove cached intersection tests for a given HighVariable +public: + void moveIntersectTests(HighVariable *high1,HighVariable *high2); + bool updateHigh(HighVariable *a); ///< Make sure given HighVariable's Cover is up-to-date + bool intersection(HighVariable *a,HighVariable *b); + void clear(void) { highedgemap.clear(); } +}; + /// 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc index 76f4ed86fd..a382431cb2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc @@ -978,6 +978,9 @@ void MapState::gatherVarnodes(const Funcdata &fd) if (vn->isFree()) continue; uintb start = vn->getOffset(); Datatype *ct = vn->getType(); + // Assume parents are present so partials aren't needed + if (ct->getMetatype() == TYPE_PARTIALSTRUCT) continue; + if (ct->getMetatype() == TYPE_PARTIALUNION) continue; // Do not force Varnode flags on the entry // as the flags were inherited from the previous // (now obsolete) entry @@ -1008,6 +1011,7 @@ void MapState::gatherHighs(const Funcdata &fd) varvec.push_back(high); uintb start = vn->getOffset(); Datatype *ct = high->getType(); // Get type from high + if (ct->getMetatype() == TYPE_PARTIALUNION) continue; addRange(start,ct,0,RangeHint::fixed,-1); } for(int4 i=0;igetSpace(); uintb off = vn->getOffset(); - uintb maxoff = off + (vn->getSize() - 1); + 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) + 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); + uintb endOff = vn->getOffset() + (vn->getSize() - 1); + if (endOff > maxOff) + maxOff = endOff; flags |= vn->getFlags(); bounds.push_back(iter); iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written); diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/piecestruct.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/piecestruct.xml new file mode 100644 index 0000000000..225b6b2531 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/piecestruct.xml @@ -0,0 +1,39 @@ + + + + +554889e54883ec4089c84589c24589c8 +89f966894ddc89f166894dd88855d488 +45d04489d08845cc4489c08845c86448 +8b042528000000488945f831c00fbf45 +d88945ecc165ec100fb745dc0fb7c009 +45ec8b45ec8945f00fbe45c825ff0000 +008945ecc165ec080fbe45cc0fb6c009 +45ecc165ec080fbe45d00fb6c00945ec +c165ec080fbe45d40fb6c00945ec8b45 +ec8945f4488d45f04889c7e81affffff +90488b45f86448330425280000007405 +e8bbfdffffc9c3 + + + + + +Stack_18\.a = a; +Stack_18\.b = b; +Stack_18\.arr\[0\] = c; +Stack_18\.arr\[1\] = d; +Stack_18\.arr\[2\] = e; +Stack_18\.arr\[3\] = f; +