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. /// \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. /// 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(); HighVariable *high = vn->getHigh();
if (!high->isMark()) return; // Not one of the variables needing a name 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); map<HighVariable *,OpRecommend>::iterator iter = recmap.find(high);
if (iter != recmap.end()) { // We have seen this varnode before 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(); const AddrSpaceManager *manage = data.getArch();
VarnodeLocSet::const_iterator iter,enditer; VarnodeLocSet::const_iterator iter,enditer;
AddrSpace *spc; 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 for(int4 i=0;i<manage->numSpaces();++i) { // Build a list of nameable highs
spc = manage->getSpace(i); spc = manage->getSpace(i);
if (spc == (AddrSpace *)0) continue; if (spc == (AddrSpace *)0) continue;
if (spc == constSpace) continue;
enditer = data.endLoc(spc); enditer = data.endLoc(spc);
for(iter=data.beginLoc(spc);iter!=enditer;++iter) { for(iter=data.beginLoc(spc);iter!=enditer;++iter) {
Varnode *curvn = *iter; Varnode *curvn = *iter;
if (curvn->isFree()) { if (curvn->isFree()) {
if (curvn->getSymbolEntry() != (SymbolEntry *)0)
data.linkSymbol(curvn); // Special equate symbol
continue; continue;
} }
if (curvn->isSpacebase())
linkSpacebaseSymbol(curvn, data, namerec);
Varnode *vn = curvn->getHigh()->getNameRepresentative(); Varnode *vn = curvn->getHigh()->getNameRepresentative();
if (vn != curvn) continue; // Hit each high only once if (vn != curvn) continue; // Hit each high only once
HighVariable *high = vn->getHigh(); HighVariable *high = vn->getHigh();
if (!high->hasName()) continue; if (!high->hasName()) continue;
Symbol *sym = data.linkSymbol(vn); Symbol *sym = data.linkSymbol(vn);
if (sym != (Symbol *)0) { // Can we associate high with a nameable symbol if (sym != (Symbol *)0) { // Can we associate high with a nameable symbol
if (sym->isNameUndefined()) if (sym->isNameUndefined() && high->getSymbolOffset() < 0)
namerec.push_back(vn); namerec.push_back(vn); // Add if no name, and we have a high representing the whole
if (sym->isSizeTypeLocked()) { if (sym->isSizeTypeLocked()) {
if (vn->getSize() == sym->getType()->getSize()) if (vn->getSize() == sym->getType()->getSize())
sym->getScope()->overrideSizeLockType(sym,high->getType()); 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); lookForBadJumpTables(data);
lookForFuncParamNames(data,namerec); lookForFuncParamNames(data,namerec);
ScopeLocal *localmap = data.getScopeLocal();
int4 base = 1; int4 base = 1;
for(uint4 i=0;i<namerec.size();++i) { for(uint4 i=0;i<namerec.size();++i) {
Varnode *vn = namerec[i]; Varnode *vn = namerec[i];
HighVariable *high = vn->getHigh(); Symbol *sym = vn->getHigh()->getSymbol();
Symbol *sym = high->getSymbol();
if (sym->isNameUndefined()) { if (sym->isNameUndefined()) {
string newname; Scope *scope = sym->getScope();
Address usepoint; string newname = scope->buildDefaultName(sym, base, vn);
if (!vn->isAddrTied()) scope->renameSymbol(sym,newname);
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);
} }
} }
data.getScopeLocal()->assignDefaultNames(base);
return 0; return 0;
} }
@ -3852,67 +3871,15 @@ int4 ActionDynamicSymbols::apply(Funcdata &data)
{ {
ScopeLocal *localmap = data.getScopeLocal(); ScopeLocal *localmap = data.getScopeLocal();
list<SymbolEntry>::const_iterator iter,enditer; list<SymbolEntry>::iterator iter,enditer;
iter = localmap->beginDynamic(); iter = localmap->beginDynamic();
enditer = localmap->endDynamic(); enditer = localmap->endDynamic();
DynamicHash dhash; DynamicHash dhash;
while(iter != enditer) { while(iter != enditer) {
const SymbolEntry &entry( *iter ); SymbolEntry *entry = &(*iter);
++iter; ++iter;
Symbol *sym = entry.getSymbol(); if (data.attemptDynamicMappingLate(entry, dhash))
dhash.clear(); count += 1;
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;
} }
return 0; return 0;
} }
@ -4893,8 +4860,10 @@ void universal_action(Architecture *conf)
act->addAction( new ActionMergeRequired("merge") ); act->addAction( new ActionMergeRequired("merge") );
act->addAction( new ActionMarkExplicit("merge") ); act->addAction( new ActionMarkExplicit("merge") );
act->addAction( new ActionMarkImplied("merge") ); // This must come BEFORE general merging act->addAction( new ActionMarkImplied("merge") ); // This must come BEFORE general merging
act->addAction( new ActionMergeMultiEntry("merge") );
act->addAction( new ActionMergeCopy("merge") ); act->addAction( new ActionMergeCopy("merge") );
act->addAction( new ActionDominantCopy("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 ActionMarkIndirectOnly("merge") ); // Must come after required merges but before speculative
act->addAction( new ActionMergeAdjacent("merge") ); act->addAction( new ActionMergeAdjacent("merge") );
act->addAction( new ActionMergeType("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; } 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) /// \brief Try to merge Varnodes of the same type (if they don't hold different values at the same time)
class ActionMergeType : public Action { class ActionMergeType : public Action {
public: public:
@ -452,8 +463,9 @@ class ActionNameVars : public Action {
}; };
static void makeRec(ProtoParameter *param,Varnode *vn,map<HighVariable *,OpRecommend> &recmap); 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 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 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: public:
ActionNameVars(const string &g) : Action(rule_onceperfunc,"namevars",g) {} ///< Constructor ActionNameVars(const string &g) : Action(rule_onceperfunc,"namevars",g) {} ///< Constructor
virtual Action *clone(const ActionGroupList &grouplist) const { virtual Action *clone(const ActionGroupList &grouplist) const {

View file

@ -17,6 +17,8 @@
#include "funcdata.hh" #include "funcdata.hh"
#include <ctype.h> #include <ctype.h>
uint8 Symbol::ID_BASE = 0x4000000000000000L;
/// This SymbolEntry is unintegrated. An address or hash must be provided /// This SymbolEntry is unintegrated. An address or hash must be provided
/// either directly or via restoreXml(). /// either directly or via restoreXml().
/// \param sym is the Symbol \b this will be a map for /// \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"))); 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 /// \return the first SymbolEntry
SymbolEntry *Symbol::getFirstWholeMap(void) const SymbolEntry *Symbol::getFirstWholeMap(void) const
@ -260,6 +277,57 @@ SymbolEntry *Symbol::getMapEntry(const Address &addr) const
return (SymbolEntry *)0; 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 /// \param s is the output stream
void Symbol::saveXmlHeader(ostream &s) const void Symbol::saveXmlHeader(ostream &s) const
@ -278,18 +346,20 @@ void Symbol::saveXmlHeader(ostream &s) const
a_v_b(s,"indirectstorage",true); a_v_b(s,"indirectstorage",true);
if ((flags&Varnode::hiddenretparm)!=0) if ((flags&Varnode::hiddenretparm)!=0)
a_v_b(s,"hiddenretparm",true); a_v_b(s,"hiddenretparm",true);
if ((dispflags&isolate)!=0)
a_v_b(s,"merge",false);
int4 format = getDisplayFormat(); int4 format = getDisplayFormat();
if (format != 0) { if (format != 0) {
s << " format=\""; s << " format=\"";
if (format == Symbol::force_hex) if (format == force_hex)
s << "hex\""; s << "hex\"";
else if (format == Symbol::force_dec) else if (format == force_dec)
s << "dec\""; s << "dec\"";
else if (format == Symbol::force_char) else if (format == force_char)
s << "char\""; s << "char\"";
else if (format == Symbol::force_oct) else if (format == force_oct)
s << "oct\""; s << "oct\"";
else if (format == Symbol::force_bin) else if (format == force_bin)
s << "bin\""; s << "bin\"";
else else
s << "hex\""; s << "hex\"";
@ -305,50 +375,87 @@ void Symbol::restoreXmlHeader(const Element *el)
{ {
name.clear(); name.clear();
category = -1; category = -1;
symbolId = 0;
for(int4 i=0;i<el->getNumAttributes();++i) { for(int4 i=0;i<el->getNumAttributes();++i) {
if (el->getAttributeName(i)=="name") const string &attName(el->getAttributeName(i));
name = el->getAttributeValue(i); switch (attName[0]) {
else if (el->getAttributeName(i)=="cat") { case 'c':
istringstream s(el->getAttributeValue("cat")); if (attName == "cat") {
s.unsetf(ios::dec | ios::hex | ios::oct); istringstream s(el->getAttributeValue(i));
s >> category; s.unsetf(ios::dec | ios::hex | ios::oct);
} s >> category;
else if (el->getAttributeName(i)=="namelock") { }
if (xml_readbool(el->getAttributeValue(i))) break;
flags |= Varnode::namelock; case 'f':
} if (attName == "format") {
else if (el->getAttributeName(i)=="typelock") { const string &formString(el->getAttributeValue(i));
if (xml_readbool(el->getAttributeValue(i))) if (formString == "hex")
flags |= Varnode::typelock; dispflags |= force_hex;
} else if (formString == "dec")
else if (el->getAttributeName(i)=="readonly") { dispflags |= force_dec;
if (xml_readbool(el->getAttributeValue(i))) else if (formString == "char")
flags |= Varnode::readonly; dispflags |= force_char;
} else if (formString == "oct")
else if (el->getAttributeName(i)=="volatile") { dispflags |= force_oct;
if (xml_readbool(el->getAttributeValue(i))) else if (formString == "bin")
flags |= Varnode::volatil; dispflags |= force_bin;
} }
else if (el->getAttributeName(i)=="indirectstorage") { break;
if (xml_readbool(el->getAttributeValue(i))) case 'h':
flags |= Varnode::indirectstorage; if (attName == "hiddenretparm") {
} if (xml_readbool(el->getAttributeValue(i)))
else if (el->getAttributeName(i)=="hiddenretparm") { flags |= Varnode::hiddenretparm;
if (xml_readbool(el->getAttributeValue(i))) }
flags |= Varnode::hiddenretparm; break;
} case 'i':
else if (el->getAttributeName(i)=="format") { if (attName == "id") {
const string &formString( el->getAttributeValue(i)); istringstream s(el->getAttributeValue(i));
if (formString == "hex") s.unsetf(ios::dec | ios::hex | ios::oct);
dispflags |= Symbol::force_hex; s >> symbolId;
else if (formString == "dec") if ((symbolId >> 56) == (ID_BASE >> 56))
dispflags |= Symbol::force_dec; symbolId = 0; // Don't keep old internal id's
else if (formString == "char") }
dispflags |= Symbol::force_char; else if (attName == "indirectstorage") {
else if (formString == "oct") if (xml_readbool(el->getAttributeValue(i)))
dispflags |= Symbol::force_oct; flags |= Varnode::indirectstorage;
else if (formString == "bin") }
dispflags |= Symbol::force_bin; 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) { if (category == 0) {
@ -465,10 +572,12 @@ void FunctionSymbol::saveXml(ostream &s) const
{ {
if (fd != (Funcdata *)0) if (fd != (Funcdata *)0)
fd->saveXml(s,false); // Save the function itself fd->saveXml(s,symbolId,false); // Save the function itself
else { else {
s << "<functionshell"; s << "<functionshell";
a_v(s,"name",name); a_v(s,"name",name);
if (symbolId != 0)
a_v_u(s,"id",symbolId);
s << "/>\n"; s << "/>\n";
} }
} }
@ -478,7 +587,7 @@ void FunctionSymbol::restoreXml(const Element *el)
{ {
if (el->getName() == "function") { if (el->getName() == "function") {
fd = new Funcdata("",scope,Address()); fd = new Funcdata("",scope,Address());
fd->restoreXml(el); symbolId = fd->restoreXml(el);
name = fd->getName(); name = fd->getName();
if (consumeSize < fd->getSize()) { if (consumeSize < fd->getSize()) {
if ((fd->getSize()>1)&&(fd->getSize() <= 8)) if ((fd->getSize()>1)&&(fd->getSize() <= 8))
@ -486,7 +595,17 @@ void FunctionSymbol::restoreXml(const Element *el)
} }
} }
else { // functionshell 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; 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; pair<ScopeMap::iterator,bool> res;
if (child->name.size()==0) if (child->name.size()==0)
throw LowlevelError("Non-global scope has empty name"); 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. /// 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 name is the name of the new Symbol
/// \param ct is a data-type to assign to 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; 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 /// \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, /// 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) void ScopeInternal::addSymbolInternal(Symbol *sym)
{ {
if (sym->symbolId == 0) {
sym->symbolId = Symbol::ID_BASE + (((uint8)uniqueId & 0xffff) << 40) + nextUniqueId;
nextUniqueId += 1;
}
try { try {
if (sym->name.size() == 0) if (sym->name.size() == 0)
sym->name = buildUndefinedName(); 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()); list<SymbolEntry>::iterator iter = rangemap->insert(initdata,addr.getOffset(),lastaddress.getOffset());
// Store reference to map in symbol // Store reference to map in symbol
sym->mapentry.push_back(iter); sym->mapentry.push_back(iter);
if (sz == sym->type->getSize()) {
sym->wholeCount += 1;
if (sym->wholeCount == 2)
multiEntrySet.insert(sym);
}
return &(*iter); return &(*iter);
} }
@ -1530,6 +1765,11 @@ SymbolEntry *ScopeInternal::addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 h
list<SymbolEntry>::iterator iter = dynamicentry.end(); list<SymbolEntry>::iterator iter = dynamicentry.end();
--iter; --iter;
sym->mapentry.push_back(iter); // Store reference to map entry in symbol 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(); return &dynamicentry.back();
} }
@ -1594,6 +1834,7 @@ list<SymbolEntry>::iterator ScopeInternal::endDynamic(void)
ScopeInternal::ScopeInternal(const string &nm,Architecture *g) ScopeInternal::ScopeInternal(const string &nm,Architecture *g)
: Scope(nm,g) : Scope(nm,g)
{ {
nextUniqueId = 0;
int4 numspaces = g->numSpaces(); int4 numspaces = g->numSpaces();
for(int4 i=0;i<numspaces;++i) for(int4 i=0;i<numspaces;++i)
maptable.push_back((EntryMap *)0); maptable.push_back((EntryMap *)0);
@ -1624,6 +1865,7 @@ void ScopeInternal::clear(void)
Symbol *sym = *iter++; Symbol *sym = *iter++;
removeSymbol(sym); removeSymbol(sym);
} }
nextUniqueId = 0;
} }
/// Look for NULL entries in the category tables. If there are, /// 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; vector<list<SymbolEntry>::iterator>::iterator iter;
if (symbol->category >= 0) { if (symbol->wholeCount > 1)
vector<Symbol *> &list(category[symbol->category]); multiEntrySet.erase(symbol);
list[symbol->catindex] = (Symbol *)0;
while((!list.empty())&&(list.back() == (Symbol *)0))
list.pop_back();
}
// Remove each mapping of the symbol // Remove each mapping of the symbol
for(iter=symbol->mapentry.begin();iter!=symbol->mapentry.end();++iter) { for(iter=symbol->mapentry.begin();iter!=symbol->mapentry.end();++iter) {
AddrSpace *spc = (*(*iter)).getAddr().getSpace(); AddrSpace *spc = (*(*iter)).getAddr().getSpace();
@ -1762,6 +1999,20 @@ void ScopeInternal::removeSymbol(Symbol *symbol)
rangemap->erase( *iter ); 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); nametree.erase(symbol);
delete symbol; delete symbol;
} }
@ -1770,9 +2021,13 @@ 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; string oldname = sym->name;
sym->name = newname; sym->name = newname;
insertNameTree(sym); 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) void ScopeInternal::retypeSymbol(Symbol *sym,Datatype *ct)
@ -1795,6 +2050,7 @@ void ScopeInternal::retypeSymbol(Symbol *sym,Datatype *ct)
// Remove the map entry // Remove the map entry
rangemap->erase(iter); rangemap->erase(iter);
sym->mapentry.pop_back(); // Remove reference to map entry sym->mapentry.pop_back(); // Remove reference to map entry
sym->wholeCount = 0;
// Now we are ready to change the type // Now we are ready to change the type
sym->type = ct; 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, string ScopeInternal::buildVariableName(const Address &addr,
const Address &pc, const Address &pc,
Datatype *ct,int4 &index,uint4 flags) const Datatype *ct,int4 &index,uint4 flags) const
@ -2435,6 +2700,26 @@ void ScopeInternal::setCategory(Symbol *sym,int4 cat,int4 ind)
list[sym->catindex] = sym; 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 /// Check to make sure the Scope is a \e namespace then remove all
/// its address ranges from the map. /// its address ranges from the map.
/// \param scope is the given Scope /// \param scope is the given Scope
@ -2509,9 +2794,12 @@ void Database::attachScope(Scope *newscope,Scope *parent)
throw LowlevelError("Multiple global scopes"); throw LowlevelError("Multiple global scopes");
if (newscope->name.size() != 0) if (newscope->name.size() != 0)
throw LowlevelError("Global scope does not have empty name"); throw LowlevelError("Global scope does not have empty name");
newscope->assignId(0);
globalscope = newscope; globalscope = newscope;
return; return;
} }
newscope->assignId(nextScopeId);
nextScopeId += 1;
parent->attachScope(newscope); parent->attachScope(newscope);
} }
@ -2525,7 +2813,7 @@ void Database::deleteScope(Scope *scope)
delete scope; delete scope;
} }
else { else {
ScopeKey key(scope->name,scope->dedupId); ScopeKey key(scope->name,scope->uniqueId);
ScopeMap::iterator iter = scope->parent->children.find(key); ScopeMap::iterator iter = scope->parent->children.find(key);
if (iter == scope->parent->children.end()) if (iter == scope->parent->children.end())
throw LowlevelError("Could not remove parent reference to: "+scope->name); throw LowlevelError("Could not remove parent reference to: "+scope->name);

View file

@ -157,15 +157,17 @@ class Symbol {
protected: protected:
Scope *scope; ///< The scope that owns this symbol Scope *scope; ///< The scope that owns this symbol
string name; ///< The local name of the 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 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 uint4 flags; ///< Varnode-like properties of the symbol
// only typelock,namelock,readonly,externref // only typelock,namelock,readonly,externref
// addrtied, persist inherited from scope // addrtied, persist inherited from scope
uint4 dispflags; ///< Flags affecting the display of this symbol uint4 dispflags; ///< Flags affecting the display of this symbol
int2 category; ///< Special category (-1==none 0=parameter 1=equate) int2 category; ///< Special category (-1==none 0=parameter 1=equate)
uint2 catindex; ///< Index within category 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 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 virtual ~Symbol(void) {} ///< Destructor
void setDisplayFormat(uint4 val); ///< Set the display format for \b this Symbol void setDisplayFormat(uint4 val); ///< Set the display format for \b this Symbol
void checkSizeTypeLock(void); ///< Calculate if \b size_typelock property is on 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_oct = 3, ///< Force octal printing of constant symbol
force_bin = 4, ///< Force binary 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 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 /// \brief Construct given a name and data-type
Symbol(Scope *sc,const string &nm,Datatype *ct) 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() /// \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 const string &getName(void) const { return name; } ///< Get the local name of the symbol
Datatype *getType(void) const { return type; } ///< Get the data-type 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 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 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 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 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 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 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 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 *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 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 saveXmlHeader(ostream &s) const; ///< Save basic Symbol properties as XML attributes
void restoreXmlHeader(const Element *el); ///< Restore basic Symbol properties from XML void restoreXmlHeader(const Element *el); ///< Restore basic Symbol properties from XML
void saveXmlBody(ostream &s) const; ///< Save details of the Symbol to 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 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 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 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 /// Force a specific display format for constant symbols
@ -404,12 +418,13 @@ class Scope {
ScopeMap children; ///< Sorted list of child scopes ScopeMap children; ///< Sorted list of child scopes
void attachScope(Scope *child); ///< Attach a new child Scope to \b this 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 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: protected:
Architecture *glb; ///< Architecture of \b this scope Architecture *glb; ///< Architecture of \b this scope
string name; ///< Name 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 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, static const Scope *stackAddr(const Scope *scope1,
const Scope *scope2, const Scope *scope2,
const Address &addr, const Address &addr,
@ -479,6 +494,7 @@ protected:
virtual SymbolEntry *addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 hash,int4 off,int4 sz, virtual SymbolEntry *addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 hash,int4 off,int4 sz,
const RangeList &uselim)=0; const RangeList &uselim)=0;
SymbolEntry *addMap(const SymbolEntry &entry); ///< Integrate a SymbolEntry into the range maps 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: public:
#ifdef OPACTION_DEBUG #ifdef OPACTION_DEBUG
mutable bool debugon; mutable bool debugon;
@ -487,7 +503,7 @@ public:
#endif #endif
/// \brief Construct an empty scope, given a name and Architecture /// \brief Construct an empty scope, given a name and Architecture
Scope(const string &nm,Architecture *g) { 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 #ifdef OPACTION_DEBUG
debugon = false; debugon = false;
#endif #endif
@ -515,6 +531,7 @@ public:
virtual bool inScope(const Address &addr,int4 size, const Address &usepoint) const { virtual bool inScope(const Address &addr,int4 size, const Address &usepoint) const {
return rangetree.inRange(addr,size); } 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 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 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 /// \param res will contain any matching Symbols
virtual void findByName(const string &name,vector<Symbol *> &res) const=0; 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 /// \brief Convert an \e external \e reference to the referenced function
/// ///
/// \param sym is the Symbol marking the external reference /// \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 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 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 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 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) 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 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); ExternRefSymbol *addExternalRef(const Address &addr,const Address &refaddr,const string &nm);
LabSymbol *addCodeLabel(const Address &addr,const string &nm); LabSymbol *addCodeLabel(const Address &addr,const string &nm);
Symbol *addDynamicSymbol(const string &nm,Datatype *ct,const Address &caddr,uint8 hash); 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; 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 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<EntryMap *> maptable; ///< Rangemaps of SymbolEntry, one map for each address space
vector<vector<Symbol *> > category; ///< References to Symbol objects organized by category vector<vector<Symbol *> > category; ///< References to Symbol objects organized by category
list<SymbolEntry> dynamicentry; ///< Dynamic symbol entries list<SymbolEntry> dynamicentry; ///< Dynamic symbol entries
SymbolNameTree multiEntrySet; ///< Set of symbols with multiple entries
uint8 nextUniqueId; ///< Next available symbol id
public: public:
ScopeInternal(const string &nm,Architecture *g); ///< Construct the Scope ScopeInternal(const string &nm,Architecture *g); ///< Construct the Scope
virtual void clear(void); virtual void clear(void);
@ -716,6 +747,7 @@ public:
virtual list<SymbolEntry>::const_iterator endDynamic(void) const; virtual list<SymbolEntry>::const_iterator endDynamic(void) const;
virtual list<SymbolEntry>::iterator beginDynamic(void); virtual list<SymbolEntry>::iterator beginDynamic(void);
virtual list<SymbolEntry>::iterator endDynamic(void); virtual list<SymbolEntry>::iterator endDynamic(void);
virtual void removeSymbolMappings(Symbol *symbol);
virtual void removeSymbol(Symbol *symbol); virtual void removeSymbol(Symbol *symbol);
virtual void renameSymbol(Symbol *sym,const string &newname); virtual void renameSymbol(Symbol *sym,const string &newname);
virtual void retypeSymbol(Symbol *sym,Datatype *ct); virtual void retypeSymbol(Symbol *sym,Datatype *ct);
@ -734,6 +766,7 @@ public:
virtual SymbolEntry *findOverlap(const Address &addr,int4 size) const; virtual SymbolEntry *findOverlap(const Address &addr,int4 size) const;
virtual void findByName(const string &name,vector<Symbol *> &res) 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 Funcdata *resolveExternalRefFunction(ExternRefSymbol *sym) const;
virtual string buildVariableName(const Address &addr, virtual string buildVariableName(const Address &addr,
@ -747,6 +780,9 @@ public:
virtual int4 getCategorySize(int4 cat) const; virtual int4 getCategorySize(int4 cat) const;
virtual Symbol *getCategorySymbol(int4 cat,int4 ind) const; virtual Symbol *getCategorySymbol(int4 cat,int4 ind) const;
virtual void setCategory(Symbol *sym,int4 cat,int4 ind); 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 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 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 Scope *globalscope; ///< A quick reference to the \e global Scope
ScopeResolve resolvemap; ///< The Address to \e namespace map ScopeResolve resolvemap; ///< The Address to \e namespace map
partmap<Address,uint4> flagbase; ///< Map of global properties 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 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 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 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); static void parseParentTag(const Element *el,string &name,vector<string> &parnames);
public: 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 ~Database(void); ///< Destructor
Architecture *getArch(void) const { return glb; } ///< Get the Architecture associate with \b this Architecture *getArch(void) const { return glb; } ///< Get the Architecture associate with \b this
void attachScope(Scope *newscope,Scope *parent); ///< Register a new Scope 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 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 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 begin(void) const { throw LowlevelError("begin unimplemented"); }
virtual MapIterator end(void) const { throw LowlevelError("end 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 clearUnlockedCategory(int4 cat) { throw LowlevelError("clearUnlockedCategory unimplemented"); }
virtual void clearUnlocked(void) { throw LowlevelError("clearUnlocked unimplemented"); } virtual void clearUnlocked(void) { throw LowlevelError("clearUnlocked unimplemented"); }
virtual void restrictScope(Funcdata *f) { throw LowlevelError("restrictScope 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 removeSymbol(Symbol *symbol) { throw LowlevelError("removeSymbol unimplemented"); }
virtual void renameSymbol(Symbol *sym,const string &newname) { throw LowlevelError("renameSymbol unimplemented"); } virtual void renameSymbol(Symbol *sym,const string &newname) { throw LowlevelError("renameSymbol unimplemented"); }
virtual void retypeSymbol(Symbol *sym,Datatype *ct) { throw LowlevelError("retypeSymbol 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 addr is the given address
/// \param h is the hash /// \param h is the hash
/// \return the matching Varnode or NULL /// \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); 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 fd is the function holding the data-flow
/// \param addr is the given address /// \param addr is the given address
/// \param h is the given hash /// \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); OpCode opc = getOpCodeFromHash(h);

View file

@ -79,11 +79,11 @@ public:
void clear(void); ///< Called for each additional hash (after the first) 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 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 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 uint8 getHash(void) const { return hash; } ///< Get the (current) hash
const Address &getAddress(void) const { return addrresult; } ///< Get the (current) address 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 int4 getSlotFromHash(uint8 h); ///< Retrieve the encoded slot from a hash
static uint4 getMethodFromHash(uint8 h); ///< Retrieve the encoded method 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 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 (res->sym == (Symbol *)0) {
if (scope->discoverScope(pieces.addr,pieces.type->getSize(),usepoint) != scope) if (scope->discoverScope(pieces.addr,pieces.type->getSize(),usepoint) != scope)
usepoint = restricted_usepoint; usepoint = restricted_usepoint;
string name; res->sym = scope->addSymbol(nm,pieces.type,pieces.addr,usepoint)->getSymbol();
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();
scope->setCategory(res->sym,0,i); scope->setCategory(res->sym,0,i);
if ((pieces.flags & (Varnode::indirectstorage|Varnode::hiddenretparm)) != 0) if ((pieces.flags & (Varnode::indirectstorage|Varnode::hiddenretparm)) != 0)
scope->setAttribute(res->sym,pieces.flags & (Varnode::indirectstorage|Varnode::hiddenretparm)); 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 void Funcdata::saveXmlHigh(ostream &s) const
{ {
int4 j;
Varnode *vn; Varnode *vn;
HighVariable *high; HighVariable *high;
@ -620,32 +619,7 @@ void Funcdata::saveXmlHigh(ostream &s) const
high = vn->getHigh(); high = vn->getHigh();
if (high->isMark()) continue; if (high->isMark()) continue;
high->setMark(); high->setMark();
vn = high->getNameRepresentative(); // Get representative varnode high->saveXml(s);
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>";
} }
for(iter=beginLoc();iter!=endLoc();++iter) { for(iter=beginLoc();iter!=endLoc();++iter) {
vn = *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 /// If indicated by the caller, a description of the entire PcodeOp and Varnode
/// tree is also emitted. /// tree is also emitted.
/// \param s is the output stream /// \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 /// \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"; s << "<function";
if (id != 0)
a_v_u(s, "id", id);
a_v(s,"name",name); a_v(s,"name",name);
a_v_i(s,"size",size); a_v_i(s,"size",size);
if (hasNoCode()) 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, /// From an XML \<function> tag, recover the name, address, prototype, symbol,
/// jump-table, and override information for \b this function. /// jump-table, and override information for \b this function.
/// \param el is the root \<function> tag /// \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 // clear(); // Shouldn't be needed
name.clear(); name.clear();
size = -1; size = -1;
uint8 id = 0;
AddrSpace *stackid = glb->getStackSpace(); AddrSpace *stackid = glb->getStackSpace();
for(int4 i=0;i<el->getNumAttributes();++i) { 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); name = el->getAttributeValue(i);
else if (el->getAttributeName(i) == "size") { else if (attrName == "size") {
istringstream s( el->getAttributeValue(i)); istringstream s( el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct); s.unsetf(ios::dec | ios::hex | ios::oct);
s >> size; 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))) if (xml_readbool(el->getAttributeValue(i)))
flags |= no_code; flags |= no_code;
} }
@ -805,6 +790,7 @@ void Funcdata::restoreXml(const Element *el)
funcp.setScope(localmap,baseaddr+ -1); funcp.setScope(localmap,baseaddr+ -1);
} }
localmap->resetLocalWindow(); localmap->resetLocalWindow();
return id;
} }
/// \brief Inject p-code from a \e payload into \b this live function /// \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 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 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 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 saveXml(ostream &s,uint8 id,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 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 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 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 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 clearDeadVarnodes(void); ///< Delete any dead Varnodes
void calcNZMask(void); ///< Calculate \e non-zero masks for all Varnodes void calcNZMask(void); ///< Calculate \e non-zero masks for all Varnodes
void clearDeadOps(void) { obank.destroyDead(); } ///< Delete any dead PcodeOps 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 *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 void buildDynamicSymbol(Varnode *vn); ///< Build a \e dynamic Symbol associated with the given Varnode
bool attemptDynamicMapping(SymbolEntry *entry,DynamicHash &dhash); 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 Merge &getMerge(void) { return covermerge; } ///< Get the Merge object for \b this function
// op routines // 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 /// 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 /// Varnode is marked as \e implicit and has its data-type set
/// \param op is the given PTRADD /// \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) 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 // One more chance to find entry, now that we know usepoint
uint4 vflags=0; uint4 vflags=0;
SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),vn->getUsePoint(*this),vflags); SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),vn->getUsePoint(*this),vflags);
if (entry != (SymbolEntry *)0) { // Let entry try to force type if (entry != (SymbolEntry *)0) // Let entry try to force type
entry->updateType(vn); vn->setSymbolProperties(entry);
if (entry->getSymbol()->isTypeLocked()) else
vn->mapentry = entry; vn->setFlags(vflags & ~Varnode::typelock); // typelock set by updateType
}
vn->setFlags(vflags & ~Varnode::typelock); // typelock set by updateType
} }
if (vn->cover == (Cover *)0) { if (vn->cover == (Cover *)0) {
@ -108,12 +106,10 @@ Varnode *Funcdata::newVarnodeOut(int4 s,const Address &m,PcodeOp *op)
uint4 vflags = 0; uint4 vflags = 0;
SymbolEntry *entry = localmap->queryProperties(m,s,op->getAddr(),vflags); SymbolEntry *entry = localmap->queryProperties(m,s,op->getAddr(),vflags);
if (entry != (SymbolEntry *)0) { if (entry != (SymbolEntry *)0)
entry->updateType(vn); vn->setSymbolProperties(entry);
if (entry->getSymbol()->isTypeLocked()) else
vn->mapentry = entry; vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
}
vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
return vn; return vn;
} }
@ -153,12 +149,10 @@ Varnode *Funcdata::newVarnode(int4 s,const Address &m,Datatype *ct)
uint4 vflags=0; uint4 vflags=0;
SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),Address(),vflags); SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),Address(),vflags);
if (entry != (SymbolEntry *)0) { // Let entry try to force type if (entry != (SymbolEntry *)0) // Let entry try to force type
entry->updateType(vn); vn->setSymbolProperties(entry);
if (entry->getSymbol()->isTypeLocked()) else
vn->mapentry = entry; vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
}
vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
return vn; return vn;
} }
@ -313,18 +307,10 @@ HighVariable *Funcdata::findHigh(const string &name) const
localmap->queryByName(name,symList); localmap->queryByName(name,symList);
if (symList.empty()) return (HighVariable *)0; if (symList.empty()) return (HighVariable *)0;
Symbol *sym = symList[0]; Symbol *sym = symList[0];
SymbolEntry *entry = sym->getFirstWholeMap(); Varnode *vn = findLinkedVarnode(sym->getFirstWholeMap());
if (vn != (Varnode *)0)
return vn->getHigh();
VarnodeLocSet::const_iterator iter,enditer;
HighVariable *high;
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; return (HighVariable *)0;
} }
@ -913,7 +899,16 @@ bool Funcdata::syncVarnodesWithSymbol(VarnodeLocSet::const_iterator &iter,uint4
vn = *iter++; vn = *iter++;
if (vn->isFree()) continue; if (vn->isFree()) continue;
vnflags = vn->getFlags(); 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; updateoccurred = true;
vn->setFlags(flags); vn->setFlags(flags);
vn->clearFlags((~flags)&mask); vn->clearFlags((~flags)&mask);
@ -927,6 +922,49 @@ bool Funcdata::syncVarnodesWithSymbol(VarnodeLocSet::const_iterator &iter,uint4
return updateoccurred; 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 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 /// The only reason a Symbol doesn't get set is if, the HighVariable
/// is global and there is no pre-existing Symbol. (see mapGlobals()) /// is global and there is no pre-existing Symbol. (see mapGlobals())
@ -941,38 +979,117 @@ Symbol *Funcdata::linkSymbol(Varnode *vn)
Symbol *sym = high->getSymbol(); Symbol *sym = high->getSymbol();
if (sym != (Symbol *)0) return sym; // Symbol already assigned if (sym != (Symbol *)0) return sym; // Symbol already assigned
entry = vn->getSymbolEntry(); // Check if we have a symbol already cached Address usepoint = vn->getUsePoint(*this);
if (entry == (SymbolEntry *)0) { // Find any entry overlapping base address
Address usepoint = vn->getUsePoint(*this); entry = localmap->queryProperties(vn->getAddr(), 1, usepoint, flags);
// Find any entry overlapping base address if (entry != (SymbolEntry *) 0) {
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
sym = entry->getSymbol(); sym = entry->getSymbol();
if (sym != (Symbol *)0) { vn->setSymbolEntry(entry);
int4 offset; }
if (sym->getCategory() == 1) // For equates we don't care about size else { // Must create a symbol entry
offset = -1; if (!vn->isPersist()) { // Only create local symbol
else if ((sym->getType()->getSize() == vn->getSize())&& entry = localmap->addSymbol("", high->getType(), vn->getAddr(), usepoint);
(entry->getAddr() == vn->getAddr())&&(!entry->isPiece())) // A matching entry sym = entry->getSymbol();
offset = -1; vn->setSymbolEntry(entry);
else }
offset = vn->getAddr().overlap(0,entry->getAddr(),sym->getType()->getSize()) + entry->getOffset();
high->setSymbol(sym,offset);
} }
return sym; 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 /// 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 /// created that is associated with the Varnode via a hash of its local data-flow (rather
/// than its storage address). /// than its storage address).
@ -994,7 +1111,7 @@ void Funcdata::buildDynamicSymbol(Varnode *vn)
throw RecovError("Unable to find unique hash for varnode"); throw RecovError("Unable to find unique hash for varnode");
Symbol *sym = localmap->addDynamicSymbol("",high->getType(),dhash.getAddress(),dhash.getHash()); 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 /// \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 (vn == (Varnode *)0) return false;
if (entry->getSymbol()->getCategory() == 1) { // Is this an equate symbol if (entry->getSymbol()->getCategory() == 1) { // Is this an equate symbol
if (vn->mapentry != entry) { // Check we haven't marked this before if (vn->mapentry != entry) { // Check we haven't marked this before
uint4 flags = entry->getAllFlags(); // Mark that the varnode is mapped vn->setSymbolEntry(entry);
vn->setFlags(flags & ~Varnode::typelock); // Don't pass data-type and typelock to Varnode
vn->mapentry = entry;
return true; return true;
} }
} }
else if (entry->updateType(vn)) { else if (entry->getSize() == vn->getSize()) {
if (entry->getSize() != vn->getSize()) return false; if (vn->setSymbolProperties(entry))
uint4 flags = entry->getAllFlags(); return true;
vn->setFlags(flags & ~Varnode::typelock); // Mark that the varnode is mapped
if (entry->getSymbol()->isTypeLocked())
vn->mapentry = entry;
return true;
} }
return false; 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 /// \brief Replace all read references to the first Varnode with a second Varnode
/// ///
/// \param vn is the first Varnode (being replaced) /// \param vn is the first Varnode (being replaced)

View file

@ -314,7 +314,7 @@ void DecompileAt::rawAction(void)
ParamIDAnalysis pidanalysis( fd, false ); ParamIDAnalysis pidanalysis( fd, false );
pidanalysis.saveXml( sout, true ); pidanalysis.saveXml( sout, true );
} }
fd->saveXml(sout,ghidra->getSendSyntaxTree()); fd->saveXml(sout,0,ghidra->getSendSyntaxTree());
if (ghidra->getSendCCode()&& if (ghidra->getSendCCode()&&
(ghidra->allacts.getCurrentName() == "decompile")) (ghidra->allacts.getCurrentName() == "decompile"))
ghidra->print->docFunction(fd); 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 scope = dcp->fd->getScopeLocal(); // force it to be in function scope
Symbol *sym = scope->addSymbol(name,ct,loc,pc)->getSymbol(); Symbol *sym = scope->addSymbol(name,ct,loc,pc)->getSymbol();
scope->setAttribute(sym,Varnode::typelock); scope->setAttribute(sym,Varnode::typelock);
sym->setIsolated(true);
if (name.size() > 0) if (name.size() > 0)
scope->setAttribute(sym,Varnode::namelock); 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()) else if (high_out->isExtraOut())
return false; 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; return true;
} }
@ -136,6 +145,14 @@ bool Merge::mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in)
Varnode *vn = high_in->getInputVarnode(); Varnode *vn = high_in->getInputVarnode();
if (vn->isIllegalInput()&&(!vn->isIndirectOnly())) return false; 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; return true;
} }
@ -151,9 +168,6 @@ bool Merge::mergeTestSpeculative(HighVariable *high_out,HighVariable *high_in)
{ {
if (!mergeTestAdjacent(high_out,high_in)) return false; 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 // Don't merge anything with a global speculatively
if (high_out->isPersist()) return false; if (high_out->isPersist()) return false;
if (high_in->isPersist()) return false; if (high_in->isPersist()) return false;
@ -268,7 +282,7 @@ void Merge::mergeOpcode(OpCode opc)
vn2 = op->getIn(j); vn2 = op->getIn(j);
if (!mergeTestBasic(vn2)) continue; if (!mergeTestBasic(vn2)) continue;
if (mergeTestRequired(vn1->getHigh(),vn2->getHigh())) 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 /// \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, /// 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) { 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 mergeAddrTied(void);
void mergeMarker(void); void mergeMarker(void);
void mergeAdjacent(void); void mergeAdjacent(void);
void mergeMultiEntry(void);
bool hideShadows(HighVariable *high); bool hideShadows(HighVariable *high);
void processCopyTrims(void); void processCopyTrims(void);
void markInternalCopies(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 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 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 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 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 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 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) { else if (ct->getMetatype() == TYPE_SPACEBASE) {
TypeSpacebase *sb = (TypeSpacebase *)ct; HighVariable *high = op->getIn(1)->getHigh();
Scope *scope = sb->getMap(); Symbol *symbol = high->getSymbol();
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;
arrayvalue = false; arrayvalue = false;
if (entry != (SymbolEntry *)0) { if (symbol != (Symbol *)0) {
ct = entry->getSymbol()->getType(); ct = symbol->getType();
// The '&' is dropped if the output type is an array // The '&' is dropped if the output type is an array
if (ct->getMetatype()==TYPE_ARRAY) { if (ct->getMetatype()==TYPE_ARRAY) {
arrayvalue = valueon; // If printing value, use [0] arrayvalue = valueon; // If printing value, use [0]
@ -795,18 +790,21 @@ void PrintC::opPtrsub(const PcodeOp *op)
if (arrayvalue) if (arrayvalue)
pushOp(&subscript,op); 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); pushUnnamedLocation(addr,(Varnode *)0,op);
}
else { else {
int4 off = (int4)(addr.getOffset() - entry->getAddr().getOffset()) + entry->getOffset(); int4 off = high->getSymbolOffset();
if (off == 0) if (off == 0)
pushSymbol(entry->getSymbol(),(Varnode *)0,op); pushSymbol(symbol,(Varnode *)0,op);
else { else {
// If this "value" is getting used as a storage location // If this "value" is getting used as a storage location
// we can't use a cast in its description, so turn off // we can't use a cast in its description, so turn off
// casting when printing the partial symbol // casting when printing the partial symbol
// Datatype *exttype = ((mods & print_store_value)!=0) ? (Datatype *)0 : ct; // 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) if (arrayvalue)
@ -1641,6 +1639,21 @@ void PrintC::pushSymbol(const Symbol *sym,const Varnode *vn,const PcodeOp *op)
else else
tokenColor = EmitXml::var_color; tokenColor = EmitXml::var_color;
// FIXME: resolve scopes // 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)); 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 iter = scope->begin();
MapIterator enditer = scope->end(); MapIterator enditer = scope->end();
for(;iter!=enditer;++iter) { for(;iter!=enditer;++iter) {
if ((*iter)->isPiece()) continue; // Don't do a partial entry const SymbolEntry *entry = *iter;
Symbol *sym = (*iter)->getSymbol(); if (entry->isPiece()) continue; // Don't do a partial entry
Symbol *sym = entry->getSymbol();
if (sym->getCategory() != cat) continue; if (sym->getCategory() != cat) continue;
if (sym->getName().size() == 0) continue; if (sym->getName().size() == 0) continue;
if (dynamic_cast<FunctionSymbol *>(sym) != (FunctionSymbol *)0) if (dynamic_cast<FunctionSymbol *>(sym) != (FunctionSymbol *)0)
continue; continue;
if (dynamic_cast<LabSymbol *>(sym) != (LabSymbol *)0) if (dynamic_cast<LabSymbol *>(sym) != (LabSymbol *)0)
continue; continue;
if (sym->isMultiEntry()) {
if (sym->getFirstWholeMap() != entry)
continue; // Only emit the first SymbolEntry for declaration of multi-entry Symbol
}
notempty = true; notempty = true;
emitVarDeclStatement(sym); emitVarDeclStatement(sym);
} }
list<SymbolEntry>::const_iterator iter_d = scope->beginDynamic(); list<SymbolEntry>::const_iterator iter_d = scope->beginDynamic();
list<SymbolEntry>::const_iterator enditer_d = scope->endDynamic(); list<SymbolEntry>::const_iterator enditer_d = scope->endDynamic();
for(;iter_d!=enditer_d;++iter_d) { 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(); Symbol *sym = (*iter_d).getSymbol();
if (sym->getCategory() != cat) continue; if (sym->getCategory() != cat) continue;
if (sym->getName().size() == 0) continue; if (sym->getName().size() == 0) continue;
@ -2204,6 +2223,10 @@ bool PrintC::emitScopeVarDecls(const Scope *scope,int4 cat)
continue; continue;
if (dynamic_cast<LabSymbol *>(sym) != (LabSymbol *)0) if (dynamic_cast<LabSymbol *>(sym) != (LabSymbol *)0)
continue; continue;
if (sym->isMultiEntry()) {
if (sym->getFirstWholeMap() != entry)
continue;
}
notempty = true; notempty = true;
emitVarDeclStatement(sym); emitVarDeclStatement(sym);
} }

View file

@ -1884,9 +1884,9 @@ int4 RuleDoubleShift::applyOp(PcodeOp *op,Funcdata &data)
} }
/// \class RuleDoubleArithShift /// \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. /// The shift amounts add up to the point where the sign bit has saturated the entire result.
void RuleDoubleArithShift::getOpList(vector<uint4> &oplist) const 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(const LanedRegister *lanedR) { size = 0; mask = lanedR->sizeBitMask; normalize(); } ///< Constructor
LanedIterator(void) { size = -1; mask = 0; } ///< Constructor for ending iterator LanedIterator(void) { size = -1; mask = 0; } ///< Constructor for ending iterator
LanedIterator &operator++(void) { size += 1; normalize(); return *this; } ///< Preincrement operator 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 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); } ///< Equal operator
bool operator!=(const LanedIterator &op2) const { return (size != op2.size); } ///< Not-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: private:
int4 wholeSize; ///< Size of the whole register int4 wholeSize; ///< Size of the whole register
uint4 sizeBitMask; ///< A 1-bit for every permissible lane size uint4 sizeBitMask; ///< A 1-bit for every permissible lane size

View file

@ -23,13 +23,57 @@ HighVariable::HighVariable(Varnode *vn)
{ {
numMergeClasses = 1; numMergeClasses = 1;
highflags = HighVariable::flagsdirty | HighVariable::typedirty | HighVariable::coverdirty; highflags = flagsdirty | namerepdirty | typedirty | coverdirty;
flags = 0; flags = 0;
type = (Datatype *)0; type = (Datatype *)0;
symbol = (Symbol *)0; symbol = (Symbol *)0;
nameRepresentative = (Varnode *)0;
symboloffset = -1; symboloffset = -1;
inst.push_back(vn); inst.push_back(vn);
vn->setHigh( this, numMergeClasses-1 ); 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. /// Only update if the cover is marked as \e dirty.
@ -38,8 +82,8 @@ HighVariable::HighVariable(Varnode *vn)
void HighVariable::updateCover(void) const void HighVariable::updateCover(void) const
{ {
if ((highflags & HighVariable::coverdirty)==0) return; // Cover info is upto date if ((highflags & coverdirty)==0) return; // Cover info is upto date
highflags &= ~HighVariable::coverdirty; highflags &= ~coverdirty;
wholecover.clear(); wholecover.clear();
if (!inst[0]->hasCover()) return; if (!inst[0]->hasCover()) return;
@ -53,11 +97,11 @@ void HighVariable::updateCover(void) const
void HighVariable::updateFlags(void) const void HighVariable::updateFlags(void) const
{ {
vector<Varnode *>::const_iterator iter; if ((highflags & flagsdirty)==0) return; // flags are up to date
uint4 fl;
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) for(iter=inst.begin();iter!=inst.end();++iter)
fl |= (*iter)->getFlags(); fl |= (*iter)->getFlags();
@ -65,7 +109,7 @@ void HighVariable::updateFlags(void) const
flags &= (Varnode::mark | Varnode::typelock); flags &= (Varnode::mark | Varnode::typelock);
// Update all but these // Update all but these
flags |= fl & ~(Varnode::mark | Varnode::directwrite | Varnode::typelock ); 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. /// Using Datatype::typeOrder, find the member Varnode with the most specific data-type.
@ -98,8 +142,8 @@ void HighVariable::updateType(void) const
{ {
Varnode *vn; Varnode *vn;
if ((highflags&HighVariable::typedirty)==0) return; // Type is up to date if ((highflags&typedirty)==0) return; // Type is up to date
highflags &= ~HighVariable::typedirty; // Mark type as clean highflags &= ~typedirty; // Mark type as clean
if ((highflags & type_finalized)!=0) return; // Type has been finalized if ((highflags & type_finalized)!=0) return; // Type has been finalized
vn = getTypeRepresentative(); vn = getTypeRepresentative();
@ -110,6 +154,24 @@ void HighVariable::updateType(void) const
flags |= Varnode::typelock; 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 /// Compare two Varnode objects based just on their storage address
/// \param a is the first Varnode to compare /// \param a is the first Varnode to compare
/// \param b is the second Varnode /// \param b is the second Varnode
@ -168,18 +230,22 @@ bool HighVariable::compareName(Varnode *vn1,Varnode *vn2)
Varnode *HighVariable::getNameRepresentative(void) const Varnode *HighVariable::getNameRepresentative(void) const
{ {
if ((highflags & namerepdirty)==0)
return nameRepresentative; // Name representative is up to date
highflags &= ~namerepdirty;
vector<Varnode *>::const_iterator iter; vector<Varnode *>::const_iterator iter;
Varnode *rep,*vn; Varnode *vn;
iter = inst.begin(); iter = inst.begin();
rep = *iter; nameRepresentative = *iter;
++iter; ++iter;
for(;iter!=inst.end();++iter) { for(;iter!=inst.end();++iter) {
vn = *iter; vn = *iter;
if (compareName(rep,vn)) if (compareName(nameRepresentative,vn))
rep = 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. /// 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) { for(;iter!=inst.end();++iter) {
if (*iter == vn) { if (*iter == vn) {
inst.erase(iter); inst.erase(iter);
highflags |= (HighVariable::flagsdirty|HighVariable::coverdirty|HighVariable::typedirty); highflags |= (flagsdirty|namerepdirty|coverdirty|typedirty);
if (vn->getSymbolEntry() != (SymbolEntry *)0)
highflags |= symboldirty;
return; 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 /// The data-type its dirtying mechanism is disabled. The data-type will not change, unless
/// this method is called again. /// this method is called again.
/// \param tp is the data-type to set /// \param tp is the data-type to set
@ -219,12 +301,14 @@ void HighVariable::merge(HighVariable *tv2,bool isspeculative)
if (tv2 == this) return; if (tv2 == this) return;
// if (isAddrTied() && tv2->isAddrTied()) { highflags |= (flagsdirty|namerepdirty|typedirty);
// if (tied_varnode()->getAddr() != tv2->tied_varnode()->getAddr()) if (tv2->symbol != (Symbol *)0) { // Check if we inherit a Symbol
// throw LowlevelError("Merging different addrtieds"); if ((tv2->highflags & symboldirty)==0) {
// } symbol = tv2->symbol; // Overwrite our Symbol (assume it is the same)
symboloffset = tv2->symboloffset;
highflags |= (HighVariable::flagsdirty|HighVariable::typedirty); highflags &= ~((uint4)symboldirty); // Mark that we are not symbol dirty
}
}
if (isspeculative) { if (isspeculative) {
for(i=0;i<tv2->inst.size();++i) { 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); std::merge(instcopy.begin(),instcopy.end(),tv2->inst.begin(),tv2->inst.end(),inst.begin(),compareJustLoc);
tv2->inst.clear(); 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); wholecover.merge(tv2->wholecover);
else else
highflags |= HighVariable::coverdirty; highflags |= coverdirty;
delete tv2; delete tv2;
} }
@ -359,6 +443,46 @@ int4 HighVariable::instanceIndex(const Varnode *vn) const
return -1; 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 #ifdef MERGEMULTI_DEBUG
/// \brief Check that there are no internal Cover intersections within \b this /// \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 /// So we keep track of when these inherited values are \e dirty
enum { enum {
flagsdirty = 1, ///< Boolean properties for the HighVariable are dirty flagsdirty = 1, ///< Boolean properties for the HighVariable are dirty
typedirty = 2, ///< The data-type for the HighVariable is dirty namerepdirty = 2, ///< The name representative for the HighVariable is dirty
coverdirty = 4, ///< The cover for the HighVariable is dirty typedirty = 4, ///< The data-type for the HighVariable is dirty
copy_in1 = 8, ///< There exists at least 1 COPY into \b this HighVariable from other HighVariables coverdirty = 8, ///< The cover for the HighVariable is dirty
copy_in2 = 16, ///< There exists at least 2 COPYs into \b this HighVariable from other HighVariables symboldirty = 0x10, ///< The symbol attachment is dirty
type_finalized = 32 ///< Set if a final data-type is locked in and dirtying is disabled 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: private:
friend class Varnode;
friend class Merge; friend class Merge;
vector<Varnode *> inst; ///< The member Varnode objects making up \b this HighVariable vector<Varnode *> inst; ///< The member Varnode objects making up \b this HighVariable
int4 numMergeClasses; ///< Number of different speculative merge classes in \b this int4 numMergeClasses; ///< Number of different speculative merge classes in \b this
mutable uint4 highflags; ///< Dirtiness flags mutable uint4 highflags; ///< Dirtiness flags
mutable uint4 flags; ///< Boolean properties inherited from Varnode members 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 Cover wholecover; ///< The ranges of code addresses covered by this HighVariable
mutable Symbol *symbol; ///< The Symbol \b this HighVariable is tied to mutable Symbol *symbol; ///< The Symbol \b this HighVariable is tied to
mutable int4 symboloffset; ///< -1=perfect symbol match >=0, offset 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 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 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 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 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 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 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 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 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 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: public:
HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode
Datatype *getType(void) const { updateType(); return type; } ///< Get the data-type Datatype *getType(void) const { updateType(); return type; } ///< Get the data-type
Symbol *getSymbol(void) const { updateSymbol(); return symbol; } ///< Get the Symbol associated with \b this or null
/// \brief Set the Symbol associated with \b this HighVariable. SymbolEntry *getSymbolEntry(void) const; /// Get the SymbolEntry mapping to \b this or null
///
/// 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
int4 getSymbolOffset(void) const { return symboloffset; } ///< Get the Symbol offset associated with \b this 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 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 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 void finalizeDatatype(Datatype *tp); ///< Set a final datatype for \b this variable
/// \brief Print details of the cover for \b this (for debug purposes) /// \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 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 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 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. /// \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 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 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 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 #ifdef MERGEMULTI_DEBUG
void verifyCover(void) const; void verifyCover(void) const;
#endif #endif

View file

@ -16,39 +16,6 @@
#include "varmap.hh" #include "varmap.hh"
#include "funcdata.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 /// \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 /// 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; rangeLocked = false;
stackGrowsNegative = true; stackGrowsNegative = true;
restrictScope(fd); 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 /// 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()) { while(iter!=nametree.end()) {
Symbol *sym = *iter++; Symbol *sym = *iter++;
if (sym->isNameLocked()&&(!sym->isTypeLocked())) { if (sym->isNameLocked()&&(!sym->isTypeLocked())) {
SymbolEntry *entry = sym->getFirstWholeMap(); addRecommendName(sym);
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);
}
} }
} }
} }
@ -447,12 +398,6 @@ string ScopeLocal::buildVariableName(const Address &addr,
Datatype *ct, Datatype *ct,
int4 &index,uint4 flags) const 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) && if (((flags & (Varnode::addrtied|Varnode::persist))==Varnode::addrtied) &&
addr.getSpace() == space) { addr.getSpace() == space) {
if (fd->getFuncProto().getLocalRange().inRange(addr,1)) { if (fd->getFuncProto().getLocalRange().inRange(addr,1)) {
@ -521,10 +466,7 @@ void ScopeLocal::createEntry(const RangeHint &a)
if (num>1) if (num>1)
ct = glb->types->getTypeArray(num,ct); ct = glb->types->getTypeArray(num,ct);
int4 index=0; addSymbol("",ct,addr,usepoint);
string nm = buildVariableName(addr,usepoint,ct,index,Varnode::addrtied);
addSymbol(nm,ct,addr,usepoint);
} }
/// Set up basic offset boundaries for what constitutes a local variable /// 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; int4 size = (endpoint - addr.getOffset()) + 1;
Datatype *ct = fd->getArch()->types->getBase(size,TYPE_UNKNOWN); 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 { try {
addSymbol(nm,ct,addr,usepoint)->getSymbol(); addSymbol("",ct,addr,usepoint)->getSymbol();
} }
catch(LowlevelError &err) { catch(LowlevelError &err) {
fd->warningHeader(err.explain); 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 /// 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. /// 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 /// This method runs through the recommended names and checks if they can be applied to an existing
/// unnamed Symbol. /// unnamed Symbol.
/// \param resname will hold the new name strings void ScopeLocal::recoverNameRecommendationsForSymbols(void)
/// \param ressym will hold the list of Symbols corresponding to the new name strings
void ScopeLocal::makeNameRecommendationsForSymbols(vector<string> &resname,vector<Symbol *> &ressym) const
{ // 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) { for(iter=nameRecommend.begin();iter!=nameRecommend.end();++iter) {
VarnodeLocSet::const_iterator biter,eiter; const Address &addr((*iter).getAddr());
bool isaddrtied; const Address &usepoint((*iter).getUseAddr());
const Address &addr((*iter).first.getAddr()); int4 size = (*iter).getSize();
const Address &useaddr((*iter).first.getUseAddr()); Symbol *sym;
int4 size = (*iter).first.getSize(); Varnode *vn = (Varnode *)0;
if (useaddr.isInvalid()) { if (usepoint.isInvalid()) {
isaddrtied = true; SymbolEntry *entry = findOverlap(addr, size); // Recover any Symbol regardless of usepoint
biter = fd->beginLoc(size,addr); if (entry == (SymbolEntry *)0) continue;
eiter = fd->endLoc(size,addr); 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 { else {
isaddrtied = false; vn = fd->findVarnodeWritten(size,addr,usepoint);
biter = fd->beginLoc(size,addr,useaddr); if (vn == (Varnode *)0) continue;
eiter = fd->endLoc(size,addr,useaddr); 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) { if (!sym->isNameUndefined()) continue;
Varnode *vn = *biter; renameSymbol(sym,makeNameUnique((*iter).getName()));
if (!vn->isAnnotation()) { setSymbolId(sym, (*iter).getSymbolId());
Symbol *sym = vn->getHigh()->getSymbol(); setAttribute(sym, Varnode::namelock);
if (sym != (Symbol *)0) { if (vn != (Varnode *)0) {
if (sym->isNameUndefined()) { fd->remapVarnode(vn, sym, usepoint);
resname.push_back( (*iter).second);
ressym.push_back(sym);
break;
}
}
}
if (isaddrtied) break;
++biter;
} }
} }
@ -1290,38 +1278,36 @@ void ScopeLocal::makeNameRecommendationsForSymbols(vector<string> &resname,vecto
if (vn == (Varnode *)0) continue; if (vn == (Varnode *)0) continue;
if (vn->isAnnotation()) continue; if (vn->isAnnotation()) continue;
Symbol *sym = vn->getHigh()->getSymbol(); Symbol *sym = vn->getHigh()->getSymbol();
if (sym != (Symbol *)0) { if (sym == (Symbol *)0) continue;
if (sym->isNameUndefined()) { if (sym->getScope() != this) continue;
resname.push_back( dynEntry.getName() ); if (!sym->isNameUndefined()) continue;
ressym.push_back(sym); 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 /// 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.
/// Recommended names are associated with a storage address, a use point, and a suggested size.
/// The name may be reattached to a Symbol after decompilation. /// The name may be reattached to a Symbol after decompilation.
/// \param addr is the storage address /// \param sym is the given Symbol to treat as a name recommendation
/// \param usepoint is the address of the code use point void ScopeLocal::addRecommendName(Symbol *sym)
/// \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)
{ {
nameRecommend[ AddressUsePointPair(addr,usepoint,sz) ] = nm; SymbolEntry *entry = sym->getFirstWholeMap();
} if (entry == (SymbolEntry *) 0) return;
if (entry->isDynamic()) {
/// \brief Add a new recommended name for a dynamic storage location to the list dynRecommend.push_back(DynamicRecommend(entry->getFirstUseAddress(), entry->getHash(), sym->getName(), sym->getId()));
/// }
/// This recommended name is assigned a storage location via the DynamicHash mechanism. else {
/// The name may be reattached to a Symbol after decompilation. Address usepoint;
/// \param addr is the address of the code use point if (!entry->getUseLimit().empty()) {
/// \param hash is the hash encoding context for identifying the storage location const Range *range = entry->getUseLimit().getFirstRange();
/// \param nm is the recommended name usepoint = Address(range->getSpace(), range->getFirst());
void ScopeLocal::addDynamicRecommend(const Address &usepoint,uint8 hash,const string &nm) }
nameRecommend.push_back(NameRecommend(entry->getAddr(),usepoint, entry->getSize(), sym->getName(), sym->getId()));
{ }
dynRecommend.push_back(DynamicRecommend(usepoint,hash,nm)); if (sym->getCategory() < 0)
removeSymbol(sym);
} }

View file

@ -21,26 +21,28 @@
#include "database.hh" #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. /// The name is associated with a static Address and use point in the code. Symbols
/// This object sorts first based on the storage address then on the use point. /// present at the end of function decompilation without a name can acquire \b this name
class AddressUsePointPair { /// if their storage matches.
class NameRecommend {
Address addr; ///< The starting address of the storage location Address addr; ///< The starting address of the storage location
Address useaddr; ///< The code address at the point of use Address useaddr; ///< The code address at the point of use
int4 size; ///< An optional/recommended size for the variable being stored 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: public:
AddressUsePointPair(const Address &ad,const Address &use,int4 sz); ///< Constructor NameRecommend(const Address &ad,const Address &use,int4 sz,const string &nm,uint8 id) :
AddressUsePointPair(const AddressUsePointPair &op2) : addr(op2.addr), useaddr(op2.useaddr) { addr(ad), useaddr(use), size(sz), name(nm), symbolId(id) {} ///< Constructor
size = op2.size; } ///< Copy constructor
const Address &getAddr(void) const { return addr; } ///< Get the storage address const Address &getAddr(void) const { return addr; } ///< Get the storage address
const Address &getUseAddr(void) const { return useaddr; } ///< Get the use point address const Address &getUseAddr(void) const { return useaddr; } ///< Get the use point address
int4 getSize(void) const { return size; } ///< Get the optional size int4 getSize(void) const { return size; } ///< Get the optional size
bool operator<(const AddressUsePointPair &op2) const; ///< Compare operation string getName(void) const { return name; } ///< Get the recommended name
bool operator==(const AddressUsePointPair &op2) const; ///< Test for equality 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 /// A recommendation for a symbol name whose storage is dynamic. The storage
/// is identified using the DynamicHash mechanism and may or may not exist. /// 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 Address usePoint; ///< Use point of the Symbol
uint8 hash; ///< Hash encoding the Symbols environment uint8 hash; ///< Hash encoding the Symbols environment
string name; ///< The local symbol name recommendation string name; ///< The local symbol name recommendation
uint8 symbolId; ///< Id associated with the original Symbol
public: public:
DynamicRecommend(const Address &addr,uint8 h,const string &nm) : DynamicRecommend(const Address &addr,uint8 h,const string &nm,uint8 id) :
usePoint(addr) { usePoint(addr), hash(h), name(nm), symbolId(id) {} ///< Constructor
hash = h;
name = nm;
} ///< Constructor
const Address &getAddress(void) const { return usePoint; } ///< Get the use point address const Address &getAddress(void) const { return usePoint; } ///< Get the use point address
uint8 getHash(void) const { return hash; } ///< Get the dynamic hash uint8 getHash(void) const { return hash; } ///< Get the dynamic hash
string getName(void) const { return name; } ///< Get the recommended name 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 /// \brief Partial data-type information mapped to a specific range of bytes
@ -181,7 +182,7 @@ public:
class ScopeLocal : public ScopeInternal { class ScopeLocal : public ScopeInternal {
AddrSpace *space; ///< Address space containing the local stack AddrSpace *space; ///< Address space containing the local stack
RangeList localRange; ///< The set of addresses that might hold mapped locals (not parameters) 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 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 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 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 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 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 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 addRecommendName(Symbol *sym); ///< Convert the given symbol to a name recommendation
void addDynamicRecommend(const Address &usepoint,uint8 hash,const string &nm);
void collectNameRecs(void); ///< Collect names of unlocked Symbols on the stack void collectNameRecs(void); ///< Collect names of unlocked Symbols on the stack
public: public:
ScopeLocal(AddrSpace *spc,Funcdata *fd,Architecture *g); ///< Constructor 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 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 restructureVarnode(bool aliasyes); ///< Layout mapped symbols based on Varnode information
void restructureHigh(void); ///< Layout mapped symbols based on HighVariable 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 #endif

View file

@ -360,6 +360,54 @@ void Varnode::setDef(PcodeOp *op)
setFlags(Varnode::coverdirty|Varnode::written); 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 /// 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 change a previously locked Datatype (unless \b override flag is \b true)
/// - Don't consider an \b undefined type to be locked /// - 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 mapentry = vn->mapentry; // Copy any symbol
flags &= ~(Varnode::typelock | Varnode::namelock); flags &= ~(Varnode::typelock | Varnode::namelock);
flags |= (Varnode::typelock | Varnode::namelock) & vn->flags; flags |= (Varnode::typelock | Varnode::namelock) & vn->flags;
if (high != (HighVariable *)0) if (high != (HighVariable *)0) {
high->typeDirty(); high->typeDirty();
if (mapentry != (SymbolEntry *)0)
high->setSymbol(this);
}
} }
/// Symbol information (if present) is copied from the given constant Varnode into \b 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 // These functions should be only private things used by VarnodeBank
void setInput(void) { setFlags(Varnode::input|Varnode::coverdirty); } ///< Mark Varnode as \e input void setInput(void) { setFlags(Varnode::input|Varnode::coverdirty); } ///< Mark Varnode as \e input
void setDef(PcodeOp *op); ///< Set the defining PcodeOp of this Varnode 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 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 eraseDescend(PcodeOp *op); ///< Erase a descendant (reading) PcodeOp from this Varnode's list
void destroyDescend(void); ///< Clear all descendant (reading) PcodeOps void destroyDescend(void); ///< Clear all descendant (reading) PcodeOps

View file

@ -208,8 +208,9 @@ public class DecompilerParameterIdCmd extends BackgroundCommand {
boolean commitReturn = true; boolean commitReturn = true;
if (!commitVoidReturn) { if (!commitVoidReturn) {
DataType returnType = hfunc.getFunctionPrototype().getReturnType(); DataType returnType = hfunc.getFunctionPrototype().getReturnType();
if (returnType instanceof VoidDataType) if (returnType instanceof VoidDataType) {
commitReturn = false; commitReturn = false;
}
} }
if (commitReturn) { if (commitReturn) {
HighFunctionDBUtil.commitReturnToDatabase(hfunc, SourceType.ANALYSIS); HighFunctionDBUtil.commitReturnToDatabase(hfunc, SourceType.ANALYSIS);
@ -274,7 +275,7 @@ public class DecompilerParameterIdCmd extends BackgroundCommand {
if (sym.getName().equals("in_FS_OFFSET")) { if (sym.getName().equals("in_FS_OFFSET")) {
continue; continue;
} }
if (!sym.getHighVariable().getStorage().isRegisterStorage()) { if (!sym.getStorage().isRegisterStorage()) {
continue; continue;
} }

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -57,7 +56,7 @@ public class ClangVariableDecl extends ClangTokenGroup {
public void restoreFromXML(XmlPullParser parser,PcodeFactory pfactory) { public void restoreFromXML(XmlPullParser parser,PcodeFactory pfactory) {
XmlElement node = parser.peek(); XmlElement node = parser.peek();
super.restoreFromXML(parser,pfactory); super.restoreFromXML(parser,pfactory);
int symref = SpecXmlUtils.decodeInt(node.getAttribute(ClangXML.SYMREF)); long symref = SpecXmlUtils.decodeLong(node.getAttribute(ClangXML.SYMREF));
HighSymbol sym = pfactory.getSymbol(symref); HighSymbol sym = pfactory.getSymbol(symref);
if (sym == null) { if (sym == null) {
Msg.error(this, "Invalid symbol reference: " + symref); Msg.error(this, "Invalid symbol reference: " + symref);
@ -65,9 +64,5 @@ public class ClangVariableDecl extends ClangTokenGroup {
} }
typevar = sym.getHighVariable(); typevar = sym.getHighVariable();
datatype = sym.getDataType(); datatype = sym.getDataType();
if (typevar == null) {
Msg.error(this, "High variable not found: " + sym.getName());
return;
}
} }
} }

View file

@ -671,8 +671,9 @@ public class DecompileCallback {
if (extRef != null) { if (extRef != null) {
func = listing.getFunctionAt(extRef.getToAddress()); func = listing.getFunctionAt(extRef.getToAddress());
if (func == null) { if (func == null) {
String res = HighFunction.buildFunctionShellXML(extRef.getLabel(), addr); HighSymbol shellSymbol =
return buildResult(addr, null, res, null); new HighFunctionShellSymbol(0, extRef.getLabel(), addr, dtmanage);
return buildResult(shellSymbol, null);
} }
} }
else { else {
@ -690,13 +691,12 @@ public class DecompileCallback {
int extrapop = getExtraPopOverride(func, addr); int extrapop = getExtraPopOverride(func, addr);
hfunc.grabFromFunction(extrapop, false, (extrapop != default_extrapop)); hfunc.grabFromFunction(extrapop, false, (extrapop != default_extrapop));
String res = hfunc.buildFunctionXML(addr, 2); HighSymbol funcSymbol = new HighFunctionSymbol(addr, 2, hfunc);
Namespace namespc = func.getParentNamespace(); Namespace namespc = func.getParentNamespace();
if (debug != null) { if (debug != null) {
debug.getFNTypes(hfunc); debug.getFNTypes(hfunc);
} }
res = buildResult(addr, null, res, namespc); return buildResult(funcSymbol, namespc);
return res;
} }
catch (Exception e) { catch (Exception e) {
Msg.error(this, Msg.error(this,
@ -790,7 +790,7 @@ public class DecompileCallback {
return name; return name;
} }
private String buildResult(Address addr, Address pc, String sym, Namespace namespc) { private String buildResult(HighSymbol highSymbol, Namespace namespc) {
StringBuilder res = new StringBuilder(); StringBuilder res = new StringBuilder();
res.append("<result>\n"); res.append("<result>\n");
res.append("<parent>\n"); res.append("<parent>\n");
@ -801,16 +801,15 @@ public class DecompileCallback {
HighFunction.createNamespaceTag(res, namespc); HighFunction.createNamespaceTag(res, namespc);
} }
res.append("</parent>\n"); res.append("</parent>\n");
String addrRes = Varnode.buildXMLAddress(addr);
if (debug != null) { if (debug != null) {
StringBuilder res2 = new StringBuilder(); StringBuilder res2 = new StringBuilder();
HighSymbol.buildMapSymXML(res2, addrRes, pc, sym); HighSymbol.buildMapSymXML(res2, highSymbol);
String res2string = res2.toString(); String res2string = res2.toString();
debug.getMapped(namespc, res2string); debug.getMapped(namespc, res2string);
res.append(res2string); res.append(res2string);
} }
else { else {
HighSymbol.buildMapSymXML(res, addrRes, pc, sym); HighSymbol.buildMapSymXML(res, highSymbol);
} }
res.append("</result>\n"); res.append("</result>\n");
@ -818,30 +817,25 @@ public class DecompileCallback {
} }
private String buildData(Data data) { // Convert global variable to XML private String buildData(Data data) { // Convert global variable to XML
Address addr = data.getMinAddress();
Symbol sym = data.getPrimarySymbol(); Symbol sym = data.getPrimarySymbol();
boolean readonly = data.isConstant(); HighCodeSymbol highSymbol;
boolean isVolatile = data.isVolatile(); if (sym != null) {
if (!readonly) { highSymbol = new HighCodeSymbol(sym.getID(), sym.getName(), data, dtmanage);
readonly = isReadOnlyNoData(addr);
} }
if (!isVolatile) { else {
isVolatile = isVolatileNoData(addr); highSymbol = new HighCodeSymbol(0,
SymbolUtilities.getDynamicName(program, data.getAddress()), data, dtmanage);
SymbolEntry entry = highSymbol.getFirstWholeMap();
if (data.getDataType() == DataType.DEFAULT && !entry.isReadOnly() &&
!entry.isVolatile()) {
return null;
}
} }
if ((data.getDataType() == DataType.DEFAULT) && (sym == null) && !isVolatile && !readonly) {
return null;
}
String name = sym != null ? sym.getName() : SymbolUtilities.getDynamicName(program, addr);
int sz = data.getLength();
String symstring = MappedSymbol.buildSymbolXML(dtmanage, name, data.getDataType(), sz, true,
true, readonly, isVolatile, -1, -1);
if (debug != null) { if (debug != null) {
debug.getType(data.getDataType()); debug.getType(highSymbol.getDataType());
} }
Namespace namespc = (sym != null) ? sym.getParentNamespace() : null; Namespace namespc = (sym != null) ? sym.getParentNamespace() : null;
return buildResult(addr, null, symstring, namespc); return buildResult(highSymbol, namespc);
} }
private StringBuilder buildRegister(Register reg) { private StringBuilder buildRegister(Register reg) {
@ -861,28 +855,9 @@ public class DecompileCallback {
* @return the XML description * @return the XML description
*/ */
private String buildLabel(Symbol sym, Address addr) { private String buildLabel(Symbol sym, Address addr) {
// TODO: Assume this is not data HighSymbol labelSymbol = new HighLabelSymbol(sym.getName(), addr, dtmanage);
boolean isVolatile = isVolatileNoData(addr);
if (!isVolatile) {
isVolatile = program.getLanguage().isVolatile(addr);
}
boolean readonly = isReadOnlyNoData(addr);
StringBuilder buf = new StringBuilder();
buf.append("<labelsym");
SpecXmlUtils.xmlEscapeAttribute(buf, "name", sym.getName());
SpecXmlUtils.encodeBooleanAttribute(buf, "namelock", true);
SpecXmlUtils.encodeBooleanAttribute(buf, "typelock", true);
if (readonly) {
SpecXmlUtils.encodeBooleanAttribute(buf, "readonly", true);
}
if (isVolatile) {
SpecXmlUtils.encodeBooleanAttribute(buf, "volatile", true);
}
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "cat", -1);
buf.append("/>\n");
Namespace namespc = sym.getParentNamespace(); Namespace namespc = sym.getParentNamespace();
return buildResult(sym.getAddress(), null, buf.toString(), namespc); return buildResult(labelSymbol, namespc);
} }
/** /**
@ -958,12 +933,12 @@ public class DecompileCallback {
hfunc.grabFromFunction(extrapop, includeDefaultNames, hfunc.grabFromFunction(extrapop, includeDefaultNames,
(extrapop != default_extrapop)); (extrapop != default_extrapop));
String funcsym = hfunc.buildFunctionXML(entry, (int) (diff + 1)); HighSymbol functionSymbol = new HighFunctionSymbol(entry, (int) (diff + 1), hfunc);
Namespace namespc = func.getParentNamespace(); Namespace namespc = func.getParentNamespace();
if (debug != null) { if (debug != null) {
debug.getFNTypes(hfunc); debug.getFNTypes(hfunc);
} }
return buildResult(entry, null, funcsym, namespc); return buildResult(functionSymbol, namespc);
} }
} }
@ -1097,15 +1072,6 @@ public class DecompileCallback {
} }
private String buildExternalRef(Address addr, ExternalReference ref) { private String buildExternalRef(Address addr, ExternalReference ref) {
StringBuilder resBuf = new StringBuilder();
resBuf.append("<externrefsymbol");
String nm = ref.getLabel();
if ((nm != null) && (nm.length() > 0)) { // Give the symbol a name if we can
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", nm + "_exref");
}
resBuf.append(">\n");
resBuf.append(Varnode.buildXMLAddress(addr));
// res += Varnode.buildXMLAddress(ref.getToAddress());
// The decompiler model was to assume that the ExternalReference // The decompiler model was to assume that the ExternalReference
// object could resolve the physical address where the dll // object could resolve the physical address where the dll
// function was getting loaded, just as a linker would do. // function was getting loaded, just as a linker would do.
@ -1119,8 +1085,8 @@ public class DecompileCallback {
// the address of the reference to hang the function on, and make // the address of the reference to hang the function on, and make
// no attempt to get a realistic linked address. This works because // no attempt to get a realistic linked address. This works because
// we never read bytes or look up code units at the address. // we never read bytes or look up code units at the address.
resBuf.append("</externrefsymbol>\n"); HighSymbol externSymbol = new HighExternalSymbol(ref.getLabel(), addr, addr, dtmanage);
return buildResult(addr, null, resBuf.toString(), null); return buildResult(externSymbol, null);
} }
private void buildTrackSet(StringBuilder buf, Register reg, long val) { private void buildTrackSet(StringBuilder buf, Register reg, long val) {

View file

@ -680,7 +680,7 @@ public class DecompilerUtils {
// prefer the selection over the current location // prefer the selection over the current location
ClangToken token = decompilerPanel.getSelectedToken(); ClangToken token = decompilerPanel.getSelectedToken();
if (token == null) { if (token == null) {
token = decompilerPanel.getTokenAtCursor(); token = context.getTokenAtCursor();
} }
Varnode varnode = DecompilerUtils.getVarnodeRef(token); Varnode varnode = DecompilerUtils.getVarnodeRef(token);

View file

@ -21,9 +21,13 @@ import docking.ActionContext;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import ghidra.app.context.NavigatableActionContext; import ghidra.app.context.NavigatableActionContext;
import ghidra.app.context.RestrictedAddressSetContext; import ghidra.app.context.RestrictedAddressSetContext;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangTokenGroup;
import ghidra.app.decompiler.component.DecompilerPanel; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.HighFunction;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.UndefinedFunction; import ghidra.util.UndefinedFunction;
import utility.function.Callback; import utility.function.Callback;
@ -33,6 +37,9 @@ public class DecompilerActionContext extends NavigatableActionContext
private final Address functionEntryPoint; private final Address functionEntryPoint;
private final boolean isDecompiling; private final boolean isDecompiling;
private ClangToken tokenAtCursor = null;
private boolean tokenIsInitialized = false;
public DecompilerActionContext(DecompilerProvider provider, Address functionEntryPoint, public DecompilerActionContext(DecompilerProvider provider, Address functionEntryPoint,
boolean isDecompiling) { boolean isDecompiling) {
super(provider, provider); super(provider, provider);
@ -53,6 +60,18 @@ public class DecompilerActionContext extends NavigatableActionContext
return (DecompilerProvider) super.getComponentProvider(); return (DecompilerProvider) super.getComponentProvider();
} }
public PluginTool getTool() {
return getComponentProvider().getTool();
}
public ClangToken getTokenAtCursor() {
if (!tokenIsInitialized) {
tokenAtCursor = getDecompilerPanel().getTokenAtCursor();
tokenIsInitialized = true;
}
return tokenAtCursor;
}
public DecompilerPanel getDecompilerPanel() { public DecompilerPanel getDecompilerPanel() {
return getComponentProvider().getDecompilerPanel(); return getComponentProvider().getDecompilerPanel();
} }
@ -61,11 +80,23 @@ public class DecompilerActionContext extends NavigatableActionContext
return getComponentProvider().getController().getFunction(); return getComponentProvider().getController().getFunction();
} }
public HighFunction getHighFunction() {
return getComponentProvider().getController().getHighFunction();
}
public ClangTokenGroup getCCodeModel() {
return getComponentProvider().getController().getCCodeModel();
}
public boolean hasRealFunction() { public boolean hasRealFunction() {
Function f = getFunction(); Function f = getFunction();
return f != null && !(f instanceof UndefinedFunction); return f != null && !(f instanceof UndefinedFunction);
} }
public void setStatusMessage(String msg) {
getComponentProvider().getController().setStatusMessage(msg);
}
/** /**
* The companion method of {@link #checkActionEnablement(Supplier)}. Decompiler actions * The companion method of {@link #checkActionEnablement(Supplier)}. Decompiler actions
* must call this method from their {@link DockingActionIf#actionPerformed(ActionContext)} * must call this method from their {@link DockingActionIf#actionPerformed(ActionContext)}

View file

@ -68,8 +68,14 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private DockingAction backwardSliceToOpsAction; private DockingAction backwardSliceToOpsAction;
private DockingAction lockProtoAction; private DockingAction lockProtoAction;
private DockingAction lockLocalAction; private DockingAction lockLocalAction;
private DockingAction renameVarAction; private DockingAction renameLocalAction;
private DockingAction retypeVarAction; private DockingAction renameGlobalAction;
private DockingAction renameFieldAction;
private DockingAction retypeLocalAction;
private DockingAction retypeGlobalAction;
private DockingAction retypeReturnAction;
private DockingAction retypeFieldAction;
private DockingAction isolateVarAction;
private DockingAction specifyCProtoAction; private DockingAction specifyCProtoAction;
private DockingAction overrideSigAction; private DockingAction overrideSigAction;
private DockingAction deleteSigAction; private DockingAction deleteSigAction;
@ -754,16 +760,16 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String functionGroup = "1 - Function Group"; String functionGroup = "1 - Function Group";
int subGroupPosition = 0; int subGroupPosition = 0;
specifyCProtoAction = new SpecifyCPrototypeAction(tool, controller); specifyCProtoAction = new SpecifyCPrototypeAction();
setGroupInfo(specifyCProtoAction, functionGroup, subGroupPosition++); setGroupInfo(specifyCProtoAction, functionGroup, subGroupPosition++);
overrideSigAction = new OverridePrototypeAction(tool, controller); overrideSigAction = new OverridePrototypeAction();
setGroupInfo(overrideSigAction, functionGroup, subGroupPosition++); setGroupInfo(overrideSigAction, functionGroup, subGroupPosition++);
deleteSigAction = new DeletePrototypeOverrideAction(tool, controller); deleteSigAction = new DeletePrototypeOverrideAction();
setGroupInfo(deleteSigAction, functionGroup, subGroupPosition++); setGroupInfo(deleteSigAction, functionGroup, subGroupPosition++);
renameFunctionAction = new RenameFunctionAction(tool, controller); renameFunctionAction = new RenameFunctionAction();
setGroupInfo(renameFunctionAction, functionGroup, subGroupPosition++); setGroupInfo(renameFunctionAction, functionGroup, subGroupPosition++);
// //
@ -772,17 +778,35 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String variableGroup = "2 - Variable Group"; String variableGroup = "2 - Variable Group";
subGroupPosition = 0; // reset for the next group subGroupPosition = 0; // reset for the next group
renameVarAction = new RenameVariableAction(tool, controller); renameLocalAction = new RenameLocalAction();
setGroupInfo(renameVarAction, variableGroup, subGroupPosition++); setGroupInfo(renameLocalAction, variableGroup, subGroupPosition++);
retypeVarAction = new RetypeVariableAction(tool, controller); renameGlobalAction = new RenameGlobalAction();
setGroupInfo(retypeVarAction, variableGroup, subGroupPosition++); setGroupInfo(renameGlobalAction, variableGroup, subGroupPosition++);
renameFieldAction = new RenameFieldAction();
setGroupInfo(renameFieldAction, variableGroup, subGroupPosition++);
retypeLocalAction = new RetypeLocalAction();
setGroupInfo(retypeLocalAction, variableGroup, subGroupPosition++);
retypeGlobalAction = new RetypeGlobalAction();
setGroupInfo(retypeGlobalAction, variableGroup, subGroupPosition++);
retypeReturnAction = new RetypeReturnAction();
setGroupInfo(retypeReturnAction, variableGroup, subGroupPosition++);
retypeFieldAction = new RetypeFieldAction();
setGroupInfo(retypeFieldAction, variableGroup, subGroupPosition++);
isolateVarAction = new IsolateVariableAction();
setGroupInfo(isolateVarAction, variableGroup, subGroupPosition++);
decompilerCreateStructureAction = decompilerCreateStructureAction =
new DecompilerStructureVariableAction(owner, tool, controller); new DecompilerStructureVariableAction(owner, tool, controller);
setGroupInfo(decompilerCreateStructureAction, variableGroup, subGroupPosition++); setGroupInfo(decompilerCreateStructureAction, variableGroup, subGroupPosition++);
editDataTypeAction = new EditDataTypeAction(tool, controller); editDataTypeAction = new EditDataTypeAction();
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++); setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
// //
@ -796,10 +820,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String commitGroup = "3 - Commit Group"; String commitGroup = "3 - Commit Group";
subGroupPosition = 0; // reset for the next group subGroupPosition = 0; // reset for the next group
lockProtoAction = new CommitParamsAction(tool, controller); lockProtoAction = new CommitParamsAction();
setGroupInfo(lockProtoAction, commitGroup, subGroupPosition++); setGroupInfo(lockProtoAction, commitGroup, subGroupPosition++);
lockLocalAction = new CommitLocalsAction(tool, controller); lockLocalAction = new CommitLocalsAction();
setGroupInfo(lockLocalAction, commitGroup, subGroupPosition++); setGroupInfo(lockLocalAction, commitGroup, subGroupPosition++);
subGroupPosition = 0; // reset for the next group subGroupPosition = 0; // reset for the next group
@ -809,19 +833,19 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
// //
String highlightGroup = "4a - Highlight Group"; String highlightGroup = "4a - Highlight Group";
tool.setMenuGroup(new String[] { "Highlight" }, highlightGroup); tool.setMenuGroup(new String[] { "Highlight" }, highlightGroup);
defUseHighlightAction = new HighlightDefinedUseAction(controller); defUseHighlightAction = new HighlightDefinedUseAction();
setGroupInfo(defUseHighlightAction, highlightGroup, subGroupPosition++); setGroupInfo(defUseHighlightAction, highlightGroup, subGroupPosition++);
forwardSliceAction = new ForwardSliceAction(controller); forwardSliceAction = new ForwardSliceAction();
setGroupInfo(forwardSliceAction, highlightGroup, subGroupPosition++); setGroupInfo(forwardSliceAction, highlightGroup, subGroupPosition++);
backwardSliceAction = new BackwardsSliceAction(); backwardSliceAction = new BackwardsSliceAction();
setGroupInfo(backwardSliceAction, highlightGroup, subGroupPosition++); setGroupInfo(backwardSliceAction, highlightGroup, subGroupPosition++);
forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction(controller); forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction();
setGroupInfo(forwardSliceToOpsAction, highlightGroup, subGroupPosition++); setGroupInfo(forwardSliceToOpsAction, highlightGroup, subGroupPosition++);
backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction(controller); backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction();
setGroupInfo(backwardSliceToOpsAction, highlightGroup, subGroupPosition++); setGroupInfo(backwardSliceToOpsAction, highlightGroup, subGroupPosition++);
tool.setMenuGroup(new String[] { "Secondary Highlight" }, highlightGroup); tool.setMenuGroup(new String[] { "Secondary Highlight" }, highlightGroup);
@ -850,7 +874,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String searchGroup = "comment2 - Search Group"; String searchGroup = "comment2 - Search Group";
subGroupPosition = 0; // reset for the next group subGroupPosition = 0; // reset for the next group
findAction = new FindAction(tool, controller); findAction = new FindAction();
setGroupInfo(findAction, searchGroup, subGroupPosition++); setGroupInfo(findAction, searchGroup, subGroupPosition++);
// //
@ -865,7 +889,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
findReferencesAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup); findReferencesAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
FindReferencesToSymbolAction findReferencesToSymbolAction = FindReferencesToSymbolAction findReferencesToSymbolAction =
new FindReferencesToSymbolAction(tool); new FindReferencesToSymbolAction();
setGroupInfo(findReferencesToSymbolAction, searchGroup, subGroupPosition++); setGroupInfo(findReferencesToSymbolAction, searchGroup, subGroupPosition++);
findReferencesToSymbolAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup); findReferencesToSymbolAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
addLocalAction(findReferencesToSymbolAction); addLocalAction(findReferencesToSymbolAction);
@ -889,8 +913,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
// These actions are not in the popup menu // These actions are not in the popup menu
// //
debugFunctionAction = new DebugDecompilerAction(controller); debugFunctionAction = new DebugDecompilerAction(controller);
convertAction = new ExportToCAction(controller); convertAction = new ExportToCAction();
cloneDecompilerAction = new CloneDecompilerAction(this, controller); cloneDecompilerAction = new CloneDecompilerAction();
addLocalAction(refreshAction); addLocalAction(refreshAction);
addLocalAction(selectAllAction); addLocalAction(selectAllAction);
@ -901,12 +925,18 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
addLocalAction(backwardSliceToOpsAction); addLocalAction(backwardSliceToOpsAction);
addLocalAction(lockProtoAction); addLocalAction(lockProtoAction);
addLocalAction(lockLocalAction); addLocalAction(lockLocalAction);
addLocalAction(renameVarAction); addLocalAction(renameLocalAction);
addLocalAction(renameGlobalAction);
addLocalAction(renameFieldAction);
addLocalAction(setSecondaryHighlightAction); addLocalAction(setSecondaryHighlightAction);
addLocalAction(setSecondaryHighlightColorChooserAction); addLocalAction(setSecondaryHighlightColorChooserAction);
addLocalAction(removeSecondaryHighlightAction); addLocalAction(removeSecondaryHighlightAction);
addLocalAction(removeAllSecondadryHighlightsAction); addLocalAction(removeAllSecondadryHighlightsAction);
addLocalAction(retypeVarAction); addLocalAction(retypeLocalAction);
addLocalAction(retypeGlobalAction);
addLocalAction(retypeReturnAction);
addLocalAction(retypeFieldAction);
addLocalAction(isolateVarAction);
addLocalAction(decompilerCreateStructureAction); addLocalAction(decompilerCreateStructureAction);
tool.addAction(listingCreateStructureAction); tool.addAction(listingCreateStructureAction);
addLocalAction(editDataTypeAction); addLocalAction(editDataTypeAction);
@ -943,7 +973,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private void graphServiceAdded() { private void graphServiceAdded() {
if (graphASTControlFlowAction == null && tool.getService(GraphService.class) != null) { if (graphASTControlFlowAction == null && tool.getService(GraphService.class) != null) {
graphASTControlFlowAction = new GraphASTControlFlowAction(plugin, controller); graphASTControlFlowAction = new GraphASTControlFlowAction();
addLocalAction(graphASTControlFlowAction); addLocalAction(graphASTControlFlowAction);
} }
} }

View file

@ -17,8 +17,16 @@ package ghidra.app.plugin.core.decompile.actions;
import docking.ActionContext; import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.DecompilePlugin; import ghidra.app.plugin.core.decompile.DecompilePlugin;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.datatype.DataTypeSelectionDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
/** /**
* A base class for {@link DecompilePlugin} actions that handles checking whether the * A base class for {@link DecompilePlugin} actions that handles checking whether the
@ -59,6 +67,136 @@ public abstract class AbstractDecompilerAction extends DockingAction {
}); });
} }
/**
* Find the HighSymbol the decompiler associates with a specific address.
* @param addr is the specific address
* @param highFunction is the decompiler results in which to search for the symbol
* @return the matching symbol or null if no symbol exists
*/
private static HighSymbol findHighSymbol(Address addr, HighFunction highFunction) {
HighSymbol highSymbol = null;
if (addr.isStackAddress()) {
LocalSymbolMap lsym = highFunction.getLocalSymbolMap();
highSymbol = lsym.findLocal(addr, null);
}
else {
GlobalSymbolMap gsym = highFunction.getGlobalSymbolMap();
highSymbol = gsym.getSymbol(addr);
}
return highSymbol;
}
/**
* Track down the HighSymbol associated with a particular token. The token may be directly attached to
* the symbol, or it may be a reference that needs to be looked up.
* @param token is the given token
* @param highFunction is the decompiler model of the function
* @return the associated HighSymbol or null if one can't be found
*/
public static HighSymbol findHighSymbolFromToken(ClangToken token, HighFunction highFunction) {
HighVariable variable = token.getHighVariable();
HighSymbol highSymbol = null;
if (variable == null) {
// Token may be from a variable reference, in which case we have to dig to find the actual symbol
Address storageAddress =
getStorageAddress(token, highFunction.getFunction().getProgram());
if (storageAddress == null) {
return null;
}
highSymbol = findHighSymbol(storageAddress, highFunction);
}
else {
highSymbol = variable.getSymbol();
}
return highSymbol;
}
/**
* Get the storage address of the variable attached to the given token, if any.
* The variable may be directly referenced by the token, or indirectly referenced as a point.
* @param tokenAtCursor is the given token
* @param program is the Program
* @return the storage Address or null if there is no variable attached
*/
private static Address getStorageAddress(ClangToken tokenAtCursor, Program program) {
Varnode vnode = tokenAtCursor.getVarnode();
Address storageAddress = null;
if (vnode != null) {
storageAddress = vnode.getAddress();
}
// op could be a PTRSUB, need to dig it out...
else if (tokenAtCursor instanceof ClangVariableToken) {
PcodeOp op = ((ClangVariableToken) tokenAtCursor).getPcodeOp();
storageAddress = HighFunctionDBUtil.getSpacebaseReferenceAddress(program, op);
}
return storageAddress;
}
/**
* Get the structure associated with a field token
* @param tok is the token representing a field
* @return the structure which contains this field
*/
public static Structure getStructDataType(ClangToken tok) {
// We already know tok is a ClangFieldToken
ClangFieldToken fieldtok = (ClangFieldToken) tok;
DataType dt = fieldtok.getDataType();
if (dt == null) {
return null;
}
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof Structure) {
return (Structure) dt;
}
return null;
}
/**
* Compare the given HighFunction's idea of the prototype with the Function's idea.
* Return true if there is a difference. If a specific symbol is being changed,
* it can be passed in to check whether or not the prototype is being affected.
* @param highSymbol (if not null) is the symbol being modified
* @param hfunction is the given HighFunction
* @return true if there is a difference (and a full commit is required)
*/
protected static boolean checkFullCommit(HighSymbol highSymbol, HighFunction hfunction) {
if (highSymbol != null && !highSymbol.isParameter()) {
return false;
}
Function function = hfunction.getFunction();
Parameter[] parameters = function.getParameters();
LocalSymbolMap localSymbolMap = hfunction.getLocalSymbolMap();
int numParams = localSymbolMap.getNumParams();
if (numParams != parameters.length) {
return true;
}
for (int i = 0; i < numParams; i++) {
HighSymbol param = localSymbolMap.getParamSymbol(i);
if (param.getCategoryIndex() != i) {
return true;
}
VariableStorage storage = param.getStorage();
// Don't compare using the equals method so that DynamicVariableStorage can match
if (0 != storage.compareTo(parameters[i].getVariableStorage())) {
return true;
}
}
return false;
}
public DataType chooseDataType(PluginTool tool, Program program, DataType currentDataType) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
DataTypeSelectionDialog chooserDialog = new DataTypeSelectionDialog(tool, dataTypeManager,
Integer.MAX_VALUE, AllowedDataTypes.FIXED_LENGTH);
chooserDialog.setInitialDataType(currentDataType);
tool.showDialog(chooserDialog);
return chooserDialog.getUserChosenDataType();
}
/** /**
* Subclasses return true if they are enabled for the given context * Subclasses return true if they are enabled for the given context
* *

View file

@ -16,7 +16,6 @@
package ghidra.app.plugin.core.decompile.actions; package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.TokenHighlights; import ghidra.app.decompiler.component.TokenHighlights;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
@ -32,13 +31,13 @@ public abstract class AbstractSetSecondaryHighlightAction extends AbstractDecomp
return false; return false;
} }
DecompilerPanel panel = context.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = panel.getTokenAtCursor();
if (tokenAtCursor == null) { if (tokenAtCursor == null) {
return false; return false;
} }
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens(); TokenHighlights highlightedTokens =
context.getDecompilerPanel().getSecondaryHighlightedTokens();
if (highlightedTokens.contains(tokenAtCursor)) { if (highlightedTokens.contains(tokenAtCursor)) {
return false; // already highlighted return false; // already highlighted
} }

View file

@ -36,8 +36,7 @@ public class BackwardsSliceAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = context.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
return varnode != null; return varnode != null;
} }
@ -45,13 +44,13 @@ public class BackwardsSliceAction extends AbstractDecompilerAction {
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = context.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode == null) { if (varnode == null) {
return; return;
} }
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
decompilerPanel.clearPrimaryHighlights(); decompilerPanel.clearPrimaryHighlights();
PcodeOp op = tokenAtCursor.getPcodeOp(); PcodeOp op = tokenAtCursor.getPcodeOp();

View file

@ -19,38 +19,36 @@ import java.util.Set;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction { public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public BackwardsSliceToPCodeOpsAction(DecompilerController controller) { public BackwardsSliceToPCodeOpsAction() {
super("Highlight Backward Inst Slice"); super("Highlight Backward Inst Slice");
this.controller = controller;
setPopupMenuData( setPopupMenuData(
new MenuData(new String[] { "Highlight", "Backward Inst Slice" }, "Decompile")); new MenuData(new String[] { "Highlight", "Backward Inst Slice" }, "Decompile"));
} }
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
return varnode != null; return varnode != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode != null) { if (varnode != null) {
PcodeOp op = tokenAtCursor.getPcodeOp(); PcodeOp op = tokenAtCursor.getPcodeOp();
Set<PcodeOp> backwardSlice = DecompilerUtils.getBackwardSliceToPCodeOps(varnode); Set<PcodeOp> backwardSlice = DecompilerUtils.getBackwardSliceToPCodeOps(varnode);
backwardSlice.add(op); backwardSlice.add(op);
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
decompilerPanel.clearPrimaryHighlights(); decompilerPanel.clearPrimaryHighlights();
decompilerPanel.addPcodeOpHighlights(backwardSlice, decompilerPanel.addPcodeOpHighlights(backwardSlice,
decompilerPanel.getCurrentVariableHighlightColor()); decompilerPanel.getCurrentVariableHighlightColor());

View file

@ -22,21 +22,14 @@ import javax.swing.ImageIcon;
import docking.action.KeyBindingData; import docking.action.KeyBindingData;
import docking.action.ToolBarData; import docking.action.ToolBarData;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.plugin.core.decompile.DecompilerProvider;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import resources.ResourceManager; import resources.ResourceManager;
public class CloneDecompilerAction extends AbstractDecompilerAction { public class CloneDecompilerAction extends AbstractDecompilerAction {
private final DecompilerProvider provider; public CloneDecompilerAction() {
private DecompilerController controller;
public CloneDecompilerAction(DecompilerProvider provider, DecompilerController controller) {
super("Decompile Clone"); super("Decompile Clone");
this.provider = provider;
this.controller = controller;
ImageIcon image = ResourceManager.loadImage("images/camera-photo.png"); ImageIcon image = ResourceManager.loadImage("images/camera-photo.png");
setToolBarData(new ToolBarData(image, "ZZZ")); setToolBarData(new ToolBarData(image, "ZZZ"));
setDescription("Create a snapshot (disconnected) copy of this Decompiler window "); setDescription("Create a snapshot (disconnected) copy of this Decompiler window ");
@ -47,11 +40,11 @@ public class CloneDecompilerAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return controller.getFunction() != null; return context.getFunction() != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
provider.cloneWindow(); context.getComponentProvider().cloneWindow();
} }
} }

View file

@ -16,55 +16,35 @@
package ghidra.app.plugin.core.decompile.actions; package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangFunction;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighFunctionDBUtil; import ghidra.program.model.pcode.HighFunctionDBUtil;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
public class CommitLocalsAction extends AbstractDecompilerAction { public class CommitLocalsAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public CommitLocalsAction(PluginTool tool, DecompilerController controller) { public CommitLocalsAction() {
super("Commit Locals"); super("Commit Locals");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Commit Locals" }, "Commit")); setPopupMenuData(new MenuData(new String[] { "Commit Locals" }, "Commit"));
setDescription( setDescription(
"Save Local variable definitions to Program, locking them into their current type definitions"); "Save Local variable definitions to Program, locking them into their current type definitions");
} }
private HighFunction getHighFunction() {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
if (tokenAtCursor == null) {
return null;
}
ClangFunction clfunc = tokenAtCursor.getClangFunction();
if (clfunc == null) {
return null;
}
return clfunc.getHighFunction();
}
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
if (!context.hasRealFunction()) { if (!context.hasRealFunction()) {
return false; return false;
} }
return getHighFunction() != null; return context.getHighFunction() != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = controller.getProgram(); Program program = context.getProgram();
int transaction = program.startTransaction("Commit Params/Return"); int transaction = program.startTransaction("Commit Params/Return");
try { try {
HighFunction hfunc = getHighFunction(); HighFunction hfunc = context.getHighFunction();
HighFunctionDBUtil.commitLocalsToDatabase(hfunc, SourceType.USER_DEFINED); HighFunctionDBUtil.commitLocalsToDatabase(hfunc, SourceType.USER_DEFINED);
} }
finally { finally {

View file

@ -19,12 +19,7 @@ import java.awt.event.KeyEvent;
import docking.action.KeyBindingData; import docking.action.KeyBindingData;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangFunction;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighFunctionDBUtil; import ghidra.program.model.pcode.HighFunctionDBUtil;
@ -33,48 +28,29 @@ import ghidra.util.Msg;
import ghidra.util.exception.*; import ghidra.util.exception.*;
public class CommitParamsAction extends AbstractDecompilerAction { public class CommitParamsAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public CommitParamsAction(PluginTool tool, DecompilerController controller) { public CommitParamsAction() {
super("Commit Params/Return"); super("Commit Params/Return");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Commit Params/Return" }, "Commit")); setPopupMenuData(new MenuData(new String[] { "Commit Params/Return" }, "Commit"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_P, 0)); setKeyBindingData(new KeyBindingData(KeyEvent.VK_P, 0));
setDescription( setDescription(
"Save Parameters/Return definitions to Program, locking them into their current type definitions"); "Save Parameters/Return definitions to Program, locking them into their current type definitions");
} }
private HighFunction getHighFunction() {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
// getTokenAtCursor can explicitly return null, so we must check here
// before dereferencing it.
if (tokenAtCursor == null) {
return null;
}
ClangFunction clfunc = tokenAtCursor.getClangFunction();
if (clfunc == null) {
return null;
}
return clfunc.getHighFunction();
}
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
if (!context.hasRealFunction()) { if (!context.hasRealFunction()) {
return false; return false;
} }
return getHighFunction() != null; return context.getHighFunction() != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = controller.getProgram(); Program program = context.getProgram();
int transaction = program.startTransaction("Commit Params/Return"); int transaction = program.startTransaction("Commit Params/Return");
try { try {
HighFunction hfunc = getHighFunction(); HighFunction hfunc = context.getHighFunction();
SourceType source = SourceType.ANALYSIS; SourceType source = SourceType.ANALYSIS;
if (hfunc.getFunction().getSignatureSource() == SourceType.USER_DEFINED) { if (hfunc.getFunction().getSignatureSource() == SourceType.USER_DEFINED) {
source = SourceType.USER_DEFINED; source = SourceType.USER_DEFINED;

View file

@ -27,7 +27,8 @@ import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.util.filechooser.ExtensionFileFilter; import ghidra.util.filechooser.ExtensionFileFilter;
public class DebugDecompilerAction extends AbstractDecompilerAction { public class DebugDecompilerAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private DecompilerController controller;
public DebugDecompilerAction(DecompilerController controller) { public DebugDecompilerAction(DecompilerController controller) {
super("Debug Function Decompilation"); super("Debug Function Decompilation");
@ -37,12 +38,12 @@ public class DebugDecompilerAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return controller.getFunction() != null; return context.getFunction() != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
JComponent parentComponent = controller.getDecompilerPanel(); JComponent parentComponent = context.getDecompilerPanel();
GhidraFileChooser fileChooser = new GhidraFileChooser(parentComponent); GhidraFileChooser fileChooser = new GhidraFileChooser(parentComponent);
fileChooser.setTitle("Please Choose Output File"); fileChooser.setTitle("Please Choose Output File");
fileChooser.setFileFilter(new ExtensionFileFilter(new String[] { "xml" }, "XML Files")); fileChooser.setFileFilter(new ExtensionFileFilter(new String[] { "xml" }, "XML Files"));

View file

@ -17,10 +17,7 @@ package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.symbol.CodeSymbol; import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
@ -31,17 +28,13 @@ import ghidra.util.Msg;
import ghidra.util.UndefinedFunction; import ghidra.util.UndefinedFunction;
public class DeletePrototypeOverrideAction extends AbstractDecompilerAction { public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public DeletePrototypeOverrideAction(PluginTool tool, DecompilerController controller) { public DeletePrototypeOverrideAction() {
super("Remove Signature Override"); super("Remove Signature Override");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Remove Signature Override" }, "Decompile")); setPopupMenuData(new MenuData(new String[] { "Remove Signature Override" }, "Decompile"));
} }
public static CodeSymbol getSymbol(DecompilerController controller) { public static CodeSymbol getSymbol(Function func, ClangToken tokenAtCursor) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
if (tokenAtCursor == null) { if (tokenAtCursor == null) {
return null; return null;
} }
@ -49,7 +42,6 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
if (addr == null) { if (addr == null) {
return null; return null;
} }
Function func = controller.getFunction();
Namespace overspace = HighFunction.findOverrideSpace(func); Namespace overspace = HighFunction.findOverrideSpace(func);
if (overspace == null) { if (overspace == null) {
return null; return null;
@ -76,25 +68,25 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction(); Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) { if (function == null || function instanceof UndefinedFunction) {
return false; return false;
} }
return getSymbol(controller) != null; return getSymbol(function, context.getTokenAtCursor()) != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
CodeSymbol sym = getSymbol(controller); Function func = context.getFunction();
Function func = controller.getFunction(); CodeSymbol sym = getSymbol(func, context.getTokenAtCursor());
Program program = func.getProgram(); Program program = func.getProgram();
SymbolTable symtab = program.getSymbolTable(); SymbolTable symtab = program.getSymbolTable();
int transaction = program.startTransaction("Remove Override Signature"); int transaction = program.startTransaction("Remove Override Signature");
boolean commit = true; boolean commit = true;
if (!symtab.removeSymbolSpecial(sym)) { if (!symtab.removeSymbolSpecial(sym)) {
commit = false; commit = false;
Msg.showError(getClass(), controller.getDecompilerPanel(), Msg.showError(getClass(), context.getDecompilerPanel(),
"Removing Override Signature Failed", "Error removing override signature"); "Removing Override Signature Failed", "Error removing override signature");
} }
program.endTransaction(transaction, commit); program.endTransaction(transaction, commit);

View file

@ -17,7 +17,6 @@ package ghidra.app.plugin.core.decompile.actions;
import docking.ActionContext; import docking.ActionContext;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerUtils; import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
@ -29,13 +28,9 @@ import ghidra.program.model.listing.Function;
import ghidra.util.UndefinedFunction; import ghidra.util.UndefinedFunction;
public class EditDataTypeAction extends AbstractDecompilerAction { public class EditDataTypeAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private final PluginTool tool;
public EditDataTypeAction(PluginTool tool, DecompilerController controller) { public EditDataTypeAction() {
super("Edit Data Type"); super("Edit Data Type");
this.tool = tool;
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Edit Data Type" }, "Decompile")); setPopupMenuData(new MenuData(new String[] { "Edit Data Type" }, "Decompile"));
} }
@ -44,7 +39,7 @@ public class EditDataTypeAction extends AbstractDecompilerAction {
return (context instanceof DecompilerActionContext); return (context instanceof DecompilerActionContext);
} }
private boolean hasCustomEditorForBaseDataType(DataType dataType) { private boolean hasCustomEditorForBaseDataType(PluginTool tool, DataType dataType) {
DataType baseDataType = DataTypeUtils.getBaseDataType(dataType); DataType baseDataType = DataTypeUtils.getBaseDataType(dataType);
final DataTypeManagerService service = tool.getService(DataTypeManagerService.class); final DataTypeManagerService service = tool.getService(DataTypeManagerService.class);
return baseDataType != null && service.isEditable(baseDataType); return baseDataType != null && service.isEditable(baseDataType);
@ -53,7 +48,7 @@ public class EditDataTypeAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction(); Function function = context.getFunction();
if (function instanceof UndefinedFunction) { if (function instanceof UndefinedFunction) {
return false; return false;
} }
@ -63,7 +58,7 @@ public class EditDataTypeAction extends AbstractDecompilerAction {
return false; return false;
} }
return hasCustomEditorForBaseDataType(dataType); return hasCustomEditorForBaseDataType(context.getTool(), dataType);
} }
@Override @Override
@ -76,7 +71,8 @@ public class EditDataTypeAction extends AbstractDecompilerAction {
if (baseDtDTM != dataTypeManager) { if (baseDtDTM != dataTypeManager) {
baseDataType = baseDataType.clone(dataTypeManager); baseDataType = baseDataType.clone(dataTypeManager);
} }
final DataTypeManagerService service = tool.getService(DataTypeManagerService.class); final DataTypeManagerService service =
context.getTool().getService(DataTypeManagerService.class);
service.edit(baseDataType); service.edit(baseDataType);
} }

View file

@ -23,7 +23,7 @@ import docking.action.ToolBarData;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.filechooser.GhidraFileChooser;
import ghidra.app.decompiler.*; import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerController; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.preferences.Preferences; import ghidra.framework.preferences.Preferences;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -33,11 +33,9 @@ import resources.ResourceManager;
public class ExportToCAction extends AbstractDecompilerAction { public class ExportToCAction extends AbstractDecompilerAction {
private static final ImageIcon EXPORT_ICON = ResourceManager.loadImage("images/page_edit.png"); private static final ImageIcon EXPORT_ICON = ResourceManager.loadImage("images/page_edit.png");
private static final String LAST_USED_C_FILE = "last.used.decompiler.c.export.file"; private static final String LAST_USED_C_FILE = "last.used.decompiler.c.export.file";
private final DecompilerController controller;
public ExportToCAction(DecompilerController controller) { public ExportToCAction() {
super("Export to C"); super("Export to C");
this.controller = controller;
setToolBarData(new ToolBarData(EXPORT_ICON, "Local")); setToolBarData(new ToolBarData(EXPORT_ICON, "Local"));
setDescription("Export the current function to C"); setDescription("Export the current function to C");
} }
@ -55,11 +53,11 @@ public class ExportToCAction extends AbstractDecompilerAction {
Preferences.store(); Preferences.store();
} }
private File getFile() { private File getFile(DecompilerPanel decompilerPanel) {
File lastUsedFile = readLastUsedFile(); File lastUsedFile = readLastUsedFile();
String[] extensions = new String[] { "h", "c", "cpp" }; String[] extensions = new String[] { "h", "c", "cpp" };
GhidraFileChooser fileChooser = new GhidraFileChooser(controller.getDecompilerPanel()); GhidraFileChooser fileChooser = new GhidraFileChooser(decompilerPanel);
fileChooser.setFileFilter(new ExtensionFileFilter(extensions, "C/C++ Files")); fileChooser.setFileFilter(new ExtensionFileFilter(extensions, "C/C++ Files"));
if (lastUsedFile != null) { if (lastUsedFile != null) {
fileChooser.setSelectedFile(lastUsedFile); fileChooser.setSelectedFile(lastUsedFile);
@ -87,19 +85,19 @@ public class ExportToCAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return controller.getFunction() != null && controller.getCCodeModel() != null; return context.getFunction() != null && context.getCCodeModel() != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
File file = getFile(); File file = getFile(context.getDecompilerPanel());
if (file == null) { if (file == null) {
return; return;
} }
if (file.exists()) { if (file.exists()) {
if (OptionDialog.showYesNoDialog(controller.getDecompilerPanel(), if (OptionDialog.showYesNoDialog(context.getDecompilerPanel(),
"Overwrite Existing File?", "Overwrite Existing File?",
"Do you want to overwrite the existing file?") == OptionDialog.OPTION_TWO) { "Do you want to overwrite the existing file?") == OptionDialog.OPTION_TWO) {
return; return;
@ -108,16 +106,16 @@ public class ExportToCAction extends AbstractDecompilerAction {
try { try {
PrintWriter writer = new PrintWriter(new FileOutputStream(file)); PrintWriter writer = new PrintWriter(new FileOutputStream(file));
ClangTokenGroup grp = controller.getCCodeModel(); ClangTokenGroup grp = context.getCCodeModel();
PrettyPrinter printer = new PrettyPrinter(controller.getFunction(), grp); PrettyPrinter printer = new PrettyPrinter(context.getFunction(), grp);
DecompiledFunction decompFunc = printer.print(true); DecompiledFunction decompFunc = printer.print(true);
writer.write(decompFunc.getC()); writer.write(decompFunc.getC());
writer.close(); writer.close();
controller.setStatusMessage( context.setStatusMessage(
"Successfully exported function(s) to " + file.getAbsolutePath()); "Successfully exported function(s) to " + file.getAbsolutePath());
} }
catch (IOException e) { catch (IOException e) {
Msg.showError(getClass(), controller.getDecompilerPanel(), "Export to C Failed", Msg.showError(getClass(), context.getDecompilerPanel(), "Export to C Failed",
"Error exporting to C: " + e); "Error exporting to C: " + e);
} }
} }

View file

@ -130,7 +130,7 @@ public class FillOutStructureCmd extends BackgroundCommand {
} }
} }
if (var == null) { if (var == null || var.getSymbol() == null || var.getOffset() >= 0) {
return false; return false;
} }
@ -232,7 +232,8 @@ public class FillOutStructureCmd extends BackgroundCommand {
private void commitVariable(HighVariable var, DataType newDt, boolean isThisParam) { private void commitVariable(HighVariable var, DataType newDt, boolean isThisParam) {
if (!isThisParam) { if (!isThisParam) {
try { try {
HighFunctionDBUtil.updateDBVariable(var, null, newDt, SourceType.USER_DEFINED); HighFunctionDBUtil.updateDBVariable(var.getSymbol(), null, newDt,
SourceType.USER_DEFINED);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e); throw new AssertException("Unexpected exception", e);

View file

@ -24,28 +24,25 @@ import docking.action.MenuData;
import docking.widgets.*; import docking.widgets.*;
import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.component.*; import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
public class FindAction extends AbstractDecompilerAction { public class FindAction extends AbstractDecompilerAction {
private FindDialog findDialog; private FindDialog findDialog;
private final DecompilerPanel decompilerPanel;
private final PluginTool tool;
public FindAction(PluginTool tool, DecompilerController controller) { public FindAction() {
super("Find"); super("Find");
this.tool = tool;
this.decompilerPanel = controller.getDecompilerPanel();
setPopupMenuData(new MenuData(new String[] { "Find..." }, "Decompile")); setPopupMenuData(new MenuData(new String[] { "Find..." }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK)); setKeyBindingData(new KeyBindingData(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
setEnabled(true); setEnabled(true);
} }
protected FindDialog getFindDialog() { protected FindDialog getFindDialog(DecompilerPanel decompilerPanel) {
if (findDialog == null) { if (findDialog == null) {
findDialog = new FindDialog("Decompiler Find Text", new DecompilerSearcher()) { findDialog =
new FindDialog("Decompiler Find Text", new DecompilerSearcher(decompilerPanel)) {
@Override @Override
protected void dialogClosed() { protected void dialogClosed() {
// clear the search results when the dialog is closed // clear the search results when the dialog is closed
@ -57,7 +54,13 @@ public class FindAction extends AbstractDecompilerAction {
return findDialog; return findDialog;
} }
private class DecompilerSearcher implements FindDialogSearcher { private static class DecompilerSearcher implements FindDialogSearcher {
private DecompilerPanel decompilerPanel;
public DecompilerSearcher(DecompilerPanel dPanel) {
decompilerPanel = dPanel;
}
@Override @Override
public CursorPosition getCursorPosition() { public CursorPosition getCursorPosition() {
@ -119,13 +122,14 @@ public class FindAction extends AbstractDecompilerAction {
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
FindDialog dialog = getFindDialog(); DecompilerPanel decompilerPanel = context.getDecompilerPanel();
FindDialog dialog = getFindDialog(decompilerPanel);
String text = decompilerPanel.getHighlightedText(); String text = decompilerPanel.getHighlightedText();
if (text != null) { if (text != null) {
dialog.setSearchText(text); dialog.setSearchText(text);
} }
// show over the root frame, so the user can still see the Decompiler window // show over the root frame, so the user can still see the Decompiler window
tool.showDialog(dialog); context.getTool().showDialog(dialog);
} }
} }

View file

@ -22,7 +22,6 @@ import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.plugin.core.decompile.DecompilerProvider; import ghidra.app.plugin.core.decompile.DecompilerProvider;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@ -40,11 +39,9 @@ public class FindReferencesToSymbolAction extends AbstractDecompilerAction {
private static final String MENU_ITEM_TEXT = "Find References to"; private static final String MENU_ITEM_TEXT = "Find References to";
public static final String NAME = "Find References to Symbol"; public static final String NAME = "Find References to Symbol";
private PluginTool tool;
public FindReferencesToSymbolAction(PluginTool tool) { public FindReferencesToSymbolAction() {
super(NAME); super(NAME);
this.tool = tool;
setPopupMenuData( setPopupMenuData(
new MenuData(new String[] { LocationReferencesService.MENU_GROUP, MENU_ITEM_TEXT })); new MenuData(new String[] { LocationReferencesService.MENU_GROUP, MENU_ITEM_TEXT }));
@ -116,7 +113,8 @@ public class FindReferencesToSymbolAction extends AbstractDecompilerAction {
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
LocationReferencesService service = tool.getService(LocationReferencesService.class); LocationReferencesService service =
context.getTool().getService(LocationReferencesService.class);
if (service == null) { if (service == null) {
Msg.showError(this, null, "Missing Plugin", Msg.showError(this, null, "Missing Plugin",
"The " + LocationReferencesService.class.getSimpleName() + " is not installed.\n" + "The " + LocationReferencesService.class.getSimpleName() + " is not installed.\n" +

View file

@ -19,32 +19,29 @@ import java.util.Set;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
public class ForwardSliceAction extends AbstractDecompilerAction { public class ForwardSliceAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public ForwardSliceAction(DecompilerController controller) { public ForwardSliceAction() {
super("Highlight Forward Slice"); super("Highlight Forward Slice");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Highlight", "Forward Slice" }, "Decompile")); setPopupMenuData(new MenuData(new String[] { "Highlight", "Forward Slice" }, "Decompile"));
} }
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
return varnode != null; return varnode != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode == null) { if (varnode == null) {
return; return;
@ -52,6 +49,7 @@ public class ForwardSliceAction extends AbstractDecompilerAction {
PcodeOp op = tokenAtCursor.getPcodeOp(); PcodeOp op = tokenAtCursor.getPcodeOp();
Set<Varnode> forwardSlice = DecompilerUtils.getForwardSlice(varnode); Set<Varnode> forwardSlice = DecompilerUtils.getForwardSlice(varnode);
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
decompilerPanel.clearPrimaryHighlights(); decompilerPanel.clearPrimaryHighlights();
SliceHighlightColorProvider colorProvider = SliceHighlightColorProvider colorProvider =

View file

@ -19,38 +19,36 @@ import java.util.Set;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction { public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public ForwardSliceToPCodeOpsAction(DecompilerController controller) { public ForwardSliceToPCodeOpsAction() {
super("Highlight Forward Inst Slice"); super("Highlight Forward Inst Slice");
this.controller = controller;
setPopupMenuData( setPopupMenuData(
new MenuData(new String[] { "Highlight", "Forward Inst Slice" }, "Decompile")); new MenuData(new String[] { "Highlight", "Forward Inst Slice" }, "Decompile"));
} }
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
return varnode != null; return varnode != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode != null) { if (varnode != null) {
PcodeOp op = tokenAtCursor.getPcodeOp(); PcodeOp op = tokenAtCursor.getPcodeOp();
Set<PcodeOp> forwardSlice = DecompilerUtils.getForwardSliceToPCodeOps(varnode); Set<PcodeOp> forwardSlice = DecompilerUtils.getForwardSliceToPCodeOps(varnode);
forwardSlice.add(op); forwardSlice.add(op);
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
decompilerPanel.clearPrimaryHighlights(); decompilerPanel.clearPrimaryHighlights();
decompilerPanel.addPcodeOpHighlights(forwardSlice, decompilerPanel.addPcodeOpHighlights(forwardSlice,
decompilerPanel.getCurrentVariableHighlightColor()); decompilerPanel.getCurrentVariableHighlightColor());

View file

@ -16,11 +16,9 @@
package ghidra.app.plugin.core.decompile.actions; package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.services.GraphService; import ghidra.app.services.GraphService;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.HighFunction;
@ -28,23 +26,20 @@ import ghidra.util.Msg;
import ghidra.util.task.TaskLauncher; import ghidra.util.task.TaskLauncher;
public class GraphASTControlFlowAction extends AbstractDecompilerAction { public class GraphASTControlFlowAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private final PluginTool tool;
public GraphASTControlFlowAction(Plugin plugin, DecompilerController controller) { public GraphASTControlFlowAction() {
super("Graph AST Control Flow"); super("Graph AST Control Flow");
this.tool = plugin.getTool();
this.controller = controller;
setMenuBarData(new MenuData(new String[] { "Graph AST Control Flow" }, "graph")); setMenuBarData(new MenuData(new String[] { "Graph AST Control Flow" }, "graph"));
} }
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return controller.getFunction() != null; return context.getFunction() != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
PluginTool tool = context.getTool();
GraphService graphService = tool.getService(GraphService.class); GraphService graphService = tool.getService(GraphService.class);
if (graphService == null) { if (graphService == null) {
Msg.showError(this, tool.getToolFrame(), "AST Graph Failed", Msg.showError(this, tool.getToolFrame(), "AST Graph Failed",
@ -56,8 +51,8 @@ public class GraphASTControlFlowAction extends AbstractDecompilerAction {
Options options = tool.getOptions("Graph"); Options options = tool.getOptions("Graph");
boolean reuseGraph = options.getBoolean("Reuse Graph", false); boolean reuseGraph = options.getBoolean("Reuse Graph", false);
int codeLimitPerBlock = options.getInt("Max Code Lines Displayed", 10); int codeLimitPerBlock = options.getInt("Max Code Lines Displayed", 10);
HighFunction highFunction = controller.getHighFunction(); HighFunction highFunction = context.getHighFunction();
Address locationAddr = controller.getLocation().getAddress(); Address locationAddr = context.getLocation().getAddress();
ASTGraphTask task = new ASTGraphTask(graphService, !reuseGraph, codeLimitPerBlock, ASTGraphTask task = new ASTGraphTask(graphService, !reuseGraph, codeLimitPerBlock,
locationAddr, highFunction, ASTGraphTask.CONTROL_FLOW_GRAPH); locationAddr, highFunction, ASTGraphTask.CONTROL_FLOW_GRAPH);
new TaskLauncher(task, tool.getToolFrame()); new TaskLauncher(task, tool.getToolFrame());

View file

@ -19,37 +19,35 @@ import java.util.Set;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
public class HighlightDefinedUseAction extends AbstractDecompilerAction { public class HighlightDefinedUseAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public HighlightDefinedUseAction(DecompilerController controller) { public HighlightDefinedUseAction() {
super("Highlight Defined Use"); super("Highlight Defined Use");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Highlight", "Def-use" }, "Decompile")); setPopupMenuData(new MenuData(new String[] { "Highlight", "Def-use" }, "Decompile"));
} }
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
return varnode != null; return varnode != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor); Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode == null) { if (varnode == null) {
return; return;
} }
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
decompilerPanel.clearPrimaryHighlights(); decompilerPanel.clearPrimaryHighlights();
Set<Varnode> varnodes = Set.of(varnode); Set<Varnode> varnodes = Set.of(varnode);

View file

@ -0,0 +1,86 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.UndefinedFunction;
public class IsolateVariableAction extends AbstractDecompilerAction {
public IsolateVariableAction() {
super("Split out as New Variable");
setPopupMenuData(new MenuData(new String[] { "Split out as New Variable" }, "Decompile"));
// setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
HighVariable variable = tokenAtCursor.getHighVariable();
if (!(variable instanceof HighLocal)) {
return false;
}
HighSymbol highSymbol = variable.getSymbol();
if (highSymbol == null) {
return false;
}
if (highSymbol.isIsolated()) {
return false;
}
Varnode vn = tokenAtCursor.getVarnode();
if (vn == null) {
return false;
}
short mergeGroup = vn.getMergeGroup();
boolean mergeSplit = false;
for (Varnode var : variable.getInstances()) {
if (var.getMergeGroup() != mergeGroup) {
mergeSplit = true;
break;
}
}
if (!mergeSplit) {
return false;
}
return true;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
final ClangToken tokenAtCursor = context.getTokenAtCursor();
HighSymbol highSymbol = tokenAtCursor.getHighVariable().getSymbol();
IsolateVariableTask newVariableTask =
new IsolateVariableTask(context.getTool(), context.getProgram(),
context.getDecompilerPanel(), tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
newVariableTask.runTask(false);
}
}

View file

@ -0,0 +1,113 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
public class IsolateVariableTask extends RenameTask {
private HighSymbol highSymbol;
private HighFunction highFunction;
private Function function;
private SourceType srcType;
private String originalName;
private boolean nameIsReserved;
private boolean instanceIsMapped;
public IsolateVariableTask(PluginTool tool, Program program, DecompilerPanel panel,
ClangToken token, HighSymbol sym, SourceType st) {
super(tool, program, panel, token, "");
highSymbol = sym;
highFunction = highSymbol.getHighFunction();
function = highFunction.getFunction();
srcType = st;
originalName = highSymbol.getName();
nameIsReserved = highSymbol.isNameLocked();
instanceIsMapped = false;
if (nameIsReserved) {
Varnode vn = token.getVarnode();
if (vn != null) {
instanceIsMapped =
(vn.getMergeGroup() == vn.getHigh().getRepresentative().getMergeGroup());
}
}
if (!nameIsReserved || instanceIsMapped) {
// We can keep the current name if
// either no locked symbol is using it, or if this instance is directly mapped to it
oldName = originalName;
}
}
@Override
public String getTransactionName() {
return "Name New Variable";
}
@Override
public boolean isValid(String newNm) {
newName = newNm;
if (newName.equals(originalName)) {
if (nameIsReserved && !instanceIsMapped) {
errorMsg = "The name \"" + originalName + "\" is attached to another instance";
return false;
}
return true;
}
LocalSymbolMap localSymbolMap = highFunction.getLocalSymbolMap();
if (localSymbolMap.containsVariableWithName(newName) ||
isSymbolInFunction(function, newName)) {
errorMsg = "Duplicate name";
return false;
}
return true;
}
@Override
public void commit() throws DuplicateNameException, InvalidInputException {
// Split out the specific instance into its own symbol
Varnode vn = tokenAtCursor.getVarnode();
try {
HighVariable highVariable = highFunction.splitOutMergeGroup(vn.getHigh(), vn);
highSymbol = highVariable.getSymbol();
}
catch (PcodeException e) {
Msg.showError(this, tool.getToolFrame(), "New Variable Failed", e.getMessage());
return;
}
DataType dataType = highSymbol.getDataType();
if (Undefined.isUndefined(dataType)) {
// An undefined datatype will not be considered typelocked. Since the new variable
// needs to be typelocked we use an unsigned integer of equivalent size
dataType = AbstractIntegerDataType.getUnsignedDataType(dataType.getLength(),
program.getDataTypeManager());
}
// Create the new variable, typelocking in a new data-type
HighFunctionDBUtil.updateDBVariable(highSymbol, newName, dataType, srcType);
}
}

View file

@ -20,8 +20,6 @@ import java.util.Iterator;
import docking.action.MenuData; import docking.action.MenuData;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import ghidra.app.decompiler.*; import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.plugin.core.function.EditFunctionSignatureDialog; import ghidra.app.plugin.core.function.EditFunctionSignatureDialog;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@ -36,9 +34,6 @@ import ghidra.util.exception.CancelledException;
public class OverridePrototypeAction extends AbstractDecompilerAction { public class OverridePrototypeAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private final PluginTool tool;
public class ProtoOverrideDialog extends EditFunctionSignatureDialog { public class ProtoOverrideDialog extends EditFunctionSignatureDialog {
private FunctionDefinition functionDefinition; private FunctionDefinition functionDefinition;
@ -86,21 +81,18 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
} }
} }
public OverridePrototypeAction(PluginTool tool, DecompilerController controller) { public OverridePrototypeAction() {
super("Override Signature"); super("Override Signature");
this.tool = tool;
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Override Signature" }, "Decompile")); setPopupMenuData(new MenuData(new String[] { "Override Signature" }, "Decompile"));
} }
/** /**
* Try to find the PcodeOp representing the call the user has selected * Try to find the PcodeOp representing the call the user has selected
* @param controller the decompiler controller * @param program is the Program
* @param tokenAtCursor is the point in the window the user has selected
* @return the PcodeOp or null * @return the PcodeOp or null
*/ */
public static PcodeOp getCallOp(DecompilerController controller) { public static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
if (tokenAtCursor == null) { if (tokenAtCursor == null) {
return null; return null;
} }
@ -112,7 +104,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
if (addr == null) { if (addr == null) {
return null; return null;
} }
Instruction instr = controller.getProgram().getListing().getInstructionAt(addr); Instruction instr = program.getListing().getInstructionAt(addr);
if (instr == null) { if (instr == null) {
return null; return null;
} }
@ -135,12 +127,11 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
return null; return null;
} }
private Function getCalledFunction(PcodeOp op) { private Function getCalledFunction(Program program, PcodeOp op) {
if (op.getOpcode() != PcodeOp.CALL) { if (op.getOpcode() != PcodeOp.CALL) {
return null; return null;
} }
Address addr = op.getInput(0).getAddress(); Address addr = op.getInput(0).getAddress();
Program program = controller.getProgram();
FunctionManager functionManager = program.getFunctionManager(); FunctionManager functionManager = program.getFunctionManager();
Function function = functionManager.getFunctionAt(addr); Function function = functionManager.getFunctionAt(addr);
if (function != null) { if (function != null) {
@ -196,26 +187,26 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction(); Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) { if (function == null || function instanceof UndefinedFunction) {
return false; return false;
} }
return getCallOp(controller) != null; return getCallOp(context.getProgram(), context.getTokenAtCursor()) != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
Function func = controller.getFunction(); Function func = context.getFunction();
Program program = func.getProgram(); Program program = func.getProgram();
PcodeOp op = getCallOp(controller); PcodeOp op = getCallOp(program, context.getTokenAtCursor());
Function calledfunc = getCalledFunction(op); Function calledfunc = getCalledFunction(program, op);
boolean varargs = false; boolean varargs = false;
if (calledfunc != null) { if (calledfunc != null) {
varargs = calledfunc.hasVarArgs(); varargs = calledfunc.hasVarArgs();
} }
if ((op.getOpcode() == PcodeOp.CALL) && !varargs) { if ((op.getOpcode() == PcodeOp.CALL) && !varargs) {
if (OptionDialog.showOptionDialog(controller.getDecompilerPanel(), if (OptionDialog.showOptionDialog(context.getDecompilerPanel(),
"Warning : Localized Override", "Warning : Localized Override",
"Incorrect information entered here may hide other good information.\n" + "Incorrect information entered here may hide other good information.\n" +
"For direct calls, it is usually better to alter the prototype on the function\n" + "For direct calls, it is usually better to alter the prototype on the function\n" +
@ -233,6 +224,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
} }
String signature = generateSignature(op, name); String signature = generateSignature(op, name);
PluginTool tool = context.getTool();
ProtoOverrideDialog dialog = new ProtoOverrideDialog(tool, func, signature, conv); ProtoOverrideDialog dialog = new ProtoOverrideDialog(tool, func, signature, conv);
// dialog.setHelpLocation( new HelpLocation( getOwner(), "Edit_Function_Signature" ) ); // dialog.setHelpLocation( new HelpLocation( getOwner(), "Edit_Function_Signature" ) );
tool.showDialog(dialog); tool.showDialog(dialog);
@ -247,7 +239,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
commit = true; commit = true;
} }
catch (Exception e) { catch (Exception e) {
Msg.showError(getClass(), controller.getDecompilerPanel(), "Override Signature Failed", Msg.showError(getClass(), context.getDecompilerPanel(), "Override Signature Failed",
"Error overriding signature: " + e); "Error overriding signature: " + e);
} }
finally { finally {

View file

@ -44,20 +44,20 @@ public class RemoveSecondaryHighlightAction extends AbstractDecompilerAction {
return false; return false;
} }
DecompilerPanel panel = context.getDecompilerPanel(); ClangToken token = context.getTokenAtCursor();
ClangToken token = panel.getTokenAtCursor();
if (token == null) { if (token == null) {
return false; return false;
} }
DecompilerPanel panel = context.getDecompilerPanel();
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens(); TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
return highlightedTokens.contains(token); return highlightedTokens.contains(token);
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
ClangToken token = context.getTokenAtCursor();
DecompilerPanel panel = context.getDecompilerPanel(); DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
panel.removeSecondaryHighlight(token); panel.removeSecondaryHighlight(token);
} }
} }

View file

@ -1,70 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.util.bean.SetEquateDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighConstant;
import ghidra.util.HelpLocation;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
public class RenameConstantTask extends RenameTask {
private HighConstant high;
private Program program;
public RenameConstantTask(PluginTool tool, String old, HighConstant h, Program program) {
super(tool, old);
high = h;
this.program = program;
}
@Override
public void commit() throws DuplicateNameException, InvalidInputException {
// TODO: Constant equates do not work properly with decompiler
//high.rename(newName,SourceType.USER_DEFINED);
}
@Override
public String getTransactionName() {
return "Set Equate";
}
@Override
public boolean isValid(String newNm) {
newName = newNm;
return true;
}
/* (non-Javadoc)
* @see ghidra.app.plugin.core.decompile.actions.RenameTask#runDialog()
*/
@Override
public boolean runDialog() {
// NOTE: acstion must ensure that HighConstant datatype produces Scalar value and is integer type
// BooleanDataType and CharDataType do not produce scalar values in assembly listing.
SetEquateDialog setEquateDialog = new SetEquateDialog(tool, program, high.getScalar());
setEquateDialog.setHelpLocation(new HelpLocation("EquatesPlugin", "Set_Equate"));
if (setEquateDialog.showSetDialog() == SetEquateDialog.CANCELED) {
return false;
}
isValid(setEquateDialog.getEquateName());
return true;
}
}

View file

@ -0,0 +1,79 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Function;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
/**
* Action triggered from a specific token in the decompiler window to rename a field within
* a structure data-type. If the field already exists within the specific structure, it is
* simply renamed. Otherwise, if the decompiler has discovered an undefined structure offset, a new
* field is added to the structure with this offset and the user selected name. In either case,
* the altered structure is committed permanently to the program's database.
*/
public class RenameFieldAction extends AbstractDecompilerAction {
public RenameFieldAction() {
super("Rename Field");
setPopupMenuData(new MenuData(new String[] { "Rename Field" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
return (tokenAtCursor instanceof ClangFieldToken);
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
PluginTool tool = context.getTool();
final ClangToken tokenAtCursor = context.getTokenAtCursor();
Structure dt = getStructDataType(tokenAtCursor);
if (dt == null) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Could not find structure datatype");
return;
}
int offset = ((ClangFieldToken) tokenAtCursor).getOffset();
if (offset < 0 || offset >= dt.getLength()) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Could not resolve field within structure");
return;
}
RenameStructureFieldTask nameTask =
new RenameStructureFieldTask(tool, context.getProgram(), context.getDecompilerPanel(),
tokenAtCursor, dt, offset);
nameTask.runTask(true);
}
}

View file

@ -21,49 +21,43 @@ import docking.action.KeyBindingData;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangFuncNameToken; import ghidra.app.decompiler.ClangFuncNameToken;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*; import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.AddEditDialog; import ghidra.app.util.AddEditDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.UndefinedFunction; import ghidra.util.UndefinedFunction;
public class RenameFunctionAction extends AbstractDecompilerAction { public class RenameFunctionAction extends AbstractDecompilerAction {
private final DecompilerController controller; public RenameFunctionAction() {
private final PluginTool tool;
public RenameFunctionAction(PluginTool tool, DecompilerController controller) {
super("Rename Function"); super("Rename Function");
this.tool = tool;
this.controller = controller;
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0)); setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0));
setPopupMenuData(new MenuData(new String[] { "Rename Function" }, "Decompile")); setPopupMenuData(new MenuData(new String[] { "Rename Function" }, "Decompile"));
} }
private Function getFunction() { private Function getFunction(Program program, ClangToken tokenAtCursor) {
// try to look up the function that is at the current cursor location // try to look up the function that is at the current cursor location
// If there isn't one, just use the function we are in. // If there isn't one, just use the function we are in.
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
if (tokenAtCursor instanceof ClangFuncNameToken) { if (tokenAtCursor instanceof ClangFuncNameToken) {
return DecompilerUtils.getFunction(controller.getProgram(), return DecompilerUtils.getFunction(program, (ClangFuncNameToken) tokenAtCursor);
(ClangFuncNameToken) tokenAtCursor);
} }
return null; return null;
} }
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function func = getFunction(); Function func =
getFunction(context.getProgram(), context.getTokenAtCursor());
return func != null && !(func instanceof UndefinedFunction); return func != null && !(func instanceof UndefinedFunction);
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
Function function = getFunction(); Program program = context.getProgram();
AddEditDialog dialog = new AddEditDialog("Edit Function Name", tool); Function function = getFunction(program, context.getTokenAtCursor());
dialog.editLabel(function.getSymbol(), controller.getProgram()); AddEditDialog dialog = new AddEditDialog("Edit Function Name", context.getTool());
dialog.editLabel(function.getSymbol(), program);
} }
} }

View file

@ -0,0 +1,86 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.HighCodeSymbol;
import ghidra.program.model.pcode.HighSymbol;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
/**
* Action triggered from a specific token in the decompiler window to rename a global variable.
* The variable is associated with an address. There may already be a symbol in the database
* there, in which case the symbol is simply renamed. Otherwise a new symbol is added.
*/
public class RenameGlobalAction extends AbstractDecompilerAction {
public RenameGlobalAction() {
super("Rename Global");
setPopupMenuData(new MenuData(new String[] { "Rename Global" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
if (tokenAtCursor instanceof ClangFieldToken) {
return false;
}
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
if (highSymbol == null) {
return false;
}
return highSymbol.isGlobal();
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
PluginTool tool = context.getTool();
final ClangToken tokenAtCursor = context.getTokenAtCursor();
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
Address addr = null;
if (highSymbol instanceof HighCodeSymbol) {
addr = ((HighCodeSymbol) highSymbol).getStorage().getMinAddress();
}
if (addr == null || !addr.isMemoryAddress()) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Memory storage not found for global variable");
return;
}
RenameGlobalVariableTask nameTask =
new RenameGlobalVariableTask(tool, context.getProgram(), context.getDecompilerPanel(),
tokenAtCursor, addr);
nameTask.runTask(true);
}
}

View file

@ -15,6 +15,8 @@
*/ */
package ghidra.app.plugin.core.decompile.actions; package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@ -24,14 +26,13 @@ import ghidra.util.exception.InvalidInputException;
public class RenameGlobalVariableTask extends RenameTask { public class RenameGlobalVariableTask extends RenameTask {
private Address address; // Address of global variable private Address address; // Address of global variable
private Program program;
private SymbolTable symboltable; private SymbolTable symboltable;
private Symbol symbol; private Symbol symbol;
public RenameGlobalVariableTask(PluginTool tool, String old, Address addr, Program prog) { public RenameGlobalVariableTask(PluginTool tool, Program program, DecompilerPanel panel,
super(tool, old); ClangToken token, Address addr) {
super(tool, program, panel, token, token.getText());
address = addr; address = addr;
program = prog;
symboltable = program.getSymbolTable(); symboltable = program.getSymbolTable();
symbol = null; symbol = null;
} }

View file

@ -0,0 +1,88 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.HighSymbol;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.UndefinedFunction;
/**
* Action triggered from a specific token in the decompiler window to rename a local variable.
* If a matching variable in the database already exists, it is simply renamed. Otherwise
* a new variable is added to the database. In this case the new variable is assigned
* an "undefined" datatype, which leaves it un-typelocked, and the decompiler will take
* the name but lets the data-type continue to "float" and can speculatively merge the
* variable with others.
*
* If the selected variable is an input parameter, other input parameters within the decompiler
* model will need to be committed, if they do not already exist in the database, as any parameters
* committed to the database are forcing on the decompiler. Any new parameters committed this way
* inherit their name from the decompiler model, but the parameters will not be type-locked, allowing
* their data-type to "float".
*/
public class RenameLocalAction extends AbstractDecompilerAction {
public RenameLocalAction() {
super("Rename Variable");
setPopupMenuData(new MenuData(new String[] { "Rename Variable" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
if (tokenAtCursor instanceof ClangFieldToken) {
return false;
}
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
if (highSymbol == null) {
return false;
}
return !highSymbol.isGlobal();
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
PluginTool tool = context.getTool();
final ClangToken tokenAtCursor = context.getTokenAtCursor();
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
RenameVariableTask nameTask = new RenameVariableTask(tool, context.getProgram(),
context.getDecompilerPanel(),
tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
nameTask.runTask(true);
}
}

View file

@ -15,11 +15,11 @@
*/ */
package ghidra.app.plugin.core.decompile.actions; package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.*;
import ghidra.program.model.data.DataTypeComponent; import ghidra.program.model.listing.Program;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.Undefined1DataType;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
@ -28,8 +28,9 @@ public class RenameStructureFieldTask extends RenameTask {
private Structure structure; private Structure structure;
public int offset; public int offset;
public RenameStructureFieldTask(PluginTool tool,String old,Structure structure,int offset) { public RenameStructureFieldTask(PluginTool tool, Program program, DecompilerPanel panel,
super(tool,old); ClangToken token, Structure structure, int offset) {
super(tool, program, panel, token, token.getText());
this.structure = structure; this.structure = structure;
this.offset = offset; this.offset = offset;
} }
@ -45,8 +46,9 @@ public class RenameStructureFieldTask extends RenameTask {
DataType newtype = new Undefined1DataType(); DataType newtype = new Undefined1DataType();
structure.replaceAtOffset(offset, newtype,1, newName, "Created by retype action"); structure.replaceAtOffset(offset, newtype,1, newName, "Created by retype action");
} }
else else {
comp.setFieldName(newName); comp.setFieldName(newName);
}
} }
@Override @Override
@ -58,10 +60,12 @@ public class RenameStructureFieldTask extends RenameTask {
public boolean isValid(String newNm) { public boolean isValid(String newNm) {
newName = newNm; newName = newNm;
DataTypeComponent[] comp = structure.getComponents(); DataTypeComponent[] comp = structure.getComponents();
for(int i=0;i<comp.length;++i) { for (DataTypeComponent element : comp) {
// if (comp[i].getDataType() == DataType.DEFAULT) continue; // Placeholder, don't compare name // if (comp[i].getDataType() == DataType.DEFAULT) continue; // Placeholder, don't compare name
String fieldname = comp[i].getFieldName(); String fieldname = element.getFieldName();
if (fieldname == null) continue; if (fieldname == null) {
continue;
}
if (fieldname.equals(newName)) { if (fieldname.equals(newName)) {
errorMsg = "Duplicate Field Name"; errorMsg = "Duplicate Field Name";
return false; return false;

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +17,13 @@ package ghidra.app.plugin.core.decompile.actions;
import docking.widgets.dialogs.InputDialog; import docking.widgets.dialogs.InputDialog;
import docking.widgets.dialogs.InputDialogListener; import docking.widgets.dialogs.InputDialogListener;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
@ -31,9 +36,16 @@ public abstract class RenameTask {
protected String oldName; protected String oldName;
protected String errorMsg = null; // Error to return if isValid returns false protected String errorMsg = null; // Error to return if isValid returns false
protected PluginTool tool; protected PluginTool tool;
protected Program program;
protected DecompilerPanel decompilerPanel;
protected ClangToken tokenAtCursor;
public RenameTask(PluginTool tool,String old) { public RenameTask(PluginTool tool, Program program, DecompilerPanel panel, ClangToken token,
String old) {
this.tool = tool; this.tool = tool;
this.program = program;
this.decompilerPanel = panel;
this.tokenAtCursor = token;
oldName = old; oldName = old;
} }
@ -47,10 +59,12 @@ public abstract class RenameTask {
/** /**
* Bring up a dialog that is initialized with the old name, and allows the user to select a new name * Bring up a dialog that is initialized with the old name, and allows the user to select a new name
* @param oldNameIsCancel is true if the user keeping/entering the old name is considered a cancel
* @return true unless the user canceled * @return true unless the user canceled
*/ */
public boolean runDialog() { private boolean runDialog(boolean oldNameIsCancel) {
InputDialogListener listener = new InputDialogListener() { InputDialogListener listener = new InputDialogListener() {
@Override
public boolean inputIsValid(InputDialog dialog) { public boolean inputIsValid(InputDialog dialog) {
String name = dialog.getValue(); String name = dialog.getValue();
if ((name==null)||(name.length()==0)) { if ((name==null)||(name.length()==0)) {
@ -62,8 +76,9 @@ public abstract class RenameTask {
return true; // but valid (ends up being equivalent to cancel return true; // but valid (ends up being equivalent to cancel
} }
boolean res = isValid(name); boolean res = isValid(name);
if (!res) if (!res) {
dialog.setStatusText(errorMsg); dialog.setStatusText(errorMsg);
}
return res; return res;
} }
}; };
@ -77,10 +92,42 @@ public abstract class RenameTask {
if (renameVarDialog.isCanceled()) { if (renameVarDialog.isCanceled()) {
return false; return false;
} }
if (newName.equals(oldName)) // No change to name if (oldNameIsCancel && newName.equals(oldName)) {
return false; return false;
}
return true; return true;
} }
/**
* Perform the task of selecting a new name and committing it to the database
* @param oldNameIsCancel is true if the user entering/keeping the old name is considered a cancel
*/
public void runTask(boolean oldNameIsCancel) {
boolean dialogres = runDialog(oldNameIsCancel);
if (dialogres) {
int transaction = program.startTransaction(getTransactionName());
boolean commit = false;
try {
commit();
commit = true;
}
catch (DuplicateNameException e) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed", e.getMessage());
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed", e.getMessage());
}
finally {
program.endTransaction(transaction, commit);
decompilerPanel.tokenRenamed(tokenAtCursor, getNewName());
}
}
}
public static boolean isSymbolInFunction(Function function, String name) {
SymbolTable symbolTable = function.getProgram().getSymbolTable();
return !symbolTable.getSymbols(name, function).isEmpty();
}
} }

View file

@ -1,266 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
public class RenameVariableAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private final PluginTool tool;
private RenameTask nameTask = null;
public RenameVariableAction(PluginTool tool, DecompilerController controller) {
super("Rename Variable");
this.tool = tool;
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Rename Variable" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0));
}
public static Address getStorageAddress(ClangToken tokenAtCursor,
DecompilerController controller) {
Varnode vnode = tokenAtCursor.getVarnode();
Address storageAddress = null;
if (vnode != null) {
storageAddress = vnode.getAddress();
}
// op could be a PTRSUB, need to dig it out...
else if (tokenAtCursor instanceof ClangVariableToken) {
PcodeOp op = ((ClangVariableToken) tokenAtCursor).getPcodeOp();
if (op == null) {
return null;
}
if (op.getOpcode() == PcodeOp.PTRSUB) {
vnode = op.getInput(0);
if (vnode.isRegister()) {
AddressSpace stackspace =
controller.getProgram().getAddressFactory().getStackSpace();
if (stackspace != null) {
Address caddr = op.getInput(1).getAddress();
storageAddress = stackspace.getAddress(caddr.getOffset());
}
}
else {
Address caddr = op.getInput(1).getAddress();
storageAddress =
controller.getLocation().getAddress().getNewAddress(caddr.getOffset());
}
}
}
return storageAddress;
}
public static HighVariable forgeHighVariable(Address addr, DecompilerController controller) {
HighVariable res = null;
Program program = controller.getProgram();
HighFunction hfunc = controller.getDecompileData().getHighFunction();
if (addr.isStackAddress()) {
LocalSymbolMap lsym = hfunc.getLocalSymbolMap();
HighSymbol hsym = lsym.findLocal(addr, null);
if (hsym != null) {
res = hsym.getHighVariable();
}
}
else {
Data data = program.getListing().getDataAt(addr);
if (data != null) {
DataType dt = data.getDataType();
try {
res = new HighGlobal(data.getLabel(), dt, new Varnode(addr, dt.getLength()),
null, hfunc);
}
catch (InvalidInputException e) {
Msg.error(RenameVariableAction.class, e.getMessage());
}
}
}
return res;
}
/**
* Get the structure associated with a field token
* @param tok is the token representing a field
* @return the structure which contains this field
*/
public static Structure getStructDataType(ClangToken tok) {
// We already know tok is a ClangFieldToken
ClangFieldToken fieldtok = (ClangFieldToken) tok;
DataType dt = fieldtok.getDataType();
if (dt == null) {
return null;
}
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof Structure) {
return (Structure) dt;
}
return null;
}
/**
* Get the offset of the field within its structure
* @param tok is the display token associated with the field
* @return the offset, in bytes, of that field within its structure
*/
public static int getDataTypeOffset(ClangToken tok) {
// Assume tok is already vetted as a structure carrying ClangFieldToken
return ((ClangFieldToken) tok).getOffset();
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction();
if (function instanceof UndefinedFunction) {
return false;
}
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
if (tokenAtCursor instanceof ClangFieldToken) {
DataType dt = getStructDataType(tokenAtCursor);
if (dt == null) {
return false;
}
getPopupMenuData().setMenuItemName("Rename Field");
return true;
}
HighVariable variable = tokenAtCursor.getHighVariable();
if (variable == null) {
// not sure why variables with an & in front of them have no highVariable
Address storageAddress = getStorageAddress(tokenAtCursor, controller);
if (storageAddress == null) {
return false;
}
variable = forgeHighVariable(storageAddress, controller);
if (variable == null) {
return false;
}
}
if (variable instanceof HighLocal) {
getPopupMenuData().setMenuItemName("Rename Variable");
return true;
}
else if (variable instanceof HighGlobal) {
getPopupMenuData().setMenuItemName("Rename Global");
return true;
}
//TODO: Constant equates do not work properly with decompiler
// else if (variable instanceof HighConstant) {
// getPopupMenuData().setMenuItemName("Rename Constant");
// return true;
// }
return false;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
final ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
HighVariable variable = tokenAtCursor.getHighVariable();
if (variable == null) {
if (tokenAtCursor instanceof ClangVariableToken) {
Address addr = getStorageAddress(tokenAtCursor, controller);
variable = forgeHighVariable(addr, controller);
}
}
//TODO: Constant equates do not work properly with decompiler
// if (variable instanceof HighConstant) {
// nameTask =
// new RenameConstantTask(tool, tokenAtCursor.getText(), (HighConstant) variable,
// controller.getProgram());
// }
// else
if (variable instanceof HighLocal) {
nameTask =
new RenameVariableTask(tool, variable.getName(), controller.getHighFunction(),
variable, tokenAtCursor.getVarnode(), SourceType.USER_DEFINED);
}
else if (variable instanceof HighGlobal) {
Address addr = variable.getRepresentative().getAddress();
if (addr == null || !addr.isMemoryAddress()) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Memory storage not found for global variable");
return;
}
nameTask = new RenameGlobalVariableTask(tool, tokenAtCursor.getText(), addr,
controller.getProgram());
}
else if (tokenAtCursor instanceof ClangFieldToken) {
Structure dt = getStructDataType(tokenAtCursor);
if (dt == null) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Could not find structure datatype");
return;
}
int offset = getDataTypeOffset(tokenAtCursor);
if (offset < 0 || offset >= dt.getLength()) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Could not resolve field within structure");
return;
}
nameTask = new RenameStructureFieldTask(tool, tokenAtCursor.getText(), dt, offset);
}
else {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Selected variable does not support renaming");
return;
}
boolean dialogres = nameTask.runDialog();
if (dialogres) {
Program program = controller.getProgram();
int transaction = program.startTransaction(nameTask.getTransactionName());
boolean commit = false;
try {
nameTask.commit();
commit = true;
}
catch (DuplicateNameException e) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed", e.getMessage());
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed", e.getMessage());
}
finally {
program.endTransaction(transaction, commit);
controller.getDecompilerPanel().tokenRenamed(tokenAtCursor, nameTask.getNewName());
}
}
}
}

View file

@ -15,34 +15,33 @@
*/ */
package ghidra.app.plugin.core.decompile.actions; package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*; import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
public class RenameVariableTask extends RenameTask { public class RenameVariableTask extends RenameTask {
private HighVariable var; private HighSymbol highSymbol;
private Varnode exactSpot; private Varnode exactSpot;
private HighFunction hfunction; private HighFunction hfunction;
private Program program;
private Function function; private Function function;
private boolean commitRequired; // Set to true if all parameters are committed before renaming private boolean commitRequired; // Set to true if all parameters are committed before renaming
private SourceType srctype; // Desired source type for the variable being renamed private SourceType srctype; // Desired source type for the variable being renamed
private SourceType signatureSrcType; // Signature source type of the function (which will be preserved) private SourceType signatureSrcType; // Signature source type of the function (which will be preserved)
public RenameVariableTask(PluginTool tool, String old, HighFunction hfunc, HighVariable v, public RenameVariableTask(PluginTool tool, Program program, DecompilerPanel panel,
Varnode ex, SourceType st) { ClangToken token, HighSymbol sym, SourceType st) {
super(tool, old); super(tool, program, panel, token, sym.getName());
var = v; highSymbol = sym;
exactSpot = ex; exactSpot = token.getVarnode();
hfunction = hfunc; hfunction = sym.getHighFunction();
function = hfunc.getFunction(); function = hfunction.getFunction();
program = function.getProgram();
srctype = st; srctype = st;
signatureSrcType = function.getSignatureSource(); signatureSrcType = function.getSignatureSource();
} }
@ -55,39 +54,40 @@ public class RenameVariableTask extends RenameTask {
HighFunctionDBUtil.commitReturnToDatabase(hfunction, signatureSrcType); HighFunctionDBUtil.commitReturnToDatabase(hfunction, signatureSrcType);
} }
} }
HighFunctionDBUtil.updateDBVariable(var, newName, null, srctype); HighFunctionDBUtil.updateDBVariable(highSymbol, newName, null, srctype);
} }
@Override @Override
public boolean isValid(String newNm) { public boolean isValid(String newNm) {
newName = newNm; newName = newNm;
LocalSymbolMap localSymbolMap = hfunction.getLocalSymbolMap(); LocalSymbolMap localSymbolMap = hfunction.getLocalSymbolMap();
if (localSymbolMap.containsVariableWithName(newName) || isSymbolInFunction(newName)) { if (localSymbolMap.containsVariableWithName(newName) ||
isSymbolInFunction(function, newName)) {
errorMsg = "Duplicate name"; errorMsg = "Duplicate name";
return false; return false;
} }
commitRequired = RetypeVariableAction.checkFullCommit(var, hfunction); commitRequired = AbstractDecompilerAction.checkFullCommit(highSymbol, hfunction);
if (commitRequired) { if (commitRequired) {
exactSpot = null; // Don't try to split out if we need to commit exactSpot = null; // Don't try to split out if we need to commit
} }
if (exactSpot != null) { // The user pointed at a particular usage, not just the vardecl if (exactSpot != null && !highSymbol.isNameLocked()) { // The user pointed at a particular usage, not just the vardecl
try { try {
var = hfunction.splitOutMergeGroup(var, exactSpot); HighVariable var = hfunction.splitOutMergeGroup(exactSpot.getHigh(), exactSpot);
highSymbol = var.getSymbol();
} }
catch (PcodeException e) { catch (PcodeException e) {
errorMsg = "Rename Failed: " + e.getMessage(); errorMsg = "Rename Failed: " + e.getMessage();
return false; return false;
} }
} }
if (highSymbol == null) {
errorMsg = "Rename Failed: No symbol";
return false;
}
return true; return true;
} }
private boolean isSymbolInFunction(String name) {
SymbolTable symbolTable = program.getSymbolTable();
return !symbolTable.getSymbols(name, function).isEmpty();
}
@Override @Override
public String getTransactionName() { public String getTransactionName() {
return "Rename Local Variable"; return "Rename Local Variable";

View file

@ -0,0 +1,181 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
/**
* Action triggered from a specific token in the decompiler window to change the data-type of
* a field within a structure data-type. The field must already exist, except in the case of a
* completely undefined structure. The data-type of the field is changed according to the user
* selection. If the size of the seleted data-type is bigger, this can trigger other fields in
* the structure to be removed and may change the size of the structure. The modified data-type
* is permanently committed to the program's database.
*/
public class RetypeFieldAction extends AbstractDecompilerAction {
public RetypeFieldAction() {
super("Retype Field");
setPopupMenuData(new MenuData(new String[] { "Retype Field" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK));
}
/**
* Return the index of the last component that would be overwritten by a new datatype, if we started overwriting
* with at a specific component. All components, except the first, must be undefined datatypes or we return -1
* @param struct is the structure we are testing
* @param comp is the starting component to overwrite
* @param newtype is the datatype to overwrite with
* @return the index of the last component overwritten or -1
*/
private static int getEndComponentIndex(Structure struct, DataTypeComponent comp,
DataType newtype) {
int newlen = newtype.getLength();
if (newlen <= 0) {
return -1; // Don't support variable length types
}
DataType curtype = comp.getDataType();
newlen -= curtype.getLength();
int index = comp.getOrdinal();
while (newlen > 0) {
index += 1;
if (index >= struct.getNumComponents()) {
return -1; // Not enough space in the structure
}
comp = struct.getComponent(index);
// String nm = comp.getFieldName();
// if ((nm !=null)&&(nm.length()!=0))
// return -1;
curtype = comp.getDataType();
// if (!Undefined.isUndefined(curtype))
// return -1; // Overlaps non-undefined datatype
if (curtype != DataType.DEFAULT) {
return -1; // Only allow overwrite of placeholder components
}
newlen -= curtype.getLength();
}
return index;
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
if (tokenAtCursor instanceof ClangFieldToken) {
DataType dt = getStructDataType(tokenAtCursor);
return (dt != null);
}
return false;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = context.getProgram();
PluginTool tool = context.getTool();
ClangToken tokenAtCursor = context.getTokenAtCursor();
DataTypeManager dataTypeManager = program.getDataTypeManager();
DataType dataType = null;
Structure struct = getStructDataType(tokenAtCursor);
int offset = ((ClangFieldToken) tokenAtCursor).getOffset();
if (struct == null) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type structure");
return;
}
if (offset < 0 || offset >= struct.getLength()) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type structure: " + struct.getName());
return;
}
DataTypeComponent comp = struct.getComponentAt(offset);
if (comp == null) {
dataType = chooseDataType(tool, program, DataType.DEFAULT);
}
else {
dataType = chooseDataType(tool, program, comp.getDataType());
}
if (dataType == null) {
return;
}
boolean successfulMod = false;
if (comp == null) {
if (!struct.isNotYetDefined()) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Could not find component of '" + struct.getName() + "' to retype");
return;
}
// note if we reach here the offset must be zero, so assume we are inserting newtype
int transaction = program.startTransaction("Retype Structure Field");
try {
// Make sure datatype is using the program's data organization before testing fit
if (dataType.getDataTypeManager() != dataTypeManager) {
dataType = dataTypeManager.resolve(dataType, null);
}
struct.insert(0, dataType);
successfulMod = true;
}
finally {
program.endTransaction(transaction, successfulMod);
}
return;
}
int transaction = program.startTransaction("Retype Structure Field");
try {
// Make sure datatype is using the program's data organization before testing fit
if (dataType.getDataTypeManager() != dataTypeManager) {
dataType = dataTypeManager.resolve(dataType, null);
}
int startind = comp.getOrdinal();
int endind = getEndComponentIndex(struct, comp, dataType);
if (endind < 0) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type structure '" + struct.getName() + "': Datatype did not fit");
return;
}
for (int i = endind; i > startind; --i) { // Clear all but first field
struct.clearComponent(i);
}
struct.replaceAtOffset(comp.getOffset(), dataType, dataType.getLength(),
comp.getFieldName(), comp.getComment());
successfulMod = true;
}
finally {
program.endTransaction(transaction, successfulMod);
}
}
}

View file

@ -0,0 +1,119 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunctionDBUtil;
import ghidra.program.model.pcode.HighSymbol;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
import ghidra.util.exception.*;
/**
* Action triggered from a specific token in the decompiler window to change the data-type
* associated with a global variable. If the variable does not already exist in the program database,
* it will be created using storage address the decompiler has assigned to the variable within its model.
* In either case, there is a preexisting notion of variable storage. This action may allow the newly
* selected data-type to be of a different size relative to this preexisting storage, constrained by
* other global variables that might already consume storage.
*/
public class RetypeGlobalAction extends AbstractDecompilerAction {
public RetypeGlobalAction() {
super("Retype Global");
setPopupMenuData(new MenuData(new String[] { "Retype Global" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
if (tokenAtCursor instanceof ClangFieldToken) {
return false;
}
if (tokenAtCursor.Parent() instanceof ClangReturnType) {
return false;
}
if (!tokenAtCursor.isVariableRef()) {
return false;
}
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
if (highSymbol == null) {
return false;
}
return highSymbol.isGlobal();
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = context.getProgram();
PluginTool tool = context.getTool();
ClangToken tokenAtCursor = context.getTokenAtCursor();
DataType dataType = null;
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
if (highSymbol == null) {
return;
}
dataType = chooseDataType(tool, program, highSymbol.getDataType());
if (dataType == null) {
return;
}
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean successfulMod = false;
int transaction = program.startTransaction("Retype Global");
try {
if (dataType.getDataTypeManager() != dataTypeManager) {
dataType = dataTypeManager.resolve(dataType, null);
}
HighFunctionDBUtil.updateDBVariable(highSymbol, null, dataType,
SourceType.USER_DEFINED);
successfulMod = true;
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e);
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type variable '" + highSymbol.getName() + "': " + e.getMessage());
}
finally {
program.endTransaction(transaction, successfulMod);
}
}
}

View file

@ -0,0 +1,167 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
import ghidra.util.exception.*;
/**
* Action triggered from a specific token in the decompiler window to change the data-type
* associated with a variable in the local scope of the function. This can be an input parameter,
* a stack variable, a variable associated with a register, or a "dynamic" variable. If the
* variable does not already exist in the program database, it will be created using storage the
* decompiler has assigned to the variable within its model. In either case, there is a preexisting
* notion of variable storage. This action may allow the newly selected data-type to be of a
* different size relative to this preexisting storage, constrained by other variables that might
* already consume storage.
*
* If the selected variable is an input parameter, other input parameters within the decompiler
* model will need to be committed, if they do not already exist in the database, as any parameters
* committed to the database are forcing on the decompiler. Any new parameters committed this way
* inherit their name from the decompiler model, but the parameters will not be type-locked, allowing
* their data-type to "float".
*
*/
public class RetypeLocalAction extends AbstractDecompilerAction {
public RetypeLocalAction() {
super("Retype Variable");
setPopupMenuData(new MenuData(new String[] { "Retype Variable" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK));
}
private void retypeSymbol(Program program, HighSymbol highSymbol, Varnode exactSpot,
DataType dt, PluginTool tool) {
HighFunction hfunction = highSymbol.getHighFunction();
boolean commitRequired = checkFullCommit(highSymbol, hfunction);
if (commitRequired) {
exactSpot = null; // Don't try to split out if commit is required
}
if (exactSpot != null) { // The user pointed at a particular usage, not just the vardecl
try {
HighVariable var = hfunction.splitOutMergeGroup(exactSpot.getHigh(), exactSpot);
highSymbol = var.getSymbol();
}
catch (PcodeException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed", e.getMessage());
return;
}
}
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean successfulMod = false;
int transaction = program.startTransaction("Retype Variable");
try {
if (dt.getDataTypeManager() != dataTypeManager) {
dt = dataTypeManager.resolve(dt, null);
}
if (commitRequired) {
// Don't use datatypes of other parameters if the datatypes were floating.
// Datatypes were floating if signature source was DEFAULT
boolean useDataTypes =
hfunction.getFunction().getSignatureSource() != SourceType.DEFAULT;
try {
HighFunctionDBUtil.commitParamsToDatabase(hfunction, useDataTypes,
SourceType.USER_DEFINED);
if (useDataTypes) {
HighFunctionDBUtil.commitReturnToDatabase(hfunction,
SourceType.USER_DEFINED);
}
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e);
}
catch (InvalidInputException e) {
Msg.showError(this, null, "Parameter Commit Failed", e.getMessage());
}
}
HighFunctionDBUtil.updateDBVariable(highSymbol, null, dt, SourceType.USER_DEFINED);
successfulMod = true;
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e);
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type variable '" + highSymbol.getName() + "': " + e.getMessage());
}
finally {
program.endTransaction(transaction, successfulMod);
}
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
if (tokenAtCursor instanceof ClangFieldToken) {
return false;
}
if (tokenAtCursor.Parent() instanceof ClangReturnType) {
return false;
}
if (!tokenAtCursor.isVariableRef()) {
return false;
}
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
if (highSymbol == null) {
return false;
}
return !highSymbol.isGlobal();
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = context.getProgram();
PluginTool tool = context.getTool();
ClangToken tokenAtCursor = context.getTokenAtCursor();
DataType dataType = null;
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
if (highSymbol == null) {
return;
}
dataType = chooseDataType(tool, program, highSymbol.getDataType());
if (dataType == null) {
return;
}
retypeSymbol(program, highSymbol, tokenAtCursor.getVarnode(), dataType, tool);
}
}

View file

@ -0,0 +1,117 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import docking.widgets.OptionDialog;
import ghidra.app.decompiler.ClangReturnType;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighFunctionDBUtil;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
import ghidra.util.exception.*;
/**
* Action triggered from a specific token in the decompiler window to change the return type of
* the function. The user selected data-type is permanently set as the return type. As the
* return type is part of the function prototype and is forcing on the decompiler,
* this action may trigger input parameters to be committed to the database as well. This situation
* currently triggers a confirmation dialog. If new input parameters need to be committed, their
* name and data-type are taken from the decompiler model.
*/
public class RetypeReturnAction extends AbstractDecompilerAction {
public RetypeReturnAction() {
super("Retype Return");
setPopupMenuData(new MenuData(new String[] { "Retype Return" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
return (tokenAtCursor.Parent() instanceof ClangReturnType);
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = context.getProgram();
PluginTool tool = context.getTool();
ClangToken tokenAtCursor = context.getTokenAtCursor();
HighFunction highFunction = context.getHighFunction();
Function function = highFunction.getFunction();
DataTypeManager dataTypeManager = program.getDataTypeManager();
ClangReturnType parent = (ClangReturnType) tokenAtCursor.Parent();
DataType dataType = chooseDataType(tool, program, parent.getDataType());
if (dataType == null) {
return;
}
boolean commitRequired = checkFullCommit(null, highFunction);
if (commitRequired) {
int resp = OptionDialog.showOptionDialog(tool.getToolFrame(),
"Parameter Commit Required",
"Retyping the return value requires all other parameters to be committed!\nContinue with retype?",
"Continue");
if (resp != OptionDialog.OPTION_ONE) {
return;
}
}
boolean successfulMod = false;
int transactionID = program.startTransaction("Retype return type");
try {
if (dataType.getDataTypeManager() != dataTypeManager) {
dataType = dataTypeManager.resolve(dataType, null);
}
if (commitRequired) {
try {
HighFunctionDBUtil.commitParamsToDatabase(highFunction, true,
SourceType.USER_DEFINED);
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e);
}
catch (InvalidInputException e) {
Msg.showError(this, null, "Parameter Commit Failed", e.getMessage());
}
}
function.setReturnType(dataType, SourceType.USER_DEFINED);
successfulMod = true;
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type return type '" + getName() + "': " + e.getMessage());
}
program.endTransaction(transactionID, successfulMod);
}
}

View file

@ -1,432 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import docking.widgets.OptionDialog;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.datatype.DataTypeSelectionDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
import ghidra.util.exception.*;
public class RetypeVariableAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private final PluginTool tool;
public RetypeVariableAction(PluginTool tool, DecompilerController controller) {
super("Retype Variable");
this.tool = tool;
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Retype Variable" }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK));
}
private void retypeReturnType(DataType dataType, ClangReturnType parent) {
Program program = controller.getProgram();
DataTypeManager dataTypeManager = program.getDataTypeManager();
HighFunction hfunction = getHighFunctionFromReturnTypeToken(parent);
if (hfunction == null) {
return;
}
boolean commitRequired = checkFullCommit(null, hfunction);
if (commitRequired) {
int resp = OptionDialog.showOptionDialog(tool.getToolFrame(),
"Parameter Commit Required",
"Retyping the return value requires all other parameters to be committed!\nContinue with retype?",
"Continue");
if (resp != OptionDialog.OPTION_ONE) {
return;
}
}
Function function = hfunction.getFunction();
boolean successfulMod = false;
int transactionID = program.startTransaction("Retype return type");
try {
if (dataType.getDataTypeManager() != dataTypeManager) {
dataType = dataTypeManager.resolve(dataType, null);
}
if (commitRequired) {
try {
HighFunctionDBUtil.commitParamsToDatabase(hfunction, true,
SourceType.USER_DEFINED);
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e);
}
catch (InvalidInputException e) {
Msg.showError(this, null, "Parameter Commit Failed", e.getMessage());
}
}
function.setReturnType(dataType, SourceType.USER_DEFINED);
successfulMod = true;
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type return type '" + getName() + "': " + e.getMessage());
}
program.endTransaction(transactionID, successfulMod);
}
private HighFunction getHighFunctionFromReturnTypeToken(ClangReturnType returnType) {
Varnode varnode = returnType.getVarnode();
// varnode is null for void return type
if (varnode == null) {
ClangNode proto = returnType.Parent();
if (proto instanceof ClangFuncProto) {
ClangFunction func = ((ClangFuncProto) proto).getClangFunction();
if (func == null) {
return null;
}
return func.getHighFunction();
}
return null;
}
HighVariable high = varnode.getHigh();
return high.getHighFunction();
}
private DataType chooseDataType(DataType currentDataType) {
Program program = controller.getProgram();
DataTypeManager dataTypeManager = program.getDataTypeManager();
DataTypeSelectionDialog chooserDialog = new DataTypeSelectionDialog(tool, dataTypeManager,
Integer.MAX_VALUE, AllowedDataTypes.FIXED_LENGTH);
chooserDialog.setInitialDataType(currentDataType);
tool.showDialog(chooserDialog);
return chooserDialog.getUserChosenDataType();
}
private void retypeVariable(HighVariable var, Varnode exactSpot, DataType dt) {
HighFunction hfunction = var.getHighFunction();
boolean commitRequired = checkFullCommit(var, hfunction);
if (commitRequired) {
// int resp = OptionDialog.showOptionDialog(tool.getToolFrame(),
// "Parameter Commit Required",
// "Retyping a parameter requires all other parameters to be committed!\nContinue with retype?",
// "Continue");
// if (resp != OptionDialog.OPTION_ONE) {
// return;
// }
exactSpot = null; // Don't try to split out if commit is required
}
if (exactSpot != null) { // The user pointed at a particular usage, not just the vardecl
try {
var = hfunction.splitOutMergeGroup(var, exactSpot);
}
catch (PcodeException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed", e.getMessage());
return;
}
}
Program program = controller.getProgram();
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean successfulMod = false;
int transaction = program.startTransaction("Retype Variable");
try {
if (dt.getDataTypeManager() != dataTypeManager) {
dt = dataTypeManager.resolve(dt, null);
}
if (commitRequired) {
// Don't use datatypes of other parameters if the datatypes were floating.
// Datatypes were floating if signature source was DEFAULT
boolean useDataTypes =
hfunction.getFunction().getSignatureSource() != SourceType.DEFAULT;
try {
HighFunctionDBUtil.commitParamsToDatabase(hfunction, useDataTypes,
SourceType.USER_DEFINED);
if (useDataTypes) {
HighFunctionDBUtil.commitReturnToDatabase(hfunction,
SourceType.USER_DEFINED);
}
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e);
}
catch (InvalidInputException e) {
Msg.showError(this, null, "Parameter Commit Failed", e.getMessage());
}
}
HighFunctionDBUtil.updateDBVariable(var, null, dt, SourceType.USER_DEFINED);
successfulMod = true;
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e);
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type variable '" + var.getName() + "': " + e.getMessage());
}
finally {
program.endTransaction(transaction, successfulMod);
}
}
/**
* Return the index of the last component that would be overwritten by a new datatype, if we started overwriting
* with at a specific component. All components, except the first, must be undefined datatypes or we return -1
* @param struct is the structure we are testing
* @param comp is the starting component to overwrite
* @param newtype is the datatype to overwrite with
* @return the index of the last component overwritten or -1
*/
private int getEndComponentIndex(Structure struct, DataTypeComponent comp, DataType newtype) {
int newlen = newtype.getLength();
if (newlen <= 0) {
return -1; // Don't support variable length types
}
DataType curtype = comp.getDataType();
newlen -= curtype.getLength();
int index = comp.getOrdinal();
while (newlen > 0) {
index += 1;
if (index >= struct.getNumComponents()) {
return -1; // Not enough space in the structure
}
comp = struct.getComponent(index);
// String nm = comp.getFieldName();
// if ((nm !=null)&&(nm.length()!=0))
// return -1;
curtype = comp.getDataType();
// if (!Undefined.isUndefined(curtype))
// return -1; // Overlaps non-undefined datatype
if (curtype != DataType.DEFAULT) {
return -1; // Only allow overwrite of placeholder components
}
newlen -= curtype.getLength();
}
return index;
}
private void retypeStructVariable(Structure dt, DataTypeComponent comp, DataType newtype) {
Program program = controller.getProgram();
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean successfulMod = false;
if (comp == null) {
if (!dt.isNotYetDefined()) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Could not find component of '" + dt.getName() + "' to retype");
return;
}
// note if we reach here the offset must be zero, so assume we are inserting newtype
int transaction = program.startTransaction("Retype Structure Field");
try {
// Make sure datatype is using the program's data organization before testing fit
if (newtype.getDataTypeManager() != dataTypeManager) {
newtype = dataTypeManager.resolve(newtype, null);
}
dt.insert(0, newtype);
successfulMod = true;
}
finally {
program.endTransaction(transaction, successfulMod);
}
return;
}
int transaction = program.startTransaction("Retype Structure Field");
try {
// Make sure datatype is using the program's data organization before testing fit
if (newtype.getDataTypeManager() != dataTypeManager) {
newtype = dataTypeManager.resolve(newtype, null);
}
int startind = comp.getOrdinal();
int endind = getEndComponentIndex(dt, comp, newtype);
if (endind < 0) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type structure '" + dt.getName() + "': Datatype did not fit");
return;
}
for (int i = endind; i > startind; --i) { // Clear all but first field
dt.clearComponent(i);
}
dt.replaceAtOffset(comp.getOffset(), newtype, newtype.getLength(), comp.getFieldName(),
comp.getComment());
successfulMod = true;
}
finally {
program.endTransaction(transaction, successfulMod);
}
}
/**
* Compare the given HighFunction's idea of the prototype with the Function's idea.
* Return true if there is a difference. If a specific parameter is being changed,
* it can be passed in indicating that slot can be skipped during the comparison.
* @param var (if not null) is a specific parameter to skip the check for
* @param hfunction is the given HighFunction
* @return true if there is a difference (and a full commit is required)
*/
public static boolean checkFullCommit(HighVariable var, HighFunction hfunction) {
if ((var != null) && (!(var instanceof HighParam))) {
return false;
}
Function function = hfunction.getFunction();
Parameter[] parameters = function.getParameters();
LocalSymbolMap localSymbolMap = hfunction.getLocalSymbolMap();
int numParams = localSymbolMap.getNumParams();
if (numParams != parameters.length) {
return true;
}
for (int i = 0; i < numParams; i++) {
HighParam param = localSymbolMap.getParam(i);
if (param.getSlot() != i) {
return true;
}
VariableStorage storage = param.getStorage();
// Don't compare using the equals method so that DynamicVariableStorage can match
if (0 != storage.compareTo(parameters[i].getVariableStorage())) {
return true;
}
}
return false;
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction();
if (function instanceof UndefinedFunction) {
return false;
}
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
if (tokenAtCursor instanceof ClangFieldToken) {
DataType dt = RenameVariableAction.getStructDataType(tokenAtCursor);
if (dt == null) {
return false;
}
getPopupMenuData().setMenuItemName("Retype Field");
return true;
}
if (tokenAtCursor.Parent() instanceof ClangReturnType) {
getPopupMenuData().setMenuItemName("Retype Return");
return true;
}
if (!tokenAtCursor.isVariableRef()) {
return false;
}
HighVariable variable = tokenAtCursor.getHighVariable();
if (variable == null) {
Address addr = RenameVariableAction.getStorageAddress(tokenAtCursor, controller);
if (addr == null) {
return false;
}
variable = RenameVariableAction.forgeHighVariable(addr, controller);
if (variable == null) {
return false;
}
}
if (variable instanceof HighConstant) {
// getPopupMenuData().setMenuItemName("Retype Constant");
// return true;
}
else if (variable instanceof HighLocal) {
getPopupMenuData().setMenuItemName("Retype Variable");
return true;
}
else if (variable instanceof HighGlobal) {
getPopupMenuData().setMenuItemName("Retype Global");
return true;
}
return false;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
HighVariable variable = null;
Structure struct = null;
DataTypeComponent comp = null;
DataType dataType = null;
if (tokenAtCursor instanceof ClangFieldToken) {
struct = RenameVariableAction.getStructDataType(tokenAtCursor);
int offset = RenameVariableAction.getDataTypeOffset(tokenAtCursor);
if (struct == null) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type structure");
return;
}
if (offset < 0 || offset >= struct.getLength()) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type structure: " + struct.getName());
return;
}
comp = struct.getComponentAt(offset);
if (comp == null) {
dataType = chooseDataType(DataType.DEFAULT);
}
else {
dataType = chooseDataType(comp.getDataType());
}
}
else if (tokenAtCursor.Parent() instanceof ClangReturnType) {
ClangReturnType parent = (ClangReturnType) tokenAtCursor.Parent();
dataType = chooseDataType(parent.getDataType());
if (dataType == null) {
return;
}
retypeReturnType(dataType, parent);
return;
}
else {
variable = tokenAtCursor.getHighVariable();
if (variable == null) {
Address addr = RenameVariableAction.getStorageAddress(tokenAtCursor, controller);
if (addr == null) {
return;
}
variable = RenameVariableAction.forgeHighVariable(addr, controller);
if (variable == null) {
return;
}
}
dataType = chooseDataType(variable.getDataType());
}
if (dataType == null) {
return;
}
if (struct != null) {
retypeStructVariable(struct, comp, dataType);
}
else {
retypeVariable(variable, tokenAtCursor.getVarnode(), dataType);
}
}
}

View file

@ -18,7 +18,6 @@ package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.ClangHighlightController; import ghidra.app.decompiler.component.ClangHighlightController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
/** /**
@ -40,8 +39,7 @@ public class SetSecondaryHighlightAction extends AbstractSetSecondaryHighlightAc
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel panel = context.getDecompilerPanel(); ClangToken token = context.getTokenAtCursor();
ClangToken token = panel.getTokenAtCursor(); context.getDecompilerPanel().addSecondaryHighlight(token);
panel.addSecondaryHighlight(token);
} }
} }

View file

@ -39,8 +39,8 @@ public class SetSecondaryHighlightColorChooserAction extends AbstractSetSecondar
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
ClangToken token = context.getTokenAtCursor();
DecompilerPanel panel = context.getDecompilerPanel(); DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
TokenHighlightColors colors = panel.getSecondaryHighlightColors(); TokenHighlightColors colors = panel.getSecondaryHighlightColors();
List<Color> recentColors = colors.getRecentColors(); List<Color> recentColors = colors.getRecentColors();

View file

@ -20,7 +20,7 @@ import java.util.List;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangFuncNameToken; import ghidra.app.decompiler.ClangFuncNameToken;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*; import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.plugin.core.function.editor.*; import ghidra.app.plugin.core.function.editor.*;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
@ -33,13 +33,9 @@ import ghidra.program.model.symbol.SourceType;
import ghidra.util.UndefinedFunction; import ghidra.util.UndefinedFunction;
public class SpecifyCPrototypeAction extends AbstractDecompilerAction { public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private final PluginTool tool;
public SpecifyCPrototypeAction(PluginTool tool, DecompilerController controller) { public SpecifyCPrototypeAction() {
super("Edit Function Signature"); super("Edit Function Signature");
this.tool = tool;
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Edit Function Signature" }, "Decompile")); setPopupMenuData(new MenuData(new String[] { "Edit Function Signature" }, "Decompile"));
} }
@ -132,17 +128,16 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
} }
/** /**
* @param function is the current function
* @param tokenAtCursor is the user selected token
* @return the currently highlighted function or the currently decompiled * @return the currently highlighted function or the currently decompiled
* function if there isn't one. * function if there isn't one.
*/ */
synchronized Function getFunction() { synchronized Function getFunction(Function function, ClangToken tokenAtCursor) {
// try to look up the function that is at the current cursor location // try to look up the function that is at the current cursor location
// If there isn't one, just use the function we are in. // If there isn't one, just use the function we are in.
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Function function = controller.getFunction();
if (tokenAtCursor instanceof ClangFuncNameToken) { if (tokenAtCursor instanceof ClangFuncNameToken) {
Function tokenFunction = DecompilerUtils.getFunction(controller.getProgram(), Function tokenFunction = DecompilerUtils.getFunction(function.getProgram(),
(ClangFuncNameToken) tokenAtCursor); (ClangFuncNameToken) tokenAtCursor);
if (tokenFunction != null) { if (tokenFunction != null) {
function = tokenFunction; function = tokenFunction;
@ -153,22 +148,24 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction(); Function function = context.getFunction();
if (function instanceof UndefinedFunction) { if (function instanceof UndefinedFunction) {
return false; return false;
} }
return getFunction() != null; return getFunction(function, context.getTokenAtCursor()) != null;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
Function function = getFunction(); Function function =
getFunction(context.getFunction(), context.getTokenAtCursor());
PluginTool tool = context.getTool();
DataTypeManagerService service = tool.getService(DataTypeManagerService.class); DataTypeManagerService service = tool.getService(DataTypeManagerService.class);
FunctionEditorModel model = new FunctionEditorModel(service, function); FunctionEditorModel model = new FunctionEditorModel(service, function);
HighFunction hf = controller.getHighFunction(); HighFunction hf = context.getHighFunction();
FunctionPrototype functionPrototype = hf.getFunctionPrototype(); FunctionPrototype functionPrototype = hf.getFunctionPrototype();
// If editing the decompiled function (i.e., not a subfunction) and function // If editing the decompiled function (i.e., not a subfunction) and function

View file

@ -0,0 +1,403 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.cmd.function.DeleteFunctionCmd;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangVariableToken;
import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.actions.*;
import ghidra.framework.options.Options;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Undefined;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
public class HighSymbolTest extends AbstractDecompilerTest {
@Override
protected String getProgramName() {
return "Winmine__XP.exe.gzf";
}
protected ClangTextField getLineStarting(String val) {
DecompilerPanel panel = provider.getDecompilerPanel();
List<Field> fields = panel.getFields();
for (Field field : fields) {
ClangTextField textField = (ClangTextField) field;
String text = textField.getText().trim();
if (text.startsWith(val)) {
return textField;
}
}
return null;
}
protected ClangTextField getLineContaining(String val) {
DecompilerPanel panel = provider.getDecompilerPanel();
List<Field> fields = panel.getFields();
for (Field field : fields) {
ClangTextField textField = (ClangTextField) field;
String text = textField.getText();
if (text.contains(val)) {
return textField;
}
}
return null;
}
protected HighFunction getHighFunction() {
return provider.getController().getHighFunction();
}
private void renameGlobalVariable(HighSymbol highSymbol, ClangToken tokenAtCursor,
String newName) {
Address addr = highSymbol.getStorage().getMinAddress();
RenameGlobalVariableTask rename = new RenameGlobalVariableTask(provider.getTool(),
highSymbol.getProgram(), provider.getDecompilerPanel(), tokenAtCursor, addr);
assertTrue(rename.isValid(newName));
modifyProgram(p -> {
rename.commit();
});
waitForDecompiler();
}
private void deleteFunction(String address) {
modifyProgram(p -> {
Address addr = p.getAddressFactory().getAddress(address);
DeleteFunctionCmd deleteCmd = new DeleteFunctionCmd(addr);
deleteCmd.applyTo(p);
});
}
private void createFunction(String address) {
modifyProgram(p -> {
Address addr = p.getAddressFactory().getAddress(address);
CreateFunctionCmd createCmd = new CreateFunctionCmd(addr);
createCmd.applyTo(p);
});
}
private void turnOffAnalysis() {
modifyProgram(p -> {
Options options = p.getOptions(Program.ANALYSIS_PROPERTIES);
options.setBoolean("Decompiler Parameter ID", false);
options.setBoolean("Stack", false);
});
}
private void renameVariable(HighSymbol highSymbol, ClangToken tokenAtCursor, String newName) {
RenameVariableTask rename =
new RenameVariableTask(provider.getTool(), highSymbol.getProgram(),
provider.getDecompilerPanel(), tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
assertTrue(rename.isValid(newName));
modifyProgram(p -> {
rename.commit();
});
waitForDecompiler();
}
private void isolateVariable(HighSymbol highSymbol, ClangToken tokenAtCursor, String newName) {
IsolateVariableTask isolate = new IsolateVariableTask(provider.getTool(), program,
provider.getDecompilerPanel(), tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
assertTrue(isolate.isValid(newName));
modifyProgram(p -> {
isolate.commit();
});
waitForDecompiler();
}
private void renameExisting(HighSymbol highSymbol, ClangToken tokenAtCursor, String newName) {
SymbolEntry oldEntry = highSymbol.getFirstWholeMap();
long oldId = highSymbol.getId();
if (highSymbol.isGlobal()) {
renameGlobalVariable(highSymbol, tokenAtCursor, newName);
}
else {
renameVariable(highSymbol, tokenAtCursor, newName);
}
Symbol symbol = program.getSymbolTable().getSymbol(oldId);
assertEquals(symbol.getName(), newName);
HighFunction highFunction = getHighFunction();
HighSymbol newHighSymbol = highFunction.getLocalSymbolMap().getSymbol(oldId);
if (newHighSymbol == null) {
newHighSymbol = highFunction.getGlobalSymbolMap().getSymbol(oldId);
}
assertNotNull(newHighSymbol);
SymbolEntry newEntry = newHighSymbol.getFirstWholeMap();
assertEquals(oldEntry.getStorage(), newEntry.getStorage());
}
@Test
public void testHighSymbol_globalRename() {
decompile("1001b49");
ClangTextField line = getLineStarting("DAT_010056a0");
FieldLocation loc = loc(line.getLineNumber(), 5);
ClangToken token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
HighVariable variable = token.getHighVariable();
assertTrue(variable instanceof HighGlobal);
HighSymbol highSymbol = variable.getSymbol();
assertTrue(highSymbol instanceof HighCodeSymbol);
HighCodeSymbol highCode = (HighCodeSymbol) highSymbol;
CodeSymbol codeSymbol = highCode.getCodeSymbol();
assertNull(codeSymbol); // A DAT_ should not have a permanent CodeSymbol
Data data = highCode.getData();
assertNotNull(data);
assertEquals(data.getAddress().getOffset(), 0x10056a0L);
assertEquals(data.getBaseDataType().getLength(), 2);
renameGlobalVariable(highSymbol, token, "newGlobal");
waitForDecompiler();
line = getLineStarting("newGlobal");
loc = loc(line.getLineNumber(), 5);
token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
variable = token.getHighVariable();
assertTrue(variable instanceof HighGlobal);
highSymbol = variable.getSymbol();
assertTrue(highSymbol instanceof HighCodeSymbol);
highCode = (HighCodeSymbol) highSymbol;
assertTrue(highCode.isGlobal());
assertTrue(highCode.isNameLocked());
assertTrue(highCode.isTypeLocked());
codeSymbol = highCode.getCodeSymbol();
assertNotNull(codeSymbol);
assertEquals(codeSymbol.getID(), highCode.getId());
renameExisting(highSymbol, token, "nameAgain");
}
@Test
public void testHighSymbol_localStackDynamic() {
decompile("10015a6");
ClangTextField line = getLineContaining(" = 0xc;");
FieldLocation loc = loc(line.getLineNumber(), 5);
ClangToken token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
HighVariable variable = token.getHighVariable();
assertTrue(variable instanceof HighLocal);
HighSymbol highSymbol = variable.getSymbol();
SymbolEntry entry = highSymbol.getFirstWholeMap();
assertTrue(entry instanceof MappedEntry); // Comes back initially as untied stack location
int stackCount = 0;
int regCount = 0;
int numInst = variable.getInstances().length;
for (Varnode var : variable.getInstances()) {
if (var.isRegister() || var.isAddrTied()) {
regCount += 1;
}
else if (var.getAddress().isStackAddress()) {
stackCount += 1;
}
}
assertTrue(stackCount > 0); // Verify speculative merge
assertTrue(regCount > 0);
renameVariable(highSymbol, token, "newLocal");
line = getLineStarting("newLocal");
loc = loc(line.getLineNumber(), 5);
token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
variable = token.getHighVariable();
assertTrue(variable instanceof HighLocal);
highSymbol = variable.getSymbol();
entry = highSymbol.getFirstWholeMap();
assertTrue(entry instanceof DynamicEntry); // After rename comes back as HASH
assertTrue(entry.getPCAdress().getOffset() == 0x10016a3);
assertTrue(highSymbol.isNameLocked());
assertFalse(highSymbol.isTypeLocked());
assertEquals(numInst, variable.getInstances().length);
assertEquals(variable.getRepresentative().getAddress().getOffset(), 0xfffffffffffffff0L);
renameExisting(highSymbol, token, "nameAgain");
}
@Test
public void testHighSymbol_localArray() {
decompile("10016ba");
ClangTextField line = getLineStarting("wsprintfW");
FieldLocation loc = loc(line.getLineNumber(), 14);
ClangToken token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
assertNull(token.getHighVariable()); // No HighVariable associated with the token
PcodeOp op = ((ClangVariableToken) token).getPcodeOp();
Address addr = HighFunctionDBUtil.getSpacebaseReferenceAddress(provider.getProgram(), op);
HighFunction highFunction = getHighFunction();
LocalSymbolMap lsym = highFunction.getLocalSymbolMap();
HighSymbol highSymbol = lsym.findLocal(addr, null);
assertEquals(highSymbol.getName(), "local_44");
renameVariable(highSymbol, token, "newArray");
line = getLineStarting("wsprintfW");
token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
assertEquals(token.getText(), "newArray"); // Name has changed
highFunction = getHighFunction();
lsym = highFunction.getLocalSymbolMap();
highSymbol = lsym.findLocal(addr, null);
assertEquals(highSymbol.getName(), "newArray");
assertTrue(highSymbol.isNameLocked());
assertFalse(highSymbol.isTypeLocked());
SymbolEntry entry = highSymbol.getFirstWholeMap();
assertTrue(entry instanceof MappedEntry);
assertEquals(entry.getStorage().getMinAddress(), addr);
assertEquals(entry.getSize(), 64);
renameExisting(highSymbol, token, "nameAgain");
}
@Test
public void testHighSymbol_localRegister() {
decompile("1002607");
ClangTextField line = getLineStarting("iVar");
FieldLocation loc = loc(line.getLineNumber(), 1);
ClangToken token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
HighVariable variable = token.getHighVariable();
assertTrue(variable instanceof HighLocal);
HighSymbol highSymbol = variable.getSymbol();
SymbolEntry entry = highSymbol.getFirstWholeMap();
Address addr = entry.getStorage().getMinAddress();
assertTrue(entry instanceof MappedEntry); // Comes back initially as untied stack location
assertEquals(addr.getAddressSpace().getName(), "register");
renameVariable(highSymbol, token, "newReg");
line = getLineContaining("newReg < 0x40");
assertNotNull(line);
loc = loc(line.getLineNumber(), 11);
token = line.getToken(loc);
HighFunction highFunction = getHighFunction();
highSymbol = highFunction.getLocalSymbolMap().findLocal(addr, entry.getPCAdress());
assertNotNull(highSymbol);
assertEquals(highSymbol.getName(), "newReg");
assertTrue(highSymbol.isNameLocked());
assertFalse(highSymbol.isTypeLocked());
renameExisting(highSymbol, token, "nameAgain");
}
@Test
public void testHighSymbol_parameter() {
decompile("1002d7a");
ClangTextField line = getLineContaining("strlen");
FieldLocation loc = loc(line.getLineNumber(), 20);
ClangToken token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
HighVariable variable = token.getHighVariable();
assertTrue(variable instanceof HighParam);
HighSymbol highSymbol = variable.getSymbol();
assertEquals(highSymbol.getName(), "param_2");
assertTrue(highSymbol.isParameter());
assertEquals(highSymbol.getCategoryIndex(), 1);
SymbolEntry entry = highSymbol.getFirstWholeMap();
Address addr = entry.getStorage().getMinAddress();
assertEquals(addr.getOffset(), 8L);
renameExisting(highSymbol, token, "paramAgain");
}
@Test
public void testHighSymbol_multipleUsePoints() {
decompile("1001915");
ClangTextField line = getLineContaining("0x4e");
FieldLocation loc = loc(line.getLineNumber(), 4);
ClangToken token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
HighVariable variable = token.getHighVariable();
assertTrue(variable instanceof HighLocal);
HighSymbol highSymbol = variable.getSymbol();
SymbolEntry entry = highSymbol.getFirstWholeMap();
assertTrue(entry instanceof MappedEntry);
Address usepoint = token.getVarnode().getPCAddress();
renameVariable(highSymbol, token, "newLocal");
line = getLineContaining("0x4e");
token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
assertEquals(token.getText(), "newLocal"); // Name has changed
variable = token.getHighVariable();
highSymbol = variable.getSymbol();
entry = highSymbol.getFirstWholeMap();
assertEquals(usepoint, entry.getPCAdress()); // Make sure the same usepoint comes back
}
@Test
public void testHighSymbol_freeParameter() {
deleteFunction("10015a6");
turnOffAnalysis();
createFunction("10015a6");
decompile("10015a6");
ClangTextField line = getLineContaining("param_4 +");
FieldLocation loc = loc(line.getLineNumber(), 23);
ClangToken token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
HighSymbol highSymbol = token.getHighVariable().getSymbol();
renameVariable(highSymbol, token, "newParam");
line = getLineContaining("newParam +");
token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
HighVariable variable = token.getHighVariable();
assertTrue(variable instanceof HighParam);
assertEquals(((HighParam) variable).getSlot(), 3);
highSymbol = variable.getSymbol();
assertTrue(highSymbol.isNameLocked());
assertFalse(highSymbol.isTypeLocked());
Function function = highSymbol.getHighFunction().getFunction();
Parameter[] parameters = function.getParameters();
assertEquals(parameters.length, 4);
for (int i = 0; i < 4; ++i) {
DataType dt = parameters[i].getDataType();
assertTrue(Undefined.isUndefined(dt));
assertEquals(dt.getLength(), 4);
}
assertEquals(parameters[3].getName(), "newParam");
}
@Test
public void testHighSymbol_isolate() {
decompile("1002a22");
ClangTextField line = getLineContaining(" >> 1");
FieldLocation loc = loc(line.getLineNumber(), 2);
ClangToken token = line.getToken(loc);
assertTrue(token instanceof ClangVariableToken);
HighVariable variable = token.getHighVariable();
Varnode[] instances = variable.getInstances();
short maxMerge = 0;
for (Varnode vn : instances) {
if (vn.getMergeGroup() > maxMerge) {
maxMerge = vn.getMergeGroup();
}
}
assertEquals(maxMerge, 1); // Make sure there are 2 merge groups
String name = token.getText();
isolateVariable(variable.getSymbol(), token, name);
line = getLineContaining(" >> 1");
token = line.getToken(loc);
variable = token.getHighVariable();
assertEquals(variable.getInstances().length, 1);
HighSymbol highSymbol = variable.getSymbol();
assertEquals(highSymbol.getName(), name);
assertTrue(highSymbol.isNameLocked());
assertTrue(highSymbol.isTypeLocked());
}
}

View file

@ -0,0 +1,123 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* A HighSymbol mapping based on local hashing of the symbol's Varnode within a
* function's syntax tree. The storage address of a temporary Varnode (a Varnode in
* the "unique" address space) is too ephemeral to use as a permanent way to identify it.
* This symbol stores a hash (generated by DynamicHash) that is better suited to
* identifying the Varnode.
*/
public class DynamicEntry extends SymbolEntry {
private long hash; // Hash encoding the specific Varnode
/**
* Constructor for use with restoreXML
* @param sym is the owning HighSymbol
*/
public DynamicEntry(HighSymbol sym) {
super(sym);
}
/**
* Construct given the underlying symbol, defining Address of the Varnode, and the hash value
* @param sym is the given symbol
* @param addr is the defining Address
* @param h is the hash value
*/
public DynamicEntry(HighSymbol sym, Address addr, long h) {
super(sym);
pcaddr = addr;
hash = h;
}
/**
* @return the hash value
*/
public long getHash() {
return hash;
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement addrel = parser.start("hash");
hash = SpecXmlUtils.decodeLong(addrel.getAttribute("val"));
parser.end(addrel);
parseRangeList(parser);
}
@Override
public void saveXml(StringBuilder buf) {
buf.append("<hash val=\"0x").append(Long.toHexString(hash)).append("\"/>");
buildRangelistXML(buf);
}
@Override
public VariableStorage getStorage() {
Program program = symbol.getProgram();
try {
return new VariableStorage(program, AddressSpace.HASH_SPACE.getAddress(getHash()),
getSize());
}
catch (InvalidInputException e) {
throw new AssertException("Unexpected exception", e);
}
}
/**
* Build a new DynamicEntry, given the underlying temporary
* Varnode attached to a symbol. The hash is created from local information in the
* syntax tree near the Varnode.
* @param vn is the underlying Varnode
* @return the new DynamicEntry
*/
public static DynamicEntry build(Varnode vn) {
HighVariable highVariable = vn.getHigh();
HighSymbol highSymbol = highVariable.getSymbol();
HighFunction highFunction = highSymbol.getHighFunction();
DynamicHash dynamicHash = new DynamicHash(vn, highFunction);
DynamicEntry entry =
new DynamicEntry(highSymbol, dynamicHash.getAddress(), dynamicHash.getHash());
return entry;
}
@Override
public int getSize() {
return symbol.type.getLength();
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean isVolatile() {
return false;
}
}

View file

@ -1,127 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* Decompiler symbol whose references are encoded as dynamic hashes into the PcodeSyntaxTree
*
*/
public class DynamicSymbol extends HighSymbol {
public static class Entry {
public final Address pcaddr;
public final long hash;
public final int format;
public Entry(Address addr,long h,int f) {
pcaddr = addr;
hash = h;
format = f;
}
}
private Entry[] refs;
public DynamicSymbol() { // For use with restoreXML
refs = new Entry[0];
}
public DynamicSymbol(String nm,DataType tp,int size,HighFunction func,Address addr,long hash,int format) {
super(nm,tp,size,addr,func);
refs = new Entry[1];
refs[0] = new Entry(addr,hash,format);
}
public long getHash() {
return refs[0].hash;
}
public void addReference(Address addr,long hash,int format) {
Entry[] newrefs = new Entry[refs.length + 1];
for(int i=0;i<refs.length;++i)
newrefs[i] = refs[i];
newrefs[refs.length] = new Entry(addr,hash,format);
refs = newrefs;
if (refs.length == 1)
pcaddr = addr; // Store first address as official pcaddr for symbol
}
protected void buildHashXML(StringBuilder buf) {
for(int i=0;i<refs.length;++i) {
buf.append("<hash val=\"0x").append(Long.toHexString(refs[i].hash)).append("\"/>");
buildRangelistXML(buf, refs[i].pcaddr);
}
}
@Override
public String buildXML() {
String sym = buildSymbolXML(function.getDataTypeManager(), name, type, size,
isTypeLocked(), isNameLocked(), isReadOnly(), false, 0);
StringBuilder res = new StringBuilder();
res.append("<mapsym type=\"dynamic\">\n");
res.append(sym);
buildHashXML(res);
res.append("</mapsym>\n");
return res.toString();
}
@Override
public int restoreXML(XmlPullParser parser, HighFunction func) throws PcodeXMLException {
XmlElement symel = parser.start("symbol");
int symbolId = restoreSymbolXML(symel, func);
type = func.getDataTypeManager().readXMLDataType(parser);
size = type.getLength();
parser.end(symel);
if (size == 0)
throw new PcodeXMLException("Invalid symbol 0-sized data-type: " + type.getName());
while(parser.peek().isStart()) {
long hash = 0;
int format = 0;
XmlElement addrel = parser.start("hash");
hash = SpecXmlUtils.decodeLong(addrel.getAttribute("val"));
format = SpecXmlUtils.decodeInt(symel.getAttribute("format"));
parser.end(addrel);
Address addr = parseRangeList(parser);
addReference(addr,hash,format);
}
return symbolId;
}
public static String buildSymbolXML(PcodeDataTypeManager dtmanage, String nm,
DataType dt, int length, boolean tl, boolean nl, boolean ro, boolean isVolatile,
int format) {
StringBuilder res = new StringBuilder();
res.append("<symbol");
if (nm != null) {
SpecXmlUtils.xmlEscapeAttribute(res, "name", nm);
}
SpecXmlUtils.encodeBooleanAttribute(res, "typelock", tl);
SpecXmlUtils.encodeBooleanAttribute(res, "namelock", nl);
SpecXmlUtils.encodeBooleanAttribute(res, "readonly", ro);
if (isVolatile)
SpecXmlUtils.encodeBooleanAttribute(res, "volatile", true);
res.append(">\n");
res.append(dtmanage.buildTypeRef(dt, length));
res.append("</symbol>\n");
return res.toString();
}
}

View file

@ -21,7 +21,7 @@ import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement; import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser; import ghidra.xml.XmlPullParser;
public class EquateSymbol extends DynamicSymbol { public class EquateSymbol extends HighSymbol {
public static final int FORMAT_DEFAULT = 0; public static final int FORMAT_DEFAULT = 0;
public static final int FORMAT_HEX = 1; public static final int FORMAT_HEX = 1;
@ -33,29 +33,37 @@ public class EquateSymbol extends DynamicSymbol {
private long value; // Value of the equate private long value; // Value of the equate
private int convert; // Non-zero if this is a conversion equate private int convert; // Non-zero if this is a conversion equate
public EquateSymbol() { public EquateSymbol(HighFunction func) {
super(func);
} }
public EquateSymbol(String nm,long val,HighFunction func,Address addr,long hash,int format) { public EquateSymbol(long uniqueId, String nm, long val, HighFunction func, Address addr,
super(nm, DataType.DEFAULT, 1, func, addr, hash, format); long hash) {
super(uniqueId, nm, DataType.DEFAULT, func);
category = 1;
value = val; value = val;
convert = FORMAT_DEFAULT; convert = FORMAT_DEFAULT;
DynamicEntry entry = new DynamicEntry(this, addr, hash);
addMapEntry(entry);
} }
public EquateSymbol(int conv,long val,HighFunction func,Address addr,long hash,int format) { public EquateSymbol(long uniqueId, int conv, long val, HighFunction func, Address addr,
super("", DataType.DEFAULT, 1, func, addr, hash, format); long hash) {
super(uniqueId, "", DataType.DEFAULT, func);
category = 1;
value = val; value = val;
convert = conv; convert = conv;
DynamicEntry entry = new DynamicEntry(this, addr, hash);
addMapEntry(entry);
} }
public long getValue() { return value; } public long getValue() { return value; }
@Override @Override
public int restoreXML(XmlPullParser parser, HighFunction func) throws PcodeXMLException { public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement symel = parser.start("equatesymbol"); XmlElement symel = parser.start("equatesymbol");
int symbolId = restoreSymbolXML(symel, func); restoreXMLHeader(symel);
type = DataType.DEFAULT; type = DataType.DEFAULT;
size = 1;
convert = FORMAT_DEFAULT; convert = FORMAT_DEFAULT;
String formString = symel.getAttribute("format"); String formString = symel.getAttribute("format");
if (formString != null) { if (formString != null) {
@ -78,47 +86,12 @@ public class EquateSymbol extends DynamicSymbol {
parser.start("value"); parser.start("value");
value = SpecXmlUtils.decodeLong(parser.end().getText()); // End <value> tag value = SpecXmlUtils.decodeLong(parser.end().getText()); // End <value> tag
parser.end(symel); parser.end(symel);
if (size == 0) {
throw new PcodeXMLException("Invalid symbol 0-sized data-type: " + type.getName());
}
while(parser.peek().isStart()) {
long hash = 0;
int format = 0;
XmlElement addrel = parser.start("hash");
hash = SpecXmlUtils.decodeLong(addrel.getAttribute("val"));
format = SpecXmlUtils.decodeInt(addrel.getAttribute("format"));
parser.end(addrel);
Address addr = parseRangeList(parser);
addReference(addr,hash,format);
}
return symbolId;
} }
@Override @Override
public String buildXML() { public void saveXML(StringBuilder buf) {
String sym = buildSymbolXML(function.getDataTypeManager(), name, value, isNameLocked(), false, convert); buf.append("<equatesymbol");
StringBuilder res = new StringBuilder(); saveXMLHeader(buf);
res.append("<mapsym type=\"equate\">\n");
res.append(sym);
buildHashXML(res);
res.append("</mapsym>\n");
return res.toString();
}
public static String buildSymbolXML(PcodeDataTypeManager dtmanage, String nm,long value,
boolean nl, boolean isVolatile,int convert) {
StringBuilder res = new StringBuilder();
res.append("<equatesymbol");
if (nm != null) {
SpecXmlUtils.xmlEscapeAttribute(res, "name", nm);
}
SpecXmlUtils.encodeBooleanAttribute(res, "typelock", true);
SpecXmlUtils.encodeBooleanAttribute(res, "namelock", nl);
SpecXmlUtils.encodeSignedIntegerAttribute(res, "cat", 1); // Specify category 1 for the equate
if (isVolatile) {
SpecXmlUtils.encodeBooleanAttribute(res, "volatile", true);
}
if (convert != 0) { if (convert != 0) {
String formString = "hex"; String formString = "hex";
if (convert == FORMAT_HEX) { if (convert == FORMAT_HEX) {
@ -136,14 +109,13 @@ public class EquateSymbol extends DynamicSymbol {
else if (convert == FORMAT_CHAR) { else if (convert == FORMAT_CHAR) {
formString = "char"; formString = "char";
} }
SpecXmlUtils.encodeStringAttribute(res, "format", formString); SpecXmlUtils.encodeStringAttribute(buf, "format", formString);
} }
res.append(">\n"); buf.append(">\n");
res.append(" <value>0x"); buf.append(" <value>0x");
res.append(Long.toHexString(value)); buf.append(Long.toHexString(value));
res.append("</value>\n"); buf.append("</value>\n");
res.append("</equatesymbol>\n"); buf.append("</equatesymbol>\n");
return res.toString();
} }
public static int convertName(String nm,long val) { public static int convertName(String nm,long val) {

View file

@ -210,9 +210,9 @@ public class FunctionPrototype {
* @return the i'th HighParam to this function prototype or null * @return the i'th HighParam to this function prototype or null
* if this prototype is not backed by a LocalSymbolMap * if this prototype is not backed by a LocalSymbolMap
*/ */
public HighParam getParam(int i) { public HighSymbol getParam(int i) {
if (localsyms != null) { if (localsyms != null) {
return localsyms.getParam(i); return localsyms.getParamSymbol(i);
} }
return null; return null;
} }

View file

@ -0,0 +1,159 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import java.util.HashMap;
import java.util.Iterator;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.database.symbol.FunctionSymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
/**
* A container for global symbols in the decompiler's model of a function. It contains
* HighSymbol objects for any symbol accessed by the particular function that is in either
* the global scope or some other global namespace. Currently the container is populated
* indirectly from the HighGlobal objects marshaled back from the decompiler, using either
* the populateSymbol() or newSymbol() methods. HighSymbols are stored by Address and by id,
* which matches the formal SymbolDB id when it exists.
*/
public class GlobalSymbolMap {
private Program program;
private HighFunction func; // Is the full decompiler model of the function to which this belongs
private SymbolTable symbolTable; // Used for cross-referencing with Ghidra's database backed Symbols
private HashMap<Address, HighSymbol> addrMappedSymbols; // Look up symbols by address
private HashMap<Long, HighSymbol> symbolMap; // Look up symbol by id
private long uniqueSymbolId; // Next available symbol id
/**
* Construct a global symbol map attached to a particular function model.
* @param f is the decompiler function model
*/
public GlobalSymbolMap(HighFunction f) {
program = f.getFunction().getProgram();
func = f;
symbolTable = program.getSymbolTable();
addrMappedSymbols = new HashMap<Address, HighSymbol>();
symbolMap = new HashMap<Long, HighSymbol>();
uniqueSymbolId = 0;
}
private void insertSymbol(HighSymbol sym, Address addr) {
long uniqueId = sym.getId();
if ((uniqueId >> 56) == (HighSymbol.ID_BASE >> 56)) {
long val = uniqueId & 0x7fffffff;
if (val > uniqueSymbolId) {
uniqueSymbolId = val;
}
}
symbolMap.put(uniqueId, sym);
addrMappedSymbols.put(addr, sym);
}
/**
* Create a HighSymbol based on the id of the underlying Ghidra Symbol. The Symbol
* is looked up in the SymbolTable and then a HighSymbol is created with the name and
* dataType associated with the Symbol. If a Symbol cannot be found, null is returned.
* @param id is the database id of the CodeSymbol
* @param dataType is the recovered data-type of the symbol
* @param sz is the size in bytes of the desired symbol
* @return the CodeSymbol wrapped as a HighSymbol or null
*/
public HighSymbol populateSymbol(long id, DataType dataType, int sz) {
if ((id >> 56) == (HighSymbol.ID_BASE >> 56)) {
return null; // This is an internal id, not a database key
}
Symbol symbol = symbolTable.getSymbol(id);
if (symbol == null) {
return null;
}
HighSymbol highSym = null;
if (symbol instanceof CodeSymbol) {
if (dataType == null) {
Object dataObj = symbol.getObject();
if (dataObj instanceof Data) {
dataType = ((Data) dataObj).getDataType();
sz = dataType.getLength();
}
else {
dataType = DataType.DEFAULT;
sz = 1;
}
}
highSym = new HighCodeSymbol((CodeSymbol) symbol, dataType, sz, func);
}
else if (symbol instanceof FunctionSymbol) {
highSym = new HighFunctionShellSymbol(id, symbol.getName(), symbol.getAddress(),
func.getDataTypeManager());
}
else {
return null;
}
insertSymbol(highSym, symbol.getAddress());
return highSym;
}
/**
* Create a HighSymbol corresponding to an underlying Data object. The name of the symbol is
* generated dynamically. A symbol is always returned unless the address is invalid,
* in which case null is returned.
* @param id is the id to associate with the new symbol
* @param addr is the address of the Data object
* @param dataType is the recovered data-type of the symbol
* @param sz is the size in bytes of the symbol
* @return the new HighSymbol or null
*/
public HighCodeSymbol newSymbol(long id, Address addr, DataType dataType, int sz) {
if (dataType == null) {
dataType = DataType.DEFAULT;
sz = 1;
}
HighCodeSymbol symbol = new HighCodeSymbol(id, addr, dataType, sz, func);
insertSymbol(symbol, addr);
return symbol;
}
/**
* Retrieve a HighSymbol based on an id
* @param id is the id
* @return the matching HighSymbol or null
*/
public HighSymbol getSymbol(long id) {
return symbolMap.get(id);
}
/**
* Retrieve a HighSymbol based on an Address
* @param addr is the given Address
* @return the matching HighSymbol or null
*/
public HighSymbol getSymbol(Address addr) {
return addrMappedSymbols.get(addr);
}
/**
* Get an iterator over all HighSymbols in this container
* @return the iterator
*/
public Iterator<HighSymbol> getSymbols() {
return symbolMap.values().iterator();
}
}

View file

@ -0,0 +1,157 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.XmlPullParser;
/**
* A global symbol as part of the decompiler's model of a function. This symbol can
* be backed by a formal CodeSymbol, obtained using getCodeSymbol(). This symbol can be backed
* by a formal Data object, obtained using getData(). If there is a backing CodeSymbol, this takes its name,
* otherwise the name is dynamically generated using SymbolUtilities. The data-type attached to this does
* not necessarily match the backing CodeSymbol or Data object.
*/
public class HighCodeSymbol extends HighSymbol {
private CodeSymbol symbol;
/**
* Construct with a backing CodeSymbol. An attempt is made to also find a backing Data object.
* @param sym is the backing CodeSymbol
* @param dataType is the (possibly distinct) data-type associated with the new HighSymbol
* @param sz is the storage size, in bytes, of the symbol
* @param func is the decompiler function model owning the new HighSymbol
*/
public HighCodeSymbol(CodeSymbol sym, DataType dataType, int sz, HighFunction func) {
super(sym.getID(), sym.getName(), dataType, func);
symbol = sym;
setNameLock(true);
setTypeLock(true);
Data data = null;
Object dataObj = symbol.getObject();
if (dataObj instanceof Data) {
data = (Data) dataObj;
}
VariableStorage store;
try {
store = new VariableStorage(symbol.getProgram(), symbol.getAddress(), sz);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
SymbolEntry entry;
if (data != null) {
entry = new MappedDataEntry(this, store, data);
}
else {
entry = new MappedEntry(this, store, null);
}
addMapEntry(entry);
}
/**
* Construct with just a (global) storage address and size. There will be no backing CodeSymbol.
* An attempt is made to find a backing Data object.
* @param id is the id to associate with the new HighSymbol
* @param addr is the starting Address of the symbol storage
* @param dataType is the data-type associated with the new symbol
* @param sz is the size of the symbol storage in bytes
* @param func is the decompiler function model owning the new symbol
*/
public HighCodeSymbol(long id, Address addr, DataType dataType, int sz, HighFunction func) {
super(id, SymbolUtilities.getDynamicName(func.getFunction().getProgram(), addr), dataType,
func);
symbol = null;
setNameLock(true);
setTypeLock(true);
Program program = func.getFunction().getProgram();
Data data = program.getListing().getDataAt(addr);
VariableStorage store;
try {
store = new VariableStorage(program, addr, sz);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
SymbolEntry entry;
if (data != null) {
entry = new MappedDataEntry(this, store, data);
}
else {
entry = new MappedEntry(this, store, null);
}
addMapEntry(entry);
}
/**
* Constructor for HighSymbol which is unattached to a HighFunction
* @param id is the unique id to assign
* @param nm is the name of the symbol
* @param data is an underlying Data object defining the storage and data-type
* @param dtmanage is the data-type manager for XML reference
*/
public HighCodeSymbol(long id, String nm, Data data, PcodeDataTypeManager dtmanage) {
super(id, nm, data.getDataType(), true, true, dtmanage);
Program program = dtmanage.getProgram();
VariableStorage store;
try {
store = new VariableStorage(program, data.getMinAddress(), data.getLength());
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
SymbolEntry entry = new MappedDataEntry(this, store, data);
addMapEntry(entry);
}
@Override
public boolean isGlobal() {
return true;
}
/**
* Get the CodeSymbol backing this, if it exists
* @return the CodeSymbol or null
*/
public CodeSymbol getCodeSymbol() {
return symbol;
}
/**
* Get the Data object backing this, if it exists
* @return the Data object or null
*/
public Data getData() {
SymbolEntry entry = entryList[0];
if (entry instanceof MappedDataEntry) {
return ((MappedDataEntry) entry).getData();
}
return null;
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
super.restoreXML(parser);
symbol = null;
}
}

View file

@ -18,8 +18,11 @@ package ghidra.program.model.pcode;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.AbstractIntegerDataType; import ghidra.program.model.data.AbstractIntegerDataType;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.scalar.Scalar; import ghidra.program.model.scalar.Scalar;
import ghidra.util.exception.InvalidInputException; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/** /**
* *
@ -28,9 +31,17 @@ import ghidra.util.exception.InvalidInputException;
*/ */
public class HighConstant extends HighVariable { public class HighConstant extends HighVariable {
private DynamicSymbol symbol; private HighSymbol symbol;
private Address pcaddr; // null or Address of PcodeOp which defines the representative private Address pcaddr; // null or Address of PcodeOp which defines the representative
/**
* Constructor for use with restoreXml
* @param func is the HighFunction this constant belongs to
*/
public HighConstant(HighFunction func) {
super(func);
}
/** /**
* Construct a constant NOT associated with a symbol * Construct a constant NOT associated with a symbol
* @param name name of variable * @param name name of variable
@ -38,33 +49,14 @@ public class HighConstant extends HighVariable {
* @param vn constant varnode * @param vn constant varnode
* @param pc code unit address where constant is used * @param pc code unit address where constant is used
* @param func the associated high function * @param func the associated high function
* @throws InvalidInputException
*/ */
public HighConstant(String name, DataType type, Varnode vn, Address pc, HighFunction func) public HighConstant(String name, DataType type, Varnode vn, Address pc, HighFunction func) {
throws InvalidInputException {
super(name, type, vn, null, func); super(name, type, vn, null, func);
pcaddr = pc; pcaddr = pc;
} }
/** @Override
* Construct constant associated with a dynamic symbol public HighSymbol getSymbol() {
* @param name name of variable
* @param type data type of variable
* @param vn constant varnode
* @param pc code unit address where constant is used
* @param sym associated dynamic symbol
* @throws InvalidInputException
*/
public HighConstant(String name, DataType type, Varnode vn, Address pc, DynamicSymbol sym)
throws InvalidInputException {
this(name, type, vn, pc, sym.getHighFunction());
symbol = sym;
}
/**
* @return associated dynamic symbol or null
*/
public DynamicSymbol getSymbol() {
return symbol; return symbol;
} }
@ -95,4 +87,35 @@ public class HighConstant extends HighVariable {
return new Scalar(getSize() * 8, value, signed); return new Scalar(getSize() * 8, value, signed);
} }
@Override
public void restoreXml(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("high");
long symref = SpecXmlUtils.decodeLong(el.getAttribute("symref"));
restoreInstances(parser, el);
pcaddr = function.getPCAddress(represent);
if (symref != 0) {
symbol = function.getLocalSymbolMap().getSymbol(symref);
if (symbol == null) {
symbol = function.getGlobalSymbolMap().getSymbol(symref);
}
if (symbol == null) {
GlobalSymbolMap globalMap = function.getGlobalSymbolMap();
Program program = function.getFunction().getProgram();
symbol = globalMap.populateSymbol(symref, null, -1);
if (symbol == null) {
PcodeOp op = ((VarnodeAST) represent).getLoneDescend();
Address addr = HighFunctionDBUtil.getSpacebaseReferenceAddress(program, op);
if (addr != null) {
symbol = globalMap.newSymbol(symref, addr, DataType.DEFAULT, 1);
}
}
}
else if (symbol.getFirstWholeMap() instanceof DynamicEntry) {
name = symbol.getName();
symbol.setHighVariable(this);
}
}
parser.end(el);
}
} }

View file

@ -0,0 +1,67 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
/**
* A symbol, within a decompiler model, for a function without a body in the current Program.
* The Address of this symbol corresponds to the code location that CALL instructions refer to.
* In anticipation of a (not fully resolved) thunking mechanism, this symbol also has a separate
* resolve Address, which is where the decompiler expects to retrieve the detailed Function object.
*/
public class HighExternalSymbol extends HighSymbol {
private Address resolveAddress; // The location of the Function object
/**
* Construct the external reference symbol given a name, the symbol Address, and a
* resolving Address.
* @param nm is the given name
* @param addr is the symbol Address
* @param resolveAddr is the resolve Address
* @param dtmanage is a PcodeDataTypeManager for facilitating XML marshaling
*/
public HighExternalSymbol(String nm, Address addr, Address resolveAddr,
PcodeDataTypeManager dtmanage) {
super(0, nm, DataType.DEFAULT, true, true, dtmanage);
resolveAddress = resolveAddr;
VariableStorage store;
try {
store = new VariableStorage(getProgram(), addr, 1);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
MappedEntry entry = new MappedEntry(this, store, null);
addMapEntry(entry);
}
@Override
public void saveXML(StringBuilder buf) {
buf.append("<externrefsymbol");
if ((name != null) && (name.length() > 0)) { // Give the symbol a name if we can
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name + "_exref");
}
buf.append(">\n");
buf.append(Varnode.buildXMLAddress(resolveAddress));
buf.append("</externrefsymbol>\n");
}
}

View file

@ -21,10 +21,10 @@ import java.util.List;
import org.xml.sax.*; import org.xml.sax.*;
import ghidra.program.database.function.FunctionDB;
import ghidra.program.database.symbol.CodeSymbol; import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException; import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
@ -48,6 +48,7 @@ public class HighFunction extends PcodeSyntaxTree {
private CompilerSpec compilerSpec; private CompilerSpec compilerSpec;
private FunctionPrototype proto; // The high-level prototype associated with the function private FunctionPrototype proto; // The high-level prototype associated with the function
private LocalSymbolMap localSymbols; private LocalSymbolMap localSymbols;
private GlobalSymbolMap globalSymbols;
private List<JumpTable> jumpTables; private List<JumpTable> jumpTables;
private List<DataTypeSymbol> protoOverrides; private List<DataTypeSymbol> protoOverrides;
private boolean showNamespace = true; private boolean showNamespace = true;
@ -67,6 +68,7 @@ public class HighFunction extends PcodeSyntaxTree {
this.compilerSpec = compilerSpec; this.compilerSpec = compilerSpec;
this.showNamespace = showNamespace; this.showNamespace = showNamespace;
localSymbols = new LocalSymbolMap(this, "stack"); localSymbols = new LocalSymbolMap(this, "stack");
globalSymbols = new GlobalSymbolMap(this);
proto = new FunctionPrototype(localSymbols, function); proto = new FunctionPrototype(localSymbols, function);
jumpTables = null; jumpTables = null;
protoOverrides = null; protoOverrides = null;
@ -79,6 +81,17 @@ public class HighFunction extends PcodeSyntaxTree {
return func; return func;
} }
/**
* Get the id with the associated function symbol, if it exists
* @return the id or 0 otherwise
*/
public long getID() {
if (func instanceof FunctionDB) {
return func.getSymbol().getID();
}
return 0;
}
/** /**
* @return get the language parser used to disassemble * @return get the language parser used to disassemble
*/ */
@ -115,12 +128,19 @@ public class HighFunction extends PcodeSyntaxTree {
return localSymbols; return localSymbols;
} }
/**
* @return a map describing global variables accessed by this function
*/
public GlobalSymbolMap getGlobalSymbolMap() {
return globalSymbols;
}
public HighSymbol getMappedSymbol(Address addr, Address pcaddr) { public HighSymbol getMappedSymbol(Address addr, Address pcaddr) {
return localSymbols.findLocal(addr, pcaddr); return localSymbols.findLocal(addr, pcaddr);
} }
@Override @Override
public HighSymbol getSymbol(int symbolId) { public HighSymbol getSymbol(long symbolId) {
return localSymbols.getSymbol(symbolId); return localSymbols.getSymbol(symbolId);
} }
@ -200,41 +220,29 @@ public class HighFunction extends PcodeSyntaxTree {
} }
private void readHighXML(XmlPullParser parser) throws PcodeXMLException { private void readHighXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("high"); XmlElement el = parser.peek();
String classstring = el.getAttribute("class"); String classstring = el.getAttribute("class");
int symref = SpecXmlUtils.decodeInt(el.getAttribute("symref")); HighVariable var;
int repref = SpecXmlUtils.decodeInt(el.getAttribute("repref")); switch (classstring.charAt(0)) {
Varnode rep = getRef(repref); case 'o':
if (rep == null) { var = new HighOther(this);
throw new PcodeXMLException("Undefined varnode reference"); break;
case 'g':
var = new HighGlobal(this);
break;
case 'l':
var = new HighLocal(this);
break;
case 'p':
var = new HighParam(this);
break;
case 'c':
var = new HighConstant(this);
break;
default:
throw new PcodeXMLException("Unknown HighVariable class string: " + classstring);
} }
var.restoreXml(parser);
DataType type = null;
ArrayList<Varnode> vnlist = new ArrayList<Varnode>();
int sz = -1;
if (parser.peek().isStart()) {
type = getDataTypeManager().readXMLDataType(parser);
}
if (type == null) {
throw new PcodeXMLException("Missing <type> for HighVariable");
}
// TODO: I'm not sure the decompiler's type size is preserved
// by the conversion to a GHIDRA type
sz = type.getLength();
while (parser.peek().isStart()) {
Varnode vn = Varnode.readXML(parser, this);
vnlist.add(vn);
}
Varnode[] vnarray = new Varnode[vnlist.size()];
vnlist.toArray(vnarray);
// VARDO: does rep varnode size differ from type length ?
newHigh(symref, type, sz, vnarray, rep, classstring);
parser.end(el);
} }
private void readHighlistXML(XmlPullParser parser) throws PcodeXMLException { private void readHighlistXML(XmlPullParser parser) throws PcodeXMLException {
@ -320,78 +328,7 @@ public class HighFunction extends PcodeSyntaxTree {
parser.end(el); parser.end(el);
} }
private HighVariable newHigh(int symref, DataType tp, int sz, Varnode[] inst, Varnode rep, protected Address getPCAddress(Varnode rep) {
String classstring) throws PcodeXMLException {
try {
HighVariable var = null;
if (classstring.equals("local")) {
HighSymbol sym = null;
if (symref != 0) {
sym = localSymbols.getSymbol(symref);
}
if (sym != null) {
var = sym.getHighVariable();
}
if (var == null) {
if (sym instanceof DynamicSymbol) {
// establish HighLocal for DynamicSymbol
var = new HighLocal(tp, rep, inst, sym.getPCAddress(), sym);
sym.setHighVariable(var);
}
else {
// The variable may be a partial, in which case
// we treat it as special
var = new HighOther(tp, rep, inst, getPCAddress(rep), this);
}
}
}
else if (classstring.equals("constant")) {
HighSymbol sym = null;
if (symref != 0) {
sym = localSymbols.getSymbol(symref);
if (sym != null) {
var = sym.getHighVariable();
}
}
if (var == null) {
if (sym instanceof DynamicSymbol) {
var = new HighConstant(sym.getName(), tp, rep, getPCAddress(rep),
(DynamicSymbol) sym);
sym.setHighVariable(var);
}
else {
var = new HighConstant(null, tp, rep, getPCAddress(rep), this);
}
}
}
else if (classstring.equals("global")) {
// TODO: should we have access to global symbols
var = new HighGlobal(null, tp, rep, inst, this);
}
else if (classstring.equals("other")) {
// TODO: How do these compare with local ??
var = new HighOther(tp, rep, inst, getPCAddress(rep), this);
}
else {
throw new PcodeXMLException("Bad class string: " + classstring);
}
if (rep.getSize() == var.getSize()) {
var.attachInstances(inst, rep);
}
else { // Make sure varnodes are linked to HighVariable even if not formal instances, why do we do this???
for (Varnode element : inst) {
((VarnodeAST) element).setHigh(var);
}
}
var.setHighOnInstances();
return var;
}
catch (InvalidInputException e) {
throw new PcodeXMLException("Bad storage node", e);
}
}
private Address getPCAddress(Varnode rep) {
Address pcaddr = null; Address pcaddr = null;
if (!rep.isAddrTied()) { if (!rep.isAddrTied()) {
pcaddr = rep.getPCAddress(); pcaddr = rep.getPCAddress();
@ -469,9 +406,8 @@ public class HighFunction extends PcodeSyntaxTree {
// Note that we don't need to distinguish between unique,register,ram etc. and don't // Note that we don't need to distinguish between unique,register,ram etc. and don't
// need to separate out first use versus mapped use. When the high local is written // need to separate out first use versus mapped use. When the high local is written
// to database, these issues will be resolved at that point. // to database, these issues will be resolved at that point.
sym = localSymbols.newMappedSymbol(highloc.getName(), highloc.getDataType(), sym = localSymbols.newMappedSymbol(0, highloc.getName(), highloc.getDataType(),
new VariableStorage(func.getProgram(), vn), vn.getPCAddress(), -1, new VariableStorage(func.getProgram(), vn), vn.getPCAddress(), -1);
vn.hashCode());
reslocal = new HighLocal(highloc.getDataType(), vn, null, vn.getPCAddress(), sym); reslocal = new HighLocal(highloc.getDataType(), vn, null, vn.getPCAddress(), sym);
resremain = highloc; // Keep remaining varnodes in old high resremain = highloc; // Keep remaining varnodes in old high
@ -499,15 +435,19 @@ public class HighFunction extends PcodeSyntaxTree {
* this doesn't need to be strictly accurate as it is only used to associate the function with * this doesn't need to be strictly accurate as it is only used to associate the function with
* addresses near its entry point. * addresses near its entry point.
* *
* @param id is the id associated with the function symbol
* @param entryPoint pass null to use the function entryPoint, pass an address to force an entry point * @param entryPoint pass null to use the function entryPoint, pass an address to force an entry point
* @param size describes how many bytes the function occupies as code * @param size describes how many bytes the function occupies as code
* @return the XML string * @return the XML string
*/ */
public String buildFunctionXML(Address entryPoint, int size) { public String buildFunctionXML(long id, Address entryPoint, int size) {
// Functions aren't necessarily contiguous with the smallest address being the entry point // Functions aren't necessarily contiguous with the smallest address being the entry point
// So size needs to be smaller than size of the contiguous chunk containing the entry point // So size needs to be smaller than size of the contiguous chunk containing the entry point
StringBuilder resBuf = new StringBuilder(); StringBuilder resBuf = new StringBuilder();
resBuf.append("<function"); resBuf.append("<function");
if (id != 0) {
SpecXmlUtils.encodeUnsignedIntegerAttribute(resBuf, "id", id);
}
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", func.getName(showNamespace)); SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", func.getName(showNamespace));
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size); SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
if (func.isInline()) { if (func.isInline()) {
@ -558,26 +498,6 @@ public class HighFunction extends PcodeSyntaxTree {
return resBuf.toString(); return resBuf.toString();
} }
/**
* Build the XML representation of only the shell function info not including everything known
* about the function.
*
* @param name name of the function
* @param addr address the function is located at
*
* @return the XML string
*/
static public String buildFunctionShellXML(String name, Address addr) {
StringBuilder resBuf = new StringBuilder();
resBuf.append("<function");
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", name);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", 1);
resBuf.append(">\n");
resBuf.append(Varnode.buildXMLAddress(addr));
resBuf.append("</function>\n");
return resBuf.toString();
}
public static ErrorHandler getErrorHandler(final Object errOriginator, public static ErrorHandler getErrorHandler(final Object errOriginator,
final String targetName) { final String targetName) {
return new ErrorHandler() { return new ErrorHandler() {

View file

@ -17,8 +17,7 @@ package ghidra.program.model.pcode;
import java.util.*; import java.util.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.*;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
@ -41,8 +40,10 @@ public class HighFunctionDBUtil {
public static final String AUTO_CAT = "/auto_proto"; // Category for auto generated prototypes public static final String AUTO_CAT = "/auto_proto"; // Category for auto generated prototypes
/** /**
* Commit function return to the underlying database. * Commit the decompiler's version of the function return data-type to the database.
* @param highFunction * The decompiler's version of the prototype model is committed as well
* @param highFunction is the decompiler's model of the function
* @param source is the desired SourceType for the commit
*/ */
public static void commitReturnToDatabase(HighFunction highFunction, SourceType source) { public static void commitReturnToDatabase(HighFunction highFunction, SourceType source) {
try { try {
@ -85,7 +86,9 @@ public class HighFunctionDBUtil {
List<Parameter> params = getParameters(highFunction, useDataTypes); List<Parameter> params = getParameters(highFunction, useDataTypes);
commitParamsToDatabase(function, highFunction.getFunctionPrototype(), params, FunctionPrototype functionPrototype = highFunction.getFunctionPrototype();
String modelName = (functionPrototype != null) ? functionPrototype.getModelName() : null;
commitParamsToDatabase(function, modelName, params,
highFunction.getFunctionPrototype().isVarArg(), true, source); highFunction.getFunctionPrototype().isVarArg(), true, source);
} }
@ -98,7 +101,7 @@ public class HighFunctionDBUtil {
List<Parameter> params = new ArrayList<Parameter>(); List<Parameter> params = new ArrayList<Parameter>();
int paramCnt = symbolMap.getNumParams(); int paramCnt = symbolMap.getNumParams();
for (int i = 0; i < paramCnt; ++i) { for (int i = 0; i < paramCnt; ++i) {
HighParam param = symbolMap.getParam(i); HighSymbol param = symbolMap.getParamSymbol(i);
String name = param.getName(); String name = param.getName();
DataType dataType; DataType dataType;
if (useDataTypes) { if (useDataTypes) {
@ -114,22 +117,26 @@ public class HighFunctionDBUtil {
} }
/** /**
* Commit the specified parameter list to the specified function. * Commit a specified set of parameters for the given function to the database.
* @param function * The name, data-type, and storage is committed for each parameter. The parameters are
* @param params * provided along with a formal PrototypeModel. If the parameters fit the model, they are
* committed using "dynamic" storage. Otherwise, they are committed using "custom" storage.
* @param function is the Function being modified
* @param modelName is the name of the underlying PrototypeModel
* @param params is the formal list of parameter objects
* @param hasVarArgs is true if the prototype can take variable arguments
* @param renameConflicts if true any name conflicts will be resolved * @param renameConflicts if true any name conflicts will be resolved
* by renaming the conflicting local variable/label * by renaming the conflicting local variable/label
* @param source source type * @param source source type
* @throws DuplicateNameException if commit of parameters caused conflict with other * @throws DuplicateNameException if commit of parameters caused conflict with other
* local variable/label. Should not occur if renameConflicts is true. * local variable/label. Should not occur if renameConflicts is true.
* @throws InvalidInputException * @throws InvalidInputException for invalid variable names or for parameter data-types that aren't fixed length
* @throws DuplicateNameException is there are collisions between variable names in the function's scope
*/ */
public static void commitParamsToDatabase(Function function, FunctionPrototype prototype, public static void commitParamsToDatabase(Function function, String modelName,
List<Parameter> params, boolean hasVarArgs, boolean renameConflicts, SourceType source) List<Parameter> params, boolean hasVarArgs, boolean renameConflicts, SourceType source)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {
String modelName = prototype != null ? prototype.getModelName() : null;
try { try {
function.updateFunction(modelName, null, params, function.updateFunction(modelName, null, params,
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, source); FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, source);
@ -181,6 +188,7 @@ public class HighFunctionDBUtil {
break; break;
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
// Continue looping until we get a unique symbol name
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
break; break;
@ -189,9 +197,10 @@ public class HighFunctionDBUtil {
} }
/** /**
* Commit all local variables to the underlying database. * Commit local variables from the decompiler's model of the function to the database.
* @param highFunction * This does NOT include formal function parameters.
* @param source source type * @param highFunction is the decompiler's model of the function
* @param source is the desired SourceType for the commit
*/ */
public static void commitLocalsToDatabase(HighFunction highFunction, SourceType source) { public static void commitLocalsToDatabase(HighFunction highFunction, SourceType source) {
@ -202,23 +211,22 @@ public class HighFunctionDBUtil {
Iterator<HighSymbol> iter = highFunction.getLocalSymbolMap().getSymbols(); Iterator<HighSymbol> iter = highFunction.getLocalSymbolMap().getSymbols();
while (iter.hasNext()) { while (iter.hasNext()) {
HighSymbol sym = iter.next(); HighSymbol sym = iter.next();
HighVariable high = sym.getHighVariable(); if (sym.isParameter() || sym.isGlobal()) {
if ((high instanceof HighParam) || !(high instanceof HighLocal)) {
continue; continue;
} }
String name = sym.getName();
HighLocal local = (HighLocal) high;
String name = local.getName();
try { try {
Variable var = clearConflictingLocalVariables(local); Variable var =
clearConflictingLocalVariables(function, sym.getStorage(), sym.getPCAddress());
if (var == null) { if (var == null) {
var = createLocalVariable(local, null, null, source); var = createLocalVariable(function, sym.getDataType(), sym.getStorage(),
sym.getPCAddress(), source);
if (name != null) { if (name != null) {
var.setName(name, source); var.setName(name, source);
} }
} }
else { else {
var.setDataType(local.getDataType(), local.getStorage(), false, source); var.setDataType(sym.getDataType(), sym.getStorage(), false, source);
var.setName(name, source); var.setName(name, source);
} }
} }
@ -230,28 +238,27 @@ public class HighFunctionDBUtil {
} }
/** /**
* Create a local DB variable with a default name * Create a local DB variable with a default name. Storage and data-type for the variable
* @param local * are provided explicitly.
* @param dt data type or null to use local data type defined by local high variable * @param function is the function owning the new variable
* @param storage storage or null to use storage defined by local high variable * @param dt is the given data-type
* @param source * @param storage is the given storage
* @return * @param pcAddr is point where the variable is instantiated or null
* @throws InvalidInputException * @param source is the source type of the new variable
* @throws DuplicateNameException * @return the new local variable
* @throws InvalidInputException is a valid variable can't be created
*/ */
private static Variable createLocalVariable(HighLocal local, DataType dt, private static Variable createLocalVariable(Function function, DataType dt,
VariableStorage storage, SourceType source) throws InvalidInputException { VariableStorage storage, Address pcAddr, SourceType source)
Function function = local.getHighFunction().getFunction(); throws InvalidInputException {
Program program = function.getProgram(); Program program = function.getProgram();
if (storage == null || storage.isUniqueStorage()) { int firstUseOffset = 0;
storage = local.getStorage(); if (pcAddr != null) {
firstUseOffset = (int) pcAddr.subtract(function.getEntryPoint());
} }
if (dt == null) { Variable var = new LocalVariableImpl(null, firstUseOffset, dt, storage, program);
dt = local.getDataType();
}
Variable var = new LocalVariableImpl(null, local.getFirstUseOffset(), dt, storage, program);
try { try {
var = function.addLocalVariable(var, SourceType.ANALYSIS); var = function.addLocalVariable(var, source);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception with default name", e); throw new AssertException("Unexpected exception with default name", e);
@ -259,8 +266,8 @@ public class HighFunctionDBUtil {
Register reg = var.getRegister(); Register reg = var.getRegister();
if (reg != null) { if (reg != null) {
program.getReferenceManager().addRegisterReference(local.getPCAddress(), -1, reg, program.getReferenceManager().addRegisterReference(pcAddr, -1, reg, RefType.WRITE,
RefType.WRITE, source); source);
} }
return var; return var;
@ -283,47 +290,99 @@ public class HighFunctionDBUtil {
long hash = var.getFirstStorageVarnode().getOffset(); long hash = var.getFirstStorageVarnode().getOffset();
Iterator<HighSymbol> symbols = highFunction.getLocalSymbolMap().getSymbols(); Iterator<HighSymbol> symbols = highFunction.getLocalSymbolMap().getSymbols();
while (symbols.hasNext()) { while (symbols.hasNext()) {
HighVariable high = symbols.next().getHighVariable(); HighSymbol symbol = symbols.next();
if (!(high instanceof HighLocal)) { SymbolEntry entry = symbol.getFirstWholeMap();
if (!(entry instanceof DynamicEntry)) {
continue; continue;
} }
// Note: assumes there is only one hash method used for unique locals // Note: assumes there is only one hash method used for unique locals
if (((HighLocal) high).buildDynamicHash() == hash) { if (((DynamicEntry) entry).getHash() == hash) {
return true; if (symbol.getHighVariable() != null) {
return true; // Hash successfully attached to a variable
}
} }
} }
return false; return false;
} }
/**
* Given a particular seed Variable, find the set of local Variables that are intended to be
* merged containing that seed. The result will be an array with at least the seed variable in it.
* @param function is the function containing the local variables
* @param seed is the seed local variable
* @return an array of all Variables intended to be merged.
*/
private static Variable[] gatherMergeSet(Function function, Variable seed) {
TreeMap<String, Variable> nameMap = new TreeMap<String, Variable>();
for (Variable var : function.getAllVariables()) {
nameMap.put(var.getName(), var);
}
String baseName = seed.getName();
int pos = baseName.lastIndexOf('$');
if (pos >= 0) {
baseName = baseName.substring(0, pos);
}
DataType dataType = seed.getDataType();
Variable currentVar = nameMap.get(baseName);
int index = 0;
boolean sawSeed = false;
ArrayList<Variable> mergeArray = new ArrayList<Variable>();
for (;;) {
if (currentVar == null) {
break;
}
if (!currentVar.getDataType().equals(dataType)) {
break;
}
if (index != 0 && currentVar instanceof Parameter) {
break;
}
if (index != 0 && currentVar.hasStackStorage()) {
break;
}
if (currentVar == seed) {
sawSeed = true;
}
mergeArray.add(currentVar);
index += 1;
String newName = baseName + '$' + Integer.toString(index);
currentVar = nameMap.get(newName);
}
Variable[] res;
if (!sawSeed) {
res = new Variable[1];
res[0] = seed;
}
else {
res = new Variable[mergeArray.size()];
mergeArray.toArray(res);
}
return res;
}
/** /**
* Low-level routine for clearing any variables in the * Low-level routine for clearing any variables in the
* database which conflict with this variable and return * database which conflict with this variable and return
* one of them for re-use. The returned variable still * one of them for re-use. The returned variable still
* exists within the function at the same first-use-offset. * exists within the function at the same first-use-offset.
* @param function is the function containing the local variables
* @param storage is the storage area to clear
* @param pcAddr is the point of use
* @return existing variable with identical storage and first-use offset or null * @return existing variable with identical storage and first-use offset or null
*/ */
private static Variable clearConflictingLocalVariables(HighLocal local) { private static Variable clearConflictingLocalVariables(Function function,
VariableStorage storage, Address pcAddr) {
if (local instanceof HighParam) { int firstUseOffset = 0;
throw new IllegalArgumentException(); if (pcAddr != null) {
firstUseOffset = (int) pcAddr.subtract(function.getEntryPoint());
} }
if (storage.isHashStorage()) {
HighFunction highFunction = local.getHighFunction(); long hashVal = storage.getFirstVarnode().getOffset();
Function func = highFunction.getFunction(); for (Variable ul : function.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER)) {
HighSymbol symbol = local.getSymbol();
VariableStorage storage = local.getStorage();
int firstUseOffset = local.getFirstUseOffset();
if (symbol instanceof DynamicSymbol || storage.isUniqueStorage()) {
if (!(symbol instanceof DynamicSymbol)) {
return null;
}
DynamicSymbol dynamicSym = (DynamicSymbol) symbol;
for (Variable ul : func.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER)) {
// Note: assumes there is only one hash method used for unique locals // Note: assumes there is only one hash method used for unique locals
if (ul.getFirstStorageVarnode().getOffset() == dynamicSym.getHash()) { if (ul.getFirstStorageVarnode().getOffset() == hashVal) {
return ul; return ul;
} }
} }
@ -331,7 +390,7 @@ public class HighFunctionDBUtil {
} }
Variable matchingVariable = null; Variable matchingVariable = null;
for (Variable otherVar : func.getLocalVariables()) { for (Variable otherVar : function.getLocalVariables()) {
if (otherVar.getFirstUseOffset() != firstUseOffset) { if (otherVar.getFirstUseOffset() != firstUseOffset) {
// other than parameters we will have a hard time identifying // other than parameters we will have a hard time identifying
// local variable conflicts due to differences in scope (i.e., first-use) // local variable conflicts due to differences in scope (i.e., first-use)
@ -345,7 +404,7 @@ public class HighFunctionDBUtil {
matchingVariable = otherVar; matchingVariable = otherVar;
continue; continue;
} }
func.removeVariable(otherVar); function.removeVariable(otherVar);
} }
} }
@ -353,20 +412,20 @@ public class HighFunctionDBUtil {
} }
/** /**
* Get database parameter which corresponds to HighParam, where we anticipate that * Get database parameter which corresponds to the given symbol, where we anticipate that
* the parameter will be modified to match the HighParam. The entire prototype is * the parameter will be modified to match the symbol. The entire prototype is
* committed to the database if necessary. An exception is thrown if a modifiable parameter * committed to the database if necessary. An exception is thrown if a modifiable parameter
* can't be found/created. * can't be found/created.
* @param param is the HighParam describing the desired function parameter * @param param is the HighSymbol describing the desired function parameter
* @return the matching parameter that can be modified * @return the matching parameter that can be modified
* @throws InvalidInputException if the desired parameter cannot be modified * @throws InvalidInputException if the desired parameter cannot be modified
*/ */
private static Parameter getDatabaseParameter(HighParam param) throws InvalidInputException { private static Parameter getDatabaseParameter(HighSymbol param) throws InvalidInputException {
HighFunction highFunction = param.getHighFunction(); HighFunction highFunction = param.getHighFunction();
Function function = highFunction.getFunction(); Function function = highFunction.getFunction();
int slot = param.getSlot(); int slot = param.getCategoryIndex();
Parameter[] parameters = function.getParameters(); Parameter[] parameters = function.getParameters();
if (slot < parameters.length) { if (slot < parameters.length) {
if (parameters[slot].isAutoParameter()) { if (parameters[slot].isAutoParameter()) {
@ -393,10 +452,9 @@ public class HighFunctionDBUtil {
} }
/** /**
* Retype the specified variable in the database. All parameters may be flushed * Rename and/or retype the specified variable in the database. All parameters may be flushed
* to the database if typed parameter inconsistency detected. * to the database if typed parameter inconsistency detected.
* Only variable types HighParam, HighLocal and HighGlobal are supported. * @param highSymbol is the symbol being updated
* @param variable
* @param name new variable name or null to use retain current variable name * @param name new variable name or null to use retain current variable name
* @param dataType newly assigned data type or null to retain current variable datatype. * @param dataType newly assigned data type or null to retain current variable datatype.
* Only a fixed-length data type may be specified. If size varies from the current size, * Only a fixed-length data type may be specified. If size varies from the current size,
@ -408,10 +466,10 @@ public class HighFunctionDBUtil {
* variable/label within the function's namespace * variable/label within the function's namespace
* @throws UnsupportedOperationException if unsupported variable type is specified * @throws UnsupportedOperationException if unsupported variable type is specified
*/ */
public static void updateDBVariable(HighVariable variable, String name, DataType dataType, public static void updateDBVariable(HighSymbol highSymbol, String name, DataType dataType,
SourceType source) throws InvalidInputException, DuplicateNameException { SourceType source) throws InvalidInputException, DuplicateNameException {
HighFunction highFunction = variable.getHighFunction(); HighFunction highFunction = highSymbol.getHighFunction();
Function function = highFunction.getFunction(); Function function = highFunction.getFunction();
Program program = function.getProgram(); Program program = function.getProgram();
@ -423,15 +481,14 @@ public class HighFunctionDBUtil {
"Data type is not fixed-length: " + dataType.getName()); "Data type is not fixed-length: " + dataType.getName());
} }
resized = (dataType.getLength() != variable.getSize()); resized = (dataType.getLength() != highSymbol.getSize());
} }
boolean isRename = name != null; boolean isRename = name != null;
if (variable instanceof HighParam) { if (highSymbol.isParameter()) {
HighParam param = (HighParam) variable; Parameter dbParam = getDatabaseParameter(highSymbol);
Parameter dbParam = getDatabaseParameter(param); VariableStorage storage = highSymbol.getStorage();
VariableStorage storage = param.getStorage();
if (dataType != null) { if (dataType != null) {
if (resized && function.hasCustomVariableStorage()) { if (resized && function.hasCustomVariableStorage()) {
VariableStorage newStorage = VariableStorage newStorage =
@ -446,43 +503,76 @@ public class HighFunctionDBUtil {
dbParam.setName(name, source); dbParam.setName(name, source);
} }
} }
else if (variable instanceof HighLocal) { else if (!highSymbol.isGlobal()) {
HighLocal local = (HighLocal) variable; Variable[] varList = null;
VariableStorage storage = local.getStorage(); VariableStorage storage = highSymbol.getStorage();
boolean usesHashStorage = storage.isHashStorage(); Address pcAddr = highSymbol.getPCAddress();
HighVariable tmpHigh = highSymbol.getHighVariable();
Variable var = clearConflictingLocalVariables(local); if (!storage.isHashStorage() && tmpHigh != null &&
if (dataType == null) { tmpHigh.requiresDynamicStorage()) {
DynamicEntry entry = DynamicEntry.build(tmpHigh.getRepresentative());
storage = entry.getStorage();
pcAddr = entry.getPCAdress(); // The address may change from original Varnode
}
else {
Variable var = clearConflictingLocalVariables(function, storage, pcAddr);
if (var != null) { if (var != null) {
dataType = var.getDataType(); // Use preexisting datatype if it fits in desired storage if (!resized) {
varList = gatherMergeSet(function, var); // Cannot resize a whole multi-merge
}
else {
varList = new Variable[1];
varList[0] = var;
}
}
}
boolean usesHashStorage = storage.isHashStorage();
if (dataType == null) {
if (varList != null) {
dataType = varList[0].getDataType(); // Use preexisting datatype if it fits in desired storage
} }
else { else {
dataType = Undefined.getUndefinedDataType(variable.getSize()); dataType = Undefined.getUndefinedDataType(highSymbol.getSize());
dataType = dataType.clone(program.getDataTypeManager()); dataType = dataType.clone(program.getDataTypeManager());
} }
} }
if (resized) { if (resized) {
if (usesHashStorage) { if (usesHashStorage) {
throw new InvalidInputException( throw new InvalidInputException(
"Variable size (" + local.getSize() + ") may not be changed: type '" + "Variable size (" + highSymbol.getSize() + ") may not be changed: type '" +
dataType.getName() + "' length is " + dataType.getLength()); dataType.getName() + "' length is " + dataType.getLength());
} }
storage = VariableUtilities.resizeStorage(storage, dataType, true, function); storage = VariableUtilities.resizeStorage(storage, dataType, true, function);
} }
if (var == null) { if (varList == null) {
var = createLocalVariable(local, dataType, storage, source); Variable var = createLocalVariable(function, dataType, storage, pcAddr, source);
varList = new Variable[1];
varList[0] = var;
}
else if (resized) {
// Set resized data-type on existing Variable
varList[0].setDataType(dataType, storage, true, source);
} }
else { else {
// fixup reused variable // Set data-type on existing merge set
var.setDataType(dataType, storage, true, source); for (Variable var : varList) {
if (name == null) { var.setDataType(dataType, source);
name = local.getName(); // must update name if not specified
} }
} }
if (name == null) {
name = highSymbol.getName(); // must update name if not specified
}
Variable renameVar = null;
try { try {
// must set/correct name int index = 0;
var.setName(name, source); String curName = name;
for (Variable var : varList) {
renameVar = var;
var.setName(curName, source);
index += 1;
curName = name + '$' + Integer.toString(index);
}
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
if (isRename) { if (isRename) {
@ -493,36 +583,35 @@ public class HighFunctionDBUtil {
Msg.error(HighFunctionDBUtil.class, Msg.error(HighFunctionDBUtil.class,
"Name conflict while naming local variable: " + function.getName() + ":" + "Name conflict while naming local variable: " + function.getName() + ":" +
name); name);
var.setName(null, SourceType.DEFAULT); renameVar.setName(null, SourceType.DEFAULT);
} }
catch (DuplicateNameException e1) { catch (DuplicateNameException e1) {
throw new AssertException("Unexpected exception with default name", e); throw new AssertException("Unexpected exception with default name", e);
} }
} }
} }
else if (variable instanceof HighGlobal) { else { // A global symbol
VariableStorage storage = variable.getStorage(); VariableStorage storage = highSymbol.getStorage();
if (!storage.isMemoryStorage()) { if (!storage.isMemoryStorage()) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Database supports global memory variables only"); "Database supports global memory variables only");
} }
HighGlobal global = (HighGlobal) variable;
if (name == null) { if (name == null) {
name = global.getName(); name = highSymbol.getName();
if (name != null && SymbolUtilities.isDynamicSymbolPattern(name, true)) { if (name != null && SymbolUtilities.isDynamicSymbolPattern(name, true)) {
name = null; name = null;
} }
} }
if (dataType != null) { if (dataType != null) {
setGlobalDataType(global, dataType); setGlobalDataType(highSymbol, dataType);
} }
if (name != null) { if (name != null) {
try { try {
setGlobalName((HighGlobal) variable, variable.getName(), source); setGlobalName(highSymbol, highSymbol.getName(), source);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
if (isRename) { if (isRename) {
@ -531,13 +620,9 @@ public class HighFunctionDBUtil {
} }
} }
} }
else {
throw new UnsupportedOperationException(
"Database support not provided for " + variable.getClass().getSimpleName());
}
} }
private static void setGlobalName(HighGlobal global, String name, SourceType source) private static void setGlobalName(HighSymbol global, String name, SourceType source)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {
Program program = global.getHighFunction().getFunction().getProgram(); Program program = global.getHighFunction().getFunction().getProgram();
VariableStorage storage = global.getStorage(); VariableStorage storage = global.getStorage();
@ -555,7 +640,7 @@ public class HighFunctionDBUtil {
} }
} }
private static Data setGlobalDataType(HighGlobal global, DataType dt) private static Data setGlobalDataType(HighSymbol global, DataType dt)
throws InvalidInputException { throws InvalidInputException {
Program program = global.getHighFunction().getFunction().getProgram(); Program program = global.getHighFunction().getFunction().getProgram();
VariableStorage storage = global.getStorage(); VariableStorage storage = global.getStorage();
@ -592,26 +677,26 @@ public class HighFunctionDBUtil {
} }
/** /**
* Commit an override of a calls prototype to the database * Commit an overriding prototype for a particular call site to the database. The override
* @param function is the Function whose call is being overriden * only applies to the function(s) containing the actual call site. Calls to the same function from
* @param callsite is the address of the call * other sites are unaffected. This is used typically either for indirect calls are for calls to
* @param sig signature override * a function with a variable number of parameters.
* @throws InvalidInputException * @param function is the Function whose call site is being overridden
* @throws DuplicateNameException * @param callsite is the address of the calling instruction (the call site)
* @param sig is the overriding function signature
* @throws InvalidInputException if there are problems committing the override symbol
*/ */
public static void writeOverride(Function function, Address callsite, FunctionSignature sig) public static void writeOverride(Function function, Address callsite, FunctionSignature sig)
throws InvalidInputException, DuplicateNameException { throws InvalidInputException {
ParameterDefinition[] params = sig.getArguments(); ParameterDefinition[] params = sig.getArguments();
FunctionSignatureImpl fsig = new FunctionSignatureImpl("tmpname"); // Empty datatype, will get renamed later FunctionDefinitionDataType fsig = new FunctionDefinitionDataType("tmpname"); // Empty datatype, will get renamed later
fsig.setGenericCallingConvention(sig.getGenericCallingConvention()); fsig.setGenericCallingConvention(sig.getGenericCallingConvention());
fsig.setArguments(params); fsig.setArguments(params);
fsig.setReturnType(sig.getReturnType()); fsig.setReturnType(sig.getReturnType());
fsig.setVarArgs(sig.hasVarArgs()); fsig.setVarArgs(sig.hasVarArgs());
FunctionDefinitionDataType dt = new FunctionDefinitionDataType(fsig); DataTypeSymbol datsym = new DataTypeSymbol(fsig, "prt", AUTO_CAT);
DataTypeSymbol datsym = new DataTypeSymbol(dt, "prt", AUTO_CAT);
Program program = function.getProgram(); Program program = function.getProgram();
SymbolTable symtab = program.getSymbolTable(); SymbolTable symtab = program.getSymbolTable();
DataTypeManager dtmanage = program.getDataTypeManager(); DataTypeManager dtmanage = program.getDataTypeManager();
@ -624,9 +709,9 @@ public class HighFunctionDBUtil {
/** /**
* Read a call prototype override which corresponds to the specified override code symbol * Read a call prototype override which corresponds to the specified override code symbol
* @param sym special call override code symbol whose address corresponds to a callsite * @param sym special call override code symbol whose address corresponds to a call site
* @return call prototype override DataTypeSymbol or null if associated function signature * @return call prototype override DataTypeSymbol or null if associated function signature
* datatype could not be found * data-type could not be found
*/ */
public static DataTypeSymbol readOverride(Symbol sym) { public static DataTypeSymbol readOverride(Symbol sym) {
DataTypeSymbol datsym = DataTypeSymbol.readSymbol(AUTO_CAT, sym); DataTypeSymbol datsym = DataTypeSymbol.readSymbol(AUTO_CAT, sym);
@ -640,4 +725,34 @@ public class HighFunctionDBUtil {
return datsym; return datsym;
} }
/**
* Get the Address referred to by a spacebase reference. Address-of references are encoded in
* the p-code syntax tree as: vn = PTRSUB(<spacebase>, #const). This decodes the reference and
* returns the Address
* @param program is the program containing the Address
* @param op is the PTRSUB op encoding the reference
* @return the recovered Address (or null if not correct form)
*/
public static Address getSpacebaseReferenceAddress(Program program, PcodeOp op) {
Address storageAddress = null;
if (op == null) {
return storageAddress;
}
if (op.getOpcode() == PcodeOp.PTRSUB) {
Varnode vnode = op.getInput(0);
if (vnode.isRegister()) {
AddressSpace stackspace = program.getAddressFactory().getStackSpace();
if (stackspace != null) {
Address caddr = op.getInput(1).getAddress();
storageAddress = stackspace.getAddress(caddr.getOffset());
}
}
else {
Address caddr = op.getInput(1).getAddress();
storageAddress = program.getAddressFactory().getDefaultAddressSpace().getAddress(
caddr.getOffset());
}
}
return storageAddress;
}
} }

View file

@ -0,0 +1,64 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
/**
* A function symbol that represents only a shell of (the name and address) the function,
* when no other information is available.
*/
public class HighFunctionShellSymbol extends HighSymbol {
/**
* Construct the function shell given a name and address
* @param id is an id to associate with the new symbol
* @param nm is the given name
* @param addr is the given address
* @param manage is PcodeDataTypeManager to facilitate XML marshaling
*/
public HighFunctionShellSymbol(long id, String nm, Address addr, PcodeDataTypeManager manage) {
super(id, nm, DataType.DEFAULT, true, true, manage);
VariableStorage store;
try {
store = new VariableStorage(getProgram(), addr, 1);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
MappedEntry entry = new MappedEntry(this, store, null);
addMapEntry(entry);
}
@Override
public boolean isGlobal() {
return true;
}
@Override
public void saveXML(StringBuilder buf) {
buf.append("<function");
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name);
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "size", 1);
buf.append(">\n");
buf.append(Varnode.buildXMLAddress(getStorage().getMinAddress()));
buf.append("</function>\n");
}
}

View file

@ -0,0 +1,64 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
/**
* A function symbol that encapsulates detailed information about a particular function
* for the purposes of decompilation. The detailed model is provided by a backing HighFunction object.
*/
public class HighFunctionSymbol extends HighSymbol {
/**
* Construct given an Address, size, and decompiler function model for the symbol.
* The Address is typically the entry point of the function but may be different
* if the function is getting mapped from elsewhere (i.e. the EXTERNAL space). The size
* is given in bytes but generally isn't the true size of the function. The size needs to
* make the symbol just big enough to absorb any off-cut Address queries.
* @param addr is the starting Address of the symbol
* @param size is the pseudo-size of the function
* @param function is the decompiler model of the function
*/
public HighFunctionSymbol(Address addr, int size, HighFunction function) {
super(function.getID(), "", DataType.DEFAULT, function);
VariableStorage store;
try {
store = new VariableStorage(getProgram(), addr, size);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
MappedEntry entry = new MappedEntry(this, store, null);
addMapEntry(entry);
}
@Override
public boolean isGlobal() {
return true;
}
@Override
public void saveXML(StringBuilder buf) {
MappedEntry entry = (MappedEntry) getFirstWholeMap();
String funcString =
function.buildFunctionXML(getId(), entry.getStorage().getMinAddress(), entry.getSize());
buf.append(funcString);
}
}

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,8 +15,11 @@
*/ */
package ghidra.program.model.pcode; package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.util.exception.InvalidInputException; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/** /**
* *
@ -26,16 +28,69 @@ import ghidra.util.exception.InvalidInputException;
*/ */
public class HighGlobal extends HighVariable { public class HighGlobal extends HighVariable {
private HighSymbol symbol;
/** /**
* @param name name of global variable * Constructor for use with restoreXml
* @param type data type of variable * @param high is the HighFunction this global is accessed by
* @param vn global variable storage
* @param func the associated high function
* @throws InvalidInputException
*/ */
public HighGlobal(String name, DataType type, Varnode vn, Varnode[] inst, HighFunction func) public HighGlobal(HighFunction high) {
throws InvalidInputException { super(high);
super(name, type, vn, inst, func);
} }
public HighGlobal(HighSymbol sym, Varnode vn, Varnode[] inst) {
super(sym.getName(), sym.getDataType(), vn, inst, sym.getHighFunction());
symbol = sym;
}
@Override
public HighSymbol getSymbol() {
return symbol;
}
@Override
public void restoreXml(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("high");
long symref = SpecXmlUtils.decodeLong(el.getAttribute("symref"));
String attrString = el.getAttribute("offset");
offset = -1;
if (attrString != null) {
offset = SpecXmlUtils.decodeInt(attrString);
}
restoreInstances(parser, el);
if (symref == 0) {
throw new PcodeXMLException("Missing symref attribute in <high> tag");
}
symbol = function.getGlobalSymbolMap().getSymbol(symref);
if (symbol == null) { // If we don't already have symbol, synthesize it
DataType symbolType;
int symbolSize;
if (offset < 0) { // Variable type and size matches symbol
symbolType = type;
symbolSize = getSize();
}
else {
symbolType = null;
symbolSize = -1;
}
GlobalSymbolMap globalMap = function.getGlobalSymbolMap();
symbol = globalMap.populateSymbol(symref, symbolType, symbolSize);
if (symbol == null) {
Address addr = represent.getAddress();
if (offset > 0) {
addr = addr.subtract(offset);
}
symbol = globalMap.newSymbol(symref, addr, symbolType, symbolSize);
if (symbol == null) {
throw new PcodeXMLException("Bad global storage: " + addr.toString());
}
}
}
if (offset < 0) {
name = symbol.getName();
}
symbol.setHighVariable(this);
parser.end(el);
}
} }

View file

@ -0,0 +1,54 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
/**
* A symbol with no underlying data-type. A label within code. This is used to
* model named jump targets within a function to the decompiler.
*/
public class HighLabelSymbol extends HighSymbol {
/**
* Construct the label given a name and address
* @param nm is the given name
* @param addr is the given Address
* @param dtmanage is a PcodeDataManager to facilitate XML marshaling
*/
public HighLabelSymbol(String nm, Address addr, PcodeDataTypeManager dtmanage) {
super(0, nm, DataType.DEFAULT, true, true, dtmanage);
VariableStorage store;
try {
store = new VariableStorage(getProgram(), addr, 1);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
MappedEntry entry = new MappedEntry(this, store, null);
addMapEntry(entry);
}
@Override
public void saveXML(StringBuilder buf) {
buf.append("<labelsym");
saveXMLHeader(buf);
buf.append("/>\n");
}
}

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,18 +16,23 @@
package ghidra.program.model.pcode; package ghidra.program.model.pcode;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program; import ghidra.util.xml.SpecXmlUtils;
import ghidra.program.model.listing.VariableStorage; import ghidra.xml.XmlElement;
import ghidra.util.exception.AssertException; import ghidra.xml.XmlPullParser;
import ghidra.util.exception.InvalidInputException;
public class HighLocal extends HighVariable { public class HighLocal extends HighVariable {
private Address pcaddr; // null or Address of PcodeOp which defines the representative private Address pcaddr; // null or Address of PcodeOp which defines the representative
private HighSymbol symbol; private HighSymbol symbol;
private long hash = 0; // 60-bit hash value, 0 indicates not-yet-computed or not-applicable
/**
* Constructor for use with restoreXml
* @param high is the HighFunction containing this local variable
*/
public HighLocal(HighFunction high) {
super(high);
}
public HighLocal(DataType type, Varnode vn, Varnode[] inst, Address pc, HighSymbol sym) { public HighLocal(DataType type, Varnode vn, Varnode[] inst, Address pc, HighSymbol sym) {
super(sym.getName(), type, vn, inst, sym.getHighFunction()); super(sym.getName(), type, vn, inst, sym.getHighFunction());
@ -36,39 +40,11 @@ public class HighLocal extends HighVariable {
symbol = sym; symbol = sym;
} }
@Override
public HighSymbol getSymbol() { public HighSymbol getSymbol() {
return symbol; return symbol;
} }
// @Override
// public void retype(DataType newtype, SourceType srctype) throws InvalidInputException {
// Function f = getHighFunction().getFunction();
// Varnode rep = getRepresentative();
// Program program = f.getProgram();
// newtype = newtype.clone(program.getDataTypeManager());
// VariableStorage storage = rep.getStorage(program);
// VariableStorage newStorage =
// VariableUtilities.resizeStorage(storage, newtype, true, getHighFunction().getFunction());
// Varnode newrep = getHighFunction().createFromPieces(newStorage);
// Varnode[] inst = getInstances();
// int pos;
// for (pos = 0; pos < inst.length; ++pos)
// if (inst[pos] == rep) {
// inst[pos] = newrep;
// break;
// }
// attachInstances(inst, newrep);
// try {
// super.retype(newtype, srctype);
// }
// catch (InvalidInputException e) {
// if (pos < inst.length)
// inst[pos] = rep;
// attachInstances(inst, rep); // Restore original varnode
// throw e;
// }
// }
/** /**
* @return instruction address the variable comes into scope within the function * @return instruction address the variable comes into scope within the function
*/ */
@ -76,75 +52,30 @@ public class HighLocal extends HighVariable {
return pcaddr; return pcaddr;
} }
protected int getFirstUseOffset() {
Address pcaddr = getPCAddress();
if (pcaddr == null || getRepresentative().getAddress().isStackAddress()) {
return 0;
}
return (int) pcaddr.subtract(getHighFunction().getFunction().getEntryPoint());
}
// static DataType getUndefinedType(DataType originalType) {
//
// // handle pointer conversion
// if (originalType instanceof Pointer) {
// Pointer ptr = (Pointer) originalType;
// DataType innerDt = ptr.getDataType();
// DataType replacementDt = innerDt;
// if (!(originalType instanceof Undefined)) {
// replacementDt = getUndefinedType(innerDt);
// }
// if (replacementDt != innerDt) {
// return new PointerDataType(replacementDt, ptr.getLength(), ptr.getDataTypeManager());
// }
// return originalType;
// }
//
// int size = originalType.getLength();
// if (size <= 8) {
// return Undefined.getUndefinedDataType(size);
// }
// return originalType; // too big for undefined type
// }
@Override @Override
public VariableStorage getStorage() { public void restoreXml(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("high");
Program program = getHighFunction().getFunction().getProgram(); long symref = SpecXmlUtils.decodeLong(el.getAttribute("symref"));
Varnode represent = getRepresentative(); offset = -1;
String attrString = el.getAttribute("offset");
if (symbol instanceof DynamicSymbol || represent.isUnique()) { if (attrString != null) {
long hash = buildDynamicHash(); offset = SpecXmlUtils.decodeInt(attrString);
try {
return new VariableStorage(program, AddressSpace.HASH_SPACE.getAddress(hash),
represent.getSize());
}
catch (InvalidInputException e) {
throw new AssertException("Unexpected exception", e);
}
} }
restoreInstances(parser, el);
symbol = function.getLocalSymbolMap().getSymbol(symref);
if (symbol == null) {
throw new PcodeXMLException("HighLocal is missing symbol");
}
if (offset < 0) {
name = symbol.getName();
}
else {
name = "UNNAMED";
}
pcaddr = symbol.getPCAddress();
symbol.setHighVariable(this);
if (symbol instanceof MappedSymbol) { parser.end(el);
return ((MappedSymbol) symbol).getStorage();
}
return super.getStorage();
}
public long buildDynamicHash() {
if (hash != 0) {
return hash;
}
if (symbol instanceof DynamicSymbol) {
hash = ((DynamicSymbol) symbol).getHash();
pcaddr = symbol.getPCAddress();
}
else if (getRepresentative().isUnique()) {
DynamicHash dynamicHash = new DynamicHash(getRepresentative(), getHighFunction());
hash = dynamicHash.getHash();
pcaddr = dynamicHash.getAddress();
}
return hash;
} }
} }

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +17,9 @@ package ghidra.program.model.pcode;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.util.exception.InvalidInputException; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/** /**
* *
@ -28,30 +29,30 @@ import ghidra.util.exception.InvalidInputException;
*/ */
public class HighOther extends HighVariable { public class HighOther extends HighVariable {
private DynamicSymbol symbol;
private Address pcaddr; // Address of PcodeOp which defines the representative private Address pcaddr; // Address of PcodeOp which defines the representative
private HighSymbol symbol; // Possibly a dynamic global symbol
/**
* Constructor for use with restoreXml
* @param high is the HighFunction containing the variable
*/
public HighOther(HighFunction high) {
super(high);
}
/** /**
* Construct a unique high NOT associated with a symbol * Construct a unique high NOT associated with a symbol
* @param type data type of variable * @param type data type of variable
* @param vn variable storage * @param vn is the representative Varnode
* @param inst is the list of Varnodes making up the variable
* @param pc code unit address where unique is first assigned (first-use) * @param pc code unit address where unique is first assigned (first-use)
* @param func the associated high function * @param func the associated high function
* @throws InvalidInputException
*/ */
public HighOther(DataType type, Varnode vn, Varnode[] inst, Address pc, HighFunction func) public HighOther(DataType type, Varnode vn, Varnode[] inst, Address pc, HighFunction func) {
throws InvalidInputException {
super(null, type, vn, inst, func); super(null, type, vn, inst, func);
pcaddr = pc; pcaddr = pc;
} }
/**
* @return associated dynamic symbol or null
*/
public DynamicSymbol getSymbol() {
return symbol;
}
/** /**
* @return instruction address the variable comes into scope within the function * @return instruction address the variable comes into scope within the function
*/ */
@ -59,4 +60,31 @@ public class HighOther extends HighVariable {
return pcaddr; return pcaddr;
} }
@Override
public HighSymbol getSymbol() {
return symbol;
}
@Override
public void restoreXml(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("high");
long symref = SpecXmlUtils.decodeLong(el.getAttribute("symref"));
offset = -1;
String attrString = el.getAttribute("offset");
restoreInstances(parser, el);
name = "UNNAMED";
pcaddr = function.getPCAddress(represent);
if (symref != 0) {
offset = -1;
if (attrString != null) {
offset = SpecXmlUtils.decodeInt(attrString);
}
symbol = function.getLocalSymbolMap().getSymbol(symref);
if (symbol != null && offset < 0) {
name = symbol.getName();
}
}
parser.end(el);
}
} }

View file

@ -17,6 +17,7 @@ package ghidra.program.model.pcode;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.xml.XmlPullParser;
/** /**
* *
@ -26,9 +27,17 @@ import ghidra.program.model.data.DataType;
public class HighParam extends HighLocal { public class HighParam extends HighLocal {
private int slot; private int slot;
/**
* Constructor for use with restoreXml
* @param high is the HighFunction containing this parameter
*/
public HighParam(HighFunction high) {
super(high);
}
/** /**
* @param tp data type of variable * @param tp data type of variable
* @param rep represented varnode * @param rep is the representative input Varnode
* @param pc null or Address of PcodeOp which defines the representative * @param pc null or Address of PcodeOp which defines the representative
* @param slot parameter index starting at 0 * @param slot parameter index starting at 0
* @param sym associated symbol * @param sym associated symbol
@ -46,8 +55,10 @@ public class HighParam extends HighLocal {
} }
@Override @Override
protected int getFirstUseOffset() { public void restoreXml(XmlPullParser parser) throws PcodeXMLException {
return 0; super.restoreXml(parser);
HighSymbol sym = getSymbol();
slot = sym.getCategoryIndex();
} }
} }

View file

@ -16,158 +16,462 @@
package ghidra.program.model.pcode; package ghidra.program.model.pcode;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.xml.SpecXmlUtils; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement; import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser; import ghidra.xml.XmlPullParser;
public abstract class HighSymbol { /**
* A symbol within the decompiler's model of a particular function. The symbol has a name and a data-type
* along with other properties. The symbol is mapped to one or more storage locations by attaching a
* SymbolEntry for each mapping.
*/
public class HighSymbol {
public static final long ID_BASE = 0x4000000000000000L; // Put keys in the dynamic symbol portion of the key space
protected String name; protected String name;
protected DataType type; protected DataType type;
protected int size; // Size of this variable
protected Address pcaddr; // first-use address
protected HighFunction function; // associated function protected HighFunction function; // associated function
protected int category; // Sub-class of symbol -1=none 0=parameter 1=equate
protected int categoryIndex; // Numbering within the sub-class
private boolean namelock; // Is this variable's name locked private boolean namelock; // Is this variable's name locked
private boolean typelock; // Is this variable's datatype locked private boolean typelock; // Is this variable's datatype locked
private boolean readonly; private long id; // Unique id of this symbol
protected SymbolEntry[] entryList; // List of mappings for this symbol
private HighVariable highVariable; private HighVariable highVariable;
private PcodeDataTypeManager dtmanage; // Datatype manager for XML generation
public HighSymbol() { // For use with restoreXML /**
* Constructor for use with restoreXML
* @param func is the HighFunction using the symbol
*/
protected HighSymbol(HighFunction func) {
function = func;
dtmanage = function.getDataTypeManager();
} }
public HighSymbol(String nm,DataType tp,int sz,Address pc,HighFunction func) { /**
* Construct a base symbol, given a name and data-type. Mappings must be attached separately.
* @param uniqueId is the id to associate with the new symbol
* @param nm is the given name
* @param tp is the given data-type
* @param func is the function model owning the new symbol
*/
protected HighSymbol(long uniqueId, String nm, DataType tp, HighFunction func) {
function = func;
dtmanage = function.getDataTypeManager();
name = nm; name = nm;
type = tp; type = tp;
size = sz;
pcaddr = pc;
namelock = false; namelock = false;
typelock = false; typelock = false;
function = func; id = uniqueId;
category = -1;
categoryIndex = -1;
} }
/**
* Construct a symbol that is not attached to a function model. The symbol is given
* a name, data-type, and other basic attributes. Mappings must be attached separately.
* @param uniqueId is the id to associate with the new symbol
* @param nm is the given name
* @param tp is the given data-type
* @param tlock is true if the symbol is type locked
* @param nlock is true if the symbol is name locked
* @param manage is a PcodeDataTypeManager to facilitate XML marshaling
*/
protected HighSymbol(long uniqueId, String nm, DataType tp, boolean tlock, boolean nlock,
PcodeDataTypeManager manage) {
function = null;
dtmanage = manage;
name = nm;
type = tp;
namelock = nlock;
typelock = tlock;
id = uniqueId;
category = -1;
categoryIndex = -1;
}
protected void addMapEntry(SymbolEntry entry) {
if (entryList == null) {
entryList = new SymbolEntry[1];
entryList[0] = entry;
}
else {
SymbolEntry[] newList = new SymbolEntry[entryList.length + 1];
for (int i = 0; i < entryList.length; ++i) {
newList[i] = entryList[i];
}
newList[entryList.length] = entry;
entryList = newList;
}
}
/**
* Get id associated with this symbol.
* @return the id
*/
public long getId() {
return id;
}
/**
* Fetch the corresponding database Symbol if it exists.
* @return the matching Symbol object or null
*/
public Symbol getSymbol() {
if (id != 0) {
return function.getFunction().getProgram().getSymbolTable().getSymbol(id);
}
return null;
}
/**
* Associate a particular HighVariable with this symbol. This is used to link the symbol
* into the decompiler's description of how a function manipulates a particular symbol.
* @param high is the associated HighVariable
*/
public void setHighVariable(HighVariable high) { public void setHighVariable(HighVariable high) {
this.highVariable = high; this.highVariable = high;
} }
/**
* Get the HighVariable associate with this symbol if any. This allows the user to go straight
* into the decompiler's function to see how the symbol gets manipulated.
* @return the associated HighVariable or null
*/
public HighVariable getHighVariable() { public HighVariable getHighVariable() {
return highVariable; return highVariable;
} }
/**
* Get the base name of this symbol
* @return the name
*/
public String getName() { public String getName() {
return name; return name;
} }
/**
* Get the Program object containing the function being modeled.
* @return the Program
*/
public Program getProgram() {
return dtmanage.getProgram();
}
/**
* @return the data-type associate with this symbol
*/
public DataType getDataType() { public DataType getDataType() {
return type; return type;
} }
/**
* @return the number of bytes consumed by the storage for this symbol
*/
public int getSize() { public int getSize() {
return size; return entryList[0].getSize();
} }
/**
* Get the first code Address, within the function, where this symbol's storage actually
* holds the value of the symbol. If there is more than one mapping for the symbol, this
* returns the code Address for the first mapping. A null value indicates that the storage
* is valid over the whole function (at least). If the value is non-null, the symbol storage
* may be used for other purposes at prior locations.
* @return the first use code Address or null
*/
public Address getPCAddress() { public Address getPCAddress() {
return pcaddr; return entryList[0].pcaddr;
} }
/**
* Get the first code Address (expressed as a different in bytes from the starting address of the
* function) where this symbol's storage actually holds the value of the symbol. A value of 0 indicates
* that the storage is valid across the entire function. A negative value indicates the storage is
* an input to the function.
* @return the first-use offset of this symbol's storage
*/
protected int getFirstUseOffset() {
Address pcaddr = entryList[0].pcaddr;
if (pcaddr == null) {
return 0;
}
return (int) pcaddr.subtract(getHighFunction().getFunction().getEntryPoint());
}
/**
* Get the function model of which this symbol is a part.
* @return the HighFunction owning this symbol
*/
public HighFunction getHighFunction() { public HighFunction getHighFunction() {
return function; return function;
} }
/**
* Set the category and associated index for this symbol. The category indicates a specific sub-class
* of symbols. Currently -1=none, 0=parameter, 1=equate
* @param cat is the category
* @param index is the category index ("slot" for parameters)
*/
protected void setCategory(int cat, int index) {
category = cat;
categoryIndex = index;
}
/**
* Set whether this symbol's data-type is considered "locked". If it is "locked",
* this symbol's data-type is considered unchangeable during decompilation. The data-type
* will be forced into the decompiler's model of the function to the extent possible.
* @param typelock is true if the data-type should be considered "locked".
*/
public void setTypeLock(boolean typelock) { public void setTypeLock(boolean typelock) {
this.typelock = typelock; this.typelock = typelock;
} }
/**
* Set whether this symbol's name is considered "locked". If it is "locked", the decompiler
* will use the name when labeling the storage described by this symbol.
* @param namelock is true if the name should be considered "locked".
*/
public void setNameLock(boolean namelock) { public void setNameLock(boolean namelock) {
this.namelock = namelock; this.namelock = namelock;
} }
public void setReadOnly(boolean readOnly) { /**
this.readonly = readOnly; * If this returns true, this symbol's data-type is "locked", meaning
} * it is considered unchangeable during decompilation. The data-type
* will be forced into the decompiler's model of the function to the extent possible.
* @return true if the data-type is considered "locked".
*/
public boolean isTypeLocked() { public boolean isTypeLocked() {
return typelock; return typelock;
} }
/**
* If this returns true, this symbol's name is "locked". meaning the decompiler
* is forced to use the name when labeling the storage described by this symbol.
* @return true if the name is considered "locked".
*/
public boolean isNameLocked() { public boolean isNameLocked() {
return namelock; return namelock;
} }
public boolean isReadOnly() { /**
return readonly; * If this returns true, the decompiler will not speculatively merge this with
* other variables.
* Currently, being isolated is equivalent to being typelocked.
* @return true if this will not be merged with other variables
*/
public boolean isIsolated() {
return typelock;
} }
public abstract String buildXML(); /**
* @return true if the symbol's value is considered read-only (by the decompiler)
*/
public boolean isReadOnly() {
return entryList[0].isReadOnly();
}
public abstract int restoreXML(XmlPullParser parser,HighFunction func) throws PcodeXMLException; /**
* Is this symbol a parameter for a function
* @return true if this is a parameter
*/
public boolean isParameter() {
return (category == 0);
}
protected int restoreSymbolXML(XmlElement symel,HighFunction func) throws PcodeXMLException { /**
function = func; * For parameters (category=0), this method returns the position of the parameter within the function prototype.
int symbolId = SpecXmlUtils.decodeInt(symel.getAttribute("id")); * @return the category index for this symbol
if (symbolId == 0) { */
public int getCategoryIndex() {
return categoryIndex;
}
/**
* Is this symbol in the global scope or some other global namespace
* @return true if this is global
*/
public boolean isGlobal() {
return false;
}
/**
* @return the first mapping object attached to this symbol
*/
public SymbolEntry getFirstWholeMap() {
return entryList[0];
}
/**
* @return the storage associated with this symbol (associated with the first mapping)
*/
public VariableStorage getStorage() {
return entryList[0].getStorage();
}
/**
* Write out attributes for the base XML tag
* @param buf is the XML output stream
*/
protected void saveXMLHeader(StringBuilder buf) {
if ((id >> 56) != (ID_BASE >> 56)) { // Don't send down internal ids
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "id", id);
}
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name);
SpecXmlUtils.encodeBooleanAttribute(buf, "typelock", typelock);
SpecXmlUtils.encodeBooleanAttribute(buf, "namelock", namelock);
SpecXmlUtils.encodeBooleanAttribute(buf, "readonly", isReadOnly());
boolean isVolatile = entryList[0].isVolatile();
if (isVolatile) {
SpecXmlUtils.encodeBooleanAttribute(buf, "volatile", true);
}
if (isIsolated()) {
SpecXmlUtils.encodeBooleanAttribute(buf, "merge", false);
}
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "cat", category);
if (categoryIndex >= 0) {
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "index", categoryIndex);
}
}
/**
* Save the symbol description as a tag to the XML stream. This does NOT save the mappings.
* @param buf is the XML stream
*/
public void saveXML(StringBuilder buf) {
buf.append("<symbol");
saveXMLHeader(buf);
buf.append(">\n");
buf.append(dtmanage.buildTypeRef(type, getSize()));
buf.append("</symbol>\n");
}
protected void restoreXMLHeader(XmlElement symel) throws PcodeXMLException {
id = SpecXmlUtils.decodeLong(symel.getAttribute("id"));
if (id == 0) {
throw new PcodeXMLException("missing unique symbol id"); throw new PcodeXMLException("missing unique symbol id");
} }
typelock = false; typelock = false;
String typelockstr = symel.getAttribute("typelock"); String typelockstr = symel.getAttribute("typelock");
if ((typelockstr != null) && (SpecXmlUtils.decodeBoolean(typelockstr))) if ((typelockstr != null) && (SpecXmlUtils.decodeBoolean(typelockstr))) {
typelock = true; typelock = true;
}
namelock = false; namelock = false;
String namelockstr = symel.getAttribute("namelock"); String namelockstr = symel.getAttribute("namelock");
if ((namelockstr != null) && (SpecXmlUtils.decodeBoolean(namelockstr))) if ((namelockstr != null) && (SpecXmlUtils.decodeBoolean(namelockstr))) {
namelock = true; namelock = true;
name = symel.getAttribute("name"); }
// isolate = false;
// String isolatestr = symel.getAttribute("merge");
// if ((isolatestr != null) && !SpecXmlUtils.decodeBoolean(isolatestr)) {
// isolate = true;
// }
return symbolId; name = symel.getAttribute("name");
categoryIndex = -1;
category = -1;
if (symel.hasAttribute("cat")) {
category = SpecXmlUtils.decodeInt(symel.getAttribute("cat"));
if (category == 0) {
categoryIndex = SpecXmlUtils.decodeInt(symel.getAttribute("index"));
}
}
} }
protected Address parseRangeList(XmlPullParser parser) { /**
Address addr = null; * Restore this symbol object and its associated mappings from an XML description
XmlElement rangelistel = parser.start("rangelist"); * in the given stream.
if (parser.peek().isStart()) { * @param parser is the given XML stream
// we only use this to establish first-use * @throws PcodeXMLException if the XML description is invalid
XmlElement rangeel = parser.start("range"); */
String spc = rangeel.getAttribute("space"); public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
long offset = SpecXmlUtils.decodeLong(rangeel.getAttribute("first")); XmlElement symel = parser.start("symbol");
addr = function.getAddressFactory().getAddressSpace(spc).getAddress(offset); restoreXMLHeader(symel);
addr = function.getFunction().getEntryPoint().getAddressSpace().getOverlayAddress(addr); type = dtmanage.readXMLDataType(parser);
parser.end(rangeel); parser.end(symel);
if (categoryIndex >= 0 && name.startsWith("$$undef")) {
// use default parameter name
name = "param_" + Integer.toString(categoryIndex + 1);
} }
parser.end(rangelistel); while (parser.peek().isStart()) {
return addr; XmlElement el = parser.peek();
SymbolEntry entry;
if (el.getName().equals("hash")) {
entry = new DynamicEntry(this);
}
else if (this instanceof HighCodeSymbol) {
entry = new MappedDataEntry(this);
}
else {
entry = new MappedEntry(this);
}
entry.restoreXML(parser);
addMapEntry(entry);
}
} }
public static void buildMapSymXML(StringBuilder res, String addrHashRes, Address pc, String sym) { /**
res.append("<mapsym>\n"); * Restore a full HighSymbol from the next &lt;mapsym&gt; tag in the given XML stream.
res.append(sym); * This method acts as an XML based HighSymbol factory, instantiating the correct class
res.append(addrHashRes); * based on the particular tags.
if (pc == null || pc.isExternalAddress()) { * @param parser is the given XML stream
res.append("<rangelist/>"); * @param isGlobal is true if this symbol is being read into a global scope
* @param high is the function model that will own the new symbol
* @return the new symbol
* @throws PcodeXMLException if the XML description is invalid
*/
public static HighSymbol restoreMapSymXML(XmlPullParser parser, boolean isGlobal,
HighFunction high) throws PcodeXMLException {
HighSymbol res = null;
parser.start("mapsym");
XmlElement symel = parser.peek();
if (symel.getName().equals("equatesymbol")) {
res = new EquateSymbol(high);
}
else if (isGlobal) {
// res = new HighCodeSymbol(high);
// Currently the decompiler does not send back global symbols. They are inferred from the HighVariables
} }
else { else {
buildRangelistXML(res, pc); res = new HighSymbol(high);
}
res.restoreXML(parser);
while (parser.peek().isStart()) {
SymbolEntry entry;
if (parser.peek().getName().equals("hash")) {
entry = new DynamicEntry(res);
}
else {
entry = new MappedEntry(res);
}
entry.restoreXML(parser);
res.addMapEntry(entry);
}
parser.end();
return res;
}
/**
* Write out the given symbol with all its mapping as a &lt;mapsym&gt; tag to the given XML stream.
* @param res is the given XML stream
* @param sym is the given symbol
*/
public static void buildMapSymXML(StringBuilder res, HighSymbol sym) {
res.append("<mapsym>\n");
sym.saveXML(res);
for (SymbolEntry entry : sym.entryList) {
entry.saveXml(res);
} }
res.append("</mapsym>\n"); res.append("</mapsym>\n");
} }
public static void buildRangelistXML(StringBuilder res, Address pc) {
res.append("<rangelist>");
if (pc != null) {
AddressSpace space = pc.getAddressSpace();
if (space.isOverlaySpace()) {
space = space.getPhysicalSpace();
pc = space.getAddress(pc.getOffset());
}
res.append("<range");
SpecXmlUtils.encodeStringAttribute(res, "space", space.getName());
long off = pc.getUnsignedOffset();
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "first", off);
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "last", off);
res.append("/>");
}
res.append("</rangelist>\n");
}
} }

View file

@ -15,11 +15,12 @@
*/ */
package ghidra.program.model.pcode; package ghidra.program.model.pcode;
import java.util.ArrayList;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program; import ghidra.util.xml.SpecXmlUtils;
import ghidra.program.model.listing.VariableStorage; import ghidra.xml.XmlElement;
import ghidra.util.Msg; import ghidra.xml.XmlPullParser;
import ghidra.util.exception.InvalidInputException;
/** /**
* *
@ -29,11 +30,20 @@ import ghidra.util.exception.InvalidInputException;
*/ */
public abstract class HighVariable { public abstract class HighVariable {
private String name; protected String name;
private DataType type; protected DataType type;
private Varnode represent; // A representative varnode protected Varnode represent; // A representative varnode
private Varnode[] instances; // Instances of this high-level variable protected Varnode[] instances; // Instances of this high-level variable
private HighFunction function; // associated function protected int offset = -1; // Offset (in bytes) into containing symbol (-1 indicates whole match)
protected HighFunction function; // associated function
/**
* Constructor for use with restoreXml
* @param func is the HighFunction this variable belongs to
*/
protected HighVariable(HighFunction func) {
function = func;
}
protected HighVariable(String nm, DataType tp, Varnode rep, Varnode[] inst, HighFunction func) { protected HighVariable(String nm, DataType tp, Varnode rep, Varnode[] inst, HighFunction func) {
name = nm; name = nm;
@ -46,9 +56,10 @@ public abstract class HighVariable {
* Link Varnodes directly to this HighVariable * Link Varnodes directly to this HighVariable
*/ */
protected void setHighOnInstances() { protected void setHighOnInstances() {
for(int i=0;i<instances.length;++i) { for (Varnode instance : instances) {
if (instances[i] instanceof VarnodeAST) if (instance instanceof VarnodeAST) {
((VarnodeAST)instances[i]).setHigh(this); ((VarnodeAST)instance).setHigh(this);
}
} }
} }
@ -97,6 +108,21 @@ public abstract class HighVariable {
return instances; return instances;
} }
/**
* Retrieve any underlying HighSymbol
* @return the HighSymbol
*/
public abstract HighSymbol getSymbol();
/**
* Get the offset of this variable into its containing HighSymbol. If the value
* is -1, this indicates that this HighVariable matches the size and storage of the symbol.
* @return the offset
*/
public int getOffset() {
return offset;
}
/** /**
* Attach an instance or additional location the variable can be found in. * Attach an instance or additional location the variable can be found in.
* *
@ -109,20 +135,67 @@ public abstract class HighVariable {
instances = new Varnode[1]; instances = new Varnode[1];
instances[0] = rep; instances[0] = rep;
} }
else else {
instances = inst; instances = inst;
}
} }
public VariableStorage getStorage() { /**
Program program = getHighFunction().getFunction().getProgram(); * Restore the data-type and the Varnode instances of this HighVariable.
try { * The "representative" Varnode is also populated.
if (represent != null && (represent.isAddress() || represent.isRegister())) { * @param parser is the XML stream
return new VariableStorage(program, represent); * @param el is the root <high> tag
} * @throws PcodeXMLException if the XML is not valid
*/
protected void restoreInstances(XmlPullParser parser, XmlElement el)
throws PcodeXMLException {
int repref = SpecXmlUtils.decodeInt(el.getAttribute("repref"));
Varnode rep = function.getRef(repref);
if (rep == null) {
throw new PcodeXMLException("Undefined varnode reference");
} }
catch (InvalidInputException e) {
Msg.error(this, "Failed to define variable storage: " + this, e); type = null;
ArrayList<Varnode> vnlist = new ArrayList<Varnode>();
if (parser.peek().isStart()) {
type = function.getDataTypeManager().readXMLDataType(parser);
} }
return VariableStorage.UNASSIGNED_STORAGE;
if (type == null) {
throw new PcodeXMLException("Missing <type> for HighVariable");
}
while (parser.peek().isStart()) {
Varnode vn = Varnode.readXML(parser, function);
vnlist.add(vn);
}
Varnode[] vnarray = new Varnode[vnlist.size()];
vnlist.toArray(vnarray);
attachInstances(vnarray, rep);
setHighOnInstances();
} }
/**
* Return true in when the HighVariable should be recorded (in the database) using dynamic storage
* rather than using the actual address space and offset of the representative varnode. Dynamic storage
* is typically needed if the actual storage is ephemeral (in the unique space).
* @return true if this needs dynamic storage
*/
public boolean requiresDynamicStorage() {
if (represent.isUnique()) {
return true; // Temporary Varnodes always need dynamic storage
}
if (represent.getAddress().isStackAddress() && !represent.isAddrTied()) {
return true;
}
return false;
}
/**
* Restore this HighVariable from a <high> XML tag
* @param parser is the XML stream
* @throws PcodeXMLException if the XML is not valid
*/
public abstract void restoreXml(XmlPullParser parser) throws PcodeXMLException;
} }

View file

@ -29,18 +29,20 @@ import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser; import ghidra.xml.XmlPullParser;
/** /**
* * A container for local symbols within the decompiler's model of a function. It contains HighSymbol
* * objects for any symbol within the scope of the function, including parameters. The container is populated
* Local variables visible to a function. This includes mapped (on the stack) and * either from the underlying Function object (when sending information to the decompiler) or read in from
* unmapped (only stored in a register). * an XML description (when receiving a function model from the decompiler). HighSymbols can be obtained
* * via Address using findLocal() or by id using getSymbol(). Parameters can be accessed specifically
* using getParamSymbol().
*/ */
public class LocalSymbolMap { public class LocalSymbolMap {
private HighFunction func; // Function to which these variables are local private HighFunction func; // Function to which these variables are local
private String spacename; private String spacename;
private HashMap<MappedVarKey, HighSymbol> addrMappedSymbols; // Hashed by addr and pcaddr private HashMap<MappedVarKey, HighSymbol> addrMappedSymbols; // Hashed by addr and pcaddr
private HashMap<Integer, HighSymbol> symbolMap; // Hashed by unique key private HashMap<Long, HighSymbol> symbolMap; // Hashed by unique key
private MappedSymbol[] paramSymbols; private HighSymbol[] paramSymbols;
private long uniqueSymbolId; // Next available symbol id
/** /**
* @param highFunc HighFunction the local variables are defined within. * @param highFunc HighFunction the local variables are defined within.
@ -50,50 +52,149 @@ public class LocalSymbolMap {
func = highFunc; func = highFunc;
spacename = spcname; spacename = spcname;
addrMappedSymbols = new HashMap<MappedVarKey, HighSymbol>(); addrMappedSymbols = new HashMap<MappedVarKey, HighSymbol>();
symbolMap = new HashMap<Integer, HighSymbol>(); symbolMap = new HashMap<Long, HighSymbol>();
paramSymbols = new MappedSymbol[0]; paramSymbols = new HighSymbol[0];
uniqueSymbolId = 0;
} }
/**
* Get the decompiler's function model owning this container
* @return the owning HighFunction
*/
public HighFunction getHighFunction() { public HighFunction getHighFunction() {
return func; return func;
} }
/**
* Assign a unique id to a new symbol being put in this container.
* @return the unique id
*/
private long getNextId() {
long key = HighSymbol.ID_BASE + uniqueSymbolId;
uniqueSymbolId += 1;
return key;
}
/**
* Construct and return a map from a HighSymbol's name to the HighSymbol object
* @return the new name to symbol map
*/
public Map<String, HighSymbol> getNameToSymbolMap() {
Map<String, HighSymbol> newMap = new TreeMap<String, HighSymbol>();
for (HighSymbol highSymbol : symbolMap.values()) {
newMap.put(highSymbol.getName(), highSymbol);
}
return newMap;
}
/**
* Remove the given HighSymbol from this container.
* The key is removed from the main symbolMap. It is also removed from the MappedEntry map
* and from the list of parameter symbols if applicable.
* @param highSymbol is the given symbol
*/
private void removeSymbol(HighSymbol highSymbol) {
SymbolEntry mapEntry = highSymbol.getFirstWholeMap();
if (mapEntry instanceof MappedEntry) {
MappedVarKey key = new MappedVarKey(mapEntry.getStorage(), mapEntry.getPCAdress());
addrMappedSymbols.remove(key);
}
symbolMap.remove(highSymbol.getId());
if (highSymbol.isParameter()) {
int index = highSymbol.getCategoryIndex();
HighSymbol[] newArray = new HighSymbol[paramSymbols.length - 1];
for (int i = 0; i < index; ++i) {
newArray[i] = paramSymbols[i];
}
for (int i = index + 1; i < paramSymbols.length; ++i) {
HighSymbol paramSym = paramSymbols[i];
newArray[i - 1] = paramSym;
paramSym.categoryIndex -= 1;
}
}
}
/**
* Given names of the form: "baseName", "baseName$1", "baseName@2", ...
* find the corresponding HighSymbols in this container and merge them into a single HighSymbol.
* The name passed into this method must be of the form "baseName$1", the base name is extracted from it.
* @param name is a string with the base name concatenated with "$1"
* @param nameMap is a map from all symbols names in this container to the corresponding HighSymbol
*/
private void mergeNamedSymbols(String name, Map<String, HighSymbol> nameMap) {
String baseName = name.substring(0, name.length() - 2);
HighSymbol baseSymbol = nameMap.get(baseName);
if (baseSymbol == null || !baseSymbol.isTypeLocked() ||
(baseSymbol instanceof EquateSymbol)) {
return;
}
DataType baseDataType = baseSymbol.getDataType();
for (int index = 1;; ++index) {
String nextName = baseName + '$' + Integer.toString(index);
HighSymbol nextSymbol = nameMap.get(nextName);
if (nextSymbol == null || !nextSymbol.isTypeLocked() || nextSymbol.isParameter() ||
(baseSymbol instanceof EquateSymbol)) {
break;
}
if (!nextSymbol.getDataType().equals(baseDataType)) { // Data-types of symbols being merged must match
break;
}
SymbolEntry mapEntry = nextSymbol.getFirstWholeMap();
if (mapEntry.getPCAdress() == null) { // Don't merge from an address tied symbol
break;
}
baseSymbol.addMapEntry(mapEntry);
removeSymbol(nextSymbol);
}
}
/** /**
* Populate the local variable map from information attached to the Program DB's function. * Populate the local variable map from information attached to the Program DB's function.
* @param includeDefaultNames is true if default symbol names should be considered locked * @param includeDefaultNames is true if default symbol names should be considered locked
*/ */
public void grabFromFunction(boolean includeDefaultNames) { public void grabFromFunction(boolean includeDefaultNames) {
ArrayList<String> mergeNames = null;
Function dbFunction = func.getFunction(); Function dbFunction = func.getFunction();
int uniqueSymbolId = 0;
Variable locals[] = dbFunction.getLocalVariables(); Variable locals[] = dbFunction.getLocalVariables();
for (Variable local : locals) { for (Variable local : locals) {
Variable var = local; if (!local.isValid()) {
if (!var.isValid()) {
// exclude locals which don't have valid storage // exclude locals which don't have valid storage
continue; continue;
} }
DataType dt = var.getDataType(); DataType dt = local.getDataType();
boolean istypelock = true; boolean istypelock = true;
boolean isnamelock = true; boolean isnamelock = true;
if (Undefined.isUndefined(dt)) { if (Undefined.isUndefined(dt)) {
istypelock = false; istypelock = false;
} }
int sz = var.getLength(); String name = local.getName();
String name = var.getName(); if (name.length() > 2 && name.charAt(name.length() - 2) == '$') {
// An indication of names like "name", "name@1", "name@2"
if (name.charAt(name.length() - 1) == '1') {
if (mergeNames == null) {
mergeNames = new ArrayList<String>();
}
mergeNames.add(name);
}
}
VariableStorage storage = var.getVariableStorage(); VariableStorage storage = local.getVariableStorage();
long id = 0;
Symbol symbol = local.getSymbol();
if (symbol != null) {
id = symbol.getID();
}
Address defAddr = null; Address defAddr = null;
if (!storage.isStackStorage()) { if (!storage.isStackStorage()) {
defAddr = dbFunction.getEntryPoint().addWrap(var.getFirstUseOffset()); defAddr = dbFunction.getEntryPoint().addWrap(local.getFirstUseOffset());
} }
HighSymbol sym; HighSymbol sym;
if (storage.isHashStorage()) { if (storage.isHashStorage()) {
sym = sym =
newDynamicSymbol(name, dt, sz, storage.getFirstVarnode().getOffset(), defAddr, newDynamicSymbol(id, name, dt, storage.getFirstVarnode().getOffset(), defAddr);
0, ++uniqueSymbolId);
} }
else { else {
sym = newMappedSymbol(name, dt, storage, defAddr, -1, ++uniqueSymbolId); sym = newMappedSymbol(id, name, dt, storage, defAddr, -1);
} }
sym.setTypeLock(istypelock); sym.setTypeLock(istypelock);
sym.setNameLock(isnamelock); sym.setNameLock(isnamelock);
@ -105,7 +206,7 @@ public class LocalSymbolMap {
Address pcaddr = dbFunction.getEntryPoint(); Address pcaddr = dbFunction.getEntryPoint();
pcaddr = pcaddr.subtractWrap(1); pcaddr = pcaddr.subtractWrap(1);
List<MappedSymbol> paramList = new ArrayList<MappedSymbol>(); List<HighSymbol> paramList = new ArrayList<HighSymbol>();
for (int i = 0; i < p.length; ++i) { for (int i = 0; i < p.length; ++i) {
Parameter var = p[i]; Parameter var = p[i];
if (!var.isValid()) { if (!var.isValid()) {
@ -114,10 +215,23 @@ public class LocalSymbolMap {
} }
DataType dt = var.getDataType(); DataType dt = var.getDataType();
String name = var.getName(); String name = var.getName();
if (name.length() > 2 && name.charAt(name.length() - 2) == '$') {
// An indication of names like "name", "name@1", "name@2"
if (name.charAt(name.length() - 1) == '1') {
if (mergeNames == null) {
mergeNames = new ArrayList<String>();
}
mergeNames.add(name);
}
}
VariableStorage storage = var.getVariableStorage(); VariableStorage storage = var.getVariableStorage();
Address resAddr = storage.isStackStorage() ? null : pcaddr; Address resAddr = storage.isStackStorage() ? null : pcaddr;
MappedSymbol paramSymbol = long id = 0;
newMappedSymbol(name, dt, storage, resAddr, i, ++uniqueSymbolId); Symbol symbol = var.getSymbol();
if (symbol != null) {
id = symbol.getID();
}
HighSymbol paramSymbol = newMappedSymbol(id, name, dt, storage, resAddr, i);
paramList.add(paramSymbol); paramList.add(paramSymbol);
boolean namelock = true; boolean namelock = true;
if (!includeDefaultNames) { if (!includeDefaultNames) {
@ -127,11 +241,12 @@ public class LocalSymbolMap {
paramSymbol.setTypeLock(lock); paramSymbol.setTypeLock(lock);
} }
paramSymbols = new MappedSymbol[paramList.size()]; paramSymbols = new HighSymbol[paramList.size()];
paramList.toArray(paramSymbols); paramList.toArray(paramSymbols);
Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR); Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR);
uniqueSymbolId = grabEquates(dbFunction, uniqueSymbolId); grabEquates(dbFunction);
grabMerges(mergeNames);
} }
private boolean isUserDefinedName(String name) { private boolean isUserDefinedName(String name) {
@ -151,22 +266,8 @@ public class LocalSymbolMap {
* @throws PcodeXMLException for problems sub tags * @throws PcodeXMLException for problems sub tags
*/ */
private HighSymbol parseSymbolXML(XmlPullParser parser) throws PcodeXMLException { private HighSymbol parseSymbolXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement node = parser.start("mapsym"); HighSymbol res = HighSymbol.restoreMapSymXML(parser, false, func);
String typename = node.getAttribute("type"); insertSymbol(res);
HighSymbol res = null;
if (typename == null) {
res = new MappedSymbol();
}
else if (typename.equals("dynamic")) {
res = new DynamicSymbol();
}
else if (typename.equals("equate")) {
res = new EquateSymbol();
}
int symbolId = res.restoreXML(parser, func);
parser.end(node);
insertSymbol(res,symbolId);
return res; return res;
} }
@ -195,11 +296,11 @@ public class LocalSymbolMap {
parser.end(el); parser.end(el);
} }
private static final Comparator<MappedSymbol> PARAM_SYMBOL_SLOT_COMPARATOR = private static final Comparator<HighSymbol> PARAM_SYMBOL_SLOT_COMPARATOR =
new Comparator<MappedSymbol>() { new Comparator<HighSymbol>() {
@Override @Override
public int compare(MappedSymbol sym1, MappedSymbol sym2) { public int compare(HighSymbol sym1, HighSymbol sym2) {
return sym1.getSlot() - sym2.getSlot(); return sym1.getCategoryIndex() - sym2.getCategoryIndex();
} }
}; };
@ -210,14 +311,14 @@ public class LocalSymbolMap {
*/ */
public void parseSymbolList(XmlPullParser parser) throws PcodeXMLException { public void parseSymbolList(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("symbollist"); XmlElement el = parser.start("symbollist");
ArrayList<MappedSymbol> parms = new ArrayList<MappedSymbol>(); ArrayList<HighSymbol> parms = new ArrayList<HighSymbol>();
while (parser.peek().isStart()) { while (parser.peek().isStart()) {
HighSymbol sym = parseSymbolXML(parser); HighSymbol sym = parseSymbolXML(parser);
if (sym instanceof MappedSymbol && ((MappedSymbol) sym).isParameter()) { if (sym.isParameter()) {
parms.add((MappedSymbol) sym); parms.add(sym);
} }
} }
paramSymbols = new MappedSymbol[parms.size()]; paramSymbols = new HighSymbol[parms.size()];
parms.toArray(paramSymbols); parms.toArray(paramSymbols);
Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR); Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR);
parser.end(el); parser.end(el);
@ -243,7 +344,7 @@ public class LocalSymbolMap {
Iterator<HighSymbol> iter = symbolMap.values().iterator(); Iterator<HighSymbol> iter = symbolMap.values().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
HighSymbol sym = iter.next(); HighSymbol sym = iter.next();
res.append(sym.buildXML()); HighSymbol.buildMapSymXML(res, sym);
} }
res.append("</symbollist>\n"); res.append("</symbollist>\n");
res.append("</scope>\n"); res.append("</scope>\n");
@ -290,18 +391,30 @@ public class LocalSymbolMap {
* @param id symbol-id * @param id symbol-id
* @return variable or null if not found * @return variable or null if not found
*/ */
public HighSymbol getSymbol(int id) { public HighSymbol getSymbol(long id) {
return symbolMap.get(id); return symbolMap.get(id);
} }
/**
* Get the number of parameter symbols in this scope
* @return the number of parameters
*/
public int getNumParams() { public int getNumParams() {
return paramSymbols.length; return paramSymbols.length;
} }
public MappedSymbol getParamSymbol(int i) { /**
* @param i is the desired parameter position
* @return the i-th parameter HighSymbol
*/
public HighSymbol getParamSymbol(int i) {
return paramSymbols[i]; return paramSymbols[i];
} }
/**
* @param i is the desired parameter position
* @return the i-th parameter variable
*/
public HighParam getParam(int i) { public HighParam getParam(int i) {
return (HighParam) paramSymbols[i].getHighVariable(); return (HighParam) paramSymbols[i].getHighVariable();
} }
@ -316,43 +429,64 @@ public class LocalSymbolMap {
return false; return false;
} }
public MappedSymbol newMappedSymbol(String nm, DataType dt, VariableStorage store, protected HighSymbol newMappedSymbol(long id, String nm, DataType dt, VariableStorage store,
Address pcaddr, int slot, int id) { Address pcaddr, int slot) {
MappedSymbol sym = new MappedSymbol(nm, dt, store, pcaddr, func, slot); if (id == 0) {
insertSymbol(sym,id); id = getNextId();
}
HighSymbol sym = new HighSymbol(id, nm, dt, func);
if (slot >= 0) {
sym.setCategory(0, slot);
}
MappedEntry entry = new MappedEntry(sym, store, pcaddr);
sym.addMapEntry(entry);
insertSymbol(sym);
return sym; return sym;
} }
public DynamicSymbol newDynamicSymbol(String nm, DataType dt, int sz, long hash, protected HighSymbol newDynamicSymbol(long id, String nm, DataType dt, long hash,
Address pcaddr, int format, int id) { Address pcaddr) {
DynamicSymbol sym = new DynamicSymbol(nm, dt, sz, func, pcaddr, hash, format); if (id == 0) {
insertSymbol(sym,id); id = getNextId();
}
HighSymbol sym = new HighSymbol(id, nm, dt, func);
DynamicEntry entry = new DynamicEntry(sym, pcaddr, hash);
sym.addMapEntry(entry);
insertSymbol(sym);
return sym; return sym;
} }
private void insertSymbol(HighSymbol sym,int id) { private void insertSymbol(HighSymbol sym) {
if (sym instanceof MappedSymbol) { long uniqueId = sym.getId();
MappedSymbol mapSym = (MappedSymbol)sym; if ((uniqueId >> 56) == (HighSymbol.ID_BASE >> 56)) {
MappedVarKey key = new MappedVarKey(mapSym.getStorage(),mapSym.getPCAddress()); long val = uniqueId & 0x7fffffff;
if (val > uniqueSymbolId) {
uniqueSymbolId = val;
}
}
if (sym.entryList[0] instanceof MappedEntry) {
MappedVarKey key = new MappedVarKey(sym.getStorage(), sym.getPCAddress());
addrMappedSymbols.put(key, sym); addrMappedSymbols.put(key, sym);
} }
symbolMap.put(id, sym); symbolMap.put(uniqueId, sym);
} }
private void newEquateSymbol(String nm, long val, long hash, Address addr, int format, private void newEquateSymbol(long uniqueId, String nm, long val, long hash, Address addr,
TreeMap<String, DynamicSymbol> constantSymbolMap) { TreeMap<String, HighSymbol> constantSymbolMap) {
DynamicSymbol eqSymbol = constantSymbolMap.get(nm); HighSymbol eqSymbol = constantSymbolMap.get(nm);
if (eqSymbol != null) { if (eqSymbol != null) {
eqSymbol.addReference(addr, hash, format); // New reference to same symbol return; // New reference to same symbol
return; }
if (uniqueId == 0) {
uniqueId = getNextId();
} }
int conv = EquateSymbol.convertName(nm, val); int conv = EquateSymbol.convertName(nm, val);
if (conv < 0) { if (conv < 0) {
eqSymbol = new EquateSymbol(nm, val, func, addr, hash, format); eqSymbol = new EquateSymbol(uniqueId, nm, val, func, addr, hash);
eqSymbol.setNameLock(true); eqSymbol.setNameLock(true);
} }
else { else {
eqSymbol = new EquateSymbol(conv, val, func, addr, hash, format); eqSymbol = new EquateSymbol(uniqueId, conv, val, func, addr, hash);
} }
//Do NOT setTypeLock //Do NOT setTypeLock
constantSymbolMap.put(nm, eqSymbol); constantSymbolMap.put(nm, eqSymbol);
@ -361,11 +495,9 @@ public class LocalSymbolMap {
/** /**
* Build dynamic symbols based on equates * Build dynamic symbols based on equates
* @param dbFunction is the function to pull equates for * @param dbFunction is the function to pull equates for
* @param uniqueSymbolId is the next available symbol id
* @return the next available symbol id
*/ */
private int grabEquates(Function dbFunction, int uniqueSymbolId) { private void grabEquates(Function dbFunction) {
TreeMap<String, DynamicSymbol> constantSymbolMap = null; TreeMap<String, HighSymbol> constantSymbolMap = null;
// Find named constants via Equates // Find named constants via Equates
Program program = dbFunction.getProgram(); Program program = dbFunction.getProgram();
EquateTable equateTable = program.getEquateTable(); EquateTable equateTable = program.getEquateTable();
@ -381,9 +513,9 @@ public class LocalSymbolMap {
long hash[] = DynamicHash.calcConstantHash(instr, eq.getValue()); long hash[] = DynamicHash.calcConstantHash(instr, eq.getValue());
for (long element : hash) { for (long element : hash) {
if (constantSymbolMap == null) { if (constantSymbolMap == null) {
constantSymbolMap = new TreeMap<String, DynamicSymbol>(); constantSymbolMap = new TreeMap<String, HighSymbol>();
} }
newEquateSymbol(eq.getDisplayName(), eq.getValue(), element, defAddr, 0, newEquateSymbol(0, eq.getDisplayName(), eq.getValue(), element, defAddr,
constantSymbolMap); constantSymbolMap);
} }
} }
@ -404,17 +536,25 @@ public class LocalSymbolMap {
// Add constant dynamic symbols to map // Add constant dynamic symbols to map
if (constantSymbolMap != null) { if (constantSymbolMap != null) {
for (DynamicSymbol sym : constantSymbolMap.values()) { for (HighSymbol sym : constantSymbolMap.values()) {
symbolMap.put(++uniqueSymbolId, sym); long id = getNextId();
symbolMap.put(id, sym);
} }
} }
return uniqueSymbolId; }
private void grabMerges(ArrayList<String> mergeNames) {
if (mergeNames == null) {
return;
}
Map<String, HighSymbol> nameToSymbolMap = getNameToSymbolMap();
for (String name : mergeNames) {
mergeNamedSymbols(name, nameToSymbolMap);
}
} }
/** /**
* Hashing keys for Local variables * Hashing keys for Local variables
*
*
*/ */
class MappedVarKey { class MappedVarKey {
private Address addr; private Address addr;

View file

@ -0,0 +1,75 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.VariableStorage;
import ghidra.xml.XmlPullParser;
/**
* A normal address based HighSymbol mapping with an associated Data object
*/
public class MappedDataEntry extends MappedEntry {
private Data data; // Backing data object
/**
* Constructor for use with restoreXML
* @param sym is the owning HighSymbol
*/
public MappedDataEntry(HighSymbol sym) {
super(sym);
}
/**
* Construct given a symbol, storage, and a backing Data object
* @param sym the given symbol
* @param store the given storage
* @param d the backing Data object
*/
public MappedDataEntry(HighSymbol sym, VariableStorage store, Data d) {
super(sym, store, null);
data = d;
}
/**
* @return the backing Data object
*/
public Data getData() {
return data;
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
super.restoreXML(parser);
data = symbol.getProgram().getListing().getDataAt(storage.getMinAddress());
}
@Override
public boolean isReadOnly() {
if (data.isConstant()) {
return true;
}
return super.isReadOnly();
}
@Override
public boolean isVolatile() {
if (data.isVolatile()) {
return true;
}
return super.isVolatile();
}
}

View file

@ -0,0 +1,163 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.*;
import ghidra.program.model.data.AbstractFloatDataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* A normal mapping of a HighSymbol to a particular Address, consuming a set number of bytes
*/
public class MappedEntry extends SymbolEntry {
protected VariableStorage storage;
/**
* For use with restoreXML
* @param sym is the owning symbol
*/
public MappedEntry(HighSymbol sym) {
super(sym);
}
/**
* Construct given a symbol, storage, and first-use Address
* @param sym is the given symbol
* @param store is the given storage
* @param addr is the first-use Address (or null)
*/
public MappedEntry(HighSymbol sym, VariableStorage store, Address addr) {
super(sym);
storage = store;
pcaddr = addr;
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
HighFunction function = symbol.function;
Program program = function.getFunction().getProgram();
AddressFactory addrFactory = function.getAddressFactory();
XmlElement addrel = parser.start("addr");
int sz = symbol.type.getLength();
if (sz == 0) {
throw new PcodeXMLException(
"Invalid symbol 0-sized data-type: " + symbol.type.getName());
}
try {
Address varAddr = Varnode.readXMLAddress(addrel, addrFactory);
AddressSpace spc = varAddr.getAddressSpace();
if ((spc == null) || (spc.getType() != AddressSpace.TYPE_VARIABLE)) {
storage = new VariableStorage(program, varAddr, sz);
}
else {
storage = function.readXMLVarnodePieces(addrel, varAddr);
}
}
catch (InvalidInputException e) {
throw new PcodeXMLException("Invalid storage: " + e.getMessage());
}
parser.end(addrel);
parseRangeList(parser);
}
@Override
public void saveXml(StringBuilder buf) {
int logicalsize = 0; // Assume datatype size and storage size are the same
int typeLength = symbol.type.getLength();
if (typeLength != storage.size() && symbol.type instanceof AbstractFloatDataType) {
logicalsize = typeLength; // Force a logicalsize
}
String addrRes = Varnode.buildXMLAddress(storage.getVarnodes(), logicalsize);
buf.append(addrRes);
buildRangelistXML(buf);
}
@Override
public VariableStorage getStorage() {
return storage;
}
@Override
public int getSize() {
return storage.size();
}
@Override
public boolean isReadOnly() {
Address addr = storage.getMinAddress();
if (addr == null) {
return false;
}
boolean readonly = false;
Program program = symbol.getProgram();
MemoryBlock block = program.getMemory().getBlock(addr);
if (block != null) {
readonly = !block.isWrite();
// if the block says read-only, check the refs to the variable
// if the block says read-only, check the refs to the variable
if (readonly) {
ReferenceIterator refIter = program.getReferenceManager().getReferencesTo(addr);
int count = 0;
// boolean foundRead = false;
while (refIter.hasNext() && count < 100) {
Reference ref = refIter.next();
if (ref.getReferenceType().isWrite()) {
readonly = false;
break;
}
if (ref.getReferenceType().isRead()) {
// foundRead = true;
}
count++;
}
// TODO: Don't do override if no read reference found
//
// if we only have indirect refs to it, don't assume readonly!
//if (!foundRead && readonly && count > 1) {
// readonly = false;
//}
// they must be reading it multiple times for some reason
// if (readonly && count > 1) {
// readonly = false;
// }
}
}
return readonly;
}
@Override
public boolean isVolatile() {
Address addr = storage.getMinAddress();
if (addr == null) {
return false;
}
Program program = symbol.getProgram();
if (program.getLanguage().isVolatile(addr)) {
return true;
}
MemoryBlock block = program.getMemory().getBlock(addr);
return (block != null && block.isVolatile());
}
}

View file

@ -1,161 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.ParamEntry;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
public class MappedSymbol extends HighSymbol {
private VariableStorage storage;
private int slot; // parameter slot, -1 for non-parameter
public MappedSymbol() { // For use with restoreXML
}
public MappedSymbol(String name, DataType dt, VariableStorage store, Address pcaddr,
HighFunction func, int slot) {
super(name, dt, store.size(), pcaddr, func);
if (store.size() != dt.getLength()) {
if (ParamEntry.getMetatype(dt) != ParamEntry.TYPE_FLOAT)
throw new IllegalArgumentException("Specified size does not match storage size");
}
this.storage = store;
this.slot = slot;
Varnode rep = function.createFromStorage(null, storage, dt.getLength());
HighVariable var;
if (slot < 0)
var = new HighLocal(type, rep, null, pcaddr, this);
else
var = new HighParam(type, rep, pcaddr, slot, this);
setHighVariable(var);
var.setHighOnInstances();
}
public VariableStorage getStorage() {
return storage;
}
public boolean isParameter() {
return slot >= 0;
}
public int getSlot() {
return slot;
}
@Override
public String buildXML() {
if (storage.getMinAddress() == null) {
return ""; // skip unassigned/bad variable
}
StringBuilder res = new StringBuilder();
int cat = isParameter() ? 0 : -1;
String sym = buildSymbolXML(function.getDataTypeManager(),name, type, size,
isTypeLocked(), isNameLocked(), false, false, cat, slot);
int logicalsize = 0; // Assume datatype size and storage size are the same
if ((type != null) && (type.getLength() != storage.size())) // If sizesdiffer
logicalsize = type.getLength(); // Force a logicalsize
String addrRes = Varnode.buildXMLAddress(storage.getVarnodes(), logicalsize);
buildMapSymXML(res, addrRes, getPCAddress(), sym);
return res.toString();
}
@Override
public int restoreXML(XmlPullParser parser,HighFunction func) throws PcodeXMLException {
XmlElement symel = parser.start("symbol");
int symbolId = restoreSymbolXML(symel, func);
slot = -1;
int cat = -1;
if (symel.hasAttribute("cat")) {
cat = SpecXmlUtils.decodeInt(symel.getAttribute("cat"));
if (cat == 0)
slot = SpecXmlUtils.decodeInt(symel.getAttribute("index"));
}
type = func.getDataTypeManager().readXMLDataType(parser);
parser.end(symel);
if (slot >= 0 && name.startsWith("$$undef")) {
// use default parameter name
name = "param_" + Integer.toString(slot + 1);
}
Program program = function.getFunction().getProgram();
AddressFactory addrFactory = function.getAddressFactory();
Address addr = null;
XmlElement addrel = parser.start("addr");
int sz = type.getLength();
if (sz == 0) {
throw new PcodeXMLException("Invalid symbol 0-sized data-type: " + type.getName());
}
try {
Address varAddr = Varnode.readXMLAddress(addrel, addrFactory);
AddressSpace spc = varAddr.getAddressSpace();
if ((spc == null) || (spc.getType() != AddressSpace.TYPE_VARIABLE)) {
storage = new VariableStorage(program, varAddr, sz);
}
else {
addr = varAddr;
storage = func.readXMLVarnodePieces(addrel, varAddr);
}
}
catch (InvalidInputException e) {
throw new PcodeXMLException("Invalid storage: " + e.getMessage());
}
size = storage.size();
parser.end(addrel);
pcaddr = parseRangeList(parser);
Varnode rep = function.createFromStorage(addr, storage, sz);
HighVariable var;
if (slot < 0)
var = new HighLocal(type, rep, null, pcaddr, this);
else
var = new HighParam(type, rep, pcaddr, slot, this);
setHighVariable(var);
var.setHighOnInstances();
return symbolId;
}
public static String buildSymbolXML(PcodeDataTypeManager dtmanage, String nm,
DataType dt, int length, boolean tl, boolean nl, boolean ro,
boolean isVolatile, int cat, int slot) {
StringBuilder res = new StringBuilder();
res.append("<symbol");
SpecXmlUtils.xmlEscapeAttribute(res, "name", nm);
SpecXmlUtils.encodeBooleanAttribute(res, "typelock", tl);
SpecXmlUtils.encodeBooleanAttribute(res, "namelock", nl);
SpecXmlUtils.encodeBooleanAttribute(res, "readonly", ro);
if (isVolatile)
SpecXmlUtils.encodeBooleanAttribute(res, "volatile", true);
SpecXmlUtils.encodeSignedIntegerAttribute(res, "cat", cat);
if (slot >= 0) {
SpecXmlUtils.encodeSignedIntegerAttribute(res, "index", slot);
}
res.append(">\n");
res.append(dtmanage.buildTypeRef(dt, length));
res.append("</symbol>\n");
return res.toString();
}
}

View file

@ -57,7 +57,8 @@ public interface PcodeFactory {
public VariableStorage buildStorage(Varnode vn) throws InvalidInputException; public VariableStorage buildStorage(Varnode vn) throws InvalidInputException;
public Varnode getRef(int refid); public Varnode getRef(int refid);
public PcodeOp getOpRef(int refid); public PcodeOp getOpRef(int refid);
public HighSymbol getSymbol(int symbolId);
public HighSymbol getSymbol(long symbolId);
public Varnode setInput(Varnode vn,boolean val); public Varnode setInput(Varnode vn,boolean val);
public void setAddrTied(Varnode vn,boolean val); public void setAddrTied(Varnode vn,boolean val);
public void setPersistant(Varnode vn,boolean val); public void setPersistant(Varnode vn,boolean val);

View file

@ -142,8 +142,9 @@ public class PcodeSyntaxTree implements PcodeFactory {
// bad prototypes causing the decompiler to produce weird stuff // bad prototypes causing the decompiler to produce weird stuff
// TODO: We should emit some kind of warning // TODO: We should emit some kind of warning
int sz = 0; int sz = 0;
for (Varnode piece : pieces) for (Varnode piece : pieces) {
sz += piece.getSize(); sz += piece.getSize();
}
Address uniqaddr = addrFactory.getUniqueSpace().getAddress(0x20000000); Address uniqaddr = addrFactory.getUniqueSpace().getAddress(0x20000000);
storage = new VariableStorage(datatypeManager.getProgram(),uniqaddr,sz); storage = new VariableStorage(datatypeManager.getProgram(),uniqaddr,sz);
} }
@ -156,26 +157,30 @@ public class PcodeSyntaxTree implements PcodeFactory {
else { else {
offObject = new Integer((int)offset); offObject = new Integer((int)offset);
offset += roundsize; offset += roundsize;
if (offset > joinAllocate) if (offset > joinAllocate) {
joinAllocate = (int)offset; joinAllocate = (int)offset;
}
} }
if (joinmap == null) if (joinmap == null) {
joinmap = new HashMap<Integer,VariableStorage>(); joinmap = new HashMap<Integer,VariableStorage>();
}
joinmap.put(offObject, storage); joinmap.put(offObject, storage);
return storage; return storage;
} }
private VariableStorage findJoinStorage(long offset) { private VariableStorage findJoinStorage(long offset) {
if (joinmap == null) if (joinmap == null) {
return null; return null;
}
return joinmap.get(new Integer((int)offset)); return joinmap.get(new Integer((int)offset));
} }
@Override @Override
public VariableStorage buildStorage(Varnode vn) throws InvalidInputException { public VariableStorage buildStorage(Varnode vn) throws InvalidInputException {
Address addr = vn.getAddress(); Address addr = vn.getAddress();
if (addr.getAddressSpace().getType() == AddressSpace.TYPE_VARIABLE) if (addr.getAddressSpace().getType() == AddressSpace.TYPE_VARIABLE) {
return findJoinStorage(addr.getOffset()); return findJoinStorage(addr.getOffset());
}
return new VariableStorage(datatypeManager.getProgram(),vn); return new VariableStorage(datatypeManager.getProgram(),vn);
} }
@ -306,10 +311,12 @@ public class PcodeSyntaxTree implements PcodeFactory {
@Override @Override
public Varnode newVarnode(int sz, Address addr, int id) { public Varnode newVarnode(int sz, Address addr, int id) {
Varnode vn = vbank.create(sz, addr, id); Varnode vn = vbank.create(sz, addr, id);
if (uniqId <= id) if (uniqId <= id) {
uniqId = id + 1; uniqId = id + 1;
if (refmap != null) }
if (refmap != null) {
refmap.put(id, vn); refmap.put(id, vn);
}
return vn; return vn;
} }
@ -342,10 +349,12 @@ public class PcodeSyntaxTree implements PcodeFactory {
@Override @Override
public Varnode setInput(Varnode vn, boolean val) { public Varnode setInput(Varnode vn, boolean val) {
if ((!vn.isInput()) && val) if ((!vn.isInput()) && val) {
return vbank.setInput(vn); return vbank.setInput(vn);
if (vn.isInput() && (!val)) }
if (vn.isInput() && (!val)) {
vbank.makeFree(vn); vbank.makeFree(vn);
}
return vn; return vn;
} }
@ -360,13 +369,14 @@ public class PcodeSyntaxTree implements PcodeFactory {
@Override @Override
public Varnode getRef(int id) { public Varnode getRef(int id) {
if (refmap == null) if (refmap == null) {
return null; return null;
}
return refmap.get(id); return refmap.get(id);
} }
@Override @Override
public HighSymbol getSymbol(int symbolId) { public HighSymbol getSymbol(long symbolId) {
return null; return null;
} }
@ -410,8 +420,9 @@ public class PcodeSyntaxTree implements PcodeFactory {
@Override @Override
public PcodeOp getOpRef(int id) { public PcodeOp getOpRef(int id) {
if (oprefmap == null) if (oprefmap == null) {
buildOpRefs(); buildOpRefs();
}
return oprefmap.get(id); return oprefmap.get(id);
} }
@ -437,12 +448,16 @@ public class PcodeSyntaxTree implements PcodeFactory {
public void setOutput(PcodeOp op, Varnode vn) { public void setOutput(PcodeOp op, Varnode vn) {
if (vn == op.getOutput()) if (vn == op.getOutput())
{
return; // Output already set to this return; // Output already set to this
if (op.getOutput() != null) }
if (op.getOutput() != null) {
unSetOutput(op); unSetOutput(op);
}
if (vn.getDef() != null) // Varnode is already an output if (vn.getDef() != null) {
unSetOutput(vn.getDef()); unSetOutput(vn.getDef());
}
vn = vbank.setDef(vn, op); vn = vbank.setDef(vn, op);
op.setOutput(vn); op.setOutput(vn);
} }
@ -450,16 +465,21 @@ public class PcodeSyntaxTree implements PcodeFactory {
public void unSetOutput(PcodeOp op) { public void unSetOutput(PcodeOp op) {
Varnode vn = op.getOutput(); Varnode vn = op.getOutput();
if (vn == null) if (vn == null)
{
return; // Nothing to do return; // Nothing to do
}
op.setOutput(null); op.setOutput(null);
vbank.makeFree(vn); vbank.makeFree(vn);
} }
public void setInput(PcodeOp op, Varnode vn, int slot) { public void setInput(PcodeOp op, Varnode vn, int slot) {
if (slot >= op.getNumInputs()) if (slot >= op.getNumInputs())
{
op.setInput(null, slot); // Expand number of inputs as necessary op.setInput(null, slot); // Expand number of inputs as necessary
if (op.getInput(slot) != null) }
if (op.getInput(slot) != null) {
unSetInput(op, slot); unSetInput(op, slot);
}
if (vn != null) { if (vn != null) {
VarnodeAST vnast = (VarnodeAST) vn; VarnodeAST vnast = (VarnodeAST) vn;
vnast.addDescendant(op); vnast.addDescendant(op);
@ -484,29 +504,35 @@ public class PcodeSyntaxTree implements PcodeFactory {
public void unlink(PcodeOpAST op) { public void unlink(PcodeOpAST op) {
unSetOutput(op); unSetOutput(op);
for (int i = 0; i < op.getNumInputs(); ++i) for (int i = 0; i < op.getNumInputs(); ++i) {
unSetInput(op, i); unSetInput(op, i);
if (op.getParent() != null) }
if (op.getParent() != null) {
unInsert(op); unInsert(op);
}
} }
@Override @Override
public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList<Varnode> inputs, Varnode output) public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList<Varnode> inputs, Varnode output)
throws UnknownInstructionException { throws UnknownInstructionException {
PcodeOp op = opbank.create(opc, inputs.size(), sq); PcodeOp op = opbank.create(opc, inputs.size(), sq);
if (output != null) if (output != null) {
setOutput(op, output); setOutput(op, output);
for (int i = 0; i < inputs.size(); ++i) }
for (int i = 0; i < inputs.size(); ++i) {
setInput(op, inputs.get(i), i); setInput(op, inputs.get(i), i);
if (oprefmap != null) }
if (oprefmap != null) {
oprefmap.put(sq.getTime(), op); oprefmap.put(sq.getTime(), op);
}
return op; return op;
} }
private void readVarnodeXML(XmlPullParser parser) throws PcodeXMLException { private void readVarnodeXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("varnodes"); XmlElement el = parser.start("varnodes");
while (parser.peek().isStart()) while (parser.peek().isStart()) {
Varnode.readXML(parser, this); Varnode.readXML(parser, this);
}
parser.end(el); parser.end(el);
} }
@ -524,8 +550,9 @@ public class PcodeSyntaxTree implements PcodeFactory {
bl.insertEnd(op); bl.insertEnd(op);
} }
int index = bl.getIndex(); int index = bl.getIndex();
while (bblocks.size() <= index) while (bblocks.size() <= index) {
bblocks.add(null); bblocks.add(null);
}
bblocks.set(index, bl); bblocks.set(index, bl);
parser.end(el); parser.end(el);
} }
@ -542,17 +569,20 @@ public class PcodeSyntaxTree implements PcodeFactory {
public void readXML(XmlPullParser parser) throws PcodeXMLException { public void readXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("ast"); XmlElement el = parser.start("ast");
if (!vbank.isEmpty()) if (!vbank.isEmpty()) {
clear(); clear();
}
readVarnodeXML(parser); readVarnodeXML(parser);
buildVarnodeRefs(); // Build the HashMap buildVarnodeRefs(); // Build the HashMap
BlockMap blockMap = new BlockMap(addrFactory); BlockMap blockMap = new BlockMap(addrFactory);
while (parser.peek().isStart()) { while (parser.peek().isStart()) {
XmlElement subel = parser.peek(); XmlElement subel = parser.peek();
if (subel.getName().equals("block")) if (subel.getName().equals("block")) {
readBasicBlockXML(parser, blockMap); // Read a basic block and all its PcodeOps readBasicBlockXML(parser, blockMap); // Read a basic block and all its PcodeOps
else // Read block edges }
else {
readBlockEdgeXML(parser); readBlockEdgeXML(parser);
}
} }
parser.end(el); parser.end(el);
} }

View file

@ -0,0 +1,125 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* A mapping from a HighSymbol object to the storage that holds the symbol's value.
*
*/
public abstract class SymbolEntry {
protected HighSymbol symbol; // The Symbol owning this entry
protected Address pcaddr; // Start of code range where this SymbolEntry applies
/**
* Constructor for use with restoreXML
* @param sym is the symbol owning this entry
*/
public SymbolEntry(HighSymbol sym) {
symbol = sym;
}
/**
* Restore this entry from the given XML stream. Typically more than one tag is consumed
* @param parser is the given XML stream
* @throws PcodeXMLException if the XML is invalid
*/
public abstract void restoreXML(XmlPullParser parser)
throws PcodeXMLException;
/**
* Save this entry as (a set of) XML tags to the given stream
* @param buf is the given stream
*/
public abstract void saveXml(StringBuilder buf);
/**
* Get the storage associated with this particular mapping of the Symbol
* @return the storage object
*/
public abstract VariableStorage getStorage();
/**
* Get the number of bytes consumed by the symbol when using this storage
* @return the size of this entry
*/
public abstract int getSize();
/**
* @return true if the mapped storage is read-only
*/
public abstract boolean isReadOnly();
/**
* @return true if the mapped storage is volatile
*/
public abstract boolean isVolatile();
/**
* The storage used to hold this Symbol may be used for other purposes at different points in
* the code. This returns the earliest address in the code where this storage is used for this symbol
* @return the starting address where the Symbol uses this storage
*/
public Address getPCAdress() {
return pcaddr;
}
protected void parseRangeList(XmlPullParser parser) {
XmlElement rangelistel = parser.start("rangelist");
if (parser.peek().isStart()) {
// we only use this to establish first-use
XmlElement rangeel = parser.start("range");
String spc = rangeel.getAttribute("space");
long offset = SpecXmlUtils.decodeLong(rangeel.getAttribute("first"));
pcaddr = symbol.function.getAddressFactory().getAddressSpace(spc).getAddress(offset);
pcaddr =
symbol.function.getFunction().getEntryPoint().getAddressSpace().getOverlayAddress(
pcaddr);
parser.end(rangeel);
}
parser.end(rangelistel);
}
protected void buildRangelistXML(StringBuilder res) {
if (pcaddr == null || pcaddr.isExternalAddress()) {
res.append("<rangelist/>");
return;
}
res.append("<rangelist>");
AddressSpace space = pcaddr.getAddressSpace();
long off;
if (space.isOverlaySpace()) {
space = space.getPhysicalSpace();
off = space.getAddress(pcaddr.getOffset()).getUnsignedOffset();
}
else {
off = pcaddr.getUnsignedOffset();
}
res.append("<range");
SpecXmlUtils.encodeStringAttribute(res, "space", space.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "first", off);
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "last", off);
res.append("/>");
res.append("</rangelist>\n");
}
}