Merge remote-tracking branch 'origin/caheckman_DecompilerTestFramework'

This commit is contained in:
ghidra1 2021-02-02 12:06:04 -05:00
commit 09478efc2a
62 changed files with 3126 additions and 1204 deletions

View file

@ -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 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
@ -870,18 +870,6 @@ GENERATE_XML = NO
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
# dump the program listings (including syntax highlighting
# and cross-referencing information) to the XML output. Note that

View file

@ -56,6 +56,9 @@ LNK=
# Source files
ALL_SOURCE= $(wildcard *.cc)
ALL_NAMES=$(subst .cc,,$(ALL_SOURCE))
UNITTEST_SOURCE= $(wildcard ../unittests/*.cc)
UNITTEST_NAMES=$(subst .cc,,$(UNITTEST_SOURCE))
UNITTEST_STRIP=$(subst ../unittests/,,$(UNITTEST_NAMES))
COREEXT_SOURCE= $(wildcard coreext_*.cc)
COREEXT_NAMES=$(subst .cc,,$(COREEXT_SOURCE))
@ -91,7 +94,7 @@ GHIDRA= ghidra_arch inject_ghidra ghidra_translate loadimage_ghidra \
# Additional files specific to the sleigh compiler
SLACOMP=slgh_compile slghparse slghscan
# Additional special files that should not be considered part of the library
SPECIAL=consolemain sleighexample test
SPECIAL=consolemain sleighexample test testfunction
# Any additional modules for the command line decompiler
EXTRA= $(filter-out $(CORE) $(DECCORE) $(SLEIGH) $(GHIDRA) $(SLACOMP) $(SPECIAL),$(ALL_NAMES))
@ -114,8 +117,8 @@ COMMANDLINE_NAMES=$(CORE) $(DECCORE) $(EXTRA) $(SLEIGH) consolemain
COMMANDLINE_DEBUG=-DCPUI_DEBUG -D__TERMINAL__
COMMANDLINE_OPT=-D__TERMINAL__
TEST_NAMES=$(CORE) $(DECCORE) $(SLEIGH) test
TEST_DEBUG=-D__TERMINAL__ -g -O0
TEST_NAMES=$(CORE) $(DECCORE) $(SLEIGH) $(EXTRA) testfunction test
TEST_DEBUG=-D__TERMINAL__
GHIDRA_NAMES=$(CORE) $(DECCORE) $(GHIDRA)
GHIDRA_NAMES_DBG=$(GHIDRA_NAMES) callgraph ifacedecomp ifaceterm interface
@ -136,7 +139,7 @@ LIBDECOMP_NAMES=$(CORE) $(DECCORE) $(EXTRA) $(SLEIGH)
# object file macros
COMMANDLINE_DBG_OBJS=$(COMMANDLINE_NAMES:%=com_dbg/%.o)
COMMANDLINE_OPT_OBJS=$(COMMANDLINE_NAMES:%=com_opt/%.o)
TEST_DEBUG_OBJS=$(TEST_NAMES:%=test_dbg/%.o)
TEST_DEBUG_OBJS=$(TEST_NAMES:%=test_dbg/%.o) $(UNITTEST_STRIP:%=test_dbg/%.o)
GHIDRA_DBG_OBJS=$(GHIDRA_NAMES_DBG:%=ghi_dbg/%.o)
GHIDRA_OPT_OBJS=$(GHIDRA_NAMES:%=ghi_opt/%.o)
SLEIGH_DBG_OBJS=$(SLEIGH_NAMES:%=sla_dbg/%.o)
@ -214,7 +217,9 @@ com_dbg/%.o: %.cc
com_opt/%.o: %.cc
$(CXX) $(ARCH_TYPE) -c $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(COMMANDLINE_OPT) $< -o $@
test_dbg/%.o: %.cc
$(CXX) $(ARCH_TYPE) -c $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) $< -o $@
$(CXX) $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) $< -o $@
test_dbg/%.o: ../unittests/%.cc
$(CXX) -I. $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) $< -o $@
ghi_dbg/%.o: %.cc
$(CXX) $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(GHIDRA_DEBUG) $< -o $@
ghi_opt/%.o: %.cc
@ -248,7 +253,7 @@ decomp_opt: $(COMMANDLINE_OPT_OBJS)
$(CXX) $(OPT_CXXFLAGS) $(ARCH_TYPE) -o decomp_opt $(COMMANDLINE_OPT_OBJS) $(BFDLIB) $(LNK)
ghidra_test_dbg: $(TEST_DEBUG_OBJS)
$(CXX) $(OPT_CXXFLAGS) $(ARCH_TYPE) -o ghidra_test_dbg $(TEST_DEBUG_OBJS) $(BFDLIB) $(LNK)
$(CXX) $(DBG_CXXFLAGS) $(ARCH_TYPE) -o ghidra_test_dbg $(TEST_DEBUG_OBJS) $(BFDLIB) $(LNK)
test: ghidra_test_dbg
./ghidra_test_dbg
@ -339,10 +344,10 @@ com_opt/depend: $(COMMANDLINE_NAMES:%=%.cc)
sed 's,\(.*\)\.o[ :]*,com_opt/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
test_dbg/depend: $(TEST_NAMES:%=%.cc)
test_dbg/depend: $(TEST_NAMES:%=%.cc) $(UNITTEST_NAMES:%=%.cc)
mkdir -p test_dbg
@set -e; rm -f $@; \
$(CXX) -MM $(TEST_DEBUG) $^ > $@.$$$$; \
$(CXX) -I. -MM $(TEST_DEBUG) $^ > $@.$$$$; \
sed 's,\(.*\)\.o[ :]*,test_dbg/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$

View file

@ -64,6 +64,20 @@ ArchitectureCapability *ArchitectureCapability::findCapability(Document *doc)
return (ArchitectureCapability *)0;
}
/// Return the ArchitectureCapability object with the matching name
/// \param name is the name to match
/// \return the ArchitectureCapability or null if no match is found
ArchitectureCapability *ArchitectureCapability::getCapability(const string &name)
{
for(int4 i=0;i<thelist.size();++i) {
ArchitectureCapability *res = thelist[i];
if (res->getName() == name)
return res;
}
return (ArchitectureCapability *)0;
}
/// Modify order that extensions are searched, to effect which gets a chance
/// to run first.
/// Right now all we need to do is make sure the raw architecture comes last

View file

@ -104,6 +104,7 @@ public:
static ArchitectureCapability *findCapability(const string &filename); ///< Find an extension to process a file
static ArchitectureCapability *findCapability(Document *doc); ///< Find an extension to process an XML document
static ArchitectureCapability *getCapability(const string &name); ///< Get a capability by name
static void sortCapabilities(void); ///< Sort extensions
static uint4 getMajorVersion(void) { return majorversion; } ///< Get \e major decompiler version
static uint4 getMinorVersion(void) { return minorversion; } ///< Get \e minor decompiler version

View file

@ -574,7 +574,10 @@ int4 FlowBlock::getOutIndex(const FlowBlock *bl) const
void FlowBlock::printHeader(ostream &s) const
{
s << dec << index << ' ' << getStart() << '-' << getStop();
s << dec << index;
if (!getStart().isInvalid() && !getStop().isInvalid()) {
s << ' ' << getStart() << '-' << getStop();
}
}
/// Recursively print out the hierarchical structure of \b this FlowBlock.

View file

@ -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; }

View file

@ -54,8 +54,8 @@ public:
};
Comment(uint4 tp,const Address &fad,const Address &ad,int4 uq,const string &txt); ///< Constructor
Comment(void) {} ///< Constructor for use with restoreXml
void setEmitted(bool val) const { emitted = val; }
bool isEmitted(void) const { return emitted; }
void setEmitted(bool val) const { emitted = val; } ///< Mark that \b this comment has been emitted
bool isEmitted(void) const { return emitted; } ///< Return \b true if \b this comment is already emitted
uint4 getType(void) const { return type; } ///< Get the properties associated with the comment
const Address &getFuncAddr(void) const { return funcaddr; } ///< Get the address of the function containing the comment
const Address &getAddr(void) const { return addr; } ///< Get the address to which the instruction is attached

View file

@ -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<Varnode *> &worklist)

View file

@ -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)
{

View file

@ -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),

View file

@ -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

View file

@ -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)

View file

@ -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<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc)
{

View file

@ -127,7 +127,7 @@ public:
bool getSendParamMeasures(void) const { return sendParamMeasures; } ///< Get the current setting for emitting parameter info
virtual void getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc);
void getStringData(vector<uint1> &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

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Interface to the decompilation routines
/// \file ifacedecomp.hh
/// \brief Console interface commands for the decompiler engine
#ifndef __IFACE_DECOMP__
#define __IFACE_DECOMP__
@ -27,20 +28,22 @@
#include "rulecompile.hh"
#endif
/// \brief Interface capability point for all decompiler commands
class IfaceDecompCapability : public IfaceCapability {
static IfaceDecompCapability ifaceDecompCapability; // Singleton instance
IfaceDecompCapability(void); // Singleton
IfaceDecompCapability(const IfaceDecompCapability &op2); // Not implemented
IfaceDecompCapability &operator=(const IfaceDecompCapability &op2); // Not implemented
static IfaceDecompCapability ifaceDecompCapability; ///< Singleton instance
IfaceDecompCapability(void); ///< Singleton constructor
IfaceDecompCapability(const IfaceDecompCapability &op2); ///< Not implemented
IfaceDecompCapability &operator=(const IfaceDecompCapability &op2); ///< Not implemented
public:
virtual void registerCommands(IfaceStatus *status);
};
/// \brief Common data shared by decompiler commands
class IfaceDecompData : public IfaceData {
public:
Funcdata *fd; // Current function data
Architecture *conf;
CallGraph *cgraph;
Funcdata *fd; ///< Current function active in the console
Architecture *conf; ///< Current architecture/program active in the console
CallGraph *cgraph; ///< Call-graph information for the program
map<Funcdata*,PrototypePieces> prototypePieces;
void storePrototypePieces( Funcdata *fd_in, PrototypePieces pp_in ) { prototypePieces.insert(pair<Funcdata*,PrototypePieces>(fd_in,pp_in)); }
@ -52,18 +55,24 @@ public:
#ifdef OPACTION_DEBUG
bool jumptabledebug;
#endif
IfaceDecompData(void);
IfaceDecompData(void); ///< Constructor
virtual ~IfaceDecompData(void);
void allocateCallGraph(void);
void abortFunction(ostream &s);
void clearArchitecture(void);
void allocateCallGraph(void); ///< Allocate the call-graph object
void abortFunction(ostream &s); ///< Clear references to current function
void clearArchitecture(void); ///< Free all resources for the current architecture/program
void followFlow(ostream &s,int4 size);
Varnode *readVarnode(istream &s); ///< Read a varnode from the given stream
};
/// \brief Disassembly emitter that prints to a console stream
///
/// An instruction is printed to a stream simply, as an address
/// followed by the mnemonic and then column aligned operands.
class IfaceAssemblyEmit : public AssemblyEmit {
int4 mnemonicpad; // How much to pad the mnemonic
ostream *s;
int4 mnemonicpad; ///< How much to pad the mnemonic
ostream *s; ///< The current stream to write to
public:
IfaceAssemblyEmit(ostream *val,int4 mp) { s = val; mnemonicpad=mp; }
IfaceAssemblyEmit(ostream *val,int4 mp) { s = val; mnemonicpad=mp; } ///< Constructor
virtual void dump(const Address &addr,const string &mnem,const string &body) {
addr.printRaw(*s);
*s << ": " << mnem;
@ -72,22 +81,31 @@ public:
}
};
extern void execute(IfaceStatus *status,IfaceDecompData *dcp);
extern void mainloop(IfaceStatus *status);
extern void execute(IfaceStatus *status,IfaceDecompData *dcp); ///< Execute one command for the console
extern void mainloop(IfaceStatus *status); ///< Execute commands as they become available
/// \brief Root class for all decompiler specific commands
///
/// Commands share the data object IfaceDecompData and are capable of
/// iterating over all functions in the program/architecture.
class IfaceDecompCommand : public IfaceCommand {
protected:
IfaceStatus *status;
IfaceDecompData *dcp;
void iterateScopesRecursive(Scope *scope);
void iterateFunctionsAddrOrder(Scope *scope);
IfaceStatus *status; ///< The console owning \b this command
IfaceDecompData *dcp; ///< Data common to decompiler commands
void iterateScopesRecursive(Scope *scope); ///< Iterate recursively over all functions in given scope
void iterateFunctionsAddrOrder(Scope *scope); ///< Iterate over all functions in a given scope
public:
virtual void setData(IfaceStatus *root,IfaceData *data) { status = root; dcp = (IfaceDecompData *)data; }
virtual string getModule(void) const { return "decompile"; }
virtual IfaceData *createData(void) { return new IfaceDecompData(); }
/// \brief Perform the per-function aspect of \b this command.
///
/// \param fd is the particular function to operate on
virtual void iterationCallback(Funcdata *fd) {}
void iterateFunctionsAddrOrder(void);
void iterateFunctionsLeafOrder(void);
void iterateFunctionsAddrOrder(void); ///< Iterate command over all functions in all scopes
void iterateFunctionsLeafOrder(void); ///< Iterate command over all functions in a call-graph traversal
};
class IfcSource : public IfaceDecompCommand {
@ -257,11 +275,6 @@ public:
virtual void execute(istream &s);
};
class IfcBreakjump : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPrintTree : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
@ -282,18 +295,10 @@ public:
virtual void execute(istream &s);
};
class IfcParamIDAnalysis : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPrintParamMeasures : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPrintParamMeasuresXml : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcRename : public IfaceDecompCommand {
public:
@ -403,6 +408,10 @@ public:
class IfcPrintInputs : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
static bool nonTrivialUse(Varnode *vn); ///< Check for non-trivial use of given Varnode
static int4 checkRestore(Varnode *vn); ///< Check if a Varnode is \e restored to its original input value
static bool findRestore(Varnode *vn,Funcdata *fd); ///< Check if storage is \e restored
static void print(Funcdata *fd,ostream &s); ///< Print information about function inputs
};
class IfcPrintInputsAll : public IfaceDecompCommand {
@ -465,6 +474,8 @@ class IfcDuplicateHash : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
virtual void iterationCallback(Funcdata *fd);
static void check(Funcdata *fd,ostream &s); ///< Check for duplicate hashes in given function
};
class IfcCallGraphDump : public IfaceDecompCommand {
@ -474,7 +485,7 @@ public:
class IfcCallGraphBuild : public IfaceDecompCommand {
protected:
bool quick;
bool quick; ///< Set to \b true if a quick analysis is desired
public:
virtual void execute(istream &s);
virtual void iterationCallback(Funcdata *fd);
@ -490,8 +501,6 @@ public:
};
class IfcCallGraphList : public IfaceDecompCommand {
protected:
bool quick;
public:
virtual void execute(istream &s);
virtual void iterationCallback(Funcdata *fd);
@ -505,6 +514,8 @@ public:
class IfcCallFixup : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
static void readPcodeSnippet(istream &s,string &name,string &outname,vector<string> &inname,
string &pcodestring);
};
class IfcCallOtherFixup : public IfaceDecompCommand {
@ -557,12 +568,12 @@ class IfcParseRule : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
#endif
class IfcExperimentalRules : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
#endif
#ifdef OPACTION_DEBUG
class IfcDebugAction : public IfaceDecompCommand {
@ -600,6 +611,11 @@ public:
virtual void execute(istream &s);
};
class IfcBreakjump : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
#endif
#endif

View file

@ -16,8 +16,9 @@
#include "ifaceterm.hh"
IfaceTerm::IfaceTerm(const string &prmpt,istream &is,ostream &os)
: IfaceStatus(prmpt,is,os)
: IfaceStatus(prmpt,os)
{
sptr = &is;
#ifdef __TERMINAL__
struct termios ittypass;
@ -57,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<string> fullcommand;
istringstream s(line);
string tok;
@ -229,3 +237,28 @@ void IfaceTerm::readLine(string &line)
} while(val != '\n');
}
void IfaceTerm::pushScript(const string &filename,const string &newprompt)
{
ifstream *s = new ifstream(filename.c_str());
if (!*s)
throw IfaceParseError("Unable to open script file");
inputstack.push_back(sptr);
sptr = s;
IfaceStatus::pushScript(filename,newprompt);
}
void IfaceTerm::popScript(void)
{
delete sptr;
sptr = inputstack.back();
inputstack.pop_back();
}
bool IfaceTerm::isStreamFinished(void) const
{
if (done||inerror) return true;
return sptr->eof();
}

View file

@ -13,7 +13,9 @@
* 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,15 +25,26 @@ 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
int4 doCompletion(string &line,int4 cursor);
istream *sptr; ///< The base input stream for the interface
vector<istream *> 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);
virtual bool isStreamFinished(void) const;
};

View file

@ -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;i<thelist.size();++i)
thelist[i]->registerCommands(status);
}
@ -114,10 +117,12 @@ bool RemoteSocket::isSocketOpen(void)
#endif
IfaceStatus::IfaceStatus(const string &prmpt,istream &is,ostream &os,int4 mxhist)
/// \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)
{
sptr = &is;
optr = &os;
fileoptr = optr; // Bulk out, defaults to command line output
sorted = false;
@ -129,28 +134,30 @@ IfaceStatus::IfaceStatus(const string &prmpt,istream &is,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)
ifstream *s = new ifstream(filename.c_str());
if (!*s)
throw IfaceParseError("Unable to open script file");
inputstack.push_back(sptr);
{
promptstack.push_back(prompt);
uint4 flags = 0;
if (errorisdone)
flags |= 1;
flagstack.push_back(flags);
sptr = s;
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)
delete sptr;
sptr = inputstack.back();
inputstack.pop_back();
{
prompt = promptstack.back();
promptstack.pop_back();
uint4 flags = flagstack.back();
@ -162,15 +169,17 @@ void IfaceStatus::popScript(void)
void IfaceStatus::reset(void)
{
while(!inputstack.empty())
while(!promptstack.empty())
popScript();
errorisdone = false;
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
@ -180,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
{
@ -191,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;
@ -208,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<string> &list)
{
@ -228,7 +243,7 @@ IfaceStatus::~IfaceStatus(void)
((ofstream *)fileoptr)->close();
delete fileoptr;
}
while(!inputstack.empty())
while(!promptstack.empty())
popScript();
for(int4 i=0;i<comlist.size();++i)
delete comlist[i];
@ -238,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);
@ -270,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<string,IfaceData *>::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)
{
@ -318,9 +353,16 @@ bool IfaceStatus::runCommand(void)
return true; // Indicate a command was executed
}
void IfaceStatus::restrict(vector<IfaceCommand *>::const_iterator &first,
vector<IfaceCommand *>::const_iterator &last,
vector<string> &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<IfaceCommand *>::const_iterator &first,
vector<IfaceCommand *>::const_iterator &last,
vector<string> &input)
{
vector<IfaceCommand *>::const_iterator newfirst,newlast;
@ -356,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<string> &expand,istream &s,
vector<IfaceCommand *>::const_iterator &first,
vector<IfaceCommand *>::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;
@ -395,7 +443,7 @@ int4 IfaceStatus::expandCom(vector<string> &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));
@ -412,9 +460,13 @@ void IfaceCommand::addWords(const vector<string> &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<string>::const_iterator iter1,iter2;
@ -433,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
@ -448,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
@ -471,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)
{
@ -491,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)
{
@ -511,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)
{
@ -521,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

View file

@ -4,32 +4,17 @@
* 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.
*/
// 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 +55,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<string> com; // The command
vector<string> 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<string> &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<string> &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,74 +144,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<IfaceCapability *> thelist;
static vector<IfaceCapability *> 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 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<istream *> inputstack;
vector<string> promptstack;
vector<uint4> flagstack;
string prompt;
int4 maxhistory;
int4 curhistory; // most recent history
vector<string> history;
bool sorted; // Are commands sorted
bool inerror; // -true- if last command did not succeed
bool errorisdone; // -true- if any error terminates the process
void restrict(vector<IfaceCommand *>::const_iterator &first,vector<IfaceCommand *>::const_iterator &last,vector<string> &input);
virtual void readLine(string &line) { getline(*sptr,line,'\n'); }
void saveHistory(const string &line);
vector<string> promptstack; ///< Stack of command prompts corresponding to script nesting level
vector<uint4> 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<string> 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<IfaceCommand *>::const_iterator &first,
vector<IfaceCommand *>::const_iterator &last,vector<string> &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); ///< Store the given command line into \e history
protected:
istream *sptr; // Where to get input
vector<IfaceCommand *> comlist; // List of commands
map<string,IfaceData *> datamap; // Data associated with particular modules
bool inerror; ///< Set to \b true if last command did not succeed
vector<IfaceCommand *> comlist; ///< List of registered commands
map<string,IfaceData *> datamap; ///< Data associated with particular modules
int4 expandCom(vector<string> &expand,istream &s,
vector<IfaceCommand *>::const_iterator &first,
vector<IfaceCommand *>::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,istream &is,ostream &os,int4 mxhist=10);
virtual ~IfaceStatus(void);
void setErrorIsDone(bool val) { errorisdone = val; }
void pushScript(const string &filename,const string &newprompt);
void popScript(void);
void reset(void);
int4 getNumInputStreamSize(void) const { return inputstack.size(); }
void writePrompt(void) { *optr << prompt; }
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); ///< 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(); }
bool isStreamFinished(void) const { if (done||inerror) return true; return sptr->eof(); }
bool isInError(void) const { return inerror; }
void evaluateError(void);
static void wordsToString(string &res,const vector<string> &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<string> &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"; }

View file

@ -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

View file

@ -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
{

View file

@ -249,7 +249,7 @@ void Override::printRaw(ostream &s,Architecture *glb) const
s << "dead code delay on " << spc->getName() << " set to " << dec << deadcodedelay[i] << endl;
}
for(iter=forcegoto.begin();iter!=forcegoto.end();++iter)
for(iter=indirectover.begin();iter!=indirectover.end();++iter)
s << "override indirect at " << (*iter).first << " to call directly to " << (*iter).second << endl;
map<Address,FuncProto *>::const_iterator fiter;

View file

@ -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);

View file

@ -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)
{

View file

@ -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<uint4> &oplist) const
{

View file

@ -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<RelativeRecord>::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<PcodeData>::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;i<numops;++i) {
@ -238,12 +274,23 @@ void SleighBuilder::buildEmpty(Constructor *ct,int4 secnum)
}
}
/// Bits used to make temporary registers unique across multiple instructions
/// are generated based on the given address.
/// \param addr is the given Address
void SleighBuilder::setUniqueOffset(const Address &addr)
{
uniqueoffset = (addr.getOffset() & uniquemask)<<4;
}
/// \brief Constructor
///
/// \param w is the parsed instruction
/// \param dcache is a cache of nearby instruction parses
/// \param pc will hold the PcodeData and VarnodeData objects produced by \b this builder
/// \param cspc is the constant address space
/// \param uspc is the unique address space
/// \param umask is the mask to use to find unique bits within an Address
SleighBuilder::SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCacher *pc,AddrSpace *cspc,
AddrSpace *uspc,uint4 umask)
: PcodeBuilder(0)
@ -259,7 +306,8 @@ SleighBuilder::SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCache
void SleighBuilder::appendBuild(OpTpl *bld,int4 secnum)
{ // Append pcode for a particular build statement
{
// Append p-code for a particular build statement
int4 index = bld->getIn(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 \<sleigh> 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);
}

View file

@ -13,6 +13,9 @@
* 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 +23,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<PcodeData> issued;
list<RelativeRecord> label_refs; // References to labels
vector<uintb> 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<PcodeData> issued; ///< P-code ops issued for the current instruction
list<RelativeRecord> label_refs; ///< References to labels
vector<uintb> 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 +78,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 +89,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 +145,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);

View file

@ -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__

View file

@ -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<uint1> byteData; // UTF8 encoded string data
bool isTruncated; ///< \b true if the the string is truncated
vector<uint1> byteData; ///< UTF8 encoded string data
};
map<Address,StringData> stringMap; ///< Map from address to string data
int4 maximumChars; ///< Maximum characters in a string before truncating

View file

@ -162,6 +162,12 @@ public:
bool doTrace(void); ///< Trace logical value as far as possible
};
/// \brief Class for splitting data-flow on \e laned registers
///
/// From a root Varnode and a description of its \e lanes, trace data-flow as far as
/// possible through the function, propagating each lane, using the doTrace() method. Then
/// using the apply() method, data-flow can be split, making each lane in every traced
/// register into an explicit Varnode
class LaneDivide : public TransformManager {
/// \brief Description of a large Varnode that needs to be traced (in the worklist)
class WorkNode {

View file

@ -13,470 +13,81 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// \file test.cc
/// \brief Unit tests for Ghidra C++ components.
#include "float.hh"
#include "opbehavior.hh"
#include "test.hh"
#include "testfunction.hh"
#include <cmath>
#include <cstdint>
#include <cstring>
vector<UnitTest *> UnitTest::tests;
#include <limits>
#include <vector>
/// Run all the tests unless a non-empty set of names is passed in.
/// In which case, only the named tests in the set are run.
/// \param testNames is the set of names
void UnitTest::run(set<string> &testNames)
// utility functions
float floatFromRawBits(uintb e) {
float f;
memcpy(&f, &e, 4);
return f;
}
{
int total = 0;
int passed = 0;
uintb floatToRawBits(float f) {
uintb result = 0;
memcpy(&result, &f, 4);
return result;
}
double doubleFromRawBits(uintb e) {
double f;
memcpy(&f, &e, 8);
return f;
}
uintb doubleToRawBits(double f) {
uintb result = 0;
memcpy(&result, &f, 8);
return result;
}
// macros to preserve call site
#define ASSERT_FLOAT_ENCODING(f) \
do { \
FloatFormat format(4); \
\
uintb true_encoding = floatToRawBits(f); \
uintb encoding = format.getEncoding(f); \
\
ASSERT_EQUALS(true_encoding, encoding); \
} while (0);
#define ASSERT_DOUBLE_ENCODING(f) \
do { \
FloatFormat format(8); \
\
uintb true_encoding = doubleToRawBits(f); \
uintb encoding = format.getEncoding(f); \
\
ASSERT_EQUALS(true_encoding, encoding); \
} while (0);
//// FloatFormat tests
static std::vector<float> float_test_values{
-0.0f,
+0.0f,
-1.0f,
+1.0f,
-1.234f,
+1.234f,
-std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::min() - std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::min(),
std::numeric_limits<float>::min() + std::numeric_limits<float>::denorm_min(),
-std::numeric_limits<float>::min() + std::numeric_limits<float>::denorm_min(),
-std::numeric_limits<float>::min(),
-std::numeric_limits<float>::min() - std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::max(),
std::numeric_limits<float>::quiet_NaN(),
-std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::infinity()
};
static std::vector<int> int_test_values = {
0, -1, 1, 1234, -1234, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()
};
TEST(float_encoding_normal) {
ASSERT_FLOAT_ENCODING(1.234);
ASSERT_FLOAT_ENCODING(-1.234);
}
TEST(double_encoding_normal) {
ASSERT_DOUBLE_ENCODING(1.234);
ASSERT_DOUBLE_ENCODING(-1.234);
}
TEST(float_encoding_nan) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::quiet_NaN());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::quiet_NaN());
}
TEST(double_encoding_nan) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::quiet_NaN());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::quiet_NaN());
}
TEST(float_encoding_subnormal) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::denorm_min());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::denorm_min());
}
TEST(double_encoding_subnormal) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::denorm_min());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::denorm_min());
}
TEST(float_encoding_min_normal) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::min());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::min());
}
TEST(double_encoding_min_normal) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::min());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::min());
}
TEST(float_encoding_infinity) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::infinity());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::infinity());
}
TEST(double_encoding_infinity) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::infinity());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::infinity());
}
TEST(float_midpoint_rounding) {
FloatFormat ff(4);
// IEEE754 recommends "round to nearest even" for binary formats, like single and double
// precision floating point. It rounds to the nearest integer (significand) when unambiguous,
// and to the nearest even on the midpoint.
// There are 52 bits of significand in a double and 23 in a float.
// Below we construct a sequence of double precision values to demonstrate each case
// in rounding,
// d0 - zeros in low 29 bits, round down
// d1 - on the rounding midpoint with integer even integer part, round down
// d2 - just above the midpoint, round up
double d0 = doubleFromRawBits(0x4010000000000000L);
double d1 = doubleFromRawBits(0x4010000010000000L);
double d2 = doubleFromRawBits(0x4010000010000001L);
// d3 - zeros in low 29 bits, round down
// d4 - on the rounding midpoint with integer part odd, round up
// d5 - just above the midpoint, round up
double d3 = doubleFromRawBits(0x4010000020000000L);
double d4 = doubleFromRawBits(0x4010000030000000L);
double d5 = doubleFromRawBits(0x4010000030000001L);
float f0 = (float)d0;
float f1 = (float)d1;
float f2 = (float)d2;
float f3 = (float)d3;
float f4 = (float)d4;
float f5 = (float)d5;
uintb e0 = ff.getEncoding(d0);
uintb e1 = ff.getEncoding(d1);
uintb e2 = ff.getEncoding(d2);
uintb e3 = ff.getEncoding(d3);
uintb e4 = ff.getEncoding(d4);
uintb e5 = ff.getEncoding(d5);
ASSERT_EQUALS(floatToRawBits(f0), e0);
ASSERT_EQUALS(floatToRawBits(f1), e1);
ASSERT_EQUALS(floatToRawBits(f2), e2);
ASSERT_EQUALS(floatToRawBits(f3), e3);
ASSERT_EQUALS(floatToRawBits(f4), e4);
ASSERT_EQUALS(floatToRawBits(f5), e5);
ASSERT_EQUALS(e0, e1);
ASSERT_NOT_EQUALS(e1, e2);
ASSERT_NOT_EQUALS(e3, e4);
ASSERT_EQUALS(e4, e5);
}
// op tests
// generated
TEST(float_opNan) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = isnan(f);
uintb encoding = format.getEncoding(f);
uintb result = format.opNan(encoding);
ASSERT_EQUALS(true_result, result);
for(auto &t : UnitTest::tests) {
if (testNames.size() > 0 && testNames.find(t->name) == testNames.end()) {
continue;
}
}
TEST(float_opNeg) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(-f);
uintb encoding = format.getEncoding(f);
uintb result = format.opNeg(encoding);
ASSERT_EQUALS(true_result, result);
std::cerr << "testing : " << t->name << " ..." << std::endl;
++total;
try {
t->func();
++passed;
std::cerr << " passed." << std::endl;
} catch(...) {
}
}
std::cerr << "==============================" << std::endl;
std::cerr << passed << "/" << total << " tests passed." << std::endl;
}
int main(int argc, char **argv) {
bool runUnitTests = true;
bool runDataTests = true;
TEST(float_opAbs) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(abs(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opAbs(encoding);
ASSERT_EQUALS(true_result, result);
argc -= 1;
argv += 1;
set<string> unitTestNames;
set<string> dataTestNames;
string dirname("../datatests");
if (argc > 0) {
string command(argv[0]);
if (command == "-path") {
dirname = argv[1];
runDataTests = true;
argv += 2;
argc -= 2;
}
}
TEST(float_opSqrt) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(sqrtf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opSqrt(encoding);
ASSERT_EQUALS(true_result, result);
}
if (argc > 0) {
string command(argv[0]);
if (command == "unittests") {
runUnitTests = true;
runDataTests = false; // Run only unit tests
unitTestNames.insert(argv + 1,argv + argc);
}
}
TEST(float_opCeil) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(ceilf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opCeil(encoding);
ASSERT_EQUALS(true_result, result);
else if (command == "datatests") {
runUnitTests = false; // Run only data-tests
runDataTests = true;
dataTestNames.insert(argv + 1,argv + argc);
}
}
TEST(float_opFloor) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(floorf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opFloor(encoding);
ASSERT_EQUALS(true_result, result);
else {
cout << "USAGE: ghidra_test [-path <datatestdir>] [[unittests|datatests] [testname1 testname2 ...]]" << endl;
}
}
if (runUnitTests)
UnitTest::run(unitTestNames);
if (runDataTests) {
cout << endl << endl;
const char *sleighhomepath = getenv("SLEIGHHOME");
if (sleighhomepath != (const char *)0)
cout << "Using SLEIGHHOME=" << sleighhomepath << endl;
else
cout << "No SLEIGHHOME environment variable" << endl;
startDecompilerLibrary(sleighhomepath);
FunctionTestCollection::runTestCollections(dirname,dataTestNames);
}
}
TEST(float_opRound) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(roundf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opRound(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opInt2Float_size4) {
FloatFormat format(4);
for(int i:int_test_values) {
uintb true_result = floatToRawBits((float)i);
uintb result = format.opInt2Float(i, 4);
ASSERT_EQUALS(true_result, result);
}
}
// TODO other sized ints
TEST(float_to_double_opFloat2Float) {
FloatFormat format(4);
FloatFormat format8(8);
for(float f:float_test_values) {
uintb true_result = doubleToRawBits((double)f);
uintb encoding = format.getEncoding(f);
uintb result = format.opFloat2Float(encoding, format8);
ASSERT_EQUALS(true_result, result);
}
}
// TODO float2float going the other direction, double_to_float_opFloat2Float
TEST(float_opTrunc_to_int) {
FloatFormat format(4);
FloatFormat format8(8);
for(float f:float_test_values) {
// avoid undefined behavior
if((int64_t)f > std::numeric_limits<int>::max() || (int64_t)f < std::numeric_limits<int>::min())
continue;
uintb true_result = ((uintb)(int32_t)f) & 0xffffffff;
uintb encoding = format.getEncoding(f);
uintb result = format.opTrunc(encoding, 4);
ASSERT_EQUALS(true_result, result);
}
}
// TODO trunc to other sizes
TEST(float_opEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1==f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opNotEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1!=f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opNotEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opLess) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1<f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opLess(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opLessEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1<=f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opLessEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opAdd) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1+f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opAdd(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opDiv) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1/f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opDiv(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opMult) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1*f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opMult(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opSub) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1-f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opSub(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
// end generated

View file

@ -19,48 +19,59 @@
/// Include this file and any additional headers. Use TEST(testname) as
/// prototype in test function definitions. E.g.
/// test.cc:
/// #include "float.hh"
/// #include "test.hh"
/// \#include "float.hh"
/// \#include "test.hh"
///
/// TEST(zero_is_less_than_one) {
/// ASSERT(0.0 < 1.0);
/// }
///
#include <cstdio>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <set>
#include <string>
#include <iostream>
namespace {
struct Test;
typedef void (*testfunc_t)();
typedef void (*testfunc_t)(); ///< A unit-test function
std::vector<Test *> tests;
/// \brief Simple unit test class
///
/// The macro TEST instantiates this object with a name and function pointer.
/// The static run() method calls all the function pointers of all instantiated
/// objects.
struct UnitTest {
static std::vector<UnitTest *> tests; ///< The collection of test objects
std::string name; ///< Name of the test
testfunc_t func; ///< Call-back function executing the test
struct Test {
std::string name;
testfunc_t func;
/// \brief Constructor
///
/// \param name is the identifier for the test
/// \param func is a call-back function that executes the test
UnitTest(const std::string &name,testfunc_t func) :
name(name), func(func)
{
tests.push_back(this);
}
Test(const std::string &name, testfunc_t func) : name(name), func(func) {
tests.push_back(this);
}
};
} // namespace
static void run(std::set<std::string> &testNames); ///< Run all the instantiated tests
};
/// \brief Main unit test macro
#define TEST(testname) \
void testname(); \
Test testname##_obj{ #testname, testname }; \
UnitTest testname##_obj{ #testname, testname }; \
void testname()
/// \brief Assert that a boolean is \b true for a unit test
#define ASSERT(test) \
if (!(test)) { \
std::cerr << " failed at " << __FILE__ << ":" << __LINE__ << " asserting \"" << #test << "\"." << std::endl; \
throw 0; \
}
/// \brief Assert that two values are equal for a unit test
#define ASSERT_EQUALS(a, b) \
if ((a) != (b)) { \
std::stringstream ssa, ssb; \
@ -71,6 +82,7 @@ namespace {
throw 0; \
}
/// \brief Assert that two values are not equal for a unit test
#define ASSERT_NOT_EQUALS(a, b) \
if ((a) == (b)) { \
std::stringstream ssa, ssb; \
@ -80,26 +92,3 @@ namespace {
<< " != " << ssb.str() << "\"." << std::endl; \
throw 0; \
}
int main(int argc, char **argv) {
int total = 0;
int passed = 0;
std::set<std::string> testnames(argv + 1, argv + argc);
for (auto &t : tests) {
if(testnames.size()>0 && testnames.find(t->name)==testnames.end()) {
continue;
}
std::cerr << "testing : " << t->name << " ..." << std::endl;
++total;
try {
t->func();
++passed;
std::cerr << " passed." << std::endl;
} catch (...) {
}
}
std::cerr << "==============================" << std::endl;
std::cerr << passed << "/" << total << " tests passed." << std::endl;
}

View file

@ -0,0 +1,345 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "testfunction.hh"
#include "filemanage.hh"
void FunctionTestProperty::startTest(void) const
{
count = 0;
}
void FunctionTestProperty::processLine(const string &line) const
{
if (regex_search(line,pattern))
count += 1;
}
bool FunctionTestProperty::endTest(void) const
{
return (count >= minimumMatch && count <= maximumMatch);
}
void FunctionTestProperty::restoreXml(const Element *el)
{
name = el->getAttributeValue("name");
istringstream s1(el->getAttributeValue("min"));
s1 >> minimumMatch;
istringstream s2(el->getAttributeValue("max"));
s2 >> maximumMatch;
pattern = regex(el->getContent());
}
void ConsoleCommands::readLine(string &line)
{
if (pos >= commands.size()) {
line.clear();
return;
}
line = commands[pos];
pos += 1;
}
ConsoleCommands::ConsoleCommands(void) :
IfaceStatus("> ", cout)
{
pos = 0;
IfaceCapability::registerAllCommands(this);
}
void ConsoleCommands::reset(void)
{
commands.clear();
pos = 0;
inerror = false;
done = false;
}
/// \param el is the root \<script> tag
void ConsoleCommands::restoreXml(const Element *el)
{
const List &list(el->getChildren());
List::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter) {
const Element *subel = *iter;
commands.push_back(subel->getContent());
}
pos = 0;
}
void FunctionTestCollection::clear(void)
{
dcp->clearArchitecture();
testList.clear();
console.reset();
}
/// Instantiate an Architecture object
void FunctionTestCollection::buildProgram(DocumentStorage &docStorage)
{
ArchitectureCapability *capa = ArchitectureCapability::getCapability("xml");
if (capa == (ArchitectureCapability *)0)
throw IfaceExecutionError("Missing XML architecture capability");
dcp->conf = capa->buildArchitecture("test", "", console.optr);
string errmsg;
bool iserror = false;
try {
dcp->conf->init(docStorage);
dcp->conf->readLoaderSymbols("::"); // Read in loader symbols
} catch(XmlError &err) {
errmsg = err.explain;
iserror = true;
} catch(LowlevelError &err) {
errmsg = err.explain;
iserror = true;
}
if (iserror)
throw IfaceExecutionError("Error during architecture initialization: " + errmsg);
}
/// Let each test initialize itself thru its startTest() method
void FunctionTestCollection::startTests(void) const
{
list<FunctionTestProperty>::const_iterator iter;
for(iter=testList.begin();iter!=testList.end();++iter) {
(*iter).startTest();
}
}
/// Each test gets a chance to process a line of output
/// \param line is the given line of output
void FunctionTestCollection::passLineToTests(const string &line) const
{
list<FunctionTestProperty>::const_iterator iter;
for(iter=testList.begin();iter!=testList.end();++iter) {
(*iter).processLine(line);
}
}
/// \brief Do the final evaluation of each test
///
/// This is called after each test has been fed all lines of output.
/// The result of each test is printed to the \e midStream, and then
/// failures are written to the lateStream in order to see a summary.
/// \param midStream is the stream write results to as the test is performed
/// \param lateStream collects failures to display as a summary
void FunctionTestCollection::evaluateTests(ostream &midStream,list<string> &lateStream) const
{
list<FunctionTestProperty>::const_iterator iter;
for(iter=testList.begin();iter!=testList.end();++iter) {
numTestsApplied += 1;
if ((*iter).endTest()) {
midStream << "Success -- " << (*iter).getName() << endl;
numTestsSucceeded += 1;
}
else {
midStream << "FAIL -- " << (*iter).getName() << endl;
lateStream.push_back((*iter).getName());
}
}
}
FunctionTestCollection::FunctionTestCollection(void)
{
dcp = (IfaceDecompData *)console.getData("decompile");
console.setErrorIsDone(true);
numTestsApplied = 0;
numTestsSucceeded = 0;
}
/// Load the architecture based on the discovered \<binaryimage> tag.
/// Collect the script commands and the specific tests.
/// \param filename is the XML file holding the test data
void FunctionTestCollection::loadTest(const string &filename)
{
fileName = filename;
DocumentStorage docStorage;
Document *doc = docStorage.openDocument(filename);
Element *el = doc->getRoot();
if (el->getName() == "decompilertest")
restoreXml(docStorage,el);
else if (el->getName() == "binaryimage")
restoreXmlOldForm(docStorage,el);
else
throw IfaceParseError("Test file " + filename + " has unrecognized XML tag: "+el->getName());
}
void FunctionTestCollection::restoreXml(DocumentStorage &store,const Element *el)
{
clear();
const List &list(el->getChildren());
List::const_iterator iter = list.begin();
bool sawScript = false;
bool sawTests = false;
bool sawProgram = false;
while(iter != list.end()) {
const Element *subel = *iter;
++iter;
if (subel->getName() == "script") {
sawScript = true;
console.restoreXml(subel);
}
else if (subel->getName() == "stringmatch") {
sawTests = true;
testList.emplace_back();
testList.back().restoreXml(subel);
}
else if (subel->getName() == "binaryimage") {
sawProgram = true;
store.registerTag(subel);
buildProgram(store);
}
else
throw IfaceParseError("Unknown tag in <decompiletest>: "+subel->getName());
}
if (!sawScript)
throw IfaceParseError("Did not see <script> tag in <decompiletest>");
if (!sawTests)
throw IfaceParseError("Did not see any <stringmatch> tags in <decompiletest>");
if (!sawProgram)
throw IfaceParseError("No <binaryimage> tag in <decompiletest>");
}
/// Pull the script and tests from a comment in \<binaryimage>
void FunctionTestCollection::restoreXmlOldForm(DocumentStorage &store,const Element *el)
{
clear();
throw IfaceParseError("Old format test not supported");
}
/// Run the script commands on the current program.
/// Collect any bulk output, and run tests over the output.
/// Report test failures back to the caller
/// \param midStream is the output stream to write to during the test
/// \param lateStream collects messages for a final summary
void FunctionTestCollection::runTests(ostream &midStream,list<string> &lateStream)
{
numTestsApplied = 0;
numTestsSucceeded = 0;
ostringstream midBuffer; // Collect command console output
console.optr = &midBuffer;
ostringstream bulkout;
console.fileoptr = &bulkout;
mainloop(&console);
console.optr = &midStream;
console.fileoptr = &midStream;
if (console.isInError()) {
midStream << "Error: Did not apply tests in " << fileName << endl;
midStream << midBuffer.str() << endl;
ostringstream fs;
fs << "Execution failed for " << fileName;
lateStream.push_back(fs.str());
return;
}
string result = bulkout.str();
if (result.size() == 0) {
ostringstream fs;
fs << "No output for " << fileName;
lateStream.push_back(fs.str());
return;
}
startTests();
string::size_type prevpos = 0;
string::size_type pos = result.find_first_of('\n');
while(pos != string::npos) {
string line = result.substr(prevpos,pos - prevpos);
passLineToTests(line);
prevpos = pos + 1;
pos = result.find_first_of('\n',prevpos);
}
if (prevpos != result.size()) {
string line = result.substr(prevpos); // Process final line without a newline char
passLineToTests(line);
}
evaluateTests(midStream, lateStream);
}
/// Run through all XML files in the given directory, processing each in turn.
/// \param dirname is a directory containing the XML test files
/// \param testNames (if not empty) specifies particular tests to run
void FunctionTestCollection::runTestCollections(const string &dirname,set<string> &testNames)
{
FileManage fileManage;
set<string> fullNames;
for(set<string>::iterator iter=testNames.begin();iter!=testNames.end();++iter) {
string val = dirname;
if (dirname.back() != '/')
val += '/';
val += *iter;
fullNames.insert(val);
}
fileManage.addDir2Path(dirname);
vector<string> testFiles;
fileManage.matchList(testFiles,".xml",true);
int4 totalTestsApplied = 0;
int4 totalTestsSucceeded = 0;
list<string> failures;
FunctionTestCollection testCollection;
for(int4 i=0;i<testFiles.size();++i) {
if (!fullNames.empty() && fullNames.find(testFiles[i]) == fullNames.end())
continue;
try {
testCollection.loadTest(testFiles[i]);
testCollection.runTests(cout, failures);
totalTestsApplied += testCollection.getTestsApplied();
totalTestsSucceeded += testCollection.getTestsSucceeded();
} catch(IfaceParseError &err) {
ostringstream fs;
fs << "Error parsing " << testFiles[i] << ": " << err.explain;
cout << fs.str() << endl;
failures.push_back(fs.str());
} catch(IfaceExecutionError &err) {
ostringstream fs;
fs << "Error executing " << testFiles[i] << ": " << err.explain;
cout << fs.str() << endl;
failures.push_back(fs.str());
}
}
cout << endl;
cout << "Total tests applied = " << totalTestsApplied << endl;
cout << "Total passing tests = " << totalTestsSucceeded << endl;
cout << endl;
if (!failures.empty()) {
cout << "Failures: " << endl;
list<string>::const_iterator iter = failures.begin();
for(int4 i=0;i<10;++i) {
cout << " " << *iter << endl;
++iter;
if (iter == failures.end()) break;
}
}
}

View file

@ -0,0 +1,88 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// \file testfunction.hh
/// \brief Framework for decompiler data driven single function tests
#ifndef __TESTFUNCTION__
#define __TESTFUNCTION__
#include "libdecomp.hh"
#include <iostream>
#include <regex>
/// \brief A single property to be searched for in the output of a function decompilation
///
/// This is generally a regular expression run over the characters in the
/// decompiled "source" form of the function.
/// The property may "match" more than once or not at all.
class FunctionTestProperty {
int4 minimumMatch; ///< Minimum number of times property is expected to match
int4 maximumMatch; ///< Maximum number of times property is expected to match
string name; ///< Name of the test, to be printed in test summaries
regex pattern; ///< Regular expression to match against a line of output
mutable uint4 count; ///< Number of times regular expression has been seen
public:
string getName(void) const { return name; } ///< Get the name of the property
void startTest(void) const; ///< Reset "state", counting number of matching lines
void processLine(const string &line) const; ///< Search thru \e line, update state if match found
bool endTest(void) const; ///< Return results of property search
void restoreXml(const Element *el); ///< Reconstruct the property from an XML tag
};
/// \brief A console command run as part of a test sequence
class ConsoleCommands : public IfaceStatus {
vector<string> commands; ///< Sequence of commands
uint4 pos; ///< Position of next command to execute
virtual void readLine(string &line);
public:
ConsoleCommands(void); ///< Constructor
void reset(void); ///< Reset console for a new program
virtual bool isStreamFinished(void) const { return pos == commands.size(); }
void restoreXml(const Element *el); ///< Reconstruct the command from an XML tag
};
/// \brief A collection of tests around a single program/function
///
/// The collection of tests is loaded from a single XML file via loadTest(),
/// and the tests are run by calling runTests().
/// An entire program is loaded and possibly annotated by a series of
/// console command lines. Decompiler output is also triggered by a command,
/// and then the output is scanned for by the test objects (FunctionTestProperty).
/// Results of passed/failed tests are collected. If the command line script
/// does not complete properly, this is considered a special kind of failure.
class FunctionTestCollection {
IfaceDecompData *dcp; ///< Program data for the test collection
string fileName; ///< Name of the file containing test data
list<FunctionTestProperty> testList; ///< List of tests for this collection
ConsoleCommands console; ///< Decompiler console for executing scripts
mutable int4 numTestsApplied; ///< Count of tests that were executed
mutable int4 numTestsSucceeded; ///< Count of tests that passed
void clear(void); ///< Clear any previous architecture and function
void buildProgram(DocumentStorage &store); ///< Build program (Architecture) from \<binaryimage> tag
void startTests(void) const; ///< Initialize each FunctionTestProperty
void passLineToTests(const string &line) const; ///< Let all tests analyze a line of the results
void evaluateTests(ostream &midStream,list<string> &lateStream) const;
public:
FunctionTestCollection(void); ///< Constructor
int4 getTestsApplied(void) const { return numTestsApplied; } ///< Get the number of tests executed
int4 getTestsSucceeded(void) const { return numTestsSucceeded; } ///< Get the number of tests that passed
void loadTest(const string &filename); ///< Load a test program, tests, and script
void restoreXml(DocumentStorage &store,const Element *el); ///< Load tests from a \<decompilertest> tag.
void restoreXmlOldForm(DocumentStorage &store,const Element *el); ///< Load tests from \<binaryimage> tag.
void runTests(ostream &midStream,list<string> &lateStream); ///< Run the script and perform the tests
static void runTestCollections(const string &dirname,set<string> &testNames); ///< Run test files in a whole directory
};
#endif

View file

@ -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
};

View file

@ -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)
{

View file

@ -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

View file

@ -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
{

View file

@ -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)
{