/* ### * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "double.hh" namespace ghidra { /// Internally, the \b lo and \b hi Varnodes are set to null, and the \b val field /// holds the constant value. /// \param sz is the size in bytes of the constant /// \param v is the constant value SplitVarnode::SplitVarnode(int4 sz,uintb v) { val = v; wholesize = sz; lo = (Varnode *)0; hi = (Varnode *)0; whole = (Varnode *)0; defpoint = (PcodeOp *)0; defblock = (BlockBasic *)0; } /// \param sz is the size of the constant in bytes /// \param v is the constant value void SplitVarnode::initPartial(int4 sz,uintb v) { val = v; wholesize = sz; lo = (Varnode *)0; hi = (Varnode *)0; whole = (Varnode *)0; defpoint = (PcodeOp *)0; defblock = (BlockBasic *)0; } /// The Varnode pieces can be constant, in which case a constant SplitVarnode is initialized and /// a constant value is built from the pieces. The given most significant piece can be null, indicating /// that the most significant piece of the whole is an implied zero. /// \param sz is the size of the logical whole in bytes /// \param l is the given (least significant) Varnode piece /// \param h is the given (most significant) Varnode piece void SplitVarnode::initPartial(int4 sz,Varnode *l,Varnode *h) { if (h == (Varnode *)0) { // hi is an implied zero hi = (Varnode *)0; if (l->isConstant()) { val = l->getOffset(); // Assume l is a constant lo = (Varnode *)0; } else lo = l; } else { if (l->isConstant() && h->isConstant()) { val = h->getOffset(); val <<= (l->getSize()*8); val |= l->getOffset(); lo = (Varnode *)0; hi = (Varnode *)0; } else { lo = l; hi = h; } } wholesize = sz; whole = (Varnode *)0; defpoint = (PcodeOp *)0; defblock = (BlockBasic *)0; } /// The \b lo, \b hi, and \b whole fields are filled in. The definition point remains uninitialized. /// \param w is the given whole Varnode /// \param l is the given (least significant) Varnode piece /// \param h is the given (most significant) Varnode piece void SplitVarnode::initAll(Varnode *w,Varnode *l,Varnode *h) { wholesize = w->getSize(); lo = l; hi = h; whole = w; defpoint = (PcodeOp *)0; defblock = (BlockBasic *)0; } /// Verify that the given most significant piece is formed via CPUI_SUBPIECE and search /// for the least significant piece being formed as a CPUI_SUBPIECE of the same whole. /// \param h is the given (most significant) Varnode piece /// \return \b true if the matching \b whole and least significant piece is found bool SplitVarnode::inHandHi(Varnode *h) { if (!h->isPrecisHi()) return false; // Check for mark, in order to have quick -false- in most cases // Search for the companion if (h->isWritten()) { PcodeOp *op = h->getDef(); // We could check for double loads here if (op->code() == CPUI_SUBPIECE) { Varnode *w = op->getIn(0); if (op->getIn(1)->getOffset() != (uintb)(w->getSize()-h->getSize())) return false; list::const_iterator iter,enditer; iter = w->beginDescend(); enditer = w->endDescend(); while(iter != enditer) { PcodeOp *tmpop = *iter; ++iter; if (tmpop->code() != CPUI_SUBPIECE) continue; Varnode *tmplo = tmpop->getOut(); if (!tmplo->isPrecisLo()) continue; if (tmplo->getSize() + h->getSize() != w->getSize()) continue; if (tmpop->getIn(1)->getOffset() != 0) continue; // There could conceivably be more than one, but this shouldn't happen with CSE initAll(w,tmplo,h); return true; } } } return false; } /// Verify that the given least significant piece is formed via CPUI_SUBPIECE and search /// for the most significant piece being formed as a CPUI_SUBPIECE of the same whole. /// \param l is the given (least significant) Varnode piece /// \return \b true if the matching \b whole and most significant piece is found bool SplitVarnode::inHandLo(Varnode *l) { if (!l->isPrecisLo()) return false; // Check for mark, in order to have quick -false- in most cases // Search for the companion if (l->isWritten()) { PcodeOp *op = l->getDef(); // We could check for double loads here if (op->code() == CPUI_SUBPIECE) { Varnode *w = op->getIn(0); if (op->getIn(1)->getOffset() != 0) return false; list::const_iterator iter,enditer; iter = w->beginDescend(); enditer = w->endDescend(); while(iter != enditer) { PcodeOp *tmpop = *iter; ++iter; if (tmpop->code() != CPUI_SUBPIECE) continue; Varnode *tmphi = tmpop->getOut(); if (!tmphi->isPrecisHi()) continue; if (tmphi->getSize() + l->getSize() != w->getSize()) continue; if (tmpop->getIn(1)->getOffset() != (uintb)l->getSize()) continue; // There could conceivably be more than one, but this shouldn't happen with CSE initAll(w,l,tmphi); return true; } } } return false; } /// The given least significant Varnode must already be marked as a piece. /// Initialize the SplitVarnode with the given piece and the \b whole that it came from. /// If a matching most significant piece can be found, as another CPUI_SUBPIECE off of the same /// \b whole, initialize that as well. Otherwise leave the most significant piece as null. /// \param l is the given (least significant) Varnode piece /// \return \b true if the SplitVarnode is successfully initialized bool SplitVarnode::inHandLoNoHi(Varnode *l) { if (!l->isPrecisLo()) return false; if (!l->isWritten()) return false; PcodeOp *op = l->getDef(); if (op->code() != CPUI_SUBPIECE) return false; if (op->getIn(1)->getOffset() != 0) return false; Varnode *w = op->getIn(0); list::const_iterator iter,enditer; iter = w->beginDescend(); enditer = w->endDescend(); while(iter != enditer) { PcodeOp *tmpop = *iter; ++iter; if (tmpop->code() != CPUI_SUBPIECE) continue; Varnode *tmphi = tmpop->getOut(); if (!tmphi->isPrecisHi()) continue; if (tmphi->getSize() + l->getSize() != w->getSize()) continue; if (tmpop->getIn(1)->getOffset() != (uintb)l->getSize()) continue; // There could conceivably be more than one, but this shouldn't happen with CSE initAll(w,l,tmphi); return true; } initAll(w,l,(Varnode *)0); return true; } /// Initialize the SplitVarnode given the most significant piece, if it is concatenated together /// immediately with is least significant piece. The CPUI_PIECE and the matching least significant /// piece must be unique. If these are found, \b hi, \b lo, and \b whole are all filled in. /// \param h is the given (most significant) piece /// \return \b true if initialization was successful and the least significant piece was found bool SplitVarnode::inHandHiOut(Varnode *h) { list::const_iterator iter,enditer; iter = h->beginDescend(); enditer = h->endDescend(); Varnode *loTmp = (Varnode *)0; Varnode *outvn = (Varnode *)0; while(iter != enditer) { PcodeOp *pieceop = *iter; ++iter; if (pieceop->code() != CPUI_PIECE) continue; if (pieceop->getIn(0) != h) continue; Varnode *l = pieceop->getIn(1); if (!l->isPrecisLo()) continue; if (loTmp != (Varnode *)0) return false; // Whole is not unique loTmp = l; outvn = pieceop->getOut(); } if (loTmp != (Varnode *)0) { initAll(outvn,loTmp,h); return true; } return false; } /// Initialize the SplitVarnode given the least significant piece, if it is concatenated together /// immediately with is nost significant piece. The CPUI_PIECE and the matching most significant /// piece must be unique. If these are found, \b hi, \b lo, and \b whole are all filled in. /// \param l is the given (least significant) piece /// \return \b true if initialization was successful and the most significant piece was found bool SplitVarnode::inHandLoOut(Varnode *l) { list::const_iterator iter,enditer; iter = l->beginDescend(); enditer = l->endDescend(); Varnode *hiTmp = (Varnode *)0; Varnode *outvn = (Varnode *)0; while(iter != enditer) { PcodeOp *pieceop = *iter; ++iter; if (pieceop->code() != CPUI_PIECE) continue; if (pieceop->getIn(1) != l) continue; Varnode *h = pieceop->getIn(0); if (!h->isPrecisHi()) continue; if (hiTmp != (Varnode *)0) return false; // Whole is not unique hiTmp = h; outvn = pieceop->getOut(); } if (hiTmp != (Varnode *)0) { initAll(outvn,l,hiTmp); return true; } return false; } /// Look for CPUI_SUBPIECE operations off of a common Varnode. /// The \b whole field is set to this Varnode if found; the definition point and block are /// filled in and \b true is returned. Otherwise \b false is returned. /// \return \b true if the \b whole Varnode is found bool SplitVarnode::findWholeSplitToPieces(void) { if (whole == (Varnode*)0) { if (hi == (Varnode*)0) return false; if (lo == (Varnode*)0) return false; if (!hi->isWritten()) return false; PcodeOp *subhi = hi->getDef(); if (subhi->code() == CPUI_COPY) { // Go thru one level of copy, if the piece is addrtied Varnode *otherhi = subhi->getIn(0); if (!otherhi->isWritten()) return false; subhi = otherhi->getDef(); } if (subhi->code() != CPUI_SUBPIECE) return false; if (subhi->getIn(1)->getOffset() != wholesize - hi->getSize()) return false; Varnode *putativeWhole = subhi->getIn(0); if (putativeWhole->getSize() != wholesize) return false; if (!lo->isWritten()) return false; PcodeOp *sublo = lo->getDef(); if (sublo->code() == CPUI_COPY) { // Go thru one level of copy, if the piece is addrtied Varnode *otherlo = sublo->getIn(0); if (!otherlo->isWritten()) return false; sublo = otherlo->getDef(); } if (sublo->code() != CPUI_SUBPIECE) return false; if (putativeWhole != sublo->getIn(0)) return false; // Doesn't match between pieces if (sublo->getIn(1)->getOffset() != 0) return false; whole = putativeWhole; } if (whole->isWritten()) { defpoint = whole->getDef(); defblock = defpoint->getParent(); } else if (whole->isInput()) { defpoint = (PcodeOp *)0; defblock = (BlockBasic *)0; } return true; } /// Set the basic block, \b defblock, and PcodeOp, \b defpoint, where they are defined. /// Its possible that \b lo and \b hi are \e input Varnodes with no natural defining PcodeOp, /// in which case \b defpoint is set to null and \b defblock is set to the function entry block. /// The method returns \b true, if the definition point is found, which amounts to returning /// \b false if the SplitVarnode is only half constant or half input. /// \return \b true if the definition point is located bool SplitVarnode::findDefinitionPoint(void) { PcodeOp *lastop; if (hi != (Varnode *)0 && hi->isConstant()) return false; // If one but not both is constant if (lo->isConstant()) return false; if (hi == (Varnode *)0) { // Implied zero extension if (lo->isInput()) { defblock = (BlockBasic *)0; defpoint = (PcodeOp *)0; } else if (lo->isWritten()) { defpoint = lo->getDef(); defblock = defpoint->getParent(); } else return false; } else if (hi->isWritten()) { if (!lo->isWritten()) return false; // Do not allow mixed input/non-input pairs lastop = hi->getDef(); defblock = lastop->getParent(); PcodeOp *lastop2 = lo->getDef(); BlockBasic *otherblock = lastop2->getParent(); if (defblock != otherblock) { defpoint = lastop; FlowBlock *curbl = defblock; while(curbl != (FlowBlock *)0) { // Make sure defblock dominated by otherblock curbl = curbl->getImmedDom(); if (curbl == otherblock) return true; } defblock = otherblock; // Try lo as final defining location otherblock = lastop->getParent(); defpoint = lastop2; curbl = defblock; while(curbl != (FlowBlock *)0) { curbl = curbl->getImmedDom(); if (curbl == otherblock) return true; } defblock = (BlockBasic *)0; return false; // Not defined in same basic block } if (lastop2->getSeqNum().getOrder() > lastop->getSeqNum().getOrder()) lastop = lastop2; defpoint = lastop; } else if (hi->isInput()) { if (!lo->isInput()) return false; // Do not allow mixed input/non-input pairs defblock = (BlockBasic *)0; defpoint = (PcodeOp *)0; } return true; } /// If both \b lo and \b hi pieces are written, the earlier of the two defining PcodeOps /// is returned. Otherwise null is returned. /// \return the earlier of the two defining PcodeOps or null PcodeOp *SplitVarnode::findEarliestSplitPoint(void) { if (!hi->isWritten()) return (PcodeOp *)0; if (!lo->isWritten()) return (PcodeOp *)0; PcodeOp *hiop = hi->getDef(); PcodeOp *loop = lo->getDef(); if (loop->getParent() != hiop->getParent()) return (PcodeOp *)0; return (loop->getSeqNum().getOrder() < hiop->getSeqNum().getOrder()) ? loop : hiop; } /// We scan for concatenations formed out of \b hi and \b lo, in the correct significance order. /// We assume \b hi and \b lo are defined in the same basic block (or are both inputs) and that /// the concatenation is also in this block. If such a concatenation is found, \b whole is set to the /// concatenated Varnode, the defining block and PcodeOp is filled in, and \b true is returned. /// \return \b true if a \b whole concatenated from \b hi and \b lo is found bool SplitVarnode::findWholeBuiltFromPieces(void) { if (hi==(Varnode *)0) return false; if (lo==(Varnode *)0) return false; list::const_iterator iter,enditer; iter = lo->beginDescend(); enditer = lo->endDescend(); PcodeOp *res = (PcodeOp *)0; BlockBasic *bb; if (lo->isWritten()) bb = lo->getDef()->getParent(); else if (lo->isInput()) bb = (BlockBasic *)0; else throw LowlevelError("Trying to find whole on free varnode"); while(iter != enditer) { PcodeOp *op = *iter; ++iter; if (op->code() != CPUI_PIECE) continue; if (op->getIn(0) != hi) continue; if (bb != (BlockBasic *)0) { if (op->getParent() != bb) continue; // Not defined in earliest block } else if (!op->getParent()->isEntryPoint()) continue; if (res == (PcodeOp *)0) res = op; else { if (op->getSeqNum().getOrder() < res->getSeqNum().getOrder()) // Find "earliest" whole res = op; } } if (res == (PcodeOp *)0) whole = (Varnode *)0; else { defpoint = res; defblock = defpoint->getParent(); whole = res->getOut(); } return (whole!=(Varnode *)0); } /// The whole Varnode must be defined or definable \e before the given PcodeOp. /// This is checked by comparing the given PcodeOp to the defining PcodeOp and block for \b this, /// which are filled in if they weren't before. /// \param existop is the given PcodeOp /// \return \b true if a whole Varnode exists or can be defined before the given PcodeOp bool SplitVarnode::isWholeFeasible(PcodeOp *existop) { if (isConstant()) return true; if ((lo!=(Varnode *)0)&&(hi!=(Varnode *)0)) if (lo->isConstant() != hi->isConstant()) return false; // Mixed constant/non-constant if (!findWholeSplitToPieces()) { if (!findWholeBuiltFromPieces()) { if (!findDefinitionPoint()) return false; } } if (defblock == (BlockBasic *)0) return true; FlowBlock *curbl = existop->getParent(); if (curbl == defblock) // If defined in same block as -existop- check PcodeOp ordering return (defpoint->getSeqNum().getOrder() <= existop->getSeqNum().getOrder()); while(curbl != (FlowBlock *)0) { // Make sure defbock dominates block containing -existop- curbl = curbl->getImmedDom(); if (curbl == defblock) return true; } return false; } /// This is similar to isWholeFeasible(), but the \b whole must be defined before the end of the given /// basic block. /// \param bl is the given basic block /// \return \b true if a whole Varnode exists or can be defined before the end of the given basic block bool SplitVarnode::isWholePhiFeasible(FlowBlock *bl) { if (isConstant()) return false; if (!findWholeSplitToPieces()) { if (!findWholeBuiltFromPieces()) { if (!findDefinitionPoint()) return false; } } if (defblock == (BlockBasic *)0) return true; if (bl == defblock) // If defined in same block return true; while(bl != (FlowBlock *)0) { // Make sure defblock dominates block containing -existop- bl = bl->getImmedDom(); if (bl == defblock) return true; } return false; } /// This method assumes isWholeFeasible has been called and returned \b true. /// If the \b whole didn't already exist, it is created as the concatenation of its two pieces. /// If the pieces were constant, a constant whole Varnode is created. /// If the \b hi piece was null, the whole is created as a CPUI_ZEXT of the \b lo. /// \param data is the function owning the Varnode pieces void SplitVarnode::findCreateWhole(Funcdata &data) { if (isConstant()) { whole = data.newConstant(wholesize,val); return; } else { if (lo != (Varnode *)0) lo->setPrecisLo(); // Mark the pieces if (hi != (Varnode *)0) hi->setPrecisHi(); } if (whole != (Varnode *)0) return; // Already found the whole PcodeOp *concatop; Address addr; BlockBasic *topblock = (BlockBasic *)0; if (defblock != (BlockBasic *)0) addr = defpoint->getAddr(); else { topblock = (BlockBasic *)data.getBasicBlocks().getStartBlock(); addr = topblock->getStart(); } if (hi != (Varnode*)0) { concatop = data.newOp(2,addr); // Do we need to pick something other than a unique???? whole = data.newUniqueOut(wholesize,concatop); data.opSetOpcode(concatop,CPUI_PIECE); data.opSetOutput(concatop,whole); data.opSetInput(concatop,hi,0); data.opSetInput(concatop,lo,1); } else { concatop = data.newOp(1,addr); whole = data.newUniqueOut(wholesize,concatop); data.opSetOpcode(concatop,CPUI_INT_ZEXT); data.opSetOutput(concatop,whole); data.opSetInput(concatop,lo,0); } if (defblock != (BlockBasic *)0) data.opInsertAfter(concatop,defpoint); else data.opInsertBegin(concatop,topblock); defpoint = concatop; defblock = concatop->getParent(); } /// If the \b whole does not already exist, it is created as a \e unique register. /// The new Varnode must later be set explicitly as the output of some PcodeOp. /// \param data is the function owning the Varnode pieces void SplitVarnode::findCreateOutputWhole(Funcdata &data) { // Create the actual -whole- varnode lo->setPrecisLo(); // Mark the pieces hi->setPrecisHi(); if (whole != (Varnode *)0) return; whole = data.newUnique(wholesize); } /// If the pieces can be treated as a contiguous whole, use the same storage location to construct the \b whole, /// otherwise use a \b join address for storage. /// \param data is the function owning the pieces void SplitVarnode::createJoinedWhole(Funcdata &data) { lo->setPrecisLo(); hi->setPrecisHi(); if (whole != (Varnode *)0) return; Address newaddr; if (!isAddrTiedContiguous(lo,hi,newaddr)) { newaddr = data.getArch()->constructJoinAddress(data.getArch()->translate,hi->getAddr(),hi->getSize(), lo->getAddr(),lo->getSize()); } whole = data.newVarnode(wholesize,newaddr); whole->setWriteMask(); } /// Assume \b lo was initially defined in some other way but now needs to be defined as a split from /// a new \b whole Varnode. The original PcodeOp defining \b lo is transformed into a CPUI_SUBPIECE. /// The method findCreateOutputWhole() must already have been called on \b this. void SplitVarnode::buildLoFromWhole(Funcdata &data) { PcodeOp *loop = lo->getDef(); if (loop == (PcodeOp *)0) throw LowlevelError("Building low piece that was originally undefined"); vector inlist; inlist.push_back(whole); inlist.push_back(data.newConstant(4,0)); if (loop->code() == CPUI_MULTIEQUAL) { // When converting the MULTIEQUAL to a SUBPIECE, we need to reinsert the op so that we don't // get a break in the sequence of MULTIEQUALs at the beginning of the block BlockBasic *bl = loop->getParent(); data.opUninsert(loop); data.opSetOpcode(loop,CPUI_SUBPIECE); data.opSetAllInput(loop,inlist); data.opInsertBegin(loop,bl); } else if (loop->code() == CPUI_INDIRECT) { // When converting an INDIRECT to a SUBPIECE, we need to reinsert the op AFTER the affector PcodeOp *affector = PcodeOp::getOpFromConst(loop->getIn(1)->getAddr()); if (!affector->isDead()) data.opUninsert(loop); data.opSetOpcode(loop,CPUI_SUBPIECE); data.opSetAllInput(loop,inlist); if (!affector->isDead()) data.opInsertAfter(loop,affector); } else { data.opSetOpcode(loop,CPUI_SUBPIECE); data.opSetAllInput(loop,inlist); } } /// Assume \b hi was initially defined in some other way but now needs to be defined as a split from /// a new \b whole Varnode. The original PcodeOp defining \b hi is transformed into a CPUI_SUBPIECE. /// The method findCreateOutputWhole() must already have been called on \b this. void SplitVarnode::buildHiFromWhole(Funcdata &data) { PcodeOp *hiop = hi->getDef(); if (hiop == (PcodeOp *)0) throw LowlevelError("Building low piece that was originally undefined"); vector inlist; inlist.push_back(whole); inlist.push_back(data.newConstant(4,lo->getSize())); if (hiop->code() == CPUI_MULTIEQUAL) { // When converting the MULTIEQUAL to a SUBPIECE, we need to reinsert the op so that we don't // get a break in the sequence of MULTIEQUALs at the beginning of the block BlockBasic *bl = hiop->getParent(); data.opUninsert(hiop); data.opSetOpcode(hiop,CPUI_SUBPIECE); data.opSetAllInput(hiop,inlist); data.opInsertBegin(hiop,bl); } else if (hiop->code() == CPUI_INDIRECT) { // When converting the INDIRECT to a SUBPIECE, we need to reinsert AFTER the affector PcodeOp *affector = PcodeOp::getOpFromConst(hiop->getIn(1)->getAddr()); if (!affector->isDead()) data.opUninsert(hiop); data.opSetOpcode(hiop,CPUI_SUBPIECE); data.opSetAllInput(hiop,inlist); if (!affector->isDead()) data.opInsertAfter(hiop,affector); } else { data.opSetOpcode(hiop,CPUI_SUBPIECE); data.opSetAllInput(hiop,inlist); } } // void SplitVarnode::buildHiFromLoHalf(Funcdata &data,SplitVarnode &oldin,PcodeOp *newwholeop) // { // Only the lo half of the new logical whole is explicitly constructed, the old input high // // is recycled for the hi half of the output, define the new hi half as a SUBPIECE and scan // // through the uses of the old hi half to see which should be switch to the new hi half // PcodeOp *newhiop = data.newOp(2,newwholeop->getAddr()); // data.opSetOpcode(newhiop,CPUI_SUBPIECE); // data.opSetOutput(newhiop,hi); // hi was not defined previously // data.opSetInput(newhiop,whole,0); // data.opSetInput(newhiop,data.newConstant(4,lo->getSize()),1); // data.opInsertAfter(newhiop,newwholeop); // Varnode *oldhi = oldin.getHi(); // list::const_iterator iter,enditer; // iter = oldhi->beginDescend(); // enditer = oldhi->endDescend(); // while(iter != enditer) { // PcodeOp *testop = *iter; // ++iter; // int4 ord = testop->compareOrder(newhiop); // if (ord == 1) { // newhiop executes earlier than testop // int4 slot = testop->getSlot(oldhi); // data.opSetInput(testop,hi,slot); // } // } // } /// Its assumed that \b this is the output of the double precision operation being performed. /// The \b whole Varnode may not yet exist. This method returns the first PcodeOp where the \b whole /// needs to exist. If no such PcodeOp exists, null is returned. /// \return the first PcodeOp where the \b whole needs to exist or null PcodeOp *SplitVarnode::findOutExist(void) { if (findWholeBuiltFromPieces()) { return defpoint; } return findEarliestSplitPoint(); } /// If the logical whole is a constant and is too big to be represented internally return \b true. /// \return \b true if \b this is a constant and too big bool SplitVarnode::exceedsConstPrecision(void) const { return isConstant() && (wholesize > sizeof(uintb)); } /// \brief Check if the values in the given Varnodes differ by the given size /// /// Return \b true, if the (possibly dynamic) value represented by the given \b vn1 plus \b size1 /// produces the value in the given \b vn2. For constants, the values can be computed directly, but /// otherwise \b vn1 and \b vn2 must be defined by INT_ADD operations from a common ancestor. /// \param vn1 is the first given Varnode /// \param vn2 is the second given Varnode /// \param size1 is the given size to add to \b vn1 /// \return \b true if the values in \b vn1 and \b vn2 are related by the given size bool SplitVarnode::adjacentOffsets(Varnode *vn1,Varnode *vn2,uintb size1) { if (vn1->isConstant()) { if (!vn2->isConstant()) return false; return ((vn1->getOffset() + size1) == vn2->getOffset()); } if (!vn2->isWritten()) return false; PcodeOp *op2 = vn2->getDef(); if (op2->code() != CPUI_INT_ADD) return false; if (!op2->getIn(1)->isConstant()) return false; uintb c2 = op2->getIn(1)->getOffset(); if (op2->getIn(0) == vn1) return (size1 == c2); if (!vn1->isWritten()) return false; PcodeOp *op1 = vn1->getDef(); if (op1->code() != CPUI_INT_ADD) return false; if (!op1->getIn(1)->isConstant()) return false; uintb c1 = op1->getIn(1)->getOffset(); if (op1->getIn(0) != op2->getIn(0)) return false; return ((c1 + size1) == c2); } /// \brief Verify that the pointers into the given LOAD/STORE PcodeOps address contiguous memory /// /// The two given PcodeOps must either both be LOADs or both be STOREs. The pointer for the /// first PcodeOp is labeled as the most significant piece of the contiguous whole, the /// second PcodeOp is labeled as the least significant piece. The p-code defining the pointers is examined /// to determine if the two memory regions being pointed at really form one contiguous region. /// If the regions are contiguous and the pointer labeling is valid, \b true is returned, the PcodeOps are sorted /// into \b first and \b second based on Address, and the address space of the memory region is passed back. /// \param most is the given LOAD/STORE PcodeOp referring to the most significant region /// \param least is the given LOAD/STORE PcodeOp referring to the least significant region /// \param first is used to pass back the earliest of the address sorted PcodeOps /// \param second is used to pass back the latest of the address sorted PcodeOps /// \param spc is used to pass back the LOAD address space /// \param sizeres is used to pass back the combined LOAD size /// \return true if the given PcodeOps are contiguous LOADs bool SplitVarnode::testContiguousPointers(PcodeOp *most,PcodeOp *least,PcodeOp *&first,PcodeOp *&second,AddrSpace *&spc) { spc = least->getIn(0)->getSpaceFromConst(); if (most->getIn(0)->getSpaceFromConst() != spc) return false; if (spc->isBigEndian()) { // Convert significance order to address order first = most; second = least; } else { first = least; second = most; } Varnode *firstptr = first->getIn(1); if (firstptr->isFree()) return false; int4 sizeres; if (first->code() == CPUI_LOAD) sizeres = first->getOut()->getSize(); // # of bytes read by lowest address load else // CPUI_STORE sizeres = first->getIn(2)->getSize(); // Check if the loads are adjacent to each other return adjacentOffsets(first->getIn(1),second->getIn(1),(uintb)sizeres); } /// \brief Return \b true if the given pieces can be melded into a contiguous storage location /// /// The given Varnodes must be \e address \e tied, and their storage must line up, respecting their /// significance as pieces. /// \param lo is the given least significant piece /// \param hi is the given most significant piece /// \param res is used to pass back the starting address of the contigous range /// \return \b true if the pieces are address tied and form a contiguous range bool SplitVarnode::isAddrTiedContiguous(Varnode *lo,Varnode *hi,Address &res) { if (!lo->isAddrTied()) return false; if (!hi->isAddrTied()) return false; // Make sure there is no explicit symbol that would prevent the pieces from being joined SymbolEntry *entryLo = lo->getSymbolEntry(); SymbolEntry *entryHi = hi->getSymbolEntry(); if (entryLo != (SymbolEntry *)0 || entryHi != (SymbolEntry *)0) { if (entryLo == (SymbolEntry *)0 || entryHi == (SymbolEntry *)0) return false; // One is marked with a symbol, the other is not if (entryLo->getSymbol() != entryHi->getSymbol()) return false; // They are part of different symbols } AddrSpace *spc = lo->getSpace(); if (spc != hi->getSpace()) return false; uintb looffset = lo->getOffset(); uintb hioffset = hi->getOffset(); if (spc->isBigEndian()) { if (hioffset >= looffset) return false; if (hioffset + hi->getSize() != looffset) return false; res = hi->getAddr(); } else { if (looffset >= hioffset) return false; if (looffset + lo->getSize() != hioffset) return false; res = lo->getAddr(); } return true; } /// \brief Create a list of all the possible pairs that contain the same logical value as the given Varnode /// /// The given Varnode is assumed to be the logical whole that is being used in a double precision calculation. /// At least one of the most or least significant pieces must be extracted from the whole and must be /// marked as a double precision piece. /// \param w is the given Varnode whole /// \param splitvec is the container for holding any discovered SplitVarnodes void SplitVarnode::wholeList(Varnode *w,vector &splitvec) { SplitVarnode basic; basic.whole = w; basic.hi = (Varnode *)0; basic.lo = (Varnode *)0; basic.wholesize = w->getSize(); list::const_iterator iter,enditer; iter = basic.whole->beginDescend(); enditer = basic.whole->endDescend(); int4 res = 0; while(iter != enditer) { PcodeOp *subop = *iter; ++iter; if (subop->code() != CPUI_SUBPIECE) continue; Varnode *vn = subop->getOut(); if (vn->isPrecisHi()) { if (subop->getIn(1)->getOffset() != basic.wholesize - vn->getSize()) continue; basic.hi = vn; res |= 2; } else if (vn->isPrecisLo()) { if (subop->getIn(1)->getOffset() != 0) continue; basic.lo = vn; res |= 1; } } if (res==0) return; if (res == 3 && (basic.lo->getSize() + basic.hi->getSize() != basic.wholesize)) return; splitvec.push_back(basic); findCopies(basic,splitvec); } /// \brief Find copies from (the pieces of) the given SplitVarnode /// /// Scan for each piece being used as input to COPY operations. If the each piece is /// copied within the same basic block to contiguous storage locations, create a new /// SplitVarnode from COPY outputs and add it to the list. /// \param in is the given SplitVarnode /// \param splitvec is the container for holding SplitVarnode copies void SplitVarnode::findCopies(const SplitVarnode &in,vector &splitvec) { if (!in.hasBothPieces()) return; list::const_iterator iter,enditer; iter = in.getLo()->beginDescend(); enditer = in.getLo()->endDescend(); while(iter != enditer) { PcodeOp *loop = *iter; ++iter; if (loop->code() != CPUI_COPY) continue; Varnode *locpy = loop->getOut(); Address addr = locpy->getAddr(); // Calculate address of hi part if (addr.isBigEndian()) addr = addr - (in.getHi()->getSize()); else addr = addr + locpy->getSize(); list::const_iterator iter2,enditer2; iter2 = in.getHi()->beginDescend(); enditer2 = in.getHi()->endDescend(); while(iter2 != enditer2) { PcodeOp *hiop = *iter2; ++iter2; if (hiop->code() != CPUI_COPY) continue; Varnode *hicpy = hiop->getOut(); if (hicpy->getAddr() != addr) continue; if (hiop->getParent() != loop->getParent()) continue; SplitVarnode newsplit; newsplit.initAll(in.getWhole(),locpy,hicpy); splitvec.push_back(newsplit); } } } /// \brief For the given CBRANCH PcodeOp, pass back the \b true and \b false basic blocks /// /// The result depends on the \e boolean \e flip property of the CBRANCH, and the user can /// also flip the meaning of the branches. /// \param boolop is the given CBRANCH PcodeOp /// \param flip is \b true if the caller wants to flip the meaning of the blocks /// \param trueout is used to pass back the true fall-through block /// \param falseout is used to pass back the false fall-through block void SplitVarnode::getTrueFalse(PcodeOp *boolop,bool flip,BlockBasic *&trueout,BlockBasic *&falseout) { BlockBasic *parent = boolop->getParent(); BlockBasic *trueblock = (BlockBasic *)parent->getTrueOut(); BlockBasic *falseblock =(BlockBasic *)parent->getFalseOut(); if (boolop->isBooleanFlip() != flip) { trueout = falseblock; falseout = trueblock; } else { trueout = trueblock; falseout = falseblock; } } /// \brief Return \b true if the basic block containing the given CBRANCH PcodeOp performs no other operation. /// /// The basic block can contain the CBRANCH and the one PcodeOp producing the boolean value. /// Otherwise \b false is returned. /// \param branchop is the given CBRANCH /// \return \b true if the parent basic block performs only the branch operation bool SplitVarnode::otherwiseEmpty(PcodeOp *branchop) { BlockBasic *bl = branchop->getParent(); if (bl->sizeIn() != 1) return false; PcodeOp *otherop = (PcodeOp *)0; Varnode *vn = branchop->getIn(1); if (vn->isWritten()) otherop = vn->getDef(); list::const_iterator iter,enditer; iter = bl->beginOp(); enditer = bl->endOp(); while(iter != enditer) { PcodeOp *op = *iter; ++iter; if (op == otherop) continue; if (op == branchop) continue; return false; } return true; } /// \brief Verify that the given PcodeOp is a CPUI_INT_MULT by -1 /// /// The PcodeOp must be a CPUI_INT_MULT and the second operand must be a constant -1. /// \param op is the given PcodeOp /// \return \b true if the PcodeOp is a multiple by -1 bool SplitVarnode::verifyMultNegOne(PcodeOp *op) { if (op->code() != CPUI_INT_MULT) return false; Varnode *in1 = op->getIn(1); if (!in1->isConstant()) return false; if (in1->getOffset() != calc_mask(in1->getSize())) return false; return true; } /// \brief Check that the logical version of a (generic) binary double-precision operation can be created /// /// This checks only the most generic aspects of the calculation. The input and output whole Varnodes /// must already exist or be creatable. The point where the output Varnode must exist is identified /// and returned. If the binary operation cannot be created, null is returned. /// \param out is the output of the binary operation /// \param in1 is the first input of the binary operation /// \param in2 is the second input of the binary operation /// \return the first PcodeOp where the output whole must exist PcodeOp *SplitVarnode::prepareBinaryOp(SplitVarnode &out,SplitVarnode &in1,SplitVarnode &in2) { PcodeOp *existop = out.findOutExist(); // Find point where output whole needs to exist if (existop == (PcodeOp *)0) return existop; // If we can find no such point return false; if (!in1.isWholeFeasible(existop)) return (PcodeOp *)0; if (!in2.isWholeFeasible(existop)) return (PcodeOp *)0; return existop; } /// \brief Rewrite a double precision binary operation by replacing the pieces with unified Varnodes /// /// This assumes that we have checked that the transformation is possible via the various /// verify and prepare methods. After this method is called, the logical inputs and output of /// the calculation will exist as real Varnodes. /// \param data is the function owning the operation /// \param out is the output of the binary operation /// \param in1 is the first input to the binary operation /// \param in2 is the second input to the binary operation /// \param existop is the precalculated PcodeOp where the output whole Varnode must exist /// \param opc is the opcode of the operation void SplitVarnode::createBinaryOp(Funcdata &data,SplitVarnode &out,SplitVarnode &in1,SplitVarnode &in2, PcodeOp *existop,OpCode opc) { out.findCreateOutputWhole(data); in1.findCreateWhole(data); in2.findCreateWhole(data); if (existop->code() != CPUI_PIECE) { // If the output whole didn't previously exist PcodeOp *newop = data.newOp(2,existop->getAddr()); // new op which creates the output whole data.opSetOpcode(newop,opc); data.opSetOutput(newop,out.getWhole()); data.opSetInput(newop,in1.getWhole(),0); data.opSetInput(newop,in2.getWhole(),1); data.opInsertBefore(newop,existop); out.buildLoFromWhole(data); out.buildHiFromWhole(data); } else { // The whole previously existed data.opSetOpcode(existop,opc); // our new op replaces the op previously defining the output whole data.opSetInput(existop,in1.getWhole(),0); data.opSetInput(existop,in2.getWhole(),1); } } /// \brief Make sure input and output operands of a double precision shift operation are compatible /// /// Do generic testing that the input and output whole Varnodes can be created. Calculate the /// PcodeOp where the output whole must exist and return it. If logical operation cannot be created, /// return null. /// \param out is the output of the double precision shift operation /// \param in is the (first) input operand of the double precision shift operation /// \return the PcodeOp where output whole must exist or null PcodeOp *SplitVarnode::prepareShiftOp(SplitVarnode &out,SplitVarnode &in) { PcodeOp *existop = out.findOutExist(); // Find point where output whole needs to exist if (existop == (PcodeOp *)0) return existop; if (!in.isWholeFeasible(existop)) return (PcodeOp *)0; return existop; } /// \brief Rewrite a double precision shift by replacing hi/lo pieces with unified Varnodes /// /// This assumes that we have checked that the transformation is possible by calling the appropriate /// verify and prepare methods. After this method is called, the logical inputs and output of /// the calculation will exist as real Varnodes. The \e shift \e amount is not treated as a double /// precision variable. /// \param data is the function owning the operation /// \param out is the output of the double precision operation /// \param in is the first input of the operation /// \param sa is the Varnode indicating the \e shift \e amount for the operation /// \param existop is the first PcodeOp where the output whole needs to exist /// \param opc is the opcode of the particular shift operation void SplitVarnode::createShiftOp(Funcdata &data,SplitVarnode &out,SplitVarnode &in,Varnode *sa, PcodeOp *existop,OpCode opc) { out.findCreateOutputWhole(data); in.findCreateWhole(data); if (sa->isConstant()) sa = data.newConstant(sa->getSize(),sa->getOffset()); if (existop->code() != CPUI_PIECE) { // If the output whole didn't previously exist PcodeOp *newop = data.newOp(2,existop->getAddr()); data.opSetOpcode(newop,opc); data.opSetOutput(newop,out.getWhole()); data.opSetInput(newop,in.getWhole(),0); data.opSetInput(newop,sa,1); data.opInsertBefore(newop,existop); out.buildLoFromWhole(data); out.buildHiFromWhole(data); } else { // The whole previously existed, we remake the defining op data.opSetOpcode(existop,opc); data.opSetInput(existop,in.getWhole(),0); data.opSetInput(existop,sa,1); } } /// \brief Try to perform one transform on a logical double precision operation given a specific input /// /// All the various double precision forms are lined up against the input. The first one that matches /// has its associated transform performed and then 1 is returned. If no form matches, 0 is returned. /// \param in is the given double precision input /// \param data is the function owning the Varnodes /// \return a count of the number of transforms applied, 0 or 1 int4 SplitVarnode::applyRuleIn(SplitVarnode &in,Funcdata &data) { for(int4 i=0;i<2;++i) { Varnode *vn; vn = (i==0) ? in.getHi() : in.getLo(); if (vn == (Varnode *)0) continue; bool workishi = (i==0); list::const_iterator iter,enditer; iter = vn->beginDescend(); enditer = vn->endDescend(); while(iter != enditer) { PcodeOp *workop = *iter; ++iter; switch(workop->code()) { case CPUI_INT_ADD: { AddForm addform; if (addform.applyRule(in,workop,workishi,data)) return 1; SubForm subform; if (subform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INT_AND: { Equal3Form equal3form; if (equal3form.applyRule(in,workop,workishi,data)) return 1; LogicalForm logicalform; if (logicalform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INT_OR: { LogicalForm logicalform; if (logicalform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INT_XOR: { LogicalForm logicalform; if (logicalform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INT_EQUAL: case CPUI_INT_NOTEQUAL: { LessThreeWay lessthreeway; if (lessthreeway.applyRule(in,workop,workishi,data)) return 1; Equal1Form equal1form; if (equal1form.applyRule(in,workop,workishi,data)) return 1; Equal2Form equal2form; if (equal2form.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INT_LESS: case CPUI_INT_LESSEQUAL: { LessThreeWay lessthreeway; if (lessthreeway.applyRule(in,workop,workishi,data)) return 1; LessConstForm lessconstform; if (lessconstform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INT_SLESS: { LessConstForm lessconstform; if (lessconstform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INT_SLESSEQUAL: { LessConstForm lessconstform; if (lessconstform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INT_LEFT: { ShiftForm shiftform; if (shiftform.applyRuleLeft(in,workop,workishi,data)) return 1; } break; case CPUI_INT_RIGHT: { ShiftForm shiftform; if (shiftform.applyRuleRight(in,workop,workishi,data)) return 1; } break; case CPUI_INT_SRIGHT: { ShiftForm shiftform; if (shiftform.applyRuleRight(in,workop,workishi,data)) return 1; } break; case CPUI_INT_MULT: { MultForm multform; if (multform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_MULTIEQUAL: { PhiForm phiform; if (phiform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_INDIRECT: { IndirectForm indform; if (indform.applyRule(in,workop,workishi,data)) return 1; } break; case CPUI_COPY: if (workop->getOut()->isAddrForce()) { CopyForceForm copyform; if (copyform.applyRule(in, workop, workishi, data)) return 1; } break; default: break; } } } return 0; } /// \brief Make sure input operands of a double precision compare operation are compatible /// /// Do generic testing that the input whole Varnodes can be created. If they can be created, return \b true. /// \param in1 is the first input operand of the double precision compare operation /// \param in2 is the second input operand of the double precision compare operation /// \return \b true if the logical transformation can be performed bool SplitVarnode::prepareBoolOp(SplitVarnode &in1,SplitVarnode &in2,PcodeOp *testop) { if (!in1.isWholeFeasible(testop)) return false; if (!in2.isWholeFeasible(testop)) return false; return true; } /// \brief Rewrite a double precision boolean operation by replacing the input pieces with unified Varnodes /// /// This assumes we checked that the transformation is possible by calling the various verify and prepare /// methods. The inputs to the given PcodeOp producing the final boolean value are replaced with new /// logical Varnodes, and the opcode is updated. The output Varnode is not affected. /// \param data is the function owning the operation /// \param boolop is the given PcodeOp producing the final boolean value /// \param in1 is the first input to the operation /// \param in2 is the second input to the operation /// \param opc is the opcode of the operation void SplitVarnode::replaceBoolOp(Funcdata &data,PcodeOp *boolop,SplitVarnode &in1,SplitVarnode &in2, OpCode opc) { in1.findCreateWhole(data); in2.findCreateWhole(data); data.opSetOpcode(boolop,opc); data.opSetInput(boolop,in1.getWhole(),0); data.opSetInput(boolop,in2.getWhole(),1); } /// \brief Create a new compare PcodeOp, replacing the boolean Varnode taken as input by the given CBRANCH /// /// The inputs to the new compare operation are Varnodes representing the logical whole of the double precision /// pieces. /// \param data is the function owning the operation /// \param cbranch is the given CBRANCH PcodeOp /// \param in1 is the first input to the compare operation /// \param in2 is the second input to the compare operation /// \param opc is the opcode of the compare operation void SplitVarnode::createBoolOp(Funcdata &data,PcodeOp *cbranch,SplitVarnode &in1,SplitVarnode &in2, OpCode opc) { PcodeOp *addrop = cbranch; Varnode *boolvn = cbranch->getIn(1); if (boolvn->isWritten()) addrop = boolvn->getDef(); // Use the address of the comparison operator in1.findCreateWhole(data); in2.findCreateWhole(data); PcodeOp *newop = data.newOp(2,addrop->getAddr()); data.opSetOpcode(newop,opc); Varnode *newbool = data.newUniqueOut(1,newop); data.opSetInput(newop,in1.getWhole(),0); data.opSetInput(newop,in2.getWhole(),1); data.opInsertBefore(newop,cbranch); data.opSetInput(cbranch,newbool,1); // CBRANCH now determined by new compare } /// \brief Check that the logical version of a CPUI_MULTIEQUAL operation can be created /// /// This checks only the most generic aspects of the calculation. The input and output whole Varnodes /// must already exist or be creatable. The point where the output Varnode must exist is identified /// and returned. If the MULTIEQUAL operation cannot be created, null is returned. /// \param out is the output of the MULTIEQUAL operation /// \param inlist is a vector of the input operands to the MULTIEQUAL /// \return the first PcodeOp where the output whole must exist PcodeOp *SplitVarnode::preparePhiOp(SplitVarnode &out,vector &inlist) { PcodeOp *existop = out.findEarliestSplitPoint(); // Point where output whole needs to exist if (existop == (PcodeOp *)0) return existop; // existop should always be a MULTIEQUAL defining one of the pieces if (existop->code() != CPUI_MULTIEQUAL) throw LowlevelError("Trying to create phi-node double precision op with phi-node pieces"); BlockBasic *bl = existop->getParent(); int4 numin = inlist.size(); for(int4 i=0;igetIn(i))) return (PcodeOp *)0; return existop; } /// \brief Rewrite a double precision MULTIEQUAL operation by replacing the pieces with unified Varnodes /// /// This assumes that we have checked that the transformation is possible via the various /// verify and prepare methods. After this method is called, the logical inputs and output of /// the calculation will exist as real Varnodes. /// \param data is the function owning the operation /// \param out is the output of the MULTIEQUAL operation /// \param inlist is the list of input operands to the MULTIEQUAL /// \param existop is the precalculated PcodeOp where the output whole Varnode must exist void SplitVarnode::createPhiOp(Funcdata &data,SplitVarnode &out,vector &inlist, PcodeOp *existop) { // Unlike replaceBoolOp, we MUST create a newop even if the output whole already exists // because the MULTIEQUAL has a lot of placement constraints on it out.findCreateOutputWhole(data); int4 numin = inlist.size(); for(int4 i=0;igetAddr()); data.opSetOpcode(newop,CPUI_MULTIEQUAL); data.opSetOutput(newop,out.getWhole()); for(int4 i=0;igetAddr()); data.opSetOpcode(newop,CPUI_INDIRECT); data.opSetOutput(newop,out.getWhole()); data.opSetInput(newop,in.getWhole(),0); data.opSetInput(newop,data.newVarnodeIop(affector),1); data.opInsertBefore(newop,affector); out.buildLoFromWhole(data); out.buildHiFromWhole(data); } /// \brief Rewrite the double precision version of a COPY to an address forced Varnode /// /// This assumes that we have checked that the transformation is possible. The logical input must already /// exist, and after this method is called, the logical output will also exist. The original COPY pieces /// are explicitly destroyed. /// \param data is the function owning the COPYs /// \param addr is the storage address being COPYed /// \param in is the input to the COPYs /// \param copylo is the original least significant COPY /// \param copyhi is the original most significant COPY void SplitVarnode::replaceCopyForce(Funcdata &data,const Address &addr,SplitVarnode &in,PcodeOp *copylo,PcodeOp *copyhi) { Varnode *inVn = in.getWhole(); bool returnForm = copyhi->isReturnCopy(); if (returnForm && inVn->getAddr() != addr) { // Placeholder for global propagation past a RETURN needs an additional COPY PcodeOp *otherPoint1 = copyhi->getIn(0)->getDef(); PcodeOp *otherPoint2 = copylo->getIn(0)->getDef(); // We know these are COPYs in the same basic block. Compute the later one. if (otherPoint1->getSeqNum().getOrder() < otherPoint2->getSeqNum().getOrder()) otherPoint1 = otherPoint2; PcodeOp *otherCopy = data.newOp(1, otherPoint1->getAddr()); data.opSetOpcode(otherCopy, CPUI_COPY); Varnode *vn = data.newVarnodeOut(in.getSize(), addr, otherCopy); data.opSetInput(otherCopy,inVn,0); data.opInsertBefore(otherCopy, otherPoint1); inVn = vn; } PcodeOp *wholeCopy = data.newOp(1, copyhi->getAddr()); data.opSetOpcode(wholeCopy, CPUI_COPY); Varnode *outVn = data.newVarnodeOut(in.getSize(), addr, wholeCopy); outVn->setAddrForce(); if (returnForm) data.markReturnCopy(wholeCopy); data.opSetInput(wholeCopy,inVn,0); data.opInsertBefore(wholeCopy, copyhi); data.opDestroy(copyhi); // Destroy the original COPYs. Outputs have no descendants. data.opDestroy(copylo); } bool AddForm::checkForCarry(PcodeOp *op) { // If -op- matches a CARRY construction based on lo1 (i.e. CARRY(x,lo1) ) // set lo1 (and negconst if lo1 is a constant) to be the corresponding part of the carry // and return true if (op->code() != CPUI_INT_ZEXT) return false; if (!op->getIn(0)->isWritten()) return false; PcodeOp *carryop = op->getIn(0)->getDef(); if (carryop->code() == CPUI_INT_CARRY) { // Normal CARRY form if (carryop->getIn(0) == lo1) lo2 = carryop->getIn(1); else if (carryop->getIn(1) == lo1) lo2 = carryop->getIn(0); else return false; if (lo2->isConstant()) return false; return true; } if (carryop->code() == CPUI_INT_LESS) { // Possible CARRY Varnode *tmpvn = carryop->getIn(0); if (tmpvn->isConstant()) { if (carryop->getIn(1) != lo1) return false; negconst = tmpvn->getOffset(); // In constant forms, the <= will get converted to a < // Note the lessthan to less conversion adds 1 then the 2's complement subtracts 1 and negates // So all we really need to do is negate negconst = (~negconst) & calc_mask(lo1->getSize()); lo2 = (Varnode *)0; return true; } else if (tmpvn->isWritten()) { // Calculate CARRY relative to result of loadd PcodeOp *loadd_op = tmpvn->getDef(); // This is the putative loadd if (loadd_op->code() != CPUI_INT_ADD) return false; Varnode *othervn; if (loadd_op->getIn(0)==lo1) othervn = loadd_op->getIn(1); else if (loadd_op->getIn(1)==lo1) othervn = loadd_op->getIn(0); else return false; // One side of the add must be lo1 if (othervn->isConstant()) { negconst = othervn->getOffset(); lo2 = (Varnode *)0; Varnode *relvn = carryop->getIn(1); if (relvn == lo1) return true; // Comparison can be relative to lo1 if (!relvn->isConstant()) return false; if (relvn->getOffset() != negconst) return false; // Otherwise must be relative to (constant)lo2 return true; } else { lo2 = othervn; // Other side of putative loadd must be lo2 Varnode *compvn = carryop->getIn(1); if ((compvn == lo2)||(compvn == lo1)) return true; } } return false; } if (carryop->code() == CPUI_INT_NOTEQUAL) { // Possible CARRY against -1 if (!carryop->getIn(1)->isConstant()) return false; if (carryop->getIn(0) != lo1) return false; if (carryop->getIn(1)->getOffset() != 0) return false; negconst = calc_mask(lo1->getSize()); // Original CARRY constant must have been -1 lo2 = (Varnode *)0; return true; } return false; } // Given a known double precision input, look for a double precision add, // recovering the other double input and the double output // // Assume hi1, lo1 is a known double precision pair, we look for // reshi = hi1 + hi2 + hizext (2 variants here) // hizext = zext(bool) // { (2 variants) // bool = (-lo1 <= lo2) OR // bool = (-lo2 <= lo1) (multiple ways to calculate negation) // } // reslo = lo1 + lo2 bool AddForm::verify(Varnode *h,Varnode *l,PcodeOp *op) { hi1 = h; lo1 = l; slot1 = op->getSlot(hi1); for(int4 i=0;i<3;++i) { if (i==0) { // Assume we have to descend one more add add2 = op->getOut()->loneDescend(); if (add2 == (PcodeOp *)0) continue; if (add2->code() != CPUI_INT_ADD) continue; reshi = add2->getOut(); hizext1 = op->getIn(1-slot1); hizext2 = add2->getIn(1-add2->getSlot(op->getOut())); } else if (i==1) { // Assume we are at the bottom most of two adds Varnode *tmpvn = op->getIn(1-slot1); if (!tmpvn->isWritten()) continue; add2 = tmpvn->getDef(); if (add2->code() != CPUI_INT_ADD) continue; reshi = op->getOut(); hizext1 = add2->getIn(0); hizext2 = add2->getIn(1); } else { // Assume there is only one add, with second implied add by 0 reshi = op->getOut(); hizext1 = op->getIn(1-slot1); hizext2 = (Varnode *)0; } for(int4 j=0;j<2;++j) { if (i==2) { // hi2 is an implied 0 if (!hizext1->isWritten()) continue; zextop = hizext1->getDef(); hi2 = (Varnode *)0; } else if (j==0) { if (!hizext1->isWritten()) continue; zextop = hizext1->getDef(); hi2 = hizext2; } else { if (!hizext2->isWritten()) continue; zextop = hizext2->getDef(); hi2 = hizext1; } if (!checkForCarry(zextop)) continue; // Calculate lo2 and negconst list::const_iterator iter2,enditer2; iter2 = lo1->beginDescend(); enditer2 = lo1->endDescend(); while(iter2 != enditer2) { loadd = *iter2; ++iter2; if (loadd->code() != CPUI_INT_ADD) continue; Varnode *tmpvn = loadd->getIn(1-loadd->getSlot(lo1)); if (lo2 == (Varnode *)0) { if (!tmpvn->isConstant()) continue; if (tmpvn->getOffset() != negconst) continue; // Must add same constant used to calculate CARRY lo2 = tmpvn; } else if (lo2->isConstant()) { if (!tmpvn->isConstant()) continue; if (lo2->getOffset() != tmpvn->getOffset()) continue; } else if (loadd->getIn(1-loadd->getSlot(lo1)) != lo2) // Must add same value used to calculate CARRY continue; reslo = loadd->getOut(); return true; } } } return false; } bool AddForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verify(in.getHi(),in.getLo(),op)) return false; indoub.initPartial(in.getSize(),lo2,hi2); if (indoub.exceedsConstPrecision()) return false; outdoub.initPartial(in.getSize(),reslo,reshi); existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub); if (existop == (PcodeOp *)0) return false; SplitVarnode::createBinaryOp(data,outdoub,in,indoub,existop,CPUI_INT_ADD); return true; } // Given a known double precision input, look for a double precision subtraction, // recovering the other double input and the double output // // Assume hi1, lo1 is a known double precision pair, we look for // reshi = hi1 + -hi2 + - zext(lo1 < lo2) // reslo = lo1 + -lo2 bool SubForm::verify(Varnode *h,Varnode *l,PcodeOp *op) { list::const_iterator iter2,enditer2; hi1 = h; lo1 = l; slot1 = op->getSlot(hi1); for(int4 i=0;i<2;++i) { if (i==0) { // Assume we have to descend one more add add2 = op->getOut()->loneDescend(); if (add2 == (PcodeOp *)0) continue; if (add2->code() != CPUI_INT_ADD) continue; reshi = add2->getOut(); hineg1 = op->getIn(1-slot1); hineg2 = add2->getIn(1-add2->getSlot(op->getOut())); } else { Varnode *tmpvn = op->getIn(1-slot1); if (!tmpvn->isWritten()) continue; add2 = tmpvn->getDef(); if (add2->code() != CPUI_INT_ADD) continue; reshi = op->getOut(); hineg1 = add2->getIn(0); hineg2 = add2->getIn(1); } if (!hineg1->isWritten()) continue; if (!hineg2->isWritten()) continue; if (!SplitVarnode::verifyMultNegOne(hineg1->getDef())) continue; if (!SplitVarnode::verifyMultNegOne(hineg2->getDef())) continue; hizext1 = hineg1->getDef()->getIn(0); hizext2 = hineg2->getDef()->getIn(0); for(int4 j=0;j<2;++j) { if (j==0) { if (!hizext1->isWritten()) continue; zextop = hizext1->getDef(); hi2 = hizext2; } else { if (!hizext2->isWritten()) continue; zextop = hizext2->getDef(); hi2 = hizext1; } if (zextop->code() != CPUI_INT_ZEXT) continue; if (!zextop->getIn(0)->isWritten()) continue; lessop = zextop->getIn(0)->getDef(); if (lessop->code() != CPUI_INT_LESS) continue; if (lessop->getIn(0) != lo1) continue; lo2 = lessop->getIn(1); iter2 = lo1->beginDescend(); enditer2 = lo1->endDescend(); while(iter2 != enditer2) { loadd = *iter2; ++iter2; if (loadd->code() != CPUI_INT_ADD) continue; Varnode *tmpvn = loadd->getIn(1-loadd->getSlot(lo1)); if (!tmpvn->isWritten()) continue; negop = tmpvn->getDef(); if (!SplitVarnode::verifyMultNegOne(negop)) continue; if (negop->getIn(0) != lo2) continue; reslo = loadd->getOut(); return true; } } } return false; } bool SubForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verify(in.getHi(),in.getLo(),op)) return false; indoub.initPartial(in.getSize(),lo2,hi2); if (indoub.exceedsConstPrecision()) return false; outdoub.initPartial(in.getSize(),reslo,reshi); existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub); if (existop == (PcodeOp *)0) return false; SplitVarnode::createBinaryOp(data,outdoub,in,indoub,existop,CPUI_INT_SUB); return true; } int4 LogicalForm::findHiMatch(void) { // Look for the op computing the most significant part of the result for which -loop- computes // the least significant part, look for a known double precis out, then look for known double // precis in. If the other input is constant, look for a unique op that might be computing the high, // Return 0 if we found an op, return -1, if we can't find an op, return -2 if no op exists Varnode *lo1Tmp = in.getLo(); Varnode *vn2 = loop->getIn( 1-loop->getSlot( lo1Tmp ) ); SplitVarnode out; if (out.inHandLoOut(lo1Tmp)) { // If we already know what the double precision output looks like Varnode *hi = out.getHi(); if (hi->isWritten()) { // Just look at construction of hi precisi PcodeOp *maybeop = hi->getDef(); if (maybeop->code() == loop->code()) { if (maybeop->getIn(0) == hi1) { if (maybeop->getIn(1)->isConstant() == vn2->isConstant()) { hiop = maybeop; return 0; } } else if (maybeop->getIn(1) == hi1) { if (maybeop->getIn(0)->isConstant() == vn2->isConstant()) { hiop = maybeop; return 0; } } } } } if (!vn2->isConstant()) { SplitVarnode in2; if (in2.inHandLo(vn2)) { // If we already know what the other double precision input looks like list::const_iterator iter,enditer; iter = in2.getHi()->beginDescend(); enditer = in2.getHi()->endDescend(); while(iter != enditer) { PcodeOp *maybeop = *iter; ++iter; if (maybeop->code() == loop->code()) { if ((maybeop->getIn(0) == hi1)||(maybeop->getIn(1) == hi1)) { hiop = maybeop; return 0; } } } } return -1; } else { list::const_iterator iter,enditer; iter = hi1->beginDescend(); enditer = hi1->endDescend(); int4 count = 0; PcodeOp *lastop = (PcodeOp *)0; while(iter != enditer) { PcodeOp *maybeop = *iter; ++iter; if (maybeop->code() == loop->code()) { if (maybeop->getIn(1)->isConstant()) { count += 1; if (count > 1) break; lastop = maybeop; } } } if (count == 1) { hiop = lastop; return 0; } if (count >1) return -1; // Couldn't distinguish between multiple possibilities } return -2; } // Given a known double precision input, look for a double precision logical operation // recovering the other double input and the double output // // Assume hi1, lo1 is a known double precision pair, we look for // reshi = hi1 & hi2 // reslo = lo1 & lo2 bool LogicalForm::verify(Varnode *h,Varnode *l,PcodeOp *lop) { loop = lop; lo1 = l; hi1 = h; int4 res = findHiMatch(); if (res == 0) { // We found a matching lo presis operation lo2 = loop->getIn(1-loop->getSlot(lo1)); hi2 = hiop->getIn(1-hiop->getSlot(hi1)); if ((lo2==lo1)||(lo2==hi1)||(hi2==hi1)||(hi2==lo1)) return false; // No manipulation of itself if (lo2 == hi2) return false; return true; } return false; } bool LogicalForm::applyRule(SplitVarnode &i,PcodeOp *lop,bool workishi,Funcdata &data) { if (workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verify(in.getHi(),in.getLo(),lop)) return false; outdoub.initPartial(in.getSize(),loop->getOut(),hiop->getOut()); indoub.initPartial(in.getSize(),lo2,hi2); if (indoub.exceedsConstPrecision()) return false; existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub); if (existop == (PcodeOp *)0) return false; SplitVarnode::createBinaryOp(data,outdoub,in,indoub,existop,loop->code()); return true; } // Given a known double precis input, look for double precision compares of the form // a == b, a != b // // We look for // hibool = hi1 == hi2 // lobool = lo1 == lo2 // each of the bools induces a CBRANCH // if (hibool) blocksecond else blockfalse // blocksecond: if (lobool) blocktrue else blockfalse bool Equal1Form::applyRule(SplitVarnode &i,PcodeOp *hop,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in1 = i; hiop = hop; hi1 = in1.getHi(); lo1 = in1.getLo(); hi1slot = hiop->getSlot(hi1); hi2 = hiop->getIn(1-hi1slot); notequalformhi = (hiop->code() == CPUI_INT_NOTEQUAL); list::const_iterator iter,enditer; list::const_iterator iter2,enditer2; list::const_iterator iter3,enditer3; iter = lo1->beginDescend(); enditer = lo1->endDescend(); while(iter != enditer) { loop = *iter; ++iter; if (loop->code() == CPUI_INT_EQUAL) notequalformlo = false; else if (loop->code() == CPUI_INT_NOTEQUAL) notequalformlo = true; else continue; lo1slot = loop->getSlot(lo1); lo2 = loop->getIn(1-lo1slot); iter2 = hiop->getOut()->beginDescend(); enditer2 = hiop->getOut()->endDescend(); while(iter2 != enditer2) { hibool = *iter2; ++iter2; iter3 = loop->getOut()->beginDescend(); enditer3 = loop->getOut()->endDescend(); while(iter3 != enditer3) { lobool = *iter3; ++iter3; in2.initPartial(in1.getSize(),lo2,hi2); if (in2.exceedsConstPrecision()) continue; if ((hibool->code() == CPUI_CBRANCH)&&(lobool->code()==CPUI_CBRANCH)) { // Branching form of the equal operation BlockBasic *hibooltrue,*hiboolfalse; BlockBasic *lobooltrue,*loboolfalse; SplitVarnode::getTrueFalse(hibool,notequalformhi,hibooltrue,hiboolfalse); SplitVarnode::getTrueFalse(lobool,notequalformlo,lobooltrue,loboolfalse); if ((hibooltrue == lobool->getParent())&& // hi is checked first then lo (hiboolfalse == loboolfalse)&& SplitVarnode::otherwiseEmpty(lobool)) { if (SplitVarnode::prepareBoolOp(in1,in2,hibool)) { setonlow = true; SplitVarnode::createBoolOp(data,hibool,in1,in2,notequalformhi ? CPUI_INT_NOTEQUAL : CPUI_INT_EQUAL); // We change lobool so that it always goes to the original TRUE block data.opSetInput(lobool,data.newConstant(1,notequalformlo ? 0 : 1),1); return true; } } else if ((lobooltrue == hibool->getParent())&& // lo is checked first then hi (hiboolfalse == loboolfalse)&& SplitVarnode::otherwiseEmpty(hibool)) { if (SplitVarnode::prepareBoolOp(in1,in2,lobool)) { setonlow = false; SplitVarnode::createBoolOp(data,lobool,in1,in2,notequalformlo ? CPUI_INT_NOTEQUAL : CPUI_INT_EQUAL); // We change hibool so that it always goes to the original TRUE block data.opSetInput(hibool,data.newConstant(1,notequalformhi ? 0 : 1),1); return true; } } } } } } return false; } bool Equal2Form::replace(Funcdata &data) { if (hi2->isConstant()&&lo2->isConstant()) { uintb val = hi2->getOffset(); val <<= 8*lo1->getSize(); val |= lo2->getOffset(); param2.initPartial(in.getSize(),val); return SplitVarnode::prepareBoolOp(in,param2,boolAndOr); } if (hi2->isConstant()||lo2->isConstant()) { // Some kind of mixed form return false; } param2.initPartial(in.getSize(),lo2,hi2); return SplitVarnode::prepareBoolOp(in,param2,boolAndOr); } // Given a known double precis input, look for double precision compares of the form // a == b, a != b // // We look for // res = (hi1 == hi2) && (lo1 == lo2) or // res = (hi1 != hi2) || (lo1 != lo2) bool Equal2Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; hi1 = in.getHi(); lo1 = in.getLo(); OpCode eqCode = op->code(); int4 hi1slot = op->getSlot(hi1); hi2 = op->getIn(1-hi1slot); Varnode *outvn = op->getOut(); list::const_iterator iter,enditer; iter = outvn->beginDescend(); enditer = outvn->endDescend(); while(iter != enditer) { boolAndOr = *iter; ++iter; if (eqCode == CPUI_INT_EQUAL && boolAndOr->code() != CPUI_BOOL_AND) continue; if (eqCode == CPUI_INT_NOTEQUAL && boolAndOr->code() != CPUI_BOOL_OR) continue; int4 slot = boolAndOr->getSlot(outvn); Varnode *othervn = boolAndOr->getIn(1-slot); if (!othervn->isWritten()) continue; PcodeOp *equalLo = othervn->getDef(); if (equalLo->code() != eqCode) continue; if (equalLo->getIn(0) == lo1) { lo2 = equalLo->getIn(1); } else if (equalLo->getIn(1) == lo1) { lo2 = equalLo->getIn(0); } else { continue; } if (!replace(data)) continue; if (param2.exceedsConstPrecision()) continue; SplitVarnode::replaceBoolOp(data,boolAndOr,in,param2,eqCode); return true; } return false; } bool Equal3Form::verify(Varnode *h,Varnode *l,PcodeOp *aop) { if (aop->code() != CPUI_INT_AND) return false; hi = h; lo = l; andop = aop; int4 hislot = andop->getSlot(hi); if (andop->getIn(1-hislot) != lo) return false; // hi and lo must be ANDed together compareop = andop->getOut()->loneDescend(); if (compareop == (PcodeOp *)0) return false; if ((compareop->code()!=CPUI_INT_EQUAL)&&(compareop->code()!=CPUI_INT_NOTEQUAL)) return false; uintb allonesval = calc_mask(lo->getSize()); smallc = compareop->getIn(1); if (!smallc->isConstant()) return false; if (smallc->getOffset() != allonesval) return false; return true; } // Given a known double precis input, look for double precision compares of the form // a == -1, a != -1 // // We look for // hi & lo == -1 bool Equal3Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verify(in.getHi(),in.getLo(),op)) return false; SplitVarnode in2(in.getSize(),calc_mask(in.getSize())); // Create the -1 value if (in2.exceedsConstPrecision()) return false; if (!SplitVarnode::prepareBoolOp(in,in2,compareop)) return false; SplitVarnode::replaceBoolOp(data,compareop,in,in2,compareop->code()); return true; } bool LessThreeWay::mapBlocksFromLow(BlockBasic *lobl) { // Assuming -lobl- is the block containing the low precision test of a double precision lessthan // Map out all the blocks if possible, otherwise return false lolessbl = lobl; if (lolessbl->sizeIn() != 1) return false; if (lolessbl->sizeOut() != 2) return false; hieqbl = (BlockBasic *)lolessbl->getIn(0); if (hieqbl->sizeIn() != 1) return false; if (hieqbl->sizeOut() != 2) return false; hilessbl = (BlockBasic *)hieqbl->getIn(0); if (hilessbl->sizeOut() != 2) return false; return true; } bool LessThreeWay::mapOpsFromBlocks(void) { lolessbool = lolessbl->lastOp(); if (lolessbool == (PcodeOp *)0) return false; if (lolessbool->code() != CPUI_CBRANCH) return false; hieqbool = hieqbl->lastOp(); if (hieqbool == (PcodeOp *)0) return false; if (hieqbool->code() != CPUI_CBRANCH) return false; hilessbool = hilessbl->lastOp(); if (hilessbool == (PcodeOp *)0) return false; if (hilessbool->code() != CPUI_CBRANCH) return false; Varnode *vn; hiflip = false; equalflip = false; loflip = false; midlessform = false; lolessiszerocomp = false; vn = hieqbool->getIn(1); if (!vn->isWritten()) return false; hiequal = vn->getDef(); switch(hiequal->code()) { case CPUI_INT_EQUAL: midlessform = false; break; case CPUI_INT_NOTEQUAL: midlessform = false; break; case CPUI_INT_LESS: midlessequal = false; midsigncompare = false; midlessform = true; break; case CPUI_INT_LESSEQUAL: midlessequal = true; midsigncompare = false; midlessform = true; break; case CPUI_INT_SLESS: midlessequal = false; midsigncompare = true; midlessform = true; break; case CPUI_INT_SLESSEQUAL: midlessequal = true; midsigncompare = true; midlessform = true; break; default: return false; } vn = lolessbool->getIn(1); if (!vn->isWritten()) return false; loless = vn->getDef(); switch(loless->code()) { // Only unsigned forms case CPUI_INT_LESS: lolessequalform = false; break; case CPUI_INT_LESSEQUAL: lolessequalform = true; break; case CPUI_INT_EQUAL: if (!loless->getIn(1)->isConstant()) return false; if (loless->getIn(1)->getOffset()!=0) return false; lolessiszerocomp = true; lolessequalform = true; break; case CPUI_INT_NOTEQUAL: if (!loless->getIn(1)->isConstant()) return false; if (loless->getIn(1)->getOffset()!=0) return false; lolessiszerocomp = true; lolessequalform = false; break; default: return false; } vn = hilessbool->getIn(1); if (!vn->isWritten()) return false; hiless = vn->getDef(); switch(hiless->code()){ case CPUI_INT_LESS: hilessequalform = false; signcompare = false; break; case CPUI_INT_LESSEQUAL: hilessequalform = true; signcompare = false; break; case CPUI_INT_SLESS: hilessequalform = false; signcompare = true; break; case CPUI_INT_SLESSEQUAL: hilessequalform = true; signcompare = true; break; default: return false; } return true; } bool LessThreeWay::checkSignedness(void) { if (midlessform) { if (midsigncompare != signcompare) return false; } return true; } bool LessThreeWay::normalizeHi(void) { Varnode *tmpvn; vnhil1 = hiless->getIn(0); vnhil2 = hiless->getIn(1); if (vnhil1->isConstant()) { // Start with constant on the right hiflip = !hiflip; hilessequalform = !hilessequalform; tmpvn = vnhil1; vnhil1 = vnhil2; vnhil2 = tmpvn; } hiconstform = false; if (vnhil2->isConstant()) { if (in.getSize() > sizeof(uintb)) return false; // Must have enough precision for constant hiconstform = true; hival = vnhil2->getOffset(); SplitVarnode::getTrueFalse(hilessbool,hiflip,hilesstrue,hilessfalse); int4 inc = 1; if (hilessfalse != hieqbl) { // Make sure the hiless false branch goes to the hieq block hiflip = !hiflip; hilessequalform = !hilessequalform; tmpvn = vnhil1; vnhil1 = vnhil2; vnhil2 = tmpvn; inc = -1; } if (hilessequalform) { // Make sure to normalize lessequal to less hival += inc; hival &= calc_mask(in.getSize()); hilessequalform = false; } hival >>= in.getLo()->getSize() * 8; } else { if (hilessequalform) { // Make sure the false branch contains the equal case hilessequalform = false; hiflip = !hiflip; tmpvn = vnhil1; vnhil1 = vnhil2; vnhil2 = tmpvn; } } return true; } bool LessThreeWay::normalizeMid(void) { Varnode *tmpvn; vnhie1 = hiequal->getIn(0); vnhie2 = hiequal->getIn(1); if (vnhie1->isConstant()) { // Make sure constant is on the right tmpvn = vnhie1; vnhie1 = vnhie2; vnhie2 = tmpvn; if (midlessform) { equalflip = !equalflip; midlessequal = !midlessequal; } } midconstform = false; if (vnhie2->isConstant()) { if (!hiconstform) return false; // If mid is constant, both mid and hi must be constant midconstform = true; midval = vnhie2->getOffset(); if (vnhie2->getSize() == in.getSize()) { // Convert to comparison on high part uintb lopart = midval & calc_mask(in.getLo()->getSize()); midval >>= in.getLo()->getSize()*8; if (midlessform) { if (midlessequal) { if (lopart!=calc_mask(in.getLo()->getSize())) return false; } else { if (lopart != 0) return false; } } else return false; // Compare is forcing restriction on lo part } if (midval != hival) { // If the mid and hi don't match if (!midlessform) return false; midval += (midlessequal) ? 1 : -1; // We may just be one off midval &= calc_mask(in.getLo()->getSize()); midlessequal = !midlessequal; if (midval != hival) return false; // Last chance } } if (midlessform) { // Normalize to EQUAL if (!midlessequal) { equalflip = !equalflip; } } else { if (hiequal->code() == CPUI_INT_NOTEQUAL) { equalflip = !equalflip; } } return true; } bool LessThreeWay::normalizeLo(void) { // This is basically identical to normalizeHi Varnode *tmpvn; vnlo1 = loless->getIn(0); vnlo2 = loless->getIn(1); if (lolessiszerocomp) { loconstform = true; if (lolessequalform) { // Treat as if we see vnlo1 <= 0 loval = 1; lolessequalform = false; } else { // Treat as if we see 0 < vnlo1 loflip = !loflip; loval = 1; } return true; } if (vnlo1->isConstant()) { // Make sure constant is on the right loflip = !loflip; lolessequalform = !lolessequalform; tmpvn = vnlo1; vnlo1 = vnlo2; vnlo2 = tmpvn; } loconstform = false; if (vnlo2->isConstant()) { // Make sure normalize lessequal to less loconstform = true; loval = vnlo2->getOffset(); if (lolessequalform) { loval += 1; loval &= calc_mask(vnlo2->getSize()); lolessequalform = false; } } else { if (lolessequalform) { lolessequalform = false; loflip = !loflip; tmpvn = vnlo1; vnlo1 = vnlo2; vnlo2 = tmpvn; } } return true; } bool LessThreeWay::checkBlockForm(void) { SplitVarnode::getTrueFalse(hilessbool,hiflip,hilesstrue,hilessfalse); SplitVarnode::getTrueFalse(lolessbool,loflip,lolesstrue,lolessfalse); SplitVarnode::getTrueFalse(hieqbool,equalflip,hieqtrue,hieqfalse); if ((hilesstrue == lolesstrue)&& (hieqfalse == lolessfalse)&& (hilessfalse == hieqbl)&& (hieqtrue == lolessbl)) { if (SplitVarnode::otherwiseEmpty(hieqbool)&&SplitVarnode::otherwiseEmpty(lolessbool)) return true; } // else if ((hilessfalse == lolessfalse)&& // (hieqfalse == lolesstrue)&& // (hilesstrue == hieqbl)&& // (hieqtrue == lolessbl)) { // if (SplitVarnode::otherwiseEmpty(hieqbool)&&SplitVarnode::otherwiseEmpty(lolessbool)) // return true; // } return false; } bool LessThreeWay::checkOpForm(void) { lo = in.getLo(); hi = in.getHi(); if (midconstform) { if (!hiconstform) return false; if (vnhie2->getSize() == in.getSize()) { if ((vnhie1!=vnhil1)&&(vnhie1!=vnhil2)) return false; } else { if (vnhie1!=in.getHi()) return false; } // normalizeMid checks that midval == hival } else { // hi and hi2 must appear as inputs in both -hiless- and -hiequal- if ((vnhil1!=vnhie1)&&(vnhil1!=vnhie2)) return false; if ((vnhil2!=vnhie1)&&(vnhil2!=vnhie2)) return false; } if ((hi!=(Varnode *)0)&&(hi == vnhil1)) { if (hiconstform) return false; hislot = 0; hi2 = vnhil2; if (vnlo1 != lo) { // Pieces must be on the same side Varnode *tmpvn = vnlo1; vnlo1 = vnlo2; vnlo2 = tmpvn; if (vnlo1 != lo) return false; loflip = !loflip; lolessequalform = !lolessequalform; } lo2 = vnlo2; } else if ((hi!=(Varnode *)0)&&(hi == vnhil2)) { if (hiconstform) return false; hislot = 1; hi2 = vnhil1; if (vnlo2 != lo) { Varnode *tmpvn = vnlo1; vnlo1 = vnlo2; vnlo2 = tmpvn; if (vnlo2 != lo) return false; loflip = !loflip; lolessequalform = !lolessequalform; } lo2 = vnlo1; } else if (in.getWhole() == vnhil1) { if (!hiconstform) return false; if (!loconstform) return false; if (vnlo1 != lo) return false; hislot = 0; } else if (in.getWhole() == vnhil2) { // Whole constant appears on the left if (!hiconstform) return false; if (!loconstform) return false; if (vnlo2 != lo) { loflip = !loflip; loval -= 1; loval &= calc_mask(lo->getSize()); if (vnlo1 != lo) return false; } hislot = 1; } else return false; return true; } void LessThreeWay::setOpCode(void) { // Decide on the opcode of the final double precision compare if (lolessequalform != hiflip) finalopc = signcompare ? CPUI_INT_SLESSEQUAL : CPUI_INT_LESSEQUAL; else finalopc = signcompare ? CPUI_INT_SLESS : CPUI_INT_LESS; if (hiflip) { hislot = 1-hislot; hiflip = false; } } bool LessThreeWay::setBoolOp(void) { // Make changes to the threeway branch so that it becomes a single double precision branch if (hislot==0) { if (SplitVarnode::prepareBoolOp(in,in2,hilessbool)) return true; } else { if (SplitVarnode::prepareBoolOp(in2,in,hilessbool)) return true; } return false; } bool LessThreeWay::mapFromLow(PcodeOp *op) { // Given the less than comparison for the lo piece and an input varnode explicitly marked as isPrecisLo // try to map out the threeway lessthan form PcodeOp *loop = op->getOut()->loneDescend(); if (loop == (PcodeOp *)0) return false; if (!mapBlocksFromLow(loop->getParent())) return false; if (!mapOpsFromBlocks()) return false; if (!checkSignedness()) return false; if (!normalizeHi()) return false; if (!normalizeMid()) return false; if (!normalizeLo()) return false; if (!checkOpForm()) return false; if (!checkBlockForm()) return false; return true; } bool LessThreeWay::testReplace(void) { setOpCode(); if (hiconstform) { in2.initPartial(in.getSize(),(hival<<(8*in.getLo()->getSize()))|loval); if (!setBoolOp()) return false; } else { in2.initPartial(in.getSize(), lo2,hi2); if (!setBoolOp()) return false; } return true; } // Given a known double precis input, look for double precision less than forms, i.e. // a < b, a s< b, a <= b, a s<= b // // In this form we look for three separate comparison ops // hiless = hi1 LESS hi2 where LESS is in { <, s<, <=, s<= } // hiequal = hi1 == hi2 // loless = lo1 < lo2 OR lo1 <= lo2 where the comparison is unsigned // // This boolean values are either combined in the following formula: // resbool = hiless || (hiequal && loless) // OR each of the three initial comparison induces a CBRANCH // if (hiless) blocktrue else blocksecond // blocksecond: if (hiequal) blockthird else blockfalse // blockthird: if (loless) blocktrue else blockfalse bool LessThreeWay::applyRule(SplitVarnode &i,PcodeOp *loop,bool workishi,Funcdata &data) { if (workishi) return false; if (i.getLo() == (Varnode *)0) return false; // Doesn't necessarily need the hi in = i; if (!mapFromLow(loop)) return false; bool res = testReplace(); if (res) { if (in2.exceedsConstPrecision()) return false; if (hislot==0) SplitVarnode::createBoolOp(data,hilessbool,in,in2,finalopc); else SplitVarnode::createBoolOp(data,hilessbool,in2,in,finalopc); // We change hieqbool so that it always goes to the original FALSE block data.opSetInput(hieqbool,data.newConstant(1,equalflip ? 1 : 0),1); // The lolessbool block now becomes unreachable and is eventually removed } return res; } // Sometimes double precision compares only involve the high portion of the value // The canonical example being determining whether val > 0, where we only have to // calculate (hi > 0). This rule takes // hi COMPARE #const // and transforms it to // whole COMPARE #constextend // where #constextend is built from #const by postpending either all 0 bits or 1 bits bool LessConstForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data) { if (!workishi) return false; if (i.getHi()==(Varnode *)0) return false; // We don't necessarily need the lo part in = i; vn = in.getHi(); inslot = op->getSlot(vn); cvn = op->getIn(1-inslot); int4 losize = in.getSize() - vn->getSize(); if (!cvn->isConstant()) return false; signcompare = ((op->code()==CPUI_INT_SLESSEQUAL)||(op->code()==CPUI_INT_SLESS)); hilessequalform = ((op->code()==CPUI_INT_SLESSEQUAL)||(op->code()==CPUI_INT_LESSEQUAL)); uintb val = cvn->getOffset() << 8*losize; if (hilessequalform != (inslot==1)) val |= calc_mask(losize); // This rule can apply and mess up less,equal rules, so we only apply it if it directly affects a branch PcodeOp *desc = op->getOut()->loneDescend(); if (desc == (PcodeOp *)0) return false; if (desc->code() != CPUI_CBRANCH) return false; constin.initPartial(in.getSize(),val); if (constin.exceedsConstPrecision()) return false; if (inslot==0) { if (SplitVarnode::prepareBoolOp(in,constin,op)) { SplitVarnode::replaceBoolOp(data,op,in,constin,op->code()); return true; } } else { if (SplitVarnode::prepareBoolOp(constin,in,op)) { SplitVarnode::replaceBoolOp(data,op,constin,in,op->code()); return true; } } return false; } bool ShiftForm::mapLeft(void) { // Assume reshi, reslo are filled in, fill in other ops and varnodes if (!reslo->isWritten()) return false; if (!reshi->isWritten()) return false; loshift = reslo->getDef(); opc = loshift->code(); if (opc != CPUI_INT_LEFT) return false; orop = reshi->getDef(); if ((orop->code() != CPUI_INT_OR)&&(orop->code() != CPUI_INT_XOR)&&(orop->code() != CPUI_INT_ADD)) return false; midlo = orop->getIn(0); midhi = orop->getIn(1); if (!midlo->isWritten()) return false; if (!midhi->isWritten()) return false; if (midhi->getDef()->code() != CPUI_INT_LEFT) { Varnode *tmpvn = midhi; midhi = midlo; midlo = tmpvn; } midshift = midlo->getDef(); if (midshift->code() != CPUI_INT_RIGHT) return false; // Must be unsigned RIGHT hishift = midhi->getDef(); if (hishift->code() != CPUI_INT_LEFT) return false; if (lo != loshift->getIn(0)) return false; if (hi != hishift->getIn(0)) return false; if (lo != midshift->getIn(0)) return false; salo = loshift->getIn(1); sahi = hishift->getIn(1); samid = midshift->getIn(1); return true; } bool ShiftForm::mapRight(void) { // Assume reshi, reslo are filled in, fill in other ops and varnodes if (!reslo->isWritten()) return false; if (!reshi->isWritten()) return false; hishift = reshi->getDef(); opc = hishift->code(); if ((opc != CPUI_INT_RIGHT)&&(opc != CPUI_INT_SRIGHT)) return false; orop = reslo->getDef(); if ((orop->code() != CPUI_INT_OR)&&(orop->code() != CPUI_INT_XOR)&&(orop->code() != CPUI_INT_ADD)) return false; midlo = orop->getIn(0); midhi = orop->getIn(1); if (!midlo->isWritten()) return false; if (!midhi->isWritten()) return false; if (midlo->getDef()->code() != CPUI_INT_RIGHT) { // Must be unsigned RIGHT Varnode *tmpvn = midhi; midhi = midlo; midlo = tmpvn; } midshift = midhi->getDef(); if (midshift->code() != CPUI_INT_LEFT) return false; loshift = midlo->getDef(); if (loshift->code() != CPUI_INT_RIGHT) return false; // Must be unsigned RIGHT if (lo != loshift->getIn(0)) return false; if (hi != hishift->getIn(0)) return false; if (hi != midshift->getIn(0)) return false; salo = loshift->getIn(1); sahi = hishift->getIn(1); samid = midshift->getIn(1); return true; } bool ShiftForm::verifyShiftAmount(void) { // Make sure all the shift amount varnodes are consistent if (!salo->isConstant()) return false; if (!samid->isConstant()) return false; if (!sahi->isConstant()) return false; uintb val = salo->getOffset(); if (val != sahi->getOffset()) return false; if (val >= 8*lo->getSize()) return false; // If shift amount is so big, we would not use this form val = 8*lo->getSize() - val; if (samid->getOffset() != val) return false; return true; } bool ShiftForm::verifyLeft(Varnode *h,Varnode *l,PcodeOp *loop) { hi = h; lo = l; loshift = loop; reslo = loshift->getOut(); list::const_iterator iter,enditer; iter = hi->beginDescend(); enditer = hi->endDescend(); while(iter != enditer) { hishift = *iter; ++iter; if (hishift->code() != CPUI_INT_LEFT) continue; Varnode *outvn = hishift->getOut(); list::const_iterator iter2,enditer2; iter2 = outvn->beginDescend(); enditer2 = outvn->endDescend(); while(iter2 != enditer2) { midshift = *iter2; ++iter2; Varnode *tmpvn = midshift->getOut(); if (tmpvn == (Varnode *)0) continue; reshi = tmpvn; if (!mapLeft()) continue; if (!verifyShiftAmount()) continue; return true; } } return false; } bool ShiftForm::verifyRight(Varnode *h,Varnode *l,PcodeOp *hiop) { hi = h; lo = l; hishift = hiop; reshi = hiop->getOut(); list::const_iterator iter,enditer; iter = lo->beginDescend(); enditer = lo->endDescend(); while(iter != enditer) { loshift = *iter; ++iter; if (loshift->code() != CPUI_INT_RIGHT) continue; Varnode *outvn = loshift->getOut(); list::const_iterator iter2,enditer2; iter2 = outvn->beginDescend(); enditer2 = outvn->endDescend(); while(iter2 != enditer2) { midshift = *iter2; ++iter2; Varnode *tmpvn = midshift->getOut(); if (tmpvn == (Varnode *)0) continue; reslo = tmpvn; if (!mapRight()) continue; if (!verifyShiftAmount()) continue; return true; } } return false; } bool ShiftForm::applyRuleLeft(SplitVarnode &i,PcodeOp *loop,bool workishi,Funcdata &data) { if (workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verifyLeft(in.getHi(),in.getLo(),loop)) return false; out.initPartial(in.getSize(),reslo,reshi); existop = SplitVarnode::prepareShiftOp(out,in); if (existop == (PcodeOp *)0) return false; SplitVarnode::createShiftOp(data,out,in,salo,existop,opc); return true; } bool ShiftForm::applyRuleRight(SplitVarnode &i,PcodeOp *hiop,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verifyRight(in.getHi(),in.getLo(),hiop)) return false; out.initPartial(in.getSize(),reslo,reshi); existop = SplitVarnode::prepareShiftOp(out,in); if (existop == (PcodeOp *)0) return false; SplitVarnode::createShiftOp(data,out,in,salo,existop,opc); return true; } bool MultForm::mapResHiSmallConst(Varnode *rhi) { // find reshi=hi1*lo2 + (tmp>>32) reshi = rhi; if (!reshi->isWritten()) return false; add1 = reshi->getDef(); if (add1->code() != CPUI_INT_ADD) return false; Varnode *ad1,*ad2; ad1 = add1->getIn(0); ad2 = add1->getIn(1); if (!ad1->isWritten()) return false; if (!ad2->isWritten()) return false; multhi1 = ad1->getDef(); if (multhi1->code() != CPUI_INT_MULT) { subhi = multhi1; multhi1 = ad2->getDef(); } else subhi = ad2->getDef(); if (multhi1->code() != CPUI_INT_MULT) return false; if (subhi->code() != CPUI_SUBPIECE) return false; midtmp = subhi->getIn(0); if (!midtmp->isWritten()) return false; multlo = midtmp->getDef(); if (multlo->code() != CPUI_INT_MULT) return false; lo1zext = multlo->getIn(0); lo2zext = multlo->getIn(1); return true; } bool MultForm::mapResHi(Varnode *rhi) { // Find reshi=hi1*lo2 + hi2*lo1 + (tmp>>32) reshi = rhi; if (!reshi->isWritten()) return false; add1 = reshi->getDef(); if (add1->code() != CPUI_INT_ADD) return false; Varnode *ad1,*ad2,*ad3; ad1 = add1->getIn(0); ad2 = add1->getIn(1); if (!ad1->isWritten()) return false; if (!ad2->isWritten()) return false; add2 = ad1->getDef(); if (add2->code() == CPUI_INT_ADD) { ad1 = add2->getIn(0); ad3 = add2->getIn(1); } else { add2 = ad2->getDef(); if (add2->code() != CPUI_INT_ADD) return false; ad2 = add2->getIn(0); ad3 = add2->getIn(1); } if (!ad1->isWritten()) return false; if (!ad2->isWritten()) return false; if (!ad3->isWritten()) return false; subhi = ad1->getDef(); if (subhi->code() == CPUI_SUBPIECE) { multhi1 = ad2->getDef(); multhi2 = ad3->getDef(); } else { subhi = ad2->getDef(); if (subhi->code() == CPUI_SUBPIECE) { multhi1 = ad1->getDef(); multhi2 = ad3->getDef(); } else { subhi = ad3->getDef(); if (subhi->code() == CPUI_SUBPIECE) { multhi1 = ad1->getDef(); multhi2 = ad2->getDef(); } else return false; } } if (multhi1->code() != CPUI_INT_MULT) return false; if (multhi2->code() != CPUI_INT_MULT) return false; midtmp = subhi->getIn(0); if (!midtmp->isWritten()) return false; multlo = midtmp->getDef(); if (multlo->code() != CPUI_INT_MULT) return false; lo1zext = multlo->getIn(0); lo2zext = multlo->getIn(1); return true; } bool MultForm::findLoFromInSmallConst(void) { // Assuming we have -multhi1-, -lo1-, and -hi1- in hand, try to label -lo2- Varnode *vn1 = multhi1->getIn(0); Varnode *vn2 = multhi1->getIn(1); if (vn1 == hi1) lo2 = vn2; else if (vn2 == hi1) lo2 = vn1; else return false; if (!lo2->isConstant()) return false; hi2 = (Varnode *)0; // hi2 is an implied zero in this case return true; } bool MultForm::findLoFromIn(void) { // Assuming we have -multhi1-, -multhi2-, -lo1-, and -hi1- in hand, try to label lo2/hi2 pair Varnode *vn1 = multhi1->getIn(0); Varnode *vn2 = multhi1->getIn(1); if ((vn1 != lo1)&&(vn2!=lo1)) { // Try to normalize so multhi1 contains lo1 PcodeOp *tmpop = multhi1; multhi1 = multhi2; multhi2 = tmpop; vn1 = multhi1->getIn(0); vn2 = multhi1->getIn(1); } if (vn1 == lo1) hi2 = vn2; else if (vn2 == lo1) hi2 = vn1; else return false; vn1 = multhi2->getIn(0); // multhi2 should contain hi1 and lo2 vn2 = multhi2->getIn(1); if (vn1 == hi1) lo2 = vn2; else if (vn2 == hi1) lo2 = vn1; else return false; return true; } bool MultForm::zextOf(Varnode *big,Varnode *small) { // Verify that big is (some form of) a zero extension of small PcodeOp *op; if (small->isConstant()) { if (!big->isConstant()) return false; if (big->getOffset() == small->getOffset()) return true; return false; } if (!big->isWritten()) return false; op = big->getDef(); if (op->code() == CPUI_INT_ZEXT) return (op->getIn(0) == small); if (op->code() == CPUI_INT_AND) { if (!op->getIn(1)->isConstant()) return false; if (op->getIn(1)->getOffset() != calc_mask(small->getSize())) return false; Varnode *whole = op->getIn(0); if (!small->isWritten()) return false; PcodeOp *sub = small->getDef(); if (sub->code() != CPUI_SUBPIECE) return false; return (sub->getIn(0) == whole); } return false; } bool MultForm::verifyLo(void) { // Given we have labelled lo1/hi1 lo2/hi2, make sure midtmp is formed properly // This also works for the small constant model lo1/hi1 and lo2 const. if (subhi->getIn(1)->getOffset() != lo1->getSize()) return false; if (zextOf(lo1zext,lo1)) { if (zextOf(lo2zext,lo2)) return true; } else if (zextOf(lo1zext,lo2)) { if (zextOf(lo2zext,lo1)) return true; } return false; } bool MultForm::findResLo(void) { // Assuming we found -midtmp-, find potential reslo list::const_iterator iter,enditer; iter = midtmp->beginDescend(); enditer = midtmp->endDescend(); while(iter != enditer) { PcodeOp *op = *iter; ++iter; if (op->code() != CPUI_SUBPIECE) continue; if (op->getIn(1)->getOffset() != 0) continue; // Must grab low bytes reslo = op->getOut(); if (reslo->getSize() != lo1->getSize()) continue; return true; } // If we reach here, it may be that separate multiplies of lo1*lo2 were used for reshi and reslo iter = lo1->beginDescend(); enditer = lo1->endDescend(); while(iter != enditer) { PcodeOp *op = *iter; ++iter; if (op->code() != CPUI_INT_MULT) continue; Varnode *vn1 = op->getIn(0); Varnode *vn2 = op->getIn(1); if (lo2->isConstant()) { if ((!vn1->isConstant() || (vn1->getOffset() != lo2->getOffset())) && (!vn2->isConstant() || (vn2->getOffset() != lo2->getOffset()))) continue; } else if ((op->getIn(0)!=lo2)&&(op->getIn(1)!=lo2)) continue; reslo = op->getOut(); return true; } return false; } bool MultForm::mapFromInSmallConst(Varnode *rhi) { if (!mapResHiSmallConst(rhi)) return false; if (!findLoFromInSmallConst()) return false; if (!verifyLo()) return false; if (!findResLo()) return false; return true; } bool MultForm::mapFromIn(Varnode *rhi) { // Try to do full mapping from -in- given a putative reshi if (!mapResHi(rhi)) return false; if (!findLoFromIn()) return false; if (!verifyLo()) return false; if (!findResLo()) return false; return true; } bool MultForm::replace(Funcdata &data) { // We have matched a double precision multiply, now transform to logical variables outdoub.initPartial(in.getSize(),reslo,reshi); in2.initPartial(in.getSize(),lo2,hi2); if (in2.exceedsConstPrecision()) return false; existop = SplitVarnode::prepareBinaryOp(outdoub,in,in2); if (existop == (PcodeOp *)0) return false; SplitVarnode::createBinaryOp(data,outdoub,in,in2,existop,CPUI_INT_MULT); return true; } bool MultForm::verify(Varnode *h,Varnode *l,PcodeOp *hop) { hi1 = h; lo1 = l; list::const_iterator iter,enditer; iter = hop->getOut()->beginDescend(); enditer = hop->getOut()->endDescend(); while(iter != enditer) { add1 = *iter; ++iter; if (add1->code() != CPUI_INT_ADD) continue; list::const_iterator iter2,enditer2; iter2 = add1->getOut()->beginDescend(); enditer2 = add1->getOut()->endDescend(); while(iter2 != enditer2) { add2 = *iter2; ++iter2; if (add2->code() != CPUI_INT_ADD) continue; if (mapFromIn(add2->getOut())) return true; } if (mapFromIn(add1->getOut())) return true; if (mapFromInSmallConst(add1->getOut())) return true; } return false; } bool MultForm::applyRule(SplitVarnode &i,PcodeOp *hop,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verify(in.getHi(),in.getLo(),hop)) return false; if (replace(data)) return true; return false; } // Given a known double precis coming together with two other pieces (via phi-nodes) // Create a double precision phi-node bool PhiForm::verify(Varnode *h,Varnode *l,PcodeOp *hphi) { hibase = h; lobase = l; hiphi = hphi; inslot = hiphi->getSlot(hibase); if (hiphi->getOut()->hasNoDescend()) return false; blbase = hiphi->getParent(); list::const_iterator iter,enditer; iter = lobase->beginDescend(); enditer = lobase->endDescend(); while(iter != enditer) { lophi = *iter; ++iter; if (lophi->code() != CPUI_MULTIEQUAL) continue; if (lophi->getParent() != blbase) continue; if (lophi->getIn(inslot) != lobase) continue; return true; } return false; } bool PhiForm::applyRule(SplitVarnode &i,PcodeOp *hphi,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verify(in.getHi(),in.getLo(),hphi)) return false; int4 numin = hiphi->numInput(); vector inlist; for(int4 j=0;jgetIn(j); Varnode *vlo = lophi->getIn(j); inlist.push_back(SplitVarnode(vlo,vhi)); } outvn.initPartial(in.getSize(),lophi->getOut(),hiphi->getOut()); existop = SplitVarnode::preparePhiOp(outvn,inlist); if (existop != (PcodeOp *)0) { SplitVarnode::createPhiOp(data,outvn,inlist,existop); return true; } return false; } bool IndirectForm::verify(Varnode *h,Varnode *l,PcodeOp *ind) { // Verify the basic double precision indirect form and fill out the pieces hi = h; lo = l; indhi = ind; if (indhi->getIn(1)->getSpace()->getType()!=IPTR_IOP) return false; affector = PcodeOp::getOpFromConst(indhi->getIn(1)->getAddr()); if (affector->isDead()) return false; reshi = indhi->getOut(); if (reshi->getSpace()->getType() == IPTR_INTERNAL) return false; // Indirect must not be through a temporary list::const_iterator iter,enditer; iter = lo->beginDescend(); enditer = lo->endDescend(); while(iter != enditer) { indlo = *iter; ++iter; if (indlo->code() != CPUI_INDIRECT) continue; if (indlo->getIn(1)->getSpace()->getType()!=IPTR_IOP) continue; if (affector != PcodeOp::getOpFromConst(indlo->getIn(1)->getAddr())) continue; // hi and lo must be affected by same op reslo = indlo->getOut(); if (reslo->getSpace()->getType() == IPTR_INTERNAL) return false; // Indirect must not be through a temporary if (reslo->isAddrTied() || reshi->isAddrTied()) { Address addr; // If one piece is address tied, the other must be as well, and they must fit together as contiguous whole if (!SplitVarnode::isAddrTiedContiguous(reslo, reshi, addr)) return false; } return true; } return false; } bool IndirectForm::applyRule(SplitVarnode &i,PcodeOp *ind,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verify(in.getHi(),in.getLo(),ind)) return false; outvn.initPartial(in.getSize(),reslo,reshi); if (!SplitVarnode::prepareIndirectOp(in,affector)) return false; SplitVarnode::replaceIndirectOp(data,outvn,in,affector); return true; } /// Starting with the input pieces, identify the matching COPYs and verify that they act as a single /// address forced COPY with no descendants. /// \param h is the most significant input piece /// \param l is the least significant input piece /// \param w is the preexisting logical whole /// \param cpy is the COPY of the most significant piece bool CopyForceForm::verify(Varnode *h,Varnode *l,Varnode *w,PcodeOp *cpy) { if (w == (Varnode *)0) return false; copyhi = cpy; if (copyhi->getIn(0) != h) return false; reshi = copyhi->getOut(); if (!reshi->isAddrForce() || !reshi->hasNoDescend()) return false; list::const_iterator iter,enditer; iter = l->beginDescend(); enditer = l->endDescend(); while(iter != enditer) { copylo = *iter; ++iter; if (copylo->code() != CPUI_COPY || copylo->getParent() != copyhi->getParent()) continue; reslo = copylo->getOut(); if (!reslo->isAddrForce() || !reslo->hasNoDescend()) continue; if (!SplitVarnode::isAddrTiedContiguous(reslo, reshi, addrOut)) // Output MUST be contiguous addresses continue; if (copyhi->isReturnCopy()) { // Special form has additional requirements if (h->loneDescend() == (PcodeOp *)0) continue; if (l->loneDescend() == (PcodeOp *)0) continue; if (w->getAddr() != addrOut) { // Input whole MUST also be the same address // Unless there are addition COPYs from the same basic block if (!h->isWritten() || !l->isWritten()) continue; PcodeOp *otherLo = l->getDef(); PcodeOp *otherHi = h->getDef(); if (otherLo->code() != CPUI_COPY || otherHi->code() != CPUI_COPY) continue; if (otherLo->getParent() != otherHi->getParent()) continue; } } return true; } return false; } /// \param i is the putative input to the COPYs /// \param cpy is a putative COPY /// \param workishi is \b true if the COPY is of the most significant piece /// \param data is the function bool CopyForceForm::applyRule(SplitVarnode &i,PcodeOp *cpy,bool workishi,Funcdata &data) { if (!workishi) return false; if (!i.hasBothPieces()) return false; in = i; if (!verify(in.getHi(),in.getLo(),in.getWhole(),cpy)) return false; SplitVarnode::replaceCopyForce(data, addrOut, in, copylo, copyhi); return true; } void RuleDoubleIn::reset(Funcdata &data) { data.setDoublePrecisRecovery(true); // Mark that we are doing double precision recovery } void RuleDoubleIn::getOpList(vector &oplist) const { oplist.push_back(CPUI_SUBPIECE); } /// \brief Determine if the given Varnode from a SUBPIECE should be marked as a double precision piece /// /// If the given Varnode looks like the most significant piece, there is another SUBPIECE that looks /// like the least significant piece, and the whole is from an operation that produces a logical whole, /// then mark the Varnode (and its companion) as double precision pieces and return 1. /// \param vn is the given Varnode /// \param subpieceOp is the SUBPIECE PcodeOp producing the Varnode /// \return 1 if the pieces are marked, 0 otherwise int4 RuleDoubleIn::attemptMarking(Varnode *vn,PcodeOp *subpieceOp) { Varnode *whole = subpieceOp->getIn(0); if (whole->isTypeLock()) { if (!whole->getType()->isPrimitiveWhole()) return 0; // Don't mark for double precision if not a primitive type } int4 offset = (int4)subpieceOp->getIn(1)->getOffset(); if (offset != vn->getSize()) return 0; if (offset * 2 != whole->getSize()) return 0; // Truncate exactly half if (whole->isInput()) { if (!whole->isTypeLock()) return 0; } else if (!whole->isWritten()) { return 0; } else { // Categorize opcodes as "producing a logical whole" // Its hard to tell if a logical op is really being used to act on the "logical whole" TypeOp *typeop = whole->getDef()->getOpcode(); if (!typeop->isArithmeticOp() && !typeop->isFloatingPointOp()) return 0; } Varnode *vnLo = (Varnode *)0; list::const_iterator iter; for(iter=whole->beginDescend();iter!=whole->endDescend();++iter) { PcodeOp *op = *iter; if (op->code() != CPUI_SUBPIECE) continue; if (op->getIn(1)->getOffset() != 0) continue; if (op->getOut()->getSize() == vn->getSize()) { vnLo = op->getOut(); break; } } if (vnLo == (Varnode *)0) return 0; vnLo->setPrecisLo(); vn->setPrecisHi(); return 1; } int4 RuleDoubleIn::applyOp(PcodeOp *op,Funcdata &data) { Varnode *outvn = op->getOut(); if (!outvn->isPrecisLo()) { if (outvn->isPrecisHi()) return 0; return attemptMarking(outvn, op); } if (data.hasUnreachableBlocks()) return 0; vector splitvec; SplitVarnode::wholeList(op->getIn(0),splitvec); if (splitvec.empty()) return 0; for(int4 i=0;i &oplist) const { oplist.push_back(CPUI_PIECE); } /// \brief Determine if the given inputs to a PIECE should marked as double precision pieces /// /// If the concatenation of the pieces is used as a logical whole by other ops, the two pieces /// are marked and 1 is returned. /// \param vnhi is the most significant input to the PIECE /// \param vnlo is the least significant input /// \param pieceOp is the op reading the pieces /// \return 1 if the pieces are marked, 0 otherwise int4 RuleDoubleOut::attemptMarking(Varnode *vnhi,Varnode *vnlo,PcodeOp *pieceOp) { Varnode *whole = pieceOp->getOut(); if (whole->isTypeLock()) { if (!whole->getType()->isPrimitiveWhole()) return 0; // Don't mark for double precision if not a primitive type } if (vnhi->getSize() != vnlo->getSize()) return 0; SymbolEntry *entryhi = vnhi->getSymbolEntry(); SymbolEntry *entrylo = vnlo->getSymbolEntry(); if (entryhi != (SymbolEntry *)0 || entrylo != (SymbolEntry *)0) { if (entryhi == (SymbolEntry *)0 || entrylo == (SymbolEntry *)0) return 0; // One has a symbol, one doesn't if (entryhi->getSymbol() != entrylo->getSymbol()) return 0; // Not from the same symbol } bool isWhole = false; list::const_iterator iter; for(iter=whole->beginDescend();iter!=whole->endDescend();++iter) { TypeOp *typeop = (*iter)->getOpcode(); // Categorize op as "reading a logical whole" if (typeop->isArithmeticOp() || typeop->isFloatingPointOp()) { isWhole = true; break; } } if (!isWhole) return 0; vnhi->setPrecisHi(); vnlo->setPrecisLo(); return 1; } int4 RuleDoubleOut::applyOp(PcodeOp *op,Funcdata &data) { Varnode *vnhi= op->getIn(0); Varnode *vnlo = op->getIn(1); // Currently this only implements collapsing input varnodes read by CPUI_PIECE // So we put the test for this particular case early if (!vnhi->isInput() || !vnlo->isInput()) return 0; if (!vnhi->isPersist() || !vnlo->isPersist()) return 0; if (!vnhi->isPrecisHi() || !vnlo->isPrecisLo()) { return attemptMarking(vnhi,vnlo,op); } if (data.hasUnreachableBlocks()) return 0; Address addr; if (!SplitVarnode::isAddrTiedContiguous(vnlo, vnhi, addr)) return 0; data.combineInputVarnodes(vnhi,vnlo); return 1; } /// \brief Scan for conflicts between two LOADs or STOREs that would prevent them from being combined /// /// The PcodeOps must be in the same basic block. Each PcodeOp that falls in between is examined /// to determine if it writes to the same address space as the LOADs or STOREs, which indicates that /// combining isn't possible. If the LOADs and STOREs can be combined, the later of the two PcodeOps /// is returned, otherwise null is returned. /// /// In the case of STORE ops, an extra container for INDIRECT PcodeOps is passed in. INDIRECTs that /// are caused by the STORE ops themselves are collected in the container. /// \param op1 is a given LOAD or STORE /// \param op2 is the other given LOAD or STORE /// \param spc is the address space referred to by the LOAD/STOREs /// \param indirects if non-null is used to collect INDIRECTs caused by STOREs PcodeOp *RuleDoubleLoad::noWriteConflict(PcodeOp *op1,PcodeOp *op2,AddrSpace *spc,vector *indirects) { const BlockBasic *bb = op1->getParent(); // Force the two ops to be in the same basic block if (bb != op2->getParent()) return (PcodeOp *)0; if (op2->getSeqNum().getOrder() < op1->getSeqNum().getOrder()) { PcodeOp *tmp = op2; op2 = op1; op1 = tmp; } PcodeOp *startop = op1; if (op1->code() == CPUI_STORE) { // Extend the range of PcodeOps to include any CPUI_INDIRECTs associated with the initial STORE PcodeOp *tmpOp = startop->previousOp(); while(tmpOp != (PcodeOp *)0 && tmpOp->code() == CPUI_INDIRECT) { startop = tmpOp; tmpOp = tmpOp->previousOp(); } } list::iterator iter = startop->getBasicIter(); list::iterator enditer = op2->getBasicIter(); while(iter != enditer) { PcodeOp *curop = *iter; Varnode *outvn; PcodeOp *affector; ++iter; if (curop == op1) continue; switch(curop->code()) { case CPUI_STORE: if (curop->getIn(0)->getSpaceFromConst() == spc) return (PcodeOp *)0; // Don't go any further trying to resolve alias break; case CPUI_INDIRECT: affector = PcodeOp::getOpFromConst(curop->getIn(1)->getAddr()); if (affector == op1 || affector == op2) { if (indirects != (vector *)0) indirects->push_back(curop); } else { if (curop->getOut()->getSpace() == spc) return (PcodeOp *)0; } break; case CPUI_CALL: case CPUI_CALLIND: case CPUI_CALLOTHER: case CPUI_RETURN: case CPUI_BRANCH: case CPUI_CBRANCH: case CPUI_BRANCHIND: return (PcodeOp *)0; default: outvn = curop->getOut(); if (outvn != (Varnode *)0) { if (outvn->getSpace() == spc) return (PcodeOp *)0; } break; } } return op2; } void RuleDoubleLoad::getOpList(vector &oplist) const { oplist.push_back(CPUI_PIECE); } int4 RuleDoubleLoad::applyOp(PcodeOp *op,Funcdata &data) { PcodeOp *loadlo,*loadhi; // Load from lowest address, highest (NOT significance) AddrSpace *spc; int4 size; Varnode *piece0 = op->getIn(0); Varnode *piece1 = op->getIn(1); if (!piece0->isWritten()) return 0; if (!piece1->isWritten()) return 0; PcodeOp *load1 = piece1->getDef(); if (load1->code() != CPUI_LOAD) return false; PcodeOp *load0 = piece0->getDef(); OpCode opc = load0->code(); int4 offset = 0; if (opc == CPUI_SUBPIECE) { // Check for 2 LOADs but most significant part of most significant LOAD is discarded if (load0->getIn(1)->getOffset() != 0) return false; Varnode *vn0 = load0->getIn(0); if (!vn0->isWritten()) return false; offset = vn0->getSize() - piece0->getSize(); load0 = vn0->getDef(); opc = load0->code(); } if (opc != CPUI_LOAD) return false; if (!SplitVarnode::testContiguousPointers(load0,load1,loadlo,loadhi,spc)) return 0; size = piece0->getSize() + piece1->getSize(); PcodeOp *latest = noWriteConflict(loadlo,loadhi,spc,(vector *)0); if (latest == (PcodeOp *)0) return 0; // There was a conflict // Create new load op that combines the two smaller loads PcodeOp *newload = data.newOp(2,latest->getAddr()); Varnode *vnout = data.newUniqueOut(size,newload); Varnode *spcvn = data.newVarnodeSpace(spc); data.opSetOpcode(newload,CPUI_LOAD); data.opSetInput(newload,spcvn,0); Varnode *addrvn = loadlo->getIn(1); if (spc->isBigEndian() && offset != 0) { // If the most significant part of LOAD is discarded, we need to add discard amount to pointer PcodeOp *newadd = data.newOp(2,latest->getAddr()); Varnode *addout = data.newUniqueOut(addrvn->getSize(),newadd); data.opSetOpcode(newadd,CPUI_INT_ADD); data.opSetInput(newadd,addrvn,0); data.opSetInput(newadd,data.newConstant(addrvn->getSize(), offset),1); data.opInsertAfter(newadd,latest); addrvn = addout; latest = newadd; } data.opSetInput(newload,addrvn,1); // We need to guarantee that -newload- reads -addrvn- after // it has been defined. So insert it after the latest. data.opInsertAfter(newload,latest); // Change the concatenation to a copy from the big load data.opRemoveInput(op,1); data.opSetOpcode(op,CPUI_COPY); data.opSetInput(op,vnout,0); return 1; } void RuleDoubleStore::getOpList(vector &oplist) const { oplist.push_back(CPUI_STORE); } int4 RuleDoubleStore::applyOp(PcodeOp *op,Funcdata &data) { PcodeOp *storelo,*storehi; AddrSpace *spc; Varnode *vnlo = op->getIn(2); if (!vnlo->isPrecisLo()) return 0; if (!vnlo->isWritten()) return 0; PcodeOp *subpieceOpLo = vnlo->getDef(); if (subpieceOpLo->code() != CPUI_SUBPIECE) return 0; if (subpieceOpLo->getIn(1)->getOffset() != 0) return 0; Varnode *whole = subpieceOpLo->getIn(0); if (whole->isFree()) return 0; list::const_iterator iter; for(iter=whole->beginDescend();iter!=whole->endDescend();++iter) { PcodeOp *subpieceOpHi = *iter; if (subpieceOpHi->code() != CPUI_SUBPIECE) continue; if (subpieceOpHi == subpieceOpLo) continue; int4 offset = (int4)subpieceOpHi->getIn(1)->getOffset(); if (offset != vnlo->getSize()) continue; Varnode *vnhi = subpieceOpHi->getOut(); if (!vnhi->isPrecisHi()) continue; if (vnhi->getSize() != whole->getSize() - offset) continue; list::const_iterator iter2; for(iter2=vnhi->beginDescend();iter2!=vnhi->endDescend();++iter2) { PcodeOp *storeOp2 = *iter2; if (storeOp2->code() != CPUI_STORE) continue; if (storeOp2->getIn(2) != vnhi) continue; if (SplitVarnode::testContiguousPointers(storeOp2, op, storelo, storehi, spc)) { vector indirects; PcodeOp *latest = RuleDoubleLoad::noWriteConflict(storelo,storehi,spc,&indirects); if (latest == (PcodeOp *)0) continue; // There was a conflict if (!testIndirectUse(storelo, storehi, indirects)) continue; // Create new STORE op that combines the two smaller STOREs PcodeOp *newstore = data.newOp(3,latest->getAddr()); Varnode *spcvn = data.newVarnodeSpace(spc); data.opSetOpcode(newstore,CPUI_STORE); data.opSetInput(newstore,spcvn,0); Varnode *addrvn = storelo->getIn(1); if (addrvn->isConstant()) addrvn = data.newConstant(addrvn->getSize(),addrvn->getOffset()); data.opSetInput(newstore,addrvn,1); data.opSetInput(newstore,whole,2); // We need to guarantee that -newstore- reads -addrvn- after // it has been defined. So insert it after the latest. data.opInsertAfter(newstore,latest); data.opDestroy(op); // Get rid of the original STOREs data.opDestroy(storeOp2); reassignIndirects(data, newstore, indirects); return 1; } } } return 0; } /// \brief Test if output Varnodes from a list of PcodeOps are used anywhere within a range of PcodeOps /// /// The range of PcodeOps bounded by given starting and ending PcodeOps. An output Varnode is /// used within the range if there is a PcodeOp in the range that takes the Varnode as input. /// \param op1 is the given starting PcodeOp of the range /// \param op2 is the given ending PcodeOp of the range /// \param indirects is the list of PcodesOps whose output are tested /// \return \b true if no output in the list is used in the range bool RuleDoubleStore::testIndirectUse(PcodeOp *op1,PcodeOp *op2,const vector &indirects) { if (op2->getSeqNum().getOrder() < op1->getSeqNum().getOrder()) { PcodeOp *tmp = op2; op2 = op1; op1 = tmp; } for(int4 i=0;igetOut(); list::const_iterator iter; int4 usecount = 0; int4 usebyop2 = 0; for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) { PcodeOp *op = *iter; usecount += 1; if (op->getParent() != op1->getParent()) continue; if (op->getSeqNum().getOrder() < op1->getSeqNum().getOrder()) continue; if (op->getSeqNum().getOrder() > op2->getSeqNum().getOrder()) continue; // Its likely that INDIRECTs from the first STORE feed INDIRECTs for the second STORE if (op->code() == CPUI_INDIRECT && op2 == PcodeOp::getOpFromConst(op->getIn(1)->getAddr())) { usebyop2 += 1; // Note this pairing continue; } return false; } // As an INDIRECT whose output Varnode feeds into later INDIRECTs must be removed, we need the following test. // If some uses of the output feed into later INDIRECTs, but not ALL do, then return false if (usebyop2 > 0 && usecount != usebyop2) return false; if (usebyop2 > 1) return false; } return true; } /// \brief Reassign INDIRECTs to a new given STORE /// /// The INDIRECTs are associated with old STOREs that are being removed. /// Each INDIRECT is moved from its position near the old STORE to be near the new STORE and /// the affect iop operand is set to point at the new STORE. /// \param data is the function owning the INDIRECTs /// \param newStore is the given new STORE PcodeOp /// \param indirects is the list of INDIRECT PcodeOps to reassign void RuleDoubleStore::reassignIndirects(Funcdata &data,PcodeOp *newStore,const vector &indirects) { // Search for INDIRECT pairs. The earlier is deleted. The later gains the earlier's input. for(int4 i=0;isetMark(); Varnode *vn = op->getIn(0); if (!vn->isWritten()) continue; PcodeOp *earlyop = vn->getDef(); if (earlyop->isMark()) { data.opSetInput(op,earlyop->getIn(0),0); // Grab the earlier op's input, replacing the use of its output data.opDestroy(earlyop); } } for(int4 i=0;iclearMark(); if (op->isDead()) continue; data.opUninsert(op); data.opInsertBefore(op,newStore); // Move the INDIRECT to the new STORE data.opSetInput(op,data.newVarnodeIop(newStore),1); // Assign the INDIRECT to the new STORE } } } // End namespace ghidra