Merge remote-tracking branch 'origin/GT_3392_DecompilerAPI'

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -360,6 +360,54 @@ void Varnode::setDef(PcodeOp *op)
setFlags(Varnode::coverdirty|Varnode::written);
}
/// The given Symbol's data-type and flags are inherited by \b this Varnode.
/// If the Symbol is \e type-locked, a reference to the Symbol is set on \b this Varnode.
/// \param entry is a mapping to the given Symbol
/// \return \b true if any properties have changed
bool Varnode::setSymbolProperties(SymbolEntry *entry)
{
bool res = entry->updateType(this);
if (entry->getSymbol()->isTypeLocked()) {
if (mapentry != entry) {
mapentry = entry;
if (high != (HighVariable *)0)
high->setSymbol(this);
res = true;
}
}
setFlags(entry->getAllFlags() & ~Varnode::typelock);
return res;
}
/// A reference to the given Symbol is set on \b this Varnode.
/// The data-type on \b this Varnode is not changed.
/// \param entry is a mapping to the given Symbol
void Varnode::setSymbolEntry(SymbolEntry *entry)
{
mapentry = entry;
uint4 fl = Varnode::mapped; // Flags are generally not changed, but we do mark this as mapped
if (entry->getSymbol()->isNameLocked())
fl |= Varnode::namelock;
setFlags(fl);
if (high != (HighVariable *)0)
high->setSymbol(this);
}
/// Link Symbol information to \b this as a \b reference. This only works for a constant Varnode.
/// This used when there is a constant address reference to the Symbol and the Varnode holds the
/// reference, not the actual value of the Symbol.
/// \param entry is a mapping to the given Symbol
/// \off is the byte offset into the Symbol of the reference
void Varnode::setSymbolReference(SymbolEntry *entry,int4 off)
{
if (high != (HighVariable *)0) {
high->setSymbolReference(entry->getSymbol(), off);
}
}
/// Change the Datatype and lock state associated with this Varnode if various conditions are met
/// - Don't change a previously locked Datatype (unless \b override flag is \b true)
/// - Don't consider an \b undefined type to be locked
@ -394,8 +442,11 @@ void Varnode::copySymbol(const Varnode *vn)
mapentry = vn->mapentry; // Copy any symbol
flags &= ~(Varnode::typelock | Varnode::namelock);
flags |= (Varnode::typelock | Varnode::namelock) & vn->flags;
if (high != (HighVariable *)0)
if (high != (HighVariable *)0) {
high->typeDirty();
if (mapentry != (SymbolEntry *)0)
high->setSymbol(this);
}
}
/// Symbol information (if present) is copied from the given constant Varnode into \b this,

View file

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

View file

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

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (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) {
XmlElement node = parser.peek();
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);
if (sym == null) {
Msg.error(this, "Invalid symbol reference: " + symref);
@ -65,9 +64,5 @@ public class ClangVariableDecl extends ClangTokenGroup {
}
typevar = sym.getHighVariable();
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) {
func = listing.getFunctionAt(extRef.getToAddress());
if (func == null) {
String res = HighFunction.buildFunctionShellXML(extRef.getLabel(), addr);
return buildResult(addr, null, res, null);
HighSymbol shellSymbol =
new HighFunctionShellSymbol(0, extRef.getLabel(), addr, dtmanage);
return buildResult(shellSymbol, null);
}
}
else {
@ -690,13 +691,12 @@ public class DecompileCallback {
int extrapop = getExtraPopOverride(func, addr);
hfunc.grabFromFunction(extrapop, false, (extrapop != default_extrapop));
String res = hfunc.buildFunctionXML(addr, 2);
HighSymbol funcSymbol = new HighFunctionSymbol(addr, 2, hfunc);
Namespace namespc = func.getParentNamespace();
if (debug != null) {
debug.getFNTypes(hfunc);
}
res = buildResult(addr, null, res, namespc);
return res;
return buildResult(funcSymbol, namespc);
}
catch (Exception e) {
Msg.error(this,
@ -790,7 +790,7 @@ public class DecompileCallback {
return name;
}
private String buildResult(Address addr, Address pc, String sym, Namespace namespc) {
private String buildResult(HighSymbol highSymbol, Namespace namespc) {
StringBuilder res = new StringBuilder();
res.append("<result>\n");
res.append("<parent>\n");
@ -801,16 +801,15 @@ public class DecompileCallback {
HighFunction.createNamespaceTag(res, namespc);
}
res.append("</parent>\n");
String addrRes = Varnode.buildXMLAddress(addr);
if (debug != null) {
StringBuilder res2 = new StringBuilder();
HighSymbol.buildMapSymXML(res2, addrRes, pc, sym);
HighSymbol.buildMapSymXML(res2, highSymbol);
String res2string = res2.toString();
debug.getMapped(namespc, res2string);
res.append(res2string);
}
else {
HighSymbol.buildMapSymXML(res, addrRes, pc, sym);
HighSymbol.buildMapSymXML(res, highSymbol);
}
res.append("</result>\n");
@ -818,30 +817,25 @@ public class DecompileCallback {
}
private String buildData(Data data) { // Convert global variable to XML
Address addr = data.getMinAddress();
Symbol sym = data.getPrimarySymbol();
boolean readonly = data.isConstant();
boolean isVolatile = data.isVolatile();
if (!readonly) {
readonly = isReadOnlyNoData(addr);
HighCodeSymbol highSymbol;
if (sym != null) {
highSymbol = new HighCodeSymbol(sym.getID(), sym.getName(), data, dtmanage);
}
if (!isVolatile) {
isVolatile = isVolatileNoData(addr);
else {
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) {
debug.getType(data.getDataType());
debug.getType(highSymbol.getDataType());
}
Namespace namespc = (sym != null) ? sym.getParentNamespace() : null;
return buildResult(addr, null, symstring, namespc);
return buildResult(highSymbol, namespc);
}
private StringBuilder buildRegister(Register reg) {
@ -861,28 +855,9 @@ public class DecompileCallback {
* @return the XML description
*/
private String buildLabel(Symbol sym, Address addr) {
// TODO: Assume this is not data
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");
HighSymbol labelSymbol = new HighLabelSymbol(sym.getName(), addr, dtmanage);
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,
(extrapop != default_extrapop));
String funcsym = hfunc.buildFunctionXML(entry, (int) (diff + 1));
HighSymbol functionSymbol = new HighFunctionSymbol(entry, (int) (diff + 1), hfunc);
Namespace namespc = func.getParentNamespace();
if (debug != null) {
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) {
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
// object could resolve the physical address where the dll
// 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
// no attempt to get a realistic linked address. This works because
// we never read bytes or look up code units at the address.
resBuf.append("</externrefsymbol>\n");
return buildResult(addr, null, resBuf.toString(), null);
HighSymbol externSymbol = new HighExternalSymbol(ref.getLabel(), addr, addr, dtmanage);
return buildResult(externSymbol, null);
}
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
ClangToken token = decompilerPanel.getSelectedToken();
if (token == null) {
token = decompilerPanel.getTokenAtCursor();
token = context.getTokenAtCursor();
}
Varnode varnode = DecompilerUtils.getVarnodeRef(token);

View file

@ -21,9 +21,13 @@ import docking.ActionContext;
import docking.action.DockingActionIf;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.context.RestrictedAddressSetContext;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangTokenGroup;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.HighFunction;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
import utility.function.Callback;
@ -33,6 +37,9 @@ public class DecompilerActionContext extends NavigatableActionContext
private final Address functionEntryPoint;
private final boolean isDecompiling;
private ClangToken tokenAtCursor = null;
private boolean tokenIsInitialized = false;
public DecompilerActionContext(DecompilerProvider provider, Address functionEntryPoint,
boolean isDecompiling) {
super(provider, provider);
@ -53,6 +60,18 @@ public class DecompilerActionContext extends NavigatableActionContext
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() {
return getComponentProvider().getDecompilerPanel();
}
@ -61,11 +80,23 @@ public class DecompilerActionContext extends NavigatableActionContext
return getComponentProvider().getController().getFunction();
}
public HighFunction getHighFunction() {
return getComponentProvider().getController().getHighFunction();
}
public ClangTokenGroup getCCodeModel() {
return getComponentProvider().getController().getCCodeModel();
}
public boolean hasRealFunction() {
Function f = getFunction();
return f != null && !(f instanceof UndefinedFunction);
}
public void setStatusMessage(String msg) {
getComponentProvider().getController().setStatusMessage(msg);
}
/**
* The companion method of {@link #checkActionEnablement(Supplier)}. Decompiler actions
* 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 lockProtoAction;
private DockingAction lockLocalAction;
private DockingAction renameVarAction;
private DockingAction retypeVarAction;
private DockingAction renameLocalAction;
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 overrideSigAction;
private DockingAction deleteSigAction;
@ -754,16 +760,16 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String functionGroup = "1 - Function Group";
int subGroupPosition = 0;
specifyCProtoAction = new SpecifyCPrototypeAction(tool, controller);
specifyCProtoAction = new SpecifyCPrototypeAction();
setGroupInfo(specifyCProtoAction, functionGroup, subGroupPosition++);
overrideSigAction = new OverridePrototypeAction(tool, controller);
overrideSigAction = new OverridePrototypeAction();
setGroupInfo(overrideSigAction, functionGroup, subGroupPosition++);
deleteSigAction = new DeletePrototypeOverrideAction(tool, controller);
deleteSigAction = new DeletePrototypeOverrideAction();
setGroupInfo(deleteSigAction, functionGroup, subGroupPosition++);
renameFunctionAction = new RenameFunctionAction(tool, controller);
renameFunctionAction = new RenameFunctionAction();
setGroupInfo(renameFunctionAction, functionGroup, subGroupPosition++);
//
@ -772,17 +778,35 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String variableGroup = "2 - Variable Group";
subGroupPosition = 0; // reset for the next group
renameVarAction = new RenameVariableAction(tool, controller);
setGroupInfo(renameVarAction, variableGroup, subGroupPosition++);
renameLocalAction = new RenameLocalAction();
setGroupInfo(renameLocalAction, variableGroup, subGroupPosition++);
retypeVarAction = new RetypeVariableAction(tool, controller);
setGroupInfo(retypeVarAction, variableGroup, subGroupPosition++);
renameGlobalAction = new RenameGlobalAction();
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 =
new DecompilerStructureVariableAction(owner, tool, controller);
setGroupInfo(decompilerCreateStructureAction, variableGroup, subGroupPosition++);
editDataTypeAction = new EditDataTypeAction(tool, controller);
editDataTypeAction = new EditDataTypeAction();
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
//
@ -796,10 +820,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String commitGroup = "3 - Commit Group";
subGroupPosition = 0; // reset for the next group
lockProtoAction = new CommitParamsAction(tool, controller);
lockProtoAction = new CommitParamsAction();
setGroupInfo(lockProtoAction, commitGroup, subGroupPosition++);
lockLocalAction = new CommitLocalsAction(tool, controller);
lockLocalAction = new CommitLocalsAction();
setGroupInfo(lockLocalAction, commitGroup, subGroupPosition++);
subGroupPosition = 0; // reset for the next group
@ -809,19 +833,19 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
//
String highlightGroup = "4a - Highlight Group";
tool.setMenuGroup(new String[] { "Highlight" }, highlightGroup);
defUseHighlightAction = new HighlightDefinedUseAction(controller);
defUseHighlightAction = new HighlightDefinedUseAction();
setGroupInfo(defUseHighlightAction, highlightGroup, subGroupPosition++);
forwardSliceAction = new ForwardSliceAction(controller);
forwardSliceAction = new ForwardSliceAction();
setGroupInfo(forwardSliceAction, highlightGroup, subGroupPosition++);
backwardSliceAction = new BackwardsSliceAction();
setGroupInfo(backwardSliceAction, highlightGroup, subGroupPosition++);
forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction(controller);
forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction();
setGroupInfo(forwardSliceToOpsAction, highlightGroup, subGroupPosition++);
backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction(controller);
backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction();
setGroupInfo(backwardSliceToOpsAction, highlightGroup, subGroupPosition++);
tool.setMenuGroup(new String[] { "Secondary Highlight" }, highlightGroup);
@ -850,7 +874,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String searchGroup = "comment2 - Search Group";
subGroupPosition = 0; // reset for the next group
findAction = new FindAction(tool, controller);
findAction = new FindAction();
setGroupInfo(findAction, searchGroup, subGroupPosition++);
//
@ -865,7 +889,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
findReferencesAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
FindReferencesToSymbolAction findReferencesToSymbolAction =
new FindReferencesToSymbolAction(tool);
new FindReferencesToSymbolAction();
setGroupInfo(findReferencesToSymbolAction, searchGroup, subGroupPosition++);
findReferencesToSymbolAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
addLocalAction(findReferencesToSymbolAction);
@ -889,8 +913,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
// These actions are not in the popup menu
//
debugFunctionAction = new DebugDecompilerAction(controller);
convertAction = new ExportToCAction(controller);
cloneDecompilerAction = new CloneDecompilerAction(this, controller);
convertAction = new ExportToCAction();
cloneDecompilerAction = new CloneDecompilerAction();
addLocalAction(refreshAction);
addLocalAction(selectAllAction);
@ -901,12 +925,18 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
addLocalAction(backwardSliceToOpsAction);
addLocalAction(lockProtoAction);
addLocalAction(lockLocalAction);
addLocalAction(renameVarAction);
addLocalAction(renameLocalAction);
addLocalAction(renameGlobalAction);
addLocalAction(renameFieldAction);
addLocalAction(setSecondaryHighlightAction);
addLocalAction(setSecondaryHighlightColorChooserAction);
addLocalAction(removeSecondaryHighlightAction);
addLocalAction(removeAllSecondadryHighlightsAction);
addLocalAction(retypeVarAction);
addLocalAction(retypeLocalAction);
addLocalAction(retypeGlobalAction);
addLocalAction(retypeReturnAction);
addLocalAction(retypeFieldAction);
addLocalAction(isolateVarAction);
addLocalAction(decompilerCreateStructureAction);
tool.addAction(listingCreateStructureAction);
addLocalAction(editDataTypeAction);
@ -943,7 +973,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private void graphServiceAdded() {
if (graphASTControlFlowAction == null && tool.getService(GraphService.class) != null) {
graphASTControlFlowAction = new GraphASTControlFlowAction(plugin, controller);
graphASTControlFlowAction = new GraphASTControlFlowAction();
addLocalAction(graphASTControlFlowAction);
}
}

View file

@ -17,8 +17,16 @@ package ghidra.app.plugin.core.decompile.actions;
import docking.ActionContext;
import docking.action.DockingAction;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.DecompilePlugin;
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
@ -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
*

View file

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

View file

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

View file

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

View file

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

View file

@ -16,55 +16,35 @@
package ghidra.app.plugin.core.decompile.actions;
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.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighFunctionDBUtil;
import ghidra.program.model.symbol.SourceType;
public class CommitLocalsAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public CommitLocalsAction(PluginTool tool, DecompilerController controller) {
public CommitLocalsAction() {
super("Commit Locals");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Commit Locals" }, "Commit"));
setDescription(
"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
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
if (!context.hasRealFunction()) {
return false;
}
return getHighFunction() != null;
return context.getHighFunction() != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = controller.getProgram();
Program program = context.getProgram();
int transaction = program.startTransaction("Commit Params/Return");
try {
HighFunction hfunc = getHighFunction();
HighFunction hfunc = context.getHighFunction();
HighFunctionDBUtil.commitLocalsToDatabase(hfunc, SourceType.USER_DEFINED);
}
finally {

View file

@ -19,12 +19,7 @@ import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
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.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighFunctionDBUtil;
@ -33,48 +28,29 @@ import ghidra.util.Msg;
import ghidra.util.exception.*;
public class CommitParamsAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public CommitParamsAction(PluginTool tool, DecompilerController controller) {
public CommitParamsAction() {
super("Commit Params/Return");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Commit Params/Return" }, "Commit"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_P, 0));
setDescription(
"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
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
if (!context.hasRealFunction()) {
return false;
}
return getHighFunction() != null;
return context.getHighFunction() != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = controller.getProgram();
Program program = context.getProgram();
int transaction = program.startTransaction("Commit Params/Return");
try {
HighFunction hfunc = getHighFunction();
HighFunction hfunc = context.getHighFunction();
SourceType source = SourceType.ANALYSIS;
if (hfunc.getFunction().getSignatureSource() == 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;
public class DebugDecompilerAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private DecompilerController controller;
public DebugDecompilerAction(DecompilerController controller) {
super("Debug Function Decompilation");
@ -37,12 +38,12 @@ public class DebugDecompilerAction extends AbstractDecompilerAction {
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return controller.getFunction() != null;
return context.getFunction() != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
JComponent parentComponent = controller.getDecompilerPanel();
JComponent parentComponent = context.getDecompilerPanel();
GhidraFileChooser fileChooser = new GhidraFileChooser(parentComponent);
fileChooser.setTitle("Please Choose Output File");
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 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.framework.plugintool.PluginTool;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
@ -31,17 +28,13 @@ import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public DeletePrototypeOverrideAction(PluginTool tool, DecompilerController controller) {
public DeletePrototypeOverrideAction() {
super("Remove Signature Override");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Remove Signature Override" }, "Decompile"));
}
public static CodeSymbol getSymbol(DecompilerController controller) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
public static CodeSymbol getSymbol(Function func, ClangToken tokenAtCursor) {
if (tokenAtCursor == null) {
return null;
}
@ -49,7 +42,6 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
if (addr == null) {
return null;
}
Function func = controller.getFunction();
Namespace overspace = HighFunction.findOverrideSpace(func);
if (overspace == null) {
return null;
@ -76,25 +68,25 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction();
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
return getSymbol(controller) != null;
return getSymbol(function, context.getTokenAtCursor()) != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
CodeSymbol sym = getSymbol(controller);
Function func = controller.getFunction();
Function func = context.getFunction();
CodeSymbol sym = getSymbol(func, context.getTokenAtCursor());
Program program = func.getProgram();
SymbolTable symtab = program.getSymbolTable();
int transaction = program.startTransaction("Remove Override Signature");
boolean commit = true;
if (!symtab.removeSymbolSpecial(sym)) {
commit = false;
Msg.showError(getClass(), controller.getDecompilerPanel(),
Msg.showError(getClass(), context.getDecompilerPanel(),
"Removing Override Signature Failed", "Error removing override signature");
}
program.endTransaction(transaction, commit);

View file

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

View file

@ -23,7 +23,7 @@ import docking.action.ToolBarData;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
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.preferences.Preferences;
import ghidra.util.Msg;
@ -33,11 +33,9 @@ import resources.ResourceManager;
public class ExportToCAction extends AbstractDecompilerAction {
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 final DecompilerController controller;
public ExportToCAction(DecompilerController controller) {
public ExportToCAction() {
super("Export to C");
this.controller = controller;
setToolBarData(new ToolBarData(EXPORT_ICON, "Local"));
setDescription("Export the current function to C");
}
@ -55,11 +53,11 @@ public class ExportToCAction extends AbstractDecompilerAction {
Preferences.store();
}
private File getFile() {
private File getFile(DecompilerPanel decompilerPanel) {
File lastUsedFile = readLastUsedFile();
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"));
if (lastUsedFile != null) {
fileChooser.setSelectedFile(lastUsedFile);
@ -87,19 +85,19 @@ public class ExportToCAction extends AbstractDecompilerAction {
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return controller.getFunction() != null && controller.getCCodeModel() != null;
return context.getFunction() != null && context.getCCodeModel() != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
File file = getFile();
File file = getFile(context.getDecompilerPanel());
if (file == null) {
return;
}
if (file.exists()) {
if (OptionDialog.showYesNoDialog(controller.getDecompilerPanel(),
if (OptionDialog.showYesNoDialog(context.getDecompilerPanel(),
"Overwrite Existing File?",
"Do you want to overwrite the existing file?") == OptionDialog.OPTION_TWO) {
return;
@ -108,16 +106,16 @@ public class ExportToCAction extends AbstractDecompilerAction {
try {
PrintWriter writer = new PrintWriter(new FileOutputStream(file));
ClangTokenGroup grp = controller.getCCodeModel();
PrettyPrinter printer = new PrettyPrinter(controller.getFunction(), grp);
ClangTokenGroup grp = context.getCCodeModel();
PrettyPrinter printer = new PrettyPrinter(context.getFunction(), grp);
DecompiledFunction decompFunc = printer.print(true);
writer.write(decompFunc.getC());
writer.close();
controller.setStatusMessage(
context.setStatusMessage(
"Successfully exported function(s) to " + file.getAbsolutePath());
}
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);
}
}

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

View file

@ -24,28 +24,25 @@ import docking.action.MenuData;
import docking.widgets.*;
import docking.widgets.fieldpanel.field.Field;
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.framework.plugintool.PluginTool;
import ghidra.util.HelpLocation;
public class FindAction extends AbstractDecompilerAction {
private FindDialog findDialog;
private final DecompilerPanel decompilerPanel;
private final PluginTool tool;
public FindAction(PluginTool tool, DecompilerController controller) {
public FindAction() {
super("Find");
this.tool = tool;
this.decompilerPanel = controller.getDecompilerPanel();
setPopupMenuData(new MenuData(new String[] { "Find..." }, "Decompile"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
setEnabled(true);
}
protected FindDialog getFindDialog() {
protected FindDialog getFindDialog(DecompilerPanel decompilerPanel) {
if (findDialog == null) {
findDialog = new FindDialog("Decompiler Find Text", new DecompilerSearcher()) {
findDialog =
new FindDialog("Decompiler Find Text", new DecompilerSearcher(decompilerPanel)) {
@Override
protected void dialogClosed() {
// clear the search results when the dialog is closed
@ -57,7 +54,13 @@ public class FindAction extends AbstractDecompilerAction {
return findDialog;
}
private class DecompilerSearcher implements FindDialogSearcher {
private static class DecompilerSearcher implements FindDialogSearcher {
private DecompilerPanel decompilerPanel;
public DecompilerSearcher(DecompilerPanel dPanel) {
decompilerPanel = dPanel;
}
@Override
public CursorPosition getCursorPosition() {
@ -119,13 +122,14 @@ public class FindAction extends AbstractDecompilerAction {
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
FindDialog dialog = getFindDialog();
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
FindDialog dialog = getFindDialog(decompilerPanel);
String text = decompilerPanel.getHighlightedText();
if (text != null) {
dialog.setSearchText(text);
}
// 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.navigation.locationreferences.LocationReferencesService;
import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
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";
public static final String NAME = "Find References to Symbol";
private PluginTool tool;
public FindReferencesToSymbolAction(PluginTool tool) {
public FindReferencesToSymbolAction() {
super(NAME);
this.tool = tool;
setPopupMenuData(
new MenuData(new String[] { LocationReferencesService.MENU_GROUP, MENU_ITEM_TEXT }));
@ -116,7 +113,8 @@ public class FindReferencesToSymbolAction extends AbstractDecompilerAction {
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
LocationReferencesService service = tool.getService(LocationReferencesService.class);
LocationReferencesService service =
context.getTool().getService(LocationReferencesService.class);
if (service == null) {
Msg.showError(this, null, "Missing Plugin",
"The " + LocationReferencesService.class.getSimpleName() + " is not installed.\n" +

View file

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

View file

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

View file

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

View file

@ -19,37 +19,35 @@ import java.util.Set;
import docking.action.MenuData;
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.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
public class HighlightDefinedUseAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public HighlightDefinedUseAction(DecompilerController controller) {
public HighlightDefinedUseAction() {
super("Highlight Defined Use");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Highlight", "Def-use" }, "Decompile"));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
ClangToken tokenAtCursor = context.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
return varnode != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
ClangToken tokenAtCursor = context.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode == null) {
return;
}
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
decompilerPanel.clearPrimaryHighlights();
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.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.plugin.core.function.EditFunctionSignatureDialog;
import ghidra.framework.plugintool.PluginTool;
@ -36,9 +34,6 @@ import ghidra.util.exception.CancelledException;
public class OverridePrototypeAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private final PluginTool tool;
public class ProtoOverrideDialog extends EditFunctionSignatureDialog {
private FunctionDefinition functionDefinition;
@ -86,21 +81,18 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
}
}
public OverridePrototypeAction(PluginTool tool, DecompilerController controller) {
public OverridePrototypeAction() {
super("Override Signature");
this.tool = tool;
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Override Signature" }, "Decompile"));
}
/**
* 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
*/
public static PcodeOp getCallOp(DecompilerController controller) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
public static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) {
if (tokenAtCursor == null) {
return null;
}
@ -112,7 +104,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
if (addr == null) {
return null;
}
Instruction instr = controller.getProgram().getListing().getInstructionAt(addr);
Instruction instr = program.getListing().getInstructionAt(addr);
if (instr == null) {
return null;
}
@ -135,12 +127,11 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
return null;
}
private Function getCalledFunction(PcodeOp op) {
private Function getCalledFunction(Program program, PcodeOp op) {
if (op.getOpcode() != PcodeOp.CALL) {
return null;
}
Address addr = op.getInput(0).getAddress();
Program program = controller.getProgram();
FunctionManager functionManager = program.getFunctionManager();
Function function = functionManager.getFunctionAt(addr);
if (function != null) {
@ -196,26 +187,26 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction();
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
return getCallOp(controller) != null;
return getCallOp(context.getProgram(), context.getTokenAtCursor()) != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Function func = controller.getFunction();
Function func = context.getFunction();
Program program = func.getProgram();
PcodeOp op = getCallOp(controller);
Function calledfunc = getCalledFunction(op);
PcodeOp op = getCallOp(program, context.getTokenAtCursor());
Function calledfunc = getCalledFunction(program, op);
boolean varargs = false;
if (calledfunc != null) {
varargs = calledfunc.hasVarArgs();
}
if ((op.getOpcode() == PcodeOp.CALL) && !varargs) {
if (OptionDialog.showOptionDialog(controller.getDecompilerPanel(),
if (OptionDialog.showOptionDialog(context.getDecompilerPanel(),
"Warning : Localized Override",
"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" +
@ -233,6 +224,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
}
String signature = generateSignature(op, name);
PluginTool tool = context.getTool();
ProtoOverrideDialog dialog = new ProtoOverrideDialog(tool, func, signature, conv);
// dialog.setHelpLocation( new HelpLocation( getOwner(), "Edit_Function_Signature" ) );
tool.showDialog(dialog);
@ -247,7 +239,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
commit = true;
}
catch (Exception e) {
Msg.showError(getClass(), controller.getDecompilerPanel(), "Override Signature Failed",
Msg.showError(getClass(), context.getDecompilerPanel(), "Override Signature Failed",
"Error overriding signature: " + e);
}
finally {

View file

@ -44,20 +44,20 @@ public class RemoveSecondaryHighlightAction extends AbstractDecompilerAction {
return false;
}
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
ClangToken token = context.getTokenAtCursor();
if (token == null) {
return false;
}
DecompilerPanel panel = context.getDecompilerPanel();
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
return highlightedTokens.contains(token);
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
ClangToken token = context.getTokenAtCursor();
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
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 ghidra.app.decompiler.ClangFuncNameToken;
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.util.AddEditDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.UndefinedFunction;
public class RenameFunctionAction extends AbstractDecompilerAction {
private final DecompilerController controller;
private final PluginTool tool;
public RenameFunctionAction(PluginTool tool, DecompilerController controller) {
public RenameFunctionAction() {
super("Rename Function");
this.tool = tool;
this.controller = controller;
setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0));
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
// If there isn't one, just use the function we are in.
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
if (tokenAtCursor instanceof ClangFuncNameToken) {
return DecompilerUtils.getFunction(controller.getProgram(),
(ClangFuncNameToken) tokenAtCursor);
return DecompilerUtils.getFunction(program, (ClangFuncNameToken) tokenAtCursor);
}
return null;
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function func = getFunction();
Function func =
getFunction(context.getProgram(), context.getTokenAtCursor());
return func != null && !(func instanceof UndefinedFunction);
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Function function = getFunction();
AddEditDialog dialog = new AddEditDialog("Edit Function Name", tool);
dialog.editLabel(function.getSymbol(), controller.getProgram());
Program program = context.getProgram();
Function function = getFunction(program, context.getTokenAtCursor());
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;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
@ -24,14 +26,13 @@ import ghidra.util.exception.InvalidInputException;
public class RenameGlobalVariableTask extends RenameTask {
private Address address; // Address of global variable
private Program program;
private SymbolTable symboltable;
private Symbol symbol;
public RenameGlobalVariableTask(PluginTool tool, String old, Address addr, Program prog) {
super(tool, old);
public RenameGlobalVariableTask(PluginTool tool, Program program, DecompilerPanel panel,
ClangToken token, Address addr) {
super(tool, program, panel, token, token.getText());
address = addr;
program = prog;
symboltable = program.getSymbolTable();
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;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeComponent;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.Undefined1DataType;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
@ -28,8 +28,9 @@ public class RenameStructureFieldTask extends RenameTask {
private Structure structure;
public int offset;
public RenameStructureFieldTask(PluginTool tool,String old,Structure structure,int offset) {
super(tool,old);
public RenameStructureFieldTask(PluginTool tool, Program program, DecompilerPanel panel,
ClangToken token, Structure structure, int offset) {
super(tool, program, panel, token, token.getText());
this.structure = structure;
this.offset = offset;
}
@ -45,8 +46,9 @@ public class RenameStructureFieldTask extends RenameTask {
DataType newtype = new Undefined1DataType();
structure.replaceAtOffset(offset, newtype,1, newName, "Created by retype action");
}
else
else {
comp.setFieldName(newName);
}
}
@Override
@ -58,10 +60,12 @@ public class RenameStructureFieldTask extends RenameTask {
public boolean isValid(String newNm) {
newName = newNm;
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
String fieldname = comp[i].getFieldName();
if (fieldname == null) continue;
String fieldname = element.getFieldName();
if (fieldname == null) {
continue;
}
if (fieldname.equals(newName)) {
errorMsg = "Duplicate Field Name";
return false;

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (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.InputDialogListener;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
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.InvalidInputException;
@ -31,9 +36,16 @@ public abstract class RenameTask {
protected String oldName;
protected String errorMsg = null; // Error to return if isValid returns false
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.program = program;
this.decompilerPanel = panel;
this.tokenAtCursor = token;
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
* @param oldNameIsCancel is true if the user keeping/entering the old name is considered a cancel
* @return true unless the user canceled
*/
public boolean runDialog() {
private boolean runDialog(boolean oldNameIsCancel) {
InputDialogListener listener = new InputDialogListener() {
@Override
public boolean inputIsValid(InputDialog dialog) {
String name = dialog.getValue();
if ((name==null)||(name.length()==0)) {
@ -62,8 +76,9 @@ public abstract class RenameTask {
return true; // but valid (ends up being equivalent to cancel
}
boolean res = isValid(name);
if (!res)
if (!res) {
dialog.setStatusText(errorMsg);
}
return res;
}
};
@ -77,10 +92,42 @@ public abstract class RenameTask {
if (renameVarDialog.isCanceled()) {
return false;
}
if (newName.equals(oldName)) // No change to name
return false;
if (oldNameIsCancel && newName.equals(oldName)) {
return false;
}
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;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.framework.plugintool.PluginTool;
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.program.model.symbol.SymbolTable;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
public class RenameVariableTask extends RenameTask {
private HighVariable var;
private HighSymbol highSymbol;
private Varnode exactSpot;
private HighFunction hfunction;
private Program program;
private Function function;
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 signatureSrcType; // Signature source type of the function (which will be preserved)
public RenameVariableTask(PluginTool tool, String old, HighFunction hfunc, HighVariable v,
Varnode ex, SourceType st) {
super(tool, old);
var = v;
exactSpot = ex;
hfunction = hfunc;
function = hfunc.getFunction();
program = function.getProgram();
public RenameVariableTask(PluginTool tool, Program program, DecompilerPanel panel,
ClangToken token, HighSymbol sym, SourceType st) {
super(tool, program, panel, token, sym.getName());
highSymbol = sym;
exactSpot = token.getVarnode();
hfunction = sym.getHighFunction();
function = hfunction.getFunction();
srctype = st;
signatureSrcType = function.getSignatureSource();
}
@ -55,39 +54,40 @@ public class RenameVariableTask extends RenameTask {
HighFunctionDBUtil.commitReturnToDatabase(hfunction, signatureSrcType);
}
}
HighFunctionDBUtil.updateDBVariable(var, newName, null, srctype);
HighFunctionDBUtil.updateDBVariable(highSymbol, newName, null, srctype);
}
@Override
public boolean isValid(String newNm) {
newName = newNm;
LocalSymbolMap localSymbolMap = hfunction.getLocalSymbolMap();
if (localSymbolMap.containsVariableWithName(newName) || isSymbolInFunction(newName)) {
if (localSymbolMap.containsVariableWithName(newName) ||
isSymbolInFunction(function, newName)) {
errorMsg = "Duplicate name";
return false;
}
commitRequired = RetypeVariableAction.checkFullCommit(var, hfunction);
commitRequired = AbstractDecompilerAction.checkFullCommit(highSymbol, hfunction);
if (commitRequired) {
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 {
var = hfunction.splitOutMergeGroup(var, exactSpot);
HighVariable var = hfunction.splitOutMergeGroup(exactSpot.getHigh(), exactSpot);
highSymbol = var.getSymbol();
}
catch (PcodeException e) {
errorMsg = "Rename Failed: " + e.getMessage();
return false;
}
}
if (highSymbol == null) {
errorMsg = "Rename Failed: No symbol";
return false;
}
return true;
}
private boolean isSymbolInFunction(String name) {
SymbolTable symbolTable = program.getSymbolTable();
return !symbolTable.getSymbols(name, function).isEmpty();
}
@Override
public String getTransactionName() {
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 ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.ClangHighlightController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
/**
@ -40,8 +39,7 @@ public class SetSecondaryHighlightAction extends AbstractSetSecondaryHighlightAc
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
panel.addSecondaryHighlight(token);
ClangToken token = context.getTokenAtCursor();
context.getDecompilerPanel().addSecondaryHighlight(token);
}
}

View file

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

View file

@ -20,7 +20,7 @@ import java.util.List;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangFuncNameToken;
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.function.editor.*;
import ghidra.app.services.DataTypeManagerService;
@ -33,13 +33,9 @@ import ghidra.program.model.symbol.SourceType;
import ghidra.util.UndefinedFunction;
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");
this.tool = tool;
this.controller = controller;
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
* 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
// 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) {
Function tokenFunction = DecompilerUtils.getFunction(controller.getProgram(),
Function tokenFunction = DecompilerUtils.getFunction(function.getProgram(),
(ClangFuncNameToken) tokenAtCursor);
if (tokenFunction != null) {
function = tokenFunction;
@ -153,22 +148,24 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction();
Function function = context.getFunction();
if (function instanceof UndefinedFunction) {
return false;
}
return getFunction() != null;
return getFunction(function, context.getTokenAtCursor()) != null;
}
@Override
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);
FunctionEditorModel model = new FunctionEditorModel(service, function);
HighFunction hf = controller.getHighFunction();
HighFunction hf = context.getHighFunction();
FunctionPrototype functionPrototype = hf.getFunctionPrototype();
// 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.XmlPullParser;
public class EquateSymbol extends DynamicSymbol {
public class EquateSymbol extends HighSymbol {
public static final int FORMAT_DEFAULT = 0;
public static final int FORMAT_HEX = 1;
@ -33,29 +33,37 @@ public class EquateSymbol extends DynamicSymbol {
private long value; // Value of the 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) {
super(nm, DataType.DEFAULT, 1, func, addr, hash, format);
public EquateSymbol(long uniqueId, String nm, long val, HighFunction func, Address addr,
long hash) {
super(uniqueId, nm, DataType.DEFAULT, func);
category = 1;
value = val;
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) {
super("", DataType.DEFAULT, 1, func, addr, hash, format);
public EquateSymbol(long uniqueId, int conv, long val, HighFunction func, Address addr,
long hash) {
super(uniqueId, "", DataType.DEFAULT, func);
category = 1;
value = val;
convert = conv;
DynamicEntry entry = new DynamicEntry(this, addr, hash);
addMapEntry(entry);
}
public long getValue() { return value; }
@Override
public int restoreXML(XmlPullParser parser, HighFunction func) throws PcodeXMLException {
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement symel = parser.start("equatesymbol");
int symbolId = restoreSymbolXML(symel, func);
restoreXMLHeader(symel);
type = DataType.DEFAULT;
size = 1;
convert = FORMAT_DEFAULT;
String formString = symel.getAttribute("format");
if (formString != null) {
@ -78,47 +86,12 @@ public class EquateSymbol extends DynamicSymbol {
parser.start("value");
value = SpecXmlUtils.decodeLong(parser.end().getText()); // End <value> tag
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
public String buildXML() {
String sym = buildSymbolXML(function.getDataTypeManager(), name, value, isNameLocked(), false, convert);
StringBuilder res = new StringBuilder();
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);
}
public void saveXML(StringBuilder buf) {
buf.append("<equatesymbol");
saveXMLHeader(buf);
if (convert != 0) {
String formString = "hex";
if (convert == FORMAT_HEX) {
@ -136,14 +109,13 @@ public class EquateSymbol extends DynamicSymbol {
else if (convert == FORMAT_CHAR) {
formString = "char";
}
SpecXmlUtils.encodeStringAttribute(res, "format", formString);
SpecXmlUtils.encodeStringAttribute(buf, "format", formString);
}
res.append(">\n");
res.append(" <value>0x");
res.append(Long.toHexString(value));
res.append("</value>\n");
res.append("</equatesymbol>\n");
return res.toString();
buf.append(">\n");
buf.append(" <value>0x");
buf.append(Long.toHexString(value));
buf.append("</value>\n");
buf.append("</equatesymbol>\n");
}
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
* if this prototype is not backed by a LocalSymbolMap
*/
public HighParam getParam(int i) {
public HighSymbol getParam(int i) {
if (localsyms != null) {
return localsyms.getParam(i);
return localsyms.getParamSymbol(i);
}
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.data.AbstractIntegerDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
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 {
private DynamicSymbol symbol;
private HighSymbol symbol;
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
* @param name name of variable
@ -38,33 +49,14 @@ public class HighConstant extends HighVariable {
* @param vn constant varnode
* @param pc code unit address where constant is used
* @param func the associated high function
* @throws InvalidInputException
*/
public HighConstant(String name, DataType type, Varnode vn, Address pc, HighFunction func)
throws InvalidInputException {
public HighConstant(String name, DataType type, Varnode vn, Address pc, HighFunction func) {
super(name, type, vn, null, func);
pcaddr = pc;
}
/**
* Construct constant associated with a dynamic symbol
* @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() {
@Override
public HighSymbol getSymbol() {
return symbol;
}
@ -95,4 +87,35 @@ public class HighConstant extends HighVariable {
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 ghidra.program.database.function.FunctionDB;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*;
@ -48,6 +48,7 @@ public class HighFunction extends PcodeSyntaxTree {
private CompilerSpec compilerSpec;
private FunctionPrototype proto; // The high-level prototype associated with the function
private LocalSymbolMap localSymbols;
private GlobalSymbolMap globalSymbols;
private List<JumpTable> jumpTables;
private List<DataTypeSymbol> protoOverrides;
private boolean showNamespace = true;
@ -67,6 +68,7 @@ public class HighFunction extends PcodeSyntaxTree {
this.compilerSpec = compilerSpec;
this.showNamespace = showNamespace;
localSymbols = new LocalSymbolMap(this, "stack");
globalSymbols = new GlobalSymbolMap(this);
proto = new FunctionPrototype(localSymbols, function);
jumpTables = null;
protoOverrides = null;
@ -79,6 +81,17 @@ public class HighFunction extends PcodeSyntaxTree {
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
*/
@ -115,12 +128,19 @@ public class HighFunction extends PcodeSyntaxTree {
return localSymbols;
}
/**
* @return a map describing global variables accessed by this function
*/
public GlobalSymbolMap getGlobalSymbolMap() {
return globalSymbols;
}
public HighSymbol getMappedSymbol(Address addr, Address pcaddr) {
return localSymbols.findLocal(addr, pcaddr);
}
@Override
public HighSymbol getSymbol(int symbolId) {
public HighSymbol getSymbol(long symbolId) {
return localSymbols.getSymbol(symbolId);
}
@ -200,41 +220,29 @@ public class HighFunction extends PcodeSyntaxTree {
}
private void readHighXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("high");
XmlElement el = parser.peek();
String classstring = el.getAttribute("class");
int symref = SpecXmlUtils.decodeInt(el.getAttribute("symref"));
int repref = SpecXmlUtils.decodeInt(el.getAttribute("repref"));
Varnode rep = getRef(repref);
if (rep == null) {
throw new PcodeXMLException("Undefined varnode reference");
HighVariable var;
switch (classstring.charAt(0)) {
case 'o':
var = new HighOther(this);
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);
}
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);
var.restoreXml(parser);
}
private void readHighlistXML(XmlPullParser parser) throws PcodeXMLException {
@ -320,78 +328,7 @@ public class HighFunction extends PcodeSyntaxTree {
parser.end(el);
}
private HighVariable newHigh(int symref, DataType tp, int sz, Varnode[] inst, 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) {
protected Address getPCAddress(Varnode rep) {
Address pcaddr = null;
if (!rep.isAddrTied()) {
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
// 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.
sym = localSymbols.newMappedSymbol(highloc.getName(), highloc.getDataType(),
new VariableStorage(func.getProgram(), vn), vn.getPCAddress(), -1,
vn.hashCode());
sym = localSymbols.newMappedSymbol(0, highloc.getName(), highloc.getDataType(),
new VariableStorage(func.getProgram(), vn), vn.getPCAddress(), -1);
reslocal = new HighLocal(highloc.getDataType(), vn, null, vn.getPCAddress(), sym);
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
* 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 size describes how many bytes the function occupies as code
* @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
// So size needs to be smaller than size of the contiguous chunk containing the entry point
StringBuilder resBuf = new StringBuilder();
resBuf.append("<function");
if (id != 0) {
SpecXmlUtils.encodeUnsignedIntegerAttribute(resBuf, "id", id);
}
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", func.getName(showNamespace));
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
if (func.isInline()) {
@ -558,26 +498,6 @@ public class HighFunction extends PcodeSyntaxTree {
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,
final String targetName) {
return new ErrorHandler() {

View file

@ -17,8 +17,7 @@ package ghidra.program.model.pcode;
import java.util.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
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
/**
* Commit function return to the underlying database.
* @param highFunction
* Commit the decompiler's version of the function return data-type to the database.
* 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) {
try {
@ -85,7 +86,9 @@ public class HighFunctionDBUtil {
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);
}
@ -98,7 +101,7 @@ public class HighFunctionDBUtil {
List<Parameter> params = new ArrayList<Parameter>();
int paramCnt = symbolMap.getNumParams();
for (int i = 0; i < paramCnt; ++i) {
HighParam param = symbolMap.getParam(i);
HighSymbol param = symbolMap.getParamSymbol(i);
String name = param.getName();
DataType dataType;
if (useDataTypes) {
@ -114,22 +117,26 @@ public class HighFunctionDBUtil {
}
/**
* Commit the specified parameter list to the specified function.
* @param function
* @param params
* Commit a specified set of parameters for the given function to the database.
* The name, data-type, and storage is committed for each parameter. The parameters are
* 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
* by renaming the conflicting local variable/label
* @param source source type
* @throws DuplicateNameException if commit of parameters caused conflict with other
* 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)
throws DuplicateNameException, InvalidInputException {
String modelName = prototype != null ? prototype.getModelName() : null;
try {
function.updateFunction(modelName, null, params,
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, source);
@ -181,6 +188,7 @@ public class HighFunctionDBUtil {
break;
}
catch (DuplicateNameException e) {
// Continue looping until we get a unique symbol name
}
catch (InvalidInputException e) {
break;
@ -189,9 +197,10 @@ public class HighFunctionDBUtil {
}
/**
* Commit all local variables to the underlying database.
* @param highFunction
* @param source source type
* Commit local variables from the decompiler's model of the function to the database.
* This does NOT include formal function parameters.
* @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) {
@ -202,23 +211,22 @@ public class HighFunctionDBUtil {
Iterator<HighSymbol> iter = highFunction.getLocalSymbolMap().getSymbols();
while (iter.hasNext()) {
HighSymbol sym = iter.next();
HighVariable high = sym.getHighVariable();
if ((high instanceof HighParam) || !(high instanceof HighLocal)) {
if (sym.isParameter() || sym.isGlobal()) {
continue;
}
HighLocal local = (HighLocal) high;
String name = local.getName();
String name = sym.getName();
try {
Variable var = clearConflictingLocalVariables(local);
Variable var =
clearConflictingLocalVariables(function, sym.getStorage(), sym.getPCAddress());
if (var == null) {
var = createLocalVariable(local, null, null, source);
var = createLocalVariable(function, sym.getDataType(), sym.getStorage(),
sym.getPCAddress(), source);
if (name != null) {
var.setName(name, source);
}
}
else {
var.setDataType(local.getDataType(), local.getStorage(), false, source);
var.setDataType(sym.getDataType(), sym.getStorage(), false, source);
var.setName(name, source);
}
}
@ -230,28 +238,27 @@ public class HighFunctionDBUtil {
}
/**
* Create a local DB variable with a default name
* @param local
* @param dt data type or null to use local data type defined by local high variable
* @param storage storage or null to use storage defined by local high variable
* @param source
* @return
* @throws InvalidInputException
* @throws DuplicateNameException
* Create a local DB variable with a default name. Storage and data-type for the variable
* are provided explicitly.
* @param function is the function owning the new variable
* @param dt is the given data-type
* @param storage is the given storage
* @param pcAddr is point where the variable is instantiated or null
* @param source is the source type of the new variable
* @return the new local variable
* @throws InvalidInputException is a valid variable can't be created
*/
private static Variable createLocalVariable(HighLocal local, DataType dt,
VariableStorage storage, SourceType source) throws InvalidInputException {
Function function = local.getHighFunction().getFunction();
private static Variable createLocalVariable(Function function, DataType dt,
VariableStorage storage, Address pcAddr, SourceType source)
throws InvalidInputException {
Program program = function.getProgram();
if (storage == null || storage.isUniqueStorage()) {
storage = local.getStorage();
int firstUseOffset = 0;
if (pcAddr != null) {
firstUseOffset = (int) pcAddr.subtract(function.getEntryPoint());
}
if (dt == null) {
dt = local.getDataType();
}
Variable var = new LocalVariableImpl(null, local.getFirstUseOffset(), dt, storage, program);
Variable var = new LocalVariableImpl(null, firstUseOffset, dt, storage, program);
try {
var = function.addLocalVariable(var, SourceType.ANALYSIS);
var = function.addLocalVariable(var, source);
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception with default name", e);
@ -259,8 +266,8 @@ public class HighFunctionDBUtil {
Register reg = var.getRegister();
if (reg != null) {
program.getReferenceManager().addRegisterReference(local.getPCAddress(), -1, reg,
RefType.WRITE, source);
program.getReferenceManager().addRegisterReference(pcAddr, -1, reg, RefType.WRITE,
source);
}
return var;
@ -283,47 +290,99 @@ public class HighFunctionDBUtil {
long hash = var.getFirstStorageVarnode().getOffset();
Iterator<HighSymbol> symbols = highFunction.getLocalSymbolMap().getSymbols();
while (symbols.hasNext()) {
HighVariable high = symbols.next().getHighVariable();
if (!(high instanceof HighLocal)) {
HighSymbol symbol = symbols.next();
SymbolEntry entry = symbol.getFirstWholeMap();
if (!(entry instanceof DynamicEntry)) {
continue;
}
// Note: assumes there is only one hash method used for unique locals
if (((HighLocal) high).buildDynamicHash() == hash) {
return true;
if (((DynamicEntry) entry).getHash() == hash) {
if (symbol.getHighVariable() != null) {
return true; // Hash successfully attached to a variable
}
}
}
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
* database which conflict with this variable and return
* one of them for re-use. The returned variable still
* 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
*/
private static Variable clearConflictingLocalVariables(HighLocal local) {
private static Variable clearConflictingLocalVariables(Function function,
VariableStorage storage, Address pcAddr) {
if (local instanceof HighParam) {
throw new IllegalArgumentException();
int firstUseOffset = 0;
if (pcAddr != null) {
firstUseOffset = (int) pcAddr.subtract(function.getEntryPoint());
}
if (storage.isHashStorage()) {
HighFunction highFunction = local.getHighFunction();
Function func = highFunction.getFunction();
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)) {
long hashVal = storage.getFirstVarnode().getOffset();
for (Variable ul : function.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER)) {
// 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;
}
}
@ -331,7 +390,7 @@ public class HighFunctionDBUtil {
}
Variable matchingVariable = null;
for (Variable otherVar : func.getLocalVariables()) {
for (Variable otherVar : function.getLocalVariables()) {
if (otherVar.getFirstUseOffset() != firstUseOffset) {
// other than parameters we will have a hard time identifying
// local variable conflicts due to differences in scope (i.e., first-use)
@ -345,7 +404,7 @@ public class HighFunctionDBUtil {
matchingVariable = otherVar;
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
* the parameter will be modified to match the HighParam. The entire prototype is
* Get database parameter which corresponds to the given symbol, where we anticipate that
* 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
* 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
* @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();
Function function = highFunction.getFunction();
int slot = param.getSlot();
int slot = param.getCategoryIndex();
Parameter[] parameters = function.getParameters();
if (slot < parameters.length) {
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.
* Only variable types HighParam, HighLocal and HighGlobal are supported.
* @param variable
* @param highSymbol is the symbol being updated
* @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.
* 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
* @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 {
HighFunction highFunction = variable.getHighFunction();
HighFunction highFunction = highSymbol.getHighFunction();
Function function = highFunction.getFunction();
Program program = function.getProgram();
@ -423,15 +481,14 @@ public class HighFunctionDBUtil {
"Data type is not fixed-length: " + dataType.getName());
}
resized = (dataType.getLength() != variable.getSize());
resized = (dataType.getLength() != highSymbol.getSize());
}
boolean isRename = name != null;
if (variable instanceof HighParam) {
HighParam param = (HighParam) variable;
Parameter dbParam = getDatabaseParameter(param);
VariableStorage storage = param.getStorage();
if (highSymbol.isParameter()) {
Parameter dbParam = getDatabaseParameter(highSymbol);
VariableStorage storage = highSymbol.getStorage();
if (dataType != null) {
if (resized && function.hasCustomVariableStorage()) {
VariableStorage newStorage =
@ -446,43 +503,76 @@ public class HighFunctionDBUtil {
dbParam.setName(name, source);
}
}
else if (variable instanceof HighLocal) {
HighLocal local = (HighLocal) variable;
VariableStorage storage = local.getStorage();
boolean usesHashStorage = storage.isHashStorage();
Variable var = clearConflictingLocalVariables(local);
if (dataType == null) {
else if (!highSymbol.isGlobal()) {
Variable[] varList = null;
VariableStorage storage = highSymbol.getStorage();
Address pcAddr = highSymbol.getPCAddress();
HighVariable tmpHigh = highSymbol.getHighVariable();
if (!storage.isHashStorage() && tmpHigh != 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) {
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 {
dataType = Undefined.getUndefinedDataType(variable.getSize());
dataType = Undefined.getUndefinedDataType(highSymbol.getSize());
dataType = dataType.clone(program.getDataTypeManager());
}
}
if (resized) {
if (usesHashStorage) {
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());
}
storage = VariableUtilities.resizeStorage(storage, dataType, true, function);
}
if (var == null) {
var = createLocalVariable(local, dataType, storage, source);
if (varList == null) {
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 {
// fixup reused variable
var.setDataType(dataType, storage, true, source);
if (name == null) {
name = local.getName(); // must update name if not specified
// Set data-type on existing merge set
for (Variable var : varList) {
var.setDataType(dataType, source);
}
}
if (name == null) {
name = highSymbol.getName(); // must update name if not specified
}
Variable renameVar = null;
try {
// must set/correct name
var.setName(name, source);
int index = 0;
String curName = name;
for (Variable var : varList) {
renameVar = var;
var.setName(curName, source);
index += 1;
curName = name + '$' + Integer.toString(index);
}
}
catch (DuplicateNameException e) {
if (isRename) {
@ -493,36 +583,35 @@ public class HighFunctionDBUtil {
Msg.error(HighFunctionDBUtil.class,
"Name conflict while naming local variable: " + function.getName() + ":" +
name);
var.setName(null, SourceType.DEFAULT);
renameVar.setName(null, SourceType.DEFAULT);
}
catch (DuplicateNameException e1) {
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()) {
throw new UnsupportedOperationException(
"Database supports global memory variables only");
}
HighGlobal global = (HighGlobal) variable;
if (name == null) {
name = global.getName();
name = highSymbol.getName();
if (name != null && SymbolUtilities.isDynamicSymbolPattern(name, true)) {
name = null;
}
}
if (dataType != null) {
setGlobalDataType(global, dataType);
setGlobalDataType(highSymbol, dataType);
}
if (name != null) {
try {
setGlobalName((HighGlobal) variable, variable.getName(), source);
setGlobalName(highSymbol, highSymbol.getName(), source);
}
catch (DuplicateNameException e) {
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 {
Program program = global.getHighFunction().getFunction().getProgram();
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 {
Program program = global.getHighFunction().getFunction().getProgram();
VariableStorage storage = global.getStorage();
@ -592,26 +677,26 @@ public class HighFunctionDBUtil {
}
/**
* Commit an override of a calls prototype to the database
* @param function is the Function whose call is being overriden
* @param callsite is the address of the call
* @param sig signature override
* @throws InvalidInputException
* @throws DuplicateNameException
* Commit an overriding prototype for a particular call site to the database. The override
* only applies to the function(s) containing the actual call site. Calls to the same function from
* other sites are unaffected. This is used typically either for indirect calls are for calls to
* a function with a variable number of parameters.
* @param function is the Function whose call site is being overridden
* @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)
throws InvalidInputException, DuplicateNameException {
throws InvalidInputException {
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.setArguments(params);
fsig.setReturnType(sig.getReturnType());
fsig.setVarArgs(sig.hasVarArgs());
FunctionDefinitionDataType dt = new FunctionDefinitionDataType(fsig);
DataTypeSymbol datsym = new DataTypeSymbol(dt, "prt", AUTO_CAT);
DataTypeSymbol datsym = new DataTypeSymbol(fsig, "prt", AUTO_CAT);
Program program = function.getProgram();
SymbolTable symtab = program.getSymbolTable();
DataTypeManager dtmanage = program.getDataTypeManager();
@ -624,9 +709,9 @@ public class HighFunctionDBUtil {
/**
* 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
* datatype could not be found
* data-type could not be found
*/
public static DataTypeSymbol readOverride(Symbol sym) {
DataTypeSymbol datsym = DataTypeSymbol.readSymbol(AUTO_CAT, sym);
@ -640,4 +725,34 @@ public class HighFunctionDBUtil {
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
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +15,11 @@
*/
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
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 {
private HighSymbol symbol;
/**
* @param name name of global variable
* @param type data type of variable
* @param vn global variable storage
* @param func the associated high function
* @throws InvalidInputException
* Constructor for use with restoreXml
* @param high is the HighFunction this global is accessed by
*/
public HighGlobal(String name, DataType type, Varnode vn, Varnode[] inst, HighFunction func)
throws InvalidInputException {
super(name, type, vn, inst, func);
public HighGlobal(HighFunction high) {
super(high);
}
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
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,18 +16,23 @@
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType;
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;
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 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) {
super(sym.getName(), type, vn, inst, sym.getHighFunction());
@ -36,39 +40,11 @@ public class HighLocal extends HighVariable {
symbol = sym;
}
@Override
public HighSymbol getSymbol() {
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
*/
@ -76,75 +52,30 @@ public class HighLocal extends HighVariable {
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
public VariableStorage getStorage() {
Program program = getHighFunction().getFunction().getProgram();
Varnode represent = getRepresentative();
if (symbol instanceof DynamicSymbol || represent.isUnique()) {
long hash = buildDynamicHash();
try {
return new VariableStorage(program, AddressSpace.HASH_SPACE.getAddress(hash),
represent.getSize());
}
catch (InvalidInputException e) {
throw new AssertException("Unexpected exception", e);
}
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");
if (attrString != null) {
offset = SpecXmlUtils.decodeInt(attrString);
}
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) {
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;
parser.end(el);
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (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.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 {
private DynamicSymbol symbol;
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
* @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 func the associated high function
* @throws InvalidInputException
*/
public HighOther(DataType type, Varnode vn, Varnode[] inst, Address pc, HighFunction func)
throws InvalidInputException {
public HighOther(DataType type, Varnode vn, Varnode[] inst, Address pc, HighFunction func) {
super(null, type, vn, inst, func);
pcaddr = pc;
}
/**
* @return associated dynamic symbol or null
*/
public DynamicSymbol getSymbol() {
return symbol;
}
/**
* @return instruction address the variable comes into scope within the function
*/
@ -59,4 +60,31 @@ public class HighOther extends HighVariable {
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.data.DataType;
import ghidra.xml.XmlPullParser;
/**
*
@ -26,9 +27,17 @@ import ghidra.program.model.data.DataType;
public class HighParam extends HighLocal {
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 rep represented varnode
* @param rep is the representative input Varnode
* @param pc null or Address of PcodeOp which defines the representative
* @param slot parameter index starting at 0
* @param sym associated symbol
@ -46,8 +55,10 @@ public class HighParam extends HighLocal {
}
@Override
protected int getFirstUseOffset() {
return 0;
public void restoreXml(XmlPullParser parser) throws PcodeXMLException {
super.restoreXml(parser);
HighSymbol sym = getSymbol();
slot = sym.getCategoryIndex();
}
}

View file

@ -16,158 +16,462 @@
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
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.xml.XmlElement;
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 DataType type;
protected int size; // Size of this variable
protected Address pcaddr; // first-use address
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 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 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;
type = tp;
size = sz;
pcaddr = pc;
namelock = 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) {
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() {
return highVariable;
}
/**
* Get the base name of this symbol
* @return the name
*/
public String getName() {
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() {
return type;
}
/**
* @return the number of bytes consumed by the storage for this symbol
*/
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() {
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() {
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) {
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) {
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() {
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() {
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;
int symbolId = SpecXmlUtils.decodeInt(symel.getAttribute("id"));
if (symbolId == 0) {
/**
* For parameters (category=0), this method returns the position of the parameter within the function prototype.
* @return the category index for this symbol
*/
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");
}
typelock = false;
String typelockstr = symel.getAttribute("typelock");
if ((typelockstr != null) && (SpecXmlUtils.decodeBoolean(typelockstr)))
if ((typelockstr != null) && (SpecXmlUtils.decodeBoolean(typelockstr))) {
typelock = true;
}
namelock = false;
String namelockstr = symel.getAttribute("namelock");
if ((namelockstr != null) && (SpecXmlUtils.decodeBoolean(namelockstr)))
if ((namelockstr != null) && (SpecXmlUtils.decodeBoolean(namelockstr))) {
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;
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"));
addr = function.getAddressFactory().getAddressSpace(spc).getAddress(offset);
addr = function.getFunction().getEntryPoint().getAddressSpace().getOverlayAddress(addr);
parser.end(rangeel);
/**
* Restore this symbol object and its associated mappings from an XML description
* in the given stream.
* @param parser is the given XML stream
* @throws PcodeXMLException if the XML description is invalid
*/
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement symel = parser.start("symbol");
restoreXMLHeader(symel);
type = dtmanage.readXMLDataType(parser);
parser.end(symel);
if (categoryIndex >= 0 && name.startsWith("$$undef")) {
// use default parameter name
name = "param_" + Integer.toString(categoryIndex + 1);
}
parser.end(rangelistel);
return addr;
while (parser.peek().isStart()) {
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");
res.append(sym);
res.append(addrHashRes);
if (pc == null || pc.isExternalAddress()) {
res.append("<rangelist/>");
/**
* Restore a full HighSymbol from the next &lt;mapsym&gt; tag in the given XML stream.
* This method acts as an XML based HighSymbol factory, instantiating the correct class
* based on the particular tags.
* @param parser is the given XML stream
* @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 {
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");
}
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;
import java.util.ArrayList;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.Msg;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
*
@ -29,11 +30,20 @@ import ghidra.util.exception.InvalidInputException;
*/
public abstract class HighVariable {
private String name;
private DataType type;
private Varnode represent; // A representative varnode
private Varnode[] instances; // Instances of this high-level variable
private HighFunction function; // associated function
protected String name;
protected DataType type;
protected Varnode represent; // A representative varnode
protected Varnode[] instances; // Instances of this high-level variable
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) {
name = nm;
@ -46,9 +56,10 @@ public abstract class HighVariable {
* Link Varnodes directly to this HighVariable
*/
protected void setHighOnInstances() {
for(int i=0;i<instances.length;++i) {
if (instances[i] instanceof VarnodeAST)
((VarnodeAST)instances[i]).setHigh(this);
for (Varnode instance : instances) {
if (instance instanceof VarnodeAST) {
((VarnodeAST)instance).setHigh(this);
}
}
}
@ -97,6 +108,21 @@ public abstract class HighVariable {
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.
*
@ -109,20 +135,67 @@ public abstract class HighVariable {
instances = new Varnode[1];
instances[0] = rep;
}
else
else {
instances = inst;
}
}
public VariableStorage getStorage() {
Program program = getHighFunction().getFunction().getProgram();
try {
if (represent != null && (represent.isAddress() || represent.isRegister())) {
return new VariableStorage(program, represent);
}
/**
* Restore the data-type and the Varnode instances of this HighVariable.
* The "representative" Varnode is also populated.
* @param parser is the XML stream
* @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;
/**
*
*
* Local variables visible to a function. This includes mapped (on the stack) and
* unmapped (only stored in a register).
*
* 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
* either from the underlying Function object (when sending information to the decompiler) or read in from
* 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 {
private HighFunction func; // Function to which these variables are local
private String spacename;
private HashMap<MappedVarKey, HighSymbol> addrMappedSymbols; // Hashed by addr and pcaddr
private HashMap<Integer, HighSymbol> symbolMap; // Hashed by unique key
private MappedSymbol[] paramSymbols;
private HashMap<Long, HighSymbol> symbolMap; // Hashed by unique key
private HighSymbol[] paramSymbols;
private long uniqueSymbolId; // Next available symbol id
/**
* @param highFunc HighFunction the local variables are defined within.
@ -50,50 +52,149 @@ public class LocalSymbolMap {
func = highFunc;
spacename = spcname;
addrMappedSymbols = new HashMap<MappedVarKey, HighSymbol>();
symbolMap = new HashMap<Integer, HighSymbol>();
paramSymbols = new MappedSymbol[0];
symbolMap = new HashMap<Long, HighSymbol>();
paramSymbols = new HighSymbol[0];
uniqueSymbolId = 0;
}
/**
* Get the decompiler's function model owning this container
* @return the owning HighFunction
*/
public HighFunction getHighFunction() {
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.
* @param includeDefaultNames is true if default symbol names should be considered locked
*/
public void grabFromFunction(boolean includeDefaultNames) {
ArrayList<String> mergeNames = null;
Function dbFunction = func.getFunction();
int uniqueSymbolId = 0;
Variable locals[] = dbFunction.getLocalVariables();
for (Variable local : locals) {
Variable var = local;
if (!var.isValid()) {
if (!local.isValid()) {
// exclude locals which don't have valid storage
continue;
}
DataType dt = var.getDataType();
DataType dt = local.getDataType();
boolean istypelock = true;
boolean isnamelock = true;
if (Undefined.isUndefined(dt)) {
istypelock = false;
}
int sz = var.getLength();
String name = var.getName();
String name = local.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;
if (!storage.isStackStorage()) {
defAddr = dbFunction.getEntryPoint().addWrap(var.getFirstUseOffset());
defAddr = dbFunction.getEntryPoint().addWrap(local.getFirstUseOffset());
}
HighSymbol sym;
if (storage.isHashStorage()) {
sym =
newDynamicSymbol(name, dt, sz, storage.getFirstVarnode().getOffset(), defAddr,
0, ++uniqueSymbolId);
newDynamicSymbol(id, name, dt, storage.getFirstVarnode().getOffset(), defAddr);
}
else {
sym = newMappedSymbol(name, dt, storage, defAddr, -1, ++uniqueSymbolId);
sym = newMappedSymbol(id, name, dt, storage, defAddr, -1);
}
sym.setTypeLock(istypelock);
sym.setNameLock(isnamelock);
@ -105,7 +206,7 @@ public class LocalSymbolMap {
Address pcaddr = dbFunction.getEntryPoint();
pcaddr = pcaddr.subtractWrap(1);
List<MappedSymbol> paramList = new ArrayList<MappedSymbol>();
List<HighSymbol> paramList = new ArrayList<HighSymbol>();
for (int i = 0; i < p.length; ++i) {
Parameter var = p[i];
if (!var.isValid()) {
@ -114,10 +215,23 @@ public class LocalSymbolMap {
}
DataType dt = var.getDataType();
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();
Address resAddr = storage.isStackStorage() ? null : pcaddr;
MappedSymbol paramSymbol =
newMappedSymbol(name, dt, storage, resAddr, i, ++uniqueSymbolId);
long id = 0;
Symbol symbol = var.getSymbol();
if (symbol != null) {
id = symbol.getID();
}
HighSymbol paramSymbol = newMappedSymbol(id, name, dt, storage, resAddr, i);
paramList.add(paramSymbol);
boolean namelock = true;
if (!includeDefaultNames) {
@ -127,11 +241,12 @@ public class LocalSymbolMap {
paramSymbol.setTypeLock(lock);
}
paramSymbols = new MappedSymbol[paramList.size()];
paramSymbols = new HighSymbol[paramList.size()];
paramList.toArray(paramSymbols);
Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR);
uniqueSymbolId = grabEquates(dbFunction, uniqueSymbolId);
grabEquates(dbFunction);
grabMerges(mergeNames);
}
private boolean isUserDefinedName(String name) {
@ -151,22 +266,8 @@ public class LocalSymbolMap {
* @throws PcodeXMLException for problems sub tags
*/
private HighSymbol parseSymbolXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement node = parser.start("mapsym");
String typename = node.getAttribute("type");
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);
HighSymbol res = HighSymbol.restoreMapSymXML(parser, false, func);
insertSymbol(res);
return res;
}
@ -195,11 +296,11 @@ public class LocalSymbolMap {
parser.end(el);
}
private static final Comparator<MappedSymbol> PARAM_SYMBOL_SLOT_COMPARATOR =
new Comparator<MappedSymbol>() {
private static final Comparator<HighSymbol> PARAM_SYMBOL_SLOT_COMPARATOR =
new Comparator<HighSymbol>() {
@Override
public int compare(MappedSymbol sym1, MappedSymbol sym2) {
return sym1.getSlot() - sym2.getSlot();
public int compare(HighSymbol sym1, HighSymbol sym2) {
return sym1.getCategoryIndex() - sym2.getCategoryIndex();
}
};
@ -210,14 +311,14 @@ public class LocalSymbolMap {
*/
public void parseSymbolList(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("symbollist");
ArrayList<MappedSymbol> parms = new ArrayList<MappedSymbol>();
ArrayList<HighSymbol> parms = new ArrayList<HighSymbol>();
while (parser.peek().isStart()) {
HighSymbol sym = parseSymbolXML(parser);
if (sym instanceof MappedSymbol && ((MappedSymbol) sym).isParameter()) {
parms.add((MappedSymbol) sym);
if (sym.isParameter()) {
parms.add(sym);
}
}
paramSymbols = new MappedSymbol[parms.size()];
paramSymbols = new HighSymbol[parms.size()];
parms.toArray(paramSymbols);
Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR);
parser.end(el);
@ -243,7 +344,7 @@ public class LocalSymbolMap {
Iterator<HighSymbol> iter = symbolMap.values().iterator();
while (iter.hasNext()) {
HighSymbol sym = iter.next();
res.append(sym.buildXML());
HighSymbol.buildMapSymXML(res, sym);
}
res.append("</symbollist>\n");
res.append("</scope>\n");
@ -290,18 +391,30 @@ public class LocalSymbolMap {
* @param id symbol-id
* @return variable or null if not found
*/
public HighSymbol getSymbol(int id) {
public HighSymbol getSymbol(long id) {
return symbolMap.get(id);
}
/**
* Get the number of parameter symbols in this scope
* @return the number of parameters
*/
public int getNumParams() {
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];
}
/**
* @param i is the desired parameter position
* @return the i-th parameter variable
*/
public HighParam getParam(int i) {
return (HighParam) paramSymbols[i].getHighVariable();
}
@ -316,43 +429,64 @@ public class LocalSymbolMap {
return false;
}
public MappedSymbol newMappedSymbol(String nm, DataType dt, VariableStorage store,
Address pcaddr, int slot, int id) {
MappedSymbol sym = new MappedSymbol(nm, dt, store, pcaddr, func, slot);
insertSymbol(sym,id);
protected HighSymbol newMappedSymbol(long id, String nm, DataType dt, VariableStorage store,
Address pcaddr, int slot) {
if (id == 0) {
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;
}
public DynamicSymbol newDynamicSymbol(String nm, DataType dt, int sz, long hash,
Address pcaddr, int format, int id) {
DynamicSymbol sym = new DynamicSymbol(nm, dt, sz, func, pcaddr, hash, format);
insertSymbol(sym,id);
protected HighSymbol newDynamicSymbol(long id, String nm, DataType dt, long hash,
Address pcaddr) {
if (id == 0) {
id = getNextId();
}
HighSymbol sym = new HighSymbol(id, nm, dt, func);
DynamicEntry entry = new DynamicEntry(sym, pcaddr, hash);
sym.addMapEntry(entry);
insertSymbol(sym);
return sym;
}
private void insertSymbol(HighSymbol sym,int id) {
if (sym instanceof MappedSymbol) {
MappedSymbol mapSym = (MappedSymbol)sym;
MappedVarKey key = new MappedVarKey(mapSym.getStorage(),mapSym.getPCAddress());
private void insertSymbol(HighSymbol sym) {
long uniqueId = sym.getId();
if ((uniqueId >> 56) == (HighSymbol.ID_BASE >> 56)) {
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);
}
symbolMap.put(id, sym);
symbolMap.put(uniqueId, sym);
}
private void newEquateSymbol(String nm, long val, long hash, Address addr, int format,
TreeMap<String, DynamicSymbol> constantSymbolMap) {
DynamicSymbol eqSymbol = constantSymbolMap.get(nm);
private void newEquateSymbol(long uniqueId, String nm, long val, long hash, Address addr,
TreeMap<String, HighSymbol> constantSymbolMap) {
HighSymbol eqSymbol = constantSymbolMap.get(nm);
if (eqSymbol != null) {
eqSymbol.addReference(addr, hash, format); // New reference to same symbol
return;
return; // New reference to same symbol
}
if (uniqueId == 0) {
uniqueId = getNextId();
}
int conv = EquateSymbol.convertName(nm, val);
if (conv < 0) {
eqSymbol = new EquateSymbol(nm, val, func, addr, hash, format);
eqSymbol = new EquateSymbol(uniqueId, nm, val, func, addr, hash);
eqSymbol.setNameLock(true);
}
else {
eqSymbol = new EquateSymbol(conv, val, func, addr, hash, format);
eqSymbol = new EquateSymbol(uniqueId, conv, val, func, addr, hash);
}
//Do NOT setTypeLock
constantSymbolMap.put(nm, eqSymbol);
@ -361,11 +495,9 @@ public class LocalSymbolMap {
/**
* Build dynamic symbols based on equates
* @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) {
TreeMap<String, DynamicSymbol> constantSymbolMap = null;
private void grabEquates(Function dbFunction) {
TreeMap<String, HighSymbol> constantSymbolMap = null;
// Find named constants via Equates
Program program = dbFunction.getProgram();
EquateTable equateTable = program.getEquateTable();
@ -381,9 +513,9 @@ public class LocalSymbolMap {
long hash[] = DynamicHash.calcConstantHash(instr, eq.getValue());
for (long element : hash) {
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);
}
}
@ -404,17 +536,25 @@ public class LocalSymbolMap {
// Add constant dynamic symbols to map
if (constantSymbolMap != null) {
for (DynamicSymbol sym : constantSymbolMap.values()) {
symbolMap.put(++uniqueSymbolId, sym);
for (HighSymbol sym : constantSymbolMap.values()) {
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
*
*
*/
class MappedVarKey {
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 Varnode getRef(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 void setAddrTied(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
// TODO: We should emit some kind of warning
int sz = 0;
for (Varnode piece : pieces)
for (Varnode piece : pieces) {
sz += piece.getSize();
}
Address uniqaddr = addrFactory.getUniqueSpace().getAddress(0x20000000);
storage = new VariableStorage(datatypeManager.getProgram(),uniqaddr,sz);
}
@ -156,26 +157,30 @@ public class PcodeSyntaxTree implements PcodeFactory {
else {
offObject = new Integer((int)offset);
offset += roundsize;
if (offset > joinAllocate)
if (offset > joinAllocate) {
joinAllocate = (int)offset;
}
}
if (joinmap == null)
if (joinmap == null) {
joinmap = new HashMap<Integer,VariableStorage>();
}
joinmap.put(offObject, storage);
return storage;
}
private VariableStorage findJoinStorage(long offset) {
if (joinmap == null)
if (joinmap == null) {
return null;
}
return joinmap.get(new Integer((int)offset));
}
@Override
public VariableStorage buildStorage(Varnode vn) throws InvalidInputException {
Address addr = vn.getAddress();
if (addr.getAddressSpace().getType() == AddressSpace.TYPE_VARIABLE)
if (addr.getAddressSpace().getType() == AddressSpace.TYPE_VARIABLE) {
return findJoinStorage(addr.getOffset());
}
return new VariableStorage(datatypeManager.getProgram(),vn);
}
@ -306,10 +311,12 @@ public class PcodeSyntaxTree implements PcodeFactory {
@Override
public Varnode newVarnode(int sz, Address addr, int id) {
Varnode vn = vbank.create(sz, addr, id);
if (uniqId <= id)
if (uniqId <= id) {
uniqId = id + 1;
if (refmap != null)
}
if (refmap != null) {
refmap.put(id, vn);
}
return vn;
}
@ -342,10 +349,12 @@ public class PcodeSyntaxTree implements PcodeFactory {
@Override
public Varnode setInput(Varnode vn, boolean val) {
if ((!vn.isInput()) && val)
if ((!vn.isInput()) && val) {
return vbank.setInput(vn);
if (vn.isInput() && (!val))
}
if (vn.isInput() && (!val)) {
vbank.makeFree(vn);
}
return vn;
}
@ -360,13 +369,14 @@ public class PcodeSyntaxTree implements PcodeFactory {
@Override
public Varnode getRef(int id) {
if (refmap == null)
if (refmap == null) {
return null;
}
return refmap.get(id);
}
@Override
public HighSymbol getSymbol(int symbolId) {
public HighSymbol getSymbol(long symbolId) {
return null;
}
@ -410,8 +420,9 @@ public class PcodeSyntaxTree implements PcodeFactory {
@Override
public PcodeOp getOpRef(int id) {
if (oprefmap == null)
if (oprefmap == null) {
buildOpRefs();
}
return oprefmap.get(id);
}
@ -437,12 +448,16 @@ public class PcodeSyntaxTree implements PcodeFactory {
public void setOutput(PcodeOp op, Varnode vn) {
if (vn == op.getOutput())
{
return; // Output already set to this
if (op.getOutput() != null)
}
if (op.getOutput() != null) {
unSetOutput(op);
}
if (vn.getDef() != null) // Varnode is already an output
if (vn.getDef() != null) {
unSetOutput(vn.getDef());
}
vn = vbank.setDef(vn, op);
op.setOutput(vn);
}
@ -450,16 +465,21 @@ public class PcodeSyntaxTree implements PcodeFactory {
public void unSetOutput(PcodeOp op) {
Varnode vn = op.getOutput();
if (vn == null)
{
return; // Nothing to do
}
op.setOutput(null);
vbank.makeFree(vn);
}
public void setInput(PcodeOp op, Varnode vn, int slot) {
if (slot >= op.getNumInputs())
{
op.setInput(null, slot); // Expand number of inputs as necessary
if (op.getInput(slot) != null)
}
if (op.getInput(slot) != null) {
unSetInput(op, slot);
}
if (vn != null) {
VarnodeAST vnast = (VarnodeAST) vn;
vnast.addDescendant(op);
@ -484,29 +504,35 @@ public class PcodeSyntaxTree implements PcodeFactory {
public void unlink(PcodeOpAST op) {
unSetOutput(op);
for (int i = 0; i < op.getNumInputs(); ++i)
for (int i = 0; i < op.getNumInputs(); ++i) {
unSetInput(op, i);
if (op.getParent() != null)
}
if (op.getParent() != null) {
unInsert(op);
}
}
@Override
public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList<Varnode> inputs, Varnode output)
throws UnknownInstructionException {
PcodeOp op = opbank.create(opc, inputs.size(), sq);
if (output != null)
if (output != null) {
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);
if (oprefmap != null)
}
if (oprefmap != null) {
oprefmap.put(sq.getTime(), op);
}
return op;
}
private void readVarnodeXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("varnodes");
while (parser.peek().isStart())
while (parser.peek().isStart()) {
Varnode.readXML(parser, this);
}
parser.end(el);
}
@ -524,8 +550,9 @@ public class PcodeSyntaxTree implements PcodeFactory {
bl.insertEnd(op);
}
int index = bl.getIndex();
while (bblocks.size() <= index)
while (bblocks.size() <= index) {
bblocks.add(null);
}
bblocks.set(index, bl);
parser.end(el);
}
@ -542,17 +569,20 @@ public class PcodeSyntaxTree implements PcodeFactory {
public void readXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("ast");
if (!vbank.isEmpty())
if (!vbank.isEmpty()) {
clear();
}
readVarnodeXML(parser);
buildVarnodeRefs(); // Build the HashMap
BlockMap blockMap = new BlockMap(addrFactory);
while (parser.peek().isStart()) {
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
else // Read block edges
}
else {
readBlockEdgeXML(parser);
}
}
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");
}
}