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.
///
/// 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
@ -2466,9 +2450,10 @@ void ActionNameVars::linkSpacebaseSymbol(Varnode *vn,Funcdata &data,vector<Varno
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
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())
namerec.push_back(vn);
namerec.push_back(offVn);
}
}
@ -2506,8 +2491,8 @@ void ActionNameVars::linkSymbols(Funcdata &data,vector<Varnode *> &namerec)
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());
@ -2523,33 +2508,21 @@ int4 ActionNameVars::apply(Funcdata &data)
vector<Varnode *> 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);
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;
}

View file

@ -463,7 +463,6 @@ 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);

View file

@ -1641,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,
@ -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;
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
@ -1967,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;
}
@ -2654,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

View file

@ -170,7 +170,6 @@ protected:
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 setSymbolId(uint8 val) { symbolId = val; } ///< Assign a unique id to the symbol
void checkSizeTypeLock(void); ///< Calculate if \b size_typelock property is on
public:
/// \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,
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;
@ -531,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
@ -706,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
};
@ -745,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);
@ -777,6 +780,7 @@ 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

View file

@ -101,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

@ -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

@ -362,6 +362,7 @@ public:
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial) const;
bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes);
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 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
@ -382,8 +383,12 @@ 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);

View file

@ -307,26 +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();
if (entry->isDynamic()) {
DynamicHash dhash;
Varnode *vn = dhash.findVarnode(this, entry->getFirstUseAddress(), entry->getHash());
if (vn == (Varnode *)0 || vn->isAnnotation())
return (HighVariable *)0;
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;
}
@ -929,6 +913,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())
@ -990,6 +1017,44 @@ Symbol *Funcdata::linkSymbolReference(Varnode *vn)
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

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
@ -321,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);
}
}
}
@ -446,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)) {
@ -520,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
@ -1220,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);
@ -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
/// 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;
SymbolEntry *entry = sym->getFirstWholeMap();
if (entry->getAddr() != addr)
continue;
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);
}
}
@ -1289,38 +1277,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,23 +21,20 @@
#include "database.hh"
/// \brief An Address pair with a point of use
///
/// 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 {
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
@ -48,15 +45,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 +177,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 +186,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 +212,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

@ -382,7 +382,10 @@ void Varnode::setSymbolEntry(SymbolEntry *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)
high->setSymbol(this);
}

View file

@ -64,28 +64,24 @@ public class RenameVariableAction extends AbstractDecompilerAction {
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();
if (addr.isStackAddress()) {
LocalSymbolMap lsym = hfunc.getLocalSymbolMap();
HighSymbol hsym = lsym.findLocal(addr, null);
if (hsym != null) {
res = hsym.getHighVariable();
}
highSymbol = lsym.findLocal(addr, null);
}
else {
GlobalSymbolMap gsym = hfunc.getGlobalSymbolMap();
HighSymbol hsym = 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);
highSymbol = gsym.getSymbol(addr);
}
}
}
return res;
return highSymbol;
}
/**
@ -140,29 +136,28 @@ public class RenameVariableAction extends AbstractDecompilerAction {
return true;
}
HighVariable variable = tokenAtCursor.getHighVariable();
HighSymbol highSymbol = 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);
if (storageAddress == null) {
return false;
}
variable = forgeHighVariable(storageAddress, controller);
if (variable == null) {
highSymbol = findHighSymbol(storageAddress, controller);
}
else {
highSymbol = variable.getSymbol();
}
if (highSymbol == null) {
return false;
}
}
if (variable.getSymbol() == null) {
return false;
}
if (variable instanceof HighLocal) {
getPopupMenuData().setMenuItemName("Rename Variable");
return true;
}
else if (variable instanceof HighGlobal) {
if (highSymbol.isGlobal()) {
getPopupMenuData().setMenuItemName("Rename Global");
return true;
}
return false;
else {
getPopupMenuData().setMenuItemName("Rename Variable");
}
return true;
}
@Override
@ -170,24 +165,22 @@ public class RenameVariableAction extends AbstractDecompilerAction {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
final ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
HighVariable variable = tokenAtCursor.getHighVariable();
HighSymbol highSymbol = null;
if (variable == null) {
if (tokenAtCursor instanceof ClangVariableToken) {
Address addr = getStorageAddress(tokenAtCursor, controller);
variable = forgeHighVariable(addr, controller);
highSymbol = findHighSymbol(addr, controller);
}
}
if (variable instanceof HighLocal) {
nameTask =
new RenameVariableTask(tool, variable.getSymbol().getName(),
controller.getHighFunction(), variable, tokenAtCursor.getVarnode(),
SourceType.USER_DEFINED);
else {
highSymbol = variable.getSymbol();
}
else if (variable instanceof HighGlobal) {
if (highSymbol != null) {
if (highSymbol.isGlobal()) {
Address addr = null;
HighSymbol sym = variable.getSymbol();
if (sym instanceof HighCodeSymbol) {
addr = ((HighCodeSymbol) sym).getStorage().getMinAddress();
if (highSymbol instanceof HighCodeSymbol) {
addr = ((HighCodeSymbol) highSymbol).getStorage().getMinAddress();
}
if (addr == null || !addr.isMemoryAddress()) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
@ -197,6 +190,11 @@ public class RenameVariableAction extends AbstractDecompilerAction {
nameTask = new RenameGlobalVariableTask(tool, tokenAtCursor.getText(), addr,
controller.getProgram());
}
else {
nameTask = new RenameVariableTask(tool, highSymbol, tokenAtCursor.getVarnode(),
SourceType.USER_DEFINED);
}
}
else if (tokenAtCursor instanceof ClangFieldToken) {
Structure dt = getStructDataType(tokenAtCursor);
if (dt == null) {

View file

@ -26,7 +26,7 @@ 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;
@ -35,13 +35,12 @@ public class RenameVariableTask extends RenameTask {
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;
public RenameVariableTask(PluginTool tool, HighSymbol sym, Varnode ex, SourceType st) {
super(tool, sym.getName());
highSymbol = sym;
exactSpot = ex;
hfunction = hfunc;
function = hfunc.getFunction();
hfunction = sym.getHighFunction();
function = hfunction.getFunction();
program = function.getProgram();
srctype = st;
signatureSrcType = function.getSignatureSource();
@ -55,7 +54,7 @@ public class RenameVariableTask extends RenameTask {
HighFunctionDBUtil.commitReturnToDatabase(hfunction, signatureSrcType);
}
}
HighFunctionDBUtil.updateDBVariable(var.getSymbol(), newName, null, srctype);
HighFunctionDBUtil.updateDBVariable(highSymbol, newName, null, srctype);
}
@Override
@ -66,21 +65,22 @@ public class RenameVariableTask extends RenameTask {
errorMsg = "Duplicate name";
return false;
}
commitRequired = RetypeVariableAction.checkFullCommit(var, hfunction);
commitRequired = RetypeVariableAction.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 (var.getSymbol() == null) {
if (highSymbol == null) {
errorMsg = "Rename Failed: No symbol";
return false;
}

View file

@ -124,17 +124,18 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
return chooserDialog.getUserChosenDataType();
}
private void retypeVariable(HighVariable var, Varnode exactSpot, DataType dt) {
HighFunction hfunction = var.getHighFunction();
private void retypeSymbol(HighSymbol highSymbol, Varnode exactSpot, DataType dt) {
HighFunction hfunction = highSymbol.getHighFunction();
boolean commitRequired = checkFullCommit(var, hfunction);
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 {
var = hfunction.splitOutMergeGroup(var, exactSpot);
HighVariable var = hfunction.splitOutMergeGroup(exactSpot.getHigh(), exactSpot);
highSymbol = var.getSymbol();
}
catch (PcodeException e) {
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());
}
}
HighFunctionDBUtil.updateDBVariable(var.getSymbol(), null, dt, SourceType.USER_DEFINED);
HighFunctionDBUtil.updateDBVariable(highSymbol, null, dt, SourceType.USER_DEFINED);
successfulMod = true;
}
catch (DuplicateNameException e) {
@ -177,7 +178,7 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
}
catch (InvalidInputException e) {
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 {
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.
* 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
* 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)
*/
public static boolean checkFullCommit(HighVariable var, HighFunction hfunction) {
if ((var != null) && (!(var instanceof HighParam))) {
public static boolean checkFullCommit(HighSymbol highSymbol, HighFunction hfunction) {
if (highSymbol != null && !highSymbol.isParameter()) {
return false;
}
Function function = hfunction.getFunction();
@ -333,35 +334,34 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
return false;
}
HighVariable variable = tokenAtCursor.getHighVariable();
HighSymbol highSymbol = null;
if (variable == null) {
Address addr = RenameVariableAction.getStorageAddress(tokenAtCursor, controller);
if (addr == null) {
return false;
}
variable = RenameVariableAction.forgeHighVariable(addr, controller);
if (variable == null) {
highSymbol = RenameVariableAction.findHighSymbol(addr, controller);
}
else {
highSymbol = variable.getSymbol();
}
if (highSymbol == null) {
return false;
}
}
if (variable.getSymbol() == null) {
return false;
}
if (variable instanceof HighLocal) {
getPopupMenuData().setMenuItemName("Retype Variable");
return true;
}
else if (variable instanceof HighGlobal) {
if (highSymbol.isGlobal()) {
getPopupMenuData().setMenuItemName("Retype Global");
return true;
}
return false;
else {
getPopupMenuData().setMenuItemName("Retype Variable");
}
return true;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
HighVariable variable = null;
HighSymbol highSymbol = null;
Structure struct = null;
DataTypeComponent comp = null;
@ -397,18 +397,21 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
return;
}
else {
variable = tokenAtCursor.getHighVariable();
HighVariable variable = tokenAtCursor.getHighVariable();
if (variable == null) {
Address addr = RenameVariableAction.getStorageAddress(tokenAtCursor, controller);
if (addr == null) {
return;
}
variable = RenameVariableAction.forgeHighVariable(addr, controller);
if (variable == null || variable.getSymbol() == null || variable.getOffset() >= 0) {
highSymbol = RenameVariableAction.findHighSymbol(addr, controller);
}
else {
highSymbol = variable.getSymbol();
}
if (highSymbol == null) {
return;
}
}
dataType = chooseDataType(variable.getDataType());
dataType = chooseDataType(highSymbol.getDataType());
}
if (dataType == null) {
@ -418,7 +421,7 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
retypeStructVariable(struct, comp, dataType);
}
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.DecompilerPanel;
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.model.address.Address;
import ghidra.program.model.listing.Data;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
public class HighSymbolTest extends AbstractDecompilerTest {
@Override
@ -52,16 +55,64 @@ public class HighSymbolTest extends AbstractDecompilerTest {
return null;
}
private void renameGlobalVariable(HighSymbol highSymbol, HighVariable highVar, Varnode exact,
String newName) {
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, Varnode exact, String newName) {
Address addr = highSymbol.getStorage().getMinAddress();
RenameGlobalVariableTask rename = new RenameGlobalVariableTask(provider.getTool(),
highSymbol.getName(), addr, highSymbol.getProgram());
assertTrue(rename.isValid("newGlobal"));
assertTrue(rename.isValid(newName));
modifyProgram(p -> {
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
@ -85,7 +136,7 @@ public class HighSymbolTest extends AbstractDecompilerTest {
assertEquals(data.getAddress().getOffset(), 0x10056a0L);
assertEquals(data.getBaseDataType().getLength(), 2);
renameGlobalVariable(highSymbol, variable, null, "newGlobal");
renameGlobalVariable(highSymbol, null, "newGlobal");
waitForDecompiler();
line = getLineStarting("newGlobal");
loc = loc(line.getLineNumber(), 5);
@ -102,5 +153,150 @@ public class HighSymbolTest extends AbstractDecompilerTest {
codeSymbol = highCode.getCodeSymbol();
assertNotNull(codeSymbol);
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
* 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 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,
@ -415,10 +415,10 @@ public class HighFunctionDBUtil {
* variable/label within the function's namespace
* @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 {
HighFunction highFunction = variable.getHighFunction();
HighFunction highFunction = highSymbol.getHighFunction();
Function function = highFunction.getFunction();
Program program = function.getProgram();
@ -430,14 +430,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.isParameter()) {
Parameter dbParam = getDatabaseParameter(variable);
VariableStorage storage = variable.getStorage();
if (highSymbol.isParameter()) {
Parameter dbParam = getDatabaseParameter(highSymbol);
VariableStorage storage = highSymbol.getStorage();
if (dataType != null) {
if (resized && function.hasCustomVariableStorage()) {
VariableStorage newStorage =
@ -452,18 +452,18 @@ public class HighFunctionDBUtil {
dbParam.setName(name, source);
}
}
else if (!variable.isGlobal()) {
else if (!highSymbol.isGlobal()) {
Variable var;
VariableStorage storage;
HighVariable tmpHigh = variable.getHighVariable();
if (tmpHigh != null && tmpHigh.requiresDynamicStorage()) {
VariableStorage storage = highSymbol.getStorage();
HighVariable tmpHigh = highSymbol.getHighVariable();
if (!storage.isHashStorage() && tmpHigh != null &&
tmpHigh.requiresDynamicStorage()) {
storage =
DynamicEntry.buildDynamicStorage(tmpHigh.getRepresentative(), highFunction);
var = null;
}
else {
storage = variable.getStorage();
var = clearConflictingLocalVariables(variable);
var = clearConflictingLocalVariables(highSymbol);
}
boolean usesHashStorage = storage.isHashStorage();
if (dataType == null) {
@ -471,28 +471,28 @@ public class HighFunctionDBUtil {
dataType = var.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 (" + variable.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(variable, dataType, storage, source);
var = createLocalVariable(highSymbol, dataType, storage, source);
}
else {
// fixup reused variable
var.setDataType(dataType, storage, true, source);
}
if (name == null) {
name = variable.getName(); // must update name if not specified
name = highSymbol.getName(); // must update name if not specified
}
try {
// must set/correct name
@ -516,26 +516,26 @@ public class HighFunctionDBUtil {
}
else { // A global symbol
VariableStorage storage = variable.getStorage();
VariableStorage storage = highSymbol.getStorage();
if (!storage.isMemoryStorage()) {
throw new UnsupportedOperationException(
"Database supports global memory variables only");
}
if (name == null) {
name = variable.getName();
name = highSymbol.getName();
if (name != null && SymbolUtilities.isDynamicSymbolPattern(name, true)) {
name = null;
}
}
if (dataType != null) {
setGlobalDataType(variable, dataType);
setGlobalDataType(highSymbol, dataType);
}
if (name != null) {
try {
setGlobalName(variable, variable.getName(), source);
setGlobalName(highSymbol, highSymbol.getName(), source);
}
catch (DuplicateNameException e) {
if (isRename) {

View file

@ -19,6 +19,7 @@ import ghidra.program.model.address.Address;
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;
@ -118,6 +119,17 @@ public class HighSymbol {
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.

View file

@ -83,24 +83,27 @@ public class LocalSymbolMap {
Function dbFunction = func.getFunction();
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;
}
String name = var.getName();
String name = local.getName();
VariableStorage storage = var.getVariableStorage();
long id = getNextId();
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()) {
@ -131,7 +134,11 @@ public class LocalSymbolMap {
String name = var.getName();
VariableStorage storage = var.getVariableStorage();
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);
paramList.add(paramSymbol);
boolean namelock = true;