diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index e8285c1c7e..fb3123c178 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -276,16 +276,18 @@ void Architecture::clearAnalysis(Funcdata *fd) /// Symbols do not necessarily need to be available for the decompiler. /// This routine loads all the \e load \e image knows about into the symbol table -void Architecture::readLoaderSymbols(void) +/// \param delim is the delimiter separating namespaces from symbol base names +void Architecture::readLoaderSymbols(const string &delim) { if (loadersymbols_parsed) return; // already read - Scope *scope = symboltab->getGlobalScope(); loader->openSymbols(); loadersymbols_parsed = true; LoadImageFunc record; while(loader->getNextSymbol(record)) { - scope->addFunction(record.address,record.name); + string basename; + Scope *scope = symboltab->findCreateScopeFromSymbolName(record.name, delim, basename, (Scope *)0); + scope->addFunction(record.address,basename); } loader->closeSymbols(); } @@ -324,9 +326,13 @@ SegmentOp *Architecture::getSegmentOp(AddrSpace *spc) const void Architecture::setPrototype(const PrototypePieces &pieces) { - Funcdata *fd = symboltab->getGlobalScope()->queryFunction( pieces.name ); + string basename; + Scope *scope = symboltab->resolveScopeFromSymbolName(pieces.name, "::", basename, (Scope *)0); + if (scope == (Scope *)0) + throw ParseError("Unknown namespace: " + pieces.name); + Funcdata *fd = scope->queryFunction( basename ); if (fd == (Funcdata *)0) - throw ParseError("Unknown function name: "+pieces.name); + throw ParseError("Unknown function name: " + pieces.name); fd->getFuncProto().setPieces(pieces); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index a27d801f82..496db81809 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -177,7 +177,7 @@ public: int4 getMinimumLanedRegisterSize(void) const; ///< Get the minimum size of a laned register in bytes void setDefaultModel(const string &nm); ///< Set the default PrototypeModel void clearAnalysis(Funcdata *fd); ///< Clear analysis specific to a function - void readLoaderSymbols(void); ///< Read any symbols from loader into database + void readLoaderSymbols(const string &delim); ///< Read any symbols from loader into database void collectBehaviors(vector &behave) const; ///< Provide a list of OpBehavior objects SegmentOp *getSegmentOp(AddrSpace *spc) const; ///< Retrieve the \e segment op for the given space if any void setPrototype(const PrototypePieces &pieces); ///< Set the prototype for a particular function diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc index 97092c3342..9880890614 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc @@ -96,7 +96,7 @@ void IfcLoadFile::execute(istream &s) return; } if (capa->getName() == "xml") // If file is xml - dcp->conf->readLoaderSymbols(); // Read in loader symbols + dcp->conf->readLoaderSymbols("::"); // Read in loader symbols #ifdef OPACTION_DEBUG dcp->conf->setDebugStream(status->optr); #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index dfbec2d55f..fd8f9ad546 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -1087,8 +1087,16 @@ SymbolEntry *ActionConstantPtr::isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op // Since we are looking for a global address // Assume it is address tied and use empty usepoint SymbolEntry *entry = data.getScopeLocal()->getParent()->queryContainer(rampoint,1,Address()); - if (needexacthit&&(entry != (SymbolEntry *)0)) { - if (entry->getAddr() != rampoint) + if (entry != (SymbolEntry *)0) { + Datatype *ptrType = entry->getSymbol()->getType(); + if (ptrType->getMetatype() == TYPE_ARRAY) { + Datatype *ct = ((TypeArray *)ptrType)->getBase(); + // In the special case of strings (character arrays) we allow the constant pointer to + // refer to the middle of the string + if (ct->isCharPrint()) + needexacthit = false; + } + if (needexacthit && entry->getAddr() != rampoint) return (SymbolEntry *)0; } return entry; @@ -3916,9 +3924,9 @@ int4 ActionInputPrototype::apply(Funcdata &data) } } if (data.isHighOn()) - data.getFuncProto().updateInputTypes(triallist,&active); + data.getFuncProto().updateInputTypes(data,triallist,&active); else - data.getFuncProto().updateInputNoTypes(triallist,&active,data.getArch()->types); + data.getFuncProto().updateInputNoTypes(data,triallist,&active); } data.clearDeadVarnodes(); #ifdef OPACTION_DEBUG diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc index 4f1cb2c140..0bbdc35c52 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc @@ -295,18 +295,32 @@ int4 Symbol::getMapEntryPosition(const SymbolEntry *entry) const return -1; } -/// A value of 0 means the base Symbol name is visible and not overridden in the given use scope. +/// For a given context scope where \b this Symbol is used, determine how many elements of +/// the full namespace path need to be printed to correctly distinguish it. +/// A value of 0 means the base symbol name is visible and not overridden in the context scope. /// A value of 1 means the base name may be overridden, but the parent scope name is not. -/// The minimual number of names that distinguishes \b this Symbol uniquely within the +/// The minimal number of names that distinguishes the symbol name uniquely within the /// use scope is returned. -/// \param useScope is the given scope where \b this Symbol is being used -/// \return the number of (extra) names needed to distinguish \b this Symbol +/// \param useScope is the given scope where the symbol is being used +/// \return the number of (extra) names needed to distinguish the symbol int4 Symbol::getResolutionDepth(const Scope *useScope) const { if (scope == useScope) return 0; // Symbol is in scope where it is used + if (useScope == (const Scope *)0) { // Treat null useScope as resolving the full path + const Scope *point = scope; + int4 count = 0; + while(point != (const Scope *)0) { + count += 1; + point = point->getParent(); + } + return count-1; // Don't print global scope + } + if (depthScope == useScope) + return depthResolution; + depthScope = useScope; const Scope *distinguishScope = scope->findDistinguishingScope(useScope); - int4 depth = 0; + depthResolution = 0; string distinguishName; const Scope *terminatingScope; if (distinguishScope == (const Scope *)0) { // Symbol scope is ancestor of use scope @@ -317,15 +331,15 @@ int4 Symbol::getResolutionDepth(const Scope *useScope) const distinguishName = distinguishScope->getName(); const Scope *currentScope = scope; while(currentScope != distinguishScope) { // For any scope up to the distinguishing scope - depth += 1; // Print its name + depthResolution += 1; // Print its name currentScope = currentScope->getParent(); } - depth += 1; // Also print the distinguishing scope name + depthResolution += 1; // Also print the distinguishing scope name terminatingScope = distinguishScope->getParent(); } if (useScope->isNameUsed(distinguishName,terminatingScope)) - depth += 1; // Name was overridden, we need one more distinguishing name - return depth; + depthResolution += 1; // Name was overridden, we need one more distinguishing name + return depthResolution; } /// \param s is the output stream @@ -564,7 +578,7 @@ Funcdata *FunctionSymbol::getFunction(void) { if (fd != (Funcdata *)0) return fd; SymbolEntry *entry = getFirstWholeMap(); - fd = new Funcdata(name,scope,entry->getAddr()); + fd = new Funcdata(name,scope,entry->getAddr(),this); return fd; } @@ -586,7 +600,7 @@ void FunctionSymbol::restoreXml(const Element *el) { if (el->getName() == "function") { - fd = new Funcdata("",scope,Address()); + fd = new Funcdata("",scope,Address(),this); symbolId = fd->restoreXml(el); name = fd->getName(); if (consumeSize < fd->getSize()) { @@ -1055,13 +1069,23 @@ void Scope::removeRange(AddrSpace *spc,uintb first,uintb last) /// In particular, the SymbolEntry is assumed to map the entire Symbol. /// \param entry is the given SymbolEntry /// \return a SymbolEntry which has been fully integrated -SymbolEntry *Scope::addMap(const SymbolEntry &entry) +SymbolEntry *Scope::addMap(SymbolEntry &entry) { // First set properties of this symbol based on scope // entry.symbol->flags |= Varnode::mapped; if (isGlobal()) entry.symbol->flags |= Varnode::persist; + else if (!entry.addr.isInvalid()) { + // If this is not a global scope, but the address is in the global discovery range + // we still mark the symbol as persistent + Scope *glbScope = glb->symboltab->getGlobalScope(); + Address addr; + if (glbScope->inScope(entry.addr, 1, addr)) { + entry.symbol->flags |= Varnode::persist; + entry.uselimit.clear(); // FIXME: Kludge for incorrectly generated XML + } + } SymbolEntry *res; int4 consumeSize = entry.symbol->getBytesConsumed(); @@ -1377,43 +1401,25 @@ void Scope::getNameSegments(vector &vec) const } /// Put the parent scopes of \b this into an array in order, starting with the global scope. -/// This scope itself will not be in the array. /// \param vec is storage for the array of scopes -void Scope::getScopePath(vector &vec) const +void Scope::getScopePath(vector &vec) const { int4 count = 0; - Scope *cur = parent; - while(cur != (Scope *)0) { // Count number of elements in path + const Scope *cur = this; + while(cur != (const Scope *)0) { // Count number of elements in path count += 1; cur = cur->parent; } vec.resize(count); - cur = parent; - while(cur != (Scope *)0) { + cur = this; + while(cur != (const Scope *)0) { count -= 1; vec[count] = cur; cur = cur->parent; } } -/// Test for the presence of a symbol with the given name in either \b this scope or -/// an ancestor scope up to but not including the given terminating scope. -/// If the name is used \b true is returned. -/// \param nm is the given name to test -/// \param op2 is the terminating ancestor scope (or null) -bool Scope::isNameUsed(const string &nm,const Scope *op2) const - -{ - const Scope *currentScope = this; - while(currentScope != op2) { - if (currentScope->isNameUsed(name)) - return true; - currentScope = currentScope->parent; - } - return false; -} - /// Any two scopes share at least the \e global scope as a common ancestor. We find the first scope /// that is \e not in common. The scope returned will always be an ancestor of \b this. /// If \b this is an ancestor of the other given scope, then null is returned. @@ -1422,12 +1428,12 @@ bool Scope::isNameUsed(const string &nm,const Scope *op2) const const Scope *Scope::findDistinguishingScope(const Scope *op2) const { - if (this == op2) return (Scope *)0; // Quickly check most common cases + if (this == op2) return (const Scope *)0; // Quickly check most common cases if (parent == op2) return this; - if (op2->parent == this) return op2; + if (op2->parent == this) return (const Scope *)0; if (parent == op2->parent) return this; - vector thisPath; - vector op2Path; + vector thisPath; + vector op2Path; getScopePath(thisPath); op2->getScopePath(op2Path); int4 min = thisPath.size(); @@ -1440,7 +1446,7 @@ const Scope *Scope::findDistinguishingScope(const Scope *op2) const if (min < thisPath.size()) return thisPath[min]; // thisPath matches op2Path but is longer if (min < op2Path.size()) - return (Scope *)0; // op2Path matches thisPath but is longer + return (const Scope *)0; // op2Path matches thisPath but is longer return this; // ancestor paths are identical (only base scopes differ) } @@ -1453,7 +1459,7 @@ Symbol *Scope::addSymbol(const string &name,Datatype *ct) { Symbol *sym; - sym = new Symbol(this,name,ct); + sym = new Symbol(owner,name,ct); addSymbolInternal(sym); // Let this scope lay claim to the new object return sym; } @@ -1474,7 +1480,7 @@ SymbolEntry *Scope::addSymbol(const string &name,Datatype *ct, { Symbol *sym; - sym = new Symbol(this,name,ct); + sym = new Symbol(owner,name,ct); addSymbolInternal(sym); return addMapPoint(sym,addr,usepoint); } @@ -1509,19 +1515,19 @@ Symbol *Scope::addMapSym(const Element *el) Symbol *sym; const string &symname( subel->getName() ); if (symname == "symbol") - sym = new Symbol(this); + sym = new Symbol(owner); else if (symname == "dynsymbol") - sym = new Symbol(this); + sym = new Symbol(owner); else if (symname == "equatesymbol") - sym = new EquateSymbol(this); + sym = new EquateSymbol(owner); else if (symname == "function") - sym = new FunctionSymbol(this,glb->min_funcsymbol_size); + sym = new FunctionSymbol(owner,glb->min_funcsymbol_size); else if (symname == "functionshell") - sym = new FunctionSymbol(this,glb->min_funcsymbol_size); + sym = new FunctionSymbol(owner,glb->min_funcsymbol_size); else if (symname == "labelsym") - sym = new LabSymbol(this); + sym = new LabSymbol(owner); else if (symname == "externrefsymbol") - sym = new ExternRefSymbol(this); + sym = new ExternRefSymbol(owner); else throw LowlevelError("Unknown symbol type: "+symname); try { // Protect against duplicate scope errors @@ -1563,7 +1569,7 @@ FunctionSymbol *Scope::addFunction(const Address &addr,const string &nm) errmsg += " overlaps object: "+overlap->getSymbol()->getName(); glb->printMessage(errmsg); } - sym = new FunctionSymbol(this,nm,glb->min_funcsymbol_size); + sym = new FunctionSymbol(owner,nm,glb->min_funcsymbol_size); addSymbolInternal(sym); // Map symbol to base address of function // there is no limit on the applicability of this map within scope @@ -1584,7 +1590,7 @@ ExternRefSymbol *Scope::addExternalRef(const Address &addr,const Address &refadd { ExternRefSymbol *sym; - sym = new ExternRefSymbol(this,refaddr,nm); + sym = new ExternRefSymbol(owner,refaddr,nm); addSymbolInternal(sym); // Map symbol to given address // there is no limit on applicability of this map within scope @@ -1612,7 +1618,7 @@ LabSymbol *Scope::addCodeLabel(const Address &addr,const string &nm) errmsg += " overlaps object: "+overlap->getSymbol()->getName(); glb->printMessage(errmsg); } - sym = new LabSymbol(this,nm); + sym = new LabSymbol(owner,nm); addSymbolInternal(sym); addMapPoint(sym,addr,Address()); return sym; @@ -1632,7 +1638,7 @@ Symbol *Scope::addDynamicSymbol(const string &nm,Datatype *ct,const Address &cad { Symbol *sym; - sym = new Symbol(this,nm,ct); + sym = new Symbol(owner,nm,ct); addSymbolInternal(sym); RangeList rnglist; if (!caddr.isInvalid()) @@ -1696,6 +1702,12 @@ bool Scope::isReadOnly(const Address &addr,int4 size,const Address &usepoint) co return ((flags & Varnode::readonly)!=0); } +Scope *ScopeInternal::buildSubScope(const string &nm) + +{ + return new ScopeInternal(nm,glb); +} + void ScopeInternal::addSymbolInternal(Symbol *sym) { @@ -1832,12 +1844,17 @@ list::iterator ScopeInternal::endDynamic(void) /// \param nm is the name of the Scope /// \param g is the Architecture it belongs to ScopeInternal::ScopeInternal(const string &nm,Architecture *g) - : Scope(nm,g) + : Scope(nm,g,this) { nextUniqueId = 0; - int4 numspaces = g->numSpaces(); - for(int4 i=0;inumSpaces(),(EntryMap *)0); +} + +ScopeInternal::ScopeInternal(const string &nm,Architecture *g, Scope *own) + : Scope(nm,g,own) +{ + nextUniqueId = 0; + maptable.resize(g->numSpaces(),(EntryMap *)0); } ScopeInternal::~ScopeInternal(void) @@ -2277,13 +2294,21 @@ void ScopeInternal::findByName(const string &name,vector &res) const } } -bool ScopeInternal::isNameUsed(const string &name) const +bool ScopeInternal::isNameUsed(const string &nm,const Scope *op2) const { - Symbol sym((Scope *)0,name,(Datatype *)0); + Symbol sym((Scope *)0,nm,(Datatype *)0); SymbolNameTree::const_iterator iter = nametree.lower_bound(&sym); - if (iter == nametree.end()) return false; - return ((*iter)->getName() == name); + if (iter != nametree.end()) { + if ((*iter)->getName() == nm) + return true; + } + Scope *par = getParent(); + if (par == (Scope *)0 || par == op2) + return false; + if (par->getParent() == (Scope *)0) // Never recurse into global scope + return false; + return par->isNameUsed(nm, op2); } string ScopeInternal::buildVariableName(const Address &addr, @@ -2564,6 +2589,23 @@ void ScopeInternal::processHole(const Element *el) } } +/// \brief Parse a \ tag indicating a named symbol with no storage or data-type info +/// +/// Let the decompiler know that a name is occupied within the scope for isNameUsed queries, without +/// specifying storage and data-type information about the symbol. This is modeled currently by +/// creating an unmapped symbol. +/// \param el is the \ element +void ScopeInternal::processCollision(const Element *el) + +{ + const string &nm(el->getAttributeValue("name")); + SymbolNameTree::const_iterator iter = findFirstByName(nm); + if (iter == nametree.end()) { + Datatype *ct = glb->types->getBase(1,TYPE_INT); + addSymbol(nm,ct); + } +} + /// \brief Insert a Symbol into the \b nametree /// /// Duplicate symbol names are allowed for by establishing a deduplication id for the Symbol. @@ -2638,6 +2680,8 @@ void ScopeInternal::restoreXml(const Element *el) } else if (subel->getName() == "hole") processHole(subel); + else if (subel->getName() == "collision") + processCollision(subel); else throw LowlevelError("Unknown symbollist tag: "+subel->getName()); ++iter2; @@ -2892,6 +2936,22 @@ void Database::removeRange(Scope *scope,AddrSpace *spc,uintb first,uintb last) fillResolve(scope); } +/// Look for an immediate child scope by name in a given parent. If does not exist, +/// create a new scope with the name and attach it to the parent. +/// \param nm is the base name of the desired subscope +/// \param parent is the given parent scope to search +/// \return the subscope object either found or created +Scope *Database::findCreateSubscope(const string &nm,Scope *parent) + +{ + Scope *res = parent->resolveScope(nm); + if (res != (Scope *)0) + return res; + res = globalscope->buildSubScope(nm); + attachScope(res, parent); + return res; +} + /// An \e absolute \e path of Scope names must be provided, from the global /// Scope down to the desired Scope. If the first path name is blank (""), it /// matches the global Scope. If the first path name is not blank, the @@ -2926,8 +2986,8 @@ Scope *Database::resolveScope(const vector &subnames) const /// \param basename will hold the passed back base Symbol name /// \param start is the Scope to start drilling down from, or NULL for the global scope /// \return the Scope being referred to by the name -Scope *Database::resolveScopeSymbolName(const string &fullname,const string &delim,string &basename, - Scope *start) const +Scope *Database::resolveScopeFromSymbolName(const string &fullname,const string &delim,string &basename, + Scope *start) const { if (start == (Scope *)0) start = globalscope; @@ -2947,6 +3007,37 @@ Scope *Database::resolveScopeSymbolName(const string &fullname,const string &del return start; } +/// \brief Find and/or create Scopes associated with a qualified Symbol name +/// +/// The name is parsed using a \b delimiter that is passed in. The name can +/// be only partially qualified by passing in a starting Scope, which the +/// name is assumed to be relative to. Otherwise the name is assumed to be +/// relative to the global Scope. The unqualified (base) name of the Symbol +/// is passed back to the caller. Any missing scope in the path is created. +/// \param fullname is the qualified Symbol name +/// \param delim is the delimiter separating names +/// \param basename will hold the passed back base Symbol name +/// \param start is the Scope to start drilling down from, or NULL for the global scope +/// \return the Scope being referred to by the name +Scope *Database::findCreateScopeFromSymbolName(const string &fullname,const string &delim,string &basename, + Scope *start) +{ + if (start == (Scope *)0) + start = globalscope; + + string::size_type mark = 0; + string::size_type endmark; + for(;;) { + endmark = fullname.find(delim,mark); + if (endmark == string::npos) break; + string scopename = fullname.substr(mark,endmark-mark); + start = findCreateSubscope(scopename, start); + mark = endmark + delim.size(); + } + basename = fullname.substr(mark,endmark); + return start; +} + /// \brief Determine the lowest-level Scope which might contain the given address as a Symbol /// /// As currently implemented, this method can only find a \e namespace Scope. @@ -3095,28 +3186,13 @@ void Database::restoreXml(const Element *el) for(;iter!=list.end();++iter) { const Element *subel = *iter; - Scope *new_scope; string name; vector parnames; parseParentTag(subel,name,parnames); parnames.push_back(name); - new_scope = resolveScope(parnames); - if (new_scope == (Scope *)0) { - // Scope wasn't pre-existing - Scope *curscope = globalscope; - int4 i; - for(i=1;iresolveScope(parnames[i]); // Resolve through any pre-existing scopes - if (nextscope == (Scope *)0) break; - curscope = nextscope; - } - while(i != parnames.size()) { - new_scope = new ScopeInternal(parnames[i],glb); // Create any new scopes, up to and including - attachScope(new_scope,curscope); // the scope represented by this Element - curscope = new_scope; - i += 1; - } - } + Scope *new_scope = globalscope; + for(int4 i=1;irestoreXml(subel); } } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh index fb524ac6e9..10b2c0f547 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh @@ -167,6 +167,8 @@ protected: uint2 catindex; ///< Index within category uint8 symbolId; ///< Unique id, 0=unassigned vector::iterator> mapentry; ///< List of storage locations labeled with \b this Symbol + mutable const Scope *depthScope; ///< Scope associated with current depth resolution + mutable int4 depthResolution; ///< Number of namespace elements required to resolve symbol in current scope uint4 wholeCount; ///< Number of SymbolEntries that map to the whole Symbol virtual ~Symbol(void) {} ///< Destructor void setDisplayFormat(uint4 val); ///< Set the display format for \b this Symbol @@ -183,13 +185,9 @@ public: isolate = 16, ///< Symbol should not speculatively merge automatically merge_problems = 32 ///< Set if some SymbolEntrys did not get merged }; - /// \brief Construct given a name and data-type - Symbol(Scope *sc,const string &nm,Datatype *ct) - { scope=sc; name=nm; nameDedup=0; type=ct; flags=0; dispflags=0; category=-1; symbolId=0; wholeCount=0; } - - /// \brief Construct for use with restoreXml() - Symbol(Scope *sc) { scope=sc; nameDedup=0; flags=0; dispflags=0; category=-1; symbolId = 0; wholeCount=0; } + Symbol(Scope *sc,const string &nm,Datatype *ct); ///< Construct given a name and data-type + Symbol(Scope *sc); ///< Construct for use with restoreXml() const string &getName(void) const { return name; } ///< Get the local name of the symbol Datatype *getType(void) const { return type; } ///< Get the data-type uint8 getId(void) const { return symbolId; } ///< Get a unique id for the symbol @@ -214,7 +212,7 @@ public: int4 numEntries(void) const { return mapentry.size(); } ///< Return the number of SymbolEntrys SymbolEntry *getMapEntry(int4 i) const { return &(*mapentry[i]); } ///< Return the i-th SymbolEntry for \b this Symbol int4 getMapEntryPosition(const SymbolEntry *entry) const; ///< Position of given SymbolEntry within \b this multi-entry Symbol - int4 getResolutionDepth(const Scope *useScope) const; ///< Get the number of scope names to print to resolve symbol in given context + int4 getResolutionDepth(const Scope *useScope) const; ///< Get number of scope names needed to resolve \b this symbol void saveXmlHeader(ostream &s) const; ///< Save basic Symbol properties as XML attributes void restoreXmlHeader(const Element *el); ///< Restore basic Symbol properties from XML void saveXmlBody(ostream &s) const; ///< Save details of the Symbol to XML @@ -415,11 +413,11 @@ class Scope { friend class ScopeCompare; RangeList rangetree; ///< Range of data addresses \e owned by \b this scope Scope *parent; ///< The parent scope + Scope *owner; ///< Scope using \b this as a cache ScopeMap children; ///< Sorted list of child scopes void attachScope(Scope *child); ///< Attach a new child Scope to \b this void detachScope(ScopeMap::iterator iter); ///< Detach a child Scope from \b this void assignId(uint4 val) { uniqueId = val; } ///< Let the database assign a unique id to \b this scope - protected: Architecture *glb; ///< Architecture of \b this scope string name; ///< Name of \b this scope @@ -454,6 +452,15 @@ protected: LabSymbol **addrmatch); const RangeList &getRangeTree(void) const { return rangetree; } ///< Access the address ranges owned by \b this Scope + + /// \brief Build an unattached Scope to be associated as a sub-scope of \b this + /// + /// This is a Scope object \e factory, intended to be called off of the global scope for building + /// global namespace scopes. Function scopes are handled differently. + /// \param nm is the name of the new scope + /// \return the new Scope object + virtual Scope *buildSubScope(const string &nm)=0; + virtual void restrictScope(Funcdata *f); ///< Convert \b this to a local Scope // These add/remove range are for scope \b discovery, i.e. we may @@ -493,7 +500,7 @@ protected: /// \return the newly created SymbolEntry virtual SymbolEntry *addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 hash,int4 off,int4 sz, const RangeList &uselim)=0; - SymbolEntry *addMap(const SymbolEntry &entry); ///< Integrate a SymbolEntry into the range maps + 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 public: #ifdef OPACTION_DEBUG @@ -502,8 +509,8 @@ public: void turnOffDebug(void) const { debugon = false; } #endif /// \brief Construct an empty scope, given a name and Architecture - Scope(const string &nm,Architecture *g) { - name = nm; glb = g; parent = (Scope *)0; fd = (Funcdata *)0; uniqueId = 0; + Scope(const string &nm,Architecture *g,Scope *own) { + name = nm; glb = g; parent = (Scope *)0; fd = (Funcdata *)0; uniqueId = 0; owner=own; #ifdef OPACTION_DEBUG debugon = false; #endif @@ -604,13 +611,14 @@ public: /// \param res will contain any matching Symbols virtual void findByName(const string &name,vector &res) const=0; - /// \brief Check if the given name is used within \b this scope. + /// \brief Check if the given name is occurs within the given scope path. /// - /// Only \b this scope is checked. If one or more symbols exist with the given name, - /// \b true is returned. - /// \param name is the given name to check for - /// \return \b true if the name is used within \b this scope - virtual bool isNameUsed(const string &name) const=0; + /// Test for the presence of a symbol with the given name in either \b this scope or + /// an ancestor scope up to but not including the given terminating scope. + /// If the name is used \b true is returned. + /// \param nm is the given name to test + /// \param op2 is the terminating ancestor scope (or null) + virtual bool isNameUsed(const string &nm,const Scope *op2) const=0; /// \brief Convert an \e external \e reference to the referenced function /// @@ -694,8 +702,7 @@ public: bool isSubScope(const Scope *scp) const; ///< Is this a sub-scope of the given Scope string getFullName(void) const; ///< Get the full name of \b this Scope void getNameSegments(vector &vec) const; ///< Get the fullname of \b this in segments - void getScopePath(vector &vec) const; ///< Get the ordered list of parent scopes to \b this - bool isNameUsed(const string &nm,const Scope *op2) const; ///< Is the given name in use within given scope path + void getScopePath(vector &vec) const; ///< Get the ordered list of scopes up to \b this const Scope *findDistinguishingScope(const Scope *op2) const; ///< Find first ancestor of \b this not shared by given scope Architecture *getArch(void) const { return glb; } ///< Get the Architecture associated with \b this Scope *getParent(void) const { return parent; } ///< Get the parent Scope (or NULL if \b this is the global Scope) @@ -720,9 +727,11 @@ public: /// a \b maptable, which is a list of rangemaps that own the SymbolEntry objects. class ScopeInternal : public Scope { void processHole(const Element *el); + void processCollision(const Element *el); void insertNameTree(Symbol *sym); SymbolNameTree::const_iterator findFirstByName(const string &name) const; protected: + virtual Scope *buildSubScope(const string &nm); ///< Build an unattached Scope to be associated as a sub-scope of \b this virtual void addSymbolInternal(Symbol *sym); virtual SymbolEntry *addMapInternal(Symbol *sym,uint4 exfl,const Address &addr,int4 off,int4 sz,const RangeList &uselim); virtual SymbolEntry *addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 hash,int4 off,int4 sz, @@ -735,6 +744,7 @@ protected: uint8 nextUniqueId; ///< Next available symbol id public: ScopeInternal(const string &nm,Architecture *g); ///< Construct the Scope + ScopeInternal(const string &nm,Architecture *g, Scope *own); ///< Construct as a cache virtual void clear(void); virtual void categorySanity(void); ///< Make sure Symbol categories are sane virtual void clearCategory(int4 cat); @@ -766,7 +776,7 @@ public: virtual SymbolEntry *findOverlap(const Address &addr,int4 size) const; virtual void findByName(const string &name,vector &res) const; - virtual bool isNameUsed(const string &name) const; + virtual bool isNameUsed(const string &nm,const Scope *op2) const; virtual Funcdata *resolveExternalRefFunction(ExternRefSymbol *sym) const; virtual string buildVariableName(const Address &addr, @@ -858,7 +868,9 @@ public: void removeRange(Scope *scope,AddrSpace *spc,uintb first,uintb last); ///< Remove an address range from \e ownership of a Scope Scope *getGlobalScope(void) const { return globalscope; } ///< Get the global Scope Scope *resolveScope(const vector &subnames) const; ///< Look-up a Scope by name - Scope *resolveScopeSymbolName(const string &fullname,const string &delim,string &basename,Scope *start) const; + Scope *resolveScopeFromSymbolName(const string &fullname,const string &delim,string &basename,Scope *start) const; + Scope *findCreateSubscope(const string &nm,Scope *parent); /// Find (and if not found create) a specific subscope + Scope *findCreateScopeFromSymbolName(const string &fullname,const string &delim,string &basename,Scope *start); const Scope *mapScope(const Scope *qpoint,const Address &addr,const Address &usepoint) const; Scope *mapScope(Scope *qpoint,const Address &addr,const Address &usepoint); uint4 getProperty(const Address &addr) const { return flagbase.getValue(addr); } ///< Get boolean properties at the given address @@ -870,4 +882,41 @@ public: void restoreXmlScope(const Element *el,Scope *new_scope); ///< Register and fill out a single Scope from XML }; +/// \param sc is the scope containing the new symbol +/// \param nm is the local name of the symbol +/// \param ct is the data-type of the symbol +inline Symbol::Symbol(Scope *sc,const string &nm,Datatype *ct) + +{ + scope=sc; + name=nm; + nameDedup=0; + type=ct; + flags=0; + dispflags=0; + category=-1; + catindex = 0; + symbolId=0; + wholeCount=0; + depthScope = (const Scope *)0; + depthResolution = 0; +} + +/// \param sc is the scope containing the new symbol +inline Symbol::Symbol(Scope *sc) + +{ + scope=sc; + nameDedup=0; + type = (Datatype *)0; + flags=0; + dispflags=0; + category=-1; + catindex = 0; + symbolId = 0; + wholeCount=0; + depthScope = (const Scope *)0; + depthResolution = 0; +} + #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc index 165fd087e3..3d4f47e952 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc @@ -16,12 +16,18 @@ #include "database_ghidra.hh" #include "funcdata.hh" +Scope *ScopeGhidra::buildSubScope(const string &nm) + +{ + return new ScopeGhidraNamespace(nm,ghidra); +} + /// \param g is the Architecture and connection to the Ghidra client ScopeGhidra::ScopeGhidra(ArchitectureGhidra *g) - : Scope("",g) + : Scope("",g,this) { ghidra = g; - cache = new ScopeInternal("",g); + cache = new ScopeInternal("",g,this); cacheDirty = false; } @@ -31,50 +37,51 @@ ScopeGhidra::~ScopeGhidra(void) delete cache; } -/// Ghidra may report that a Symbol is in a \e namespace. This method -/// creates a dedicated Scope object to hold such a Symbol. The immediate -/// parent for the new Scope must already exist. -/// \param nm is the name of the new \e namespace -/// \param par is the parent Scope -/// \return the new \e namespace Scope -Scope *ScopeGhidra::createNewScope(const string &nm,Scope *par) const - -{ - Scope *newscope = new ScopeGhidraNamespace(nm,ghidra); - ghidra->symboltab->attachScope(newscope,par); -// Document *doc = ghidra->getScopeProperties(newscope); -// if (doc == (Document *)0) -// throw LowlevelError("Bad getScopeProperties response"); -// const Element *root = doc->getRoot(); -// if (root->getName() == "rangelist") { -// RangeList newrangetree; -// newrangetree.restoreXml(root,ghidra); -// ghidra->symboltab->set_range(newscope,newrangetree); -// } -// delete doc; - return newscope; -} - -/// The Ghidra client reports a \e namespace path associated with -/// Symbol. Determine if this Scope already exists in the cache and built +/// The Ghidra client reports a \e namespace id associated with +/// Symbol. Determine if a matching \e namespac Scope already exists in the cache and build /// it if it isn't. This may mean creating a new \e namespace Scope. -/// \param path is absolute path to the desired Scope -/// \return the Scope matching the path. -Scope *ScopeGhidra::reresolveScope(const vector &path) const +/// \param id is the ID associated with the Ghidra namespace +/// \return the Scope matching the id. +Scope *ScopeGhidra::reresolveScope(uint8 id) const { - if (path.size()==1) return cache; - // Get pointer to ourselves (which is not const) - Scope *curscope = glb->symboltab->getGlobalScope(); - int4 i; - for(i=1;iresolveScope(path[i]); - if (nextscope == (Scope *)0) break; - curscope = nextscope; + if (id == 0) return cache; + map::const_iterator miter = namespaceMap.find(id); + if (miter != namespaceMap.end()) + return (*miter).second; // Scope was previously cached + + Document *doc = ghidra->getNamespacePath(id); + if (doc == (Document *)0) + throw LowlevelError("Could not get namespace info"); + + Scope *curscope = glb->symboltab->getGlobalScope(); // Get pointer to ourselves (which is not const) + try { + const List &list(doc->getRoot()->getChildren()); + List::const_iterator iter = list.begin(); + ++iter; // Skip element describing the root scope + while(iter != list.end()) { + const Element *el = *iter; + ++iter; + uint8 scopeId; + istringstream s(el->getAttributeValue("id")); + s.unsetf(ios::dec | ios::hex | ios::oct); + s >> scopeId; + miter = namespaceMap.find(scopeId); + if (miter == namespaceMap.end()) { + curscope = glb->symboltab->findCreateSubscope(el->getContent(), curscope); + ScopeGhidraNamespace *ghidraScope = (ScopeGhidraNamespace *)curscope; + if (ghidraScope->getClientId() == 0) + ghidraScope->setClientId(scopeId); + namespaceMap[scopeId] = curscope; + } + else + curscope = (*miter).second; + } + delete doc; } - while(i != path.size()) { - curscope = createNewScope(path[i],curscope); - i += 1; + catch(LowlevelError &err) { + delete doc; + throw err; } return curscope; } @@ -126,16 +133,14 @@ Symbol *ScopeGhidra::dump2Cache(Document *doc) const } List::const_iterator iter = el->getChildren().begin(); - // The first subnode must be scope information - el = *iter; - vector path; - const List &list2(el->getChildren()); - List::const_iterator iter2; - for(iter2=list2.begin();iter2!=list2.end();++iter2) - path.push_back( (*iter2)->getContent() ); + uint8 scopeId; + { + istringstream s(el->getAttributeValue("id")); + s.unsetf(ios::dec | ios::hex | ios::oct); + s >> scopeId; + } - Scope *scope = reresolveScope(path); - ++iter; // The second part is a + Scope *scope = reresolveScope(scopeId); el = *iter; try { sym = scope->addMapSym(el); @@ -255,6 +260,7 @@ void ScopeGhidra::clear(void) { cache->clear(); holes.clear(); + namespaceMap.clear(); if (cacheDirty) { ghidra->symboltab->setProperties(flagbaseDefault); // Restore database properties to defaults cacheDirty = false; @@ -402,3 +408,13 @@ SymbolEntry *ScopeGhidraNamespace::addMapInternal(Symbol *sym,uint4 exfl,const A glb->symboltab->addRange(this,res->getAddr().getSpace(),res->getFirst(),res->getLast()); return res; } + +bool ScopeGhidraNamespace::isNameUsed(const string &nm,const Scope *op2) const + +{ + if (ArchitectureGhidra::isDynamicSymbolName(nm)) + return false; // Just assume default FUN_ and DAT_ names don't collide + const ScopeGhidraNamespace *otherScope = dynamic_cast(op2); + uint8 otherId = (otherScope != (const ScopeGhidraNamespace *)0) ? otherScope->getClientId() : 0; + return ghidra->isNameUsed(nm, scopeId, otherId); +} diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.hh index ae685a58ad..3f5e4e1af8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.hh @@ -36,18 +36,19 @@ class ScopeGhidra : public Scope { ArchitectureGhidra *ghidra; ///< Architecture and connection to the Ghidra client mutable ScopeInternal *cache; ///< An internal cache of previously fetched Symbol objects mutable RangeList holes; ///< List of (queried) memory ranges with no Symbol in them + mutable map namespaceMap; ///< Map from id to formal global namespace objects vector spacerange; ///< List of address spaces that are in the global range partmap flagbaseDefault; ///< Default boolean properties on memory mutable bool cacheDirty; ///< Is flagbaseDefault different from cache Symbol *dump2Cache(Document *doc) const; ///< Parse a response into the cache Symbol *removeQuery(const Address &addr) const; ///< Process a query that missed the cache void processHole(const Element *el) const; ///< Process a response describing a hole - Scope *createNewScope(const string &nm,Scope *par) const; ///< Create a global \e namespace Scope - Scope *reresolveScope(const vector &path) const; ///< Find the Scope that will contain a result Symbol + Scope *reresolveScope(uint8 id) const; ///< Find the Scope that will contain a result Symbol virtual void addRange(AddrSpace *spc,uintb first,uintb last); virtual void removeRange(AddrSpace *spc,uintb first,uintb last) { throw LowlevelError("remove_range should not be performed on ghidra scope"); } + virtual Scope *buildSubScope(const string &nm); virtual void addSymbolInternal(Symbol *sym) { throw LowlevelError("add_symbol_internal unimplemented"); } virtual SymbolEntry *addMapInternal(Symbol *sym,uint4 exfl,const Address &addr,int4 off,int4 sz, const RangeList &uselim) { throw LowlevelError("addMap unimplemented"); } @@ -89,7 +90,7 @@ public: virtual SymbolEntry *findOverlap(const Address &addr,int4 size) const { throw LowlevelError("findOverlap unimplemented"); } virtual void findByName(const string &name,vector &res) const { throw LowlevelError("findByName unimplemented"); } - virtual bool isNameUsed(const string &name) const { throw LowlevelError("isNameUsed unimplemented"); } + virtual bool isNameUsed(const string &nm,const Scope *op2) const { throw LowlevelError("isNameUsed unimplemented"); } virtual MapIterator begin(void) const { throw LowlevelError("begin unimplemented"); } virtual MapIterator end(void) const { throw LowlevelError("end unimplemented"); } @@ -124,11 +125,19 @@ public: /// be a ScopeGhidra. This will query the Ghidra client on behalf of the \e namespace and /// register any new symbols with \b this Scope. class ScopeGhidraNamespace : public ScopeInternal { + friend class ScopeGhidra; + ArchitectureGhidra *ghidra; ///< Connection to the Ghidra client + uint8 scopeId; ///< Internal id allowing Ghidra client to reference formal namespaces + void setClientId(uint8 id) { scopeId = id; } +protected: virtual SymbolEntry *addMapInternal(Symbol *sym,uint4 exfl,const Address &addr,int4 off,int4 sz, const RangeList &uselim); public: - ScopeGhidraNamespace(const string &nm,Architecture *g) - : ScopeInternal(nm,g) {} ///< Constructor + ScopeGhidraNamespace(const string &nm,ArchitectureGhidra *g) + : ScopeInternal(nm,g) { ghidra = g; scopeId = 0; } ///< Constructor + + uint8 getClientId(void) const { return scopeId; } ///< Get the Ghidra specific id + virtual bool isNameUsed(const string &nm,const Scope *op2) const; }; #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc index 4a0cc9b984..fe37245d79 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc @@ -71,6 +71,7 @@ FloatFormat::FloatFormat(int4 sz) jbitimplied = true; } maxexponent = (1<getAttributeValue("jbitimplied")); maxexponent = (1<sym == (Symbol *)0) { - if (scope->discoverScope(pieces.addr,pieces.type->getSize(),usepoint) != scope) + if (scope->discoverScope(pieces.addr,pieces.type->getSize(),usepoint) == (Scope *)0) usepoint = restricted_usepoint; res->sym = scope->addSymbol(nm,pieces.type,pieces.addr,usepoint)->getSymbol(); scope->setCategory(res->sym,0,i); @@ -3182,9 +3182,10 @@ void FuncProto::cancelInjectId(void) /// given a list of Varnodes and their associated trial information, /// create an input parameter for each trial in order, grabbing data-type /// information from the Varnode. Any old input parameters are cleared. +/// \param data is the function containing the trial Varnodes /// \param triallist is the list of Varnodes /// \param activeinput is the trial container -void FuncProto::updateInputTypes(const vector &triallist,ParamActive *activeinput) +void FuncProto::updateInputTypes(Funcdata &data,const vector &triallist,ParamActive *activeinput) { if (isInputLocked()) return; // Input is locked, do no updating @@ -3195,15 +3196,25 @@ void FuncProto::updateInputTypes(const vector &triallist,ParamActive ParamTrial &trial(activeinput->getTrial(i)); if (trial.isUsed()) { Varnode *vn = triallist[trial.getSlot()-1]; - if (!vn->isMark()) { - ParameterPieces pieces; + if (vn->isMark()) continue; + ParameterPieces pieces; + if (vn->isPersist()) { + int4 sz; + pieces.addr = data.findDisjointCover(vn, sz); + if (sz == vn->getSize()) + pieces.type = vn->getHigh()->getType(); + else + pieces.type = data.getArch()->types->getBase(sz, TYPE_UNKNOWN); + pieces.flags = 0; + } + else { pieces.addr = trial.getAddress(); pieces.type = vn->getHigh()->getType(); pieces.flags = 0; - store->setInput(count,"",pieces); - count += 1; - vn->setMark(); // Make sure vn is used only once } + store->setInput(count,"",pieces); + count += 1; + vn->setMark(); } } for(int4 i=0;i &triallist,ParamActive /// This is accomplished in the same way as if there were data-types but instead of /// pulling a data-type from the Varnode, only the size is used. /// Undefined data-types are pulled from the given TypeFactory +/// \param data is the function containing the trial Varnodes /// \param triallist is the list of Varnodes /// \param activeinput is the trial container -/// \param factory is the given TypeFactory -void FuncProto::updateInputNoTypes(const vector &triallist,ParamActive *activeinput, - TypeFactory *factory) +void FuncProto::updateInputNoTypes(Funcdata &data,const vector &triallist,ParamActive *activeinput) { if (isInputLocked()) return; // Input is locked, do no updating store->clearAllInputs(); int4 count = 0; int4 numtrials = activeinput->getNumTrials(); + TypeFactory *factory = data.getArch()->types; for(int4 i=0;igetTrial(i)); if (trial.isUsed()) { Varnode *vn = triallist[trial.getSlot()-1]; - if (!vn->isMark()) { - ParameterPieces pieces; - pieces.type = factory->getBase(vn->getSize(),TYPE_UNKNOWN); - pieces.addr = trial.getAddress(); + if (vn->isMark()) continue; + ParameterPieces pieces; + if (vn->isPersist()) { + int4 sz; + pieces.addr = data.findDisjointCover(vn, sz); + pieces.type = factory->getBase(sz, TYPE_UNKNOWN); pieces.flags = 0; - store->setInput(count,"",pieces); - count += 1; - vn->setMark(); // Make sure vn is used only once } + else { + pieces.addr = trial.getAddress(); + pieces.type = factory->getBase(vn->getSize(),TYPE_UNKNOWN); + pieces.flags = 0; + } + store->setInput(count,"",pieces); + count += 1; + vn->setMark(); // Make sure vn is used only once } } for(int4 i=0;igetSize(),newop); // Its possible vn is free, in which case the SetInput would give it multiple descendants // See we construct a new version - if (vn->isFree() && (!vn->hasNoDescend())) + if (vn->isFree() && !vn->isConstant() && !vn->hasNoDescend()) vn = data.newVarnode(vn->getSize(),vn->getAddr()); data.opSetInput(newop,vn,0); data.opSetInput(newop,data.newConstant(4,0),1); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh index ee746a3b00..3fc240280e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh @@ -1327,8 +1327,8 @@ public: bool checkInputSplit(const Address &loc,int4 size,int4 splitpoint) const { return model->checkInputSplit(loc,size,splitpoint); } - void updateInputTypes(const vector &triallist,ParamActive *activeinput); - void updateInputNoTypes(const vector &triallist,ParamActive *activeinput,TypeFactory *factory); + void updateInputTypes(Funcdata &data,const vector &triallist,ParamActive *activeinput); + void updateInputNoTypes(Funcdata &data,const vector &triallist,ParamActive *activeinput); void updateOutputTypes(const vector &triallist); void updateOutputNoTypes(const vector &triallist,TypeFactory *factory); void updateAllTypes(const vector &namelist,const vector &typelist,bool dtdtdt); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc index 87c9f82499..a2390354a9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc @@ -20,7 +20,7 @@ /// \param scope is Symbol scope associated with the function /// \param addr is the entry address for 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,int4 sz) +Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz) : baseaddr(addr), funcp(), vbank(scope->getArch(), @@ -31,6 +31,7 @@ Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,int4 sz) { // Initialize high-level properties of // function by giving address and size + functionSymbol = sym; flags = 0; clean_up_index = 0; high_level_index = 0; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 71504ec713..5e57bba8e6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -63,9 +63,10 @@ class Funcdata { uint4 high_level_index; ///< Creation index of first Varnode created after HighVariables are created uint4 cast_phase_index; ///< Creation index of first Varnode created after ActionSetCasts uint4 minLanedSize; ///< Minimum Varnode size to check as LanedRegister - Architecture *glb; ///< Global configuration data - string name; ///< Name of function int4 size; ///< Number of bytes of binary data in function body + Architecture *glb; ///< Global configuration data + FunctionSymbol *functionSymbol; ///< The symbol representing \b this function + string name; ///< Name of function Address baseaddr; ///< Starting code address of binary data FuncProto funcp; ///< Prototype of this function ScopeLocal *localmap; ///< Local variables (symbols in the function scope) @@ -119,12 +120,13 @@ 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,int4 sz=0); ///< Constructor + Funcdata(const string &nm,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 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 the function + Architecture *getArch(void) const { return glb; } ///< Get the program/architecture owning \b this function + FunctionSymbol *getSymbol(void) const { return functionSymbol; } ///< Return the symbol associated with \b this function bool isHighOn(void) const { return ((flags&highlevel_on)!=0); } ///< Are high-level variables assigned to Varnodes bool isProcStarted(void) const { return ((flags&processing_started)!=0); } ///< Has processing of the function started bool isProcComplete(void) const { return ((flags&processing_complete)!=0); } ///< Is processing of the function complete @@ -264,6 +266,8 @@ public: void adjustInputVarnodes(const Address &addr,int4 size); void deleteVarnode(Varnode *vn) { vbank.destroy(vn); } ///< Delete the given varnode + Address findDisjointCover(Varnode *vn,int4 &sz); ///< Find range covering given Varnode and any intersecting Varnodes + /// \brief Find the first input Varnode covered by the given range /// /// \param s is the size of the range in bytes diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc index fbcb1e48cc..61c406b489 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc @@ -495,7 +495,7 @@ int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow) s1 << name << "@@jump@"; op->getAddr().printRaw(s1); - Funcdata partial(s1.str(),localmap->getParent(),baseaddr); + Funcdata partial(s1.str(),localmap->getParent(),baseaddr,(FunctionSymbol *)0); partial.flags |= jumptablerecovery_on; // Mark that this Funcdata object is dedicated to jumptable recovery partial.truncatedFlow(this,flow); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc index 2a895e3640..d18dd0fb41 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc @@ -1308,6 +1308,35 @@ void Funcdata::splitUses(Varnode *vn) // Dead-code actions should remove original op } +/// Find the minimal Address range covering the given Varnode that doesn't split other Varnodes +/// \param vn is the given Varnode +/// \param sz is used to pass back the size of the resulting range +/// \return the starting address of the resulting range +Address Funcdata::findDisjointCover(Varnode *vn,int4 &sz) + +{ + Address addr = vn->getAddr(); + Address endaddr = addr + vn->getSize(); + VarnodeLocSet::const_iterator iter = vn->lociter; + + while(iter != beginLoc()) { + --iter; + Varnode *curvn = *iter; + Address curEnd = curvn->getAddr() + curvn->getSize(); + if (curEnd <= addr) break; + addr = curvn->getAddr(); + } + iter = vn->lociter; + while(iter != endLoc()) { + Varnode *curvn = *iter; + ++iter; + if (endaddr <= curvn->getAddr()) break; + endaddr = curvn->getAddr() + curvn->getSize(); + } + sz = (int4)(endaddr.getOffset() - addr.getOffset()); + return addr; +} + /// Search for \e addrtied Varnodes whose storage falls in the global Scope, then /// build a new global Symbol if one didn't exist before. void Funcdata::mapGlobals(void) @@ -1326,6 +1355,7 @@ void Funcdata::mapGlobals(void) vn = *iter++; if (vn->isFree()) continue; if (!vn->isPersist()) continue; // Could be a code ref + if (vn->getSymbolEntry() != (SymbolEntry *)0) continue; maxvn = vn; Address addr = vn->getAddr(); Address endaddr = addr + vn->getSize(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc index 4191aa1003..632193eaca 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc @@ -81,6 +81,34 @@ int4 ArchitectureGhidra::readToAnyBurst(istream &s) } } +/// Read the string protocol start, a single character, then the protocol end. +/// If the character is a 't', return \b true, otherwise \b false. +/// \param s is the input stream from the client +/// \return the passed back boolean value +bool ArchitectureGhidra::readBoolStream(istream &s) + +{ + int4 c; + bool res; + + int4 type = readToAnyBurst(s); + if (type != 14) throw JavaError("alignment","Expecting string"); + c = s.get(); + res = (c == 't'); + c = s.get(); + while(c==0) { + c = s.get(); + } + if (c==1) { + c = s.get(); + if (c == 15) + return res; + } + if (c<0) // If pipe closed, our parent process is probably dead + exit(1); // So we exit to avoid a runaway process + throw JavaError("alignment","Expecting string terminator"); +} + /// Characters are read up to the next protocol marked and placed into a string. /// The protocol marker is consumed and must indicate the end of a string /// or an exception is thrown. @@ -515,6 +543,48 @@ Document *ArchitectureGhidra::getExternalRefXML(const Address &addr) return readXMLAll(sin); } +/// Ask the Ghidra client to list all namespace elements between the global root +/// and the namespace of the given id. The client should return a \ tag with +/// a \ child for each namespace in the path. +/// \param id is the given id of the namespace to resolve +/// \return the XML document +Document *ArchitectureGhidra::getNamespacePath(uint8 id) + +{ + sout.write("\000\000\001\004",4); + writeStringStream(sout,"getNamespacePath"); + sout.write("\000\000\001\016",4); // Beginning of string header + sout << hex << id; + sout.write("\000\000\001\017",4); + sout.write("\000\000\001\005",4); + sout.flush(); + + return readXMLAll(sin); +} + +bool ArchitectureGhidra::isNameUsed(const string &nm,uint8 startId,uint8 stopId) + +{ + sout.write("\000\000\001\004",4); + writeStringStream(sout,"isNameUsed"); + sout.write("\000\000\001\016",4); // Beginning of string header + sout << nm; + sout.write("\000\000\001\017",4); + sout.write("\000\000\001\016",4); // Beginning of string header + sout << hex << startId; + sout.write("\000\000\001\017",4); + sout.write("\000\000\001\016",4); // Beginning of string header + sout << hex << stopId; + sout.write("\000\000\001\017",4); + sout.write("\000\000\001\005",4); + sout.flush(); + + readToResponse(sin); + bool res = readBoolStream(sin); + readResponseEnd(sin); + return res; +} + /// Get the name of the primary symbol at the given address. /// This is used to fetch within function \e labels. Only a name is returned. /// \param addr is the given address @@ -772,3 +842,25 @@ ArchitectureGhidra::ArchitectureGhidra(const string &pspec,const string &cspec,c sendCcode = true; sendParamMeasures = false; } + +bool ArchitectureGhidra::isDynamicSymbolName(const string &nm) + +{ + if (nm.size() < 8) return false; // 4 characters of prefix, at least 4 of address + if (nm[3] != '_') return false; + if (nm[0]=='F' && nm[1]=='U' && nm[2]=='N') { + } + else if (nm[0]=='D' && nm[1]=='A' && nm[2]=='T') { + } + else { + return false; + } + for(int4 i=nm.size()-4;i='0' && c<='9') continue; + if (c>='a' && c<='f') continue; + return false; + } + return true; +} + diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh index c9ecf47381..6befb115f8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh @@ -92,6 +92,8 @@ public: uint1 *getPcodePacked(const Address &addr); ///< Get p-code for a single instruction Document *getMappedSymbolsXML(const Address &addr); ///< Get symbols associated with the given address Document *getExternalRefXML(const Address &addr); ///< Retrieve a description of an external function + Document *getNamespacePath(uint8 id); ///< Get a description of a namespace path + bool isNameUsed(const string &nm,uint8 startId,uint8 stopId); ///< Is given name used along namespace path string getCodeLabel(const Address &addr); ///< Retrieve a label at the given address Document *getType(const string &name,uint8 id); ///< Retrieve a data-type description for the given name and id Document *getComments(const Address &fad,uint4 flags); ///< Retrieve comments for a particular function @@ -130,6 +132,7 @@ public: static void segvHandler(int4 sig); ///< Handler for a segment violation (SIGSEGV) signal static int4 readToAnyBurst(istream &s); ///< Read the next message protocol marker + static bool readBoolStream(istream &s); ///< Read a boolean value from the client static void readStringStream(istream &s,string &res); ///< Receive a string from the client static void writeStringStream(ostream &s,const string &msg); ///< Send a string to the client static void readToResponse(istream &s); ///< Read the query response protocol marker @@ -139,6 +142,8 @@ public: static uint1 *readPackedStream(istream &s); ///< Read packed p-code op information static uint1 *readPackedAll(istream &s); ///< Read a whole response as packed p-code op information static void passJavaException(ostream &s,const string &tp,const string &msg); + + static bool isDynamicSymbolName(const string &nm); ///< Check if name is of form FUN_.. or DAT_.. }; #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.y b/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.y index 3836408878..33141afdab 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.y +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.y @@ -524,7 +524,7 @@ uint4 GrammarLexer::moveState(char lookahead) } else if ((lookahead>='a')&&(lookahead<='z')) { } - else if (lookahead == '_') { + else if (lookahead == '_' || lookahead == ':') { } else { state = start; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc index 3520b2383b..8e12a899de 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc @@ -66,7 +66,7 @@ LocationMap::iterator LocationMap::add(Address addr,int4 size,int4 pass,int4 &in /// describing the associated range and when it was heritaged. /// \param addr is the given address /// \return the iterator to the SizeMap entry or the end iterator is the address is unheritaged -LocationMap::iterator LocationMap::find(Address addr) +LocationMap::iterator LocationMap::find(const Address &addr) { iterator iter = themap.upper_bound(addr); // First range after address @@ -80,7 +80,7 @@ LocationMap::iterator LocationMap::find(Address addr) /// Return the pass number when the given address was heritaged, or -1 if it was not heritaged /// \param addr is the given address /// \return the pass number of -1 -int4 LocationMap::findPass(Address addr) const +int4 LocationMap::findPass(const Address &addr) const { map::const_iterator iter = themap.upper_bound(addr); // First range after address diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh index 025cfeb3db..ab75eea3c4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh @@ -47,12 +47,12 @@ private: map themap; ///< Heritaged addresses mapped to range size and pass number public: iterator add(Address addr,int4 size,int4 pass,int4 &intersect); ///< Mark new address as \b heritaged - iterator find(Address addr); ///< Look up if/how given address was heritaged - int4 findPass(Address addr) const; ///< Look up if/how given address was heritaged - void erase(iterator iter) { themap.erase(iter); } ///< Remove a particular entry from the map - iterator begin(void) { return themap.begin(); } ///< Get starting iterator over heritaged ranges - iterator end(void) { return themap.end(); } ///< Get ending iterator over heritaged ranges - void clear(void) { themap.clear(); } ///< Clear the map of heritaged ranges + iterator find(const Address &addr); ///< Look up if/how given address was heritaged + int4 findPass(const Address &addr) const; ///< Look up if/how given address was heritaged + void erase(iterator iter) { themap.erase(iter); } ///< Remove a particular entry from the map + iterator begin(void) { return themap.begin(); } ///< Get starting iterator over heritaged ranges + iterator end(void) { return themap.end(); } ///< Get ending iterator over heritaged ranges + void clear(void) { themap.clear(); } ///< Clear the map of heritaged ranges }; /// \brief Priority queue for the phi-node (MULTIEQUAL) placement algorithm diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc index 3ab5ae4b62..b0b2325e44 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc @@ -393,7 +393,7 @@ void IfcFuncload::execute(istream &s) throw IfaceExecutionError("No image loaded"); string basename; - Scope *funcscope = dcp->conf->symboltab->resolveScopeSymbolName(funcname,"::",basename,(Scope *)0); + Scope *funcscope = dcp->conf->symboltab->resolveScopeFromSymbolName(funcname,"::",basename,(Scope *)0); if (funcscope == (Scope *)0) throw IfaceExecutionError("Bad namespace: "+funcname); dcp->fd = funcscope->queryFunction( basename ); // Is function already in database @@ -438,7 +438,7 @@ void IfcReadSymbols::execute(istream &s) if (dcp->conf->loader == (LoadImage *)0) throw IfaceExecutionError("No binary loaded"); - dcp->conf->readLoaderSymbols(); + dcp->conf->readLoaderSymbols("::"); } void IfcMapaddress::execute(istream &s) @@ -460,9 +460,16 @@ void IfcMapaddress::execute(istream &s) Symbol *sym; uint4 flags = Varnode::namelock|Varnode::typelock; flags |= dcp->conf->symboltab->getProperty(addr); // Inherit existing properties - sym = dcp->conf->symboltab->getGlobalScope()->addSymbol(name,ct,addr,Address())->getSymbol(); + string basename; + Scope *scope = dcp->conf->symboltab->findCreateScopeFromSymbolName(name, "::", basename, (Scope *)0); + sym = scope->addSymbol(basename,ct,addr,Address())->getSymbol(); sym->getScope()->setAttribute(sym,flags); + if (scope->getParent() != (Scope *)0) { // If this is a global namespace scope + SymbolEntry *e = sym->getFirstWholeMap(); // Adjust range + dcp->conf->symboltab->addRange(scope,e->getAddr().getSpace(),e->getFirst(),e->getLast()); + } } + } void IfcMaphash::execute(istream &s) @@ -497,7 +504,9 @@ void IfcMapfunction::execute(istream &s) s >> name; // Read optional name if (name.empty()) dcp->conf->nameFunction(addr,name); // Pick default name if necessary - dcp->fd = dcp->conf->symboltab->getGlobalScope()->addFunction(addr,name)->getFunction(); + string basename; + Scope *scope = dcp->conf->symboltab->findCreateScopeFromSymbolName(name, "::", basename, (Scope *)0); + dcp->fd = scope->addFunction(addr,name)->getFunction(); string nocode; s >> ws >> nocode; @@ -1774,18 +1783,15 @@ void IfcPrintMap::execute(istream &s) if (dcp->conf == (Architecture *)0) throw IfaceExecutionError("No load image"); - if ((name=="global")||(dcp->fd==(Funcdata *)0)) { - scope = dcp->conf->symboltab->getGlobalScope(); - name = ""; + if (name.size() != 0 || dcp->fd==(Funcdata *)0) { + string fullname = name + "::a"; // Add fake variable name + scope = dcp->conf->symboltab->resolveScopeFromSymbolName(fullname, "::", fullname, (Scope *)0); } else scope = dcp->fd->getScopeLocal(); - if (name.size() != 0) { - scope = scope->resolveScope(name); - if (scope == (Scope *)0) - throw IfaceExecutionError("No map named: "+name); - } + if (scope == (Scope *)0) + throw IfaceExecutionError("No map named: "+name); *status->fileoptr << scope->getFullName() << endl; scope->printBounds(*status->fileoptr); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc index 32ea9769fa..4e20a4a032 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc @@ -81,6 +81,7 @@ OptionDatabase::OptionDatabase(Architecture *g) registerOption(new OptionToggleRule()); registerOption(new OptionAliasBlock()); registerOption(new OptionMaxInstruction()); + registerOption(new OptionNamespaceStrategy()); } OptionDatabase::~OptionDatabase(void) @@ -833,3 +834,23 @@ string OptionMaxInstruction::apply(Architecture *glb,const string &p1,const stri glb->max_instructions = newMax; return "Maximum instructions per function set"; } + +/// \class OptionNamespaceStrategy +/// \brief How should namespace tokens be displayed +/// +/// The first parameter gives the strategy identifier, mapping to PrintLanguage::namespace_strategy. +string OptionNamespaceStrategy::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const + +{ + PrintLanguage::namespace_strategy strategy; + if (p1 == "minimal") + strategy = PrintLanguage::MINIMAL_NAMESPACES; + else if (p1 == "all") + strategy = PrintLanguage::ALL_NAMESPACES; + else if (p1 == "none") + strategy = PrintLanguage::NO_NAMESPACES; + else + throw ParseError("Must specify a valid strategy"); + glb->print->setNamespaceStrategy(strategy); + return "Namespace strategy set"; +} diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh index 5cd7674407..37c692077a 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh @@ -270,4 +270,10 @@ public: virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const; }; +class OptionNamespaceStrategy : public ArchOption { +public: + OptionNamespaceStrategy(void) { name = "namespacestrategy"; } ///< Constructor + virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const; +}; + #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index dabcb040fc..7f4241e6db 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -38,6 +38,7 @@ OpToken PrintC::binary_plus = { "+", 2, 50, true, OpToken::binary, 1, 0, (OpToke OpToken PrintC::binary_minus = { "-", 2, 50, false, OpToken::binary, 1, 0, (OpToken *)0 }; OpToken PrintC::shift_left = { "<<", 2, 46, false, OpToken::binary, 1, 0, (OpToken *)0 }; OpToken PrintC::shift_right = { ">>", 2, 46, false, OpToken::binary, 1, 0, (OpToken *)0 }; +OpToken PrintC::shift_sright = { ">>", 2, 46, false, OpToken::binary, 1, 0, (OpToken *)0 }; OpToken PrintC::less_than = { "<", 2, 42, false, OpToken::binary, 1, 0, (OpToken *)0 }; OpToken PrintC::less_equal = { "<=", 2, 42, false, OpToken::binary, 1, 0, (OpToken *)0 }; OpToken PrintC::greater_than = { ">", 2, 42, false, OpToken::binary, 1, 0, (OpToken *)0 }; @@ -167,6 +168,68 @@ void PrintC::pushPrototypeInputs(const FuncProto *proto) } } +/// Calculate what elements of a given symbol's namespace path are necessary to distinguish +/// it within the current scope. Then print these elements. +/// \param symbol is the given symbol +void PrintC::pushSymbolScope(const Symbol *symbol) + +{ + int4 scopedepth; + if (namespc_strategy == MINIMAL_NAMESPACES) + scopedepth = symbol->getResolutionDepth(curscope); + else if (namespc_strategy == ALL_NAMESPACES) { + if (symbol->getScope() == curscope) + scopedepth = 0; + else + scopedepth = symbol->getResolutionDepth((const Scope *)0); + } + else + scopedepth = 0; + if (scopedepth != 0) { + vector scopeList; + const Scope *point = symbol->getScope(); + for(int4 i=0;igetParent(); + pushOp(&scope, (PcodeOp *)0); + } + for(int4 i=scopedepth-1;i>=0;--i) { + pushAtom(Atom(scopeList[i]->getName(),syntax,EmitXml::global_color,(PcodeOp *)0,(Varnode *)0)); + } + } +} + +/// Emit the elements of the given function's namespace path that distinguish it within +/// the current scope. +/// \param fd is the given function +void PrintC::emitSymbolScope(const Symbol *symbol) + +{ + int4 scopedepth; + if (namespc_strategy == MINIMAL_NAMESPACES) + scopedepth = symbol->getResolutionDepth(curscope); + else if (namespc_strategy == ALL_NAMESPACES) { + if (symbol->getScope() == curscope) + scopedepth = 0; + else + scopedepth = symbol->getResolutionDepth((const Scope *)0); + } + else + scopedepth = 0; + if (scopedepth != 0) { + vector scopeList; + const Scope *point = symbol->getScope(); + for(int4 i=0;igetParent(); + } + for(int4 i=scopedepth-1;i>=0;--i) { + emit->print(scopeList[i]->getName().c_str(), EmitXml::global_color); + emit->print(scope.print, EmitXml::no_color); + } + } +} + /// Store off array sizes for printing after the identifier /// \param ct is the data-type to push /// \param noident is \b true if an identifier will not be pushed as part of the declaration @@ -453,8 +516,12 @@ void PrintC::opCall(const PcodeOp *op) string name = genericFunctionName(fc->getEntryAddress()); pushAtom(Atom(name,functoken,EmitXml::funcname_color,op,(const Funcdata *)0)); } - else + else { + Funcdata *fd = fc->getFuncdata(); + if (fd != (Funcdata *)0) + pushSymbolScope(fd->getSymbol()); pushAtom(Atom(fc->getName(),functoken,EmitXml::funcname_color,op,(const Funcdata *)0)); + } } else { clear(); @@ -718,8 +785,10 @@ void PrintC::opPtrsub(const PcodeOp *op) int4 newoff; const TypeField *fld = ((TypeStruct *)ct)->getField((int4)suboff,0,&newoff); if (fld == (const TypeField *)0) { - if (ct->getSize() <= suboff) + if (ct->getSize() <= suboff) { + clear(); throw LowlevelError("PTRSUB out of bounds into struct"); + } // Try to match the Ghidra's default field name from DataTypeComponent.getDefaultFieldName ostringstream s; s << "field_0x" << hex << suboff; @@ -1087,38 +1156,59 @@ void PrintC::push_integer(uintb val,int4 sz,bool sign, /// \param op is the PcodeOp using the value void PrintC::push_float(uintb val,int4 sz,const Varnode *vn,const PcodeOp *op) { - ostringstream t; + string token; const FloatFormat *format = glb->translate->getFloatFormat(sz); if (format == (const FloatFormat *)0) { - t << "FLOAT_UNKNOWN"; + token = "FLOAT_UNKNOWN"; } else { FloatFormat::floatclass type; double floatval = format->getHostFloat(val,&type); if (type == FloatFormat::infinity) { if (format->extractSign(val)) - t << '-'; - t << "INFINITY"; + token = "-INFINITY"; + else + token = "INFINITY"; } else if (type == FloatFormat::nan) { if (format->extractSign(val)) - t << '-'; - t << "NAN"; + token = "-NAN"; + else + token = "NAN"; } else { - if ((mods & force_scinote)!=0) + ostringstream t; + if ((mods & force_scinote)!=0) { t.setf( ios::scientific ); // Set to scientific notation - else - t.setf( ios::fixed ); // Otherwise use fixed notation - t.precision(8); // Number of digits of precision - t << floatval; + t.precision(format->getDecimalPrecision()-1); + t << floatval; + token = t.str(); + } + else { + // Try to print "minimal" accurate representation of the float + t.unsetf( ios::floatfield ); // Use "default" notation + t.precision(format->getDecimalPrecision()); + t << floatval; + token = t.str(); + bool looksLikeFloat = false; + for(int4 i=0;ihasMergeProblems() && vn != (Varnode *)0) { HighVariable *high = vn->getHigh(); if (high->isUnmerged()) { @@ -1913,6 +2003,8 @@ void PrintC::resetDefaults(void) void PrintC::adjustTypeOperators(void) { + scope.print = "::"; + shift_right.print = ">>"; TypeOp::selectJavaOperators(glb->inst,false); } @@ -2158,12 +2250,14 @@ void PrintC::emitFunctionDeclaration(const Funcdata *fd) } } int4 id1 = emit->openGroup(); + emitSymbolScope(fd->getSymbol()); emit->tagFuncName(fd->getName().c_str(),EmitXml::funcname_color, fd,(PcodeOp *)0); emit->spaces(function_call.spacing,function_call.bump); int4 id2 = emit->openParen('('); emit->spaces(0,function_call.bump); + pushScope(fd->getScopeLocal()); // Enter the function's scope for parameters emitPrototypeInputs(proto); emit->closeParen(')',id2); emit->closeGroup(id1); @@ -2220,7 +2314,7 @@ void PrintC::docFunction(const Funcdata *fd) int4 id1 = emit->beginFunction(fd); emitCommentFuncHeader(fd); emit->tagLine(); - emitFunctionDeclaration(fd); + emitFunctionDeclaration(fd); // Causes us to enter function's scope emit->tagLine(); emit->tagLine(); int4 id = emit->startIndent(); @@ -2230,6 +2324,7 @@ void PrintC::docFunction(const Funcdata *fd) emitBlockGraph(&fd->getBasicBlocks()); else emitBlockGraph(&fd->getStructure()); + popScope(); // Exit function's scope emit->stopIndent(id); emit->tagLine(); emit->print("}"); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh index 5426790072..431cbdedf7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh @@ -82,6 +82,7 @@ protected: static OpToken binary_minus; ///< The \e binary \e subtraction operator static OpToken shift_left; ///< The \e left \e shift operator static OpToken shift_right; ///< The \e right \e shift operator + static OpToken shift_sright; ///< The signed \e right \e shift operator static OpToken less_than; ///< The \e less \e than operator static OpToken less_equal; ///< The \e less \e than \e or \e equal operator static OpToken greater_than; ///< The \e greater \e than operator @@ -124,6 +125,8 @@ protected: // Routines that are specific to C/C++ void buildTypeStack(const Datatype *ct,vector &typestack); ///< Prepare to push components of a data-type declaration void pushPrototypeInputs(const FuncProto *proto); ///< Push input parameters + void pushSymbolScope(const Symbol *symbol); ///< Push tokens resolving a symbol's scope + void emitSymbolScope(const Symbol *symbol); ///< Emit tokens resolving a symbol's scope virtual void pushTypeStart(const Datatype *ct,bool noident); ///< Push part of a data-type declaration onto the RPN stack, up to the identifier virtual void pushTypeEnd(const Datatype *ct); ///< Push the tail ends of a data-type declaration onto the RPN stack void pushBoolConstant(uintb val,const TypeBase *ct,const Varnode *vn, @@ -251,7 +254,7 @@ public: virtual void opIntOr(const PcodeOp *op) { opBinary(&bitwise_or,op); } virtual void opIntLeft(const PcodeOp *op) { opBinary(&shift_left,op); } virtual void opIntRight(const PcodeOp *op) { opBinary(&shift_right,op); } - virtual void opIntSright(const PcodeOp *op) { opBinary(&shift_right,op); } + virtual void opIntSright(const PcodeOp *op) { opBinary(&shift_sright,op); } virtual void opIntMult(const PcodeOp *op) { opBinary(&multiply,op); } virtual void opIntDiv(const PcodeOp *op) { opBinary(÷,op); } virtual void opIntSdiv(const PcodeOp *op) { opBinary(÷,op); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc index c6cf5d5f25..394c5e2538 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc @@ -46,6 +46,20 @@ PrintJava::PrintJava(Architecture *glb,const string &nm) : PrintC(glb,nm) castStrategy = new CastStrategyJava(); } +void PrintJava::docFunction(const Funcdata *fd) + +{ + bool singletonFunction = false; + if (curscope == (const Scope *)0) { + singletonFunction = true; + // Always assume we are in the scope of the parent class + pushScope(fd->getScopeLocal()->getParent()); + } + PrintC::docFunction(fd); + if (singletonFunction) + popScope(); +} + /// Print a data-type up to the identifier, store off array sizes /// for printing after the identifier. Find the root type (the one with an identifier) /// and the count number of wrapping arrays. @@ -99,6 +113,8 @@ void PrintJava::pushTypeEnd(const Datatype *ct) void PrintJava::adjustTypeOperators(void) { + scope.print = "."; + shift_right.print = ">>>"; TypeOp::selectJavaOperators(glb->inst,true); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.hh index 14e8f2c522..fc62ab931f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.hh @@ -59,6 +59,7 @@ class PrintJava : public PrintC { virtual void printUnicode(ostream &s,int4 onechar) const; public: PrintJava(Architecture *g,const string &nm="java-language"); ///< Constructor + virtual void docFunction(const Funcdata *fd); virtual void pushTypeStart(const Datatype *ct,bool noident); virtual void pushTypeEnd(const Datatype *ct); virtual bool doEmitWideCharPrefix(void) const { return false; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc index 2d11add68c..bb73411e3b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc @@ -105,6 +105,16 @@ void PrintLanguage::setCommentDelimeter(const string &start,const string &stop,b } } +void PrintLanguage::popScope(void) + +{ + scopestack.pop_back(); + if (scopestack.empty()) + curscope = (Scope *)0; + else + curscope = scopestack.back(); +} + /// This generally will recursively push an entire expression onto the RPN stack, /// up to Varnode objects marked as \e explicit, and will decide token order /// and parenthesis placement. As the ordering gets resolved, @@ -565,6 +575,7 @@ void PrintLanguage::resetDefaultsInternal(void) mods = 0; head_comment_type = Comment::header | Comment::warningheader; line_commentindent = 20; + namespc_strategy = MINIMAL_NAMESPACES; instr_comment_type = Comment::user2 | Comment::warning; } @@ -658,10 +669,8 @@ void PrintLanguage::clear(void) mods = modstack.front(); modstack.clear(); } - if (!scopestack.empty()) { - curscope = scopestack.front(); - scopestack.clear(); - } + scopestack.clear(); + curscope = (const Scope *)0; revpol.clear(); pending = 0; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh index 933b266638..e06c7c4abd 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh @@ -162,6 +162,13 @@ public: blanktoken ///< For anonymous types }; + /// \brief Strategies for displaying namespace tokens + enum namespace_strategy { + MINIMAL_NAMESPACES = 0, ///< (default) Print just enough namespace info to fully resolve symbol + NO_NAMESPACES = 1, ///< Never print namespace information + ALL_NAMESPACES = 2 ///< Always print all namespace information + }; + /// \brief An entry on the reverse polish notation (RPN) stack struct ReversePolish { const OpToken *tok; ///< The operator token @@ -230,8 +237,7 @@ public: private: string name; ///< The name of the high-level language vector modstack; ///< Printing modification stack - vector scopestack; ///< The symbol scope stack - Scope *curscope; ///< The current symbol scope + vector scopestack; ///< The symbol scope stack vector revpol; ///< The Reverse Polish Notation (RPN) token stack vector nodepend; ///< Data-flow nodes waiting to be pushed onto the RPN stack int4 pending; ///< Number of data-flow nodes waiting to be pushed @@ -240,19 +246,21 @@ private: string commentend; ///< Delimiter characters (if any) for the end of a comment protected: Architecture *glb; ///< The Architecture owning the language emitter + const Scope *curscope; ///< The current symbol scope CastStrategy *castStrategy; ///< The strategy for emitting explicit \e case operations EmitXml *emit; ///< The low-level token emitter uint4 mods; ///< Currently active printing modifications uint4 instr_comment_type; ///< Type of instruction comments to display uint4 head_comment_type; ///< Type of header comments to display + namespace_strategy namespc_strategy; ///< How should namespace tokens be displayed #ifdef CPUI_DEBUG bool isStackEmpty(void) const { return (nodepend.empty()&&revpol.empty()); } ///< Return \b true if the RPN stack is empty bool isModStackEmpty(void) const { return modstack.empty(); } ///< Return \b true if the printing modification stack is empty #endif // Routines that are probably consistent across languages bool isSet(uint4 m) const { return ((mods & m)!=0); } ///< Is the given printing modification active - void pushScope(Scope *sc) { scopestack.push_back(sc); curscope = sc; } ///< Push a new symbol scope - void popScope(void) { scopestack.pop_back(); curscope = scopestack.back(); } ///< Pop to the previous symbol scope + void pushScope(const Scope *sc) { scopestack.push_back(sc); curscope = sc; } ///< Push a new symbol scope + void popScope(void); ///< Pop to the previous symbol scope void pushMod(void) { modstack.push_back(mods); } ///< Push current printing modifications to the stack void popMod(void) { mods = modstack.back(); modstack.pop_back(); } ///< Pop to the previous printing modifications void setMod(uint4 m) { mods |= m; } ///< Activate the given printing modification @@ -405,7 +413,6 @@ public: CastStrategy *getCastStrategy(void) const { return castStrategy; } ///< Get the casting strategy for the language ostream *getOutputStream(void) const { return emit->getOutputStream(); } ///< Get the output stream being emitted to void setOutputStream(ostream *t) { emit->setOutputStream(t); } ///< Set the output stream to emit to - void setScope(Scope *sc) { curscope = sc; } ///< Set the current Symbol scope void setMaxLineSize(int4 mls) { emit->setMaxLineSize(mls); } ///< Set the maximum number of characters per line void setIndentIncrement(int4 inc) { emit->setIndentIncrement(inc); } ///< Set the number of characters to indent per level of code nesting void setLineCommentIndent(int4 val); ///< Set the number of characters to indent comment lines @@ -413,6 +420,7 @@ public: bool usecommentfill); ///< Establish comment delimiters for the language uint4 getInstructionComment(void) const { return instr_comment_type; } ///< Get the type of comments suitable within the body of a function void setInstructionComment(uint4 val) { instr_comment_type = val; } ///< Set the type of comments suitable within the body of a function + void setNamespaceStrategy(namespace_strategy strat) { namespc_strategy = strat; } ///< Set how namespace tokens are displayed uint4 getHeaderComment(void) const { return head_comment_type; } ///< Get the type of comments suitable for a function header void setHeaderComment(uint4 val) { head_comment_type = val; } ///< Set the type of comments suitable for a function header bool emitsXml(void) const { return emit->emitsXml(); } ///< Does the low-level emitter, emit XML markup diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index e15e9870bc..558ed12b65 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -6553,7 +6553,6 @@ bool RulePtrsubCharConstant::pushConstFurther(Funcdata &data,TypePointer *outtyp Varnode *vn = op->getIn(1); if (!vn->isConstant()) return false; // that is adding a constant uintb addval = vn->getOffset(); - if (addval > 128) return false; // Sanity check on string size addval *= op->getIn(2)->getOffset(); val += addval; Varnode *newconst = data.newConstant(vn->getSize(),val); 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 a1516041a0..a4d12379c4 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 @@ -31,7 +31,6 @@ import ghidra.program.model.lang.*; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.*; -import ghidra.util.exception.CancelledException; import ghidra.util.task.CancelledListener; import ghidra.util.task.TaskMonitor; import ghidra.xml.XmlPullParser; @@ -131,7 +130,7 @@ public class DecompInterface { } /** - * Returns true if debug has been enabled for the current/next decompilation. + * @return true if debug has been enabled for the current/next decompilation. */ public boolean debugEnabled() { return debug != null; @@ -199,8 +198,8 @@ public class DecompInterface { /** * This is the main routine for making sure that a decompiler * process is active and that it is initialized properly - * @throws IOException - * @throws DecompileException + * @throws IOException for any problems with the pipe to the decompiler process + * @throws DecompileException for errors initializing decompiler options etc. */ protected void initializeProcess() throws IOException, DecompileException { if (decompCallback == null) { @@ -233,7 +232,6 @@ public class DecompInterface { } if (xmlOptions != null) { decompProcess.setMaxResultSize(xmlOptions.getMaxPayloadMBytes()); - decompProcess.setShowNamespace(xmlOptions.isDisplayNamespaces()); if (!decompProcess.sendCommand1Param("setOptions", xmlOptions.getXML(this)).toString().equals("t")) { throw new IOException("Did not accept decompiler options"); @@ -590,7 +588,6 @@ public class DecompInterface { try { verifyProcess(); decompProcess.setMaxResultSize(xmlOptions.getMaxPayloadMBytes()); - decompProcess.setShowNamespace(xmlOptions.isDisplayNamespaces()); return decompProcess.sendCommand1Param("setOptions", xmloptions.getXML(this)).toString().equals("t"); } @@ -701,8 +698,7 @@ public class DecompInterface { if (program == null) { return new DecompileResults(func, pcodelanguage, null, dtmanage, decompileMessage, null, - DecompileProcess.DisposeState.DISPOSED_ON_CANCEL, - false /* cancelled--doesn't matter */); + DecompileProcess.DisposeState.DISPOSED_ON_CANCEL); } try { @@ -747,7 +743,7 @@ public class DecompInterface { stream = res.getInputStream(); } return new DecompileResults(func, pcodelanguage, compilerSpec, dtmanage, decompileMessage, - stream, processState, isDisplayNamespace()); + stream, processState); } /** @@ -796,12 +792,4 @@ public class DecompInterface { public CompilerSpec getCompilerSpec() { return compilerSpec; } - - private boolean isDisplayNamespace() { - if (xmlOptions == null) { - return false; // not sure if this can happen - } - return xmlOptions.isDisplayNamespaces(); - } - } 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 da9365cad8..6674cc9567 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 @@ -54,6 +54,7 @@ import ghidra.util.xml.XmlUtilities; */ public class DecompileCallback { + public final static int MAX_SYMBOL_COUNT = 16; /** * Data returned for a query about strings */ @@ -77,7 +78,6 @@ public class DecompileCallback { private PcodeDataTypeManager dtmanage; private Charset utf8Charset; private String nativeMessage; - private boolean showNamespace; private InstructionBlock lastPseudoInstructionBlock; private Disassembler pseudoDisassembler; @@ -153,10 +153,6 @@ public class DecompileCallback { nativeMessage = msg; } - public void setShowNamespace(boolean showNamespace) { - this.showNamespace = showNamespace; - } - public synchronized int readXMLSize(String addrxml) { int attrstart = addrxml.indexOf("size=\""); if (attrstart >= 4) { @@ -528,6 +524,15 @@ public class DecompileCallback { return sym.getName(); } + private Namespace getNameSpaceByID(long id) { + Symbol namespaceSym = program.getSymbolTable().getSymbol(id); + Object namespace = namespaceSym.getObject(); + if (namespace instanceof Namespace) { + return (Namespace) namespace; + } + return null; + } + private String getNamespacePrefix(Namespace ns) { if (ns.getID() == Namespace.GLOBAL_NAMESPACE_ID) { return null; @@ -543,6 +548,72 @@ public class DecompileCallback { return name; } + /** + * Decide if a given name is used by any namespace between a starting namespace + * and a stopping namespace. I.e. check for a name collision along a specific namespace path. + * Currently, Ghidra is inefficient at calculating this perfectly, so this routine calculates + * an approximation that can occasionally indicate a collision when there isn't. + * @param name is the given name to check for collisions + * @param startId is the id specifying the starting namespace + * @param stopId is the id specifying the stopping namespace + * @return true if the name (likely) occurs in one of the namespaces on the path + */ + public boolean isNameUsed(String name, long startId, long stopId) { + Namespace namespace = getNameSpaceByID(startId); + int pathSize = 0; + Namespace curspace = namespace; + long curId = namespace.getID(); + while (curId != stopId && curId != 0 && !(curspace instanceof Library)) { + pathSize += 1; + curspace = curspace.getParentNamespace(); + curId = curspace.getID(); + } + long path[] = new long[pathSize]; + curspace = namespace; + path[0] = startId; + for (int i = 1; i < pathSize; ++i) { + curspace = curspace.getParentNamespace(); + path[i] = curspace.getID(); + } + int count = 0; + SymbolIterator iter = program.getSymbolTable().getSymbols(name); + for (;;) { + if (!iter.hasNext()) { + break; + } + count += 1; + if (count > MAX_SYMBOL_COUNT) { + break; + } + Namespace symSpace = iter.next().getParentNamespace(); + long id = symSpace.getID(); + if (id == Namespace.GLOBAL_NAMESPACE_ID) { + continue; // Common case we know can't match anything in path + } + for (int i = 0; i < pathSize; ++i) { + if (path[i] == id) { + if (debug != null) { + debug.nameIsUsed(symSpace, name); + } + return true; + } + } + } + return (count > MAX_SYMBOL_COUNT); + } + + /** + * Return an XML description of the formal namespace path to the given namespace + * @param id is the ID of the given namespace + * @return a parent XML tag + */ + public String getNamespacePath(long id) { + Namespace namespace = getNameSpaceByID(id); + StringBuilder buf = new StringBuilder(); + HighFunction.createNamespaceTag(buf, namespace, true); + return buf.toString(); + } + private void generateHeaderCommentXML(Function func, StringBuilder buf) { Address addr = func.getEntryPoint(); String text = listing.getComment(CodeUnit.PLATE_COMMENT, addr); @@ -697,14 +768,13 @@ public class DecompileCallback { return null; } - HighFunction hfunc = - new HighFunction(func, pcodelanguage, pcodecompilerspec, dtmanage, showNamespace); + HighFunction hfunc = new HighFunction(func, pcodelanguage, pcodecompilerspec, dtmanage); int extrapop = getExtraPopOverride(func, addr); hfunc.grabFromFunction(extrapop, false, (extrapop != default_extrapop)); HighSymbol funcSymbol = new HighFunctionSymbol(addr, 2, hfunc); - Namespace namespc = func.getParentNamespace(); + Namespace namespc = funcSymbol.getNamespace(); if (debug != null) { debug.getFNTypes(hfunc); } @@ -802,16 +872,17 @@ public class DecompileCallback { } private String buildResult(HighSymbol highSymbol, Namespace namespc) { - StringBuilder res = new StringBuilder(); - res.append("\n"); - res.append("\n"); - if (namespc == null) { - res.append(""); // Assume global scope + long namespaceId; + if (namespc == null || namespc instanceof Library) { + namespaceId = Namespace.GLOBAL_NAMESPACE_ID; } else { - HighFunction.createNamespaceTag(res, namespc); + namespaceId = namespc.getID(); } - res.append("\n"); + StringBuilder res = new StringBuilder(); + res.append("\n"); if (debug != null) { StringBuilder res2 = new StringBuilder(); HighSymbol.buildMapSymXML(res2, highSymbol); @@ -938,14 +1009,14 @@ public class DecompileCallback { long diff = addr.getOffset() - entry.getOffset(); if ((diff >= 0) && (diff < 8)) { HighFunction hfunc = new HighFunction(func, pcodelanguage, pcodecompilerspec, - dtmanage, showNamespace); + dtmanage); int extrapop = getExtraPopOverride(func, addr); hfunc.grabFromFunction(extrapop, includeDefaultNames, (extrapop != default_extrapop)); HighSymbol functionSymbol = new HighFunctionSymbol(entry, (int) (diff + 1), hfunc); - Namespace namespc = func.getParentNamespace(); + Namespace namespc = functionSymbol.getNamespace(); if (debug != null) { debug.getFNTypes(hfunc); } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java index fc3fb38b9c..16e12029e4 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java @@ -522,18 +522,19 @@ public class DecompileDebug { } while (scopename != null) { StringBuilder datahead = new StringBuilder(); + Namespace parentNamespace; datahead.append("\n"); - datahead.append("\n"); - HighFunction.createNamespaceTag(datahead, scopename.getParentNamespace()); - datahead.append("\n"); + HighFunction.createNamespaceTag(datahead, parentNamespace, false); if (scopename != globalnamespace) { datahead.append("\n"); } @@ -676,6 +677,14 @@ public class DecompileDebug { cpool.add(buf.toString()); } + public void nameIsUsed(Namespace spc, String nm) { + StringBuilder buffer = new StringBuilder(); + buffer.append("\n"); + getMapped(spc, buffer.toString()); + } + public void addFlowOverride(Address addr,FlowOverride fo) { StringBuilder buf = new StringBuilder(); buf.append("DexAnalysisState which corresponds to the specified program instance. - * @param program + * @param program is the specified program instance * @return DexAnalysisState for specified program instance + * @throws IOException if there are problems during construction of the state object */ - public static DexAnalysisState getState(Program program) throws IOException { + public synchronized static DexAnalysisState getState(Program program) throws IOException { DexAnalysisState analysisState = AnalysisStateInfo.getAnalysisState(program, DexAnalysisState.class); if (analysisState == null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java index 45dcbbc356..faa800e92c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java @@ -51,22 +51,19 @@ public class HighFunction extends PcodeSyntaxTree { private GlobalSymbolMap globalSymbols; private List jumpTables; private List protoOverrides; - private boolean showNamespace = true; /** * @param function function associated with the higher level function abstraction. * @param language description of the processor language of the function * @param compilerSpec description of the compiler that produced the function * @param dtManager data type manager - * @param showNamespace true signals to print function names with their namespace */ public HighFunction(Function function, Language language, CompilerSpec compilerSpec, - PcodeDataTypeManager dtManager, boolean showNamespace) { + PcodeDataTypeManager dtManager) { super(function.getProgram().getAddressFactory(), dtManager); func = function; this.language = language; this.compilerSpec = compilerSpec; - this.showNamespace = showNamespace; localSymbols = new LocalSymbolMap(this, "stack"); globalSymbols = new GlobalSymbolMap(this); proto = new FunctionPrototype(localSymbols, function); @@ -260,9 +257,9 @@ public class HighFunction extends PcodeSyntaxTree { public void readXML(XmlPullParser parser) throws PcodeXMLException { XmlElement start = parser.start("function"); String name = start.getAttribute("name"); - if (!func.getName(showNamespace).equals(name)) { + if (!func.getName().equals(name)) { throw new PcodeXMLException( - "Function name mismatch: " + func.getName(showNamespace) + " + " + name); + "Function name mismatch: " + func.getName() + " + " + name); } while (!parser.peek().isEnd()) { XmlElement subel = parser.peek(); @@ -436,11 +433,12 @@ public class HighFunction extends PcodeSyntaxTree { * addresses near its entry point. * * @param id is the id associated with the function symbol + * @param namespace is the namespace containing the function symbol * @param entryPoint pass null to use the function entryPoint, pass an address to force an entry point * @param size describes how many bytes the function occupies as code * @return the XML string */ - public String buildFunctionXML(long id, Address entryPoint, int size) { + public String buildFunctionXML(long id, Namespace namespace, Address entryPoint, int size) { // Functions aren't necessarily contiguous with the smallest address being the entry point // So size needs to be smaller than size of the contiguous chunk containing the entry point StringBuilder resBuf = new StringBuilder(); @@ -448,7 +446,7 @@ public class HighFunction extends PcodeSyntaxTree { if (id != 0) { SpecXmlUtils.encodeUnsignedIntegerAttribute(resBuf, "id", id); } - SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", func.getName(showNamespace)); + SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", func.getName()); SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size); if (func.isInline()) { SpecXmlUtils.encodeBooleanAttribute(resBuf, "inline", true); @@ -463,12 +461,12 @@ public class HighFunction extends PcodeSyntaxTree { else { resBuf.append(Varnode.buildXMLAddress(entryPoint)); // Address is forced on XML } - resBuf.append(localSymbols.buildLocalDbXML()); + localSymbols.buildLocalDbXML(resBuf, namespace); proto.buildPrototypeXML(resBuf, getDataTypeManager()); if ((jumpTables != null) && (jumpTables.size() > 0)) { resBuf.append("\n"); - for (int i = 0; i < jumpTables.size(); ++i) { - jumpTables.get(i).buildXml(resBuf); + for (JumpTable jumpTable : jumpTables) { + jumpTable.buildXml(resBuf); } resBuf.append("\n"); } @@ -478,8 +476,7 @@ public class HighFunction extends PcodeSyntaxTree { } if ((protoOverrides != null) && (protoOverrides.size() > 0)) { PcodeDataTypeManager dtmanage = getDataTypeManager(); - for (int i = 0; i < protoOverrides.size(); ++i) { - DataTypeSymbol sym = protoOverrides.get(i); + for (DataTypeSymbol sym : protoOverrides) { Address addr = sym.getAddress(); FunctionPrototype fproto = new FunctionPrototype( (FunctionSignature) sym.getDataType(), compilerSpec, false); @@ -618,22 +615,39 @@ public class HighFunction extends PcodeSyntaxTree { } } - static public void createNamespaceTag(StringBuilder buf, Namespace namespc) { - if (namespc == null) { - return; - } - ArrayList arr = new ArrayList(); - Namespace curspc = namespc; - while (curspc != null) { - arr.add(0, curspc.getName()); - curspc = curspc.getParentNamespace(); - } - buf.append("\n"); // Force global scope to have empty name - for (int i = 1; i < arr.size(); ++i) { - buf.append(""); - SpecXmlUtils.xmlEscape(buf, arr.get(i)); - buf.append("\n"); + /** + * Append an XML <parent> tag to the buffer describing the formal path elements + * from the root (global) namespace up to the given namespace + * @param buf is the buffer to write to + * @param namespace is the namespace being described + * @param includeId is true if the XML tag should include namespace ids + */ + static public void createNamespaceTag(StringBuilder buf, Namespace namespace, + boolean includeId) { + buf.append("\n"); + if (namespace != null) { + ArrayList arr = new ArrayList(); + Namespace curspc = namespace; + while (curspc != null) { + arr.add(0, curspc); + if (curspc instanceof Library) { + break; // Treat library namespace as root + } + curspc = curspc.getParentNamespace(); + } + buf.append("\n"); // Force global scope to have empty name + for (int i = 1; i < arr.size(); ++i) { + Namespace curScope = arr.get(i); + buf.append("'); + SpecXmlUtils.xmlEscape(buf, curScope.getName()); + buf.append("\n"); + } } + buf.append("\n"); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionSymbol.java index a5eb542e3e..fda2c4ccad 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionSymbol.java @@ -17,7 +17,9 @@ package ghidra.program.model.pcode; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; +import ghidra.program.model.listing.Function; import ghidra.program.model.listing.VariableStorage; +import ghidra.program.model.symbol.Namespace; import ghidra.util.exception.InvalidInputException; /** @@ -54,11 +56,25 @@ public class HighFunctionSymbol extends HighSymbol { return true; } + @Override + public Namespace getNamespace() { + Function func = function.getFunction(); + Namespace namespc = func.getParentNamespace(); + while (func.isThunk() && namespc.getID() == Namespace.GLOBAL_NAMESPACE_ID) { + // Thunks can be in a different namespace than the thunked function. + // We choose the thunk's namespace unless it is the global namespace + func = func.getThunkedFunction(false); + namespc = func.getParentNamespace(); + } + return namespc; + } + @Override public void saveXML(StringBuilder buf) { MappedEntry entry = (MappedEntry) getFirstWholeMap(); String funcString = - function.buildFunctionXML(getId(), entry.getStorage().getMinAddress(), entry.getSize()); + function.buildFunctionXML(getId(), getNamespace(), entry.getStorage().getMinAddress(), + entry.getSize()); buf.append(funcString); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParamID.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParamID.java index c207104db0..e565cf089a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParamID.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParamID.java @@ -48,23 +48,20 @@ public class HighParamID extends PcodeSyntaxTree { private Integer protoextrapop; private List inputlist = new ArrayList(); private List outputlist = new ArrayList(); - private boolean showNamespace = true; /** * @param function function associated with the higher level function abstraction. * @param language language parser used to disassemble/get info on the language. * @param compilerSpec the compiler spec. * @param dtManager data type manager. - * @param showNamespace true to show the parameters namespace. */ public HighParamID(Function function, Language language, CompilerSpec compilerSpec, - PcodeDataTypeManager dtManager, boolean showNamespace) { + PcodeDataTypeManager dtManager) { super(function.getProgram().getAddressFactory(), dtManager); func = function; modelname = null; protoextrapop = PrototypeModel.UNKNOWN_EXTRAPOP; - this.showNamespace = showNamespace; } /** @@ -137,8 +134,8 @@ public class HighParamID extends PcodeSyntaxTree { public void readXML(XmlPullParser parser) throws PcodeXMLException { XmlElement start = parser.start("parammeasures"); functionname = start.getAttribute("name"); - if (!func.getName(showNamespace).equals(functionname)) { - throw new PcodeXMLException("Function name mismatch: " + func.getName(showNamespace) + + if (!func.getName().equals(functionname)) { + throw new PcodeXMLException("Function name mismatch: " + func.getName() + " + " + functionname); } while (!parser.peek().isEnd()) { @@ -157,10 +154,12 @@ public class HighParamID extends PcodeSyntaxTree { subel = parser.start("proto"); modelname = subel.getAttribute("model"); String val = subel.getAttribute("extrapop"); - if (val.equals("unknown")) + if (val.equals("unknown")) { protoextrapop = PrototypeModel.UNKNOWN_EXTRAPOP; - else + } + else { protoextrapop = SpecXmlUtils.decodeInt(val); + } parser.end(subel); } else if (subel.getName().equals("input")) { @@ -243,23 +242,28 @@ public class HighParamID extends PcodeSyntaxTree { try { //TODO: Currently, only storing one output, so looking for the best to report. When possible, change this to report all int best_index = 0; - if (getNumOutputs() > 1) - for (int i = 1; i < getNumOutputs(); i++) + if (getNumOutputs() > 1) { + for (int i = 1; i < getNumOutputs(); i++) { if (getOutput(i).getRank() < getOutput(best_index).getRank()) {//TODO: create mirror of ranks on high side (instead of using numbers?) best_index = i; } + } + } if (getNumOutputs() != 0) { ParamMeasure pm = getOutput(best_index); pm.getRank(); //TODO (maybe): this value is not used or stored on the java side at this point Varnode vn = pm.getVarnode(); DataType dataType; - if (storeDataTypes) + if (storeDataTypes) { dataType = pm.getDataType(); - else + } + else { dataType = dtManage.findUndefined(vn.getSize()); + } //Msg.debug(this, "func: " + func.getName() + " -- type: " + dataType.getName()); - if (!(dataType == null || dataType instanceof VoidDataType)) + if (!(dataType == null || dataType instanceof VoidDataType)) { func.setReturn(dataType, buildStorage(vn), SourceType.ANALYSIS); + } } } catch (InvalidInputException e) { @@ -282,10 +286,12 @@ public class HighParamID extends PcodeSyntaxTree { DataType dataType; //Msg.debug(this, "function(" + func.getName() + ")--param size: " + vn.getSize() + // "--type before store: " + pm.getDataType().getName()); - if (storeDataTypes) + if (storeDataTypes) { dataType = pm.getDataType(); - else + } + else { dataType = dtManage.findUndefined(vn.getSize()); + } Variable v = new ParameterImpl(null, dataType, buildStorage(vn), func.getProgram()); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java index 1a106184c1..2a3c2b3fd3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java @@ -19,6 +19,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Program; import ghidra.program.model.listing.VariableStorage; +import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Symbol; import ghidra.util.xml.SpecXmlUtils; import ghidra.xml.XmlElement; @@ -130,6 +131,18 @@ public class HighSymbol { return null; } + /** + * Fetch the namespace owning this symbol, if it exists. + * @return the Namespace object or null + */ + public Namespace getNamespace() { + Symbol sym = getSymbol(); + if (sym != null) { + return sym.getParentNamespace(); + } + return null; + } + /** * Associate a particular HighVariable with this symbol. This is used to link the symbol * into the decompiler's description of how a function manipulates a particular symbol. 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 c343a8720f..2c93cecd7b 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 @@ -17,8 +17,7 @@ package ghidra.program.model.pcode; import java.util.*; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressIterator; +import ghidra.program.model.address.*; import ghidra.program.model.data.DataType; import ghidra.program.model.data.Undefined; import ghidra.program.model.listing.*; @@ -184,16 +183,18 @@ public class LocalSymbolMap { if (symbol != null) { id = symbol.getID(); } - Address defAddr = null; - if (!storage.isStackStorage()) { - defAddr = dbFunction.getEntryPoint().addWrap(local.getFirstUseOffset()); - } HighSymbol sym; if (storage.isHashStorage()) { + Address defAddr = dbFunction.getEntryPoint().addWrap(local.getFirstUseOffset()); sym = newDynamicSymbol(id, name, dt, storage.getFirstVarnode().getOffset(), defAddr); } else { + Address defAddr = null; + int addrType = storage.getFirstVarnode().getAddress().getAddressSpace().getType(); + if (addrType != AddressSpace.TYPE_STACK && addrType != AddressSpace.TYPE_RAM) { + defAddr = dbFunction.getEntryPoint().addWrap(local.getFirstUseOffset()); + } sym = newMappedSymbol(id, name, dt, storage, defAddr, -1); } sym.setTypeLock(istypelock); @@ -325,31 +326,29 @@ public class LocalSymbolMap { } /** - * @return an XML document string representing this local variable map. + * Output an XML document representing this local variable map. + * @param resBuf is the buffer to write to + * @param namespace if the namespace of the function */ - public String buildLocalDbXML() { // Get memory mapped local variables - StringBuilder res = new StringBuilder(); - res.append("\n"); - res.append("\n"); - res.append("\n"); - HighFunction.createNamespaceTag(res, func.getFunction().getParentNamespace()); - res.append("\n"); - res.append("\n"); // Empty address range - res.append("\n"); + public void buildLocalDbXML(StringBuilder resBuf, Namespace namespace) { // Get memory mapped local variables + resBuf.append("\n"); + resBuf.append("\n"); + HighFunction.createNamespaceTag(resBuf, namespace, false); + resBuf.append("\n"); // Empty address range + resBuf.append("\n"); Iterator iter = symbolMap.values().iterator(); while (iter.hasNext()) { HighSymbol sym = iter.next(); - HighSymbol.buildMapSymXML(res, sym); + HighSymbol.buildMapSymXML(resBuf, sym); } - res.append("\n"); - res.append("\n"); - res.append("\n"); - return res.toString(); + resBuf.append("\n"); + resBuf.append("\n"); + resBuf.append("\n"); } /**