diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/Doxyfile b/Ghidra/Features/Decompiler/src/decompile/cpp/Doxyfile index e8e78f0f4a..e41d0dca86 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 slgh_compile.hh slgh_compile.hh slghpattern.hh slghpattern.cc slghpatexpress.hh slghpatexpress.cc slghsymbol.hh slghsymbol.cc ifacedecomp.hh ifacedecomp.cc ifaceterm.hh ifaceterm.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 interface.hh interface.cc loadimage_bfd.hh loadimage_bfd.cc pcodecompile.cc pcodecompile.hh pcodeparse.hh pcodeparse.cc inject_sleigh.hh inject_sleigh.cc context.hh context.cc consolemain.cc sleighexample.cc xml.cc +EXCLUDE = unify.hh unify.cc rulecompile.hh rulecompile.cc slgh_compile.hh slgh_compile.cc slghparse.cc slghparse.hh slghscan.cc slghpattern.hh slghpattern.cc slghpatexpress.hh slghpatexpress.cc slghsymbol.hh slghsymbol.cc ifacedecomp.hh ifacedecomp.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 inject_sleigh.hh inject_sleigh.cc context.hh context.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/block.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh index a6a8125727..daf040a65e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.hh @@ -597,8 +597,8 @@ class BlockWhileDo : public BlockGraph { bool testIterateForm(void) const; ///< Return \b false if the iterate statement is of an unacceptable form public: BlockWhileDo(void) { initializeOp = (PcodeOp *)0; iterateOp = (PcodeOp *)0; loopDef = (PcodeOp *)0; } ///< Constructor - PcodeOp *getInitializeOp(void) const { return initializeOp; } - PcodeOp *getIterateOp(void) const { return iterateOp; } + PcodeOp *getInitializeOp(void) const { return initializeOp; } ///< Get root of initialize statement or null + PcodeOp *getIterateOp(void) const { return iterateOp; } ///< Get root of iterate statement or null bool hasOverflowSyntax(void) const { return ((getFlags() & f_whiledo_overflow)!=0); } ///< Does \b this require overflow syntax void setOverflowSyntax(void) { setFlag(f_whiledo_overflow); } ///< Set that \b this requires overflow syntax virtual block_type getType(void) const { return t_whiledo; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 6fb1aa38ed..b9c43246fe 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -3477,9 +3477,10 @@ bool ActionDeadCode::isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount /// \brief Check if there are any unconsumed LOADs that may be from volatile addresses. /// /// It may be too early to remove certain LOAD operations even though their result isn't -/// consumed because it be of a volatile address with side effects. If a LOAD meets this +/// consumed because it may be of a volatile address with side effects. If a LOAD meets this /// criteria, it is added to the worklist and \b true is returned. /// \param data is the function being analyzed +/// \param worklist is the container of consumed Varnodes to further process /// \return \b true if there was at least one LOAD added to the worklist bool ActionDeadCode::lastChanceLoad(Funcdata &data,vector &worklist) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc index e0068dceb2..1b4ec7b834 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc @@ -2817,7 +2817,7 @@ void Database::fillResolve(Scope *scope) /// Initialize a new symbol table, with no initial scopes or symbols. /// \param g is the Architecture that owns the symbol table -/// \param isByName is \b true if scope ids are calculated as a hash of the scope name. +/// \param idByName is \b true if scope ids are calculated as a hash of the scope name. Database::Database(Architecture *g,bool idByName) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc index f7c3e95c33..670d94c9f7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc @@ -19,6 +19,7 @@ /// \param nm is the (base) name of the function /// \param scope is Symbol scope associated with the function /// \param addr is the entry address for the function +/// \param sym is the symbol representing the function /// \param sz is the number of bytes (of code) in the function body Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz) : baseaddr(addr), diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 32492629b8..dc5c877889 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -249,7 +249,6 @@ public: int4 numCalls(void) const { return qlst.size(); } ///< Get the number of calls made by \b this function FuncCallSpecs *getCallSpecs(int4 i) const { return qlst[i]; } ///< Get the i-th call specification FuncCallSpecs *getCallSpecs(const PcodeOp *op) const; ///< Get the call specification associated with a CALL op - void updateOpFromSpec(FuncCallSpecs *fc); int4 fillinExtrapop(void); ///< Recover and return the \e extrapop for this function // Varnode routines diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc index a42520cee8..c01052b5ad 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc @@ -291,7 +291,7 @@ void Funcdata::destroyVarnode(Varnode *vn) /// Check if the given storage range is a potential laned register. /// If so, record the storage with the matching laned register record. -/// \param s is the size of the storage range in bytes +/// \param size is the size of the storage range in bytes /// \param addr is the starting address of the storage range void Funcdata::checkForLanedRegister(int4 size,const Address &addr) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc index f0bd755d67..d34513ef04 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc @@ -690,6 +690,18 @@ void ArchitectureGhidra::getBytes(uint1 *buf,int4 size,const Address &inaddr) readResponseEnd(sin); } +/// \brief Get string data at a specific address +/// +/// The data is always returned as a sequence of bytes in UTF-8 format. The in-memory form of +/// the string may be different than UTF-8 but is always translated into UTF-8 by this method. +/// The caller can inform the in-memory format of the string by specifying a specific string +/// data-type. A maximum number of bytes to return is specified. If this is exceeded, a boolean +/// reference is set to \b true. +/// \param buffer will hold the string bytes in UTF-8 format +/// \param addr is program Address that holds the string data in memory +/// \param ct is string data-type expected +/// \param maxBytes is the maximum number of bytes to return +/// \param isTrunc is the boolean reference indicating whether the data is truncated void ArchitectureGhidra::getStringData(vector &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh index 36d565cdea..f796c7ea76 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh @@ -127,7 +127,7 @@ public: bool getSendParamMeasures(void) const { return sendParamMeasures; } ///< Get the current setting for emitting parameter info - virtual void getStringData(vector &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc); + void getStringData(vector &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc); virtual void printMessage(const string &message) const; static void segvHandler(int4 sig); ///< Handler for a segment violation (SIGSEGV) signal diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.cc index 8d92af273e..41e8ab718d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.cc @@ -58,9 +58,16 @@ IfaceTerm::~IfaceTerm(void) #endif } +/// Respond to a TAB key press and try to 'complete' any existing tokens. +/// The method is handed the current state of the command-line in a string, and +/// it updates the command-line in place. +/// +/// \param line is current command-line and will hold the final completion +/// \param cursor is the current position of the cursor +/// \return the (possibly new) position of the cursor, after completion int4 IfaceTerm::doCompletion(string &line,int4 cursor) -{ // Try to complete the current command +{ vector fullcommand; istringstream s(line); string tok; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.hh index e47c5c8e06..cd14acabe5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.hh @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// Add some terminal capabilities to the command-line interface + +/// \file ifaceterm.hh +/// \brief Add some terminal capabilities to the command-line interface (IfaceStatus) + #include "interface.hh" #ifdef __TERMINAL__ @@ -23,18 +26,24 @@ extern "C" { } #endif +/// \brief Implement the command-line interface on top of a specific input stream +/// +/// An initial input stream is provided as the base stream to parse for commands. +/// Additional input streams can be stacked by invoking scripts. +/// If the stream supports it, the stream parser recognizes special command-line editing +/// and completion keys. class IfaceTerm : public IfaceStatus { #ifdef __TERMINAL__ - bool is_terminal; // True if the input stream is a terminal - int4 ifd; // Underlying file descriptor - struct termios itty; // Original terminal settings + bool is_terminal; ///< True if the input stream is a terminal + int4 ifd; ///< Underlying file descriptor + struct termios itty; ///< Original terminal settings #endif - istream *sptr; // Where to get input - vector inputstack; - int4 doCompletion(string &line,int4 cursor); + istream *sptr; ///< The base input stream for the interface + vector inputstack; ///< Stack of nested input streams + int4 doCompletion(string &line,int4 cursor); ///< 'Complete' the current command line virtual void readLine(string &line); public: - IfaceTerm(const string &prmpt,istream &is,ostream &os); + IfaceTerm(const string &prmpt,istream &is,ostream &os); ///< Constructor virtual ~IfaceTerm(void); virtual void pushScript(const string &filename,const string &newprompt); virtual void popScript(void); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/interface.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/interface.cc index 701713ab17..9c5674b8f0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/interface.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/interface.cc @@ -29,9 +29,12 @@ void IfaceCapability::initialize(void) thelist.push_back(this); } +/// Allow each capability to register its own commands +/// +/// \param status is the command line interface to register commands with void IfaceCapability::registerAllCommands(IfaceStatus *status) -{ // Allow each capability to register its own commands +{ for(uint4 i=0;iregisterCommands(status); } @@ -114,6 +117,9 @@ bool RemoteSocket::isSocketOpen(void) #endif +/// \param prmpt is the base command line prompt +/// \param os is the base stream to write output to +/// \param mxhist is the maximum number of lines to store in history IfaceStatus::IfaceStatus(const string &prmpt,ostream &os,int4 mxhist) { @@ -128,9 +134,15 @@ IfaceStatus::IfaceStatus(const string &prmpt,ostream &os,int4 mxhist) curhistory = 0; } +/// \brief Provide a new script file to execute, with an associated command prompt +/// +/// The script provides a subsidiary input stream to the current stream. +/// Once commands from the script are complete, processing will resume on this stream. +/// \param filename is the name of the file containing the script +/// \param newprompt is the command line prompt void IfaceStatus::pushScript(const string &filename,const string &newprompt) -{ // Push new input stream on stack (with new prompt) +{ promptstack.push_back(prompt); uint4 flags = 0; if (errorisdone) @@ -139,9 +151,13 @@ void IfaceStatus::pushScript(const string &filename,const string &newprompt) prompt = newprompt; } +/// \brief Return to processing the parent stream +/// +/// The current input stream, as established by a script, is popped from the stack, +/// along with its command prompt, and processing continues with the previous stream. void IfaceStatus::popScript(void) -{ // Pop the current input stream (and current prompt) +{ prompt = promptstack.back(); promptstack.pop_back(); uint4 flags = flagstack.back(); @@ -159,9 +175,11 @@ void IfaceStatus::reset(void) done = false; } +/// The line is saved in a circular history buffer +/// \param line is the command line to save void IfaceStatus::saveHistory(const string &line) -{ // Save line in circular history buffer +{ if (history.size() < maxhistory) history.push_back(line); else @@ -171,6 +189,10 @@ void IfaceStatus::saveHistory(const string &line) curhistory = 0; } +/// A command line is selected by specifying how many steps in time +/// to go back through the list of successful command lines. +/// \param line will hold the selected command line from history +/// \param i is the number of steps back to go void IfaceStatus::getHistory(string &line,int4 i) const { @@ -182,9 +204,10 @@ void IfaceStatus::getHistory(string &line,int4 i) const line = history[i]; } +// The last command has failed, decide if we are completely abandoning this stream void IfaceStatus::evaluateError(void) -{ // The last command has failed, decide if we are completely abandoning this stream +{ if (errorisdone) { *optr << "Aborting process" << endl; inerror = true; @@ -199,6 +222,7 @@ void IfaceStatus::evaluateError(void) inerror = false; } +/// Concatenate a list of tokens into a single string, separated by a space character void IfaceStatus::wordsToString(string &res,const vector &list) { @@ -229,13 +253,24 @@ IfaceStatus::~IfaceStatus(void) delete (*iter).second; } +/// \brief Register a command with this interface +/// +/// A command object is associated with one or more tokens on the command line. +/// A string containing up to 5 tokens can be associated with the command. +/// +/// \param fptr is the IfaceCommand object +/// \param nm1 is the first token representing the command +/// \param nm2 is the second token (or null) +/// \param nm3 is the third token (or null) +/// \param nm4 is the fourth token (or null) +/// \param nm5 is the fifth token (or null) void IfaceStatus::registerCom(IfaceCommand *fptr,const char *nm1, const char *nm2, const char *nm3, const char *nm4, const char *nm5) -{ // Register an interface command +{ fptr->addWord(nm1); if (nm2 != (const char *)0) fptr->addWord(nm2); @@ -261,15 +296,24 @@ void IfaceStatus::registerCom(IfaceCommand *fptr,const char *nm1, fptr->setData(this,data); // Inform command of its data } +/// Commands (IfaceCommand) are associated with a particular module that has +/// a formal name and a data object associated with it. This method +/// retrieves the module specific data object by name. +/// \param nm is the name of the module +/// \return the IfaceData object or null IfaceData *IfaceStatus::getData(const string &nm) const -{ // Get data corresponding to the named module +{ map::const_iterator iter = datamap.find(nm); if (iter == datamap.end()) return (IfaceData *)0; return (*iter).second; } +/// A single command line is read (via readLine) and executed. +/// If the command is successfully executed, the command line is +/// committed to history and \b true is returned. +/// \return \b true if a command successfully executes bool IfaceStatus::runCommand(void) { @@ -309,9 +353,16 @@ bool IfaceStatus::runCommand(void) return true; // Indicate a command was executed } -void IfaceStatus::restrict(vector::const_iterator &first, - vector::const_iterator &last, - vector &input) +/// \brief Restrict range of possible commands given a list of command line tokens +/// +/// Given a set of tokens partially describing a command, provide the most narrow +/// range of IfaceCommand objects that could be referred to. +/// \param first will hold an iterator to the first command in the range +/// \param last will hold an iterator (one after) the last command in the range +/// \param input is the list of command tokens to match on +void IfaceStatus::restrictCom(vector::const_iterator &first, + vector::const_iterator &last, + vector &input) { vector::const_iterator newfirst,newlast; @@ -347,16 +398,22 @@ static bool maxmatch(string &res,const string &op1,const string &op2) return true; } +/// \brief Expand tokens from the given input stream to a full command +/// +/// A range of possible commands is returned. Processing of the stream +/// stops as soon as at least one complete command is recognized. +/// Tokens partially matching a command are expanded to the full command +/// and passed back. +/// \param expand will hold the list of expanded tokens +/// \param s is the input stream tokens are read from +/// \param first will hold the beginning of the matching range of commands +/// \param last will hold the end of the matching range of commands +/// \return the number of matching commands int4 IfaceStatus::expandCom(vector &expand,istream &s, vector::const_iterator &first, vector::const_iterator &last) -{ // Expand tokens on stream to full command - // Return range of possible commands - // If command is complete with extra arguments - // return (dont process) remaining args - // Return number of matching commands - +{ int4 pos; // Which word are we currently expanding string tok; bool res; @@ -386,7 +443,7 @@ int4 IfaceStatus::expandCom(vector &expand,istream &s, } s >> tok; // Get next token expand.push_back(tok); - restrict(first,last,expand); + restrictCom(first,last,expand); if (first == last) // If subrange is empty, return 0 return 0; res = maxmatch(tok, (*first)->getCommandWord(pos), (*(last-1))->getCommandWord(pos)); @@ -403,9 +460,13 @@ void IfaceCommand::addWords(const vector &wordlist) com.push_back( *iter ); } +/// The commands are ordered lexicographically and alphabetically by +/// the comparing tokens in their respective command line strings +/// \param op2 is the other command to compare with \b this +/// \return -1, 0, 1 if \b this is earlier, equal to, or after to the other command int4 IfaceCommand::compare(const IfaceCommand &op2) const -{ // Sort command based on names +{ int4 res; vector::const_iterator iter1,iter2; @@ -424,12 +485,15 @@ int4 IfaceCommand::compare(const IfaceCommand &op2) const return 0; // Never reaches here } +/// \param res is overwritten with the full command line string void IfaceCommand::commandString(string &res) const { IfaceStatus::wordsToString(res,com); } +/// \class IfcQuit +/// \brief Quit command to terminate processing from the given interface void IfcQuit::execute(istream &s) { // Generic quit call back @@ -439,6 +503,8 @@ void IfcQuit::execute(istream &s) status->done = true; // Set flag to drop out of mainloop } +/// \class IfcHistory +/// \brief History command to list the most recent successful commands void IfcHistory::execute(istream &s) { // List most recent command lines @@ -462,6 +528,8 @@ void IfcHistory::execute(istream &s) } } +/// \class IfcOpenfile +/// \brief Open file command to redirect bulk output to a specific file stream void IfcOpenfile::execute(istream &s) { @@ -482,6 +550,8 @@ void IfcOpenfile::execute(istream &s) } } +/// \class IfcOpenfileAppend +/// \brief Open file command directing bulk output to be appended to a specific file void IfcOpenfileAppend::execute(istream &s) { @@ -502,6 +572,10 @@ void IfcOpenfileAppend::execute(istream &s) } } +/// \class IfcClosefile +/// \brief Close command, closing the current bulk output file. +/// +/// Subsequent bulk output is redirected to the basic interface output stream void IfcClosefile::execute(istream &s) { @@ -512,6 +586,8 @@ void IfcClosefile::execute(istream &s) status->fileoptr = status->optr; } +/// \class IfcEcho +/// \brief Echo command to echo the current command line to the bulk output stream void IfcEcho::execute(istream &s) { // Echo command line to fileoptr diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/interface.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/interface.hh index 8afe79f6bd..20077debb8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/interface.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/interface.hh @@ -13,23 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// Very generic command line executor class: IfaceStatus -// A new class instance derived from IfaceCommand is attached to a command line via registerCom -// i.e. -// IfaceStatus stat(cin,cout); -// stat.registerCom(new IfcQuit(),"quit"); -// stat.registerCom(new IfcOpenfileAppend(),"openfile","append"); -// stat.mainloop(); -// Command line processing is started with mainloop, which prints a -// prompt set with setprompt, allows bash style command line editing, including -// command completion and history, and executes the corresponding IfaceCommand.execute callback. -// Command words only have to match enough to disambiguate it from other commands. - -// Custom history size can be passed in constructor to IfaceStatus. -// Applications should inherit from base class IfaceStatus in order -// to get custom data into IfaceCommand callbacks and to redefine -// the virtual function execute for custom error handling. +/// \file interface.hh +/// \brief Classes and utilities for a \e generic command-line interface #ifndef __INTERFACE__ #define __INTERFACE__ @@ -70,43 +56,87 @@ public: #endif +/// \brief An exception specific to the command line interface struct IfaceError { - string explain; // Explanatory string - IfaceError(const string &s) { explain = s; } + string explain; ///< Explanatory string + IfaceError(const string &s) { explain = s; } ///< Constructor }; +/// \brief An exception describing a parsing error in a command line +/// +/// Thrown when attempting to parse a command line. Options are missing or are in +/// the wrong form etc. struct IfaceParseError : public IfaceError { - IfaceParseError(const string &s) : IfaceError(s) {} + IfaceParseError(const string &s) : IfaceError(s) {} ///< Constructor }; +/// \brief An exception throw during the execution of a command +/// +/// Processing of a specific command has started but has reached an error state struct IfaceExecutionError : public IfaceError { - IfaceExecutionError(const string &s) : IfaceError(s) {} + IfaceExecutionError(const string &s) : IfaceError(s) {} ///< Constructor }; class IfaceStatus; // Forward declaration -class IfaceData { // Data specialized for a particular command +/// \brief Data specialized for a particular command module +/// +/// IfaceCommands can have specialized data that is shared with other commands in +/// the same module. This is the root object for all such data. +class IfaceData { public: - virtual ~IfaceData(void) {} + virtual ~IfaceData(void) {} ///< Destructor }; +/// \brief A command that can be executed from the command line +/// +/// The command has data associated with it (via setData()) and is executed +/// via the execute() method. The command can get additional parameters from +/// the command line by reading the input stream passed to it. +/// The command is associated with a specific sequence of words (tokens) +/// that should appear at the start of the command line. class IfaceCommand { - vector com; // The command + vector com; ///< The token sequence associated with the command public: - virtual ~IfaceCommand(void) {} + virtual ~IfaceCommand(void) {} ///< Destructor + + /// \brief Associate a specific data object with this command. + /// + /// \param root is the interface object this command is registered with + /// \param data is the data object the command should use virtual void setData(IfaceStatus *root,IfaceData *data)=0; + + /// Execute this command. Additional state can be read from the given command line stream. + /// Otherwise, the command gets its data from its registered IfaceData object + /// \param s is the input stream from the command line virtual void execute(istream &s)=0; + + /// \brief Get the formal module name to which this command belongs + /// + /// Commands in the same module share data through their registered IfaceData object + /// \return the formal module name virtual string getModule(void) const=0; + + /// \brief Create a specialized data object for \b this command (and its module) + /// + /// This method is only called once per module + /// \return the newly created data object for the module virtual IfaceData *createData(void)=0; + + /// \brief Add a token to the command line string associated with this command + /// + /// \param temp is the new token to add void addWord(const string &temp) { com.push_back(temp); } - void removeWord(void) { com.pop_back(); } - const string &getCommandWord(int4 i) const { return com[i]; } - void addWords(const vector &wordlist); - int4 numWords(void) const { return com.size(); } - void commandString(string &res) const; - int4 compare(const IfaceCommand &op2) const; + + void removeWord(void) { com.pop_back(); } ///< Remove the last token from the associated command line string + const string &getCommandWord(int4 i) const { return com[i]; } ///< Get the i-th command token + void addWords(const vector &wordlist); ///< Add words to the associated command line string + int4 numWords(void) const { return com.size(); } ///< Return the number of tokens in the command line string + void commandString(string &res) const; ///< Get the complete command line string + int4 compare(const IfaceCommand &op2) const; ///< Order two commands by their command line strings }; +/// \brief A dummy command used during parsing class IfaceCommandDummy : public IfaceCommand { public: virtual void setData(IfaceStatus *root,IfaceData *data) {} @@ -115,73 +145,113 @@ public: virtual IfaceData *createData(void) { return (IfaceData *)0; } }; +/// \brief Compare to commands as pointers +/// +/// \param a is a pointer to the first command +/// \param b is a pointer to the second command +/// \return \b true if the first pointer is ordered before the second inline bool compare_ifacecommand(const IfaceCommand *a,const IfaceCommand *b) { return (0>a->compare(*b)); } +/// \brief Groups of console commands that are \e discovered by the loader +/// +/// Any IfaceCommand that is registered with a grouping derived from this class +/// is automatically made available to any IfaceStatus object just by calling +/// the static registerAllCommands() class IfaceCapability : public CapabilityPoint { - static vector thelist; + static vector thelist; ///< The global list of discovered command groupings protected: - string name; // Identifying name for the capability + string name; ///< Identifying name for the capability public: - const string &getName(void) const { return name; } + const string &getName(void) const { return name; } ///< Get the name of the capability virtual void initialize(void); - virtual void registerCommands(IfaceStatus *status)=0; + virtual void registerCommands(IfaceStatus *status)=0; ///< Register commands for \b this grouping - static void registerAllCommands(IfaceStatus *status); + static void registerAllCommands(IfaceStatus *status); ///< Register all discovered commands with the interface }; -/// \brief Current state of the console mode interface +/// \brief A generic console mode interface and command executor +/// +/// Input is provided one command line at a time by providing calling readLine(). +/// Output goes to a provided ostream, \e optr. Output to a separate bulk stream +/// can be enabled by setting \e fileoptr. +/// +/// A derived IfaceCommand is attached to a command string via registerCom() +/// i.e. +/// stat.registerCom(new IfcQuit(),"quit"); +/// stat.registerCom(new IfcOpenfileAppend(),"openfile","append"); +/// stat.mainloop(); + +/// Command line processing is started with mainloop(), which prints a command prompt, +/// allows command line editing, including command completion and history, and executes +/// the corresponding IfaceComman::execute() callback. +/// Command words only have to match enough to disambiguate it from other commands. + +/// A Custom history size and command prompt can be passed to the constructor. +/// Applications should inherit from base class IfaceStatus in order to +/// - Override the readLine() method +/// - Override pushScript() and popScript() to allow command scripts +/// - Get custom data into IfaceCommand callbacks class IfaceStatus { - vector promptstack; - vector flagstack; - string prompt; - int4 maxhistory; - int4 curhistory; // most recent history - vector history; - bool sorted; // Are commands sorted - bool errorisdone; // -true- if any error terminates the process - void restrict(vector::const_iterator &first,vector::const_iterator &last,vector &input); + vector promptstack; ///< Stack of command prompts corresponding to script nesting level + vector flagstack; ///< Stack of flag state corresponding to script nesting level + string prompt; ///< The current command prompt + int4 maxhistory; ///< Maximum number of command lines to store in history + int4 curhistory; ///< Most recent history + vector history; ///< History of commands executed through this interface + bool sorted; ///< Set to \b true if commands are sorted + bool errorisdone; ///< Set to \b true if any error terminates the process + void restrictCom(vector::const_iterator &first, + vector::const_iterator &last,vector &input); + + /// \brief Read the next command line + /// + /// \param line is filled in with the next command to execute virtual void readLine(string &line)=0; - void saveHistory(const string &line); + void saveHistory(const string &line); ///< Store the given command line into \e history protected: - bool inerror; // -true- if last command did not succeed - vector comlist; // List of commands - map datamap; // Data associated with particular modules + bool inerror; ///< Set to \b true if last command did not succeed + vector comlist; ///< List of registered commands + map datamap; ///< Data associated with particular modules int4 expandCom(vector &expand,istream &s, vector::const_iterator &first, vector::const_iterator &last); public: - bool done; - ostream *optr; // Where to put command line output - ostream *fileoptr; // Where to put bulk output + bool done; ///< Set to \b true (by a command) to indicate processing is finished + ostream *optr; ///< Where to put command line output + ostream *fileoptr; ///< Where to put bulk output - IfaceStatus(const string &prmpt,ostream &os,int4 mxhist=10); - virtual ~IfaceStatus(void); - void setErrorIsDone(bool val) { errorisdone = val; } + IfaceStatus(const string &prmpt,ostream &os,int4 mxhist=10); ///< Constructor + virtual ~IfaceStatus(void); ///< Destructor + void setErrorIsDone(bool val) { errorisdone = val; } ///< Set if processing should terminate on an error virtual void pushScript(const string &filename,const string &newprompt); virtual void popScript(void); - void reset(void); - int4 getNumInputStreamSize(void) const { return promptstack.size(); } - void writePrompt(void) { *optr << prompt; } + void reset(void); ///< Pop any existing script streams and return to processing from the base stream + int4 getNumInputStreamSize(void) const { return promptstack.size(); } ///< Get depth of script nesting + void writePrompt(void) { *optr << prompt; } ///< Write the current command prompt to the current output stream void registerCom(IfaceCommand *fptr, const char *nm1, const char *nm2 = (const char *)0, const char *nm3 = (const char *)0, const char *nm4 = (const char *)0, const char *nm5 = (const char *)0); - IfaceData *getData(const string &nm) const; - bool runCommand(void); - void getHistory(string &line,int4 i) const; - int4 getHistorySize(void) const { return history.size(); } - virtual bool isStreamFinished(void) const=0; - bool isInError(void) const { return inerror; } - void evaluateError(void); - static void wordsToString(string &res,const vector &list); + IfaceData *getData(const string &nm) const; ///< Get data associated with a IfaceCommand module + bool runCommand(void); ///< Run the next command + void getHistory(string &line,int4 i) const; ///< Get the i-th command line from history + int4 getHistorySize(void) const { return history.size(); } ///< Get the number of command lines in history + virtual bool isStreamFinished(void) const=0; ///< Return \b true if the current stream is finished + bool isInError(void) const { return inerror; } ///< Return \b true if the last command failed + void evaluateError(void); ///< Adjust which stream to process based on last error + static void wordsToString(string &res,const vector &list); ///< Concatenate tokens }; +/// \brief A root class for a basic set of commands +/// +/// Commands derived from this class are in the "base" module. +/// They are useful as part of any interface class IfaceBaseCommand : public IfaceCommand { protected: - IfaceStatus *status; + IfaceStatus *status; ///< The interface owning this command instance public: virtual void setData(IfaceStatus *root,IfaceData *data) { status = root; } virtual string getModule(void) const { return "base"; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh index fdced4e67a..2b29a6c855 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh @@ -500,7 +500,7 @@ class JumpTable { /// \brief An address table index and its corresponding out-edge struct IndexPair { int4 blockPosition; ///< Out-edge index for the basic-block - int4 addressIndex; /// Index of address targetting the basic-block + int4 addressIndex; ///< Index of address targeting the basic-block IndexPair(int4 pos,int4 index) { blockPosition = pos; addressIndex = index; } ///< Constructor bool operator<(const IndexPair &op2) const; ///< Compare by position then by index static bool compareByPosition(const IndexPair &op1,const IndexPair &op2); ///< Compare just by position diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc index 2ec50db12b..12dd1874e8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc @@ -838,6 +838,10 @@ string OptionAliasBlock::apply(Architecture *glb,const string &p1,const string & return "Alias block level set to " + p1; } +/// \class OptionMaxInstruction +/// \brief Maximum number of instructions that can be processed in a single function +/// +/// The first parameter is an integer specifying the maximum. string OptionMaxInstruction::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.hh index 5de1739874..053ec38d0f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.hh @@ -727,7 +727,7 @@ class EmitPrettyPrint : public EmitXml { void print(const TokenSplit &tok); ///< Output the given token to the low-level emitter void advanceleft(void); ///< Emit tokens that have been fully committed void scan(void); ///< Process a new token - void resetDefaultsPrettyPrint(void) { setMaxLineSize(100); } + void resetDefaultsPrettyPrint(void) { setMaxLineSize(100); } ///< Reset the defaults public: EmitPrettyPrint(void); ///< Construct with an initial maximum line size virtual ~EmitPrettyPrint(void); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index 8fed0baa45..79da4cfa31 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -199,9 +199,9 @@ void PrintC::pushSymbolScope(const Symbol *symbol) } } -/// Emit the elements of the given function's namespace path that distinguish it within +/// Emit the elements of the given symbol's namespace path that distinguish it within /// the current scope. -/// \param fd is the given function +/// \param symbol is the given Symbol void PrintC::emitSymbolScope(const Symbol *symbol) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index a097b4a446..a1c01498ed 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -9085,6 +9085,11 @@ int4 RulePiecePathology::applyOp(PcodeOp *op,Funcdata &data) return tracePathologyForward(op, data); } +/// \class RuleXorSwap +/// \brief Simplify limited chains of XOR operations +/// +/// `V = (a ^ b) ^ a => V = b` +/// `V = a ^ (b ^ a) => V = b` void RuleXorSwap::getOpList(vector &oplist) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.cc index 0c2d470e06..bf65a9d5f0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.cc @@ -32,10 +32,13 @@ PcodeCacher::~PcodeCacher(void) delete [] poolstart; } +/// Expand the VarnodeData pool so that \e size more elements fit, and return +/// a pointer to first available element. +/// \param size is the number of elements to expand the pool by +/// \return the first available VarnodeData VarnodeData *PcodeCacher::expandPool(uint4 size) -{ // Expand the pool so that -size- more elements fit - // Return pointer to first available element +{ uint4 curmax = endpool - poolstart; uint4 cursize = curpool - poolstart; if (cursize + size <= curmax) @@ -75,18 +78,26 @@ VarnodeData *PcodeCacher::expandPool(uint4 size) return newpool + cursize; } +/// Store off a reference to the Varnode and the absolute index of the next +/// instruction. The Varnode must be an operand of the current instruction. +/// \param ptr is the Varnode reference void PcodeCacher::addLabelRef(VarnodeData *ptr) -{ // Store off a reference to a label and the next instruction - // address +{ label_refs.emplace_back(); label_refs.back().dataptr = ptr; label_refs.back().calling_index = issued.size(); } +/// The label has an id that is referred to by Varnodes holding +/// intra-instruction branch targets, prior to converting +/// them to a \e relative \e branch offset. The label is associated with +/// the absolute index of the next PcodeData object to be issued, +/// facilitating this conversion. +/// \param id is the given id of the label void PcodeCacher::addLabel(uint4 id) -{ // Attach a label to the address of the next instruction +{ while(labels.size() <= id) labels.push_back(0xbadbeef); labels[ id ] = issued.size(); @@ -101,11 +112,12 @@ void PcodeCacher::clear(void) labels.clear(); } +/// Assuming all the PcodeData has been generated for an +/// instruction, go resolve any relative offsets and back +/// patch their value(s) into the PcodeData void PcodeCacher::resolveRelatives(void) -{ // Assuming all the PcodeData has been generated for an - // instruction, go resolve any relative offsets and back - // patch their value(s) into the PcodeData +{ list::const_iterator iter; for(iter=label_refs.begin();iter!=label_refs.end();++iter) { VarnodeData *ptr = (*iter).dataptr; @@ -119,18 +131,25 @@ void PcodeCacher::resolveRelatives(void) } } +/// Each p-code operation is presented to the emitter via its dump() method. +/// \param addr is the Address associated with the p-code operation +/// \param emt is the emitter void PcodeCacher::emit(const Address &addr,PcodeEmit *emt) const -{ // Emit any cached pcode +{ vector::const_iterator iter; for(iter=issued.begin();iter!=issued.end();++iter) emt->dump(addr,(*iter).opc,(*iter).outvar,(*iter).invar,(*iter).isize); } +/// \brief Generate a concrete VarnodeData object from the given template (VarnodeTpl) +/// +/// \param vntpl is the template to reference +/// \param vn is the object to fill in with concrete values void SleighBuilder::generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn) -{ // Generate a concrete varnode -vn- from the template -vntpl- +{ vn.space = vntpl->getSpace().fixSpace(*walker); vn.size = vntpl->getSize().fix(*walker); if (vn.space == const_space) @@ -143,9 +162,18 @@ void SleighBuilder::generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn) vn.offset = vn.space->wrapOffset(vntpl->getOffset().fix(*walker)); } +/// \brief Generate a pointer VarnodeData from a dynamic template (VarnodeTpl) +/// +/// The symbol represents a value referenced through a dynamic pointer. +/// This method generates the varnode representing the pointer itself and also +/// returns the address space in anticipation of generating the LOAD or STORE +/// that actually manipulates the value. +/// \param vntpl is the dynamic template to reference +/// \param vn is the object to fill with concrete values +/// \return the address space being pointed to AddrSpace *SleighBuilder::generatePointer(const VarnodeTpl *vntpl,VarnodeData &vn) -{ // Generate the pointer varnode -vn- from a dynamic template -vntpl- +{ const FixedHandle &hand(walker->getFixedHandle(vntpl->getOffset().getHandleIndex())); vn.space = hand.offset_space; vn.size = hand.offset_size; @@ -218,9 +246,17 @@ void SleighBuilder::dump(OpTpl *op) } } +/// \brief Build a named p-code section of a constructor that contains only implied BUILD directives +/// +/// If a named section of a constructor is empty, we still need to walk +/// through any subtables that might contain p-code in their named sections. +/// This method treats each subtable operand as an implied \e build directive, +/// in the otherwise empty section. +/// \param ct is the matching currently Constructor being built +/// \param secnum is the particular \e named section number to build void SleighBuilder::buildEmpty(Constructor *ct,int4 secnum) -{ // Build a named p-code section of a constructor that contains only implied BUILD directives +{ int4 numops = ct->getNumOperands(); for(int4 i=0;igetIn(0)->getOffset().getReal(); // Recover operand index from build statement // Check if operand is a subtable SubtableSymbol *sym = (SubtableSymbol *)walker->getConstructor()->getOperand(index)->getDefiningSymbol(); @@ -283,8 +331,9 @@ void SleighBuilder::appendBuild(OpTpl *bld,int4 secnum) void SleighBuilder::delaySlot(OpTpl *op) -{ // Append pcode for an entire instruction (delay slot) - // in the middle of the current instruction +{ + // Append pcode for an entire instruction (delay slot) + // in the middle of the current instruction ParserWalker *tmp = walker; uintb olduniqueoffset = uniqueoffset; @@ -319,7 +368,8 @@ void SleighBuilder::setLabel(OpTpl *op) void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum) -{ // Weave in the p-code section from an instruction at another address +{ + // Weave in the p-code section from an instruction at another address // bld-param(0) contains the address of the instruction // bld-param(1) contains the section number if (secnum>=0) @@ -352,6 +402,8 @@ void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum) uniqueoffset = olduniqueoffset; } +/// \param min is the minimum number of allocations before a reuse is expected +/// \param hashsize is the number of elements in the hash-table void DisassemblyCache::initialize(int4 min,int4 hashsize) { @@ -382,6 +434,10 @@ void DisassemblyCache::free(void) delete [] hashtable; } +/// \param ccache is the ContextCache front-end shared across all the parser contexts +/// \param cspace is the constant address space used for minting constant Varnodes +/// \param cachesize is the number of distinct ParserContext objects in this cache +/// \param windowsize is the size of the ParserContext hash-table DisassemblyCache::DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize) { @@ -390,13 +446,17 @@ DisassemblyCache::DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 c initialize(cachesize,windowsize); // Set default settings for the cache } +/// Return a (possibly cached) ParserContext that is associated with \e addr +/// If n different calls to this interface are made with n different Addresses, if +/// - n <= minimumreuse AND +/// - all the addresses are within the windowsize (=mask+1) +/// +/// then the cacher guarantees that you get all different ParserContext objects +/// \param addr is the Address to disassemble at +/// \return the ParserContext associated with the address ParserContext *DisassemblyCache::getParserContext(const Address &addr) -{ // Return a (possibly cached) ParserContext that is associated with -addr- - // If n different calls to this interface are made with n different Addresses, if - // n <= minimumreuse AND - // all the addresses are within the windowsize (=mask+1) - // then the cacher guarantees that you get all different ParserContext objects +{ int4 hashindex = ((int4) addr.getOffset()) & mask; ParserContext *res = hashtable[ hashindex ]; if (res->getAddr() == addr) @@ -411,6 +471,8 @@ ParserContext *DisassemblyCache::getParserContext(const Address &addr) return res; } +/// \param ld is the LoadImage to draw program bytes from +/// \param c_db is the context database Sleigh::Sleigh(LoadImage *ld,ContextDatabase *c_db) : SleighBase() @@ -435,10 +497,13 @@ Sleigh::~Sleigh(void) clearForDelete(); } +/// Completely clear everything except the base and reconstruct +/// with a new LoadImage and ContextDatabase +/// \param ld is the new LoadImage +/// \param c_db is the new ContextDatabase void Sleigh::reset(LoadImage *ld,ContextDatabase *c_db) -{ // Completely clear everything except the base and reconstruct - // with a new loader and context +{ clearForDelete(); pcode_cache.clear(); loader = ld; @@ -447,6 +512,8 @@ void Sleigh::reset(LoadImage *ld,ContextDatabase *c_db) discache = (DisassemblyCache *)0; } +/// The .sla file from the document store is loaded and cache objects are prepared +/// \param store is the document store containing the main \ tag. void Sleigh::initialize(DocumentStorage &store) { @@ -467,10 +534,18 @@ void Sleigh::initialize(DocumentStorage &store) discache = new DisassemblyCache(cache,getConstantSpace(),parser_cachesize,parser_windowsize); } +/// \brief Obtain a parse tree for the instruction at the given address +/// +/// The tree may be cached from a previous access. If the address +/// has not been parsed, disassembly is performed, and a new parse tree +/// is prepared. Depending on the desired \e state, the parse tree +/// can be prepared either for disassembly or for p-code generation. +/// \param addr is the given address of the instruction +/// \param state is the desired parse state. +/// \return the parse tree object (ParseContext) ParserContext *Sleigh::obtainContext(const Address &addr,int4 state) const -{ // Obtain a ParserContext for the instruction at the given -addr-. This may be cached. - // Make sure parsing has proceeded to at least the given -state. +{ ParserContext *pos = discache->getParserContext(addr); int4 curstate = pos->getParserState(); if (curstate >= state) @@ -485,10 +560,11 @@ ParserContext *Sleigh::obtainContext(const Address &addr,int4 state) const return pos; } +/// Resolve \e all the constructors involved in the instruction at the indicated address +/// \param pos is the parse object that will hold the resulting tree void Sleigh::resolve(ParserContext &pos) const -{ // Resolve ALL the constructors involved in the - // instruction at this address +{ loader->loadFill(pos.getBuffer(),16,pos.getAddr()); ParserWalkerChange walker(&pos); pos.deallocateState(walker); // Clear the previous resolve and initialize the walker @@ -538,9 +614,12 @@ void Sleigh::resolve(ParserContext &pos) const pos.setParserState(ParserContext::disassembly); } +/// Resolve handle templates for the given parse tree, assuming Constructors +/// are already resolved. +/// \param pos is the given parse tree void Sleigh::resolveHandles(ParserContext &pos) const -{ // Resolve handles (assuming Constructors already resolved) +{ TripleSymbol *triple; Constructor *ct; int4 oper,numoper; @@ -671,7 +750,7 @@ int4 Sleigh::oneInstruction(PcodeEmit &emit,const Address &baseaddr) const void Sleigh::registerContext(const string &name,int4 sbit,int4 ebit) -{ // Inform translator of existence of context variable +{ context_db->registerVariable(name,sbit,ebit); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.hh index 6519a09900..f4881b03fa 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.hh @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/// \file sleigh.hh +/// \brief Classes and utilities for the main SLEIGH engine + #ifndef __SLEIGH__ #define __SLEIGH__ @@ -20,29 +24,52 @@ class LoadImage; +/// \brief Class for describing a relative p-code branch destination +/// +/// An intra-instruction p-code branch takes a \e relative operand. +/// The actual value produced during p-code generation is calculated at +/// the last second using \b this. It stores the index of the BRANCH +/// instruction and a reference to its destination operand. This initially +/// holds a reference to a destination \e label symbol, but is later updated +/// with the final relative value. struct RelativeRecord { - VarnodeData *dataptr; // Record containing relative offset - uintb calling_index; // Index of instruction containing relative offset + VarnodeData *dataptr; ///< Varnode indicating relative offset + uintb calling_index; ///< Index of instruction containing relative offset }; -struct PcodeData { // Data for building one pcode instruction - OpCode opc; - VarnodeData *outvar; // Points to outvar is there is an output - VarnodeData *invar; // Inputs - int4 isize; // Number of inputs +/// \brief Data for building one p-code instruction +/// +/// Raw data used by the emitter to produce a single PcodeOp +struct PcodeData { + OpCode opc; ///< The op code + VarnodeData *outvar; ///< Output Varnode data (or null) + VarnodeData *invar; ///< Array of input Varnode data + int4 isize; ///< Number of input Varnodes }; -class PcodeCacher { // Cached chunk of pcode, prior to emitting - VarnodeData *poolstart; - VarnodeData *curpool; - VarnodeData *endpool; - vector issued; - list label_refs; // References to labels - vector labels; // Locations of labels - VarnodeData *expandPool(uint4 size); +/// \brief Class for caching a chunk of p-code, prior to emitting +/// +/// The engine accumulates PcodeData and VarnodeData objects for +/// a single instruction. Once the full instruction is constructed, +/// the objects are passed to the emitter (PcodeEmit) via the emit() method. +/// The class acts as a pool of memory for PcodeData and VarnodeData objects +/// that can be reused repeatedly to emit multiple instructions. +class PcodeCacher { + VarnodeData *poolstart; ///< Start of the pool of VarnodeData objects + VarnodeData *curpool; ///< First unused VarnodeData + VarnodeData *endpool; ///< End of the pool of VarnodeData objects + vector issued; ///< P-code ops issued for the current instruction + list label_refs; ///< References to labels + vector labels; ///< Locations of labels + VarnodeData *expandPool(uint4 size); ///< Expand the memory pool public: - PcodeCacher(void); - ~PcodeCacher(void); + PcodeCacher(void); ///< Constructor + ~PcodeCacher(void); ///< Destructor + + /// \brief Allocate data objects for a new set of Varnodes + /// + /// \param size is the number of objects to allocate + /// \return a pointer to the array of available VarnodeData objects VarnodeData *allocateVarnodes(uint4 size) { VarnodeData *newptr = curpool + size; if (newptr <= endpool) { @@ -52,6 +79,10 @@ public: } return expandPool(size); } + + /// \brief Allocate a data object for a new p-code operation + /// + /// \return the new PcodeData object PcodeData *allocateInstruction(void) { issued.emplace_back(); PcodeData *res = &issued.back(); @@ -59,41 +90,54 @@ public: res->invar = (VarnodeData *)0; return res; } - void addLabelRef(VarnodeData *ptr); - void addLabel(uint4 id); - void clear(void); - void resolveRelatives(void); - void emit(const Address &addr,PcodeEmit *emt) const; + void addLabelRef(VarnodeData *ptr); ///< Denote a Varnode holding a \e relative \e branch offset + void addLabel(uint4 id); ///< Attach a label to the \e next p-code instruction + void clear(void); ///< Reset the cache so that all objects are unallocated + void resolveRelatives(void); ///< Rewrite branch target Varnodes as \e relative offsets + void emit(const Address &addr,PcodeEmit *emt) const; ///< Pass the cached p-code data to the emitter }; +/// \brief A container for disassembly context used by the SLEIGH engine +/// +/// This acts as a factor for the ParserContext objects which are used to disassemble +/// a single instruction. These all share a ContextCache which is a front end for +/// accessing the ContextDatabase and resolving context variables from the SLEIGH spec. +/// ParserContext objects are stored in a hash-table keyed by the address of the instruction. class DisassemblyCache { - ContextCache *contextcache; - AddrSpace *constspace; - int4 minimumreuse; // Can call getParserContext this many times, before a ParserContext is reused - uint4 mask; // Size of the hashtable in form 2^n-1 - ParserContext **list; // (circular) array of currently cached ParserContext objects - int4 nextfree; // Current end/beginning of circular list - ParserContext **hashtable; // Hashtable for looking up ParserContext via Address - void initialize(int4 min,int4 hashsize); - void free(void); + ContextCache *contextcache; ///< Cached values from the ContextDatabase + AddrSpace *constspace; ///< The constant address space + int4 minimumreuse; ///< Can call getParserContext this many times, before a ParserContext is reused + uint4 mask; ///< Size of the hashtable in form 2^n-1 + ParserContext **list; ///< (circular) array of currently cached ParserContext objects + int4 nextfree; ///< Current end/beginning of circular list + ParserContext **hashtable; ///< Hashtable for looking up ParserContext via Address + void initialize(int4 min,int4 hashsize); ///< Initialize the hash-table of ParserContexts + void free(void); ///< Free the hash-table of ParserContexts public: - DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize); - ~DisassemblyCache(void) { free(); } - ParserContext *getParserContext(const Address &addr); + DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize); ///< Constructor + ~DisassemblyCache(void) { free(); } ///< Destructor + ParserContext *getParserContext(const Address &addr); ///< Get the parser for a particular Address }; +/// \brief Build p-code from a pre-parsed instruction +/// +/// Through the build() method, \b this walks the parse tree and prepares data +/// for final emission as p-code. (The final emitting is done separately through the +/// PcodeCacher.emit() method). Generally, only p-code for one instruction is prepared. +/// But, through the \b delay-slot mechanism, build() may recursively visit +/// additional instructions. class SleighBuilder : public PcodeBuilder { virtual void dump( OpTpl *op ); - AddrSpace *const_space; - AddrSpace *uniq_space; - uintb uniquemask; - uintb uniqueoffset; - DisassemblyCache *discache; - PcodeCacher *cache; + AddrSpace *const_space; ///< The constant address space + AddrSpace *uniq_space; ///< The unique address space + uintb uniquemask; ///< Mask of address bits to use to uniquify temporary registers + uintb uniqueoffset; ///< Uniquifier bits for \b this instruction + DisassemblyCache *discache; ///< Cache of disassembled instructions + PcodeCacher *cache; ///< Cache accumulating p-code data for the instruction void buildEmpty(Constructor *ct,int4 secnum); void generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn); AddrSpace *generatePointer(const VarnodeTpl *vntpl,VarnodeData &vn); - void setUniqueOffset(const Address &addr); + void setUniqueOffset(const Address &addr); ///< Set uniquifying bits for the current instruction public: SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCacher *pc,AddrSpace *cspc,AddrSpace *uspc,uint4 umask); virtual void appendBuild(OpTpl *bld,int4 secnum); @@ -102,21 +146,31 @@ public: virtual void appendCrossBuild(OpTpl *bld,int4 secnum); }; +/// \brief A full SLEIGH engine +/// +/// Its provided with a LoadImage of the bytes to be disassembled and +/// a ContextDatabase. +/// +/// Assembly is produced via the printAssembly() method, provided with an +/// AssemblyEmit object and an Address. +/// +/// P-code is produced via the oneInstruction() method, provided with a PcodeEmit +/// object and an Address. class Sleigh : public SleighBase { - LoadImage *loader; - ContextDatabase *context_db; - ContextCache *cache; - mutable DisassemblyCache *discache; - mutable PcodeCacher pcode_cache; - void clearForDelete(void); + LoadImage *loader; ///< The mapped bytes in the program + ContextDatabase *context_db; ///< Database of context values steering disassembly + ContextCache *cache; ///< Cache of recently used context values + mutable DisassemblyCache *discache; ///< Cache of recently parsed instructions + mutable PcodeCacher pcode_cache; ///< Cache of p-code data just prior to emitting + void clearForDelete(void); ///< Delete the context and disassembly caches protected: ParserContext *obtainContext(const Address &addr,int4 state) const; - void resolve(ParserContext &pos) const; - void resolveHandles(ParserContext &pos) const; + void resolve(ParserContext &pos) const; ///< Generate a parse tree suitable for disassembly + void resolveHandles(ParserContext &pos) const; ///< Prepare the parse tree for p-code generation public: - Sleigh(LoadImage *ld,ContextDatabase *c_db); - virtual ~Sleigh(void); - void reset(LoadImage *ld,ContextDatabase *c_db); + Sleigh(LoadImage *ld,ContextDatabase *c_db); ///< Constructor + virtual ~Sleigh(void); ///< Destructor + void reset(LoadImage *ld,ContextDatabase *c_db); ///< Reset the engine for a new program virtual void initialize(DocumentStorage &store); virtual void registerContext(const string &name,int4 sbit,int4 ebit); virtual void setContextDefault(const string &nm,uintm val); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/string_ghidra.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/string_ghidra.hh index 870d5cad0d..884e7e6719 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/string_ghidra.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/string_ghidra.hh @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/// \file ghidra_string.hh +/// \file string_ghidra.hh /// \brief Implementation of the StringManager through the ghidra client #ifndef __STRING_GHIDRA__ diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.hh index b8db95d2bd..64f091d234 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.hh @@ -30,10 +30,11 @@ class Architecture; /// Stores the decoded string until its needed for presentation. class StringManager { protected: + /// \brief String data (a sequence of bytes) stored by StringManager class StringData { public: - bool isTruncated; // \b true if the the string is truncated - vector byteData; // UTF8 encoded string data + bool isTruncated; ///< \b true if the the string is truncated + vector byteData; ///< UTF8 encoded string data }; map stringMap; ///< Map from address to string data int4 maximumChars; ///< Maximum characters in a string before truncating diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh index a278c219c7..382964fb60 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh @@ -201,7 +201,7 @@ public: bool isFloatExtension(void) const { return (pieces.size() == 1); } ///< Does this record extend a float varnode const VarnodeData &getPiece(int4 i) const { return pieces[i]; } ///< Get the i-th piece const VarnodeData &getUnified(void) const { return unified; } ///< Get the Varnode whole - Address getEquivalentAddress(uintb offset,int4 &pos) const; ///< Given offset in \join space, get equivalent address of piece + Address getEquivalentAddress(uintb offset,int4 &pos) const; ///< Given offset in \e join space, get equivalent address of piece bool operator<(const JoinRecord &op2) const; ///< Compare records lexigraphically by pieces }; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index da6c32bc05..b05109d696 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -318,7 +318,7 @@ void Datatype::saveXmlRef(ostream &s) const /// A CPUI_PTRSUB must act on a pointer data-type where the given offset addresses a component. /// Perform this check. -/// \param is the given offset +/// \param offset is the given offset /// \return \b true if \b this is a suitable PTRSUB data-type bool Datatype::isPtrsubMatching(uintb offset) const @@ -422,7 +422,7 @@ uint8 Datatype::hashName(const string &nm) /// The hashing is reversible by feeding the output ID back into this function with the same size. /// \param id is the given ID to (de)uniquify /// \param size is the instance size of the structure -/// \param return the (de)uniquified id +/// \return the (de)uniquified id uint8 Datatype::hashSize(uint8 id,int4 size) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh index 8e9fc15685..5f4552b909 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh @@ -123,6 +123,7 @@ public: /// Given a specific language and PcodeOp, emit the expression rooted at the operation. /// \param lng is the PrintLanguage to emit /// \param op is the specific PcodeOp + /// \param readOp is the PcodeOp consuming the output (or null) virtual void push(PrintLanguage *lng,const PcodeOp *op,const PcodeOp *readOp) const=0; /// \brief Print (for debugging purposes) \b this specific PcodeOp to the stream diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc index 6e8b0ab779..7a651026cf 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc @@ -22,7 +22,7 @@ /// properly, in which case the union of the two ranges can exist without /// destroying data-type information. /// \param b is the range to reconcile with \b this -/// \param \b true if the data-type information can be reconciled +/// \return \b true if the data-type information can be reconciled bool RangeHint::reconcile(const RangeHint *b) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc index f132dadf28..9192305070 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc @@ -399,7 +399,7 @@ void Varnode::setSymbolEntry(SymbolEntry *entry) /// This used when there is a constant address reference to the Symbol and the Varnode holds the /// reference, not the actual value of the Symbol. /// \param entry is a mapping to the given Symbol -/// \off is the byte offset into the Symbol of the reference +/// \param off is the byte offset into the Symbol of the reference void Varnode::setSymbolReference(SymbolEntry *entry,int4 off) {