GP-653 added support for user-defined compiler spec extensions

This commit is contained in:
caheckman 2021-04-29 16:17:25 -04:00 committed by ghidra1
parent cce6807470
commit ed82c2cb34
2915 changed files with 333939 additions and 4451 deletions

View file

@ -27,7 +27,7 @@
vector<ArchitectureCapability *> ArchitectureCapability::thelist;
const uint4 ArchitectureCapability::majorversion = 4;
const uint4 ArchitectureCapability::minorversion = 0;
const uint4 ArchitectureCapability::minorversion = 1;
/// This builds a list of just the ArchitectureCapability extensions
void ArchitectureCapability::initialize(void)
@ -1218,6 +1218,26 @@ void Architecture::parseCompilerConfig(DocumentStorage &store)
else if (elname == "inferptrbounds")
parseInferPtrBounds(*iter);
}
el = store.getTag("specextensions"); // Look for any user-defined configuration document
if (el != (const Element *)0) {
const List &userlist(el->getChildren());
for(iter=userlist.begin();iter!=userlist.end();++iter) {
const string &elname( (*iter)->getName() );
if (elname == "prototype")
parseProto(*iter);
else if (elname == "callfixup") {
pcodeinjectlib->restoreXmlInject(archid+" : compiler spec", (*iter)->getAttributeValue("name"),
InjectPayload::CALLFIXUP_TYPE, *iter);
}
else if (elname == "callotherfixup") {
userops.parseCallOtherFixup(*iter,this);
}
else if (elname == "global")
globaltags.push_back(*iter);
}
}
// <global> tags instantiate the base symbol table
// They need to know about all spaces, so it must come
// after parsing of <stackpointer> and <spacebase>

View file

@ -1852,7 +1852,7 @@ int4 ActionReturnRecovery::apply(Funcdata &data)
int4 slot = trial.getSlot();
vn = op->getIn(slot);
if (ancestorReal.execute(op,slot,&trial,false))
if (data.ancestorOpUse(maxancestor,vn,op,trial))
if (data.ancestorOpUse(maxancestor,vn,op,trial,0))
trial.markActive(); // This varnode sees active use as a parameter
count += 1;
}
@ -4305,6 +4305,8 @@ bool ActionInferTypes::propagateGoodEdge(PcodeOp *op,int4 inslot,int4 outslot,Va
case CPUI_MULTIEQUAL:
if ((inslot!=-1)&&(outslot!=-1)) return false; // Must propagate input <-> output
break;
case CPUI_INT_SLESS:
case CPUI_INT_SLESSEQUAL:
case CPUI_INT_LESS:
case CPUI_INT_LESSEQUAL:
if ((inslot==-1)||(outslot==-1)) return false; // Must propagate input <-> input
@ -4400,6 +4402,11 @@ bool ActionInferTypes::propagateTypeEdge(TypeFactory *typegrp,PcodeOp *op,int4 i
else
newtype = alttype;
break;
case CPUI_INT_SLESS:
case CPUI_INT_SLESSEQUAL:
if (alttype->getMetatype() != TYPE_INT) return false; // Only propagate signed things
newtype = alttype;
break;
case CPUI_NEW:
{
Varnode *invn = op->getIn(0);

View file

@ -27,7 +27,7 @@
implement these models provides the quickest inroad into
obtaining an overall understanding of the code.
We list all these fundemental classes here, loosely grouped
We list all these fundamental classes here, loosely grouped
as follows. There is one set of classes that describe the
\e Syntax \e Trees, which are built up from the original p-code,
and transformed during the decompiler's simplification process.
@ -43,18 +43,18 @@
- AddrSpace
- A place within the reverse engineering model where data
can be stored. The typical address spaces are \b ram,
modeling the main databus of a processor, and \b register,
modeling a processors on board registers. Data is stored a
byte at a time at \b offsets within the AddrSpace.
modeling the main databus of a processor, and \b register,
modeling a processor's on board registers. Data is stored a
byte at a time at \b offsets within the AddrSpace.
.
- Address
- An AddrSpace and an offset within the space forms the
Address of the byte at that offset.
Address of the byte at that offset.
.
- Varnode
- A contiguous set of bytes, given by an Address and a size,
encoding a single value in the model. In terms of SSA
syntax tree, a Varnode is also a node in the tree.
encoding a single value in the model. In terms of SSA
syntax tree, a Varnode is also a node in the tree.
.
- SeqNum
- A \e sequence \e number that extends Address for distinguishing PcodeOps
@ -161,14 +161,14 @@
and local scope.
\code
string & getName(); // get name of function
Address & getAddress(); // get Address of function's entry point
int4 numCalls(); // number of subfunctions called by this function
string & getName(); // get name of function
Address & getAddress(); // get Address of function's entry point
int4 numCalls(); // number of subfunctions called by this function
FuncCallSpecs *getCallSpecs(int4 i); // get specs for one of the subfunctions
BlockGraph & getBasicBlocks(); // get the collection of basic blocks
BlockGraph & getBasicBlocks(); // get the collection of basic blocks
iterator beginLoc(Address &); // Search for Varnodes in tree
iterator beginLoc(int4,Address &); // based on the Varnode's address
iterator beginLoc(int4,Address &); // based on the Varnode's address
iterator beginLoc(int4,Address &,Address &,uintm);
iterator beginDef(uint4,Address &); // Search for Varnode based on the
// address of its defining operation
@ -221,14 +221,14 @@
array, and structure qualifiers.
\code
class TypePointer : public Datatype { // pointer to (some other type)
Datatype *getBase(); // get Datatype being pointed to
class TypePointer : public Datatype { // pointer to (some other type)
Datatype *getBase(); // get Datatype being pointed to
};
class TypeArray : public Datatype { // array of (some other type)
Datatype *getBase(); // get Datatype of array element
class TypeArray : public Datatype { // array of (some other type)
Datatype *getBase(); // get Datatype of array element
};
class TypeStruct : public Datatype { // structure with fields of (some other types)
TypeField *getField(int4,int4,int4 *); // get Datatype of a field
class TypeStruct : public Datatype { // structure with fields of (some other types)
TypeField *getField(int4,int4,int4 *); // get Datatype of a field
};
\endcode
@ -237,12 +237,12 @@
This is a container for Datatypes.
\code
Datatype *findByName(string &); // find a Datatype by name
Datatype *getTypeVoid(); // retrieve common types
Datatype *findByName(string &); // find a Datatype by name
Datatype *getTypeVoid(); // retrieve common types
Datatype *getTypeChar();
Datatype *getBase(int4 size,type_metatype);
Datatype *getTypePointer(int4,Datatype *,uint4); // get a pointer to another type
Datatype *getTypeArray(int4,Datatype *); // get an array of another type
Datatype *getTypePointer(int4,Datatype *,uint4); // get a pointer to another type
Datatype *getTypeArray(int4,Datatype *); // get an array of another type
\endcode
\section classhighvariable HighVariable
@ -257,7 +257,7 @@
\code
int4 numInstances(); // get number of different Varnodes associated
// with this variable.
Varnode * getInstance(int4); // get (one of the) Varnodes associated with
Varnode * getInstance(int4); // get (one of the) Varnodes associated with
// this variable.
Datatype * getType(); // get Datatype of this variable
Symbol * getSymbol(); // get Symbol associated with this variable
@ -274,11 +274,11 @@
lives in a scope, has a name, and has a Datatype.
\code
string & getName(); // get the name of the symbol
Datatype * getType(); // get the Datatype of the symbol
Scope * getScope(); // get the scope containing the symbol
string & getName(); // get the name of the symbol
Datatype * getType(); // get the Datatype of the symbol
Scope * getScope(); // get the scope containing the symbol
SymbolEntry * getFirstWholeMap(); // get the (first) SymbolEntry associated
// with this symbol
// with this symbol
\endcode
\section classsymbolentry SymbolEntry
@ -300,16 +300,16 @@
This is a container for symbols.
\code
SymbolEntry *findAddr(Address &,Address &); // find a Symbol by address
SymbolEntry *findAddr(Address &,Address &); // find a Symbol by address
SymbolEntry *findContainer(Address &,int4,Address &); // find containing symbol
Funcdata * findFunction(Address &); // find a function by entry address
Symbol * findByName(string &); // find a Symbol by name
SymbolEntry *queryByAddr(Address &,Address &); // search for symbols across multiple scopes
Funcdata * findFunction(Address &); // find a function by entry address
Symbol * findByName(string &); // find a Symbol by name
SymbolEntry *queryByAddr(Address &,Address &); // search for symbols across multiple scopes
SymbolEntry *queryContainer(Address &,int4,Address &);
Funcdata * queryFunction(Address &);
Scope * discoverScope(Address &,int4,Address &); // discover scope of an address
string & getName(); // get name of scope
Scope * getParent(); // get parent scope
string & getName(); // get name of scope
Scope * getParent(); // get parent scope
\endcode
\section classdatabase Database

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,7 +37,7 @@
the main code workflow.
The library provides its own Register
Transfer Languate (RTL), referred to internally as \b p-code,
Transfer Language (RTL), referred to internally as \b p-code,
which is designed specifically for reverse engineering
applications. The disassembly of processor specific machine-code
languages, and subsequent translation into \b p-code, forms
@ -275,7 +274,7 @@
about the variables it analyzes, as this kind of
information is generally not present in the input
binary. Some information can be gathered about a
variable, based on the instructions it is used in (.i.e
variable, based on the instructions it is used in (i.e.
if it is used in a floating point instruction). Other
information about type might be available from header
files or from the user. Once this is gathered, the
@ -301,7 +300,7 @@
compiler would, but to simplify and normalize for
easier understanding and recognition by human analysts
(and follow on machine processing). Typical examples
of transforms include, copy propagation, constant
of transforms include: copy propagation, constant
propagation, collecting terms, cancellation of
operators and other algebraic simplifications, undoing
multiplication and division optimizations, commuting
@ -373,7 +372,7 @@
Even after the initial merging of variables in phase 1,
there are generally still too many for normal C code. So
the decompiler, does additional, more speculative merging.
the decompiler does additional, more speculative merging.
It first tries to merge the inputs and outputs of copy
operations, and then the inputs and outputs of more
general operations. And finally, merging is attempted on

View file

@ -3174,17 +3174,20 @@ void FuncProto::setOutputLock(bool val)
store->getOutput()->setTypeLock(val);
}
/// This value can be used as a hint as to how much of the return value is important and
/// is used to inform the dead code \e consume algorithm.
/// \param val is the estimated number of bytes or 0
/// \return \b true if the value was changed
/// Provide a hint as to how many bytes of the return value are important.
/// The smallest hint is used to inform the dead-code removal algorithm.
/// \param val is the hint (number of bytes or 0 for all bytes)
/// \return \b true if the smallest hint has changed
bool FuncProto::setReturnBytesConsumed(int4 val)
{
int4 oldVal = returnBytesConsumed;
if (oldVal == 0 || val < oldVal)
if (val == 0)
return false;
if (returnBytesConsumed == 0 || val < returnBytesConsumed) {
returnBytesConsumed = val;
return (oldVal != val);
return true;
}
return false;
}
/// \brief Assuming \b this prototype is locked, calculate the \e extrapop
@ -4744,7 +4747,7 @@ void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
trial.markNoUse();
}
else if (ancestorReal.execute(op,slot,&trial,false)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial))
if (data.ancestorOpUse(maxancestor,vn,op,trial,0))
trial.markActive();
else
trial.markInactive();
@ -4754,7 +4757,7 @@ void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
}
else {
if (ancestorReal.execute(op,slot,&trial,true)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial,0)) {
trial.markActive();
if (trial.hasCondExeEffect())
activeinput.markNeedsFinalCheck();

View file

@ -314,7 +314,7 @@ public:
enum {
unaffected = 1, ///< The sub-function does not change the value at all
killedbycall = 2, ///< The memory is changed and is completely unrelated to its original value
return_address = 3, ///< The memory is being used to pass back a return value from the sub-function
return_address = 3, ///< The memory is being used to store the return address
unknown_effect = 4 ///< An unknown effect (indicates the absence of an EffectRecord)
};
private:

View file

@ -58,6 +58,11 @@ class Funcdata {
baddata_present = 0x800, ///< Set if function flowed into bad data
double_precis_on = 0x1000 ///< Set if we are performing double precision recovery
};
enum {
traverse_actionalt = 1, ///< Alternate path traverses a solid action or \e non-incidental COPY
traverse_indirect = 2, ///< Main path traverses an INDIRECT
traverse_indirectalt = 4 ///< Alternate path traverses an INDIRECT
};
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
@ -116,6 +121,7 @@ class Funcdata {
void nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop);
void nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime);
void nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge);
static bool isAlternatePathValid(const Varnode *vn,uint4 flags);
static bool descendantsOutside(Varnode *vn);
static void saveVarnodeXml(ostream &s,VarnodeLocSet::const_iterator iter,VarnodeLocSet::const_iterator enditer);
static bool checkIndirectUse(Varnode *vn);
@ -363,9 +369,9 @@ public:
HighVariable *findHigh(const string &name) const; ///< Find a high-level variable by name
void mapGlobals(void); ///< Make sure there is a Symbol entry for all global Varnodes
bool checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,const ParamTrial &trial) const;
bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial) const;
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial) const;
bool checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,uint4 flags,const ParamTrial &trial) const;
bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial,uint4 mainFlags) const;
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const;
bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes);
void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset);
bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image

View file

@ -189,7 +189,7 @@ Varnode *Funcdata::newVarnodeSpace(AddrSpace *spc)
{
Datatype *ct = glb->types->getBase(sizeof(spc),TYPE_UNKNOWN);
Varnode *vn = vbank.create(sizeof(spc),glb->createConstFromSpace(spc),ct);
assignHigh(vn);
return vn;
@ -357,7 +357,7 @@ Varnode *Funcdata::setInputVarnode(Varnode *vn)
}
}
}
vn = vbank.setInput(vn);
setVarnodeProperties(vn);
uint4 effecttype = funcp.hasEffect(vn->getAddr(),vn->getSize());
@ -393,7 +393,7 @@ void Funcdata::adjustInputVarnodes(const Address &addr,int4 size)
throw LowlevelError("Cannot properly adjust input varnodes");
inlist.push_back(vn);
}
for(uint4 i=0;i<inlist.size();++i) {
Varnode *vn = inlist[i];
int4 sa = addr.justifiedContain(size,vn->getAddr(),vn->getSize(),false);
@ -405,7 +405,7 @@ void Funcdata::adjustInputVarnodes(const Address &addr,int4 size)
Varnode *newvn = newVarnodeOut(vn->getSize(),vn->getAddr(),subop);
// newvn must not be free in order to give all vn's descendants
opInsertBegin(subop,(BlockBasic *)bblocks.getBlock(0));
totalReplace(vn,newvn);
totalReplace(vn,newvn);
deleteVarnode(vn); // Get rid of old input before creating new input
inlist[i] = newvn;
}
@ -546,7 +546,7 @@ bool Funcdata::fillinReadOnly(Varnode *vn)
vn->clearFlags(Varnode::readonly); // Treat as writeable
return true;
}
if (vn->getSpace()->isBigEndian()) { // Big endian
res = 0;
for(int4 i=0;i<vn->getSize();++i) {
@ -1472,6 +1472,30 @@ void Funcdata::mapGlobals(void)
warningHeader("Globals starting with '_' overlap smaller symbols at the same address");
}
/// \brief Return \b true if the alternate path looks more valid than the main path.
///
/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN.
/// Evaluate which path most likely represents actual parameter/return value passing,
/// based on traversal information about each path.
/// \param vn is the Varnode terminating the \e alternate path
/// \param flags indicates traversals for both paths
/// \return \b true if the alternate path is preferred
bool Funcdata::isAlternatePathValid(const Varnode *vn,uint4 flags)
{
if ((flags & (traverse_indirect | traverse_indirectalt)) == traverse_indirect)
// If main path traversed an INDIRECT but the alternate did not
return true; // Main path traversed INDIRECT, alternate did not
if ((flags & (traverse_indirect | traverse_indirectalt)) == traverse_indirectalt)
return false; // Alternate path traversed INDIRECT, main did not
if ((flags & traverse_actionalt) != 0)
return true; // Alternate path traversed a dedicated COPY
if (vn->loneDescend() == (PcodeOp*)0) return false;
const PcodeOp *op = vn->getDef();
if (op == (PcodeOp*)0) return true;
return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values
}
/// \brief Test for legitimate double use of a parameter trial
///
/// The given trial is a \e putative input to first CALL, but can also trace its data-flow
@ -1480,9 +1504,10 @@ void Funcdata::mapGlobals(void)
/// \param opmatch is the first CALL linked to the trial
/// \param op is the second CALL
/// \param vn is the Varnode parameter for the second CALL
/// \param flags indicates what p-code ops were crossed to reach \e vn
/// \param trial is the given parameter trial
/// \return \b true for a legitimate double use
bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,const ParamTrial &trial) const
bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,uint4 flags,const ParamTrial &trial) const
{
int4 j = op->getSlot(vn);
@ -1508,10 +1533,16 @@ bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const
}
}
}
if (fc->isInputActive()) {
const ParamTrial &curtrial( fc->getActiveInput()->getTrialForInputVarnode(j) );
if ((!curtrial.isChecked())||(!curtrial.isActive())) return true;
if (curtrial.isChecked()) {
if (curtrial.isActive())
return false;
}
else if (isAlternatePathValid(vn,flags))
return false;
return true;
}
return false;
}
@ -1523,28 +1554,31 @@ bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const
/// \param invn is the given Varnode
/// \param opmatch is the putative CALL op using the Varnode for parameter passing
/// \param trial is the parameter trial object associated with the Varnode
/// \param mainFlags are flags describing traversals along the \e main path, from \e invn to \e opmatch
/// \return \b true if the Varnode seems only to be used as parameter to \b opmatch
bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial) const
bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial,uint4 mainFlags) const
{
vector<const Varnode *> varlist;
vector<TraverseNode> varlist;
list<PcodeOp *>::const_iterator iter;
const Varnode *vn,*subvn;
const PcodeOp *op;
int4 i;
bool res = true;
varlist.reserve(64);
invn->setMark(); // Marks prevent infinite loops
varlist.push_back(invn);
i = 0;
while(i < varlist.size()) {
vn = varlist[i++];
varlist.emplace_back(invn,mainFlags);
for(i=0;i < varlist.size();++i) {
vn = varlist[i].vn;
uint4 baseFlags = varlist[i].flags;
for(iter=vn->descend.begin();iter!=vn->descend.end();++iter) {
op = *iter;
if (op == opmatch) {
if (op->getIn(trial.getSlot())==vn) continue;
}
uint4 curFlags = baseFlags;
switch(op->code()) {
case CPUI_BRANCH: // These ops define a USE of a variable
case CPUI_CBRANCH:
@ -1555,17 +1589,39 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
break;
case CPUI_CALL:
case CPUI_CALLIND:
if (checkCallDoubleUse(opmatch,op,vn,trial)) continue;
if (checkCallDoubleUse(opmatch,op,vn,curFlags,trial)) continue;
res = false;
break;
case CPUI_INDIRECT:
curFlags |= Funcdata::traverse_indirectalt;
break;
case CPUI_COPY:
if ((op->getOut()->getSpace()->getType()!=IPTR_INTERNAL)&&!op->isIncidentalCopy()&&!vn->isIncidentalCopy()) {
curFlags |= Funcdata::traverse_actionalt;
}
break;
case CPUI_RETURN:
if (opmatch->code()==CPUI_RETURN) { // Are we in a different return
if (op->getIn(trial.getSlot())==vn) // But at the same slot
continue;
}
else if (activeoutput != (ParamActive *)0) { // Are we in the middle of analyzing returns
if (op->getIn(0) != vn) { // Unless we hold actual return value
if (!isAlternatePathValid(vn,curFlags))
continue; // Don't consider this a "use"
}
}
res = false;
break;
case CPUI_MULTIEQUAL:
case CPUI_PIECE:
case CPUI_SUBPIECE:
case CPUI_INT_SEXT:
case CPUI_INT_ZEXT:
case CPUI_CAST:
break;
default:
curFlags |= Funcdata::traverse_actionalt;
break;
}
if (!res) break;
@ -1576,7 +1632,7 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
break;
}
if (!subvn->isMark()) {
varlist.push_back(subvn);
varlist.emplace_back(subvn,curFlags);
subvn->setMark();
}
}
@ -1584,7 +1640,7 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
if (!res) break;
}
for(i=0;i<varlist.size();++i)
varlist[i]->clearMark();
varlist[i].vn->clearMark();
return res;
}
@ -1596,9 +1652,10 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
/// \param invn is the given trial Varnode to test
/// \param op is the given CALL or RETURN
/// \param trial is the associated parameter trial object
/// \param mainFlags describes traversals along the path from \e invn to \e op
/// \return \b true if the Varnode is only used for the CALL/RETURN
bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
const PcodeOp *op,ParamTrial &trial) const
const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const
{
int4 i;
@ -1610,9 +1667,9 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
if (!invn->isTypeLock()) return false;
// If the input is typelocked
// this is as good as being written
return onlyOpUse(invn,op,trial); // Test if varnode is only used in op
return onlyOpUse(invn,op,trial,mainFlags); // Test if varnode is only used in op
}
const PcodeOp *def = invn->getDef();
switch(def->code()) {
case CPUI_INDIRECT:
@ -1620,7 +1677,7 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
// as an "only use"
if (def->isIndirectCreation())
return false;
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial);
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags | Funcdata::traverse_indirect);
case CPUI_MULTIEQUAL:
// Check if there is any ancestor whose only
// use is in this op
@ -1628,7 +1685,7 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
def->setMark(); // Mark that this MULTIEQUAL is on the path
// Note: onlyOpUse is using Varnode::setMark
for(i=0;i<def->numInput();++i) {
if (ancestorOpUse(maxlevel-1,def->getIn(i),op,trial)) {
if (ancestorOpUse(maxlevel-1,def->getIn(i),op,trial, mainFlags)) {
def->clearMark();
return true;
}
@ -1637,13 +1694,12 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
return false;
case CPUI_COPY:
if ((invn->getSpace()->getType()==IPTR_INTERNAL)||def->isIncidentalCopy()||def->getIn(0)->isIncidentalCopy()) {
if (!ancestorOpUse(maxlevel-1,def->getIn(0),op,trial)) return false;
return true;
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags);
}
break;
case CPUI_PIECE:
// Concatenation tends to be artificial, so recurse through the least significant part
return ancestorOpUse(maxlevel-1,def->getIn(1),op,trial);
return ancestorOpUse(maxlevel-1,def->getIn(1),op,trial,mainFlags);
case CPUI_SUBPIECE:
// This is a rather kludgy way to get around where a DIV (or other similar) instruction
// causes a register that looks like the high precision piece of the function return
@ -1664,7 +1720,7 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
break;
}
// This varnode must be top ancestor at this point
return onlyOpUse(invn,op,trial); // Test if varnode is only used in op
return onlyOpUse(invn,op,trial,mainFlags); // Test if varnode is only used in op
}
/// \return \b true if there are two input flows, one of which is a normal \e solid flow

View file

@ -284,7 +284,7 @@ void ArchitectureGhidra::buildSpecFile(DocumentStorage &store)
istringstream cstream(cspecxml);
doc = store.parseDocument(cstream);
store.registerTag(doc->getRoot());
istringstream tstream(tspecxml);
doc = store.parseDocument(tstream);
store.registerTag(doc->getRoot());
@ -293,10 +293,10 @@ void ArchitectureGhidra::buildSpecFile(DocumentStorage &store)
doc = store.parseDocument(corestream);
store.registerTag(doc->getRoot());
pspecxml = ""; // Strings aren't used again free memory
cspecxml = "";
tspecxml = "";
corespecxml = "";
pspecxml.clear(); // Strings aren't used again free memory
cspecxml.clear();
tspecxml.clear();
corespecxml.clear();
}
void ArchitectureGhidra::postSpecFile(void)

View file

@ -82,7 +82,8 @@ class ArchitectureGhidra : public Architecture {
virtual void postSpecFile(void);
virtual void resolveArchitecture(void);
public:
ArchitectureGhidra(const string &pspec,const string &cspec,const string &tspec,const string &corespec,istream &i,ostream &o);
ArchitectureGhidra(const string &pspec,const string &cspec,const string &tspec,const string &corespec,
istream &i,ostream &o);
const string &getWarnings(void) const { return warnings; } ///< Get warnings produced by the last decompilation
void clearWarnings(void) { warnings.clear(); } ///< Clear warnings
Document *getRegister(const string &regname); ///< Retrieve a register description given a name

View file

@ -174,6 +174,10 @@ void RegisterProgram::rawAction(void)
}
}
ghidra = new ArchitectureGhidra(pspec,cspec,tspec,corespec,sin,sout);
pspec.clear();
cspec.clear();
tspec.clear();
corespec.clear();
DocumentStorage store; // temp storage of initialization xml docs
ghidra->init(store);

View file

@ -130,6 +130,45 @@ FlowBlock *PriorityQueue::extract(void)
return res;
}
/// Initialize heritage state information for a particular address space
/// \param spc is the address space
HeritageInfo::HeritageInfo(AddrSpace *spc)
{
if (spc == (AddrSpace *)0) {
space = (AddrSpace *)0;
delay = 0;
deadcodedelay = 0;
hasCallPlaceholders = false;
}
else if (!spc->isHeritaged()) {
space = (AddrSpace *)0;
delay = spc->getDelay();
deadcodedelay = spc->getDeadcodeDelay();
hasCallPlaceholders = false;
}
else {
space = spc;
delay = spc->getDelay();
deadcodedelay = spc->getDeadcodeDelay();
hasCallPlaceholders = (spc->getType() == IPTR_SPACEBASE);
}
deadremoved = 0;
warningissued = false;
loadGuardSearch = false;
}
void HeritageInfo::reset(void)
{
// Leave any override intact: deadcodedelay = delay;
deadremoved = 0;
if (space != (AddrSpace *)0)
hasCallPlaceholders = (space->getType() == IPTR_SPACEBASE);
warningissued = false;
loadGuardSearch = false;
}
/// Instantiate the heritage manager for a particular function.
/// \param data is the function
Heritage::Heritage(Funcdata *data)
@ -1180,16 +1219,10 @@ void Heritage::guardCalls(uint4 flags,const Address &addr,int4 size,vector<Varno
uintb off = addr.getOffset();
bool tryregister = true;
if (spc->getType() == IPTR_SPACEBASE) {
if (fc->getStackPlaceholderSlot() < 0) { // Any stack resolution is complete (or never started)
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
else
tryregister = false; // Do not attempt to register this stack loc as a trial
}
else { // Stack has not been resolved, so we need to abort
fc->abortSpacebaseRelative(*fd);
tryregister = false;
}
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
else
tryregister = false; // Do not attempt to register this stack loc as a trial
}
Address transAddr(spc,off); // Address relative to callee's stack
if (tryregister) {
@ -1695,6 +1728,19 @@ static void verify_dfs(const vector<FlowBlock *> &list,vector<vector<FlowBlock *
}
#endif
/// Assuming we are just about to do heritage on an address space,
/// clear any placeholder LOADs associated with it on CALLs.
/// \param info is state for the specific address space
void Heritage::clearStackPlaceholders(HeritageInfo *info)
{
int4 numCalls = fd->numCalls();
for(int4 i=0;i<numCalls;++i) {
fd->getCallSpecs(i)->abortSpacebaseRelative(*fd);
}
info->hasCallPlaceholders = false; // Mark that clear has taken place
}
/// \brief Perform one level of Varnode splitting to match a JoinRecord
///
/// Split all the pieces in \b lastcombo, putting them into \b nextlev in order,
@ -2288,16 +2334,9 @@ void Heritage::buildInfoList(void)
{
if (!infolist.empty()) return;
const AddrSpaceManager *manage = fd->getArch();
infolist.resize(manage->numSpaces());
for(int4 i=0;i<manage->numSpaces();++i) {
AddrSpace *spc = manage->getSpace(i);
if (spc == (AddrSpace *)0)
infolist[i].set((AddrSpace *)0,0,0);
else if (!spc->isHeritaged())
infolist[i].set((AddrSpace *)0,spc->getDelay(),spc->getDeadcodeDelay());
else
infolist[i].set(spc,spc->getDelay(),spc->getDeadcodeDelay());
}
infolist.reserve(manage->numSpaces());
for(int4 i=0;i<manage->numSpaces();++i)
infolist.emplace_back(manage->getSpace(i));
}
/// From any address space that is active for this pass, free Varnodes are collected
@ -2328,6 +2367,9 @@ void Heritage::heritage(void)
info = &infolist[i];
if (!info->isHeritaged()) continue;
if (pass < info->delay) continue; // It is too soon to heritage this space
if (info->hasCallPlaceholders)
clearStackPlaceholders(info);
if (!info->loadGuardSearch) {
info->loadGuardSearch = true;
if (discoverIndexedStackPointers(info->space,freeStores,true)) {

View file

@ -90,11 +90,11 @@ class HeritageInfo {
int4 deadremoved; ///< >0 if Varnodes in this space have been eliminated
bool loadGuardSearch; ///< \b true if the search for LOAD ops to guard has been performed
bool warningissued; ///< \b true if warning issued previously
void set(AddrSpace *spc,int4 dl,int4 dcdl) {
space=spc; delay=dl; deadcodedelay=dcdl; deadremoved=0; warningissued=false; loadGuardSearch = false; } ///< Set all fields
bool hasCallPlaceholders; ///< \b true for the \e stack space, if stack placeholders have not been removed
bool isHeritaged(void) const { return (space != (AddrSpace *)0); } ///< Return \b true if heritage is performed on this space
void reset(void) {
deadremoved = 0; deadcodedelay = delay; warningissued = false; loadGuardSearch = false; } ///< Reset
void reset(void); ///< Reset the state
public:
HeritageInfo(AddrSpace *spc); ///< Constructor
};
/// \brief Description of a LOAD operation that needs to be guarded
@ -222,6 +222,7 @@ class Heritage {
/// \brief Get the heritage status for the given address space
const HeritageInfo *getInfo(AddrSpace *spc) const { return &(infolist[spc->getIndex()]); }
void clearStackPlaceholders(HeritageInfo *info); ///< Clear remaining stack placeholder LOADs on any call
void splitJoinLevel(vector<Varnode *> &lastcombo,vector<Varnode *> &nextlev,JoinRecord *joinrec);
void splitJoinRead(Varnode *vn,JoinRecord *joinrec);
void splitJoinWrite(Varnode *vn,JoinRecord *joinrec);

View file

@ -293,6 +293,23 @@ int4 PcodeInjectLibrarySleigh::registerDynamicInject(InjectPayload *payload)
return id;
}
/// \brief Force a payload to be dynamic for debug purposes
///
/// Debug information may include inject information for payloads that aren't dynamic.
/// We substitute a dynamic payload so that analysis uses the debug info to inject, rather
/// than the hard-coded payload information.
/// \param injectid is the id of the payload to treat dynamic
/// \return the new dynamic payload object
InjectPayloadDynamic *PcodeInjectLibrarySleigh::forceDebugDynamic(int4 injectid)
{
InjectPayload *oldPayload = injection[injectid];
InjectPayloadDynamic *newPayload = new InjectPayloadDynamic(glb,oldPayload->getName(),oldPayload->getType());
delete oldPayload;
injection[injectid] = newPayload;
return newPayload;
}
void PcodeInjectLibrarySleigh::parseInject(InjectPayload *payload)
{
@ -399,9 +416,10 @@ void PcodeInjectLibrarySleigh::restoreDebug(const Element *el)
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> type;
int4 id = getPayloadId(type,name);
InjectPayloadDynamic *payload = (InjectPayloadDynamic *)getPayload(id);
if (payload->getSource() != "dynamic")
throw LowlevelError("Mismatch with debug inject XML");
InjectPayloadDynamic *payload = dynamic_cast<InjectPayloadDynamic *>(getPayload(id));
if (payload == (InjectPayloadDynamic *)0) {
payload = forceDebugDynamic(id);
}
payload->restoreEntry(subel);
}
}

View file

@ -91,6 +91,7 @@ class PcodeInjectLibrarySleigh : public PcodeInjectLibrary {
vector<OpBehavior *> inst;
InjectContextSleigh contextCache;
int4 registerDynamicInject(InjectPayload *payload);
InjectPayloadDynamic *forceDebugDynamic(int4 injectid);
void parseInject(InjectPayload *payload);
protected:
virtual int4 allocateInject(const string &sourceName,const string &name,int4 type);

View file

@ -216,10 +216,10 @@ void SegmentOp::restoreXml(const Element *el)
throw LowlevelError("Bad segment pattern tag: "+subel->getName());
}
if (injectId < 0)
throw LowlevelError("Missing <execute> child in <segmentop> tag");
throw LowlevelError("Missing <pcode> child in <segmentop> tag");
InjectPayload *payload = glb->pcodeinjectlib->getPayload(injectId);
if (payload->sizeOutput() != 1)
throw LowlevelError("<execute> child of <segmentop> tag must declare one <output>");
throw LowlevelError("<pcode> child of <segmentop> tag must declare one <output>");
if (payload->sizeInput() == 1) {
innerinsize = payload->getInput(0).getSize();
}
@ -228,7 +228,7 @@ void SegmentOp::restoreXml(const Element *el)
innerinsize = payload->getInput(1).getSize();
}
else
throw LowlevelError("<execute> child of <segmentop> tag must declare one or two <input> tags");
throw LowlevelError("<pcode> child of <segmentop> tag must declare one or two <input> tags");
}
/// \param g is the Architecture owning this set of jump assist scripts

View file

@ -376,6 +376,13 @@ public:
#endif
};
/// \brief Node for a forward traversal of a Varnode expression
struct TraverseNode {
const Varnode *vn; ///< Varnode at the point of traversal
uint4 flags; ///< Flags associated with the node
TraverseNode(const Varnode *v,uint4 f) { vn = v; flags = f; }
};
bool contiguous_test(Varnode *vn1,Varnode *vn2); ///< Test if Varnodes are pieces of a whole
Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1,
Varnode *vn2); ///< Retrieve the whole Varnode given pieces

View file

@ -127,6 +127,12 @@ void XmlArchitecture::restoreXml(DocumentStorage &store)
++iter;
}
}
if (iter != list.end()) {
if ((*iter)->getName() == "specextensions") {
store.registerTag(*iter);
++iter;
}
}
if (iter!=list.end()) {
if ((*iter)->getName() == "coretypes") {
store.registerTag(*iter);