Refactor variable renaming

This commit is contained in:
caheckman 2020-01-09 13:02:10 -05:00
parent b88ea8c927
commit c0dfa509ee
18 changed files with 647 additions and 339 deletions

View file

@ -2332,23 +2332,6 @@ void ActionNameVars::lookForBadJumpTables(Funcdata &data)
} }
} }
/// From among the \e name \e recommendations (symbol information that wasn't locked)
/// find current symbols for which the name can still apply and apply it.
/// \param data is the function being analyzed
void ActionNameVars::lookForRecommendedNames(Funcdata &data)
{
ScopeLocal *localmap = data.getScopeLocal();
vector<string> names;
vector<Symbol *> symbols;
localmap->makeNameRecommendationsForSymbols(names,symbols);
for(uint4 i=0;i<names.size();++i) {
Symbol *sym = symbols[i];
sym->getScope()->renameSymbol(sym,localmap->makeNameUnique(names[i]));
}
}
/// \brief Add a recommendation to the database based on a particular sub-function parameter. /// \brief Add a recommendation to the database based on a particular sub-function parameter.
/// ///
/// We know \b vn holds data-flow for parameter \b param, try to attach its name to \b vn's symbol. /// We know \b vn holds data-flow for parameter \b param, try to attach its name to \b vn's symbol.
@ -2375,6 +2358,7 @@ void ActionNameVars::makeRec(ProtoParameter *param,Varnode *vn,map<HighVariable
} }
HighVariable *high = vn->getHigh(); HighVariable *high = vn->getHigh();
if (!high->isMark()) return; // Not one of the variables needing a name if (!high->isMark()) return; // Not one of the variables needing a name
if (high->isAddrTied()) return; // Don't propagate parameter name to address tied variable
map<HighVariable *,OpRecommend>::iterator iter = recmap.find(high); map<HighVariable *,OpRecommend>::iterator iter = recmap.find(high);
if (iter != recmap.end()) { // We have seen this varnode before if (iter != recmap.end()) { // We have seen this varnode before
@ -2466,9 +2450,10 @@ void ActionNameVars::linkSpacebaseSymbol(Varnode *vn,Funcdata &data,vector<Varno
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter; PcodeOp *op = *iter;
if (op->code() != CPUI_PTRSUB) continue; if (op->code() != CPUI_PTRSUB) continue;
Symbol *sym = data.linkSymbolReference(op->getIn(1)); Varnode *offVn = op->getIn(1);
Symbol *sym = data.linkSymbolReference(offVn);
if ((sym != (Symbol *)0) && sym->isNameUndefined()) if ((sym != (Symbol *)0) && sym->isNameUndefined())
namerec.push_back(vn); namerec.push_back(offVn);
} }
} }
@ -2506,8 +2491,8 @@ void ActionNameVars::linkSymbols(Funcdata &data,vector<Varnode *> &namerec)
if (!high->hasName()) continue; if (!high->hasName()) continue;
Symbol *sym = data.linkSymbol(vn); Symbol *sym = data.linkSymbol(vn);
if (sym != (Symbol *)0) { // Can we associate high with a nameable symbol if (sym != (Symbol *)0) { // Can we associate high with a nameable symbol
if (sym->isNameUndefined()) if (sym->isNameUndefined() && high->getSymbolOffset() < 0)
namerec.push_back(vn); namerec.push_back(vn); // Add if no name, and we have a high representing the whole
if (sym->isSizeTypeLocked()) { if (sym->isSizeTypeLocked()) {
if (vn->getSize() == sym->getType()->getSize()) if (vn->getSize() == sym->getType()->getSize())
sym->getScope()->overrideSizeLockType(sym,high->getType()); sym->getScope()->overrideSizeLockType(sym,high->getType());
@ -2523,33 +2508,21 @@ int4 ActionNameVars::apply(Funcdata &data)
vector<Varnode *> namerec; vector<Varnode *> namerec;
linkSymbols(data, namerec); linkSymbols(data, namerec);
lookForRecommendedNames(data); // Make sure recommended names hit before subfunc data.getScopeLocal()->recoverNameRecommendationsForSymbols(); // Make sure recommended names hit before subfunc
lookForBadJumpTables(data); lookForBadJumpTables(data);
lookForFuncParamNames(data,namerec); lookForFuncParamNames(data,namerec);
ScopeLocal *localmap = data.getScopeLocal();
int4 base = 1; int4 base = 1;
for(uint4 i=0;i<namerec.size();++i) { for(uint4 i=0;i<namerec.size();++i) {
Varnode *vn = namerec[i]; Varnode *vn = namerec[i];
HighVariable *high = vn->getHigh(); Symbol *sym = vn->getHigh()->getSymbol();
Symbol *sym = high->getSymbol();
if (sym->isNameUndefined()) { if (sym->isNameUndefined()) {
string newname; Scope *scope = sym->getScope();
Address usepoint; string newname = scope->buildDefaultName(sym, base, vn);
if (!vn->isAddrTied()) scope->renameSymbol(sym,newname);
usepoint = vn->getUsePoint(data);
if (high->isInput()) {
int4 index = -1;
if (sym->getCategory()==0)
index = sym->getCategory()+1;
newname = localmap->buildVariableName(vn->getAddr(),usepoint,high->getType(),index,vn->getFlags());
}
else
newname = localmap->buildVariableName(vn->getAddr(),usepoint,high->getType(),base,vn->getFlags());
sym->getScope()->renameSymbol(sym,newname);
} }
} }
data.getScopeLocal()->assignDefaultNames(base);
return 0; return 0;
} }

View file

@ -463,7 +463,6 @@ class ActionNameVars : public Action {
}; };
static void makeRec(ProtoParameter *param,Varnode *vn,map<HighVariable *,OpRecommend> &recmap); static void makeRec(ProtoParameter *param,Varnode *vn,map<HighVariable *,OpRecommend> &recmap);
static void lookForBadJumpTables(Funcdata &data); ///< Mark the switch variable for bad jump-tables static void lookForBadJumpTables(Funcdata &data); ///< Mark the switch variable for bad jump-tables
static void lookForRecommendedNames(Funcdata &data); ///< Try to apply names from unlocked symbols
static void lookForFuncParamNames(Funcdata &data,const vector<Varnode *> &varlist); static void lookForFuncParamNames(Funcdata &data,const vector<Varnode *> &varlist);
static void linkSpacebaseSymbol(Varnode *vn,Funcdata &data,vector<Varnode *> &namerec); static void linkSpacebaseSymbol(Varnode *vn,Funcdata &data,vector<Varnode *> &namerec);
static void linkSymbols(Funcdata &data,vector<Varnode *> &namerec); static void linkSymbols(Funcdata &data,vector<Varnode *> &namerec);

View file

@ -1641,6 +1641,45 @@ Symbol *Scope::addDynamicSymbol(const string &nm,Datatype *ct,const Address &cad
return sym; return sym;
} }
/// Create default name given information in the Symbol and possibly a representative Varnode.
/// This method extracts the crucial properties and then uses the buildVariableName method to
/// construct the actual name.
/// \param sym is the given Symbol to name
/// \param base is an index (which may get updated) used to uniquify the name
/// \param vn is an optional (may be null) Varnode representative of the Symbol
/// \return the default name
string Scope::buildDefaultName(Symbol *sym,int4 &base,Varnode *vn) const
{
if (vn != (Varnode *)0 && !vn->isConstant()) {
Address usepoint;
if (!vn->isAddrTied() && fd != (Funcdata *)0)
usepoint = vn->getUsePoint(*fd);
HighVariable *high = vn->getHigh();
if (sym->getCategory() == 0 || high->isInput()) {
int4 index = -1;
if (sym->getCategory()==0)
index = sym->getCategoryIndex()+1;
return buildVariableName(vn->getAddr(),usepoint,sym->getType(),index,vn->getFlags() | Varnode::input);
}
return buildVariableName(vn->getAddr(),usepoint,sym->getType(),base,vn->getFlags());
}
if (sym->numEntries() != 0) {
SymbolEntry *entry = sym->getMapEntry(0);
Address addr = entry->getAddr();
Address usepoint = entry->getFirstUseAddress();
uint4 flags = usepoint.isInvalid() ? Varnode::addrtied : 0;
if (sym->getCategory() == 0) { // If this is a parameter
flags |= Varnode::input;
int4 index = sym->getCategoryIndex() + 1;
return buildVariableName(addr, usepoint, sym->getType(), index, flags);
}
return buildVariableName(addr, usepoint, sym->getType(), base, flags);
}
// Should never reach here
return buildVariableName(Address(), Address(), sym->getType(), base, 0);
}
/// \brief Is the given memory range marked as \e read-only /// \brief Is the given memory range marked as \e read-only
/// ///
/// Check for Symbols relative to \b this Scope that are marked as \e read-only, /// Check for Symbols relative to \b this Scope that are marked as \e read-only,
@ -1943,18 +1982,11 @@ void ScopeInternal::clearUnlockedCategory(int4 cat)
} }
} }
void ScopeInternal::removeSymbol(Symbol *symbol) void ScopeInternal::removeSymbolMappings(Symbol *symbol)
{ {
vector<list<SymbolEntry>::iterator>::iterator iter; vector<list<SymbolEntry>::iterator>::iterator iter;
if (symbol->category >= 0) {
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) if (symbol->wholeCount > 1)
multiEntrySet.erase(symbol); multiEntrySet.erase(symbol);
// Remove each mapping of the symbol // Remove each mapping of the symbol
@ -1967,6 +1999,20 @@ void ScopeInternal::removeSymbol(Symbol *symbol)
rangemap->erase( *iter ); rangemap->erase( *iter );
} }
} }
symbol->wholeCount = 0;
symbol->mapentry.clear();
}
void ScopeInternal::removeSymbol(Symbol *symbol)
{
if (symbol->category >= 0) {
vector<Symbol *> &list(category[symbol->category]);
list[symbol->catindex] = (Symbol *)0;
while((!list.empty())&&(list.back() == (Symbol *)0))
list.pop_back();
}
removeSymbolMappings(symbol);
nametree.erase(symbol); nametree.erase(symbol);
delete symbol; delete symbol;
} }
@ -2654,6 +2700,26 @@ void ScopeInternal::setCategory(Symbol *sym,int4 cat,int4 ind)
list[sym->catindex] = sym; list[sym->catindex] = sym;
} }
/// Run through all the symbols whose name is undefined. Build a variable name, uniquify it, and
/// rename the variable.
/// \param base is the base index to start at for generating generic names
void ScopeInternal::assignDefaultNames(int4 &base)
{
SymbolNameTree::const_iterator iter;
Symbol testsym((Scope *)0,"$$undef",(Datatype *)0);
iter = nametree.upper_bound(&testsym);
while(iter != nametree.end()) {
Symbol *sym = *iter;
if (!sym->isNameUndefined()) break;
++iter; // Advance before renaming
string nm = buildDefaultName(sym, base, (Varnode *)0);
renameSymbol(sym, nm);
}
}
/// Check to make sure the Scope is a \e namespace then remove all /// Check to make sure the Scope is a \e namespace then remove all
/// its address ranges from the map. /// its address ranges from the map.
/// \param scope is the given Scope /// \param scope is the given Scope

View file

@ -170,7 +170,6 @@ protected:
uint4 wholeCount; ///< Number of SymbolEntries that map to the whole Symbol uint4 wholeCount; ///< Number of SymbolEntries that map to the whole Symbol
virtual ~Symbol(void) {} ///< Destructor virtual ~Symbol(void) {} ///< Destructor
void setDisplayFormat(uint4 val); ///< Set the display format for \b this Symbol void setDisplayFormat(uint4 val); ///< Set the display format for \b this Symbol
void setSymbolId(uint8 val) { symbolId = val; } ///< Assign a unique id to the symbol
void checkSizeTypeLock(void); ///< Calculate if \b size_typelock property is on void checkSizeTypeLock(void); ///< Calculate if \b size_typelock property is on
public: public:
/// \brief Possible display (dispflag) properties for a Symbol /// \brief Possible display (dispflag) properties for a Symbol
@ -495,6 +494,7 @@ protected:
virtual SymbolEntry *addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 hash,int4 off,int4 sz, virtual SymbolEntry *addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 hash,int4 off,int4 sz,
const RangeList &uselim)=0; const RangeList &uselim)=0;
SymbolEntry *addMap(const SymbolEntry &entry); ///< Integrate a SymbolEntry into the range maps SymbolEntry *addMap(const SymbolEntry &entry); ///< Integrate a SymbolEntry into the range maps
void setSymbolId(Symbol *sym,uint8 id) const { sym->symbolId = id; } ///< Adjust the id associated with a symbol
public: public:
#ifdef OPACTION_DEBUG #ifdef OPACTION_DEBUG
mutable bool debugon; mutable bool debugon;
@ -531,6 +531,7 @@ public:
virtual bool inScope(const Address &addr,int4 size, const Address &usepoint) const { virtual bool inScope(const Address &addr,int4 size, const Address &usepoint) const {
return rangetree.inRange(addr,size); } return rangetree.inRange(addr,size); }
virtual void removeSymbolMappings(Symbol *symbol)=0; ///< Remove all SymbolEntrys from the given Symbol
virtual void removeSymbol(Symbol *symbol)=0; ///< Remove the given Symbol from \b this Scope virtual void removeSymbol(Symbol *symbol)=0; ///< Remove the given Symbol from \b this Scope
virtual void renameSymbol(Symbol *sym,const string &newname)=0; ///< Rename a Symbol within \b this Scope virtual void renameSymbol(Symbol *sym,const string &newname)=0; ///< Rename a Symbol within \b this Scope
@ -706,6 +707,7 @@ public:
ExternRefSymbol *addExternalRef(const Address &addr,const Address &refaddr,const string &nm); ExternRefSymbol *addExternalRef(const Address &addr,const Address &refaddr,const string &nm);
LabSymbol *addCodeLabel(const Address &addr,const string &nm); LabSymbol *addCodeLabel(const Address &addr,const string &nm);
Symbol *addDynamicSymbol(const string &nm,Datatype *ct,const Address &caddr,uint8 hash); Symbol *addDynamicSymbol(const string &nm,Datatype *ct,const Address &caddr,uint8 hash);
string buildDefaultName(Symbol *sym,int4 &base,Varnode *vn) const; ///< Create a default name for the given Symbol
bool isReadOnly(const Address &addr,int4 size,const Address &usepoint) const; bool isReadOnly(const Address &addr,int4 size,const Address &usepoint) const;
void printBounds(ostream &s) const { rangetree.printBounds(s); } ///< Print a description of \b this Scope's \e owned memory ranges void printBounds(ostream &s) const { rangetree.printBounds(s); } ///< Print a description of \b this Scope's \e owned memory ranges
}; };
@ -745,6 +747,7 @@ public:
virtual list<SymbolEntry>::const_iterator endDynamic(void) const; virtual list<SymbolEntry>::const_iterator endDynamic(void) const;
virtual list<SymbolEntry>::iterator beginDynamic(void); virtual list<SymbolEntry>::iterator beginDynamic(void);
virtual list<SymbolEntry>::iterator endDynamic(void); virtual list<SymbolEntry>::iterator endDynamic(void);
virtual void removeSymbolMappings(Symbol *symbol);
virtual void removeSymbol(Symbol *symbol); virtual void removeSymbol(Symbol *symbol);
virtual void renameSymbol(Symbol *sym,const string &newname); virtual void renameSymbol(Symbol *sym,const string &newname);
virtual void retypeSymbol(Symbol *sym,Datatype *ct); virtual void retypeSymbol(Symbol *sym,Datatype *ct);
@ -777,6 +780,7 @@ public:
virtual int4 getCategorySize(int4 cat) const; virtual int4 getCategorySize(int4 cat) const;
virtual Symbol *getCategorySymbol(int4 cat,int4 ind) const; virtual Symbol *getCategorySymbol(int4 cat,int4 ind) const;
virtual void setCategory(Symbol *sym,int4 cat,int4 ind); virtual void setCategory(Symbol *sym,int4 cat,int4 ind);
void assignDefaultNames(int4 &base); ///< Assign a default name (via buildVariableName) to any unnamed symbol
set<Symbol *>::const_iterator beginMultiEntry(void) const { return multiEntrySet.begin(); } ///< Start of symbols with more than one entry set<Symbol *>::const_iterator 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 set<Symbol *>::const_iterator endMultiEntry(void) const { return multiEntrySet.end(); } ///< End of symbols with more than one entry
static void savePathXml(ostream &s,const vector<string> &vec); ///< Save a path with \<val> tags static void savePathXml(ostream &s,const vector<string> &vec); ///< Save a path with \<val> tags

View file

@ -101,6 +101,7 @@ public:
virtual void clearUnlockedCategory(int4 cat) { throw LowlevelError("clearUnlockedCategory unimplemented"); } virtual void clearUnlockedCategory(int4 cat) { throw LowlevelError("clearUnlockedCategory unimplemented"); }
virtual void clearUnlocked(void) { throw LowlevelError("clearUnlocked unimplemented"); } virtual void clearUnlocked(void) { throw LowlevelError("clearUnlocked unimplemented"); }
virtual void restrictScope(Funcdata *f) { throw LowlevelError("restrictScope unimplemented"); } virtual void restrictScope(Funcdata *f) { throw LowlevelError("restrictScope unimplemented"); }
virtual void removeSymbolMappings(Symbol *symbol) { throw LowlevelError("removeSymbolMappings unimplemented"); }
virtual void removeSymbol(Symbol *symbol) { throw LowlevelError("removeSymbol unimplemented"); } virtual void removeSymbol(Symbol *symbol) { throw LowlevelError("removeSymbol unimplemented"); }
virtual void renameSymbol(Symbol *sym,const string &newname) { throw LowlevelError("renameSymbol unimplemented"); } virtual void renameSymbol(Symbol *sym,const string &newname) { throw LowlevelError("renameSymbol unimplemented"); }
virtual void retypeSymbol(Symbol *sym,Datatype *ct) { throw LowlevelError("retypeSymbol unimplemented"); } virtual void retypeSymbol(Symbol *sym,Datatype *ct) { throw LowlevelError("retypeSymbol unimplemented"); }

View file

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

View file

@ -362,6 +362,7 @@ public:
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial) const; bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial) const;
bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes); bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes);
void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset); void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset);
void splitVarnode(Varnode *vn,int4 lowsize,Varnode *&vnlo,Varnode *& vnhi);
bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image
bool replaceVolatile(Varnode *vn); ///< Replace accesses of the given Varnode with \e volatile operations bool replaceVolatile(Varnode *vn); ///< Replace accesses of the given Varnode with \e volatile operations
void markIndirectOnly(void); ///< Mark \e illegal \e input Varnodes used only in INDIRECTs void markIndirectOnly(void); ///< Mark \e illegal \e input Varnodes used only in INDIRECTs
@ -382,8 +383,12 @@ public:
void clearDeadVarnodes(void); ///< Delete any dead Varnodes void clearDeadVarnodes(void); ///< Delete any dead Varnodes
void calcNZMask(void); ///< Calculate \e non-zero masks for all Varnodes void calcNZMask(void); ///< Calculate \e non-zero masks for all Varnodes
void clearDeadOps(void) { obank.destroyDead(); } ///< Delete any dead PcodeOps void clearDeadOps(void) { obank.destroyDead(); } ///< Delete any dead PcodeOps
void clearSymbolLinks(HighVariable *high); ///< Clear Symbols attached to Varnodes in the given HighVariable
void remapVarnode(Varnode *vn,Symbol *sym,const Address &usepoint);
void remapDynamicVarnode(Varnode *vn,Symbol *sym,const Address &usepoint,uint8 hash);
Symbol *linkSymbol(Varnode *vn); ///< Find or create Symbol associated with given Varnode Symbol *linkSymbol(Varnode *vn); ///< Find or create Symbol associated with given Varnode
Symbol *linkSymbolReference(Varnode *vn); ///< Discover and attach Symbol to a constant reference 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 findLinkedVarnodes(SymbolEntry *entry,vector<Varnode *> &res) const; ///< Find Varnodes that map to the given SymbolEntry
void buildDynamicSymbol(Varnode *vn); ///< Build a \e dynamic Symbol associated with the given Varnode void buildDynamicSymbol(Varnode *vn); ///< Build a \e dynamic Symbol associated with the given Varnode
bool attemptDynamicMapping(SymbolEntry *entry,DynamicHash &dhash); bool attemptDynamicMapping(SymbolEntry *entry,DynamicHash &dhash);

View file

@ -307,26 +307,10 @@ HighVariable *Funcdata::findHigh(const string &name) const
localmap->queryByName(name,symList); localmap->queryByName(name,symList);
if (symList.empty()) return (HighVariable *)0; if (symList.empty()) return (HighVariable *)0;
Symbol *sym = symList[0]; Symbol *sym = symList[0];
SymbolEntry *entry = sym->getFirstWholeMap(); Varnode *vn = findLinkedVarnode(sym->getFirstWholeMap());
if (vn != (Varnode *)0)
if (entry->isDynamic()) {
DynamicHash dhash;
Varnode *vn = dhash.findVarnode(this, entry->getFirstUseAddress(), entry->getHash());
if (vn == (Varnode *)0 || vn->isAnnotation())
return (HighVariable *)0;
return vn->getHigh(); return vn->getHigh();
}
VarnodeLocSet::const_iterator iter,enditer;
HighVariable *high;
iter = vbank.beginLoc(entry->getSize(),entry->getAddr());
enditer = vbank.endLoc(entry->getSize(),entry->getAddr());
for(;iter!=enditer;++iter) {
high = (*iter)->getHigh();
if (high->getSymbol() == sym)
return high;
}
return (HighVariable *)0; return (HighVariable *)0;
} }
@ -929,6 +913,49 @@ bool Funcdata::syncVarnodesWithSymbol(VarnodeLocSet::const_iterator &iter,uint4
return updateoccurred; return updateoccurred;
} }
/// For each instance Varnode, remove any SymbolEntry reference and associated properties.
/// \param high is the given HighVariable to clear
void Funcdata::clearSymbolLinks(HighVariable *high)
{
for(int4 i=0;i<high->numInstances();++i) {
Varnode *vn = high->getInstance(i);
vn->mapentry = (SymbolEntry *)0;
vn->clearFlags(Varnode::namelock | Varnode::typelock | Varnode::mapped);
}
}
/// \brief Remap a Symbol to a given Varnode using a static mapping
///
/// Any previous links between the Symbol, the Varnode, and the associate HighVariable are
/// removed. Then a new link is created.
/// \param vn is the given Varnode
/// \param sym is the Symbol the Varnode maps to
/// \param usepoint is the desired usepoint for the mapping
void Funcdata::remapVarnode(Varnode *vn,Symbol *sym,const Address &usepoint)
{
clearSymbolLinks(vn->getHigh());
SymbolEntry *entry = localmap->remapSymbol(sym, vn->getAddr(), usepoint);
vn->setSymbolEntry(entry);
}
/// \brief Remap a Symbol to a given Varnode using a new dynamic mapping
///
/// Any previous links between the Symbol, the Varnode, and the associate HighVariable are
/// removed. Then a new dynamic link is created.
/// \param vn is the given Varnode
/// \param sym is the Symbol the Varnode maps to
/// \param usepoint is the code Address where the Varnode is defined
/// \param hash is the hash for the new dynamic mapping
void Funcdata::remapDynamicVarnode(Varnode *vn,Symbol *sym,const Address &usepoint,uint8 hash)
{
clearSymbolLinks(vn->getHigh());
SymbolEntry *entry = localmap->remapSymbolDynamic(sym, hash, usepoint);
vn->setSymbolEntry(entry);
}
/// The Symbol is really attached to the Varnode's HighVariable (which must exist). /// The Symbol is really attached to the Varnode's HighVariable (which must exist).
/// The only reason a Symbol doesn't get set is if, the HighVariable /// The only reason a Symbol doesn't get set is if, the HighVariable
/// is global and there is no pre-existing Symbol. (see mapGlobals()) /// is global and there is no pre-existing Symbol. (see mapGlobals())
@ -990,6 +1017,44 @@ Symbol *Funcdata::linkSymbolReference(Varnode *vn)
return entry->getSymbol(); 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 /// Look for Varnodes that are (should be) mapped to the given SymbolEntry and
/// add them to the end of the result list. /// add them to the end of the result list.
/// \param entry is the given SymbolEntry to match /// \param entry is the given SymbolEntry to match

View file

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

View file

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

View file

@ -382,7 +382,10 @@ void Varnode::setSymbolEntry(SymbolEntry *entry)
{ {
mapentry = entry; mapentry = entry;
setFlags(Varnode::mapped); // Flags are generally not changed, but we do mark this as mapped 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) if (high != (HighVariable *)0)
high->setSymbol(this); high->setSymbol(this);
} }

View file

@ -64,28 +64,24 @@ public class RenameVariableAction extends AbstractDecompilerAction {
return storageAddress; return storageAddress;
} }
public static HighVariable forgeHighVariable(Address addr, DecompilerController controller) { /**
HighVariable res = null; * Find the HighSymbol the decompiler associates with a specific address.
* @param addr is the specific address
* @param controller is the decompiler being queried
* @return the matching symbol or null if no symbol exists
*/
public static HighSymbol findHighSymbol(Address addr, DecompilerController controller) {
HighSymbol highSymbol = null;
HighFunction hfunc = controller.getDecompileData().getHighFunction(); HighFunction hfunc = controller.getDecompileData().getHighFunction();
if (addr.isStackAddress()) { if (addr.isStackAddress()) {
LocalSymbolMap lsym = hfunc.getLocalSymbolMap(); LocalSymbolMap lsym = hfunc.getLocalSymbolMap();
HighSymbol hsym = lsym.findLocal(addr, null); highSymbol = lsym.findLocal(addr, null);
if (hsym != null) {
res = hsym.getHighVariable();
}
} }
else { else {
GlobalSymbolMap gsym = hfunc.getGlobalSymbolMap(); GlobalSymbolMap gsym = hfunc.getGlobalSymbolMap();
HighSymbol hsym = gsym.getSymbol(addr); highSymbol = gsym.getSymbol(addr);
if (hsym != null) {
res = hsym.getHighVariable();
if (res == null) {
Varnode vnrep = new Varnode(addr, hsym.getSize());
res = new HighGlobal(hsym, vnrep, null);
}
}
} }
return res; return highSymbol;
} }
/** /**
@ -140,29 +136,28 @@ public class RenameVariableAction extends AbstractDecompilerAction {
return true; return true;
} }
HighVariable variable = tokenAtCursor.getHighVariable(); HighVariable variable = tokenAtCursor.getHighVariable();
HighSymbol highSymbol = null;
if (variable == null) { if (variable == null) {
// not sure why variables with an & in front of them have no highVariable // Token may be from a variable reference, in which case we have to dig to find the actual symbol
Address storageAddress = getStorageAddress(tokenAtCursor, controller); Address storageAddress = getStorageAddress(tokenAtCursor, controller);
if (storageAddress == null) { if (storageAddress == null) {
return false; return false;
} }
variable = forgeHighVariable(storageAddress, controller); highSymbol = findHighSymbol(storageAddress, controller);
if (variable == null) {
return false;
}
} }
if (variable.getSymbol() == null) { else {
highSymbol = variable.getSymbol();
}
if (highSymbol == null) {
return false; return false;
} }
if (variable instanceof HighLocal) { if (highSymbol.isGlobal()) {
getPopupMenuData().setMenuItemName("Rename Variable");
return true;
}
else if (variable instanceof HighGlobal) {
getPopupMenuData().setMenuItemName("Rename Global"); getPopupMenuData().setMenuItemName("Rename Global");
return true;
} }
return false; else {
getPopupMenuData().setMenuItemName("Rename Variable");
}
return true;
} }
@Override @Override
@ -170,32 +165,35 @@ public class RenameVariableAction extends AbstractDecompilerAction {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
final ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor(); final ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
HighVariable variable = tokenAtCursor.getHighVariable(); HighVariable variable = tokenAtCursor.getHighVariable();
HighSymbol highSymbol = null;
if (variable == null) { if (variable == null) {
if (tokenAtCursor instanceof ClangVariableToken) { if (tokenAtCursor instanceof ClangVariableToken) {
Address addr = getStorageAddress(tokenAtCursor, controller); Address addr = getStorageAddress(tokenAtCursor, controller);
variable = forgeHighVariable(addr, controller); highSymbol = findHighSymbol(addr, controller);
} }
} }
if (variable instanceof HighLocal) { else {
nameTask = highSymbol = variable.getSymbol();
new RenameVariableTask(tool, variable.getSymbol().getName(), }
controller.getHighFunction(), variable, tokenAtCursor.getVarnode(), if (highSymbol != null) {
if (highSymbol.isGlobal()) {
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;
}
nameTask = new RenameGlobalVariableTask(tool, tokenAtCursor.getText(), addr,
controller.getProgram());
}
else {
nameTask = new RenameVariableTask(tool, highSymbol, tokenAtCursor.getVarnode(),
SourceType.USER_DEFINED); SourceType.USER_DEFINED);
}
else if (variable instanceof HighGlobal) {
Address addr = null;
HighSymbol sym = variable.getSymbol();
if (sym instanceof HighCodeSymbol) {
addr = ((HighCodeSymbol) sym).getStorage().getMinAddress();
} }
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) { else if (tokenAtCursor instanceof ClangFieldToken) {
Structure dt = getStructDataType(tokenAtCursor); Structure dt = getStructDataType(tokenAtCursor);

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.InvalidInputException;
public class RenameVariableTask extends RenameTask { public class RenameVariableTask extends RenameTask {
private HighVariable var; private HighSymbol highSymbol;
private Varnode exactSpot; private Varnode exactSpot;
private HighFunction hfunction; private HighFunction hfunction;
private Program program; private Program program;
@ -35,13 +35,12 @@ public class RenameVariableTask extends RenameTask {
private SourceType srctype; // Desired source type for the variable being renamed private SourceType srctype; // Desired source type for the variable being renamed
private SourceType signatureSrcType; // Signature source type of the function (which will be preserved) private SourceType signatureSrcType; // Signature source type of the function (which will be preserved)
public RenameVariableTask(PluginTool tool, String old, HighFunction hfunc, HighVariable v, public RenameVariableTask(PluginTool tool, HighSymbol sym, Varnode ex, SourceType st) {
Varnode ex, SourceType st) { super(tool, sym.getName());
super(tool, old); highSymbol = sym;
var = v;
exactSpot = ex; exactSpot = ex;
hfunction = hfunc; hfunction = sym.getHighFunction();
function = hfunc.getFunction(); function = hfunction.getFunction();
program = function.getProgram(); program = function.getProgram();
srctype = st; srctype = st;
signatureSrcType = function.getSignatureSource(); signatureSrcType = function.getSignatureSource();
@ -55,7 +54,7 @@ public class RenameVariableTask extends RenameTask {
HighFunctionDBUtil.commitReturnToDatabase(hfunction, signatureSrcType); HighFunctionDBUtil.commitReturnToDatabase(hfunction, signatureSrcType);
} }
} }
HighFunctionDBUtil.updateDBVariable(var.getSymbol(), newName, null, srctype); HighFunctionDBUtil.updateDBVariable(highSymbol, newName, null, srctype);
} }
@Override @Override
@ -66,21 +65,22 @@ public class RenameVariableTask extends RenameTask {
errorMsg = "Duplicate name"; errorMsg = "Duplicate name";
return false; return false;
} }
commitRequired = RetypeVariableAction.checkFullCommit(var, hfunction); commitRequired = RetypeVariableAction.checkFullCommit(highSymbol, hfunction);
if (commitRequired) { if (commitRequired) {
exactSpot = null; // Don't try to split out if we need to commit exactSpot = null; // Don't try to split out if we need to commit
} }
if (exactSpot != null) { // The user pointed at a particular usage, not just the vardecl if (exactSpot != null && !highSymbol.isNameLocked()) { // The user pointed at a particular usage, not just the vardecl
try { try {
var = hfunction.splitOutMergeGroup(var, exactSpot); HighVariable var = hfunction.splitOutMergeGroup(exactSpot.getHigh(), exactSpot);
highSymbol = var.getSymbol();
} }
catch (PcodeException e) { catch (PcodeException e) {
errorMsg = "Rename Failed: " + e.getMessage(); errorMsg = "Rename Failed: " + e.getMessage();
return false; return false;
} }
} }
if (var.getSymbol() == null) { if (highSymbol == null) {
errorMsg = "Rename Failed: No symbol"; errorMsg = "Rename Failed: No symbol";
return false; return false;
} }

View file

@ -124,17 +124,18 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
return chooserDialog.getUserChosenDataType(); return chooserDialog.getUserChosenDataType();
} }
private void retypeVariable(HighVariable var, Varnode exactSpot, DataType dt) { private void retypeSymbol(HighSymbol highSymbol, Varnode exactSpot, DataType dt) {
HighFunction hfunction = var.getHighFunction(); HighFunction hfunction = highSymbol.getHighFunction();
boolean commitRequired = checkFullCommit(var, hfunction); boolean commitRequired = checkFullCommit(highSymbol, hfunction);
if (commitRequired) { if (commitRequired) {
exactSpot = null; // Don't try to split out if commit is required 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 if (exactSpot != null) { // The user pointed at a particular usage, not just the vardecl
try { try {
var = hfunction.splitOutMergeGroup(var, exactSpot); HighVariable var = hfunction.splitOutMergeGroup(exactSpot.getHigh(), exactSpot);
highSymbol = var.getSymbol();
} }
catch (PcodeException e) { catch (PcodeException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed", e.getMessage()); Msg.showError(this, tool.getToolFrame(), "Retype Failed", e.getMessage());
@ -169,7 +170,7 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
Msg.showError(this, null, "Parameter Commit Failed", e.getMessage()); Msg.showError(this, null, "Parameter Commit Failed", e.getMessage());
} }
} }
HighFunctionDBUtil.updateDBVariable(var.getSymbol(), null, dt, SourceType.USER_DEFINED); HighFunctionDBUtil.updateDBVariable(highSymbol, null, dt, SourceType.USER_DEFINED);
successfulMod = true; successfulMod = true;
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
@ -177,7 +178,7 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed", Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Failed to re-type variable '" + var.getName() + "': " + e.getMessage()); "Failed to re-type variable '" + highSymbol.getName() + "': " + e.getMessage());
} }
finally { finally {
program.endTransaction(transaction, successfulMod); program.endTransaction(transaction, successfulMod);
@ -272,14 +273,14 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
/** /**
* Compare the given HighFunction's idea of the prototype with the Function's idea. * 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, * Return true if there is a difference. If a specific symbol is being changed,
* it can be passed in indicating that slot can be skipped during the comparison. * it can be passed in to check whether or not the prototype is being affected.
* @param var (if not null) is a specific parameter to skip the check for * @param highSymbol (if not null) is the symbol being modified
* @param hfunction is the given HighFunction * @param hfunction is the given HighFunction
* @return true if there is a difference (and a full commit is required) * @return true if there is a difference (and a full commit is required)
*/ */
public static boolean checkFullCommit(HighVariable var, HighFunction hfunction) { public static boolean checkFullCommit(HighSymbol highSymbol, HighFunction hfunction) {
if ((var != null) && (!(var instanceof HighParam))) { if (highSymbol != null && !highSymbol.isParameter()) {
return false; return false;
} }
Function function = hfunction.getFunction(); Function function = hfunction.getFunction();
@ -333,35 +334,34 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
return false; return false;
} }
HighVariable variable = tokenAtCursor.getHighVariable(); HighVariable variable = tokenAtCursor.getHighVariable();
HighSymbol highSymbol = null;
if (variable == null) { if (variable == null) {
Address addr = RenameVariableAction.getStorageAddress(tokenAtCursor, controller); Address addr = RenameVariableAction.getStorageAddress(tokenAtCursor, controller);
if (addr == null) { if (addr == null) {
return false; return false;
} }
variable = RenameVariableAction.forgeHighVariable(addr, controller); highSymbol = RenameVariableAction.findHighSymbol(addr, controller);
if (variable == null) {
return false;
}
} }
if (variable.getSymbol() == null) { else {
highSymbol = variable.getSymbol();
}
if (highSymbol == null) {
return false; return false;
} }
if (variable instanceof HighLocal) { if (highSymbol.isGlobal()) {
getPopupMenuData().setMenuItemName("Retype Variable");
return true;
}
else if (variable instanceof HighGlobal) {
getPopupMenuData().setMenuItemName("Retype Global"); getPopupMenuData().setMenuItemName("Retype Global");
return true;
} }
return false; else {
getPopupMenuData().setMenuItemName("Retype Variable");
}
return true;
} }
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel(); DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor(); ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
HighVariable variable = null; HighSymbol highSymbol = null;
Structure struct = null; Structure struct = null;
DataTypeComponent comp = null; DataTypeComponent comp = null;
@ -397,18 +397,21 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
return; return;
} }
else { else {
variable = tokenAtCursor.getHighVariable(); HighVariable variable = tokenAtCursor.getHighVariable();
if (variable == null) { if (variable == null) {
Address addr = RenameVariableAction.getStorageAddress(tokenAtCursor, controller); Address addr = RenameVariableAction.getStorageAddress(tokenAtCursor, controller);
if (addr == null) { if (addr == null) {
return; return;
} }
variable = RenameVariableAction.forgeHighVariable(addr, controller); highSymbol = RenameVariableAction.findHighSymbol(addr, controller);
if (variable == null || variable.getSymbol() == null || variable.getOffset() >= 0) {
return;
}
} }
dataType = chooseDataType(variable.getDataType()); else {
highSymbol = variable.getSymbol();
}
if (highSymbol == null) {
return;
}
dataType = chooseDataType(highSymbol.getDataType());
} }
if (dataType == null) { if (dataType == null) {
@ -418,7 +421,7 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
retypeStructVariable(struct, comp, dataType); retypeStructVariable(struct, comp, dataType);
} }
else { else {
retypeVariable(variable, tokenAtCursor.getVarnode(), dataType); retypeSymbol(highSymbol, tokenAtCursor.getVarnode(), dataType);
} }
} }
} }

View file

@ -28,10 +28,13 @@ import ghidra.app.decompiler.ClangVariableToken;
import ghidra.app.decompiler.component.ClangTextField; import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.decompiler.component.DecompilerPanel; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.actions.RenameGlobalVariableTask; import ghidra.app.plugin.core.decompile.actions.RenameGlobalVariableTask;
import ghidra.app.plugin.core.decompile.actions.RenameVariableTask;
import ghidra.program.database.symbol.CodeSymbol; import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Data;
import ghidra.program.model.pcode.*; import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
public class HighSymbolTest extends AbstractDecompilerTest { public class HighSymbolTest extends AbstractDecompilerTest {
@Override @Override
@ -52,16 +55,64 @@ public class HighSymbolTest extends AbstractDecompilerTest {
return null; return null;
} }
private void renameGlobalVariable(HighSymbol highSymbol, HighVariable highVar, Varnode exact, protected ClangTextField getLineContaining(String val) {
String newName) { 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, Varnode exact, String newName) {
Address addr = highSymbol.getStorage().getMinAddress(); Address addr = highSymbol.getStorage().getMinAddress();
RenameGlobalVariableTask rename = new RenameGlobalVariableTask(provider.getTool(), RenameGlobalVariableTask rename = new RenameGlobalVariableTask(provider.getTool(),
highSymbol.getName(), addr, highSymbol.getProgram()); highSymbol.getName(), addr, highSymbol.getProgram());
assertTrue(rename.isValid("newGlobal")); assertTrue(rename.isValid(newName));
modifyProgram(p -> { modifyProgram(p -> {
rename.commit(); rename.commit();
}); });
waitForDecompiler();
}
private void renameVariable(HighSymbol highSymbol, Varnode exact, String newName) {
RenameVariableTask rename = new RenameVariableTask(provider.getTool(), highSymbol, exact,
SourceType.USER_DEFINED);
assertTrue(rename.isValid(newName));
modifyProgram(p -> {
rename.commit();
});
waitForDecompiler();
}
private void renameExisting(HighSymbol highSymbol, Varnode exact, String newName) {
SymbolEntry oldEntry = highSymbol.getFirstWholeMap();
long oldId = highSymbol.getId();
if (highSymbol.isGlobal()) {
renameGlobalVariable(highSymbol, exact, newName);
}
else {
renameVariable(highSymbol, exact, 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 @Test
@ -85,7 +136,7 @@ public class HighSymbolTest extends AbstractDecompilerTest {
assertEquals(data.getAddress().getOffset(), 0x10056a0L); assertEquals(data.getAddress().getOffset(), 0x10056a0L);
assertEquals(data.getBaseDataType().getLength(), 2); assertEquals(data.getBaseDataType().getLength(), 2);
renameGlobalVariable(highSymbol, variable, null, "newGlobal"); renameGlobalVariable(highSymbol, null, "newGlobal");
waitForDecompiler(); waitForDecompiler();
line = getLineStarting("newGlobal"); line = getLineStarting("newGlobal");
loc = loc(line.getLineNumber(), 5); loc = loc(line.getLineNumber(), 5);
@ -102,5 +153,150 @@ public class HighSymbolTest extends AbstractDecompilerTest {
codeSymbol = highCode.getCodeSymbol(); codeSymbol = highCode.getCodeSymbol();
assertNotNull(codeSymbol); assertNotNull(codeSymbol);
assertEquals(codeSymbol.getID(), highCode.getId()); assertEquals(codeSymbol.getID(), highCode.getId());
renameExisting(highSymbol, null, "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.getVarnode(), "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, null, "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.getVarnode(), "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, null, "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.getVarnode(), "newReg");
line = getLineContaining("newReg < 0x40");
assertNotNull(line);
HighFunction highFunction = getHighFunction();
highSymbol = highFunction.getLocalSymbolMap().findLocal(addr, entry.getPCAdress());
assertNotNull(highSymbol);
assertEquals(highSymbol.getName(), "newReg");
assertTrue(highSymbol.isNameLocked());
assertFalse(highSymbol.isTypeLocked());
renameExisting(highSymbol, null, "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, null, "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.getVarnode(), "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
} }
} }

View file

@ -403,7 +403,7 @@ public class HighFunctionDBUtil {
/** /**
* Rename and/or retype the specified variable in the database. All parameters may be flushed * Rename and/or retype the specified variable in the database. All parameters may be flushed
* to the database if typed parameter inconsistency detected. * to the database if typed parameter inconsistency detected.
* @param variable is the symbol being updated * @param highSymbol is the symbol being updated
* @param name new variable name or null to use retain current variable name * @param name new variable name or null to use retain current variable name
* @param dataType newly assigned data type or null to retain current variable datatype. * @param dataType newly assigned data type or null to retain current variable datatype.
* Only a fixed-length data type may be specified. If size varies from the current size, * Only a fixed-length data type may be specified. If size varies from the current size,
@ -415,10 +415,10 @@ public class HighFunctionDBUtil {
* variable/label within the function's namespace * variable/label within the function's namespace
* @throws UnsupportedOperationException if unsupported variable type is specified * @throws UnsupportedOperationException if unsupported variable type is specified
*/ */
public static void updateDBVariable(HighSymbol variable, String name, DataType dataType, public static void updateDBVariable(HighSymbol highSymbol, String name, DataType dataType,
SourceType source) throws InvalidInputException, DuplicateNameException { SourceType source) throws InvalidInputException, DuplicateNameException {
HighFunction highFunction = variable.getHighFunction(); HighFunction highFunction = highSymbol.getHighFunction();
Function function = highFunction.getFunction(); Function function = highFunction.getFunction();
Program program = function.getProgram(); Program program = function.getProgram();
@ -430,14 +430,14 @@ public class HighFunctionDBUtil {
"Data type is not fixed-length: " + dataType.getName()); "Data type is not fixed-length: " + dataType.getName());
} }
resized = (dataType.getLength() != variable.getSize()); resized = (dataType.getLength() != highSymbol.getSize());
} }
boolean isRename = name != null; boolean isRename = name != null;
if (variable.isParameter()) { if (highSymbol.isParameter()) {
Parameter dbParam = getDatabaseParameter(variable); Parameter dbParam = getDatabaseParameter(highSymbol);
VariableStorage storage = variable.getStorage(); VariableStorage storage = highSymbol.getStorage();
if (dataType != null) { if (dataType != null) {
if (resized && function.hasCustomVariableStorage()) { if (resized && function.hasCustomVariableStorage()) {
VariableStorage newStorage = VariableStorage newStorage =
@ -452,18 +452,18 @@ public class HighFunctionDBUtil {
dbParam.setName(name, source); dbParam.setName(name, source);
} }
} }
else if (!variable.isGlobal()) { else if (!highSymbol.isGlobal()) {
Variable var; Variable var;
VariableStorage storage; VariableStorage storage = highSymbol.getStorage();
HighVariable tmpHigh = variable.getHighVariable(); HighVariable tmpHigh = highSymbol.getHighVariable();
if (tmpHigh != null && tmpHigh.requiresDynamicStorage()) { if (!storage.isHashStorage() && tmpHigh != null &&
tmpHigh.requiresDynamicStorage()) {
storage = storage =
DynamicEntry.buildDynamicStorage(tmpHigh.getRepresentative(), highFunction); DynamicEntry.buildDynamicStorage(tmpHigh.getRepresentative(), highFunction);
var = null; var = null;
} }
else { else {
storage = variable.getStorage(); var = clearConflictingLocalVariables(highSymbol);
var = clearConflictingLocalVariables(variable);
} }
boolean usesHashStorage = storage.isHashStorage(); boolean usesHashStorage = storage.isHashStorage();
if (dataType == null) { if (dataType == null) {
@ -471,28 +471,28 @@ public class HighFunctionDBUtil {
dataType = var.getDataType(); // Use preexisting datatype if it fits in desired storage dataType = var.getDataType(); // Use preexisting datatype if it fits in desired storage
} }
else { else {
dataType = Undefined.getUndefinedDataType(variable.getSize()); dataType = Undefined.getUndefinedDataType(highSymbol.getSize());
dataType = dataType.clone(program.getDataTypeManager()); dataType = dataType.clone(program.getDataTypeManager());
} }
} }
if (resized) { if (resized) {
if (usesHashStorage) { if (usesHashStorage) {
throw new InvalidInputException( throw new InvalidInputException(
"Variable size (" + variable.getSize() + ") may not be changed: type '" + "Variable size (" + highSymbol.getSize() + ") may not be changed: type '" +
dataType.getName() + "' length is " + dataType.getLength()); dataType.getName() + "' length is " + dataType.getLength());
} }
storage = VariableUtilities.resizeStorage(storage, dataType, true, function); storage = VariableUtilities.resizeStorage(storage, dataType, true, function);
} }
if (var == null) { if (var == null) {
var = createLocalVariable(variable, dataType, storage, source); var = createLocalVariable(highSymbol, dataType, storage, source);
} }
else { else {
// fixup reused variable // fixup reused variable
var.setDataType(dataType, storage, true, source); var.setDataType(dataType, storage, true, source);
} }
if (name == null) { if (name == null) {
name = variable.getName(); // must update name if not specified name = highSymbol.getName(); // must update name if not specified
} }
try { try {
// must set/correct name // must set/correct name
@ -516,26 +516,26 @@ public class HighFunctionDBUtil {
} }
else { // A global symbol else { // A global symbol
VariableStorage storage = variable.getStorage(); VariableStorage storage = highSymbol.getStorage();
if (!storage.isMemoryStorage()) { if (!storage.isMemoryStorage()) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Database supports global memory variables only"); "Database supports global memory variables only");
} }
if (name == null) { if (name == null) {
name = variable.getName(); name = highSymbol.getName();
if (name != null && SymbolUtilities.isDynamicSymbolPattern(name, true)) { if (name != null && SymbolUtilities.isDynamicSymbolPattern(name, true)) {
name = null; name = null;
} }
} }
if (dataType != null) { if (dataType != null) {
setGlobalDataType(variable, dataType); setGlobalDataType(highSymbol, dataType);
} }
if (name != null) { if (name != null) {
try { try {
setGlobalName(variable, variable.getName(), source); setGlobalName(highSymbol, highSymbol.getName(), source);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
if (isRename) { if (isRename) {

View file

@ -19,6 +19,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage; import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.xml.SpecXmlUtils; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement; import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser; import ghidra.xml.XmlPullParser;
@ -118,6 +119,17 @@ public class HighSymbol {
return id; 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 * 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. * into the decompiler's description of how a function manipulates a particular symbol.

View file

@ -83,24 +83,27 @@ public class LocalSymbolMap {
Function dbFunction = func.getFunction(); Function dbFunction = func.getFunction();
Variable locals[] = dbFunction.getLocalVariables(); Variable locals[] = dbFunction.getLocalVariables();
for (Variable local : locals) { for (Variable local : locals) {
Variable var = local; if (!local.isValid()) {
if (!var.isValid()) {
// exclude locals which don't have valid storage // exclude locals which don't have valid storage
continue; continue;
} }
DataType dt = var.getDataType(); DataType dt = local.getDataType();
boolean istypelock = true; boolean istypelock = true;
boolean isnamelock = true; boolean isnamelock = true;
if (Undefined.isUndefined(dt)) { if (Undefined.isUndefined(dt)) {
istypelock = false; istypelock = false;
} }
String name = var.getName(); String name = local.getName();
VariableStorage storage = var.getVariableStorage(); VariableStorage storage = local.getVariableStorage();
long id = getNextId(); long id = 0;
Symbol symbol = local.getSymbol();
if (symbol != null) {
id = symbol.getID();
}
Address defAddr = null; Address defAddr = null;
if (!storage.isStackStorage()) { if (!storage.isStackStorage()) {
defAddr = dbFunction.getEntryPoint().addWrap(var.getFirstUseOffset()); defAddr = dbFunction.getEntryPoint().addWrap(local.getFirstUseOffset());
} }
HighSymbol sym; HighSymbol sym;
if (storage.isHashStorage()) { if (storage.isHashStorage()) {
@ -131,7 +134,11 @@ public class LocalSymbolMap {
String name = var.getName(); String name = var.getName();
VariableStorage storage = var.getVariableStorage(); VariableStorage storage = var.getVariableStorage();
Address resAddr = storage.isStackStorage() ? null : pcaddr; Address resAddr = storage.isStackStorage() ? null : pcaddr;
long id = getNextId(); long id = 0;
Symbol symbol = var.getSymbol();
if (symbol != null) {
id = symbol.getID();
}
HighSymbol paramSymbol = newMappedSymbol(id, name, dt, storage, resAddr, i); HighSymbol paramSymbol = newMappedSymbol(id, name, dt, storage, resAddr, i);
paramList.add(paramSymbol); paramList.add(paramSymbol);
boolean namelock = true; boolean namelock = true;