Merge remote-tracking branch 'origin/caheckman_recentBranches'

This commit is contained in:
ghidravore 2020-05-22 13:29:33 -04:00
commit 875eed4c3b
60 changed files with 2939 additions and 1043 deletions

View file

@ -252,9 +252,11 @@
<H3><A name="c_cpp"/><A name="Options_C_C__""/>C/C++</H3>
<BLOCKQUOTE>
<P>Create a C/C++ file containing all datatypes from the program's <A href=
"help/topics/DataTypeManagerPlugin/data_type_manager_description.htm">data type
manager</A> and all of the functions in the program.&nbsp;</P>
<P>Create a C/C++ file containing functions decompiled from the program and, optionally,
containing definitions of all datatypes from the program's
<A href="help/topics/DataTypeManagerPlugin/data_type_manager_description.htm">
data type manager</A>. The datatype definitions and a prototype declaration for each function
can be placed in a separate header file.&nbsp;</P>
<H4>C/C++ Options</H4>
@ -266,6 +268,14 @@
<LI><B>Create C File (.c)</B> - Select to create a .c file.</LI>
<LI><B>Use C++ Style Comments (//)</B> - Select to use // or /* style comments.</LI>
<LI><B>Emit data-type definitions</B> - Select to export a C/C++ definition for each data-type.</LI>
<LI><B>Function tags to filter</B> - Optionally list function tags to filter which functions are exported.
Multiple tags should be comma separated.</LI>
<LI><B>Function tags excluded</B> - Select to exclude all tagged functions from being exported. Deselect to
export only tagged functions.</LI>
</UL>
</BLOCKQUOTE>

View file

@ -270,6 +270,7 @@ model {
include "database.cc"
include "cpool.cc"
include "comment.cc"
include "stringmanage.cc"
include "fspec.cc"
include "action.cc"
include "loadimage.cc"
@ -320,6 +321,7 @@ model {
include "cpool_ghidra.cc"
include "ghidra_process.cc"
include "comment_ghidra.cc"
include "string_ghidra.cc"
// include "callgraph.cc" // uncomment for debug
// include "ifacedecomp.cc" // uncomment for debug
// include "ifaceterm.cc" // uncomment for debug

View file

@ -75,7 +75,7 @@ EXTERNAL_CONSOLEEXT_NAMES=$(subst .cc,,$(notdir $(EXTERNAL_CONSOLEEXT_SOURCE)))
CORE= xml space float address pcoderaw translate opcodes globalcontext
# Additional core files for any projects that decompile
DECCORE=capability architecture options graph cover block cast typeop database cpool \
comment fspec action loadimage grammar varnode op \
comment stringmanage fspec action loadimage grammar varnode op \
type variable varmap jumptable emulate emulateutil flow userop \
funcdata funcdata_block funcdata_op funcdata_varnode pcodeinject \
heritage prefersplit rangeutil ruleaction subflow blockaction merge double \
@ -87,7 +87,7 @@ SLEIGH= sleigh pcodeparse pcodecompile sleighbase slghsymbol \
# Additional files for the GHIDRA specific build
GHIDRA= ghidra_arch inject_ghidra ghidra_translate loadimage_ghidra \
typegrp_ghidra database_ghidra ghidra_context cpool_ghidra \
ghidra_process comment_ghidra $(GHIDRAEXT_NAMES)
ghidra_process comment_ghidra string_ghidra $(GHIDRAEXT_NAMES)
# Additional files specific to the sleigh compiler
SLACOMP=slgh_compile slghparse slghscan
# Additional special files that should not be considered part of the library

View file

@ -182,6 +182,12 @@ bool Action::setBreakPoint(uint4 tp,const string &specify)
return false;
}
void Action::clearBreakPoints(void)
{
breakpoint = 0;
}
/// If enabled, a warning will be printed whenever this action applies.
/// The warning can be toggled for \b this Action or some sub-action by
/// specifying its name.
@ -371,6 +377,15 @@ void ActionGroup::addAction(Action *ac)
list.push_back(ac);
}
void ActionGroup::clearBreakPoints(void)
{
vector<Action *>::const_iterator iter;
for(iter=list.begin();iter!= list.end();++iter)
(*iter)->clearBreakPoints();
Action::clearBreakPoints();
}
Action *ActionGroup::clone(const ActionGroupList &grouplist) const
{
@ -870,6 +885,15 @@ int4 ActionPool::apply(Funcdata &data)
return 0; // Indicate successful completion
}
void ActionPool::clearBreakPoints(void)
{
vector<Rule *>::const_iterator iter;
for(iter=allrules.begin();iter!=allrules.end();++iter)
(*iter)->clearBreakPoints();
Action::clearBreakPoints();
}
Action *ActionPool::clone(const ActionGroupList &grouplist) const
{
@ -955,13 +979,26 @@ ActionDatabase::~ActionDatabase(void)
delete (*iter).second;
}
/// This provides the database with the single Action from which all other
/// \e root Actions are derived. The Action has a reserved name "universal"
/// \param act is the universal Action
void ActionDatabase::registerUniversal(Action *act)
/// Clear out (possibly altered) root Actions. Reset the default groups.
/// Set the default root action "decompile"
void ActionDatabase::resetDefaults(void)
{
registerAction(universalname,act);
Action *universalAction = (Action *)0;
map<string,Action *>::iterator iter;
iter = actionmap.find(universalname);
if (iter != actionmap.end())
universalAction = (*iter).second;
for(iter = actionmap.begin();iter!=actionmap.end();++iter) {
Action *curAction = (*iter).second;
if (curAction != universalAction)
delete curAction; // Clear out any old (modified) root actions
}
actionmap.clear();
registerAction(universalname, universalAction);
buildDefaultGroups();
setCurrent("decompile"); // The default root action
}
const ActionGroupList &ActionDatabase::getGroup(const string &grp) const
@ -1019,13 +1056,15 @@ Action *ActionDatabase::toggleAction(const string &grp, const string &basegrp,bo
/// \param argv is a list of static char pointers, which must end with a NULL pointer, or a zero length string.
void ActionDatabase::setGroup(const string &grp,const char **argv)
{ ActionGroupList &curgrp( groupmap[ grp ] );
{
ActionGroupList &curgrp( groupmap[ grp ] );
curgrp.list.clear(); // Clear out any old members
for(int4 i=0;;++i) {
if (argv[i] == (char *)0) break;
if (argv[i][0] == '\0') break;
curgrp.list.insert( argv[i] );
}
isDefaultGroups = false;
}
/// Copy an existing \e root Action by copying its grouplist, giving it a new name.
@ -1038,6 +1077,7 @@ void ActionDatabase::cloneGroup(const string &oldname,const string &newname)
{
const ActionGroupList &curgrp(getGroup(oldname)); // Should already exist
groupmap[ newname ] = curgrp; // Copy the group
isDefaultGroups = false;
}
/// Add a group to the grouplist for a particular \e root Action.
@ -1046,7 +1086,9 @@ void ActionDatabase::cloneGroup(const string &oldname,const string &newname)
/// \param basegroup is the group to add
/// \return \b true for a new addition, \b false is the group was already present
bool ActionDatabase::addToGroup(const string &grp, const string &basegroup)
{
isDefaultGroups = false;
ActionGroupList &curgrp( groupmap[ grp ] );
return curgrp.list.insert( basegroup ).second;
}
@ -1057,7 +1099,9 @@ bool ActionDatabase::addToGroup(const string &grp, const string &basegroup)
/// \param basegrp is the group to remove
/// \return \b true if the group existed and was removed
bool ActionDatabase::removeFromGroup(const string &grp, const string &basegrp)
{
isDefaultGroups = false;
ActionGroupList &curgrp( groupmap[ grp ] );
return (curgrp.list.erase(basegrp) > 0);
}

View file

@ -99,6 +99,7 @@ public:
virtual void printStatistics(ostream &s) const; ///< Dump statistics to stream
int4 perform(Funcdata &data); ///< Perform this action (if necessary)
bool setBreakPoint(uint4 tp,const string &specify); ///< Set a breakpoint on this action
virtual void clearBreakPoints(void); ///< Clear all breakpoints set on \b this Action
bool setWarning(bool val,const string &specify); ///< Set a warning on this action
bool disableRule(const string &specify); ///< Disable a specific Rule within \b this
bool enableRule(const string &specify); ///< Enable a specific Rule within \b this
@ -147,6 +148,7 @@ public:
ActionGroup(uint4 f,const string &nm) : Action(f,nm,"") {} ///< Construct given properties and a name
virtual ~ActionGroup(void); ///< Destructor
void addAction(Action *ac); ///< Add an Action to the group
virtual void clearBreakPoints(void);
virtual Action *clone(const ActionGroupList &grouplist) const;
virtual void reset(Funcdata &data);
virtual void resetStats(void);
@ -216,6 +218,7 @@ public:
uint4 getNumApply(void) { return count_apply; } ///< Get number of successful applications
void setBreak(uint4 tp) { breakpoint |= tp; } ///< Set a breakpoint on \b this Rule
void clearBreak(uint4 tp) { breakpoint &= ~tp; } ///< Clear a breakpoint on \b this Rule
void clearBreakPoints(void) { breakpoint = 0; } ///< Clear all breakpoints on \b this Rule
void turnOnWarnings(void) { flags |= warnings_on; } ///< Enable warnings for \b this Rule
void turnOffWarnings(void) { flags &= ~warnings_on; } ///< Disable warnings for \b this Rule
bool isDisabled(void) const { return ((flags & type_disable)!=0); } ///< Return \b true if \b this Rule is disabled
@ -266,6 +269,7 @@ public:
ActionPool(uint4 f,const string &nm) : Action(f,nm,"") {} ///< Construct providing properties and name
virtual ~ActionPool(void); ///< Destructor
void addRule(Rule *rl); ///< Add a Rule to the pool
virtual void clearBreakPoints(void);
virtual Action *clone(const ActionGroupList &grouplist) const;
virtual void reset(Funcdata &data);
virtual void resetStats(void);
@ -296,14 +300,16 @@ class ActionDatabase {
string currentactname; ///< The name associated with the current root Action
map<string,ActionGroupList> groupmap; ///< Map from root Action name to the grouplist it uses
map<string,Action *> actionmap; ///< Map from name to root Action
bool isDefaultGroups; ///< \b true if only the default groups are set
static const char universalname[]; ///< The name of the \e universal root Action
void registerAction(const string &nm,Action *act); ///< Register a \e root Action
void buildDefaultGroups(void); ///< Set up descriptions of preconfigured root Actions
Action *getAction(const string &nm) const; ///< Look up a \e root Action by name
Action *deriveAction(const string &baseaction,const string &grp); ///< Derive a \e root Action
public:
ActionDatabase(void) { currentact = (Action *)0; } ///< Constructor
ActionDatabase(void) { currentact = (Action *)0; isDefaultGroups = false; } ///< Constructor
~ActionDatabase(void); ///< Destructor
void registerUniversal(Action *act); ///< Register the \e universal root Action
void resetDefaults(void); ///< (Re)set the default configuration
Action *getCurrent(void) const { return currentact; } ///< Get the current \e root Action
const string &getCurrentName(void) const { return currentactname; } ///< Get the name of the current \e root Action
const ActionGroupList &getGroup(const string &grp) const; ///< Get a specific grouplist by name
@ -314,6 +320,7 @@ public:
void cloneGroup(const string &oldname,const string &newname); ///< Clone a \e root Action
bool addToGroup(const string &grp,const string &basegroup); ///< Add a group to a \e root Action
bool removeFromGroup(const string &grp,const string &basegroup); ///< Remove a group from a \e root Action
void universalAction(Architecture *glb); ///< Build the universal action
};
#endif

View file

@ -16,6 +16,7 @@
// Set up decompiler for specific architectures
#include "coreaction.hh"
#include "flow.hh"
#ifdef CPUI_RULECOMPILE
#include "rulecompile.hh"
#endif
@ -86,17 +87,10 @@ Architecture::Architecture(void)
{
// endian = -1;
trim_recurse_max = 5; // Reasonable default value
max_implied_ref = 2; // 2 is best, in specific cases a higher number might be good
max_term_duplication = 2; // 2 and 3 (4) are pretty reasonable
max_basetype_size = 10; // Needs to be 8 or bigger
resetDefaultsInternal();
min_funcsymbol_size = 1;
aggressive_ext_trim = false;
readonlypropagate = false;
infer_pointers = true;
funcptr_align = 0;
flowoptions = 0;
alias_block_level = 2; // Block structs and arrays by default
defaultfp = (ProtoModel *)0;
defaultReturnAddr.space = (AddrSpace *)0;
evalfp_current = (ProtoModel *)0;
@ -106,6 +100,7 @@ Architecture::Architecture(void)
loader = (LoadImage *)0;
pcodeinjectlib = (PcodeInjectLibrary *)0;
commentdb = (CommentDatabase *)0;
stringManager = (StringManager *)0;
cpool = (ConstantPool *)0;
symboltab = new Database(this);
context = (ContextDatabase *)0;
@ -158,6 +153,8 @@ Architecture::~Architecture(void)
delete pcodeinjectlib;
if (commentdb != (CommentDatabase *)0)
delete commentdb;
if (stringManager != (StringManager *)0)
delete stringManager;
if (cpool != (ConstantPool *)0)
delete cpool;
if (context != (ContextDatabase *)0)
@ -274,6 +271,7 @@ void Architecture::clearAnalysis(Funcdata *fd)
fd->clear(); // Clear stuff internal to function
// Clear out any analysis generated comments
commentdb->clearType(fd->getAddress(),Comment::warning|Comment::warningheader);
stringManager->clear();
}
/// Symbols do not necessarily need to be available for the decompiler.
@ -411,6 +409,7 @@ void Architecture::saveXml(ostream &s) const
symboltab->saveXml(s);
context->saveXml(s);
commentdb->saveXml(s);
stringManager->saveXml(s);
if (!cpool->empty())
cpool->saveXml(s);
s << "</save_state>\n";
@ -443,6 +442,8 @@ void Architecture::restoreXml(DocumentStorage &store)
context->restoreXml(subel,this);
else if (subel->getName() == "commentdb")
commentdb->restoreXml(subel,this);
else if (subel->getName() == "stringmanage")
stringManager->restoreXml(subel,this);
else if (subel->getName() == "constantpool")
cpool->restoreXml(subel,*types);
else if (subel->getName() == "optionslist")
@ -508,8 +509,8 @@ void Architecture::buildAction(DocumentStorage &store)
{
parseExtraRules(store); // Look for any additional rules
universal_action(this);
allacts.setCurrent("decompile");
allacts.universalAction(this);
allacts.resetDefaults();
}
/// This builds the database which holds the status registers setings and other
@ -581,6 +582,14 @@ void Architecture::buildCommentDB(DocumentStorage &store)
commentdb = new CommentDatabaseInternal();
}
/// Build container that holds decoded strings
/// \param store may hold configuration information
void Architecture::buildStringManager(DocumentStorage &store)
{
stringManager = new StringManagerUnicode(this,2048);
}
/// Some processor models (Java byte-code) need a database of constants.
/// The database is always built, but may remain empty.
/// \param store may hold configuration information
@ -1243,6 +1252,7 @@ void Architecture::init(DocumentStorage &store)
buildContext(store);
buildTypegrp(store);
buildCommentDB(store);
buildStringManager(store);
buildConstantPool(store);
restoreFromSpec(store);
@ -1253,6 +1263,31 @@ void Architecture::init(DocumentStorage &store)
fillinReadOnlyFromLoader();
}
void Architecture::resetDefaultsInternal(void)
{
trim_recurse_max = 5;
max_implied_ref = 2; // 2 is best, in specific cases a higher number might be good
max_term_duplication = 2; // 2 and 3 (4) are reasonable
max_basetype_size = 10; // Needs to be 8 or bigger
flowoptions = FlowInfo::error_toomanyinstructions;
max_instructions = 100000;
infer_pointers = true;
readonlypropagate = false;
alias_block_level = 2; // Block structs and arrays by default
}
/// Reset options that can be modified by the OptionDatabase. This includes
/// options specific to this class and options under PrintLanguage and ActionDatabase
void Architecture::resetDefaults(void)
{
resetDefaultsInternal();
allacts.resetDefaults();
for(int4 i=0;i<printlist.size();++i)
printlist[i]->resetDefaults();
}
Address SegmentedResolver::resolve(uintb val,int4 sz,const Address &point,uintb &fullEncoding)
{

View file

@ -28,6 +28,7 @@
#include "loadimage.hh"
#include "globalcontext.hh"
#include "comment.hh"
#include "stringmanage.hh"
#include "userop.hh"
#include "options.hh"
#include "transform.hh"
@ -130,6 +131,7 @@ public:
vector<AddrSpace *> inferPtrSpaces; ///< Set of address spaces in which a pointer constant is inferable
int4 funcptr_align; ///< How many bits of alignment a function ptr has
uint4 flowoptions; ///< options passed to flow following engine
uint4 max_instructions; ///< Maximum instructions that can be processed in one function
int4 alias_block_level; ///< Aliases blocked by 0=none, 1=struct, 2=array, 3=all
vector<Rule *> extra_pool_rules; ///< Extra rules that go in the main pool (cpu specific, experimental)
@ -146,6 +148,7 @@ public:
PcodeInjectLibrary *pcodeinjectlib; ///< Pcode injection manager
RangeList nohighptr; ///< Ranges for which high-level pointers are not possible
CommentDatabase *commentdb; ///< Comments for this architecture
StringManager *stringManager; ///< Manager of decoded strings
ConstantPool *cpool; ///< Deferred constant values
PrintLanguage *print; ///< Current high-level language printer
vector<PrintLanguage *> printlist; ///< List of high-level language printers supported
@ -164,6 +167,8 @@ public:
#endif
Architecture(void); ///< Construct an uninitialized Architecture
void init(DocumentStorage &store); ///< Load the image and configure architecture
void resetDefaultsInternal(void); ///< Reset default values for options specific to Architecture
void resetDefaults(void); ///< Reset defaults values for options owned by \b this
ProtoModel *getModel(const string &nm) const; ///< Get a specific PrototypeModel
bool hasModel(const string &nm) const; ///< Does this Architecture have a specific PrototypeModel
bool highPtrPossible(const Address &loc,int4 size) const; ///< Are pointers possible to the given location?
@ -224,6 +229,7 @@ protected:
virtual void buildTypegrp(DocumentStorage &store); ///< Build the data-type factory/container
virtual void buildCommentDB(DocumentStorage &store); ///< Build the comment database
virtual void buildStringManager(DocumentStorage &store); ///< Build the string manager
virtual void buildConstantPool(DocumentStorage &store); ///< Build the constant pool
virtual void buildInstructions(DocumentStorage &store); ///< Register the p-code operations
virtual void buildAction(DocumentStorage &store); ///< Build the Action framework

View file

@ -232,9 +232,14 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype,
isptr = true;
}
if (curtype == reqtype) return (Datatype *)0; // Different typedefs could point to the same type
if ((reqbase->getMetatype()==TYPE_VOID)||(reqbase->getMetatype()==TYPE_VOID))
if ((reqbase->getMetatype()==TYPE_VOID)||(curtype->getMetatype()==TYPE_VOID))
return (Datatype *)0; // Don't cast from or to VOID
if (reqbase->getSize() != curbase->getSize()) return reqtype; // Always cast change in size
if (reqbase->getSize() != curbase->getSize()) {
if (reqbase->isVariableLength() && isptr && reqbase->hasSameVariableBase(curbase)) {
return (Datatype *)0; // Don't need a cast
}
return reqtype; // Otherwise, always cast change in size
}
switch(reqbase->getMetatype()) {
case TYPE_UNKNOWN:
return (Datatype *)0;
@ -370,7 +375,7 @@ Datatype *CastStrategyJava::castStandard(Datatype *reqtype,Datatype *curtype,
if ((reqbase->getMetatype()==TYPE_PTR)||(curbase->getMetatype()==TYPE_PTR))
return (Datatype *)0; // There must be explicit cast op between objects, so assume no cast necessary
if ((reqbase->getMetatype()==TYPE_VOID)||(reqbase->getMetatype()==TYPE_VOID))
if ((reqbase->getMetatype()==TYPE_VOID)||(curtype->getMetatype()==TYPE_VOID))
return (Datatype *)0; // Don't cast from or to VOID
if (reqbase->getSize() != curbase->getSize()) return reqtype; // Always cast change in size
switch(reqbase->getMetatype()) {

View file

@ -759,6 +759,15 @@ bool ConditionalExecution::verify(void)
return true;
}
/// Set up for testing ConditionalExecution on multiple iblocks
/// \param f is the function to do testing on
ConditionalExecution::ConditionalExecution(Funcdata *f)
{
fd = f;
buildHeritageArray(); // Cache an array depending on the particular heritage pass
}
/// The given block is tested as a possible \b iblock. If this configuration
/// works and is not a \b directsplit, \b true is returned.
/// If the configuration works as a \b directsplit, then recursively check that
@ -772,7 +781,6 @@ bool ConditionalExecution::trial(BlockBasic *ib)
{
iblock = ib;
buildHeritageArray();
if (!verify()) return false;
PcodeOp *cbranch_copy;

View file

@ -160,7 +160,7 @@ class ConditionalExecution {
void fixReturnOp(void);
bool verify(void); ///< Verify that we have a removable \b iblock
public:
ConditionalExecution(Funcdata *f) { fd = f; } ///< Constructor
ConditionalExecution(Funcdata *f); ///< Constructor
bool trial(BlockBasic *ib); ///< Test for a modifiable configuration around the given block
void execute(void); ///< Eliminate the unnecessary path join at \b iblock
};

View file

@ -170,29 +170,31 @@ int main(int argc,char **argv)
{
const char *initscript = (const char *)0;
vector<string> extrapaths;
int4 i=1;
while((i<argc)&&(argv[i][0]=='-')) {
if (argv[i][1]=='i')
initscript = argv[++i];
else if (argv[i][1]=='s')
extrapaths.push_back(argv[++i]);
i += 1;
}
string ghidraroot = FileManage::discoverGhidraRoot(argv[0]);
if (ghidraroot.size() == 0) {
const char *sleighhomepath = getenv("SLEIGHHOME");
if (sleighhomepath == (const char *)0) {
if (extrapaths.empty()) {
cerr << "Could not discover root of Ghidra installation" << endl;
exit(1);
}
{
vector<string> extrapaths;
int4 i = 1;
while ((i < argc) && (argv[i][0] == '-')) {
if (argv[i][1] == 'i')
initscript = argv[++i];
else if (argv[i][1] == 's')
extrapaths.push_back(argv[++i]);
i += 1;
}
else
ghidraroot = sleighhomepath;
string ghidraroot = FileManage::discoverGhidraRoot(argv[0]);
if (ghidraroot.size() == 0) {
const char *sleighhomepath = getenv("SLEIGHHOME");
if (sleighhomepath == (const char*) 0) {
if (extrapaths.empty()) {
cerr << "Could not discover root of Ghidra installation" << endl;
exit(1);
}
}
else
ghidraroot = sleighhomepath;
}
startDecompilerLibrary(ghidraroot.c_str(), extrapaths);
}
startDecompilerLibrary(ghidraroot.c_str(),extrapaths);
IfaceStatus *status;
try {

View file

@ -607,6 +607,7 @@ int4 ActionLaneDivide::apply(Funcdata &data)
if (allStorageProcessed) break;
}
data.clearLanedAccessMap();
data.setLanedRegGenerated();
return 0;
}
@ -1209,31 +1210,12 @@ int4 ActionDeindirect::apply(Funcdata &data)
return 0;
}
/// Check if the given Varnode has a matching LanedRegister record. If so, add its
/// storage location to the given function's laned access list.
/// \param data is the given function
/// \param vn is the given Varnode
void ActionVarnodeProps::markLanedVarnode(Funcdata &data,Varnode *vn)
{
if (vn->isConstant()) return;
Architecture *glb = data.getArch();
const LanedRegister *lanedRegister = glb->getLanedRegister(vn->getAddr(),vn->getSize());
if (lanedRegister != (const LanedRegister *)0)
data.markLanedVarnode(vn,lanedRegister);
}
int4 ActionVarnodeProps::apply(Funcdata &data)
{
Architecture *glb = data.getArch();
bool cachereadonly = glb->readonlypropagate;
int4 minLanedSize = 1000000; // Default size meant to filter no Varnodes
if (!data.isLanedRegComplete()) {
int4 sz = glb->getMinimumLanedRegisterSize();
if (sz > 0)
minLanedSize = sz;
}
int4 pass = data.getHeritagePass();
VarnodeLocSet::const_iterator iter;
Varnode *vn;
@ -1242,9 +1224,29 @@ int4 ActionVarnodeProps::apply(Funcdata &data)
vn = *iter++; // Advance iterator in case vn is deleted
if (vn->isAnnotation()) continue;
int4 vnSize = vn->getSize();
if (vnSize >= minLanedSize)
markLanedVarnode(data, vn);
if (vn->hasActionProperty()) {
if (vn->isAutoLiveHold()) {
if (pass > 0) {
if (vn->isWritten()) {
PcodeOp *loadOp = vn->getDef();
if (loadOp->code() == CPUI_LOAD) {
Varnode *ptr = loadOp->getIn(1);
if (ptr->isConstant() || ptr->isReadOnly())
continue;
if (ptr->isWritten()) {
PcodeOp *copyOp = ptr->getDef();
if (copyOp->code() == CPUI_COPY) {
ptr = copyOp->getIn(0);
if (ptr->isConstant() || ptr->isReadOnly())
continue;
}
}
}
}
vn->clearAutoLiveHold();
count += 1;
}
}
else if (vn->hasActionProperty()) {
if (cachereadonly&&vn->isReadOnly()) {
if (data.fillinReadOnly(vn)) // Try to replace vn with its lookup in LoadImage
count += 1;
@ -3404,6 +3406,84 @@ uintb ActionDeadCode::gatherConsumedReturn(Funcdata &data)
return consumeVal;
}
/// \brief Determine if the given Varnode may eventually collapse to a constant
///
/// Recursively check if the Varnode is either:
/// - Copied from a constant
/// - The result of adding constants
/// - Loaded from a pointer that is a constant
///
/// \param vn is the given Varnode
/// \param addCount is the number of CPUI_INT_ADD operations seen so far
/// \param loadCount is the number of CPUI_LOAD operations seen so far
/// \return \b true if the Varnode (might) collapse to a constant
bool ActionDeadCode::isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount)
{
if (vn->isConstant()) return true;
if (!vn->isWritten()) return false;
PcodeOp *op = vn->getDef();
while(op->code() == CPUI_COPY) {
vn = op->getIn(0);
if (vn->isConstant()) return true;
if (!vn->isWritten()) return false;
op = vn->getDef();
}
switch(op->code()) {
case CPUI_INT_ADD:
if (addCount > 0) return false;
if (!isEventualConstant(op->getIn(0),addCount+1,loadCount))
return false;
return isEventualConstant(op->getIn(1),addCount+1,loadCount);
case CPUI_LOAD:
if (loadCount > 0) return false;
return isEventualConstant(op->getIn(1),0,loadCount+1);
case CPUI_INT_LEFT:
case CPUI_INT_RIGHT:
case CPUI_INT_SRIGHT:
case CPUI_INT_MULT:
if (!op->getIn(1)->isConstant())
return false;
return isEventualConstant(op->getIn(0),addCount,loadCount);
case CPUI_INT_ZEXT:
case CPUI_INT_SEXT:
return isEventualConstant(op->getIn(0),addCount,loadCount);
default:
break;
}
return false;
}
/// \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
/// criteria, it is added to the worklist and \b true is returned.
/// \param data is the function being analyzed
/// \return \b true if there was at least one LOAD added to the worklist
bool ActionDeadCode::lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist)
{
if (data.getHeritagePass() > 1) return false;
if (data.isJumptableRecoveryOn()) return false;
list<PcodeOp *>::const_iterator iter = data.beginOp(CPUI_LOAD);
list<PcodeOp *>::const_iterator enditer = data.endOp(CPUI_LOAD);
bool res = false;
while(iter != enditer) {
PcodeOp *op = *iter;
++iter;
if (op->isDead()) continue;
Varnode *vn = op->getOut();
if (vn->isConsumeVacuous()) continue;
if (isEventualConstant(op->getIn(1), 0, 0)) {
pushConsumed(~(uintb)0, vn, worklist);
vn->setAutoLiveHold();
res = true;
}
}
return res;
}
int4 ActionDeadCode::apply(Funcdata &data)
{
@ -3446,11 +3526,11 @@ int4 ActionDeadCode::apply(Funcdata &data)
op->clearIndirectSource();
if (op->isCall()) {
if (op->code() == CPUI_CALLOTHER) {
// Postpone setting consumption on CALL and CALLIND inputs
if (op->isCallWithoutSpec()) {
for(i=0;i<op->numInput();++i)
pushConsumed(~((uintb)0),op->getIn(i),worklist);
}
// Postpone setting consumption on CALL and CALLIND inputs
if (!op->isAssignment())
continue;
}
@ -3497,6 +3577,11 @@ int4 ActionDeadCode::apply(Funcdata &data)
while(!worklist.empty())
propagateConsumed(worklist);
if (lastChanceLoad(data, worklist)) {
while(!worklist.empty())
propagateConsumed(worklist);
}
for(i=0;i<manage->numSpaces();++i) {
spc = manage->getSpace(i);
if (spc == (AddrSpace *)0 || !spc->doesDeadcode()) continue;
@ -4724,11 +4809,12 @@ void TermOrder::sortTerms(void)
sort(sorter.begin(),sorter.end(),additiveCompare);
}
/// Build the default \e root Actions: decompile, jumptable, normalize, paramid, register, firstpass
/// \param allacts is the database that will hold the \e root Actions
void build_defaultactions(ActionDatabase &allacts)
/// (Re)build the default \e root Actions: decompile, jumptable, normalize, paramid, register, firstpass
void ActionDatabase::buildDefaultGroups(void)
{
if (isDefaultGroups) return;
groupmap.clear();
const char *members[] = { "base", "protorecovery", "protorecovery_a", "deindirect", "localrecovery",
"deadcode", "typerecovery", "stackptrflow",
"blockrecovery", "stackvars", "deadcontrolflow", "switchnorm",
@ -4737,36 +4823,37 @@ void build_defaultactions(ActionDatabase &allacts)
"segment", "returnsplit", "nodejoin", "doubleload", "doubleprecis",
"unreachable", "subvar", "floatprecision",
"conditionalexe", "" };
allacts.setGroup("decompile",members);
setGroup("decompile",members);
const char *jumptab[] = { "base", "noproto", "localrecovery", "deadcode", "stackptrflow",
"stackvars", "analysis", "segment", "subvar", "conditionalexe", "" };
allacts.setGroup("jumptable",jumptab);
setGroup("jumptable",jumptab);
const char *normali[] = { "base", "protorecovery", "protorecovery_b", "deindirect", "localrecovery",
"deadcode", "stackptrflow", "normalanalysis",
"stackvars", "deadcontrolflow", "analysis", "fixateproto", "nodejoin",
"unreachable", "subvar", "floatprecision", "normalizebranches",
"conditionalexe", "" };
allacts.setGroup("normalize",normali);
setGroup("normalize",normali);
const char *paramid[] = { "base", "protorecovery", "protorecovery_b", "deindirect", "localrecovery",
"deadcode", "typerecovery", "stackptrflow", "siganalysis",
"stackvars", "deadcontrolflow", "analysis", "fixateproto",
"unreachable", "subvar", "floatprecision",
"conditionalexe", "" };
allacts.setGroup("paramid",paramid);
setGroup("paramid",paramid);
const char *regmemb[] = { "base", "analysis", "subvar", "" };
allacts.setGroup("register",regmemb);
setGroup("register",regmemb);
const char *firstmem[] = { "base", "" };
allacts.setGroup("firstpass",firstmem);
setGroup("firstpass",firstmem);
isDefaultGroups = true;
}
/// Construct the \b universal Action that contains all possible components
/// \param conf is the Architecture that will use the Action
void universal_action(Architecture *conf)
void ActionDatabase::universalAction(Architecture *conf)
{
vector<Rule *>::iterator iter;
@ -4778,9 +4865,8 @@ void universal_action(Architecture *conf)
ActionGroup *actstackstall;
AddrSpace *stackspace = conf->getStackSpace();
build_defaultactions(conf->allacts);
act = new ActionRestartGroup(Action::rule_onceperfunc,"universal",1);
conf->allacts.registerUniversal(act);
registerAction(universalname,act);
act->addAction( new ActionStart("base"));
act->addAction( new ActionConstbase("base"));

View file

@ -211,9 +211,7 @@ public:
/// - Read-only Varnodes are converted to the underlying constant
/// - Volatile Varnodes are converted read/write functions
/// - Varnodes whose values are not consumed are replaced with constant 0 Varnodes
/// - Large Varnodes are flagged for lane analysis
class ActionVarnodeProps : public Action {
void markLanedVarnode(Funcdata &data,Varnode *vn); ///< Mark possible laned register storage
public:
ActionVarnodeProps(const string &g) : Action(0,"varnodeprops",g) {} ///< Constructor
virtual Action *clone(const ActionGroupList &grouplist) const {
@ -543,6 +541,8 @@ class ActionDeadCode : public Action {
static bool neverConsumed(Varnode *vn,Funcdata &data);
static void markConsumedParameters(FuncCallSpecs *fc,vector<Varnode *> &worklist);
static uintb gatherConsumedReturn(Funcdata &data);
static bool isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount);
static bool lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist);
public:
ActionDeadCode(const string &g) : Action(0,"deadcode",g) {} ///< Constructor
virtual Action *clone(const ActionGroupList &grouplist) const {

View file

@ -553,7 +553,7 @@ void ParamListStandard::assignMap(const vector<Datatype *> &proto,bool isinput,T
spc = typefactory.getArch()->getDefaultDataSpace();
int4 pointersize = spc->getAddrSize();
int4 wordsize = spc->getWordSize();
Datatype *pointertp = typefactory.getTypePointerAbsolute(pointersize,proto[i],wordsize);
Datatype *pointertp = typefactory.getTypePointer(pointersize,proto[i],wordsize);
res.back().addr = assignAddress(pointertp,status);
res.back().type = pointertp;
res.back().flags = Varnode::indirectstorage;
@ -1102,7 +1102,7 @@ void ParamListStandardOut::assignMap(const vector<Datatype *> &proto,bool isinpu
spc = typefactory.getArch()->getDefaultDataSpace();
int4 pointersize = spc->getAddrSize();
int4 wordsize = spc->getWordSize();
Datatype *pointertp = typefactory.getTypePointerAbsolute(pointersize, proto[0], wordsize);
Datatype *pointertp = typefactory.getTypePointer(pointersize, proto[0], wordsize);
res.back().addr = assignAddress(pointertp,status);
if (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign return value as a pointer");

View file

@ -36,6 +36,7 @@ Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,int4 sz)
high_level_index = 0;
cast_phase_index = 0;
glb = scope->getArch();
minLanedSize = glb->getMinimumLanedRegisterSize();
name = nm;
size = sz;
@ -69,6 +70,7 @@ void Funcdata::clear(void)
clean_up_index = 0;
high_level_index = 0;
cast_phase_index = 0;
minLanedSize = glb->getMinimumLanedRegisterSize();
localmap->clearUnlocked(); // Clear non-permanent stuff
localmap->resetLocalWindow();
@ -134,7 +136,7 @@ void Funcdata::startProcessing(void)
warningHeader("This is an inlined function");
Address baddr(baseaddr.getSpace(),0);
Address eaddr(baseaddr.getSpace(),~((uintb)0));
followFlow(baddr,eaddr,0);
followFlow(baddr,eaddr);
structureReset();
sortCallSpecs(); // Must come after structure reset
heritage.buildInfoList();
@ -343,7 +345,7 @@ void Funcdata::spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const
Symbol *sym = entry->getSymbol();
Datatype *entrytype = sym->getType();
Datatype *ptrentrytype = glb->types->getTypePointer(sz,entrytype,spaceid->getWordSize());
Datatype *ptrentrytype = glb->types->getTypePointerStripArray(sz,entrytype,spaceid->getWordSize());
bool typelock = sym->isTypeLocked();
if (typelock && (entrytype->getMetatype() == TYPE_UNKNOWN))
typelock = false;

View file

@ -56,13 +56,13 @@ class Funcdata {
restart_pending = 0x200, ///< Analysis must be restarted (because of new override info)
unimplemented_present = 0x400, ///< Set if function contains unimplemented instructions
baddata_present = 0x800, ///< Set if function flowed into bad data
double_precis_on = 0x1000, ///< Set if we are performing double precision recovery
big_varnodes_generated = 0x2000 ///< Set when search for laned registers is complete
double_precis_on = 0x1000 ///< Set if we are performing double precision recovery
};
uint4 flags; ///< Boolean properties associated with \b this function
uint4 clean_up_index; ///< Creation index of first Varnode created after start of cleanup
uint4 high_level_index; ///< Creation index of first Varnode created after HighVariables are created
uint4 cast_phase_index; ///< Creation index of first Varnode created after ActionSetCasts
uint4 minLanedSize; ///< Minimum Varnode size to check as LanedRegister
Architecture *glb; ///< Global configuration data
string name; ///< Name of function
int4 size; ///< Number of bytes of binary data in function body
@ -132,8 +132,7 @@ public:
bool isTypeRecoveryOn(void) const { return ((flags&typerecovery_on)!=0); } ///< Has data-type recovery processes started
bool hasNoCode(void) const { return ((flags & no_code)!=0); } ///< Return \b true if \b this function has no code body
void setNoCode(bool val) { if (val) flags |= no_code; else flags &= ~no_code; } ///< Toggle whether \b this has a body
bool isLanedRegComplete(void) const { return ((flags&big_varnodes_generated)!=0); } ///< Have potential laned registers been generated
void setLanedRegGenerated(void) { flags |= big_varnodes_generated; } ///< Mark that laned registers have been collected
void setLanedRegGenerated(void) { minLanedSize = 1000000; } ///< Mark that laned registers have been collected
/// \brief Toggle whether \b this is being used for jump-table recovery
///
@ -161,7 +160,7 @@ public:
void startCleanUp(void) { clean_up_index = vbank.getCreateIndex(); } ///< Start \e clean-up phase
uint4 getCleanUpIndex(void) const { return clean_up_index; } ///< Get creation index at the start of \b clean-up phase
void followFlow(const Address &baddr,const Address &eadddr,uint4 insn_max);
void followFlow(const Address &baddr,const Address &eadddr);
void truncatedFlow(const Funcdata *fd,const FlowInfo *flow);
bool inlineFlow(Funcdata *inlinefd,FlowInfo &flow,PcodeOp *callop);
void overrideFlow(const Address &addr,uint4 type);
@ -201,6 +200,8 @@ public:
Varnode *findSpacebaseInput(AddrSpace *id) const;
void spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const Address &rampoint,uintb origval,int4 origsize);
int4 getHeritagePass(void) const { return heritage.getPass(); } ///< Get overall count of heritage passes
/// \brief Get the number of heritage passes performed for the given address space
///
/// \param spc is the address space
@ -350,7 +351,7 @@ public:
/// \brief End of (input or free) Varnodes at a given storage address
VarnodeDefSet::const_iterator endDef(uint4 fl,const Address &addr) const { return vbank.endDef(fl,addr); }
void markLanedVarnode(Varnode *vn,const LanedRegister *lanedReg); ///< Mark Varnode as potential laned register
void checkForLanedRegister(int4 size,const Address &addr); ///< Check for a potential laned register
map<VarnodeData,const LanedRegister *>::const_iterator beginLaneAccess(void) const { return lanedMap.begin(); } ///< Beginning iterator over laned accesses
map<VarnodeData,const LanedRegister *>::const_iterator endLaneAccess(void) const { return lanedMap.end(); } ///< Ending iterator over laned accesses
void clearLanedAccessMap(void) { lanedMap.clear(); } ///< Clear records from the laned access list
@ -434,7 +435,7 @@ public:
void opMarkNonPrinting(PcodeOp *op) { op->setFlag(PcodeOp::nonprinting); } ///< Mark PcodeOp as not being printed
void opMarkSpecialPrint(PcodeOp *op) { op->setAdditionalFlag(PcodeOp::special_print); } ///< Mark PcodeOp as needing special printing
void opMarkNoCollapse(PcodeOp *op) { op->setFlag(PcodeOp::nocollapse); } ///< Mark PcodeOp as not collapsible
void opMarkCpoolTransformed(PcodeOp *op) { op->setFlag(PcodeOp::is_cpool_transformed); } ///< Mark cpool record was visited
void opMarkCpoolTransformed(PcodeOp *op) { op->setAdditionalFlag(PcodeOp::is_cpool_transformed); } ///< Mark cpool record was visited
void opMarkCalculatedBool(PcodeOp *op) { op->setFlag(PcodeOp::calculated_bool); } ///< Mark PcodeOp as having boolean output
void opMarkSpacebasePtr(PcodeOp *op) { op->setFlag(PcodeOp::spacebase_ptr); } ///< Mark PcodeOp as LOAD/STORE from spacebase ptr
void opClearSpacebasePtr(PcodeOp *op) { op->clearFlag(PcodeOp::spacebase_ptr); } ///< Unmark PcodeOp as using spacebase ptr
@ -508,7 +509,9 @@ public:
void switchEdge(FlowBlock *inblock,BlockBasic *outbefore,FlowBlock *outafter);
void spliceBlockBasic(BlockBasic *bl); ///< Merge the given basic block with the block it flows into
void installSwitchDefaults(void); ///< Make sure default switch cases are properly labeled
static bool replaceLessequal(Funcdata &data,PcodeOp *op); ///< Replace INT_LESSEQUAL and INT_SLESSEQUAL expressions
bool replaceLessequal(PcodeOp *op); ///< Replace INT_LESSEQUAL and INT_SLESSEQUAL expressions
bool distributeIntMultAdd(PcodeOp *op); ///< Distribute constant coefficient to additive input
bool collapseIntMultMult(Varnode *vn); ///< Collapse constant coefficients for two chained CPUI_INT_MULT
static bool compareCallspecs(const FuncCallSpecs *a,const FuncCallSpecs *b);
#ifdef OPACTION_DEBUG

View file

@ -793,7 +793,7 @@ void Funcdata::nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop)
uint4 vflags = opvn->getFlags();
vflags &= (Varnode::externref | Varnode::volatil | Varnode::incidental_copy |
Varnode::readonly | Varnode::persist |
Varnode::addrtied | Varnode::addrforce | Varnode::auto_live);
Varnode::addrtied | Varnode::addrforce);
newvn->setFlags(vflags);
}

View file

@ -700,12 +700,10 @@ void Funcdata::markIndirectCreation(PcodeOp *indop,bool possibleOutput)
/// \brief Generate raw p-code for the function
///
/// Follow flow from the entry point generating PcodeOps for each instruction encountered.
/// The caller can provide a bounding range that constrains where control can flow to
/// and can also provide a maximum number of instructions that will be followed.
/// The caller can provide a bounding range that constrains where control can flow to.
/// \param baddr is the beginning of the constraining range
/// \param eaddr is the end of the constraining range
/// \param insn_max is the maximum number of instructions to follow
void Funcdata::followFlow(const Address &baddr,const Address &eaddr,uint4 insn_max)
void Funcdata::followFlow(const Address &baddr,const Address &eaddr)
{
if (!obank.empty()) {
@ -719,8 +717,7 @@ void Funcdata::followFlow(const Address &baddr,const Address &eaddr,uint4 insn_m
FlowInfo flow(*this,obank,bblocks,qlst);
flow.setRange(baddr,eaddr);
flow.setFlags(fl);
if (insn_max != 0)
flow.setMaximumInstructions(insn_max);
flow.setMaximumInstructions(glb->max_instructions);
flow.generateOps();
size = flow.getSize();
// Cannot keep track of function sizes in general because of non-contiguous functions
@ -972,10 +969,9 @@ void Funcdata::overrideFlow(const Address &addr,uint4 type)
/// - `c <= x` with `c-1 < x` OR
/// - `x <= c` with `x < c+1`
///
/// \param data is the function being analyzed
/// \param op is comparison PcodeOp
/// \return true if a valid replacement was performed
bool Funcdata::replaceLessequal(Funcdata &data,PcodeOp *op)
bool Funcdata::replaceLessequal(PcodeOp *op)
{
Varnode *vn;
@ -998,17 +994,105 @@ bool Funcdata::replaceLessequal(Funcdata &data,PcodeOp *op)
if (op->code() == CPUI_INT_SLESSEQUAL) {
if ((val<0)&&(val+diff>0)) return false; // Check for sign overflow
if ((val>0)&&(val+diff<0)) return false;
data.opSetOpcode(op,CPUI_INT_SLESS);
opSetOpcode(op,CPUI_INT_SLESS);
}
else { // Check for unsigned overflow
if ((diff==-1)&&(val==0)) return false;
if ((diff==1)&&(val==-1)) return false;
data.opSetOpcode(op,CPUI_INT_LESS);
opSetOpcode(op,CPUI_INT_LESS);
}
uintb res = (val+diff) & calc_mask(vn->getSize());
Varnode *newvn = data.newConstant(vn->getSize(),res);
Varnode *newvn = newConstant(vn->getSize(),res);
newvn->copySymbol(vn); // Preserve data-type (and any Symbol info)
data.opSetInput(op,newvn,i);
opSetInput(op,newvn,i);
return true;
}
/// If a term has a multiplicative coefficient, but the underlying term is still additive,
/// in some situations we may need to distribute the coefficient before simplifying further.
/// The given PcodeOp is a INT_MULT where the second input is a constant. We also
/// know the first input is formed with INT_ADD. Distribute the coefficient to the INT_ADD inputs.
/// \param op is the given PcodeOp
/// \return \b true if the action was performed
bool Funcdata::distributeIntMultAdd(PcodeOp *op)
{
Varnode *newvn0,*newvn1;
PcodeOp *addop = op->getIn(0)->getDef();
Varnode *vn0 = addop->getIn(0);
Varnode *vn1 = addop->getIn(1);
if ((vn0->isFree())&&(!vn0->isConstant())) return false;
if ((vn1->isFree())&&(!vn1->isConstant())) return false;
uintb coeff = op->getIn(1)->getOffset();
int4 size = op->getOut()->getSize();
// Do distribution
if (vn0->isConstant()) {
uintb val = coeff * vn0->getOffset();
val &= calc_mask(size);
newvn0 = newConstant(size,val);
}
else {
PcodeOp *newop0 = newOp(2,op->getAddr());
opSetOpcode(newop0,CPUI_INT_MULT);
newvn0 = newUniqueOut(size,newop0);
opSetInput(newop0, vn0, 0); // To first input of original add
Varnode *newcvn = newConstant(size,coeff);
opSetInput(newop0, newcvn, 1);
opInsertBefore(newop0, op);
}
if (vn1->isConstant()) {
uintb val = coeff * vn1->getOffset();
val &= calc_mask(size);
newvn1 = newConstant(size,val);
}
else {
PcodeOp *newop1 = newOp(2,op->getAddr());
opSetOpcode(newop1,CPUI_INT_MULT);
newvn1 = newUniqueOut(size,newop1);
opSetInput(newop1, vn1, 0); // To second input of original add
Varnode *newcvn = newConstant(size,coeff);
opSetInput(newop1, newcvn, 1);
opInsertBefore(newop1, op);
}
opSetInput( op, newvn0, 0); // new ADD's inputs are outputs of new MULTs
opSetInput( op, newvn1, 1);
opSetOpcode(op, CPUI_INT_ADD);
return true;
}
/// If:
/// - The given Varnode is defined by a CPUI_INT_MULT.
/// - The second input to the INT_MULT is a constant.
/// - The first input is defined by another CPUI_INT_MULT,
/// - This multiply is also by a constant.
///
/// The constants are combined and \b true is returned.
/// Otherwise no change is made and \b false is returned.
/// \param vn is the given Varnode
/// \return \b true if a change was made
bool Funcdata::collapseIntMultMult(Varnode *vn)
{
if (!vn->isWritten()) return false;
PcodeOp *op = vn->getDef();
if (op->code() != CPUI_INT_MULT) return false;
Varnode *constVnFirst = op->getIn(1);
if (!constVnFirst->isConstant()) return false;
if (!op->getIn(0)->isWritten()) return false;
PcodeOp *otherMultOp = op->getIn(0)->getDef();
if (otherMultOp->code() != CPUI_INT_MULT) return false;
Varnode *constVnSecond = otherMultOp->getIn(1);
if (!constVnSecond->isConstant()) return false;
Varnode *invn = otherMultOp->getIn(0);
if (invn->isFree()) return false;
int4 size = invn->getSize();
uintb val = (constVnFirst->getOffset() * constVnSecond->getOffset()) & calc_mask(size);
Varnode *newvn = newConstant(size,val);
opSetInput(op,newvn,1);
opSetInput(op,invn,0);
return true;
}
@ -1109,7 +1193,7 @@ void opFlipInPlaceExecute(Funcdata &data,vector<PcodeOp *> &fliplist)
data.opSwapInput(op,0,1);
if ((opc == CPUI_INT_LESSEQUAL)||(opc == CPUI_INT_SLESSEQUAL))
Funcdata::replaceLessequal(data,op);
data.replaceLessequal(op);
}
}
}

View file

@ -85,6 +85,9 @@ Varnode *Funcdata::newUnique(int4 s,Datatype *ct)
ct = glb->types->getBase(s,TYPE_UNKNOWN);
Varnode *vn = vbank.createUnique(s,ct);
assignHigh(vn);
if (s >= minLanedSize)
checkForLanedRegister(s, vn->getAddr());
// No chance of matching localmap
return vn;
}
@ -104,6 +107,8 @@ Varnode *Funcdata::newVarnodeOut(int4 s,const Address &m,PcodeOp *op)
op->setOutput(vn);
assignHigh(vn);
if (s >= minLanedSize)
checkForLanedRegister(s,m);
uint4 vflags = 0;
SymbolEntry *entry = localmap->queryProperties(m,s,op->getAddr(),vflags);
if (entry != (SymbolEntry *)0)
@ -126,6 +131,8 @@ Varnode *Funcdata::newUniqueOut(int4 s,PcodeOp *op)
Varnode *vn = vbank.createDefUnique(s,ct,op);
op->setOutput(vn);
assignHigh(vn);
if (s >= minLanedSize)
checkForLanedRegister(s, vn->getAddr());
// No chance of matching localmap
return vn;
}
@ -147,6 +154,8 @@ Varnode *Funcdata::newVarnode(int4 s,const Address &m,Datatype *ct)
vn = vbank.create(s,m,ct);
assignHigh(vn);
if (s >= minLanedSize)
checkForLanedRegister(s,m);
uint4 vflags=0;
SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),Address(),vflags);
if (entry != (SymbolEntry *)0) // Let entry try to force type
@ -248,7 +257,7 @@ Varnode *Funcdata::cloneVarnode(const Varnode *vn)
// These are the flags we allow to be cloned
vflags &= (Varnode::annotation | Varnode::externref |
Varnode::readonly | Varnode::persist |
Varnode::addrtied | Varnode::addrforce | Varnode::auto_live |
Varnode::addrtied | Varnode::addrforce |
Varnode::indirect_creation | Varnode::incidental_copy |
Varnode::volatil | Varnode::mapped);
newvn->setFlags(vflags);
@ -280,19 +289,21 @@ void Funcdata::destroyVarnode(Varnode *vn)
vbank.destroy(vn);
}
/// Record the given Varnode as a potential laned register access.
/// The address and size of the Varnode is recorded, anticipating that new
/// Varnodes at the same storage location may be created
/// \param vn is the given Varnode to mark
/// \param lanedReg is the laned register record to associate with the Varnode
void Funcdata::markLanedVarnode(Varnode *vn,const LanedRegister *lanedReg)
/// 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 addr is the starting address of the storage range
void Funcdata::checkForLanedRegister(int4 size,const Address &addr)
{
const LanedRegister *lanedRegister = glb->getLanedRegister(addr,size);
if (lanedRegister == (const LanedRegister *)0)
return;
VarnodeData storage;
storage.space = vn->getSpace();
storage.offset = vn->getOffset();
storage.size = vn->getSize();
lanedMap[storage] = lanedReg;
storage.space = addr.getSpace();
storage.offset = addr.getOffset();
storage.size = size;
lanedMap[storage] = lanedRegister;
}
/// Look up the Symbol visible in \b this function's Scope and return the HighVariable
@ -492,7 +503,7 @@ void Funcdata::transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffs
{
uintb newConsume = (vn->getConsume() >> 8*lsbOffset) & calc_mask(newVn->getSize());
uint4 vnFlags = vn->getFlags() & (Varnode::directwrite|Varnode::addrforce|Varnode::auto_live);
uint4 vnFlags = vn->getFlags() & (Varnode::directwrite|Varnode::addrforce);
newVn->setFlags(vnFlags); // Preserve addrforce setting
newVn->setConsume(newConsume);
@ -794,7 +805,7 @@ void Funcdata::calcNZMask(void)
/// \brief Update Varnode properties based on (new) Symbol information
///
/// Boolean properties \b addrtied, \b addrforce, \b auto_live, and \b nolocalalias
/// Boolean properties \b addrtied, \b addrforce, and \b nolocalalias
/// for Varnodes are updated based on new Symbol information they map to.
/// The caller can elect to update data-type information as well, where Varnodes
/// and their associated HighVariables have their data-type finalized based symbols.
@ -863,7 +874,7 @@ bool Funcdata::syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes)
/// to point to the first Varnode after the affected set.
///
/// The only properties that can be effectively changed with this
/// routine are \b mapped, \b addrtied, \b addrforce, \b auto_live, and \b nolocalalias.
/// routine are \b mapped, \b addrtied, \b addrforce, and \b nolocalalias.
/// HighVariable splits must occur if \b addrtied is cleared.
///
/// If the given data-type is non-null, an attempt is made to update all the Varnodes
@ -884,13 +895,13 @@ bool Funcdata::syncVarnodesWithSymbol(VarnodeLocSet::const_iterator &iter,uint4
// We take special care with the addrtied flag
// as we cannot set it here if it is clear
// We can CLEAR but not SET the addrtied flag
// If addrtied is cleared, so should addrforce and auto_live
// If addrtied is cleared, so should addrforce
if ((flags&Varnode::addrtied)==0) // Is the addrtied flags cleared
mask |= Varnode::addrtied | Varnode::addrforce | Varnode::auto_live;
mask |= Varnode::addrtied | Varnode::addrforce;
// We can set the nolocalalias flag, but not clear it
// If nolocalalias is set, then addrforce should be cleared
if ((flags&Varnode::nolocalalias)!=0)
mask |= Varnode::nolocalalias | Varnode::addrforce | Varnode::auto_live;
mask |= Varnode::nolocalalias | Varnode::addrforce;
flags &= mask;
vn = *iter;

View file

@ -19,6 +19,7 @@
#include "ghidra_translate.hh"
#include "typegrp_ghidra.hh"
#include "comment_ghidra.hh"
#include "string_ghidra.hh"
#include "cpool_ghidra.hh"
#include "inject_ghidra.hh"
@ -346,6 +347,12 @@ void ArchitectureGhidra::buildCommentDB(DocumentStorage &store)
commentdb = new CommentDatabaseGhidra(this);
}
void ArchitectureGhidra::buildStringManager(DocumentStorage &store)
{
stringManager = new GhidraStringManager(this,2048);
}
void ArchitectureGhidra::buildConstantPool(DocumentStorage &store)
{
@ -615,6 +622,49 @@ void ArchitectureGhidra::getBytes(uint1 *buf,int4 size,const Address &inaddr)
readResponseEnd(sin);
}
void ArchitectureGhidra::getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc)
{
sout.write("\000\000\001\004",4);
writeStringStream(sout,"getString");
sout.write("\000\000\001\016",4); // Beginning of string header
addr.saveXml(sout,maxBytes);
sout.write("\000\000\001\017",4);
writeStringStream(sout,ct->getName());
sout.write("\000\000\001\016",4); // Beginning of string header
sout << dec << (int8)ct->getId(); // Pass as a signed integer
sout.write("\000\000\001\017",4);
sout.write("\000\000\001\005",4);
sout.flush();
readToResponse(sin);
int4 type = readToAnyBurst(sin);
if (type == 12) {
int4 c = sin.get();
uint4 size = (c-0x20);
c = sin.get();
size ^= ((c-0x20)<<6);
isTrunc = (sin.get() != 0);
buffer.reserve(size);
uint1 *dblbuf = new uint1[size * 2];
sin.read((char *)dblbuf,size*2);
for (int4 i=0; i < size; i++) {
buffer.push_back(((dblbuf[i*2]-'A') << 4) | (dblbuf[i*2 + 1]-'A'));
}
delete [] dblbuf;
type = readToAnyBurst(sin);
if (type != 13)
throw JavaError("alignment","Expecting byte alignment end");
type = readToAnyBurst(sin);
}
if ((type&1)==1) {
// Leave the buffer empty
}
else
throw JavaError("alignment","Expecting end of query response");
}
/// \brief Retrieve p-code to inject for a specific context
///
/// The particular injection is named and is of one of the types:

View file

@ -74,6 +74,7 @@ class ArchitectureGhidra : public Architecture {
virtual PcodeInjectLibrary *buildPcodeInjectLibrary(void);
virtual void buildTypegrp(DocumentStorage &store);
virtual void buildCommentDB(DocumentStorage &store);
virtual void buildStringManager(DocumentStorage &store);
virtual void buildConstantPool(DocumentStorage &store);
virtual void buildContext(DocumentStorage &store);
virtual void buildSpecFile(DocumentStorage &store);
@ -124,6 +125,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);
virtual void printMessage(const string &message) const;
static void segvHandler(int4 sig); ///< Handler for a segment violation (SIGSEGV) signal

View file

@ -17,44 +17,47 @@
#include "flow.hh"
#include "blockaction.hh"
#ifdef OPACTION_DEBUG
#ifdef __REMOTE_SOCKET__
#include "ifacedecomp.hh"
static IfaceStatus *ghidra_dcp = (IfaceStatus *)0;
static RemoteSocket *remote = (RemoteSocket *)0;
void turn_on_debugging(Funcdata *fd)
/// \brief Establish a debug console for decompilation of the given function
///
/// Attempt to connect to a UNIX domain socket and direct the i/o streams to
/// the decompiler console interface. The socket must have been previously established
/// by another process.
/// From the command-line, `nc -l -U /tmp/ghidrasocket` for example.
void connect_to_console(Funcdata *fd)
{
if (ghidra_dcp == (IfaceStatus *)0) {
ghidra_dcp = new IfaceStatus("[ghidradbg]> ",cin,cout);
ghidra_dcp->optr = (ostream *)0;
ghidra_dcp->fileoptr = (ostream *)0;
IfaceCapability::registerAllCommands(ghidra_dcp);
if (remote == (RemoteSocket *)0) {
remote = new RemoteSocket();
if (remote->open("/tmp/ghidrasocket")) {
ghidra_dcp = new IfaceStatus("[ghidradbg]> ",*remote->getInputStream(),*remote->getOutputStream());
IfaceCapability::registerAllCommands(ghidra_dcp);
}
}
// Check if debug script exists
ifstream is("ghidracom.txt");
if (!is) return;
is.close();
if (!remote->isSocketOpen())
return;
IfaceDecompData *decomp_data = (IfaceDecompData *)ghidra_dcp->getData("decompile");
decomp_data->fd = fd;
decomp_data->conf = fd->getArch();
ghidra_dcp->pushScript("ghidracom.txt","ghidradbg> ");
ghidra_dcp->optr = new ofstream("ghidrares.txt");
ghidra_dcp->fileoptr = ghidra_dcp->optr;
decomp_data->conf->setDebugStream(ghidra_dcp->optr);
ostream *oldPrintStream = decomp_data->conf->print->getOutputStream();
bool emitXml = decomp_data->conf->print->emitsXml();
decomp_data->conf->setDebugStream(remote->getOutputStream());
decomp_data->conf->print->setOutputStream(remote->getOutputStream());
decomp_data->conf->print->setXML(false);
ghidra_dcp->reset();
mainloop(ghidra_dcp);
ghidra_dcp->popScript();
}
void turn_off_debugging(Funcdata *fd)
{
if (ghidra_dcp->optr != (ostream *)0) {
delete ghidra_dcp->optr;
ghidra_dcp->optr = (ostream *)0;
}
decomp_data->conf->clearAnalysis(fd);
decomp_data->conf->print->setOutputStream(oldPrintStream);
decomp_data->conf->print->setXML(emitXml);
fd->debugDisable();
decomp_data->conf->allacts.getCurrent()->clearBreakPoints();
}
#endif
@ -213,9 +216,13 @@ void DeregisterProgram::loadParameters(void)
void DeregisterProgram::rawAction(void)
{
#ifdef OPACTION_DEBUG
#ifdef __REMOTE_SOCKET__
if (ghidra_dcp != (IfaceStatus *)0)
delete ghidra_dcp;
if (remote != (RemoteSocket *)0)
delete remote;
ghidra_dcp = (IfaceStatus *)0;
remote = (RemoteSocket *)0;
#endif
if (ghidra != (ArchitectureGhidra *)0) {
res = 1;
@ -245,6 +252,7 @@ void FlushNative::rawAction(void)
ghidra->symboltab->deleteSubScopes(globscope); // Flush cached function and globals database
ghidra->types->clearNoncore(); // Reset type information
ghidra->commentdb->clear(); // Clear any comments
ghidra->stringManager->clear(); // Clear string decodings
ghidra->cpool->clear();
res = 0;
}
@ -283,14 +291,11 @@ void DecompileAt::rawAction(void)
throw LowlevelError(s.str());
}
if (!fd->isProcStarted()) {
#ifdef OPACTION_DEBUG
turn_on_debugging(fd);
#ifdef __REMOTE_SOCKET__
connect_to_console(fd);
#endif
ghidra->allacts.getCurrent()->reset( *fd );
ghidra->allacts.getCurrent()->perform( *fd );
#ifdef OPACTION_DEBUG
turn_off_debugging(fd);
#endif
}
sout.write("\000\000\001\016",4);
@ -433,6 +438,7 @@ void SetOptions::rawAction(void)
{
res = false;
ghidra->resetDefaults();
ghidra->options->restoreXml(doc->getRoot());
delete doc;
doc = (Document *)0;

View file

@ -230,9 +230,8 @@ public:
virtual void rawAction(void);
};
#ifdef OPACTION_DEBUG
extern void turn_on_debugging(Funcdata *fd);
extern void turn_off_debugging(Funcdata *fd);
#ifdef __REMOTE_SOCKET__
extern void connect_to_console(Funcdata *fd);
#endif
#endif

View file

@ -654,7 +654,7 @@ Datatype *PointerModifier::modType(Datatype *base,const TypeDeclarator *decl,Arc
{
int4 addrsize = glb->getDefaultDataSpace()->getAddrSize();
Datatype *restype;
restype = glb->types->getTypePointerAbsolute(addrsize,base,glb->getDefaultDataSpace()->getWordSize());
restype = glb->types->getTypePointer(addrsize,base,glb->getDefaultDataSpace()->getWordSize());
return restype;
}
@ -1037,7 +1037,7 @@ Datatype *CParse::newStruct(const string &ident,vector<TypeDeclarator *> *declis
sublist.back().offset = -1; // Let typegrp figure out offset
}
if (!glb->types->setFields(sublist,res,-1)) {
if (!glb->types->setFields(sublist,res,-1,0)) {
setError("Bad structure definition");
glb->types->destroyType(res);
return (Datatype *)0;

View file

@ -800,10 +800,15 @@ bool Heritage::protectFreeStores(AddrSpace *spc,vector<PcodeOp *> &freeStores)
++iter;
if (op->isDead()) continue;
Varnode *vn = op->getIn(1);
if (vn->isWritten()) {
PcodeOp *copyOp = vn->getDef();
if (copyOp->code() == CPUI_COPY)
vn = copyOp->getIn(0);
while (vn->isWritten()) {
PcodeOp *defOp = vn->getDef();
OpCode opc = defOp->code();
if (opc == CPUI_COPY)
vn = defOp->getIn(0);
else if (opc == CPUI_INT_ADD && defOp->getIn(1)->isConstant())
vn = defOp->getIn(0);
else
break;
}
if (vn->isFree() && vn->getSpace() == spc) {
fd->opMarkSpacebasePtr(op); // Mark op as spacebase STORE, even though we're not sure
@ -913,8 +918,17 @@ bool Heritage::discoverIndexedStackPointers(AddrSpace *spc,vector<PcodeOp *> &fr
}
case CPUI_STORE:
{
if (curNode.traversals != 0) {
generateStoreGuard(curNode, op, spc);
if (op->getIn(1) == curNode.vn) { // Make sure the STORE pointer comes from our path
if (curNode.traversals != 0) {
generateStoreGuard(curNode, op, spc);
}
else {
// If there were no traversals (of non-constant ADD or MULTIEQUAL) then the
// pointer is equal to the stackpointer plus a constant (through an indirect is possible)
// This will likely get resolved in the next heritage pass, but we leave the
// spacebaseptr mark on, so that that the indirects don't get removed
fd->opMarkSpacebasePtr(op);
}
}
break;
}
@ -2340,7 +2354,7 @@ const LoadGuard *Heritage::getStoreGuard(PcodeOp *op) const
/// \brief Get the number times heritage was performed for the given address space
///
/// A negative number indicates the number of passes to be wait before the first
/// A negative number indicates the number of passes to wait before the first
/// heritage will occur.
/// \param spc is the given address space
/// \return the number of heritage passes performed
@ -2350,7 +2364,7 @@ int4 Heritage::numHeritagePasses(AddrSpace *spc) const
const HeritageInfo *info = getInfo(spc);
if (!info->isHeritaged())
throw LowlevelError("Trying to calculate passes for non-heritaged space");
return (info->delay - pass);
return (pass - info->delay);
}
/// Record that Varnodes have been removed from the given space so that we can

View file

@ -219,7 +219,7 @@ class Heritage {
/// \brief Get the heritage status for the given address space
HeritageInfo *getInfo(AddrSpace *spc) { return &(infolist[spc->getIndex()]); }
/// \brief Get the heriage status for the given address space
/// \brief Get the heritage status for the given address space
const HeritageInfo *getInfo(AddrSpace *spc) const { return &(infolist[spc->getIndex()]); }
void splitJoinLevel(vector<Varnode *> &lastcombo,vector<Varnode *> &nextlev,JoinRecord *joinrec);
@ -267,6 +267,8 @@ class Heritage {
public:
Heritage(Funcdata *data); ///< Constructor
int4 getPass(void) const { return pass; } ///< Get overall count of heritage passes
/// \brief Get the pass number when the given address was heritaged
///
/// \param addr is the given address

View file

@ -127,6 +127,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status)
status->registerCom(new IfcCallFixup(),"fixup","call");
status->registerCom(new IfcCallOtherFixup(),"fixup","callother");
status->registerCom(new IfcVolatile(),"volatile");
status->registerCom(new IfcReadonly(),"readonly");
status->registerCom(new IfcPreferSplit(),"prefersplit");
status->registerCom(new IfcStructureBlocks(),"structure","blocks");
status->registerCom(new IfcAnalyzeRange(), "analyze","range");
@ -224,6 +225,14 @@ IfaceDecompData::~IfaceDecompData(void)
// fd will get deleted with Database
}
void IfaceDecompData::allocateCallGraph(void)
{
if (cgraph != (CallGraph *)0)
delete cgraph;
cgraph = new CallGraph(conf);
}
void IfaceDecompData::abortFunction(ostream &s)
{ // Clear references to current function
@ -360,10 +369,10 @@ static void IfcFollowFlow(ostream &s,IfaceDecompData *dcp,const Address &offset,
if (size==0) {
Address baddr(dcp->fd->getAddress().getSpace(),0);
Address eaddr(dcp->fd->getAddress().getSpace(),dcp->fd->getAddress().getSpace()->getHighest());
dcp->fd->followFlow(baddr,eaddr,0);
dcp->fd->followFlow(baddr,eaddr);
}
else
dcp->fd->followFlow(offset,offset+size,0);
dcp->fd->followFlow(offset,offset+size);
s << "Function " << dcp->fd->getName() << ": ";
dcp->fd->getAddress().printRaw(s);
s << endl;
@ -2095,10 +2104,7 @@ void IfcDuplicateHash::iterationCallback(Funcdata *fd)
void IfcCallGraphBuild::execute(istream &s)
{ // Build call graph from existing function starts
if (dcp->cgraph != (CallGraph *)0)
delete dcp->cgraph;
dcp->cgraph = new CallGraph(dcp->conf);
dcp->allocateCallGraph();
dcp->cgraph->buildAllNodes(); // Build a node in the graph for existing symbols
quick = false;
@ -2145,11 +2151,7 @@ void IfcCallGraphBuild::iterationCallback(Funcdata *fd)
void IfcCallGraphBuildQuick::execute(istream &s)
{ // Build call graph from existing function starts, do only disassembly
if (dcp->cgraph != (CallGraph *)0)
delete dcp->cgraph;
dcp->cgraph = new CallGraph(dcp->conf);
dcp->allocateCallGraph();
dcp->cgraph->buildAllNodes(); // Build a node in the graph for existing symbols
quick = true;
iterateFunctionsAddrOrder();
@ -2198,7 +2200,7 @@ void IfcCallGraphLoad::execute(istream &s)
DocumentStorage store;
Document *doc = store.parseDocument(is);
dcp->cgraph = new CallGraph(dcp->conf);
dcp->allocateCallGraph();
dcp->cgraph->restoreXml(doc->getRoot());
*status->optr << "Successfully read in callgraph" << endl;
@ -2304,6 +2306,22 @@ void IfcVolatile::execute(istream &s)
*status->optr << "Successfully marked range as volatile" << endl;
}
void IfcReadonly::execute(istream &s)
{
int4 size = 0;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read required address
if (size == 0)
throw IfaceExecutionError("Must specify a size");
Range range( addr.getSpace(), addr.getOffset(), addr.getOffset() + (size-1));
dcp->conf->symboltab->setPropertyRange(Varnode::readonly,range);
*status->optr << "Successfully marked range as readonly" << endl;
}
void IfcPreferSplit::execute(istream &s)
{ // Mark a particular storage location as something we would prefer to split
@ -2727,6 +2745,7 @@ void mainloop(IfaceStatus *status) {
for(;;) {
while(!status->isStreamFinished()) {
status->writePrompt();
status->optr->flush();
execute(status,dcp);
}
if (status->done) break;

View file

@ -54,6 +54,7 @@ public:
#endif
IfaceDecompData(void);
virtual ~IfaceDecompData(void);
void allocateCallGraph(void);
void abortFunction(ostream &s);
void clearArchitecture(void);
};
@ -531,6 +532,11 @@ public:
virtual void execute(istream &s);
};
class IfcReadonly : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPreferSplit : public IfaceDecompCommand {
public:
virtual void execute(istream &s);

View file

@ -14,6 +14,12 @@
* limitations under the License.
*/
#include "interface.hh"
#ifdef __REMOTE_SOCKET__
#include "sys/socket.h"
#include "sys/un.h"
#include "unistd.h"
#include "ext/stdio_filebuf.h"
#endif
vector<IfaceCapability *> IfaceCapability::thelist;
@ -30,6 +36,84 @@ void IfaceCapability::registerAllCommands(IfaceStatus *status)
thelist[i]->registerCommands(status);
}
#ifdef __REMOTE_SOCKET__
RemoteSocket::RemoteSocket(void)
{
fileDescriptor = 0;
inbuf = (basic_filebuf<char> *)0;
outbuf = (basic_filebuf<char> *)0;
inStream = (istream *)0;
outStream = (ostream *)0;
isOpen = false;
}
void RemoteSocket::close(void)
{
if (inStream != (istream *)0) {
delete inStream;
inStream = (istream *)0;
}
if (outStream != (ostream *)0) {
delete outStream;
outStream = (ostream *)0;
}
if (inbuf != (basic_filebuf<char> *)0) {
// Destroying the buffer should automatically close the socket
delete inbuf;
inbuf = (basic_filebuf<char> *)0;
}
if (outbuf != (basic_filebuf<char> *)0) {
delete outbuf;
outbuf = (basic_filebuf<char> *)0;
}
isOpen = false;
}
bool RemoteSocket::open(const string &filename)
{
if (isOpen) return false;
if ((fileDescriptor = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
throw IfaceError("Could not create socket");
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
int4 len = filename.length();
if (len >= sizeof(addr.sun_path))
throw IfaceError("Socket name too long");
memcpy(addr.sun_path,filename.c_str(),len);
addr.sun_path[len] = '\0';
len += sizeof(addr.sun_family);
if (connect(fileDescriptor, (struct sockaddr *)&addr, len) < 0) {
::close(fileDescriptor);
return false;
}
fdopen(fileDescriptor, "r");
inbuf = new __gnu_cxx::stdio_filebuf<char>(fileDescriptor,ios::in);
fdopen(fileDescriptor, "w");
outbuf = new __gnu_cxx::stdio_filebuf<char>(fileDescriptor,ios::out);
inStream = new istream(inbuf);
outStream = new ostream(outbuf);
isOpen = true;
return true;
}
bool RemoteSocket::isSocketOpen(void)
{
if (!isOpen) return false;
if (inStream->eof()) {
close();
return false;
}
return true;
}
#endif
IfaceStatus::IfaceStatus(const string &prmpt,istream &is,ostream &os,int4 mxhist)
{
@ -75,6 +159,15 @@ void IfaceStatus::popScript(void)
inerror = false;
}
void IfaceStatus::reset(void)
{
while(!inputstack.empty())
popScript();
errorisdone = false;
done = false;
}
void IfaceStatus::saveHistory(const string &line)
{ // Save line in circular history buffer

View file

@ -44,6 +44,32 @@
using namespace std;
#ifdef __REMOTE_SOCKET__
/// \brief A wrapper around a UNIX domain socket
///
/// The open() command attempts to connect to given socket name,
/// which must have been previously established by an external process.
/// The socket is bound to a C++ istream and ostream.
class RemoteSocket {
int fileDescriptor; ///< Descriptor for the socket
basic_filebuf<char> *inbuf; ///< Input buffer associated with the socket
basic_filebuf<char> *outbuf; ///< Output buffer for the socket
istream *inStream; ///< The C++ input stream
ostream *outStream; ///< The C++ output stream
bool isOpen; ///< Has the socket been opened
public:
RemoteSocket(void); ///< Constructor
~RemoteSocket(void) { close(); } ///< Destructor
bool open(const string &filename); ///< Connect to the given socket
bool isSocketOpen(void); ///< Return \b true if the socket is ready to transfer data
istream *getInputStream(void) { return inStream; } ///< Get the input stream
ostream *getOutputStream(void) { return outStream; } ///< Get the output stream
void close(void); ///< Close the streams and socket
};
#endif
struct IfaceError {
string explain; // Explanatory string
IfaceError(const string &s) { explain = s; }
@ -136,6 +162,7 @@ public:
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; }
void registerCom(IfaceCommand *fptr, const char *nm1,

View file

@ -627,6 +627,9 @@ void PcodeOpBank::addToCodeList(PcodeOp *op)
case CPUI_STORE:
op->codeiter = storelist.insert(storelist.end(),op);
break;
case CPUI_LOAD:
op->codeiter = loadlist.insert(loadlist.end(), op);
break;
case CPUI_RETURN:
op->codeiter = returnlist.insert(returnlist.end(),op);
break;
@ -648,6 +651,9 @@ void PcodeOpBank::removeFromCodeList(PcodeOp *op)
case CPUI_STORE:
storelist.erase(op->codeiter);
break;
case CPUI_LOAD:
loadlist.erase(op->codeiter);
break;
case CPUI_RETURN:
returnlist.erase(op->codeiter);
break;
@ -663,6 +669,7 @@ void PcodeOpBank::clearCodeLists(void)
{
storelist.clear();
loadlist.clear();
returnlist.clear();
useroplist.clear();
}
@ -896,6 +903,8 @@ list<PcodeOp *>::const_iterator PcodeOpBank::begin(OpCode opc) const
switch(opc) {
case CPUI_STORE:
return storelist.begin();
case CPUI_LOAD:
return loadlist.begin();
case CPUI_RETURN:
return returnlist.begin();
case CPUI_CALLOTHER:
@ -912,6 +921,8 @@ list<PcodeOp *>::const_iterator PcodeOpBank::end(OpCode opc) const
switch(opc) {
case CPUI_STORE:
return storelist.end();
case CPUI_LOAD:
return loadlist.end();
case CPUI_RETURN:
return returnlist.end();
case CPUI_CALLOTHER:

View file

@ -95,7 +95,7 @@ public:
spacebase_ptr = 0x4000000, ///< Loads or stores from a dynamic pointer into a spacebase
indirect_creation = 0x8000000, ///< Output varnode is created by indirect effect
calculated_bool = 0x10000000, ///< Output has been determined to be a 1-bit boolean value
is_cpool_transformed = 0x20000000, ///< Have we checked for cpool transforms
has_callspec = 0x20000000, ///< Op has a call specification associated with it
ptrflow = 0x40000000, ///< Op consumes or produces a ptr
indirect_store = 0x80000000 ///< CPUI_INDIRECT is caused by CPUI_STORE
};
@ -107,7 +107,8 @@ public:
special_print = 0x10, ///< Op is marked for special printing
modified = 0x20, ///< This op has been modified by the current action
warning = 0x40, ///< Warning has been generated for this op
incidental_copy = 0x80 ///< Treat this as \e incidental for parameter recovery algorithms
incidental_copy = 0x80, ///< Treat this as \e incidental for parameter recovery algorithms
is_cpool_transformed = 0x100 ///< Have we checked for cpool transforms
};
private:
TypeOp *opcode; ///< Pointer to class providing behavioral details of the operation
@ -164,6 +165,8 @@ public:
bool isDead(void) const { return ((flags&PcodeOp::dead)!=0); } ///< Return \b true if this op is dead
bool isAssignment(void) const { return (output!=(Varnode *)0); } ///< Return \b true is this op has an output
bool isCall(void) const { return ((flags&PcodeOp::call)!=0); } ///< Return \b true if this op indicates call semantics
/// \brief Return \b true if this op acts as call but does not have a full specification
bool isCallWithoutSpec(void) const { return ((flags&(PcodeOp::call|PcodeOp::has_callspec))==PcodeOp::call); }
bool isMarker(void) const { return ((flags&PcodeOp::marker)!=0); } ///< Return \b true is a special SSA form op
bool isIndirectCreation(void) const { return ((flags&PcodeOp::indirect_creation)!=0); } ///< Return \b true if op creates a varnode indirectly
bool isIndirectStore(void) const { return ((flags&PcodeOp::indirect_store)!=0); } ///< Return \b true if \b this INDIRECT is caused by STORE
@ -203,7 +206,7 @@ public:
/// \brief Return \b true if output is 1-bit boolean
bool isCalculatedBool(void) const { return ((flags&(PcodeOp::calculated_bool|PcodeOp::booloutput))!=0); }
/// \brief Return \b true if we have already examined this cpool
bool isCpoolTransformed(void) const { return ((flags&PcodeOp::is_cpool_transformed)!=0); }
bool isCpoolTransformed(void) const { return ((addlflags&PcodeOp::is_cpool_transformed)!=0); }
bool isCollapsible(void) const; ///< Return \b true if this can be collapsed to a COPY of a constant
/// \brief Return \b true if this LOADs or STOREs from a dynamic \e spacebase pointer
bool usesSpacebasePtr(void) const { return ((flags&PcodeOp::spacebase_ptr)!=0); }
@ -247,6 +250,7 @@ class PcodeOpBank {
list<PcodeOp *> deadlist; ///< List of \e dead PcodeOps
list<PcodeOp *> alivelist; ///< List of \e alive PcodeOps
list<PcodeOp *> storelist; ///< List of STORE PcodeOps
list<PcodeOp *> loadlist; ///< list of LOAD PcodeOps
list<PcodeOp *> returnlist; ///< List of RETURN PcodeOps
list<PcodeOp *> useroplist; ///< List of user-defined PcodeOps
list<PcodeOp *> deadandgone; ///< List of retired PcodeOps

View file

@ -80,6 +80,7 @@ OptionDatabase::OptionDatabase(Architecture *g)
registerOption(new OptionJumpLoad());
registerOption(new OptionToggleRule());
registerOption(new OptionAliasBlock());
registerOption(new OptionMaxInstruction());
}
OptionDatabase::~OptionDatabase(void)
@ -816,3 +817,19 @@ string OptionAliasBlock::apply(Architecture *glb,const string &p1,const string &
return "Alias block level unchanged";
return "Alias block level set to " + p1;
}
string OptionMaxInstruction::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
{
if (p1.size() == 0)
throw ParseError("Must specify number of instructions");
int4 newMax = -1;
istringstream s1(p1);
s1.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
s1 >> newMax;
if (newMax < 0)
throw ParseError("Bad maxinstruction parameter");
glb->max_instructions = newMax;
return "Maximum instructions per function set";
}

View file

@ -264,4 +264,10 @@ public:
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
};
class OptionMaxInstruction : public ArchOption {
public:
OptionMaxInstruction(void) { name="maxinstruction"; } ///< Constructor
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
};
#endif

View file

@ -344,6 +344,12 @@ void EmitXml::spaces(int4 num,int4 bump)
}
}
void EmitXml::resetDefaults(void)
{
resetDefaultsInternal();
}
int4 TokenSplit::countbase = 0;
/// Emit markup or content corresponding to \b this token on a low-level emitter.
@ -536,15 +542,15 @@ void TokenSplit::printDebug(ostream &s) const
}
#endif
EmitPrettyPrint::EmitPrettyPrint(int4 mls)
: EmitXml(), scanqueue( 3*mls ), tokqueue( 3*mls )
EmitPrettyPrint::EmitPrettyPrint(void)
: EmitXml(), scanqueue( 3*100 ), tokqueue( 3*100 )
{
lowlevel = new EmitNoXml(); // Do not emit xml by default
maxlinesize = mls;
spaceremain = maxlinesize;
needbreak = false;
commentmode = false;
resetDefaultsPrettyPrint();
}
EmitPrettyPrint::~EmitPrettyPrint(void)
@ -1213,3 +1219,11 @@ void EmitPrettyPrint::setMaxLineSize(int4 val)
spaceremain = maxlinesize;
clear();
}
void EmitPrettyPrint::resetDefaults(void)
{
lowlevel->resetDefaults();
resetDefaultsInternal();
resetDefaultsPrettyPrint();
}

View file

@ -80,8 +80,9 @@ protected:
int4 indentlevel; ///< Current indent level (in fixed width characters)
int4 parenlevel; ///< Current depth of parentheses
int4 indentincrement; ///< Change in indentlevel per level of nesting
void resetDefaultsInternal(void) { indentincrement = 2; } ///< Set options to default values for EmitXml
public:
EmitXml(void) { s = (ostream *)0; indentlevel=0; parenlevel=0; indentincrement=2; } ///< Constructor
EmitXml(void) { s = (ostream *)0; indentlevel=0; parenlevel=0; resetDefaultsInternal(); } ///< Constructor
/// \brief Possible types of syntax highlighting
enum syntax_highlight {
@ -196,6 +197,9 @@ public:
/// \return \b true if \b this produces an XML markup of its emitted source code
virtual bool emitsXml(void) const { return true; }
/// \brief (Re)set the default emitting options
virtual void resetDefaults(void);
/// \brief Get the current parentheses depth
///
/// \return the current number of open parenthetical groups
@ -649,9 +653,11 @@ template<typename _type>
void circularqueue<_type>::setMax(int4 sz)
{
delete [] cache;
max = sz;
cache = new _type [ sz ];
if (max != sz) {
delete [] cache;
max = sz;
cache = new _type [ sz ];
}
left = 1; // This operation empties queue
right = 0;
}
@ -721,8 +727,9 @@ 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); }
public:
EmitPrettyPrint(int4 mls); ///< Construct with an initial maximum line size
EmitPrettyPrint(void); ///< Construct with an initial maximum line size
virtual ~EmitPrettyPrint(void);
virtual int4 beginDocument(void);
virtual void endDocument(int4 id);
@ -768,6 +775,7 @@ public:
virtual int4 getMaxLineSize(void) const { return maxlinesize; }
virtual void setCommentFill(const string &fill) { commentfill = fill; }
virtual bool emitsXml(void) const { return lowlevel->emitsXml(); }
virtual void resetDefaults(void);
void setXML(bool val); ///< Toggle whether the low-level emitter emits XML markup or not
};

View file

@ -94,12 +94,6 @@ PrintLanguage *PrintCCapability::buildLanguage(Architecture *glb)
PrintC::PrintC(Architecture *g,const string &nm) : PrintLanguage(g,nm)
{
option_NULL = false;
option_inplace_ops = false;
option_convention = true;
option_nocasts = false;
option_unplaced = false;
option_hide_exts = true;
nullToken = "NULL";
// Set the flip tokens
@ -111,7 +105,7 @@ PrintC::PrintC(Architecture *g,const string &nm) : PrintLanguage(g,nm)
not_equal.negate = &equal;
castStrategy = new CastStrategyC();
setCStyleComments();
resetDefaultsPrintC();
}
/// Push nested components of a data-type declaration onto a stack, so we can access it bottom up
@ -651,6 +645,13 @@ void PrintC::opPtradd(const PcodeOp *op)
{
bool printval = isSet(print_load_value|print_store_value);
uint4 m = mods & ~(print_load_value|print_store_value);
if (!printval) {
TypePointer *tp = (TypePointer *)op->getIn(0)->getHigh()->getType();
if (tp->getMetatype() == TYPE_PTR) {
if (tp->getPtrTo()->getMetatype() == TYPE_ARRAY)
printval = true;
}
}
if (printval) // Use array notation if we need value
pushOp(&subscript,op);
else // just a '+'
@ -1170,7 +1171,7 @@ void PrintC::printUnicode(ostream &s,int4 onechar) const
s << "\\x" << setfill('0') << setw(8) << hex << onechar;
return;
}
writeUtf8(s, onechar); // emit normally
StringManager::writeUtf8(s, onechar); // emit normally
}
void PrintC::pushType(const Datatype *ct)
@ -1210,32 +1211,6 @@ bool PrintC::doEmitWideCharPrefix(void) const
return true;
}
/// \brief Check if the byte buffer has a (unicode) string terminator
///
/// \param buffer is the byte buffer
/// \param size is the number of bytes in the buffer
/// \param charsize is the presumed size (in bytes) of character elements
/// \return \b true if a string terminator is found
bool PrintC::hasCharTerminator(uint1 *buffer,int4 size,int4 charsize)
{
for(int4 i=0;i<size;i+=charsize) {
bool isTerminator = true;
for(int4 j=0;j<charsize;++j) {
if (buffer[i+j] != 0) { // Non-zero bytes means character can't be a null terminator
isTerminator = false;
break;
}
}
if (isTerminator) return true;
}
return false;
}
#define STR_LITERAL_BUFFER_MAXSIZE 2048
#define STR_LITERAL_BUFFER_INCREMENT 32
/// \brief Print a quoted (unicode) string at the given address.
///
/// Data for the string is obtained directly from the LoadImage. The bytes are checked
@ -1243,43 +1218,40 @@ bool PrintC::hasCharTerminator(uint1 *buffer,int4 size,int4 charsize)
/// pass, the string is emitted.
/// \param s is the output stream to print to
/// \param addr is the address of the string data within the LoadImage
/// \param charsize is the number of bytes in an encoded element (i.e. UTF8, UTF16, or UTF32)
/// \param charType is the underlying character data-type
/// \return \b true if a proper string was found and printed to the stream
bool PrintC::printCharacterConstant(ostream &s,const Address &addr,int4 charsize) const
bool PrintC::printCharacterConstant(ostream &s,const Address &addr,Datatype *charType) const
{
uint1 buffer[STR_LITERAL_BUFFER_MAXSIZE+4]; // Additional buffer for get_codepoint skip readahead
int4 curBufferSize = 0;
bool foundTerminator = false;
try {
do {
uint4 newBufferSize = curBufferSize + STR_LITERAL_BUFFER_INCREMENT;
glb->loader->loadFill(buffer+curBufferSize,STR_LITERAL_BUFFER_INCREMENT,addr + curBufferSize);
foundTerminator = hasCharTerminator(buffer+curBufferSize,STR_LITERAL_BUFFER_INCREMENT,charsize);
curBufferSize = newBufferSize;
} while ((curBufferSize < STR_LITERAL_BUFFER_MAXSIZE)&&(!foundTerminator));
} catch(DataUnavailError &err) {
return false;
}
buffer[curBufferSize] = 0; // Make sure bytes for final codepoint read are initialized
buffer[curBufferSize+1] = 0;
buffer[curBufferSize+2] = 0;
buffer[curBufferSize+3] = 0;
bool bigend = glb->translate->isBigEndian();
bool res;
if (isCharacterConstant(buffer,curBufferSize,charsize)) {
if (doEmitWideCharPrefix() && charsize > 1)
s << 'L'; // Print symbol indicating wide character
s << '"';
if (!escapeCharacterData(s,buffer,curBufferSize,charsize,bigend))
s << "...\" /* TRUNCATED STRING LITERAL */";
else s << '"';
StringManager *manager = glb->stringManager;
res = true;
}
// Retrieve UTF8 version of string
bool isTrunc = false;
const vector<uint1> &buffer(manager->getStringData(addr, charType, isTrunc));
if (buffer.empty())
return false;
if (doEmitWideCharPrefix() && charType->getSize() > 1 && !charType->isOpaqueString())
s << 'L'; // Print symbol indicating wide character
s << '"';
escapeCharacterData(s,buffer.data(),buffer.size(),1,glb->translate->isBigEndian());
if (isTrunc)
s << "...\" /* TRUNCATED STRING LITERAL */";
else
res = false;
return res;
s << '"';
return true;
}
void PrintC::resetDefaultsPrintC(void)
{
option_convention = true;
option_hide_exts = true;
option_inplace_ops = false;
option_nocasts = false;
option_NULL = false;
option_unplaced = false;
setCStyleComments();
}
/// \brief Push a single character constant to the RPN stack
@ -1367,7 +1339,7 @@ bool PrintC::pushPtrCharConstant(uintb val,const TypePointer *ct,const Varnode *
ostringstream str;
Datatype *subct = ct->getPtrTo();
if (!printCharacterConstant(str,stringaddr,subct->getSize()))
if (!printCharacterConstant(str,stringaddr,subct))
return false; // Can we get a nice ASCII string
pushAtom(Atom(str.str(),vartoken,EmitXml::const_color,op,vn));
@ -1571,7 +1543,7 @@ void PrintC::pushSymbol(const Symbol *sym,const Varnode *vn,const PcodeOp *op)
SymbolEntry *entry = sym->getFirstWholeMap();
if (entry != (SymbolEntry *)0) {
ostringstream s;
if (printCharacterConstant(s,entry->getAddr(),subct->getSize())) {
if (printCharacterConstant(s,entry->getAddr(),subct)) {
pushAtom(Atom(s.str(),vartoken,EmitXml::const_color,op,vn));
return;
}
@ -1931,6 +1903,13 @@ void PrintC::emitGotoStatement(const FlowBlock *bl,const FlowBlock *exp_bl,
emit->endStatement(id);
}
void PrintC::resetDefaults(void)
{
PrintLanguage::resetDefaults();
resetDefaultsPrintC();
}
void PrintC::adjustTypeOperators(void)
{
@ -1950,25 +1929,6 @@ void PrintC::setCommentStyle(const string &nm)
throw LowlevelError("Unknown comment style. Use \"c\" or \"cplusplus\"");
}
bool PrintC::isCharacterConstant(const uint1 *buf,int4 size,int4 charsize) const
{
// Return true if this looks like a c-string
// If the string is encoded in UTF8 or ASCII, we get (on average) a bit of check
// per character. For UTF16, the surrogate reserved area gives at least some check.
if (buf == (const uint1 *)0) return false;
bool bigend = glb->translate->isBigEndian();
int4 i=0;
int4 skip = charsize;
while(i<size) {
int4 codepoint = getCodepoint(buf+i,charsize,bigend,skip);
if (codepoint < 0) return false;
if (codepoint == 0) break;
i += skip;
}
return true;
}
/// \brief Emit the definition of the given data-type
///
/// This is currently limited to a 'struct' or 'enum' definitions. The

View file

@ -157,8 +157,8 @@ protected:
void opFunc(const PcodeOp *op); ///< Push a \e functional expression based on the given p-code op to the RPN stack
void opTypeCast(const PcodeOp *op); ///< Push the given p-code op using type-cast syntax to the RPN stack
void opHiddenFunc(const PcodeOp *op); ///< Push the given p-code op as a hidden token
static bool hasCharTerminator(uint1 *buffer,int4 size,int4 charsize);
bool printCharacterConstant(ostream &s,const Address &addr,int4 charsize) const;
bool printCharacterConstant(ostream &s,const Address &addr,Datatype *charType) const;
void resetDefaultsPrintC(void); ///< Set default values for options specific to PrintC
virtual void pushConstant(uintb val,const Datatype *ct,
const Varnode *vn,const PcodeOp *op);
virtual bool pushEquate(uintb val,int4 sz,const EquateSymbol *sym,
@ -200,9 +200,9 @@ public:
void setDisplayUnplaced(bool val) { option_unplaced = val; } ///< Toggle whether \e unplaced comments are displayed in the header
void setHideImpliedExts(bool val) { option_hide_exts = val; } ///< Toggle whether implied extensions are hidden
virtual ~PrintC(void) {}
virtual void resetDefaults(void);
virtual void adjustTypeOperators(void);
virtual void setCommentStyle(const string &nm);
virtual bool isCharacterConstant(const uint1 *buf,int4 size,int4 charsize) const;
virtual void docTypeDefinitions(const TypeFactory *typegrp);
virtual void docAllGlobals(void);
virtual void docSingleGlobal(const Symbol *sym);

View file

@ -190,7 +190,7 @@ void PrintJava::printUnicode(ostream &s,int4 onechar) const
s << "\\ux" << setfill('0') << setw(8) << hex << onechar;
return;
}
writeUtf8(s, onechar); // Emit normally
StringManager::writeUtf8(s, onechar); // Emit normally
}
void PrintJava::opLoad(const PcodeOp *op)

View file

@ -61,13 +61,10 @@ PrintLanguage::PrintLanguage(Architecture *g,const string &nm)
castStrategy = (CastStrategy *)0;
name = nm;
curscope = (Scope *)0;
emit = new EmitPrettyPrint(100);
emit = new EmitPrettyPrint();
mods = 0;
pending = 0;
line_commentindent = 20;
instr_comment_type = Comment::user2 | Comment::warning;
head_comment_type = Comment::header | Comment::warningheader;
resetDefaultsInternal();
}
PrintLanguage::~PrintLanguage(void)
@ -481,136 +478,6 @@ bool PrintLanguage::unicodeNeedsEscape(int4 codepoint)
return false;
}
/// Encode the given unicode codepoint as UTF8 (1, 2, 3, or 4 bytes) and
/// write the bytes to the stream.
/// \param s is the output stream
/// \param codepoint is the unicode codepoint
void PrintLanguage::writeUtf8(ostream &s,int4 codepoint)
{
uint1 bytes[4];
int4 size;
if (codepoint < 0)
throw LowlevelError("Negative unicode codepoint");
if (codepoint < 128) {
s.put((uint1)codepoint);
return;
}
int4 bits = mostsigbit_set(codepoint) + 1;
if (bits > 21)
throw LowlevelError("Bad unicode codepoint");
if (bits < 12) { // Encode with two bytes
bytes[0] = 0xc0 ^ ((codepoint >> 6)&0x1f);
bytes[1] = 0x80 ^ (codepoint & 0x3f);
size = 2;
}
else if (bits < 17) {
bytes[0] = 0xe0 ^ ((codepoint >> 12)&0xf);
bytes[1] = 0x80 ^ ((codepoint >> 6)&0x3f);
bytes[2] = 0x80 ^ (codepoint & 0x3f);
size = 3;
}
else {
bytes[0] = 0xf0 ^ ((codepoint >> 18) & 7);
bytes[1] = 0x80 ^ ((codepoint >> 12) & 0x3f);
bytes[2] = 0x80 ^ ((codepoint >> 6) & 0x3f);
bytes[3] = 0x80 ^ (codepoint & 0x3f);
size = 4;
}
s.write((char *)bytes, size);
}
/// Pull the first two bytes from the byte array and combine them in the indicated endian order
/// \param buf is the byte array
/// \param bigend is \b true to request big endian encoding
/// \return the decoded UTF16 element
inline int4 PrintLanguage::readUtf16(const uint1 *buf,bool bigend)
{
int4 codepoint;
if (bigend) {
codepoint = buf[0];
codepoint <<= 8;
codepoint += buf[1];
}
else {
codepoint = buf[1];
codepoint <<= 8;
codepoint += buf[0];
}
return codepoint;
}
/// \brief Extract the next \e unicode \e codepoint from an array of character data
///
/// One or more bytes is consumed from the array, and the number of bytes used is passed back.
/// \param buf is a pointer to the bytes in the character array
/// \param charsize is 1 for UTF8, 2 for UTF16, or 4 for UTF32
/// \param bigend is \b true for big endian encoding of the UTF element
/// \param skip is a reference for passing back the number of bytes consumed
/// \return the codepoint or -1 if the encoding is invalid
int4 PrintLanguage::getCodepoint(const uint1 *buf,int4 charsize,bool bigend,int4 &skip)
{
int4 codepoint;
int4 sk = 0;
if (charsize==2) { // UTF-16
codepoint = readUtf16(buf,bigend);
sk += 2;
if ((codepoint>=0xD800)&&(codepoint<=0xDBFF)) { // high surrogate
int4 trail=readUtf16(buf+2,bigend);
sk += 2;
if ((trail<0xDC00)||(trail>0xDFFF)) return -1; // Bad trail
codepoint = (codepoint<<10) + trail + (0x10000 - (0xD800 << 10) - 0xDC00);
}
else if ((codepoint>=0xDC00)&&(codepoint<=0xDFFF)) return -1; // trail before high
}
else if (charsize==1) { // UTF-8
int4 val = buf[0];
if ((val&0x80)==0) {
codepoint = val;
sk = 1;
}
else if ((val&0xe0)==0xc0) {
int4 val2 = buf[1];
sk = 2;
if ((val2&0xc0)!=0x80) return -1; // Not a valid UTF8-encoding
codepoint = ((val&0x1f)<<6) | (val2 & 0x3f);
}
else if ((val&0xf0)==0xe0) {
int4 val2 = buf[1];
int4 val3 = buf[2];
sk = 3;
if (((val2&0xc0)!=0x80)||((val3&0xc0)!=0x80)) return -1; // invalid encoding
codepoint = ((val&0xf)<<12) | ((val2&0x3f)<<6) | (val3 & 0x3f);
}
else if ((val&0xf8)==0xf0) {
int4 val2 = buf[1];
int4 val3 = buf[2];
int4 val4 = buf[3];
sk = 4;
if (((val2&0xc0)!=0x80)||((val3&0xc0)!=0x80)||((val4&0xc0)!=0x80)) return -1; // invalid encoding
codepoint = ((val&7)<<18) | ((val2&0x3f)<<12) | ((val3&0x3f)<<6) | (val4 & 0x3f);
}
else
return -1;
}
else if (charsize == 4) { // UTF-32
sk = 4;
if (bigend)
codepoint = (buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + buf[3];
else
codepoint = (buf[3]<<24) + (buf[2]<<16) + (buf[1]<<8) + buf[0];
}
else
return -1;
if (codepoint >= 0xd800 && codepoint <= 0xdfff)
return -1; // Reserved for surrogates, invalid codepoints
skip = sk;
return codepoint;
}
/// \brief Emit a byte buffer to the stream as unicode characters.
///
/// Characters are emitted until we reach a terminator character or \b count bytes is consumed.
@ -627,7 +494,7 @@ bool PrintLanguage::escapeCharacterData(ostream &s,const uint1 *buf,int4 count,i
int4 skip = charsize;
int4 codepoint = 0;
while(i<count) {
codepoint = getCodepoint(buf+i,charsize,bigend,skip);
codepoint = StringManager::getCodepoint(buf+i,charsize,bigend,skip);
if (codepoint == 0 || codepoint == -1) break;
printUnicode(s,codepoint);
i += skip;
@ -692,6 +559,15 @@ void PrintLanguage::opUnary(const OpToken *tok,const PcodeOp *op)
pushVnImplied(op->getIn(0),op,mods);
}
void PrintLanguage::resetDefaultsInternal(void)
{
mods = 0;
head_comment_type = Comment::header | Comment::warningheader;
line_commentindent = 20;
instr_comment_type = Comment::user2 | Comment::warning;
}
/// The comment will get emitted as a single line using the high-level language's
/// delimiters with the given indent level
/// \param indent is the number of characters to indent
@ -767,6 +643,13 @@ void PrintLanguage::setFlat(bool val)
mods &= ~flat;
}
void PrintLanguage::resetDefaults(void)
{
emit->resetDefaults();
resetDefaultsInternal();
}
void PrintLanguage::clear(void)
{

View file

@ -267,14 +267,13 @@ protected:
void emitOp(const ReversePolish &entry); ///< Send an operator token from the RPN to the emitter
void emitAtom(const Atom &atom); ///< Send an variable token from the RPN to the emitter
static bool unicodeNeedsEscape(int4 codepoint); ///< Determine if the given codepoint needs to be escaped
static void writeUtf8(ostream &s,int4 codepoint); ///< Write unicode character to stream in UTF8 encoding
static int4 readUtf16(const uint1 *buf,bool bigend); ///< Read a 2-byte UTF16 element from a byte array
static int4 getCodepoint(const uint1 *buf,int4 charsize,bool bigend,int4 &skip);
bool escapeCharacterData(ostream &s,const uint1 *buf,int4 count,int4 charsize,bool bigend) const;
void recurse(void); ///< Emit from the RPN stack as much as possible
void opBinary(const OpToken *tok,const PcodeOp *op); ///< Push a binary operator onto the RPN stack
void opUnary(const OpToken *tok,const PcodeOp *op); ///< Push a unary operator onto the RPN stack
int4 getPending(void) const { return pending; } ///< Get the number of pending nodes yet to be put on the RPN stack
void resetDefaultsInternal(void); ///< Reset options to default for PrintLanguage
/// \brief Print a single unicode character as a \e character \e constant for the high-level language
///
@ -421,6 +420,7 @@ public:
void setFlat(bool val); ///< Set whether nesting code structure should be emitted
virtual void adjustTypeOperators(void)=0; ///< Set basic data-type information for p-code operators
virtual void resetDefaults(void); ///< Set printing options to their default value
virtual void clear(void); ///< Clear the RPN stack and the low-level emitter
virtual void setIntegerFormat(const string &nm); ///< Set the default integer format
@ -431,14 +431,6 @@ public:
/// \param nm is the configuration description
virtual void setCommentStyle(const string &nm)=0;
/// \brief Decide is the given byte array looks like a character string
///
/// This looks for encodings and/or a terminator that is appropriate for the high-level language
/// \param buf is a pointer to the byte array
/// \param size is the number of bytes in the array
/// \param charsize is the size in bytes of the encoding element (i.e. UTF8, UTF16, etc.) to assume
virtual bool isCharacterConstant(const uint1 *buf,int4 size,int4 charsize) const=0;
/// \brief Emit definitions of data-types
///
/// \param typegrp is the container for the data-types that should be defined

View file

@ -94,57 +94,6 @@ Varnode *RuleCollectTerms::getMultCoeff(Varnode *vn,uintb &coef)
return testop->getIn(0);
}
/// If a term has a multiplicative coefficient, but the underlying term is still additive,
/// in some situations we may need to distribute the coefficient before simplifying further.
/// The given PcodeOp is a INT_MULT where the second input is a constant. We also
/// know the first input is formed with INT_ADD. Distribute the coefficient to the INT_ADD inputs.
/// \param data is the function being analyzed
/// \param op is the given PcodeOp
/// \return 1 if the action was performed
int4 RuleCollectTerms::doDistribute(Funcdata &data,PcodeOp *op)
{
Varnode *newvn1,*newvn2,*newcvn;
PcodeOp *newop1, *newop2;
PcodeOp *addop = op->getIn(0)->getDef();
Varnode *vn0 = addop->getIn(0);
Varnode *vn1 = addop->getIn(1);
if ((vn0->isFree())&&(!vn0->isConstant())) return 0;
if ((vn1->isFree())&&(!vn1->isConstant())) return 0;
uintb coeff = op->getIn(1)->getOffset();
int4 size = op->getOut()->getSize();
// Do distribution
newop1 = data.newOp(2,op->getAddr());
data.opSetOpcode(newop1,CPUI_INT_MULT);
newvn1 = data.newUniqueOut(size,newop1);
newop2 = data.newOp(2,op->getAddr());
data.opSetOpcode(newop2,CPUI_INT_MULT);
newvn2 = data.newUniqueOut(size,newop2);
if (vn0->isConstant())
data.opSetInput(newop1, data.newConstant(size,vn0->getOffset()),0);
else
data.opSetInput(newop1, vn0, 0); // To first input of original add
newcvn = data.newConstant(size,coeff);
data.opSetInput(newop1, newcvn, 1);
data.opInsertBefore(newop1, op);
if (vn1->isConstant())
data.opSetInput(newop2, data.newConstant(size,vn1->getOffset()),0);
else
data.opSetInput(newop2, vn1, 0); // To second input of original add
newcvn = data.newConstant(size,coeff);
data.opSetInput(newop2, newcvn, 1);
data.opInsertBefore(newop2, op);
data.opSetInput( op, newvn1, 0); // new ADD's inputs are outputs of new MULTs
data.opSetInput( op, newvn2, 1);
data.opSetOpcode(op, CPUI_INT_ADD);
return 1;
}
/// \class RuleCollectTerms
/// \brief Collect terms in a sum: `V * c + V * d => V * (c + d)`
void RuleCollectTerms::getOpList(vector<uint4> &oplist) const
@ -177,9 +126,9 @@ int4 RuleCollectTerms::applyOp(PcodeOp *op,Funcdata &data)
vn2 = getMultCoeff(vn2,coef2);
if (vn1 == vn2) { // Terms that can be combined
if (order[i-1]->getMultiplier() != (PcodeOp *)0)
return doDistribute(data,order[i-1]->getMultiplier());
return data.distributeIntMultAdd(order[i-1]->getMultiplier()) ? 1 : 0;
if (order[i]->getMultiplier() != (PcodeOp *)0)
return doDistribute(data,order[i]->getMultiplier());
return data.distributeIntMultAdd(order[i]->getMultiplier()) ? 1 : 0;
coef1 = (coef1 + coef2) & calc_mask(vn1->getSize()); // The new coefficient
Varnode *newcoeff = data.newConstant(vn1->getSize(),coef1);
Varnode *zerocoeff = data.newConstant(vn1->getSize(),0);
@ -660,7 +609,7 @@ void RuleIntLessEqual::getOpList(vector<uint4> &oplist) const
int4 RuleIntLessEqual::applyOp(PcodeOp *op,Funcdata &data)
{
if (Funcdata::replaceLessequal(data,op))
if (data.replaceLessequal(op))
return 1;
return 0;
}
@ -2932,10 +2881,17 @@ int4 RuleIndirectCollapse::applyOp(PcodeOp *op,Funcdata &data)
return 0;
}
else if (indop->usesSpacebasePtr()) {
const LoadGuard *guard = data.getStoreGuard(indop);
if (guard != (const LoadGuard *)0) {
if (guard->isGuarded(op->getOut()->getAddr()))
if (indop->code() == CPUI_STORE) {
const LoadGuard *guard = data.getStoreGuard(indop);
if (guard != (const LoadGuard *)0) {
if (guard->isGuarded(op->getOut()->getAddr()))
return 0;
}
else {
// A marked STORE that is not guarded should eventually get converted to a COPY
// so we keep the INDIRECT until that happens
return 0;
}
}
}
else
@ -5646,262 +5602,528 @@ int4 RuleEqual2Constant::applyOp(PcodeOp *op,Funcdata &data)
return 1;
}
/// \brief Accumulate details of given term and continue tree traversal
///
/// If the given Varnode is a constant or multiplicative term, update
/// totals in the state object. If the Varnode is additive, traverse its sub-terms.
/// \param vn is the given Varnode term
/// \param state is the state object
/// \return \b true if Varnode is a NON-multiple
bool RulePtrArith::checkTerm(Varnode *vn,AddTreeState *state)
void AddTreeState::clear(void)
{
uintb val;
intb rem;
Varnode *vnconst,*vnterm;
PcodeOp *def;
multsum = 0;
nonmultsum = 0;
multiple.clear();
coeff.clear();
nonmult.clear();
correct = 0;
offset = 0;
valid = true;
isDistributeUsed = false;
isSubtype = false;
distributeOp = (PcodeOp *)0;
}
if (vn == state->ptr) return false;
if (vn->isConstant()) {
val = vn->getOffset();
if (state->size == 0)
rem = val;
else {
intb sval = (intb)val;
sign_extend(sval,vn->getSize()*8-1);
rem = sval % state->size;
}
if (rem!=0) { // constant is not multiple of size
state->nonmultsum += val;
return true;
}
state->multsum += val; // Add multiples of size into multsum
return false;
}
if (vn->isWritten()) {
def = vn->getDef();
if (def->code() == CPUI_INT_ADD) // Recurse
return spanAddTree(def,state);
if (def->code() == CPUI_COPY) { // Not finished reducing yet
state->valid = false;
return false;
}
if (def->code() == CPUI_INT_MULT) { // Check for constant coeff indicating size
vnconst = def->getIn(1);
vnterm = def->getIn(0);
if (vnconst->isConstant()) {
val = vnconst->getOffset();
if (state->size == 0)
rem = val;
else {
intb sval = (intb) val;
sign_extend(sval, vn->getSize() * 8 - 1);
rem = sval % state->size;
}
if (rem!=0) {
if ((val > state->size)&&(state->size!=0)) {
state->valid = false; // Size is too big: pointer type must be wrong
return false;
}
return true;
}
if (rem==0) {
state->multiple.push_back(vnterm);
state->coeff.push_back(val);
return false;
AddTreeState::AddTreeState(Funcdata &d,PcodeOp *op,int4 slot)
: data(d)
{
baseOp = op;
ptr = op->getIn(slot);
ct = (const TypePointer *)ptr->getType();
ptrsize = ptr->getSize();
ptrmask = calc_mask(ptrsize);
baseType = ct->getPtrTo();
size = AddrSpace::byteToAddressInt(baseType->getSize(),ct->getWordSize());
multsum = 0; // Sums start out as zero
nonmultsum = 0;
correct = 0;
offset = 0;
valid = true; // Valid until proven otherwise
preventDistribution = false;
isDistributeUsed = false;
isSubtype = false;
distributeOp = (PcodeOp *)0;
}
/// Even if the current base data-type is not an array, the pointer expression may incorporate
/// an array access for a sub component. This manifests as a non-constant non-multiple terms in
/// the tree. If this term is itself defined by a CPUI_INT_MULT with a constant, the constant
/// indicates a likely element size. Return a non-zero value, the likely element size, if there
/// is evidence of a non-constant non-multiple term. Return zero otherwise.
/// \return a non-zero value indicating likely element size, or zero
uint4 AddTreeState::findArrayHint(void) const
{
uint4 res = 0;
for(int4 i=0;i<nonmult.size();++i) {
Varnode *vn = nonmult[i];
if (vn->isConstant()) continue;
uint4 vncoeff = 1;
if (vn->isWritten()) {
PcodeOp *op = vn->getDef();
if (op->code() == CPUI_INT_MULT) {
Varnode *vnconst = op->getIn(1);
if (vnconst->isConstant()) {
intb sval = vnconst->getOffset();
sign_extend(sval,vnconst->getSize()*8-1);
vncoeff = (sval < 0) ? (uint4)-sval : (uint4)sval;
}
}
}
if (vncoeff > res)
res = vncoeff;
}
return res;
}
/// \brief Given an offset into the base data-type and array hints find sub-component being referenced
///
/// An explicit offset should target a specific sub data-type,
/// but array indexing may confuse things. This method passes
/// back the offset of the best matching component, searching among components
/// that are \e nearby the given offset, preferring a matching array element size
/// and a component start that is nearer to the offset.
/// \param off is the given offset into the data-type
/// \param arrayHint if non-zero indicates array access, where the value is the element size
/// \param newoff is used to pass back the actual offset of the selected component
/// \return \b true if a good component match was found
bool AddTreeState::hasMatchingSubType(uintb off,uint4 arrayHint,uintb *newoff) const
{
if (arrayHint == 0)
return (baseType->getSubType(off,newoff) != (Datatype *)0);
int4 elSizeBefore;
uintb offBefore;
Datatype *typeBefore = baseType->nearestArrayedComponentBackward(off, &offBefore, &elSizeBefore);
if (typeBefore != (Datatype *)0) {
if (arrayHint == 1 || elSizeBefore == arrayHint) {
int4 sizeAddr = AddrSpace::byteToAddressInt(typeBefore->getSize(),ct->getWordSize());
if (offBefore < sizeAddr) {
// If the offset is \e inside a component with a compatible array, return it.
*newoff = offBefore;
return true;
}
}
}
int4 elSizeAfter;
uintb offAfter;
Datatype *typeAfter = baseType->nearestArrayedComponentForward(off, &offAfter, &elSizeAfter);
if (typeBefore == (Datatype *)0 && typeAfter == (Datatype *)0)
return (baseType->getSubType(off,newoff) != (Datatype *)0);
if (typeBefore == (Datatype *)0) {
*newoff = offAfter;
return true;
}
if (typeAfter == (Datatype *)0) {
*newoff = offBefore;
return true;
}
uintb distBefore = offBefore;
uintb distAfter = -offAfter;
if (arrayHint != 1) {
if (elSizeBefore != arrayHint)
distBefore += 0x1000;
if (elSizeAfter != arrayHint)
distAfter += 0x1000;
}
*newoff = (distAfter < distBefore) ? offAfter : offBefore;
return true;
}
/// Examine a CPUI_INT_MULT element in the middle of the add tree. Determine if we treat
/// the output simply as a leaf, or if the multiply needs to be distributed to an
/// additive subtree. If the Varnode is a leaf of the tree, return \b true if
/// it is considered a multiple of the base data-type size. If the Varnode is the
/// root of another additive sub-tree, return \b true if no sub-node is a multiple.
/// \param vn is the output Varnode of the operation
/// \param op is the CPUI_INT_MULT operation
/// \param treeCoeff is constant multiple being applied to the node
/// \return \b true if there are no multiples of the base data-type size discovered
bool AddTreeState::checkMultTerm(Varnode *vn,PcodeOp *op,uintb treeCoeff)
{
Varnode *vnconst = op->getIn(1);
Varnode *vnterm = op->getIn(0);
uintb val;
if (vnterm->isFree()) {
valid = false;
return false;
}
if (vnconst->isConstant()) {
val = (vnconst->getOffset() * treeCoeff) & ptrmask;
intb sval = (intb) val;
sign_extend(sval, vn->getSize() * 8 - 1);
intb rem = (size == 0) ? sval : sval % size;
if (rem != 0) {
if ((val > size) && (size != 0)) {
valid = false; // Size is too big: pointer type must be wrong
return false;
}
if (!preventDistribution) {
if (vnterm->isWritten() && vnterm->getDef()->code() == CPUI_INT_ADD) {
if (distributeOp == (PcodeOp *)0)
distributeOp = op;
return spanAddTree(vnterm->getDef(), val);
}
}
return true;
}
else {
if (treeCoeff != 1)
isDistributeUsed = true;
multiple.push_back(vnterm);
coeff.push_back(sval);
return false;
}
}
return true;
}
/// \brief Traverse the additive expression accumulating offset information.
///
/// If the given Varnode is a constant or multiplicative term, update
/// totals. If the Varnode is additive, traverse its sub-terms.
/// \param vn is the given Varnode term
/// \param treeCoeff is a constant multiple applied to the entire sub-tree
/// \return \b true if the sub-tree rooted at the given Varnode contains no multiples
bool AddTreeState::checkTerm(Varnode *vn,uintb treeCoeff)
{
uintb val;
PcodeOp *def;
if (vn == ptr) return false;
if (vn->isConstant()) {
val = vn->getOffset() * treeCoeff;
intb sval = (intb)val;
sign_extend(sval,vn->getSize()*8-1);
intb rem = (size == 0) ? sval : (sval % size);
if (rem!=0) { // constant is not multiple of size
if (treeCoeff != 1) {
// An offset "into" the base data-type makes little sense unless is has subcomponents
if (baseType->getMetatype() == TYPE_ARRAY || baseType->getMetatype() == TYPE_STRUCT)
isDistributeUsed = true;
}
nonmultsum += val;
return true;
}
if (treeCoeff != 1)
isDistributeUsed = true;
multsum += val; // Add multiples of size into multsum
return false;
}
if (vn->isWritten()) {
def = vn->getDef();
if (def->code() == CPUI_INT_ADD) // Recurse
return spanAddTree(def, treeCoeff);
if (def->code() == CPUI_COPY) { // Not finished reducing yet
valid = false;
return false;
}
if (def->code() == CPUI_INT_MULT) // Check for constant coeff indicating size
return checkMultTerm(vn, def, treeCoeff);
}
else if (vn->isFree()) {
valid = false;
return false;
}
return true;
}
/// Recursively walk the sub-tree from the given root.
/// Terms that are a \e multiple of the base data-type size are accumulated either in
/// the the sum of constant multiples or the container of non-constant multiples.
/// Terms that are a \e non-multiple are accumulated either in the sum of constant
/// non-multiples or the container of non-constant non-multiples. The constant
/// non-multiples are counted twice, once in the sum, and once in the container.
/// This routine returns \b true if no node of the sub-tree is considered a multiple
/// of the base data-type size (or \b false if any node is considered a multiple).
/// \param op is the root of the sub-expression to traverse
/// \param state holds the offset information
/// \return \b true if the sub-expression is invalid or a NON-multiple
bool RulePtrArith::spanAddTree(PcodeOp *op,AddTreeState *state)
/// \param treeCoeff is a constant multiple applied to the entire additive tree
/// \return \b true if the given sub-tree contains no multiple nodes
bool AddTreeState::spanAddTree(PcodeOp *op,uintb treeCoeff)
{
bool one_is_non,two_is_non;
one_is_non = checkTerm(op->getIn(0),state);
if (!state->valid) return false;
two_is_non = checkTerm(op->getIn(1),state);
if (!state->valid) return false;
one_is_non = checkTerm(op->getIn(0),treeCoeff);
if (!valid) return false;
two_is_non = checkTerm(op->getIn(1),treeCoeff);
if (!valid) return false;
if (one_is_non&&two_is_non) return true;
if (one_is_non)
state->nonmult.push_back(op->getIn(0));
nonmult.push_back(op->getIn(0));
if (two_is_non)
state->nonmult.push_back(op->getIn(1));
return false;
nonmult.push_back(op->getIn(1));
return false; // At least one of the sides contains multiples
}
/// \brief Rewrite a pointer expression using PTRSUB and PTRADD
///
/// Given a base pointer of known data-type and an additive expression involving
/// the pointer, group the terms of the expression into:
/// - Constant multiple of the base data-type
/// - Non-constant multiples of the base data-type
/// - Multiples of an array element size: rewrite using PTRADD
/// - Drill down into sub-components of the base data-type: rewrite using PTRSUB
/// - Remaining offsets
///
/// \param bottom_op is the root Varnode of the expression
/// \param ptr_op is the PcodeOp taking the base pointer as input
/// \param slot is the input slot of the base pointer
/// \param data is the function being analyzed
/// \return 1 if modifications are made, 0 otherwise
int4 RulePtrArith::transformPtr(PcodeOp *bottom_op,PcodeOp *ptr_op,int4 slot,Funcdata &data)
/// Make final calcultions to determine if a pointer to a sub data-type of the base
/// data-type is being calculated, which will result in a CPUI_PTRSUB being generated.
void AddTreeState::calcSubtype(void)
{
AddTreeState state;
const TypePointer *ct;
bool yes_subtype,offset_corrected;
int4 i,ptrsize;
uintb ptrmask,correct,coeff,offset,nonmultbytes,extra;
Varnode *ptrbump; // Will contain final result of PTR addition
Varnode *fullother; // Will contain anything else not part of PTR add
Varnode *newvn;
PcodeOp *newop = (PcodeOp *)0;
state.ptr = ptr_op->getIn(slot);
ct = (const TypePointer *) state.ptr->getType();
ptrsize = state.ptr->getSize();
state.size = AddrSpace::byteToAddressInt(ct->getPtrTo()->getSize(),ct->getWordSize());
state.multsum = 0; // Sums start out as zero
state.nonmultsum = 0;
state.valid = true; // Valid until proven otherwise
spanAddTree(bottom_op,&state); // Find multiples and non-multiples of ct->getSize()
if (!state.valid) return 0; // Were there any show stoppers
ptrmask = calc_mask(ptrsize);
state.nonmultsum &= ptrmask; // Make sure we are modulo ptr's space
state.multsum &= ptrmask;
if (state.size == 0)
offset = state.nonmultsum;
nonmultsum &= ptrmask; // Make sure we are modulo ptr's space
multsum &= ptrmask;
if (size == 0 || nonmultsum < size)
offset = nonmultsum;
else {
intb snonmult = (intb)state.nonmultsum;
// For a sum that falls completely outside the data-type, there is presumably some
// type of constant term added to an array index either at the current level or lower.
// If we knew here whether an array of the baseType was possible we could make a slightly
// better decision.
intb snonmult = (intb)nonmultsum;
sign_extend(snonmult,ptrsize*8-1);
snonmult = snonmult % state.size;
offset = (snonmult < 0) ? (uintb)(snonmult + state.size) : (uintb)snonmult;
}
correct = state.nonmultsum - offset;
state.nonmultsum = offset;
state.multsum = (state.multsum + correct) & ptrmask; // Some extra multiples of size
// Figure out if there is any subtype
if (state.nonmult.empty()) {
if ((state.multsum==0)&&state.multiple.empty()) // Is there anything at all
return 0;
yes_subtype = false; // If there are no offsets INTO the pointer
offset = 0; // Do not check if there are sub structures
}
else if (ct->getPtrTo()->getMetatype()==TYPE_SPACEBASE) {
nonmultbytes = AddrSpace::addressToByte(state.nonmultsum,ct->getWordSize()); // Convert to bytes
// Get offset into mapped variable
if (ct->getPtrTo()->getSubType(nonmultbytes,&extra)==(Datatype *)0)
return 0; // Cannot find mapped variable but nonmult is non-empty
extra = AddrSpace::byteToAddress(extra,ct->getWordSize()); // Convert back to address units
offset = (state.nonmultsum - extra) & ptrmask;
yes_subtype = true;
}
else if (ct->getPtrTo()->getMetatype()==TYPE_STRUCT) {
nonmultbytes = AddrSpace::addressToByte(state.nonmultsum,ct->getWordSize()); // Convert to bytes
// Get offset into field in structure
if (ct->getPtrTo()->getSubType(nonmultbytes,&extra)==(Datatype *)0) {
if (nonmultbytes >= ct->getPtrTo()->getSize()) return 0; // Out of structure's bounds
extra = 0; // No field, but pretend there is something there
snonmult = snonmult % size;
if (snonmult >= 0)
// We assume the sum is big enough it represents an array index at this level
offset = (uintb)snonmult;
else {
// For a negative sum, if the baseType is a structure and there is array hints,
// we assume the sum is an array index at a lower level
if (baseType->getMetatype() == TYPE_STRUCT && findArrayHint() != 0)
offset = nonmultsum;
else
offset = (uintb)(snonmult + size);
}
extra = AddrSpace::byteToAddress(extra,ct->getWordSize()); // Convert back to address units
offset = (state.nonmultsum - extra) & ptrmask;
yes_subtype = true;
}
else if (ct->getPtrTo()->getMetatype()==TYPE_ARRAY) {
yes_subtype = true;
correct = nonmultsum - offset;
nonmultsum = offset;
multsum = (multsum + correct) & ptrmask; // Some extra multiples of size
if (nonmult.empty()) {
if ((multsum == 0) && multiple.empty()) { // Is there anything at all
valid = false;
return;
}
isSubtype = false; // There are no offsets INTO the pointer
}
else if (baseType->getMetatype() == TYPE_SPACEBASE) {
uintb nonmultbytes = AddrSpace::addressToByte(nonmultsum,ct->getWordSize()); // Convert to bytes
uintb extra;
uint4 arrayHint = findArrayHint();
// Get offset into mapped variable
if (!hasMatchingSubType(nonmultbytes, arrayHint, &extra)) {
valid = false; // Cannot find mapped variable but nonmult is non-empty
return;
}
extra = AddrSpace::byteToAddress(extra, ct->getWordSize()); // Convert back to address units
offset = (nonmultsum - extra) & ptrmask;
isSubtype = true;
}
else if (baseType->getMetatype() == TYPE_STRUCT) {
uintb nonmultbytes = AddrSpace::addressToByte(nonmultsum,ct->getWordSize()); // Convert to bytes
uintb extra;
uint4 arrayHint = findArrayHint();
// Get offset into field in structure
if (!hasMatchingSubType(nonmultbytes, arrayHint, &extra)) {
if (nonmultbytes >= baseType->getSize()) { // Compare as bytes! not address units
valid = false; // Out of structure's bounds
return;
}
extra = 0; // No field, but pretend there is something there
}
extra = AddrSpace::byteToAddress(extra, ct->getWordSize()); // Convert back to address units
offset = (nonmultsum - extra) & ptrmask;
isSubtype = true;
}
else if (baseType->getMetatype() == TYPE_ARRAY) {
isSubtype = true;
offset = 0;
}
else // No struct or array, but nonmult is non-empty
return 0; // There is substructure we don't know about
else {
// No struct or array, but nonmult is non-empty
valid = false; // There is substructure we don't know about
}
}
// Be sure to preserve sign in division below
// Calc size-relative constant PTR addition
intb smultsum = (intb)state.multsum;
/// Construct part of the tree that sums to a multiple of the base data-type size.
/// This value will be added to the base pointer as a CPUI_PTRADD. The final Varnode produced
/// by the sum is returned. If there are no multiples, null is returned.
/// \return the output Varnode of the multiple tree or null
Varnode *AddTreeState::buildMultiples(void)
{
Varnode *resNode;
// Be sure to preserve sign in division below
// Calc size-relative constant PTR addition
intb smultsum = (intb)multsum;
sign_extend(smultsum,ptrsize*8-1);
coeff = (state.size==0) ? (uintb)0 : (smultsum / state.size) & ptrmask;
if (coeff == 0)
ptrbump = (Varnode *)0;
uintb constCoeff = (size==0) ? (uintb)0 : (smultsum / size) & ptrmask;
if (constCoeff == 0)
resNode = (Varnode *)0;
else
ptrbump = data.newConstant(ptrsize,coeff);
for(i=0;i<state.multiple.size();++i) {
coeff = (state.size==0) ? (uintb)0 : state.coeff[i] / state.size;
newvn = state.multiple[i];
if (coeff != 1) {
newop = data.newOpBefore(bottom_op,CPUI_INT_MULT,newvn,data.newConstant(ptrsize,coeff));
newvn = newop->getOut();
resNode= data.newConstant(ptrsize,constCoeff);
for(int4 i=0;i<multiple.size();++i) {
uintb finalCoeff = (size==0) ? (uintb)0 : (coeff[i] / size) & ptrmask;
Varnode *vn = multiple[i];
if (finalCoeff != 1) {
PcodeOp *op = data.newOpBefore(baseOp,CPUI_INT_MULT,vn,data.newConstant(ptrsize,finalCoeff));
vn = op->getOut();
}
if (ptrbump == (Varnode *)0)
ptrbump = newvn;
if (resNode == (Varnode *)0)
resNode = vn;
else {
newop = data.newOpBefore(bottom_op,CPUI_INT_ADD, newvn, ptrbump);
ptrbump = newop->getOut();
PcodeOp *op = data.newOpBefore(baseOp,CPUI_INT_ADD, vn, resNode);
resNode = op->getOut();
}
}
// Create PTRADD portion of operation
if (ptrbump != (Varnode *)0) {
newop = data.newOpBefore(bottom_op,CPUI_PTRADD,
state.ptr,ptrbump,data.newConstant(ptrsize,state.size));
ptrbump = newop->getOut();
}
else
ptrbump = state.ptr;
// Add up any remaining pieces
return resNode;
}
/// Create a subtree summing all the elements that aren't multiples of the base data-type size.
/// Correct for any double counting of non-multiple constants.
/// Return the final Varnode holding the sum or null if there are no terms.
/// \return the final Varnode or null
Varnode *AddTreeState::buildExtra(void)
{
correct = (correct+offset) & ptrmask; // Total correction that needs to be made
offset_corrected= (correct==0);
fullother = (Varnode *)0;
for(i=0;i<state.nonmult.size();++i) {
newvn = state.nonmult[i];
if ((!offset_corrected)&&(newvn->isConstant()))
if (newvn->getOffset() == correct) {
bool offset_corrected= (correct==0);
Varnode *resNode = (Varnode *)0;
for(int4 i=0;i<nonmult.size();++i) {
Varnode *vn = nonmult[i];
if ((!offset_corrected)&&(vn->isConstant()))
if (vn->getOffset() == correct) {
offset_corrected = true;
continue;
}
if (fullother == (Varnode *)0)
fullother = newvn;
if (resNode == (Varnode *)0)
resNode = vn;
else {
newop = data.newOpBefore(bottom_op,CPUI_INT_ADD,newvn,fullother);
fullother = newop->getOut();
PcodeOp *op = data.newOpBefore(baseOp,CPUI_INT_ADD,vn,resNode);
resNode = op->getOut();
}
}
if (!offset_corrected) {
newvn = data.newConstant(ptrsize,uintb_negate(correct-1,ptrsize));
if (fullother == (Varnode *)0)
fullother = newvn;
Varnode *vn = data.newConstant(ptrsize,uintb_negate(correct-1,ptrsize));
if (resNode == (Varnode *)0)
resNode = vn;
else {
newop = data.newOpBefore(bottom_op,CPUI_INT_ADD,newvn,fullother);
fullother = newop->getOut();
PcodeOp *op = data.newOpBefore(baseOp,CPUI_INT_ADD,vn,resNode);
resNode = op->getOut();
}
}
// Do PTRSUB portion of operation
if (yes_subtype) {
newop = data.newOpBefore(bottom_op,CPUI_PTRSUB,ptrbump,data.newConstant(ptrsize,offset));
ptrbump = newop->getOut();
return resNode;
}
/// \return \b true if a transform was applied
bool AddTreeState::apply(void)
{
spanAddTree(baseOp,1);
if (!valid) return false; // Were there any show stoppers
if (distributeOp != (PcodeOp *)0 && !isDistributeUsed) {
clear();
preventDistribution = true;
spanAddTree(baseOp,1);
}
calcSubtype();
if (!valid) return false;
while(valid && distributeOp != (PcodeOp *)0) {
if (!data.distributeIntMultAdd(distributeOp)) {
valid = false;
break;
}
// Collapse any z = (x * #c) * #d expressions produced by the distribute
data.collapseIntMultMult(distributeOp->getIn(0));
data.collapseIntMultMult(distributeOp->getIn(1));
clear();
spanAddTree(baseOp,1);
if (distributeOp != (PcodeOp *)0 && !isDistributeUsed) {
clear();
preventDistribution = true;
spanAddTree(baseOp,1);
}
calcSubtype();
}
if (!valid) {
// Distribution transforms were made
ostringstream s;
s << "Problems distributing in pointer arithmetic at ";
baseOp->getAddr().printRaw(s);
data.warningHeader(s.str());
return true;
}
buildTree();
return true;
}
/// The original ADD tree has been successfully spit into \e multiple and
/// \e non-multiple pieces. Rewrite the tree as a pointer expression, putting
/// any \e multiple pieces into a PTRADD operation, creating a PTRSUB if a sub
/// data-type offset has been calculated, and preserving and remaining terms.
void AddTreeState::buildTree(void)
{
Varnode *multNode = buildMultiples();
Varnode *extraNode = buildExtra();
PcodeOp *newop = (PcodeOp *)0;
// Create PTRADD portion of operation
if (multNode != (Varnode *)0) {
newop = data.newOpBefore(baseOp,CPUI_PTRADD,ptr,multNode,data.newConstant(ptrsize,size));
multNode = newop->getOut();
}
else
multNode = ptr; // Zero multiple terms
// Create PTRSUB portion of operation
if (isSubtype) {
newop = data.newOpBefore(baseOp,CPUI_PTRSUB,multNode,data.newConstant(ptrsize,offset));
multNode = newop->getOut();
}
if (fullother != (Varnode *)0)
newop = data.newOpBefore(bottom_op,CPUI_INT_ADD,ptrbump,fullother);
// Add back in any remaining terms
if (extraNode != (Varnode *)0)
newop = data.newOpBefore(baseOp,CPUI_INT_ADD,multNode,extraNode);
if (newop == (PcodeOp *)0) {
// This shouldn't happen, but does because of negative offsets
// Should be fixed when we convert everything to signed calc
data.warning("ptrarith problems",ptr_op->getAddr());
return 0;
// This should never happen
data.warning("ptrarith problems",baseOp->getAddr());
return;
}
data.opSetOutput(newop,bottom_op->getOut());
data.opDestroy(bottom_op);
return 1;
data.opSetOutput(newop,baseOp->getOut());
data.opDestroy(baseOp);
}
/// \brief Verify that given PcodeOp occurs at the bottom of the CPUI_INT_ADD tree
///
/// The main RulePtrArith algorithm assumes that the pointer Varnode is at the bottom
/// of the expression tree that is adding an offset to the pointer. This routine
/// verifies this condition.
/// \param op is the given PcodeOp which is the putative last operation in the tree
/// \param slot is the slot of the pointer Varnode within the given PcodeOp
/// \return \b true if the pointer is at the bottom of the tree, \b false otherwise
bool RulePtrArith::verifyAddTreeBottom(PcodeOp *op,int4 slot)
{
Varnode *vn = op->getOut();
Varnode *ptrbase = op->getIn(slot);
list<PcodeOp *>::const_iterator iter=vn->beginDescend();
OpCode opc;
if (iter == vn->endDescend()) return false; // Don't bother if no descendants
PcodeOp *lowerop = *iter++;
opc = lowerop->code();
if (vn->isSpacebase()) // For the RESULT to be a spacebase pointer
if (iter!=vn->endDescend()) // It must have only 1 descendant
return false;
if (opc == CPUI_INT_ADD) // Check for lone descendant which is an ADD
if (iter==vn->endDescend())
return false; // this is not bottom of add tree
if (ptrbase->isSpacebase() && (ptrbase->isInput()||(ptrbase->isConstant())) &&
(op->getIn(1-slot)->isConstant())) {
// Look for ANY descendant which LOADs or STOREs off of vn
if ((opc==CPUI_LOAD)||(opc==CPUI_STORE)) {
if (lowerop->getIn(1) == vn)
return false;
}
while(iter!=vn->endDescend()) {
opc = (*iter)->code();
if ((opc==CPUI_LOAD)||(opc==CPUI_STORE)) {
if ((*iter)->getIn(1) == vn)
return false;
}
++iter;
}
}
return true;
}
/// \class RulePtrArith
@ -5932,56 +6154,25 @@ void RulePtrArith::getOpList(vector<uint4> &oplist) const
int4 RulePtrArith::applyOp(PcodeOp *op,Funcdata &data)
{
int4 i;
int4 slot;
const Datatype *ct = (const Datatype *)0; // Unnecessary initialization
const TypePointer *tp;
Varnode *vn,*ptrbase;
PcodeOp *lowerop;
if (!data.isTypeRecoveryOn()) return 0;
for(i=0;i<op->numInput();++i) { // Search for pointer type
ct = op->getIn(i)->getType();
for(slot=0;slot<op->numInput();++slot) { // Search for pointer type
ct = op->getIn(slot)->getType();
if (ct->getMetatype() == TYPE_PTR) break;
}
if (i == op->numInput()) return 0;
if (slot == op->numInput()) return 0;
if (!verifyAddTreeBottom(op, slot)) return 0;
vn = op->getOut();
ptrbase = op->getIn(i);
list<PcodeOp *>::const_iterator iter=vn->beginDescend();
OpCode opc;
if (iter == vn->endDescend()) return 0; // Don't bother if no descendants
lowerop = *iter++;
opc = lowerop->code();
if (vn->isSpacebase()) // For the RESULT to be a spacebase pointer
if (iter!=vn->endDescend()) return 0; // It must have only 1 descendant
if (opc == CPUI_INT_ADD) // Check for lone descendant which is an ADD
if (iter==vn->endDescend()) return 0; // this is not bottom of add tree
if (ptrbase->isSpacebase() && (ptrbase->isInput()||(ptrbase->isConstant())) &&
(op->getIn(1-i)->isConstant())) {
// Look for ANY descendant which
// LOADs or STOREs off of vn
if ((opc==CPUI_LOAD)||(opc==CPUI_STORE)) {
if (lowerop->getIn(1) == vn)
return 0;
}
while(iter!=vn->endDescend()) {
opc = (*iter)->code();
if ((opc==CPUI_LOAD)||(opc==CPUI_STORE)) {
if ((*iter)->getIn(1) == vn)
return 0;
}
++iter;
}
}
tp = (const TypePointer *) ct;
const TypePointer *tp = (const TypePointer *) ct;
ct = tp->getPtrTo(); // Type being pointed to
int4 unitsize = AddrSpace::addressToByteInt(1,tp->getWordSize());
if (ct->getSize() == unitsize) { // Degenerate case
vector<Varnode *> newparams;
newparams.push_back( ptrbase );
newparams.push_back( op->getIn(1-i) );
newparams.push_back( op->getIn(slot) );
newparams.push_back( op->getIn(1-slot) );
newparams.push_back( data.newConstant(tp->getSize(),1));
data.opSetAllInput(op,newparams);
data.opSetOpcode(op,CPUI_PTRADD);
@ -5992,7 +6183,10 @@ int4 RulePtrArith::applyOp(PcodeOp *op,Funcdata &data)
// probably some sort of padding going on
return 0;
return transformPtr(op,op,i,data);
AddTreeState state(data,op,slot);
if (!state.apply())
return 0;
return 1;
}
/// \class RuleStructOffset0
@ -6402,14 +6596,8 @@ int4 RulePtrsubCharConstant::applyOp(PcodeOp *op,Funcdata &data)
if (!scope->isReadOnly(symaddr,1,op->getAddr()))
return 0;
// Check if data at the address looks like a string
uint1 buffer[128];
try {
data.getArch()->loader->loadFill(buffer,128,symaddr);
} catch(DataUnavailError &err) {
if (!data.getArch()->stringManager->isString(symaddr, basetype))
return 0;
}
bool isstring = data.getArch()->print->isCharacterConstant(buffer,128,basetype->getSize());
if (!isstring) return 0;
// If we reach here, the PTRSUB should be converted to a (COPY of a) pointer constant.
bool removeCopy = false;

View file

@ -28,16 +28,51 @@
#include "action.hh"
/// \brief Structure for sorting out pointer expression trees
///
/// Given a base pointer of known data-type and an additive expression involving
/// the pointer, group the terms of the expression into:
/// - A constant multiple of the base data-type
/// - Non-constant multiples of the base data-type
/// - An constant offset to a sub-component of the base data-type
/// - An remaining terms
///
/// The \e multiple terms are rewritten using a CPUI_PTRADD. The constant offset
/// is rewritten using a CPUI_PTRSUB. Other terms are added back in. Analysis may cause
/// multiplication (CPUI_INT_MULT) by a constant to be distributed to its CPUI_INT_ADD input.
class AddTreeState {
public:
Funcdata &data; ///< The function containing the expression
PcodeOp *baseOp; ///< Base of the ADD tree
Varnode *ptr; ///< The pointer varnode
int4 size; ///< Size of ptr type in question
const TypePointer *ct; ///< The pointer data-type
const Datatype *baseType; ///< The base data-type being pointed at
int4 ptrsize; ///< Size of the pointer
int4 size; ///< Size of data-type being pointed to (in address units)
uintb ptrmask; ///< Mask for modulo calculations in ptr space
uintb offset; ///< Number of bytes we dig into the base data-type
uintb correct; ///< Number of bytes being double counted
vector<Varnode *> multiple; ///< Varnodes which are multiples of size
vector<uintb> coeff; ///< Associated constant multiple
vector<intb> coeff; ///< Associated constant multiple
vector<Varnode *> nonmult; ///< Varnodes which are not multiples
PcodeOp *distributeOp; ///< A CPUI_INT_MULT op that needs to be distributed
uintb multsum; ///< Sum of multiple constants
uintb nonmultsum; ///< Sum of non-multiple constants
bool valid; ///< Full tree search was performed
bool preventDistribution; ///< Do not distribute "multiply by constant" operation
bool isDistributeUsed; ///< Are terms produced by distributing used
bool isSubtype; ///< Is there a sub-type (using CPUI_PTRSUB)
bool valid; ///< Set to \b true if the whole expression can be transformed
uint4 findArrayHint(void) const; ///< Look for evidence of an array in a sub-component
bool hasMatchingSubType(uintb off,uint4 arrayHint,uintb *newoff) const;
bool checkMultTerm(Varnode *vn,PcodeOp *op, uintb treeCoeff); ///< Accumulate details of INT_MULT term and continue traversal if appropriate
bool checkTerm(Varnode *vn, uintb treeCoeff); ///< Accumulate details of given term and continue tree traversal
bool spanAddTree(PcodeOp *op, uintb treeCoeff); ///< Walk the given sub-tree accumulating details
void calcSubtype(void); ///< Calculate final sub-type offset
Varnode *buildMultiples(void); ///< Build part of tree that is multiple of base size
Varnode *buildExtra(void); ///< Build part of tree not accounted for by multiples or \e offset
void buildTree(void); ///< Build the transformed ADD tree
void clear(void); ///< Reset for a new ADD tree traversal
public:
AddTreeState(Funcdata &d,PcodeOp *op,int4 slot); ///< Construct given root of ADD tree and pointer
bool apply(void); ///< Attempt to transform the pointer expression
};
class RuleEarlyRemoval : public Rule {
@ -58,7 +93,6 @@ public:
// };
class RuleCollectTerms : public Rule {
static Varnode *getMultCoeff(Varnode *vn,uintb &coef); ///< Get the multiplicative coefficient
static int4 doDistribute(Funcdata &data,PcodeOp *op); ///< Distribute coefficient within one term
public:
RuleCollectTerms(const string &g) : Rule(g, 0, "collect_terms") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
@ -993,9 +1027,7 @@ public:
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RulePtrArith : public Rule {
static bool checkTerm(Varnode *vn,AddTreeState *state);
static bool spanAddTree(PcodeOp *op,AddTreeState *state);
static int4 transformPtr(PcodeOp *bottom_op,PcodeOp *ptr_op,int4 slot,Funcdata &data);
static bool verifyAddTreeBottom(PcodeOp *op,int4 slot);
public:
RulePtrArith(const string &g) : Rule(g, 0, "ptrarith") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {

View file

@ -0,0 +1,46 @@
/* ###
* 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 "string_ghidra.hh"
GhidraStringManager::GhidraStringManager(ArchitectureGhidra *g,int4 max)
: StringManager(max)
{
glb = g;
testBuffer = new uint1[max];
}
GhidraStringManager::~GhidraStringManager(void)
{
delete [] testBuffer;
}
const vector<uint1> &GhidraStringManager::getStringData(const Address &addr,Datatype *charType,bool &isTrunc)
{
map<Address,StringData>::iterator iter;
iter = stringMap.find(addr);
if (iter != stringMap.end()) {
isTrunc = (*iter).second.isTruncated;
return (*iter).second.byteData;
}
StringData &stringData(stringMap[addr]);
stringData.isTruncated = false;
glb->getStringData(stringData.byteData, addr, charType, maximumChars,stringData.isTruncated);
isTrunc = stringData.isTruncated;
return stringData.byteData;
}

View file

@ -0,0 +1,39 @@
/* ###
* 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 ghidra_string.hh
/// \brief Implementation of the StringManager through the ghidra client
#ifndef __STRING_GHIDRA__
#define __STRING_GHIDRA__
#include "ghidra_arch.hh"
/// \brief Implementation of the StringManager that queries through the ghidra client
///
/// This acts as a front end to Ghidra's string formats and encodings.
/// The client translates any type of string into a UTF8 representation, and this
/// class stores it for final presentation. Escaping the UTF8 string is left up
/// to the PrintLanguage.
class GhidraStringManager : public StringManager {
ArchitectureGhidra *glb; ///< The ghidra client interface
uint1 *testBuffer; ///< Temporary storage for storing bytes from client
public:
GhidraStringManager(ArchitectureGhidra *g,int4 max); ///< Constructor
virtual ~GhidraStringManager(void);
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType,bool &isTrunc);
};
#endif

View file

@ -0,0 +1,392 @@
/* ###
* 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 "stringmanage.hh"
#include "architecture.hh"
/// \param max is the maximum number of characters to allow before truncating string
StringManager::StringManager(int4 max)
{
maximumChars = max;
}
StringManager::~StringManager(void)
{
clear();
}
/// Encode the given unicode codepoint as UTF8 (1, 2, 3, or 4 bytes) and
/// write the bytes to the stream.
/// \param s is the output stream
/// \param codepoint is the unicode codepoint
void StringManager::writeUtf8(ostream &s,int4 codepoint)
{
uint1 bytes[4];
int4 size;
if (codepoint < 0)
throw LowlevelError("Negative unicode codepoint");
if (codepoint < 128) {
s.put((uint1)codepoint);
return;
}
int4 bits = mostsigbit_set(codepoint) + 1;
if (bits > 21)
throw LowlevelError("Bad unicode codepoint");
if (bits < 12) { // Encode with two bytes
bytes[0] = 0xc0 ^ ((codepoint >> 6)&0x1f);
bytes[1] = 0x80 ^ (codepoint & 0x3f);
size = 2;
}
else if (bits < 17) {
bytes[0] = 0xe0 ^ ((codepoint >> 12)&0xf);
bytes[1] = 0x80 ^ ((codepoint >> 6)&0x3f);
bytes[2] = 0x80 ^ (codepoint & 0x3f);
size = 3;
}
else {
bytes[0] = 0xf0 ^ ((codepoint >> 18) & 7);
bytes[1] = 0x80 ^ ((codepoint >> 12) & 0x3f);
bytes[2] = 0x80 ^ ((codepoint >> 6) & 0x3f);
bytes[3] = 0x80 ^ (codepoint & 0x3f);
size = 4;
}
s.write((char *)bytes, size);
}
/// Returns \b true if the data is some kind of complete string.
/// A given character data-type can be used as a hint for the encoding.
/// The string decoding can be cached internally.
/// \param addr is the given address
/// \param charType is the given character data-type
/// \return \b true if the address represents string data
bool StringManager::isString(const Address &addr,Datatype *charType)
{
bool isTrunc; // unused here
const vector<uint1> &buffer(getStringData(addr,charType,isTrunc));
return !buffer.empty();
}
/// Write \<stringmanage> tag, with \<string> sub-tags.
/// \param s is the stream to write to
void StringManager::saveXml(ostream &s) const
{
s << "<stringmanage>\n";
map<Address,StringData>::const_iterator iter1;
for(iter1=stringMap.begin();iter1!=stringMap.end();++iter1) {
s << "<string>\n";
(*iter1).first.saveXml(s);
const StringData &stringData( (*iter1).second );
s << " <bytes";
a_v_b(s, "trunc", stringData.isTruncated);
s << ">\n" << setfill('0');
for(int4 i=0;stringData.byteData.size();++i) {
s << hex << setw(2) << (int4)stringData.byteData[i];
if (i%20 == 19)
s << "\n ";
}
s << "\n </bytes>\n";
}
s << "</stringmanage>\n";
}
/// Read \<stringmanage> tag, with \<string> sub-tags.
/// \param el is the root tag element
/// \param m is the manager for looking up AddressSpaces
void StringManager::restoreXml(const Element *el, const AddrSpaceManager *m)
{
const List &list(el->getChildren());
List::const_iterator iter1;
for (iter1 = list.begin(); iter1 != list.end(); ++iter1) {
List::const_iterator iter2 = (*iter1)->getChildren().begin();
Address addr = Address::restoreXml(*iter2, m);
++iter2;
StringData &stringData(stringMap[addr]);
stringData.isTruncated = xml_readbool((*iter2)->getAttributeValue("trunc"));
istringstream is((*iter2)->getContent());
int4 val;
char c1, c2;
is >> ws;
c1 = is.get();
c2 = is.get();
while ((c1 > 0) && (c2 > 0)) {
if (c1 <= '9')
c1 = c1 - '0';
else if (c1 <= 'F')
c1 = c1 + 10 - 'A';
else
c1 = c1 + 10 - 'a';
if (c2 <= '9')
c2 = c2 - '0';
else if (c2 <= 'F')
c2 = c2 + 10 - 'A';
else
c2 = c2 + 10 - 'a';
val = c1 * 16 + c2;
stringData.byteData.push_back((uint1) val);
is >> ws;
c1 = is.get();
c2 = is.get();
}
}
}
/// \param buffer is the byte buffer
/// \param size is the number of bytes in the buffer
/// \param charsize is the presumed size (in bytes) of character elements
/// \return \b true if a string terminator is found
bool StringManager::hasCharTerminator(const uint1 *buffer,int4 size,int4 charsize)
{
for(int4 i=0;i<size;i+=charsize) {
bool isTerminator = true;
for(int4 j=0;j<charsize;++j) {
if (buffer[i+j] != 0) { // Non-zero bytes means character can't be a null terminator
isTerminator = false;
break;
}
}
if (isTerminator) return true;
}
return false;
}
/// Pull the first two bytes from the byte array and combine them in the indicated endian order
/// \param buf is the byte array
/// \param bigend is \b true to request big endian encoding
/// \return the decoded UTF16 element
inline int4 StringManager::readUtf16(const uint1 *buf,bool bigend)
{
int4 codepoint;
if (bigend) {
codepoint = buf[0];
codepoint <<= 8;
codepoint += buf[1];
}
else {
codepoint = buf[1];
codepoint <<= 8;
codepoint += buf[0];
}
return codepoint;
}
/// One or more bytes is consumed from the array, and the number of bytes used is passed back.
/// \param buf is a pointer to the bytes in the character array
/// \param charsize is 1 for UTF8, 2 for UTF16, or 4 for UTF32
/// \param bigend is \b true for big endian encoding of the UTF element
/// \param skip is a reference for passing back the number of bytes consumed
/// \return the codepoint or -1 if the encoding is invalid
int4 StringManager::getCodepoint(const uint1 *buf,int4 charsize,bool bigend,int4 &skip)
{
int4 codepoint;
int4 sk = 0;
if (charsize==2) { // UTF-16
codepoint = readUtf16(buf,bigend);
sk += 2;
if ((codepoint>=0xD800)&&(codepoint<=0xDBFF)) { // high surrogate
int4 trail=readUtf16(buf+2,bigend);
sk += 2;
if ((trail<0xDC00)||(trail>0xDFFF)) return -1; // Bad trail
codepoint = (codepoint<<10) + trail + (0x10000 - (0xD800 << 10) - 0xDC00);
}
else if ((codepoint>=0xDC00)&&(codepoint<=0xDFFF)) return -1; // trail before high
}
else if (charsize==1) { // UTF-8
int4 val = buf[0];
if ((val&0x80)==0) {
codepoint = val;
sk = 1;
}
else if ((val&0xe0)==0xc0) {
int4 val2 = buf[1];
sk = 2;
if ((val2&0xc0)!=0x80) return -1; // Not a valid UTF8-encoding
codepoint = ((val&0x1f)<<6) | (val2 & 0x3f);
}
else if ((val&0xf0)==0xe0) {
int4 val2 = buf[1];
int4 val3 = buf[2];
sk = 3;
if (((val2&0xc0)!=0x80)||((val3&0xc0)!=0x80)) return -1; // invalid encoding
codepoint = ((val&0xf)<<12) | ((val2&0x3f)<<6) | (val3 & 0x3f);
}
else if ((val&0xf8)==0xf0) {
int4 val2 = buf[1];
int4 val3 = buf[2];
int4 val4 = buf[3];
sk = 4;
if (((val2&0xc0)!=0x80)||((val3&0xc0)!=0x80)||((val4&0xc0)!=0x80)) return -1; // invalid encoding
codepoint = ((val&7)<<18) | ((val2&0x3f)<<12) | ((val3&0x3f)<<6) | (val4 & 0x3f);
}
else
return -1;
}
else if (charsize == 4) { // UTF-32
sk = 4;
if (bigend)
codepoint = (buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + buf[3];
else
codepoint = (buf[3]<<24) + (buf[2]<<16) + (buf[1]<<8) + buf[0];
}
else
return -1;
if (codepoint >= 0xd800 && codepoint <= 0xdfff)
return -1; // Reserved for surrogates, invalid codepoints
skip = sk;
return codepoint;
}
/// \param g is the underlying architecture (and loadimage)
/// \param max is the maximum number of bytes to allow in a decoded string
StringManagerUnicode::StringManagerUnicode(Architecture *g,int4 max)
: StringManager(max)
{
glb = g;
testBuffer = new uint1[max];
}
StringManagerUnicode::~StringManagerUnicode(void)
{
delete [] testBuffer;
}
const vector<uint1> &StringManagerUnicode::getStringData(const Address &addr,Datatype *charType,bool &isTrunc)
{
map<Address,StringData>::iterator iter;
iter = stringMap.find(addr);
if (iter != stringMap.end()) {
isTrunc = (*iter).second.isTruncated;
return (*iter).second.byteData;
}
StringData &stringData(stringMap[addr]); // Allocate (initially empty) byte vector
stringData.isTruncated = false;
isTrunc = false;
if (charType->isOpaqueString()) // Cannot currently test for an opaque encoding
return stringData.byteData; // Return the empty buffer
int4 curBufferSize = 0;
int4 charsize = charType->getSize();
bool foundTerminator = false;
try {
do {
int4 amount = 32; // Grab 32 bytes of image at a time
uint4 newBufferSize = curBufferSize + amount;
if (newBufferSize > maximumChars) {
newBufferSize = maximumChars;
amount = newBufferSize - curBufferSize;
if (amount == 0) {
return stringData.byteData; // Could not find terminator
}
}
glb->loader->loadFill(testBuffer + curBufferSize, amount,
addr + curBufferSize);
foundTerminator = hasCharTerminator(testBuffer + curBufferSize, amount,
charsize);
curBufferSize = newBufferSize;
} while (!foundTerminator);
} catch (DataUnavailError &err) {
return stringData.byteData; // Return the empty buffer
}
int4 numChars = checkCharacters(testBuffer, curBufferSize, charsize);
if (numChars < 0)
return stringData.byteData; // Return the empty buffer (invalid encoding)
if (charsize == 1 && numChars < maximumChars) {
stringData.byteData.reserve(curBufferSize);
stringData.byteData.assign(testBuffer,testBuffer+curBufferSize);
}
else {
// We need to translate to UTF8 and/or truncate
ostringstream s;
if (!writeUnicode(s, testBuffer, curBufferSize, charsize))
return stringData.byteData; // Return the empty buffer
string resString = s.str();
int4 newSize = resString.size();
stringData.byteData.reserve(newSize + 1);
const uint1 *ptr = (const uint1 *)resString.c_str();
stringData.byteData.assign(ptr,ptr+newSize);
stringData.byteData[newSize] = 0; // Make sure there is a null terminator
}
stringData.isTruncated = (numChars >= maximumChars);
isTrunc = stringData.isTruncated;
return stringData.byteData;
}
/// Check that the given buffer contains valid unicode.
/// If the string is encoded in UTF8 or ASCII, we get (on average) a bit of check
/// per character. For UTF16, the surrogate reserved area gives at least some check.
/// \param buf is the byte array to check
/// \param size is the size of the buffer in bytes
/// \param charsize is the UTF encoding (1=UTF8, 2=UTF16, 4=UTF32)
/// \return the number of characters or -1 if there is an invalid encoding
int4 StringManagerUnicode::checkCharacters(const uint1 *buf,int4 size,int4 charsize) const
{
if (buf == (const uint1 *)0) return -1;
bool bigend = glb->translate->isBigEndian();
int4 i=0;
int4 count=0;
int4 skip = charsize;
while(i<size) {
int4 codepoint = getCodepoint(buf+i,charsize,bigend,skip);
if (codepoint < 0) return -1;
if (codepoint == 0) break;
count += 1;
i += skip;
}
return count;
}
/// Assume the buffer contains a null terminated unicode encoded string.
/// Write the characters out (as UTF8) to the stream.
/// \param s is the output stream
/// \param buffer is the given byte buffer
/// \param size is the number of bytes in the buffer
/// \param charsize specifies the encoding (1=UTF8 2=UTF16 4=UTF32)
/// \return \b true if the byte array contains valid unicode
bool StringManagerUnicode::writeUnicode(ostream &s,uint1 *buffer,int4 size,int4 charsize)
{
bool bigend = glb->translate->isBigEndian();
int4 i=0;
int4 count=0;
int4 skip = charsize;
while(i<size) {
int4 codepoint = getCodepoint(buffer+i,charsize,bigend,skip);
if (codepoint < 0) return false;
if (codepoint == 0) break; // Terminator
writeUtf8(s, codepoint);
i += skip;
count += 1;
if (count >= maximumChars)
break;
}
return true;
}

View file

@ -0,0 +1,83 @@
/* ###
* 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 stringmanage.hh
/// \brief Classes for decoding and storing string data
#ifndef __STRING_MANAGE__
#define __STRING_MANAGE__
#include "type.hh"
class Architecture;
/// \brief Storage for decoding and storing strings associated with an address
///
/// Looks at data in the loadimage to determine if it represents a "string".
/// Decodes the string for presentation in the output.
/// Stores the decoded string until its needed for presentation.
class StringManager {
protected:
class StringData {
public:
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
public:
StringManager(int4 max); ///< Constructor
virtual ~StringManager(void); ///< Destructor
void clear(void) { stringMap.clear(); } ///< Clear out any cached strings
bool isString(const Address &addr,Datatype *charType); // Determine if data at the given address is a string
/// \brief Retrieve string data at the given address as a UTF8 byte array
///
/// If the address does not represent string data, a zero length vector is returned. Otherwise,
/// the string data is fetched, converted to a UTF8 encoding, cached and returned.
/// \param addr is the given address
/// \param charType is a character data-type indicating the encoding
/// \param isTrunc passes back whether the string is truncated
/// \return the byte array of UTF8 data
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType,bool &isTrunc)=0;
void saveXml(ostream &s) const; ///< Save cached strings to a stream as XML
void restoreXml(const Element *el,const AddrSpaceManager *m); ///< Restore string cache from XML
static bool hasCharTerminator(const uint1 *buffer,int4 size,int4 charsize); ///< Check for a unicode string terminator
static int4 readUtf16(const uint1 *buf,bool bigend); ///< Read a UTF16 code point from a byte array
static void writeUtf8(ostream &s,int4 codepoint); ///< Write unicode character to stream in UTF8 encoding
static int4 getCodepoint(const uint1 *buf,int4 charsize,bool bigend,int4 &skip); ///< Extract next \e unicode \e codepoint
};
/// \brief An implementation of StringManager that understands terminated unicode strings
///
/// This class understands UTF8, UTF16, and UTF32 encodings. It reports a string if its
/// sees a valid encoding that is null terminated.
class StringManagerUnicode : public StringManager {
Architecture *glb; ///< Underlying architecture
uint1 *testBuffer; ///< Temporary buffer for pulling in loadimage bytes
int4 checkCharacters(const uint1 *buf,int4 size,int4 charsize) const; ///< Make sure buffer has valid bounded set of unicode
public:
StringManagerUnicode(Architecture *g,int4 max); ///< Constructor
virtual ~StringManagerUnicode(void);
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType,bool &isTrunc);
bool writeUnicode(ostream &s,uint1 *buffer,int4 size,int4 charsize); ///< Translate/copy unicode to UTF8
};
#endif

View file

@ -63,6 +63,20 @@ void print_data(ostream &s,uint1 *buffer,int4 size,const Address &baseaddr)
}
}
/// If \b this and the other given data-type are both variable length and come from the
/// the same base data-type, return \b true.
/// \param ct is the other given data-type to compare with \b this
/// \return \b true if they are the same variable length data-type.
bool Datatype::hasSameVariableBase(const Datatype *ct) const
{
if (!isVariableLength()) return false;
if (!ct->isVariableLength()) return false;
uint8 thisId = hashSize(id, size);
uint8 themId = hashSize(ct->id, ct->size);
return (thisId == themId);
}
/// Print a raw description of the type to stream. Intended for debugging.
/// Not intended to produce parsable C.
/// \param s is the output stream
@ -90,7 +104,33 @@ Datatype *Datatype::getSubType(uintb off,uintb *newoff) const
return (Datatype *)0;
}
/// Compare \b this with another data-type.
/// Find the first component data-type after the given offset that is (or contains)
/// an array, and pass back the difference between the component's start and the given offset.
/// Return the component data-type or null if no array is found.
/// \param off is the given offset into \b this data-type
/// \param newoff is used to pass back the offset difference
/// \param elSize is used to pass back the array element size
/// \return the component data-type or null
Datatype *Datatype::nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const
{
return (TypeArray *)0;
}
/// Find the first component data-type before the given offset that is (or contains)
/// an array, and pass back the difference between the component's start and the given offset.
/// Return the component data-type or null if no array is found.
/// \param off is the given offset into \b this data-type
/// \param newoff is used to pass back the offset difference
/// \param elSize is used to pass back the array element size
/// \return the component data-type or null
Datatype *Datatype::nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const
{
return (TypeArray *)0;
}
// Compare \b this with another data-type.
/// 0 (equality) means the data-types are functionally equivalent (even if names differ)
/// Smaller types come earlier. More specific types come earlier.
/// \param op is the data-type to compare with \b this
@ -240,8 +280,13 @@ void Datatype::saveXmlBasic(ostream &s) const
{
a_v(s,"name",name);
if (id != 0) {
s << " id=\"0x" << hex << id << '\"';
uint8 saveId;
if (isVariableLength())
saveId = hashSize(id, size);
else
saveId = id;
if (saveId != 0) {
s << " id=\"0x" << hex << saveId << '\"';
}
a_v_i(s,"size",size);
string metastring;
@ -249,6 +294,10 @@ void Datatype::saveXmlBasic(ostream &s) const
a_v(s,"metatype",metastring);
if ((flags & coretype)!=0)
a_v_b(s,"core",true);
if (isVariableLength())
a_v_b(s,"varlength",true);
if ((flags & opaque_string)!=0)
a_v_b(s,"opaquestring",true);
}
/// Write a simple reference to \b this data-type as an XML \<typeref> tag,
@ -283,18 +332,31 @@ void Datatype::restoreXmlBasic(const Element *el)
metatype = string2metatype( el->getAttributeValue("metatype") );
id = 0;
for(int4 i=0;i<el->getNumAttributes();++i) {
if (el->getAttributeName(i) == "core") {
const string &attribName( el->getAttributeName(i) );
if (attribName == "core") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= coretype;
}
else if (el->getAttributeName(i) == "id") {
else if (attribName == "id") {
istringstream i1(el->getAttributeValue(i));
i1.unsetf(ios::dec | ios::hex | ios::oct);
i1 >> id;
}
else if (attribName == "varlength") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= variable_length;
}
else if (attribName == "opaquestring") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= opaque_string;
}
}
if ((id==0)&&(name.size()>0)) // If there is a type name
id = hashName(name); // There must be some kind of id
if (isVariableLength()) {
// Id needs to be unique compared to another data-type with the same name
id = hashSize(id, size);
}
}
/// Restore a Datatype object from an XML element
@ -326,6 +388,21 @@ uint8 Datatype::hashName(const string &nm)
return res;
}
/// This allows IDs for variable length structures to be uniquefied based on size.
/// A base ID is given and a size of the specific instance. A unique ID is returned.
/// 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
uint8 Datatype::hashSize(uint8 id,int4 size)
{
uint8 sizeHash = size;
sizeHash *= 0x98251033aecbabaf; // Hash the size
id ^= sizeHash;
return id;
}
void TypeChar::saveXml(ostream &s) const
{
@ -740,7 +817,7 @@ void TypeStruct::setFields(const vector<TypeField> &fd)
/// \return the index into the field list or -1
int4 TypeStruct::getFieldIter(int4 off) const
{ // Find subfield of given offset
{
int4 min = 0;
int4 max = field.size()-1;
@ -758,6 +835,30 @@ int4 TypeStruct::getFieldIter(int4 off) const
return -1;
}
/// The field returned may or may not contain the offset. If there are no fields
/// that occur earlier than the offset, return -1.
/// \param off is the given offset
/// \return the index of the nearest field or -1
int4 TypeStruct::getLowerBoundField(int4 off) const
{
if (field.empty()) return -1;
int4 min = 0;
int4 max = field.size()-1;
while(min < max) {
int4 mid = (min + max + 1)/2;
if (field[mid].offset > off)
max = mid - 1;
else { // curfield.offset <= off
min = mid;
}
}
if (min == max && field[min].offset <= off)
return min;
return -1;
}
/// Given a byte range within \b this data-type, determine the field it is contained in
/// and pass back the renormalized offset.
/// \param off is the byte offset into \b this
@ -792,6 +893,61 @@ Datatype *TypeStruct::getSubType(uintb off,uintb *newoff) const
return curfield.type;
}
Datatype *TypeStruct::nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const
{
int4 i = getLowerBoundField(off);
while(i >= 0) {
const TypeField &subfield( field[i] );
int4 diff = (int4)off - subfield.offset;
if (diff > 128) break;
Datatype *subtype = subfield.type;
if (subtype->getMetatype() == TYPE_ARRAY) {
*newoff = (intb)diff;
*elSize = ((TypeArray *)subtype)->getBase()->getSize();
return subtype;
}
else {
uintb suboff;
Datatype *res = subtype->nearestArrayedComponentBackward(subtype->getSize(), &suboff, elSize);
if (res != (Datatype *)0) {
*newoff = (intb)diff;
return subtype;
}
}
i -= 1;
}
return (Datatype *)0;
}
Datatype *TypeStruct::nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const
{
int4 i = getLowerBoundField(off);
i += 1;
while(i<field.size()) {
const TypeField &subfield( field[i] );
int4 diff = subfield.offset - off;
if (diff > 128) break;
Datatype *subtype = subfield.type;
if (subtype->getMetatype() == TYPE_ARRAY) {
*newoff = (intb)-diff;
*elSize = ((TypeArray *)subtype)->getBase()->getSize();
return subtype;
}
else {
uintb suboff;
Datatype *res = subtype->nearestArrayedComponentForward(0, &suboff, elSize);
if (res != (Datatype *)0) {
*newoff = (intb)-diff;
return subtype;
}
}
i += 1;
}
return (Datatype *)0;
}
int4 TypeStruct::compare(const Datatype &op,int4 level) const
{
if (size != op.getSize()) return (op.getSize()-size);
@ -1144,6 +1300,74 @@ Datatype *TypeSpacebase::getSubType(uintb off,uintb *newoff) const
return smallest->getSymbol()->getType();
}
Datatype *TypeSpacebase::nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const
{
Scope *scope = getMap();
off = AddrSpace::byteToAddress(off, spaceid->getWordSize()); // Convert from byte offset to address unit
// It should always be the case that the given offset represents a full encoding of the
// pointer, so the point of context is unused and the size is given as -1
Address nullPoint;
uintb fullEncoding;
Address addr = glb->resolveConstant(spaceid, off, -1, nullPoint, fullEncoding);
SymbolEntry *smallest = scope->queryContainer(addr,1,nullPoint);
Address nextAddr;
Datatype *symbolType;
if (smallest == (SymbolEntry *)0 || smallest->getOffset() != 0)
nextAddr = addr + 32;
else {
symbolType = smallest->getSymbol()->getType();
if (symbolType->getMetatype() == TYPE_STRUCT) {
uintb structOff = addr.getOffset() - smallest->getAddr().getOffset();
uintb dummyOff;
Datatype *res = symbolType->nearestArrayedComponentForward(structOff, &dummyOff, elSize);
if (res != (Datatype *)0) {
*newoff = structOff;
return symbolType;
}
}
int4 size = AddrSpace::byteToAddressInt(smallest->getSize(), spaceid->getWordSize());
nextAddr = smallest->getAddr() + size;
}
if (nextAddr < addr)
return (Datatype *)0; // Don't let the address wrap
smallest = scope->queryContainer(nextAddr,1,nullPoint);
if (smallest == (SymbolEntry *)0 || smallest->getOffset() != 0)
return (Datatype *)0;
symbolType = smallest->getSymbol()->getType();
*newoff = addr.getOffset() - smallest->getAddr().getOffset();
if (symbolType->getMetatype() == TYPE_ARRAY) {
*elSize = ((TypeArray *)symbolType)->getBase()->getSize();
return symbolType;
}
if (symbolType->getMetatype() == TYPE_STRUCT) {
uintb dummyOff;
Datatype *res = symbolType->nearestArrayedComponentForward(0, &dummyOff, elSize);
if (res != (Datatype *)0)
return symbolType;
}
return (Datatype *)0;
}
Datatype *TypeSpacebase::nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const
{
Datatype *subType = getSubType(off, newoff);
if (subType == (Datatype *)0)
return (Datatype *)0;
if (subType->getMetatype() == TYPE_ARRAY) {
*elSize = ((TypeArray *)subType)->getBase()->getSize();
return subType;
}
if (subType->getMetatype() == TYPE_STRUCT) {
uintb dummyOff;
Datatype *res = subType->nearestArrayedComponentBackward(*newoff,&dummyOff,elSize);
if (res != (Datatype *)0)
return subType;
}
return (Datatype *)0;
}
int4 TypeSpacebase::compare(const Datatype &op,int4 level) const
{
@ -1483,8 +1707,9 @@ Datatype *TypeFactory::setName(Datatype *ct,const string &n)
/// \param fd is the list of fields to set
/// \param ot is the TypeStruct object to modify
/// \param fixedsize is 0 or the forced size of the structure
/// \param flags are other flags to set on the structure
/// \return true if modification was successful
bool TypeFactory::setFields(vector<TypeField> &fd,TypeStruct *ot,int4 fixedsize)
bool TypeFactory::setFields(vector<TypeField> &fd,TypeStruct *ot,int4 fixedsize,uint4 flags)
{
int4 offset,cursize,curalign;
@ -1529,6 +1754,7 @@ bool TypeFactory::setFields(vector<TypeField> &fd,TypeStruct *ot,int4 fixedsize)
tree.erase(ot);
ot->setFields(fd);
ot->flags |= (flags & (Datatype::opaque_string | Datatype::variable_length));
if (fixedsize > 0) { // If the caller is trying to force a size
if (fixedsize > ot->size) // If the forced size is bigger than the size required for fields
ot->size = fixedsize; // Force the bigger size
@ -1746,22 +1972,18 @@ TypeCode *TypeFactory::getTypeCode(const string &nm)
return (TypeCode *) findAdd(tmp);
}
/// This creates a pointer to a given data-type. It doesn't allow
/// a "pointer to array" to be created however and will drill-down to
/// the first non-array data-type
/// This creates a pointer to a given data-type. If the given data-type is
/// an array, the TYPE_ARRAY property is stripped off, and a pointer to
/// the array element data-type is returned.
/// \param s is the size of the pointer
/// \param pt is the pointed-to data-type
/// \param ws is the wordsize associated with the pointer
/// \return the TypePointer object
TypePointer *TypeFactory::getTypePointer(int4 s,Datatype *pt,uint4 ws)
TypePointer *TypeFactory::getTypePointerStripArray(int4 s,Datatype *pt,uint4 ws)
{ // Create pointer to type -pt-
if (pt->getMetatype() == TYPE_ARRAY) {
// Do no allow pointers to array
do {
pt = ((TypeArray *)pt)->getBase();
} while(pt->getMetatype() == TYPE_ARRAY);
}
{
if (pt->getMetatype() == TYPE_ARRAY)
pt = ((TypeArray *)pt)->getBase(); // Strip the first ARRAY type
TypePointer tmp(s,pt,ws);
return (TypePointer *) findAdd(tmp);
}
@ -1771,7 +1993,7 @@ TypePointer *TypeFactory::getTypePointer(int4 s,Datatype *pt,uint4 ws)
/// \param pt is the pointed-to data-type
/// \param ws is the wordsize associated with the pointer
/// \return the TypePointer object
TypePointer *TypeFactory::getTypePointerAbsolute(int4 s,Datatype *pt,uint4 ws)
TypePointer *TypeFactory::getTypePointer(int4 s,Datatype *pt,uint4 ws)
{
TypePointer tmp(s,pt,ws);
@ -1890,9 +2112,13 @@ Datatype *TypeFactory::downChain(Datatype *ptrtype,uintb &off)
if (ptrtype->metatype != TYPE_PTR) return (Datatype *)0;
TypePointer *ptype = (TypePointer *)ptrtype;
Datatype *pt = ptype->ptrto;
// If we know we have exactly one of an array, strip the array to get pointer to element
bool doStrip = (pt->getMetatype() != TYPE_ARRAY);
pt = pt->getSubType(off,&off);
if (pt == (Datatype *)0)
return (Datatype *)0;
if (doStrip)
return getTypePointerStripArray(ptype->size, pt, ptype->getWordSize());
return getTypePointer(ptype->size,pt,ptype->getWordSize());
}
@ -2073,20 +2299,27 @@ Datatype *TypeFactory::restoreXmlTypeNoRef(const Element *el,bool forcecore)
int4 num = el->getNumAttributes();
uint8 newid = 0;
int4 structsize = 0;
bool isVarLength = false;
for(int4 i=0;i<num;++i) {
if (el->getAttributeName(i) == "id") {
const string &attribName(el->getAttributeName(i));
if (attribName == "id") {
istringstream s(el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> newid;
}
else if (el->getAttributeName(i) == "size") {
else if (attribName == "size") {
istringstream s(el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> structsize;
}
else if (attribName == "varlength") {
isVarLength = xml_readbool(el->getAttributeValue(i));
}
}
if (newid == 0)
newid = Datatype::hashName(structname);
if (isVarLength)
newid = Datatype::hashSize(newid, structsize);
ct = findByIdLocal(structname,newid);
bool stubfirst = false;
if (ct == (Datatype *)0) {
@ -2105,7 +2338,7 @@ Datatype *TypeFactory::restoreXmlTypeNoRef(const Element *el,bool forcecore)
throw LowlevelError("Redefinition of structure: "+structname);
}
else // If structure is a placeholder stub
if (!setFields(ts.field,(TypeStruct *)ct,ts.size)) // Define structure now by copying fields
if (!setFields(ts.field,(TypeStruct *)ct,ts.size,ts.flags)) // Define structure now by copying fields
throw LowlevelError("Bad structure definition");
}
break;

View file

@ -73,7 +73,9 @@ protected:
enumtype = 4, ///< An enumeration type (as well as an integer)
poweroftwo = 8, ///< An enumeration type where all values are of 2^^n form
utf16 = 16, ///< 16-bit wide chars in unicode UTF16
utf32 = 32 ///< 32-bit wide chars in unicode UTF32
utf32 = 32, ///< 32-bit wide chars in unicode UTF32
opaque_string = 64, ///< Structure that should be treated as a string
variable_length = 128 ///< May be other structures with same name different lengths
};
friend class TypeFactory;
friend struct DatatypeCompare;
@ -85,6 +87,7 @@ protected:
void restoreXmlBasic(const Element *el); ///< Recover basic data-type properties
virtual void restoreXml(const Element *el,TypeFactory &typegrp); ///< Restore data-type from XML
static uint8 hashName(const string &nm); ///< Produce a data-type id by hashing the type name
static uint8 hashSize(uint8 id,int4 size); ///< Reversibly hash size into id
public:
/// Construct the base data-type copying low-level properties of another
Datatype(const Datatype &op) { size = op.size; name=op.name; metatype=op.metatype; flags=op.flags; id=op.id; }
@ -94,12 +97,15 @@ public:
Datatype(int4 s,type_metatype m,const string &n) { name=n; size=s; metatype=m; flags=0; id=0; }
virtual ~Datatype(void) {} ///< Destructor
bool isCoreType(void) const { return ((flags&coretype)!=0); } ///< Is this a core data-type
bool isCharPrint(void) const { return ((flags&(chartype|utf16|utf32))!=0); } ///< Does this print as a 'char'
bool isCharPrint(void) const { return ((flags&(chartype|utf16|utf32|opaque_string))!=0); } ///< Does this print as a 'char'
bool isEnumType(void) const { return ((flags&enumtype)!=0); } ///< Is this an enumerated type
bool isPowerOfTwo(void) const { return ((flags&poweroftwo)!=0); } ///< Is this a flag-based enumeration
bool isASCII(void) const { return ((flags&chartype)!=0); } ///< Does this print as an ASCII 'char'
bool isUTF16(void) const { return ((flags&utf16)!=0); } ///< Does this print as UTF16 'wchar'
bool isUTF32(void) const { return ((flags&utf32)!=0); } ///< Does this print as UTF32 'wchar'
bool isVariableLength(void) const { return ((flags&variable_length)!=0); } ///< Is \b this a variable length structure
bool hasSameVariableBase(const Datatype *ct) const; ///< Are these the same variable length data-type
bool isOpaqueString(void) const { return ((flags&opaque_string)!=0); } ///< Is \b this an opaquely encoded string
uint4 getInheritable(void) const { return (flags & coretype); } ///< Get properties pointers inherit
type_metatype getMetatype(void) const { return metatype; } ///< Get the type \b meta-type
uint8 getId(void) const { return id; } ///< Get the type id
@ -107,6 +113,8 @@ public:
const string &getName(void) const { return name; } ///< Get the type name
virtual void printRaw(ostream &s) const; ///< Print a description of the type to stream
virtual Datatype *getSubType(uintb off,uintb *newoff) const; ///< Recover component data-type one-level down
virtual Datatype *nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const;
virtual Datatype *nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const;
virtual int4 numDepend(void) const { return 0; } ///< Return number of component sub-types
virtual Datatype *getDepend(int4 index) const { return (Datatype *)0; } ///< Return the i-th component sub-type
virtual void printNameBase(ostream &s) const { if (!name.empty()) s<<name[0]; } ///< Print name as short prefix
@ -304,6 +312,7 @@ protected:
vector<TypeField> field; ///< The list of fields
void setFields(const vector<TypeField> &fd); ///< Establish fields for \b this
int4 getFieldIter(int4 off) const; ///< Get index into field list
int4 getLowerBoundField(int4 off) const; ///< Get index of last field before or equal to given offset
virtual void restoreXml(const Element *el,TypeFactory &typegrp);
public:
TypeStruct(const TypeStruct &op); ///< Construct from another TypeStruct
@ -312,6 +321,8 @@ public:
vector<TypeField>::const_iterator endField(void) const { return field.end(); } ///< End of fields
const TypeField *getField(int4 off,int4 sz,int4 *newoff) const; ///< Get field based on offset
virtual Datatype *getSubType(uintb off,uintb *newoff) const;
virtual Datatype *nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const;
virtual Datatype *nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const;
virtual int4 numDepend(void) const { return field.size(); }
virtual Datatype *getDepend(int4 index) const { return field[index].type; }
virtual int4 compare(const Datatype &op,int4 level) const; // For tree structure
@ -370,6 +381,8 @@ public:
Scope *getMap(void) const; ///< Get the symbol table indexed by \b this
Address getAddress(uintb off,int4 sz,const Address &point) const; ///< Construct an Address given an offset
virtual Datatype *getSubType(uintb off,uintb *newoff) const;
virtual Datatype *nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const;
virtual Datatype *nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const;
virtual int4 compare(const Datatype &op,int4 level) const;
virtual int4 compareDependency(const Datatype &op) const; // For tree structure
virtual Datatype *clone(void) const { return new TypeSpacebase(*this); }
@ -412,7 +425,7 @@ public:
Architecture *getArch(void) const { return glb; } ///< Get the Architecture object
Datatype *findByName(const string &n); ///< Return type of given name
Datatype *setName(Datatype *ct,const string &n); ///< Set the given types name
bool setFields(vector<TypeField> &fd,TypeStruct *ot,int4 fixedsize); ///< Set fields on a TypeStruct
bool setFields(vector<TypeField> &fd,TypeStruct *ot,int4 fixedsize,uint4 flags); ///< Set fields on a TypeStruct
bool setEnumValues(const vector<string> &namelist,
const vector<uintb> &vallist,
const vector<bool> &assignlist,
@ -424,8 +437,8 @@ public:
Datatype *getBase(int4 s,type_metatype m); ///< Get atomic type
Datatype *getBase(int4 s,type_metatype m,const string &n); ///< Get named atomic type
TypeCode *getTypeCode(void); ///< Get an "anonymous" function data-type
TypePointer *getTypePointer(int4 s,Datatype *pt,uint4 ws); ///< Construct a pointer data-type
TypePointer *getTypePointerAbsolute(int4 s,Datatype *pt,uint4 ws); ///< Construct an absolute pointer data-type
TypePointer *getTypePointerStripArray(int4 s,Datatype *pt,uint4 ws); ///< Construct a pointer data-type, stripping an ARRAY level
TypePointer *getTypePointer(int4 s,Datatype *pt,uint4 ws); ///< Construct an absolute pointer data-type
TypePointer *getTypePointerNoDepth(int4 s,Datatype *pt,uint4 ws); ///< Construct a depth limited pointer data-type
TypeArray *getTypeArray(int4 as,Datatype *ao); ///< Construct an array data-type
TypeStruct *getTypeStruct(const string &n); ///< Create an (empty) structure

View file

@ -537,7 +537,7 @@ void TypeOpBranchind::printRaw(ostream &s,const PcodeOp *op)
TypeOpCall::TypeOpCall(TypeFactory *t) : TypeOp(t,CPUI_CALL,"call")
{
opflags = (PcodeOp::special|PcodeOp::call|PcodeOp::coderef|PcodeOp::nocollapse);
opflags = (PcodeOp::special|PcodeOp::call|PcodeOp::has_callspec|PcodeOp::coderef|PcodeOp::nocollapse);
behave = new OpBehavior(CPUI_CALL,false,true); // Dummy behavior
}
@ -610,7 +610,7 @@ Datatype *TypeOpCall::getOutputLocal(const PcodeOp *op) const
TypeOpCallind::TypeOpCallind(TypeFactory *t) : TypeOp(t,CPUI_CALLIND,"callind")
{
opflags = PcodeOp::special|PcodeOp::call|PcodeOp::nocollapse;
opflags = PcodeOp::special|PcodeOp::call|PcodeOp::has_callspec|PcodeOp::nocollapse;
behave = new OpBehavior(CPUI_CALLIND,false,true); // Dummy behavior
}

View file

@ -239,6 +239,7 @@ CPUI_DEBUG -- This is the ONE debug switch that should be passed in
#ifdef CPUI_DEBUG
# define OPACTION_DEBUG
# define PRETTY_DEBUG
//# define __REMOTE_SOCKET__
//# define TYPEPROP_DEBUG
//# define DFSVERIFY_DEBUG
//# define BLOCKCONSISTENT_DEBUG

View file

@ -83,29 +83,27 @@ public:
namelock = 0x200, ///< The Name of the Varnode is locked
nolocalalias = 0x400, ///< There are no aliases pointing to this varnode
volatil = 0x800, ///< This varnode's value is volatile
spacebase_placeholder = 0x1000, ///< This varnode is inserted artificially to track a register
///< value at a specific point in the code
externref = 0x2000, ///< Varnode address is specially mapped by the loader
readonly = 0x4000, ///< Varnode is stored at a readonly location
persist = 0x8000, ///< Persists after (and before) function
addrtied = 0x10000, ///< High-level variable is tied to address
unaffected = 0x20000, ///< Input which is unaffected by the function
spacebase = 0x40000, ///< This is a base register for an address space
indirectonly = 0x80000, ///< If all uses of illegalinput varnode are inputs to INDIRECT
directwrite = 0x100000, ///< (could be) Directly affected by a valid input
addrforce = 0x200000, ///< Varnode is used to force variable into an address
externref = 0x1000, ///< Varnode address is specially mapped by the loader
readonly = 0x2000, ///< Varnode is stored at a readonly location
persist = 0x4000, ///< Persists after (and before) function
addrtied = 0x8000, ///< High-level variable is tied to address
unaffected = 0x10000, ///< Input which is unaffected by the function
spacebase = 0x20000, ///< This is a base register for an address space
indirectonly = 0x40000, ///< If all uses of illegalinput varnode are inputs to INDIRECT
directwrite = 0x80000, ///< (could be) Directly affected by a valid input
addrforce = 0x100000, ///< Varnode is used to force variable into an address
mapped = 0x400000, ///< Varnode has a database entry associated with it
indirect_creation = 0x800000, ///< The value in this Varnode is created indirectly
return_address = 0x1000000, ///< Is the varnode storage for a return address
coverdirty = 0x2000000, ///< Cover is not upto date
precislo = 0x4000000, ///< Is this Varnode the low part of a double precision value
precishi = 0x8000000, ///< Is this Varnode the high part of a double precision value
indirectstorage = 0x10000000, ///< Is this Varnode storing a pointer to the actual symbol
hiddenretparm = 0x20000000, ///< Does this varnode point to the return value storage location
incidental_copy = 0x40000000, ///< Do copies of this varnode happen as a side-effect
auto_live = 0x80000000 ///< Is this varnode automatically considered live, never removed as dead-code
mapped = 0x200000, ///< Varnode has a database entry associated with it
indirect_creation = 0x400000, ///< The value in this Varnode is created indirectly
return_address = 0x800000, ///< Is the varnode storage for a return address
coverdirty = 0x1000000, ///< Cover is not upto date
precislo = 0x2000000, ///< Is this Varnode the low part of a double precision value
precishi = 0x4000000, ///< Is this Varnode the high part of a double precision value
indirectstorage = 0x8000000, ///< Is this Varnode storing a pointer to the actual symbol
hiddenretparm = 0x10000000, ///< Does this varnode point to the return value storage location
incidental_copy = 0x20000000, ///< Do copies of this varnode happen as a side-effect
autolive_hold = 0x40000000 ///< Temporarily block dead-code removal of \b this
};
/// Additional boolean properties on a Varnode
enum addl_flags {
@ -117,7 +115,9 @@ public:
ptrflow = 0x20, ///< If this varnode flows to or from a pointer
unsignedprint = 0x40, ///< Constant that must be explicitly printed as unsigned
stack_store = 0x80, ///< Created by an explicit STORE
locked_input = 0x100 ///< Input that exists even if its unused
locked_input = 0x100, ///< Input that exists even if its unused
spacebase_placeholder = 0x200 ///< This varnode is inserted artificially to track a register
///< value at a specific point in the code
};
private:
mutable uint4 flags; ///< The collection of boolean attributes for this Varnode
@ -229,14 +229,15 @@ public:
/// Are all Varnodes at this storage location components of the same high-level variable?
bool isAddrTied(void) const { return ((flags&(Varnode::addrtied|Varnode::insert))==(Varnode::addrtied|Varnode::insert)); }
bool isAddrForce(void) const { return ((flags&Varnode::addrforce)!=0); } ///< Is \b this value forced into a particular storage location?
bool isAutoLive(void) const { return ((flags&Varnode::auto_live)!=0); } ///< Is \b this varnode exempt from dead-code removal?
bool isAutoLive(void) const { return ((flags&(Varnode::addrforce|Varnode::autolive_hold))!=0); } ///< Is \b this varnode exempt from dead-code removal?
bool isAutoLiveHold(void) const { return ((flags&Varnode::autolive_hold)!=0); } ///< Is there a temporary hold on dead-code removal?
bool isMapped(void) const { return ((flags&Varnode::mapped)!=0); } ///< Is there or should be formal symbol information associated with \b this?
bool isUnaffected(void) const { return ((flags&Varnode::unaffected)!=0); } ///< Is \b this a value that is supposed to be preserved across the function?
bool isSpacebase(void) const { return ((flags&Varnode::spacebase)!=0); } ///< Is this location used to store the base point for a virtual address space?
bool isReturnAddress(void) const { return ((flags&Varnode::return_address)!=0); } ///< Is this storage for a calls return address?
bool isPtrCheck(void) const { return ((addlflags&Varnode::ptrcheck)!=0); } ///< Has \b this been checked as a constant pointer to a mapped symbol?
bool isPtrFlow(void) const { return ((addlflags&Varnode::ptrflow)!=0); } ///< Does this varnode flow to or from a known pointer
bool isSpacebasePlaceholder(void) const { return ((flags&Varnode::spacebase_placeholder)!=0); } ///< Is \b this used specifically to track stackpointer values?
bool isSpacebasePlaceholder(void) const { return ((addlflags&Varnode::spacebase_placeholder)!=0); } ///< Is \b this used specifically to track stackpointer values?
bool hasNoLocalAlias(void) const { return ((flags&Varnode::nolocalalias)!=0); } ///< Are there (not) any local pointers that might affect \b this?
bool isMark(void) const { return ((flags&Varnode::mark)!=0); } ///< Has \b this been visited by the current algorithm?
bool isActiveHeritage(void) const { return ((addlflags&Varnode::activeheritage)!=0); } ///< Is \b this currently being traced by the Heritage algorithm?
@ -277,10 +278,8 @@ public:
void clearMark(void) const { flags &= ~Varnode::mark; } ///< Clear the mark on this Varnode
void setDirectWrite(void) { flags |= Varnode::directwrite; } ///< Mark \b this as directly affected by a legal input
void clearDirectWrite(void) { flags &= ~Varnode::directwrite; } ///< Mark \b this as not directly affected by a legal input
void setAddrForce(void) { setFlags(Varnode::addrforce | Varnode::auto_live); } ///< Mark as forcing a value into \b this particular storage location
void clearAddrForce(void) { clearFlags(Varnode::addrforce | Varnode::auto_live); } ///< Clear the forcing attribute
void setAutoLive(void) { flags |= Varnode::auto_live; } ///< Mark varnode as exempt from dead-code removal
void clearAutoLive(void) { flags &= ~Varnode::auto_live; } ///< Clear exemption for dead-code removal
void setAddrForce(void) { setFlags(Varnode::addrforce); } ///< Mark as forcing a value into \b this particular storage location
void clearAddrForce(void) { clearFlags(Varnode::addrforce); } ///< Clear the forcing attribute
void setImplied(void) { setFlags(Varnode::implied); } ///< Mark \b this as an \e implied variable in the final C source
void clearImplied(void) { clearFlags(Varnode::implied); } ///< Clear the \e implied mark on this Varnode
void setExplicit(void) { setFlags(Varnode::explict); } ///< Mark \b this as an \e explicit variable in the final C source
@ -291,14 +290,16 @@ public:
void clearPtrCheck(void) { addlflags &= ~Varnode::ptrcheck; } ///< Clear the pointer check mark on this Varnode
void setPtrFlow(void) { addlflags |= Varnode::ptrflow; } ///< Set \b this as flowing to or from pointer
void clearPtrFlow(void) { addlflags &= ~Varnode::ptrflow; } ///< Indicate that this varnode is not flowing to or from pointer
void setSpacebasePlaceholder(void) { setFlags(Varnode::spacebase_placeholder); } ///< Mark \b this as a special Varnode for tracking stackpointer values
void clearSpacebasePlaceholder(void) { clearFlags(Varnode::spacebase_placeholder); } ///< Clear the stackpointer tracking mark
void setSpacebasePlaceholder(void) { addlflags |= Varnode::spacebase_placeholder; } ///< Mark \b this as a special Varnode for tracking stackpointer values
void clearSpacebasePlaceholder(void) { addlflags &= ~Varnode::spacebase_placeholder; } ///< Clear the stackpointer tracking mark
void setPrecisLo(void) { setFlags(Varnode::precislo); } ///< Mark \b this as the low portion of a double precision value
void clearPrecisLo(void) { clearFlags(Varnode::precislo); } ///< Clear the mark indicating a double precision portion
void setPrecisHi(void) { setFlags(Varnode::precishi); } ///< Mark \b this as the high portion of a double precision value
void clearPrecisHi(void) { clearFlags(Varnode::precishi); } ///< Clear the mark indicating a double precision portion
void setWriteMask(void) { addlflags |= Varnode::writemask; } ///< Mark \b this as not a true \e write when computing SSA form
void clearWriteMask(void) { addlflags &= ~Varnode::writemask; } ///< Clear the mark indicating \b this is not a true write
void setAutoLiveHold(void) { flags |= Varnode::autolive_hold; } ///< Place temporary hold on dead code removal
void clearAutoLiveHold(void) { flags &= ~Varnode::autolive_hold; } ///< Clear temporary hold on dead code removal
void setUnsignedPrint(void) { addlflags |= Varnode::unsignedprint; } ///< Force \b this to be printed as unsigned
bool updateType(Datatype *ct,bool lock,bool override); ///< (Possibly) set the Datatype given various restrictions
void setStackStore(void) { addlflags |= Varnode::stack_store; } ///< Mark as produced by explicit CPUI_STORE

View file

@ -18,6 +18,7 @@ package ghidra.app.decompiler;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.ArrayList;
import javax.xml.parsers.SAXParser;
@ -27,14 +28,15 @@ import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import ghidra.app.cmd.function.CallDepthChangeInfo;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsImpl;
import ghidra.program.disassemble.Disassembler;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.ConstantPool.Record;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
@ -52,6 +54,14 @@ import ghidra.util.xml.XmlUtilities;
*/
public class DecompileCallback {
/**
* Data returned for a query about strings
*/
public static class StringData {
boolean isTruncated; // Did we truncate the string
public byte[] byteData; // The UTF8 encoding of the string
}
private DecompileDebug debug;
private Program program;
private Listing listing;
@ -65,6 +75,7 @@ public class DecompileCallback {
private AddressFactory addrfactory;
private ConstantPool cpool;
private PcodeDataTypeManager dtmanage;
private Charset utf8Charset;
private String nativeMessage;
private boolean showNamespace;
@ -84,6 +95,7 @@ public class DecompileCallback {
cpool = null;
nativeMessage = null;
debug = null;
utf8Charset = Charset.availableCharsets().get(CharsetInfo.UTF8);
}
private static SAXParser getSAXParser() throws PcodeXMLException {
@ -714,7 +726,6 @@ public class DecompileCallback {
resBuf.append("\n"); // Make into official XML document
String res = resBuf.toString();
if (debug != null) {
debug.getType(name, res);
debug.getType(type);
}
return res;
@ -1177,6 +1188,129 @@ public class DecompileCallback {
return listing.getFunctionAt(addr);
}
/**
* Return true if there are no "replacement" characters in the string
* @param string is the string to test
* @return true if no replacements
*/
private boolean isValidChars(String string) {
char replaceChar = '\ufffd';
for (int i = 0; i < string.length(); ++i) {
char c = string.charAt(i);
if (c == replaceChar) {
return false;
}
}
return true;
}
/**
* Check for a string at an address and return a UTF8 encoded byte array.
* If there is already data present at the address, use this to determine the
* string encoding. Otherwise use the data-type info passed in to determine the encoding.
* Check that the bytes at the address represent a valid string encoding that doesn't
* exceed the maximum character limit passed in. Return null if the string is invalid.
* Return the string translated into a UTF8 byte array otherwise. A (valid) empty
* string is returned as a zero length array.
* @param addrString is the XML encoded address and maximum byte limit
* @param dtName is the name of a character data-type
* @param dtId is the id associated with the character data-type
* @return the UTF8 encoded byte array or null
*/
public StringData getStringData(String addrString, String dtName, String dtId) {
Address addr;
int maxChars;
try {
maxChars = readXMLSize(addrString);
addr = Varnode.readXMLAddress(addrString, addrfactory, funcEntry.getAddressSpace());
if (addr == Address.NO_ADDRESS) {
throw new PcodeXMLException("Address does not physically map");
}
}
catch (PcodeXMLException e) {
Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage());
return null;
}
Data data = program.getListing().getDataContaining(addr);
Settings settings = SettingsImpl.NO_SETTINGS;
AbstractStringDataType dataType = null;
StringDataInstance stringInstance = null;
int length = 0;
if (data != null) {
if (data.getDataType() instanceof AbstractStringDataType) {
// There is already a string here. Use its configuration to
// set up the StringDataInstance
settings = data;
dataType = (AbstractStringDataType) data.getDataType();
length = data.getLength();
if (length <= 0) {
return null;
}
long diff = addr.subtract(data.getAddress()) *
addr.getAddressSpace().getAddressableUnitSize();
if (diff < 0 || diff >= length) {
return null;
}
length -= diff;
MemoryBufferImpl buf = new MemoryBufferImpl(program.getMemory(), addr, 64);
stringInstance = dataType.getStringDataInstance(buf, settings, length);
}
}
if (stringInstance == null) {
// There is no string and/or something else at the address.
// Setup StringDataInstance based on raw memory
DataType dt = dtmanage.findBaseType(dtName, dtId);
if (dt instanceof AbstractStringDataType) {
dataType = (AbstractStringDataType) dt;
}
else {
if (dt != null) {
int size = dt.getLength();
if (size == 2) {
dataType = TerminatedUnicodeDataType.dataType;
}
else if (size == 4) {
dataType = TerminatedUnicode32DataType.dataType;
}
else {
dataType = TerminatedStringDataType.dataType;
}
}
else {
dataType = TerminatedStringDataType.dataType;
}
}
MemoryBufferImpl buf = new MemoryBufferImpl(program.getMemory(), addr, 64);
stringInstance = dataType.getStringDataInstance(buf, settings, maxChars);
length = stringInstance.getStringLength();
if (length < 0 || length > maxChars) {
return null;
}
}
String stringVal;
if (stringInstance.isShowTranslation() && stringInstance.getTranslatedValue() != null) {
stringVal = stringInstance.getTranslatedValue();
}
else {
stringVal = stringInstance.getStringValue();
}
if (!isValidChars(stringVal)) {
return null;
}
StringData stringData = new StringData();
stringData.isTruncated = false;
if (stringVal.length() > maxChars) {
stringData.isTruncated = true;
stringVal = stringVal.substring(0, maxChars);
}
stringData.byteData = stringVal.getBytes(utf8Charset);
if (debug != null) {
debug.getStringData(addr, stringData);
}
return stringData;
}
//==================================================================================================
// Inner Classes
//==================================================================================================

View file

@ -18,6 +18,7 @@ package ghidra.app.decompiler;
import java.io.*;
import java.util.*;
import ghidra.app.decompiler.DecompileCallback.StringData;
import ghidra.app.plugin.processors.sleigh.ContextCache;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.plugin.processors.sleigh.symbol.ContextSymbol;
@ -35,28 +36,34 @@ import ghidra.program.model.symbol.Namespace;
import ghidra.util.Msg;
import ghidra.util.xml.SpecXmlUtils;
/**
* A container for collecting communication between the decompiler and the Ghidra database,
* as serviced through DecompileCallback during decompilation of a function.
* The query results can then be dumped as an XML document.
* The container is populated through methods that mirror the various methods in DecompileCallback.
*/
public class DecompileDebug {
private Function func;
private Program program;
private File debugFile;
private ArrayList<Namespace> dbscope;
private ArrayList<String> database;
private ArrayList<String> type;
private ArrayList<DataType> dtypes;
private ArrayList<String> symbol;
private ArrayList<String> context;
private ArrayList<String> cpool;
private ArrayList<String> flowoverride;
private ArrayList<String> inject;
private TreeSet<ByteChunk> byteset;
private TreeSet<Address> contextchange;
private Register contextRegister;
private ProgramContext progctx;
private String comments;
private Namespace globalnamespace;
private AddressRange readonlycache;
private boolean readonlycacheval;
private PcodeDataTypeManager dtmanage;
private Function func; // The function being decompiled
private Program program; // The program
private File debugFile; // The file to dump the XML document to
private ArrayList<Namespace> dbscope; // Symbol query: scope
private ArrayList<String> database; // description of the symbol
private ArrayList<DataType> dtypes; // Data-types queried
private ArrayList<String> symbol; // Names of code labels
private ArrayList<String> context; // Tracked register values associated with an address
private ArrayList<String> cpool; // Constant pool results
private ArrayList<String> flowoverride; // Flow overrides associated with an address
private ArrayList<String> inject; // Injection payloads
private TreeSet<ByteChunk> byteset; // Load image bytes
private TreeSet<Address> contextchange; // Addresses at which there is a context change
private TreeMap<Address, StringData> stringmap; // Strings queried with their associated start address
private Register contextRegister; // The global context register
private ProgramContext progctx; // Program context
private String comments; // All comments associated with the function (in XML form)
private Namespace globalnamespace; // The global namespace
private AddressRange readonlycache; // Current range of addresses with same readonly value (for internal use)
private boolean readonlycacheval; // Current readonly value (for internal use)
private PcodeDataTypeManager dtmanage; // Decompiler's data-type manager
class ByteChunk implements Comparable<ByteChunk> {
public Address addr;
@ -68,22 +75,28 @@ public class DecompileDebug {
val = new byte[16];
min = (int) ad.getOffset() & 15;
int len = v.length - off;
if (min + len >= 16)
if (min + len >= 16) {
len = 16 - min;
}
max = min + len;
for (int i = 0; i < 16; ++i)
for (int i = 0; i < 16; ++i) {
val[i] = 0;
for (int i = 0; i < len; ++i)
}
for (int i = 0; i < len; ++i) {
val[min + i] = v[off + i];
}
}
public void merge(ByteChunk op2) {
for (int i = op2.min; i < op2.max; ++i)
for (int i = op2.min; i < op2.max; ++i) {
val[i] = op2.val[i];
if (op2.min < min)
}
if (op2.min < min) {
min = op2.min;
if (op2.max > max)
}
if (op2.max > max) {
max = op2.max;
}
}
@Override
@ -97,13 +110,13 @@ public class DecompileDebug {
debugFile = debugf;
dbscope = new ArrayList<Namespace>();
database = new ArrayList<String>();
type = new ArrayList<String>();
dtypes = new ArrayList<DataType>();
symbol = new ArrayList<String>();
context = new ArrayList<String>();
cpool = new ArrayList<String>();
byteset = new TreeSet<ByteChunk>();
contextchange = new TreeSet<Address>();
stringmap = new TreeMap<Address, StringData>();
flowoverride = new ArrayList<String>();
inject = new ArrayList<String>();
contextRegister = null;
@ -127,8 +140,9 @@ public class DecompileDebug {
public void shutdown(Language pcodelanguage, String xmlOptions) {
OutputStream debugStream;
if (debugFile.exists())
if (debugFile.exists()) {
debugFile.delete();
}
try {
debugStream = new BufferedOutputStream(new FileOutputStream(debugFile));
}
@ -155,6 +169,7 @@ public class DecompileDebug {
dumpTrackedContext(debugStream);
debugStream.write("</context_points>\n".getBytes());
dumpComments(debugStream);
dumpStringData(debugStream);
dumpCPool(debugStream);
dumpConfiguration(debugStream, xmlOptions);
dumpFlowOverride(debugStream);
@ -177,14 +192,16 @@ public class DecompileDebug {
binimage += "\">\n";
debugStream.write(binimage.getBytes());
dumpBytes(debugStream);
for (int i = 0; i < symbol.size(); ++i)
debugStream.write((symbol.get(i)).getBytes());
for (String element : symbol) {
debugStream.write((element).getBytes());
}
debugStream.write("</binaryimage>\n".getBytes());
}
private boolean isReadOnly(Address addr) {
if ((readonlycache != null) && (readonlycache.contains(addr)))
if ((readonlycache != null) && (readonlycache.contains(addr))) {
return readonlycacheval;
}
MemoryBlock block = program.getMemory().getBlock(addr);
readonlycache = null;
readonlycacheval = false;
@ -221,24 +238,31 @@ public class DecompileDebug {
SpecXmlUtils.encodeStringAttribute(buf, "space", space.getPhysicalSpace().getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "offset", chunk.addr.getOffset() +
chunk.min);
if (lastreadonly)
if (lastreadonly) {
SpecXmlUtils.encodeBooleanAttribute(buf, "readonly", lastreadonly);
}
buf.append(">\n");
tagstarted = true;
}
for (int i = 0; i < chunk.min; ++i)
{
buf.append(" "); // pad the hex display to 16 bytes
}
for (int i = chunk.min; i < chunk.max; ++i) {
int hi = (chunk.val[i] >> 4) & 0xf;
int lo = chunk.val[i] & 0xf;
if (hi > 9)
if (hi > 9) {
hi += 'a' - 10;
else
}
else {
hi += '0';
if (lo > 9)
}
if (lo > 9) {
lo += 'a' - 10;
else
}
else {
lo += '0';
}
buf.append((char) hi);
buf.append((char) lo);
}
@ -252,24 +276,57 @@ public class DecompileDebug {
lastspace = space;
}
}
if (tagstarted)
if (tagstarted) {
buf.append("</bytechunk>\n");
}
debugStream.write(buf.toString().getBytes());
}
// private void dumpTypes(OutputStream debugStream) throws IOException {
// StringBuilder buf = new StringBuilder();
// buf.append("<typegrp");
// SpecXmlUtils.encodeSignedIntegerAttribute(buf, "structalign", 4);
// SpecXmlUtils.encodeSignedIntegerAttribute(buf, "enumsize", 4);
// SpecXmlUtils.encodeBooleanAttribute(buf, "enumsigned", false);
// buf.append(">\n");
// // structalign should come out of pcodelanguage.getCompilerSpec()
// debugStream.write(buf.toString().getBytes());
// for (int i = type.size() - 1; i >= 0; --i)
// debugStream.write((type.get(i)).getBytes());
// debugStream.write("</typegrp>\n".getBytes());
// }
/**
* Dump information on strings that were queried by the decompiler.
* @param debugStream is the stream to write to
* @throws IOException if any i/o error occurs
*/
private void dumpStringData(OutputStream debugStream) throws IOException {
if (stringmap.isEmpty()) {
return;
}
StringBuilder buf = new StringBuilder();
buf.append("<stringmanage>\n");
for (Map.Entry<Address, StringData> entry : stringmap.entrySet()) {
buf.append("<string>\n<addr");
Varnode.appendSpaceOffset(buf, entry.getKey());
buf.append("/>\n<bytes");
SpecXmlUtils.encodeBooleanAttribute(buf, "trunc", entry.getValue().isTruncated);
buf.append(">\n ");
int count = 0;
for (byte element : entry.getValue().byteData) {
int hi = (element >> 4) & 0xf;
int lo = element & 0xf;
if (hi > 9) {
hi += 'a' - 10;
}
else {
hi += '0';
}
if (lo > 9) {
lo += 'a' - 10;
}
else {
lo += '0';
}
buf.append((char) hi);
buf.append((char) lo);
if (count % 20 == 19) {
buf.append("\n ");
}
}
buf.append("00\n</bytes>\n");
buf.append("</string>\n");
}
buf.append("</stringmanage>\n");
debugStream.write(buf.toString().getBytes());
}
private void dumpDataTypes(OutputStream debugStream) throws IOException {
int intSize = program.getCompilerSpec().getDataOrganization().getIntegerSize();
@ -285,12 +342,15 @@ public class DecompileDebug {
DataTypeDependencyOrderer TypeOrderer =
new DataTypeDependencyOrderer(program.getDataTypeManager(), dtypes);
//First output all structures as zero size so to avoid any cyclic dependencies.
for (DataType dataType : TypeOrderer.getStructList())
for (DataType dataType : TypeOrderer.getStructList()) {
debugStream.write((dtmanage.buildStructTypeZeroSizeOveride(dataType) + "\n").toString().getBytes());
}
//Next, use the dependency stack to output types.
for (DataType dataType : TypeOrderer.getDependencyList())
if (!(dataType instanceof BuiltIn)) //If we don't do this, we have a problem with DataType "string" (array of size=-1)
for (DataType dataType : TypeOrderer.getDependencyList()) {
if (!(dataType instanceof BuiltIn)) {
debugStream.write((dtmanage.buildType(dataType, dataType.getLength()) + "\n").toString().getBytes());
}
}
debugStream.write("</typegrp>\n".getBytes());
}
@ -299,20 +359,24 @@ public class DecompileDebug {
// set associated with the function start
// and it is written in xml as if it were
// the default tracking set
for (int i = 0; i < context.size(); ++i)
debugStream.write((context.get(i)).getBytes());
for (String element : context) {
debugStream.write((element).getBytes());
}
}
private ArrayList<ContextSymbol> getContextSymbols() {
Language lang = program.getLanguage();
if (!(lang instanceof SleighLanguage))
if (!(lang instanceof SleighLanguage)) {
return null;
}
ArrayList<ContextSymbol> res = new ArrayList<ContextSymbol>();
ghidra.app.plugin.processors.sleigh.symbol.Symbol[] list =
((SleighLanguage) lang).getSymbolTable().getSymbolList();
for (Symbol element : list)
if (element instanceof ContextSymbol)
for (Symbol element : list) {
if (element instanceof ContextSymbol) {
res.add((ContextSymbol) element);
}
}
return res;
}
@ -323,8 +387,9 @@ public class DecompileDebug {
*/
private void getContextChangePoints(Address addr) {
AddressRange addrrange = progctx.getRegisterValueRangeContaining(contextRegister, addr);
if (addrrange == null)
if (addrrange == null) {
return;
}
contextchange.add(addrrange.getMinAddress());
try {
Address nextaddr = addrrange.getMaxAddress().add(1);
@ -339,13 +404,14 @@ public class DecompileDebug {
* body of the function. Right now we only get the context at the
* beginning of the function because its difficult to tell where the
* context changes.
* @param debugStream
* @throws IOException
* @param debugStream is the stream being written to
* @throws IOException for any i/o error
*/
private void dumpPointsetContext(OutputStream debugStream) throws IOException {
ArrayList<ContextSymbol> ctxsymbols = getContextSymbols();
if (ctxsymbols == null)
if (ctxsymbols == null) {
return;
}
ContextCache ctxcache = new ContextCache();
ctxcache.registerVariable(contextRegister);
int[] buf = new int[ctxcache.getContextSize()];
@ -359,22 +425,27 @@ public class DecompileDebug {
StringBuilder stringBuf = new StringBuilder();
if (lastbuf != null) { // Check to make sure we don't have identical context data
int i;
for (i = 0; i < buf.length; ++i)
if (buf[i] != lastbuf[i])
for (i = 0; i < buf.length; ++i) {
if (buf[i] != lastbuf[i]) {
break;
}
}
if (i == buf.length)
{
continue; // If all data is identical, then changepoint is not necessary
}
}
else
else {
lastbuf = new int[buf.length];
for (int i = 0; i < buf.length; ++i)
}
for (int i = 0; i < buf.length; ++i) {
lastbuf[i] = buf[i];
}
stringBuf.append("<context_pointset");
Varnode.appendSpaceOffset(stringBuf, addr);
stringBuf.append(">\n");
for (int i = 0; i < ctxsymbols.size(); ++i) {
ContextSymbol sym = ctxsymbols.get(i);
for (ContextSymbol sym : ctxsymbols) {
int sbit = sym.getLow();
int ebit = sym.getHigh();
int word = sbit / (8 * 4);
@ -395,37 +466,48 @@ public class DecompileDebug {
}
private void dumpCPool(OutputStream debugStream) throws IOException {
if (cpool.size() == 0) return;
if (cpool.size() == 0) {
return;
}
debugStream.write("<constantpool>\n".getBytes());
for(String rec : cpool)
for(String rec : cpool) {
debugStream.write(rec.getBytes());
}
debugStream.write("</constantpool>\n".getBytes());
}
private void dumpComments(OutputStream debugStream) throws IOException {
if (comments != null)
if (comments != null) {
debugStream.write(comments.getBytes());
}
}
private void dumpConfiguration(OutputStream debugStream, String xmlOptions) throws IOException {
if ((xmlOptions != null) && (xmlOptions.length() != 0))
if ((xmlOptions != null) && (xmlOptions.length() != 0)) {
debugStream.write(xmlOptions.getBytes());
}
}
private void dumpFlowOverride(OutputStream debugStream) throws IOException {
if (flowoverride.size() == 0) return;
if (flowoverride.size() == 0) {
return;
}
debugStream.write("<flowoverridelist>\n".getBytes());
for(int i=0;i<flowoverride.size();++i)
debugStream.write(flowoverride.get(i).getBytes());
for (String element : flowoverride) {
debugStream.write(element.getBytes());
}
debugStream.write("</flowoverridelist>\n".getBytes());
}
private void dumpInject(OutputStream debugStream) throws IOException {
if (inject.size() == 0) return;
if (inject.size() == 0) {
return;
}
debugStream.write("<injectdebug>\n".getBytes());
for(int i=0;i<inject.size();++i)
debugStream.write(inject.get(i).getBytes());
for (String element : inject) {
debugStream.write(element.getBytes());
}
debugStream.write("</injectdebug>\n".getBytes());
}
@ -434,23 +516,27 @@ public class DecompileDebug {
debugStream.write("<db>\n".getBytes());
for (int i = 0; i < database.size(); ++i) {
scopename = dbscope.get(i);
if (scopename != null)
if (scopename != null) {
break;
}
}
while (scopename != null) {
StringBuilder datahead = new StringBuilder();
datahead.append("<scope");
// Force globalnamespace to have blank name
if (scopename != globalnamespace)
if (scopename != globalnamespace) {
SpecXmlUtils.xmlEscapeAttribute(datahead, "name", scopename.getName());
else
}
else {
SpecXmlUtils.encodeStringAttribute(datahead, "name", "");
}
datahead.append(">\n");
datahead.append("<parent>\n");
HighFunction.createNamespaceTag(datahead, scopename.getParentNamespace());
datahead.append("</parent>\n");
if (scopename != globalnamespace)
if (scopename != globalnamespace) {
datahead.append("<rangeequalssymbols/>\n");
}
datahead.append("<symbollist>\n");
debugStream.write(datahead.toString().getBytes());
for (int i = 0; i < database.size(); ++i) {
@ -462,10 +548,11 @@ public class DecompileDebug {
}
debugStream.write("</symbollist>\n</scope>\n".getBytes());
scopename = null;
for (int i = 0; i < dbscope.size(); ++i) {
scopename = dbscope.get(i);
if (scopename != null)
for (Namespace element : dbscope) {
scopename = element;
if (scopename != null) {
break;
}
}
}
debugStream.write("</db>\n".getBytes());
@ -476,7 +563,7 @@ public class DecompileDebug {
}
public void getPcode(Address addr, Instruction instr) {
if (instr != null)
if (instr != null) {
try {
byte[] bytes;
int delaySlotsCnt = instr.getDelaySlotDepth();
@ -510,6 +597,7 @@ public class DecompileDebug {
catch (MemoryAccessException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
}
}
public void getBytes(Address addr, byte[] res) {
@ -521,14 +609,19 @@ public class DecompileDebug {
ByteChunk match = byteset.tailSet(chunk).first();
match.merge(chunk);
}
else
else {
byteset.add(chunk);
}
Address newaddr = chunk.addr.add(chunk.max);
off += newaddr.getOffset() - addr.getOffset();
addr = newaddr;
}
}
public void getStringData(Address addr, StringData stringData) {
stringmap.put(addr, stringData);
}
public void getComments(String comm) {
comments = comm; // Already in XML form
}
@ -545,25 +638,24 @@ public class DecompileDebug {
}
public void getMapped(Namespace namespc, String res) {
if (namespc == null)
if (namespc == null) {
dbscope.add(globalnamespace);
else
}
else {
dbscope.add(namespc);
}
database.add(res);
}
public void getType(String name, String res) {
type.add(res);
}
public void getType(DataType dt) {
dtypes.add(dt);
}
public void getFNTypes(HighFunction hfunc) {
getType(hfunc.getFunctionPrototype().getReturnType());
for (int i = 0; i < hfunc.getFunctionPrototype().getNumParams(); i++)
for (int i = 0; i < hfunc.getFunctionPrototype().getNumParams(); i++) {
getType(hfunc.getFunctionPrototype().getParam(i).getDataType());
}
}
public void getTrackedRegisters(String doc) {
@ -575,8 +667,9 @@ public class DecompileDebug {
buf.append("<ref");
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "a", refs[0]);
long val = 0;
if (refs.length > 1)
if (refs.length > 1) {
val = refs[1];
}
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "b", val);
buf.append("/>\n");
buf.append(rec);
@ -586,16 +679,21 @@ public class DecompileDebug {
public void addFlowOverride(Address addr,FlowOverride fo) {
StringBuilder buf = new StringBuilder();
buf.append("<flow type=\"");
if (fo == FlowOverride.BRANCH)
if (fo == FlowOverride.BRANCH) {
buf.append("branch");
else if (fo == FlowOverride.CALL)
}
else if (fo == FlowOverride.CALL) {
buf.append("call");
else if (fo == FlowOverride.CALL_RETURN)
}
else if (fo == FlowOverride.CALL_RETURN) {
buf.append("callreturn");
else if (fo == FlowOverride.RETURN)
}
else if (fo == FlowOverride.RETURN) {
buf.append("return");
else
}
else {
buf.append("none");
}
buf.append("\"><addr");
Varnode.appendSpaceOffset(buf,func.getEntryPoint());
buf.append("/><addr");
@ -604,12 +702,12 @@ public class DecompileDebug {
flowoverride.add(buf.toString());
}
public void addInject(Address addr,String name,int type,String payload) {
public void addInject(Address addr,String name,int injectType,String payload) {
StringBuilder buf = new StringBuilder();
buf.append("<inject name=\"");
buf.append(name);
buf.append('"');
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "type", type);
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "type", injectType);
buf.append(">\n <addr");
Varnode.appendSpaceOffset(buf, addr);
buf.append("/>\n <payload><![CDATA[\n");

View file

@ -629,6 +629,10 @@ public class DecompileOptions {
eliminateUnreachable ? "on" : "off");
appendOption(buf, "currentaction", iface.getSimplificationStyle(), "doubleprecis",
simplifyDoublePrecision ? "on" : "off");
// Must set language early so that the object is in place before other option changes
appendOption(buf, "setlanguage", displayLanguage.toString(), "", "");
appendOption(buf, "ignoreunimplemented", ignoreunimpl ? "on" : "off", "", "");
appendOption(buf, "inferconstptr", inferconstptr ? "on" : "off", "", "");
appendOption(buf, "nullprinting", nullToken ? "on" : "off", "", "");
@ -652,8 +656,6 @@ public class DecompileOptions {
appendOption(buf, "integerformat", integerFormat.getOptionString(), "", "");
appendOption(buf, "setlanguage", displayLanguage.toString(), "", "");
appendOption(buf, "protoeval", protoEvalModel, "", "");
buf.append("</optionslist>\n");
return buf.toString();

View file

@ -326,7 +326,12 @@ public class DecompileProcess {
}
break;
case 'S':
getSymbol(); // getSymbol
if (name.equals("getString")) {
getStringData();
}
else {
getSymbol(); // getSymbol
}
break;
case 'T':
if (name.equals("getType")) {
@ -778,6 +783,35 @@ public class DecompileProcess {
write(query_response_end);
}
private void getStringData() throws IOException {
String addr = readQueryString();
String dtName = readQueryString();
String dtId = readQueryString();
DecompileCallback.StringData stringData = callback.getStringData(addr, dtName, dtId);
write(query_response_start);
if (stringData != null) {
byte[] res = stringData.byteData;
int sz = res.length + 1; // We add a null terminator character
int sz1 = (sz & 0x3f) + 0x20;
sz >>>= 6;
int sz2 = (sz & 0x3f) + 0x20;
write(byte_start);
write(sz1);
write(sz2);
write(stringData.isTruncated ? 1 : 0);
byte[] dblres = new byte[res.length * 2 + 2];
for (int i = 0; i < res.length; i++) {
dblres[i * 2] = (byte) (((res[i] >> 4) & 0xf) + 65);
dblres[i * 2 + 1] = (byte) ((res[i] & 0xf) + 65);
}
dblres[res.length * 2] = 65; // Adding null terminator
dblres[res.length * 2 + 1] = 65;
write(dblres);
write(byte_end);
}
write(query_response_end);
}
private void write(byte[] bytes) throws IOException {
if (nativeOut == null) {
return;

View file

@ -119,7 +119,7 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
ParameterDefinition[] args = new ParameterDefinitionImpl[np];
for (int i = 0; i < np; i++) {
HighParam parm = hf.getLocalSymbolMap().getParam(i);
HighSymbol parm = hf.getLocalSymbolMap().getParamSymbol(i);
args[i] = new ParameterDefinitionImpl(parm.getName(), parm.getDataType(), null);
}
fsig.setArguments(args);

View file

@ -29,6 +29,7 @@ import ghidra.app.util.*;
import ghidra.framework.model.DomainObject;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.util.OptionsService;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.*;
@ -44,12 +45,21 @@ public class CppExporter extends Exporter {
public static final String CREATE_C_FILE = "Create C File (.c)";
public static final String CREATE_HEADER_FILE = "Create Header File (.h)";
public static final String USE_CPP_STYLE_COMMENTS = "Use C++ Style comments (//)";
public static final String EMIT_TYPE_DEFINITONS = "Emit data-type definitions";
public static final String FUNCTION_TAG_FILTERS = "Function tags to filter";
public static final String FUNCTION_TAG_EXCLUDE = "Function tags excluded";
private static String EOL = System.getProperty("line.separator");
private boolean isCreateHeaderFile = false;
private boolean isCreateCFile = true;
private boolean isUseCppStyleComments = true;
private boolean emitDataTypeDefinitions = true;
private String tagOptions = "";
private ArrayList<FunctionTag> tagList = null;
private boolean tagsExclude = true;
//private boolean isSplitFunctions = false;
private DecompileOptions options;
private boolean userSuppliedOptions = false;
@ -64,6 +74,18 @@ public class CppExporter extends Exporter {
this.userSuppliedOptions = true;
}
public CppExporter(boolean createHeader, boolean createFile, boolean emitTypes,
boolean excludeTags, String tags) {
this();
isCreateHeaderFile = createHeader;
isCreateCFile = createFile;
emitDataTypeDefinitions = emitTypes;
tagsExclude = excludeTags;
if (tags != null) {
tagOptions = tags;
}
}
@Override
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
TaskMonitor monitor) throws IOException, ExporterException {
@ -75,6 +97,7 @@ public class CppExporter extends Exporter {
Program program = (Program) domainObj;
configureOptions(program);
configureFunctionTags(program);
if (addrSet == null) {
addrSet = program.getMemory();
@ -99,7 +122,9 @@ public class CppExporter extends Exporter {
ParallelDecompiler.createChunkingParallelDecompiler(callback, chunkingMonitor);
try {
writeProgramDataTypes(program, header, headerWriter, cFileWriter, chunkingMonitor);
if (emitDataTypeDefinitions) {
writeProgramDataTypes(program, header, headerWriter, cFileWriter, chunkingMonitor);
}
chunkingMonitor.checkCanceled();
decompileAndExport(addrSet, program, headerWriter, cFileWriter, parallelDecompiler,
@ -150,7 +175,21 @@ public class CppExporter extends Exporter {
functions.clear();
}
functions.add(iterator.next());
Function currentFunction = iterator.next();
if (tagList != null) {
Set<FunctionTag> tags = currentFunction.getTags();
boolean hasTag = false;
for (FunctionTag tag : tagList) {
if (tags.contains(tag)) {
hasTag = true;
break;
}
}
if (tagsExclude == hasTag) {
continue;
}
}
functions.add(currentFunction);
}
// handle any remaining functions
@ -214,6 +253,27 @@ public class CppExporter extends Exporter {
}
}
private void configureFunctionTags(Program program) {
if (tagOptions != null && tagOptions.length() != 0) {
FunctionManager functionManager = program.getFunctionManager();
if (functionManager instanceof FunctionManagerDB) {
FunctionTagManager tagManager =
((FunctionManagerDB) functionManager).getFunctionTagManager();
String[] split = tagOptions.split(",");
tagList = new ArrayList<FunctionTag>();
for (String tag : split) {
FunctionTag functionTag = tagManager.getFunctionTag(tag.trim());
if (functionTag != null) {
tagList.add(functionTag);
}
}
if (tagList.isEmpty()) {
tagList = null;
}
}
}
}
private void writeProgramDataTypes(Program program, File header, PrintWriter headerWriter,
PrintWriter cFileWriter, TaskMonitor monitor) throws IOException, CancelledException {
if (headerWriter != null) {
@ -256,10 +316,13 @@ public class CppExporter extends Exporter {
@Override
public List<Option> getOptions(DomainObjectService domainObjectService) {
ArrayList<Option> list = new ArrayList<>();
list.add(new Option(CREATE_HEADER_FILE, new Boolean(isCreateHeaderFile)));
list.add(new Option(CREATE_C_FILE, new Boolean(isCreateCFile)));
list.add(new Option(CREATE_HEADER_FILE, Boolean.valueOf(isCreateHeaderFile)));
list.add(new Option(CREATE_C_FILE, Boolean.valueOf(isCreateCFile)));
//list.add(new Option(SPLIT_FILE, new Boolean(isSplitFunctions)));
list.add(new Option(USE_CPP_STYLE_COMMENTS, new Boolean(isUseCppStyleComments)));
list.add(new Option(USE_CPP_STYLE_COMMENTS, Boolean.valueOf(isUseCppStyleComments)));
list.add(new Option(EMIT_TYPE_DEFINITONS, Boolean.valueOf(emitDataTypeDefinitions)));
list.add(new Option(FUNCTION_TAG_FILTERS, tagOptions));
list.add(new Option(FUNCTION_TAG_EXCLUDE, Boolean.valueOf(tagsExclude)));
return list;
}
@ -280,6 +343,15 @@ public class CppExporter extends Exporter {
else if (optName.equals(USE_CPP_STYLE_COMMENTS)) {
isUseCppStyleComments = ((Boolean) option.getValue()).booleanValue();
}
else if (optName.equals(EMIT_TYPE_DEFINITONS)) {
emitDataTypeDefinitions = ((Boolean) option.getValue()).booleanValue();
}
else if (optName.equals(FUNCTION_TAG_FILTERS)) {
tagOptions = (String) option.getValue();
}
else if (optName.equals(FUNCTION_TAG_EXCLUDE)) {
tagsExclude = ((Boolean) option.getValue()).booleanValue();
}
else {
throw new OptionException("Unknown option: " + optName);
}
@ -304,7 +376,7 @@ public class CppExporter extends Exporter {
/**
* Generate suitable C-style definition statements (#define) for any fake data-type names
* which may be produced by the decompiler (e.g., unkint, unkuint, etc.).
* @param dataOrganization
* @param dataOrganization is the data organization to result the size of core types.
* @return multi-line string containing C-style declarations of fake decompiler types.
*/
private static String getFakeCTypeDefinitions(DataOrganization dataOrganization) {

View file

@ -318,12 +318,43 @@ public class PcodeDataTypeManager {
throw new IllegalArgumentException("Unsupported character size");
}
private String buildTypeInternal(DataType type, int size) { // Build all of type except name attribute
if (type instanceof TypeDef) {
type = ((TypeDef) type).getBaseDataType();
private void appendOpaqueString(StringBuilder resBuf, DataType type, int size) {
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "struct");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeBooleanAttribute(resBuf, "opaquestring", true);
SpecXmlUtils.encodeBooleanAttribute(resBuf, "varlength", true);
resBuf.append(">\n");
resBuf.append("<field name=\"unknown_data1\"");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "offset", 0);
resBuf.append("> <typeref name=\"byte\"/></field>\n");
size -= 1;
resBuf.append("<field name=\"opaque_data\"");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "offset", 1);
resBuf.append("> <type");
SpecXmlUtils.encodeStringAttribute(resBuf, "name", "");
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size);
resBuf.append("><typeref name=\"byte\"/></type>");
resBuf.append("</field>\n");
}
private String buildTypeInternal(DataType origType, int size) { // Build all of type except name attribute
DataType type;
if (origType instanceof TypeDef) {
type = ((TypeDef) origType).getBaseDataType();
}
else {
type = origType;
}
StringBuilder resBuf = new StringBuilder();
if (type instanceof Pointer) {
if (origType == type) {
SpecXmlUtils.encodeStringAttribute(resBuf, "name", "");
}
else {
appendNameIdAttributes(resBuf, origType);
}
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "ptr");
int ptrLen = type.getLength();
if (ptrLen <= 0) {
@ -344,26 +375,35 @@ public class PcodeDataTypeManager {
if (ptrto == null) {
ptrtoTypeRef = buildTypeRef(DefaultDataType.dataType, 1);
}
else if ((ptrto instanceof StringDataType) ||
(type instanceof TerminatedStringDataType)) { // Convert pointer to string
ptrtoTypeRef = getCharTypeRef(dataOrganization.getCharSize()); // to pointer to char
}
else if (ptrto instanceof StringUTF8DataType) { // Convert pointer to string
// TODO: Need to ensure that UTF8 decoding applies
ptrtoTypeRef = getCharTypeRef(1); // to pointer to char
else if (ptrto instanceof AbstractStringDataType) {
if ((ptrto instanceof StringDataType) ||
(type instanceof TerminatedStringDataType)) { // Convert pointer to string
ptrtoTypeRef = getCharTypeRef(dataOrganization.getCharSize()); // to pointer to char
}
else if (ptrto instanceof StringUTF8DataType) { // Convert pointer to string
// TODO: Need to ensure that UTF8 decoding applies
ptrtoTypeRef = getCharTypeRef(1); // to pointer to char
}
else if ((ptrto instanceof UnicodeDataType) ||
(ptrto instanceof TerminatedUnicodeDataType)) {
ptrtoTypeRef = getCharTypeRef(2);
}
else if ((ptrto instanceof Unicode32DataType) ||
(ptrto instanceof TerminatedUnicode32DataType)) {
ptrtoTypeRef = getCharTypeRef(4);
}
else {
ptrtoTypeRef = new StringBuilder();
ptrtoTypeRef.append("<type");
appendNameIdAttributes(ptrtoTypeRef, ptrto);
appendOpaqueString(ptrtoTypeRef, ptrto, 16384);
ptrtoTypeRef.append("</type>\n");
}
}
else if (ptrto instanceof FunctionDefinition) {
// FunctionDefinition may have size of -1, do not translate to undefined
ptrtoTypeRef = buildTypeRef(ptrto, ptrto.getLength());
}
else if ((ptrto instanceof UnicodeDataType) ||
(ptrto instanceof TerminatedUnicodeDataType)) {
ptrtoTypeRef = getCharTypeRef(2);
}
else if ((ptrto instanceof Unicode32DataType) ||
(ptrto instanceof TerminatedUnicode32DataType)) {
ptrtoTypeRef = getCharTypeRef(4);
}
else if (ptrto.getLength() < 0 && !(ptrto instanceof FunctionDefinition)) {
ptrtoTypeRef = buildTypeRef(Undefined1DataType.dataType, 1);
}
@ -373,6 +413,12 @@ public class PcodeDataTypeManager {
resBuf.append(ptrtoTypeRef);
}
else if (type instanceof Array) {
if (origType == type) {
SpecXmlUtils.encodeStringAttribute(resBuf, "name", "");
}
else {
appendNameIdAttributes(resBuf, origType);
}
int sz = type.getLength();
if (sz == 0) {
sz = size;
@ -386,6 +432,7 @@ public class PcodeDataTypeManager {
buildTypeRef(((Array) type).getDataType(), ((Array) type).getElementLength()));
}
else if (type instanceof Structure) {
appendNameIdAttributes(resBuf, origType);
// if size is 0, insert an Undefined4 component
//
int sz = type.getLength();
@ -417,6 +464,7 @@ public class PcodeDataTypeManager {
// TODO: trailing flexible array component not yet supported
}
else if (type instanceof Enum) {
appendNameIdAttributes(resBuf, origType);
Enum enumDt = (Enum) type;
long[] keys = enumDt.getValues();
String metatype = "uint";
@ -438,6 +486,7 @@ public class PcodeDataTypeManager {
}
}
else if (type instanceof CharDataType) {
appendNameIdAttributes(resBuf, origType);
boolean signed = ((CharDataType) type).isSigned();
int sz = type.getLength();
if (sz <= 0) {
@ -455,44 +504,57 @@ public class PcodeDataTypeManager {
}
else if (type instanceof WideCharDataType || type instanceof WideChar16DataType ||
type instanceof WideChar32DataType) {
appendNameIdAttributes(resBuf, origType);
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "int");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", type.getLength());
SpecXmlUtils.encodeBooleanAttribute(resBuf, "utf", true);
resBuf.append('>');
}
else if ((type instanceof StringDataType) || (type instanceof TerminatedStringDataType)) {
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size);
resBuf.append('>');
resBuf.append(getCharTypeRef(dataOrganization.getCharSize()));
}
else if (type instanceof StringUTF8DataType) {
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size);
resBuf.append('>');
resBuf.append(getCharTypeRef(1)); // TODO: Need to ensure that UTF8 decoding applies
}
else if ((type instanceof UnicodeDataType) || (type instanceof TerminatedUnicodeDataType)) {
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size / 2);
resBuf.append('>');
resBuf.append(getCharTypeRef(2));
}
else if ((type instanceof Unicode32DataType) ||
(type instanceof TerminatedUnicode32DataType)) {
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size / 4);
resBuf.append('>');
resBuf.append(getCharTypeRef(4));
else if (type instanceof AbstractStringDataType) {
if ((type instanceof StringDataType) || (type instanceof TerminatedStringDataType)) {
SpecXmlUtils.encodeStringAttribute(resBuf, "name", "");
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size);
resBuf.append('>');
resBuf.append(getCharTypeRef(dataOrganization.getCharSize()));
}
else if (type instanceof StringUTF8DataType) {
SpecXmlUtils.encodeStringAttribute(resBuf, "name", "");
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size);
resBuf.append('>');
resBuf.append(getCharTypeRef(1)); // TODO: Need to ensure that UTF8 decoding applies
}
else if ((type instanceof UnicodeDataType) ||
(type instanceof TerminatedUnicodeDataType)) {
SpecXmlUtils.encodeStringAttribute(resBuf, "name", "");
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size / 2);
resBuf.append('>');
resBuf.append(getCharTypeRef(2));
}
else if ((type instanceof Unicode32DataType) ||
(type instanceof TerminatedUnicode32DataType)) {
SpecXmlUtils.encodeStringAttribute(resBuf, "name", "");
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", size / 4);
resBuf.append('>');
resBuf.append(getCharTypeRef(4));
}
else {
appendNameIdAttributes(resBuf, type);
appendOpaqueString(resBuf, type, size);
}
}
else if (type instanceof FunctionDefinition) {
if (size <= 0) {
size = 1;
}
appendNameIdAttributes(resBuf, origType);
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "code");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", 1); // Force size of 1
resBuf.append('>');
@ -502,6 +564,7 @@ public class PcodeDataTypeManager {
fproto.buildPrototypeXML(resBuf, this);
}
else if (type instanceof BooleanDataType) {
appendNameIdAttributes(resBuf, origType);
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "bool");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", type.getLength());
resBuf.append('>');
@ -512,11 +575,13 @@ public class PcodeDataTypeManager {
if (sz <= 0) {
sz = size;
}
appendNameIdAttributes(resBuf, origType);
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", signed ? "int" : "uint");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", sz);
resBuf.append('>');
}
else if (type instanceof AbstractFloatDataType) {
appendNameIdAttributes(resBuf, origType);
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "float");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", type.getLength());
resBuf.append('>');
@ -526,22 +591,36 @@ public class PcodeDataTypeManager {
if (sz <= 0) {
sz = size;
}
appendNameIdAttributes(resBuf, origType);
if (sz < 16) {
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "unknown");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", sz);
resBuf.append('>');
}
else {
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "array");
// Build an "opaque" structure with no fields
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "struct");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", sz);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "arraysize", sz);
resBuf.append('>');
resBuf.append(buildTypeRef(new ByteDataType(), 1));
}
}
return resBuf.toString();
}
private void appendNameIdAttributes(StringBuilder resBuf, DataType type) {
if (type instanceof BuiltIn) {
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name",
((BuiltIn) type).getDecompilerDisplayName(displayLanguage));
}
else {
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", type.getName());
long id = progDataTypes.getID(type);
if (id > 0) {
SpecXmlUtils.encodeUnsignedIntegerAttribute(resBuf, "id", id);
}
}
}
/**
* Build an XML document string representing the type information for a data type
*
@ -559,23 +638,6 @@ public class PcodeDataTypeManager {
return resBuf.append("<void/>");
}
resBuf.append("<type");
if ((type instanceof Pointer) || (type instanceof Array) ||
(!(type instanceof FunctionDefinition) && type.getLength() <= 0)) {
SpecXmlUtils.encodeStringAttribute(resBuf, "name", "");
}
else {
if (type instanceof BuiltIn) {
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name",
((BuiltIn) type).getDecompilerDisplayName(displayLanguage));
}
else {
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", type.getName());
long id = progDataTypes.getID(type);
if (id > 0) {
SpecXmlUtils.encodeUnsignedIntegerAttribute(resBuf, "id", id);
}
}
}
resBuf.append(buildTypeInternal(type, size));
resBuf.append("</type>");
return resBuf;

View file

@ -5328,13 +5328,31 @@ define pcodeop maxps;
:MAXPS XmmReg, m128 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x5F; XmmReg ... & m128 { XmmReg = maxps(XmmReg, m128); }
:MAXPS XmmReg1, XmmReg2 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x5F; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = maxps(XmmReg1, XmmReg2); }
define pcodeop maxsd;
:MAXSD XmmReg, m64 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5F; XmmReg ... & m64 { XmmReg = maxsd(XmmReg, m64); }
:MAXSD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5F; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = maxsd(XmmReg1, XmmReg2); }
:MAXSD XmmReg, m64 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5F; XmmReg ... & m64
{
local tmp:8 = m64;
if (tmp f< XmmReg[0,64]) goto inst_next;
XmmReg[0,64] = tmp;
}
define pcodeop maxss;
:MAXSS XmmReg, m32 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x5F; XmmReg ... & m32 { XmmReg = maxss(XmmReg, m32); }
:MAXSS XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x5F; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = maxss(XmmReg1, XmmReg2); }
:MAXSD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5F; xmmmod=3 & XmmReg1 & XmmReg2
{
if (XmmReg2[0,64] f< XmmReg1[0,64]) goto inst_next;
XmmReg1[0,64] = XmmReg2[0,64];
}
:MAXSS XmmReg, m32 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x5F; XmmReg ... & m32
{
local tmp:4 = m32;
if (tmp f< XmmReg[0,32]) goto inst_next;
XmmReg[0,32] = tmp;
}
:MAXSS XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x5F; xmmmod=3 & XmmReg1 & XmmReg2
{
if (XmmReg2[0,32] f< XmmReg1[0,32]) goto inst_next;
XmmReg1[0,32] = XmmReg2[0,32];
}
define pcodeop minpd;
:MINPD XmmReg, m128 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x5D; XmmReg ... & m128 { XmmReg = minpd(XmmReg, m128); }
@ -5344,13 +5362,31 @@ define pcodeop minps;
:MINPS XmmReg, m128 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x5D; XmmReg ... & m128 { XmmReg = minps(XmmReg, m128); }
:MINPS XmmReg1, XmmReg2 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x5D; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = minps(XmmReg1, XmmReg2); }
define pcodeop minsd;
:MINSD XmmReg, m64 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5D; XmmReg ... & m64 { XmmReg = minsd(XmmReg, m64); }
:MINSD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5D; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = minsd(XmmReg1, XmmReg2); }
:MINSD XmmReg, m64 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5D; XmmReg ... & m64
{
local tmp:8 = m64;
if (XmmReg[0,64] f< tmp) goto inst_next;
XmmReg[0,64] = tmp;
}
define pcodeop minss;
:MINSS XmmReg, m32 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x5D; XmmReg ... & m32 { XmmReg = minss(XmmReg, m32); }
:MINSS XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x5D; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = minss(XmmReg1, XmmReg2); }
:MINSD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5D; xmmmod=3 & XmmReg1 & XmmReg2
{
if (XmmReg1[0,64] f< XmmReg2[0,64]) goto inst_next;
XmmReg1[0,64] = XmmReg2[0,64];
}
:MINSS XmmReg, m32 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x5D; XmmReg ... & m32
{
local tmp:4 = m32;
if (XmmReg[0,32] f< tmp) goto inst_next;
XmmReg[0,32] = tmp;
}
:MINSS XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x5D; xmmmod=3 & XmmReg1 & XmmReg2
{
if (XmmReg1[0,32] f< XmmReg2[0,32]) goto inst_next;
XmmReg1[0,32] = XmmReg2[0,32];
}
:MOVAPD XmmReg, m128 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x28; m128 & XmmReg ...
{
@ -5701,15 +5737,11 @@ define pcodeop movmskps;
XmmReg1[0,32] = XmmReg1[0,32] f* XmmReg2[0,32];
}
# special FLOATING POINT bitwise OR
define pcodeop orpd;
:ORPD XmmReg, m128 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x56; XmmReg ... & m128 { XmmReg = orpd(XmmReg, m128); }
:ORPD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x56; xmmmod = 3 & XmmReg1 & XmmReg2 { XmmReg1 = orpd(XmmReg1, XmmReg2); }
:ORPD XmmReg, m128 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x56; XmmReg ... & m128 { XmmReg = XmmReg | m128; }
:ORPD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x56; xmmmod = 3 & XmmReg1 & XmmReg2 { XmmReg1 = XmmReg1 | XmmReg2; }
# special FLOATING POINT bitwise OR
define pcodeop orps;
:ORPS XmmReg, m128 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x56; XmmReg ... & m128 { XmmReg = orps(XmmReg, m128); }
:ORPS XmmReg1, XmmReg2 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x56; xmmmod = 3 & XmmReg1 & XmmReg2 { XmmReg1 = orps(XmmReg1, XmmReg2); }
:ORPS XmmReg, m128 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x56; XmmReg ... & m128 { XmmReg = XmmReg | m128; }
:ORPS XmmReg1, XmmReg2 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x56; xmmmod = 3 & XmmReg1 & XmmReg2 { XmmReg1 = XmmReg1 | XmmReg2; }
# what about these ?????
define pcodeop packsswb;
@ -7603,21 +7635,41 @@ define pcodeop sqrtps;
:SQRTPS XmmReg, m128 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x51; XmmReg ... & m128 { XmmReg = sqrtps(XmmReg, m128); }
:SQRTPS XmmReg1, XmmReg2 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x51; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = sqrtps(XmmReg1, XmmReg2); }
define pcodeop sqrtsd;
:SQRTSD XmmReg, m64 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x51; XmmReg ... & m64 { XmmReg = sqrtsd(XmmReg, m64); }
:SQRTSD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x51; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = sqrtsd(XmmReg1, XmmReg2); }
:SQRTSD XmmReg, m64 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x51; XmmReg ... & m64 { XmmReg[0,64] = sqrt(m64); }
:SQRTSD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x51; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1[0,64] = sqrt(XmmReg2[0,64]); }
define pcodeop sqrtss;
:SQRTSS XmmReg, m32 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x51; XmmReg ... & m32 { XmmReg = sqrtss(XmmReg, m32); }
:SQRTSS XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x51; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = sqrtss(XmmReg1, XmmReg2); }
:SQRTSS XmmReg, m32 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x51; XmmReg ... & m32 { XmmReg[0,32] = sqrt(m32); }
:SQRTSS XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F3) & byte=0x0F; byte=0x51; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1[0,32] = sqrt(XmmReg2[0,32]); }
define pcodeop subpd;
:SUBPD XmmReg, m128 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x5C;XmmReg ... & m128 { XmmReg = subpd(XmmReg, m128); }
:SUBPD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x5C; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = subpd(XmmReg1, XmmReg2); }
:SUBPD XmmReg, m128 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x5C;XmmReg ... & m128
{
local m:16 = m128;
XmmReg[0,64] = XmmReg[0,64] f- m[0,64];
XmmReg[64,64] = XmmReg[64,64] f- m[64,64];
}
define pcodeop subps;
:SUBPS XmmReg, m128 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x5C; XmmReg ... & m128 { XmmReg = subps(XmmReg, m128); }
:SUBPS XmmReg1, XmmReg2 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x5C; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1 = subps(XmmReg1, XmmReg2); }
:SUBPD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x5C; xmmmod=3 & XmmReg1 & XmmReg2
{
XmmReg1[0,64] = XmmReg1[0,64] f- XmmReg2[0,64];
XmmReg1[64,64] = XmmReg1[64,64] f- XmmReg2[64,64];
}
:SUBPS XmmReg, m128 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x5C; XmmReg ... & m128
{
local m:16 = m128;
XmmReg[0,32] = XmmReg[0,32] f- m[0,32];
XmmReg[32,32] = XmmReg[32,32] f- m[32,32];
XmmReg[64,32] = XmmReg[64,32] f- m[64,32];
XmmReg[96,32] = XmmReg[96,32] f- m[96,32];
}
:SUBPS XmmReg1, XmmReg2 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x5C; xmmmod=3 & XmmReg1 & XmmReg2
{
XmmReg1[0,32] = XmmReg1[0,32] f- XmmReg2[0,32];
XmmReg1[32,32] = XmmReg1[32,32] f- XmmReg2[32,32];
XmmReg1[64,32] = XmmReg1[64,32] f- XmmReg2[64,32];
XmmReg1[96,32] = XmmReg1[96,32] f- XmmReg2[96,32];
}
:SUBSD XmmReg, m64 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5C; XmmReg ... & m64 { XmmReg[0,64] = XmmReg[0,64] f- m64; }
:SUBSD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_F2) & byte=0x0F; byte=0x5C; xmmmod=3 & XmmReg1 & XmmReg2 { XmmReg1[0,64] = XmmReg1[0,64] f- XmmReg2[0,64]; }
@ -7723,7 +7775,6 @@ define pcodeop subps;
XmmReg1[32,32] = XmmReg2[0,32]; # XmmReg1 and XmmReg2 could be the same register, preserve Db till last
}
# special FLOATING POINT bitwise XOR
:XORPD XmmReg, m128 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x57; m128 & XmmReg ...
{
local m:16 = m128;
@ -7731,14 +7782,12 @@ define pcodeop subps;
XmmReg[64,64] = ( XmmReg[64,64] ^ m[64,64] );
}
# special FLOATING POINT bitwise XOR
:XORPD XmmReg1, XmmReg2 is vexMode=0 & $(PRE_66) & byte=0x0F; byte=0x57; xmmmod=3 & XmmReg1 & XmmReg2
{
XmmReg1[0,64] = ( XmmReg1[0,64] ^ XmmReg2[0,64] );
XmmReg1[64,64] = ( XmmReg1[64,64] ^ XmmReg2[64,64] );
}
# special FLOATING POINT bitwise XOR
:XORPS XmmReg, m128 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x57; m128 & XmmReg ...
{
local m:16 = m128;
@ -7748,7 +7797,6 @@ define pcodeop subps;
XmmReg[96,32] = ( XmmReg[96,32] ^ m[96,32] );
}
# special FLOATING POINT bitwise XOR
:XORPS XmmReg1, XmmReg2 is vexMode=0 & mandover=0 & byte=0x0F; byte=0x57; xmmmod=3 & XmmReg1 & XmmReg2
{
XmmReg1[0,32] = ( XmmReg1[0,32] ^ XmmReg2[0,32] );