diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index f5cd4e5a99..dfbec2d55f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -1215,6 +1215,7 @@ int4 ActionVarnodeProps::apply(Funcdata &data) { Architecture *glb = data.getArch(); bool cachereadonly = glb->readonlypropagate; + int4 pass = data.getHeritagePass(); VarnodeLocSet::const_iterator iter; Varnode *vn; @@ -1223,7 +1224,29 @@ int4 ActionVarnodeProps::apply(Funcdata &data) vn = *iter++; // Advance iterator in case vn is deleted if (vn->isAnnotation()) continue; int4 vnSize = vn->getSize(); - if (vn->hasActionProperty()) { + if (vn->isAutoLiveHold()) { + if (pass > 0) { + if (vn->isWritten()) { + PcodeOp *loadOp = vn->getDef(); + if (loadOp->code() == CPUI_LOAD) { + Varnode *ptr = loadOp->getIn(1); + if (ptr->isConstant() || ptr->isReadOnly()) + continue; + if (ptr->isWritten()) { + PcodeOp *copyOp = ptr->getDef(); + if (copyOp->code() == CPUI_COPY) { + ptr = copyOp->getIn(0); + if (ptr->isConstant() || ptr->isReadOnly()) + continue; + } + } + } + } + vn->clearAutoLiveHold(); + count += 1; + } + } + else if (vn->hasActionProperty()) { if (cachereadonly&&vn->isReadOnly()) { if (data.fillinReadOnly(vn)) // Try to replace vn with its lookup in LoadImage count += 1; @@ -3383,6 +3406,84 @@ uintb ActionDeadCode::gatherConsumedReturn(Funcdata &data) return consumeVal; } +/// \brief Determine if the given Varnode may eventually collapse to a constant +/// +/// Recursively check if the Varnode is either: +/// - Copied from a constant +/// - The result of adding constants +/// - Loaded from a pointer that is a constant +/// +/// \param vn is the given Varnode +/// \param addCount is the number of CPUI_INT_ADD operations seen so far +/// \param loadCount is the number of CPUI_LOAD operations seen so far +/// \return \b true if the Varnode (might) collapse to a constant +bool ActionDeadCode::isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount) + +{ + if (vn->isConstant()) return true; + if (!vn->isWritten()) return false; + PcodeOp *op = vn->getDef(); + while(op->code() == CPUI_COPY) { + vn = op->getIn(0); + if (vn->isConstant()) return true; + if (!vn->isWritten()) return false; + op = vn->getDef(); + } + switch(op->code()) { + case CPUI_INT_ADD: + if (addCount > 0) return false; + if (!isEventualConstant(op->getIn(0),addCount+1,loadCount)) + return false; + return isEventualConstant(op->getIn(1),addCount+1,loadCount); + case CPUI_LOAD: + if (loadCount > 0) return false; + return isEventualConstant(op->getIn(1),0,loadCount+1); + case CPUI_INT_LEFT: + case CPUI_INT_RIGHT: + case CPUI_INT_SRIGHT: + case CPUI_INT_MULT: + if (!op->getIn(1)->isConstant()) + return false; + return isEventualConstant(op->getIn(0),addCount,loadCount); + case CPUI_INT_ZEXT: + case CPUI_INT_SEXT: + return isEventualConstant(op->getIn(0),addCount,loadCount); + default: + break; + } + return false; +} + +/// \brief Check if there are any unconsumed LOADs that may be from volatile addresses. +/// +/// It may be too early to remove certain LOAD operations even though their result isn't +/// consumed because it be of a volatile address with side effects. If a LOAD meets this +/// criteria, it is added to the worklist and \b true is returned. +/// \param data is the function being analyzed +/// \return \b true if there was at least one LOAD added to the worklist +bool ActionDeadCode::lastChanceLoad(Funcdata &data,vector &worklist) + +{ + if (data.getHeritagePass() > 1) return false; + if (data.isJumptableRecoveryOn()) return false; + list::const_iterator iter = data.beginOp(CPUI_LOAD); + list::const_iterator enditer = data.endOp(CPUI_LOAD); + bool res = false; + while(iter != enditer) { + PcodeOp *op = *iter; + ++iter; + if (op->isDead()) continue; + Varnode *vn = op->getOut(); + if (vn->isConsumeVacuous()) continue; + if (isEventualConstant(op->getIn(1), 0, 0)) { + pushConsumed(~(uintb)0, vn, worklist); + vn->setAutoLiveHold(); + res = true; + } + } + return res; +} + int4 ActionDeadCode::apply(Funcdata &data) { @@ -3476,6 +3577,11 @@ int4 ActionDeadCode::apply(Funcdata &data) while(!worklist.empty()) propagateConsumed(worklist); + if (lastChanceLoad(data, worklist)) { + while(!worklist.empty()) + propagateConsumed(worklist); + } + for(i=0;inumSpaces();++i) { spc = manage->getSpace(i); if (spc == (AddrSpace *)0 || !spc->doesDeadcode()) continue; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index fded0da14b..14de6c6b31 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -541,6 +541,8 @@ class ActionDeadCode : public Action { static bool neverConsumed(Varnode *vn,Funcdata &data); static void markConsumedParameters(FuncCallSpecs *fc,vector &worklist); static uintb gatherConsumedReturn(Funcdata &data); + static bool isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount); + static bool lastChanceLoad(Funcdata &data,vector &worklist); public: ActionDeadCode(const string &g) : Action(0,"deadcode",g) {} ///< Constructor virtual Action *clone(const ActionGroupList &grouplist) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 467b604792..71504ec713 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -200,6 +200,8 @@ public: Varnode *findSpacebaseInput(AddrSpace *id) const; void spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const Address &rampoint,uintb origval,int4 origsize); + int4 getHeritagePass(void) const { return heritage.getPass(); } ///< Get overall count of heritage passes + /// \brief Get the number of heritage passes performed for the given address space /// /// \param spc is the address space diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh index 9a27e51a6c..025cfeb3db 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh @@ -267,6 +267,8 @@ class Heritage { public: Heritage(Funcdata *data); ///< Constructor + int4 getPass(void) const { return pass; } ///< Get overall count of heritage passes + /// \brief Get the pass number when the given address was heritaged /// /// \param addr is the given address diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh index a3e4dabafe..f5e13c5802 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh @@ -102,7 +102,8 @@ public: precishi = 0x4000000, ///< Is this Varnode the high part of a double precision value indirectstorage = 0x8000000, ///< Is this Varnode storing a pointer to the actual symbol hiddenretparm = 0x10000000, ///< Does this varnode point to the return value storage location - incidental_copy = 0x20000000 ///< Do copies of this varnode happen as a side-effect + incidental_copy = 0x20000000, ///< Do copies of this varnode happen as a side-effect + autolive_hold = 0x40000000 ///< Temporarily block dead-code removal of \b this }; /// Additional boolean properties on a Varnode enum addl_flags { @@ -228,7 +229,8 @@ public: /// Are all Varnodes at this storage location components of the same high-level variable? bool isAddrTied(void) const { return ((flags&(Varnode::addrtied|Varnode::insert))==(Varnode::addrtied|Varnode::insert)); } bool isAddrForce(void) const { return ((flags&Varnode::addrforce)!=0); } ///< Is \b this value forced into a particular storage location? - bool isAutoLive(void) const { return ((flags&Varnode::addrforce)!=0); } ///< Is \b this varnode exempt from dead-code removal? + bool isAutoLive(void) const { return ((flags&(Varnode::addrforce|Varnode::autolive_hold))!=0); } ///< Is \b this varnode exempt from dead-code removal? + bool isAutoLiveHold(void) const { return ((flags&Varnode::autolive_hold)!=0); } ///< Is there a temporary hold on dead-code removal? bool isMapped(void) const { return ((flags&Varnode::mapped)!=0); } ///< Is there or should be formal symbol information associated with \b this? bool isUnaffected(void) const { return ((flags&Varnode::unaffected)!=0); } ///< Is \b this a value that is supposed to be preserved across the function? bool isSpacebase(void) const { return ((flags&Varnode::spacebase)!=0); } ///< Is this location used to store the base point for a virtual address space? @@ -296,6 +298,8 @@ public: void clearPrecisHi(void) { clearFlags(Varnode::precishi); } ///< Clear the mark indicating a double precision portion void setWriteMask(void) { addlflags |= Varnode::writemask; } ///< Mark \b this as not a true \e write when computing SSA form void clearWriteMask(void) { addlflags &= ~Varnode::writemask; } ///< Clear the mark indicating \b this is not a true write + void setAutoLiveHold(void) { flags |= Varnode::autolive_hold; } ///< Place temporary hold on dead code removal + void clearAutoLiveHold(void) { flags &= ~Varnode::autolive_hold; } ///< Clear temporary hold on dead code removal void setUnsignedPrint(void) { addlflags |= Varnode::unsignedprint; } ///< Force \b this to be printed as unsigned bool updateType(Datatype *ct,bool lock,bool override); ///< (Possibly) set the Datatype given various restrictions void setStackStore(void) { addlflags |= Varnode::stack_store; } ///< Mark as produced by explicit CPUI_STORE