diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc index 783eff2f2e..985262f383 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc @@ -696,6 +696,34 @@ int4 mostsigbit_set(uintb val) return res; } +/// Count the number of more significant zero bits before the most significant +/// one bit in the representation of the given value; +/// \param val is the given value +/// \return the number of zero bits +int4 count_leading_zeros(uintb val) + +{ + if (val == 0) + return 8*sizeof(uintb); + uintb mask = ~((uintb)0); + int4 maskSize = 4*sizeof(uintb); + mask &= (mask << maskSize); + int4 bit = 0; + + do { + if ((mask & val)==0) { + bit += maskSize; + maskSize >>= 1; + mask |= (mask >> maskSize); + } + else { + maskSize >>= 1; + mask &= (mask << maskSize); + } + } while(maskSize != 0); + return bit; +} + /// Return smallest number of form 2^n-1, bigger or equal to the given value /// \param val is the given value /// \return the mask diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh index 868bb479c5..db8cd5abe7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh @@ -482,7 +482,7 @@ inline uintb pcode_left(uintb val,int4 sa) { return val << sa; } -extern bool signbit_negative(uintb val,int4 size); ///< Return true if the sign-big is set +extern bool signbit_negative(uintb val,int4 size); ///< Return true if the sign-bit is set extern uintb calc_mask(int4 size); ///< Calculate a mask for a given byte size extern uintb uintb_negate(uintb in,int4 size); ///< Negate the \e sized value extern uintb sign_extend(uintb in,int4 sizein,int4 sizeout); ///< Sign-extend a value between two byte sizes @@ -493,7 +493,8 @@ extern void byte_swap(intb &val,int4 size); ///< Swap bytes in the given value extern uintb byte_swap(uintb val,int4 size); ///< Return the given value with bytes swapped extern int4 leastsigbit_set(uintb val); ///< Return index of least significant bit set in given value -extern int4 mostsigbit_set(uintb val); ///< Return index of most significant bit set in given val +extern int4 mostsigbit_set(uintb val); ///< Return index of most significant bit set in given value +extern int4 count_leading_zeros(uintb val); ///< Return the number of leading zero bits in the given value extern uintb coveringmask(uintb val); ///< Return a mask that \e covers the given value extern int4 bit_transitions(uintb val,int4 sz); ///< Calculate the number of bit transitions in the sized value diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc index 52a585f608..c1cdbb2725 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc @@ -369,6 +369,31 @@ bool FlowBlock::dominates(const FlowBlock *subBlock) const return false; } +/// \brief Check if the condition from the given block holds for \b this block +/// +/// We assume the given block has 2 out-edges and that \b this block is immediately reached by +/// one of these two edges. Some condition holds when traversing the out-edge to \b this, and the complement +/// of the condition holds for traversing the other out-edge. We verify that the condition holds for +/// this entire block. More specifically, we check that that there is no path to \b this through the +/// sibling edge, where the complement of the condition holds (unless we loop back through the conditional block). +/// \param cond is the conditional block with 2 out-edges +/// \return \b true if the condition holds for this block +bool FlowBlock::restrictedByConditional(const FlowBlock *cond) const + +{ + if (sizeIn() == 1) return true; // Its impossible for any path to come through sibling to this + if (getImmedDom() != cond) return false; // This is not dominated by conditional block at all + for(int4 i=0;igetImmedDom(); + } + } + return true; +} + /// \return \b true if \b this is the top of a loop bool FlowBlock::hasLoopIn(void) const diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh index 16d5945441..053f67f38c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh @@ -215,6 +215,7 @@ public: FlowBlock *getFrontLeaf(void); ///< Get the first leaf FlowBlock int4 calcDepth(const FlowBlock *leaf) const; ///< Get the depth of the given component FlowBlock bool dominates(const FlowBlock *subBlock) const; ///< Does \b this block dominate the given block + bool restrictedByConditional(const FlowBlock *cond) const; int4 sizeOut(void) const { return outofthis.size(); } ///< Get the number of out edges int4 sizeIn(void) const { return intothis.size(); } ///< Get the number of in edges bool hasLoopIn(void) const; ///< Is there a looping edge coming into \b this block diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index f6ac48691e..d5107c1dbc 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -3246,7 +3246,7 @@ int4 ActionConditionalConst::apply(Funcdata &data) if (flipEdge) constEdge = 1 - constEdge; FlowBlock *constBlock = bl->getOut(constEdge); - if (constBlock->sizeIn() != 1) continue; // Must only be one path to constant block directly through CBRANCH + if (!constBlock->restrictedByConditional(bl)) continue; // Make sure condition holds propagateConstant(varVn,constVn,constBlock,data); } return 0; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc index af165006a9..e579224f19 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc @@ -129,6 +129,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status) status->registerCom(new IfcVolatile(),"volatile"); status->registerCom(new IfcPreferSplit(),"prefersplit"); status->registerCom(new IfcStructureBlocks(),"structure","blocks"); + status->registerCom(new IfcAnalyzeRange(), "analyze","range"); #ifdef CPUI_RULECOMPILE status->registerCom(new IfcParseRule(),"parse","rule"); status->registerCom(new IfcExperimentalRules(),"experimental","rules"); @@ -2474,6 +2475,56 @@ void IfcCountPcode::execute(istream &s) *status->optr << "Count - pcode = " << dec << count << endl; } +void IfcAnalyzeRange::execute(istream &s) + +{ + if (dcp->conf == (Architecture *)0) + throw IfaceExecutionError("Image not loaded"); + if (dcp->fd == (Funcdata *)0) + throw IfaceExecutionError("No function selected"); + + bool useFullWidener; + string token; + s >> ws >> token; + if (token == "full") + useFullWidener = true; + else if (token == "partial") { + useFullWidener = false; + } + else + throw IfaceParseError("Must specify \"full\" or \"partial\" widening"); + Varnode *vn = iface_read_varnode(dcp,s); + vector sinks; + vector reads; + sinks.push_back(vn); + for(list::const_iterator iter=vn->beginDescend();iter!=vn->endDescend();++iter) { + PcodeOp *op = *iter; + if (op->code() == CPUI_LOAD || op->code() == CPUI_STORE) + reads.push_back(op); + } + Varnode *stackReg = dcp->fd->findSpacebaseInput(dcp->conf->getStackSpace()); + ValueSetSolver vsSolver; + vsSolver.establishValueSets(sinks, reads, stackReg, false); + if (useFullWidener) { + WidenerFull widener; + vsSolver.solve(10000,widener); + } + else { + WidenerNone widener; + vsSolver.solve(10000,widener); + } + list::const_iterator iter; + for(iter=vsSolver.beginValueSets();iter!=vsSolver.endValueSets();++iter) { + (*iter).printRaw(*status->optr); + *status->optr << endl; + } + map::const_iterator riter; + for(riter=vsSolver.beginValueSetReads();riter!=vsSolver.endValueSetReads();++riter) { + (*riter).second.printRaw(*status->optr); + *status->optr << endl; + } +} + #ifdef OPACTION_DEBUG void IfcDebugAction::execute(istream &s) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh index f7b16fc1e5..95c1622381 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh @@ -541,6 +541,11 @@ public: virtual void execute(istream &s); }; +class IfcAnalyzeRange : public IfaceDecompCommand { +public: + virtual void execute(istream &s); +}; + #ifdef CPUI_RULECOMPILE class IfcParseRule : public IfaceDecompCommand { public: diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc index 821eba0d49..76d68ea210 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc @@ -231,11 +231,19 @@ void EmulateFunction::collectLoadPoints(vector &res) const } } +/// 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. +/// \param nm is the given number void JumpValuesRange::truncate(int4 nm) { - // FIXME: This doesn't work if there is a stride - range = CircleRange(range.getMin(),range.getMin() + (nm-1),range.getMask()); + int4 rangeSize = 8*sizeof(uintb) - count_leading_zeros(range.getMask()); + rangeSize >>= 3; + uintb left = range.getMin(); + int4 step = range.getStep(); + uintb right = (left + step * nm) & range.getMask(); + range.setRange(left, right, rangeSize, step); } uintb JumpValuesRange::getSize(void) const @@ -403,18 +411,21 @@ bool JumpBasic::ispoint(Varnode *vn) return true; } -void JumpBasic::setStride(Varnode *vn,CircleRange &rng) +/// If the some of the least significant bits of the given Varnode are known to +/// be zero, translate this into a stride for the jumptable range. +/// \param vn is the given Varnode +/// \return the calculated stride = 1,2,4,... +int4 JumpBasic::getStride(Varnode *vn) { uintb mask = vn->getNZMask(); - int4 stride = 0; + int4 stride = 1; while((mask&1)==0) { mask >>= 1; - stride += 1; + stride <<= 1; } - if (stride==0) return; - if (stride > 6) return; - rng.setStride(stride); + if (stride > 32) return 1; + return stride; } uintb JumpBasic::backup2Switch(Funcdata *fd,uintb output,Varnode *outvn,Varnode *invn) @@ -915,23 +926,25 @@ void JumpBasic::calcRange(Varnode *vn,CircleRange &rng) const // by using the precalculated guard ranges. // Get an initial range, based on the size/type of -vn- + int4 stride = 1; if (vn->isConstant()) rng = CircleRange(vn->getOffset(),vn->getSize()); else if (vn->isWritten() && vn->getDef()->isBoolOutput()) - rng = CircleRange(0,1,1); // Only 0 or 1 possible + rng = CircleRange(0,2,1,1); // Only 0 or 1 possible else { // Should we go ahead and use nzmask in all cases? - uintb mask = calc_mask(vn->getSize()); + uintb maxValue = 0; // Every possible value if (vn->isWritten()) { PcodeOp *andop = vn->getDef(); if (andop->code() == CPUI_INT_AND) { Varnode *constvn = andop->getIn(1); if (constvn->isConstant()) { - mask = coveringmask( constvn->getOffset() ); + maxValue = coveringmask( constvn->getOffset() ); + maxValue = (maxValue + 1) & calc_mask(vn->getSize()); } } } - rng = CircleRange(0,mask,mask); - setStride(vn,rng); + stride = getStride(vn); + rng = CircleRange(0,maxValue,vn->getSize(),stride); } // Intersect any guard ranges which apply to -vn- @@ -950,7 +963,7 @@ void JumpBasic::calcRange(Varnode *vn,CircleRange &rng) const // in which case the guard might not check for it. If the // size is too big, we try only positive values if (rng.getSize() > 0x10000) { - CircleRange positive(0,rng.getMask()>>1,rng.getMask()); + CircleRange positive(0,(rng.getMask()>>1)+1,vn->getSize(),stride); positive.intersect(rng); if (!positive.isEmpty()) rng = positive; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh index f3fc7465b5..b8da3ea37b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh @@ -145,7 +145,7 @@ public: void setRange(const CircleRange &rng) { range = rng; } void setStartVn(Varnode *vn) { normqvn = vn; } void setStartOp(PcodeOp *op) { startop = op; } - virtual void truncate(int4 nm); + virtual void truncate(int4 nm); ///< Truncate the number of values to the given number virtual uintb getSize(void) const; virtual bool contains(uintb val) const; virtual bool initializeForReading(void) const; @@ -233,7 +233,7 @@ protected: Varnode *switchvn; // The unnormalized switch varnode static bool isprune(Varnode *vn); static bool ispoint(Varnode *vn); - static void setStride(Varnode *vn,CircleRange &rng); + static int4 getStride(Varnode *vn); ///< Get the step/stride associated with the Varnode static uintb backup2Switch(Funcdata *fd,uintb output,Varnode *outvn,Varnode *invn); void findDeterminingVarnodes(PcodeOp *op,int4 slot); void analyzeGuards(BlockBasic *bl,int4 pathout); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc index 53908b6edc..566a2f926d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc @@ -14,19 +14,21 @@ * limitations under the License. */ #include "rangeutil.hh" +#include "block.hh" const char CircleRange::arrange[] = "gcgbegdagggggggeggggcgbggggggggcdfgggggggegdggggbgggfggggcgbegda"; -void CircleRange::calcStepShift(void) +/// All the instantiations where left == right represent the same set. We +/// normalize the representation so we can compare sets easily. +void CircleRange::normalize(void) { - shift = 0; - step = 1; - uintb maskcopy = mask; - while((maskcopy & 1)==0) { - maskcopy >>= 1; - shift += 1; - step <<= 1; + if (left == right) { + if (step != 1) + left = left % step; + else + left = 0; + right = left; } } @@ -50,22 +52,25 @@ void CircleRange::complement(void) } /// If the original range contained -/// - 0 and 1 => the new range is everything +/// - 0 and 1 => the new range is [0,2) /// - 0 only => the new range is [0,1) -/// - 1 only => the new range is [1,0) +/// - 1 only => the new range is [1,2) /// - neither 0 or 1 => the new range is empty -void CircleRange::convertToBoolean(void) +/// +/// \return \b true if the range contains both 0 and 1 +bool CircleRange::convertToBoolean(void) { - if (isempty) return; + if (isempty) return false; bool contains_zero = contains(0); bool contains_one = contains(1); - mask = 1; + mask = 0xff; step = 1; - shift = 0; if (contains_zero && contains_one) { - left = right = 0; + left = 0; + right = 2; isempty = false; + return true; } else if (contains_zero) { left = 0; @@ -74,63 +79,108 @@ void CircleRange::convertToBoolean(void) } else if (contains_one) { left = 1; - right = 0; + right = 2; isempty = false; } else isempty = true; + return false; } -/// Given a range mask, restrict a left/right specified range to a new size and stride. -/// This assumes the specified range is not empty. -/// \param newmask is the mask encoding the new size and stride +/// \brief Recalculate range based on new stride +/// +/// Restrict a left/right specified range to a new stride, given the step and +/// remainder it needs to match. This assumes the specified range is not empty. +/// \param mask is the domain mask +/// \param step is the new stride +/// \param oldStep is the original step (always smaller) +/// \param rem is the given remainder to match /// \param myleft is a reference to the left boundary of the specified range /// \param myright is a reference to the right boundary of the specified range /// \return \b true if result is empty -bool CircleRange::newStride(uintb newmask,uintb &myleft,uintb &myright) +bool CircleRange::newStride(uintb mask,int4 step,int4 oldStep,uint4 rem,uintb &myleft,uintb &myright) { - if (myleft > newmask) { - if (myright > newmask) { // Both bounds out of range of newmask - if (myleft < myright) return true; // Old range is completely out of bounds of new mask - myleft = 0; - myright = 0; // Old range contained everything in newmask - return false; - } - myleft = 0; // Take everything up to left edge of new range + if (oldStep != 1) { + uint4 oldRem = (uint4)(myleft % oldStep); + if (oldRem != (rem % oldStep)) + return true; // Step is completely off } - if (myright > newmask) { - myright = 0; // Take everything up to right edge of new range - } - if (myleft == myright) { - myleft = 0; // Normalize the everything - myright = 0; - return false; - } - uintb tmp = (myleft + (~newmask)) & newmask; // Bump boundaries up to proper stride post - uintb tmpr = (myright + (~newmask)) & newmask; - if (tmpr==tmp) { // If the bounds are now equal - if (tmp!=myleft) return true; // We cut elements out from left end and now nothing is left - myleft = 0; - myright = 0; // Or all missing missing elements in old range, were off-stride, - return false; // and now we have everything - } - myleft = tmp; - myright = tmpr; + bool origOrder = (myleft < myright); + uint4 leftRem = (uint4)(myleft % step); + uint4 rightRem = (uint4)(myright % step); + if (leftRem > rem) + myleft += rem + step - leftRem; + else + myleft += rem - leftRem; + + if (rightRem > rem) + myright += rem + step - rightRem; + else + myright += rem - rightRem; + myleft &= mask; + myright &= mask; + + bool newOrder = (myleft < myright); + if (origOrder != newOrder) + return true; + return false; // not empty } -/// Give specific left/right boundaries and a mask. -/// \param mn is the left boundary of the range -/// \param mx is the right boundary of the range -/// \param m is the mask encoding a size and stride -CircleRange::CircleRange(uintb mn,uintb mx,uintb m) +/// \brief Make \b this range fit in a new domain +/// +/// Truncate any part of the range outside of the new domain. +/// If the original range is completely outside of the new domain, +/// return \b true (empty). Step information is preserved. +/// \param newMask is the mask for the new domain +/// \param newStep is the step associated with the range +/// \param myleft is a reference to the left edge of the range to fit +/// \param myright is a reference to the right edge of the range to fit +/// \return \b true if the truncated domain is empty +bool CircleRange::newDomain(uintb newMask,int4 newStep,uintb &myleft,uintb &myright) { - mask = m; - calcStepShift(); - left = mn; - right = (mx+step)&mask; + uintb rem; + if (newStep != 1) + rem = myleft % newStep; + else + rem = 0; + if (myleft > newMask) { + if (myright > newMask) { // Both bounds out of range of newMask + if (myleft < myright) return true; // Old range is completely out of bounds of new mask + myleft = rem; + myright = rem; // Old range contained everything in newMask + return false; + } + myleft = rem; // Take everything up to left edge of new range + } + if (myright > newMask) { + myright = rem; // Take everything up to right edge of new range + } + if (myleft == myright) { + myleft = rem; // Normalize the everything + myright = rem; + } + return false; // not empty +} + +/// Give specific left/right boundaries and step information. +/// The first element in the set is given left boundary. The sequence +/// then proceeds by the given \e step up to (but not including) the given +/// right boundary. Care should be taken to make sure the remainders of the +/// left and right boundaries modulo the step are equal. +/// \param lft is the left boundary of the range +/// \param rgt is the right boundary of the range +/// \param size is the domain size in bytes (1,2,4,8,..) +/// \param stp is the desired step (1,2,4,8,..) +CircleRange::CircleRange(uintb lft,uintb rgt,int4 size,int4 stp) + +{ + mask = calc_mask(size); + step = stp; + left = lft; + right = rgt; isempty = false; } @@ -139,11 +189,10 @@ CircleRange::CircleRange(uintb mn,uintb mx,uintb m) CircleRange::CircleRange(bool val) { - left = val ? 1: 0; - mask = 1; + mask = 0xff; step = 1; - shift = 0; - right = val ? 0 : 1; + left = val ? 1: 0; + right = val + 1; isempty = false; } @@ -155,13 +204,52 @@ CircleRange::CircleRange(uintb val,int4 size) { mask = calc_mask(size); - shift = 0; step = 1; left = val; right = (left+1)&mask; isempty = false; } +/// \param lft is the left boundary of the range +/// \param rgt is the right boundary of the range +/// \param size is the size of the range domain in bytes +/// \param stp is the step/stride of the range +void CircleRange::setRange(uintb lft,uintb rgt,int4 size,int4 stp) + +{ + mask = calc_mask(size); + left = lft; + right = rgt; + step = stp; + isempty = false; +} + +/// A size specifies the number of bytes (*8 to get number of bits) in the mask. +/// The stride is assumed to be 1. +/// \param val is is the single value +/// \param size is the size of the mask in bytes +void CircleRange::setRange(uintb val,int4 size) + +{ + mask = calc_mask(size); + step = 1; + left = val; + right = (left+1)&mask; + isempty = false; +} + +/// Make a range of values that holds everything. +/// \param size is the size (in bytes) of the range +void CircleRange::setFull(int4 size) + +{ + mask = calc_mask(size); + step = 1; + left = 0; + right = 0; + isempty = false; +} + /// \return the number of integers contained in this range uintb CircleRange::getSize(void) const @@ -169,13 +257,13 @@ uintb CircleRange::getSize(void) const if (isempty) return 0; uintb val; if (left < right) - val = (right-left) >> shift; + val = (right-left) / step; else { - val = (mask - (left-right) + step) >> shift; + val = (mask - (left-right) + step) / step; if (val == 0) { // This is an overflow, when all uintb values are in the range val = mask; // We lie by one, which shouldn't matter for our jumptable application - if (shift > 0) { - val = val >> shift; + if (step > 1) { + val = val / step; val += 1; } } @@ -183,6 +271,29 @@ uintb CircleRange::getSize(void) const return val; } +/// In this context, the information content of a value is the index (+1) of the +/// most significant non-zero bit (of the absolute value). This routine returns +/// the maximum information across all values in the range. +/// \return the maximum information +int4 CircleRange::getMaxInfo(void) const + +{ + uintb halfPoint = mask ^ (mask >> 1); + if (contains(halfPoint)) + return 8*sizeof(uintb) - count_leading_zeros(halfPoint); + int4 sizeLeft,sizeRight; + if ((halfPoint & left) == 0) + sizeLeft = count_leading_zeros(left); + else + sizeLeft = count_leading_zeros(~left & mask); + if ((halfPoint & right) == 0) + sizeRight = count_leading_zeros(right); + else + sizeRight = count_leading_zeros(~right & mask); + int4 size1 = 8*sizeof(uintb) - (sizeRight < sizeLeft ? sizeRight : sizeLeft); + return size1; +} + /// \param op2 is the specific range to test for containment. /// \return \b true if \b true contains the interval \b op2 bool CircleRange::contains(const CircleRange &op2) const @@ -192,7 +303,7 @@ bool CircleRange::contains(const CircleRange &op2) const return op2.isempty; if (op2.isempty) return true; - if (shift > op2.shift) return false; // Cannot be containment because step is wrong + if (step > op2.step) return false; // Cannot be containment because step is wrong if (left == right) return true; if (op2.left == op2.right) return false; @@ -211,7 +322,10 @@ bool CircleRange::contains(uintb val) const { if (isempty) return false; - if ((mask & val)!=val) return false; // Step is wrong + if (step != 1) { + if ((left % step)!=(val%step)) + return false; // Phase is wrong + } if (left < right) { if (val < left) return false; if (right <= val) return false; @@ -235,60 +349,183 @@ int4 CircleRange::circleUnion(const CircleRange &op2) { if (op2.isempty) return 0; if (isempty) { - left = op2.left; - right = op2.right; - isempty = op2.isempty; - mask = op2.mask; - step = op2.step; - shift = op2.shift; + *this = op2; return 0; } - if (mask != op2.mask) return 2; // Cannot do proper union with different strides - if ((left==right)||(op2.left==op2.right)) { - left = 0; - right = 0; + if (mask != op2.mask) return 2; // Cannot do proper union with different domains + uintb aRight = right; + uintb bRight = op2.right; + int4 newStep = step; + if (step < op2.step) { + if (isSingle()) { + newStep = op2.step; + aRight = (left + newStep) & mask; + } + else + return 2; + } + else if (op2.step < step) { + if (op2.isSingle()) { + newStep = step; + bRight = (op2.left + newStep) & mask; + } + else + return 2; + } + uintb rem; + if (newStep != 1) { + rem = left % newStep; + if (rem != (op2.left % newStep)) + return 2; + } + else + rem = 0; + if ((left==aRight)||(op2.left==bRight)) { + left = rem; + right = rem; + step = newStep; return 0; } - char overlapCode = encodeRangeOverlaps(left, right, op2.left, op2.right); + char overlapCode = encodeRangeOverlaps(left, aRight, op2.left, bRight); switch(overlapCode) { case 'a': // order (l r op2.l op2.r) case 'f': // order (op2.l op2.r l r) - if (right==op2.left) { - // left = left; - right = op2.right; + if (aRight==op2.left) { + right = bRight; + step = newStep; return 0; } - if (left==op2.right) { + if (left==bRight) { left = op2.left; - // right = right; + right = aRight; + step = newStep; return 0; } return 2; // 2 pieces; case 'b': // order (l op2.l r op2.r) - // left = left; - right = op2.right; + right = bRight; + step = newStep; return 0; case 'c': // order (l op2.l op2.r r) - // left = left; - // right = right; + right = aRight; + step = newStep; return 0; case 'd': // order (op2.l l r op2.r) left = op2.left; - right = op2.right; + right = bRight; + step = newStep; return 0; case 'e': // order (op2.l l op2.r r) left = op2.left; - // right = right; + right = aRight; + step = newStep; return 0; case 'g': // either impossible or covers whole circle - left = 0; - right = 0; + left = rem; + right = rem; + step = newStep; return 0; // entire circle is covered } return -1; // Never reach here } +/// Turn \b this into a range that contains both the original range and +/// the other given range. The resulting range may contain values that were in neither +/// of the original ranges (not a strict union). But the number of added values will be +/// minimal. This method will create a range with step if the input ranges hold single values +/// and the distance between them is a power of 2 and less or equal than a given bound. +/// \param op2 is the other given range to combine with \b this +/// \param maxStep is the step bound that can be induced for a container with two singles +/// \return \b true if the container is everything (full) +bool CircleRange::minimalContainer(const CircleRange &op2,int4 maxStep) + +{ + if (isSingle() && op2.isSingle()) { + uintb min,max; + if (getMin() < op2.getMin()) { + min = getMin(); + max = op2.getMin(); + } + else { + min = op2.getMin(); + max = getMin(); + } + uintb diff = max - min; + if (diff > 0 && diff <= maxStep) { + if (leastsigbit_set(diff) == mostsigbit_set(diff)) { + step = (int4) diff; + left = min; + right = (max + step) & mask; + return false; + } + } + } + + uintb aRight = right - step + 1; // Treat original ranges as having step=1 + uintb bRight = op2.right - op2.step + 1; + step = 1; + mask |= op2.mask; + uintb vacantSize1,vacantSize2; + + char overlapCode = encodeRangeOverlaps(left, aRight, op2.left, bRight); + switch(overlapCode) { + case 'a': // order (l r op2.l op2.r) + vacantSize1 = left + (mask - bRight) + 1; + vacantSize2 = op2.left - aRight; + if (vacantSize1 < vacantSize2) { + left = op2.left; + right = aRight; + } + else { + right = bRight; + } + break; + case 'f': // order (op2.l op2.r l r) + vacantSize1 = op2.left + (mask-aRight) + 1; + vacantSize2 = left - bRight; + if (vacantSize1 < vacantSize2) { + right = bRight; + } + else { + left = op2.left; + right = aRight; + } + break; + case 'b': // order (l op2.l r op2.r) + right = bRight; + break; + case 'c': // order (l op2.l op2.r r) + right = aRight; + break; + case 'd': // order (op2.l l r op2.r) + left = op2.left; + right = bRight; + break; + case 'e': // order (op2.l l op2.r r) + left = op2.left; + right = aRight; + break; + case 'g': // order (l op2.r op2.l r) + left = 0; // Entire circle is covered + right = 0; + break; + } + normalize(); + return (left == right); +} + +/// Convert range to its complement. The step is automatically converted to 1 first. +/// \return the original step size +int4 CircleRange::invert(void) + +{ + int4 res = step; + step = 1; + complement(); + return res; +} + /// Set \b this to the intersection of \b this and \b op2 as a /// single interval if possible. /// Return 0 if the result is valid @@ -299,26 +536,48 @@ int4 CircleRange::circleUnion(const CircleRange &op2) int4 CircleRange::intersect(const CircleRange &op2) { - int4 retval; - uintb myleft,myright,op2left,op2right; - uintb newmask; - bool myisempty,op2isempty; + int4 retval,newStep; + uintb newMask,myleft,myright,op2left,op2right; if (isempty) return 0; // Intersection with empty is empty if (op2.isempty) { isempty = true; return 0; } - newmask = mask & op2.mask; myleft = left; myright = right; op2left = op2.left; op2right = op2.right; - myisempty = newStride(newmask,myleft,myright); - op2isempty = newStride(newmask,op2left,op2right); - if (myisempty || op2isempty) { - isempty = true; - return 0; + if (step < op2.step) { + newStep = op2.step; + uint4 rem = (uint4)(op2left % newStep); + if (newStride(mask,newStep,step,rem,myleft,myright)) { // Increase the smaller stride + isempty = true; + return 0; + } + } + else if (op2.step < step) { + newStep = step; + uint4 rem = (uint4)(myleft % newStep); + if (newStride(op2.mask,newStep,op2.step,rem,op2left,op2right)) { + isempty = true; + return 0; + } + } + else + newStep = step; + newMask = mask & op2.mask; + if (mask != newMask) { + if (newDomain(newMask,newStep,myleft,myright)) { + isempty = true; + return 0; + } + } + else if (op2.mask != newMask) { + if (newDomain(newMask,newStep,op2left,op2right)) { + isempty = true; + return 0; + } } if (myleft==myright) { // Intersect with this everything left = op2left; @@ -386,11 +645,8 @@ int4 CircleRange::intersect(const CircleRange &op2) } } if (retval != 0) return retval; - if (mask != newmask) { - mask = newmask; - step = op2.step; - shift = op2.shift; - } + mask = newMask; + step = newStep; return 0; } @@ -411,56 +667,320 @@ bool CircleRange::setNZMask(uintb nzmask,int4 size) if (trans == 0) { mask = calc_mask(size); if (hasstep) { // All zeros - shift = 0; step = 1; left = 0; right = 1; // Range containing only zero } else { // All ones - shift = 0; step = 1; left = 0; right = 0; // Everything } return true; } - shift = leastsigbit_set(nzmask); + int4 shift = leastsigbit_set(nzmask); step = 1; step <<= shift; mask = calc_mask(size); - mask = mask & (mask << shift); left = 0; right = (nzmask + step) & mask; return true; } -/// The new stride is specified by giving the number of bits of shift (log2(stride)) -/// \param newshift is the number of bits of shift -void CircleRange::setStride(int4 newshift) +/// This method changes the step for \b this range, i.e. elements are removed. +/// The boundaries of the range do not change except for the remainder modulo the new step. +/// \param newStep is the new step amount +/// \param rem is the desired phase (remainder of the values modulo the step) +void CircleRange::setStride(int4 newStep,uintb rem) { bool iseverything = (!isempty) && (left==right); - if (newshift == shift) return; - if (newshift < shift) { - while(newshift < shift) { - step >>= 1; - mask = mask | (mask >> 1); // Add a bit to mask - shift -= 1; - } - } - else { - while(newshift > shift) { - step <<= 1; - mask = mask & (mask << 1); // Remove a bit from mask - shift += 1; - } - } - left &= mask; - right &= mask; + if (newStep == step) return; + uintb aRight = right - step; + step = newStep; + if (step == 1) return; // No remainder to fill in + uintb curRem = left % step; + left = (left - curRem) + rem; + curRem = aRight % step; + aRight = (aRight - curRem) + rem; + right = aRight + step; if ((!iseverything)&&(left == right)) isempty = true; } +/// \param opc is the OpCode to pull the range back through +/// \param inSize is the storage size in bytes of the resulting input +/// \param outSize is the storage size in bytes of the range to pull-back +/// \return \b true if a valid range is formed in the pull-back +bool CircleRange::pullBackUnary(OpCode opc,int4 inSize,int4 outSize) + +{ + uintb val; + // If there is nothing in the output set, no input will map to it + if (isempty) return true; + + switch(opc) { + case CPUI_BOOL_NEGATE: + if (convertToBoolean()) + break; // Both outputs possible => both inputs possible + left = left ^ 1; // Flip the boolean range + right = left +1; + break; + case CPUI_COPY: + break; // Identity transform on range + case CPUI_INT_2COMP: + val = (~left + 1 + step) & mask; + left = (~right + 1 + step) & mask; + right = val; + break; + case CPUI_INT_ZEXT: + { + val = calc_mask(inSize); // (smaller) input mask + CircleRange zextrange; + zextrange.left = 0; + zextrange.right = val + 1; // Biggest possible range of ZEXT + zextrange.mask = mask; + zextrange.step = step; // Keep the same stride + zextrange.isempty = false; + if (0 != intersect(zextrange)) + return false; + left &= val; + right &= val; + mask &= val; // Preserve the stride + break; + } + case CPUI_INT_SEXT: + { + val = calc_mask(inSize); // (smaller) input mask + CircleRange sextrange; + sextrange.left = val ^ (val >> 1); // High order bit for (small) input space + sextrange.right = sign_extend(sextrange.left, inSize, outSize); + sextrange.mask = mask; + sextrange.step = step; // Keep the same stride + sextrange.isempty = false; + if (sextrange.intersect(*this) != 0) + return false; + else { + if (!sextrange.isEmpty()) + return false; + else { + left &= val; + right &= val; + mask &= val; // Preserve the stride + } + } + break; + } + default: + return false; + } + return true; +} + +/// \param opc is the OpCode to pull the range back through +/// \param val is the constant value of the other input parameter (if present) +/// \param slot is the slot of the input variable whose range gets produced +/// \param inSize is the storage size in bytes of the resulting input +/// \param outSize is the storage size in bytes of the range to pull-back +/// \return \b true if a valid range is formed in the pull-back +bool CircleRange::pullBackBinary(OpCode opc,uintb val,int4 slot,int4 inSize,int4 outSize) + +{ + bool yescomplement; + bool bothTrueFalse; + + // If there is nothing in the output set, no input will map to it + if (isempty) return true; + + switch(opc) { + case CPUI_INT_EQUAL: + bothTrueFalse = convertToBoolean(); + mask = calc_mask(inSize); + if (bothTrueFalse) + break; // All possible outs => all possible ins + yescomplement = (left == 0); + left = val; + right = (val + 1) & mask; + if (yescomplement) + complement(); + break; + case CPUI_INT_NOTEQUAL: + bothTrueFalse = convertToBoolean(); + mask = calc_mask(inSize); + if (bothTrueFalse) break; // All possible outs => all possible ins + yescomplement = (left==0); + left = (val+1)&mask; + right = val; + if (yescomplement) + complement(); + break; + case CPUI_INT_LESS: + bothTrueFalse = convertToBoolean(); + mask = calc_mask(inSize); + if (bothTrueFalse) break; // All possible outs => all possible ins + yescomplement = (left==0); + if (slot==0) { + if (val==0) + isempty = true; // X < 0 is always false + else { + left = 0; + right = val; + } + } + else { + if (val==mask) + isempty = true; // 0xffff < X is always false + else { + left = (val+1)&mask; + right = 0; + } + } + if (yescomplement) + complement(); + break; + case CPUI_INT_LESSEQUAL: + bothTrueFalse = convertToBoolean(); + mask = calc_mask(inSize); + if (bothTrueFalse) break; // All possible outs => all possible ins + yescomplement = (left==0); + if (slot==0) { + left = 0; + right = (val+1)&mask; + } + else { + left = val; + right = 0; + } + if (yescomplement) + complement(); + break; + case CPUI_INT_SLESS: + bothTrueFalse = convertToBoolean(); + mask = calc_mask(inSize); + if (bothTrueFalse) break; // All possible outs => all possible ins + yescomplement = (left==0); + if (slot==0) { + if (val == (mask>>1)+1) + isempty = true; // X < -infinity, is always false + else { + left = (mask >> 1)+1; // -infinity + right = val; + } + } + else { + if ( val == (mask>>1) ) + isempty = true; // infinity < X, is always false + else { + left = (val+1)&mask; + right = (mask >> 1)+1; // -infinity + } + } + if (yescomplement) + complement(); + break; + case CPUI_INT_SLESSEQUAL: + bothTrueFalse = convertToBoolean(); + mask = calc_mask(inSize); + if (bothTrueFalse) break; // All possible outs => all possible ins + yescomplement = (left==0); + if (slot==0) { + left = (mask >> 1)+1; // -infinity + right = (val+1)&mask; + } + else { + left = val; + right = (mask >> 1)+1; // -infinity + } + if (yescomplement) + complement(); + break; + case CPUI_INT_CARRY: + bothTrueFalse = convertToBoolean(); + mask = calc_mask(inSize); + if (bothTrueFalse) break; // All possible outs => all possible ins + yescomplement = (left==0); + if (val==0) + isempty = true; // Nothing carries adding zero + else { + left = ((mask-val)+1)&mask; + right = 0; + } + if (yescomplement) + complement(); + break; + case CPUI_INT_ADD: + left = (left-val)&mask; + right = (right-val)&mask; + break; + case CPUI_INT_SUB: + if (slot==0) { + left = (left+val)&mask; + right = (right+val)&mask; + } + else { + left = (val-left)&mask; + right = (val-right)&mask; + } + break; + case CPUI_INT_RIGHT: + { + if (step == 1) { + uintb rightBound = (calc_mask(inSize) >> val) + 1; // The maximal right bound + if (((left >= rightBound) && (right >= rightBound) && (left >= right)) + || ((left == 0) && (right >= rightBound)) || (left == right)) { + // covers everything in range of shift + left = 0; // So domain is everything + right = 0; + } + else { + if (left > rightBound) + left = rightBound; + if (right > rightBound) + right = 0; + left = (left << val) & mask; + right = (right << val) & mask; + if (left == right) + isempty = true; + } + } + else + return false; + break; + } + case CPUI_INT_SRIGHT: + { + if (step == 1) { + uintb rightb = calc_mask(inSize); + uintb leftb = rightb >> (val + 1); + rightb = leftb ^ rightb; // Smallest negative possible + leftb += 1; // Biggest positive (+1) possible + if (((left >= leftb) && (left <= rightb) && (right >= leftb) + && (right <= rightb) && (left >= right)) || (left == right)) { + // covers everything in range of shift + left = 0; // So domain is everything + right = 0; + } + else { + if ((left > leftb) && (left < rightb)) + left = leftb; + if ((right > leftb) && (right < rightb)) + right = rightb; + left = (left << val) & mask; + right = (right << val) & mask; + if (left == right) + isempty = true; + } + } + else + return false; + break; + } + default: + return false; + } + return true; +} + /// The pull-back is performed through a given p-code \b op and set \b this /// to the resulting range (if possible). /// If there is a single unknown input, and the set of values @@ -481,313 +1001,57 @@ void CircleRange::setStride(int4 newshift) Varnode *CircleRange::pullBack(PcodeOp *op,Varnode **constMarkup,bool usenzmask) { - Varnode *res,*constvn; - uintb val; - bool yescomplement; + Varnode *res; - if ((op->numInput()==0)||(op->numInput()>2)) - return (Varnode *)0; - - // Find non-constant varnode input, and slot - // If there are two inputs, make sure second is constant - int4 slot = 0; - res = op->getIn(slot); - if (op->numInput()==2) - constvn = op->getIn(1-slot); - else - constvn = (Varnode *)0; - if (res->isConstant()) { - if (op->numInput()==1) return (Varnode *)0; - slot = 1; - constvn = res; - res = op->getIn(slot); - if (res->isConstant()) + if (op->numInput() == 1) { + res = op->getIn(0); + if (res->isConstant()) return (Varnode *)0; + if (!pullBackUnary(op->code(),res->getSize(),op->getOut()->getSize())) return (Varnode *)0; } - else if ((op->numInput()==2)&&(!constvn->isConstant())) + else if (op->numInput() == 2) { + Varnode *constvn; + uintb val; + // Find non-constant varnode input, and slot + // Make sure second input is constant + int4 slot = 0; + res = op->getIn(slot); + constvn = op->getIn(1 - slot); + if (res->isConstant()) { + slot = 1; + constvn = res; + res = op->getIn(slot); + if (res->isConstant()) + return (Varnode *) 0; + } + else if (!constvn->isConstant()) + return (Varnode *) 0; + val = constvn->getOffset(); + OpCode opc = op->code(); + if (!pullBackBinary(opc, val, slot, res->getSize(), op->getOut()->getSize())) { + if (usenzmask && opc == CPUI_SUBPIECE && val == 0) { + // If everything we are truncating is known to be zero, we may still have a range + int4 msbset = mostsigbit_set(res->getNZMask()); + msbset = (msbset + 8) / 8; + if (op->getOut()->getSize() < msbset) // Some bytes we are chopping off might not be zero + return (Varnode *) 0; + else { + mask = calc_mask(res->getSize()); // Keep the range but make the mask bigger + // If the range wraps (left>right) then, increasing the mask adds all the new space into + // the range, and it would be an inaccurate pullback by itself, but with the nzmask intersection + // all the new space will get intersected away again. + } + } + else + return (Varnode *) 0; + } + if (constvn->getSymbolEntry() != (SymbolEntry *) 0) + *constMarkup = constvn; + } + else // Neither unary or binary return (Varnode *)0; - // If there is nothing in the output set, no input will map to it - if (isempty) return res; - - switch(op->code()) { - case CPUI_BOOL_NEGATE: - convertToBoolean(); - if (left==right) break; // Both outputs possible => both inputs possible - left = left ^ 1; // Flip the boolean range - right = right ^ 1; - break; - case CPUI_INT_EQUAL: - convertToBoolean(); - mask = calc_mask(res->getSize()); - if (left==right) break; // All possible outs => all possible ins - yescomplement = (left==0); - val = constvn->getOffset(); - left = val; - right = (val+1)&mask; - if (yescomplement) - complement(); - break; - case CPUI_INT_NOTEQUAL: - convertToBoolean(); - mask = calc_mask(res->getSize()); - if (left==right) break; // All possible outs => all possible ins - yescomplement = (left==0); - val = constvn->getOffset(); - left = (val+1)&mask; - right = val; - if (yescomplement) - complement(); - break; - case CPUI_INT_LESS: - convertToBoolean(); - mask = calc_mask(res->getSize()); - if (left==right) break; // All possible outs => all possible ins - yescomplement = (left==0); - val = constvn->getOffset(); - if (slot==0) { - if (val==0) - isempty = true; // X < 0 is always false - else { - left = 0; - right = val; - } - } - else { - if (val==mask) - isempty = true; // 0xffff < X is always false - else { - left = (val+1)&mask; - right = 0; - } - } - if (yescomplement) - complement(); - break; - case CPUI_INT_LESSEQUAL: - convertToBoolean(); - mask = calc_mask(res->getSize()); - if (left==right) break; // All possible outs => all possible ins - yescomplement = (left==0); - val = constvn->getOffset(); - if (slot==0) { - left = 0; - right = (val+1)&mask; - } - else { - left = val; - right = 0; - } - if (yescomplement) - complement(); - break; - case CPUI_INT_SLESS: - convertToBoolean(); - mask = calc_mask(res->getSize()); - if (left==right) break; // All possible outs => all possible ins - yescomplement = (left==0); - val = constvn->getOffset(); - if (slot==0) { - if (val == (mask>>1)+1) - isempty = true; // X < -infinity, is always false - else { - left = (mask >> 1)+1; // -infinity - right = val; - } - } - else { - if ( val == (mask>>1) ) - isempty = true; // infinity < X, is always false - else { - left = (val+1)&mask; - right = (mask >> 1)+1; // -infinity - } - } - if (yescomplement) - complement(); - break; - case CPUI_INT_SLESSEQUAL: - convertToBoolean(); - mask = calc_mask(res->getSize()); - if (left==right) break; // All possible outs => all possible ins - yescomplement = (left==0); - val = constvn->getOffset(); - if (slot==0) { - left = (mask >> 1)+1; // -infinity - right = (val+1)&mask; - } - else { - left = val; - right = (mask >> 1)+1; // -infinity - } - if (yescomplement) - complement(); - break; - case CPUI_INT_CARRY: - convertToBoolean(); - mask = calc_mask(res->getSize()); - if (left==right) break; // All possible outs => all possible ins - yescomplement = (left==0); - val = constvn->getOffset(); - if (val==0) - isempty = true; // Nothing carries adding zero - else { - left = ((mask-val)+1)&mask; - right = 0; - } - if (yescomplement) - complement(); - break; - case CPUI_COPY: - break; // Identity transform on range - case CPUI_INT_ADD: - val = constvn->getOffset(); - if ((val&mask) == val) { // Constant must match the stride - left = (left-val)&mask; - right = (right-val)&mask; - } - else - res = (Varnode *)0; - break; - case CPUI_INT_SUB: - val = constvn->getOffset(); - if ((val&mask) == val) { // Constant must match the stride - if (slot==0) { - left = (left+val)&mask; - right = (right+val)&mask; - } - else { - left = (val-left)&mask; - right = (val-right)&mask; - } - } - else - res = (Varnode *)0; - break; - case CPUI_INT_2COMP: - val = (~left + 1 + step) & mask; - left = (~right + 1 + step) & mask; - right = val; - break; - case CPUI_SUBPIECE: - if ((!usenzmask)||(constvn->getOffset() != 0)) - res = (Varnode *)0; - else { // If everything we are truncating is known to be zero, we may still have a range - int4 msbset = mostsigbit_set(res->getNZMask()); - msbset = (msbset + 8)/8; - if (op->getOut()->getSize() < msbset) // Some bytes we are chopping off might not be zero - res = (Varnode *)0; - else { - mask = calc_mask(res->getSize()); // Keep the range but mask the mask bigger - // If the range wraps (left>right) then, increasing the mask adds all the new space into - // the range, and it would be an inaccurate pullback by itself, but with the nzmask intersection - // all the new space will get intersected away again. - } - } - break; - case CPUI_INT_ZEXT: - { - val = calc_mask(res->getSize()); // (smaller) input mask - CircleRange zextrange; - zextrange.left = 0; - zextrange.right = val+1; // Biggest possible range of ZEXT - zextrange.mask = mask; - zextrange.step = step; // Keep the same stride - zextrange.shift = shift; - zextrange.isempty = false; - if (0!=intersect(zextrange)) - res = (Varnode *)0; - left &= val; - right &= val; - mask &= val; // Preserve the stride - } - break; - case CPUI_INT_SEXT: - { - val = calc_mask(res->getSize()); // (smaller) input mask - CircleRange sextrange; - sextrange.left = val ^ (val>>1); // High order bit for (small) input space - sextrange.right = sign_extend(sextrange.left,res->getSize(),op->getOut()->getSize()); - sextrange.mask = mask; - sextrange.step = step; // Keep the same stride - sextrange.shift = shift; - sextrange.isempty = false; - if (sextrange.intersect(*this) != 0) - res = (Varnode *)0; - else { - if (!sextrange.isEmpty()) - res = (Varnode *)0; - else { - left &= val; - right &= val; - mask &= val; // Preserve the stride - } - } - } - break; - case CPUI_INT_RIGHT: - { - if (step == 1) { - val = (calc_mask(res->getSize()) >> constvn->getOffset()) + 1; // The maximal right bound - if (((left >= val)&&(right >= val)&&(left>=right))|| - ((left == 0)&&(right>=val))|| - (left == right)) { - // covers everything in range of shift - left=0; // So domain is everything - right=0; - } - else { - if (left > val) - left = val; - if (right > val) - right = 0; - left = (left << constvn->getOffset()) & mask; - right = (right << constvn->getOffset()) & mask; - if (left == right) - isempty = true; - } - } - else - res = (Varnode *)0; - } - break; - case CPUI_INT_SRIGHT: - { - if (step == 1) { - uintb rightb = calc_mask(res->getSize()); - uintb leftb = rightb >> (constvn->getOffset()+1); - rightb = leftb ^ rightb; // Smallest negative possible - leftb += 1; // Biggest positive (+1) possible - if (((left >= leftb)&&(left <= rightb)&& - (right >= leftb)&&(right <= rightb)&& - (left >= right)) - || (left==right)) { - // covers everything in range of shift - left = 0; // So domain is everything - right = 0; - } - else { - if ((left > leftb)&&(left < rightb)) - left = leftb; - if ((right > leftb)&&(right < rightb)) - right = rightb; - left = (left << constvn->getOffset()) &mask; - right = (right << constvn->getOffset()) & mask; - if (left == right) - isempty = true; - } - } - else - res = (Varnode *)0; - } - break; - default: - res = (Varnode *)0; - break; - } - if ((constvn != (Varnode *)0)&&(constvn->getSymbolEntry() != (SymbolEntry *)0)) - *constMarkup = constvn; - if ((res != (Varnode *)0)&&usenzmask) { + if (usenzmask) { CircleRange nzrange; if (!nzrange.setNZMask(res->getNZMask(),res->getSize())) return res; @@ -797,6 +1061,321 @@ Varnode *CircleRange::pullBack(PcodeOp *op,Varnode **constMarkup,bool usenzmask) return res; } +/// Push all values in the given range through a p-code operator. +/// If the output set of values forms a range, then set \b this to the range and return \b true. +/// \param opc is the given p-code operator +/// \param in1 is the given input range +/// \param inSize is the storage space in bytes for the input +/// \param outSize is the storage space in bytes for the output +/// \return \b true if the result is known and forms a range +bool CircleRange::pushForwardUnary(OpCode opc,const CircleRange &in1,int4 inSize,int4 outSize) + +{ + if (in1.isempty) { + isempty = true; + return true; + } + switch(opc) { + case CPUI_CAST: + case CPUI_COPY: + *this = in1; + break; + case CPUI_INT_ZEXT: + isempty = false; + step = in1.step; + mask = calc_mask(outSize); + left = in1.left; + right = (in1.right - in1.step) & in1.mask; + if (right < left) { // Extending causes 2 pieces + left = 0; + right = in1.mask; + } + else { + right += step; // Impossible for it to wrap with bigger mask + } + break; + case CPUI_INT_SEXT: + isempty = false; + step = in1.step; + mask = calc_mask(outSize); + left = sign_extend(in1.left, inSize, outSize); + right = sign_extend((in1.right - in1.step)&in1.mask, inSize, outSize); + if ((intb)right < (intb)left) { + right = calc_mask(inSize); + left = calc_mask(outSize) ^ right; + right += 1; + } + else + right += step; + break; + case CPUI_INT_2COMP: + isempty = false; + step = in1.step; + mask = in1.mask; + right = (~in1.left + 1 + step) & mask; + left = (~in1.right + 1 + step) & mask; + normalize(); + break; + case CPUI_INT_NEGATE: + isempty = false; + step = in1.step; + mask = in1.mask; + left = -in1.right & mask; + right = -in1.left & mask; + normalize(); + break; + case CPUI_BOOL_NEGATE: + case CPUI_FLOAT_NAN: + isempty = false; + mask = 0xff; + step = 1; + left = 0; + right = 2; + break; + default: + return false; + } + return true; +} + +/// \brief Push \b this range forward through a binary operation +/// +/// Push all values in the given ranges through a binary p-code operator. +/// If the output set of values forms a range, then set \b this to the range and return \b true. +/// \param opc is the given p-code operator +/// \param in1 is the first given input range +/// \param in2 is the second given input range +/// \param inSize is the storage space in bytes for the input +/// \param outSize is the storage space in bytes for the output +/// \param maxStep is the maximum to allow step to grow via multiplication +/// \return \b true if the result is known and forms a range +bool CircleRange::pushForwardBinary(OpCode opc,const CircleRange &in1,const CircleRange &in2,int4 inSize,int4 outSize,int4 maxStep) + +{ + if (in1.isempty || in2.isempty) { + isempty = true; + return true; + } + switch(opc) { + case CPUI_PTRSUB: + case CPUI_INT_ADD: + isempty = false; + mask = in1.mask | in2.mask; + if (in1.left == in1.right || in2.left == in2.right) { + step = (in1.step < in2.step) ? in1.step : in2.step; // Smaller step + left = (in1.left + in2.left) % step; + right = left; + } + else if (in2.isSingle()) { + step = in1.step; + left = (in1.left + in2.left) & mask; + right = (in1.right + in2.left) & mask; + } + else if (in1.isSingle()) { + step = in2.step; + left = (in2.left + in1.left) & mask; + right = (in2.right +in1.left) & mask; + } + else { + step = (in1.step < in2.step) ? in1.step : in2.step; // Smaller step + uintb size1 = (in1.left < in1.right) ? (in1.right-in1.left) : (in1.mask - (in1.left-in1.right) + in1.step); + left = (in1.left + in2.left) & mask; + right = (in1.right - in1.step + in2.right - in2.step + step) & mask; + uintb sizenew = (left < right) ? (right-left) : (mask - (left-right) + step); + if (sizenew < size1) { + right = left; // Over-flow, we covered everything + } + normalize(); + } + break; + case CPUI_INT_MULT: + { + isempty = false; + mask = in1.mask | in2.mask; + uintb constVal; + if (in1.isSingle()) { + constVal = in1.getMin(); + step = in2.step; + } + else if (in2.isSingle()) { + constVal = in2.getMin(); + step = in1.step; + } + else + return false; + uint4 tmp = (uint4)constVal; + while(step < maxStep) { + if ((tmp & 1) != 0) break; + step <<= 1; + tmp >>= 1; + } + int4 wholeSize = 8*sizeof(uintb) - count_leading_zeros(mask); + if (in1.getMaxInfo() + in2.getMaxInfo() > wholeSize) { + right = left; // Covered everything + normalize(); + return true; + } + if ((constVal & (mask ^ (mask >> 1))) != 0) { // Multiplying by negative number + left = ((in1.right - in1.step) * (in2.right - in2.step)) & mask; + right = ((in1.left * in2.left) + step) & mask; + } + else { + left = (in1.left * in2.left)&mask; + right = ((in1.right - in1.step) * (in2.right - in2.step) + step) & mask; + } + break; + } + case CPUI_INT_LEFT: + { + if (!in2.isSingle()) return false; + isempty = false; + mask = in1.mask; + step = in1.step; + uint4 sa = (uint4)in2.getMin(); + uint4 tmp = sa; + while(step < maxStep && tmp > 0) { + step <<= 1; + sa -= 1; + } + left = (in1.left << sa)&mask; + right = (in1.right << sa)&mask; + int4 wholeSize = 8*sizeof(uintb) - count_leading_zeros(mask); + if (in1.getMaxInfo() + sa > wholeSize) { + right = left; // Covered everything + normalize(); + return true; + } + break; + } + case CPUI_SUBPIECE: + { + if (!in2.isSingle()) return false; + isempty = false; + int4 sa = (int4)in2.left * 8; + mask = calc_mask(outSize); + step = (sa == 0) ? in1.step : 1; + + left = (in1.left >> sa)&mask; + right = (in1.right >> sa)&mask; + if ((left& ~mask) != (right & ~mask)) { // Truncated part is different + left = right = 0; // We cover everything + } + else { + left &= mask; + right &= mask; + normalize(); + } + break; + } + case CPUI_INT_RIGHT: + { + if (!in2.isSingle()) return false; + isempty = false; + int4 sa = (int4)in2.left; + mask = calc_mask(outSize); + step = 1; // Lose any step + if (in1.left < in1.right) { + left = in1.left >> sa; + right = ((in1.right - in1.step) >> sa) + 1; + } + else { + left = 0; + right = in1.mask >> sa; + } + if (left == right) // Don't truncate accidentally to everything + right = (left + 1)&mask; + break; + } + case CPUI_INT_SRIGHT: + { + if (!in2.isSingle()) return false; + isempty = false; + int4 sa = (int4)in2.left; + mask = calc_mask(outSize); + step = 1; // Lose any step + intb valLeft = in1.left; + intb valRight = in1.right; + int4 bitPos = 8*inSize - 1; + sign_extend(valLeft,bitPos); + sign_extend(valRight,bitPos); + if (valLeft >= valRight) { + valRight = (intb)(mask >> 1); // Max positive + valLeft = valRight + 1; // Min negative + sign_extend(valLeft,bitPos); + } + left = (valLeft >> sa) & mask; + right = (valRight >> sa) & mask; + if (left == right) // Don't truncate accidentally to everything + right = (left + 1)&mask; + break; + } + case CPUI_INT_EQUAL: + case CPUI_INT_NOTEQUAL: + case CPUI_INT_SLESS: + case CPUI_INT_SLESSEQUAL: + case CPUI_INT_LESS: + case CPUI_INT_LESSEQUAL: + case CPUI_INT_CARRY: + case CPUI_INT_SCARRY: + case CPUI_INT_SBORROW: + case CPUI_BOOL_XOR: + case CPUI_BOOL_AND: + case CPUI_BOOL_OR: + case CPUI_FLOAT_EQUAL: + case CPUI_FLOAT_NOTEQUAL: + case CPUI_FLOAT_LESS: + case CPUI_FLOAT_LESSEQUAL: + // Ops with boolean outcome. We don't try to eliminate outcomes here. + isempty = false; + mask = 0xff; + step = 1; + left = 0; // Both true and false are possible + right = 2; + break; + default: + return false; + } + return true; +} + +/// \brief Push \b this range forward through a trinary operation +/// +/// Push all values in the given ranges through a trinary p-code operator (currenly only CPUI_PTRADD). +/// If the output set of values forms a range, then set \b this to the range and return \b true. +/// \param opc is the given p-code operator +/// \param in1 is the first given input range +/// \param in2 is the second given input range +/// \param in3 is the third given input range +/// \param inSize is the storage space in bytes for the input +/// \param outSize is the storage space in bytes for the output +/// \param maxStep is the maximum to allow step to grow via multiplication +/// \return \b true if the result is known and forms a range +bool CircleRange::pushForwardTrinary(OpCode opc,const CircleRange &in1,const CircleRange &in2,const CircleRange &in3, + int4 inSize,int4 outSize,int4 maxStep) +{ + if (opc != CPUI_PTRADD) return false; + CircleRange tmpRange; + if (!tmpRange.pushForwardBinary(CPUI_INT_MULT, in2, in3, inSize, inSize, maxStep)) + return false; + return pushForwardBinary(CPUI_INT_ADD, in1, tmpRange, inSize, outSize, maxStep); +} + +/// Widen \b this range so at least one of the boundaries matches with the given +/// range, which must contain \b this. +/// \param op2 is the given containing range +/// \param leftIsStable is \b true if we want to match right boundaries +void CircleRange::widen(const CircleRange &op2,bool leftIsStable) + +{ + if (leftIsStable) { + right = (op2.right + step-1) & mask; + } + else { + left = op2.left & mask; + } + normalize(); +} + /// Recover parameters for a comparison PcodeOp, that returns true for /// input values exactly in \b this range. /// Return: @@ -852,3 +1431,1056 @@ int4 CircleRange::translate2Op(OpCode &opc,uintb &c,int4 &cslot) const } return 2; // Cannot represent } + +/// \param s is the stream to write to +void CircleRange::printRaw(ostream &s) const + +{ + if (isempty) { + s << "(empty)"; + return; + } + if (left == right) { + s << "(full"; + if (step != 1) + s << ',' << dec << step; + s << ')'; + } + else if (right == ((left+1)&mask)) { + s << '[' << hex << left << ']'; + } + else { + s << '[' << hex << left << ',' << right; + if (step != 1) + s << ',' << dec << step; + s << ')'; + } +} + +const int4 ValueSet::MAX_STEP = 32; + +/// The initial values in \b this are set based on the type of Varnode: +/// - Constant gets the single value +/// - Input gets all possible values +/// - Other Varnodes that are written start with an empty set +/// +/// \param v is the given Varnode to attach to +/// \param tCode indicates whether to treat values as constants are relative offsets +void ValueSet::setVarnode(Varnode *v,int4 tCode) + +{ + typeCode = tCode; + vn = v; + vn->setValueSet(this); + if (typeCode != 0) { + opCode = CPUI_MAX; + numParams = 0; + range.setRange(0,vn->getSize()); // Treat as offset of 0 relative to special value + leftIsStable = true; + rightIsStable = true; + } + else if (vn->isWritten()) { + PcodeOp *op = vn->getDef(); + opCode = op->code(); + if (opCode == CPUI_INDIRECT) { // Treat CPUI_INDIRECT as CPUI_COPY + numParams = 1; + opCode = CPUI_COPY; + } + else + numParams = op->numInput(); + leftIsStable = false; + rightIsStable = false; + } + else if (vn->isConstant()) { + opCode = CPUI_MAX; + numParams = 0; + range.setRange(vn->getOffset(),vn->getSize()); + leftIsStable = true; + rightIsStable = true; + } + else { // Some other form of input + opCode = CPUI_MAX; + numParams = 0; + typeCode = 0; + range.setFull(vn->getSize()); + leftIsStable = false; + rightIsStable = false; + } +} + +/// Equations are stored as an array of (slot,range) pairs, ordered on slot. +/// \param slot is the given slot +/// \param type is the constraint characteristic +/// \param constraint is the given range +void ValueSet::addEquation(int4 slot,int4 type,const CircleRange &constraint) + +{ + vector::iterator iter; + iter = equations.begin(); + while(iter != equations.end()) { + if ((*iter).slot > slot) + break; + ++iter; + } + equations.insert(iter,Equation(slot,type,constraint)); +} + +/// Examine the input value sets that determine \b this set and decide if it +/// is relative. In general, \b this will be relative if any of its inputs are. +/// Certain combinations are indeterminate, which this method flags by +/// returning \b true. The Varnode attached to \b this must have a defining op. +/// \return \b true if there is an indeterminate combination +bool ValueSet::computeTypeCode(void) + +{ + int4 relCount = 0; + int4 lastTypeCode; + PcodeOp *op = vn->getDef(); + for(int4 i=0;igetIn(i)->getValueSet(); + if (valueSet->typeCode != 0) { + relCount += 1; + lastTypeCode = valueSet->typeCode; + } + } + if (relCount == 0) { + typeCode = 0; + return false; + } + // Only certain operations can propagate a relative value set + switch(opCode) { + case CPUI_PTRSUB: + case CPUI_PTRADD: + case CPUI_INT_ADD: + case CPUI_INT_SUB: + if (relCount == 1) + typeCode = lastTypeCode; + else + return true; + break; + case CPUI_CAST: + case CPUI_COPY: + case CPUI_INDIRECT: + case CPUI_MULTIEQUAL: + typeCode = lastTypeCode; + break; + default: + return true; + } + return false; +} + +/// Recalculate \b this value set by grabbing the value sets of the inputs to the +/// operator defining the Varnode attached to \b this value set and pushing them +/// forward through the operator. +/// \return \b true if there was a change to \b this value set +bool ValueSet::iterate(Widener &widener) + +{ + if (!vn->isWritten()) return false; + if (widener.checkFreeze(*this)) return false; + if (count == 0) { + if (computeTypeCode()) { + setFull(); + return true; + } + } + count += 1; // Count this iteration + CircleRange res; + PcodeOp *op = vn->getDef(); + int4 eqPos = 0; + if (opCode == CPUI_MULTIEQUAL) { + int4 pieces = 0; + for(int4 i=0;igetIn(i)->getValueSet(); + if (doesEquationApply(eqPos, i)) { + CircleRange rangeCopy(inSet->range); + if (0 !=rangeCopy.intersect(equations[eqPos].range)) { + rangeCopy = equations[eqPos].range; + } + pieces = res.circleUnion(rangeCopy); + eqPos += 1; // Equation was used + } + else { + pieces = res.circleUnion(inSet->range); + } + if (pieces == 2) { + if (res.minimalContainer(inSet->range,MAX_STEP)) // Could not get clean union, force it + break; + } + } + if (0 != res.circleUnion(range)) { // Union with the previous iteration's set + res.minimalContainer(range,MAX_STEP); + } + leftIsStable = range.getMin() == res.getMin(); + rightIsStable = range.getEnd() == res.getEnd(); + } + else if (numParams == 1) { + ValueSet *inSet1 = op->getIn(0)->getValueSet(); + if (doesEquationApply(eqPos, 0)) { + CircleRange rangeCopy(inSet1->range); + if (0 != rangeCopy.intersect(equations[eqPos].range)) { + rangeCopy = equations[eqPos].range; + } + if (!res.pushForwardUnary(opCode, rangeCopy, inSet1->vn->getSize(), vn->getSize())) { + setFull(); + return true; + } + eqPos += 1; + } + else if (!res.pushForwardUnary(opCode, inSet1->range, inSet1->vn->getSize(), vn->getSize())) { + setFull(); + return true; + } + leftIsStable = inSet1->leftIsStable; + rightIsStable = inSet1->rightIsStable; + } + else if (numParams == 2) { + ValueSet *inSet1 = op->getIn(0)->getValueSet(); + ValueSet *inSet2 = op->getIn(1)->getValueSet(); + if (equations.size() == 0) { + if (!res.pushForwardBinary(opCode, inSet1->range, inSet2->range, inSet1->vn->getSize(), vn->getSize(), MAX_STEP)) { + setFull(); + return true; + } + } + else { + CircleRange range1 = inSet1->range; + CircleRange range2 = inSet2->range; + if (doesEquationApply(eqPos, 0)) { + if (0 != range1.intersect(equations[eqPos].range)) + range1 = equations[eqPos].range; + eqPos += 1; + } + if (doesEquationApply(eqPos, 1)) { + if (0 != range2.intersect(equations[eqPos].range)) + range2 = equations[eqPos].range; + } + if (!res.pushForwardBinary(opCode, range1, range2, inSet1->vn->getSize(), vn->getSize(), MAX_STEP)) { + setFull(); + return true; + } + } + leftIsStable = inSet1->leftIsStable && inSet2->leftIsStable; + rightIsStable = inSet1->rightIsStable && inSet2->rightIsStable; + } + else if (numParams == 3) { + ValueSet *inSet1 = op->getIn(0)->getValueSet(); + ValueSet *inSet2 = op->getIn(1)->getValueSet(); + ValueSet *inSet3 = op->getIn(2)->getValueSet(); + CircleRange range1 = inSet1->range; + CircleRange range2 = inSet2->range; + if (doesEquationApply(eqPos, 0)) { + if (0 != range1.intersect(equations[eqPos].range)) + range1 = equations[eqPos].range; + eqPos += 1; + } + if (doesEquationApply(eqPos, 1)) { + if (0 != range2.intersect(equations[eqPos].range)) + range2 = equations[eqPos].range; + } + if (!res.pushForwardTrinary(opCode, range1, range2, inSet3->range, inSet1->vn->getSize(), vn->getSize(), MAX_STEP)) { + setFull(); + return true; + } + leftIsStable = inSet1->leftIsStable && inSet2->leftIsStable; + rightIsStable = inSet1->rightIsStable && inSet2->rightIsStable; + } + else + return false; // No way to change this value set + + if (res == range) + return false; + if (partHead != (Partition *)0) { + if (!widener.doWidening(*this, range, res)) + setFull(); + } + else + range = res; + return true; +} + +/// If a landmark was associated with \b this value set, return its range, +/// otherwise return null. +/// \return the landmark range or null +const CircleRange *ValueSet::getLandMark(void) const + +{ + if (equations.empty()) + return (const CircleRange *)0; + + const Equation &landmark(equations.back()); + if (landmark.slot != numParams || typeCode != landmark.typeCode) + return (const CircleRange *)0; + return &landmark.range; +} + +/// \param s is the stream to print to +void ValueSet::printRaw(ostream &s) const + +{ + if (vn == (Varnode *)0) + s << "root"; + else + vn->printRaw(s); + if (typeCode == 0) + s << " absolute"; + else + s << " stackptr"; + if (opCode == CPUI_MAX) { + if (vn->isConstant()) + s << " const"; + else + s << " input"; + } + else + s << ' ' << get_opname(opCode); + s << ' '; + range.printRaw(s); +} + +/// \param o is the PcodeOp reading the value set +/// \param slt is the input slot the values are coming in from +void ValueSetRead::setPcodeOp(PcodeOp *o,int4 slt) + +{ + typeCode = 0; + op = o; + slot = slt; + equationTypeCode = -1; +} + +/// \param slt is the given slot +/// \param type is the constraint characteristic +/// \param constraint is the given range +void ValueSetRead::addEquation(int4 slt,int4 type,const CircleRange &constraint) + +{ + if (slot == slt) { + equationTypeCode = type; + equationConstraint = constraint; + } +} + +/// This value set will be the same as the ValueSet of the Varnode being read but may +/// be modified due to additional control-flow constraints +void ValueSetRead::compute(void) + +{ + Varnode *vn = op->getIn(slot); + ValueSet *valueSet = vn->getValueSet(); + typeCode = valueSet->getTypeCode(); + range = valueSet->getRange(); + leftIsStable = valueSet->isLeftStable(); + rightIsStable = valueSet->isRightStable(); + if (typeCode == equationTypeCode) { + if (0 != range.intersect(equationConstraint)) { + range = equationConstraint; + } + } +} + +/// \param s is the stream to print to +void ValueSetRead::printRaw(ostream &s) const + +{ + s << "Read: " << get_opname(op->code()); + s << '(' << op->getSeqNum() << ')'; + if (typeCode == 0) + s << " absolute "; + else + s << " stackptr "; + range.printRaw(s); +} + +int4 WidenerFull::determineIterationReset(const ValueSet &valueSet) + +{ + if (valueSet.getCount() >= widenIteration) + return widenIteration; // Reset to point just after any widening + return 0; // Delay widening, if we haven't performed it yet +} + +bool WidenerFull::checkFreeze(const ValueSet &valueSet) + +{ + return valueSet.getRange().isFull(); +} + +bool WidenerFull::doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange) + +{ + if (valueSet.getCount() < widenIteration) { + range = newRange; + return true; + } + else if (valueSet.getCount() == widenIteration) { + const CircleRange *landmark = valueSet.getLandMark(); + if (landmark != (const CircleRange *)0) { + bool leftIsStable = range.getMin() == newRange.getMin(); + range = newRange; // Preserve any new step information + if (landmark->contains(range)) { + range.widen(*landmark,leftIsStable); + return true; + } + else { + CircleRange constraint = *landmark; + constraint.invert(); + if (constraint.contains(range)) { + range.widen(constraint,leftIsStable); + return true; + } + } + } + } + else if (valueSet.getCount() < fullIteration) { + range = newRange; + return true; + } + return false; // Indicate that constrained widening failed (set to full) +} + +int4 WidenerNone::determineIterationReset(const ValueSet &valueSet) + +{ + if (valueSet.getCount() >= freezeIteration) + return freezeIteration; // Reset to point just after any widening + return valueSet.getCount(); +} + +bool WidenerNone::checkFreeze(const ValueSet &valueSet) + +{ + if (valueSet.getRange().isFull()) + return true; + return (valueSet.getCount() >= freezeIteration); +} + +bool WidenerNone::doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange) + +{ + range = newRange; + return true; +} + +/// \brief Construct an iterator over the outbound edges of the given ValueSet node +/// +/// Mostly this just forwards the ValueSets attached to output Varnodes +/// of the descendant ops of the Varnode attached to the given node, but this +/// allows for an artificial root node so we can simulate multiple input nodes. +/// \param node is the given ValueSet node (NULL if this is the simulated root) +/// \param roots is the list of input ValueSets to use for the simulated root +ValueSetSolver::ValueSetEdge::ValueSetEdge(ValueSet *node,const vector &roots) + +{ + vn = node->getVarnode(); + if (vn == (Varnode *)0) { // Assume this is the simulated root + rootEdges = &roots; // Set up for simulated edges + rootPos = 0; + } + else { + rootEdges = (const vector *)0; + iter = vn->beginDescend(); + } +} + +/// \brief Get the ValueSet pointed to by this iterator and advance the iterator +/// +/// This method assumes all Varnodes with an attached ValueSet have been marked. +/// \return the next ValueSet or NULL if the end of the list is reached +ValueSet *ValueSetSolver::ValueSetEdge::getNext(void) + +{ + if (vn == (Varnode *)0) { + if (rootPos < rootEdges->size()) { + ValueSet *res = (*rootEdges)[rootPos]; + rootPos += 1; + return res; + } + return (ValueSet *)0; + } + while(iter != vn->endDescend()) { + PcodeOp *op = *iter; + ++iter; + Varnode *outVn = op->getOut(); + if (outVn != (Varnode *)0 && outVn->isMark()) { + return outVn->getValueSet(); + } + } + return (ValueSet *)0; +} + +/// The new ValueSet is attached to the given Varnode +/// \param vn is the given Varnode +/// \param tCode is the type to associate with the Varnode +void ValueSetSolver::newValueSet(Varnode *vn,int4 tCode) + +{ + valueNodes.push_back(ValueSet()); + valueNodes.back().setVarnode(vn, tCode); +} + +/// This method saves a Partition to permanent storage. It marks the +/// starting node of the partition and sets up for the iterating algorithm. +/// \param part is the partition to store +void ValueSetSolver::partitionSurround(Partition &part) + +{ + recordStorage.push_back(part); + part.startNode->partHead = &recordStorage.back(); +} + +/// Knowing that the given Varnode is the head of a partition, generate +/// the partition recursively and generate the formal Partition object. +/// \param vertex is the given ValueSet (attached to the head Varnode) +/// \param part will hold the constructed Partition +void ValueSetSolver::component(ValueSet *vertex,Partition &part) + +{ + ValueSetEdge edgeIterator(vertex,rootNodes); + ValueSet *succ = edgeIterator.getNext(); + while(succ != (ValueSet *)0) { + if (succ->count == 0) + visit(succ,part); + succ = edgeIterator.getNext(); + } + partitionPrepend(vertex, part); + partitionSurround(part); +} + +/// \param vertex is the current Varnode being walked +/// \param part is the current Partition being constructed +/// \return the index of calculated head ValueSet for the current Parition +int4 ValueSetSolver::visit(ValueSet *vertex,Partition &part) + +{ + nodeStack.push_back(vertex); + depthFirstIndex += 1; + vertex->count = depthFirstIndex; + int4 head = depthFirstIndex; + bool loop = false; + ValueSetEdge edgeIterator(vertex,rootNodes); + ValueSet *succ = edgeIterator.getNext(); + while(succ != (ValueSet *)0) { + int4 min; + if (succ->count == 0) + min = visit(succ,part); + else + min = succ->count; + if (min <= head) { + head = min; + loop = true; + } + succ = edgeIterator.getNext(); + } + if (head == vertex->count) { + vertex->count = 0x7fffffff; // Set to "infinity" + ValueSet *element = nodeStack.back(); + nodeStack.pop_back(); + if (loop) { + while(element != vertex) { + element->count = 0; + element = nodeStack.back(); + nodeStack.pop_back(); + } + Partition compPart; // empty partition + component(vertex,compPart); + partitionPrepend(compPart, part); + } + else { + partitionPrepend(vertex, part); + } + } + return head; +} + +/// \brief Establish the recursive node ordering for iteratively solving the value set system. +/// +/// This algorithm is based on "Efficient chaotic iteration strategies with widenings" by +/// Francois Bourdoncle. The Varnodes in the system are ordered and a set of nested +/// Partition components are generated. Iterating the ValueSets proceeds in this order, +/// looping through the components recursively until a fixed point is reached. +/// This implementation assumes all Varnodes in the system are distinguished by +/// Varnode::isMark() returning \b true. +void ValueSetSolver::establishTopologicalOrder(void) + +{ + for(list::iterator iter=valueNodes.begin();iter!=valueNodes.end();++iter) { + (*iter).count = 0; + (*iter).next = (ValueSet *)0; + (*iter).partHead = (Partition *)0; + } + ValueSet rootNode; + rootNode.vn = (Varnode *)0; + depthFirstIndex = 0; + visit(&rootNode,orderPartition); + orderPartition.startNode = orderPartition.startNode->next; // Remove simulated root +} + +/// \brief Look for PcodeOps where the given constraint range applies and instantiate an equation +/// +/// If a read of the given Varnode is in a basic block dominated by the condition producing the +/// constraint, then either the constraint or its complement applies to the PcodeOp reading +/// the Varnode. An equation holding the constraint is added to the ValueSet of the Varnode +/// output of the PcodeOp. +/// \param vn is the given Varnode +/// \param type is the constraint characteristic +/// \param range is the known constraint (assuming the \b true branch was taken) +/// \param cbranch is conditional branch creating the constraint +void ValueSetSolver::applyConstraints(Varnode *vn,int4 type,const CircleRange &range,PcodeOp *cbranch) + +{ + FlowBlock *splitPoint = cbranch->getParent(); + FlowBlock *trueBlock,*falseBlock; + if (cbranch->isBooleanFlip()) { + trueBlock = splitPoint->getFalseOut(); + falseBlock = splitPoint->getTrueOut(); + } + else { + trueBlock = splitPoint->getTrueOut(); + falseBlock = splitPoint->getFalseOut(); + } + // Check if the only path to trueBlock or falseBlock is via a splitPoint out-edge induced by the condition + bool trueIsRestricted = trueBlock->restrictedByConditional(splitPoint); + bool falseIsRestricted = falseBlock->restrictedByConditional(splitPoint); + + list::const_iterator iter; + if (vn->isWritten()) { + ValueSet *vSet = vn->getValueSet(); + if (vSet->opCode == CPUI_MULTIEQUAL) { + vSet->addLandmark(type,range); // Leave landmark for widening + } + } + for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { + PcodeOp *op = *iter; + Varnode *outVn = (Varnode *)0; + if (!op->isMark()) { // If this is not a special read site + outVn = op->getOut(); // Make sure there is a Varnode in the system + if (outVn == (Varnode *)0) continue; + if (!outVn->isMark()) continue; + } + FlowBlock *curBlock = op->getParent(); + int4 slot = op->getSlot(vn); + for(;;) { + if (curBlock == trueBlock) { + if (!trueIsRestricted) { + // If its possible that both the true and false edges can reach trueBlock + // then the only input we can restrict is a MULTIEQUAL input along the exact true edge + if (op->code() != CPUI_MULTIEQUAL) break; + if (op->getParent() != trueBlock) break; + if (trueBlock->getIn(slot) != splitPoint) break; + } + if (outVn != (Varnode *)0) + outVn->getValueSet()->addEquation(slot, type, range); + else + readNodes[op->getSeqNum()].addEquation(slot, type, range); // Special read site + break; + } + else if (curBlock == falseBlock) { + if (!falseIsRestricted) { + // If its possible that both the true and false edges can reach falseBlock + // then the only input we can restrict is a MULTIEQUAL input along the exact false edge + if (op->code() != CPUI_MULTIEQUAL) break; + if (op->getParent() != falseBlock) break; + if (falseBlock->getIn(slot) != splitPoint) break; + } + CircleRange falseRange(range); + falseRange.invert(); + if (outVn != (Varnode *)0) + outVn->getValueSet()->addEquation(slot, type, falseRange); + else + readNodes[op->getSeqNum()].addEquation(slot, type, falseRange); // Special read site + break; + } + else if (curBlock == splitPoint || curBlock == (FlowBlock *)0) + break; + curBlock = curBlock->getImmedDom(); + } + } +} + +/// \brief Generate constraints given a Varnode path +/// +/// Knowing that there is a lifting path from the given starting Varnode to an ending Varnode +/// in the system, go ahead and lift the given range to a final constraint on the ending +/// Varnode. Then look for reads of the Varnode where the constraint applies. +/// \param type is the constraint characteristic +/// \param lift is the given range that will be lifted +/// \param startVn is the starting Varnode +/// \param endVn is the given ending Varnode in the system +/// \param cbranch is the PcodeOp causing the control-flow split +void ValueSetSolver::constraintsFromPath(int4 type,CircleRange &lift,Varnode *startVn,Varnode *endVn,PcodeOp *cbranch) + +{ + while(startVn != endVn) { + Varnode *constVn; + startVn = lift.pullBack(startVn->getDef(),&constVn,false); + if (startVn == (Varnode *)0) return; // Couldn't pull all the way back to our value set + } + for(;;) { + Varnode *constVn; + applyConstraints(endVn,type,lift,cbranch); + if (!endVn->isWritten()) break; + PcodeOp *op = endVn->getDef(); + if (op->isCall() || op->isMarker()) break; + endVn = lift.pullBack(op,&constVn,false); + if (endVn == (Varnode *)0) break; + if (!endVn->isMark()) break; + } +} + +/// Lift the set of values on the condition for the given CBRANCH to any +/// Varnode in the system, and label (the reads) of any such Varnode with +/// the constraint. If the values cannot be lifted or no Varnode in the system +/// is found, no constraints are generated. +/// \param cbranch is the given condition branch +void ValueSetSolver::constraintsFromCBranch(PcodeOp *cbranch) + +{ + Varnode *vn = cbranch->getIn(1); // Get Varnode deciding the condition + while(!vn->isMark()) { + if (!vn->isWritten()) break; + PcodeOp *op = vn->getDef(); + if (op->isCall() || op->isMarker()) + break; + int4 num = op->numInput(); + if (num == 0 || num > 2) break; + vn = op->getIn(0); + if (num == 2) { + if (vn->isConstant()) + vn = op->getIn(1); + else if (!op->getIn(1)->isConstant()) { + // If we reach here, both inputs are non-constant + generateRelativeConstraint(op, cbranch); + return; + } + // If we reach here, vn is non-constant, other input is constant + } + } + if (vn->isMark()) { + CircleRange lift(true); + Varnode *startVn = cbranch->getIn(1); + constraintsFromPath(0,lift,startVn,vn,cbranch); + } +} + +/// Given a complete data-flow system of Varnodes, look for any \e constraint: +/// - For a particular Varnode +/// - A limited set of values +/// - Due to its involvement in a branch condition +/// - Which applies at a particular \e read of the Varnode +/// +/// \param worklist is the set of Varnodes in the data-flow system (all marked) +/// \param reads is the additional set of PcodeOps that read a Varnode from the system +void ValueSetSolver::generateConstraints(const vector &worklist,const vector &reads) + +{ + vector blockList; + for(int4 i=0;igetDef(); + if (op == (PcodeOp *)0) continue; + BlockBasic *bl = (BlockBasic *)op->getParent()->getImmedDom(); + while(bl != (FlowBlock *)0) { + if (!bl->isMark()) { + bl->setMark(); + blockList.push_back(bl); + PcodeOp *lastOp = bl->lastOp(); + if (lastOp != (PcodeOp *)0 && lastOp->code() == CPUI_CBRANCH) { + constraintsFromCBranch(lastOp); + } + bl = (BlockBasic *)bl->getImmedDom(); + } + else + break; + } + } + for(int4 i=0;igetParent()->getImmedDom(); + while(bl != (FlowBlock *)0) { + if (!bl->isMark()) { + bl->setMark(); + blockList.push_back(bl); + PcodeOp *lastOp = bl->lastOp(); + if (lastOp != (PcodeOp *)0 && lastOp->code() == CPUI_CBRANCH) { + constraintsFromCBranch(lastOp); + } + bl = (BlockBasic *)bl->getImmedDom(); + } + else + break; + } + } + for(int4 i=0;iclearMark(); +} + +/// Verify that the given Varnode is produced by a straight line sequence of +/// COPYs, INT_ADDs with a constant, from the base register marked as \e relative +/// for our system. +/// \param vn is the given Varnode +/// \param typeCode will hold the base register code (if found) +/// \param value will hold the additive value relative to the base register (if found) +/// \return \b true if the Varnode is a \e relative constant +bool ValueSetSolver::checkRelativeConstant(Varnode *vn,int4 &typeCode,uintb &value) const + +{ + value = 0; + for(;;) { + if (vn->isMark()) { + ValueSet *valueSet = vn->getValueSet(); + if (valueSet->typeCode != 0) { + typeCode = valueSet->typeCode; + break; + } + } + if (!vn->isWritten()) return false; + PcodeOp *op = vn->getDef(); + OpCode opc = op->code(); + if (opc == CPUI_COPY || opc == CPUI_INDIRECT) + vn = op->getIn(0); + else if (opc == CPUI_INT_ADD || opc == CPUI_PTRSUB) { + Varnode *constVn = op->getIn(1); + if (!constVn->isConstant()) + return false; + value = (value + constVn->getOffset()) & calc_mask(constVn->getSize()); + vn = op->getIn(0); + } + else + return false; + } + return true; // Never reach here +} + +/// Given a binary PcodeOp producing a conditional branch, check if it can be interpreted +/// as a constraint relative to (the) base register specified for this system. If it can +/// be, a \e relative Equation is generated, which will apply to \e relative ValueSets. +/// \param compOp is the comparison PcodeOp +/// \param cbranch is the conditional branch +void ValueSetSolver::generateRelativeConstraint(PcodeOp *compOp,PcodeOp *cbranch) + +{ + OpCode opc = compOp->code(); + switch(opc) { + case CPUI_INT_LESS: + opc = CPUI_INT_SLESS; // Treat unsigned pointer comparisons as signed relative to the base register + break; + case CPUI_INT_LESSEQUAL: + opc = CPUI_INT_SLESSEQUAL; + break; + case CPUI_INT_SLESS: + case CPUI_INT_SLESSEQUAL: + case CPUI_INT_EQUAL: + case CPUI_INT_NOTEQUAL: + break; + default: + return; + } + int4 typeCode; + uintb value; + Varnode *vn; + Varnode *inVn0 = compOp->getIn(0); + Varnode *inVn1 = compOp->getIn(1); + CircleRange lift(true); + if (checkRelativeConstant(inVn0, typeCode, value)) { + vn = inVn1; + if (!lift.pullBackBinary(opc, value, 1, vn->getSize(), 1)) + return; + } + else if (checkRelativeConstant(inVn1,typeCode,value)) { + vn = inVn0; + if (!lift.pullBackBinary(opc, value, 0, vn->getSize(), 1)) + return; + } + else + return; // Neither side looks like a relative constant + + Varnode *endVn = vn; + while(!endVn->isMark()) { + if (!endVn->isWritten()) return; + PcodeOp *op = endVn->getDef(); + if (op->code() != CPUI_COPY && op->code() != CPUI_INDIRECT) + return; + endVn = op->getIn(0); + } + if (endVn != (Varnode *)0) + constraintsFromPath(typeCode,lift,endVn,endVn,cbranch); +} + +/// \brief Build value sets for a data-flow system +/// +/// Given a set of sinks, find all the Varnodes that flow directly into them and set up their +/// initial ValueSet objects. +/// \param sinks is the list terminating Varnodes +/// \param reads are add-on PcodeOps where we would like to know input ValueSets at the point of read +/// \param stackReg (if non-NULL) gives the stack pointer (for keeping track of relative offsets) +/// \param indirectAsCopy is \b true if solver should treat CPUI_INDIRECT as CPUI_COPY operations +void ValueSetSolver::establishValueSets(const vector &sinks,const vector &reads,Varnode *stackReg, + bool indirectAsCopy) + +{ + vector worklist; + int4 workPos = 0; + if (stackReg != (Varnode *)0) { + newValueSet(stackReg,1); // Establish stack pointer as special + stackReg->setMark(); + worklist.push_back(stackReg); + workPos += 1; + rootNodes.push_back(stackReg->getValueSet()); + } + for(int4 i=0;isetMark(); + worklist.push_back(vn); + } + while(workPos < worklist.size()) { + Varnode *vn = worklist[workPos]; + workPos += 1; + if (!vn->isWritten()) { + if (vn->isConstant()) { + // Constant inputs to binary ops should not be treated as root nodes as they + // get picked up during iteration by the other input, except in the case of a + // a PTRSUB from a spacebase constant. + if (vn->isSpacebase() || vn->loneDescend()->numInput() == 1) + rootNodes.push_back(vn->getValueSet()); + } + else + rootNodes.push_back(vn->getValueSet()); + continue; + } + PcodeOp *op = vn->getDef(); + switch(op->code()) { // Distinguish ops where we can never predict an integer range + case CPUI_INDIRECT: + if (indirectAsCopy) { + Varnode *inVn = op->getIn(0); + if (!inVn->isMark()) { + newValueSet(inVn,0); + inVn->setMark(); + worklist.push_back(inVn); + } + } + else { + vn->getValueSet()->setFull(); + rootNodes.push_back(vn->getValueSet()); + } + break; + case CPUI_CALL: + case CPUI_CALLIND: + case CPUI_CALLOTHER: + case CPUI_LOAD: + case CPUI_NEW: + case CPUI_SEGMENTOP: + case CPUI_CPOOLREF: + case CPUI_FLOAT_ADD: + case CPUI_FLOAT_DIV: + case CPUI_FLOAT_MULT: + case CPUI_FLOAT_SUB: + case CPUI_FLOAT_NEG: + case CPUI_FLOAT_ABS: + case CPUI_FLOAT_SQRT: + case CPUI_FLOAT_INT2FLOAT: + case CPUI_FLOAT_FLOAT2FLOAT: + case CPUI_FLOAT_TRUNC: + case CPUI_FLOAT_CEIL: + case CPUI_FLOAT_FLOOR: + case CPUI_FLOAT_ROUND: + vn->getValueSet()->setFull(); + rootNodes.push_back(vn->getValueSet()); + break; + default: + for(int4 i=0;inumInput();++i) { + Varnode *inVn = op->getIn(i); + if (inVn->isMark() || inVn->isAnnotation()) continue; + newValueSet(inVn,0); + inVn->setMark(); + worklist.push_back(inVn); + } + break; + } + } + for(int4 i=0;inumInput();++slot) { + Varnode *vn = op->getIn(slot); + if (vn->isMark()) { + readNodes[op->getSeqNum()].setPcodeOp(op, slot); + op->setMark(); // Mark read ops for equation generation stage + break; // Only 1 read allowed + } + } + } + generateConstraints(worklist,reads); + for(int4 i=0;iclearMark(); // Clear marks on read ops + + establishTopologicalOrder(); + for(int4 i=0;iclearMark(); +} + +/// The ValueSets are recalculated in the established topological ordering, with looping +/// at various levels until a fixed point is reached. +/// \param max is the maximum number of iterations to allow before forcing termination +/// \param widener is the Widening strategy to use to accelerate stabilization +void ValueSetSolver::solve(int4 max,Widener &widener) + +{ + maxIterations = max; + numIterations = 0; + for(list::iterator iter=valueNodes.begin();iter!=valueNodes.end();++iter) + (*iter).count = 0; + + vector componentStack; + Partition *curComponent = (Partition *)0; + ValueSet *curSet = orderPartition.startNode; + + while(curSet != (ValueSet *)0) { + numIterations += 1; + if (numIterations > maxIterations) break; // Quit if max iterations exceeded + if (curSet->partHead != (Partition *)0 && curSet->partHead != curComponent) { + componentStack.push_back(curSet->partHead); + curComponent = curSet->partHead; + curComponent->isDirty = false; + // Reset component counter upon entry + curComponent->startNode->count = widener.determineIterationReset(*curComponent->startNode); + } + if (curComponent != (Partition *)0) { + if (curSet->iterate(widener)) + curComponent->isDirty = true; + if (curComponent->stopNode != curSet) { + curSet = curSet->next; + } + else { + for(;;) { + if (curComponent->isDirty) { + curComponent->isDirty = false; + curSet = curComponent->startNode; + if (componentStack.size() > 1) { // Mark parent as dirty if we are restarting dirty child + componentStack[componentStack.size()-2]->isDirty = true; + } + break; + } + + componentStack.pop_back(); + if (componentStack.empty()) { + curComponent = (Partition *)0; + curSet = curSet->next; + break; + } + curComponent = componentStack.back(); + if (curComponent->stopNode != curSet) { + curSet = curSet->next; + break; + } + } + } + } + else { + curSet->iterate(widener); + curSet = curSet->next; + } + } + map::iterator riter; + for(riter=readNodes.begin();riter!=readNodes.end();++riter) + (*riter).second.compute(); // Calculate any follow-on value sets +} diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.hh index c0df4a28ae..d50101d847 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.hh @@ -51,35 +51,269 @@ class CircleRange { uintb mask; ///< Bit mask defining the size (modulus) and stop of the range bool isempty; ///< \b true if set is empty int4 step; ///< Explicit step size - int4 shift; ///< Number of bits in step. Equal to log2(step) static const char arrange[]; ///< Map from raw overlaps to normalized overlap code - void calcStepShift(void); ///< Calculate explicit \b step and \b skip from \b mask + void normalize(void); ///< Normalize the representation of full sets void complement(void); ///< Set \b this to the complement of itself - void convertToBoolean(void); ///< Convert \b this to boolean. - static bool newStride(uintb newmask,uintb &myleft,uintb &myright); ///< Recalculate range based on new size and stride + bool convertToBoolean(void); ///< Convert \b this to boolean. + static bool newStride(uintb mask,int4 step,int4 oldStep,uint4 rem,uintb &myleft,uintb &myright); + static bool newDomain(uintb newMask,int4 newStep,uintb &myleft,uintb &myright); static char encodeRangeOverlaps(uintb op1left,uintb op1right,uintb op2left,uintb op2right); ///< Calculate overlap code public: CircleRange(void) { isempty=true; } ///< Construct an empty range - CircleRange(uintb mn,uintb mx,uintb m); ///< Construct given specific boundaries. + CircleRange(uintb lft,uintb rgt,int4 size,int4 stp); ///< Construct given specific boundaries. CircleRange(bool val); ///< Construct a boolean range CircleRange(uintb val,int4 size); ///< Construct range with single value + void setRange(uintb lft,uintb rgt,int4 size,int4 step); ///< Set directly to a specific range + void setRange(uintb val,int4 size); ///< Set range with a single value + void setFull(int4 size); ///< Set a completely full range bool isEmpty(void) const { return isempty; } ///< Return \b true if \b this range is empty + bool isFull(void) const { return ((!isempty) && (step == 1) && (left == right)); } ///< Return \b true if \b this contains all possible values + bool isSingle(void) const { return (!isempty) && (right == ((left + step)& mask)); } ///< Return \b true if \b this contains single value uintb getMin(void) const { return left; } ///< Get the left boundary of the range uintb getMax(void) const { return (right-step)&mask; } ///< Get the right-most integer contained in the range uintb getEnd(void) const { return right; } ///< Get the right boundary of the range uintb getMask(void) const { return mask; } ///< Get the mask uintb getSize(void) const; ///< Get the size of this range + int4 getStep(void) const { return step; } ///< Get the step for \b this range + int4 getMaxInfo(void) const; ///< Get maximum information content of range + bool operator==(const CircleRange &op2) const; ///< Equals operator bool getNext(uintb &val) const { val = (val+step)&mask; return (val!=right); } ///< Advance an integer within the range bool contains(const CircleRange &op2) const; ///< Check containment of another range in \b this. bool contains(uintb val) const; ///< Check containment of a specific integer. int4 intersect(const CircleRange &op2); ///< Intersect \b this with another range bool setNZMask(uintb nzmask,int4 size); ///< Set the range based on a putative mask. int4 circleUnion(const CircleRange &op2); ///< Union two ranges. - void setStride(int4 newshift); ///< Set a new stride on \b this range. + bool minimalContainer(const CircleRange &op2,int4 maxStep); ///< Construct minimal range that contains both \b this and another range + int4 invert(void); ///< Convert to complementary range + void setStride(int4 newStep,uintb rem); ///< Set a new step on \b this range. + bool pullBackUnary(OpCode opc,int4 inSize,int4 outSize); ///< Pull-back \b this through the given unary operator + bool pullBackBinary(OpCode opc,uintb val,int4 slot,int4 inSize,int4 outSize); ///< Pull-back \b this thru binary operator Varnode *pullBack(PcodeOp *op,Varnode **constMarkup,bool usenzmask); ///< Pull-back \b this range through given PcodeOp. + bool pushForwardUnary(OpCode opc,const CircleRange &in1,int4 inSize,int4 outSize); ///< Push-forward thru given unary operator + bool pushForwardBinary(OpCode opc,const CircleRange &in1,const CircleRange &in2,int4 inSize,int4 outSize,int4 maxStep); + bool pushForwardTrinary(OpCode opc,const CircleRange &in1,const CircleRange &in2,const CircleRange &in3, + int4 inSize,int4 outSize,int4 maxStep); + void widen(const CircleRange &op2,bool leftIsStable); ///< Widen the unstable bound to match containing range int4 translate2Op(OpCode &opc,uintb &c,int4 &cslot) const; ///< Translate range to a comparison op + void printRaw(ostream &s) const; ///< Write a text representation of \b this to stream }; +class Partition; // Forward declaration +class Widener; // Forward declaration + +/// \brief A range of values attached to a Varnode within a data-flow subsystem +/// +/// This class acts as both the set of values for the Varnode and as a node in a +/// sub-graph overlaying the full data-flow of the function containing the Varnode. +/// The values are stored in the CircleRange field and can be interpreted either as +/// absolute values (if \b typeCode is 0) or as values relative to a stack pointer +/// or some other register (if \b typeCode is non-zero). +class ValueSet { +public: + static const int4 MAX_STEP; ///< Maximum step inferred for a value set + /// \brief An external that can be applied to a ValueSet + /// + /// An Equation is attached to a particular ValueSet and its underlying Varnode + /// providing additional restriction on the ValueSet of an input parameter of the + /// operation producing the Varnode. + class Equation { + friend class ValueSet; + int4 slot; ///< The input parameter slot to which the constraint is attached + int4 typeCode; ///< The constraint characteristic 0=absolute 1=relative to a spacebase register + CircleRange range; ///< The range constraint + public: + Equation(int4 s,int4 tc,const CircleRange &rng) { slot=s; typeCode = tc; range = rng; } ///< Constructor + }; +private: + friend class ValueSetSolver; + int4 typeCode; ///< 0=pure constant 1=stack relative + int4 numParams; ///< Number of input parameters to defining operation + int4 count; ///< Depth first numbering / widening count + OpCode opCode; ///< Op-code defining Varnode + bool leftIsStable; ///< Set to \b true if left boundary of range didn't change (last iteration) + bool rightIsStable; ///< Set to \b true if right boundary of range didn't change (last iteration) + Varnode *vn; ///< Varnode whose set this represents + CircleRange range; ///< Range of values or offsets in this set + vector equations; ///< Any equations associated with this value set + Partition *partHead; ///< If Varnode is a component head, pointer to corresponding Partition + ValueSet *next; ///< Next ValueSet to iterate + bool doesEquationApply(int4 num,int4 slot) const; ///< Does the indicated equation apply for the given input slot + void setFull(void) { range.setFull(vn->getSize()); typeCode = 0; } ///< Mark value set as possibly containing any value + void setVarnode(Varnode *v,int4 tCode); ///< Attach \b this to given Varnode and set initial values + void addEquation(int4 slot,int4 type,const CircleRange &constraint); ///< Insert an equation restricting \b this value set + void addLandmark(int4 type,const CircleRange &constraint) { addEquation(numParams,type,constraint); } ///< Add a widening landmark + bool computeTypeCode(void); ///< Figure out if \b this value set is absolute or relative + bool iterate(Widener &widener); ///< Regenerate \b this value set from operator inputs +public: + int4 getCount(void) const { return count; } ///< Get the current iteration count + const CircleRange *getLandMark(void) const; ///< Get any \e landmark range + int4 getTypeCode(void) const { return typeCode; } ///< Return '0' for normal constant, '1' for spacebase relative + Varnode *getVarnode(void) const { return vn; } ///< Get the Varnode attached to \b this ValueSet + const CircleRange &getRange(void) const { return range; } ///< Get the actual range of values + bool isLeftStable(void) const { return leftIsStable; } + bool isRightStable(void) const { return rightIsStable; } + void printRaw(ostream &s) const; ///< Write a text description of \b to the given stream +}; + +/// \brief A range of nodes (within the weak topological ordering) that are iterated together +class Partition { + friend class ValueSetSolver; + ValueSet *startNode; ///< Starting node of component + ValueSet *stopNode; ///< Ending node of component + bool isDirty; ///< Set to \b true if a node in \b this component has changed this iteration +public: + Partition(void) { + startNode = (ValueSet *)0; stopNode = (ValueSet *)0; isDirty = false; + } ///< Construct empty partition +}; + +/// \brief A special form of ValueSet associated with the \e read \e point of a Varnode +/// +/// When a Varnode is read, it may have a more restricted range at the point of the read +/// compared to the full scope. This class officially stores the value set at the point +/// of the read (specified by PcodeOp and slot). It is computed as a final step after +/// the main iteration has completed. +class ValueSetRead { + friend class ValueSetSolver; + int4 typeCode; ///< 0=pure constant 1=stack relative + int4 slot; ///< The slot being read + PcodeOp *op; ///< The PcodeOp at the point of the value set read + CircleRange range; ///< Range of values or offsets in this set + CircleRange equationConstraint; ///< Constraint associated with the equation + int4 equationTypeCode; ///< Type code of the associated equation + bool leftIsStable; ///< Set to \b true if left boundary of range didn't change (last iteration) + bool rightIsStable; ///< Set to \b true if right boundary of range didn't change (last iteration) + void setPcodeOp(PcodeOp *o,int4 slt); ///< Establish \e read this value set corresponds to + void addEquation(int4 slt,int4 type,const CircleRange &constraint); ///< Insert an equation restricting \b this value set +public: + int4 getTypeCode(void) const { return typeCode; } ///< Return '0' for normal constant, '1' for spacebase relative + const CircleRange &getRange(void) const { return range; } ///< Get the actual range of values + bool isLeftStable(void) const { return leftIsStable; } + bool isRightStable(void) const { return rightIsStable; } + void compute(void); ///< Compute \b this value set + void printRaw(ostream &s) const; ///< Write a text description of \b to the given stream +}; + +class Widener { +public: + virtual ~Widener(void) {} ///< Destructor + + /// \brief Upon entering a fresh partition, determine how the given ValueSet count should be reset + /// + /// \param valueSet is the given value set + /// \return the value of the iteration counter to reset to + virtual int4 determineIterationReset(const ValueSet &valueSet)=0; + + /// \brief Check if the given value set has been frozen for the remainder of the iteration process + /// + /// \param valueSet is the given value set + /// \return \b true if the valueSet will no longer change + virtual bool checkFreeze(const ValueSet &valueSet)=0; + + /// \brief For an iteration that isn't stabilizing attempt to widen the given ValueSet + /// + /// Change the given range based on its previous iteration so that it stabilizes more + /// rapidly on future iterations. + /// \param valueSet is the given value set + /// \param range is the previous form of the given range (and storage for the widening result) + /// \param newRange is the current iteration of the given range + /// \return \b true if widening succeeded + virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange)=0; +}; + +/// \brief Class for doing normal widening +/// +/// Widening is attempted at a specific iteration. If a landmark is available, it is used +/// to do a controlled widening, holding the stable range boundary constant. Otherwise a +/// full range is produced. At a later iteration, a full range is produced automatically. +class WidenerFull : public Widener { + int4 widenIteration; ///< The iteration at which widening is attempted + int4 fullIteration; ///< The iteration at which a full range is produced +public: + WidenerFull(void) { widenIteration = 2; fullIteration = 5; } ///< Constructor with default iterations + WidenerFull(int4 wide,int4 full) { widenIteration = wide; fullIteration = full; } ///< Constructor specifying iterations + virtual int4 determineIterationReset(const ValueSet &valueSet); + virtual bool checkFreeze(const ValueSet &valueSet); + virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange); +}; + +class WidenerNone : public Widener { + int4 freezeIteration; ///< The iteration at which all change ceases +public: + WidenerNone(void) { freezeIteration = 3; } + virtual int4 determineIterationReset(const ValueSet &valueSet); + virtual bool checkFreeze(const ValueSet &valueSet); + virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange); +}; + +/// \brief Class the determines a ValueSet for each Varnode in a data-flow system +/// +/// This class uses \e value \e set \e analysis to calculate (an overestimation of) +/// the range of values that can reach each Varnode. The system is formed by providing +/// a set of Varnodes for which the range is desired (the sinks) via establishValueSets(). +/// This creates a system of Varnodes (within the single function) that can flow to the sinks. +/// Running the method solve() does the analysis, and the caller can examine the results +/// by examining the ValueSet attached to any of the Varnodes in the system (via Varnode::getValueSet()). +class ValueSetSolver { + /// \brief An iterator over out-bound edges for a single ValueSet node in a data-flow system + /// + /// This is a helper class for walking a collection of ValueSets as a graph. + /// Mostly the graph mirrors the data-flow of the Varnodes underlying the ValueSets, but + /// there is support for a simulated root node. This class acts as an iterator over the outgoing + /// edges of a particular ValueSet in the graph. + class ValueSetEdge { + const vector *rootEdges; ///< The list of nodes attached to the simulated root node (or NULL) + int4 rootPos; ///< The iterator position for the simulated root node + Varnode *vn; ///< The Varnode attached to a normal ValueSet node (or NULL) + list::const_iterator iter; ///< The iterator position for a normal ValueSet node + public: + ValueSetEdge(ValueSet *node,const vector &roots); + ValueSet *getNext(void); + }; + + list valueNodes; ///< Storage for all the current value sets + map readNodes; ///< Additional, after iteration, add-on value sets + Partition orderPartition; ///< Value sets in iteration order + list recordStorage; ///< Storage for the Partitions establishing components + vector rootNodes; ///< Values treated as inputs + vector nodeStack; ///< Stack used to generate the topological ordering + int4 depthFirstIndex; ///< (Global) depth first numbering for topological ordering + int4 numIterations; ///< Count of individual ValueSet iterations + int4 maxIterations; ///< Maximum number of iterations before forcing termination + void newValueSet(Varnode *vn,int4 tCode); ///< Allocate storage for a new ValueSet + static void partitionPrepend(ValueSet *vertex,Partition &part); ///< Prepend a vertex to a partition + static void partitionPrepend(const Partition &head,Partition &part); ///< Prepend full Partition to given Partition + void partitionSurround(Partition &part); ///< Create a full partition component + void component(ValueSet *vertex,Partition &part); ///< Generate a partition component given its head + int4 visit(ValueSet *vertex,Partition &part); ///< Recursively walk the data-flow graph finding partitions + void establishTopologicalOrder(void); ///< Find the optimal order for iterating through the ValueSets + void applyConstraints(Varnode *vn,int4 type,const CircleRange &range,PcodeOp *cbranch); + void constraintsFromPath(int4 type,CircleRange &lift,Varnode *startVn,Varnode *endVn,PcodeOp *cbranch); + void constraintsFromCBranch(PcodeOp *cbranch); ///< Generate constraints arising from the given branch + void generateConstraints(const vector &worklist,const vector &reads); ///< Generate constraints given a system of Varnodes + bool checkRelativeConstant(Varnode *vn,int4 &typeCode,uintb &value) const; ///< Check if the given Varnode is a \e relative constant + void generateRelativeConstraint(PcodeOp *compOp,PcodeOp *cbranch); ///< Try to find a \e relative constraint +public: + void establishValueSets(const vector &sinks,const vector &reads,Varnode *stackReg,bool indirectAsCopy); + int4 getNumIterations(void) const { return numIterations; } ///< Get the current number of iterations + void solve(int4 max,Widener &widener); ///< Iterate the ValueSet system until it stabilizes + list::const_iterator beginValueSets(void) const { return valueNodes.begin(); } ///< Start of all ValueSets in the system + list::const_iterator endValueSets(void) const { return valueNodes.end(); } ///< End of all ValueSets in the system + map::const_iterator beginValueSetReads(void) const { return readNodes.begin(); } ///< Start of ValueSetReads + map::const_iterator endValueSetReads(void) const { return readNodes.end(); } ///< End of ValueSetReads + const ValueSetRead &getValueSetRead(const SeqNum &seq) { return (*readNodes.find(seq)).second; } ///< Get ValueSetRead by SeqNum +}; + +/// \param op2 is the range to compare \b this to +/// \return \b true if the two ranges are equal +inline bool CircleRange::operator==(const CircleRange &op2) const + +{ + if (isempty != op2.isempty) return false; + if (isempty) return true; + return (left == op2.left) && (right == op2.right) && (mask == op2.mask) && (step == op2.step); +} + /// If two ranges are labeled [l , r) and [op2.l, op2.r), the /// overlap of the ranges can be characterized by listing the four boundary /// values in order, as the circle is traversed in a clock-wise direction. This characterization can be @@ -111,4 +345,43 @@ inline char CircleRange::encodeRangeOverlaps(uintb op1left, uintb op1right, uint return arrange[val]; } +/// Perform basic checks that the selected Equation exists and applies +/// to the indicated input slot. +/// \param num is the index selecting an Equation +/// \param slot is the indicated slot +/// \return \b true if the Equation exists and applies +inline bool ValueSet::doesEquationApply(int4 num,int4 slot) const + +{ + if (num < equations.size()) { + if (equations[num].slot == slot) { + if (equations[num].typeCode == typeCode) + return true; + } + } + return false; +} + +/// \param vertex is the node that will be prepended +/// \param part is the Partition being modified +inline void ValueSetSolver::partitionPrepend(ValueSet *vertex,Partition &part) + +{ + vertex->next = part.startNode; // Attach new vertex to beginning of list + part.startNode = vertex; // Change the first value set to be the new vertex + if (part.stopNode == (ValueSet *)0) + part.stopNode = vertex; +} + +/// \param head is the partition to be prepended +/// \param part is the given partition being modified (prepended to) +inline void ValueSetSolver::partitionPrepend(const Partition &head,Partition &part) + +{ + head.stopNode->next = part.startNode; + part.startNode = head.startNode; + if (part.stopNode == (ValueSet *)0) + part.stopNode = head.stopNode; +} + #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc index 529fe2fd21..a691a6f93d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc @@ -308,10 +308,10 @@ uintb AddrSpace::read(const string &s,int4 &size) const offset = addressToByte(offset,wordsize); enddata = (const char *) tmpdata; if (enddata - s.c_str() == s.size()) { // If no size or offset override - size = getAddrSize(); // Return "natural" size + size = manage->getDefaultSize(); // Return "natural" size return offset; } - size = getAddrSize(); + size = manage->getDefaultSize(); } if (append != string::npos) { enddata = s.c_str()+append; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh index ff1f81fc26..f0cf10be50 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh @@ -28,6 +28,7 @@ class VarnodeBank; class Merge; class Funcdata; class SymbolEntry; +class ValueSet; /// \brief Compare two Varnode pointers by location then definition struct VarnodeCompareLocDef { @@ -134,7 +135,10 @@ private: VarnodeDefSet::iterator defiter; ///< Iterator into VarnodeBank sorted by definition list descend; ///< List of every op using this varnode as input mutable Cover *cover; ///< Addresses covered by the def->use of this Varnode - mutable Datatype *temptype; ///< For type propagate algorithm + mutable union { + Datatype *dataType; ///< For type propagate algorithm + ValueSet *valueSet; + } temp; uintb consumed; ///< What parts of this varnode are used uintb nzm; ///< Which bits do we know are zero friend class VarnodeBank; @@ -167,8 +171,10 @@ public: SymbolEntry *getSymbolEntry(void) const { return mapentry; } ///< Get symbol and scope information associated with this Varnode uint4 getFlags(void) const { return flags; } ///< Get all the boolean attributes Datatype *getType(void) const { return type; } ///< Get the Datatype associated with this Varnode - void setTempType(Datatype *t) const { temptype = t; } ///< Set the temporary Datatype - Datatype *getTempType(void) const { return temptype; } ///< Get the temporary Datatype (used during type propagation) + void setTempType(Datatype *t) const { temp.dataType = t; } ///< Set the temporary Datatype + Datatype *getTempType(void) const { return temp.dataType; } ///< Get the temporary Datatype (used during type propagation) + void setValueSet(ValueSet *v) const { temp.valueSet = v; } ///< Set the temporary ValueSet record + ValueSet *getValueSet(void) const { return temp.valueSet; } ///< Get the temporary ValueSet record uint4 getCreateIndex(void) const { return create_index; } ///< Get the creation index Cover *getCover(void) const { updateCover(); return cover; } ///< Get Varnode coverage information list::const_iterator beginDescend(void) const { return descend.begin(); } ///< Get iterator to list of syntax tree descendants (reads)