From ed786460caef3057948a21260f14b1179eda51a2 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Tue, 18 Feb 2020 14:27:54 -0500 Subject: [PATCH] New unnormalized switch variable calculation --- .../Decompiler/src/decompile/cpp/jumptable.cc | 96 +++++++++++++++---- .../Decompiler/src/decompile/cpp/jumptable.hh | 9 +- 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc index 1ef9d7111e..ee4ccc9053 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc @@ -562,18 +562,20 @@ static bool matching_constants(Varnode *vn1,Varnode *vn2) return true; } -/// \param op is the CBRANCH \e guarding the switch +/// \param bOp is the CBRANCH \e guarding the switch +/// \param rOp is the PcodeOp immediately reading the Varnode /// \param path is the specific branch to take from the CBRANCH to reach the switch /// \param rng is the range of values causing the switch path to be taken /// \param v is the Varnode holding the value controlling the CBRANCH -GuardRecord::GuardRecord(PcodeOp *op,int4 path,const CircleRange &rng,Varnode *v) +GuardRecord::GuardRecord(PcodeOp *bOp,PcodeOp *rOp,int4 path,const CircleRange &rng,Varnode *v) { - cbranch = op; + cbranch = bOp; + readOp = rOp; indpath = path; range = rng; vn = v; - baseVn = quasiCopy(v,bitsPreserved,false); // Look for varnode whose bits are copied + baseVn = quasiCopy(v,bitsPreserved); // Look for varnode whose bits are copied } /// \brief Determine if \b this guard applies to the given Varnode @@ -670,10 +672,9 @@ int4 GuardRecord::oneOffMatch(PcodeOp *op1,PcodeOp *op2) /// \param vn is the given Varnode /// \param bitsPreserved will hold the number of least significant bits preserved by the sequence /// \return the earliest source of the quasi-copy, which may just be the given Varnode -Varnode *GuardRecord::quasiCopy(Varnode *vn,int4 &bitsPreserved,bool noWholeValue) +Varnode *GuardRecord::quasiCopy(Varnode *vn,int4 &bitsPreserved) { - Varnode *origVn = vn; bitsPreserved = mostsigbit_set(vn->getNZMask()) + 1; if (bitsPreserved == 0) return vn; uintb mask = 1; @@ -682,11 +683,6 @@ Varnode *GuardRecord::quasiCopy(Varnode *vn,int4 &bitsPreserved,bool noWholeValu PcodeOp *op = vn->getDef(); Varnode *constVn; while(op != (PcodeOp *)0) { - if (noWholeValue && (vn != origVn)) { - uintb inputMask = vn->getNZMask() | mask; - if (mask == inputMask) - return origVn; // vn contains whole value, -noWholeValue- indicates we should abort - } switch(op->code()) { case CPUI_COPY: vn = op->getIn(0); @@ -955,6 +951,29 @@ void PathMeld::meld(vector &path,vector &slot) slot.resize(cutOff); } +/// The starting Varnode, common to all paths, is provided as an index. +/// All PcodeOps up to the final BRANCHIND are (un)marked. +/// \param val is \b true for marking, \b false for unmarking +/// \param startVarnode is the index of the starting PcodeOp +void PathMeld::markPaths(bool val,int4 startVarnode) + +{ + int4 startOp; + for(startOp=opMeld.size()-1;startOp>=0;--startOp) { + if (opMeld[startOp].rootVn == startVarnode) + break; + } + if (startOp < 0) return; + if (val) { + for(int4 i=0;i<=startOp;++i) + opMeld[i].op->setMark(); + } + else { + for(int4 i=0;i<=startOp;++i) + opMeld[i].op->clearMark(); + } +} + /// The Varnode is specified by an index into sequence of Varnodes common to all paths in \b this PathMeld. /// We find the earliest (as in executed first) PcodeOp, within \b this PathMeld that uses the Varnode as input. /// \param pos is the index of the Varnode @@ -1023,14 +1042,15 @@ void JumpBasic::analyzeGuards(BlockBasic *bl,int4 pathout) // The boolean variable could conceivably be the switch variable int4 indpathstore = prevbl->getFlipPath() ? 1-indpath : indpath; - selectguards.push_back(GuardRecord(cbranch,indpathstore,rng,vn)); + selectguards.push_back(GuardRecord(cbranch,cbranch,indpathstore,rng,vn)); for(j=0;jisWritten()) break; - vn = rng.pullBack(vn->getDef(),&markup,usenzmask); + PcodeOp *readOp = vn->getDef(); + vn = rng.pullBack(readOp,&markup,usenzmask); if (vn == (Varnode *)0) break; if (rng.isEmpty()) break; - selectguards.push_back(GuardRecord(cbranch,indpathstore,rng,vn)); + selectguards.push_back(GuardRecord(cbranch,readOp,indpathstore,rng,vn)); } } } @@ -1068,7 +1088,7 @@ void JumpBasic::calcRange(Varnode *vn,CircleRange &rng) const // Intersect any guard ranges which apply to -vn- int4 bitsPreserved; - Varnode *baseVn = GuardRecord::quasiCopy(vn, bitsPreserved, true); + Varnode *baseVn = GuardRecord::quasiCopy(vn, bitsPreserved); vector::const_iterator iter; for(iter=selectguards.begin();iter!=selectguards.end();++iter) { const GuardRecord &guard( *iter ); @@ -1175,7 +1195,7 @@ void JumpBasic::markFoldableGuards(void) { Varnode *vn = pathMeld.getVarnode(varnodeIndex); int4 bitsPreserved; - Varnode *baseVn = GuardRecord::quasiCopy(vn, bitsPreserved, true); + Varnode *baseVn = GuardRecord::quasiCopy(vn, bitsPreserved); for(int4 i=0;isetMark(); + else + readOp->clearMark(); + } +} + +/// The PcodeOps in \b this model must have been previously marked with markModel(). +/// Run through the descendants of the given Varnode and look for this mark. +/// \param vn is the given Varnode +/// \param trailOp is an optional known PcodeOp that leads to the model +/// \return \b true if the only flow is into \b this model +bool JumpBasic::flowsOnlyToModel(Varnode *vn,PcodeOp *trailOp) + +{ + list::const_iterator iter; + for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { + PcodeOp *op = *iter; + if (op == trailOp) continue; + if (!op->isMark()) + return false; + } + return true; +} + bool JumpBasic::foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump) { @@ -1275,19 +1329,18 @@ void JumpBasic::findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext { int4 i,j; - Varnode *testvn; - PcodeOp *normop; i = varnodeIndex; normalvn = pathMeld.getVarnode(i++); switchvn = normalvn; + markModel(true); int4 countaddsub=0; int4 countext=0; + PcodeOp *normop = (PcodeOp *)0; while(iloneDescend() == (PcodeOp *)0)) break; - testvn = pathMeld.getVarnode(i); + if (!flowsOnlyToModel(switchvn, normop)) break; // Switch variable should only flow into model + Varnode *testvn = pathMeld.getVarnode(i); if (!switchvn->isWritten()) break; normop = switchvn->getDef(); for(j=0;jnumInput();++j) @@ -1313,6 +1366,7 @@ void JumpBasic::findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext if (switchvn != testvn) break; i += 1; } + markModel(false); } void JumpBasic::buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh index f8a631ed6e..a7c351685c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh @@ -82,6 +82,7 @@ public: void append(const PathMeld &op2); ///< Append a new set of paths to \b this set of paths void clear(void); ///< Clear \b this to be an empty container void meld(vector &path,vector &slot); ///< Meld a new path into \b this container + void markPaths(bool val,int4 startVarnode); ///< Mark PcodeOps paths from the given start int4 numCommonVarnode(void) const { return commonVn.size(); } ///< Return the number of Varnodes common to all paths int4 numOps(void) const { return opMeld.size(); } ///< Return the number of PcodeOps across all paths Varnode *getVarnode(int4 i) const { return commonVn[i]; } ///< Get the i-th common Varnode @@ -128,20 +129,22 @@ class JumpTable; /// description of the possible values the Varnode can hold. class GuardRecord { PcodeOp *cbranch; ///< PcodeOp CBRANCH the branches around the switch + PcodeOp *readOp; ///< The immediate PcodeOp causing the restriction int4 indpath; ///< Specific CBRANCH path going to the switch CircleRange range; ///< Range of values causing the CBRANCH to take the path to the switch Varnode *vn; ///< The Varnode being restricted Varnode *baseVn; ///< Value being (quasi)copied to the Varnode int4 bitsPreserved; ///< Number of bits copied (all other bits are zero) public: - GuardRecord(PcodeOp *op,int4 path,const CircleRange &rng,Varnode *v); ///< Constructor + GuardRecord(PcodeOp *bOp,PcodeOp *rOp,int4 path,const CircleRange &rng,Varnode *v); ///< Constructor PcodeOp *getBranch(void) const { return cbranch; } ///< Get the CBRANCH associated with \b this guard + PcodeOp *getReadOp(void) const { return readOp; } ///< Get the PcodeOp immediately causing the restriction int4 getPath(void) const { return indpath; } ///< Get the specific path index going towards the switch const CircleRange &getRange(void) const { return range; } ///< Get the range of values causing the switch path to be taken void clear(void) { cbranch = (PcodeOp *)0; } ///< Mark \b this guard as unused int4 valueMatch(Varnode *vn2,Varnode *baseVn2,int4 bitsPreserved2) const; static int4 oneOffMatch(PcodeOp *op1,PcodeOp *op2); - static Varnode *quasiCopy(Varnode *vn,int4 &bitsPreserved,bool noWholeValue); + static Varnode *quasiCopy(Varnode *vn,int4 &bitsPreserved); }; /// \brief An iterator over values a switch variable can take @@ -356,6 +359,8 @@ protected: void findSmallestNormal(uint4 matchsize); void findNormalized(Funcdata *fd,BlockBasic *rootbl,int4 pathout,uint4 matchsize,uint4 maxtablesize); void markFoldableGuards(); + void markModel(bool val); ///< Mark (or unmark) all PcodeOps involved in the model + bool flowsOnlyToModel(Varnode *vn,PcodeOp *trailOp); ///< Check if the given Varnode flows to anything other than \b this model /// \brief Eliminate the given guard to \b this switch ///