diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc index d6d7f8b10e..1e175ae436 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc @@ -53,18 +53,44 @@ void LoadTable::decode(Decoder &decoder) decoder.closeElement(elemId); } -/// We assume the list of LoadTable entries is sorted and perform an in-place -/// collapse of any sequences into a single LoadTable entry. +/// Sort the entries and collapse any contiguous sequences into a single LoadTable entry. /// \param table is the list of entries to collapse void LoadTable::collapseTable(vector &table) { if (table.empty()) return; - vector::iterator iter,lastiter; + + // Test if the table is already sorted and contiguous entries + bool issorted = true; + vector::iterator iter = table.begin(); + int4 num = (*iter).num; + int4 size = (*iter).size; + Address nextaddr = (*iter).addr + size; + ++iter; + + for(;iter!=table.end();++iter) { + if ( (*iter).addr == nextaddr && (*iter).size == size) { + num += (*iter).num; + nextaddr = (*iter).addr + (*iter).size; + } + else { + issorted = false; + break; + } + } + if (issorted) { + // Table is sorted and contiguous. + table.resize(1); // Truncate everything but the first entry + table.front().num = num; + return; + } + + sort(table.begin(),table.end()); + int4 count = 1; iter = table.begin(); - lastiter = iter; - Address nextaddr = (*iter).addr + (*iter).size * (*iter).num; + vector::iterator lastiter = iter; + nextaddr = (*iter).addr + (*iter).size * (*iter).num; ++iter; for(;iter!=table.end();++iter) { if (( (*iter).addr == nextaddr ) && ((*iter).size == (*lastiter).size)) { @@ -85,12 +111,12 @@ void LoadTable::collapseTable(vector &table) void EmulateFunction::executeLoad(void) { - if (collectloads) { + if (loadpoints != (vector *)0) { uintb off = getVarnodeValue(currentOp->getIn(1)); AddrSpace *spc = currentOp->getIn(0)->getSpaceFromConst(); off = AddrSpace::addressToByte(off,spc->getWordSize()); int4 sz = currentOp->getOut()->getSize(); - loadpoints.push_back(LoadTable(Address(spc,off),sz)); + loadpoints->push_back(LoadTable(Address(spc,off),sz)); } EmulatePcodeOp::executeLoad(); } @@ -133,7 +159,7 @@ EmulateFunction::EmulateFunction(Funcdata *f) : EmulatePcodeOp(f->getArch()) { fd = f; - collectloads = false; + loadpoints = (vector *)0; } void EmulateFunction::setExecuteAddress(const Address &addr) @@ -225,39 +251,6 @@ uintb EmulateFunction::emulatePath(uintb val,const PathMeld &pathMeld, return getVarnodeValue(invn); } -/// Pass back any LOAD records collected during emulation. The individual records -/// are sorted and collapsed into concise \e table descriptions. -/// \param res will hold any resulting table descriptions -void EmulateFunction::collectLoadPoints(vector &res) const - -{ - if (loadpoints.empty()) return; - bool issorted = true; - vector::const_iterator iter; - vector::iterator lastiter; - - iter = loadpoints.begin(); - res.push_back( *iter ); // Copy the first entry - ++iter; - lastiter = res.begin(); - - Address nextaddr = (*lastiter).addr + (*lastiter).size; - for(;iter!=loadpoints.end();++iter) { - if (issorted && (( (*iter).addr == nextaddr ) && ((*iter).size == (*lastiter).size))) { - (*lastiter).num += (*iter).num; - nextaddr = (*iter).addr + (*iter).size; - } - else { - issorted = false; - res.push_back( *iter ); - } - } - if (!issorted) { - sort(res.begin(),res.end()); - LoadTable::collapseTable(res); - } -} - /// The starting value for the range and the step is preserved. The /// ending value is set so there are exactly the given number of elements /// in the range. @@ -398,8 +391,8 @@ bool JumpModelTrivial::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize, return ((size != 0)&&(size<=matchsize)); } -void JumpModelTrivial::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const - +void JumpModelTrivial::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const { addresstable.clear(); BlockBasic *bl = indop->getParent(); @@ -1423,15 +1416,14 @@ bool JumpBasic::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 m return true; } -void JumpBasic::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const - +void JumpBasic::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const { uintb val,addr; addresstable.clear(); // Clear out any partial recoveries // Build the emulation engine EmulateFunction emul(fd); - if (loadpoints != (vector *)0) - emul.setLoadCollect(true); + emul.setLoadCollect(loadpoints); uintb mask = ~((uintb)0); int4 bit = fd->getArch()->funcptr_align; @@ -1446,10 +1438,10 @@ void JumpBasic::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addr addr = AddrSpace::addressToByte(addr,spc->getWordSize()); addr &= mask; addresstable.push_back(Address(spc,addr)); + if (loadcounts != (vector *)0) + loadcounts->push_back(loadpoints->size()); notdone = jrange->next(); } - if (loadpoints != (vector *)0) - emul.collectLoadPoints(*loadpoints); } void JumpBasic::findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) @@ -1562,8 +1554,8 @@ bool JumpBasic::foldInGuards(Funcdata *fd,JumpTable *jump) return change; } -bool JumpBasic::sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable) - +bool JumpBasic::sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector &loadpoints,vector *loadcounts) { // Test all the addresses in \b this address table checking // that they are reasonable. We cut off at the first unreasonable address. @@ -1596,6 +1588,9 @@ bool JumpBasic::sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&address if (i!=addresstable.size()) { addresstable.resize(i); jrange->truncate(i); + if (loadcounts != (vector *)0) { + loadpoints.resize((*loadcounts)[i-1]); + } } return true; } @@ -1967,8 +1962,8 @@ bool JumpBasicOverride::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize return true; } -void JumpBasicOverride::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const - +void JumpBasicOverride::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const { addresstable = addrtable; // Addresses are already calculated, just copy them out } @@ -2116,8 +2111,8 @@ bool JumpAssisted::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint return true; } -void JumpAssisted::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const - +void JumpAssisted::buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const { if (userop->getIndex2Addr() == -1) throw LowlevelError("Final index2addr calculation outside of jumpassist"); @@ -2249,8 +2244,11 @@ void JumpTable::recoverModel(Funcdata *fd) /// Check pathological cases when there is only one address in the table, if we find /// this, throw the JumptableThunkError. Let the model run its sanity check. /// Print a warning if the sanity check truncates the original address table. +/// Passing in \b loadcounts indicates that LOADs were collected in \b loadpoints, which may +/// need to be truncated as well. /// \param fd is the function containing the switch -void JumpTable::sanityCheck(Funcdata *fd) +/// \param loadcounts (if non-null) associates each switch value with the number of LOADs it needed +void JumpTable::sanityCheck(Funcdata *fd,vector *loadcounts) { if (jmodel->isOverride()) @@ -2277,7 +2275,7 @@ void JumpTable::sanityCheck(Funcdata *fd) throw JumptableThunkError("Likely thunk"); } } - if (!jmodel->sanityCheck(fd,indirect,addresstable)) { + if (!jmodel->sanityCheck(fd,indirect,addresstable,loadpoints,loadcounts)) { ostringstream err; err << "Jumptable at " << opaddress << " did not pass sanity check."; throw LowlevelError(err.str()); @@ -2592,11 +2590,16 @@ void JumpTable::recoverAddresses(Funcdata *fd) } // if (sz < 2) // fd->warning("Jumptable has only one branch",opaddress); - if (collectloads) - jmodel->buildAddresses(fd,indirect,addresstable,&loadpoints); - else - jmodel->buildAddresses(fd,indirect,addresstable,(vector *)0); - sanityCheck(fd); + if (collectloads) { + vector loadcounts; + jmodel->buildAddresses(fd,indirect,addresstable,&loadpoints,&loadcounts); + sanityCheck(fd,&loadcounts); + LoadTable::collapseTable(loadpoints); + } + else { + jmodel->buildAddresses(fd,indirect,addresstable,(vector *)0,(vector *)0); + sanityCheck(fd,(vector *)0); + } } /// Do a normal recoverAddresses, but save off the old JumpModel, and if we fail recovery, put back the old model. @@ -2683,7 +2686,7 @@ bool JumpTable::recoverLabels(Funcdata *fd) else { jmodel = new JumpModelTrivial(this); jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size); - jmodel->buildAddresses(fd,indirect,addresstable,(vector *)0); + jmodel->buildAddresses(fd,indirect,addresstable,(vector *)0,(vector *)0); trivialSwitchOver(); jmodel->buildLabels(fd,addresstable,label,origmodel); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh index d3b1f4fbe4..8b77a12318 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh @@ -113,8 +113,7 @@ public: class EmulateFunction : public EmulatePcodeOp { Funcdata *fd; ///< The function being emulated map varnodeMap; ///< Light-weight memory state based on Varnodes - bool collectloads; ///< Set to \b true if the emulator collects individual LOAD addresses - vector loadpoints; ///< The set of collected LOAD records + vector *loadpoints; ///< The set of collected LOAD records (if non-null) virtual void executeLoad(void); virtual void executeBranch(void); virtual void executeBranchind(void); @@ -124,12 +123,11 @@ class EmulateFunction : public EmulatePcodeOp { virtual void fallthruOp(void); public: EmulateFunction(Funcdata *f); ///< Constructor - void setLoadCollect(bool val) { collectloads = val; } ///< Set whether we collect LOAD information + void setLoadCollect(vector *val) { loadpoints = val; } ///< Set where/if we collect LOAD information virtual void setExecuteAddress(const Address &addr); virtual uintb getVarnodeValue(Varnode *vn) const; virtual void setVarnodeValue(Varnode *vn,uintb val); uintb emulatePath(uintb val,const PathMeld &pathMeld,PcodeOp *startop,Varnode *startvn); - void collectLoadPoints(vector &res) const; ///< Recover any LOAD table descriptions }; class FlowInfo; @@ -271,7 +269,9 @@ public: /// \param indop is the root BRANCHIND of the switch /// \param addresstable will hold the list of Addresses /// \param loadpoints if non-null will hold LOAD table information used by the model - virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const=0; + /// \param loadcounts if non-null will hold number of LOADs per switch value + virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const=0; /// \brief Recover the unnormalized switch variable /// @@ -316,11 +316,16 @@ public: /// Individual addresses are checked against the function or its program to determine /// if they are reasonable. This method can optionally remove addresses from the table. /// If it does so, the underlying model is changed to reflect the removal. + /// Passing in \b loadcounts indicates that LOAD addresses were collected in \b loadpoints, + /// which may need to have elements removed as well. /// \param fd is the function containing the switch /// \param indop is the root BRANCHIND of the switch /// \param addresstable is the list of recovered Addresses, which may be modified + /// \param loadpoints are any LOAD addresses associated with the table + /// \param loadcounts (if non-null) associates each switch value with the count of LOADs used /// \return \b true if there are (at least some) reasonable addresses in the table - virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable)=0; + virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector &loadpoints,vector *loadcounts)=0; virtual JumpModel *clone(JumpTable *jt) const=0; ///< Clone \b this model @@ -351,12 +356,14 @@ public: virtual bool isOverride(void) const { return false; } virtual int4 getTableSize(void) const { return size; } virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize); - virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const; + virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const; virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {} virtual void buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const; virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop) { return (Varnode *)0; } virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; } - virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable) { return true; } + virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector &loadpoints,vector *loadcounts) { return true; } virtual JumpModel *clone(JumpTable *jt) const; }; @@ -408,12 +415,14 @@ public: virtual bool isOverride(void) const { return false; } virtual int4 getTableSize(void) const { return jrange->getSize(); } virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize); - virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const; + virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const; virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext); virtual void buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const; virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop); virtual bool foldInGuards(Funcdata *fd,JumpTable *jump); - virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable); + virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector &loadpoints,vector *loadcounts); virtual JumpModel *clone(JumpTable *jt) const; virtual void clear(void); }; @@ -471,12 +480,14 @@ public: virtual bool isOverride(void) const { return true; } virtual int4 getTableSize(void) const { return addrtable.size(); } virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize); - virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const; + virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const; // findUnnormalized inherited from JumpBasic virtual void buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const; // foldInNormalization inherited from JumpBasic virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; } - virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable) { return true; } + virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector &loadpoints,vector *loadcounts) { return true; } virtual JumpModel *clone(JumpTable *jt) const; virtual void clear(void); virtual void encode(Encoder &encoder) const; @@ -508,12 +519,14 @@ public: virtual bool isOverride(void) const { return false; } virtual int4 getTableSize(void) const { return sizeIndices+1; } virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize); - virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const; + virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector *loadpoints,vector *loadcounts) const; virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {} virtual void buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const; virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop); virtual bool foldInGuards(Funcdata *fd,JumpTable *jump); - virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable) { return true; } + virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable, + vector &loadpoints,vector *loadcounts) { return true; } virtual JumpModel *clone(JumpTable *jt) const; virtual void clear(void) { assistOp = (PcodeOp *)0; switchvn = (Varnode *)0; } }; @@ -563,7 +576,7 @@ private: bool collectloads; ///< Set to \b true if information about in-memory model data is/should be collected void recoverModel(Funcdata *fd); ///< Attempt recovery of the jump-table model void trivialSwitchOver(void); ///< Switch \b this table over to a trivial model - void sanityCheck(Funcdata *fd); ///< Perform sanity check on recovered address targets + void sanityCheck(Funcdata *fd,vector *loadpoints); ///< Perform sanity check on recovered address targets int4 block2Position(const FlowBlock *bl) const; ///< Convert a basic-block to an out-edge index from the switch. static bool isReachable(PcodeOp *op); ///< Check if the given PcodeOp still seems reachable in its function public: