/* ### * 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 "variable.hh" #include "op.hh" #include "database.hh" namespace ghidra { AttributeId ATTRIB_CLASS = AttributeId("class",66); AttributeId ATTRIB_REPREF = AttributeId("repref",67); AttributeId ATTRIB_SYMREF = AttributeId("symref",68); ElementId ELEM_HIGH = ElementId("high",82); /// Compare by offset within the group, then by size. /// \param a is the first piece to compare /// \param b is the other piece to compare /// \return \b true if \b a should be ordered before the \b b bool VariableGroup::PieceCompareByOffset::operator()(const VariablePiece *a,const VariablePiece *b) const { if (a->getOffset() != b->getOffset()) return (a->getOffset() < b->getOffset()); return (a->getSize() < b->getSize()); } /// The VariablePiece takes partial ownership of \b this, via refCount. /// \param piece is the new piece to add void VariableGroup::addPiece(VariablePiece *piece) { piece->group = this; if (!pieceSet.insert(piece).second) throw LowlevelError("Duplicate VariablePiece"); int4 pieceMax = piece->getOffset() + piece->getSize(); if (pieceMax > size) size = pieceMax; } /// The adjustment amount must be positive, and this effectively increases the size of the group. /// \param amt is the given amount to add to offsets void VariableGroup::adjustOffsets(int4 amt) { set::iterator iter; for(iter=pieceSet.begin();iter!=pieceSet.end();++iter) { (*iter)->groupOffset += amt; } size += amt; } void VariableGroup::removePiece(VariablePiece *piece) { pieceSet.erase(piece); // We currently don't adjust size here as removePiece is currently only called during clean up } /// Every VariablePiece in the given group is moved into \b this and the VariableGroup object is deleted. /// There must be no matching VariablePieces with the same size and offset between the two groups /// or a LowlevelError exception is thrown. /// \param op2 is the given VariableGroup to merge into \b this void VariableGroup::combineGroups(VariableGroup *op2) { set::iterator iter = op2->pieceSet.begin(); set::iterator enditer = op2->pieceSet.end(); while(iter != enditer) { VariablePiece *piece = *iter; ++iter; piece->transferGroup(this); } } /// Construct piece given a HighVariable and its position within the whole. /// If \b this is the first piece in the group, allocate a new VariableGroup object. /// \param h is the given HighVariable to treat as a piece /// \param offset is the byte offset of the piece within the whole /// \param grp is another HighVariable in the whole, or null if \b this is the first piece VariablePiece::VariablePiece(HighVariable *h,int4 offset,HighVariable *grp) { high = h; groupOffset = offset; size = h->getInstance(0)->getSize(); if (grp != (HighVariable *)0) group = grp->piece->getGroup(); else group = new VariableGroup(); group->addPiece(this); } VariablePiece::~VariablePiece(void) { group->removePiece(this); if (group->empty()) delete group; else markIntersectionDirty(); } void VariablePiece::markIntersectionDirty(void) const { set::const_iterator iter; for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter) (*iter)->high->highflags |= (HighVariable::intersectdirty | HighVariable::extendcoverdirty); } void VariablePiece::markExtendCoverDirty(void) const { if ((high->highflags & HighVariable::intersectdirty)!=0) return; // intersection list itself is dirty, extended covers will be recomputed anyway for(int4 i=0;ihigh->highflags |= HighVariable::extendcoverdirty; } high->highflags |= HighVariable::extendcoverdirty; } /// Compute list of exactly the HighVariable pieces that intersect with \b this. void VariablePiece::updateIntersections(void) const { if ((high->highflags & HighVariable::intersectdirty)==0) return; set::const_iterator iter; int4 endOffset = groupOffset + size; intersection.clear(); for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter) { VariablePiece *otherPiece = *iter; if (otherPiece == this) continue; if (endOffset <= otherPiece->groupOffset) continue; int4 otherEndOffset = otherPiece->groupOffset + otherPiece->size; if (groupOffset >= otherEndOffset) continue; intersection.push_back(otherPiece); } high->highflags &= ~(uint4)HighVariable::intersectdirty; } /// Union internal covers of all pieces intersecting with \b this. void VariablePiece::updateCover(void) const { if ((high->highflags & (HighVariable::coverdirty | HighVariable::extendcoverdirty))==0) return; high->updateInternalCover(); cover = high->internalCover; for(int4 i=0;ihigh; high->updateInternalCover(); cover.merge(high->internalCover); } high->highflags &= ~(uint4)HighVariable::extendcoverdirty; } /// If there are no remaining references to the old VariableGroup it is deleted. /// \param newGroup is the new VariableGroup to transfer \b this to void VariablePiece::transferGroup(VariableGroup *newGroup) { group->removePiece(this); if (group->empty()) delete group; newGroup->addPiece(this); } /// Combine the VariableGroup associated \b this and the given other VariablePiece into one group. /// Offsets are adjusted so that \b this and the other VariablePiece have the same offset. /// Combining in this way requires pieces of the same size and offset to be merged. This /// method does not do the merging but passes back a list of HighVariable pairs that need to be merged. /// The first element in the pair will have its VariablePiece in the new group, and the second element /// will have its VariablePiece freed in preparation for the merge. /// \param op2 is the given other VariablePiece /// \param mergePairs passes back the collection of HighVariable pairs that must be merged void VariablePiece::mergeGroups(VariablePiece *op2,vector &mergePairs) { int4 diff = groupOffset - op2->groupOffset; // Add to op2, or subtract from this if (diff > 0) op2->group->adjustOffsets(diff); else if (diff < 0) group->adjustOffsets(-diff); set::iterator iter = op2->group->pieceSet.begin(); set::iterator enditer = op2->group->pieceSet.end(); while(iter != enditer) { VariablePiece *piece = *iter; ++iter; set::iterator matchiter = group->pieceSet.find(piece); if (matchiter != group->pieceSet.end()) { mergePairs.push_back((*matchiter)->high); mergePairs.push_back(piece->high); piece->high->piece = (VariablePiece *)0; // Detach HighVariable from its original VariablePiece delete piece; } else piece->transferGroup(group); } } /// The new instance starts off with no associate Symbol and all properties marked as \e dirty. /// \param vn is the single Varnode member HighVariable::HighVariable(Varnode *vn) { numMergeClasses = 1; highflags = flagsdirty | namerepdirty | typedirty | coverdirty; flags = 0; type = (Datatype *)0; piece = (VariablePiece *)0; symbol = (Symbol *)0; nameRepresentative = (Varnode *)0; symboloffset = -1; inst.push_back(vn); vn->setHigh( this, numMergeClasses-1 ); if (vn->getSymbolEntry() != (SymbolEntry *)0) setSymbol(vn); } HighVariable::~HighVariable(void) { if (piece != (VariablePiece *)0) delete piece; } /// The given Varnode \b must be a member and \b must have a non-null SymbolEntry void HighVariable::setSymbol(Varnode *vn) const { SymbolEntry *entry = vn->getSymbolEntry(); if (symbol != (Symbol *)0 && symbol != entry->getSymbol()) { if ((highflags & symboldirty)==0) { ostringstream s; s << "Symbols \"" << symbol->getName() << "\" and \"" << entry->getSymbol()->getName(); s << "\" assigned to the same variable"; throw LowlevelError(s.str()); } } symbol = entry->getSymbol(); if (vn->isProtoPartial() && piece != (VariablePiece *)0) { symboloffset = piece->getOffset() + piece->getGroup()->getSymbolOffset(); } else if (entry->isDynamic()) // Dynamic symbols (that aren't partials) match whole variable symboloffset = -1; else if (symbol->getCategory() == Symbol::equate) symboloffset = -1; // For equates, we don't care about size else if (symbol->getType()->getSize() == vn->getSize() && entry->getAddr() == vn->getAddr() && !entry->isPiece()) symboloffset = -1; // A matching entry else { symboloffset = vn->getAddr().overlapJoin(0,entry->getAddr(),symbol->getType()->getSize()) + entry->getOffset(); } if (type != (Datatype *)0 && type->getMetatype() == TYPE_PARTIALUNION) highflags |= typedirty; highflags &= ~((uint4)symboldirty); // We are no longer dirty } /// Link information to \b this from a Symbol that is not attached to a member Varnode. /// This only works for a HighVariable with a constant member Varnode. This used when there /// is a constant address reference to the Symbol and the Varnode holds the reference, not /// the actual value of the Symbol. /// \param sym is the given Symbol to attach /// \param off is the byte offset into the Symbol of the reference void HighVariable::setSymbolReference(Symbol *sym,int4 off) { symbol = sym; symboloffset = off; highflags &= ~((uint4)symboldirty); } void HighVariable::transferPiece(HighVariable *tv2) { piece = tv2->piece; tv2->piece = (VariablePiece *)0; piece->setHigh(this); highflags |= (tv2->highflags & (intersectdirty | extendcoverdirty)); tv2->highflags &= ~(uint4)(intersectdirty | extendcoverdirty); } /// Except in specific circumstances, convert \b type into its stripped form. void HighVariable::stripType(void) const { if (!type->hasStripped()) return; type_metatype meta = type->getMetatype(); if (meta == TYPE_PARTIALUNION || meta == TYPE_PARTIALSTRUCT) { if (symbol != (Symbol *)0 && symboloffset != -1) { // If there is a bigger backing symbol type_metatype submeta = symbol->getType()->getMetatype(); if (submeta == TYPE_STRUCT || submeta == TYPE_UNION) return; // Don't strip the partial union } } else if (type->isEnumType()) { if (inst.size() == 1 && inst[0]->isConstant()) // Only preserve partial enum on a constant return; } type = type->getStripped(); } /// Only update if the cover is marked as \e dirty. /// Merge the covers of all Varnode instances. void HighVariable::updateInternalCover(void) const { if ((highflags & coverdirty) != 0) { internalCover.clear(); if (inst[0]->hasCover()) { for(int4 i = 0;i < inst.size();++i) internalCover.merge(*inst[i]->getCover()); } highflags &= ~coverdirty; } } /// This is \b only called by the Merge class which knows when to call it properly. void HighVariable::updateCover(void) const { if (piece == (VariablePiece *)0) updateInternalCover(); else { piece->updateIntersections(); piece->updateCover(); } } /// Only update if flags are marked as \e dirty. /// Generally if any member Varnode possesses the property, \b this HighVariable should /// inherit it. The Varnode::typelock field is not set here, but in updateType(). void HighVariable::updateFlags(void) const { if ((highflags & flagsdirty)==0) return; // flags are up to date vector::const_iterator iter; uint4 fl = 0; for(iter=inst.begin();iter!=inst.end();++iter) fl |= (*iter)->getFlags(); // Keep these flags flags &= (Varnode::mark | Varnode::typelock); // Update all but these flags |= fl & ~(Varnode::mark | Varnode::directwrite | Varnode::typelock ); highflags &= ~flagsdirty; // Clear the dirty flag } /// Find the member Varnode with the most \e specialized data-type, handling \e bool specially. /// Boolean data-types are \e specialized in the data-type lattice, but not all byte values are boolean values. /// Within the Varnode/PcodeOp tree, the \e bool data-type can only propagate to a Varnode if it is verified to /// only take the boolean values 0 and 1. Since the data-type representative represents the type of all /// instances, if any instance is not boolean, then the HighVariable cannot be boolean, even though \e bool /// is more specialized. This method uses Datatype::typeOrderBool() to implement the special handling. /// \return the representative member Varnode *HighVariable::getTypeRepresentative(void) const { vector::const_iterator iter; Varnode *vn,*rep; iter = inst.begin(); rep = *iter; ++iter; for(;iter!=inst.end();++iter) { vn = *iter; if (rep->isTypeLock() != vn->isTypeLock()) { if (vn->isTypeLock()) rep = vn; } else if (0>vn->getType()->typeOrderBool(*rep->getType())) rep = vn; } return rep; } /// Only update if the data-type is marked as \e dirty. /// Get the most locked, most specific data-type from member Varnode objects. void HighVariable::updateType(void) const { Varnode *vn; if ((highflags&typedirty)==0) return; // Type is up to date highflags &= ~typedirty; // Mark type as clean if ((highflags & type_finalized)!=0) return; // Type has been finalized vn = getTypeRepresentative(); type = vn->getType(); stripType(); // Update lock flags flags &= ~Varnode::typelock; if (vn->isTypeLock()) flags |= Varnode::typelock; } void HighVariable::updateSymbol(void) const { if ((highflags & symboldirty)==0) return; // flags are up to date highflags &= ~((uint4)symboldirty); vector::const_iterator iter; symbol = (Symbol *)0; for(iter=inst.begin();iter!=inst.end();++iter) { Varnode *vn = *iter; if (vn->getSymbolEntry() != (SymbolEntry *)0) { setSymbol(vn); return; } } } /// Compare two Varnode objects based just on their storage address /// \param a is the first Varnode to compare /// \param b is the second Varnode /// \return \b true if the first Varnode should be ordered before the second bool HighVariable::compareJustLoc(const Varnode *a,const Varnode *b) { return (a->getAddr() < b->getAddr()); } /// Given two Varnode (members), sort them based on naming properties: /// - A Varnode with an assigned name is preferred /// - An \e unaffected Varnode is preferred /// - A global Varnode is preferred /// - An \e input Varnode is preferred /// - An \e address \e tied Varnode is preferred /// - A non-temporary Varnode is preferred /// - A written Varnode is preferred /// - An earlier Varnode is preferred /// /// \return \b true if the second Varnode's name would override the first's bool HighVariable::compareName(Varnode *vn1,Varnode *vn2) { if (vn1->isNameLock()) return false; // Check for namelocks if (vn2->isNameLock()) return true; if (vn1->isUnaffected() != vn2->isUnaffected()) // Prefer unaffected return vn2->isUnaffected(); if (vn1->isPersist() != vn2->isPersist()) // Prefer persistent return vn2->isPersist(); if (vn1->isInput() != vn2->isInput()) // Prefer an input return vn2->isInput(); if (vn1->isAddrTied() != vn2->isAddrTied()) // Prefer address tied return vn2->isAddrTied(); if (vn1->isProtoPartial() != vn2->isProtoPartial()) // Prefer pieces return vn2->isProtoPartial(); // Prefer NOT internal if ((vn1->getSpace()->getType() != IPTR_INTERNAL)&& (vn2->getSpace()->getType() == IPTR_INTERNAL)) return false; if ((vn1->getSpace()->getType() == IPTR_INTERNAL)&& (vn2->getSpace()->getType() != IPTR_INTERNAL)) return true; if (vn1->isWritten() != vn2->isWritten()) // Prefer written return vn2->isWritten(); if (!vn1->isWritten()) return false; // Prefer earlier if (vn1->getDef()->getTime() != vn2->getDef()->getTime()) return (vn2->getDef()->getTime() < vn1->getDef()->getTime()); return false; } /// Members are scored based the properties that are most dominating in choosing a name. /// \return the highest scoring Varnode member Varnode *HighVariable::getNameRepresentative(void) const { if ((highflags & namerepdirty)==0) return nameRepresentative; // Name representative is up to date highflags &= ~namerepdirty; vector::const_iterator iter; Varnode *vn; iter = inst.begin(); nameRepresentative = *iter; ++iter; for(;iter!=inst.end();++iter) { vn = *iter; if (compareName(nameRepresentative,vn)) nameRepresentative = vn; } return nameRepresentative; } /// Search for the given Varnode and cut it out of the list, marking all properties as \e dirty. /// \param vn is the given Varnode member to remove void HighVariable::remove(Varnode *vn) { vector::iterator iter; iter = lower_bound(inst.begin(),inst.end(),vn,compareJustLoc); for(;iter!=inst.end();++iter) { if (*iter == vn) { inst.erase(iter); highflags |= (flagsdirty|namerepdirty|coverdirty|typedirty); if (vn->getSymbolEntry() != (SymbolEntry *)0) highflags |= symboldirty; if (piece != (VariablePiece *)0) piece->markExtendCoverDirty(); return; } } } /// Assuming there is a Symbol attached to \b this, run through the Varnode members /// until we find one with a SymbolEntry corresponding to the Symbol and return it. /// \return the SymbolEntry that mapped the Symbol to \b this or null if no Symbol is attached SymbolEntry *HighVariable::getSymbolEntry(void) const { for(int4 i=0;igetSymbolEntry(); if (entry != (SymbolEntry *)0 && entry->getSymbol() == symbol) return entry; } return (SymbolEntry *)0; } /// If there is an associated Symbol, its data-type (or the appropriate piece) is assigned /// to \b this. The dirtying mechanism is disabled so that data-type cannot change. /// \param typeFactory is the factory used to construct any required piece void HighVariable::finalizeDatatype(TypeFactory *typeFactory) { if (symbol == (Symbol *)0) return; Datatype *cur = symbol->getType(); int4 off = symboloffset; if (off < 0) off = 0; int4 sz = inst[0]->getSize(); Datatype *tp = typeFactory->getExactPiece(cur, off, sz); if (tp == (Datatype *)0 || tp->getMetatype() == TYPE_UNKNOWN) return; type = tp; stripType(); highflags |= type_finalized; } /// If one of the HighVariables is already in a group, the other HighVariable is added to this group. /// \param off is the relative byte offset of \b this with the other HighVariable /// \param hi2 is the other HighVariable void HighVariable::groupWith(int4 off,HighVariable *hi2) { if (piece == (VariablePiece *)0 && hi2->piece == (VariablePiece *)0) { hi2->piece = new VariablePiece(hi2,0); piece = new VariablePiece(this,off,hi2); hi2->piece->markIntersectionDirty(); return; } if (piece == (VariablePiece *)0) { if ((hi2->highflags & intersectdirty) == 0) hi2->piece->markIntersectionDirty(); highflags |= intersectdirty | extendcoverdirty; off += hi2->piece->getOffset(); piece = new VariablePiece(this,off,hi2); } else if (hi2->piece == (VariablePiece *)0) { int4 hi2Off = piece->getOffset() - off; if (hi2Off < 0) { piece->getGroup()->adjustOffsets(-hi2Off); hi2Off = 0; } if ((highflags & intersectdirty) == 0) piece->markIntersectionDirty(); hi2->highflags |= intersectdirty | extendcoverdirty; hi2->piece = new VariablePiece(hi2,hi2Off,this); } else { int4 offDiff = hi2->piece->getOffset() + off - piece->getOffset(); if (offDiff != 0) piece->getGroup()->adjustOffsets(offDiff); hi2->piece->getGroup()->combineGroups(piece->getGroup()); hi2->piece->markIntersectionDirty(); } } /// If \b this is part of a larger group and has had its \b symboloffset set, it can be used /// to calculate the \b symboloffset of other HighVariables in the same group, by writing it /// to the common VariableGroup object. void HighVariable::establishGroupSymbolOffset(void) { VariableGroup *group = piece->getGroup(); int4 off = symboloffset; if (off < 0) off = 0; off -= piece->getOffset(); if (off < 0) throw LowlevelError("Symbol offset is incompatible with VariableGroup"); group->setSymbolOffset(off); } /// The lists of members are merged and the other HighVariable is deleted. /// \param tv2 is the other HighVariable to merge into \b this /// \param isspeculative is \b true to keep the new members in separate \e merge classes void HighVariable::mergeInternal(HighVariable *tv2,bool isspeculative) { int4 i; highflags |= (flagsdirty|namerepdirty|typedirty); if (tv2->symbol != (Symbol *)0) { // Check if we inherit a Symbol if ((tv2->highflags & symboldirty)==0) { symbol = tv2->symbol; // Overwrite our Symbol (assume it is the same) symboloffset = tv2->symboloffset; highflags &= ~((uint4)symboldirty); // Mark that we are not symbol dirty } } if (isspeculative) { for(i=0;iinst.size();++i) { Varnode *vn = tv2->inst[i]; vn->setHigh(this,vn->getMergeGroup() + numMergeClasses); } numMergeClasses += tv2->numMergeClasses; } else { if ((numMergeClasses!=1)||(tv2->numMergeClasses!=1)) throw LowlevelError("Making a non-speculative merge after speculative merges have occurred"); for(i=0;iinst.size();++i) { Varnode *vn = tv2->inst[i]; vn->setHigh(this,vn->getMergeGroup()); } } vector instcopy(inst); inst.resize(inst.size()+tv2->inst.size(),(Varnode *)0); std::merge(instcopy.begin(),instcopy.end(),tv2->inst.begin(),tv2->inst.end(),inst.begin(),compareJustLoc); tv2->inst.clear(); if (((highflags&coverdirty)==0)&&((tv2->highflags&coverdirty)==0)) internalCover.merge(tv2->internalCover); else highflags |= coverdirty; delete tv2; } /// The HighVariables are merged internally as with mergeInternal. If \b this is part of a VariableGroup, /// extended covers of the group may be affected. If both HighVariables are part of separate groups, /// the groups are combined into one, which may induce additional HighVariable pairs within the group to be merged. /// In all cases, the other HighVariable is deleted. /// \param tv2 is the other HighVariable to merge into \b this /// \param testCache if non-null is a cache of intersection tests that must be updated to reflect the merge /// \param isspeculative is \b true to keep the new members in separate \e merge classes void HighVariable::merge(HighVariable *tv2,HighIntersectTest *testCache,bool isspeculative) { if (tv2 == this) return; if (testCache != (HighIntersectTest *)0) testCache->moveIntersectTests(this,tv2); if (piece == (VariablePiece *)0 && tv2->piece == (VariablePiece *)0) { mergeInternal(tv2,isspeculative); return; } if (tv2->piece == (VariablePiece *)0) { // Keep group that this is already in piece->markExtendCoverDirty(); mergeInternal(tv2,isspeculative); return; } if (piece == (VariablePiece *)0) { // Move ownership of the VariablePiece object from the HighVariable that will be freed transferPiece(tv2); piece->markExtendCoverDirty(); mergeInternal(tv2,isspeculative); return; } // Reaching here both HighVariables are part of a group if (isspeculative) throw LowlevelError("Trying speculatively merge variables in separate groups"); vector mergePairs; piece->mergeGroups(tv2->piece, mergePairs); for(int4 i=0;imoveIntersectTests(high1, high2); high1->mergeInternal(high2, isspeculative); } piece->markIntersectionDirty(); } /// All Varnode objects are assigned a HighVariable, including those that don't get names like /// indirect variables, constants, and annotations. Determine if \b this, as inherited from its /// member Varnodes, can have a name. /// \return \b true if \b this can have a name bool HighVariable::hasName(void) const { bool indirectonly = true; for(int4 i=0;ihasCover()) { if (inst.size() > 1) throw LowlevelError("Non-coverable varnode has been merged"); return false; } if (vn->isImplied()) { if (inst.size() > 1) throw LowlevelError("Implied varnode has been merged"); return false; } if (!vn->isIndirectOnly()) indirectonly = false; } if (isUnaffected()) { if (!isInput()) return false; if (indirectonly) return false; Varnode *vn = getInputVarnode(); if (!vn->isIllegalInput()) { // A leftover unaff illegal input gets named if (vn->isSpacebase()) // A legal input, unaff, gets named return false; // Unless it is the stackpointer } } return true; } /// This should only be called if isAddrTied() returns \b true. If there is no address tied /// member, this will throw an exception. /// \return the first address tied member Varnode *HighVariable::getTiedVarnode(void) const { int4 i; for(i=0;iisAddrTied()) return inst[i]; throw LowlevelError("Could not find address-tied varnode"); } /// This should only be called if isInput() returns \b true. If there is no input /// member, this will throw an exception. /// \return the input Varnode member Varnode *HighVariable::getInputVarnode(void) const { for(int4 i=0;iisInput()) return inst[i]; throw LowlevelError("Could not find input varnode"); } /// This is generally used for debug purposes. /// \param s is the output stream void HighVariable::printInfo(ostream &s) const { vector::const_iterator viter; Varnode *vn; updateType(); if (symbol == (Symbol *)0) { s << "Variable: UNNAMED" << endl; } else { s << "Variable: " << symbol->getName(); if (symboloffset!=-1) s << "(partial)"; s << endl; } s << "Type: "; type->printRaw(s); s << "\n\n"; for(viter=inst.begin();viter!=inst.end();++viter) { vn = *viter; s << dec << vn->getMergeGroup() << ": "; vn->printInfo(s); } } /// Find the index, for use with getInstance(), that will retrieve the given Varnode member /// \param vn is the given Varnode member /// \return the index of the member or -1 if it is not a member int4 HighVariable::instanceIndex(const Varnode *vn) const { int4 i; for(i=0;igetCreateIndex()); if (isSpacebase()||isImplied()) // This is a special variable encoder.writeString(ATTRIB_CLASS, "other"); else if (isPersist()&&isAddrTied()) // Global variable encoder.writeString(ATTRIB_CLASS, "global"); else if (isConstant()) encoder.writeString(ATTRIB_CLASS, "constant"); else if (!isPersist() && (symbol != (Symbol *)0)) { if (symbol->getCategory() == Symbol::function_parameter) encoder.writeString(ATTRIB_CLASS, "param"); else if (symbol->getScope()->isGlobal()) encoder.writeString(ATTRIB_CLASS, "global"); else encoder.writeString(ATTRIB_CLASS, "local"); } else { encoder.writeString(ATTRIB_CLASS, "other"); } if (isTypeLock()) encoder.writeBool(ATTRIB_TYPELOCK, true); if (symbol != (Symbol *)0) { encoder.writeUnsignedInteger(ATTRIB_SYMREF, symbol->getId()); if (symboloffset >= 0) encoder.writeSignedInteger(ATTRIB_OFFSET, symboloffset); } getType()->encodeRef(encoder); for(int4 j=0;jgetCreateIndex()); encoder.closeElement(ELEM_ADDR); } encoder.closeElement(ELEM_HIGH); } /// Given a Varnode at the root of an expression, we collect all the \e explicit HighVariables /// involved in the expression. This should only be run after \e explicit and \e implicit /// properties have been computed on Varnodes. The expression is traced back from the root /// until explicit Varnodes are encountered; then their HighVariable is marked and added to the list. /// The routine returns a value based on PcodeOps encountered in the expression: /// - 1 for call instructions /// - 2 for LOAD instructions /// - 3 for both call and LOAD /// - 0 for no calls or LOADS /// /// \param vn is the given root Varnode of the expression /// \param highList will hold the collected HighVariables /// \return a value based on call and LOAD instructions in the expression int4 HighVariable::markExpression(Varnode *vn,vector &highList) { HighVariable *high = vn->getHigh(); high->setMark(); highList.push_back(high); int4 retVal = 0; if (!vn->isWritten()) return retVal; vector path; PcodeOp *op = vn->getDef(); if (op->isCall()) retVal |= 1; if (op->code() == CPUI_LOAD) retVal |= 2; path.push_back(PcodeOpNode(op,0)); while(!path.empty()) { PcodeOpNode &node(path.back()); if (node.op->numInput() <= node.slot) { path.pop_back(); continue; } Varnode *curVn = node.op->getIn(node.slot); node.slot += 1; if (curVn->isAnnotation()) continue; if (curVn->isExplicit()) { high = curVn->getHigh(); if (high->isMark()) continue; // Already in the list high->setMark(); highList.push_back(high); continue; // Truncate at explicit } if (!curVn->isWritten()) continue; op = curVn->getDef(); if (op->isCall()) retVal |= 1; if (op->code() == CPUI_LOAD) retVal |= 2; path.push_back(PcodeOpNode(curVn->getDef(),0)); } return retVal; } #ifdef MERGEMULTI_DEBUG /// \brief Check that there are no internal Cover intersections within \b this /// /// Look for any pair of Varnodes whose covers intersect, but they are not /// COPY shadows. Throw an exception in this case. void HighVariable::verifyCover(void) const { Cover accumCover; for(int4 i=0;igetCover()) == 2) { for(int4 j=0;jgetCover()->intersect(*vn->getCover())==2) { if (!otherVn->copyShadow(vn)) throw LowlevelError("HighVariable has internal intersection"); } } } accumCover.merge(*vn->getCover()); } } #endif /// \brief Gather Varnode instances of the given HighVariable that intersect a cover on a specific block /// /// \param a is the given HighVariable /// \param blk is the specific block number /// \param cover is the Cover to test for intersection /// \param res will hold the resulting intersecting Varnodes void HighIntersectTest::gatherBlockVarnodes(HighVariable *a,int4 blk,const Cover &cover,vector &res) { for(int4 i=0;inumInstances();++i) { Varnode *vn = a->getInstance(i); if (1getCover()->intersectByBlock(blk,cover)) res.push_back(vn); } } /// \brief Test instances of a the given HighVariable for intersection on a specific block with a cover /// /// A list of Varnodes has already been determined to intersect on the block. For an instance that does as /// well, a final test of copy shadowing is performed with the Varnode list. If there is no shadowing, /// a merging intersection has been found and \b true is returned. /// \param a is the given HighVariable /// \param blk is the specific block number /// \param cover is the Cover to test for intersection /// \param relOff is the relative byte offset of the HighVariable to the Varnodes /// \param blist is the list of Varnodes for copy shadow testing /// \return \b true if there is an intersection preventing merging bool HighIntersectTest::testBlockIntersection(HighVariable *a,int4 blk,const Cover &cover,int4 relOff, const vector &blist) { for(int4 i=0;inumInstances();++i) { Varnode *vn = a->getInstance(i); if (2>vn->getCover()->intersectByBlock(blk,cover)) continue; for(int4 j=0;jgetCover()->intersectByBlock(blk,*vn->getCover())) { if (vn->getSize() == vn2->getSize()) { if (!vn->copyShadow(vn2)) return true; } else { if (!vn->partialCopyShadow(vn2,relOff)) return true; } } } } return false; } /// \brief Test if two HighVariables intersect on a given BlockBasic /// /// Intersections are checked only on the specified block. /// \param a is the first HighVariable /// \param b is the second HighVariable /// \param blk is the index of the BlockBasic on which to test intersection /// \return \b true if an intersection occurs in the specified block bool HighIntersectTest::blockIntersection(HighVariable *a,HighVariable *b,int4 blk) { vector blist; const Cover &aCover(a->getCover()); const Cover &bCover(b->getCover()); gatherBlockVarnodes(b,blk,aCover,blist); if (testBlockIntersection(a, blk, bCover, 0, blist)) return true; if (a->piece != (VariablePiece *)0) { int4 baseOff = a->piece->getOffset(); for(int4 i=0;ipiece->numIntersection();++i) { const VariablePiece *interPiece = a->piece->getIntersection(i); int4 off = interPiece->getOffset() - baseOff; if (testBlockIntersection(interPiece->getHigh(), blk, bCover, off, blist)) return true; } } if (b->piece != (VariablePiece *)0) { int4 bBaseOff = b->piece->getOffset(); for(int4 i=0;ipiece->numIntersection();++i) { blist.clear(); const VariablePiece *bPiece = b->piece->getIntersection(i); int4 bOff = bPiece->getOffset() - bBaseOff; gatherBlockVarnodes(bPiece->getHigh(),blk,aCover,blist); if (testBlockIntersection(a, blk, bCover, -bOff, blist)) return true; if (a->piece != (VariablePiece *)0) { int4 baseOff = a->piece->getOffset(); for(int4 j=0;jpiece->numIntersection();++j) { const VariablePiece *interPiece = a->piece->getIntersection(j); int4 off = (interPiece->getOffset() - baseOff) - bOff; if (off > 0 && off >= bPiece->getSize()) continue; // Do a piece and b piece intersect at all if (off < 0 && -off >= interPiece->getSize()) continue; if (testBlockIntersection(interPiece->getHigh(), blk, bCover, off, blist)) return true; } } } } return false; } /// All tests for pairs where either the first or second HighVariable matches the given one /// are removed. /// \param high is the given HighVariable to purge void HighIntersectTest::purgeHigh(HighVariable *high) { map::iterator iterfirst = highedgemap.lower_bound( HighEdge(high,(HighVariable *)0) ); map::iterator iterlast = highedgemap.lower_bound( HighEdge(high,(HighVariable *)~((uintp)0)) ); if (iterfirst == iterlast) return; --iterlast; // Move back 1 to prevent deleting under the iterator map::iterator iter; for(iter=iterfirst;iter!=iterlast;++iter) highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) ); highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) ); ++iterlast; // Restore original range (with possibly new open endpoint) highedgemap.erase(iterfirst,iterlast); } /// \brief Test if a given HighVariable might intersect an address tied HighVariable during a call /// /// If an address tied Varnode has aliases, we need to consider it as \e in \e scope during /// calls, even if the value is never read after the call. In particular, another Varnode /// that \e crosses the call is considered to be intersecting with the address tied Varnode. /// This method tests whether the address tied HighVariable has aliases, then if so, /// it tests if the given HighVariable intersects a call site. /// \param tied is the address tied HighVariable /// \param untied is the given HighVariable to consider for intersection /// \return \b true if we consider the HighVariables to be intersecting bool HighIntersectTest::testUntiedCallIntersection(HighVariable *tied,HighVariable *untied) { // If the address tied part is global, we do not need to test for crossings, as the // address forcing mechanism should act as a placeholder across calls if (tied->isPersist()) return false; Varnode *vn = tied->getTiedVarnode(); if (vn->hasNoLocalAlias()) return false; // A local variable is only in scope if it has aliases if (!affectingOps.isPopulated()) affectingOps.populate(); return untied->getCover().intersect(affectingOps,vn); } /// \brief Translate any intersection tests for \e high2 into tests for \e high1 /// /// The two variables will be merged and \e high2, as an object, will be freed. /// We update the cached intersection tests for \e high2 so that they will now apply to new merged \e high1 /// \param high1 is the variable object being kept /// \param high2 is the variable object being eliminated void HighIntersectTest::moveIntersectTests(HighVariable *high1,HighVariable *high2) { vector yesinter; // Highs that high2 intersects vector nointer; // Highs that high2 does not intersect map::iterator iterfirst = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)0) ); map::iterator iterlast = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)~((uintp)0)) ); map::iterator iter; for(iter=iterfirst;iter!=iterlast;++iter) { HighVariable *b = (*iter).first.b; if (b == high1) continue; if ((*iter).second) // Save all high2's intersections yesinter.push_back(b); // as they are still valid for the merge else { nointer.push_back(b); b->setMark(); // Mark that high2 did not intersect } } // Do a purge of all high2's tests if (iterfirst != iterlast) { // Delete all the high2 tests --iterlast; // Move back 1 to prevent deleting under the iterator for(iter=iterfirst;iter!=iterlast;++iter) highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) ); highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) ); ++iterlast; // Restore original range (with possibly new open endpoint) highedgemap.erase(iterfirst,iterlast); } iter = highedgemap.lower_bound( HighEdge(high1,(HighVariable *)0) ); while((iter!=highedgemap.end())&&((*iter).first.a == high1)) { if (!(*iter).second) { // If test is intersection==false if (!(*iter).first.b->isMark()) // and there was no test with high2 highedgemap.erase( iter++ ); // Delete the test else ++iter; } else // Keep any intersection==true tests ++iter; } vector::iterator titer; for(titer=nointer.begin();titer!=nointer.end();++titer) (*titer)->clearMark(); // Reinsert high2's intersection==true tests for high1 now for(titer=yesinter.begin();titer!=yesinter.end();++titer) { highedgemap[ HighEdge(high1,*titer) ] = true; highedgemap[ HighEdge(*titer,high1) ] = true; } } /// As manipulations are made, Cover information gets out of date. A \e dirty flag is used to /// indicate a particular HighVariable Cover is out-of-date. This routine checks the \e dirty /// flag and updates the Cover information if it is set. /// \param a is the HighVariable to update /// \return \b true if the HighVariable was not originally dirty bool HighIntersectTest::updateHigh(HighVariable *a) { if (!a->isCoverDirty()) return true; a->updateCover(); purgeHigh(a); return false; } /// \brief Test the intersection of two HighVariables and cache the result /// /// If the Covers of the two variables intersect, this routine returns \b true. To avoid /// expensive computation on the Cover objects themselves, the test result associated with /// the pair of HighVariables is cached. /// \param a is the first HighVariable /// \param b is the second HighVariable /// \return \b true if the variables intersect bool HighIntersectTest::intersection(HighVariable *a,HighVariable *b) { if (a==b) return false; bool ares = updateHigh(a); bool bres = updateHigh(b); if (ares && bres) { // If neither high was dirty map::iterator iter = highedgemap.find( HighEdge(a,b) ); if (iter != highedgemap.end()) // If previous test is present return (*iter).second; // Use it } bool res = false; int4 blk; vector blockisect; a->getCover().intersectList(blockisect,b->getCover(),2); for(blk=0;blkisAddrTied(); bool bTied = b->isAddrTied(); if (aTied != bTied) { // If one variable is address tied and the other isn't if (aTied) res = testUntiedCallIntersection(a,b); // Test if the non-tied variable crosses any calls else res = testUntiedCallIntersection(b,a); } } highedgemap[ HighEdge(a,b) ] = res; // Cache the result highedgemap[ HighEdge(b,a) ] = res; return res; } } // End namespace ghidra