mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
2060 lines
66 KiB
C++
2060 lines
66 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 "varnode.hh"
|
|
#include "funcdata.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
AttributeId ATTRIB_ADDRTIED = AttributeId("addrtied",30);
|
|
AttributeId ATTRIB_GRP = AttributeId("grp",31);
|
|
AttributeId ATTRIB_INPUT = AttributeId("input",32);
|
|
AttributeId ATTRIB_PERSISTS = AttributeId("persists",33);
|
|
AttributeId ATTRIB_UNAFF = AttributeId("unaff",34);
|
|
|
|
/// Compare by location then by definition.
|
|
/// This is the same as the normal varnode compare, but we distinguish identical frees by their
|
|
/// pointer address. Thus varsets defined with this comparison act like multisets for free varnodes
|
|
/// and like unique sets for everything else (with respect to the standard varnode comparison)
|
|
/// \param a is the first Varnode to compare
|
|
/// \param b is the second Varnode to compare
|
|
/// \return true if \b a occurs earlier than \b b
|
|
inline bool VarnodeCompareLocDef::operator()(const Varnode *a,const Varnode *b) const
|
|
|
|
{
|
|
uint4 f1,f2;
|
|
|
|
if (a->getAddr() != b->getAddr()) return (a->getAddr() < b->getAddr());
|
|
if (a->getSize() != b->getSize()) return (a->getSize() < b->getSize());
|
|
f1 = a->getFlags()&(Varnode::input|Varnode::written);
|
|
f2 = b->getFlags()&(Varnode::input|Varnode::written);
|
|
if (f1!=f2) return ((f1-1)<(f2-1)); // -1 forces free varnodes to come last
|
|
if (f1==Varnode::written) {
|
|
if (a->getDef()->getSeqNum() != b->getDef()->getSeqNum())
|
|
return (a->getDef()->getSeqNum() < b->getDef()->getSeqNum());
|
|
}
|
|
else if (f1 == 0) // both are free
|
|
// return (a < b); // compare pointers
|
|
return (a->getCreateIndex() < b->getCreateIndex());
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Compare by definition then by location.
|
|
/// This is different than the standard ordering but we still allow multiple identical frees.
|
|
/// \param a is the first Varnode to compare
|
|
/// \param b is the second Varnode to compare
|
|
/// \return true if \b a occurs earlier than \b b
|
|
inline bool VarnodeCompareDefLoc::operator()(const Varnode *a,const Varnode *b) const
|
|
|
|
{
|
|
uint4 f1,f2;
|
|
|
|
f1 = (a->getFlags() & (Varnode::input|Varnode::written));
|
|
f2 = (b->getFlags() & (Varnode::input|Varnode::written));
|
|
if (f1!=f2) return ((f1-1)<(f2-1));
|
|
// NOTE: The -1 forces free varnodes to come last
|
|
if (f1==Varnode::written) {
|
|
if (a->getDef()->getSeqNum() != b->getDef()->getSeqNum())
|
|
return (a->getDef()->getSeqNum() < b->getDef()->getSeqNum());
|
|
}
|
|
if (a->getAddr() != b->getAddr()) return (a->getAddr() < b->getAddr());
|
|
if (a->getSize() != b->getSize()) return (a->getSize() < b->getSize());
|
|
if (f1==0) // both are free
|
|
// return (a<b); // Compare pointers
|
|
return (a->getCreateIndex() < b->getCreateIndex());
|
|
return false;
|
|
}
|
|
|
|
/// During the course of analysis Varnodes are merged into high-level variables that are intended
|
|
/// to be closer to the concept of variables in C source code. For a large portion of the decompiler
|
|
/// analysis this concept hasn't been built yet, and this routine will return \b null.
|
|
/// But after a certain point, every Varnode managed by the Funcdata object, with the exception
|
|
/// of ones that are marked as \e annotations, is associated with some HighVariable
|
|
/// and will return a non-null result.
|
|
/// \return the associated HighVariable
|
|
HighVariable *Varnode::getHigh(void) const
|
|
|
|
{
|
|
if (high==(HighVariable *)0)
|
|
throw LowlevelError("Requesting non-existent high-level");
|
|
return high;
|
|
}
|
|
|
|
/// Return various values depending on the containment of another Varnode within \b this.
|
|
/// Return
|
|
/// - -1 if op.loc starts before -this-
|
|
/// - 0 if op is contained in -this-
|
|
/// - 1 if op.start is contained in -this-
|
|
/// - 2 if op.loc comes after -this- or
|
|
/// - 3 if op and -this- are in non-comparable spaces
|
|
/// \param op is the Varnode to test for containment
|
|
/// \return the integer containment code
|
|
int4 Varnode::contains(const Varnode &op) const
|
|
|
|
{
|
|
if (loc.getSpace() != op.loc.getSpace()) return 3;
|
|
if (loc.getSpace()->getType()==IPTR_CONSTANT) return 3;
|
|
uintb a = loc.getOffset();
|
|
uintb b = op.loc.getOffset();
|
|
if (b<a) return -1;
|
|
if (b>=a+size) return 2;
|
|
if (b+op.size > a+size) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/// Check whether the storage locations of two varnodes intersect
|
|
/// \param op is the Varnode to compare with \b this
|
|
/// \return \b true if the locations intersect
|
|
bool Varnode::intersects(const Varnode &op) const
|
|
|
|
{
|
|
if (loc.getSpace() != op.loc.getSpace()) return false;
|
|
if (loc.getSpace()->getType()==IPTR_CONSTANT) return false;
|
|
uintb a = loc.getOffset();
|
|
uintb b = op.loc.getOffset();
|
|
if (b<a) {
|
|
if (a>=b+op.size) return false;
|
|
return true;
|
|
}
|
|
if (b>=a+size) return false;
|
|
return true;
|
|
}
|
|
|
|
/// Check if \b this intersects the given Address range
|
|
/// \param op2loc is the start of the range
|
|
/// \param op2size is the size of the range in bytes
|
|
/// \return \b true if \b this intersects the range
|
|
bool Varnode::intersects(const Address &op2loc,int4 op2size) const
|
|
|
|
{
|
|
if (loc.getSpace() != op2loc.getSpace()) return false;
|
|
if (loc.getSpace()->getType()==IPTR_CONSTANT) return false;
|
|
uintb a = loc.getOffset();
|
|
uintb b = op2loc.getOffset();
|
|
if (b<a) {
|
|
if (a>=b+op2size) return false;
|
|
return true;
|
|
}
|
|
if (b>=a+size) return false;
|
|
return true;
|
|
}
|
|
|
|
int4 Varnode::characterizeOverlap(const Varnode &op) const
|
|
|
|
{
|
|
if (loc.getSpace() != op.loc.getSpace())
|
|
return 0;
|
|
if (loc.getOffset() == op.loc.getOffset()) // Left sides match
|
|
return (size == op.size) ? 2 : 1; // Either total match or partial
|
|
else if (loc.getOffset() < op.loc.getOffset()) {
|
|
uintb thisright = loc.getOffset() + (size-1);
|
|
return (thisright < op.loc.getOffset()) ? 0: 1; // Test if this ends before op begins
|
|
}
|
|
else {
|
|
uintb opright = op.loc.getOffset() + (op.size-1);
|
|
return (opright < loc.getOffset()) ? 0: 1; // Test if op ends before this begins
|
|
}
|
|
}
|
|
|
|
/// Return whether \e Least \e Signifigant \e Byte of \b this occurs in \b op
|
|
/// I.e. return
|
|
/// - 0 if it overlaps op's lsb
|
|
/// - 1 if it overlaps op's second lsb and so on
|
|
/// \param op is the Varnode to test for overlap
|
|
/// \return the relative overlap point or -1
|
|
int4 Varnode::overlap(const Varnode &op) const
|
|
|
|
{
|
|
if (!loc.isBigEndian()) // Little endian
|
|
return loc.overlap(0,op.loc,op.size);
|
|
else { // Big endian
|
|
int4 over = loc.overlap(size-1,op.loc,op.size);
|
|
if (over != -1)
|
|
return op.size-1-over;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// Return whether \e Least \e Signifigant \e Byte of \b this occurs in \b op.
|
|
/// If \b op is in the \e join space, \b this can be in one of the pieces associated with the \e join range, and
|
|
/// the offset returned will take into account the relative position of the piece within the whole \e join.
|
|
/// Otherwise, this method is equivalent to Varnode::overlap.
|
|
/// \param op is the Varnode to test for overlap
|
|
/// \return the relative overlap point or -1
|
|
int4 Varnode::overlapJoin(const Varnode &op) const
|
|
|
|
{
|
|
if (!loc.isBigEndian()) // Little endian
|
|
return loc.overlapJoin(0,op.loc,op.size);
|
|
else { // Big endian
|
|
int4 over = loc.overlapJoin(size-1,op.loc,op.size);
|
|
if (over != -1)
|
|
return op.size-1-over;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// Return whether \e Least \e Signifigant \e Byte of \b this occurs in an Address range
|
|
/// I.e. return
|
|
/// - 0 if it overlaps op's lsb
|
|
/// - 1 if it overlaps op's second lsb and so on
|
|
/// \param op2loc is the starting Address of the range
|
|
/// \param op2size is the size of the range in bytes
|
|
/// \return the relative overlap point or -1
|
|
int4 Varnode::overlap(const Address &op2loc,int4 op2size) const
|
|
|
|
{
|
|
if (!loc.isBigEndian()) // Little endian
|
|
return loc.overlap(0,op2loc,op2size);
|
|
else { // Big endian
|
|
int4 over = loc.overlap(size-1,op2loc,op2size);
|
|
if (over != -1)
|
|
return op2size-1-over;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// Rebuild variable cover based on where the Varnode
|
|
/// is defined and read. This is \e only called by the
|
|
/// Merge class which knows when to call it properly
|
|
void Varnode::updateCover(void) const
|
|
|
|
{
|
|
if ((flags & Varnode::coverdirty)!=0) {
|
|
if (hasCover()&&(cover!=(Cover *)0))
|
|
cover->rebuild(this);
|
|
clearFlags(Varnode::coverdirty);
|
|
}
|
|
}
|
|
|
|
/// Delete the Cover object. Used for dead Varnodes before full deletion.
|
|
void Varnode::clearCover(void) const
|
|
|
|
{
|
|
if (cover != (Cover *)0) {
|
|
delete cover;
|
|
cover = (Cover *)0;
|
|
}
|
|
}
|
|
|
|
/// Initialize a new Cover and set dirty bit so that updateCover will rebuild
|
|
void Varnode::calcCover(void) const
|
|
|
|
{
|
|
if (hasCover()) {
|
|
if (cover != (Cover *)0)
|
|
delete cover;
|
|
cover = new Cover;
|
|
setFlags(Varnode::coverdirty);
|
|
}
|
|
}
|
|
|
|
/// Print, to a stream, textual information about where \b this Varnode is in scope within its
|
|
/// particular Funcdata. This amounts to a list of address ranges bounding the writes and reads
|
|
/// of the Varnode
|
|
/// \param s is the output stream
|
|
void Varnode::printCover(ostream &s) const
|
|
|
|
{
|
|
if (cover == (Cover *)0)
|
|
throw LowlevelError("No cover to print");
|
|
if ((flags & Varnode::coverdirty)!=0)
|
|
s << "Cover is dirty" << endl;
|
|
else
|
|
cover->print(s);
|
|
}
|
|
|
|
/// Print boolean attribute information about \b this as keywords to a stream
|
|
/// \param s is the output stream
|
|
void Varnode::printInfo(ostream &s) const
|
|
|
|
{
|
|
type->printRaw(s);
|
|
s << " = ";
|
|
printRaw(s);
|
|
if (isAddrTied())
|
|
s << " tied";
|
|
if (isMapped())
|
|
s << " mapped";
|
|
if (isPersist())
|
|
s << " persistent";
|
|
if (isTypeLock())
|
|
s << " tlock";
|
|
if (isNameLock())
|
|
s << " nlock";
|
|
if (isSpacebase())
|
|
s << " base";
|
|
if (isUnaffected())
|
|
s << " unaff";
|
|
if (isImplied())
|
|
s << " implied";
|
|
if (isAddrForce())
|
|
s << " addrforce";
|
|
if (isReadOnly())
|
|
s << " readonly";
|
|
s << " (consumed=0x" << hex << consumed << ')';
|
|
s << " (internal=" << hex << this << ')';
|
|
s << " (create=0x" << hex << create_index << ')';
|
|
s << endl;
|
|
}
|
|
|
|
/// Erase the operation from our descendant list and set the cover dirty flag
|
|
/// \param op is the PcodeOp to remove
|
|
void Varnode::eraseDescend(PcodeOp *op)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter;
|
|
|
|
iter = descend.begin();
|
|
while (*iter != op) // Find this op in list of vn's descendants
|
|
iter++;
|
|
descend.erase(iter); // Remove it from list
|
|
setFlags(Varnode::coverdirty);
|
|
}
|
|
|
|
/// Put a new operator in the descendant list and set the cover dirty flag
|
|
/// \param op is PcodeOp to add
|
|
void Varnode::addDescend(PcodeOp *op)
|
|
|
|
{
|
|
// if (!heritageknown()) {
|
|
if (isFree()&&(!isSpacebase())) {
|
|
if (!descend.empty())
|
|
throw LowlevelError("Free varnode has multiple descendants");
|
|
}
|
|
descend.push_back(op);
|
|
setFlags(Varnode::coverdirty);
|
|
}
|
|
|
|
/// Completely clear the descendant list
|
|
/// Only called if Varnode is about to be irrevocably destroyed
|
|
void Varnode::destroyDescend(void)
|
|
|
|
{
|
|
descend.clear();
|
|
}
|
|
|
|
/// Set desired boolean attributes on this Varnode and then set dirty bits if appropriate
|
|
/// \param fl is the mask containing the list of attributes to set
|
|
void Varnode::setFlags(uint4 fl) const
|
|
|
|
{
|
|
flags |= fl;
|
|
if (high != (HighVariable *)0) {
|
|
high->flagsDirty();
|
|
if ((fl&Varnode::coverdirty)!=0)
|
|
high->coverDirty();
|
|
}
|
|
}
|
|
|
|
/// Clear desired boolean attributes on this Varnode and then set dirty bits if appropriate
|
|
/// \param fl is the mask containing the list of attributes to clear
|
|
void Varnode::clearFlags(uint4 fl) const
|
|
|
|
{
|
|
flags &= ~fl;
|
|
if (high != (HighVariable *)0) {
|
|
high->flagsDirty();
|
|
if ((fl&Varnode::coverdirty)!=0)
|
|
high->coverDirty();
|
|
}
|
|
}
|
|
|
|
/// For \b this Varnode and any others attached to the same HighVariable,
|
|
/// remove any SymbolEntry reference and associated properties.
|
|
void Varnode::clearSymbolLinks(void)
|
|
|
|
{
|
|
bool foundEntry = false;
|
|
for(int4 i=0;i<high->numInstances();++i) {
|
|
Varnode *vn = high->getInstance(i);
|
|
foundEntry = foundEntry || (vn->mapentry != (SymbolEntry *)0);
|
|
vn->mapentry = (SymbolEntry *)0;
|
|
vn->clearFlags(Varnode::namelock | Varnode::typelock | Varnode::mapped);
|
|
}
|
|
if (foundEntry)
|
|
high->symbolDirty();
|
|
}
|
|
|
|
/// Directly change the defining PcodeOp and set appropriate dirty bits
|
|
/// \param op is the pointer to the new PcodeOp, which can be \b null
|
|
void Varnode::setDef(PcodeOp *op)
|
|
|
|
{ // Set the defining op
|
|
def = op;
|
|
if (op==(PcodeOp *)0) {
|
|
setFlags(Varnode::coverdirty);
|
|
clearFlags(Varnode::written);
|
|
}
|
|
else
|
|
setFlags(Varnode::coverdirty|Varnode::written);
|
|
}
|
|
|
|
/// The given Symbol's data-type and flags are inherited by \b this Varnode.
|
|
/// If the Symbol is \e type-locked, a reference to the Symbol is set on \b this Varnode.
|
|
/// \param entry is a mapping to the given Symbol
|
|
/// \return \b true if any properties have changed
|
|
bool Varnode::setSymbolProperties(SymbolEntry *entry)
|
|
|
|
{
|
|
bool res = entry->updateType(this);
|
|
if (entry->getSymbol()->isTypeLocked()) {
|
|
if (mapentry != entry) {
|
|
mapentry = entry;
|
|
if (high != (HighVariable *)0)
|
|
high->setSymbol(this);
|
|
res = true;
|
|
}
|
|
}
|
|
setFlags(entry->getAllFlags() & ~Varnode::typelock);
|
|
return res;
|
|
}
|
|
|
|
/// A reference to the given Symbol is set on \b this Varnode.
|
|
/// The data-type on \b this Varnode is not changed.
|
|
/// \param entry is a mapping to the given Symbol
|
|
void Varnode::setSymbolEntry(SymbolEntry *entry)
|
|
|
|
{
|
|
mapentry = entry;
|
|
uint4 fl = Varnode::mapped; // Flags are generally not changed, but we do mark this as mapped
|
|
if (entry->getSymbol()->isNameLocked())
|
|
fl |= Varnode::namelock;
|
|
setFlags(fl);
|
|
if (high != (HighVariable *)0)
|
|
high->setSymbol(this);
|
|
}
|
|
|
|
/// Link Symbol information to \b this as a \b reference. This only works for a constant 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 entry is a mapping to the given Symbol
|
|
/// \param off is the byte offset into the Symbol of the reference
|
|
void Varnode::setSymbolReference(SymbolEntry *entry,int4 off)
|
|
|
|
{
|
|
if (high != (HighVariable *)0) {
|
|
high->setSymbolReference(entry->getSymbol(), off);
|
|
}
|
|
}
|
|
|
|
/// \param ct is the Datatype to change to
|
|
/// \return \b true if the Datatype changed
|
|
bool Varnode::updateType(Datatype *ct)
|
|
|
|
{
|
|
if (type == ct || isTypeLock()) return false;
|
|
type = ct;
|
|
if (high != (HighVariable *)0)
|
|
high->typeDirty();
|
|
return true;
|
|
}
|
|
|
|
/// Change the Datatype and lock state associated with this Varnode if various conditions are met
|
|
/// - Don't change a previously locked Datatype (unless \b override flag is \b true)
|
|
/// - Don't consider an \b undefined type to be locked
|
|
/// - Don't change to an identical Datatype
|
|
/// \param ct is the Datatype to change to
|
|
/// \param lock is \b true if the new Datatype should be locked
|
|
/// \param override is \b true if an old lock should be overridden
|
|
/// \return \b true if the Datatype or the lock setting was changed
|
|
bool Varnode::updateType(Datatype *ct,bool lock,bool override)
|
|
|
|
{
|
|
if (ct->getMetatype() == TYPE_UNKNOWN) // Unknown data type is ALWAYS unlocked
|
|
lock = false;
|
|
|
|
if (isTypeLock()&&(!override)) return false; // Type is locked
|
|
if ((type == ct)&&(isTypeLock()==lock)) return false; // No change
|
|
flags &= ~Varnode::typelock;
|
|
if (lock)
|
|
flags |= Varnode::typelock;
|
|
type = ct;
|
|
if (high != (HighVariable *)0)
|
|
high->typeDirty();
|
|
return true;
|
|
}
|
|
|
|
/// Copy any symbol and type information from -vn- into this
|
|
/// \param vn is the Varnode to copy from
|
|
void Varnode::copySymbol(const Varnode *vn)
|
|
|
|
{
|
|
type = vn->type; // Copy any type
|
|
mapentry = vn->mapentry; // Copy any symbol
|
|
flags &= ~(Varnode::typelock | Varnode::namelock);
|
|
flags |= (Varnode::typelock | Varnode::namelock) & vn->flags;
|
|
if (high != (HighVariable *)0) {
|
|
high->typeDirty();
|
|
if (mapentry != (SymbolEntry *)0)
|
|
high->setSymbol(this);
|
|
}
|
|
}
|
|
|
|
/// Symbol information (if present) is copied from the given constant Varnode into \b this,
|
|
/// which also must be constant, but only if the two constants are \e close in the sense of an equate.
|
|
/// \param vn is the given constant Varnode
|
|
void Varnode::copySymbolIfValid(const Varnode *vn)
|
|
|
|
{
|
|
SymbolEntry *mapEntry = vn->getSymbolEntry();
|
|
if (mapEntry == (SymbolEntry *)0)
|
|
return;
|
|
EquateSymbol *sym = dynamic_cast<EquateSymbol *>(mapEntry->getSymbol());
|
|
if (sym == (EquateSymbol *) 0)
|
|
return;
|
|
if (sym->isValueClose(loc.getOffset(), size)) {
|
|
copySymbol(vn); // Propagate the markup into our new constant
|
|
}
|
|
}
|
|
|
|
/// Compare two Varnodes
|
|
/// - First by storage location
|
|
/// - Second by size
|
|
/// - Then by defining PcodeOp SeqNum if appropriate
|
|
///
|
|
/// \e Input Varnodes come before \e written Varnodes
|
|
/// \e Free Varnodes come after everything else
|
|
/// \param op2 is the Varnode to compare \b this to
|
|
/// \return \b true if \b this is less than \b op2
|
|
bool Varnode::operator<(const Varnode &op2) const
|
|
|
|
{
|
|
uint4 f1,f2;
|
|
|
|
if (loc != op2.loc) return (loc < op2.loc);
|
|
if (size != op2.size) return (size < op2.size);
|
|
f1 = flags&(Varnode::input|Varnode::written);
|
|
f2 = op2.flags&(Varnode::input|Varnode::written);
|
|
if (f1!=f2) return ((f1-1)<(f2-1)); // -1 forces free varnodes to come last
|
|
if (f1==Varnode::written)
|
|
if (def->getSeqNum() != op2.def->getSeqNum())
|
|
return (def->getSeqNum() < op2.def->getSeqNum());
|
|
return false;
|
|
}
|
|
|
|
/// Determine if two Varnodes are equivalent. They must match
|
|
/// - Storage location
|
|
/// - Size
|
|
/// - Defining PcodeOp if it exists
|
|
///
|
|
/// \param op2 is the Varnode to compare \b this to
|
|
/// \return true if they are equivalent
|
|
bool Varnode::operator==(const Varnode &op2) const
|
|
|
|
{ // Compare two varnodes
|
|
uint4 f1,f2;
|
|
|
|
if (loc != op2.loc) return false;
|
|
if (size != op2.size) return false;
|
|
f1 = flags&(Varnode::input|Varnode::written);
|
|
f2 = op2.flags&(Varnode::input|Varnode::written);
|
|
if (f1!=f2) return false;
|
|
if (f1==Varnode::written)
|
|
if (def->getSeqNum() != op2.def->getSeqNum()) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// This is the constructor for making an unmanaged Varnode
|
|
/// It creates a \b free Varnode with possibly a Datatype attribute.
|
|
/// Most applications create Varnodes through the Funcdata interface
|
|
/// \param s is the size of the new Varnode
|
|
/// \param m is the starting storage Address
|
|
/// \param dt is the Datatype
|
|
Varnode::Varnode(int4 s,const Address &m,Datatype *dt)
|
|
: loc(m)
|
|
{ // Construct a varnode
|
|
size = s;
|
|
def = (PcodeOp *)0; // No defining op yet
|
|
type = dt;
|
|
high = (HighVariable *)0;
|
|
mapentry = (SymbolEntry *)0;
|
|
consumed = ~((uintb)0);
|
|
cover = (Cover *)0;
|
|
mergegroup = 0;
|
|
addlflags = 0;
|
|
if (m.getSpace() == (AddrSpace *)0) {
|
|
flags = 0;
|
|
return;
|
|
}
|
|
spacetype tp = m.getSpace()->getType();
|
|
if (tp==IPTR_CONSTANT) {
|
|
flags = Varnode::constant;
|
|
nzm = m.getOffset();
|
|
}
|
|
else if ((tp==IPTR_FSPEC)||(tp==IPTR_IOP)) {
|
|
flags = Varnode::annotation|Varnode::coverdirty;
|
|
nzm = ~((uintb)0);
|
|
}
|
|
else {
|
|
flags = Varnode::coverdirty;
|
|
nzm = ~((uintb)0);
|
|
}
|
|
}
|
|
|
|
/// Delete the Varnode object. This routine assumes all other cross-references have been removed.
|
|
Varnode::~Varnode(void)
|
|
|
|
{
|
|
if (cover != (Cover *)0)
|
|
delete cover;
|
|
if (high != (HighVariable *)0) {
|
|
high->remove(this);
|
|
if (high->isUnattached())
|
|
delete high;
|
|
}
|
|
}
|
|
|
|
/// This generally just returns the data-type of the Varnode itself unless it is a \e union data-type.
|
|
/// In this case, the data-type of the resolved field of the \e union, associated with writing to the Varnode,
|
|
/// is returned. The Varnode \b must be written to, to call this method.
|
|
/// \return the resolved data-type
|
|
Datatype *Varnode::getTypeDefFacing(void) const
|
|
|
|
{
|
|
if (!type->needsResolution())
|
|
return type;
|
|
return type->findResolve(def,-1);
|
|
}
|
|
|
|
/// This generally just returns the data-type of the Varnode itself unless it is a \e union data-type.
|
|
/// In this case, the data-type of the resolved field of the \e union, associated with reading the Varnode,
|
|
/// is returned.
|
|
/// \param op is the PcodeOp reading \b this Varnode
|
|
/// \return the resolved data-type
|
|
Datatype *Varnode::getTypeReadFacing(const PcodeOp *op) const
|
|
|
|
{
|
|
if (!type->needsResolution())
|
|
return type;
|
|
return type->findResolve(op, op->getSlot(this));
|
|
}
|
|
|
|
/// This generally just returns the data-type of the HighVariable associated with \b this, unless it is a
|
|
/// \e union data-type. In this case, the data-type of the resolved field of the \e union, associated with
|
|
/// writing to the Varnode, is returned.
|
|
/// \return the resolved data-type
|
|
Datatype *Varnode::getHighTypeDefFacing(void) const
|
|
|
|
{
|
|
Datatype *ct = high->getType();
|
|
if (!ct->needsResolution())
|
|
return ct;
|
|
return ct->findResolve(def,-1);
|
|
}
|
|
|
|
/// This generally just returns the data-type of the HighVariable associated with \b this, unless it is a
|
|
/// \e union data-type. In this case, the data-type of the resolved field of the \e union, associated with
|
|
/// reading the Varnode, is returned.
|
|
/// \param op is the PcodeOp reading \b this Varnode
|
|
/// \return the resolved data-type
|
|
Datatype *Varnode::getHighTypeReadFacing(const PcodeOp *op) const
|
|
|
|
{
|
|
Datatype *ct = high->getType();
|
|
if (!ct->needsResolution())
|
|
return ct;
|
|
return ct->findResolve(op, op->getSlot(this));
|
|
}
|
|
|
|
/// This is a convenience method for quickly finding the unique PcodeOp that reads this Varnode
|
|
/// \return only descendant (if there is 1 and ONLY 1) or \b null otherwise
|
|
PcodeOp *Varnode::loneDescend(void) const
|
|
|
|
{
|
|
PcodeOp *op;
|
|
|
|
if (descend.empty()) return (PcodeOp *)0; // No descendants
|
|
|
|
list<PcodeOp *>::const_iterator iter;
|
|
|
|
iter = descend.begin();
|
|
op = *iter++; // First descendant
|
|
if (iter != descend.end()) return (PcodeOp *)0; // More than 1 descendant
|
|
return op;
|
|
}
|
|
|
|
/// A Varnode can be defined as "coming into scope" at the Address of the first PcodeOp that
|
|
/// writes to that storage location. Within SSA form this \b first-use address always exists and
|
|
/// is unique if we consider inputs to come into scope at the start Address of the function they are in
|
|
/// \param fd is the Funcdata containing the tree
|
|
/// \return the first-use Address
|
|
Address Varnode::getUsePoint(const Funcdata &fd) const
|
|
|
|
{
|
|
if (isWritten())
|
|
return def->getAddr();
|
|
return fd.getAddress()+-1;
|
|
// return loc.getSpace()->getTrans()->constant(0);
|
|
}
|
|
|
|
/// Print to the stream either the name of the Varnode, such as a register name, if it exists
|
|
/// or print a shortcut character representing the AddrSpace and a hex representation of the offset.
|
|
/// This function also computes and returns the \e expected size of the identifier it prints
|
|
/// to facilitate the printing of size modifiers by other print routines
|
|
/// \param s is the output stream
|
|
/// \return the expected size
|
|
int4 Varnode::printRawNoMarkup(ostream &s) const
|
|
|
|
{
|
|
AddrSpace *spc = loc.getSpace();
|
|
const Translate *trans = spc->getTrans();
|
|
string name;
|
|
int4 expect;
|
|
|
|
name = trans->getRegisterName(spc,loc.getOffset(),size);
|
|
if (name.size()!=0) {
|
|
const VarnodeData &point(trans->getRegister(name));
|
|
uintb off = loc.getOffset()-point.offset;
|
|
s << name;
|
|
expect = point.size;
|
|
if (off != 0)
|
|
s << '+' << dec << off;
|
|
}
|
|
else {
|
|
s << loc.getShortcut(); // Print type shortcut character
|
|
expect = trans->getDefaultSize();
|
|
loc.printRaw(s);
|
|
}
|
|
return expect;
|
|
}
|
|
|
|
/// Print textual information about this Varnode including a base identifier along with enough
|
|
/// size and attribute information to uniquely identify the Varnode within a text SSA listing
|
|
/// In particular, the identifiers have either "i" or defining op SeqNum information appended
|
|
/// to them in parantheses.
|
|
/// \param s is the output stream
|
|
void Varnode::printRaw(ostream &s) const
|
|
|
|
{
|
|
int4 expect = printRawNoMarkup(s);
|
|
|
|
if (expect != size)
|
|
s << ':' << setw(1) << size;
|
|
if ((flags&Varnode::input)!=0)
|
|
s << "(i)";
|
|
if (isWritten())
|
|
s << '(' << def->getSeqNum() << ')';
|
|
if ((flags&(Varnode::insert|Varnode::constant))==0) {
|
|
s << "(free)";
|
|
return;
|
|
}
|
|
}
|
|
|
|
/// Recursively print a terse textual representation of the data-flow (SSA) tree rooted at this Varnode
|
|
/// \param s is the output stream
|
|
/// \param depth is the current depth of the tree we are at
|
|
void Varnode::printRawHeritage(ostream &s,int4 depth) const
|
|
|
|
{
|
|
for(int4 i=0;i<depth;++i)
|
|
s << ' ';
|
|
|
|
if (isConstant()) {
|
|
printRaw(s);
|
|
s << endl;
|
|
return;
|
|
}
|
|
printRaw(s);
|
|
s << ' ';
|
|
if (def != (PcodeOp *)0)
|
|
def->printRaw(s);
|
|
else
|
|
printRaw(s);
|
|
|
|
if ((flags & Varnode::input)!=0)
|
|
s << " Input";
|
|
if ((flags & Varnode::constant)!=0)
|
|
s << " Constant";
|
|
if ((flags & Varnode::annotation)!=0)
|
|
s << " Code";
|
|
|
|
if (def != (PcodeOp *)0) {
|
|
s << "\t\t" << def->getSeqNum() << endl;
|
|
for(int4 i=0;i<def->numInput();++i)
|
|
def->getIn(i)->printRawHeritage(s,depth+5);
|
|
}
|
|
else
|
|
s << endl;
|
|
}
|
|
|
|
/// If \b this is a constant, or is extended (INT_ZEXT,INT_SEXT,PIECE) from a constant,
|
|
/// the \e value of the constant (currently up to 128 bits) is passed back and \b true is returned.
|
|
/// \param val will hold the 128-bit constant value
|
|
/// \return \b true if a constant was recovered
|
|
bool Varnode::isConstantExtended(uint8 *val) const
|
|
|
|
{
|
|
if (isConstant()) {
|
|
val[0] = getOffset();
|
|
val[1] = 0;
|
|
return true;
|
|
}
|
|
if (!isWritten() || size <= 8) return false;
|
|
if (size > 16) return false; // Currently only up to 128-bit values
|
|
OpCode opc = def->code();
|
|
if (opc == CPUI_INT_ZEXT) {
|
|
Varnode *vn0 = def->getIn(0);
|
|
if (vn0->isConstant()) {
|
|
val[0] = vn0->getOffset();
|
|
val[1] = 0;
|
|
return true;
|
|
}
|
|
}
|
|
else if (opc == CPUI_INT_SEXT) {
|
|
Varnode *vn0 = def->getIn(0);
|
|
if (vn0->isConstant()) {
|
|
val[0] = vn0->getOffset();
|
|
if (vn0->getSize() < 8)
|
|
val[0] = sign_extend(val[0], vn0->getSize(), size);
|
|
val[1] = (signbit_negative(val[0], 8)) ? 0xffffffffffffffffL : 0;
|
|
return true;
|
|
}
|
|
}
|
|
else if (opc == CPUI_PIECE) {
|
|
Varnode *vnlo = def->getIn(1);
|
|
if (vnlo->isConstant()) {
|
|
val[0] = vnlo->getOffset();
|
|
Varnode *vnhi = def->getIn(0);
|
|
if (vnhi->isConstant()) {
|
|
val[1] = vnhi->getOffset();
|
|
if (vnlo->getSize() == 8)
|
|
return true;
|
|
val[0] |= val[1] << 8*vnlo->getSize();
|
|
val[1] >>= 8*(8-vnlo->getSize());
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Recursively check if the Varnode is either:
|
|
/// - Copied or extended from a constant
|
|
/// - The result of arithmetic or logical operations on constants
|
|
/// - Loaded from a pointer that is a constant
|
|
///
|
|
/// \param maxBinary is the maximum depth of binary operations to inspect (before giving up)
|
|
/// \param maxLoad is the maximum number of CPUI_LOAD operations to allow in a sequence
|
|
/// \return \b true if the Varnode (might) collapse to a constant
|
|
bool Varnode::isEventualConstant(int4 maxBinary,int4 maxLoad) const
|
|
|
|
{
|
|
const Varnode *curVn = this;
|
|
while(!curVn->isConstant()) {
|
|
if (!curVn->isWritten()) return false;
|
|
const PcodeOp *op = curVn->getDef();
|
|
switch(op->code()) {
|
|
case CPUI_LOAD:
|
|
if (maxLoad == 0) return false;
|
|
maxLoad -= 1;
|
|
curVn = op->getIn(1);
|
|
break;
|
|
case CPUI_INT_ADD:
|
|
case CPUI_INT_SUB:
|
|
case CPUI_INT_XOR:
|
|
case CPUI_INT_OR:
|
|
case CPUI_INT_AND:
|
|
if (maxBinary == 0) return false;
|
|
if (!op->getIn(0)->isEventualConstant(maxBinary-1,maxLoad))
|
|
return false;
|
|
return op->getIn(1)->isEventualConstant(maxBinary-1,maxLoad);
|
|
case CPUI_INT_ZEXT:
|
|
case CPUI_INT_SEXT:
|
|
case CPUI_COPY:
|
|
curVn = op->getIn(0);
|
|
break;
|
|
case CPUI_INT_LEFT:
|
|
case CPUI_INT_RIGHT:
|
|
case CPUI_INT_SRIGHT:
|
|
case CPUI_INT_MULT:
|
|
if (!op->getIn(1)->isConstant()) return false;
|
|
curVn = op->getIn(0);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Make an initial determination of the Datatype of this Varnode. If a Datatype is already
|
|
/// set and locked return it. Otherwise look through all the read PcodeOps and the write PcodeOp
|
|
/// to determine if the Varnode is getting used as an \b int, \b float, or \b pointer, etc.
|
|
/// Throw an exception if no Datatype can be found at all.
|
|
/// \return the determined Datatype
|
|
Datatype *Varnode::getLocalType(bool &blockup) const
|
|
|
|
{
|
|
Datatype *ct;
|
|
Datatype *newct;
|
|
|
|
if (isTypeLock()) // Our type is locked, don't change
|
|
return type; // Not a partial lock, return the locked type
|
|
|
|
ct = (Datatype *)0;
|
|
if (def != (PcodeOp *)0) {
|
|
ct = def->outputTypeLocal();
|
|
if (def->stopsTypePropagation()) {
|
|
blockup = true;
|
|
return ct;
|
|
}
|
|
}
|
|
|
|
list<PcodeOp *>::const_iterator iter;
|
|
PcodeOp *op;
|
|
int4 i;
|
|
for(iter=descend.begin();iter!=descend.end();++iter) {
|
|
op = *iter;
|
|
i = op->getSlot(this);
|
|
newct = op->inputTypeLocal(i);
|
|
|
|
if (ct == (Datatype *)0)
|
|
ct = newct;
|
|
else {
|
|
if (0>newct->typeOrder(*ct))
|
|
ct = newct;
|
|
}
|
|
}
|
|
if (ct == (Datatype *)0)
|
|
throw LowlevelError("NULL local type");
|
|
return ct;
|
|
}
|
|
|
|
/// If \b this varnode is produced by an operation with a boolean output, or if it is
|
|
/// formally marked with a boolean data-type, return \b true. The parameter \b trustAnnotation
|
|
/// toggles whether or not the formal data-type is trusted.
|
|
/// \return \b true if \b this is a formal boolean, \b false otherwise
|
|
bool Varnode::isBooleanValue(bool useAnnotation) const
|
|
|
|
{
|
|
if (isWritten()) return def->isCalculatedBool();
|
|
if (!useAnnotation)
|
|
return false;
|
|
if ((flags & (input | typelock)) == (input | typelock)) {
|
|
if (size == 1 && type->getMetatype() == TYPE_BOOL)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// If we can prove that the upper bits of \b this are zero, return \b true.
|
|
/// \param baseSize is the maximum number of least significant bytes that are allowed to be non-zero
|
|
/// \return \b true if all the most significant bytes are zero
|
|
bool Varnode::isZeroExtended(int4 baseSize) const
|
|
|
|
{
|
|
if (baseSize >= size) return false;
|
|
if (size > sizeof(uintb)) {
|
|
if (!isWritten()) return false;
|
|
if (def->code() != CPUI_INT_ZEXT) return false;
|
|
if (def->getIn(0)->getSize() > baseSize) return false;
|
|
return true;
|
|
}
|
|
uintb mask = nzm >> 8*baseSize;
|
|
return (mask == 0);
|
|
}
|
|
|
|
/// Make a local determination if \b this and \b op2 hold the same value. We check if
|
|
/// there is a common ancester for which both \b this and \b op2 are created from a direct
|
|
/// sequence of COPY operations. NOTE: This is a transitive relationship
|
|
/// \param op2 is the Varnode to compare to \b this
|
|
/// \return \b true if the Varnodes are copied from a common ancestor
|
|
bool Varnode::copyShadow(const Varnode *op2) const
|
|
|
|
{
|
|
const Varnode *vn;
|
|
|
|
if (this==op2) return true;
|
|
// Trace -this- to the source of the copy chain
|
|
vn = this;
|
|
while( (vn->isWritten()) && (vn->getDef()->code() == CPUI_COPY)) {
|
|
vn = vn->getDef()->getIn(0);
|
|
if (vn == op2) return true; // If we hit op2 then this and op2 must be the same
|
|
}
|
|
// Trace op2 to the source of copy chain
|
|
while( (op2->isWritten()) && (op2->getDef()->code() == CPUI_COPY)) {
|
|
op2 = op2->getDef()->getIn(0);
|
|
if (vn == op2) return true; // If the source is the same then this and op2 are same
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// \brief Try to find a SUBPIECE operation producing the value in \b this from the given \b whole Varnode
|
|
///
|
|
/// The amount of truncation producing \b this must be known apriori. Allow for COPY and MULTIEQUAL operations
|
|
/// in the flow path from \b whole to \b this. This method will search recursively through branches
|
|
/// of MULTIEQUAL up to a maximum depth.
|
|
/// \param leastByte is the number of least significant bytes being truncated from \b whole to get \b this
|
|
/// \param whole is the given whole Varnode
|
|
/// \param recurse is the current depth of recursion
|
|
/// \return \b true if \b this and \b whole have the prescribed SUBPIECE relationship
|
|
bool Varnode::findSubpieceShadow(int4 leastByte,const Varnode *whole,int4 recurse) const
|
|
|
|
{
|
|
const Varnode *vn = this;
|
|
while( vn->isWritten() && vn->getDef()->code() == CPUI_COPY)
|
|
vn = vn->getDef()->getIn(0);
|
|
if (!vn->isWritten()) {
|
|
if (vn->isConstant()) {
|
|
while( whole->isWritten() && whole->getDef()->code() == CPUI_COPY)
|
|
whole = whole->getDef()->getIn(0);
|
|
if (!whole->isConstant()) return false;
|
|
uintb off = whole->getOffset() >> leastByte*8;
|
|
off &= calc_mask(vn->getSize());
|
|
return (off == vn->getOffset());
|
|
}
|
|
return false;
|
|
}
|
|
OpCode opc = vn->getDef()->code();
|
|
if (opc == CPUI_SUBPIECE) {
|
|
const Varnode *tmpvn = vn->getDef()->getIn(0);
|
|
int4 off = (int4)vn->getDef()->getIn(1)->getOffset();
|
|
if (off != leastByte || tmpvn->getSize() != whole->getSize())
|
|
return false;
|
|
if (tmpvn == whole) return true;
|
|
while(tmpvn->isWritten() && tmpvn->getDef()->code() == CPUI_COPY) {
|
|
tmpvn = tmpvn->getDef()->getIn(0);
|
|
if (tmpvn == whole) return true;
|
|
}
|
|
}
|
|
else if (opc == CPUI_MULTIEQUAL) {
|
|
recurse += 1;
|
|
if (recurse > 1) return false; // Truncate the recursion at maximum depth
|
|
while( whole->isWritten() && whole->getDef()->code() == CPUI_COPY)
|
|
whole = whole->getDef()->getIn(0);
|
|
if (!whole->isWritten()) return false;
|
|
const PcodeOp *bigOp = whole->getDef();
|
|
if (bigOp->code() != CPUI_MULTIEQUAL) return false;
|
|
const PcodeOp *smallOp = vn->getDef();
|
|
if (bigOp->getParent() != smallOp->getParent()) return false;
|
|
// Recurse search through all branches of the two MULTIEQUALs
|
|
for(int4 i=0;i<smallOp->numInput();++i) {
|
|
if (!smallOp->getIn(i)->findSubpieceShadow(leastByte, bigOp->getIn(i), recurse))
|
|
return false;
|
|
}
|
|
return true; // All branches were copy shadows
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// \brief Try to find a PIECE operation that produces \b this from a given Varnode \b piece
|
|
///
|
|
/// \param leastByte is the number of least significant bytes being truncated from the
|
|
/// putative \b this to get \b piece. The routine can backtrack through COPY operations and
|
|
/// more than one PIECE operations to verify that \b this is formed out of \b piece.
|
|
/// \param piece is the given Varnode piece
|
|
/// \return \b true if \b this and \b whole have the prescribed PIECE relationship
|
|
bool Varnode::findPieceShadow(int4 leastByte,const Varnode *piece) const
|
|
|
|
{
|
|
const Varnode *vn = this;
|
|
while( vn->isWritten() && vn->getDef()->code() == CPUI_COPY)
|
|
vn = vn->getDef()->getIn(0);
|
|
if (!vn->isWritten()) return false;
|
|
OpCode opc = vn->getDef()->code();
|
|
if (opc == CPUI_PIECE) {
|
|
const Varnode *tmpvn = vn->getDef()->getIn(1); // Least significant part
|
|
if (leastByte >= tmpvn->getSize()) {
|
|
leastByte -= tmpvn->getSize();
|
|
tmpvn = vn->getDef()->getIn(0);
|
|
}
|
|
else {
|
|
if (piece->getSize() + leastByte > tmpvn->getSize()) return false;
|
|
}
|
|
if (leastByte == 0 && tmpvn->getSize() == piece->getSize()) {
|
|
if (tmpvn == piece) return true;
|
|
while(tmpvn->isWritten() && tmpvn->getDef()->code() == CPUI_COPY) {
|
|
tmpvn = tmpvn->getDef()->getIn(0);
|
|
if (tmpvn == piece) return true;
|
|
}
|
|
return false;
|
|
}
|
|
// CPUI_PIECE input is too big, recursively search for another CPUI_PIECE
|
|
return tmpvn->findPieceShadow(leastByte, piece);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// For \b this and another Varnode, establish that either:
|
|
/// - bigger = CONCAT(smaller,..) or
|
|
/// - smaller = SUBPIECE(bigger)
|
|
///
|
|
/// Check through COPY chains and verify that the form of the CONCAT or SUBPIECE matches
|
|
/// a given relative offset between the Varnodes.
|
|
/// \param op2 is the Varnode to compare to \b this
|
|
/// \param relOff is the putative relative byte offset of \b this to \b op2
|
|
/// \return \b true if one Varnode is contained, as a value, in the other
|
|
bool Varnode::partialCopyShadow(const Varnode *op2,int4 relOff) const
|
|
|
|
{
|
|
const Varnode *vn;
|
|
|
|
if (size < op2->size) {
|
|
vn = this;
|
|
}
|
|
else if (size > op2->size) {
|
|
vn = op2;
|
|
op2 = this;
|
|
relOff = -relOff;
|
|
}
|
|
else
|
|
return false;
|
|
if (relOff < 0)
|
|
return false; // Not proper containment
|
|
if (relOff + vn->getSize() > op2->getSize())
|
|
return false; // Not proper containment
|
|
|
|
bool bigEndian = getSpace()->isBigEndian();
|
|
int4 leastByte = bigEndian ? (op2->getSize() - vn->getSize()) - relOff : relOff;
|
|
if (vn->findSubpieceShadow(leastByte, op2, 0))
|
|
return true;
|
|
|
|
if (op2->findPieceShadow(leastByte, vn))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// If \b this has a data-type built out of separate pieces, return it.
|
|
/// If \b this is mapped as a partial to a symbol with one of these data-types, return it.
|
|
/// Return null otherwise.
|
|
/// \return the associated structured data-type or null
|
|
Datatype *Varnode::getStructuredType(void) const
|
|
|
|
{
|
|
Datatype *ct;
|
|
if (mapentry != (SymbolEntry *)0)
|
|
ct = mapentry->getSymbol()->getType();
|
|
else
|
|
ct = type;
|
|
if (ct->isPieceStructured())
|
|
return ct;
|
|
return (Datatype *)0;
|
|
}
|
|
|
|
/// Compare term order of two Varnodes. Used in Term Rewriting strategies to order operands of commutative ops
|
|
/// \param op is the Varnode to order against \b this
|
|
/// \return -1 if \b this comes before \b op, 1 if op before this, or 0
|
|
int4 Varnode::termOrder(const Varnode *op) const
|
|
|
|
{
|
|
if (isConstant()) {
|
|
if (!op->isConstant()) return 1;
|
|
}
|
|
else {
|
|
if (op->isConstant()) return -1;
|
|
const Varnode *vn = this;
|
|
if (vn->isWritten()&&(vn->getDef()->code() == CPUI_INT_MULT))
|
|
if (vn->getDef()->getIn(1)->isConstant())
|
|
vn = vn->getDef()->getIn(0);
|
|
if (op->isWritten()&&(op->getDef()->code() == CPUI_INT_MULT))
|
|
if (op->getDef()->getIn(1)->isConstant())
|
|
op = op->getDef()->getIn(0);
|
|
|
|
if (vn->getAddr() < op->getAddr()) return -1;
|
|
if (op->getAddr() < vn->getAddr()) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// Encode \b this as an \<addr> element, with at least the following attributes:
|
|
/// - \b space describes the AddrSpace
|
|
/// - \b offset of the Varnode within the space
|
|
/// - \b size of the Varnode is bytes
|
|
///
|
|
/// Additionally the element will contain other optional attributes.
|
|
/// \param encoder is the stream encoder
|
|
void Varnode::encode(Encoder &encoder) const
|
|
|
|
{
|
|
encoder.openElement(ELEM_ADDR);
|
|
loc.getSpace()->encodeAttributes(encoder,loc.getOffset(),size);
|
|
encoder.writeUnsignedInteger(ATTRIB_REF, getCreateIndex());
|
|
if (mergegroup != 0)
|
|
encoder.writeSignedInteger(ATTRIB_GRP, getMergeGroup());
|
|
if (isPersist())
|
|
encoder.writeBool(ATTRIB_PERSISTS, true);
|
|
if (isAddrTied())
|
|
encoder.writeBool(ATTRIB_ADDRTIED, true);
|
|
if (isUnaffected())
|
|
encoder.writeBool(ATTRIB_UNAFF, true);
|
|
if (isInput())
|
|
encoder.writeBool(ATTRIB_INPUT, true);
|
|
if (isVolatile())
|
|
encoder.writeBool(ATTRIB_VOLATILE, true);
|
|
encoder.closeElement(ELEM_ADDR);
|
|
}
|
|
|
|
/// Invoke the printRaw method on the given Varnode pointer, but take into account that it
|
|
/// might be null.
|
|
/// \param s is the output stream to write to
|
|
/// \param vn is the given Varnode pointer (may be null)
|
|
void Varnode::printRaw(ostream &s,const Varnode *vn)
|
|
|
|
{
|
|
if (vn == (const Varnode *)0) {
|
|
s << "<null>";
|
|
return;
|
|
}
|
|
vn->printRaw(s);
|
|
}
|
|
|
|
/// \param m is the underlying address space manager
|
|
VarnodeBank::VarnodeBank(AddrSpaceManager *m)
|
|
: searchvn(0,Address(Address::m_minimal),(Datatype *)0)
|
|
|
|
{
|
|
manage = m;
|
|
searchvn.flags = Varnode::input; // searchvn is always an input varnode of size 0
|
|
uniq_space = m->getUniqueSpace();
|
|
uniqbase = uniq_space->getTrans()->getUniqueStart(Translate::ANALYSIS);
|
|
uniqid = uniqbase;
|
|
create_index = 0;
|
|
}
|
|
|
|
void VarnodeBank::clear(void)
|
|
|
|
{
|
|
VarnodeLocSet::iterator iter;
|
|
|
|
for(iter=loc_tree.begin();iter!=loc_tree.end();++iter)
|
|
delete *iter;
|
|
|
|
loc_tree.clear();
|
|
def_tree.clear();
|
|
uniqid = uniqbase; // Reset counter to base value
|
|
create_index = 0; // Reset varnode creation index
|
|
}
|
|
|
|
/// The Varnode is created and inserted into the maps as \e free: not
|
|
/// defined as the output of a p-code op or the input to a function.
|
|
/// \param s is the size of the Varnode in bytes
|
|
/// \param m is the starting address
|
|
/// \param ct is the data-type of the new varnode (must not be NULL)
|
|
/// \return the newly allocated Varnode object
|
|
Varnode *VarnodeBank::create(int4 s,const Address &m,Datatype *ct)
|
|
|
|
{
|
|
Varnode *vn = new Varnode(s,m,ct);
|
|
|
|
vn->create_index = create_index++;
|
|
vn->lociter = loc_tree.insert(vn).first; // Frees can always be inserted without duplication
|
|
vn->defiter = def_tree.insert(vn).first;
|
|
return vn;
|
|
}
|
|
|
|
/// The Varnode is allocated in the \e unique space and automatically
|
|
/// assigned an offset. The Varnode is initially \e free.
|
|
/// \param s is the size of the Varnode in bytes
|
|
/// \param ct is the data-type to assign (must not be NULL)
|
|
Varnode *VarnodeBank::createUnique(int4 s,Datatype *ct)
|
|
|
|
{
|
|
Address addr(uniq_space,uniqid); // Generate a unique address
|
|
uniqid += s; // Update counter for next call
|
|
return create(s,addr,ct); // Build varnode with our generated address
|
|
}
|
|
|
|
/// The Varnode object is removed from the sorted lists and
|
|
/// its memory reclaimed
|
|
/// \param vn is the Varnode to remove
|
|
void VarnodeBank::destroy(Varnode *vn)
|
|
|
|
{
|
|
if ((vn->getDef() != (PcodeOp *)0)||(!vn->hasNoDescend()))
|
|
throw LowlevelError("Deleting integrated varnode");
|
|
|
|
loc_tree.erase(vn->lociter);
|
|
def_tree.erase(vn->defiter);
|
|
delete vn;
|
|
}
|
|
|
|
/// Enter the Varnode into both the \e location and \e definition based trees.
|
|
/// Update the Varnode iterators and flags
|
|
/// \param vn is the Varnode object to insert
|
|
/// \return the inserted object, which may not be the same as the input Varnode
|
|
Varnode *VarnodeBank::xref(Varnode *vn)
|
|
|
|
{
|
|
pair<VarnodeLocSet::iterator,bool> check;
|
|
Varnode *othervn;
|
|
|
|
check = loc_tree.insert( vn );
|
|
if (!check.second) { // Set already contains this varnode
|
|
othervn = *(check.first);
|
|
replace(vn,othervn); // Patch ops using the old varnode
|
|
delete vn;
|
|
return othervn;
|
|
}
|
|
// Otherwise a new insertion
|
|
vn->lociter = check.first;
|
|
vn->setFlags(Varnode::insert);
|
|
vn->defiter = def_tree.insert(vn).first; // Insertion should also be new in def_tree
|
|
|
|
return vn;
|
|
}
|
|
|
|
/// The Varnode is removed from the cross-referencing lists and reinserted as
|
|
/// as if it were not defined by any PcodeOp and not an input to the function.
|
|
/// If the Varnode was originally a PcodeOp output, this must be explicitly cleared.
|
|
/// \param vn is the Varnode to modify
|
|
void VarnodeBank::makeFree(Varnode *vn)
|
|
|
|
{
|
|
loc_tree.erase(vn->lociter);
|
|
def_tree.erase(vn->defiter);
|
|
|
|
vn->setDef((PcodeOp *)0); // Clear things that make vn non-free
|
|
vn->clearFlags(Varnode::insert|Varnode::input|Varnode::indirect_creation);
|
|
|
|
vn->lociter = loc_tree.insert(vn).first; // Re-insert as free varnode
|
|
vn->defiter = def_tree.insert(vn).first;
|
|
}
|
|
|
|
/// Any PcodeOps that read \b oldvn are changed to read \b newvn
|
|
/// \param oldvn is the old Varnode
|
|
/// \param newvn is the Varnode to replace it with
|
|
void VarnodeBank::replace(Varnode *oldvn,Varnode *newvn)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter,tmpiter;
|
|
PcodeOp *op;
|
|
int4 i;
|
|
|
|
iter = oldvn->descend.begin();
|
|
while(iter!=oldvn->descend.end()) {
|
|
op = *iter;
|
|
tmpiter = iter++;
|
|
if (op->output == newvn) continue; // Cannot be input to your own definition
|
|
i = op->getSlot(oldvn);
|
|
oldvn->descend.erase(tmpiter); // Sever the link fully
|
|
op->clearInput(i); // Before attempting to build the new link
|
|
newvn->addDescend(op);
|
|
op->setInput(newvn,i); // This must be called AFTER descend is updated
|
|
}
|
|
oldvn->setFlags(Varnode::coverdirty);
|
|
newvn->setFlags(Varnode::coverdirty);
|
|
}
|
|
|
|
/// Define the Varnode as an input formally; it is no longer considered \e free.
|
|
/// Its position in the cross-referencing lists will change
|
|
/// \param vn is the Varnode to mark
|
|
/// \return the modified Varnode, which be a different object than the original
|
|
Varnode *VarnodeBank::setInput(Varnode *vn)
|
|
|
|
{
|
|
if (!vn->isFree())
|
|
throw LowlevelError("Making input out of varnode which is not free");
|
|
if (vn->isConstant())
|
|
throw LowlevelError("Making input out of constant varnode");
|
|
|
|
loc_tree.erase(vn->lociter); // Erase the free version of varnode
|
|
def_tree.erase(vn->defiter);
|
|
|
|
vn->setInput(); // Set the input flag
|
|
return xref(vn);
|
|
}
|
|
|
|
/// The Varnode must initially be \e free. It will be removed
|
|
/// from the cross-referencing lists and reinserted as if its were
|
|
/// the output of the given PcodeOp. It still must be explicitly set
|
|
/// as the output.
|
|
/// \param vn is the Varnode to modify
|
|
/// \param op is the given PcodeOp
|
|
/// \return the modified Varnode, which may be a different object than the original
|
|
Varnode *VarnodeBank::setDef(Varnode *vn,PcodeOp *op)
|
|
|
|
{
|
|
if (!vn->isFree()) {
|
|
ostringstream s;
|
|
const Address &addr(op->getAddr());
|
|
s << "Defining varnode which is not free at " << addr.getShortcut();
|
|
addr.printRaw(s);
|
|
throw LowlevelError(s.str());
|
|
}
|
|
if (vn->isConstant()) {
|
|
ostringstream s;
|
|
const Address &addr(op->getAddr());
|
|
s << "Assignment to constant at " << addr.getShortcut();
|
|
addr.printRaw(s);
|
|
throw LowlevelError(s.str());
|
|
}
|
|
|
|
loc_tree.erase(vn->lociter);
|
|
def_tree.erase(vn->defiter);
|
|
|
|
vn->setDef(op); // Change the varnode to be defined
|
|
return xref(vn);
|
|
}
|
|
|
|
/// The new Varnode object will already be put in the \e definition list as if
|
|
/// it were the output of the given PcodeOp. The Varnode must still be set as the output.
|
|
/// \param s is the size in bytes
|
|
/// \param m is the starting address
|
|
/// \param ct is the data-type to associate
|
|
/// \param op is the given PcodeOp
|
|
Varnode *VarnodeBank::createDef(int4 s,const Address &m, Datatype *ct,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *vn = new Varnode(s,m,ct);
|
|
vn->create_index = create_index++;
|
|
vn->setDef(op);
|
|
return xref(vn);
|
|
}
|
|
|
|
/// The new Varnode will be assigned from the \e unique space, and
|
|
/// it will already be put in the \e definition list as if
|
|
/// it were the output of the given PcodeOp. The Varnode must still be set as the output.
|
|
/// \param s is the size in bytes
|
|
/// \param ct is the data-type to associate
|
|
/// \param op is the given PcodeOp
|
|
Varnode *VarnodeBank::createDefUnique(int4 s,Datatype *ct,PcodeOp *op)
|
|
|
|
{ // Create unique varnode as output of op
|
|
Address addr(uniq_space,uniqid);
|
|
uniqid += s;
|
|
return createDef(s,addr,ct,op);
|
|
}
|
|
|
|
/// Find a Varnode given its (loc,size) and the address where it is defined.
|
|
/// \param s is the size of the Varnode
|
|
/// \param loc is its starting address
|
|
/// \param pc is the address where it is defined
|
|
/// \param uniq is the sequence number or -1 if not specified
|
|
/// \return the matching Varnode or NULL
|
|
Varnode *VarnodeBank::find(int4 s,const Address &loc,const Address &pc,uintm uniq) const
|
|
|
|
{
|
|
VarnodeLocSet::const_iterator iter;
|
|
Varnode *vn;
|
|
PcodeOp *op;
|
|
|
|
iter = beginLoc(s,loc,pc,uniq);
|
|
while(iter != loc_tree.end()) {
|
|
vn = *iter;
|
|
if (vn->getSize() != s) break;
|
|
if (vn->getAddr() != loc) break;
|
|
op = vn->getDef();
|
|
if ((op!=(PcodeOp *)0)&&(op->getAddr() == pc)) {
|
|
if ((uniq==~((uintm)0))||(op->getTime()==uniq)) return vn;
|
|
}
|
|
++iter;
|
|
}
|
|
return (Varnode *)0;
|
|
}
|
|
|
|
/// Find a Varnode marked as a function input given its size and address
|
|
/// \param s is the size
|
|
/// \param loc is the starting address
|
|
/// \return the match Varnode object or NULL
|
|
Varnode *VarnodeBank::findInput(int4 s,const Address &loc) const
|
|
|
|
{
|
|
VarnodeLocSet::const_iterator iter;
|
|
Varnode *vn;
|
|
|
|
iter = beginLoc(s,loc,Varnode::input);
|
|
if (iter != loc_tree.end()) { // There is only one possible varnode matching this
|
|
vn = *iter;
|
|
if (vn->isInput() && (vn->getSize()==s) && (vn->getAddr()==loc))
|
|
return vn;
|
|
}
|
|
return (Varnode *)0;
|
|
}
|
|
|
|
/// Find the first Varnode completely contained within the given range, which is
|
|
/// also marked as a function input.
|
|
/// \param s is the size of the range
|
|
/// \param loc is the starting address of the range
|
|
/// \return the Varnode object or NULL if no Varnode met the conditions
|
|
Varnode *VarnodeBank::findCoveredInput(int4 s,const Address &loc) const
|
|
|
|
{
|
|
VarnodeDefSet::const_iterator iter,enditer;
|
|
Varnode *vn;
|
|
uintb highest = loc.getSpace()->getHighest();
|
|
uintb end = loc.getOffset() + s - 1;
|
|
|
|
iter = beginDef(Varnode::input,loc);
|
|
if (end==highest) { // Check for wrap around of address
|
|
Address tmp(loc.getSpace(),highest);
|
|
enditer = endDef(Varnode::input,tmp);
|
|
}
|
|
else
|
|
enditer = beginDef(Varnode::input,loc+s);
|
|
|
|
while(iter!=enditer) {
|
|
vn = *iter++; // we know vn is input with vn->Loc in (loc,loc+s)
|
|
if (vn->getOffset()+vn->getSize()-1 <= end) // vn is completely contained
|
|
return vn;
|
|
}
|
|
return (Varnode *)0;
|
|
}
|
|
|
|
/// Search for the Varnode that completely contains the given range and is marked
|
|
/// as an input to the function. If it exists, it is unique.
|
|
/// \param s is the size of the range
|
|
/// \param loc is the starting address of the range
|
|
Varnode *VarnodeBank::findCoveringInput(int4 s,const Address &loc) const
|
|
|
|
{
|
|
VarnodeDefSet::const_iterator iter;
|
|
Varnode *vn;
|
|
iter = beginDef(Varnode::input,loc);
|
|
if (iter != def_tree.end()) {
|
|
vn = *iter;
|
|
if ((vn->getAddr() != loc)&&(iter!=def_tree.begin())) {
|
|
--iter;
|
|
vn = *iter;
|
|
}
|
|
if (vn->isInput() && (vn->getSpace() == loc.getSpace()) &&
|
|
(vn->getOffset() <= loc.getOffset()) &&
|
|
(vn->getOffset() + vn->getSize()-1 >= loc.getOffset() + s -1))
|
|
return vn;
|
|
}
|
|
return (Varnode *)0;
|
|
}
|
|
|
|
/// \brief Beginning of Varnodes in given address space sorted by location
|
|
///
|
|
/// \param spaceid is the given address space
|
|
/// \return the beginning iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::beginLoc(AddrSpace *spaceid) const
|
|
|
|
{
|
|
searchvn.loc = Address(spaceid,0);
|
|
return loc_tree.lower_bound(&searchvn);
|
|
}
|
|
|
|
/// \brief Ending of Varnodes in given address space sorted by location
|
|
///
|
|
/// \param spaceid is the given address space
|
|
/// \return the ending iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::endLoc(AddrSpace *spaceid) const
|
|
|
|
{
|
|
searchvn.loc = Address(manage->getNextSpaceInOrder(spaceid),0);
|
|
return loc_tree.lower_bound(&searchvn);
|
|
}
|
|
|
|
/// \brief Beginning of Varnodes starting at a given address sorted by location
|
|
///
|
|
/// \param addr is the given starting address
|
|
/// \return the beginning iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::beginLoc(const Address &addr) const
|
|
|
|
{
|
|
searchvn.loc = addr;
|
|
return loc_tree.lower_bound(&searchvn);
|
|
}
|
|
|
|
/// \brief End of Varnodes starting at a given address sorted by location
|
|
///
|
|
/// \param addr is the given starting address
|
|
/// \return the ending iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::endLoc(const Address &addr) const
|
|
|
|
{
|
|
if (addr.getOffset() == addr.getSpace()->getHighest()) {
|
|
AddrSpace* space = addr.getSpace();
|
|
searchvn.loc = Address(manage->getNextSpaceInOrder(space),0);
|
|
}
|
|
else
|
|
searchvn.loc = addr+1;
|
|
return loc_tree.lower_bound(&searchvn);
|
|
}
|
|
|
|
/// \brief Beginning of Varnodes of given size and starting address sorted by location
|
|
///
|
|
/// \param s is the given size
|
|
/// \param addr is the given starting address
|
|
/// \return the beginning iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::beginLoc(int4 s,const Address &addr) const
|
|
|
|
{
|
|
searchvn.size = s;
|
|
searchvn.loc = addr;
|
|
VarnodeLocSet::const_iterator iter = loc_tree.lower_bound(&searchvn);
|
|
searchvn.size = 0; // Return size to 0
|
|
return iter;
|
|
}
|
|
|
|
/// \brief End of Varnodes of given size and starting address sorted by location
|
|
///
|
|
/// \param s is the given size
|
|
/// \param addr is the given starting address
|
|
/// \return the ending iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::endLoc(int4 s,const Address &addr) const
|
|
|
|
{
|
|
searchvn.size = s+1;
|
|
searchvn.loc = addr;
|
|
VarnodeLocSet::const_iterator iter = loc_tree.lower_bound(&searchvn);
|
|
searchvn.size = 0; // Return size to 0
|
|
return iter;
|
|
}
|
|
|
|
/// \brief Beginning of Varnodes sorted by location
|
|
///
|
|
/// Varnodes are restricted by a given size and location and by the property
|
|
/// - Varnode::input for Varnodes that are inputs to the function
|
|
/// - Varnode::written for Varnodes that are defined by a PcodeOp
|
|
/// - 0 for \e free Varnodes
|
|
/// \param s is the given size
|
|
/// \param addr is the given starting address
|
|
/// \param fl is the property restriction
|
|
/// \return the beginning iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::beginLoc(int4 s,const Address &addr,
|
|
uint4 fl) const
|
|
{
|
|
VarnodeLocSet::const_iterator iter;
|
|
|
|
if (fl == Varnode::input) {
|
|
searchvn.size = s;
|
|
searchvn.loc = addr;
|
|
iter = loc_tree.lower_bound(&searchvn);
|
|
searchvn.size = 0;
|
|
return iter;
|
|
}
|
|
if (fl == Varnode::written) {
|
|
SeqNum sq(Address::m_minimal); // Minimal sequence number
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.size = s;
|
|
searchvn.loc = addr;
|
|
searchvn.flags = Varnode::written;
|
|
searchvn.def = &searchop;
|
|
iter = loc_tree.lower_bound(&searchvn);
|
|
searchvn.size = 0;
|
|
searchvn.flags = Varnode::input;
|
|
return iter;
|
|
}
|
|
|
|
SeqNum sq(Address::m_maximal); // Maximal sequence number
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.size = s;
|
|
searchvn.loc = addr;
|
|
searchvn.flags = Varnode::written;
|
|
searchvn.def = &searchop;
|
|
iter = loc_tree.upper_bound(&searchvn);
|
|
searchvn.size = 0;
|
|
searchvn.flags = Varnode::input;
|
|
|
|
return iter;
|
|
}
|
|
|
|
/// \brief End of Varnodes sorted by location
|
|
///
|
|
/// Varnodes are restricted by a given size and location and by the property
|
|
/// - Varnode::input for Varnodes that are inputs to the function
|
|
/// - Varnode::written for Varnodes that are defined by a PcodeOp
|
|
/// - 0 for \e free Varnodes
|
|
/// \param s is the given size
|
|
/// \param addr is the given starting address
|
|
/// \param fl is the property restriction
|
|
/// \return the ending iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::endLoc(int4 s,const Address &addr,
|
|
uint4 fl) const
|
|
{
|
|
VarnodeLocSet::const_iterator iter;
|
|
searchvn.loc = addr;
|
|
|
|
if (fl == Varnode::written) {
|
|
searchvn.size = s;
|
|
searchvn.flags = Varnode::written;
|
|
SeqNum sq(Address::m_maximal); // Maximal sequence number
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.def = &searchop;
|
|
iter = loc_tree.upper_bound(&searchvn);
|
|
searchvn.size = 0;
|
|
searchvn.flags = Varnode::input;
|
|
return iter;
|
|
}
|
|
else if (fl == Varnode::input) {
|
|
searchvn.size = s;
|
|
iter = loc_tree.upper_bound(&searchvn);
|
|
searchvn.size = 0;
|
|
return iter;
|
|
}
|
|
|
|
searchvn.size = s+1;
|
|
iter = loc_tree.lower_bound(&searchvn); // Find following input varnode
|
|
searchvn.size = 0;
|
|
return iter;
|
|
}
|
|
|
|
/// \brief Beginning of Varnodes sorted by location
|
|
///
|
|
/// Varnodes are restricted by a given size and location and by the
|
|
/// sequence number of the PcodeOp defining it
|
|
/// \param s is the given size
|
|
/// \param addr is the given starting address
|
|
/// \param pc is the address of the PcodeOp defining the Varnode
|
|
/// \param uniq is the sequence number of the PcodeOp or -1 for now sequence number restriction
|
|
/// \return the beginning iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::beginLoc(int4 s,const Address &addr,
|
|
const Address &pc,uintm uniq) const
|
|
|
|
{ // Find first varnode of given loc and size
|
|
// defined at a particular location
|
|
VarnodeLocSet::const_iterator iter;
|
|
searchvn.size = s;
|
|
searchvn.loc = addr;
|
|
searchvn.flags = Varnode::written;
|
|
if (uniq==~((uintm)0)) // If don't care about uniq
|
|
uniq = 0; // find earliest
|
|
SeqNum sq(pc,uniq);
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.def = &searchop;
|
|
iter = loc_tree.lower_bound(&searchvn);
|
|
|
|
searchvn.size = 0;
|
|
searchvn.flags = Varnode::input;
|
|
return iter;
|
|
}
|
|
|
|
/// \brief End of Varnodes sorted by location
|
|
///
|
|
/// Varnodes are restricted by a given size and location and by the
|
|
/// sequence number of the PcodeOp defining it
|
|
/// \param s is the given size
|
|
/// \param addr is the given starting address
|
|
/// \param pc is the address of the PcodeOp defining the Varnode
|
|
/// \param uniq is the sequence number of the PcodeOp or -1 for now sequence number restriction
|
|
/// \return the ending iterator
|
|
VarnodeLocSet::const_iterator VarnodeBank::endLoc(int4 s,const Address &addr,
|
|
const Address &pc,uintm uniq) const
|
|
|
|
{
|
|
VarnodeLocSet::const_iterator iter;
|
|
searchvn.size = s;
|
|
searchvn.loc = addr;
|
|
searchvn.flags = Varnode::written;
|
|
// if (uniq==~((uintm)0))
|
|
// uniq = 0;
|
|
SeqNum sq(pc,uniq);
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.def = &searchop;
|
|
iter = loc_tree.upper_bound(&searchvn);
|
|
|
|
searchvn.size = 0;
|
|
searchvn.flags = Varnode::input;
|
|
return iter;
|
|
}
|
|
|
|
/// \brief Given start, return maximal range of overlapping Varnodes
|
|
///
|
|
/// Advance the iterator until no Varnodes after the iterator intersect any Varnodes
|
|
/// from the initial Varnode through the current iterator. The range is returned as pairs
|
|
/// of iterators to subranges. One subrange for each set of Varnodes with the same size and starting address.
|
|
/// A final iterator to the next Varnode after the overlapping set is also passed back.
|
|
/// \param iter is an iterator to the given start Varnode
|
|
/// \param bounds holds the array of iterator pairs passed back
|
|
/// \return the union of Varnode flags across the range
|
|
uint4 VarnodeBank::overlapLoc(VarnodeLocSet::const_iterator iter,vector<VarnodeLocSet::const_iterator> &bounds) const
|
|
|
|
{
|
|
Varnode *vn = *iter;
|
|
AddrSpace *spc = vn->getSpace();
|
|
uintb off = vn->getOffset();
|
|
uintb maxOff = off + (vn->getSize() - 1);
|
|
uint4 flags = vn->getFlags();
|
|
bounds.push_back(iter);
|
|
iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written);
|
|
bounds.push_back(iter);
|
|
while(iter != loc_tree.end()) {
|
|
vn = *iter;
|
|
if (vn->getSpace() != spc || vn->getOffset() > maxOff)
|
|
break;
|
|
if (vn->isFree()) {
|
|
iter = endLoc(vn->getSize(),vn->getAddr(),0);
|
|
continue;
|
|
}
|
|
uintb endOff = vn->getOffset() + (vn->getSize() - 1);
|
|
if (endOff > maxOff)
|
|
maxOff = endOff;
|
|
flags |= vn->getFlags();
|
|
bounds.push_back(iter);
|
|
iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written);
|
|
bounds.push_back(iter);
|
|
}
|
|
bounds.push_back(iter);
|
|
return flags;
|
|
}
|
|
|
|
/// \brief Beginning of varnodes with set definition property
|
|
///
|
|
/// Get an iterator to Varnodes in definition order restricted with the
|
|
/// following properties:
|
|
/// - Varnode::input for Varnodes which are inputs to the function
|
|
/// - Varnode::written for Varnodes that are defined by a PcodeOp
|
|
/// - 0 for \e free Varnodes
|
|
/// \param fl is the property restriction
|
|
/// \return the beginning iterator
|
|
VarnodeDefSet::const_iterator VarnodeBank::beginDef(uint4 fl) const
|
|
|
|
{
|
|
VarnodeDefSet::const_iterator iter;
|
|
|
|
if (fl == Varnode::input)
|
|
return def_tree.begin(); // Inputs occur first with def_tree
|
|
else if (fl == Varnode::written) {
|
|
searchvn.loc = Address(Address::m_minimal); // Lowest possible location
|
|
searchvn.flags = Varnode::written;
|
|
SeqNum sq(Address::m_minimal); // Lowest possible seqnum
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.def = &searchop;
|
|
iter = def_tree.lower_bound(&searchvn);
|
|
searchvn.flags = Varnode::input; // Reset flags
|
|
return iter;
|
|
}
|
|
|
|
// Find the start of the frees
|
|
searchvn.loc = Address(Address::m_maximal); // Maximal possible location
|
|
searchvn.flags = Varnode::written;
|
|
SeqNum sq(Address::m_maximal); // Maximal seqnum
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.def = &searchop;
|
|
iter = def_tree.upper_bound(&searchvn);
|
|
searchvn.flags = Varnode::input; // Reset flags
|
|
return iter;
|
|
}
|
|
|
|
/// \brief End of varnodes with set definition property
|
|
///
|
|
/// Get an iterator to Varnodes in definition order restricted with the
|
|
/// following properties:
|
|
/// - Varnode::input for Varnodes which are inputs to the function
|
|
/// - Varnode::written for Varnodes that are defined by a PcodeOp
|
|
/// - 0 for \e free Varnodes
|
|
/// \param fl is the property restriction
|
|
/// \return the ending iterator
|
|
VarnodeDefSet::const_iterator VarnodeBank::endDef(uint4 fl) const
|
|
|
|
{
|
|
VarnodeDefSet::const_iterator iter;
|
|
|
|
if (fl == Varnode::input) { // Highest input is lowest written
|
|
searchvn.loc = Address(Address::m_minimal); // Lowest possible location
|
|
searchvn.flags = Varnode::written;
|
|
SeqNum sq(Address::m_minimal); // Lowest possible seqnum
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.def = &searchop;
|
|
iter = def_tree.lower_bound(&searchvn);
|
|
searchvn.flags = Varnode::input; // Reset flags
|
|
return iter;
|
|
}
|
|
else if (fl == Varnode::written) { // Highest written
|
|
searchvn.loc = Address(Address::m_maximal); // Maximal possible location
|
|
searchvn.flags = Varnode::written;
|
|
SeqNum sq(Address::m_maximal); // Maximal seqnum
|
|
PcodeOp searchop(0,sq);
|
|
searchvn.def = &searchop;
|
|
iter = def_tree.upper_bound(&searchvn);
|
|
searchvn.flags = Varnode::input; // Reset flags
|
|
return iter;
|
|
}
|
|
return def_tree.end(); // Highest free is end of def_tree
|
|
}
|
|
|
|
/// \brief Beginning of varnodes starting at a given address with a set definition property
|
|
///
|
|
/// Get an iterator to Varnodes in definition order. The starting address of the Varnodes
|
|
/// must match the given address, and they are further restricted by the
|
|
/// following properties:
|
|
/// - Varnode::input for Varnodes which are inputs to the function
|
|
/// - Varnode::written for Varnodes that are defined by a PcodeOp
|
|
/// - 0 for \e free Varnodes
|
|
/// \param fl is the property restriction
|
|
/// \param addr is the given starting address
|
|
/// \return the beginning iterator
|
|
VarnodeDefSet::const_iterator VarnodeBank::beginDef(uint4 fl,const Address &addr) const
|
|
|
|
{ // Get varnodes with addr and with definition type
|
|
VarnodeDefSet::const_iterator iter;
|
|
|
|
if (fl == Varnode::written)
|
|
throw LowlevelError("Cannot get contiguous written AND addressed");
|
|
else if (fl == Varnode::input) {
|
|
searchvn.loc = addr;
|
|
iter = def_tree.lower_bound(&searchvn);
|
|
return iter;
|
|
}
|
|
|
|
// Find the start of the frees with a given address
|
|
searchvn.loc = addr;
|
|
searchvn.flags = 0;
|
|
// Since a size 0 object shouldn't exist, an upper bound
|
|
// should bump up to first free of addr with non-zero size
|
|
iter = def_tree.upper_bound(&searchvn);
|
|
searchvn.flags = Varnode::input; // Reset flags
|
|
return iter;
|
|
}
|
|
|
|
/// \brief End of varnodes starting at a given address with a set definition property
|
|
///
|
|
/// Get an iterator to Varnodes in definition order. The starting address of the Varnodes
|
|
/// must match the given address, and they are further restricted by the
|
|
/// following properties:
|
|
/// - Varnode::input for Varnodes which are inputs to the function
|
|
/// - Varnode::written for Varnodes that are defined by a PcodeOp
|
|
/// - 0 for \e free Varnodes
|
|
/// \param fl is the property restriction
|
|
/// \param addr is the given starting address
|
|
/// \return the ending iterator
|
|
VarnodeDefSet::const_iterator VarnodeBank::endDef(uint4 fl,const Address &addr) const
|
|
|
|
{
|
|
VarnodeDefSet::const_iterator iter;
|
|
|
|
if (fl == Varnode::written)
|
|
throw LowlevelError("Cannot get contiguous written AND addressed");
|
|
else if (fl == Varnode::input) {
|
|
searchvn.loc = addr;
|
|
searchvn.size = 1000000;
|
|
iter = def_tree.lower_bound(&searchvn);
|
|
searchvn.size = 0;
|
|
return iter;
|
|
}
|
|
|
|
// Find the start of the frees with a given address
|
|
searchvn.loc = addr;
|
|
searchvn.size = 1000000;
|
|
searchvn.flags = 0;
|
|
// Since a size 0 object shouldn't exist, an upper bound
|
|
// should bump up to first free of addr with non-zero size
|
|
iter = def_tree.lower_bound(&searchvn);
|
|
searchvn.flags = Varnode::input; // Reset flags
|
|
searchvn.size = 0;
|
|
return iter;
|
|
}
|
|
|
|
#ifdef VARBANK_DEBUG
|
|
/// Check tree order is still accurate
|
|
void VarnodeBank::verifyIntegrity(void) const
|
|
|
|
{
|
|
VarnodeLocSet::iterator iter;
|
|
Varnode *vn,*lastvn;
|
|
|
|
if (loc_tree.empty()) return;
|
|
iter = loc_tree.begin();
|
|
lastvn = *iter++;
|
|
if (def_tree.end() == def_tree.find(lastvn))
|
|
throw LowlevelError("Varbank first loc missing in def");
|
|
for(;iter!=loc_tree.end();++iter) {
|
|
vn = *iter;
|
|
if (def_tree.end() == def_tree.find(vn))
|
|
throw LowlevelError("Varbank loc missing in def");
|
|
if (*vn < *lastvn)
|
|
throw LowlevelError("Varbank locdef integrity test failed");
|
|
lastvn = vn;
|
|
}
|
|
|
|
VarnodeDefSet::iterator diter;
|
|
VarnodeCompareDefLoc cmp;
|
|
|
|
diter = def_tree.begin();
|
|
lastvn = *diter++;
|
|
if (loc_tree.end() == loc_tree.find(lastvn))
|
|
throw LowlevelError("Varbank first def missing in loc");
|
|
for(;diter!=def_tree.end();++diter) {
|
|
vn = *diter;
|
|
if (loc_tree.end() == loc_tree.find(vn))
|
|
throw LowlevelError("Varbank def missing in loc");
|
|
if (cmp(vn,lastvn))
|
|
throw LowlevelError("Varbank defloc integrity test failed");
|
|
lastvn = vn;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// \brief Return \b true if the alternate path looks more valid than the main path.
|
|
///
|
|
/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN.
|
|
/// Evaluate which path most likely represents actual parameter/return value passing,
|
|
/// based on traversal information about each path.
|
|
/// \param vn is the Varnode terminating the \e alternate path
|
|
/// \param flags indicates traversals for both paths
|
|
/// \return \b true if the alternate path is preferred
|
|
bool TraverseNode::isAlternatePathValid(const Varnode *vn,uint4 flags)
|
|
|
|
{
|
|
if ((flags & (indirect | indirectalt)) == indirect)
|
|
// If main path traversed an INDIRECT but the alternate did not
|
|
return true; // Main path traversed INDIRECT, alternate did not
|
|
if ((flags & (indirect | indirectalt)) == indirectalt)
|
|
return false; // Alternate path traversed INDIRECT, main did not
|
|
if ((flags & actionalt) != 0)
|
|
return true; // Alternate path traversed a dedicated COPY
|
|
if (vn->loneDescend() == (PcodeOp*)0) return false;
|
|
const PcodeOp *op = vn->getDef();
|
|
if (op == (PcodeOp*)0) return true;
|
|
while(op->isIncidentalCopy() && op->code() == CPUI_COPY) { // Skip any incidental COPY
|
|
vn = op->getIn(0);
|
|
if (vn->loneDescend() == (PcodeOp *)0) return false;
|
|
op = vn->getDef();
|
|
if (op == (PcodeOp *)0) return true;
|
|
}
|
|
return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values
|
|
}
|
|
|
|
/// Return true if \b vn1 contains the high part and \b vn2 the low part
|
|
/// of what was(is) a single value.
|
|
/// \param vn1 is the putative high Varnode
|
|
/// \param vn2 is the putative low Varnode
|
|
/// \return \b true if they are pieces of a whole
|
|
bool contiguous_test(Varnode *vn1,Varnode *vn2)
|
|
|
|
{
|
|
if (vn1->isInput()||vn2->isInput()) {
|
|
return false;
|
|
}
|
|
if ((!vn1->isWritten())||(!vn2->isWritten())) return false;
|
|
PcodeOp *op1 = vn1->getDef();
|
|
PcodeOp *op2 = vn2->getDef();
|
|
Varnode *vnwhole;
|
|
switch(op1->code()) {
|
|
case CPUI_SUBPIECE:
|
|
if (op2->code() != CPUI_SUBPIECE) return false;
|
|
vnwhole = op1->getIn(0);
|
|
if (op2->getIn(0) != vnwhole) return false;
|
|
if (op2->getIn(1)->getOffset() != 0)
|
|
return false; // Must be least sig
|
|
if (op1->getIn(1)->getOffset() != vn2->getSize())
|
|
return false; // Must be contiguous
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Assuming vn1,vn2 has passed the contiguous_test(), return
|
|
/// the Varnode containing the whole value.
|
|
/// \param data is the underlying function
|
|
/// \param vn1 is the high Varnode
|
|
/// \param vn2 is the low Varnode
|
|
/// \return the whole Varnode
|
|
Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1,Varnode *vn2)
|
|
|
|
{ if (vn1->isWritten())
|
|
if (vn1->getDef()->code() == CPUI_SUBPIECE)
|
|
return vn1->getDef()->getIn(0);
|
|
return (Varnode *)0;
|
|
}
|
|
|
|
} // End namespace ghidra
|