/* ### * 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 "merge.hh" #include "funcdata.hh" namespace ghidra { /// This instance assumes the identity of the given Varnode and the defining index is /// cached to facilitate quick sorting. /// \param v is the given Varnode void BlockVarnode::set(Varnode *v) { vn = v; const PcodeOp *op = vn->getDef(); if (op == (const PcodeOp *)0) index = 0; else index = op->getParent()->getIndex(); } /// \brief Find the first Varnode defined in the BlockBasic of the given index /// /// A BlockVarnode is identified from a sorted \b list. The position of the first BlockVarnode /// in this list that has the given BlockBasic \e index is returned. /// \param blocknum is the index of the BlockBasic to search for /// \param list is the sorted list of BlockVarnodes /// \return the index of the BlockVarnode within the list or -1 if no Varnode in the block is found int4 BlockVarnode::findFront(int4 blocknum,const vector &list) { int4 min = 0; int4 max = list.size()-1; while(min < max) { int4 cur = (min + max)/2; int4 curblock = list[cur].getIndex(); if (curblock >= blocknum) max = cur; else min = cur + 1; } if (min > max) return -1; if (list[min].getIndex() != blocknum) return -1; return min; } void StackAffectingOps::populate(void) { for(int4 i=0;igetOp(); addOp(op); } const list &storeGuard( data.getStoreGuards() ); for(list ::const_iterator iter=storeGuard.begin();iter!=storeGuard.end();++iter) { if ((*iter).isValid(CPUI_STORE)) addOp((*iter).getOp()); } finalize(); } bool StackAffectingOps::affectsTest(PcodeOp *op,Varnode *vn) const { if (op->code() == CPUI_STORE) { const LoadGuard *loadGuard = data.getStoreGuard(op); if (loadGuard == (const LoadGuard *)0) return true; return loadGuard->isGuarded(vn->getAddr()); } // We could conceivably do secondary testing of CALL ops here return true; } /// \brief Required tests to merge HighVariables that are not Cover related /// /// This is designed to short circuit merge tests, when we know properties of the /// two HighVariables preclude merging. For example, you can't merge HighVariables if: /// - They are locked to different data-types /// - They are both mapped to different address ranges /// - One is a parameter one is a global /// /// \param high_out is the first HighVariable to test /// \param high_in is the second HighVariable to test /// \return \b true if tests pass and the HighVariables are not forbidden to merge bool Merge::mergeTestRequired(HighVariable *high_out,HighVariable *high_in) { if (high_in == high_out) return true; // Already merged if (high_in->isTypeLock()) // If types are locked if (high_out->isTypeLock()) // dont merge unless if (high_in->getType() != high_out->getType()) return false; // both types are the same if (high_out->isAddrTied()) { // Do not merge address tied input if (high_in->isAddrTied()) { if (high_in->getTiedVarnode()->getAddr() != high_out->getTiedVarnode()->getAddr()) return false; // with an address tied output of different address } } if (high_in->isInput()) { // Input and persist must be different vars // as persists inherently have their own input if (high_out->isPersist()) return false; // If we don't prevent inputs and addrtieds from // being merged. Inputs can get merged with the // internal parts of structures on the stack if ((high_out->isAddrTied())&&(!high_in->isAddrTied())) return false; } else if (high_in->isExtraOut()) return false; if (high_out->isInput()) { if (high_in->isPersist()) return false; if ((high_in->isAddrTied())&&(!high_out->isAddrTied())) return false; } else if (high_out->isExtraOut()) return false; if (high_in->isProtoPartial()) { if (high_out->isProtoPartial()) return false; if (high_out->isInput()) return false; if (high_out->isAddrTied()) return false; if (high_out->isPersist()) return false; } if (high_out->isProtoPartial()) { if (high_in->isInput()) return false; if (high_in->isAddrTied()) return false; if (high_in->isPersist()) return false; } if (high_in->piece != (VariablePiece *)0 && high_out->piece != (VariablePiece *)0) { VariableGroup *groupIn = high_in->piece->getGroup(); VariableGroup *groupOut = high_out->piece->getGroup(); if (groupIn == groupOut) return false; // At least one of the pieces must represent its whole group if (high_in->piece->getSize() != groupIn->getSize() && high_out->piece->getSize() != groupOut->getSize()) return false; } Symbol *symbolIn = high_in->getSymbol(); Symbol *symbolOut = high_out->getSymbol(); if (symbolIn != (Symbol *) 0 && symbolOut != (Symbol *) 0) { if (symbolIn != symbolOut) return false; // Map to different symbols if (high_in->getSymbolOffset() != high_out->getSymbolOffset()) return false; // Map to different parts of same symbol } return true; } /// \brief Adjacency tests for merging Varnodes that are input or output to the same p-code op /// /// All the required tests (mergeTestRequired()) are performed, and then some additional tests /// are performed. This does not perform any Cover tests. /// \param high_out is the \e output HighVariable to test /// \param high_in is the \e input HighVariable to test /// \return \b true if tests pass and the HighVariables are not forbidden to merge bool Merge::mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in) { if (!mergeTestRequired(high_out,high_in)) return false; if (high_in->isNameLock() && high_out->isNameLock()) return false; // Make sure variables have the same type if (high_out->getType() != high_in->getType()) return false; // We want to isolate the use of illegal inputs // as much as possible. See we don't do any speculative // merges with them, UNLESS the illegal input is only // used indirectly if (high_out->isInput()) { Varnode *vn = high_out->getInputVarnode(); if (vn->isIllegalInput()&&(!vn->isIndirectOnly())) return false; } if (high_in->isInput()) { Varnode *vn = high_in->getInputVarnode(); if (vn->isIllegalInput()&&(!vn->isIndirectOnly())) return false; } Symbol *symbol = high_in->getSymbol(); if (symbol != (Symbol *)0) if (symbol->isIsolated()) return false; symbol = high_out->getSymbol(); if (symbol != (Symbol *)0) if (symbol->isIsolated()) return false; // Currently don't allow speculative merging of variables that are in separate overlapping collections if (high_out->piece != (VariablePiece *)0 && high_in->piece != (VariablePiece *)0) return false; return true; } /// \brief Speculative tests for merging HighVariables that are not Cover related /// /// This does all the \e required and \e adjacency merge tests and then performs additional /// tests required for \e speculative merges. /// \param high_out is the first HighVariable to test /// \param high_in is the second HighVariable to test /// \return \b true if tests pass and the HighVariables are not forbidden to merge bool Merge::mergeTestSpeculative(HighVariable *high_out,HighVariable *high_in) { if (!mergeTestAdjacent(high_out,high_in)) return false; // Don't merge anything with a global speculatively if (high_out->isPersist()) return false; if (high_in->isPersist()) return false; // Don't merge anything speculatively with input if (high_out->isInput()) return false; if (high_in->isInput()) return false; // Don't merge anything speculatively with addrtied if (high_out->isAddrTied()) return false; if (high_in->isAddrTied()) return false; return true; } /// \brief Test if the given Varnode that \e must be merged, \e can be merged. /// /// If it cannot be merged, throw an exception. /// \param vn is the given Varnode void Merge::mergeTestMust(Varnode *vn) { if (vn->hasCover() && !vn->isImplied()) return; throw LowlevelError("Cannot force merge of range"); } /// \brief Test if the given Varnode can ever be merged. /// /// Some Varnodes (constants, annotations, implied, spacebase) are never merged with another /// Varnode. /// \param vn is the Varnode to test /// \return \b true if the Varnode is not forbidden from ever merging bool Merge::mergeTestBasic(Varnode *vn) { if (vn == (Varnode *)0) return false; if (!vn->hasCover()) return false; if (vn->isImplied()) return false; if (vn->isProtoPartial()) return false; if (vn->isSpacebase()) return false; return true; } /// \brief Speculatively merge all HighVariables in the given list as well as possible /// /// The variables are first sorted by the index of the earliest block in their range. /// Then proceeding in order, an attempt is made to merge each variable with the first. /// The attempt fails if the \e speculative test doesn't pass or if there are Cover /// intersections, in which case that particular merge is skipped. void Merge::mergeLinear(vector &highvec) { vector highstack; vector::iterator initer,outiter; HighVariable *high; if (highvec.size() <= 1) return; for(initer=highvec.begin();initer!=highvec.end();++initer) testCache.updateHigh(*initer); sort(highvec.begin(),highvec.end(),compareHighByBlock); for(initer=highvec.begin();initer!=highvec.end();++initer) { high = *initer; for(outiter=highstack.begin();outiter!=highstack.end();++outiter) { if (mergeTestSpeculative(*outiter,high)) if (merge(*outiter,high,true)) break; } if (outiter==highstack.end()) highstack.push_back(high); } } /// \brief Force the merge of a ranges of Varnodes with the same size and storage address /// /// The list of Varnodes to be merged is provided as a range in the main location sorted /// container. Any Cover intersection is assumed to already be \b snipped, so any problems /// with merging cause an exception to be thrown. /// \param startiter is the beginning of the range of Varnodes with the same storage address /// \param enditer is the end of the range void Merge::mergeRangeMust(VarnodeLocSet::const_iterator startiter,VarnodeLocSet::const_iterator enditer) { HighVariable *high; Varnode *vn; vn = *startiter++; mergeTestMust(vn); high = vn->getHigh(); for(;startiter!=enditer;++startiter) { vn = *startiter; if (vn->getHigh() == high) continue; mergeTestMust(vn); if (!merge(high,vn->getHigh(),false)) throw LowlevelError("Forced merge caused intersection"); } } /// \brief Try to force merges of input to output for all p-code ops of a given type /// /// For a given opcode, run through all ops in the function in block/address order and /// try to merge each input HighVariable with the output HighVariable. If this would /// introduce Cover intersections, the merge is skipped. This is generally used to try to /// merge the input and output of COPY ops if possible. /// \param opc is the op-code type to merge void Merge::mergeOpcode(OpCode opc) { BlockBasic *bl; list::iterator iter; PcodeOp *op; Varnode *vn1,*vn2; const BlockGraph &bblocks(data.getBasicBlocks()); for(int4 i=0;ibeginOp();iter!=bl->endOp();++iter) { op = *iter; if (op->code() != opc) continue; vn1 = op->getOut(); if (!mergeTestBasic(vn1)) continue; for(int4 j=0;jnumInput();++j) { vn2 = op->getIn(j); if (!mergeTestBasic(vn2)) continue; if (mergeTestRequired(vn1->getHigh(),vn2->getHigh())) merge(vn1->getHigh(),vn2->getHigh(),false); // This is a required merge } } } } /// \brief Try to merge all HighVariables in the given range that have the same data-type /// /// HighVariables that have an instance within the given Varnode range are sorted into groups /// based on their data-type. Then an attempt is made to merge all the HighVariables within /// a group. If a particular merge causes Cover intersection, it is skipped. /// \param startiter is the start of the given range of Varnodes /// \param enditer is the end of the given range void Merge::mergeByDatatype(VarnodeLocSet::const_iterator startiter,VarnodeLocSet::const_iterator enditer) { vector highvec; list highlist; list::iterator hiter; VarnodeLocSet::const_iterator iter; Varnode *vn; HighVariable *high; Datatype *ct = (Datatype *)0; for(iter=startiter;iter!=enditer;++iter) { // Gather all the highs vn = *iter; if (vn->isFree()) continue; high = (*iter)->getHigh(); if (high->isMark()) continue; // dedup if (!mergeTestBasic(vn)) continue; high->setMark(); highlist.push_back(high); } for(hiter=highlist.begin();hiter!=highlist.end();++hiter) (*hiter)->clearMark(); while(!highlist.empty()) { highvec.clear(); hiter = highlist.begin(); high = *hiter; ct = high->getType(); highvec.push_back(high); highlist.erase(hiter++); while(hiter != highlist.end()) { high = *hiter; if (ct == high->getType()) { // Check for exact same type highvec.push_back(high); highlist.erase(hiter++); } else ++hiter; } mergeLinear(highvec); // Try to merge all highs of the same type } } /// \brief Allocate COPY PcodeOp designed to trim an overextended Cover /// /// A COPY is allocated with the given input and data-type. A \e unique space /// output is created. /// \param inVn is the given input Varnode for the new COPY /// \param addr is the address associated with the new COPY /// \param trimOp is an exemplar PcodeOp whose read is being trimmed /// \return the newly allocated COPY PcodeOp *Merge::allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trimOp) { PcodeOp *copyOp = data.newOp(1,addr); data.opSetOpcode(copyOp,CPUI_COPY); Datatype *ct = inVn->getType(); if (ct->needsResolution()) { // If the data-type needs resolution if (inVn->isWritten()) { int4 fieldNum = data.inheritResolution(ct, copyOp, -1, inVn->getDef(), -1); data.forceFacingType(ct, fieldNum, copyOp, 0); } else { int4 slot = trimOp->getSlot(inVn); const ResolvedUnion *resUnion = data.getUnionField(ct, trimOp, slot); int4 fieldNum = (resUnion == (const ResolvedUnion *)0) ? -1 : resUnion->getFieldNum(); data.forceFacingType(ct, fieldNum, copyOp, 0); } } Varnode *outVn = data.newUnique(inVn->getSize(),ct); data.opSetOutput(copyOp,outVn); data.opSetInput(copyOp,inVn,0); copyTrims.push_back(copyOp); return copyOp; } /// \brief Snip off set of \e read p-code ops for a given Varnode /// /// The data-flow for the given Varnode is truncated by creating a COPY p-code from the Varnode /// into a new temporary Varnode, then replacing the Varnode reads for a specific set of /// p-code ops with the temporary. /// \param vn is the given Varnode /// \param markedop is the specific set of PcodeOps reading the Varnode void Merge::snipReads(Varnode *vn,list &markedop) { if (markedop.empty()) return; PcodeOp *copyop,*op; BlockBasic *bl; Address pc; PcodeOp *afterop; // Figure out where copy is inserted if (vn->isInput()) { bl = (BlockBasic *) data.getBasicBlocks().getBlock(0); pc = bl->getStart(); afterop = (PcodeOp *)0; } else { bl = vn->getDef()->getParent(); pc = vn->getDef()->getAddr(); if (vn->getDef()->code() == CPUI_INDIRECT) // snip must come after OP CAUSING EFFECT // Not the indirect op itself afterop = PcodeOp::getOpFromConst(vn->getDef()->getIn(1)->getAddr()); else afterop = vn->getDef(); } copyop = allocateCopyTrim(vn, pc, markedop.front()); if (afterop == (PcodeOp *)0) data.opInsertBegin(copyop,bl); else data.opInsertAfter(copyop,afterop); list::iterator iter; for(iter=markedop.begin();iter!=markedop.end();++iter) { op = *iter; int4 slot = op->getSlot(vn); data.opSetInput(op,copyop->getOut(),slot); } } /// \brief Eliminate intersections of given Varnode with other Varnodes in a list /// /// Both the given Varnode and those in the list are assumed to be at the same storage address. /// For any intersection, identify the PcodeOp reading the given Varnode which causes the /// intersection and \e snip the read by inserting additional COPY ops. /// \param vn is the given Varnode /// \param blocksort is the list of other Varnodes sorted by their defining basic block void Merge::eliminateIntersect(Varnode *vn,const vector &blocksort) { list markedop; list::const_iterator oiter; map::const_iterator iter,enditer; Varnode *vn2; int4 boundtype; int4 overlaptype; bool insertop; for(oiter=vn->beginDescend();oiter!=vn->endDescend();++oiter) { insertop = false; Cover single; single.addDefPoint(vn); PcodeOp *op = *oiter; single.addRefPoint(op,vn); // Build range for a single read iter = single.begin(); enditer = single.end(); while(iter != enditer) { int4 blocknum = (*iter).first; ++iter; int4 slot = BlockVarnode::findFront(blocknum,blocksort); if (slot == -1) continue; while(slot < blocksort.size()) { if (blocksort[slot].getIndex() != blocknum) break; vn2 = blocksort[slot].getVarnode(); slot += 1; if (vn2 == vn) continue; boundtype = single.containVarnodeDef(vn2); if (boundtype == 0) continue; overlaptype = vn->characterizeOverlap(*vn2); if (overlaptype == 0) continue; // No overlap in storage if (overlaptype == 1) { // Partial overlap int4 off = (int4)(vn->getOffset() - vn2->getOffset()); if (vn->partialCopyShadow(vn2,off)) continue; // SUBPIECE shadow, not a new value } if (boundtype == 2) { // We have to resolve things defined at same place if (vn2->getDef() == (PcodeOp *)0) { if (vn->getDef() == (PcodeOp *)0) { if (vn < vn2) continue; // Choose an arbitrary order if both are inputs } else continue; } else { if (vn->getDef() != (PcodeOp *)0) { if (vn2->getDef()->getSeqNum().getOrder() < vn->getDef()->getSeqNum().getOrder()) continue; } } } else if (boundtype == 3) { // intersection on the tail of the range // For most operations if the READ and WRITE happen on the same op, there is really no cover // intersection because the READ happens before the op and the WRITE happens after, but // if the WRITE is for an INDIRECT that is marking the READING (call) op, and the WRITE is to // an address forced varnode, then because the write varnode must exist just before the op // there really is an intersection. if (!vn2->isAddrForce()) continue; if (!vn2->isWritten()) continue; PcodeOp *indop = vn2->getDef(); if (indop->code() != CPUI_INDIRECT) continue; // The vn2 INDIRECT must be linked to the read op if (op != PcodeOp::getOpFromConst(indop->getIn(1)->getAddr())) continue; if (overlaptype != 1) { if (vn->copyShadow(indop->getIn(0))) continue; // If INDIRECT input shadows vn, don't consider as intersection } else { int4 off = (int4)(vn->getOffset() - vn2->getOffset()); if (vn->partialCopyShadow(indop->getIn(0),off)) continue; } } insertop = true; break; // No need to continue iterating through varnodes in block } if (insertop) break; // No need to continue iterating through blocks } if (insertop) markedop.push_back(op); } snipReads(vn,markedop); } /// \brief Make sure all Varnodes with the same storage address and size can be merged /// /// The list of Varnodes to be merged is provided as a range in the main location sorted /// container. Any discovered intersection is \b snipped by splitting data-flow for one of /// the Varnodes into two or more flows, which involves inserting new COPY ops and temporaries. /// \param startiter is the beginning of the range of Varnodes with the same storage address /// \param enditer is the end of the range void Merge::unifyAddress(VarnodeLocSet::const_iterator startiter,VarnodeLocSet::const_iterator enditer) { VarnodeLocSet::const_iterator iter; Varnode *vn; vector isectlist; vector blocksort; for(iter=startiter;iter!=enditer;++iter) { vn = *iter; if (vn->isFree()) continue; isectlist.push_back(vn); } blocksort.resize(isectlist.size()); for(int4 i=0;i bounds; for(startiter=data.beginLoc();startiter!=data.endLoc();) { AddrSpace *spc = (*startiter)->getSpace(); spacetype type = spc->getType(); if (type != IPTR_PROCESSOR && type != IPTR_SPACEBASE) { startiter = data.endLoc(spc); // Skip over the whole space continue; } VarnodeLocSet::const_iterator finaliter = data.endLoc(spc); while(startiter != finaliter) { Varnode *vn = *startiter; if (vn->isFree()) { startiter = data.endLoc(vn->getSize(),vn->getAddr(),0); // Skip over any free Varnodes continue; } bounds.clear(); uint4 flags = data.overlapLoc(startiter,bounds); // Collect maximally overlapping range of Varnodes int4 max = bounds.size() - 1; // Index of last iterator if ((flags & Varnode::addrtied) != 0) { unifyAddress(startiter,bounds[max]); for(int4 i=0;i 2) { Varnode *vn1 = *bounds[0]; for(int4 i=2;igetOffset() - vn1->getOffset()); vn2->getHigh()->groupWith(off, vn1->getHigh()); } } } startiter = bounds[max]; } } } /// \brief Trim the output HighVariable of the given PcodeOp so that its Cover is tiny /// /// The given PcodeOp is assumed to force merging so that input and output Covers shouldn't /// intersect. The original PcodeOp output is \e moved so that it becomes the output of a new /// COPY, disassociating the original output Varnode from the inputs. /// \param op is the given PcodeOp void Merge::trimOpOutput(PcodeOp *op) { PcodeOp *copyop; Varnode *uniq,*vn; PcodeOp *afterop; if (op->code() == CPUI_INDIRECT) afterop = PcodeOp::getOpFromConst(op->getIn(1)->getAddr()); // Insert copyop AFTER source of indirect else afterop = op; vn = op->getOut(); Datatype *ct = vn->getType(); copyop = data.newOp(1,op->getAddr()); data.opSetOpcode(copyop,CPUI_COPY); if (ct->needsResolution()) { int4 fieldNum = data.inheritResolution(ct, copyop, -1, op, -1); data.forceFacingType(ct, fieldNum, copyop, 0); if (ct->getMetatype() == TYPE_PARTIALUNION) ct = vn->getTypeDefFacing(); } uniq = data.newUnique(vn->getSize(),ct); data.opSetOutput(op,uniq); // Output of op is now stubby uniq data.opSetOutput(copyop,vn); // Original output is bumped forward slightly data.opSetInput(copyop,uniq,0); data.opInsertAfter(copyop,afterop); } /// \brief Trim the input HighVariable of the given PcodeOp so that its Cover is tiny /// /// The given PcodeOp is assumed to force merging so that input and output Covers shouldn't /// intersect. A new COPY is inserted right before the given PcodeOp with a new /// \e unique output that replaces the specified input, disassociating it from the /// other original inputs and output. /// \param op is the given PcodeOp /// \param slot is the specified slot of the input Varnode to be trimmed void Merge::trimOpInput(PcodeOp *op,int4 slot) { PcodeOp *copyop; Varnode *vn; Address pc; if (op->code() == CPUI_MULTIEQUAL) { BlockBasic *bb = (BlockBasic *)op->getParent()->getIn(slot); pc = bb->getStop(); } else pc = op->getAddr(); vn = op->getIn(slot); copyop = allocateCopyTrim(vn, pc, op); data.opSetInput(op,copyop->getOut(),slot); if (op->code() == CPUI_MULTIEQUAL) data.opInsertEnd(copyop,(BlockBasic *)op->getParent()->getIn(slot)); else data.opInsertBefore(copyop,op); } /// \brief Force the merge of all input and output Varnodes for the given PcodeOp /// /// Data-flow for specific input and output Varnodes are \e snipped until everything /// can be merged. /// \param op is the given PcodeOp void Merge::mergeOp(PcodeOp *op) { vector testlist; HighVariable *high_out; int4 i,nexttrim,max; max = (op->code() == CPUI_INDIRECT) ? 1 : op->numInput(); high_out = op->getOut()->getHigh(); // First try to deal with non-cover related merge // restrictions for(i=0;igetIn(i)->getHigh(); if (!mergeTestRequired(high_out,high_in)) { trimOpInput(op,i); continue; } for(int4 j=0;jgetIn(j)->getHigh(),high_in)) { trimOpInput(op,i); break; } } // Now test if a merge violates cover restrictions mergeTest(high_out,testlist); for(i=0;igetIn(i)->getHigh(),testlist)) break; if (i != max) { // If there are cover restrictions nexttrim = 0; while(nexttrim < max) { trimOpInput(op,nexttrim); // Trim one of the branches testlist.clear(); // Try the merge restriction test again mergeTest(high_out,testlist); for(i=0;igetIn(i)->getHigh(),testlist)) break; if (i==max) break; // We successfully test merged everything nexttrim += 1; } if (nexttrim == max) // One last trim we can try trimOpOutput(op); } for(i=0;igetOut()->getHigh(),op->getIn(i)->getHigh())) throw LowlevelError("Non-cover related merge restriction violated, despite trims"); if (!merge(op->getOut()->getHigh(),op->getIn(i)->getHigh(),false)) { ostringstream errstr; errstr << "Unable to force merge of op at " << op->getSeqNum(); throw LowlevelError(errstr.str()); } } } /// \brief Collect Varnode instances or pieces from a specific HighVariable that are inputs to a given PcodeOp /// /// A Varnode is considered an input if it is a \e direct input to the PcodeOp or if it is /// \e indirectly affected by the PcodeOp. The specific \e read of the Varnode is passed back as /// a PcodeOp and slot pair (PcodeOpNode). The passed back PcodeOp will either be the given PcodeOp or /// an INDIRECT caused by the given PcodeOp. /// \param high is the specific HighVariable through which to search for input instances /// \param oplist will hold the PcodeOpNodes being passed back /// \param op is the given PcodeOp void Merge::collectInputs(HighVariable *high,vector &oplist,PcodeOp *op) { VariableGroup *group = (VariableGroup *)0; if (high->piece != (VariablePiece *)0) group = high->piece->getGroup(); for(;;) { for(int4 i=0;inumInput();++i) { Varnode *vn = op->getIn(i); if (vn->isAnnotation()) continue; HighVariable *testHigh = vn->getHigh(); if (testHigh == high || (testHigh->piece != (VariablePiece *)0 && testHigh->piece->getGroup() == group)) { oplist.emplace_back(op, i); } } op = op->previousOp(); if (op == (PcodeOp *)0 || op->code() != CPUI_INDIRECT) break; } } /// \brief Snip instances of the output of an INDIRECT that are also inputs to to the underlying PcodeOp /// /// Examine the output HighVariable for the given INDIRECT op. Varnode instances (or pieces) that are also /// inputs to the underlying PcodeOp causing the INDIRECT are snipped by creating a new COPY op from the /// Varnode to a new temporary and then replacing the \e read with the temporary. /// \param indop is the given INDIRECT op /// \return \b true if specific instances are snipped bool Merge::snipOutputInterference(PcodeOp *indop) { PcodeOp *op = PcodeOp::getOpFromConst(indop->getIn(1)->getAddr()); // Indirect effect op // Collect instances of output->high that are defined // before (and right up to) op. These need to be snipped. vector correctable; collectInputs(indop->getOut()->getHigh(), correctable, op); if (correctable.empty()) return false; sort(correctable.begin(),correctable.end(),PcodeOpNode::compareByHigh); PcodeOp *snipop = (PcodeOp *)0; HighVariable *curHigh = (HighVariable *)0; for(int4 i=0;igetIn(slot); if (vn->getHigh() != curHigh) { // NOTE: the covers for any input to op which is an instance of the output high must // all intersect so the varnodes must all be traceable via COPY to the same root snipop = allocateCopyTrim(vn, insertop->getAddr(), insertop); data.opInsertBefore(snipop,insertop); curHigh = vn->getHigh(); } data.opSetInput(insertop,snipop->getOut(),slot); } return true; } /// \brief Force the merge of all input and output Varnodes to a given INDIRECT op /// /// Merging INDIRECTs take a little care if their output is address forced because by convention /// the value must be present at the address BEFORE the indirect effect operation takes place. /// \param indop is the given INDIRECT void Merge::mergeIndirect(PcodeOp *indop) { Varnode *outvn = indop->getOut(); if (!outvn->isAddrForce()) { // If the output is NOT address forced mergeOp(indop); // We can merge in the same way as a MULTIEQUAL return; } Varnode *invn0 = indop->getIn(0); if (mergeTestRequired(outvn->getHigh(),invn0->getHigh())) { if (merge(invn0->getHigh(),outvn->getHigh(),false)) return; } // If we cannot merge, the only thing that can go wrong with an input trim, is if the output of // indop is involved in the input to the op causing the indirect effect. So test for this. if (snipOutputInterference(indop)) { // If we found (and snipped) something related to the output, try merging again before snipping the INDIRECT if (mergeTestRequired(outvn->getHigh(), invn0->getHigh())) { if (merge(invn0->getHigh(),outvn->getHigh(),false)) return; } } // Snip the INDIRECT itself PcodeOp *newop = allocateCopyTrim(invn0, indop->getAddr(), indop); SymbolEntry *entry = outvn->getSymbolEntry(); if (entry != (SymbolEntry *)0 && entry->getSymbol()->getType()->needsResolution()) { data.inheritResolution(entry->getSymbol()->getType(), newop, -1, indop, -1); } data.opSetInput(indop,newop->getOut(),0); data.opInsertBefore(newop,indop); if (!mergeTestRequired(outvn->getHigh(),indop->getIn(0)->getHigh()) || (!merge(indop->getIn(0)->getHigh(),outvn->getHigh(),false))) // Try merge again // if (!merge(indop->Input(0)->High(),outvn->High())) throw LowlevelError("Unable to merge address forced indirect"); } /// \brief Force the merge of input and output Varnodes to MULTIEQUAL and INDIRECT ops /// /// Run through all MULTIEQUAL and INDIRECT ops in the function. Force the merge of each /// input Varnode with the output Varnode, doing data-flow modification if necessary to /// resolve Cover intersections. void Merge::mergeMarker(void) { PcodeOp *op; list::const_iterator iter; for(iter=data.beginOpAlive();iter!=data.endOpAlive();++iter) { op = *iter; if ((!op->isMarker())||op->isIndirectCreation()) continue; if (op->code() == CPUI_INDIRECT) mergeIndirect(op); else mergeOp(op); } } /// \brief Merge together Varnodes mapped to SymbolEntrys from the same Symbol /// /// Symbols that have more than one SymbolEntry may attach to more than one Varnode. /// These Varnodes need to be merged to properly represent a single variable. void Merge::mergeMultiEntry(void) { SymbolNameTree::const_iterator iter = data.getScopeLocal()->beginMultiEntry(); SymbolNameTree::const_iterator enditer = data.getScopeLocal()->endMultiEntry(); for(;iter!=enditer;++iter) { vector mergeList; Symbol *symbol = *iter; int4 numEntries = symbol->numEntries(); int4 mergeCount = 0; int4 skipCount = 0; int4 conflictCount = 0; for(int4 i=0;igetMapEntry(i); if (entry->getSize() != symbol->getType()->getSize()) continue; data.findLinkedVarnodes(entry, mergeList); if (mergeList.size() == prevSize) skipCount += 1; // Did not discover any Varnodes corresponding to a particular SymbolEntry } if (mergeList.empty()) continue; HighVariable *high = mergeList[0]->getHigh(); testCache.updateHigh(high); for(int4 i=0;igetHigh(); if (newHigh == high) continue; // Varnodes already merged testCache.updateHigh(newHigh); if (!mergeTestRequired(high, newHigh)) { symbol->setMergeProblems(); newHigh->setUnmerged(); conflictCount += 1; continue; } if (!merge(high,newHigh,false)) { // Attempt the merge symbol->setMergeProblems(); newHigh->setUnmerged(); conflictCount += 1; continue; } mergeCount += 1; } if (skipCount != 0 || conflictCount !=0) { ostringstream s; s << "Unable to"; if (mergeCount != 0) s << " fully"; s << " merge symbol: " << symbol->getName(); if (skipCount > 0) s << " -- Some instance varnodes not found."; if (conflictCount > 0) s << " -- Some merges are forbidden"; data.warningHeader(s.str()); } } } /// \brief Run through CONCAT tree roots and group each tree /// void Merge::groupPartials(void) { for(int4 i=0;iisDead()) continue; if (!op->isPartialRoot()) continue; groupPartialRoot(op->getOut()); } } /// \brief Speculatively merge Varnodes that are input/output to the same p-code op /// /// If a single p-code op has an input and output HighVariable that share the same data-type, /// attempt to merge them. Each merge is speculative and is skipped if it would introduce Cover /// intersections. void Merge::mergeAdjacent(void) { list::const_iterator oiter; PcodeOp *op; int4 i; HighVariable *high_in,*high_out; Varnode *vn1,*vn2; const Datatype *ct; for(oiter=data.beginOpAlive();oiter!=data.endOpAlive();++oiter) { op = *oiter; if (op->isCall()) continue; vn1 = op->getOut(); if (!mergeTestBasic(vn1)) continue; high_out = vn1->getHigh(); ct = op->outputTypeLocal(); for(i=0;inumInput();++i) { if (ct != op->inputTypeLocal(i)) continue; // Only merge if types should be the same vn2 = op->getIn(i); if (!mergeTestBasic(vn2)) continue; if (vn1->getSize() != vn2->getSize()) continue; if ((vn2->getDef()==(PcodeOp *)0)&&(!vn2->isInput())) continue; high_in = vn2->getHigh(); if (!mergeTestAdjacent(high_out,high_in)) continue; if (!testCache.intersection(high_in,high_out)) // If no interval intersection merge(high_out,high_in,true); } } } /// \brief Find instance Varnodes that copied to from outside the given HighVariable /// /// Find all Varnodes in the HighVariable which are defined by a COPY from another /// Varnode which is \e not part of the same HighVariable. /// \param high is the given HighVariable /// \param singlelist will hold the resulting list of copied instances void Merge::findSingleCopy(HighVariable *high,vector &singlelist) { int4 i; Varnode *vn; PcodeOp *op; for(i=0;inumInstances();++i) { vn = high->getInstance(i); if (!vn->isWritten()) continue; op = vn->getDef(); if (op->code() != CPUI_COPY) continue; // vn must be defineed by copy if (op->getIn(0)->getHigh() == high) continue; // From something NOT in same high singlelist.push_back(vn); } } /// \brief Compare COPY ops first by Varnode input, then by block containing the op /// /// A sort designed to group COPY ops from the same Varnode together. Then within a group, /// COPYs are sorted by their containing basic block (so that dominating ops come first). /// \param op1 is the first PcodeOp being compared /// \param op2 is the second PcodeOp being compared /// \return \b true if the first PcodeOp should be ordered before the second bool Merge::compareCopyByInVarnode(PcodeOp *op1,PcodeOp *op2) { Varnode *inVn1 = op1->getIn(0); Varnode *inVn2 = op2->getIn(0); if (inVn1 != inVn2) // First compare by Varnode inputs return (inVn1->getCreateIndex() < inVn2->getCreateIndex()); int4 index1 = op1->getParent()->getIndex(); int4 index2 = op2->getParent()->getIndex(); if (index1 != index2) return (index1 < index2); return (op1->getSeqNum().getOrder() < op2->getSeqNum().getOrder()); } /// \brief Hide \e shadow Varnodes related to the given HighVariable by consolidating COPY chains /// /// If two Varnodes are copied from the same common ancestor then they will always contain the /// same value and can be considered \b shadows of the same variable. If the paths from the /// ancestor to the two Varnodes aren't properly nested, the two Varnodes will still look like /// distinct variables. This routine searches for this situation, relative to a single /// HighVariable, and alters data-flow so that copying from ancestor to first Varnode to /// second Varnode becomes a single path. Both Varnodes then ultimately become instances of the /// same HighVariable. /// \param high is the given HighVariable to search near /// \return \b true if a change was made to data-flow bool Merge::hideShadows(HighVariable *high) { vector singlelist; Varnode *vn1,*vn2; int4 i,j; bool res = false; findSingleCopy(high,singlelist); // Find all things copied into this high if (singlelist.size() <= 1) return false; for(i=0;icopyShadow(vn2)) continue; if (vn2->getCover()->containVarnodeDef(vn1)==1) { data.opSetInput(vn1->getDef(),vn2,0); res = true; break; } else if (vn1->getCover()->containVarnodeDef(vn2)==1) { data.opSetInput(vn2->getDef(),vn1,0); singlelist[j] = (Varnode *)0; res = true; } } } return res; } /// \brief Check if the given PcodeOp COPYs are redundant /// /// Both the given COPYs assign to the same HighVariable. One is redundant if there is no other /// assignment to the HighVariable between the first COPY and the second COPY. /// The first COPY must come from a block with a smaller or equal index to the second COPY. /// If the indices are equal, the first COPY must come before the second within the block. /// \param high is the HighVariable being assigned to /// \param domOp is the first COPY /// \param subOp is the second COPY /// \return \b true if the second COPY is redundant bool Merge::checkCopyPair(HighVariable *high,PcodeOp *domOp,PcodeOp *subOp) { FlowBlock *domBlock = domOp->getParent(); FlowBlock *subBlock = subOp->getParent(); if (!domBlock->dominates(subBlock)) return false; Cover range; range.addDefPoint(domOp->getOut()); range.addRefPoint(subOp,subOp->getIn(0)); Varnode *inVn = domOp->getIn(0); // Look for high Varnodes in the range for(int4 i=0;inumInstances();++i) { Varnode *vn = high->getInstance(i); if (!vn->isWritten()) continue; PcodeOp *op = vn->getDef(); if (op->code() == CPUI_COPY) { // If the write is not a COPY if (op->getIn(0) == inVn) continue; // from the same Varnode as domOp and subOp } if (range.contain(op, 1)) { // and if write is contained in range between domOp and subOp return false; // it is intervening and subOp is not redundant } } return true; } /// \brief Try to replace a set of COPYs from the same Varnode with a single dominant COPY /// /// All the COPY outputs must be instances of the same HighVariable (not the same Varnode). /// Either an existing COPY dominates all the others, or a new dominating COPY is constructed. /// The read locations of all other COPY outputs are replaced with the output of the dominating /// COPY, if it does not cause intersections in the HighVariable's Cover. Because of /// intersections, replacement may fail or partially succeed. Replacement only happens with /// COPY outputs that are temporary registers. The cover of the HighVariable may be extended /// because of a new COPY output instance. /// \param high is the HighVariable being copied to /// \param copy is the list of COPY ops into the HighVariable /// \param pos is the index of the first COPY from the specific input Varnode /// \param size is the number of COPYs (in sequence) from the same specific Varnode void Merge::buildDominantCopy(HighVariable *high,vector ©,int4 pos,int4 size) { vector blockSet; for(int4 i=0;igetParent()); BlockBasic *domBl = (BlockBasic *)FlowBlock::findCommonBlock(blockSet); PcodeOp *domCopy = copy[pos]; Varnode *rootVn = domCopy->getIn(0); Varnode *domVn = domCopy->getOut(); bool domCopyIsNew; if (domBl == domCopy->getParent()) { domCopyIsNew = false; } else { domCopyIsNew = true; PcodeOp *oldCopy = domCopy; domCopy = data.newOp(1,domBl->getStop()); data.opSetOpcode(domCopy, CPUI_COPY); Datatype *ct = rootVn->getType(); if (ct->needsResolution()) { const ResolvedUnion *resUnion = data.getUnionField(ct, oldCopy, 0); int4 fieldNum = (resUnion == (const ResolvedUnion *)0) ? -1 : resUnion->getFieldNum(); data.forceFacingType(ct, fieldNum, domCopy, 0); data.forceFacingType(ct, fieldNum, domCopy, -1); if (ct->getMetatype() == TYPE_PARTIALUNION) ct = rootVn->getTypeReadFacing(oldCopy); } domVn = data.newUnique(rootVn->getSize(), ct); data.opSetOutput(domCopy,domVn); data.opSetInput(domCopy,rootVn,0); data.opInsertEnd(domCopy, domBl); } // Cover created by removing all the COPYs from rootVn Cover bCover; for(int4 i=0;inumInstances();++i) { Varnode *vn = high->getInstance(i); if (vn->isWritten()) { PcodeOp *op = vn->getDef(); if (op->code() == CPUI_COPY) { if (op->getIn(0)->copyShadow(rootVn)) continue; } } bCover.merge(*vn->getCover()); } int4 count = size; for(int4 i=0;igetOut(); list::const_iterator iter; Cover aCover; aCover.addDefPoint(domVn); for(iter=outVn->beginDescend();iter!=outVn->endDescend();++iter) aCover.addRefPoint(*iter, outVn); if (bCover.intersect(aCover)>1) { count -= 1; op->setMark(); } } if (count <= 1) { // Don't bother if we only replace one COPY with another for (int4 i = 0; i < size; ++i) copy[pos + i]->setMark(); count = 0; if (domCopyIsNew) { data.opDestroy(domCopy); } } // Replace all non-intersecting COPYs with read of dominating Varnode for(int4 i=0;iisMark()) op->clearMark(); else { Varnode *outVn = op->getOut(); if (outVn != domVn) { outVn->getHigh()->remove(outVn); data.totalReplace(outVn, domVn); data.opDestroy(op); } } } if (count > 0 && domCopyIsNew) { high->merge(domVn->getHigh(),(HighIntersectTest *)0,true); } } /// \brief Search for and mark redundant COPY ops into the given high as \e non-printing /// /// Trimming during the merge process can insert multiple COPYs from the same source. In some /// cases, one or more COPYs may be redundant and shouldn't be printed. This method searches /// for redundancy among COPY ops that assign to the given HighVariable. /// \param high is the given HighVariable /// \param copy is the list of COPYs coming from the same source HighVariable /// \param pos is the starting index of a set of COPYs coming from the same Varnode /// \param size is the number of Varnodes in the set coming from the same Varnode void Merge::markRedundantCopies(HighVariable *high,vector ©,int4 pos,int4 size) { for (int4 i = size - 1; i > 0; --i) { PcodeOp *subOp = copy[pos + i]; if (subOp->isDead()) continue; for (int4 j = i - 1; j >= 0; --j) { // Make sure earlier index provides dominant op PcodeOp *domOp = copy[pos + j]; if (domOp->isDead()) continue; if (checkCopyPair(high, domOp, subOp)) { data.opMarkNonPrinting(subOp); break; } } } } /// \brief Determine if given Varnode is shadowed by another Varnode in the same HighVariable /// /// \param vn is the Varnode to check for shadowing /// \return \b true if \b vn is shadowed by another Varnode in its high-level variable bool Merge::shadowedVarnode(const Varnode *vn) { const Varnode *othervn; const HighVariable *high = vn->getHigh(); int4 num,i; num = high->numInstances(); for(i=0;igetInstance(i); if (othervn == vn) continue; if (vn->getCover()->intersect(*othervn->getCover()) == 2) return true; } return false; } /// \brief Find all the COPY ops into the given HighVariable /// /// Collect all the COPYs whose output is the given HighVariable but /// the input is from a different HighVariable. Returned COPYs are sorted /// first by the input Varnode then by block order. /// \param high is the given HighVariable /// \param copyIns will hold the list of COPYs /// \param filterTemps is \b true if COPYs must have a temporary output void Merge::findAllIntoCopies(HighVariable *high,vector ©Ins,bool filterTemps) { for(int4 i=0;inumInstances();++i) { Varnode *vn = high->getInstance(i); if (!vn->isWritten()) continue; PcodeOp *op = vn->getDef(); if (op->code() != CPUI_COPY) continue; if (op->getIn(0)->getHigh() == high) continue; if (filterTemps && op->getOut()->getSpace()->getType() != IPTR_INTERNAL) continue; copyIns.push_back(op); } // Group COPYs based on the incoming Varnode then block order sort(copyIns.begin(),copyIns.end(),compareCopyByInVarnode); } /// \brief Try to replace COPYs into the given HighVariable with a single dominant COPY /// /// Find groups of COPYs into the given HighVariable that come from a single source Varnode, /// then try to replace them with a COPY. /// \param high is the given HighVariable void Merge::processHighDominantCopy(HighVariable *high) { vector copyIns; findAllIntoCopies(high,copyIns,true); // Get all COPYs into this with temporary output if (copyIns.size() < 2) return; int4 pos = 0; while(pos < copyIns.size()) { // Find a group of COPYs coming from the same Varnode Varnode *inVn = copyIns[pos]->getIn(0); int4 sz = 1; while(pos + sz < copyIns.size()) { Varnode *nextVn = copyIns[pos+sz]->getIn(0); if (nextVn != inVn) break; sz += 1; } if (sz > 1) // If there is more than one COPY in a group buildDominantCopy(high, copyIns, pos, sz); // Try to construct a dominant COPY pos += sz; } } /// \brief Mark COPY ops into the given HighVariable that are redundant /// /// A COPY is redundant if another COPY performs the same action and has /// dominant control flow. The redundant COPY is not removed but is marked so that /// it doesn't print in the final source output. /// \param high is the given HighVariable void Merge::processHighRedundantCopy(HighVariable *high) { vector copyIns; findAllIntoCopies(high,copyIns,false); if (copyIns.size() < 2) return; int4 pos = 0; while(pos < copyIns.size()) { // Find a group of COPYs coming from the same Varnode Varnode *inVn = copyIns[pos]->getIn(0); int4 sz = 1; while(pos + sz < copyIns.size()) { Varnode *nextVn = copyIns[pos+sz]->getIn(0); if (nextVn != inVn) break; sz += 1; } if (sz > 1) { // If there is more than one COPY in a group markRedundantCopies(high, copyIns, pos, sz); } pos += sz; } } /// \brief Group the different nodes of a CONCAT tree into a VariableGroup /// /// This formally labels all the Varnodes in the tree as overlapping pieces of the same variable. /// The tree is reconstructed from the root Varnode. /// \param vn is the root Varnode void Merge::groupPartialRoot(Varnode *vn) { HighVariable *high = vn->getHigh(); if (high->numInstances() != 1) return; vector pieces; int4 baseOffset = 0; SymbolEntry *entry = vn->getSymbolEntry(); if (entry != (SymbolEntry *)0) { baseOffset = entry->getOffset(); } PieceNode::gatherPieces(pieces, vn, vn->getDef(), baseOffset, baseOffset); bool throwOut = false; for(int4 i=0;iisProtoPartial() || nodeVn->getHigh()->numInstances() != 1) { throwOut = true; break; } } if (throwOut) { for(int4 i=0;iclearProtoPartial(); } else { for(int4 i=0;igetHigh()->groupWith(pieces[i].getTypeOffset() - baseOffset,high); } } } /// \brief Try to reduce/eliminate COPYs produced by the merge trimming process /// /// In order to force merging of certain Varnodes, extra COPY operations may be inserted /// to reduce their Cover ranges, and multiple COPYs from the same Varnode can be created this way. /// This method collects sets of COPYs generated in this way that have the same input Varnode /// and then tries to replace the COPYs with fewer or a single COPY. void Merge::processCopyTrims(void) { vector multiCopy; for(int4 i=0;igetOut()->getHigh(); if (!high->hasCopyIn1()) { multiCopy.push_back(high); high->setCopyIn1(); } else high->setCopyIn2(); } copyTrims.clear(); for(int4 i=0;ihasCopyIn2()) // If the high has at least 2 COPYs into it processHighDominantCopy(high); // Try to replace with a dominant copy high->clearCopyIns(); } } /// \brief Mark redundant/internal COPY PcodeOps /// /// Run through all COPY, SUBPIECE, and PIECE operations (PcodeOps that copy data) and /// characterize those that are \e internal (copy data between storage locations representing /// the same variable) or \e redundant (perform the same copy as an earlier operation). /// These, as a result, are not printed in the final source code representation. void Merge::markInternalCopies(void) { vector multiCopy; list::const_iterator iter; PcodeOp *op; HighVariable *h1; Varnode *v1,*v2,*v3; VariablePiece *p1,*p2,*p3; int4 val; for(iter=data.beginOpAlive();iter!=data.endOpAlive();++iter) { op = *iter; switch(op->code()) { case CPUI_COPY: v1 = op->getOut(); h1 = v1->getHigh(); if (h1 == op->getIn(0)->getHigh()) { data.opMarkNonPrinting(op); } else { // COPY between different HighVariables if (!h1->hasCopyIn1()) { // If this is the first COPY we've seen for this high h1->setCopyIn1(); // Mark it multiCopy.push_back(h1); } else h1->setCopyIn2(); // This is at least the second COPY we've seen if (v1->hasNoDescend()) { // Don't print shadow assignments if (shadowedVarnode(v1)) { data.opMarkNonPrinting(op); } } } break; case CPUI_PIECE: // Check if output is built out of pieces of itself v1 = op->getOut(); v2 = op->getIn(0); v3 = op->getIn(1); p1 = v1->getHigh()->piece; p2 = v2->getHigh()->piece; p3 = v3->getHigh()->piece; if (p1 == (VariablePiece *)0) break; if (p2 == (VariablePiece *)0) break; if (p3 == (VariablePiece *)0) break; if (p1->getGroup() != p2->getGroup()) break; if (p1->getGroup() != p3->getGroup()) break; if (v1->getSpace()->isBigEndian()) { if (p2->getOffset() != p1->getOffset()) break; if (p3->getOffset() != p1->getOffset() + v2->getSize()) break; } else { if (p3->getOffset() != p1->getOffset()) break; if (p2->getOffset() != p1->getOffset() + v3->getSize()) break; } data.opMarkNonPrinting(op); if (v2->isImplied()) { v2->clearImplied(); v2->setExplicit(); } if (v3->isImplied()) { v3->clearImplied(); v3->setExplicit(); } break; case CPUI_SUBPIECE: v1 = op->getOut(); v2 = op->getIn(0); p1 = v1->getHigh()->piece; p2 = v2->getHigh()->piece; if (p1 == (VariablePiece *)0) break; if (p2 == (VariablePiece *)0) break; if (p1->getGroup() != p2->getGroup()) break; val = op->getIn(1)->getOffset(); if (v1->getSpace()->isBigEndian()) { if (p2->getOffset() + (v2->getSize() - v1->getSize() - val) != p1->getOffset()) break; } else { if (p2->getOffset() + val != p1->getOffset()) break; } data.opMarkNonPrinting(op); if (v2->isImplied()) { v2->clearImplied(); v2->setExplicit(); } break; default: break; } } for(int4 i=0;ihasCopyIn2()) data.getMerge().processHighRedundantCopy(high); high->clearCopyIns(); } #ifdef MERGEMULTI_DEBUG verifyHighCovers(); #endif } /// \brief Register an unmapped CONCAT stack with the merge process /// /// The given Varnode must be the root of a tree of CPUI_PIECE operations as produced by /// PieceNode::gatherPieces. These will be grouped together into a single variable. /// \param vn is the given root Varnode void Merge::registerProtoPartialRoot(Varnode *vn) { protoPartial.push_back(vn->getDef()); } /// \brief Perform low-level details of merging two HighVariables if possible /// /// This routine only fails (returning \b false) if there is a Cover intersection between /// the two variables. Otherwise, all the Varnode instances from the second HighVariable /// are merged into the first and its Cover is updated. The second variable is deleted. /// The cached intersection tests are also updated to reflect the merge. /// \param high1 is the first HighVariable being merged /// \param high2 is the second /// \param isspeculative is \b true if the desired merge is speculative /// \return \b true if the merge was successful bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative) { if (high1 == high2) return true; // Already merged if (testCache.intersection(high1,high2)) return false; high1->merge(high2,&testCache,isspeculative); // Do the actual merge high1->updateCover(); // Update cover now so that updateHigh won't purge cached tests return true; } /// \brief Clear the any cached data from the last merge process /// /// Free up resources used by cached intersection tests etc. void Merge::clear(void) { testCache.clear(); copyTrims.clear(); protoPartial.clear(); stackAffectingOps.clear(); } /// \brief Mark the given Varnode as \e implied /// /// The covers of the immediate Varnodes involved in the expression are marked as dirty. /// This assumes covers for the whole expression are ultimately marked because all its internal Varnodes /// are passed to this method. /// \param vn is the given Varnode being marked as \e implied void Merge::markImplied(Varnode *vn) { vn->setImplied(); // Mark as implied PcodeOp *op = vn->getDef(); for(int4 i=0;inumInput();++i) { Varnode *defvn = op->getIn(i); if (!defvn->hasCover()) continue; defvn->setFlags(Varnode::coverdirty); } } /// \brief Test if we can inflate the Cover of the given Varnode without incurring intersections /// /// This routine tests whether an expression involving a HighVariable can be propagated to all /// the read sites of the output Varnode of the expression. This is possible only if the /// Varnode Cover can be \b inflated to include the Cover of the HighVariable, even though the /// Varnode is not part of the HighVariable. /// \param a is the given Varnode to inflate /// \param high is the HighVariable being propagated /// \return \b true if inflating the Varnode causes an intersection bool Merge::inflateTest(Varnode *a,HighVariable *high) { HighVariable *ahigh = a->getHigh(); testCache.updateHigh(high); const Cover &highCover( high->internalCover ); // Only check for intersections with cover contributing to inflate for(int4 i=0;inumInstances();++i) { Varnode *b = ahigh->getInstance(i); if (b->copyShadow(a)) continue; // Intersection with a or shadows of a is allowed if (2==b->getCover()->intersect( highCover )) { return true; } } VariablePiece *piece = ahigh->piece; if (piece != (VariablePiece *)0) { piece->updateIntersections(); for(int4 i=0;inumIntersection();++i) { const VariablePiece *otherPiece = piece->getIntersection(i); HighVariable *otherHigh = otherPiece->getHigh(); int4 off = otherPiece->getOffset() - piece->getOffset(); for(int4 i=0;inumInstances();++i) { Varnode *b = otherHigh->getInstance(i); if (b->partialCopyShadow(a, off)) continue; // Intersection with partial shadow of a is allowed if (2==b->getCover()->intersect( highCover )) return true; } } } return false; } /// \brief Test for intersections between a given HighVariable and a list of other HighVariables /// /// If there is any Cover intersection between the given HighVariable and one in the list, /// this routine returns \b false. Otherwise, the given HighVariable is added to the end of /// the list and \b true is returned. /// \param high is the given HighVariable /// \param tmplist is the list of HighVariables to test against /// \return \b true if there are no pairwise intersections. bool Merge::mergeTest(HighVariable *high,vector &tmplist) { if (!high->hasCover()) return false; for(int4 i=0;ihasCover()) { HighVariable *high = vn->getHigh(); if (!high->hasCopyIn1()) { high->setCopyIn1(); high->verifyCover(); } } } } #endif } // End namespace ghidra