Merge remote-tracking branch 'origin/GT_3392_DecompilerAPI'

This commit is contained in:
ghidorahrex 2020-02-03 12:18:43 -05:00
commit 8319b1ba5c
99 changed files with 5477 additions and 2594 deletions

View file

@ -2332,23 +2332,6 @@ void ActionNameVars::lookForBadJumpTables(Funcdata &data)
}
}
/// From among the \e name \e recommendations (symbol information that wasn't locked)
/// find current symbols for which the name can still apply and apply it.
/// \param data is the function being analyzed
void ActionNameVars::lookForRecommendedNames(Funcdata &data)
{
ScopeLocal *localmap = data.getScopeLocal();
vector<string> names;
vector<Symbol *> symbols;
localmap->makeNameRecommendationsForSymbols(names,symbols);
for(uint4 i=0;i<names.size();++i) {
Symbol *sym = symbols[i];
sym->getScope()->renameSymbol(sym,localmap->makeNameUnique(names[i]));
}
}
/// \brief Add a recommendation to the database based on a particular sub-function parameter.
///
/// We know \b vn holds data-flow for parameter \b param, try to attach its name to \b vn's symbol.
@ -2375,6 +2358,7 @@ void ActionNameVars::makeRec(ProtoParameter *param,Varnode *vn,map<HighVariable
}
HighVariable *high = vn->getHigh();
if (!high->isMark()) return; // Not one of the variables needing a name
if (high->isAddrTied()) return; // Don't propagate parameter name to address tied variable
map<HighVariable *,OpRecommend>::iterator iter = recmap.find(high);
if (iter != recmap.end()) { // We have seen this varnode before
@ -2450,33 +2434,73 @@ void ActionNameVars::lookForFuncParamNames(Funcdata &data,const vector<Varnode *
}
}
int4 ActionNameVars::apply(Funcdata &data)
/// \brief Link symbols associated with a given \e spacebase Varnode
///
/// Look for PTRSUB ops which indicate a symbol reference within the address space
/// referred to by the \e spacebase Varnode. Decode any symbol reference and link it
/// to the appropriate HighVariable
/// \param vn is the given \e spacebase Varnode
/// \param data is the function containing the Varnode
/// \param namerec is used to store any recovered Symbol without a name
void ActionNameVars::linkSpacebaseSymbol(Varnode *vn,Funcdata &data,vector<Varnode *> &namerec)
{
if (!vn->isConstant() && !vn->isInput()) return;
list<PcodeOp *>::const_iterator iter;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
if (op->code() != CPUI_PTRSUB) continue;
Varnode *offVn = op->getIn(1);
Symbol *sym = data.linkSymbolReference(offVn);
if ((sym != (Symbol *)0) && sym->isNameUndefined())
namerec.push_back(offVn);
}
}
/// \brief Link formal Symbols to their HighVariable representative in the given Function
///
/// Run through all HighVariables for the given function and set up the explicit mapping with
/// existing Symbol objects. If there is no matching Symbol for a given HighVariable, a new
/// Symbol is created. Any Symbol that does not have a name is added to a list for further
/// name resolution.
/// \param data is the given function
/// \param namerec is the container for collecting Symbols with a name
void ActionNameVars::linkSymbols(Funcdata &data,vector<Varnode *> &namerec)
{
const AddrSpaceManager *manage = data.getArch();
VarnodeLocSet::const_iterator iter,enditer;
AddrSpace *spc;
vector<Varnode *> namerec; // Name representatives of symbols that need names
AddrSpace *constSpace = manage->getConstantSpace();
enditer = data.endLoc(constSpace);
for(iter=data.beginLoc(constSpace);iter!=enditer;++iter) {
Varnode *curvn = *iter;
if (curvn->getSymbolEntry() != (SymbolEntry *)0)
data.linkSymbol(curvn); // Special equate symbol
else if (curvn->isSpacebase())
linkSpacebaseSymbol(curvn, data, namerec);
}
for(int4 i=0;i<manage->numSpaces();++i) { // Build a list of nameable highs
spc = manage->getSpace(i);
if (spc == (AddrSpace *)0) continue;
if (spc == constSpace) continue;
enditer = data.endLoc(spc);
for(iter=data.beginLoc(spc);iter!=enditer;++iter) {
Varnode *curvn = *iter;
if (curvn->isFree()) {
if (curvn->getSymbolEntry() != (SymbolEntry *)0)
data.linkSymbol(curvn); // Special equate symbol
continue;
}
if (curvn->isSpacebase())
linkSpacebaseSymbol(curvn, data, namerec);
Varnode *vn = curvn->getHigh()->getNameRepresentative();
if (vn != curvn) continue; // Hit each high only once
HighVariable *high = vn->getHigh();
if (!high->hasName()) continue;
Symbol *sym = data.linkSymbol(vn);
if (sym != (Symbol *)0) { // Can we associate high with a nameable symbol
if (sym->isNameUndefined())
namerec.push_back(vn);
if (sym->isNameUndefined() && high->getSymbolOffset() < 0)
namerec.push_back(vn); // Add if no name, and we have a high representing the whole
if (sym->isSizeTypeLocked()) {
if (vn->getSize() == sym->getType()->getSize())
sym->getScope()->overrideSizeLockType(sym,high->getType());
@ -2484,34 +2508,29 @@ int4 ActionNameVars::apply(Funcdata &data)
}
}
}
}
lookForRecommendedNames(data); // Make sure recommended names hit before subfunc
int4 ActionNameVars::apply(Funcdata &data)
{
vector<Varnode *> namerec;
linkSymbols(data, namerec);
data.getScopeLocal()->recoverNameRecommendationsForSymbols(); // Make sure recommended names hit before subfunc
lookForBadJumpTables(data);
lookForFuncParamNames(data,namerec);
ScopeLocal *localmap = data.getScopeLocal();
int4 base = 1;
for(uint4 i=0;i<namerec.size();++i) {
Varnode *vn = namerec[i];
HighVariable *high = vn->getHigh();
Symbol *sym = high->getSymbol();
Symbol *sym = vn->getHigh()->getSymbol();
if (sym->isNameUndefined()) {
string newname;
Address usepoint;
if (!vn->isAddrTied())
usepoint = vn->getUsePoint(data);
if (high->isInput()) {
int4 index = -1;
if (sym->getCategory()==0)
index = sym->getCategory()+1;
newname = localmap->buildVariableName(vn->getAddr(),usepoint,high->getType(),index,vn->getFlags());
}
else
newname = localmap->buildVariableName(vn->getAddr(),usepoint,high->getType(),base,vn->getFlags());
sym->getScope()->renameSymbol(sym,newname);
Scope *scope = sym->getScope();
string newname = scope->buildDefaultName(sym, base, vn);
scope->renameSymbol(sym,newname);
}
}
data.getScopeLocal()->assignDefaultNames(base);
return 0;
}
@ -3852,67 +3871,15 @@ int4 ActionDynamicSymbols::apply(Funcdata &data)
{
ScopeLocal *localmap = data.getScopeLocal();
list<SymbolEntry>::const_iterator iter,enditer;
list<SymbolEntry>::iterator iter,enditer;
iter = localmap->beginDynamic();
enditer = localmap->endDynamic();
DynamicHash dhash;
while(iter != enditer) {
const SymbolEntry &entry( *iter );
SymbolEntry *entry = &(*iter);
++iter;
Symbol *sym = entry.getSymbol();
dhash.clear();
Varnode *vn = dhash.findVarnode(&data,entry.getFirstUseAddress(),entry.getHash());
if (vn == (Varnode *)0) {
// localmap->removeSymbol(sym); // If it didn't map to anything, remove it
continue;
}
if (vn->getSymbolEntry() == &entry) continue; // Already applied it
if (vn->getSize() != entry.getSize()) {
ostringstream s;
s << "Unable to use symbol ";
if (!sym->isNameUndefined())
s << sym->getName() << ' ';
s << ": Size does not match variable it labels";
data.warningHeader(s.str());
// localmap->removeSymbol(sym); // Don't use the symbol
continue;
}
if (vn->getSymbolEntry() == &entry) continue; // Already applied it
if (vn->isImplied()) { // This should be finding an explicit, but a cast may have been inserted
Varnode *newvn = (Varnode *)0;
// Look at the "other side" of the cast
if (vn->isWritten() && (vn->getDef()->code() == CPUI_CAST))
newvn = vn->getDef()->getIn(0);
else {
PcodeOp *castop = vn->loneDescend();
if ((castop != (PcodeOp *)0)&&(castop->code() == CPUI_CAST))
newvn = castop->getOut();
}
// See if the varnode on the other side is explicit
if ((newvn != (Varnode *)0)&&(newvn->isExplicit()))
vn = newvn; // in which case we use it
}
int4 offset;
if (!entry.isPiece())
offset = -1;
else
offset = entry.getOffset();
vn->getHigh()->setSymbol(sym,offset);
if (!sym->isTypeLocked()) { // If the dynamic symbol did not lock its type
localmap->retypeSymbol(sym,vn->getType()); // use the type propagated into the varnode
}
else if (sym->getType() != vn->getType()) {
ostringstream s;
s << "Unable to use type for symbol " << sym->getName();
data.warningHeader(s.str());
localmap->retypeSymbol(sym,vn->getType()); // use the type propagated into the varnode
}
// FIXME: Setting the symbol here (for the first time) gives the type no time to propagate properly
// Currently the casts aren't set properly
count += 1;
if (data.attemptDynamicMappingLate(entry, dhash))
count += 1;
}
return 0;
}
@ -4893,8 +4860,10 @@ void universal_action(Architecture *conf)
act->addAction( new ActionMergeRequired("merge") );
act->addAction( new ActionMarkExplicit("merge") );
act->addAction( new ActionMarkImplied("merge") ); // This must come BEFORE general merging
act->addAction( new ActionMergeMultiEntry("merge") );
act->addAction( new ActionMergeCopy("merge") );
act->addAction( new ActionDominantCopy("merge") );
act->addAction( new ActionDynamicSymbols("dynamic") );
act->addAction( new ActionMarkIndirectOnly("merge") ); // Must come after required merges but before speculative
act->addAction( new ActionMergeAdjacent("merge") );
act->addAction( new ActionMergeType("merge") );

View file

@ -380,6 +380,17 @@ public:
virtual int4 apply(Funcdata &data) { data.getMerge().mergeOpcode(CPUI_COPY); return 0; }
};
/// \brief Try to merge Varnodes specified by Symbols with multiple SymbolEntrys
class ActionMergeMultiEntry : public Action {
public:
ActionMergeMultiEntry(const string &g) : Action(rule_onceperfunc,"mergemultientry",g) {} ///< Constructor
virtual Action *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Action *)0;
return new ActionMergeMultiEntry(getGroup());
}
virtual int4 apply(Funcdata &data) { data.getMerge().mergeMultiEntry(); return 0; }
};
/// \brief Try to merge Varnodes of the same type (if they don't hold different values at the same time)
class ActionMergeType : public Action {
public:
@ -452,8 +463,9 @@ class ActionNameVars : public Action {
};
static void makeRec(ProtoParameter *param,Varnode *vn,map<HighVariable *,OpRecommend> &recmap);
static void lookForBadJumpTables(Funcdata &data); ///< Mark the switch variable for bad jump-tables
static void lookForRecommendedNames(Funcdata &data); ///< Try to apply names from unlocked symbols
static void lookForFuncParamNames(Funcdata &data,const vector<Varnode *> &varlist);
static void linkSpacebaseSymbol(Varnode *vn,Funcdata &data,vector<Varnode *> &namerec);
static void linkSymbols(Funcdata &data,vector<Varnode *> &namerec);
public:
ActionNameVars(const string &g) : Action(rule_onceperfunc,"namevars",g) {} ///< Constructor
virtual Action *clone(const ActionGroupList &grouplist) const {

View file

@ -17,6 +17,8 @@
#include "funcdata.hh"
#include <ctype.h>
uint8 Symbol::ID_BASE = 0x4000000000000000L;
/// This SymbolEntry is unintegrated. An address or hash must be provided
/// either directly or via restoreXml().
/// \param sym is the Symbol \b this will be a map for
@ -230,6 +232,21 @@ bool Symbol::isNameUndefined(void) const
return ((name.size()==15)&&(0==name.compare(0,7,"$$undef")));
}
/// If the given value is \b true, any Varnodes that map directly to \b this Symbol,
/// will not be speculatively merged with other Varnodes. (Required merges will still happen).
/// \param val is the given boolean value
void Symbol::setIsolated(bool val)
{
if (val) {
dispflags |= isolate;
flags |= Varnode::typelock; // Isolated Symbol must be typelocked
checkSizeTypeLock();
}
else
dispflags &= ~((uint4)isolate);
}
/// \return the first SymbolEntry
SymbolEntry *Symbol::getFirstWholeMap(void) const
@ -260,6 +277,57 @@ SymbolEntry *Symbol::getMapEntry(const Address &addr) const
return (SymbolEntry *)0;
}
/// Among all the SymbolEntrys that map \b this entire Symbol, calculate
/// the position of the given SymbolEntry within the list.
/// \param entry is the given SymbolEntry
/// \return its position within the list or -1 if it is not in the list
int4 Symbol::getMapEntryPosition(const SymbolEntry *entry) const
{
int4 pos = 0;
for(int4 i=0;i<mapentry.size();++i) {
const SymbolEntry *tmp = &(*mapentry[i]);
if (tmp == entry)
return pos;
if (entry->getSize() == type->getSize())
pos += 1;
}
return -1;
}
/// A value of 0 means the base Symbol name is visible and not overridden in the given use scope.
/// A value of 1 means the base name may be overridden, but the parent scope name is not.
/// The minimual number of names that distinguishes \b this Symbol uniquely within the
/// use scope is returned.
/// \param useScope is the given scope where \b this Symbol is being used
/// \return the number of (extra) names needed to distinguish \b this Symbol
int4 Symbol::getResolutionDepth(const Scope *useScope) const
{
if (scope == useScope) return 0; // Symbol is in scope where it is used
const Scope *distinguishScope = scope->findDistinguishingScope(useScope);
int4 depth = 0;
string distinguishName;
const Scope *terminatingScope;
if (distinguishScope == (const Scope *)0) { // Symbol scope is ancestor of use scope
distinguishName = name;
terminatingScope = scope;
}
else {
distinguishName = distinguishScope->getName();
const Scope *currentScope = scope;
while(currentScope != distinguishScope) { // For any scope up to the distinguishing scope
depth += 1; // Print its name
currentScope = currentScope->getParent();
}
depth += 1; // Also print the distinguishing scope name
terminatingScope = distinguishScope->getParent();
}
if (useScope->isNameUsed(distinguishName,terminatingScope))
depth += 1; // Name was overridden, we need one more distinguishing name
return depth;
}
/// \param s is the output stream
void Symbol::saveXmlHeader(ostream &s) const
@ -278,18 +346,20 @@ void Symbol::saveXmlHeader(ostream &s) const
a_v_b(s,"indirectstorage",true);
if ((flags&Varnode::hiddenretparm)!=0)
a_v_b(s,"hiddenretparm",true);
if ((dispflags&isolate)!=0)
a_v_b(s,"merge",false);
int4 format = getDisplayFormat();
if (format != 0) {
s << " format=\"";
if (format == Symbol::force_hex)
if (format == force_hex)
s << "hex\"";
else if (format == Symbol::force_dec)
else if (format == force_dec)
s << "dec\"";
else if (format == Symbol::force_char)
else if (format == force_char)
s << "char\"";
else if (format == Symbol::force_oct)
else if (format == force_oct)
s << "oct\"";
else if (format == Symbol::force_bin)
else if (format == force_bin)
s << "bin\"";
else
s << "hex\"";
@ -305,50 +375,87 @@ void Symbol::restoreXmlHeader(const Element *el)
{
name.clear();
category = -1;
symbolId = 0;
for(int4 i=0;i<el->getNumAttributes();++i) {
if (el->getAttributeName(i)=="name")
name = el->getAttributeValue(i);
else if (el->getAttributeName(i)=="cat") {
istringstream s(el->getAttributeValue("cat"));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> category;
}
else if (el->getAttributeName(i)=="namelock") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::namelock;
}
else if (el->getAttributeName(i)=="typelock") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::typelock;
}
else if (el->getAttributeName(i)=="readonly") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::readonly;
}
else if (el->getAttributeName(i)=="volatile") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::volatil;
}
else if (el->getAttributeName(i)=="indirectstorage") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::indirectstorage;
}
else if (el->getAttributeName(i)=="hiddenretparm") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::hiddenretparm;
}
else if (el->getAttributeName(i)=="format") {
const string &formString( el->getAttributeValue(i));
if (formString == "hex")
dispflags |= Symbol::force_hex;
else if (formString == "dec")
dispflags |= Symbol::force_dec;
else if (formString == "char")
dispflags |= Symbol::force_char;
else if (formString == "oct")
dispflags |= Symbol::force_oct;
else if (formString == "bin")
dispflags |= Symbol::force_bin;
const string &attName(el->getAttributeName(i));
switch (attName[0]) {
case 'c':
if (attName == "cat") {
istringstream s(el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> category;
}
break;
case 'f':
if (attName == "format") {
const string &formString(el->getAttributeValue(i));
if (formString == "hex")
dispflags |= force_hex;
else if (formString == "dec")
dispflags |= force_dec;
else if (formString == "char")
dispflags |= force_char;
else if (formString == "oct")
dispflags |= force_oct;
else if (formString == "bin")
dispflags |= force_bin;
}
break;
case 'h':
if (attName == "hiddenretparm") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::hiddenretparm;
}
break;
case 'i':
if (attName == "id") {
istringstream s(el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> symbolId;
if ((symbolId >> 56) == (ID_BASE >> 56))
symbolId = 0; // Don't keep old internal id's
}
else if (attName == "indirectstorage") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::indirectstorage;
}
break;
case 'm':
if (attName == "merge") {
if (!xml_readbool(el->getAttributeValue(i))) {
dispflags |= isolate;
flags |= Varnode::typelock;
}
}
break;
case 'n':
if (attName == "name")
name = el->getAttributeValue(i);
else if (attName == "namelock") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::namelock;
}
break;
case 'r':
if (attName == "readonly") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::readonly;
}
break;
case 't':
if (attName == "typelock") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::typelock;
}
break;
case 'v':
if (attName == "volatile") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= Varnode::volatil;
}
break;
default:
break;
}
}
if (category == 0) {
@ -465,10 +572,12 @@ void FunctionSymbol::saveXml(ostream &s) const
{
if (fd != (Funcdata *)0)
fd->saveXml(s,false); // Save the function itself
fd->saveXml(s,symbolId,false); // Save the function itself
else {
s << "<functionshell";
a_v(s,"name",name);
if (symbolId != 0)
a_v_u(s,"id",symbolId);
s << "/>\n";
}
}
@ -478,7 +587,7 @@ void FunctionSymbol::restoreXml(const Element *el)
{
if (el->getName() == "function") {
fd = new Funcdata("",scope,Address());
fd->restoreXml(el);
symbolId = fd->restoreXml(el);
name = fd->getName();
if (consumeSize < fd->getSize()) {
if ((fd->getSize()>1)&&(fd->getSize() <= 8))
@ -486,7 +595,17 @@ void FunctionSymbol::restoreXml(const Element *el)
}
}
else { // functionshell
name = el->getAttributeValue("name");
symbolId = 0;
for(int4 i=0;i<el->getNumAttributes();++i) {
const string &attrName(el->getAttributeName(i));
if (attrName == "name")
name = el->getAttributeValue(i);
else if (attrName == "id") {
istringstream s(el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> symbolId;
}
}
}
}
@ -683,7 +802,7 @@ void Scope::attachScope(Scope *child)
{
child->parent = this;
pair<const ScopeKey,Scope *> value(ScopeKey(child->name,child->dedupId),child);
pair<const ScopeKey,Scope *> value(ScopeKey(child->name,child->uniqueId),child);
pair<ScopeMap::iterator,bool> res;
if (child->name.size()==0)
throw LowlevelError("Non-global scope has empty name");
@ -1257,6 +1376,74 @@ void Scope::getNameSegments(vector<string> &vec) const
}
}
/// Put the parent scopes of \b this into an array in order, starting with the global scope.
/// This scope itself will not be in the array.
/// \param vec is storage for the array of scopes
void Scope::getScopePath(vector<Scope *> &vec) const
{
int4 count = 0;
Scope *cur = parent;
while(cur != (Scope *)0) { // Count number of elements in path
count += 1;
cur = cur->parent;
}
vec.resize(count);
cur = parent;
while(cur != (Scope *)0) {
count -= 1;
vec[count] = cur;
cur = cur->parent;
}
}
/// Test for the presence of a symbol with the given name in either \b this scope or
/// an ancestor scope up to but not including the given terminating scope.
/// If the name is used \b true is returned.
/// \param nm is the given name to test
/// \param op2 is the terminating ancestor scope (or null)
bool Scope::isNameUsed(const string &nm,const Scope *op2) const
{
const Scope *currentScope = this;
while(currentScope != op2) {
if (currentScope->isNameUsed(name))
return true;
currentScope = currentScope->parent;
}
return false;
}
/// Any two scopes share at least the \e global scope as a common ancestor. We find the first scope
/// that is \e not in common. The scope returned will always be an ancestor of \b this.
/// If \b this is an ancestor of the other given scope, then null is returned.
/// \param op2 is the other given Scope
/// \return the first ancestor Scope that is not in common or null
const Scope *Scope::findDistinguishingScope(const Scope *op2) const
{
if (this == op2) return (Scope *)0; // Quickly check most common cases
if (parent == op2) return this;
if (op2->parent == this) return op2;
if (parent == op2->parent) return this;
vector<Scope *> thisPath;
vector<Scope *> op2Path;
getScopePath(thisPath);
op2->getScopePath(op2Path);
int4 min = thisPath.size();
if (op2Path.size() < min)
min = op2Path.size();
for(int4 i=0;i<min;++i) {
if (thisPath[i] != op2Path[i])
return thisPath[i];
}
if (min < thisPath.size())
return thisPath[min]; // thisPath matches op2Path but is longer
if (min < op2Path.size())
return (Scope *)0; // op2Path matches thisPath but is longer
return this; // ancestor paths are identical (only base scopes differ)
}
/// The Symbol is created and added to any name map, but no SymbolEntry objects are created for it.
/// \param name is the name of the new Symbol
/// \param ct is a data-type to assign to the new Symbol
@ -1454,6 +1641,45 @@ Symbol *Scope::addDynamicSymbol(const string &nm,Datatype *ct,const Address &cad
return sym;
}
/// Create default name given information in the Symbol and possibly a representative Varnode.
/// This method extracts the crucial properties and then uses the buildVariableName method to
/// construct the actual name.
/// \param sym is the given Symbol to name
/// \param base is an index (which may get updated) used to uniquify the name
/// \param vn is an optional (may be null) Varnode representative of the Symbol
/// \return the default name
string Scope::buildDefaultName(Symbol *sym,int4 &base,Varnode *vn) const
{
if (vn != (Varnode *)0 && !vn->isConstant()) {
Address usepoint;
if (!vn->isAddrTied() && fd != (Funcdata *)0)
usepoint = vn->getUsePoint(*fd);
HighVariable *high = vn->getHigh();
if (sym->getCategory() == 0 || high->isInput()) {
int4 index = -1;
if (sym->getCategory()==0)
index = sym->getCategoryIndex()+1;
return buildVariableName(vn->getAddr(),usepoint,sym->getType(),index,vn->getFlags() | Varnode::input);
}
return buildVariableName(vn->getAddr(),usepoint,sym->getType(),base,vn->getFlags());
}
if (sym->numEntries() != 0) {
SymbolEntry *entry = sym->getMapEntry(0);
Address addr = entry->getAddr();
Address usepoint = entry->getFirstUseAddress();
uint4 flags = usepoint.isInvalid() ? Varnode::addrtied : 0;
if (sym->getCategory() == 0) { // If this is a parameter
flags |= Varnode::input;
int4 index = sym->getCategoryIndex() + 1;
return buildVariableName(addr, usepoint, sym->getType(), index, flags);
}
return buildVariableName(addr, usepoint, sym->getType(), base, flags);
}
// Should never reach here
return buildVariableName(Address(), Address(), sym->getType(), base, 0);
}
/// \brief Is the given memory range marked as \e read-only
///
/// Check for Symbols relative to \b this Scope that are marked as \e read-only,
@ -1473,6 +1699,10 @@ bool Scope::isReadOnly(const Address &addr,int4 size,const Address &usepoint) co
void ScopeInternal::addSymbolInternal(Symbol *sym)
{
if (sym->symbolId == 0) {
sym->symbolId = Symbol::ID_BASE + (((uint8)uniqueId & 0xffff) << 40) + nextUniqueId;
nextUniqueId += 1;
}
try {
if (sym->name.size() == 0)
sym->name = buildUndefinedName();
@ -1520,6 +1750,11 @@ SymbolEntry *ScopeInternal::addMapInternal(Symbol *sym,uint4 exfl,const Address
list<SymbolEntry>::iterator iter = rangemap->insert(initdata,addr.getOffset(),lastaddress.getOffset());
// Store reference to map in symbol
sym->mapentry.push_back(iter);
if (sz == sym->type->getSize()) {
sym->wholeCount += 1;
if (sym->wholeCount == 2)
multiEntrySet.insert(sym);
}
return &(*iter);
}
@ -1530,6 +1765,11 @@ SymbolEntry *ScopeInternal::addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 h
list<SymbolEntry>::iterator iter = dynamicentry.end();
--iter;
sym->mapentry.push_back(iter); // Store reference to map entry in symbol
if (sz == sym->type->getSize()) {
sym->wholeCount += 1;
if (sym->wholeCount == 2)
multiEntrySet.insert(sym);
}
return &dynamicentry.back();
}
@ -1594,6 +1834,7 @@ list<SymbolEntry>::iterator ScopeInternal::endDynamic(void)
ScopeInternal::ScopeInternal(const string &nm,Architecture *g)
: Scope(nm,g)
{
nextUniqueId = 0;
int4 numspaces = g->numSpaces();
for(int4 i=0;i<numspaces;++i)
maptable.push_back((EntryMap *)0);
@ -1624,6 +1865,7 @@ void ScopeInternal::clear(void)
Symbol *sym = *iter++;
removeSymbol(sym);
}
nextUniqueId = 0;
}
/// Look for NULL entries in the category tables. If there are,
@ -1740,18 +1982,13 @@ void ScopeInternal::clearUnlockedCategory(int4 cat)
}
}
void ScopeInternal::removeSymbol(Symbol *symbol)
void ScopeInternal::removeSymbolMappings(Symbol *symbol)
{
vector<list<SymbolEntry>::iterator>::iterator iter;
if (symbol->category >= 0) {
vector<Symbol *> &list(category[symbol->category]);
list[symbol->catindex] = (Symbol *)0;
while((!list.empty())&&(list.back() == (Symbol *)0))
list.pop_back();
}
if (symbol->wholeCount > 1)
multiEntrySet.erase(symbol);
// Remove each mapping of the symbol
for(iter=symbol->mapentry.begin();iter!=symbol->mapentry.end();++iter) {
AddrSpace *spc = (*(*iter)).getAddr().getSpace();
@ -1762,6 +1999,20 @@ void ScopeInternal::removeSymbol(Symbol *symbol)
rangemap->erase( *iter );
}
}
symbol->wholeCount = 0;
symbol->mapentry.clear();
}
void ScopeInternal::removeSymbol(Symbol *symbol)
{
if (symbol->category >= 0) {
vector<Symbol *> &list(category[symbol->category]);
list[symbol->catindex] = (Symbol *)0;
while((!list.empty())&&(list.back() == (Symbol *)0))
list.pop_back();
}
removeSymbolMappings(symbol);
nametree.erase(symbol);
delete symbol;
}
@ -1769,10 +2020,14 @@ void ScopeInternal::removeSymbol(Symbol *symbol)
void ScopeInternal::renameSymbol(Symbol *sym,const string &newname)
{
nametree.erase(sym); // Erase under old name
nametree.erase(sym); // Erase under old name
if (sym->wholeCount > 1)
multiEntrySet.erase(sym); // The multi-entry set is sorted by name, remove
string oldname = sym->name;
sym->name = newname;
insertNameTree(sym);
if (sym->wholeCount > 1)
multiEntrySet.insert(sym); // Reenter into the multi-entry set now that name is changed
}
void ScopeInternal::retypeSymbol(Symbol *sym,Datatype *ct)
@ -1795,6 +2050,7 @@ void ScopeInternal::retypeSymbol(Symbol *sym,Datatype *ct)
// Remove the map entry
rangemap->erase(iter);
sym->mapentry.pop_back(); // Remove reference to map entry
sym->wholeCount = 0;
// Now we are ready to change the type
sym->type = ct;
@ -2021,6 +2277,15 @@ void ScopeInternal::findByName(const string &name,vector<Symbol *> &res) const
}
}
bool ScopeInternal::isNameUsed(const string &name) const
{
Symbol sym((Scope *)0,name,(Datatype *)0);
SymbolNameTree::const_iterator iter = nametree.lower_bound(&sym);
if (iter == nametree.end()) return false;
return ((*iter)->getName() == name);
}
string ScopeInternal::buildVariableName(const Address &addr,
const Address &pc,
Datatype *ct,int4 &index,uint4 flags) const
@ -2435,6 +2700,26 @@ void ScopeInternal::setCategory(Symbol *sym,int4 cat,int4 ind)
list[sym->catindex] = sym;
}
/// Run through all the symbols whose name is undefined. Build a variable name, uniquify it, and
/// rename the variable.
/// \param base is the base index to start at for generating generic names
void ScopeInternal::assignDefaultNames(int4 &base)
{
SymbolNameTree::const_iterator iter;
Symbol testsym((Scope *)0,"$$undef",(Datatype *)0);
iter = nametree.upper_bound(&testsym);
while(iter != nametree.end()) {
Symbol *sym = *iter;
if (!sym->isNameUndefined()) break;
++iter; // Advance before renaming
string nm = buildDefaultName(sym, base, (Varnode *)0);
renameSymbol(sym, nm);
}
}
/// Check to make sure the Scope is a \e namespace then remove all
/// its address ranges from the map.
/// \param scope is the given Scope
@ -2509,9 +2794,12 @@ void Database::attachScope(Scope *newscope,Scope *parent)
throw LowlevelError("Multiple global scopes");
if (newscope->name.size() != 0)
throw LowlevelError("Global scope does not have empty name");
newscope->assignId(0);
globalscope = newscope;
return;
}
newscope->assignId(nextScopeId);
nextScopeId += 1;
parent->attachScope(newscope);
}
@ -2525,7 +2813,7 @@ void Database::deleteScope(Scope *scope)
delete scope;
}
else {
ScopeKey key(scope->name,scope->dedupId);
ScopeKey key(scope->name,scope->uniqueId);
ScopeMap::iterator iter = scope->parent->children.find(key);
if (iter == scope->parent->children.end())
throw LowlevelError("Could not remove parent reference to: "+scope->name);

View file

@ -157,15 +157,17 @@ class Symbol {
protected:
Scope *scope; ///< The scope that owns this symbol
string name; ///< The local name of the symbol
uint4 nameDedup; ///< id to distinguish symbols with the same name
Datatype *type; ///< The symbol's data-type
uint4 nameDedup; ///< id to distinguish symbols with the same name
uint4 flags; ///< Varnode-like properties of the symbol
// only typelock,namelock,readonly,externref
// addrtied, persist inherited from scope
uint4 dispflags; ///< Flags affecting the display of this symbol
int2 category; ///< Special category (-1==none 0=parameter 1=equate)
uint2 catindex; ///< Index within category
uint8 symbolId; ///< Unique id, 0=unassigned
vector<list<SymbolEntry>::iterator> mapentry; ///< List of storage locations labeled with \b this Symbol
uint4 wholeCount; ///< Number of SymbolEntries that map to the whole Symbol
virtual ~Symbol(void) {} ///< Destructor
void setDisplayFormat(uint4 val); ///< Set the display format for \b this Symbol
void checkSizeTypeLock(void); ///< Calculate if \b size_typelock property is on
@ -177,18 +179,20 @@ public:
force_oct = 3, ///< Force octal printing of constant symbol
force_bin = 4, ///< Force binary printing of constant symbol
force_char = 5, ///< Force integer to be printed as a character constant
size_typelock = 8 ///< Only the size of the symbol is typelocked
size_typelock = 8, ///< Only the size of the symbol is typelocked
isolate = 16, ///< Symbol should not speculatively merge automatically
merge_problems = 32 ///< Set if some SymbolEntrys did not get merged
};
/// \brief Construct given a name and data-type
Symbol(Scope *sc,const string &nm,Datatype *ct)
{ scope=sc; name=nm; nameDedup=0; type=ct; flags=0; dispflags=0; category=-1; }
{ scope=sc; name=nm; nameDedup=0; type=ct; flags=0; dispflags=0; category=-1; symbolId=0; wholeCount=0; }
/// \brief Construct for use with restoreXml()
Symbol(Scope *sc) { scope=sc; nameDedup=0; flags=0; dispflags=0; category=-1; }
Symbol(Scope *sc) { scope=sc; nameDedup=0; flags=0; dispflags=0; category=-1; symbolId = 0; wholeCount=0; }
const string &getName(void) const { return name; } ///< Get the local name of the symbol
Datatype *getType(void) const { return type; } ///< Get the data-type
uint4 getId(void) const { return (uint4)(uintp)this; } ///< Get a unique id for the symbol
uint8 getId(void) const { return symbolId; } ///< Get a unique id for the symbol
uint4 getFlags(void) const { return flags; } ///< Get the boolean properties of the Symbol
uint4 getDisplayFormat(void) const { return (dispflags & 7); } ///< Get the format to display the Symbol in
int2 getCategory(void) const { return category; } ///< Get the Symbol category
@ -199,9 +203,18 @@ public:
bool isIndirectStorage(void) const { return ((flags&Varnode::indirectstorage)!=0); } ///< Is storage really a pointer to the true Symbol
bool isHiddenReturn(void) const { return ((flags&Varnode::hiddenretparm)!=0); } ///< Is this a reference to the function return value
bool isNameUndefined(void) const; ///< Does \b this have an undefined name
bool isMultiEntry(void) const { return (wholeCount > 1); } ///< Does \b this have more than one \e entire mapping
bool hasMergeProblems(void) const { return ((dispflags & merge_problems)!=0); } ///< Were some SymbolEntrys not merged
void setMergeProblems(void) { dispflags |= merge_problems; } ///< Mark that some SymbolEntrys could not be merged
bool isIsolated(void) const { return ((dispflags & isolate)!=0); } ///< Return \b true if \b this is isolated from speculative merging
void setIsolated(bool val); ///< Set whether \b this Symbol should be speculatively merged
Scope *getScope(void) const { return scope; } ///< Get the scope owning \b this Symbol
SymbolEntry *getFirstWholeMap(void) const; ///< Get the first entire mapping of the symbol
SymbolEntry *getMapEntry(const Address &addr) const; ///< Get first mapping of the symbol that contains the given Address
int4 numEntries(void) const { return mapentry.size(); } ///< Return the number of SymbolEntrys
SymbolEntry *getMapEntry(int4 i) const { return &(*mapentry[i]); } ///< Return the i-th SymbolEntry for \b this Symbol
int4 getMapEntryPosition(const SymbolEntry *entry) const; ///< Position of given SymbolEntry within \b this multi-entry Symbol
int4 getResolutionDepth(const Scope *useScope) const; ///< Get the number of scope names to print to resolve symbol in given context
void saveXmlHeader(ostream &s) const; ///< Save basic Symbol properties as XML attributes
void restoreXmlHeader(const Element *el); ///< Restore basic Symbol properties from XML
void saveXmlBody(ostream &s) const; ///< Save details of the Symbol to XML
@ -209,6 +222,7 @@ public:
virtual void saveXml(ostream &s) const; ///< Save \b this Symbol to an XML stream
virtual void restoreXml(const Element *el); ///< Restore \b this Symbol from an XML stream
virtual int4 getBytesConsumed(void) const; ///< Get number of bytes consumed within the address->symbol map
static uint8 ID_BASE; ///< Base of internal ID's
};
/// Force a specific display format for constant symbols
@ -404,12 +418,13 @@ class Scope {
ScopeMap children; ///< Sorted list of child scopes
void attachScope(Scope *child); ///< Attach a new child Scope to \b this
void detachScope(ScopeMap::iterator iter); ///< Detach a child Scope from \b this
void assignId(uint4 val) { uniqueId = val; } ///< Let the database assign a unique id to \b this scope
protected:
Architecture *glb; ///< Architecture of \b this scope
string name; ///< Name of \b this scope
Funcdata *fd; ///< (If non-null) the function which \b this is the local Scope for
uint4 dedupId; ///< Id to dedup scopes with same name (when allowed)
uint4 uniqueId; ///< Unique id for the scope, for deduping scope names, assigning symbol ids
static const Scope *stackAddr(const Scope *scope1,
const Scope *scope2,
const Address &addr,
@ -479,6 +494,7 @@ protected:
virtual SymbolEntry *addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 hash,int4 off,int4 sz,
const RangeList &uselim)=0;
SymbolEntry *addMap(const SymbolEntry &entry); ///< Integrate a SymbolEntry into the range maps
void setSymbolId(Symbol *sym,uint8 id) const { sym->symbolId = id; } ///< Adjust the id associated with a symbol
public:
#ifdef OPACTION_DEBUG
mutable bool debugon;
@ -487,7 +503,7 @@ public:
#endif
/// \brief Construct an empty scope, given a name and Architecture
Scope(const string &nm,Architecture *g) {
name = nm; glb = g; parent = (Scope *)0; fd = (Funcdata *)0; dedupId = 0;
name = nm; glb = g; parent = (Scope *)0; fd = (Funcdata *)0; uniqueId = 0;
#ifdef OPACTION_DEBUG
debugon = false;
#endif
@ -515,6 +531,7 @@ public:
virtual bool inScope(const Address &addr,int4 size, const Address &usepoint) const {
return rangetree.inRange(addr,size); }
virtual void removeSymbolMappings(Symbol *symbol)=0; ///< Remove all SymbolEntrys from the given Symbol
virtual void removeSymbol(Symbol *symbol)=0; ///< Remove the given Symbol from \b this Scope
virtual void renameSymbol(Symbol *sym,const string &newname)=0; ///< Rename a Symbol within \b this Scope
@ -587,6 +604,14 @@ public:
/// \param res will contain any matching Symbols
virtual void findByName(const string &name,vector<Symbol *> &res) const=0;
/// \brief Check if the given name is used within \b this scope.
///
/// Only \b this scope is checked. If one or more symbols exist with the given name,
/// \b true is returned.
/// \param name is the given name to check for
/// \return \b true if the name is used within \b this scope
virtual bool isNameUsed(const string &name) const=0;
/// \brief Convert an \e external \e reference to the referenced function
///
/// \param sym is the Symbol marking the external reference
@ -669,6 +694,9 @@ public:
bool isSubScope(const Scope *scp) const; ///< Is this a sub-scope of the given Scope
string getFullName(void) const; ///< Get the full name of \b this Scope
void getNameSegments(vector<string> &vec) const; ///< Get the fullname of \b this in segments
void getScopePath(vector<Scope *> &vec) const; ///< Get the ordered list of parent scopes to \b this
bool isNameUsed(const string &nm,const Scope *op2) const; ///< Is the given name in use within given scope path
const Scope *findDistinguishingScope(const Scope *op2) const; ///< Find first ancestor of \b this not shared by given scope
Architecture *getArch(void) const { return glb; } ///< Get the Architecture associated with \b this
Scope *getParent(void) const { return parent; } ///< Get the parent Scope (or NULL if \b this is the global Scope)
Symbol *addSymbol(const string &name,Datatype *ct); ///< Add a new Symbol \e without mapping it to an address
@ -679,6 +707,7 @@ public:
ExternRefSymbol *addExternalRef(const Address &addr,const Address &refaddr,const string &nm);
LabSymbol *addCodeLabel(const Address &addr,const string &nm);
Symbol *addDynamicSymbol(const string &nm,Datatype *ct,const Address &caddr,uint8 hash);
string buildDefaultName(Symbol *sym,int4 &base,Varnode *vn) const; ///< Create a default name for the given Symbol
bool isReadOnly(const Address &addr,int4 size,const Address &usepoint) const;
void printBounds(ostream &s) const { rangetree.printBounds(s); } ///< Print a description of \b this Scope's \e owned memory ranges
};
@ -702,6 +731,8 @@ protected:
vector<EntryMap *> maptable; ///< Rangemaps of SymbolEntry, one map for each address space
vector<vector<Symbol *> > category; ///< References to Symbol objects organized by category
list<SymbolEntry> dynamicentry; ///< Dynamic symbol entries
SymbolNameTree multiEntrySet; ///< Set of symbols with multiple entries
uint8 nextUniqueId; ///< Next available symbol id
public:
ScopeInternal(const string &nm,Architecture *g); ///< Construct the Scope
virtual void clear(void);
@ -716,6 +747,7 @@ public:
virtual list<SymbolEntry>::const_iterator endDynamic(void) const;
virtual list<SymbolEntry>::iterator beginDynamic(void);
virtual list<SymbolEntry>::iterator endDynamic(void);
virtual void removeSymbolMappings(Symbol *symbol);
virtual void removeSymbol(Symbol *symbol);
virtual void renameSymbol(Symbol *sym,const string &newname);
virtual void retypeSymbol(Symbol *sym,Datatype *ct);
@ -734,6 +766,7 @@ public:
virtual SymbolEntry *findOverlap(const Address &addr,int4 size) const;
virtual void findByName(const string &name,vector<Symbol *> &res) const;
virtual bool isNameUsed(const string &name) const;
virtual Funcdata *resolveExternalRefFunction(ExternRefSymbol *sym) const;
virtual string buildVariableName(const Address &addr,
@ -747,6 +780,9 @@ public:
virtual int4 getCategorySize(int4 cat) const;
virtual Symbol *getCategorySymbol(int4 cat,int4 ind) const;
virtual void setCategory(Symbol *sym,int4 cat,int4 ind);
void assignDefaultNames(int4 &base); ///< Assign a default name (via buildVariableName) to any unnamed symbol
set<Symbol *>::const_iterator beginMultiEntry(void) const { return multiEntrySet.begin(); } ///< Start of symbols with more than one entry
set<Symbol *>::const_iterator endMultiEntry(void) const { return multiEntrySet.end(); } ///< End of symbols with more than one entry
static void savePathXml(ostream &s,const vector<string> &vec); ///< Save a path with \<val> tags
static void restorePathXml(vector<string> &vec,const Element *el); ///< Restore path from \<val> tags
};
@ -804,12 +840,13 @@ class Database {
Scope *globalscope; ///< A quick reference to the \e global Scope
ScopeResolve resolvemap; ///< The Address to \e namespace map
partmap<Address,uint4> flagbase; ///< Map of global properties
uint4 nextScopeId; ///< Id for next attached scope (0 reserved for global scope)
void clearResolve(Scope *scope); ///< Clear the \e ownership ranges associated with the given Scope
void clearResolveRecursive(Scope *scope); ///< Clear the \e ownership ranges of a given Scope and its children
void fillResolve(Scope *scope); ///< Add the \e ownership ranges of the given Scope to the map
static void parseParentTag(const Element *el,string &name,vector<string> &parnames);
public:
Database(Architecture *g) { glb=g; globalscope=(Scope *)0; flagbase.defaultValue() = 0; } ///< Constructor
Database(Architecture *g) { glb=g; globalscope=(Scope *)0; flagbase.defaultValue()=0; nextScopeId=1; } ///< Constructor
~Database(void); ///< Destructor
Architecture *getArch(void) const { return glb; } ///< Get the Architecture associate with \b this
void attachScope(Scope *newscope,Scope *parent); ///< Register a new Scope

View file

@ -89,6 +89,7 @@ public:
virtual SymbolEntry *findOverlap(const Address &addr,int4 size) const { throw LowlevelError("findOverlap unimplemented"); }
virtual void findByName(const string &name,vector<Symbol *> &res) const { throw LowlevelError("findByName unimplemented"); }
virtual bool isNameUsed(const string &name) const { throw LowlevelError("isNameUsed unimplemented"); }
virtual MapIterator begin(void) const { throw LowlevelError("begin unimplemented"); }
virtual MapIterator end(void) const { throw LowlevelError("end unimplemented"); }
@ -100,6 +101,7 @@ public:
virtual void clearUnlockedCategory(int4 cat) { throw LowlevelError("clearUnlockedCategory unimplemented"); }
virtual void clearUnlocked(void) { throw LowlevelError("clearUnlocked unimplemented"); }
virtual void restrictScope(Funcdata *f) { throw LowlevelError("restrictScope unimplemented"); }
virtual void removeSymbolMappings(Symbol *symbol) { throw LowlevelError("removeSymbolMappings unimplemented"); }
virtual void removeSymbol(Symbol *symbol) { throw LowlevelError("removeSymbol unimplemented"); }
virtual void renameSymbol(Symbol *sym,const string &newname) { throw LowlevelError("renameSymbol unimplemented"); }
virtual void retypeSymbol(Symbol *sym,Datatype *ct) { throw LowlevelError("retypeSymbol unimplemented"); }

View file

@ -393,7 +393,7 @@ void DynamicHash::uniqueHash(const Varnode *root,Funcdata *fd)
/// \param addr is the given address
/// \param h is the hash
/// \return the matching Varnode or NULL
Varnode *DynamicHash::findVarnode(Funcdata *fd,const Address &addr,uint8 h)
Varnode *DynamicHash::findVarnode(const Funcdata *fd,const Address &addr,uint8 h)
{
uint4 method = getMethodFromHash(h);
@ -423,7 +423,7 @@ Varnode *DynamicHash::findVarnode(Funcdata *fd,const Address &addr,uint8 h)
/// \param fd is the function holding the data-flow
/// \param addr is the given address
/// \param h is the given hash
void DynamicHash::gatherFirstLevelVars(vector<Varnode *> &varlist,Funcdata *fd,const Address &addr,uint8 h)
void DynamicHash::gatherFirstLevelVars(vector<Varnode *> &varlist,const Funcdata *fd,const Address &addr,uint8 h)
{
OpCode opc = getOpCodeFromHash(h);

View file

@ -79,11 +79,11 @@ public:
void clear(void); ///< Called for each additional hash (after the first)
void calcHash(const Varnode *root,uint4 method); ///< Calculate the hash for given Varnode and method
void uniqueHash(const Varnode *root,Funcdata *fd); ///< Select a unique hash for the given Varnode
Varnode *findVarnode(Funcdata *fd,const Address &addr,uint8 h);
Varnode *findVarnode(const Funcdata *fd,const Address &addr,uint8 h);
uint8 getHash(void) const { return hash; } ///< Get the (current) hash
const Address &getAddress(void) const { return addrresult; } ///< Get the (current) address
static void gatherFirstLevelVars(vector<Varnode *> &varlist,Funcdata *fd,const Address &addr,uint8 h);
static void gatherFirstLevelVars(vector<Varnode *> &varlist,const Funcdata *fd,const Address &addr,uint8 h);
static int4 getSlotFromHash(uint8 h); ///< Retrieve the encoded slot from a hash
static uint4 getMethodFromHash(uint8 h); ///< Retrieve the encoded method from a hash
static OpCode getOpCodeFromHash(uint8 h); ///< Retrieve the encoded op-code from a hash

View file

@ -2478,14 +2478,7 @@ ProtoParameter *ProtoStoreSymbol::setInput(int4 i, const string &nm,const Parame
if (res->sym == (Symbol *)0) {
if (scope->discoverScope(pieces.addr,pieces.type->getSize(),usepoint) != scope)
usepoint = restricted_usepoint;
string name;
if (nm.size()==0) {
int4 index = i+1;
name = scope->buildVariableName(pieces.addr,usepoint,pieces.type,index,Varnode::input);
}
else
name = nm;
res->sym = scope->addSymbol(name,pieces.type,pieces.addr,usepoint)->getSymbol();
res->sym = scope->addSymbol(nm,pieces.type,pieces.addr,usepoint)->getSymbol();
scope->setCategory(res->sym,0,i);
if ((pieces.flags & (Varnode::indirectstorage|Varnode::hiddenretparm)) != 0)
scope->setAttribute(res->sym,pieces.flags & (Varnode::indirectstorage|Varnode::hiddenretparm));

View file

@ -607,7 +607,6 @@ void Funcdata::saveVarnodeXml(ostream &s,VarnodeLocSet::const_iterator iter,Varn
void Funcdata::saveXmlHigh(ostream &s) const
{
int4 j;
Varnode *vn;
HighVariable *high;
@ -620,32 +619,7 @@ void Funcdata::saveXmlHigh(ostream &s) const
high = vn->getHigh();
if (high->isMark()) continue;
high->setMark();
vn = high->getNameRepresentative(); // Get representative varnode
s << "<high ";
// a_v(s,"name",high->getName());
a_v_u(s,"repref",vn->getCreateIndex());
if (high->isSpacebase()||high->isImplied()) // This is a special variable
a_v(s,"class",string("other"));
else if (high->isPersist()&&high->isAddrTied()) // Global variable
a_v(s,"class",string("global"));
else if (high->isConstant())
a_v(s,"class",string("constant"));
else if (!high->isPersist())
a_v(s,"class",string("local"));
else
a_v(s,"class",string("other"));
if (high->isTypeLock())
a_v_b(s,"typelock",true);
if (high->getSymbol() != (Symbol *)0)
a_v_u(s,"symref",high->getSymbol()->getId());
s << '>';
high->getType()->saveXml(s);
for(j=0;j<high->numInstances();++j) {
s << "<addr ";
a_v_u(s,"ref",high->getInstance(j)->getCreateIndex());
s << "/>";
}
s << "</high>";
high->saveXml(s);
}
for(iter=beginLoc();iter!=endLoc();++iter) {
vn = *iter;
@ -708,11 +682,14 @@ void Funcdata::saveXmlTree(ostream &s) const
/// If indicated by the caller, a description of the entire PcodeOp and Varnode
/// tree is also emitted.
/// \param s is the output stream
/// \param id is the unique id associated with the function symbol
/// \param savetree is \b true if the p-code tree should be emitted
void Funcdata::saveXml(ostream &s,bool savetree) const
void Funcdata::saveXml(ostream &s,uint8 id,bool savetree) const
{
s << "<function";
if (id != 0)
a_v_u(s, "id", id);
a_v(s,"name",name);
a_v_i(s,"size",size);
if (hasNoCode())
@ -738,22 +715,30 @@ void Funcdata::saveXml(ostream &s,bool savetree) const
/// From an XML \<function> tag, recover the name, address, prototype, symbol,
/// jump-table, and override information for \b this function.
/// \param el is the root \<function> tag
void Funcdata::restoreXml(const Element *el)
/// \return the symbol id associated with the function
uint8 Funcdata::restoreXml(const Element *el)
{
// clear(); // Shouldn't be needed
name.clear();
size = -1;
uint8 id = 0;
AddrSpace *stackid = glb->getStackSpace();
for(int4 i=0;i<el->getNumAttributes();++i) {
if (el->getAttributeName(i) == "name")
const string &attrName(el->getAttributeName(i));
if (attrName == "name")
name = el->getAttributeValue(i);
else if (el->getAttributeName(i) == "size") {
else if (attrName == "size") {
istringstream s( el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> size;
}
else if (el->getAttributeName(i) == "nocode") {
else if (attrName == "id") {
istringstream s( el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> id;
}
else if (attrName == "nocode") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= no_code;
}
@ -805,6 +790,7 @@ void Funcdata::restoreXml(const Element *el)
funcp.setScope(localmap,baseaddr+ -1);
}
localmap->resetLocalWindow();
return id;
}
/// \brief Inject p-code from a \e payload into \b this live function

View file

@ -171,8 +171,8 @@ public:
void printVarnodeTree(ostream &s) const; ///< Print a description of all Varnodes to a stream
void printBlockTree(ostream &s) const; ///< Print a description of control-flow structuring to a stream
void printLocalRange(ostream &s) const; ///< Print description of memory ranges associated with local scopes
void saveXml(ostream &s,bool savetree) const; ///< Emit an XML description of \b this function to stream
void restoreXml(const Element *el); ///< Restore the state of \b this function from an XML description
void saveXml(ostream &s,uint8 id,bool savetree) const; ///< Emit an XML description of \b this function to stream
uint8 restoreXml(const Element *el); ///< Restore the state of \b this function from an XML description
void saveXmlJumpTable(ostream &s) const; ///< Emit an XML description of jump-tables to stream
void restoreXmlJumpTable(const Element *el); ///< Restore jump-tables from an XML description
void saveXmlTree(ostream &s) const; ///< Save an XML description of the p-code tree to stream
@ -382,9 +382,16 @@ public:
void clearDeadVarnodes(void); ///< Delete any dead Varnodes
void calcNZMask(void); ///< Calculate \e non-zero masks for all Varnodes
void clearDeadOps(void) { obank.destroyDead(); } ///< Delete any dead PcodeOps
void clearSymbolLinks(HighVariable *high); ///< Clear Symbols attached to Varnodes in the given HighVariable
void remapVarnode(Varnode *vn,Symbol *sym,const Address &usepoint);
void remapDynamicVarnode(Varnode *vn,Symbol *sym,const Address &usepoint,uint8 hash);
Symbol *linkSymbol(Varnode *vn); ///< Find or create Symbol associated with given Varnode
Symbol *linkSymbolReference(Varnode *vn); ///< Discover and attach Symbol to a constant reference
Varnode *findLinkedVarnode(SymbolEntry *entry) const; ///< Find a Varnode matching the given Symbol mapping
void findLinkedVarnodes(SymbolEntry *entry,vector<Varnode *> &res) const; ///< Find Varnodes that map to the given SymbolEntry
void buildDynamicSymbol(Varnode *vn); ///< Build a \e dynamic Symbol associated with the given Varnode
bool attemptDynamicMapping(SymbolEntry *entry,DynamicHash &dhash);
bool attemptDynamicMappingLate(SymbolEntry *entry,DynamicHash &dhash);
Merge &getMerge(void) { return covermerge; } ///< Get the Merge object for \b this function
// op routines

View file

@ -525,6 +525,7 @@ Varnode *Funcdata::opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Var
/// CPUI_INT_MULT PcodeOp. If finalization is requested and a new PcodeOp is needed, the output
/// Varnode is marked as \e implicit and has its data-type set
/// \param op is the given PTRADD
/// \param finalize is \b true if finalization is needed for any new PcodeOp
void Funcdata::opUndoPtradd(PcodeOp *op,bool finalize)
{

View file

@ -27,12 +27,10 @@ void Funcdata::setVarnodeProperties(Varnode *vn) const
// One more chance to find entry, now that we know usepoint
uint4 vflags=0;
SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),vn->getUsePoint(*this),vflags);
if (entry != (SymbolEntry *)0) { // Let entry try to force type
entry->updateType(vn);
if (entry->getSymbol()->isTypeLocked())
vn->mapentry = entry;
}
vn->setFlags(vflags & ~Varnode::typelock); // typelock set by updateType
if (entry != (SymbolEntry *)0) // Let entry try to force type
vn->setSymbolProperties(entry);
else
vn->setFlags(vflags & ~Varnode::typelock); // typelock set by updateType
}
if (vn->cover == (Cover *)0) {
@ -108,12 +106,10 @@ Varnode *Funcdata::newVarnodeOut(int4 s,const Address &m,PcodeOp *op)
uint4 vflags = 0;
SymbolEntry *entry = localmap->queryProperties(m,s,op->getAddr(),vflags);
if (entry != (SymbolEntry *)0) {
entry->updateType(vn);
if (entry->getSymbol()->isTypeLocked())
vn->mapentry = entry;
}
vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
if (entry != (SymbolEntry *)0)
vn->setSymbolProperties(entry);
else
vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
return vn;
}
@ -153,12 +149,10 @@ Varnode *Funcdata::newVarnode(int4 s,const Address &m,Datatype *ct)
uint4 vflags=0;
SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),Address(),vflags);
if (entry != (SymbolEntry *)0) { // Let entry try to force type
entry->updateType(vn);
if (entry->getSymbol()->isTypeLocked())
vn->mapentry = entry;
}
vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
if (entry != (SymbolEntry *)0) // Let entry try to force type
vn->setSymbolProperties(entry);
else
vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
return vn;
}
@ -313,18 +307,10 @@ HighVariable *Funcdata::findHigh(const string &name) const
localmap->queryByName(name,symList);
if (symList.empty()) return (HighVariable *)0;
Symbol *sym = symList[0];
SymbolEntry *entry = sym->getFirstWholeMap();
VarnodeLocSet::const_iterator iter,enditer;
HighVariable *high;
Varnode *vn = findLinkedVarnode(sym->getFirstWholeMap());
if (vn != (Varnode *)0)
return vn->getHigh();
iter = vbank.beginLoc(entry->getSize(),entry->getAddr());
enditer = vbank.endLoc(entry->getSize(),entry->getAddr());
for(;iter!=enditer;++iter) {
high = (*iter)->getHigh();
if (high->getSymbol() == sym)
return high;
}
return (HighVariable *)0;
}
@ -913,7 +899,16 @@ bool Funcdata::syncVarnodesWithSymbol(VarnodeLocSet::const_iterator &iter,uint4
vn = *iter++;
if (vn->isFree()) continue;
vnflags = vn->getFlags();
if ((vnflags & mask) != flags) { // We have a change
if (vn->mapentry != (SymbolEntry *)0) { // If there is already an attached SymbolEntry (dynamic)
uint4 localMask = mask & ~Varnode::mapped; // Make sure 'mapped' bit is unchanged
uint4 localFlags = flags & localMask;
if ((vnflags & localMask) != localFlags) {
updateoccurred = true;
vn->setFlags(localFlags);
vn->clearFlags((~localFlags)&localMask);
}
}
else if ((vnflags & mask) != flags) { // We have a change
updateoccurred = true;
vn->setFlags(flags);
vn->clearFlags((~flags)&mask);
@ -927,6 +922,49 @@ bool Funcdata::syncVarnodesWithSymbol(VarnodeLocSet::const_iterator &iter,uint4
return updateoccurred;
}
/// For each instance Varnode, remove any SymbolEntry reference and associated properties.
/// \param high is the given HighVariable to clear
void Funcdata::clearSymbolLinks(HighVariable *high)
{
for(int4 i=0;i<high->numInstances();++i) {
Varnode *vn = high->getInstance(i);
vn->mapentry = (SymbolEntry *)0;
vn->clearFlags(Varnode::namelock | Varnode::typelock | Varnode::mapped);
}
}
/// \brief Remap a Symbol to a given Varnode using a static mapping
///
/// Any previous links between the Symbol, the Varnode, and the associate HighVariable are
/// removed. Then a new link is created.
/// \param vn is the given Varnode
/// \param sym is the Symbol the Varnode maps to
/// \param usepoint is the desired usepoint for the mapping
void Funcdata::remapVarnode(Varnode *vn,Symbol *sym,const Address &usepoint)
{
clearSymbolLinks(vn->getHigh());
SymbolEntry *entry = localmap->remapSymbol(sym, vn->getAddr(), usepoint);
vn->setSymbolEntry(entry);
}
/// \brief Remap a Symbol to a given Varnode using a new dynamic mapping
///
/// Any previous links between the Symbol, the Varnode, and the associate HighVariable are
/// removed. Then a new dynamic link is created.
/// \param vn is the given Varnode
/// \param sym is the Symbol the Varnode maps to
/// \param usepoint is the code Address where the Varnode is defined
/// \param hash is the hash for the new dynamic mapping
void Funcdata::remapDynamicVarnode(Varnode *vn,Symbol *sym,const Address &usepoint,uint8 hash)
{
clearSymbolLinks(vn->getHigh());
SymbolEntry *entry = localmap->remapSymbolDynamic(sym, hash, usepoint);
vn->setSymbolEntry(entry);
}
/// The Symbol is really attached to the Varnode's HighVariable (which must exist).
/// The only reason a Symbol doesn't get set is if, the HighVariable
/// is global and there is no pre-existing Symbol. (see mapGlobals())
@ -941,38 +979,117 @@ Symbol *Funcdata::linkSymbol(Varnode *vn)
Symbol *sym = high->getSymbol();
if (sym != (Symbol *)0) return sym; // Symbol already assigned
entry = vn->getSymbolEntry(); // Check if we have a symbol already cached
if (entry == (SymbolEntry *)0) {
Address usepoint = vn->getUsePoint(*this);
// Find any entry overlapping base address
entry = localmap->queryProperties(vn->getAddr(),1,usepoint,flags);
if (entry != (SymbolEntry *)0) {
sym = entry->getSymbol();
}
else { // Must create a symbol entry
if (!vn->isPersist()) { // Only create local symbol
entry = localmap->addSymbol("",high->getType(),vn->getAddr(),usepoint);
sym = entry->getSymbol();
}
}
}
else
Address usepoint = vn->getUsePoint(*this);
// Find any entry overlapping base address
entry = localmap->queryProperties(vn->getAddr(), 1, usepoint, flags);
if (entry != (SymbolEntry *) 0) {
sym = entry->getSymbol();
if (sym != (Symbol *)0) {
int4 offset;
if (sym->getCategory() == 1) // For equates we don't care about size
offset = -1;
else if ((sym->getType()->getSize() == vn->getSize())&&
(entry->getAddr() == vn->getAddr())&&(!entry->isPiece())) // A matching entry
offset = -1;
else
offset = vn->getAddr().overlap(0,entry->getAddr(),sym->getType()->getSize()) + entry->getOffset();
high->setSymbol(sym,offset);
vn->setSymbolEntry(entry);
}
else { // Must create a symbol entry
if (!vn->isPersist()) { // Only create local symbol
entry = localmap->addSymbol("", high->getType(), vn->getAddr(), usepoint);
sym = entry->getSymbol();
vn->setSymbolEntry(entry);
}
}
return sym;
}
/// A reference to a symbol (i.e. &varname) is typically stored as a PTRSUB operation, where the
/// first input Varnode is a \e spacebase Varnode indicating whether the symbol is on the \e stack or at
/// a \e global RAM location. The second input Varnode is a constant encoding the address of the symbol.
/// This method takes this constant Varnode, recovers the symbol it is referring to, and stores
/// on the HighVariable object attached to the Varnode.
/// \param vn is the constant Varnode (second input) to a PTRSUB operation
/// \return the symbol being referred to or null
Symbol *Funcdata::linkSymbolReference(Varnode *vn)
{
PcodeOp *op = vn->loneDescend();
Varnode *in0 = op->getIn(0);
TypePointer *ptype = (TypePointer *)in0->getHigh()->getType();
if (ptype->getMetatype() != TYPE_PTR) return (Symbol *)0;
TypeSpacebase *sb = (TypeSpacebase *)ptype->getPtrTo();
if (sb->getMetatype() != TYPE_SPACEBASE)
return (Symbol *)0;
Scope *scope = sb->getMap();
Address addr = sb->getAddress(vn->getOffset(),in0->getSize(),op->getAddr());
if (addr.isInvalid())
throw LowlevelError("Unable to generate proper address from spacebase");
SymbolEntry *entry = scope->queryContainer(addr,1,Address());
if (entry == (SymbolEntry *)0)
return (Symbol *)0;
int4 off = (int4)(addr.getOffset() - entry->getAddr().getOffset()) + entry->getOffset();
vn->setSymbolReference(entry, off);
return entry->getSymbol();
}
/// Return the (first) Varnode that matches the given SymbolEntry
/// \param entry is the given SymbolEntry
/// \return a matching Varnode or null
Varnode *Funcdata::findLinkedVarnode(SymbolEntry *entry) const
{
if (entry->isDynamic()) {
DynamicHash dhash;
Varnode *vn = dhash.findVarnode(this, entry->getFirstUseAddress(), entry->getHash());
if (vn == (Varnode *)0 || vn->isAnnotation())
return (Varnode *)0;
return vn;
}
VarnodeLocSet::const_iterator iter,enditer;
Address usestart = entry->getFirstUseAddress();
enditer = vbank.endLoc(entry->getSize(),entry->getAddr());
if (usestart.isInvalid()) {
iter = vbank.beginLoc(entry->getSize(),entry->getAddr());
if (iter == enditer)
return (Varnode *)0;
Varnode *vn = *iter;
if (!vn->isAddrTied())
return (Varnode *)0; // Varnode(s) must be address tied in order to match this symbol
return vn;
}
iter = vbank.beginLoc(entry->getSize(),entry->getAddr(),usestart,~((uintm)0));
// TODO: Use a better end iterator
for(;iter!=enditer;++iter) {
Varnode *vn = *iter;
Address usepoint = vn->getUsePoint(*this);
if (entry->inUse(usepoint))
return vn;
}
return (Varnode *)0;
}
/// Look for Varnodes that are (should be) mapped to the given SymbolEntry and
/// add them to the end of the result list.
/// \param entry is the given SymbolEntry to match
/// \param res is the container holding the result list of matching Varnodes
void Funcdata::findLinkedVarnodes(SymbolEntry *entry,vector<Varnode *> &res) const
{
if (entry->isDynamic()) {
DynamicHash dhash;
Varnode *vn = dhash.findVarnode(this,entry->getFirstUseAddress(),entry->getHash());
if (vn != (Varnode *)0)
res.push_back(vn);
}
else {
VarnodeLocSet::const_iterator iter = beginLoc(entry->getSize(),entry->getAddr());
VarnodeLocSet::const_iterator enditer = endLoc(entry->getSize(),entry->getAddr());
for(;iter!=enditer;++iter) {
Varnode *vn = *iter;
Address addr = vn->getUsePoint(*this);
if (entry->inUse(addr)) {
res.push_back(vn);
}
}
}
}
/// If a Symbol is already attached, no change is made. Otherwise a special \e dynamic Symbol is
/// created that is associated with the Varnode via a hash of its local data-flow (rather
/// than its storage address).
@ -994,7 +1111,7 @@ void Funcdata::buildDynamicSymbol(Varnode *vn)
throw RecovError("Unable to find unique hash for varnode");
Symbol *sym = localmap->addDynamicSymbol("",high->getType(),dhash.getAddress(),dhash.getHash());
high->setSymbol(sym,-1);
vn->setSymbolEntry(sym->getFirstWholeMap());
}
/// \brief Map properties of a dynamic symbol to a Varnode
@ -1015,23 +1132,72 @@ bool Funcdata::attemptDynamicMapping(SymbolEntry *entry,DynamicHash &dhash)
if (vn == (Varnode *)0) return false;
if (entry->getSymbol()->getCategory() == 1) { // Is this an equate symbol
if (vn->mapentry != entry) { // Check we haven't marked this before
uint4 flags = entry->getAllFlags(); // Mark that the varnode is mapped
vn->setFlags(flags & ~Varnode::typelock); // Don't pass data-type and typelock to Varnode
vn->mapentry = entry;
vn->setSymbolEntry(entry);
return true;
}
}
else if (entry->updateType(vn)) {
if (entry->getSize() != vn->getSize()) return false;
uint4 flags = entry->getAllFlags();
vn->setFlags(flags & ~Varnode::typelock); // Mark that the varnode is mapped
if (entry->getSymbol()->isTypeLocked())
vn->mapentry = entry;
return true;
else if (entry->getSize() == vn->getSize()) {
if (vn->setSymbolProperties(entry))
return true;
}
return false;
}
/// \brief Map the name of a dynamic symbol to a Varnode
///
/// Given a dynamic mapping, try to find the mapped Varnode, then attach the Symbol to the Varnode.
/// The name of the Symbol is used, but the data-type and possibly other properties are not
/// put on the Varnode.
/// \param entry is the (dynamic) Symbol entry
/// \param dhash is the dynamic mapping information
/// \return \b true if a Varnode was adjusted
bool Funcdata::attemptDynamicMappingLate(SymbolEntry *entry,DynamicHash &dhash)
{
dhash.clear();
Varnode *vn = dhash.findVarnode(this,entry->getFirstUseAddress(),entry->getHash());
if (vn == (Varnode *)0)
return false;
if (vn->getSymbolEntry() == entry) return false; // Already applied it
Symbol *sym = entry->getSymbol();
if (vn->getSize() != entry->getSize()) {
ostringstream s;
s << "Unable to use symbol ";
if (!sym->isNameUndefined())
s << sym->getName() << ' ';
s << ": Size does not match variable it labels";
warningHeader(s.str());
return false;
}
if (vn->isImplied()) { // This should be finding an explicit, but a cast may have been inserted
Varnode *newvn = (Varnode *)0;
// Look at the "other side" of the cast
if (vn->isWritten() && (vn->getDef()->code() == CPUI_CAST))
newvn = vn->getDef()->getIn(0);
else {
PcodeOp *castop = vn->loneDescend();
if ((castop != (PcodeOp *)0)&&(castop->code() == CPUI_CAST))
newvn = castop->getOut();
}
// See if the varnode on the other side is explicit
if ((newvn != (Varnode *)0)&&(newvn->isExplicit()))
vn = newvn; // in which case we use it
}
vn->setSymbolEntry(entry);
if (!sym->isTypeLocked()) { // If the dynamic symbol did not lock its type
localmap->retypeSymbol(sym,vn->getType()); // use the type propagated into the varnode
}
else if (sym->getType() != vn->getType()) {
ostringstream s;
s << "Unable to use type for symbol " << sym->getName();
warningHeader(s.str());
localmap->retypeSymbol(sym,vn->getType()); // use the type propagated into the varnode
}
return true;
}
/// \brief Replace all read references to the first Varnode with a second Varnode
///
/// \param vn is the first Varnode (being replaced)

View file

@ -314,7 +314,7 @@ void DecompileAt::rawAction(void)
ParamIDAnalysis pidanalysis( fd, false );
pidanalysis.saveXml( sout, true );
}
fd->saveXml(sout,ghidra->getSendSyntaxTree());
fd->saveXml(sout,0,ghidra->getSendSyntaxTree());
if (ghidra->getSendCCode()&&
(ghidra->allacts.getCurrentName() == "decompile"))
ghidra->print->docFunction(fd);

View file

@ -1270,6 +1270,7 @@ void IfcTypeVarnode::execute(istream &s)
scope = dcp->fd->getScopeLocal(); // force it to be in function scope
Symbol *sym = scope->addSymbol(name,ct,loc,pc)->getSymbol();
scope->setAttribute(sym,Varnode::typelock);
sym->setIsolated(true);
if (name.size() > 0)
scope->setAttribute(sym,Varnode::namelock);

View file

@ -103,6 +103,15 @@ bool Merge::mergeTestRequired(HighVariable *high_out,HighVariable *high_in)
else if (high_out->isExtraOut())
return false;
Symbol *symbolIn = high_in->getSymbol();
Symbol *symbolOut = high_out->getSymbol();
if (symbolIn != (Symbol *) 0 && symbolOut != (Symbol *) 0) {
if (symbolIn != symbolOut)
return false; // Map to different symbols
if (high_in->getSymbolOffset() != high_out->getSymbolOffset())
return false; // Map to different parts of same symbol
}
return true;
}
@ -136,6 +145,14 @@ bool Merge::mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in)
Varnode *vn = high_in->getInputVarnode();
if (vn->isIllegalInput()&&(!vn->isIndirectOnly())) return false;
}
Symbol *symbol = high_in->getSymbol();
if (symbol != (Symbol *)0)
if (symbol->isIsolated())
return false;
symbol = high_out->getSymbol();
if (symbol != (Symbol *)0)
if (symbol->isIsolated())
return false;
return true;
}
@ -151,9 +168,6 @@ bool Merge::mergeTestSpeculative(HighVariable *high_out,HighVariable *high_in)
{
if (!mergeTestAdjacent(high_out,high_in)) return false;
// Don't merge a mapped variable speculatively
if (high_out->isMapped()) return false;
if (high_in->isMapped()) return false;
// Don't merge anything with a global speculatively
if (high_out->isPersist()) return false;
if (high_in->isPersist()) return false;
@ -268,7 +282,7 @@ void Merge::mergeOpcode(OpCode opc)
vn2 = op->getIn(j);
if (!mergeTestBasic(vn2)) continue;
if (mergeTestRequired(vn1->getHigh(),vn2->getHigh()))
merge(vn1->getHigh(),vn2->getHigh(),false);
merge(vn1->getHigh(),vn2->getHigh(),false); // This is a required merge
}
}
}
@ -798,6 +812,67 @@ void Merge::mergeMarker(void)
}
}
/// \brief Merge together Varnodes mapped to SymbolEntrys from the same Symbol
///
/// Symbols that have more than one SymbolEntry may attach to more than one Varnode.
/// These Varnodes need to be merged to properly represent a single variable.
void Merge::mergeMultiEntry(void)
{
SymbolNameTree::const_iterator iter = data.getScopeLocal()->beginMultiEntry();
SymbolNameTree::const_iterator enditer = data.getScopeLocal()->endMultiEntry();
for(;iter!=enditer;++iter) {
vector<Varnode *> mergeList;
Symbol *symbol = *iter;
int4 numEntries = symbol->numEntries();
int4 mergeCount = 0;
int4 skipCount = 0;
int4 conflictCount = 0;
for(int4 i=0;i<numEntries;++i) {
int4 prevSize = mergeList.size();
SymbolEntry *entry = symbol->getMapEntry(i);
if (entry->getSize() != symbol->getType()->getSize())
continue;
data.findLinkedVarnodes(entry, mergeList);
if (mergeList.size() == prevSize)
skipCount += 1; // Did not discover any Varnodes corresponding to a particular SymbolEntry
}
if (mergeList.empty()) continue;
HighVariable *high = mergeList[0]->getHigh();
updateHigh(high);
for(int4 i=0;i<mergeList.size();++i) {
HighVariable *newHigh = mergeList[i]->getHigh();
if (newHigh == high) continue; // Varnodes already merged
updateHigh(newHigh);
if (!mergeTestRequired(high, newHigh)) {
symbol->setMergeProblems();
newHigh->setUnmerged();
conflictCount += 1;
continue;
}
if (!merge(high,newHigh,false)) { // Attempt the merge
symbol->setMergeProblems();
newHigh->setUnmerged();
conflictCount += 1;
continue;
}
mergeCount += 1;
}
if (skipCount != 0 || conflictCount !=0) {
ostringstream s;
s << "Unable to";
if (mergeCount != 0)
s << " fully";
s << " merge symbol: " << symbol->getName();
if (skipCount > 0)
s << " -- Some instance varnodes not found.";
if (conflictCount > 0)
s << " -- Some merges are forbidden";
data.warningHeader(s.str());
}
}
}
/// \brief Speculatively merge Varnodes that are input/output to the same p-code op
///
/// If a single p-code op has an input and output HighVariable that share the same data-type,
@ -1048,7 +1123,7 @@ void Merge::buildDominantCopy(HighVariable *high,vector<PcodeOp *> &copy,int4 po
}
}
if (count > 0 && domCopyIsNew) {
high->merge(domVn->getHigh(),false);
high->merge(domVn->getHigh(),true);
}
}

View file

@ -125,6 +125,7 @@ public:
void mergeAddrTied(void);
void mergeMarker(void);
void mergeAdjacent(void);
void mergeMultiEntry(void);
bool hideShadows(HighVariable *high);
void processCopyTrims(void);
void markInternalCopies(void);

View file

@ -97,7 +97,7 @@ public:
InjectPayload(const string &nm,int4 tp) { name=nm; type=tp; paramshift=0; dynamic = false; incidentalCopy = false; } ///< Construct for use with restoreXml
int4 getParamShift(void) const { return paramshift; } ///< Get the number of parameters shifted
bool isDynamic(void) const { return dynamic; } ///< Return \b true if p-code in the injection is generated dynamically
bool isIncidentalCopy(void) const { return incidentalCopy; }
bool isIncidentalCopy(void) const { return incidentalCopy; } ///< Return \b true if any injected COPY is considered \e incidental
int4 sizeInput(void) const { return inputlist.size(); } ///< Return the number of input parameters
int4 sizeOutput(void) const { return output.size(); } ///< Return the number of output parameters
InjectParameter &getInput(int4 i) { return inputlist[i]; } ///< Get the i-th input parameter

View file

@ -770,16 +770,11 @@ void PrintC::opPtrsub(const PcodeOp *op)
}
}
else if (ct->getMetatype() == TYPE_SPACEBASE) {
TypeSpacebase *sb = (TypeSpacebase *)ct;
Scope *scope = sb->getMap();
Address addr = sb->getAddress(op->getIn(1)->getOffset(),in0->getSize(),op->getAddr());
if (addr.isInvalid())
throw LowlevelError("Unable to generate proper address from spacebase");
SymbolEntry *entry = scope->queryContainer(addr,1,Address());
Datatype *ct = (Datatype *)0;
HighVariable *high = op->getIn(1)->getHigh();
Symbol *symbol = high->getSymbol();
arrayvalue = false;
if (entry != (SymbolEntry *)0) {
ct = entry->getSymbol()->getType();
if (symbol != (Symbol *)0) {
ct = symbol->getType();
// The '&' is dropped if the output type is an array
if (ct->getMetatype()==TYPE_ARRAY) {
arrayvalue = valueon; // If printing value, use [0]
@ -795,18 +790,21 @@ void PrintC::opPtrsub(const PcodeOp *op)
if (arrayvalue)
pushOp(&subscript,op);
}
if (entry == (SymbolEntry *)0)
if (symbol == (Symbol *)0) {
TypeSpacebase *sb = (TypeSpacebase *)ct;
Address addr = sb->getAddress(op->getIn(1)->getOffset(),in0->getSize(),op->getAddr());
pushUnnamedLocation(addr,(Varnode *)0,op);
}
else {
int4 off = (int4)(addr.getOffset() - entry->getAddr().getOffset()) + entry->getOffset();
int4 off = high->getSymbolOffset();
if (off == 0)
pushSymbol(entry->getSymbol(),(Varnode *)0,op);
pushSymbol(symbol,(Varnode *)0,op);
else {
// If this "value" is getting used as a storage location
// we can't use a cast in its description, so turn off
// casting when printing the partial symbol
// Datatype *exttype = ((mods & print_store_value)!=0) ? (Datatype *)0 : ct;
pushPartialSymbol(entry->getSymbol(),off,0,(Varnode *)0,op,(Datatype *)0);
pushPartialSymbol(symbol,off,0,(Varnode *)0,op,(Datatype *)0);
}
}
if (arrayvalue)
@ -1641,6 +1639,21 @@ void PrintC::pushSymbol(const Symbol *sym,const Varnode *vn,const PcodeOp *op)
else
tokenColor = EmitXml::var_color;
// FIXME: resolve scopes
if (sym->hasMergeProblems() && vn != (Varnode *)0) {
HighVariable *high = vn->getHigh();
if (high->isUnmerged()) {
ostringstream s;
s << sym->getName();
SymbolEntry *entry = high->getSymbolEntry();
if (entry != (SymbolEntry *)0) {
s << '$' << dec << entry->getSymbol()->getMapEntryPosition(entry);
}
else
s << "$$";
pushAtom(Atom(s.str(),vartoken,tokenColor,op,vn));
return;
}
}
pushAtom(Atom(sym->getName(),vartoken,tokenColor,op,vn));
}
@ -2182,21 +2195,27 @@ bool PrintC::emitScopeVarDecls(const Scope *scope,int4 cat)
MapIterator iter = scope->begin();
MapIterator enditer = scope->end();
for(;iter!=enditer;++iter) {
if ((*iter)->isPiece()) continue; // Don't do a partial entry
Symbol *sym = (*iter)->getSymbol();
const SymbolEntry *entry = *iter;
if (entry->isPiece()) continue; // Don't do a partial entry
Symbol *sym = entry->getSymbol();
if (sym->getCategory() != cat) continue;
if (sym->getName().size() == 0) continue;
if (dynamic_cast<FunctionSymbol *>(sym) != (FunctionSymbol *)0)
continue;
if (dynamic_cast<LabSymbol *>(sym) != (LabSymbol *)0)
continue;
if (sym->isMultiEntry()) {
if (sym->getFirstWholeMap() != entry)
continue; // Only emit the first SymbolEntry for declaration of multi-entry Symbol
}
notempty = true;
emitVarDeclStatement(sym);
}
list<SymbolEntry>::const_iterator iter_d = scope->beginDynamic();
list<SymbolEntry>::const_iterator enditer_d = scope->endDynamic();
for(;iter_d!=enditer_d;++iter_d) {
if ((*iter_d).isPiece()) continue; // Don't do a partial entry
const SymbolEntry *entry = &(*iter_d);
if (entry->isPiece()) continue; // Don't do a partial entry
Symbol *sym = (*iter_d).getSymbol();
if (sym->getCategory() != cat) continue;
if (sym->getName().size() == 0) continue;
@ -2204,6 +2223,10 @@ bool PrintC::emitScopeVarDecls(const Scope *scope,int4 cat)
continue;
if (dynamic_cast<LabSymbol *>(sym) != (LabSymbol *)0)
continue;
if (sym->isMultiEntry()) {
if (sym->getFirstWholeMap() != entry)
continue;
}
notempty = true;
emitVarDeclStatement(sym);
}

View file

@ -1884,9 +1884,9 @@ int4 RuleDoubleShift::applyOp(PcodeOp *op,Funcdata &data)
}
/// \class RuleDoubleArithShift
/// \brief Simply two sequential INT_SRIGHT: `(x s>> #c) s>> #d => x s>> saturate(#c + #d)`
/// \brief Simplify two sequential INT_SRIGHT: `(x s>> #c) s>> #d => x s>> saturate(#c + #d)`
///
/// Optimized division optimization in particular can produce a sequence of signed right shifts.
/// Division optimization in particular can produce a sequence of signed right shifts.
/// The shift amounts add up to the point where the sign bit has saturated the entire result.
void RuleDoubleArithShift::getOpList(vector<uint4> &oplist) const

View file

@ -97,12 +97,12 @@ public:
LanedIterator(const LanedRegister *lanedR) { size = 0; mask = lanedR->sizeBitMask; normalize(); } ///< Constructor
LanedIterator(void) { size = -1; mask = 0; } ///< Constructor for ending iterator
LanedIterator &operator++(void) { size += 1; normalize(); return *this; } ///< Preincrement operator
int4 operator*(void) const { return size; } /// Dereference operator
int4 operator*(void) const { return size; } ///< Dereference operator
LanedIterator &operator=(const LanedIterator &op2) { size = op2.size; mask = op2.mask; return *this; } ///< Assignment
bool operator==(const LanedIterator &op2) const { return (size == op2.size); } ///< Equal operator
bool operator!=(const LanedIterator &op2) const { return (size != op2.size); } ///< Not-equal operator
};
typedef LanedIterator const_iterator;
typedef LanedIterator const_iterator; ///< Iterator over possible lane sizes for this register
private:
int4 wholeSize; ///< Size of the whole register
uint4 sizeBitMask; ///< A 1-bit for every permissible lane size

View file

@ -23,13 +23,57 @@ HighVariable::HighVariable(Varnode *vn)
{
numMergeClasses = 1;
highflags = HighVariable::flagsdirty | HighVariable::typedirty | HighVariable::coverdirty;
highflags = flagsdirty | namerepdirty | typedirty | coverdirty;
flags = 0;
type = (Datatype *)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);
}
/// 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 (entry->isDynamic()) // Dynamic symbols match whole variable
symboloffset = -1;
else if (symbol->getCategory() == 1)
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().overlap(0,entry->getAddr(),symbol->getType()->getSize()) + entry->getOffset();
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);
}
/// Only update if the cover is marked as \e dirty.
@ -38,8 +82,8 @@ HighVariable::HighVariable(Varnode *vn)
void HighVariable::updateCover(void) const
{
if ((highflags & HighVariable::coverdirty)==0) return; // Cover info is upto date
highflags &= ~HighVariable::coverdirty;
if ((highflags & coverdirty)==0) return; // Cover info is upto date
highflags &= ~coverdirty;
wholecover.clear();
if (!inst[0]->hasCover()) return;
@ -53,11 +97,11 @@ void HighVariable::updateCover(void) const
void HighVariable::updateFlags(void) const
{
vector<Varnode *>::const_iterator iter;
uint4 fl;
if ((highflags & flagsdirty)==0) return; // flags are up to date
vector<Varnode *>::const_iterator iter;
uint4 fl = 0;
if ((highflags & HighVariable::flagsdirty)==0) return; // flags are up to date
fl = 0;
for(iter=inst.begin();iter!=inst.end();++iter)
fl |= (*iter)->getFlags();
@ -65,7 +109,7 @@ void HighVariable::updateFlags(void) const
flags &= (Varnode::mark | Varnode::typelock);
// Update all but these
flags |= fl & ~(Varnode::mark | Varnode::directwrite | Varnode::typelock );
highflags &= ~HighVariable::flagsdirty; // Clear the dirty flag
highflags &= ~flagsdirty; // Clear the dirty flag
}
/// Using Datatype::typeOrder, find the member Varnode with the most specific data-type.
@ -98,8 +142,8 @@ void HighVariable::updateType(void) const
{
Varnode *vn;
if ((highflags&HighVariable::typedirty)==0) return; // Type is up to date
highflags &= ~HighVariable::typedirty; // Mark type as clean
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();
@ -110,6 +154,24 @@ void HighVariable::updateType(void) const
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;
Varnode *vn = (Varnode *)0;
for(iter=inst.begin();iter!=inst.end();++iter) {
Varnode *tmpvn = *iter;
if (tmpvn->getSymbolEntry() != (SymbolEntry *)0)
vn = tmpvn;
}
if (vn != (Varnode *)0)
setSymbol(vn);
}
/// Compare two Varnode objects based just on their storage address
/// \param a is the first Varnode to compare
/// \param b is the second Varnode
@ -168,18 +230,22 @@ bool HighVariable::compareName(Varnode *vn1,Varnode *vn2)
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 *rep,*vn;
Varnode *vn;
iter = inst.begin();
rep = *iter;
nameRepresentative = *iter;
++iter;
for(;iter!=inst.end();++iter) {
vn = *iter;
if (compareName(rep,vn))
rep = vn;
if (compareName(nameRepresentative,vn))
nameRepresentative = vn;
}
return rep;
return nameRepresentative;
}
/// Search for the given Varnode and cut it out of the list, marking all properties as \e dirty.
@ -193,12 +259,28 @@ void HighVariable::remove(Varnode *vn)
for(;iter!=inst.end();++iter) {
if (*iter == vn) {
inst.erase(iter);
highflags |= (HighVariable::flagsdirty|HighVariable::coverdirty|HighVariable::typedirty);
highflags |= (flagsdirty|namerepdirty|coverdirty|typedirty);
if (vn->getSymbolEntry() != (SymbolEntry *)0)
highflags |= symboldirty;
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;
}
/// The data-type its dirtying mechanism is disabled. The data-type will not change, unless
/// this method is called again.
/// \param tp is the data-type to set
@ -219,12 +301,14 @@ void HighVariable::merge(HighVariable *tv2,bool isspeculative)
if (tv2 == this) return;
// if (isAddrTied() && tv2->isAddrTied()) {
// if (tied_varnode()->getAddr() != tv2->tied_varnode()->getAddr())
// throw LowlevelError("Merging different addrtieds");
// }
highflags |= (HighVariable::flagsdirty|HighVariable::typedirty);
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) {
@ -246,10 +330,10 @@ void HighVariable::merge(HighVariable *tv2,bool isspeculative)
std::merge(instcopy.begin(),instcopy.end(),tv2->inst.begin(),tv2->inst.end(),inst.begin(),compareJustLoc);
tv2->inst.clear();
if (((highflags&HighVariable::coverdirty)==0)&&((tv2->highflags&HighVariable::coverdirty)==0))
if (((highflags&coverdirty)==0)&&((tv2->highflags&coverdirty)==0))
wholecover.merge(tv2->wholecover);
else
highflags |= HighVariable::coverdirty;
highflags |= coverdirty;
delete tv2;
}
@ -359,6 +443,46 @@ int4 HighVariable::instanceIndex(const Varnode *vn) const
return -1;
}
/// \param s is the output stream to write XML to
void HighVariable::saveXml(ostream &s) const
{
Varnode *vn = getNameRepresentative(); // Get representative varnode
s << "<high ";
// a_v(s,"name",high->getName());
a_v_u(s,"repref",vn->getCreateIndex());
if (isSpacebase()||isImplied()) // This is a special variable
a_v(s,"class",string("other"));
else if (isPersist()&&isAddrTied()) // Global variable
a_v(s,"class",string("global"));
else if (isConstant())
a_v(s,"class",string("constant"));
else if (!isPersist() && (symbol != (Symbol *)0)) {
if (symbol->getCategory() == 0)
a_v(s,"class",string("param"));
else
a_v(s,"class",string("local"));
}
else {
a_v(s,"class",string("other"));
}
if (isTypeLock())
a_v_b(s,"typelock",true);
if (symbol != (Symbol *)0) {
a_v_u(s,"symref",symbol->getId());
if (symboloffset >= 0)
a_v_i(s, "offset", symboloffset);
}
s << '>';
getType()->saveXml(s);
for(int4 j=0;j<inst.size();++j) {
s << "<addr ";
a_v_u(s,"ref",inst[j]->getCreateIndex());
s << "/>";
}
s << "</high>";
}
#ifdef MERGEMULTI_DEBUG
/// \brief Check that there are no internal Cover intersections within \b this
///

View file

@ -44,19 +44,24 @@ public:
/// So we keep track of when these inherited values are \e dirty
enum {
flagsdirty = 1, ///< Boolean properties for the HighVariable are dirty
typedirty = 2, ///< The data-type for the HighVariable is dirty
coverdirty = 4, ///< The cover for the HighVariable is dirty
copy_in1 = 8, ///< There exists at least 1 COPY into \b this HighVariable from other HighVariables
copy_in2 = 16, ///< There exists at least 2 COPYs into \b this HighVariable from other HighVariables
type_finalized = 32 ///< Set if a final data-type is locked in and dirtying is disabled
namerepdirty = 2, ///< The name representative for the HighVariable is dirty
typedirty = 4, ///< The data-type for the HighVariable is dirty
coverdirty = 8, ///< The cover for the HighVariable is dirty
symboldirty = 0x10, ///< The symbol attachment is dirty
copy_in1 = 0x20, ///< There exists at least 1 COPY into \b this HighVariable from other HighVariables
copy_in2 = 0x40, ///< There exists at least 2 COPYs into \b this HighVariable from other HighVariables
type_finalized = 0x80, ///< Set if a final data-type is locked in and dirtying is disabled
unmerged = 0x100 ///< Set if part of a multi-entry Symbol but did not get merged with other SymbolEntrys
};
private:
friend class Varnode;
friend class Merge;
vector<Varnode *> inst; ///< The member Varnode objects making up \b this HighVariable
int4 numMergeClasses; ///< Number of different speculative merge classes in \b this
mutable uint4 highflags; ///< Dirtiness flags
mutable uint4 flags; ///< Boolean properties inherited from Varnode members
mutable Datatype *type; ///< The data-type for this
mutable Datatype *type; ///< The data-type for \b this
mutable Varnode *nameRepresentative; ///< The storage location used to generate a Symbol name
mutable Cover wholecover; ///< The ranges of code addresses covered by this HighVariable
mutable Symbol *symbol; ///< The Symbol \b this HighVariable is tied to
mutable int4 symboloffset; ///< -1=perfect symbol match >=0, offset
@ -64,34 +69,28 @@ private:
void updateFlags(void) const; ///< (Re)derive boolean properties of \b this from the member Varnodes
void updateCover(void) const; ///< (Re)derive the cover of \b this from the member Varnodes
void updateType(void) const; ///< (Re)derive the data-type for \b this from the member Varnodes
void updateSymbol(void) const; ///< (Re)derive the Symbol and offset for \b this from member Varnodes
void setCopyIn1(void) const { highflags |= copy_in1; } ///< Mark the existence of one COPY into \b this
void setCopyIn2(void) const { highflags |= copy_in2; } ///< Mark the existence of two COPYs into \b this
void clearCopyIns(void) const { highflags &= ~(copy_in1 | copy_in2); } ///< Clear marks indicating COPYs into \b this
bool hasCopyIn1(void) const { return ((highflags&copy_in1)!=0); } ///< Is there at least one COPY into \b this
bool hasCopyIn2(void) const { return ((highflags&copy_in2)!=0); } ///< Is there at least two COPYs into \b this
void remove(Varnode *vn); ///< Remove a member Varnode from \b this
void merge(HighVariable *tv2,bool isspeculative); ///< Merge another HighVariable into \b this
void setSymbol(Varnode *vn) const; ///< Update Symbol information for \b this from the given member Varnode
void setSymbolReference(Symbol *sym,int4 off); ///< Attach a reference to a Symbol to \b this
void flagsDirty(void) const { highflags |= flagsdirty | namerepdirty; } ///< Mark the boolean properties as \e dirty
void coverDirty(void) const { highflags |= coverdirty; } ///< Mark the cover as \e dirty
void typeDirty(void) const { highflags |= typedirty; } ///< Mark the data-type as \e dirty
void setUnmerged(void) const { highflags |= unmerged; } ///< Mark \b this as having merge problems
public:
HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode
Datatype *getType(void) const { updateType(); return type; } ///< Get the data-type
/// \brief Set the Symbol associated with \b this HighVariable.
///
/// This HighVariable does not need to be associated with the whole symbol. It can be associated with
/// a part, like a sub-field, if the size of the member Varnodes and the Symbol don't match. In this case
/// a non-zero offset may be passed in with the Symbol to indicate what part is represented by the \b this.
/// \param sym is the Symbol to associate with \b this
/// \param off is the offset in bytes, relative to the Symbol, where \b this HighVariable starts
void setSymbol(Symbol *sym,int4 off) const {
symbol = sym; symboloffset = off; }
Symbol *getSymbol(void) const { return symbol; } ///< Get the Symbol associated with \b this
Symbol *getSymbol(void) const { updateSymbol(); return symbol; } ///< Get the Symbol associated with \b this or null
SymbolEntry *getSymbolEntry(void) const; /// Get the SymbolEntry mapping to \b this or null
int4 getSymbolOffset(void) const { return symboloffset; } ///< Get the Symbol offset associated with \b this
int4 numInstances(void) const { return inst.size(); } ///< Get the number of member Varnodes \b this has
Varnode *getInstance(int4 i) const { return inst[i]; } ///< Get the i-th member Varnode
void flagsDirty(void) const { highflags |= HighVariable::flagsdirty; } ///< Mark the boolean properties as \e dirty
void coverDirty(void) const { highflags |= HighVariable::coverdirty; } ///< Mark the cover as \e dirty
void typeDirty(void) const { highflags |= HighVariable::typedirty; } ///< Mark the data-type as \e dirty
void remove(Varnode *vn); ///< Remove a member Varnode from \b this
void finalizeDatatype(Datatype *tp); ///< Set a final datatype for \b this variable
/// \brief Print details of the cover for \b this (for debug purposes)
@ -117,6 +116,7 @@ public:
void setMark(void) const { flags |= Varnode::mark; } ///< Set the mark on this variable
void clearMark(void) const { flags &= ~Varnode::mark; } ///< Clear the mark on this variable
bool isMark(void) const { return ((flags&Varnode::mark)!=0); } ///< Return \b true if \b this is marked
bool isUnmerged(void) const { return ((highflags&unmerged)!=0); } ///< Return \b true if \b this has merge problems
/// \brief Determine if \b this HighVariable has an associated cover.
///
@ -129,6 +129,7 @@ public:
bool isUnattached(void) const { return inst.empty(); } ///< Return \b true if \b this has no member Varnode
bool isTypeLock(void) const { updateType(); return ((flags & Varnode::typelock)!=0); } ///< Return \b true if \b this is \e typelocked
bool isNameLock(void) const { updateFlags(); return ((flags & Varnode::namelock)!=0); } ///< Return \b true if \b this is \e namelocked
void saveXml(ostream &s) const; ///< Save the variable to stream as an XML \<high\> tag
#ifdef MERGEMULTI_DEBUG
void verifyCover(void) const;
#endif

View file

@ -16,39 +16,6 @@
#include "varmap.hh"
#include "funcdata.hh"
/// \param ad is the storage address of the variable
/// \param use is the use point address in code
/// \param sz is the optional size of the variable
AddressUsePointPair::AddressUsePointPair(const Address &ad,const Address &use,int4 sz) : addr(ad), useaddr(use)
{
size = sz;
if (useaddr.isInvalid()) // If invalid
useaddr = Address((AddrSpace *)0,0); // Make sure to set offset to zero, so invalids compare equal
}
/// Compare first by storage address and then by use point address.
/// Do NOT compare the optional size.
/// \param op2 is the pair to compare to \b this
/// \return \b true if \b this should be sorted first
bool AddressUsePointPair::operator<(const AddressUsePointPair &op2) const
{
if (addr != op2.addr)
return (addr < op2.addr);
return (useaddr < op2.useaddr);
}
/// Storage addresses and use point addresses must match. Size does not have to match.
/// \param op2 is the pair to test \b this against for equality
/// \return \b true if \b the two pairs are equal
bool AddressUsePointPair::operator==(const AddressUsePointPair &op2) const
{
if (addr != op2.addr) return false;
return (useaddr == op2.useaddr);
}
/// \brief Can the given intersecting RangeHint coexist with \b this at their given offsets
///
/// Determine if the data-type information in the two ranges \e line \e up
@ -305,7 +272,6 @@ ScopeLocal::ScopeLocal(AddrSpace *spc,Funcdata *fd,Architecture *g) : ScopeInter
rangeLocked = false;
stackGrowsNegative = true;
restrictScope(fd);
dedupId = fd->getAddress().getOffset(); // Allow multiple scopes with same name
}
/// Turn any symbols that are \e name \e locked but not \e type \e locked into name recommendations
@ -322,22 +288,7 @@ void ScopeLocal::collectNameRecs(void)
while(iter!=nametree.end()) {
Symbol *sym = *iter++;
if (sym->isNameLocked()&&(!sym->isTypeLocked())) {
SymbolEntry *entry = sym->getFirstWholeMap();
if (entry != (SymbolEntry *)0) {
if (entry->isDynamic()) {
addDynamicRecommend(entry->getFirstUseAddress(), entry->getHash(), sym->getName());
}
else {
Address usepoint;
if (!entry->getUseLimit().empty()) {
const Range *range = entry->getUseLimit().getFirstRange();
usepoint = Address(range->getSpace(),range->getFirst());
}
addRecommendName( entry->getAddr(), usepoint, sym->getName(), entry->getSize() );
}
if (sym->getCategory()<0)
removeSymbol(sym);
}
addRecommendName(sym);
}
}
}
@ -447,12 +398,6 @@ string ScopeLocal::buildVariableName(const Address &addr,
Datatype *ct,
int4 &index,uint4 flags) const
{
map<AddressUsePointPair,string>::const_iterator iter;
iter = nameRecommend.find( AddressUsePointPair(addr,pc,0));
if (iter != nameRecommend.end()) {
// We are not checking if the recommended size matches
return makeNameUnique((*iter).second);
}
if (((flags & (Varnode::addrtied|Varnode::persist))==Varnode::addrtied) &&
addr.getSpace() == space) {
if (fd->getFuncProto().getLocalRange().inRange(addr,1)) {
@ -521,10 +466,7 @@ void ScopeLocal::createEntry(const RangeHint &a)
if (num>1)
ct = glb->types->getTypeArray(num,ct);
int4 index=0;
string nm = buildVariableName(addr,usepoint,ct,index,Varnode::addrtied);
addSymbol(nm,ct,addr,usepoint);
addSymbol("",ct,addr,usepoint);
}
/// Set up basic offset boundaries for what constitutes a local variable
@ -1221,10 +1163,8 @@ void ScopeLocal::fakeInputSymbols(void)
int4 size = (endpoint - addr.getOffset()) + 1;
Datatype *ct = fd->getArch()->types->getBase(size,TYPE_UNKNOWN);
int4 index = -1; // NOT a parameter
string nm = buildVariableName(addr,usepoint,ct,index,Varnode::input);
try {
addSymbol(nm,ct,addr,usepoint)->getSymbol();
addSymbol("",ct,addr,usepoint)->getSymbol();
}
catch(LowlevelError &err) {
fd->warningHeader(err.explain);
@ -1234,48 +1174,96 @@ void ScopeLocal::fakeInputSymbols(void)
}
}
/// \brief Try to pick recommended names for any unnamed Symbols
/// \brief Change the primary mapping for the given Symbol to be a specific storage address and use point
///
/// Remove any other mapping and create a mapping based on the given storage.
/// \param sym is the given Symbol to remap
/// \param addr is the starting address of the storage
/// \param usepoint is the use point for the mapping
/// \return the new mapping
SymbolEntry *ScopeLocal::remapSymbol(Symbol *sym,const Address &addr,const Address &usepoint)
{
SymbolEntry *entry = sym->getFirstWholeMap();
int4 size = entry->getSize();
if (!entry->isDynamic()) {
if (entry->getAddr() == addr && entry->getFirstUseAddress() == usepoint)
return entry;
}
removeSymbolMappings(sym);
RangeList rnglist;
if (!usepoint.isInvalid())
rnglist.insertRange(usepoint.getSpace(),usepoint.getOffset(),usepoint.getOffset());
return addMapInternal(sym,Varnode::mapped,addr,0,size,rnglist);
}
/// \brief Make the primary mapping for the given Symbol, dynamic
///
/// Remove any other mapping and create a new dynamic mapping based on a given
/// size and hash
/// \param sym is the given Symbol to remap
/// \param hash is the dynamic hash
/// \param usepoint is the use point for the mapping
/// \return the new dynamic mapping
SymbolEntry *ScopeLocal::remapSymbolDynamic(Symbol *sym,uint8 hash,const Address &usepoint)
{
SymbolEntry *entry = sym->getFirstWholeMap();
int4 size = entry->getSize();
if (entry->isDynamic()) {
if (entry->getHash() == hash && entry->getFirstUseAddress() == usepoint)
return entry;
}
removeSymbolMappings(sym);
RangeList rnglist;
if (!usepoint.isInvalid())
rnglist.insertRange(usepoint.getSpace(),usepoint.getOffset(),usepoint.getOffset());
return addDynamicMapInternal(sym,Varnode::mapped,hash,0,size,rnglist);
}
/// \brief Run through name recommendations, checking if any match unnamed symbols
///
/// Unlocked symbols that are presented to the decompiler are stored off as \e recommended names. These
/// can be reattached after the decompiler makes a determination of what the final Symbols are.
/// This method runs through the recommended names and checks if they can be applied to an existing
/// unnamed Symbol.
/// \param resname will hold the new name strings
/// \param ressym will hold the list of Symbols corresponding to the new name strings
void ScopeLocal::makeNameRecommendationsForSymbols(vector<string> &resname,vector<Symbol *> &ressym) const
void ScopeLocal::recoverNameRecommendationsForSymbols(void)
{ // Find nameable symbols with a varnode rep matching a name recommendation
map<AddressUsePointPair,string>::const_iterator iter;
{
list<NameRecommend>::const_iterator iter;
for(iter=nameRecommend.begin();iter!=nameRecommend.end();++iter) {
VarnodeLocSet::const_iterator biter,eiter;
bool isaddrtied;
const Address &addr((*iter).first.getAddr());
const Address &useaddr((*iter).first.getUseAddr());
int4 size = (*iter).first.getSize();
if (useaddr.isInvalid()) {
isaddrtied = true;
biter = fd->beginLoc(size,addr);
eiter = fd->endLoc(size,addr);
const Address &addr((*iter).getAddr());
const Address &usepoint((*iter).getUseAddr());
int4 size = (*iter).getSize();
Symbol *sym;
Varnode *vn = (Varnode *)0;
if (usepoint.isInvalid()) {
SymbolEntry *entry = findOverlap(addr, size); // Recover any Symbol regardless of usepoint
if (entry == (SymbolEntry *)0) continue;
if (entry->getAddr() != addr) // Make sure Symbol has matching address
continue;
sym = entry->getSymbol();
if ((sym->getFlags() & Varnode::addrtied)==0)
continue; // Symbol must be address tied to match this name recommendation
vn = fd->findLinkedVarnode(entry);
}
else {
isaddrtied = false;
biter = fd->beginLoc(size,addr,useaddr);
eiter = fd->endLoc(size,addr,useaddr);
vn = fd->findVarnodeWritten(size,addr,usepoint);
if (vn == (Varnode *)0) continue;
sym = vn->getHigh()->getSymbol();
if (sym == (Symbol *)0) continue;
if ((sym->getFlags() & Varnode::addrtied)!=0)
continue; // Cannot use untied varnode as primary map for address tied symbol
SymbolEntry *entry = sym->getFirstWholeMap();
// entry->getAddr() does not need to match address of the recommendation
if (entry->getSize() != size) continue;
}
while(biter != eiter) {
Varnode *vn = *biter;
if (!vn->isAnnotation()) {
Symbol *sym = vn->getHigh()->getSymbol();
if (sym != (Symbol *)0) {
if (sym->isNameUndefined()) {
resname.push_back( (*iter).second);
ressym.push_back(sym);
break;
}
}
}
if (isaddrtied) break;
++biter;
if (!sym->isNameUndefined()) continue;
renameSymbol(sym,makeNameUnique((*iter).getName()));
setSymbolId(sym, (*iter).getSymbolId());
setAttribute(sym, Varnode::namelock);
if (vn != (Varnode *)0) {
fd->remapVarnode(vn, sym, usepoint);
}
}
@ -1290,38 +1278,36 @@ void ScopeLocal::makeNameRecommendationsForSymbols(vector<string> &resname,vecto
if (vn == (Varnode *)0) continue;
if (vn->isAnnotation()) continue;
Symbol *sym = vn->getHigh()->getSymbol();
if (sym != (Symbol *)0) {
if (sym->isNameUndefined()) {
resname.push_back( dynEntry.getName() );
ressym.push_back(sym);
}
}
if (sym == (Symbol *)0) continue;
if (sym->getScope() != this) continue;
if (!sym->isNameUndefined()) continue;
renameSymbol(sym,makeNameUnique( dynEntry.getName() ));
setAttribute(sym, Varnode::namelock);
setSymbolId(sym, dynEntry.getSymbolId());
fd->remapDynamicVarnode(vn, sym, dynEntry.getAddress(), dynEntry.getHash());
}
}
/// \brief Add a new recommended name to the list
///
/// Recommended names are associated with a storage address, a use point, and a suggested size.
/// The symbol is stored as a name recommendation and then removed from the scope.
/// Name recommendations are associated either with a storage address and usepoint, or a dynamic hash.
/// The name may be reattached to a Symbol after decompilation.
/// \param addr is the storage address
/// \param usepoint is the address of the code use point
/// \param nm is the recommended name
/// \param sz is the suggested size the Symbol should match
void ScopeLocal::addRecommendName(const Address &addr,const Address &usepoint,const string &nm,int4 sz)
/// \param sym is the given Symbol to treat as a name recommendation
void ScopeLocal::addRecommendName(Symbol *sym)
{
nameRecommend[ AddressUsePointPair(addr,usepoint,sz) ] = nm;
}
/// \brief Add a new recommended name for a dynamic storage location to the list
///
/// This recommended name is assigned a storage location via the DynamicHash mechanism.
/// The name may be reattached to a Symbol after decompilation.
/// \param addr is the address of the code use point
/// \param hash is the hash encoding context for identifying the storage location
/// \param nm is the recommended name
void ScopeLocal::addDynamicRecommend(const Address &usepoint,uint8 hash,const string &nm)
{
dynRecommend.push_back(DynamicRecommend(usepoint,hash,nm));
SymbolEntry *entry = sym->getFirstWholeMap();
if (entry == (SymbolEntry *) 0) return;
if (entry->isDynamic()) {
dynRecommend.push_back(DynamicRecommend(entry->getFirstUseAddress(), entry->getHash(), sym->getName(), sym->getId()));
}
else {
Address usepoint;
if (!entry->getUseLimit().empty()) {
const Range *range = entry->getUseLimit().getFirstRange();
usepoint = Address(range->getSpace(), range->getFirst());
}
nameRecommend.push_back(NameRecommend(entry->getAddr(),usepoint, entry->getSize(), sym->getName(), sym->getId()));
}
if (sym->getCategory() < 0)
removeSymbol(sym);
}

View file

@ -21,26 +21,28 @@
#include "database.hh"
/// \brief An Address pair with a point of use
/// \brief A symbol name recommendation with its associated storage location
///
/// A storage location and a point in the code where the storage is referenced.
/// This object sorts first based on the storage address then on the use point.
class AddressUsePointPair {
/// The name is associated with a static Address and use point in the code. Symbols
/// present at the end of function decompilation without a name can acquire \b this name
/// if their storage matches.
class NameRecommend {
Address addr; ///< The starting address of the storage location
Address useaddr; ///< The code address at the point of use
int4 size; ///< An optional/recommended size for the variable being stored
string name; ///< The local symbol name recommendation
uint8 symbolId; ///< Id associated with the original Symbol
public:
AddressUsePointPair(const Address &ad,const Address &use,int4 sz); ///< Constructor
AddressUsePointPair(const AddressUsePointPair &op2) : addr(op2.addr), useaddr(op2.useaddr) {
size = op2.size; } ///< Copy constructor
NameRecommend(const Address &ad,const Address &use,int4 sz,const string &nm,uint8 id) :
addr(ad), useaddr(use), size(sz), name(nm), symbolId(id) {} ///< Constructor
const Address &getAddr(void) const { return addr; } ///< Get the storage address
const Address &getUseAddr(void) const { return useaddr; } ///< Get the use point address
int4 getSize(void) const { return size; } ///< Get the optional size
bool operator<(const AddressUsePointPair &op2) const; ///< Compare operation
bool operator==(const AddressUsePointPair &op2) const; ///< Test for equality
string getName(void) const { return name; } ///< Get the recommended name
uint8 getSymbolId(void) const { return symbolId; } ///< Get the original Symbol id
};
/// \brief A name recommendation for a particular dynamic location
/// \brief A name recommendation for a particular dynamic storage location
///
/// A recommendation for a symbol name whose storage is dynamic. The storage
/// is identified using the DynamicHash mechanism and may or may not exist.
@ -48,15 +50,14 @@ class DynamicRecommend {
Address usePoint; ///< Use point of the Symbol
uint8 hash; ///< Hash encoding the Symbols environment
string name; ///< The local symbol name recommendation
uint8 symbolId; ///< Id associated with the original Symbol
public:
DynamicRecommend(const Address &addr,uint8 h,const string &nm) :
usePoint(addr) {
hash = h;
name = nm;
} ///< Constructor
DynamicRecommend(const Address &addr,uint8 h,const string &nm,uint8 id) :
usePoint(addr), hash(h), name(nm), symbolId(id) {} ///< Constructor
const Address &getAddress(void) const { return usePoint; } ///< Get the use point address
uint8 getHash(void) const { return hash; } ///< Get the dynamic hash
string getName(void) const { return name; } ///< Get the recommended name
uint8 getSymbolId(void) const { return symbolId; } ///< Get the original Symbol id
};
/// \brief Partial data-type information mapped to a specific range of bytes
@ -181,7 +182,7 @@ public:
class ScopeLocal : public ScopeInternal {
AddrSpace *space; ///< Address space containing the local stack
RangeList localRange; ///< The set of addresses that might hold mapped locals (not parameters)
map<AddressUsePointPair,string> nameRecommend; ///< Symbol name recommendations for specific addresses
list<NameRecommend> nameRecommend; ///< Symbol name recommendations for specific addresses
list<DynamicRecommend> dynRecommend; ///< Symbol name recommendations for dynamic locations
bool stackGrowsNegative; ///< Marked \b true if the stack is considered to \e grow towards smaller offsets
bool rangeLocked; ///< True if the subset of addresses \e mapped to \b this scope has been locked
@ -190,8 +191,7 @@ class ScopeLocal : public ScopeInternal {
bool restructure(MapState &state); ///< Merge hints into a formal Symbol layout of the address space
void markUnaliased(const vector<uintb> &alias); ///< Mark all local symbols for which there are no aliases
void fakeInputSymbols(void); ///< Make sure all stack inputs have an associated Symbol
void addRecommendName(const Address &addr,const Address &usepoint,const string &nm,int4 sz);
void addDynamicRecommend(const Address &usepoint,uint8 hash,const string &nm);
void addRecommendName(Symbol *sym); ///< Convert the given symbol to a name recommendation
void collectNameRecs(void); ///< Collect names of unlocked Symbols on the stack
public:
ScopeLocal(AddrSpace *spc,Funcdata *fd,Architecture *g); ///< Constructor
@ -217,7 +217,9 @@ public:
void resetLocalWindow(void); ///< Reset the set of addresses that are considered mapped by the scope to the default
void restructureVarnode(bool aliasyes); ///< Layout mapped symbols based on Varnode information
void restructureHigh(void); ///< Layout mapped symbols based on HighVariable information
void makeNameRecommendationsForSymbols(vector<string> &resname,vector<Symbol *> &ressym) const;
SymbolEntry *remapSymbol(Symbol *sym,const Address &addr,const Address &usepoint);
SymbolEntry *remapSymbolDynamic(Symbol *sym,uint8 hash,const Address &usepoint);
void recoverNameRecommendationsForSymbols(void);
};
#endif

View file

@ -360,6 +360,54 @@ void Varnode::setDef(PcodeOp *op)
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
/// \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);
}
}
/// 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
@ -394,8 +442,11 @@ void Varnode::copySymbol(const Varnode *vn)
mapentry = vn->mapentry; // Copy any symbol
flags &= ~(Varnode::typelock | Varnode::namelock);
flags |= (Varnode::typelock | Varnode::namelock) & vn->flags;
if (high != (HighVariable *)0)
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,

View file

@ -154,6 +154,9 @@ private:
// These functions should be only private things used by VarnodeBank
void setInput(void) { setFlags(Varnode::input|Varnode::coverdirty); } ///< Mark Varnode as \e input
void setDef(PcodeOp *op); ///< Set the defining PcodeOp of this Varnode
bool setSymbolProperties(SymbolEntry *entry); ///< Set properties from the given Symbol to \b this Varnode
void setSymbolEntry(SymbolEntry *entry); ///< Attach a Symbol to \b this Varnode
void setSymbolReference(SymbolEntry *entry,int4 off); ///< Attach a Symbol reference to \b this
void addDescend(PcodeOp *op); ///< Add a descendant (reading) PcodeOp to this Varnode's list
void eraseDescend(PcodeOp *op); ///< Erase a descendant (reading) PcodeOp from this Varnode's list
void destroyDescend(void); ///< Clear all descendant (reading) PcodeOps