mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
Refactor variable renaming
This commit is contained in:
parent
b88ea8c927
commit
c0dfa509ee
18 changed files with 647 additions and 339 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"); }
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue