diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/template/TemplateSimplifier.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/template/TemplateSimplifier.java
index 9b132973ff..d05f5482bf 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/template/TemplateSimplifier.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/template/TemplateSimplifier.java
@@ -18,13 +18,14 @@ package ghidra.app.util.template;
import ghidra.GhidraOptions;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
+import ghidra.program.model.symbol.NameTransformer;
import ghidra.util.HelpLocation;
/**
* Class for simplify names with template data. This class can be used with tool options or
* as a stand alone configurable simplifier.
*/
-public class TemplateSimplifier {
+public class TemplateSimplifier implements NameTransformer {
public static final String SUB_OPTION_NAME = "Templates";
public static final String SIMPLIFY_TEMPLATES_OPTION =
@@ -145,6 +146,7 @@ public class TemplateSimplifier {
* @param input the input string to be simplified
* @return a simplified string
*/
+ @Override
public String simplify(String input) {
if (!doSimplify) {
return input;
@@ -192,10 +194,13 @@ public class TemplateSimplifier {
}
private String doSimplify(String input, int depth) {
- StringBuilder builder = new StringBuilder();
int pos = 0;
- TemplateString ts;
- while ((ts = findTemplateString(input, pos)) != null) {
+ TemplateString ts = findTemplateString(input, pos);
+ if (ts == null) {
+ return input; // Fast fail if no template characters present
+ }
+ StringBuilder builder = new StringBuilder();
+ do {
builder.append(input.substring(pos, ts.start));
String template = ts.getTemplate();
if (depth == 0) {
@@ -215,7 +220,9 @@ public class TemplateSimplifier {
builder.append(">");
}
pos = ts.end + 1;
+ ts = findTemplateString(input, pos);
}
+ while (ts != null);
builder.append(input.substring(pos));
return builder.toString();
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/callgraph.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/callgraph.cc
index 88ae140a38..c7dfbea0b1 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/callgraph.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/callgraph.cc
@@ -213,7 +213,7 @@ CallGraphNode *CallGraph::addNode(Funcdata *f)
throw LowlevelError("Functions with duplicate entry points: "+f->getName()+" "+node.getFuncdata()->getName());
node.entryaddr = f->getAddress();
- node.name = f->getName();
+ node.name = f->getDisplayName();
node.fd = f;
return &node;
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc
index 375708e9f5..3bd9ae6b72 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc
@@ -395,6 +395,7 @@ void Symbol::decodeHeader(Decoder &decoder)
{
name.clear();
+ displayName.clear();
category = no_category;
symbolId = 0;
for(;;) {
@@ -447,12 +448,17 @@ void Symbol::decodeHeader(Decoder &decoder)
if (decoder.readBool())
flags |= Varnode::volatil;
}
+ else if (attribId == ATTRIB_LABEL) {
+ displayName = decoder.readString();
+ }
}
if (category == function_parameter) {
catindex = decoder.readUnsignedInteger(ATTRIB_INDEX);
}
else
catindex = 0;
+ if (displayName.size() == 0)
+ displayName = name;
}
/// Encode the data-type for the Symbol
@@ -532,6 +538,7 @@ FunctionSymbol::FunctionSymbol(Scope *sc,const string &nm,int4 size)
consumeSize = size;
buildType();
name = nm;
+ displayName = nm;
}
FunctionSymbol::FunctionSymbol(Scope *sc,int4 size)
@@ -552,7 +559,7 @@ Funcdata *FunctionSymbol::getFunction(void)
{
if (fd != (Funcdata *)0) return fd;
SymbolEntry *entry = getFirstWholeMap();
- fd = new Funcdata(name,scope,entry->getAddr(),this);
+ fd = new Funcdata(name,displayName,scope,entry->getAddr(),this);
return fd;
}
@@ -575,7 +582,7 @@ void FunctionSymbol::decode(Decoder &decoder)
{
uint4 elemId = decoder.peekElement();
if (elemId == ELEM_FUNCTION) {
- fd = new Funcdata("",scope,Address(),this);
+ fd = new Funcdata("","",scope,Address(),this);
try {
symbolId = fd->decode(decoder);
} catch(RecovError &err) {
@@ -583,6 +590,7 @@ void FunctionSymbol::decode(Decoder &decoder)
throw DuplicateFunctionError(fd->getAddress(),fd->getName());
}
name = fd->getName();
+ displayName = fd->getDisplayName();
if (consumeSize < fd->getSize()) {
if ((fd->getSize()>1)&&(fd->getSize() <= 8))
consumeSize = fd->getSize();
@@ -599,6 +607,9 @@ void FunctionSymbol::decode(Decoder &decoder)
else if (attribId == ATTRIB_ID) {
symbolId = decoder.readUnsignedInteger();
}
+ else if (attribId == ATTRIB_LABEL) {
+ displayName = decoder.readString();
+ }
}
decoder.closeElement(elemId);
}
@@ -727,6 +738,7 @@ LabSymbol::LabSymbol(Scope *sc,const string &nm)
{
buildType();
name = nm;
+ displayName = nm;
}
/// \param sc is the Scope that will contain the new Symbol
@@ -766,6 +778,8 @@ void ExternRefSymbol::buildNameType(void)
name = s.str();
name += "_exref"; // Indicate this is an external reference variable
}
+ if (displayName.size() == 0)
+ displayName = name;
flags |= Varnode::externref | Varnode::typelock;
}
@@ -792,12 +806,15 @@ void ExternRefSymbol::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_EXTERNREFSYMBOL);
- name = ""; // Name is empty
+ name.clear(); // Name is empty
+ displayName.clear();
for(;;) {
uint4 attribId = decoder.getNextAttributeId();
if (attribId == 0) break;
if (attribId == ATTRIB_NAME) // Unless we see it explicitly
name = decoder.readString();
+ else if (attribId == ATTRIB_LABEL)
+ displayName = decoder.readString();
}
refaddr = Address::decode(decoder);
decoder.closeElement(elemId);
@@ -1798,8 +1815,10 @@ void ScopeInternal::addSymbolInternal(Symbol *sym)
nextUniqueId += 1;
}
try {
- if (sym->name.size() == 0)
+ if (sym->name.size() == 0) {
sym->name = buildUndefinedName();
+ sym->displayName = sym->name;
+ }
if (sym->getType() == (Datatype *)0)
throw LowlevelError(sym->getName() + " symbol created with no type");
if (sym->getType()->getSize() < 1)
@@ -2138,6 +2157,7 @@ void ScopeInternal::renameSymbol(Symbol *sym,const string &newname)
multiEntrySet.erase(sym); // The multi-entry set is sorted by name, remove
string oldname = sym->name;
sym->name = newname;
+ sym->displayName = newname;
insertNameTree(sym);
if (sym->wholeCount > 1)
multiEntrySet.insert(sym); // Reenter into the multi-entry set now that name is changed
@@ -3315,14 +3335,32 @@ void Database::decode(Decoder &decoder)
for(;;) {
uint4 subId = decoder.openElement();
if (subId != ELEM_SCOPE) break;
- string name = decoder.readString(ATTRIB_NAME);
- uint8 id = decoder.readUnsignedInteger(ATTRIB_ID);
+ string name;
+ string displayName;
+ uint8 id = 0;
+ bool seenId = false;
+ for(;;) {
+ uint4 attribId = decoder.getNextAttributeId();
+ if (attribId == 0) break;
+ if (attribId == ATTRIB_NAME)
+ name = decoder.readString();
+ else if (attribId == ATTRIB_ID) {
+ id = decoder.readUnsignedInteger();
+ seenId = true;
+ }
+ else if (attribId == ATTRIB_LABEL)
+ displayName = decoder.readString();
+ }
+ if (name.empty() || !seenId)
+ throw DecoderError("Missing name and id attributes in scope");
Scope *parentScope = (Scope *)0;
uint4 parentId = decoder.peekElement();
if (parentId == ELEM_PARENT) {
parentScope = parseParentTag(decoder);
}
Scope *newScope = findCreateScope(id, name, parentScope);
+ if (!displayName.empty())
+ newScope->setDisplayName(displayName);
newScope->decode(decoder);
decoder.closeElement(subId);
}
@@ -3356,4 +3394,39 @@ void Database::decodeScope(Decoder &decoder,Scope *newScope)
decoder.closeElement(elemId);
}
+/// Some namespace objects may already exist. Create those that don't.
+/// \param decoder is the stream to decode the path from
+/// \return the namespace described by the path
+Scope *Database::decodeScopePath(Decoder &decoder)
+
+{
+ Scope *curscope = getGlobalScope();
+ uint4 elemId = decoder.openElement(ELEM_PARENT);
+ uint4 subId = decoder.openElement();
+ decoder.closeElementSkipping(subId); // Skip element describing the root scope
+ for(;;) {
+ subId = decoder.openElement();
+ if (subId != ELEM_VAL) break;
+ string displayName;
+ uint8 scopeId = 0;
+ for(;;) {
+ uint4 attribId = decoder.getNextAttributeId();
+ if (attribId == 0) break;
+ if (attribId == ATTRIB_ID)
+ scopeId = decoder.readUnsignedInteger();
+ else if (attribId == ATTRIB_LABEL)
+ displayName = decoder.readString();
+ }
+ string name = decoder.readString(ATTRIB_CONTENT);
+ if (scopeId == 0)
+ throw DecoderError("Missing name and id in scope");
+ curscope = findCreateScope(scopeId, name, curscope);
+ if (!displayName.empty())
+ curscope->setDisplayName(displayName);
+ decoder.closeElement(subId);
+ }
+ decoder.closeElement(elemId);
+ return curscope;
+}
+
} // End namespace ghidra
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh
index 542a0d7b9c..bdf162d2af 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh
@@ -176,6 +176,7 @@ class Symbol {
protected:
Scope *scope; ///< The scope that owns this symbol
string name; ///< The local name of the symbol
+ string displayName; ///< Name to use when displaying symbol in output
Datatype *type; ///< The symbol's data-type
uint4 nameDedup; ///< id to distinguish symbols with the same name
uint4 flags; ///< Varnode-like properties of the symbol
@@ -218,6 +219,7 @@ public:
Symbol(Scope *sc,const string &nm,Datatype *ct); ///< Construct given a name and data-type
Symbol(Scope *sc); ///< Construct for use with decode()
const string &getName(void) const { return name; } ///< Get the local name of the symbol
+ const string &getDisplayName(void) const { return displayName; } ///< Get the name to display in output
Datatype *getType(void) const { return type; } ///< Get the data-type
uint8 getId(void) const { return symbolId; } ///< Get a unique id for the symbol
uint4 getFlags(void) const { return flags; } ///< Get the boolean properties of the Symbol
@@ -469,6 +471,7 @@ class Scope {
protected:
Architecture *glb; ///< Architecture of \b this scope
string name; ///< Name of \b this scope
+ string displayName; ///< Name to display in output
Funcdata *fd; ///< (If non-null) the function which \b this is the local Scope for
uint8 uniqueId; ///< Unique id for the scope, for deduping scope names, assigning symbol ids
static const Scope *stackAddr(const Scope *scope1,
@@ -551,6 +554,7 @@ protected:
const RangeList &uselim)=0;
SymbolEntry *addMap(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
+ void setDisplayName(const string &nm) { displayName = nm; } ///< Change name displayed in output
public:
#ifdef OPACTION_DEBUG
mutable bool debugon;
@@ -559,7 +563,7 @@ public:
#endif
/// \brief Construct an empty scope, given a name and Architecture
Scope(uint8 id,const string &nm,Architecture *g,Scope *own) {
- uniqueId = id; name = nm; glb = g; parent = (Scope *)0; fd = (Funcdata *)0; owner=own;
+ uniqueId = id; name = nm; displayName = nm; glb = g; parent = (Scope *)0; fd = (Funcdata *)0; owner=own;
#ifdef OPACTION_DEBUG
debugon = false;
#endif
@@ -732,6 +736,7 @@ public:
const Address &addr,const Address &usepoint);
const string &getName(void) const { return name; } ///< Get the name of the Scope
+ const string &getDisplayName(void) const { return displayName; } ///< Get name displayed in output
uint8 getId(void) const { return uniqueId; } ///< Get the globally unique id
bool isGlobal(void) const { return (fd == (Funcdata *)0); } ///< Return \b true if \b this scope is global
@@ -938,7 +943,8 @@ public:
const partmap
&getProperties(void) const { return flagbase; } ///< Get the entire property map
void encode(Encoder &encoder) const; ///< Encode the whole Database to a stream
void decode(Decoder &decoder); ///< Decode the whole database from a stream
- void decodeScope(Decoder &decoder,Scope *newScope); ///< Register and fill out a single Scope from an XML \ tag
+ void decodeScope(Decoder &decoder,Scope *newScope); ///< Register and fill out a single Scope from an XML \ tag
+ Scope *decodeScopePath(Decoder &decoder); ///< Decode a namespace path and make sure each namespace exists
};
/// \param sc is the scope containing the new symbol
@@ -949,6 +955,7 @@ inline Symbol::Symbol(Scope *sc,const string &nm,Datatype *ct)
{
scope=sc;
name=nm;
+ displayName = nm;
nameDedup=0;
type=ct;
flags=0;
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc
index fbe0bf85c7..7e7e59c5fc 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc
@@ -42,7 +42,7 @@ ScopeGhidra::~ScopeGhidra(void)
/// The Ghidra client reports a \e namespace id associated with
/// Symbol. Determine if a matching \e namespace Scope already exists in the cache and build
-/// it if it isn't. This may mean creating a new \e namespace Scope.
+/// it if it isn't. This may mean creating the \e namespace Scope and its ancestors.
/// \param id is the ID associated with the Ghidra namespace
/// \return the Scope matching the id.
Scope *ScopeGhidra::reresolveScope(uint8 id) const
@@ -58,19 +58,7 @@ Scope *ScopeGhidra::reresolveScope(uint8 id) const
if (!ghidra->getNamespacePath(id,decoder))
throw LowlevelError("Could not get namespace info");
- Scope *curscope = symboltab->getGlobalScope(); // Get pointer to ourselves (which is not const)
- uint4 elemId = decoder.openElement();
- uint4 subId = decoder.openElement();
- decoder.closeElementSkipping(subId); // Skip element describing the root scope
- for(;;) {
- subId = decoder.openElement();
- if (subId == 0) break;
- uint8 scopeId = decoder.readUnsignedInteger(ATTRIB_ID);
- curscope = symboltab->findCreateScope(scopeId, decoder.readString(ATTRIB_CONTENT), curscope);
- decoder.closeElement(subId);
- }
- decoder.closeElement(elemId);
- return curscope;
+ return symboltab->decodeScopePath(decoder);
}
/// The Ghidra client can respond to a query negatively by sending a
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc
index c1e640afc4..3b1cb47412 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc
@@ -1398,8 +1398,9 @@ void FlowInfo::recoverJumpTables(vector &newTables,vectorgetAddr().printRaw(s1);
+ string nm = s1.str();
// Prepare partial Funcdata object for analysis if necessary
- Funcdata partial(s1.str(),data.getScopeLocal()->getParent(),data.getAddress(),(FunctionSymbol *)0);
+ Funcdata partial(nm,nm,data.getScopeLocal()->getParent(),data.getAddress(),(FunctionSymbol *)0);
for(int4 i=0;igetAddress();
- if (fd->getName().size() != 0)
- name = fd->getName();
+ if (fd->getDisplayName().size() != 0)
+ name = fd->getDisplayName();
}
}
@@ -5130,7 +5130,7 @@ void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd)
{
entryaddress = newfd->getAddress();
- name = newfd->getName();
+ name = newfd->getDisplayName();
fd = newfd;
Varnode *vn = data.newVarnodeCallSpecs(this);
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc
index 69ae718d64..13e0606775 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc
@@ -30,7 +30,7 @@ ElementId ELEM_VARNODES = ElementId("varnodes",119);
/// \param addr is the entry address for the function
/// \param sym is the symbol representing the function
/// \param sz is the number of bytes (of code) in the function body
-Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz)
+Funcdata::Funcdata(const string &nm,const string &disp,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz)
: baseaddr(addr),
funcp(),
vbank(scope->getArch()),
@@ -47,6 +47,7 @@ Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,FunctionSym
glb = scope->getArch();
minLanedSize = glb->getMinimumLanedRegisterSize();
name = nm;
+ displayName = disp;
size = sz;
AddrSpace *stackid = glb->getStackSpace();
@@ -734,9 +735,13 @@ uint8 Funcdata::decode(Decoder &decoder)
if (decoder.readBool())
flags |= no_code;
}
+ else if (attribId == ATTRIB_LABEL)
+ displayName = decoder.readString();
}
if (name.size() == 0)
throw LowlevelError("Missing function name");
+ if (displayName.size() == 0)
+ displayName = name;
if (size == -1)
throw LowlevelError("Missing function size");
baseaddr = Address::decode( decoder );
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh
index b54590848b..7ea31a4cc8 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh
@@ -79,6 +79,7 @@ class Funcdata {
Architecture *glb; ///< Global configuration data
FunctionSymbol *functionSymbol; ///< The symbol representing \b this function
string name; ///< Name of function
+ string displayName; ///< Name to display in output
Address baseaddr; ///< Starting code address of binary data
FuncProto funcp; ///< Prototype of this function
ScopeLocal *localmap; ///< Local variables (symbols in the function scope)
@@ -136,9 +137,10 @@ class Funcdata {
static PcodeOp *findPrimaryBranch(PcodeOpTree::const_iterator iter,PcodeOpTree::const_iterator enditer,
bool findbranch,bool findcall,bool findreturn);
public:
- Funcdata(const string &nm,Scope *conf,const Address &addr,FunctionSymbol *sym,int4 sz=0); ///< Constructor
+ Funcdata(const string &nm,const string &disp,Scope *conf,const Address &addr,FunctionSymbol *sym,int4 sz=0); ///< Constructor
~Funcdata(void); ///< Destructor
const string &getName(void) const { return name; } ///< Get the function's local symbol name
+ const string &getDisplayName(void) const { return displayName; } ///< Get the name to display in output
const Address &getAddress(void) const { return baseaddr; } ///< Get the entry point address
int4 getSize(void) const { return size; } ///< Get the function body size in bytes
Architecture *getArch(void) const { return glb; } ///< Get the program/architecture owning \b this function
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc
index a84562e971..e4afcce855 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc
@@ -222,7 +222,7 @@ void PrintC::pushSymbolScope(const Symbol *symbol)
pushOp(&scope, (PcodeOp *)0);
}
for(int4 i=scopedepth-1;i>=0;--i) {
- pushAtom(Atom(scopeList[i]->getName(),syntax,EmitMarkup::global_color,(PcodeOp *)0,(Varnode *)0));
+ pushAtom(Atom(scopeList[i]->getDisplayName(),syntax,EmitMarkup::global_color,(PcodeOp *)0,(Varnode *)0));
}
}
}
@@ -252,7 +252,7 @@ void PrintC::emitSymbolScope(const Symbol *symbol)
point = point->getParent();
}
for(int4 i=scopedepth-1;i>=0;--i) {
- emit->print(scopeList[i]->getName(), EmitMarkup::global_color);
+ emit->print(scopeList[i]->getDisplayName(), EmitMarkup::global_color);
emit->print(scope.print1, EmitMarkup::no_color);
}
}
@@ -285,7 +285,7 @@ void PrintC::pushTypeStart(const Datatype *ct,bool noident)
}
else {
pushOp(tok,(const PcodeOp *)0);
- pushAtom(Atom(ct->getName(),typetoken,EmitMarkup::type_color,ct));
+ pushAtom(Atom(ct->getDisplayName(),typetoken,EmitMarkup::type_color,ct));
}
for(int4 i=typestack.size()-2;i>=0;--i) {
ct = typestack[i];
@@ -661,7 +661,7 @@ void PrintC::opConstructor(const PcodeOp *op,bool withNew)
if (dt->getMetatype() == TYPE_PTR) {
dt = ((TypePointer *)dt)->getPtrTo();
}
- string nm = dt->getName();
+ string nm = dt->getDisplayName();
pushOp(&function_call,op);
pushAtom(Atom(nm,optoken,EmitMarkup::funcname_color,op));
// implied vn's pushed on in reverse order for efficiency
@@ -1112,7 +1112,7 @@ void PrintC::opCpoolRefOp(const PcodeOp *op)
pushAtom(Atom(rec->getToken(),functoken,EmitMarkup::funcname_color,op,outvn));
pushOp(&comma,(const PcodeOp *)0);
pushVn(vn0,op,mods);
- pushAtom(Atom(dt->getName(),syntax,EmitMarkup::type_color,op,outvn));
+ pushAtom(Atom(dt->getDisplayName(),syntax,EmitMarkup::type_color,op,outvn));
break;
}
case CPoolRecord::primitive: // Should be eliminated
@@ -1163,7 +1163,7 @@ void PrintC::opNewOp(const PcodeOp *op)
while (dt->getMetatype() == TYPE_PTR) {
dt = ((TypePointer *)dt)->getPtrTo();
}
- nm = dt->getName();
+ nm = dt->getDisplayName();
}
pushOp(&subscript,op);
pushAtom(Atom(nm,optoken,EmitMarkup::type_color,op));
@@ -1658,7 +1658,7 @@ bool PrintC::pushPtrCodeConstant(uintb val,const TypePointer *ct,
val = AddrSpace::addressToByte(val,spc->getWordSize());
fd = glb->symboltab->getGlobalScope()->queryFunction( Address(spc,val));
if (fd != (Funcdata *)0) {
- pushAtom(Atom(fd->getName(),functoken,EmitMarkup::funcname_color,op,fd));
+ pushAtom(Atom(fd->getDisplayName(),functoken,EmitMarkup::funcname_color,op,fd));
return true;
}
return false;
@@ -1839,7 +1839,7 @@ void PrintC::pushSymbol(const Symbol *sym,const Varnode *vn,const PcodeOp *op)
HighVariable *high = vn->getHigh();
if (high->isUnmerged()) {
ostringstream s;
- s << sym->getName();
+ s << sym->getDisplayName();
SymbolEntry *entry = high->getSymbolEntry();
if (entry != (SymbolEntry *)0) {
s << '$' << dec << entry->getSymbol()->getMapEntryPosition(entry);
@@ -1850,7 +1850,7 @@ void PrintC::pushSymbol(const Symbol *sym,const Varnode *vn,const PcodeOp *op)
return;
}
}
- pushAtom(Atom(sym->getName(),vartoken,tokenColor,op,vn));
+ pushAtom(Atom(sym->getDisplayName(),vartoken,tokenColor,op,vn));
}
void PrintC::pushUnnamedLocation(const Address &addr,
@@ -1984,7 +1984,7 @@ void PrintC::pushMismatchSymbol(const Symbol *sym,int4 off,int4 sz,
// We prepend an underscore to indicate a close
// but not quite match
- string nm = '_'+sym->getName();
+ string nm = '_'+sym->getDisplayName();
pushAtom(Atom(nm,vartoken,EmitMarkup::var_color,op,vn));
}
else
@@ -2057,7 +2057,7 @@ void PrintC::emitStructDefinition(const TypeStruct *ct)
emit->tagLine();
emit->print(CLOSE_CURLY);
emit->spaces(1);
- emit->print(ct->getName());
+ emit->print(ct->getDisplayName());
emit->print(SEMICOLON);
}
@@ -2100,7 +2100,7 @@ void PrintC::emitEnumDefinition(const TypeEnum *ct)
emit->tagLine();
emit->print(CLOSE_CURLY);
emit->spaces(1);
- emit->print(ct->getName());
+ emit->print(ct->getDisplayName());
emit->print(SEMICOLON);
}
@@ -2507,7 +2507,7 @@ void PrintC::emitFunctionDeclaration(const Funcdata *fd)
}
int4 id1 = emit->openGroup();
emitSymbolScope(fd->getSymbol());
- emit->tagFuncName(fd->getName(),EmitMarkup::funcname_color,fd,(PcodeOp *)0);
+ emit->tagFuncName(fd->getDisplayName(),EmitMarkup::funcname_color,fd,(PcodeOp *)0);
emit->spaces(function_call.spacing,function_call.bump);
int4 id2 = emit->openParen(OPEN_PAREN);
@@ -3122,7 +3122,7 @@ void PrintC::emitLabel(const FlowBlock *bl)
const Scope *symScope = ((const BlockBasic *)bb)->getFuncdata()->getScopeLocal();
Symbol *sym = symScope->queryCodeLabel(addr);
if (sym != (Symbol *)0) {
- emit->tagLabel(sym->getName(),EmitMarkup::no_color,spc,off);
+ emit->tagLabel(sym->getDisplayName(),EmitMarkup::no_color,spc,off);
return;
}
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc
index 3084ffee21..5885f2d585 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc
@@ -107,7 +107,7 @@ void PrintJava::pushTypeStart(const Datatype *ct,bool noident)
pushAtom(Atom(nm,typetoken,EmitMarkup::type_color,ct));
}
else {
- pushAtom(Atom(ct->getName(),typetoken,EmitMarkup::type_color,ct));
+ pushAtom(Atom(ct->getDisplayName(),typetoken,EmitMarkup::type_color,ct));
}
for(int4 i=0;igetName(),syntax,EmitMarkup::type_color,op,outvn));
+ pushAtom(Atom(dt->getDisplayName(),syntax,EmitMarkup::type_color,op,outvn));
break;
}
case CPoolRecord::primitive: // Should be eliminated
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
index fa02605a63..30faf0b656 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
@@ -544,6 +544,9 @@ void Datatype::decodeBasic(Decoder &decoder)
uint4 val = encodeIntegerFormat(decoder.readString());
setDisplayFormat(val);
}
+ else if (attrib == ATTRIB_LABEL) {
+ displayName = decoder.readString();
+ }
}
if (size < 0)
throw LowlevelError("Bad size for type "+name);
@@ -554,6 +557,8 @@ void Datatype::decodeBasic(Decoder &decoder)
// Id needs to be unique compared to another data-type with the same name
id = hashSize(id, size);
}
+ if (displayName.empty())
+ displayName = name;
}
/// If a type id is explicitly provided for a data-type, this routine is used
@@ -3003,6 +3008,7 @@ Datatype *TypeFactory::setName(Datatype *ct,const string &n)
nametree.erase( ct ); // Erase any name reference
tree.erase(ct); // Remove new type completely from trees
ct->name = n; // Change the name
+ ct->displayName = n;
if (ct->id == 0)
ct->id = Datatype::hashName(n);
// Insert type with new name
@@ -3215,7 +3221,7 @@ TypeVoid *TypeFactory::getTypeVoid(void)
if (ct != (TypeVoid *)0)
return ct;
TypeVoid tv;
- tv.id = Datatype::hashName(tv.getName());
+ tv.id = Datatype::hashName(tv.name);
ct = (TypeVoid *)tv.clone();
tree.insert(ct);
nametree.insert(ct);
@@ -3332,6 +3338,7 @@ TypeCode *TypeFactory::getTypeCode(const string &nm)
if (nm.size()==0) return getTypeCode();
TypeCode tmp; // Generic code data-type
tmp.name = nm; // with a name
+ tmp.displayName = nm;
tmp.id = Datatype::hashName(nm);
tmp.markComplete(); // considered complete
return (TypeCode *) findAdd(tmp);
@@ -3384,6 +3391,7 @@ Datatype *TypeFactory::getTypedef(Datatype *ct,const string &name,uint8 id,uint4
}
res = ct->clone(); // Clone everything
res->name = name; // But a new name
+ res->displayName = name;
res->id = id; // and new id
res->flags &= ~((uint4)Datatype::coretype); // Not a core type
res->typedefImm = ct;
@@ -3438,6 +3446,7 @@ TypePointer *TypeFactory::getTypePointer(int4 s,Datatype *pt,uint4 ws,const stri
pt = pt->getStripped();
TypePointer tmp(s,pt,ws);
tmp.name = n;
+ tmp.displayName = n;
tmp.id = Datatype::hashName(n);
return (TypePointer *) findAdd(tmp);
}
@@ -3485,6 +3494,7 @@ TypeStruct *TypeFactory::getTypeStruct(const string &n)
{
TypeStruct tmp;
tmp.name = n;
+ tmp.displayName = n;
tmp.id = Datatype::hashName(n);
return (TypeStruct *) findAdd(tmp);
}
@@ -3505,6 +3515,7 @@ TypeUnion *TypeFactory::getTypeUnion(const string &n)
{
TypeUnion tmp;
tmp.name = n;
+ tmp.displayName = n;
tmp.id = Datatype::hashName(n);
return (TypeUnion *) findAdd(tmp);
}
@@ -3587,6 +3598,7 @@ TypePointerRel *TypeFactory::getTypePointerRel(int4 sz,Datatype *parent,Datatype
{
TypePointerRel tp(sz,ptrTo,ws,parent,off);
tp.name = nm;
+ tp.displayName = nm;
tp.id = Datatype::hashName(nm);
TypePointerRel *res = (TypePointerRel *)findAdd(tp);
return res;
@@ -3605,6 +3617,7 @@ TypePointer *TypeFactory::getTypePointerWithSpace(Datatype *ptrTo,AddrSpace *spc
{
TypePointer tp(ptrTo,spc);
tp.name = nm;
+ tp.displayName = nm;
tp.id = Datatype::hashName(nm);
TypePointer *res = (TypePointer *)findAdd(tp);
return res;
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh
index 957a788d57..0467995dd6 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh
@@ -159,12 +159,13 @@ protected:
};
friend class TypeFactory;
friend struct DatatypeCompare;
+ uint8 id; ///< A unique id for the type (or 0 if an id is not assigned)
int4 size; ///< Size (of variable holding a value of this type)
+ uint4 flags; ///< Boolean properties of the type
string name; ///< Name of type
+ string displayName; ///< Name to display in output
type_metatype metatype; ///< Meta-type - type disregarding size
sub_metatype submeta; ///< Sub-type of of the meta-type, for comparisons
- uint4 flags; ///< Boolean properties of the type
- uint8 id; ///< A unique id for the type (or 0 if an id is not assigned)
Datatype *typedefImm; ///< The immediate data-type being typedefed by \e this
void decodeBasic(Decoder &decoder); ///< Recover basic data-type properties
void encodeBasic(type_metatype meta,Encoder &encoder) const; ///< Encode basic data-type properties
@@ -176,8 +177,8 @@ protected:
static uint8 hashSize(uint8 id,int4 size); ///< Reversibly hash size into id
public:
/// Construct the base data-type copying low-level properties of another
- Datatype(const Datatype &op) { size = op.size; name=op.name; metatype=op.metatype; submeta=op.submeta; flags=op.flags;
- id=op.id; typedefImm=op.typedefImm; }
+ Datatype(const Datatype &op) { size = op.size; name=op.name; displayName=op.displayName; metatype=op.metatype;
+ submeta=op.submeta; flags=op.flags; id=op.id; typedefImm=op.typedefImm; }
/// Construct the base data-type providing size and meta-type
Datatype(int4 s,type_metatype m) { size=s; metatype=m; submeta=base2sub[m]; flags=0; id=0; typedefImm=(Datatype *)0; }
virtual ~Datatype(void) {} ///< Destructor
@@ -203,6 +204,7 @@ public:
uint8 getId(void) const { return id; } ///< Get the type id
int4 getSize(void) const { return size; } ///< Get the type size
const string &getName(void) const { return name; } ///< Get the type name
+ const string &getDisplayName(void) const { return displayName; } ///< Get string to use in display
Datatype *getTypedef(void) const { return typedefImm; } ///< Get the data-type immediately typedefed by \e this (or null)
virtual void printRaw(ostream &s) const; ///< Print a description of the type to stream
virtual const TypeField *findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const;
@@ -279,7 +281,7 @@ public:
/// Construct TypeBase from a size and meta-type
TypeBase(int4 s,type_metatype m) : Datatype(s,m) {}
/// Construct TypeBase from a size, meta-type, and name
- TypeBase(int4 s,type_metatype m,const string &n) : Datatype(s,m) { name = n; }
+ TypeBase(int4 s,type_metatype m,const string &n) : Datatype(s,m) { name = n; displayName = n; }
virtual Datatype *clone(void) const { return new TypeBase(*this); }
};
@@ -326,7 +328,7 @@ public:
/// Construct from another TypeVoid
TypeVoid(const TypeVoid &op) : Datatype(op) { flags |= Datatype::coretype; }
/// Constructor
- TypeVoid(void) : Datatype(0,TYPE_VOID) { name = "void"; flags |= Datatype::coretype; }
+ TypeVoid(void) : Datatype(0,TYPE_VOID) { name = "void"; displayName = name; flags |= Datatype::coretype; }
virtual Datatype *clone(void) const { return new TypeVoid(*this); }
virtual void encode(Encoder &encoder) const;
};
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompInterface.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompInterface.java
index 070723a0d7..aab164f9fd 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompInterface.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompInterface.java
@@ -370,7 +370,7 @@ public class DecompInterface {
}
compilerSpec = spec;
- dtmanage = new PcodeDataTypeManager(prog);
+ dtmanage = new PcodeDataTypeManager(prog, options.getNameTransformer());
try {
decompCallback =
new DecompileCallback(prog, pcodelanguage, program.getCompilerSpec(), dtmanage);
@@ -642,6 +642,9 @@ public class DecompInterface {
*/
public synchronized boolean setOptions(DecompileOptions options) {
this.options = options;
+ if (dtmanage != null) {
+ dtmanage.setNameTransformer(options.getNameTransformer());
+ }
decompileMessage = "";
// Property can be set before process exists
if (decompProcess == null) {
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java
index 037afa23ba..9e46e05f44 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java
@@ -539,7 +539,7 @@ public class DecompileCallback {
*/
public void getNamespacePath(long id, Encoder resultEncoder) throws IOException {
Namespace namespace = getNameSpaceByID(id);
- HighFunction.encodeNamespace(resultEncoder, namespace);
+ HighFunction.encodeNamespace(resultEncoder, namespace, dtmanage.getNameTransformer());
if (debug != null) {
debug.getNamespacePath(namespace);
}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java
index fb64393bd8..80645f95ec 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java
@@ -29,16 +29,16 @@ import generic.theme.Gui;
import ghidra.GhidraOptions;
import ghidra.GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES;
import ghidra.app.util.HelpTopics;
-import ghidra.framework.options.Options;
+import ghidra.app.util.template.TemplateSimplifier;
import ghidra.framework.options.ToolOptions;
-import ghidra.framework.plugintool.Plugin;
-import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramCompilerSpec;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.CompilerSpec.EvaluationModelType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.ElementId;
import ghidra.program.model.pcode.Encoder;
+import ghidra.program.model.symbol.NameTransformer;
+import ghidra.program.model.symbol.IdentityNameTransformer;
import ghidra.util.HelpLocation;
/**
@@ -397,6 +397,8 @@ public class DecompileOptions {
private DecompilerLanguage displayLanguage; // Output language displayed by the decompiler
+ private NameTransformer nameTransformer; // Transformer applied to data-type/function names
+
private String protoEvalModel; // Name of the prototype evaluation model
public DecompileOptions() {
@@ -434,16 +436,17 @@ public class DecompileOptions {
payloadLimitMBytes = SUGGESTED_MAX_PAYLOAD_BYTES;
maxIntructionsPer = SUGGESTED_MAX_INSTRUCTIONS;
cachedResultsSize = SUGGESTED_CACHED_RESULTS_SIZE;
+ nameTransformer = null;
}
/**
* Grab all the decompiler options from various sources within a specific tool and program
* and cache them in this object.
- * @param ownerPlugin the plugin that owns the "tool options" for the decompiler
+ * @param fieldOptions the Options object containing options specific to listing fields
* @param opt the Options object that contains the "tool options" specific to the decompiler
* @param program the program whose "program options" are relevant to the decompiler
*/
- public void grabFromToolAndProgram(Plugin ownerPlugin, ToolOptions opt, Program program) {
+ public void grabFromToolAndProgram(ToolOptions fieldOptions, ToolOptions opt, Program program) {
grabFromProgram(program);
@@ -491,20 +494,18 @@ public class DecompileOptions {
maxIntructionsPer = opt.getInt(MAX_INSTRUCTIONS, SUGGESTED_MAX_INSTRUCTIONS);
cachedResultsSize = opt.getInt(CACHED_RESULTS_SIZE_MSG, SUGGESTED_CACHED_RESULTS_SIZE);
- grabFromToolOptions(ownerPlugin);
+ grabFromFieldOptions(fieldOptions);
}
- private void grabFromToolOptions(Plugin ownerPlugin) {
- if (ownerPlugin == null) {
+ private void grabFromFieldOptions(ToolOptions fieldOptions) {
+ if (fieldOptions == null) {
return;
}
- PluginTool tool = ownerPlugin.getTool();
- Options toolOptions = tool.getOptions(CATEGORY_BROWSER_FIELDS);
-
CURSOR_MOUSE_BUTTON_NAMES mouseEvent =
- toolOptions.getEnum(CURSOR_HIGHLIGHT_BUTTON_NAME, CURSOR_MOUSE_BUTTON_NAMES.MIDDLE);
+ fieldOptions.getEnum(CURSOR_HIGHLIGHT_BUTTON_NAME, CURSOR_MOUSE_BUTTON_NAMES.MIDDLE);
middleMouseHighlightButton = mouseEvent.getMouseEventID();
+ nameTransformer = new TemplateSimplifier(fieldOptions);
}
/**
@@ -531,10 +532,18 @@ public class DecompileOptions {
displayLanguage = cspec.getDecompilerOutputLanguage();
}
+ /**
+ * @return the default prototype to assume if no other information about a function is known
+ */
public String getProtoEvalModel() {
return protoEvalModel;
}
+ /**
+ * Set the default prototype model for the decompiler. This is the model assumed if no other
+ * information about a function is known.
+ * @param protoEvalModel is the name of the prototype model to set as default
+ */
public void setProtoEvalModel(String protoEvalModel) {
this.protoEvalModel = protoEvalModel;
}
@@ -542,11 +551,11 @@ public class DecompileOptions {
/**
* This registers all the decompiler tool options with ghidra, and has the side effect of
* pulling all the current values for the options if they exist
- * @param ownerPlugin the plugin to which the options should be registered
- * @param opt the options object to register with
+ * @param fieldOptions the options object specific to listing fields
+ * @param opt the options object specific to the decompiler
* @param program the program
*/
- public void registerOptions(Plugin ownerPlugin, ToolOptions opt, Program program) {
+ public void registerOptions(ToolOptions fieldOptions, ToolOptions opt, Program program) {
opt.registerOption(PREDICATE_OPTIONSTRING, PREDICATE_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisPredicate"),
PREDICATE_OPTIONDESCRIPTION);
@@ -686,7 +695,7 @@ public class DecompileOptions {
"Current variable highlight");
opt.registerOption(CACHED_RESULTS_SIZE_MSG, SUGGESTED_CACHED_RESULTS_SIZE,
new HelpLocation(HelpTopics.DECOMPILER, "GeneralCacheSize"), CACHE_RESULTS_DESCRIPTION);
- grabFromToolAndProgram(ownerPlugin, opt, program);
+ grabFromToolAndProgram(fieldOptions, opt, program);
}
private static void appendOption(Encoder encoder, ElementId option, String p1, String p2,
@@ -825,10 +834,17 @@ public class DecompileOptions {
encoder.closeElement(ELEM_OPTIONSLIST);
}
+ /**
+ * @return the maximum number of characters the decompiler displays in a single line of output
+ */
public int getMaxWidth() {
return maxwidth;
}
+ /**
+ * Set the maximum number of characters the decompiler displays in a single line of output
+ * @param maxwidth is the maximum number of characters
+ */
public void setMaxWidth(int maxwidth) {
this.maxwidth = maxwidth;
}
@@ -938,139 +954,296 @@ public class DecompileOptions {
return SEARCH_HIGHLIGHT_COLOR;
}
+ /**
+ * @return the mouse button that should be used to toggle the primary token highlight
+ */
public int getMiddleMouseHighlightButton() {
return middleMouseHighlightButton;
}
+ /**
+ * @return true if Pre comments are included as part of decompiler output
+ */
public boolean isPRECommentIncluded() {
return commentPREInclude;
}
+ /**
+ * Set whether Pre comments are displayed as part of decompiler output
+ * @param commentPREInclude is true if Pre comments are output
+ */
public void setPRECommentIncluded(boolean commentPREInclude) {
this.commentPREInclude = commentPREInclude;
}
+ /**
+ * @return true if Plate comments are included as part of decompiler output
+ */
public boolean isPLATECommentIncluded() {
return commentPLATEInclude;
}
+ /**
+ * Set whether Plate comments are displayed as part of decompiler output
+ * @param commentPLATEInclude is true if Plate comments are output
+ */
public void setPLATECommentIncluded(boolean commentPLATEInclude) {
this.commentPLATEInclude = commentPLATEInclude;
}
+ /**
+ * @return true if Post comments are included as part of decompiler output
+ */
public boolean isPOSTCommentIncluded() {
return commentPOSTInclude;
}
+ /**
+ * Set whether Post comments are displayed as part of decompiler output
+ * @param commentPOSTInclude is true if Post comments are output
+ */
public void setPOSTCommentIncluded(boolean commentPOSTInclude) {
this.commentPOSTInclude = commentPOSTInclude;
}
+ /**
+ * @return true if End-of-line comments are included as part of decompiler output
+ */
public boolean isEOLCommentIncluded() {
return commentEOLInclude;
}
+ /**
+ * Set whether End-of-line comments are displayed as part of decompiler output.
+ * @param commentEOLInclude is true if End-of-line comments are output
+ */
public void setEOLCommentIncluded(boolean commentEOLInclude) {
this.commentEOLInclude = commentEOLInclude;
}
+ /**
+ * @return true if WARNING comments are included as part of decompiler output
+ */
public boolean isWARNCommentIncluded() {
return commentWARNInclude;
}
+ /**
+ * Set whether automatically generated WARNING comments are displayed as part of
+ * decompiler output.
+ * @param commentWARNInclude is true if WARNING comments are output
+ */
public void setWARNCommentIncluded(boolean commentWARNInclude) {
this.commentWARNInclude = commentWARNInclude;
}
+ /**
+ * @return true if function header comments are included as part of decompiler output
+ */
public boolean isHeadCommentIncluded() {
return commentHeadInclude;
}
+ /**
+ * Set whether function header comments are included as part of decompiler output.
+ * @param commentHeadInclude is true if header comments are output
+ */
public void setHeadCommentIncluded(boolean commentHeadInclude) {
this.commentHeadInclude = commentHeadInclude;
}
+ /**
+ * @return true if the decompiler currently eliminates unreachable code
+ */
public boolean isEliminateUnreachable() {
return eliminateUnreachable;
}
+ /**
+ * Set whether the decompiler should eliminate unreachable code as part of its analysis.
+ * @param eliminateUnreachable is true if unreachable code is eliminated
+ */
public void setEliminateUnreachable(boolean eliminateUnreachable) {
this.eliminateUnreachable = eliminateUnreachable;
}
+ /**
+ * If the decompiler currently applies transformation rules that identify and
+ * simplify double precision arithmetic operations, true is returned.
+ * @return true if the decompiler applies double precision rules
+ */
public boolean isSimplifyDoublePrecision() {
return simplifyDoublePrecision;
}
+ /**
+ * Set whether the decompiler should apply transformation rules that identify and
+ * simplify double precision arithmetic operations.
+ * @param simplifyDoublePrecision is true if double precision rules should be applied
+ */
public void setSimplifyDoublePrecision(boolean simplifyDoublePrecision) {
this.simplifyDoublePrecision = simplifyDoublePrecision;
}
+ /**
+ * @return true if line numbers should be displayed with decompiler output.
+ */
public boolean isDisplayLineNumbers() {
return displayLineNumbers;
}
+ /**
+ * @return the source programming language that decompiler output is rendered in
+ */
public DecompilerLanguage getDisplayLanguage() {
return displayLanguage;
}
+ /**
+ * Retrieve the transformer being applied to data-type, function, and namespace names.
+ * If no transform is being applied, a pass-through object is returned.
+ * @return the transformer object
+ */
+ public NameTransformer getNameTransformer() {
+ if (nameTransformer == null) {
+ nameTransformer = new IdentityNameTransformer();
+ }
+ return nameTransformer;
+ }
+
+ /**
+ * Set a specific transformer to be applied to all data-type, function, and namespace
+ * names in decompiler output. A null value indicates no transform should be applied.
+ * @param transformer is the transformer to apply
+ */
+ public void setNameTransformer(NameTransformer transformer) {
+ nameTransformer = transformer;
+ }
+
+ /**
+ * @return true if calling convention names are displayed as part of function signatures
+ */
public boolean isConventionPrint() {
return conventionPrint;
}
+ /**
+ * Set whether the calling convention name should be displayed as part of function signatures
+ * in decompiler output.
+ * @param conventionPrint is true if calling convention names should be displayed
+ */
public void setConventionPrint(boolean conventionPrint) {
this.conventionPrint = conventionPrint;
}
+ /**
+ * @return true if cast operations are not displayed in decompiler output
+ */
public boolean isNoCastPrint() {
return noCastPrint;
}
+ /**
+ * Set whether decompiler output should display cast operations.
+ * @param noCastPrint is true if casts should NOT be displayed.
+ */
public void setNoCastPrint(boolean noCastPrint) {
this.noCastPrint = noCastPrint;
}
+ /**
+ * Set the source programming language that decompiler output should be rendered in.
+ * @param val is the source language
+ */
public void setDisplayLanguage(DecompilerLanguage val) {
displayLanguage = val;
}
+ /**
+ * @return the font that should be used to render decompiler output
+ */
public Font getDefaultFont() {
return Gui.getFont(DEFAULT_FONT_ID);
}
+ /**
+ * If the time a decompiler process is allowed to analyze a single
+ * function exceeds this value, decompilation is aborted.
+ * @return the maximum time in seconds
+ */
public int getDefaultTimeout() {
return decompileTimeoutSeconds;
}
+ /**
+ * Set the maximum time (in seconds) a decompiler process is allowed to analyze a single
+ * function. If it is exceeded, decompilation is aborted.
+ * @param timeout is the maximum time in seconds
+ */
public void setDefaultTimeout(int timeout) {
decompileTimeoutSeconds = timeout;
}
+ /**
+ * If the size (in megabytes) of the payload returned by the decompiler
+ * process exceeds this value for a single function, decompilation is
+ * aborted.
+ * @return the maximum number of megabytes in a function payload
+ */
public int getMaxPayloadMBytes() {
return payloadLimitMBytes;
}
+ /**
+ * Set the maximum size (in megabytes) of the payload that can be returned by the decompiler
+ * process when analyzing a single function. If this size is exceeded, decompilation is
+ * aborted.
+ * @param mbytes is the maximum number of megabytes in a function payload
+ */
public void setMaxPayloadMBytes(int mbytes) {
payloadLimitMBytes = mbytes;
}
+ /**
+ * If the number of assembly instructions in a function exceeds this value, the function
+ * is not decompiled.
+ * @return the maximum number of instructions
+ */
public int getMaxInstructions() {
return maxIntructionsPer;
}
+ /**
+ * Set the maximum number of assembly instructions in a function to decompile.
+ * If the number exceeds this, the function is not decompiled.
+ * @param num is the number of instructions
+ */
public void setMaxInstructions(int num) {
maxIntructionsPer = num;
}
+ /**
+ * @return the style in which comments are printed in decompiler output
+ */
public CommentStyleEnum getCommentStyle() {
return commentStyle;
}
+ /**
+ * Set the style in which comments are printed as part of decompiler output
+ * @param commentStyle is the new style to set
+ */
public void setCommentStyle(CommentStyleEnum commentStyle) {
this.commentStyle = commentStyle;
}
+ /**
+ * Return the maximum number of decompiled function results that should be cached
+ * by the controller of the decompiler process.
+ * @return the number of functions to cache
+ */
public int getCacheSize() {
return cachedResultsSize;
}
+
}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java
index df900cd542..c5da8d2093 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java
@@ -21,6 +21,7 @@ import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.*;
+import ghidra.program.model.symbol.IllegalCharCppTransformer;
/**
* Class for getting at the various structures returned
@@ -197,8 +198,9 @@ public class DecompileResults {
if (docroot == null) {
return null;
}
- PrettyPrinter printer = new PrettyPrinter(function, docroot);
- return printer.print(true);
+ PrettyPrinter printer =
+ new PrettyPrinter(function, docroot, new IllegalCharCppTransformer());
+ return printer.print();
}
private void decodeStream(Decoder decoder) {
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/PrettyPrinter.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/PrettyPrinter.java
index 37fe757b04..59e0435cf7 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/PrettyPrinter.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/PrettyPrinter.java
@@ -20,13 +20,16 @@ import java.util.List;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.program.model.listing.Function;
+import ghidra.program.model.symbol.IdentityNameTransformer;
+import ghidra.program.model.symbol.NameTransformer;
import ghidra.util.StringUtilities;
/**
- * This class is used to convert a C language
- * token group into readable C code.
+ * This class is used to convert a C/C++ language
+ * token group into readable C/C++ code.
*/
public class PrettyPrinter {
+
/**
* The indent string to use when printing.
*/
@@ -34,29 +37,35 @@ public class PrettyPrinter {
private Function function;
private ClangTokenGroup tokgroup;
- private ArrayList lines = new ArrayList();
+ private ArrayList lines = new ArrayList<>();
+ private NameTransformer transformer;
/**
* Constructs a new pretty printer using the specified C language token group.
+ * The printer takes a NameTransformer that will be applied to symbols, which can replace
+ * illegal characters in the symbol name for instance. A null indicates no transform is applied.
+ * @param function is the function to be printed
* @param tokgroup the C language token group
+ * @param transformer the transformer to apply to symbols
*/
- public PrettyPrinter(Function function, ClangTokenGroup tokgroup) {
+ public PrettyPrinter(Function function, ClangTokenGroup tokgroup, NameTransformer transformer) {
this.function = function;
this.tokgroup = tokgroup;
+ this.transformer = (transformer != null) ? transformer : new IdentityNameTransformer();
flattenLines();
padEmptyLines();
}
- private void padEmptyLines() {
- for (ClangLine line : lines) {
+ private void padEmptyLines() {
+ for (ClangLine line : lines) {
ArrayList tokenList = line.getAllTokens();
if (tokenList.size() == 0) {
ClangToken spacer = ClangToken.buildSpacer(null, line.getIndent(), INDENT_STRING);
- spacer.setLineParent( line );
- tokenList.add(0, spacer);
+ spacer.setLineParent(line);
+ tokenList.add(0, spacer);
}
}
- }
+ }
public Function getFunction() {
return function;
@@ -74,11 +83,9 @@ public class PrettyPrinter {
/**
* Prints the C language token group
* into a string of C code.
- * @param removeInvalidChars true if invalid character should be
- * removed from functions and labels.
* @return a string of readable C code
*/
- public DecompiledFunction print(boolean removeInvalidChars) {
+ public DecompiledFunction print() {
StringBuffer buff = new StringBuffer();
for (ClangLine line : lines) {
@@ -87,30 +94,19 @@ public class PrettyPrinter {
for (ClangToken token : tokens) {
boolean isToken2Clean = token instanceof ClangFuncNameToken ||
- token instanceof ClangVariableToken ||
- token instanceof ClangTypeToken ||
- token instanceof ClangFieldToken ||
- token instanceof ClangLabelToken;
+ token instanceof ClangVariableToken || token instanceof ClangTypeToken ||
+ token instanceof ClangFieldToken || token instanceof ClangLabelToken;
//do not clean constant variable tokens
if (isToken2Clean && token.getSyntaxType() == ClangToken.CONST_COLOR) {
isToken2Clean = false;
}
- if (removeInvalidChars && isToken2Clean) {
- String tokenText = token.getText();
- for (int i = 0 ; i < tokenText.length() ; ++i) {
- if (StringUtilities.isValidCLanguageChar(tokenText.charAt(i))) {
- buff.append(tokenText.charAt(i));
- }
- else {
- buff.append('_');
- }
- }
- }
- else {
- buff.append(token.getText());
+ String tokenText = token.getText();
+ if (isToken2Clean) {
+ tokenText = transformer.simplify(tokenText);
}
+ buff.append(tokenText);
}
buff.append(StringUtilities.LINE_SEPARATOR);
}
@@ -119,10 +115,10 @@ public class PrettyPrinter {
private String findSignature() {
int nChildren = tokgroup.numChildren();
- for (int i = 0 ; i < nChildren ; ++i) {
+ for (int i = 0; i < nChildren; ++i) {
ClangNode node = tokgroup.Child(i);
if (node instanceof ClangFuncProto) {
- return node.toString()+";";
+ return node.toString() + ";";
}
}
return null;
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java
index 27a9213841..03065bcbd7 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java
@@ -231,7 +231,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
// Assume docroot has been built.
- PrettyPrinter printer = new PrettyPrinter(function, docroot);
+ PrettyPrinter printer = new PrettyPrinter(function, docroot, null);
lines = printer.getLines();
int lineCount = lines.size();
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java
index 47b788f529..940214d382 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java
@@ -28,6 +28,7 @@ import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.label.GDHtmlLabel;
+import ghidra.GhidraOptions;
import ghidra.app.decompiler.DecompileOptions;
import ghidra.app.util.viewer.listingpanel.ProgramLocationListener;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
@@ -88,10 +89,11 @@ public abstract class DecompilerCodeComparisonPanel 0)) {
encoder.openElement(ELEM_JUMPTABLELIST);
@@ -575,9 +580,11 @@ public class HighFunction extends PcodeSyntaxTree {
* from the root (global) namespace up to the given namespace
* @param encoder is the stream encoder
* @param namespace is the namespace being described
+ * @param transformer is used to computer the displayed version of each namespace
* @throws IOException for errors in the underlying stream
*/
- static public void encodeNamespace(Encoder encoder, Namespace namespace) throws IOException {
+ static public void encodeNamespace(Encoder encoder, Namespace namespace,
+ NameTransformer transformer) throws IOException {
encoder.openElement(ELEM_PARENT);
if (namespace != null) {
ArrayList arr = new ArrayList<>();
@@ -595,7 +602,12 @@ public class HighFunction extends PcodeSyntaxTree {
Namespace curScope = arr.get(i);
encoder.openElement(ELEM_VAL);
encoder.writeUnsignedInteger(ATTRIB_ID, curScope.getID());
- encoder.writeString(ATTRIB_CONTENT, curScope.getName());
+ String nm = curScope.getName();
+ String altName = transformer.simplify(nm);
+ if (!nm.equals(altName)) {
+ encoder.writeString(ATTRIB_LABEL, altName);
+ }
+ encoder.writeString(ATTRIB_CONTENT, nm);
encoder.closeElement(ELEM_VAL);
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionShellSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionShellSymbol.java
index d371d37a5b..a5c91a8a98 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionShellSymbol.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionShellSymbol.java
@@ -61,6 +61,10 @@ public class HighFunctionShellSymbol extends HighSymbol {
encoder.openElement(ELEM_FUNCTION);
encoder.writeUnsignedInteger(ATTRIB_ID, getId());
encoder.writeString(ATTRIB_NAME, name);
+ String altName = dtmanage.getNameTransformer().simplify(name);
+ if (!name.equals(altName)) {
+ encoder.writeString(ATTRIB_LABEL, altName);
+ }
encoder.writeSignedInteger(ATTRIB_SIZE, 1);
AddressXML.encode(encoder, getStorage().getMinAddress());
encoder.closeElement(ELEM_FUNCTION);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java
index 47cb61da2e..417e1e26d1 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java
@@ -330,14 +330,21 @@ public class LocalSymbolMap {
* Encode all the variables in this local variable map to the stream
* @param encoder is the stream encoder
* @param namespace if the namespace of the function
+ * @param transformer is used to compute a simplified version of the namespace name
* @throws IOException for errors in the underlying stream
*/
- public void encodeLocalDb(Encoder encoder, Namespace namespace) throws IOException {
+ public void encodeLocalDb(Encoder encoder, Namespace namespace, NameTransformer transformer)
+ throws IOException {
encoder.openElement(ELEM_LOCALDB);
encoder.writeBool(ATTRIB_LOCK, false);
encoder.writeSpace(ATTRIB_MAIN, localSpace);
encoder.openElement(ELEM_SCOPE);
- encoder.writeString(ATTRIB_NAME, func.getFunction().getName());
+ String nm = func.getFunction().getName();
+ encoder.writeString(ATTRIB_NAME, nm);
+ String altName = transformer.simplify(nm);
+ if (!nm.equals(altName)) {
+ encoder.writeString(ATTRIB_LABEL, altName);
+ }
encoder.openElement(ELEM_PARENT);
long parentid = Namespace.GLOBAL_NAMESPACE_ID;
if (!HighFunction.collapseToGlobal(namespace)) {
@@ -345,7 +352,7 @@ public class LocalSymbolMap {
}
encoder.writeUnsignedInteger(ATTRIB_ID, parentid);
encoder.closeElement(ELEM_PARENT);
- encoder.openElement(ELEM_RANGELIST); // Emptry address range
+ encoder.openElement(ELEM_RANGELIST); // Empty address range
encoder.closeElement(ELEM_RANGELIST);
encoder.openElement(ELEM_SYMBOLLIST);
Iterator iter = symbolMap.values().iterator();
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java
index 0cd03a5dfa..ff1e6289d9 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java
@@ -31,6 +31,7 @@ import ghidra.program.model.data.Enum;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.DecompilerLanguage;
import ghidra.program.model.listing.Program;
+import ghidra.program.model.symbol.NameTransformer;
/**
*
@@ -98,6 +99,7 @@ public class PcodeDataTypeManager {
private DataTypeManager progDataTypes; // DataTypes from a particular program
private DataTypeManager builtInDataTypes = BuiltInDataTypeManager.getDataTypeManager();
private DataOrganization dataOrganization;
+ private NameTransformer nameTransformer;
private DecompilerLanguage displayLanguage;
private boolean voidInputIsVarargs; // true if we should consider void parameter lists as varargs
// Some C header conventions use an empty prototype to mean a
@@ -107,11 +109,12 @@ public class PcodeDataTypeManager {
private VoidDataType voidDt;
private int pointerWordSize; // Wordsize to assign to all pointer datatypes
- public PcodeDataTypeManager(Program prog) {
+ public PcodeDataTypeManager(Program prog, NameTransformer simplifier) {
program = prog;
progDataTypes = prog.getDataTypeManager();
dataOrganization = progDataTypes.getDataOrganization();
+ nameTransformer = simplifier;
voidInputIsVarargs = true; // By default, do not lock-in void parameter lists
displayLanguage = prog.getCompilerSpec().getDecompilerOutputLanguage();
if (displayLanguage != DecompilerLanguage.C_LANGUAGE) {
@@ -126,6 +129,14 @@ public class PcodeDataTypeManager {
return program;
}
+ public NameTransformer getNameTransformer() {
+ return nameTransformer;
+ }
+
+ public void setNameTransformer(NameTransformer newTransformer) {
+ nameTransformer = newTransformer;
+ }
+
/**
* Find a base/built-in data-type with the given name and/or id. If an id is provided and
* a corresponding data-type exists, this data-type is returned. Otherwise the first
@@ -954,7 +965,12 @@ public class PcodeDataTypeManager {
((BuiltIn) type).getDecompilerDisplayName(displayLanguage));
}
else {
+ String name = type.getName();
+ String displayName = nameTransformer.simplify(name);
encoder.writeString(ATTRIB_NAME, type.getName());
+ if (!name.equals(displayName)) {
+ encoder.writeString(ATTRIB_LABEL, displayName);
+ }
long id = progDataTypes.getID(type);
if (id > 0) {
encoder.writeUnsignedInteger(ATTRIB_ID, id);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/IdentityNameTransformer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/IdentityNameTransformer.java
new file mode 100644
index 0000000000..d0600c7075
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/IdentityNameTransformer.java
@@ -0,0 +1,28 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.model.symbol;
+
+/**
+ * A transformer that never alters its input
+ */
+public class IdentityNameTransformer implements NameTransformer {
+
+ @Override
+ public String simplify(String input) {
+ return input;
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/IllegalCharCppTransformer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/IllegalCharCppTransformer.java
new file mode 100644
index 0000000000..40cefcf9d8
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/IllegalCharCppTransformer.java
@@ -0,0 +1,121 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.model.symbol;
+
+/**
+ * Replace illegal characters in the given name with '_'. The transformer treats the name as a
+ * C++ symbol. Letters and digits are generally legal. '~' is allowed at the start of the symbol.
+ * Template parameters, surrounded by '<' and '>', allow additional special characters. Certain
+ * special characters are allowed after the keyword "operator".
+ */
+public class IllegalCharCppTransformer implements NameTransformer {
+
+ private static int[] legalChars = null;
+ private static final int AFTER_FIRST_CHAR = 1; // Legal after the first character
+ private static final int TEMPLATE = 2; // Legal as part of template parameters
+ private static final int OPERATOR = 4; // Legal after the "operator" keyword
+ private static final int FIRST_CHAR = 8; // Legal as the first character
+
+ public IllegalCharCppTransformer() {
+ if (legalChars == null) {
+ legalChars = new int[128];
+ for (int i = 0; i < legalChars.length; ++i) {
+ legalChars[i] = 0;
+ }
+ legalChars['_'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR | FIRST_CHAR;
+ legalChars['0'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['1'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['2'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['3'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['4'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['5'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['6'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['7'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['8'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['9'] = AFTER_FIRST_CHAR | TEMPLATE | OPERATOR;
+ legalChars['*'] = TEMPLATE | OPERATOR;
+ legalChars[':'] = TEMPLATE;
+ legalChars['('] = TEMPLATE | OPERATOR;
+ legalChars[')'] = TEMPLATE | OPERATOR;
+ legalChars['['] = TEMPLATE | OPERATOR;
+ legalChars[']'] = TEMPLATE | OPERATOR;
+ legalChars[','] = TEMPLATE;
+ legalChars['&'] = TEMPLATE | OPERATOR;
+ legalChars['+'] = OPERATOR;
+ legalChars['-'] = OPERATOR;
+ legalChars['|'] = OPERATOR;
+ legalChars['='] = OPERATOR;
+ legalChars['!'] = OPERATOR;
+ legalChars['/'] = OPERATOR;
+ legalChars['%'] = OPERATOR;
+ legalChars['^'] = OPERATOR;
+ legalChars['~'] = TEMPLATE | OPERATOR | FIRST_CHAR;
+ }
+ }
+
+ @Override
+ public String simplify(String input) {
+ int templateDepth = 0;
+ char[] transform = null;
+ for (int i = 0; i < input.length(); ++i) {
+ char c = input.charAt(i);
+ if (Character.isLetter(c)) {
+ continue;
+ }
+ else if (c == '<') {
+ templateDepth += 1;
+ continue;
+ }
+ else if (c == '>') {
+ templateDepth -= 1;
+ if (templateDepth < 0) {
+ templateDepth = 0;
+ }
+ continue;
+ }
+ else if (c < 128) {
+ int val = legalChars[c];
+ if (val != 0) {
+ if (((val & AFTER_FIRST_CHAR) != 0) && i > 0) {
+ continue; // Legal after first character
+ }
+ if (((val & FIRST_CHAR) != 0) && i == 0) {
+ continue; // Legal as first character
+ }
+ if (((val & TEMPLATE) != 0) && templateDepth > 0) {
+ continue; // Legal as template parameter
+ }
+ if (((val & OPERATOR) != 0) && i >= 8 && i <= 10) {
+ if (input.startsWith("operator")) {
+ continue;
+ }
+ }
+ }
+ }
+ // If we reach here, the character is deemed illegal
+ if (transform == null) {
+ transform = new char[input.length()];
+ input.getChars(0, input.length(), transform, 0);
+ }
+ transform[i] = '_';
+ }
+ if (transform == null) {
+ return input;
+ }
+ return new String(transform);
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/NameTransformer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/NameTransformer.java
new file mode 100644
index 0000000000..39259d788d
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/NameTransformer.java
@@ -0,0 +1,31 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.model.symbol;
+
+/**
+ * Interface to transform (shorten, simplify) names of data-types, functions, and name spaces
+ * for display.
+ */
+public interface NameTransformer {
+
+ /**
+ * Return a transformed version of the given input. If no change is made, the original
+ * String object is returned.
+ * @param input is the name to transform
+ * @return the transformed version
+ */
+ public String simplify(String input);
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/symbol/IllegalCharCppTransformerTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/symbol/IllegalCharCppTransformerTest.java
new file mode 100644
index 0000000000..2e4032deb5
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/symbol/IllegalCharCppTransformerTest.java
@@ -0,0 +1,73 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.model.symbol;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import generic.test.AbstractGenericTest;
+
+public class IllegalCharCppTransformerTest extends AbstractGenericTest {
+
+ private IllegalCharCppTransformer transformer = new IllegalCharCppTransformer();
+
+ @Test
+ public void testTemplateChars() {
+ assertEquals("foo", simplify("foo"));
+ assertEquals("foo", simplify("foo"));
+ assertEquals("map", simplify("map"));
+ assertEquals("pair",
+ simplify("pair"));
+ assertEquals("_basic_string>",
+ simplify("_basic.string>"));
+ assertEquals("_________baz", simplify("*()~[]&:,baz"));
+ assertEquals("foo12______bar__operator",
+ simplify("foo12 ??_*~bar::operator"));
+ }
+
+ @Test
+ public void testOperatorChars() {
+ assertEquals("operator<<", simplify("operator<<"));
+ assertEquals("operator>>=", simplify("operator>>="));
+ assertEquals("operator++", simplify("operator++"));
+ assertEquals("operator/=", simplify("operator/="));
+ assertEquals("operator%", simplify("operator%"));
+ assertEquals("operator&&", simplify("operator&&"));
+ assertEquals("operator!=", simplify("operator!="));
+ assertEquals("operator__", simplify("operator.?"));
+ assertEquals("operator~", simplify("operator~"));
+ assertEquals("operator^", simplify("operator^"));
+ assertEquals("myoperator__", simplify("myoperator!="));
+ }
+
+ @Test
+ public void testBadChars() {
+ assertEquals("~destructor_main", simplify("~destructor.main"));
+ assertEquals("~Vector_7", simplify("~Vector~7"));
+ assertEquals("_2foo", simplify("12foo"));
+ assertEquals("std__foo", simplify("std::foo"));
+ assertEquals("bar__1", simplify("bar??1"));
+ assertEquals("_resource_352_", simplify("[resource.352]"));
+ assertEquals("_val_", simplify("!val%"));
+ // Foreign language identifiers
+ assertEquals("\u041d\u0415\u0422", simplify("\u041d\u0415\u0422"));
+ }
+
+ private String simplify(String in) {
+ return transformer.simplify(in);
+ }
+}