mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/GP-513_JoinedReturnValue'
This commit is contained in:
commit
b3e90569fd
12 changed files with 269 additions and 66 deletions
|
@ -27,7 +27,7 @@
|
||||||
vector<ArchitectureCapability *> ArchitectureCapability::thelist;
|
vector<ArchitectureCapability *> ArchitectureCapability::thelist;
|
||||||
|
|
||||||
const uint4 ArchitectureCapability::majorversion = 4;
|
const uint4 ArchitectureCapability::majorversion = 4;
|
||||||
const uint4 ArchitectureCapability::minorversion = 0;
|
const uint4 ArchitectureCapability::minorversion = 1;
|
||||||
|
|
||||||
/// This builds a list of just the ArchitectureCapability extensions
|
/// This builds a list of just the ArchitectureCapability extensions
|
||||||
void ArchitectureCapability::initialize(void)
|
void ArchitectureCapability::initialize(void)
|
||||||
|
|
|
@ -1852,7 +1852,7 @@ int4 ActionReturnRecovery::apply(Funcdata &data)
|
||||||
int4 slot = trial.getSlot();
|
int4 slot = trial.getSlot();
|
||||||
vn = op->getIn(slot);
|
vn = op->getIn(slot);
|
||||||
if (ancestorReal.execute(op,slot,&trial,false))
|
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
|
trial.markActive(); // This varnode sees active use as a parameter
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4747,7 +4747,7 @@ void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
|
||||||
trial.markNoUse();
|
trial.markNoUse();
|
||||||
}
|
}
|
||||||
else if (ancestorReal.execute(op,slot,&trial,false)) {
|
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();
|
trial.markActive();
|
||||||
else
|
else
|
||||||
trial.markInactive();
|
trial.markInactive();
|
||||||
|
@ -4757,7 +4757,7 @@ void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (ancestorReal.execute(op,slot,&trial,true)) {
|
if (ancestorReal.execute(op,slot,&trial,true)) {
|
||||||
if (data.ancestorOpUse(maxancestor,vn,op,trial)) {
|
if (data.ancestorOpUse(maxancestor,vn,op,trial,0)) {
|
||||||
trial.markActive();
|
trial.markActive();
|
||||||
if (trial.hasCondExeEffect())
|
if (trial.hasCondExeEffect())
|
||||||
activeinput.markNeedsFinalCheck();
|
activeinput.markNeedsFinalCheck();
|
||||||
|
|
|
@ -58,6 +58,11 @@ class Funcdata {
|
||||||
baddata_present = 0x800, ///< Set if function flowed into bad data
|
baddata_present = 0x800, ///< Set if function flowed into bad data
|
||||||
double_precis_on = 0x1000 ///< Set if we are performing double precision recovery
|
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 flags; ///< Boolean properties associated with \b this function
|
||||||
uint4 clean_up_index; ///< Creation index of first Varnode created after start of cleanup
|
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 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 nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop);
|
||||||
void nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime);
|
void nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime);
|
||||||
void nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge);
|
void nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge);
|
||||||
|
static bool isAlternatePathValid(const Varnode *vn,uint4 flags);
|
||||||
static bool descendantsOutside(Varnode *vn);
|
static bool descendantsOutside(Varnode *vn);
|
||||||
static void saveVarnodeXml(ostream &s,VarnodeLocSet::const_iterator iter,VarnodeLocSet::const_iterator enditer);
|
static void saveVarnodeXml(ostream &s,VarnodeLocSet::const_iterator iter,VarnodeLocSet::const_iterator enditer);
|
||||||
static bool checkIndirectUse(Varnode *vn);
|
static bool checkIndirectUse(Varnode *vn);
|
||||||
|
@ -363,9 +369,9 @@ public:
|
||||||
|
|
||||||
HighVariable *findHigh(const string &name) const; ///< Find a high-level variable by name
|
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
|
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 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) 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) const;
|
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const;
|
||||||
bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes);
|
bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes);
|
||||||
void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset);
|
void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset);
|
||||||
bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image
|
bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image
|
||||||
|
|
|
@ -189,7 +189,7 @@ Varnode *Funcdata::newVarnodeSpace(AddrSpace *spc)
|
||||||
|
|
||||||
{
|
{
|
||||||
Datatype *ct = glb->types->getBase(sizeof(spc),TYPE_UNKNOWN);
|
Datatype *ct = glb->types->getBase(sizeof(spc),TYPE_UNKNOWN);
|
||||||
|
|
||||||
Varnode *vn = vbank.create(sizeof(spc),glb->createConstFromSpace(spc),ct);
|
Varnode *vn = vbank.create(sizeof(spc),glb->createConstFromSpace(spc),ct);
|
||||||
assignHigh(vn);
|
assignHigh(vn);
|
||||||
return vn;
|
return vn;
|
||||||
|
@ -357,7 +357,7 @@ Varnode *Funcdata::setInputVarnode(Varnode *vn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vn = vbank.setInput(vn);
|
vn = vbank.setInput(vn);
|
||||||
setVarnodeProperties(vn);
|
setVarnodeProperties(vn);
|
||||||
uint4 effecttype = funcp.hasEffect(vn->getAddr(),vn->getSize());
|
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");
|
throw LowlevelError("Cannot properly adjust input varnodes");
|
||||||
inlist.push_back(vn);
|
inlist.push_back(vn);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(uint4 i=0;i<inlist.size();++i) {
|
for(uint4 i=0;i<inlist.size();++i) {
|
||||||
Varnode *vn = inlist[i];
|
Varnode *vn = inlist[i];
|
||||||
int4 sa = addr.justifiedContain(size,vn->getAddr(),vn->getSize(),false);
|
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);
|
Varnode *newvn = newVarnodeOut(vn->getSize(),vn->getAddr(),subop);
|
||||||
// newvn must not be free in order to give all vn's descendants
|
// newvn must not be free in order to give all vn's descendants
|
||||||
opInsertBegin(subop,(BlockBasic *)bblocks.getBlock(0));
|
opInsertBegin(subop,(BlockBasic *)bblocks.getBlock(0));
|
||||||
totalReplace(vn,newvn);
|
totalReplace(vn,newvn);
|
||||||
deleteVarnode(vn); // Get rid of old input before creating new input
|
deleteVarnode(vn); // Get rid of old input before creating new input
|
||||||
inlist[i] = newvn;
|
inlist[i] = newvn;
|
||||||
}
|
}
|
||||||
|
@ -546,7 +546,7 @@ bool Funcdata::fillinReadOnly(Varnode *vn)
|
||||||
vn->clearFlags(Varnode::readonly); // Treat as writeable
|
vn->clearFlags(Varnode::readonly); // Treat as writeable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vn->getSpace()->isBigEndian()) { // Big endian
|
if (vn->getSpace()->isBigEndian()) { // Big endian
|
||||||
res = 0;
|
res = 0;
|
||||||
for(int4 i=0;i<vn->getSize();++i) {
|
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");
|
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
|
/// \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
|
/// 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 opmatch is the first CALL linked to the trial
|
||||||
/// \param op is the second CALL
|
/// \param op is the second CALL
|
||||||
/// \param vn is the Varnode parameter for 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
|
/// \param trial is the given parameter trial
|
||||||
/// \return \b true for a legitimate double use
|
/// \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);
|
int4 j = op->getSlot(vn);
|
||||||
|
@ -1508,10 +1533,16 @@ bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fc->isInputActive()) {
|
if (fc->isInputActive()) {
|
||||||
const ParamTrial &curtrial( fc->getActiveInput()->getTrialForInputVarnode(j) );
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1523,28 +1554,31 @@ bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const
|
||||||
/// \param invn is the given Varnode
|
/// \param invn is the given Varnode
|
||||||
/// \param opmatch is the putative CALL op using the Varnode for parameter passing
|
/// \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 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
|
/// \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;
|
list<PcodeOp *>::const_iterator iter;
|
||||||
const Varnode *vn,*subvn;
|
const Varnode *vn,*subvn;
|
||||||
const PcodeOp *op;
|
const PcodeOp *op;
|
||||||
int4 i;
|
int4 i;
|
||||||
bool res = true;
|
bool res = true;
|
||||||
|
|
||||||
|
varlist.reserve(64);
|
||||||
invn->setMark(); // Marks prevent infinite loops
|
invn->setMark(); // Marks prevent infinite loops
|
||||||
varlist.push_back(invn);
|
varlist.emplace_back(invn,mainFlags);
|
||||||
|
|
||||||
i = 0;
|
for(i=0;i < varlist.size();++i) {
|
||||||
while(i < varlist.size()) {
|
vn = varlist[i].vn;
|
||||||
vn = varlist[i++];
|
uint4 baseFlags = varlist[i].flags;
|
||||||
for(iter=vn->descend.begin();iter!=vn->descend.end();++iter) {
|
for(iter=vn->descend.begin();iter!=vn->descend.end();++iter) {
|
||||||
op = *iter;
|
op = *iter;
|
||||||
if (op == opmatch) {
|
if (op == opmatch) {
|
||||||
if (op->getIn(trial.getSlot())==vn) continue;
|
if (op->getIn(trial.getSlot())==vn) continue;
|
||||||
}
|
}
|
||||||
|
uint4 curFlags = baseFlags;
|
||||||
switch(op->code()) {
|
switch(op->code()) {
|
||||||
case CPUI_BRANCH: // These ops define a USE of a variable
|
case CPUI_BRANCH: // These ops define a USE of a variable
|
||||||
case CPUI_CBRANCH:
|
case CPUI_CBRANCH:
|
||||||
|
@ -1555,17 +1589,39 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
|
||||||
break;
|
break;
|
||||||
case CPUI_CALL:
|
case CPUI_CALL:
|
||||||
case CPUI_CALLIND:
|
case CPUI_CALLIND:
|
||||||
if (checkCallDoubleUse(opmatch,op,vn,trial)) continue;
|
if (checkCallDoubleUse(opmatch,op,vn,curFlags,trial)) continue;
|
||||||
res = false;
|
res = false;
|
||||||
break;
|
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:
|
case CPUI_RETURN:
|
||||||
if (opmatch->code()==CPUI_RETURN) { // Are we in a different return
|
if (opmatch->code()==CPUI_RETURN) { // Are we in a different return
|
||||||
if (op->getIn(trial.getSlot())==vn) // But at the same slot
|
if (op->getIn(trial.getSlot())==vn) // But at the same slot
|
||||||
continue;
|
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;
|
res = false;
|
||||||
break;
|
break;
|
||||||
|
case CPUI_MULTIEQUAL:
|
||||||
|
case CPUI_PIECE:
|
||||||
|
case CPUI_SUBPIECE:
|
||||||
|
case CPUI_INT_SEXT:
|
||||||
|
case CPUI_INT_ZEXT:
|
||||||
|
case CPUI_CAST:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
curFlags |= Funcdata::traverse_actionalt;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!res) break;
|
if (!res) break;
|
||||||
|
@ -1576,7 +1632,7 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!subvn->isMark()) {
|
if (!subvn->isMark()) {
|
||||||
varlist.push_back(subvn);
|
varlist.emplace_back(subvn,curFlags);
|
||||||
subvn->setMark();
|
subvn->setMark();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1584,7 +1640,7 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
|
||||||
if (!res) break;
|
if (!res) break;
|
||||||
}
|
}
|
||||||
for(i=0;i<varlist.size();++i)
|
for(i=0;i<varlist.size();++i)
|
||||||
varlist[i]->clearMark();
|
varlist[i].vn->clearMark();
|
||||||
return res;
|
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 invn is the given trial Varnode to test
|
||||||
/// \param op is the given CALL or RETURN
|
/// \param op is the given CALL or RETURN
|
||||||
/// \param trial is the associated parameter trial object
|
/// \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
|
/// \return \b true if the Varnode is only used for the CALL/RETURN
|
||||||
bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
||||||
const PcodeOp *op,ParamTrial &trial) const
|
const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const
|
||||||
|
|
||||||
{
|
{
|
||||||
int4 i;
|
int4 i;
|
||||||
|
@ -1610,9 +1667,9 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
||||||
if (!invn->isTypeLock()) return false;
|
if (!invn->isTypeLock()) return false;
|
||||||
// If the input is typelocked
|
// If the input is typelocked
|
||||||
// this is as good as being written
|
// 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();
|
const PcodeOp *def = invn->getDef();
|
||||||
switch(def->code()) {
|
switch(def->code()) {
|
||||||
case CPUI_INDIRECT:
|
case CPUI_INDIRECT:
|
||||||
|
@ -1620,7 +1677,7 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
||||||
// as an "only use"
|
// as an "only use"
|
||||||
if (def->isIndirectCreation())
|
if (def->isIndirectCreation())
|
||||||
return false;
|
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:
|
case CPUI_MULTIEQUAL:
|
||||||
// Check if there is any ancestor whose only
|
// Check if there is any ancestor whose only
|
||||||
// use is in this op
|
// 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
|
def->setMark(); // Mark that this MULTIEQUAL is on the path
|
||||||
// Note: onlyOpUse is using Varnode::setMark
|
// Note: onlyOpUse is using Varnode::setMark
|
||||||
for(i=0;i<def->numInput();++i) {
|
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();
|
def->clearMark();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1637,13 +1694,12 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
||||||
return false;
|
return false;
|
||||||
case CPUI_COPY:
|
case CPUI_COPY:
|
||||||
if ((invn->getSpace()->getType()==IPTR_INTERNAL)||def->isIncidentalCopy()||def->getIn(0)->isIncidentalCopy()) {
|
if ((invn->getSpace()->getType()==IPTR_INTERNAL)||def->isIncidentalCopy()||def->getIn(0)->isIncidentalCopy()) {
|
||||||
if (!ancestorOpUse(maxlevel-1,def->getIn(0),op,trial)) return false;
|
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CPUI_PIECE:
|
case CPUI_PIECE:
|
||||||
// Concatenation tends to be artificial, so recurse through the least significant part
|
// 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:
|
case CPUI_SUBPIECE:
|
||||||
// This is a rather kludgy way to get around where a DIV (or other similar) instruction
|
// 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
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
// This varnode must be top ancestor at this point
|
// 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
|
/// \return \b true if there are two input flows, one of which is a normal \e solid flow
|
||||||
|
|
|
@ -130,6 +130,45 @@ FlowBlock *PriorityQueue::extract(void)
|
||||||
return res;
|
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.
|
/// Instantiate the heritage manager for a particular function.
|
||||||
/// \param data is the function
|
/// \param data is the function
|
||||||
Heritage::Heritage(Funcdata *data)
|
Heritage::Heritage(Funcdata *data)
|
||||||
|
@ -1180,16 +1219,10 @@ void Heritage::guardCalls(uint4 flags,const Address &addr,int4 size,vector<Varno
|
||||||
uintb off = addr.getOffset();
|
uintb off = addr.getOffset();
|
||||||
bool tryregister = true;
|
bool tryregister = true;
|
||||||
if (spc->getType() == IPTR_SPACEBASE) {
|
if (spc->getType() == IPTR_SPACEBASE) {
|
||||||
if (fc->getStackPlaceholderSlot() < 0) { // Any stack resolution is complete (or never started)
|
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
|
||||||
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
|
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
|
||||||
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
|
else
|
||||||
else
|
tryregister = false; // Do not attempt to register this stack loc as a trial
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Address transAddr(spc,off); // Address relative to callee's stack
|
Address transAddr(spc,off); // Address relative to callee's stack
|
||||||
if (tryregister) {
|
if (tryregister) {
|
||||||
|
@ -1695,6 +1728,19 @@ static void verify_dfs(const vector<FlowBlock *> &list,vector<vector<FlowBlock *
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
/// \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,
|
/// 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;
|
if (!infolist.empty()) return;
|
||||||
const AddrSpaceManager *manage = fd->getArch();
|
const AddrSpaceManager *manage = fd->getArch();
|
||||||
infolist.resize(manage->numSpaces());
|
infolist.reserve(manage->numSpaces());
|
||||||
for(int4 i=0;i<manage->numSpaces();++i) {
|
for(int4 i=0;i<manage->numSpaces();++i)
|
||||||
AddrSpace *spc = manage->getSpace(i);
|
infolist.emplace_back(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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// From any address space that is active for this pass, free Varnodes are collected
|
/// 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];
|
info = &infolist[i];
|
||||||
if (!info->isHeritaged()) continue;
|
if (!info->isHeritaged()) continue;
|
||||||
if (pass < info->delay) continue; // It is too soon to heritage this space
|
if (pass < info->delay) continue; // It is too soon to heritage this space
|
||||||
|
if (info->hasCallPlaceholders)
|
||||||
|
clearStackPlaceholders(info);
|
||||||
|
|
||||||
if (!info->loadGuardSearch) {
|
if (!info->loadGuardSearch) {
|
||||||
info->loadGuardSearch = true;
|
info->loadGuardSearch = true;
|
||||||
if (discoverIndexedStackPointers(info->space,freeStores,true)) {
|
if (discoverIndexedStackPointers(info->space,freeStores,true)) {
|
||||||
|
|
|
@ -90,11 +90,11 @@ class HeritageInfo {
|
||||||
int4 deadremoved; ///< >0 if Varnodes in this space have been eliminated
|
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 loadGuardSearch; ///< \b true if the search for LOAD ops to guard has been performed
|
||||||
bool warningissued; ///< \b true if warning issued previously
|
bool warningissued; ///< \b true if warning issued previously
|
||||||
void set(AddrSpace *spc,int4 dl,int4 dcdl) {
|
bool hasCallPlaceholders; ///< \b true for the \e stack space, if stack placeholders have not been removed
|
||||||
space=spc; delay=dl; deadcodedelay=dcdl; deadremoved=0; warningissued=false; loadGuardSearch = false; } ///< Set all fields
|
|
||||||
bool isHeritaged(void) const { return (space != (AddrSpace *)0); } ///< Return \b true if heritage is performed on this space
|
bool isHeritaged(void) const { return (space != (AddrSpace *)0); } ///< Return \b true if heritage is performed on this space
|
||||||
void reset(void) {
|
void reset(void); ///< Reset the state
|
||||||
deadremoved = 0; deadcodedelay = delay; warningissued = false; loadGuardSearch = false; } ///< Reset
|
public:
|
||||||
|
HeritageInfo(AddrSpace *spc); ///< Constructor
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Description of a LOAD operation that needs to be guarded
|
/// \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
|
/// \brief Get the heritage status for the given address space
|
||||||
const HeritageInfo *getInfo(AddrSpace *spc) const { return &(infolist[spc->getIndex()]); }
|
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 splitJoinLevel(vector<Varnode *> &lastcombo,vector<Varnode *> &nextlev,JoinRecord *joinrec);
|
||||||
void splitJoinRead(Varnode *vn,JoinRecord *joinrec);
|
void splitJoinRead(Varnode *vn,JoinRecord *joinrec);
|
||||||
void splitJoinWrite(Varnode *vn,JoinRecord *joinrec);
|
void splitJoinWrite(Varnode *vn,JoinRecord *joinrec);
|
||||||
|
|
|
@ -376,6 +376,13 @@ public:
|
||||||
#endif
|
#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
|
bool contiguous_test(Varnode *vn1,Varnode *vn2); ///< Test if Varnodes are pieces of a whole
|
||||||
Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1,
|
Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1,
|
||||||
Varnode *vn2); ///< Retrieve the whole Varnode given pieces
|
Varnode *vn2); ///< Retrieve the whole Varnode given pieces
|
||||||
|
|
|
@ -5,14 +5,15 @@ data/languages/mips.dwarf||GHIDRA||||END|
|
||||||
data/languages/mips.ldefs||GHIDRA||||END|
|
data/languages/mips.ldefs||GHIDRA||||END|
|
||||||
data/languages/mips.sinc||GHIDRA||||END|
|
data/languages/mips.sinc||GHIDRA||||END|
|
||||||
data/languages/mips16.sinc||GHIDRA||||END|
|
data/languages/mips16.sinc||GHIDRA||||END|
|
||||||
data/languages/mips32.cspec||GHIDRA||||END|
|
|
||||||
data/languages/mips32.pspec||GHIDRA||||END|
|
data/languages/mips32.pspec||GHIDRA||||END|
|
||||||
data/languages/mips32Instructions.sinc||GHIDRA||||END|
|
data/languages/mips32Instructions.sinc||GHIDRA||||END|
|
||||||
data/languages/mips32R6.pspec||GHIDRA||||END|
|
data/languages/mips32R6.pspec||GHIDRA||||END|
|
||||||
data/languages/mips32R6be.slaspec||GHIDRA||||END|
|
data/languages/mips32R6be.slaspec||GHIDRA||||END|
|
||||||
data/languages/mips32R6le.slaspec||GHIDRA||||END|
|
data/languages/mips32R6le.slaspec||GHIDRA||||END|
|
||||||
data/languages/mips32_fp64.cspec||GHIDRA||||END|
|
data/languages/mips32_fp64.cspec||GHIDRA||||END|
|
||||||
|
data/languages/mips32be.cspec||GHIDRA||||END|
|
||||||
data/languages/mips32be.slaspec||GHIDRA||||END|
|
data/languages/mips32be.slaspec||GHIDRA||||END|
|
||||||
|
data/languages/mips32le.cspec||GHIDRA||||END|
|
||||||
data/languages/mips32le.slaspec||GHIDRA||||END|
|
data/languages/mips32le.slaspec||GHIDRA||||END|
|
||||||
data/languages/mips32micro.pspec||GHIDRA||||END|
|
data/languages/mips32micro.pspec||GHIDRA||||END|
|
||||||
data/languages/mips64.cspec||GHIDRA||||END|
|
data/languages/mips64.cspec||GHIDRA||||END|
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
manualindexfile="../manuals/mipsM16.idx"
|
manualindexfile="../manuals/mipsM16.idx"
|
||||||
id="MIPS:BE:32:default">
|
id="MIPS:BE:32:default">
|
||||||
<description>MIPS32 32-bit addresses, big endian, with mips16e</description>
|
<description>MIPS32 32-bit addresses, big endian, with mips16e</description>
|
||||||
<compiler name="default" spec="mips32.cspec" id="default"/>
|
<compiler name="default" spec="mips32be.cspec" id="default"/>
|
||||||
<compiler name="Visual Studio" spec="mips32.cspec" id="windows"/>
|
<compiler name="Visual Studio" spec="mips32be.cspec" id="windows"/>
|
||||||
<external_name tool="gnu" name="mips:4000"/>
|
<external_name tool="gnu" name="mips:4000"/>
|
||||||
<external_name tool="IDA-PRO" name="mipsb"/>
|
<external_name tool="IDA-PRO" name="mipsb"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
manualindexfile="../manuals/mipsM16.idx"
|
manualindexfile="../manuals/mipsM16.idx"
|
||||||
id="MIPS:LE:32:default">
|
id="MIPS:LE:32:default">
|
||||||
<description>MIPS32 32-bit addresses, little endian, with mips16e</description>
|
<description>MIPS32 32-bit addresses, little endian, with mips16e</description>
|
||||||
<compiler name="default" spec="mips32.cspec" id="default"/>
|
<compiler name="default" spec="mips32le.cspec" id="default"/>
|
||||||
<compiler name="Visual Studio" spec="mips32.cspec" id="windows"/>
|
<compiler name="Visual Studio" spec="mips32le.cspec" id="windows"/>
|
||||||
<external_name tool="gnu" name="mips:4000"/>
|
<external_name tool="gnu" name="mips:4000"/>
|
||||||
<external_name tool="IDA-PRO" name="mipsl"/>
|
<external_name tool="IDA-PRO" name="mipsl"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
|
||||||
|
@ -284,7 +284,7 @@
|
||||||
manualindexfile="../manuals/mipsMic.idx"
|
manualindexfile="../manuals/mipsMic.idx"
|
||||||
id="MIPS:BE:32:micro">
|
id="MIPS:BE:32:micro">
|
||||||
<description>MIPS32 32-bit addresses, big endian, with microMIPS</description>
|
<description>MIPS32 32-bit addresses, big endian, with microMIPS</description>
|
||||||
<compiler name="default" spec="mips32.cspec" id="default"/>
|
<compiler name="default" spec="mips32be.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="mips:micromips"/>
|
<external_name tool="gnu" name="mips:micromips"/>
|
||||||
<external_name tool="IDA-PRO" name="mipsb"/>
|
<external_name tool="IDA-PRO" name="mipsb"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
|
||||||
|
@ -299,8 +299,8 @@
|
||||||
manualindexfile="../manuals/mipsMic.idx"
|
manualindexfile="../manuals/mipsMic.idx"
|
||||||
id="MIPS:LE:32:micro">
|
id="MIPS:LE:32:micro">
|
||||||
<description>MIPS32 32-bit addresses, little endian, with microMIPS</description>
|
<description>MIPS32 32-bit addresses, little endian, with microMIPS</description>
|
||||||
<compiler name="default" spec="mips32.cspec" id="default"/>
|
<compiler name="default" spec="mips32le.cspec" id="default"/>
|
||||||
<compiler name="Visual Studio" spec="mips32.cspec" id="windows"/>
|
<compiler name="Visual Studio" spec="mips32le.cspec" id="windows"/>
|
||||||
<external_name tool="IDA-PRO" name="mipsl"/>
|
<external_name tool="IDA-PRO" name="mipsl"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
|
|
90
Ghidra/Processors/MIPS/data/languages/mips32le.cspec
Normal file
90
Ghidra/Processors/MIPS/data/languages/mips32le.cspec
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<compiler_spec>
|
||||||
|
<data_organization>
|
||||||
|
<pointer_size value="4"/>
|
||||||
|
<float_size value="4" />
|
||||||
|
<double_size value="8" />
|
||||||
|
<long_double_size value="8" />
|
||||||
|
<size_alignment_map>
|
||||||
|
<entry size="1" alignment="1" />
|
||||||
|
<entry size="2" alignment="2" />
|
||||||
|
<entry size="4" alignment="4" />
|
||||||
|
<entry size="8" alignment="8" />
|
||||||
|
</size_alignment_map>
|
||||||
|
</data_organization>
|
||||||
|
|
||||||
|
<stackpointer register="sp" space="ram"/>
|
||||||
|
<funcptr align="2"/>
|
||||||
|
<spacebase name="gp" register="gp" space="ram"/>
|
||||||
|
<global>
|
||||||
|
<range space="gp"/>
|
||||||
|
<range space="ram"/>
|
||||||
|
<range space="register" first="0x2000" last="0x2fff"/>
|
||||||
|
</global>
|
||||||
|
<returnaddress>
|
||||||
|
<register name="ra"/>
|
||||||
|
</returnaddress>
|
||||||
|
<default_proto>
|
||||||
|
<prototype name="__stdcall" extrapop="0" stackshift="0">
|
||||||
|
<input>
|
||||||
|
<pentry minsize="1" maxsize="8" metatype="float">
|
||||||
|
<register name="f12_13"/>
|
||||||
|
</pentry>
|
||||||
|
<pentry minsize="1" maxsize="8" metatype="float">
|
||||||
|
<register name="f14_15"/>
|
||||||
|
</pentry>
|
||||||
|
<pentry minsize="1" maxsize="4">
|
||||||
|
<register name="a0"/>
|
||||||
|
</pentry>
|
||||||
|
<pentry minsize="1" maxsize="4">
|
||||||
|
<register name="a1"/>
|
||||||
|
</pentry>
|
||||||
|
<pentry minsize="1" maxsize="4">
|
||||||
|
<register name="a2"/>
|
||||||
|
</pentry>
|
||||||
|
<pentry minsize="1" maxsize="4">
|
||||||
|
<register name="a3"/>
|
||||||
|
</pentry>
|
||||||
|
<pentry minsize="1" maxsize="500" align="4">
|
||||||
|
<addr offset="16" space="stack"/>
|
||||||
|
</pentry>
|
||||||
|
</input>
|
||||||
|
<output>
|
||||||
|
<pentry minsize="1" maxsize="8" metatype="float">
|
||||||
|
<register name="f0_1"/>
|
||||||
|
</pentry>
|
||||||
|
<pentry minsize="1" maxsize="4">
|
||||||
|
<register name="v0"/>
|
||||||
|
</pentry>
|
||||||
|
<pentry minsize="5" maxsize="8">
|
||||||
|
<addr space="join" piece1="v1" piece2="v0"/>
|
||||||
|
</pentry>
|
||||||
|
</output>
|
||||||
|
<unaffected>
|
||||||
|
<register name="s0"/>
|
||||||
|
<register name="s1"/>
|
||||||
|
<register name="s2"/>
|
||||||
|
<register name="s3"/>
|
||||||
|
<register name="s4"/>
|
||||||
|
<register name="s5"/>
|
||||||
|
<register name="s6"/>
|
||||||
|
<register name="s7"/>
|
||||||
|
<register name="s8"/>
|
||||||
|
<register name="sp"/>
|
||||||
|
<register name="gp"/>
|
||||||
|
<register name="f20"/>
|
||||||
|
<register name="f22"/>
|
||||||
|
<register name="f24"/>
|
||||||
|
<register name="f26"/>
|
||||||
|
<register name="f28"/>
|
||||||
|
<register name="f30"/>
|
||||||
|
</unaffected>
|
||||||
|
<localrange>
|
||||||
|
<range space="stack" first="0xfff0bdc0" last="0xffffffff"/>
|
||||||
|
<range space="stack" first="0" last="15"/> <!-- This is backup storage space for register params, but we treat as locals -->
|
||||||
|
</localrange>
|
||||||
|
</prototype>
|
||||||
|
</default_proto>
|
||||||
|
|
||||||
|
</compiler_spec>
|
Loading…
Add table
Add a link
Reference in a new issue