ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc

1202 lines
41 KiB
C++

/* ###
* 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<VariablePiece *,VariableGroup::PieceCompareByOffset>::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<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter = op2->pieceSet.begin();
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::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<VariablePiece *,VariableGroup::PieceCompareByOffset>::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;i<intersection.size();++i) {
intersection[i]->high->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<VariablePiece *,VariableGroup::PieceCompareByOffset>::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;i<intersection.size();++i) {
const HighVariable *high = intersection[i]->high;
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<HighVariable *> &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<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter = op2->group->pieceSet.begin();
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator enditer = op2->group->pieceSet.end();
while(iter != enditer) {
VariablePiece *piece = *iter;
++iter;
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::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<Varnode *>::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<Varnode *>::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<Varnode *>::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<Varnode *>::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<Varnode *>::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;i<inst.size();++i) {
SymbolEntry *entry = inst[i]->getSymbolEntry();
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;i<tv2->inst.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;i<tv2->inst.size();++i) {
Varnode *vn = tv2->inst[i];
vn->setHigh(this,vn->getMergeGroup());
}
}
vector<Varnode *> 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<HighVariable *> mergePairs;
piece->mergeGroups(tv2->piece, mergePairs);
for(int4 i=0;i<mergePairs.size();i+=2) {
HighVariable *high1 = mergePairs[i];
HighVariable *high2 = mergePairs[i+1];
if (testCache != (HighIntersectTest *)0)
testCache->moveIntersectTests(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;i<inst.size();++i) {
Varnode *vn = inst[i];
if (!vn->hasCover()) {
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;i<inst.size();++i)
if (inst[i]->isAddrTied())
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;i<inst.size();++i)
if (inst[i]->isInput())
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<Varnode *>::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;i<inst.size();++i)
if (inst[i] == vn) return i;
return -1;
}
/// \param encoder is the stream encoder
void HighVariable::encode(Encoder &encoder) const
{
Varnode *vn = getNameRepresentative(); // Get representative varnode
encoder.openElement(ELEM_HIGH);
encoder.writeUnsignedInteger(ATTRIB_REPREF, vn->getCreateIndex());
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;j<inst.size();++j) {
encoder.openElement(ELEM_ADDR);
encoder.writeUnsignedInteger(ATTRIB_REF, inst[j]->getCreateIndex());
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<HighVariable *> &highList)
{
HighVariable *high = vn->getHigh();
high->setMark();
highList.push_back(high);
int4 retVal = 0;
if (!vn->isWritten()) return retVal;
vector<PcodeOpNode> 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;i<inst.size();++i) {
Varnode *vn = inst[i];
if (accumCover.intersect(*vn->getCover()) == 2) {
for(int4 j=0;j<i;++j) {
Varnode *otherVn = inst[j];
if (otherVn->getCover()->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<Varnode *> &res)
{
for(int4 i=0;i<a->numInstances();++i) {
Varnode *vn = a->getInstance(i);
if (1<vn->getCover()->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<Varnode *> &blist)
{
for(int4 i=0;i<a->numInstances();++i) {
Varnode *vn = a->getInstance(i);
if (2>vn->getCover()->intersectByBlock(blk,cover)) continue;
for(int4 j=0;j<blist.size();++j) {
Varnode *vn2 = blist[j];
if (1<vn2->getCover()->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<Varnode *> 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;i<a->piece->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;i<b->piece->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;j<a->piece->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<HighEdge,bool>::iterator iterfirst = highedgemap.lower_bound( HighEdge(high,(HighVariable *)0) );
map<HighEdge,bool>::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<HighEdge,bool>::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<HighVariable *> yesinter; // Highs that high2 intersects
vector<HighVariable *> nointer; // Highs that high2 does not intersect
map<HighEdge,bool>::iterator iterfirst = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)0) );
map<HighEdge,bool>::iterator iterlast = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)~((uintp)0)) );
map<HighEdge,bool>::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<HighVariable *>::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<HighEdge,bool>::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<int4> blockisect;
a->getCover().intersectList(blockisect,b->getCover(),2);
for(blk=0;blk<blockisect.size();++blk) {
if (blockIntersection(a,b,blockisect[blk])) {
res = true;
break;
}
}
if (!res) {
bool aTied = a->isAddrTied();
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