From e465f9b3d620b47ad367065fc1f0e4329ffa492a Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:59:24 +0000 Subject: [PATCH] GP-6967 Add getScopeDelimiter --- .../Decompiler/src/decompile/cpp/Doxyfile | 2 +- .../src/decompile/cpp/architecture.cc | 7 +- .../src/decompile/cpp/architecture.hh | 3 +- .../src/decompile/cpp/consolemain.cc | 2 +- .../src/decompile/cpp/coreaction.cc | 1 + .../Decompiler/src/decompile/cpp/database.cc | 30 +- .../Decompiler/src/decompile/cpp/database.hh | 4 +- .../Decompiler/src/decompile/cpp/docmain.hh | 6 +- .../src/decompile/cpp/ifacedecomp.cc | 20 +- .../src/decompile/cpp/pcodecompile.cc | 291 +++++++++++--- .../src/decompile/cpp/pcodecompile.hh | 126 +++--- .../Decompiler/src/decompile/cpp/printc.hh | 1 + .../src/decompile/cpp/printlanguage.hh | 1 + .../Decompiler/src/decompile/cpp/semantics.cc | 174 ++++++-- .../Decompiler/src/decompile/cpp/semantics.hh | 371 +++++++++++------- .../src/decompile/cpp/slgh_compile.hh | 4 +- .../Decompiler/src/decompile/cpp/subflow.cc | 4 +- .../src/decompile/cpp/testfunction.cc | 2 +- .../src/decompile/cpp/unionresolve.cc | 4 +- 19 files changed, 740 insertions(+), 313 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/Doxyfile b/Ghidra/Features/Decompiler/src/decompile/cpp/Doxyfile index a6c2c41360..ba5838a572 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/Doxyfile +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/Doxyfile @@ -457,7 +457,7 @@ RECURSIVE = NO # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. -EXCLUDE = unify.hh unify.cc rulecompile.hh rulecompile.cc slghparse.cc slghparse.hh slghscan.cc slghpattern.hh slghpattern.cc slghpatexpress.hh slghpatexpress.cc slghsymbol.hh slghsymbol.cc codedata.hh codedata.cc semantics.hh semantics.cc grammar.hh grammar.cc callgraph.hh callgraph.cc filemanage.hh filemanage.cc graph.hh graph.cc loadimage_bfd.hh loadimage_bfd.cc pcodecompile.cc pcodecompile.hh pcodeparse.hh pcodeparse.cc consolemain.cc sleighexample.cc xml.cc double.hh double.cc paramid.hh paramid.cc prefersplit.hh prefersplit.cc +EXCLUDE = unify.hh unify.cc rulecompile.hh rulecompile.cc slghparse.cc slghparse.hh slghscan.cc slghpattern.hh slghpattern.cc slghpatexpress.hh slghpatexpress.cc slghsymbol.hh slghsymbol.cc codedata.hh codedata.cc grammar.hh grammar.cc callgraph.hh callgraph.cc filemanage.hh filemanage.cc graph.hh graph.cc loadimage_bfd.hh loadimage_bfd.cc pcodeparse.hh pcodeparse.cc consolemain.cc sleighexample.cc xml.cc double.hh double.cc paramid.hh paramid.cc prefersplit.hh prefersplit.cc # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index 97c33cd9f8..eb651b0cde 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -342,8 +342,7 @@ 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 -/// \param delim is the delimiter separating namespaces from symbol base names -void Architecture::readLoaderSymbols(const string &delim) +void Architecture::readLoaderSymbols(void) { if (loadersymbols_parsed) return; // already read @@ -352,7 +351,7 @@ void Architecture::readLoaderSymbols(const string &delim) LoadImageFunc record; while(loader->getNextSymbol(record)) { string basename; - Scope *scope = symboltab->findCreateScopeFromSymbolName(record.name, delim, basename, (Scope *)0); + Scope *scope = symboltab->findCreateScopeFromSymbolName(record.name, basename, (Scope *)0); scope->addFunction(record.address,basename); } loader->closeSymbols(); @@ -393,7 +392,7 @@ void Architecture::setPrototype(const PrototypePieces &pieces) { string basename; - Scope *scope = symboltab->resolveScopeFromSymbolName(pieces.name, "::", basename, (Scope *)0); + Scope *scope = symboltab->resolveScopeFromSymbolName(pieces.name, basename, (Scope *)0); if (scope == (Scope *)0) throw ParseError("Unknown namespace: " + pieces.name); Funcdata *fd = scope->queryFunction( basename ); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index ebd0e84343..859463dc72 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -230,7 +230,7 @@ public: int4 getMinimumLanedRegisterSize(void) const; ///< Get the minimum size of a laned register in bytes void setDefaultModel(ProtoModel *model); ///< Set the default PrototypeModel void clearAnalysis(Funcdata *fd); ///< Clear analysis specific to a function - void readLoaderSymbols(const string &delim); ///< Read any symbols from loader into database + void readLoaderSymbols(void); ///< 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 @@ -251,6 +251,7 @@ public: virtual void encode(Encoder &encoder) const; ///< Encode \b this architecture to a stream virtual void restoreXml(DocumentStorage &store); ///< Restore the Architecture state from XML documents virtual void nameFunction(const Address &addr,string &name) const; ///< Pick a default name for a function + string getScopeDelimiter(void) const { return print->getScopeDelimiter(); } ///< Get the character string separating scope names #ifdef OPACTION_DEBUG void setDebugStream(ostream *s) { debugstream = s; } ///< Establish the debug console stream void printDebug(const string &message) const { *debugstream << message << endl; } ///< Print message to the debug stream diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc index 87c13b8184..eb25d5f412 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc @@ -101,7 +101,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->fileoptr); #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 3512f71359..8e2af7ad46 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -2461,6 +2461,7 @@ bool ActionSetCasts::testStructOffset0(Datatype *reqtype,Datatype *curtype,CastS /// \param dt is the data-type needed by the p-code op /// \param op is the p-code op /// \param slot is the index of the slot to test for resolution (-1 for output, >= 0 for input) +/// \param data is the function /// \return \b true if the resolution was successfully changed and a CAST is not needed bool ActionSetCasts::tryResolutionAdjustment(Datatype *dt,PcodeOp *op,int4 slot,Funcdata &data) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc index 45bc1bc3f7..0d2629940e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc @@ -1451,7 +1451,7 @@ string Scope::getFullName(void) const string fname = name; Scope *scope = parent; while(scope->parent != (Scope *)0) { - fname = scope->name + "::" + fname; + fname = scope->name + glb->getScopeDelimiter() + fname; scope = scope->parent; } return fname; @@ -2778,6 +2778,12 @@ void ScopeInternal::decode(Decoder &decoder) SymbolEntry *e = sym->getFirstWholeMap(); glb->symboltab->addRange(this,e->getAddr().getSpace(),e->getFirst(),e->getLast()); } + uint4 props = sym->getFlags() & (Varnode::readonly | Varnode::volatil); + if (props != 0) { + SymbolEntry *e = sym->getFirstWholeMap(); + Range rng(e->getAddr().getSpace(),e->getFirst(),e->getLast()); + glb->symboltab->setPropertyRange(props,rng); + } } else if (symId == ELEM_HOLE) decodeHole(decoder); @@ -3106,21 +3112,24 @@ Scope *Database::resolveScope(uint8 id) const /// starts with the delimiter, the name is assumed to be relative to the global Scope. /// The unqualified (base) name of the Symbol is passed back to the caller. /// \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::resolveScopeFromSymbolName(const string &fullname,const string &delim,string &basename, - Scope *start) const +Scope *Database::resolveScopeFromSymbolName(const string &fullname,string &basename,Scope *start) const + { if (start == (Scope *)0) start = globalscope; string::size_type mark = 0; string::size_type endmark; + string::size_type templateopen = fullname.find("<"); + string delim = glb->getScopeDelimiter(); for(;;) { endmark = fullname.find(delim,mark); if (endmark == string::npos) break; + if (templateopen != string::npos && endmark > templateopen) + break; if (endmark == 0) { // Path is "absolute" start = globalscope; // Start from the global scope } @@ -3132,7 +3141,7 @@ Scope *Database::resolveScopeFromSymbolName(const string &fullname,const string } mark = endmark + delim.size(); } - basename = fullname.substr(mark,endmark); + basename = fullname.substr(mark); return start; } @@ -3144,21 +3153,24 @@ Scope *Database::resolveScopeFromSymbolName(const string &fullname,const string /// 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) +Scope *Database::findCreateScopeFromSymbolName(const string &fullname,string &basename,Scope *start) + { if (start == (Scope *)0) start = globalscope; string::size_type mark = 0; string::size_type endmark; + string::size_type templateopen = fullname.find("<"); + string delim = glb->getScopeDelimiter(); for(;;) { endmark = fullname.find(delim,mark); if (endmark == string::npos) break; + if (templateopen != string::npos && endmark > templateopen) + break; if (!idByNameHash) throw LowlevelError("Scope name hashes not allowed"); string scopename = fullname.substr(mark,endmark-mark); @@ -3166,7 +3178,7 @@ Scope *Database::findCreateScopeFromSymbolName(const string &fullname,const stri start = findCreateScope(nameId, scopename, start); mark = endmark + delim.size(); } - basename = fullname.substr(mark,endmark); + basename = fullname.substr(mark); return start; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh index dd24c7a331..ce7fc7059d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh @@ -940,9 +940,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(uint8 id) const; ///< Look-up a Scope by id - Scope *resolveScopeFromSymbolName(const string &fullname,const string &delim,string &basename,Scope *start) const; + Scope *resolveScopeFromSymbolName(const string &fullname,string &basename,Scope *start) const; Scope *findCreateScope(uint8,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); + Scope *findCreateScopeFromSymbolName(const string &fullname,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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/docmain.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/docmain.hh index 1e3520b6f5..00ebf8d5e2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/docmain.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/docmain.hh @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,10 +21,8 @@ - \ref capabilities - \ref design - \ref workflow - - \ref ghidraimpl - \subpage sleigh - \subpage coreclasses - - \subpage termrewriting \section overview Overview diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc index e62ab6ab08..fad4bdee65 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc @@ -459,7 +459,7 @@ void IfaceDecompData::followFlow(ostream &s,int4 size) /// \class IfcFuncload /// \brief Make a specific function current: `load function ` /// -/// The name must be a fully qualified symbol with "::" separating namespaces. +/// The name must be a fully qualified symbol with namespaces. /// If the symbol represents a function, that function becomes \e current for /// the console. If there are bytes for the function, raw p-code and control-flow /// are calculated. @@ -475,7 +475,7 @@ void IfcFuncload::execute(istream &s) throw IfaceExecutionError("No image loaded"); string basename; - Scope *funcscope = dcp->conf->symboltab->resolveScopeFromSymbolName(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 @@ -534,7 +534,7 @@ void IfcReadSymbols::execute(istream &s) if (dcp->conf->loader == (LoadImage *)0) throw IfaceExecutionError("No binary loaded"); - dcp->conf->readLoaderSymbols("::"); + dcp->conf->readLoaderSymbols(); } /// \class IfcMapaddress @@ -544,8 +544,8 @@ void IfcReadSymbols::execute(istream &s) /// \code /// map address r0x1000 int4 globalvar /// \endcode -/// The symbol specified in the type declaration can qualify the namespace using the "::" -/// specifier. If there is a current function, the variable is local to the function. +/// The symbol specified in the type declaration can qualify the namespace. +/// If there is a current function, the variable is local to the function. /// Otherwise the symbol is created relative to the global scope. void IfcMapaddress::execute(istream &s) @@ -567,7 +567,7 @@ void IfcMapaddress::execute(istream &s) uint4 flags = Varnode::namelock|Varnode::typelock; flags |= dcp->conf->symboltab->getProperty(addr); // Inherit existing properties string basename; - Scope *scope = dcp->conf->symboltab->findCreateScopeFromSymbolName(name, "::", basename, (Scope *)0); + 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 @@ -670,7 +670,7 @@ void IfcMapfunction::execute(istream &s) if (name.empty()) dcp->conf->nameFunction(addr,name); // Pick default name if necessary string basename; - Scope *scope = dcp->conf->symboltab->findCreateScopeFromSymbolName(name, "::", basename, (Scope *)0); + Scope *scope = dcp->conf->symboltab->findCreateScopeFromSymbolName(name, basename, (Scope *)0); dcp->fd = scope->addFunction(addr,name)->getFunction(); string nocode; @@ -1525,7 +1525,7 @@ void IfaceDecompData::readSymbol(const string &name,vector &res) { Scope *scope = (fd == (Funcdata *)0) ? conf->symboltab->getGlobalScope() : fd->getScopeLocal(); string basename; - scope = conf->symboltab->resolveScopeFromSymbolName(name, "::", basename, scope); + scope = conf->symboltab->resolveScopeFromSymbolName(name, basename, scope); if (scope == (Scope *)0) throw IfaceParseError("Bad namespace for symbol: " + name); scope->queryByName(basename,res); @@ -2339,7 +2339,7 @@ void IfcPrintMap::execute(istream &s) throw IfaceExecutionError("No load image"); 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); + scope = dcp->conf->symboltab->resolveScopeFromSymbolName(fullname, fullname, (Scope *)0); } else scope = dcp->fd->getScopeLocal(); @@ -2987,7 +2987,7 @@ void IfcFixupApply::execute(istream &s) throw IfaceExecutionError("Unknown fixup: " + fixupName); string basename; - Scope *funcscope = dcp->conf->symboltab->resolveScopeFromSymbolName(funcName,"::",basename,(Scope *)0); + Scope *funcscope = dcp->conf->symboltab->resolveScopeFromSymbolName(funcName,basename,(Scope *)0); if (funcscope == (Scope *)0) throw IfaceExecutionError("Bad namespace: "+funcName); Funcdata *fd = funcscope->queryFunction( basename ); // Is function already in database diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc index a67a3de849..de3ec1af20 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc @@ -17,6 +17,7 @@ namespace ghidra { +/// \return the Location as a string string Location::format(void) const { @@ -25,24 +26,14 @@ string Location::format(void) const return s.str(); } - ExprTree::ExprTree(VarnodeTpl *vn) +/// \param vn is the expression root +ExprTree::ExprTree(VarnodeTpl *vn) { outvn = vn; ops = new vector; } -ExprTree::ExprTree(OpTpl *op) - -{ - ops = new vector; - ops->push_back(op); - if (op->getOut() != (VarnodeTpl *)0) - outvn = new VarnodeTpl(*op->getOut()); - else - outvn = (VarnodeTpl *)0; -} - ExprTree::~ExprTree(void) { @@ -55,10 +46,18 @@ ExprTree::~ExprTree(void) } } +/// \brief Create a new \e raw expression by appending an array of VarnodeTpl, as inputs, to a new root OpTpl +/// +/// The OpTpl may already have inputs. The root VarnodeTpl from each ExprTree passed in is appended +/// to the list of input parameters of the OpTpl, creating a single new expression. +/// The original ExprTrees and their container are all destroyed. +/// The new expression is returned, in raw form, as a flattened array of OpTpl. +/// \param op is the new root OpTpl +/// \param param is the list of expressions, each with a root VarnodeTpl +/// \return the new \e raw expression as an array of OpTpl vector *ExprTree::appendParams(OpTpl *op,vector *param) -{ // Create op expression with entire list of expression - // inputs +{ vector *res = new vector; for(int4 i=0;isize();++i) { @@ -73,20 +72,24 @@ vector *ExprTree::appendParams(OpTpl *op,vector *param) return res; } +/// \brief Convert an expression to a raw array of OpTpl +/// +/// The flattened array of OpTpl is stripped from the ExprTree, which is then destroyed. +/// \param expr is the expression to convert to an array +/// \return the raw array of OpTpl vector *ExprTree::toVector(ExprTree *expr) -{ // Grab the op vector and delete the output expression +{ vector *res = expr->ops; expr->ops = (vector *)0; delete expr; return res; } +/// \param newout is the new output being forced void ExprTree::setOutput(VarnodeTpl *newout) -{ // Force the output of the expression to be new out - // If the original output is named, this requires - // an extra COPY op +{ OpTpl *op; if (outvn == (VarnodeTpl *)0) throw SleighError("Expression has no output"); @@ -96,8 +99,8 @@ void ExprTree::setOutput(VarnodeTpl *newout) op->clearOutput(); op->setOutput(newout); } - else { - op = new OpTpl(CPUI_COPY); + else { // If the original output is named + op = new OpTpl(CPUI_COPY); // an extra COPY is required op->addInput(outvn); op->setOutput(newout); ops->push_back(op); @@ -105,6 +108,14 @@ void ExprTree::setOutput(VarnodeTpl *newout) outvn = new VarnodeTpl(*newout); } +/// \brief Force a size on a specific VarnodeTpl if possible +/// +/// If the VarnodeTpl already has a fixed size, this method does nothing. +/// If the VarnodeTpl is a temporary register then all other temporaries with the same offset in the same expression +/// are also adjusted. +/// \param vt is the specific VarnodeTpl to adjust +/// \param size is the size constant to assign +/// \param ops is the array of OpTpl in the expression void PcodeCompile::force_size(VarnodeTpl *vt,const ConstTpl &size,const vector &ops) { @@ -142,11 +153,17 @@ void PcodeCompile::force_size(VarnodeTpl *vt,const ConstTpl &size,const vector &ops) -{ // Find something to fill in zero size varnode - // j is the slot we are trying to fill (-1=output) - // Don't check output for non-zero if inputonly is true +{ VarnodeTpl *match = (VarnodeTpl *)0; VarnodeTpl *vt; int4 i,inputsize; @@ -167,12 +184,15 @@ void PcodeCompile::matchSize(int4 j,OpTpl *op,bool inputonly,const vectorgetSize(),ops); } +/// \brief Try to deduce the size of all input and output VarnodeTpl for the given OpTpl +/// +/// For any VarnodeTpl whose size is not known (isZeroSize() returns \b true), an attempt is +/// made to fill in the size from another input or output of the OpTpl. +/// \param op is the given OpTpl +/// \param ops is the complete set of OpTpl in the expression/constructor void PcodeCompile::fillinZero(OpTpl *op,const vector &ops) -{ // Try to get rid of zero size varnodes in op - // Right now this is written assuming operands for the constructor are - // are built before any other pcode in the constructor is generated - +{ int4 inputsize,i; switch(op->getOpcode()) { @@ -262,11 +282,15 @@ void PcodeCompile::fillinZero(OpTpl *op,const vector &ops) } } +/// \brief Propagate a size to all VarnodeTpl whose size is unknown +/// +/// Size information is propagated across operations and expressions as far as possible. +/// If there are any remaining VarnodeTpl whose size is unknown, return \b false. +/// \param ct is the set of p-code operations to propagate across +/// \return \b true if all VarnodeTpl have a known size bool PcodeCompile::propagateSize(ConstructTpl *ct) -{ // Fill in size for varnodes with size 0 - // Return first OpTpl with a size 0 varnode - // that cannot be filled in or NULL otherwise +{ vector zerovec,zerovec2; vector::const_iterator iter; int4 lastsize; @@ -292,9 +316,11 @@ bool PcodeCompile::propagateSize(ConstructTpl *ct) return true; } +/// Space for the register is allocated in the \e unique address space. +/// \return the new temporary register VarnodeTpl *PcodeCompile::buildTemporary(void) -{ // Build temporary variable (with zerosize) +{ VarnodeTpl *res = new VarnodeTpl(ConstTpl(uniqspace), ConstTpl(ConstTpl::real,allocateTemp()), ConstTpl(ConstTpl::real,0)); @@ -302,18 +328,24 @@ VarnodeTpl *PcodeCompile::buildTemporary(void) return res; } +/// The parsed name and the next available id are assigned to the label. The symbol is assigned to the current scope. +/// \param name is the parsed name of the label +/// \return the new symbol LabelSymbol *PcodeCompile::defineLabel(string *name) -{ // Create a label symbol +{ LabelSymbol *labsym = new LabelSymbol(*name,local_labelcount++); delete name; addSymbol(labsym); // Add symbol to local scope return labsym; } +/// A placeholder OpTpl for the label is created and returned, as an array. +/// \param labsym is the given label +/// \return an array containing the new OpTpl vector *PcodeCompile::placeLabel(LabelSymbol *labsym) -{ // Create placeholder OpTpl for a label +{ if (labsym->isPlaced()) { reportError(getLocation(labsym), "Label '" + labsym->getName() + "' is placed more than once"); } @@ -328,6 +360,13 @@ vector *PcodeCompile::placeLabel(LabelSymbol *labsym) return res; } +/// A new named temporary register is created and assigned as the root of the given expression. +/// A symbol representing the register is added to the current scope. +/// \param usesLocalKey is \b true if the name was defined with the 'local' keyword +/// \param rhs is the given expression +/// \param varname is the parsed symbol name to associate with the new register +/// \param size is non-zero if an explicit size was provided in the parsed definition +/// \return the new expression as a raw array of OpTpl vector *PcodeCompile::newOutput(bool usesLocalKey,ExprTree *rhs,string *varname,uint4 size) { @@ -348,20 +387,27 @@ vector *PcodeCompile::newOutput(bool usesLocalKey,ExprTree *rhs,string return ExprTree::toVector(rhs); } +/// A temporary register is allocated, and the symbol is added to the current scope. +/// \param varname is the parsed symbol name +/// \param size is the size of the new register (0 indicates the size is initially unknown) void PcodeCompile::newLocalDefinition(string *varname,uint4 size) -{ // Create a new temporary symbol (without generating any pcode) +{ VarnodeSymbol *sym; sym = new VarnodeSymbol(*varname,uniqspace,allocateTemp(),size); addSymbol(sym); delete varname; } +/// A new expression is created ending in a unary operation. +/// The input to the operation is the root VarnodeTpl of the given expression, +/// and the output is a new temporary register. +/// \param opc is the unary operation code +/// \param vn is the given input expression +/// \return the new expression ExprTree *PcodeCompile::createOp(OpCode opc,ExprTree *vn) -{ // Create new expression with output -outvn- - // built by performing -opc- on input vn. - // Free input expression +{ VarnodeTpl *outvn = buildTemporary(); OpTpl *op = new OpTpl(opc); op->addInput(vn->outvn); @@ -371,12 +417,16 @@ ExprTree *PcodeCompile::createOp(OpCode opc,ExprTree *vn) return vn; } -ExprTree *PcodeCompile::createOp(OpCode opc,ExprTree *vn1, - ExprTree *vn2) +/// A new expression is created ending in a binary operation. +/// Inputs to the operation are the root VarnodeTpl from the two given expressions, +/// and the output is a new temporary register. +/// \param opc is the binary operation code +/// \param vn1 is the first input expression +/// \param vn2 is the second input expression +/// \return the new combined expression +ExprTree *PcodeCompile::createOp(OpCode opc,ExprTree *vn1,ExprTree *vn2) -{ // Create new expression with output -outvn- - // built by performing -opc- on inputs vn1 and vn2. - // Free input expressions +{ VarnodeTpl *outvn = buildTemporary(); vn1->ops->insert(vn1->ops->end(),vn2->ops->begin(),vn2->ops->end()); vn2->ops->clear(); @@ -391,9 +441,18 @@ ExprTree *PcodeCompile::createOp(OpCode opc,ExprTree *vn1, return vn1; } +/// \brief Create a new binary operation combining the given input expressions and an explicit output VarnodeTpl +/// +/// A new expression is created ending in a binary operation. +/// Inputs are the root VarnodeTpl from the two given expressions. +/// \param outvn is the explicit output of the new operation +/// \param opc is the binary operation code +/// \param vn1 is the first input expression +/// \param vn2 is the second input expression +/// \return the new combined expression ExprTree *PcodeCompile::createOpOut(VarnodeTpl *outvn,OpCode opc, ExprTree *vn1,ExprTree *vn2) -{ // Create an op with explicit output and two inputs +{ vn1->ops->insert(vn1->ops->end(),vn2->ops->begin(),vn2->ops->end()); vn2->ops->clear(); OpTpl *op = new OpTpl(opc); @@ -407,9 +466,17 @@ ExprTree *PcodeCompile::createOpOut(VarnodeTpl *outvn,OpCode opc, return vn1; } +/// \brief Create a new unary operation with the given expression as input and an explicit output VarnodeTpl +/// +/// A new expression is created ending in a unary operation. +/// The input to the operation is the root VarnodeTpl of the given expression. +/// \param outvn is the explicit output of the new operation +/// \param opc is the unary operation code +/// \param vn is the input expression +/// \return the new combined expression ExprTree *PcodeCompile::createOpOutUnary(VarnodeTpl *outvn,OpCode opc,ExprTree *vn) -{ // Create an op with explicit output and 1 input +{ OpTpl *op = new OpTpl(opc); op->addInput(vn->outvn); op->setOutput(outvn); @@ -418,10 +485,16 @@ ExprTree *PcodeCompile::createOpOutUnary(VarnodeTpl *outvn,OpCode opc,ExprTree * return vn; } +/// \brief Create a new unary operation with the given expression as input and no output +/// +/// A new expression is created ending in a unary operation. +/// The input to the operation is the root VarnodeTpl of the given expression. +/// \param opc is the unary operation code +/// \param vn is the input expression +/// \return the new combined expression as raw array vector *PcodeCompile::createOpNoOut(OpCode opc,ExprTree *vn) -{ // Create new expression by creating op with given -opc- - // and single input vn. Free the input expression +{ OpTpl *op = new OpTpl(opc); op->addInput(vn->outvn); vn->outvn = (VarnodeTpl *)0; // There is no longer an output to this expression @@ -432,10 +505,17 @@ vector *PcodeCompile::createOpNoOut(OpCode opc,ExprTree *vn) return res; } +/// \brief Create a new binary operation with the given expressions as input and no output +/// +/// A new expression is created ending in a binary operation. +/// Inputs are the root VarnodeTpl of the given expressions. +/// \param opc is the binary operation code +/// \param vn1 is the first input expression +/// \param vn2 is the second input expression +/// \return the new combined expression as a raw array vector *PcodeCompile::createOpNoOut(OpCode opc,ExprTree *vn1,ExprTree *vn2) -{ // Create new expression by creating op with given -opc- - // and inputs vn1 and vn2. Free the input expressions +{ vector *res = vn1->ops; vn1->ops = (vector *)0; res->insert(res->end(),vn2->ops->begin(),vn2->ops->end()); @@ -451,6 +531,13 @@ vector *PcodeCompile::createOpNoOut(OpCode opc,ExprTree *vn1,ExprTree * return res; } +/// \brief Create a new unary operation with the given constant as input and no output. +/// +/// A new expression is created ending in a unary operation. +/// A new constant VarnodeTpl is created as input. +/// \param opc is the unary operation code +/// \param val is the constant value of the input +/// \return the new expression as a raw array vector *PcodeCompile::createOpConst(OpCode opc,uintb val) { @@ -464,9 +551,17 @@ vector *PcodeCompile::createOpConst(OpCode opc,uintb val) return res; } +/// \brief Create a new LOAD operation with the given pointer expression as input +/// +/// A new expression is created ending in a LOAD operation. +/// The input pointer is the root VarnodeTpl of the given expression. +/// The output is a new temporary register. +/// \param qual provides the address space and any knowledge of the size being LOADed +/// \param ptr is the given pointer expression +/// \return the new expression ExprTree *PcodeCompile::createLoad(StarQuality *qual,ExprTree *ptr) -{ // Create new load expression, free ptr expression +{ VarnodeTpl *outvn = buildTemporary(); OpTpl *op = new OpTpl(CPUI_LOAD); // The first varnode input to the load is a constant reference to the AddrSpace being loaded @@ -487,8 +582,16 @@ ExprTree *PcodeCompile::createLoad(StarQuality *qual,ExprTree *ptr) return ptr; } -vector *PcodeCompile::createStore(StarQuality *qual, - ExprTree *ptr,ExprTree *val) +/// \brief Create a new STORE operation with the given expressions as inputs +/// +/// A new expression is created ending in a STORE operation. +/// The inputs are the root VarnodeTpl of the given expressions. +/// \param qual provides the address space and any knowledge of the size being STOREed +/// \param ptr is the pointer expression +/// \param val is the value being STOREd +/// \return the new expression as a raw array +vector *PcodeCompile::createStore(StarQuality *qual,ExprTree *ptr,ExprTree *val) + { vector *res = ptr->ops; ptr->ops = (vector *)0; @@ -515,9 +618,17 @@ vector *PcodeCompile::createStore(StarQuality *qual, return res; } +/// \brief Create a CALLOTHER p-code op with temporary output, given a symbol and parameter expressions +/// +/// A new expression is created ending in a CALLOTHER operation. +/// Inputs are the root VarnodeTpl from the given array of expressions. +/// The output is a new temporary register. +/// \param sym is the symbol to associate with CALLOTHER +/// \param param is the array of input expressions +/// \return the new combined expression ExprTree *PcodeCompile::createUserOp(UserOpSymbol *sym,vector *param) -{ // Create userdefined pcode op, given symbol and parameters +{ VarnodeTpl *outvn = buildTemporary(); ExprTree *res = new ExprTree(); res->ops = createUserOpNoOut(sym,param); @@ -526,6 +637,13 @@ ExprTree *PcodeCompile::createUserOp(UserOpSymbol *sym,vector *param return res; } +/// \brief Create a CALLOTHER p-code op, given a symbol and parameter expressions +/// +/// A new expression is created ending in a CALLOTHER operation, with no output. +/// Inputs are the root VarnodeTpl from the given array of expressions. +/// \param sym is the symbol to associate with the CALLOTHER +/// \param param is the array of input expressions +/// \return the new combined expression as a raw array vector *PcodeCompile::createUserOpNoOut(UserOpSymbol *sym,vector *param) { @@ -537,6 +655,14 @@ vector *PcodeCompile::createUserOpNoOut(UserOpSymbol *sym,vector *param) { @@ -549,10 +675,18 @@ ExprTree *PcodeCompile::createVariadic(OpCode opc,vector *param) return res; } +/// \brief Append a binary operation to the given expression. +/// +/// A new operation is appended to the end of the expression. +/// The root VarnodeTpl of the expression becomes the first input to the operation. +/// The given constant value and size becomes the second input. +/// \param opc is the binary operation code +/// \param res is the expression being modified and the first input +/// \param constval is the second input value +/// \param constsz is the second input size void PcodeCompile::appendOp(OpCode opc,ExprTree *res,uintb constval,int4 constsz) -{ // Take output of res expression, combine with constant, - // using opc operation, return the resulting expression +{ OpTpl *op = new OpTpl(opc); VarnodeTpl *constvn = new VarnodeTpl(ConstTpl(constantspace), ConstTpl(ConstTpl::real,constval), @@ -565,10 +699,19 @@ void PcodeCompile::appendOp(OpCode opc,ExprTree *res,uintb constval,int4 constsz res->outvn = new VarnodeTpl(*outvn); } +/// \brief Build a truncated VarnodeTpl, if possible, from the given bit range +/// +/// The bit range must be on byte boundaries or NULL is returned. +/// The VarnodeTpl being must already have fixed dimensions or be a \b handle. +/// For a \b handle, the truncation is built using ConstTpl::v_offset_plus mechanics, allowing the +/// truncation for exported values to be computed in the context of a specific instruction. +/// \param basevn is the VarnodeTpl to be truncated +/// \param bitoffset is the starting bit of the range (0 indicates the least significant bit) +/// \param numbits is the number of bits in the range +/// \return the truncated VarnodeTpl (or NULL) VarnodeTpl *PcodeCompile::buildTruncatedVarnode(VarnodeTpl *basevn,uint4 bitoffset,uint4 numbits) -{ // Build a truncated form -basevn- that matches the bitrange [ -bitoffset-, -numbits- ] if possible - // using just ConstTpl mechanics, otherwise return null +{ uint4 byteoffset = bitoffset / 8; // Convert to byte units uint4 numbytes = numbits / 8; uintb fullsz = 0; @@ -609,9 +752,20 @@ VarnodeTpl *PcodeCompile::buildTruncatedVarnode(VarnodeTpl *basevn,uint4 bitoffs return res; } +/// \brief Assign an expression to a bit range within a given VarnodeTpl +/// +/// Other bits of the VarnodeTpl are preserved. The value assigned is taken from the root VarnodeTpl +/// of the expression, which is assumed to have zero bits in any position greater than or equal to \b numbits. +/// If the bit range falls on byte boundaries, the assignment is accomplished with byte based truncation operations. +/// Otherwise mask and shift operations (INT_AND, INT_OR, and INT_LEFT) are used. +/// \param vn is the given VarnodeTpl being assigned to +/// \param bitoffset is the starting bit of the range (0 indicates the least significant bit) +/// \param numbits is the number of bits in the range +/// \param rhs is the expression being assigned +/// \return a new combined expression as a raw array vector *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uint4 numbits,ExprTree *rhs) -{ // Create an expression assigning the rhs to a bitrange within sym +{ string errmsg; if (numbits == 0) errmsg = "Size of bitrange is zero"; @@ -673,12 +827,19 @@ vector *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uin return resops; } +/// \brief Create an expression computing the indicated bit range of a symbol +/// +/// The result is truncated to the smallest byte size that can contain the indicated number of bits, +/// with the desired bits shifted into the least significant positions. +/// If the bit range is on byte boundaries, the truncation is accomplished with byte based truncation operations. +/// Otherwise masks and shifts (INT_AND and INT_RIGHT) are used. +/// \param sym is the symbol representing the VarnodeTpl to truncate +/// \param bitoffset is the starting bit of the range (0 indicates the least significant bit) +/// \param numbits is the number of bits in the range +/// \return a new expression whose root VarnodeTpl holds the result ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4 numbits) -{ // Create an expression computing the indicated bitrange of sym - // The result is truncated to the smallest byte size that can - // contain the indicated number of bits. The result has the - // desired bits shifted all the way to the right +{ string errmsg; if (numbits == 0) errmsg = "Size of bitrange is zero"; @@ -754,10 +915,14 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4 return res; } +/// \brief Produce a constant VarnodeTpl that is the offset of the storage address of the given VarnodeTpl +/// +/// \param var is the given VarnodeTpl to take the address of +/// \param size is the size of the resulting pointer constant (may be 0) +/// \return the new constant VarnodeTpl VarnodeTpl *PcodeCompile::addressOf(VarnodeTpl *var,uint4 size) -{ // Produce constant varnode that is the offset - // portion of varnode -var- +{ if (size==0) { // If no size specified if (var->getSpace().getType() == ConstTpl::spaceid) { AddrSpace *spc = var->getSpace().getSpace(); // Look to the particular space diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.hh index 3caa57c69f..54e1c78276 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.hh @@ -4,15 +4,18 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ +/// \file pcodecompile.hh +/// \brief Classes for compiling p-code expressions from SLEIGH or other text specifications + #ifndef __PCODECOMPILE_HH__ #define __PCODECOMPILE_HH__ @@ -20,69 +23,104 @@ namespace ghidra { +/// \brief A location within a specification being parsed class Location { - string filename; - int4 lineno; + string filename; ///< Base name of the file being parsed + int4 lineno; ///< Line number within the file public: - Location(void) {} - Location(const string &fname, const int4 line) { filename = fname; lineno = line; } - string getFilename(void) const { return filename; } - int4 getLineno(void) const { return lineno; } - string format(void) const; + Location(void) {} ///< Construct an uninitialized location + Location(const string &fname, const int4 line) { filename = fname; lineno = line; } ///< Constructor + string getFilename(void) const { return filename; } ///< Get the base file name + int4 getLineno(void) const { return lineno; } ///< Get the line number + string format(void) const; ///< Return a text representation of \b this }; +/// \brief The address space and pointer size associated with a SLEIGH '*' operator struct StarQuality { - ConstTpl id; - uint4 size; + ConstTpl id; ///< The id of the address space + uint4 size; ///< Number of bytes being LOADed or STOREd }; -class ExprTree { // A flattened expression tree +/// \brief A p-code expression tree +/// +/// A single connected tree of OpTpl and VarnodeTpl nodes, with either a root VarnodeTpl or a root OpTpl with no output. +/// The OpTpl are stored in the flattened order the will be emitted as. +class ExprTree { friend class PcodeCompile; - vector *ops; // flattened ops making up the expression - VarnodeTpl *outvn; // Output varnode of the expression - // If the last op has an output, -outvn- is - // a COPY of that varnode + vector *ops; ///< Flattened ops making up the expression + VarnodeTpl *outvn; ///< Root VarnodeTpl: Copy of the output from the final OpTpl (or NULL) public: - ExprTree(void) { ops = (vector *)0; outvn = (VarnodeTpl *)0; } - ExprTree(VarnodeTpl *vn); - ExprTree(OpTpl *op); - ~ExprTree(void); - void setOutput(VarnodeTpl *newout); - VarnodeTpl *getOut(void) { return outvn; } - const ConstTpl &getSize(void) const { return outvn->getSize(); } + ExprTree(void) { ops = (vector *)0; outvn = (VarnodeTpl *)0; } ///< Construct an empty expression + ExprTree(VarnodeTpl *vn); ///< Construct an expression with a single VarnodeTpl + ~ExprTree(void); ///< Destructor + void setOutput(VarnodeTpl *newout); ///< Force the output of the expression to be a new VarnodeTpl + VarnodeTpl *getOut(void) { return outvn; } ///< Get the root VarnodeTpl + const ConstTpl &getSize(void) const { return outvn->getSize(); } ///< Get the size of the root VarnodeTpl static vector *appendParams(OpTpl *op,vector *param); static vector *toVector(ExprTree *expr); }; +/// \brief The base class for compiling p-code expressions from SLEIGH syntax +/// +/// This is essentially a utility class for building OpTpl, VarnodeTpl, and their associated expressions. +/// Address spaces must be provided via the setXXXSpace() methods, so that the class can model the +/// corresponding SLEIGH concepts. class PcodeCompile { - AddrSpace *defaultspace; - AddrSpace *constantspace; - AddrSpace *uniqspace; - uint4 local_labelcount; // Number of labels in current constructor - bool enforceLocalKey; // Force slaspec to use 'local' keyword when defining temporary varnodes + AddrSpace *defaultspace; ///< SLEIGH's \e default address space + AddrSpace *constantspace; ///< SLEIGH's \e constant address space + AddrSpace *uniqspace; ///< SLEIGH's \e unique address space + uint4 local_labelcount; ///< Number of labels in current constructor + bool enforceLocalKey; ///< If \b true, force specification to use 'local' keyword when defining temporary varnodes + + /// \brief Get the offset for the next available temporary register + /// + /// A fixed number of bytes in the \e unique space is consumed. + /// \return the reserved offset virtual uint4 allocateTemp(void)=0; + + /// \brief Add a new symbol to the current scope + /// + /// \param sym is the symbol being added virtual void addSymbol(SleighSymbol *sym)=0; public: PcodeCompile(void) { defaultspace=(AddrSpace *)0; constantspace=(AddrSpace *)0; - uniqspace=(AddrSpace *)0; local_labelcount=0; enforceLocalKey=false; } - virtual ~PcodeCompile(void) {} + uniqspace=(AddrSpace *)0; local_labelcount=0; enforceLocalKey=false; } ///< Constructor + virtual ~PcodeCompile(void) {} ///< Destructor + + /// \brief Get the parse location associated with the given symbol + /// + /// This is generally where the symbol is first defined. + /// \param sym is the given symbol + /// \return the location if known, or NULL otherwise virtual const Location *getLocation(SleighSymbol *sym) const=0; + + /// \brief Report a fatal error in parsing + /// + /// Parsing may continue, but a final output is not produced. + /// \param loc is the location of the error + /// \param msg is a description of the error virtual void reportError(const Location *loc, const string &msg)=0; + + /// \brief Report a non-fatal issue encountered during parsing + /// + /// \param loc is the location of the issue + /// \param msg is a description of the issue virtual void reportWarning(const Location *loc, const string &msg)=0; - void resetLabelCount(void) { local_labelcount=0; } - void setDefaultSpace(AddrSpace *spc) { defaultspace = spc; } - void setConstantSpace(AddrSpace *spc) { constantspace = spc; } - void setUniqueSpace(AddrSpace *spc) { uniqspace = spc; } - void setEnforceLocalKey(bool val) { enforceLocalKey = val; } - AddrSpace *getDefaultSpace(void) const { return defaultspace; } - AddrSpace *getConstantSpace(void) const { return constantspace; } - VarnodeTpl *buildTemporary(void); - LabelSymbol *defineLabel(string *name); - vector *placeLabel(LabelSymbol *sym); - vector *newOutput(bool usesLocalKey,ExprTree *rhs,string *varname,uint4 size=0); - void newLocalDefinition(string *varname,uint4 size=0); - ExprTree *createOp(OpCode opc,ExprTree *vn); - ExprTree *createOp(OpCode opc,ExprTree *vn1,ExprTree *vn2); + + void resetLabelCount(void) { local_labelcount=0; } ///< Reset labels. The next label generated will have id 0 + void setDefaultSpace(AddrSpace *spc) { defaultspace = spc; } ///< Set the SLEIGH \e default address space + void setConstantSpace(AddrSpace *spc) { constantspace = spc; } ///< Set the SLEIGH \e constant address space + void setUniqueSpace(AddrSpace *spc) { uniqspace = spc; } ///< Set the SLEIGH \e unique address space + void setEnforceLocalKey(bool val) { enforceLocalKey = val; } ///< Toggle whether use of the 'local' key is enforced + AddrSpace *getDefaultSpace(void) const { return defaultspace; } ///< Get the \e default address space + AddrSpace *getConstantSpace(void) const { return constantspace; } ///< Get the \e constant address space + VarnodeTpl *buildTemporary(void); ///< Create a temporary register (with 0 size) + LabelSymbol *defineLabel(string *name); ///< Create a SLEIGH label symbol + vector *placeLabel(LabelSymbol *sym); ///< Create a \e raw expression containing the given label + vector *newOutput(bool usesLocalKey,ExprTree *rhs,string *varname,uint4 size=0); ///< Create a new output VarnodeTpl for an expression + void newLocalDefinition(string *varname,uint4 size=0); ///< Create a new temporary symbol (without generating any p-code) + ExprTree *createOp(OpCode opc,ExprTree *vn); ///< Add a new unary operation to the given expression + ExprTree *createOp(OpCode opc,ExprTree *vn1,ExprTree *vn2); ///< Create a new binary operation combining the given expressions ExprTree *createOpOut(VarnodeTpl *outvn,OpCode opc,ExprTree *vn1,ExprTree *vn2); ExprTree *createOpOutUnary(VarnodeTpl *outvn,OpCode opc,ExprTree *vn); vector *createOpNoOut(OpCode opc,ExprTree *vn); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh index fb0d2fd0fb..4263d31e40 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh @@ -346,6 +346,7 @@ public: virtual void opSpullOp(const PcodeOp *op); virtual void opPopcountOp(const PcodeOp *op) { opFunc(op); } virtual void opLzcountOp(const PcodeOp *op) { opFunc(op); } + virtual string getScopeDelimiter(void) const { return scope.print1; } }; /// \brief Set of print commands for displaying an open brace '{' and setting a new indent level diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh index 222ac655b4..dab66c71a2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh @@ -602,6 +602,7 @@ public: virtual void opLzcountOp(const PcodeOp *op)=0; ///< Emit a LZCOUNT operator virtual void opSpullOp(const PcodeOp *op)=0; ///< Emit an SPULL operator virtual string unnamedField(int4 off,int4 size); ///< Generate an artificial field name + virtual string getScopeDelimiter(void) const=0; ///< Return the character string used to separate scope names static int4 mostNaturalBase(uintb val); ///< Determine the most natural base for an integer static void formatBinary(ostream &s,uintb val); ///< Print a number in binary form diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc index 7b06da8e0b..c729073918 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc @@ -18,30 +18,40 @@ namespace ghidra { +/// \param tp is the specialized constant type: \b j_start, \b j_next, \b j_next2, \b j_flowdest, \b j_curspace, etc. ConstTpl::ConstTpl(const_type tp) -{ // Constructor for relative jump constants and uniques +{ type = tp; } +/// \param tp is constant type: either \b real or \b j_relative +/// \param val is the constant value ConstTpl::ConstTpl(const_type tp,uintb val) -{ // Constructor for real constants +{ type = tp; value_real = val; value.handle_index = 0; select = v_space; } +/// \param tp is the constant type: must be \b handle +/// \param ht is the index of the sub-constructor computing the \b handle value +/// \param vf is the part (space, offset, or size) of the computed value to encode as the constant ConstTpl::ConstTpl(const_type tp,int4 ht,v_field vf) -{ // Constructor for handle constant +{ type = handle; value.handle_index = ht; select = vf; value_real = 0; } +/// \param tp is the constant type: must be \b handle +/// \param ht is the index of the sub-constructor computing the \b handle value +/// \param vf is the part of the computed value to encode: must be \b v_offset_plus +/// \param plus is the additional constant value to add to the \b handle offset ConstTpl::ConstTpl(const_type tp,int4 ht,v_field vf,uintb plus) { @@ -51,6 +61,7 @@ ConstTpl::ConstTpl(const_type tp,int4 ht,v_field vf,uintb plus) value_real = plus; } +/// \param sid is the address space to encode ConstTpl::ConstTpl(AddrSpace *sid) { @@ -74,6 +85,9 @@ bool ConstTpl::isUniqueSpace(void) const return false; } +/// The constants must be equal in both value and type. +/// \param op2 is the constant to compare with \b this +/// \return \b true if \b this is equal to \b op2, \b false otherwise bool ConstTpl::operator==(const ConstTpl &op2) const { @@ -93,6 +107,8 @@ bool ConstTpl::operator==(const ConstTpl &op2) const return true; } +/// \param op2 is the constant to compare with \b this +/// \return \b true if \b this should be ordered before \b op2 bool ConstTpl::operator<(const ConstTpl &op2) const { @@ -113,11 +129,13 @@ bool ConstTpl::operator<(const ConstTpl &op2) const return false; } +/// If this is a \b handle associated with a dynamically computed value, this method returns +/// the properties of the temporary storage used to hold the computed value. +/// \param walker is the context for the parse of a single instruction +/// \return the computed constant value uintb ConstTpl::fix(const ParserWalker &walker) const -{ // Get the value of the ConstTpl in context - // NOTE: if the property is dynamic this returns the property - // of the temporary storage +{ switch(type) { case j_start: return walker.getAddr().getOffset(); // Fill in starting address placeholder with real address @@ -208,9 +226,13 @@ AddrSpace *ConstTpl::fixSpace(const ParserWalker &walker) const throw LowlevelError("ConstTpl is not a spaceid as expected"); } +/// If \b this represents an address space, including if \b this is the address space +/// piece of a \b handle, replace the address space portion of the FixedHandle with \b this. +/// \param hand is the FixedHandle to replace +/// \param walker is the current context void ConstTpl::fillinSpace(FixedHandle &hand,const ParserWalker &walker) const -{ // Fill in the space portion of a FixedHandle, base on this ConstTpl +{ switch(type) { case j_curspace: hand.space = walker.getCurSpace(); @@ -236,9 +258,14 @@ void ConstTpl::fillinSpace(FixedHandle &hand,const ParserWalker &walker) const throw LowlevelError("ConstTpl is not a spaceid as expected"); } +/// First, \b this is \e fixed, based on the current context. Then the final fixed +/// value is copied into the FixedHandle. If \b this itself is a \b handle, the +/// entire fixed handle is copied into the FixedHandle. +/// \param hand is the FixedHandle to copy into +/// \param walker is the context to fix against void ConstTpl::fillinOffset(FixedHandle &hand,const ParserWalker &walker) const -{ // Fillin the offset portion of a FixedHandle, based on this ConstTpl +{ // If the offset value is dynamic, indicate this in the handle // we don't just fill in the temporary variable offset // we assume hand.space is already filled in @@ -256,9 +283,12 @@ void ConstTpl::fillinOffset(FixedHandle &hand,const ParserWalker &walker) const } } +/// If \b this is not a \b handle, do nothing. Otherwise, copy the details of the indexed HandleTpl into \b this. +/// Copy the piece of the HandleTpl specified by \b select. +/// \param params is the array of HandleTpl void ConstTpl::transfer(const vector ¶ms) -{ // Replace old handles with new handles +{ if (type != handle) return; HandleTpl *newhandle = params[value.handle_index]; @@ -290,6 +320,9 @@ void ConstTpl::transfer(const vector ¶ms) } } +/// This is used to help reorder sub-constructors. +/// If \b this is a \b handle, its index is translated by looking up a new value in the given array of indices. +/// \param handmap is the given array of new \b handle indices void ConstTpl::changeHandleIndex(const vector &handmap) { @@ -297,6 +330,7 @@ void ConstTpl::changeHandleIndex(const vector &handmap) value.handle_index = handmap[value.handle_index]; } +/// \param encoder is the output stream void ConstTpl::encode(Encoder &encoder) const { @@ -363,6 +397,7 @@ void ConstTpl::encode(Encoder &encoder) const } } +/// \param decoder is the input stream void ConstTpl::decode(Decoder &decoder) { @@ -422,15 +457,19 @@ void ConstTpl::decode(Decoder &decoder) decoder.closeElement(el); } +/// \param hand is the index of the sub-constructor computing the \b handle value +/// \param zerosize is \b true if the size of \b this should be disassociated from the computed \b handle and forced to zero VarnodeTpl::VarnodeTpl(int4 hand,bool zerosize) : space(ConstTpl::handle,hand,ConstTpl::v_space), offset(ConstTpl::handle,hand,ConstTpl::v_offset), size(ConstTpl::handle,hand,ConstTpl::v_size) -{ // Varnode built from a handle - // if zerosize is true, set the size constant to zero +{ if (zerosize) - size = ConstTpl(ConstTpl::real,0); + size = ConstTpl(ConstTpl::real,0); // if zerosize is true, set the size constant to zero unnamed_flag = false; } +/// \param sp represents the address space +/// \param off represents the offset into the address space +/// \param sz represents the number of bytes VarnodeTpl::VarnodeTpl(const ConstTpl &sp,const ConstTpl &off,const ConstTpl &sz) : space(sp), offset(off), size(sz) @@ -438,9 +477,10 @@ VarnodeTpl::VarnodeTpl(const ConstTpl &sp,const ConstTpl &off,const ConstTpl &sz unnamed_flag = false; } +/// \param vn is the VarnodeTpl to copy VarnodeTpl::VarnodeTpl(const VarnodeTpl &vn) : space(vn.space), offset(vn.offset), size(vn.size) -{ // A clone of the VarnodeTpl +{ unnamed_flag = vn.unnamed_flag; } @@ -452,6 +492,10 @@ bool VarnodeTpl::isLocalTemp(void) const return true; } +/// If the offset is computed by a sub-constructor using a p-code LOAD into a temporary register, +/// then return \b true. If the sub-constructor does not use a LOAD, or \b this is not a \b handle, return \b false. +/// \param walker is the instruction context (used to look-up the specific sub-constructor) +/// \return \b true if \b this is a dynamic value computed by a sub-constructor bool VarnodeTpl::isDynamic(const ParserWalker &walker) const { @@ -463,6 +507,10 @@ bool VarnodeTpl::isDynamic(const ParserWalker &walker) const return (hand.offset_space != (AddrSpace *)0); } +/// For any piece of \b this (address space,offset,size) that is a handle, copy the HandleTpl with matching index. +/// If \b this needs an additional constant added to its final offset piece, return that constant. +/// \param params is the given array of HandleTpl to match against +/// \return any additional constant that still needs to be added in, or -1 otherwise int4 VarnodeTpl::transfer(const vector ¶ms) { @@ -486,6 +534,10 @@ int4 VarnodeTpl::transfer(const vector ¶ms) return -1; } +/// This is used to help reorder sub-constructors. +/// For each piece of \b this, if it is a \b handle, its index is translated by looking up a new value in the +/// given array of indices. +/// \param handmap is the given array of new \b handle indices void VarnodeTpl::changeHandleIndex(const vector &handmap) { @@ -494,11 +546,15 @@ void VarnodeTpl::changeHandleIndex(const vector &handmap) size.changeHandleIndex(handmap); } +/// The offset piece must be \b v_offset_plus, indicating \b this is truncated. +/// Compute the final form of the truncation given the final size and endianness. +/// Also check that the truncation is in bounds for the given final size. +/// \param sz is the final size of the Varnode in bytes +/// \param isbigendian is \b true if the address space is big endian. +/// \return \b true if the truncation is in bounds bool VarnodeTpl::adjustTruncation(int4 sz,bool isbigendian) -{ // We know this->offset is an offset_plus, check that the truncation is in bounds (given -sz-) - // adjust plus for endianness if necessary - // return true if truncation is in bounds +{ if (size.getType() != ConstTpl::real) return false; int4 numbytes = (int4) size.getReal(); @@ -520,6 +576,7 @@ bool VarnodeTpl::adjustTruncation(int4 sz,bool isbigendian) return true; } +/// \param encoder is the output stream void VarnodeTpl::encode(Encoder &encoder) const { @@ -530,6 +587,7 @@ void VarnodeTpl::encode(Encoder &encoder) const encoder.closeElement(sla::ELEM_VARNODE_TPL); } +/// \param decoder is the input stream void VarnodeTpl::decode(Decoder &decoder) { @@ -540,18 +598,25 @@ void VarnodeTpl::decode(Decoder &decoder) decoder.closeElement(el); } +/// \param op2 is the VarnodeTpl to compare with \b this +/// \return \b true if address space, offset, and size or all equal bool VarnodeTpl::operator==(const VarnodeTpl &op2) const { return space==op2.space && offset==op2.offset && size==op2.size; } +/// \param op2 is the VarnodeTpl to compare with \b this +/// \return \b true if address space, offset, or size is not equal bool VarnodeTpl::operator!=(const VarnodeTpl &op2) const { return !(*this == op2); } +/// Order by address space, then offset, then size +/// \param op2 is the VarnodeTpl to order with \b this +/// \return \b true if \b this should come before \b op2 bool VarnodeTpl::operator<(const VarnodeTpl &op2) const { @@ -561,22 +626,32 @@ bool VarnodeTpl::operator<(const VarnodeTpl &op2) const return false; } +/// The constructed HandleTpl is not dynamic and matches the given VarnodeTpl +/// \param vn is the given VarnodeTpl HandleTpl::HandleTpl(const VarnodeTpl *vn) -{ // Build handle which indicates given varnode +{ space = vn->getSpace(); size = vn->getSize(); ptrspace = ConstTpl(ConstTpl::real,0); ptroffset = vn->getOffset(); } +/// \param spc is the address space +/// \param sz is the size +/// \param vn is the varnode representing the dynamic pointer +/// \param t_space is the address space of the temporary register +/// \param t_offset is the offset of the temporary register HandleTpl::HandleTpl(const ConstTpl &spc,const ConstTpl &sz,const VarnodeTpl *vn, AddrSpace *t_space,uintb t_offset) : space(spc), size(sz), ptrspace(vn->getSpace()), ptroffset(vn->getOffset()), ptrsize(vn->getSize()), temp_space(t_space), temp_offset(ConstTpl::real,t_offset) -{ // Build handle to thing being pointed at by -vn- +{ } +/// The final constant values for \b this are computed in context and stored in the given FixedHandle object. +/// \param hand is FixedHandle holding the final \b handle constants +/// \param walker is the context used to fix constants void HandleTpl::fix(FixedHandle &hand,const ParserWalker &walker) const { @@ -606,6 +681,10 @@ void HandleTpl::fix(FixedHandle &hand,const ParserWalker &walker) const } } +/// This is used to help reorder sub-constructors. +/// For each piece of \b this, if it is a \b handle, its index is translated by looking up a new value in the +/// given array of indices. +/// \param handmap is the given array of new \b handle indices void HandleTpl::changeHandleIndex(const vector &handmap) { @@ -618,6 +697,7 @@ void HandleTpl::changeHandleIndex(const vector &handmap) temp_offset.changeHandleIndex(handmap); } +/// \param encoder is the output stream void HandleTpl::encode(Encoder &encoder) const { @@ -632,6 +712,7 @@ void HandleTpl::encode(Encoder &encoder) const encoder.closeElement(sla::ELEM_HANDLE_TPL); } +/// \param decoder is the input stream void HandleTpl::decode(Decoder &decoder) { @@ -646,9 +727,10 @@ void HandleTpl::decode(Decoder &decoder) decoder.closeElement(el); } +/// An OpTpl owns its VarnodeTpl OpTpl::~OpTpl(void) -{ // An OpTpl owns its varnode_tpls +{ if (output != (VarnodeTpl *)0) delete output; vector::iterator iter; @@ -658,7 +740,7 @@ OpTpl::~OpTpl(void) bool OpTpl::isZeroSize(void) const -{ // Return if any input or output has zero size +{ vector::const_iterator iter; if (output != (VarnodeTpl *)0) @@ -668,15 +750,19 @@ bool OpTpl::isZeroSize(void) const return false; } +/// \param index is the index of the input to remove void OpTpl::removeInput(int4 index) -{ // Remove the indicated input +{ delete input[index]; for(int4 i=index;i &handmap) { @@ -688,6 +774,7 @@ void OpTpl::changeHandleIndex(const vector &handmap) (*iter)->changeHandleIndex(handmap); } +/// \param encoder is the output stream void OpTpl::encode(Encoder &encoder) const { @@ -704,6 +791,7 @@ void OpTpl::encode(Encoder &encoder) const encoder.closeElement(sla::ELEM_OP_TPL); } +/// \param decoder is the input stream void OpTpl::decode(Decoder &decoder) { @@ -727,9 +815,10 @@ void OpTpl::decode(Decoder &decoder) decoder.closeElement(el); } +/// ConstructTpl owns any OpTpl and HandleTpl ConstructTpl::~ConstructTpl(void) -{ // Constructor owns its ops and handles +{ vector::iterator oiter; for(oiter=vec.begin();oiter!=vec.end();++oiter) delete *oiter; @@ -737,6 +826,11 @@ ConstructTpl::~ConstructTpl(void) delete result; } +/// The added OpTpl can be a normal operation, which will be executed directly, or it can be a directive, like a +/// \b build or \b delayslot, which may ultimately decode to multiple operations. Additionally, an OpTpl can represent +/// a \e label, used to resolve internal p-code branches. +/// \param ot is the OpTpl to add +/// \return \b true if the operation was successfully added and did not violate a compile time rules bool ConstructTpl::addOp(OpTpl *ot) { @@ -751,6 +845,8 @@ bool ConstructTpl::addOp(OpTpl *ot) return true; } +/// \param oplist is the list of operations to add +/// \return \b true if all operations were successfully added bool ConstructTpl::addOpList(const vector &oplist) { @@ -760,10 +856,14 @@ bool ConstructTpl::addOpList(const vector &oplist) return true; } +/// For any sub-constructor that does not already one, a new \b build directive is added to the front +/// of the operation sequence. +/// \param check is an array of integers, initialized to 0, used to mark sub-constructors with a directive. +/// \param const_space is the \e constant address space +/// \return 0 upon success, 1 if there is a duplicate \b build, 2 if there is a \b build for a non-subtable int4 ConstructTpl::fillinBuild(vector &check,AddrSpace *const_space) -{ // Make sure there is a build statement for all subtable params - // Return 0 upon success, 1 if there is a duplicate BUILD, 2 if there is a build for a non-subtable +{ vector::iterator iter; OpTpl *op; VarnodeTpl *indvn; @@ -790,6 +890,7 @@ int4 ConstructTpl::fillinBuild(vector &check,AddrSpace *const_space) return 0; } +/// \return \b true if every operation is a \b build directive bool ConstructTpl::buildOnly(void) const { @@ -803,6 +904,8 @@ bool ConstructTpl::buildOnly(void) const return true; } +/// Each OpTpl operation is remapped using the given array of \b handle indices +/// \param handmap is the given array of new \b handle indices void ConstructTpl::changeHandleIndex(const vector &handmap) { @@ -823,10 +926,13 @@ void ConstructTpl::changeHandleIndex(const vector &handmap) result->changeHandleIndex(handmap); } +/// For use with optimization routines. +/// \param vn is the new input +/// \param index is the position of the operation within the sequence +/// \param slot is the input slot to replace with the new input void ConstructTpl::setInput(VarnodeTpl *vn,int4 index,int4 slot) -{ // set the VarnodeTpl input for a particular op - // for use with optimization routines +{ OpTpl *op = vec[index]; VarnodeTpl *oldvn = op->getIn(slot); op->setInput(vn,slot); @@ -834,10 +940,12 @@ void ConstructTpl::setInput(VarnodeTpl *vn,int4 index,int4 slot) delete oldvn; } +/// For use with optimization routines. +/// \param vn is the new output +/// \param index is the position of the operation within the sequence void ConstructTpl::setOutput(VarnodeTpl *vn,int4 index) -{ // set the VarnodeTpl output for a particular op - // for use with optimization routines +{ OpTpl *op = vec[index]; VarnodeTpl *oldvn = op->getOut(); op->setOutput(vn); @@ -845,9 +953,10 @@ void ConstructTpl::setOutput(VarnodeTpl *vn,int4 index) delete oldvn; } +/// \param indices is an array of the indices indicating the positions of the OpTpl to be deleted void ConstructTpl::deleteOps(const vector &indices) -{ // delete a particular set of ops +{ for(uint4 i=0;i &indices) vec.pop_back(); } +/// \param encoder is the output stream +/// \param sectionid is the id of the specific Constructor section to associate with \b this sequence void ConstructTpl::encode(Encoder &encoder,int4 sectionid) const { @@ -885,6 +996,8 @@ void ConstructTpl::encode(Encoder &encoder,int4 sectionid) const encoder.closeElement(sla::ELEM_CONSTRUCT_TPL); } +/// \param decoder is the stream to decode from +/// \return the Constructor section id associated with the sequence int4 ConstructTpl::decode(Decoder &decoder) { @@ -922,6 +1035,9 @@ int4 ConstructTpl::decode(Decoder &decoder) return sectionid; } +/// Process all the semantic operations in the given ConstructTpl, handling directives and labels. +/// \param construct is the given ConstructTpl sequence +/// \param secnum is the section number associated with the sequence void PcodeBuilder::build(ConstructTpl *construct,int4 secnum) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh index 2a768a7bef..b878b840e6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/// \file semantics.hh +/// \brief Classes describing p-code operations as parsed from a SLEIGH specification + #ifndef __SEMANTICS_HH__ #define __SEMANTICS_HH__ @@ -23,191 +26,281 @@ namespace ghidra { // We remap these opcodes for internal use during pcode generation +/// \brief The \b build directive op-code, overlayed on CPUI_MULTIEQUAL #define BUILD CPUI_MULTIEQUAL + +/// \brief The \b delayslot directive op-code, overlayed on CPUI_INDIRECT #define DELAY_SLOT CPUI_INDIRECT + +/// \brief The \b crossbuild directive op-code, overlayed on CPUI_PTRSUB #define CROSSBUILD CPUI_PTRSUB + +/// \brief The \b macro directive op-code, overlayed on CPUI_CAST #define MACROBUILD CPUI_CAST + +/// \brief The SLEIGH label op-code, overlayed on CPUI_PTRADD #define LABELBUILD CPUI_PTRADD class Translate; // Forward declaration class HandleTpl; // Forward declaration + +/// \brief A constant value encountered during SLEIGH parsing +/// +/// Any offset, size, address space, or literal constant encountered during parsing that ultimately resolves +/// to a constant value when disassembling or generating p-code for a specific instruction. The object holds details about +/// how to calculate the final constant from the SLEIGH context for the instruction. class ConstTpl { public: - enum const_type { real=0, handle=1, j_start=2, j_next=3, j_next2=4, j_curspace=5, - j_curspace_size=6, spaceid=7, j_relative=8, - j_flowref=9, j_flowref_size=10, j_flowdest=11, j_flowdest_size=12 }; - enum v_field { v_space=0, v_offset=1, v_size=2, v_offset_plus=3 }; + /// Types of constant values encountered during SLEIGH parsing. + enum const_type { + real=0, ///< A literal constant + handle=1, ///< Placeholder for a value passed back by a sub-constructor + j_start=2, ///< Address offset for the start of the current instruction + j_next=3, ///< Address offset for the start of the next instruction + j_next2=4, ///< Address offset of the instruction immediately after the next instruction + j_curspace=5, ///< Address space containing the current instruction + j_curspace_size=6, ///< Number of bytes encoding the address of the current instruction + spaceid=7, ///< An address space (encoded as a constant) + j_relative=8, ///< A relative p-code branch offset + j_flowref=9, ///< Address offset of any call reference at site of p-code injection + j_flowref_size=10, ///< Number of bytes encoding address of call reference + j_flowdest=11, ///< Address offset of call destination being injected + j_flowdest_size=12 ///< Number of bytes encoding address of call destination + }; + /// For a \b handle (a value calculated in a sub-constructor), we can sub-select which part of the value to use. + enum v_field { + v_space=0, ///< The address space associated with the \b handle + v_offset=1, ///< The offset associated with the \b handle + v_size=2, ///< Number of bytes in the \b handle + v_offset_plus=3 ///< The offset associated with the \b handle plus an additional constant offset + }; private: - const_type type; + const_type type; ///< The type of constant union { - // uintb real; // an actual constant - AddrSpace *spaceid; // Id (pointer) for registered space - int4 handle_index; // Place holder for run-time determined value - } value; - uintb value_real; - v_field select; // Which part of handle to use as constant + AddrSpace *spaceid; ///< Id referring to a registered address space + int4 handle_index; ///< For a \b handle, the index of the specific sub-constructor passing back the value + } value; ///< Specialized values a ConstTpl can represent + uintb value_real; ///< An immediate constant value or other constant offset a ConstTpl represents + v_field select; ///< Assuming ConstTpl is a \b handle, the part of the \b handle to use as constant public: - ConstTpl(void) { type = real; value_real = 0; } + ConstTpl(void) { type = real; value_real = 0; } ///< Construct a zero constant ConstTpl(const ConstTpl &op2) { - type=op2.type; value=op2.value; value_real=op2.value_real; select=op2.select; } - ConstTpl(const_type tp,uintb val); - ConstTpl(const_type tp); - ConstTpl(AddrSpace *sid); - ConstTpl(const_type tp,int4 ht,v_field vf); - ConstTpl(const_type tp,int4 ht,v_field vf,uintb plus); - bool isConstSpace(void) const; - bool isUniqueSpace(void) const; - bool operator==(const ConstTpl &op2) const; - bool operator<(const ConstTpl &op2) const; - uintb getReal(void) const { return value_real; } - AddrSpace *getSpace(void) const { return value.spaceid; } - int4 getHandleIndex(void) const { return value.handle_index; } - const_type getType(void) const { return type; } - v_field getSelect(void) const { return select; } - uintb fix(const ParserWalker &walker) const; - AddrSpace *fixSpace(const ParserWalker &walker) const; - void transfer(const vector ¶ms); - bool isZero(void) const { return ((type==real)&&(value_real==0)); } - void changeHandleIndex(const vector &handmap); - void fillinSpace(FixedHandle &hand,const ParserWalker &walker) const; - void fillinOffset(FixedHandle &hand,const ParserWalker &walker) const; - void encode(Encoder &encoder) const; - void decode(Decoder &decoder); + type=op2.type; value=op2.value; value_real=op2.value_real; select=op2.select; } ///< Copy constructor + ConstTpl(const_type tp,uintb val); ///< Constructor for real constants or relative offsets + ConstTpl(const_type tp); ///< Constructor for special constants from context + ConstTpl(AddrSpace *sid); ///< Constructor for a constant representing an address space + ConstTpl(const_type tp,int4 ht,v_field vf); ///< Constructor for a \b handle constant + ConstTpl(const_type tp,int4 ht,v_field vf,uintb plus); ///< Constructor for a \b handle offset plus a constant + bool isConstSpace(void) const; ///< Return \b true if \b this represents the \e constant address space + bool isUniqueSpace(void) const; ///< Return \b true if \b this represents the \e unique address space + bool operator==(const ConstTpl &op2) const; ///< Compare two constants for equality + bool operator<(const ConstTpl &op2) const; ///< Order two constants by type then value + uintb getReal(void) const { return value_real; } ///< Get the literal constant value associated with \b this + AddrSpace *getSpace(void) const { return value.spaceid; } ///< Get the address space \b this is encoding + int4 getHandleIndex(void) const { return value.handle_index; } ///< Get the index of the sub-constructor computing \b this + const_type getType(void) const { return type; } ///< Get the constant type + v_field getSelect(void) const { return select; } ///< Get the type of \b handle piece \b this is encoding + uintb fix(const ParserWalker &walker) const; ///< Get the final constant value of \b this in context + AddrSpace *fixSpace(const ParserWalker &walker) const; ///< Get the final address space \b this represents in context + void transfer(const vector ¶ms); ///< Copy a \b handle into \b this based on the \b handle_index + bool isZero(void) const { return ((type==real)&&(value_real==0)); } ///< Return \b true if \b this is a literal zero + void changeHandleIndex(const vector &handmap); ///< Remap the \b handle index for \b this + void fillinSpace(FixedHandle &hand,const ParserWalker &walker) const; ///< Fill in the address space of a FixedHandle, based on \b this + void fillinOffset(FixedHandle &hand,const ParserWalker &walker) const; ///< Fill in the offset of a FixedHandle, based on \b this + void encode(Encoder &encoder) const; ///< Encode \b this SLEIGH constant to an output stream + void decode(Decoder &decoder); ///< Decode \b this SLEIGH constant from a stream }; +/// \brief A (partially) resolved Varnode in a SLEIGH specification +/// +/// Variable symbols in SLEIGH are represented using \b this object. It encodes the +/// address space, offset, and size associated with the variable. In the context of +/// a specific instruction, \b this is translated into a Varnode object. class VarnodeTpl { friend class OpTpl; friend class HandleTpl; - ConstTpl space,offset,size; - bool unnamed_flag; + ConstTpl space; ///< Address space associated with \b this variable + ConstTpl offset; ///< Offset into the address space + ConstTpl size; ///< Number of bytes in \b this variable + bool unnamed_flag; ///< Set to \b true if \b this is an unnamed temporary register public: - VarnodeTpl(int4 hand,bool zerosize); - VarnodeTpl(void) : space(), offset(), size() { unnamed_flag=false; } - VarnodeTpl(const ConstTpl &sp,const ConstTpl &off,const ConstTpl &sz); - VarnodeTpl(const VarnodeTpl &vn); - const ConstTpl &getSpace(void) const { return space; } - const ConstTpl &getOffset(void) const { return offset; } - const ConstTpl &getSize(void) const { return size; } - bool isDynamic(const ParserWalker &walker) const; - int4 transfer(const vector ¶ms); - bool isZeroSize(void) const { return size.isZero(); } - bool operator==(const VarnodeTpl &op2) const; - bool operator!=(const VarnodeTpl &op2) const; - bool operator<(const VarnodeTpl &op2) const; - void setOffset(uintb constVal) { offset = ConstTpl(ConstTpl::real,constVal); } - void setRelative(uintb constVal) { offset = ConstTpl(ConstTpl::j_relative,constVal); } - void setSize(const ConstTpl &sz ) { size = sz; } - bool isUnnamed(void) const { return unnamed_flag; } - void setUnnamed(bool val) { unnamed_flag = val; } - bool isLocalTemp(void) const; - bool isRelative(void) const { return (offset.getType() == ConstTpl::j_relative); } - void changeHandleIndex(const vector &handmap); - bool adjustTruncation(int4 sz,bool isbigendian); - void encode(Encoder &encoder) const; - void decode(Decoder &decoder); + VarnodeTpl(int4 hand,bool zerosize); ///< Construct a handle + VarnodeTpl(void) : space(), offset(), size() { unnamed_flag=false; } ///< Construct an uninitialized VarnodeTpl + VarnodeTpl(const ConstTpl &sp,const ConstTpl &off,const ConstTpl &sz); ///< Construct directly from ConstTpl + VarnodeTpl(const VarnodeTpl &vn); ///< Copy constructor + const ConstTpl &getSpace(void) const { return space; } ///< Get the address space + const ConstTpl &getOffset(void) const { return offset; } ///< Get the offset + const ConstTpl &getSize(void) const { return size; } ///< Get the size + bool isDynamic(const ParserWalker &walker) const; ///< Return \b true if \b this is a dynamically computed \b handle + int4 transfer(const vector ¶ms); ///< Copy a computed HandleTpl into \b this from the given array + bool isZeroSize(void) const { return size.isZero(); } ///< Return \b true if \b this currently has a size of zero + bool operator==(const VarnodeTpl &op2) const; ///< Test if two VarnodeTpl are equal + bool operator!=(const VarnodeTpl &op2) const; ///< Test if two VarnodeTpl are not equal + bool operator<(const VarnodeTpl &op2) const; ///< Order two VarnodeTpl + void setOffset(uintb constVal) { offset = ConstTpl(ConstTpl::real,constVal); } ///< Set the offset piece to a literal constant + void setRelative(uintb constVal) { offset = ConstTpl(ConstTpl::j_relative,constVal); } ///< Set the offset piece to a relative branch offset + void setSize(const ConstTpl &sz ) { size = sz; } ///< Set the size piece + bool isUnnamed(void) const { return unnamed_flag; } ///< Return \b true if \b this is an unnamed temporary register + void setUnnamed(bool val) { unnamed_flag = val; } ///< Mark \b this as an unnamed temporary register + bool isLocalTemp(void) const; ///< Return \b true if \b this is a temporary register + bool isRelative(void) const { return (offset.getType() == ConstTpl::j_relative); } ///< Return \b true if \b this is a relative branch offset + void changeHandleIndex(const vector &handmap); ///< Remap any handle indices for \b this + bool adjustTruncation(int4 sz,bool isbigendian); ///< Adjust truncation given final size of the Varnode + void encode(Encoder &encoder) const; ///< Encode \b this VarnodeTpl to an output stream + void decode(Decoder &decoder); ///< Decode \b this VarnodeTpl from a stream }; +/// \brief An \e exported value of a sub-constructor in a SLEIGH specification +/// +/// For an output value that is equivalent to a VarnodeTpl, \b space, \b ptroffset, and \b size correspond to +/// Varnode::space, Varnode::offset, and Varnode::size. But a HandleTpl can also represent a dynamic value loaded +/// at run-time. In this case, the final value is stored in a temporary register specified by +/// \b temp_space, \b temp_offset (and \b size), and the pointer used to load the final value is specified by +/// \b ptrspace, \b ptroffset, and \b ptrsize. class HandleTpl { - ConstTpl space; - ConstTpl size; - ConstTpl ptrspace; - ConstTpl ptroffset; - ConstTpl ptrsize; - ConstTpl temp_space; - ConstTpl temp_offset; + ConstTpl space; ///< The address space of the value + ConstTpl size; ///< The size of the value + ConstTpl ptrspace; ///< (If dynamic) the address space of the pointer + ConstTpl ptroffset; ///< If dynamic, the offset of the pointer, or the offset of the value otherwise + ConstTpl ptrsize; ///< (If dynamic) the size of the pointer + ConstTpl temp_space; ///< (If dynamic) the address space of the temporary register holding the final value + ConstTpl temp_offset; ///< (If dynamic) the offset of the temporary register public: - HandleTpl(void) {} - HandleTpl(const VarnodeTpl *vn); + HandleTpl(void) {} ///< Construct an uninitialized HandleTpl + HandleTpl(const VarnodeTpl *vn); ///< Construct HandleTpl representing the given VarnodeTpl HandleTpl(const ConstTpl &spc,const ConstTpl &sz,const VarnodeTpl *vn, - AddrSpace *t_space,uintb t_offset); - const ConstTpl &getSpace(void) const { return space; } - const ConstTpl &getPtrSpace(void) const { return ptrspace; } - const ConstTpl &getPtrOffset(void) const { return ptroffset; } - const ConstTpl &getPtrSize(void) const { return ptrsize; } - const ConstTpl &getSize(void) const { return size; } - const ConstTpl &getTempSpace(void) const { return temp_space; } - const ConstTpl &getTempOffset(void) const { return temp_offset; } - void setSize(const ConstTpl &sz) { size = sz; } - void setPtrSize(const ConstTpl &sz) { ptrsize=sz; } - void setPtrOffset(uintb val) { ptroffset = ConstTpl(ConstTpl::real,val); } - void setTempOffset(uintb val) { temp_offset = ConstTpl(ConstTpl::real,val); } - void fix(FixedHandle &hand,const ParserWalker &walker) const; - void changeHandleIndex(const vector &handmap); - void encode(Encoder &encoder) const; - void decode(Decoder &decoder); + AddrSpace *t_space,uintb t_offset); ///< Construct a dynamic HandleTpl + const ConstTpl &getSpace(void) const { return space; } ///< Get the address space + const ConstTpl &getPtrSpace(void) const { return ptrspace; } ///< Get the pointer address space + const ConstTpl &getPtrOffset(void) const { return ptroffset; } ///< Get the offset (or the pointer offset) + const ConstTpl &getPtrSize(void) const { return ptrsize; } ///< Get the pointer size + const ConstTpl &getSize(void) const { return size; } ///< Get the size + const ConstTpl &getTempSpace(void) const { return temp_space; } ///< Get the temporary register address space + const ConstTpl &getTempOffset(void) const { return temp_offset; } ///< Get the temporary register offset + void setSize(const ConstTpl &sz) { size = sz; } ///< Set the size + void setPtrSize(const ConstTpl &sz) { ptrsize=sz; } ///< Set the pointer size + void setPtrOffset(uintb val) { ptroffset = ConstTpl(ConstTpl::real,val); } ///< Set the pointer offset + void setTempOffset(uintb val) { temp_offset = ConstTpl(ConstTpl::real,val); } ///< Set the temporary register offset + void fix(FixedHandle &hand,const ParserWalker &walker) const; ///< Calculate final fixed values for \b this + void changeHandleIndex(const vector &handmap); ///< Remap any handle indices for \b this + void encode(Encoder &encoder) const; ///< Encode \b this HandleTpl to an output stream + void decode(Decoder &decoder); ///< Decode \b this HandleTpl from a stream }; +/// \brief A p-code operation in a SLEIGH specification +/// +/// Each input and output to the operation is a VarnodeTpl. class OpTpl { - VarnodeTpl *output; - OpCode opc; - vector input; + VarnodeTpl *output; ///< The output variable of the operation, or NULL + OpCode opc; ///< The code describing the operation + vector input; ///< Inputs to the operation public: - OpTpl(void) : output(nullptr) {} - OpTpl(OpCode oc) { opc = oc; output = (VarnodeTpl *)0; } - ~OpTpl(void); - VarnodeTpl *getOut(void) const { return output; } - int4 numInput(void) const { return input.size(); } - VarnodeTpl *getIn(int4 i) const { return input[i]; } - OpCode getOpcode(void) const { return opc; } - bool isZeroSize(void) const; - void setOpcode(OpCode o) { opc = o; } - void setOutput(VarnodeTpl *vt) { output = vt; } - void clearOutput(void) { delete output; output = (VarnodeTpl *)0; } - void addInput(VarnodeTpl *vt) { input.push_back(vt); } - void setInput(VarnodeTpl *vt,int4 slot) { input[slot] = vt; } - void removeInput(int4 index); - void changeHandleIndex(const vector &handmap); - void encode(Encoder &encoder) const; - void decode(Decoder &decoder); + OpTpl(void) : output(nullptr) {} ///< Construct an uninitialized OpTpl + OpTpl(OpCode oc) : output(nullptr) { opc = oc; } ///< Construct an OpTpl with not inputs or output + ~OpTpl(void); ///< Destructor + VarnodeTpl *getOut(void) const { return output; } ///< Get the output VarnodeTpl (or NULL) + int4 numInput(void) const { return input.size(); } ///< Return the number of inputs to \b this + VarnodeTpl *getIn(int4 i) const { return input[i]; } ///< Get the i-th input VarnodeTpl + OpCode getOpcode(void) const { return opc; } ///< Get the operation code + bool isZeroSize(void) const; ///< Return \b true if any input or output has zero size + void setOpcode(OpCode o) { opc = o; } ///< Set the operation code + void setOutput(VarnodeTpl *vt) { output = vt; } ///< Set the output VarnodeTpl + void clearOutput(void) { delete output; output = (VarnodeTpl *)0; } ///< Remove the existing output VarnodeTpl + void addInput(VarnodeTpl *vt) { input.push_back(vt); } ///< Add an input VarnodeTpl + void setInput(VarnodeTpl *vt,int4 slot) { input[slot] = vt; } ///< Set the VarnodeTpl for a specific input slot + void removeInput(int4 index); ///< Remove the indicated input + void changeHandleIndex(const vector &handmap); ///< Remap any handle indices for inputs and outputs to \b this + void encode(Encoder &encoder) const; ///< Encode \b this OpTpl to an output stream + void decode(Decoder &decoder); ///< Decode \b this OpTpl from a stream }; +/// \brief P-code semantics for Constructor in a SLEIGH specification +/// +/// This encodes a sequence of OpTpl representing the semantic action of the Constructor, and +/// if present, the HandleTpl representing the final \e exported value. class ConstructTpl { friend class SleighCompile; protected: - uint4 delayslot; - uint4 numlabels; // Number of label templates - vector vec; - HandleTpl *result; - void setOpvec(vector &opvec) { vec = opvec; } - void setNumLabels(uint4 val) { numlabels = val; } + uint4 delayslot; ///< (Minimum) number of bytes in the delay slot + uint4 numlabels; ///< Number of label templates + vector vec; ///< Sequence of operations performed by the Constructor + HandleTpl *result; ///< Final \e exported value (or NULL) + void setOpvec(vector &opvec) { vec = opvec; } ///< Set the sequence of OpTpl + void setNumLabels(uint4 val) { numlabels = val; } ///< Set the number of labels public: - ConstructTpl(void) { delayslot=0; numlabels=0; result = (HandleTpl *)0; } - ~ConstructTpl(void); - uint4 delaySlot(void) const { return delayslot; } - uint4 numLabels(void) const { return numlabels; } - const vector &getOpvec(void) const { return vec; } - HandleTpl *getResult(void) const { return result; } - bool addOp(OpTpl *ot); - bool addOpList(const vector &oplist); - void setResult(HandleTpl *t) { result = t; } - int4 fillinBuild(vector &check,AddrSpace *const_space); - bool buildOnly(void) const; - void changeHandleIndex(const vector &handmap); - void setInput(VarnodeTpl *vn,int4 index,int4 slot); - void setOutput(VarnodeTpl *vn,int4 index); - void deleteOps(const vector &indices); - void encode(Encoder &encoder,int4 sectionid) const; - int4 decode(Decoder &decoder); + ConstructTpl(void) { delayslot=0; numlabels=0; result = (HandleTpl *)0; } ///< Construct an empty ConstructTpl + ~ConstructTpl(void); ///< Destructor + uint4 delaySlot(void) const { return delayslot; } ///< Return the number of bytes in the delay slot + uint4 numLabels(void) const { return numlabels; } ///< Get the number of labels + const vector &getOpvec(void) const { return vec; } ///< Get the sequence of p-code operations + HandleTpl *getResult(void) const { return result; } ///< Get the \e export result + bool addOp(OpTpl *ot); ///< Add an operation to the end of the sequence + bool addOpList(const vector &oplist); ///< Add a list of operations to the end of the sequence + void setResult(HandleTpl *t) { result = t; } ///< Set the \e export HandleTpl for \b this + int4 fillinBuild(vector &check,AddrSpace *const_space); ///< Make sure there is a \b build directive for all sub-constructors + bool buildOnly(void) const; ///< Check if all operations are \b build directives + void changeHandleIndex(const vector &handmap); ///< Remap handle indices for all operations + void setInput(VarnodeTpl *vn,int4 index,int4 slot); ///< Set the VarnodeTpl input for a particular OpTpl in the sequence + void setOutput(VarnodeTpl *vn,int4 index); ///< Set the VarnodeTpl output for a particular OpTpl in the sequence + void deleteOps(const vector &indices); ///< Delete the given set of operations + void encode(Encoder &encoder,int4 sectionid) const; ///< Encode details of \b this semantic sequence to an output stream + int4 decode(Decoder &decoder); ///< Decode a semantic sequence from a stream }; class PcodeEmit; // Forward declaration for emitter -class PcodeBuilder { // SLEIGH specific pcode generator - uint4 labelbase; - uint4 labelcount; +/// \brief An abstract, SLEIGH specific, p-code generator +/// +/// This is a base class for output, filtering, or otherwise processing sequences of p-code for a single +/// instruction decoded by the SLEIGH engine. The ConstructTpl of the root constructor for the instruction is fed to the +/// build() method. Each normal p-code operation then makes it to the dump() method in sequence for processing. +/// Hook points are provided to recurse into different instructions/constructors via \b build, \b delayslot, and +/// \b crossbuild directives. +class PcodeBuilder { + uint4 labelbase; ///< Starting label index for this builder + uint4 labelcount; ///< Current number of defined labels protected: - ParserWalker *walker; + ParserWalker *walker; ///< Current instruction context + + /// \brief Output/build the given p-code operation + /// + /// This is the main hook point for different p-code generation strategies. It is called once, in sequence, for + /// each p-code operation generated for a specific instruction. Directives and labels have had the opportunity to + /// be expanded or filtered by other methods. + /// \param op is the p-code operation to process virtual void dump( OpTpl *op )=0; public: - PcodeBuilder(uint4 lbcnt) { labelbase=labelcount=lbcnt; } - virtual ~PcodeBuilder(void) {} + PcodeBuilder(uint4 lbcnt) { labelbase=labelcount=lbcnt; } ///< Construct with a starting label index + virtual ~PcodeBuilder(void) {} ///< Destructor - uint4 getLabelBase(void) const { return labelbase; } - ParserWalker *getCurrentWalker() const { return walker; } - void build(ConstructTpl *construct,int4 secnum); + uint4 getLabelBase(void) const { return labelbase; } ///< Get the starting label index for \b this builder + ParserWalker *getCurrentWalker() const { return walker; } ///< Get the current instruction context + void build(ConstructTpl *construct,int4 secnum); ///< Build the semantics for the given constructor + + /// \brief Execute or filter a \b build directive in sequence + /// + /// Any recursion through \b build directives into ConstructTpl for sub-constructors happens here. + /// \param bld is the \b build directive, which encodes the particular sub-construction + /// \param secnum is the current section number of the constructor virtual void appendBuild(OpTpl *bld,int4 secnum)=0; + + /// \brief Execute or filter a \b delayslot directive in sequence + /// + /// Any recursion through \b delayslot directives into new instructions happens here. + /// \param op is the \b delayslot directive, which encodes the number of bytes in the particular delay slot. virtual void delaySlot(OpTpl *op)=0; + + /// \brief Process or filter a label in sequence + /// + /// The builder has the opportunity to record exactly where in sequence the given label occurs. + /// \param op is the OpTpl encoding the label index virtual void setLabel(OpTpl *op)=0; + + /// \brief Process or filter a \b crossbuild directive in sequence + /// + /// Any recursion through \b crossbuild directives into new instructions and sections happens here. + /// \param bld is the \b crossbuild directive + /// \param secnum is the current section number being processed virtual void appendCrossBuild(OpTpl *bld,int4 secnum)=0; }; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.hh index 623eccb4b1..1d0e5b524e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.hh @@ -301,8 +301,8 @@ public: /// various set*() methods prior to calling run_compilation. class SleighCompile : public SleighBase { friend class SleighPcode; - static const int4 UNIQUE_CROSSBUILD_POSITION = 8; - static const int4 UNIQUE_CROSSBUILD_NUMBITS = 8; + static const int4 UNIQUE_CROSSBUILD_POSITION = 8; ///< The position to insert a cross-build region, within a unique varnode + static const int4 UNIQUE_CROSSBUILD_NUMBITS = 8; ///< Number of bits inserted for the cross-build region public: SleighPcode pcode; ///< The p-code parsing (sub)engine private: diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc index d1e17ee800..901580034b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc @@ -2137,9 +2137,11 @@ bool SplitDatatype::RootPointer::backUpPointer(Datatype *impliedBase) /// find it, we back up one level (through a PTRSUB, PTRADD, or INT_ADD). If it isn't found after 1 hop, /// \b false is returned. Once this pointer is found, we back up through any single path of nested TYPE_STRUCT /// and TYPE_ARRAY offsets to establish the final root \b pointer, and \b true is returned. Any accumulated offset, -/// relative to the original LOAD or STORE pointer is recorded in the \b baseOffset. +/// relative to the original LOAD or STORE pointer is recorded in the \b baseOffset. If we encounter +/// union data-types during the traversal, we record their resolution for later reproduction. /// \param op is the LOAD or STORE /// \param valueType is the specific data-type to match +/// \param resolver records any resolved union fields /// \return \b true if the root pointer is found bool SplitDatatype::RootPointer::find(PcodeOp *op,Datatype *valueType,ResolveCache &resolver) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc index 1f3614f5e6..6b5b716500 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc @@ -156,7 +156,7 @@ void FunctionTestCollection::buildProgram(DocumentStorage &docStorage) bool iserror = false; try { dcp->conf->init(docStorage); - dcp->conf->readLoaderSymbols("::"); // Read in loader symbols + dcp->conf->readLoaderSymbols(); // Read in loader symbols } catch(DecoderError &err) { errmsg = err.explain; iserror = true; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.cc index 0d7edd1f81..09719c9ef6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.cc @@ -255,11 +255,11 @@ int4 ScoreUnionFields::scoreReturnType(Datatype *ct,const FuncProto *proto) return 0; } -/// Test if the data-type is a pointer and if the pointed-to data-type is +/// Test if a parameter trial data-type is a pointer and if the pointed-to data-type is /// compatible with the size of the value being loaded or stored. A \b score is /// passed back for how closely the data-type fits this scenario, and if it /// does we return the data-type of the pointer value. -/// \param ct is the trial data-type +/// \param trial is the parameter trial whose data-type is tested /// \param vn is the Varnode holding the value being loaded or stored /// \param score is used to pass back the score /// \return the data-type of the value or null