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/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc index af165006a9..4e3ce8b487 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,28 @@ 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"); + + Varnode *vn = iface_read_varnode(dcp,s); + vector sinks; + sinks.push_back(vn); + Varnode *stackReg = dcp->fd->findSpacebaseInput(dcp->conf->getStackSpace()); + ValueSetSolver vsSolver; + vsSolver.establishValueSets(sinks, stackReg); + vsSolver.solve(10000); + list::const_iterator iter; + for(iter=vsSolver.beginValueSets();iter!=vsSolver.endValueSets();++iter) { + (*iter).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..08abab22a7 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 mn is the left boundary of the range +/// \param mx 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 = bRight; + } + 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,262 @@ 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_COPY: + *this = in1; + break; + case CPUI_INT_ZEXT: + isempty = false; + step = in1.step; + mask = calc_mask(outSize); + if (in1.right < in1.left) { // Extending causes 2 pieces + left = 0; + right = in1.mask; + } + else { + left = in1.left; + right = in1.right; + } + 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, inSize, outSize); + if ((intb)right < (intb)left) { + right = calc_mask(inSize); + left = calc_mask(outSize) ^ right; + right += 1; + } + 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; + 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_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 >> sa; + } + 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; + } + default: + return false; + } + return true; +} + +/// 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 +1372,646 @@ int4 CircleRange::translate2Op(OpCode &opc,uintb &c,int4 &cslot) const } return 2; // Cannot represent } + +/// \param 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 << ')'; + } +} + +/// 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 + } + 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(); + } + else if (vn->isConstant()) { + opCode = CPUI_MAX; + numParams = 0; + range.setRange(vn->getOffset(),vn->getSize()); + } + else { // Some other form of input + opCode = CPUI_MAX; + numParams = 0; + range.setFull(vn->getSize()); + } +} + +/// Equations are stored as an array of (slot,range) pairs, ordered on slot. +/// \param slot is the given slot +/// \param range is the given range +void ValueSet::addEquation(int4 slot,const CircleRange &constraint) + +{ + vector::iterator iter; + iter = equations.begin(); + while(iter != equations.end()) { + if ((*iter).slot > slot) + break; + ++iter; + } + equations.insert(iter,Equation(slot,constraint)); +} + +/// For an iteration that isn't stabilizing attempt to widen \b this value set +/// so that it stabilizes more rapidly on future iterations. For initial iterations, +/// targeted widening is attempted using landmarks, otherwise \b this is widened +/// to \e everything. +/// \param newRange is the changed range for the current iteration +void ValueSet::doWidening(const CircleRange &newRange) + +{ + count += 1; + if (count < 2) { + range = newRange; + return; + } + else if (count == 2) { + if (!equations.empty()) { // Look for landmark + const Equation &landmark(equations.back()); + if (landmark.slot == numParams) { + bool leftIsStable = range.getMin() == newRange.getMin(); + if (landmark.range.contains(range)) { + range.widen(landmark.range,leftIsStable); + return; + } + else { + CircleRange constraint = landmark.range; + constraint.invert(); + if (constraint.contains(range)) { + range.widen(constraint,leftIsStable); + return; + } + } + } + } + } + else if (count <5) { + range = newRange; + return; + } + range.setFull(vn->getSize()); // In all other cases expand to everything +} + +/// Give \b this value set a chance to reset its counter do to looping +void ValueSet::looped(void) + +{ + if (count > 2) + count = 2; // Reset to point just after any widening +} + +/// 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(void) + +{ + if (!vn->isWritten()) return false; + CircleRange res; + PcodeOp *op = vn->getDef(); + int4 eqPos = 0; + if (opCode == CPUI_MULTIEQUAL) { + int4 pieces = 0; + for(int4 i=0;igetIn(i)->getValueSet(); + if (eqPos < equations.size() && equations[eqPos].slot == 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,32)) // Could not get clean union, force it + break; + } + } + if (0 != res.circleUnion(range)) { // Union with the previous iteration's set + res.minimalContainer(range,32); + } + } + else if (numParams == 1) { + ValueSet *inSet1 = op->getIn(0)->getValueSet(); + if (eqPos < equations.size() && equations[eqPos].slot == 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())) + res.setFull(vn->getSize()); + eqPos += 1; + } + else if (!res.pushForwardUnary(opCode, inSet1->range, inSet1->vn->getSize(), vn->getSize())) { + res.setFull(vn->getSize()); + } + typeCode = inSet1->typeCode; + } + else if (numParams == 2) { + ValueSet *inSet1 = op->getIn(0)->getValueSet(); + ValueSet *inSet2 = op->getIn(1)->getValueSet(); + typeCode = inSet1->typeCode + inSet2->typeCode; + if (inSet1->typeCode != 0 && inSet2->typeCode != 0) { // Combining two based constants + typeCode = 0; + res.setFull(vn->getSize()); + } + else if (equations.size() == 0) { + if (!res.pushForwardBinary(opCode, inSet1->range, inSet2->range, inSet1->vn->getSize(), vn->getSize(), 32)) + res.setFull(vn->getSize()); + } + else { + CircleRange range1 = inSet1->range; + CircleRange range2 = inSet2->range; + if (equations[eqPos].slot == 0) { + if (0 != range1.intersect(equations[eqPos].range)) + range1 = equations[eqPos].range; + eqPos += 1; + } + if (eqPos < equations.size() && equations[eqPos].slot == 1) { + if (0 != range2.intersect(equations[eqPos].range)) + range2 = equations[eqPos].range; + } + if (!res.pushForwardBinary(opCode, range1, range2, inSet1->vn->getSize(), vn->getSize(), 32)) + res.setFull(vn->getSize()); + } + } + else + return false; // No way to change this value set + + if (res == range) + return false; + if (partHead != (Partition *)0) + doWidening(res); + else + range = res; + return true; +} + +/// \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); +} + +/// \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 range is the known constraint (assuming the \b true branch was taken) +/// \param splitPoint is the basic block making the conditional branch +void ValueSetSolver::applyConstraints(Varnode *vn,const CircleRange &range,FlowBlock *splitPoint) + +{ + list::const_iterator iter; + FlowBlock *defBlock = (FlowBlock *)0; + if (vn->isWritten()) { + defBlock = vn->getDef()->getParent(); + ValueSet *vSet = vn->getValueSet(); + if (vSet->opCode == CPUI_MULTIEQUAL) { + vSet->addLandmark(range); // Leave landmark for widening + } + } + FlowBlock *trueBlock = splitPoint->getTrueOut(); + FlowBlock *falseBlock = splitPoint->getFalseOut(); + for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { + PcodeOp *op = *iter; + Varnode *outVn = op->getOut(); + if (outVn == (Varnode *)0) continue; + if (!outVn->isMark()) continue; + FlowBlock *curBlock = op->getParent(); + for(;;) { + if (curBlock == trueBlock) { + outVn->getValueSet()->addEquation(op->getSlot(vn), range); + break; + } + else if (curBlock == falseBlock) { + CircleRange falseRange(range); + falseRange.invert(); + outVn->getValueSet()->addEquation(op->getSlot(vn), falseRange); + break; + } + else if (curBlock == splitPoint || curBlock == (FlowBlock *)0 + || curBlock == defBlock) + break; + curBlock = curBlock->getImmedDom(); + } + } +} + +/// Knowing that there is a lifting path from the given CBRANCH to the given Varnode +/// in the system, go ahead and left the \b true value range to a constraint range +/// on the Varnode. Then look for reads of the Varnode where the constraint range applies. +/// \param vn is the given Varnode in the system +/// \param cbranch is the given CBRANCH +void ValueSetSolver::constraintsFromPath(Varnode *vn,PcodeOp *cbranch) + +{ + CircleRange lift(true); + Varnode *curVn = cbranch->getIn(1); + while(curVn != vn) { + Varnode *constVn; + curVn = lift.pullBack(curVn->getDef(),&constVn,false); + if (curVn == (Varnode *)0) return; // Couldn't pull all the back to our value set + } + FlowBlock *splitPoint = cbranch->getParent(); + for(;;) { + Varnode *constVn; + applyConstraints(vn,lift,splitPoint); + if (!vn->isWritten()) break; + PcodeOp *op = vn->getDef(); + if (op->isCall() || op->isMarker()) break; + vn = lift.pullBack(op,&constVn,false); + if (vn == (Varnode *)0) break; + if (!vn->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 (vn->isConstant() && num == 2) + vn = op->getIn(1); + } + if (vn->isMark()) { + constraintsFromPath(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) +void ValueSetSolver::generateConstraints(vector &worklist) + +{ + 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->code() == CPUI_CBRANCH) { + constraintsFromCBranch(lastOp); + } + bl = (BlockBasic *)bl->getImmedDom(); + } + else + break; + } + } + for(int4 i=0;iclearMark(); +} + +/// Given a set of sinks, find all the Varnodes that flow directly into them. +/// \param sinks is the list terminating Varnodes +/// \param stackReg (if non-NULL) gives the stack pointer (for keeping track of relative offsets) +void ValueSetSolver::establishValueSets(const vector &sinks,Varnode *stackReg) + +{ + vector worklist; + int4 workPos = 1; + if (stackReg != (Varnode *)0) { + newValueSet(stackReg,1); // Establish stack pointer as special + stackReg->setMark(); + worklist.push_back(stackReg); + 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()) { + if (vn->loneDescend()->numInput() == 1) + rootNodes.push_back(vn->getValueSet()); + } + else + rootNodes.push_back(vn->getValueSet()); + continue; + } + PcodeOp *op = vn->getDef(); + if (op->isCall()) { + vn->getValueSet()->range.setFull(vn->getSize()); + rootNodes.push_back(vn->getValueSet()); + continue; + } + 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); + } + } + generateConstraints(worklist); + 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 +void ValueSetSolver::solve(int4 max) + +{ + 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; + } + if (curComponent != (Partition *)0) { + if (curSet->iterate()) + curComponent->isDirty = true; + if (curComponent->stopNode != curSet) { + curSet = curSet->next; + } + else { + do { + 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; + } + else { + componentStack.pop_back(); + if (componentStack.empty()) { + curComponent = (Partition *)0; + curSet = curSet->next; + break; + } + curComponent = componentStack.back(); + curComponent->startNode->looped(); + } + } while(curComponent->stopNode == curSet); + } + } + else { + curSet->iterate(); + curSet = curSet->next; + } + } +} diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.hh index c0df4a28ae..5ee16fd5ee 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.hh @@ -51,35 +51,167 @@ 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); + 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 + +/// \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: + class Equation { + friend class ValueSet; + int4 slot; + CircleRange range; + public: + Equation(int4 s,const CircleRange &rng) { slot=s; range = rng; } ///< Constructor + }; +private: + friend class ValueSetSolver; + int4 typeCode; ///< 0=pure constant 1=stack relative + Varnode *vn; ///< Varnode whose set this represents + OpCode opCode; ///< Op-code defining Varnode + int4 numParams; ///< Number of input parameters to defining operation + CircleRange range; ///< Range of values or offsets in this set + int4 count; ///< Depth first numbering / widening count + 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 + void setVarnode(Varnode *v,int4 tCode); ///< Attach \b this to given Varnode and set initial values + void addEquation(int4 slot,const CircleRange &constraint); ///< Insert an equation restricting \b this value set + void addLandmark(const CircleRange &constraint) { addEquation(numParams,constraint); } ///< Add a widening landmark + void doWidening(const CircleRange &newRange); ///< Widen the value set so fixed point is reached sooner + void looped(void); ///< Mark that iteration has looped back to \b this + bool iterate(void); ///< Regenerate \b this value set from operator inputs +public: + 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 + 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 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 + 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,const CircleRange &range,FlowBlock *splitPoint); + void constraintsFromPath(Varnode *vn,PcodeOp *cbranch); ///< Generate constraints given a branch and matching Varnode + void constraintsFromCBranch(PcodeOp *cbranch); ///< Generate constraints arising from the given branch + void generateConstraints(vector &worklist); ///< Generate constraints given a system of Varnodes +public: + void establishValueSets(const vector &sinks,Varnode *stackReg); ///< Build value sets for a data-flow system + int4 getNumIterations(void) const { return numIterations; } ///< Get the current number of iterations + void solve(int4 max); ///< Iterate the ValueSet system until it stabilizes + list::const_iterator beginValueSets(void) { return valueNodes.begin(); } ///< Start of all ValueSets in the system + list::const_iterator endValueSets(void) { return valueNodes.end(); } ///< End of all ValueSets in the system +}; + +/// \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 +243,26 @@ inline char CircleRange::encodeRangeOverlaps(uintb op1left, uintb op1right, uint return arrange[val]; } +/// \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)