mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/caheckman_recentBranches'
This commit is contained in:
commit
875eed4c3b
60 changed files with 2939 additions and 1043 deletions
|
@ -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. </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. </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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
392
Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc
Normal file
392
Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc
Normal 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;
|
||||
}
|
83
Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.hh
Normal file
83
Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.hh
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
//==================================================================================================
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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] );
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue