mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-4370 Internal Storage
This commit is contained in:
parent
f1e2c8db04
commit
05818c5c3a
17 changed files with 219 additions and 63 deletions
|
@ -28,6 +28,7 @@ src/decompile/datatests/forloop_loaditer.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/forloop_thruspecial.xml||GHIDRA||||END|
|
src/decompile/datatests/forloop_thruspecial.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/forloop_varused.xml||GHIDRA||||END|
|
src/decompile/datatests/forloop_varused.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END|
|
src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END|
|
||||||
|
src/decompile/datatests/gp.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/ifswitch.xml||GHIDRA||||END|
|
src/decompile/datatests/ifswitch.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/impliedfield.xml||GHIDRA||||END|
|
src/decompile/datatests/impliedfield.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/indproto.xml||GHIDRA||||END|
|
src/decompile/datatests/indproto.xml||GHIDRA||||END|
|
||||||
|
|
|
@ -3857,54 +3857,6 @@ uintb ActionDeadCode::gatherConsumedReturn(Funcdata &data)
|
||||||
return consumeVal;
|
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.
|
/// \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
|
/// It may be too early to remove certain LOAD operations even though their result isn't
|
||||||
|
@ -3927,7 +3879,7 @@ bool ActionDeadCode::lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist)
|
||||||
if (op->isDead()) continue;
|
if (op->isDead()) continue;
|
||||||
Varnode *vn = op->getOut();
|
Varnode *vn = op->getOut();
|
||||||
if (vn->isConsumeVacuous()) continue;
|
if (vn->isConsumeVacuous()) continue;
|
||||||
if (isEventualConstant(op->getIn(1), 0, 0)) {
|
if (op->getIn(1)->isEventualConstant(3, 1)) {
|
||||||
pushConsumed(~(uintb)0, vn, worklist);
|
pushConsumed(~(uintb)0, vn, worklist);
|
||||||
vn->setAutoLiveHold();
|
vn->setAutoLiveHold();
|
||||||
res = true;
|
res = true;
|
||||||
|
@ -4817,6 +4769,37 @@ int4 ActionPrototypeWarnings::apply(Funcdata &data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int4 ActionInternalStorage::apply(Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
FuncProto &proto( data.getFuncProto() );
|
||||||
|
vector<VarnodeData>::const_iterator iter = proto.internalBegin();
|
||||||
|
vector<VarnodeData>::const_iterator enditer = proto.internalEnd();
|
||||||
|
while(iter != enditer) {
|
||||||
|
Address addr = (*iter).getAddr();
|
||||||
|
int4 sz = (*iter).size;
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
VarnodeLocSet::const_iterator viter = data.beginLoc(sz, addr);
|
||||||
|
VarnodeLocSet::const_iterator endviter = data.endLoc(sz, addr);
|
||||||
|
while(viter != endviter) {
|
||||||
|
Varnode *vn = *viter;
|
||||||
|
++viter;
|
||||||
|
list<PcodeOp *>::const_iterator oiter = vn->beginDescend();
|
||||||
|
while(oiter != vn->endDescend()) {
|
||||||
|
PcodeOp *op = *oiter;
|
||||||
|
++oiter;
|
||||||
|
if (op->code() == CPUI_STORE) {
|
||||||
|
if (vn->isEventualConstant(3,0)) {
|
||||||
|
op->setStoreUnmapped();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TYPEPROP_DEBUG
|
#ifdef TYPEPROP_DEBUG
|
||||||
/// \brief Log a particular data-type propagation action.
|
/// \brief Log a particular data-type propagation action.
|
||||||
///
|
///
|
||||||
|
@ -5399,6 +5382,7 @@ void ActionDatabase::universalAction(Architecture *conf)
|
||||||
actmainloop->addAction( new ActionHeritage("base") );
|
actmainloop->addAction( new ActionHeritage("base") );
|
||||||
actmainloop->addAction( new ActionParamDouble("protorecovery") );
|
actmainloop->addAction( new ActionParamDouble("protorecovery") );
|
||||||
actmainloop->addAction( new ActionSegmentize("base"));
|
actmainloop->addAction( new ActionSegmentize("base"));
|
||||||
|
actmainloop->addAction( new ActionInternalStorage("base") );
|
||||||
actmainloop->addAction( new ActionForceGoto("blockrecovery") );
|
actmainloop->addAction( new ActionForceGoto("blockrecovery") );
|
||||||
actmainloop->addAction( new ActionDirectWrite("protorecovery_a", true) );
|
actmainloop->addAction( new ActionDirectWrite("protorecovery_a", true) );
|
||||||
actmainloop->addAction( new ActionDirectWrite("protorecovery_b", false) );
|
actmainloop->addAction( new ActionDirectWrite("protorecovery_b", false) );
|
||||||
|
|
|
@ -555,7 +555,6 @@ class ActionDeadCode : public Action {
|
||||||
static bool neverConsumed(Varnode *vn,Funcdata &data);
|
static bool neverConsumed(Varnode *vn,Funcdata &data);
|
||||||
static void markConsumedParameters(FuncCallSpecs *fc,vector<Varnode *> &worklist);
|
static void markConsumedParameters(FuncCallSpecs *fc,vector<Varnode *> &worklist);
|
||||||
static uintb gatherConsumedReturn(Funcdata &data);
|
static uintb gatherConsumedReturn(Funcdata &data);
|
||||||
static bool isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount);
|
|
||||||
static bool lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist);
|
static bool lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist);
|
||||||
public:
|
public:
|
||||||
ActionDeadCode(const string &g) : Action(0,"deadcode",g) {} ///< Constructor
|
ActionDeadCode(const string &g) : Action(0,"deadcode",g) {} ///< Constructor
|
||||||
|
@ -1036,6 +1035,19 @@ public:
|
||||||
virtual int4 apply(Funcdata &data);
|
virtual int4 apply(Funcdata &data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Check for constants getting written to the stack from \e internal \e storage registers
|
||||||
|
///
|
||||||
|
/// The constant is internal to the compiler and its storage location on the stack should not be addressable.
|
||||||
|
class ActionInternalStorage : public Action {
|
||||||
|
public:
|
||||||
|
ActionInternalStorage(const string &g) : Action(rule_onceperfunc,"internalstorage",g) {} ///< Constructor
|
||||||
|
virtual Action *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Action *)0;
|
||||||
|
return new ActionInternalStorage(getGroup());
|
||||||
|
}
|
||||||
|
virtual int4 apply(Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief A class that holds a data-type traversal state during type propagation
|
/// \brief A class that holds a data-type traversal state during type propagation
|
||||||
///
|
///
|
||||||
/// For a given Varnode, this class iterates all the possible edges its
|
/// For a given Varnode, this class iterates all the possible edges its
|
||||||
|
|
|
@ -49,6 +49,7 @@ ElementId ELEM_RESOLVEPROTOTYPE = ElementId("resolveprototype",170);
|
||||||
ElementId ELEM_RETPARAM = ElementId("retparam",171);
|
ElementId ELEM_RETPARAM = ElementId("retparam",171);
|
||||||
ElementId ELEM_RETURNSYM = ElementId("returnsym",172);
|
ElementId ELEM_RETURNSYM = ElementId("returnsym",172);
|
||||||
ElementId ELEM_UNAFFECTED = ElementId("unaffected",173);
|
ElementId ELEM_UNAFFECTED = ElementId("unaffected",173);
|
||||||
|
ElementId ELEM_INTERNAL_STORAGE = ElementId("internal_storage",286);
|
||||||
|
|
||||||
/// \brief Find a ParamEntry matching the given storage Varnode
|
/// \brief Find a ParamEntry matching the given storage Varnode
|
||||||
///
|
///
|
||||||
|
@ -2324,6 +2325,7 @@ ProtoModel::ProtoModel(const string &nm,const ProtoModel &op2)
|
||||||
|
|
||||||
effectlist = op2.effectlist;
|
effectlist = op2.effectlist;
|
||||||
likelytrash = op2.likelytrash;
|
likelytrash = op2.likelytrash;
|
||||||
|
internalstorage = op2.internalstorage;
|
||||||
|
|
||||||
injectUponEntry = op2.injectUponEntry;
|
injectUponEntry = op2.injectUponEntry;
|
||||||
injectUponReturn = op2.injectUponReturn;
|
injectUponReturn = op2.injectUponReturn;
|
||||||
|
@ -2515,6 +2517,7 @@ void ProtoModel::decode(Decoder &decoder)
|
||||||
injectUponEntry = -1;
|
injectUponEntry = -1;
|
||||||
injectUponReturn = -1;
|
injectUponReturn = -1;
|
||||||
likelytrash.clear();
|
likelytrash.clear();
|
||||||
|
internalstorage.clear();
|
||||||
uint4 elemId = decoder.openElement(ELEM_PROTOTYPE);
|
uint4 elemId = decoder.openElement(ELEM_PROTOTYPE);
|
||||||
for(;;) {
|
for(;;) {
|
||||||
uint4 attribId = decoder.getNextAttributeId();
|
uint4 attribId = decoder.getNextAttributeId();
|
||||||
|
@ -2612,6 +2615,14 @@ void ProtoModel::decode(Decoder &decoder)
|
||||||
}
|
}
|
||||||
decoder.closeElement(subId);
|
decoder.closeElement(subId);
|
||||||
}
|
}
|
||||||
|
else if (subId == ELEM_INTERNAL_STORAGE) {
|
||||||
|
decoder.openElement();
|
||||||
|
while(decoder.peekElement() != 0) {
|
||||||
|
internalstorage.emplace_back();
|
||||||
|
internalstorage.back().decode(decoder);
|
||||||
|
}
|
||||||
|
decoder.closeElement(subId);
|
||||||
|
}
|
||||||
else if (subId == ELEM_PCODE) {
|
else if (subId == ELEM_PCODE) {
|
||||||
int4 injectId = glb->pcodeinjectlib->decodeInject("Protomodel : "+name, name,
|
int4 injectId = glb->pcodeinjectlib->decodeInject("Protomodel : "+name, name,
|
||||||
InjectPayload::CALLMECHANISM_TYPE,decoder);
|
InjectPayload::CALLMECHANISM_TYPE,decoder);
|
||||||
|
@ -2631,6 +2642,7 @@ void ProtoModel::decode(Decoder &decoder)
|
||||||
}
|
}
|
||||||
sort(effectlist.begin(),effectlist.end(),EffectRecord::compareByAddress);
|
sort(effectlist.begin(),effectlist.end(),EffectRecord::compareByAddress);
|
||||||
sort(likelytrash.begin(),likelytrash.end());
|
sort(likelytrash.begin(),likelytrash.end());
|
||||||
|
sort(internalstorage.begin(),internalstorage.end());
|
||||||
if (!sawlocalrange)
|
if (!sawlocalrange)
|
||||||
defaultLocalRange();
|
defaultLocalRange();
|
||||||
if (!sawparamrange)
|
if (!sawparamrange)
|
||||||
|
@ -2740,19 +2752,20 @@ void ProtoModelMerged::intersectEffects(const vector<EffectRecord> &efflist)
|
||||||
effectlist.swap(newlist);
|
effectlist.swap(newlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The \e likely-trash locations are intersected. Anything in \b this that is not also in the
|
/// The intersection of two containers of register Varnodes is calculated, and the result is
|
||||||
/// given \e likely-trash list is removed.
|
/// placed in the first container, replacing the original contents. The containers must already be sorted.
|
||||||
/// \param trashlist is the given \e likely-trash list
|
/// \param regList1 is the first container
|
||||||
void ProtoModelMerged::intersectLikelyTrash(const vector<VarnodeData> &trashlist)
|
/// \param regList2 is the second container
|
||||||
|
void ProtoModelMerged::intersectRegisters(vector<VarnodeData> ®List1,const vector<VarnodeData> ®List2)
|
||||||
|
|
||||||
{
|
{
|
||||||
vector<VarnodeData> newlist;
|
vector<VarnodeData> newlist;
|
||||||
|
|
||||||
int4 i=0;
|
int4 i=0;
|
||||||
int4 j=0;
|
int4 j=0;
|
||||||
while((i<likelytrash.size())&&(j<trashlist.size())) {
|
while((i<regList1.size())&&(j<regList2.size())) {
|
||||||
const VarnodeData &trs1( likelytrash[i] );
|
const VarnodeData &trs1( regList1[i] );
|
||||||
const VarnodeData &trs2( trashlist[j] );
|
const VarnodeData &trs2( regList2[j] );
|
||||||
|
|
||||||
if (trs1 < trs2)
|
if (trs1 < trs2)
|
||||||
i += 1;
|
i += 1;
|
||||||
|
@ -2764,7 +2777,7 @@ void ProtoModelMerged::intersectLikelyTrash(const vector<VarnodeData> &trashlist
|
||||||
j += 1;
|
j += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
likelytrash = newlist;
|
regList1.swap(newlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \param model is the new prototype model to add to the merge
|
/// \param model is the new prototype model to add to the merge
|
||||||
|
@ -2795,7 +2808,8 @@ void ProtoModelMerged::foldIn(ProtoModel *model)
|
||||||
if ((injectUponEntry != model->injectUponEntry)||(injectUponReturn != model->injectUponReturn))
|
if ((injectUponEntry != model->injectUponEntry)||(injectUponReturn != model->injectUponReturn))
|
||||||
throw LowlevelError("Cannot merge prototype models with different inject ids");
|
throw LowlevelError("Cannot merge prototype models with different inject ids");
|
||||||
intersectEffects(model->effectlist);
|
intersectEffects(model->effectlist);
|
||||||
intersectLikelyTrash(model->likelytrash);
|
intersectRegisters(likelytrash,model->likelytrash);
|
||||||
|
intersectRegisters(internalstorage,model->internalstorage);
|
||||||
// Take the union of the localrange and paramrange
|
// Take the union of the localrange and paramrange
|
||||||
set<Range>::const_iterator iter;
|
set<Range>::const_iterator iter;
|
||||||
for(iter=model->localrange.begin();iter!=model->localrange.end();++iter)
|
for(iter=model->localrange.begin();iter!=model->localrange.end();++iter)
|
||||||
|
|
|
@ -58,6 +58,7 @@ extern ElementId ELEM_RESOLVEPROTOTYPE; ///< Marshaling element \<resolveprototy
|
||||||
extern ElementId ELEM_RETPARAM; ///< Marshaling element \<retparam>
|
extern ElementId ELEM_RETPARAM; ///< Marshaling element \<retparam>
|
||||||
extern ElementId ELEM_RETURNSYM; ///< Marshaling element \<returnsym>
|
extern ElementId ELEM_RETURNSYM; ///< Marshaling element \<returnsym>
|
||||||
extern ElementId ELEM_UNAFFECTED; ///< Marshaling element \<unaffected>
|
extern ElementId ELEM_UNAFFECTED; ///< Marshaling element \<unaffected>
|
||||||
|
extern ElementId ELEM_INTERNAL_STORAGE; ///< Marshaling element \<internal_storage>
|
||||||
|
|
||||||
/// \brief Exception thrown when a prototype can't be modeled properly
|
/// \brief Exception thrown when a prototype can't be modeled properly
|
||||||
struct ParamUnassignedError : public LowlevelError {
|
struct ParamUnassignedError : public LowlevelError {
|
||||||
|
@ -749,6 +750,7 @@ class ProtoModel {
|
||||||
const ProtoModel *compatModel; ///< The model \b this is a copy of
|
const ProtoModel *compatModel; ///< The model \b this is a copy of
|
||||||
vector<EffectRecord> effectlist; ///< List of side-effects
|
vector<EffectRecord> effectlist; ///< List of side-effects
|
||||||
vector<VarnodeData> likelytrash; ///< Storage locations potentially carrying \e trash values
|
vector<VarnodeData> likelytrash; ///< Storage locations potentially carrying \e trash values
|
||||||
|
vector<VarnodeData> internalstorage; ///< Registers that hold internal compiler constants
|
||||||
int4 injectUponEntry; ///< Id of injection to perform at beginning of function (-1 means not used)
|
int4 injectUponEntry; ///< Id of injection to perform at beginning of function (-1 means not used)
|
||||||
int4 injectUponReturn; ///< Id of injection to perform after a call to this function (-1 means not used)
|
int4 injectUponReturn; ///< Id of injection to perform after a call to this function (-1 means not used)
|
||||||
RangeList localrange; ///< Memory range(s) of space-based locals
|
RangeList localrange; ///< Memory range(s) of space-based locals
|
||||||
|
@ -834,6 +836,8 @@ public:
|
||||||
vector<EffectRecord>::const_iterator effectEnd(void) const { return effectlist.end(); } ///< Get an iterator to the last EffectRecord
|
vector<EffectRecord>::const_iterator effectEnd(void) const { return effectlist.end(); } ///< Get an iterator to the last EffectRecord
|
||||||
vector<VarnodeData>::const_iterator trashBegin(void) const { return likelytrash.begin(); } ///< Get an iterator to the first \e likelytrash
|
vector<VarnodeData>::const_iterator trashBegin(void) const { return likelytrash.begin(); } ///< Get an iterator to the first \e likelytrash
|
||||||
vector<VarnodeData>::const_iterator trashEnd(void) const { return likelytrash.end(); } ///< Get an iterator to the last \e likelytrash
|
vector<VarnodeData>::const_iterator trashEnd(void) const { return likelytrash.end(); } ///< Get an iterator to the last \e likelytrash
|
||||||
|
vector<VarnodeData>::const_iterator internalBegin(void) const { return internalstorage.begin(); } ///< Get an iterator to the first \e internalstorage
|
||||||
|
vector<VarnodeData>::const_iterator internalEnd(void) const { return internalstorage.end(); } ///< Get an iterator to the last \e internalstorage
|
||||||
|
|
||||||
/// \brief Characterize whether the given range overlaps parameter storage
|
/// \brief Characterize whether the given range overlaps parameter storage
|
||||||
///
|
///
|
||||||
|
@ -1068,7 +1072,7 @@ public:
|
||||||
class ProtoModelMerged : public ProtoModel {
|
class ProtoModelMerged : public ProtoModel {
|
||||||
vector<ProtoModel *> modellist; ///< Constituent models being merged
|
vector<ProtoModel *> modellist; ///< Constituent models being merged
|
||||||
void intersectEffects(const vector<EffectRecord> &efflist); ///< Fold EffectRecords into \b this model
|
void intersectEffects(const vector<EffectRecord> &efflist); ///< Fold EffectRecords into \b this model
|
||||||
void intersectLikelyTrash(const vector<VarnodeData> &trashlist); ///< Fold \e likelytrash locations into \b this model
|
static void intersectRegisters(vector<VarnodeData> ®List1,const vector<VarnodeData> ®List2);
|
||||||
public:
|
public:
|
||||||
ProtoModelMerged(Architecture *g) : ProtoModel(g) {} ///< Constructor
|
ProtoModelMerged(Architecture *g) : ProtoModel(g) {} ///< Constructor
|
||||||
virtual ~ProtoModelMerged(void) {} ///< Destructor
|
virtual ~ProtoModelMerged(void) {} ///< Destructor
|
||||||
|
@ -1539,6 +1543,8 @@ public:
|
||||||
vector<EffectRecord>::const_iterator effectEnd(void) const; ///< Get iterator to end of EffectRecord list
|
vector<EffectRecord>::const_iterator effectEnd(void) const; ///< Get iterator to end of EffectRecord list
|
||||||
vector<VarnodeData>::const_iterator trashBegin(void) const; ///< Get iterator to front of \e likelytrash list
|
vector<VarnodeData>::const_iterator trashBegin(void) const; ///< Get iterator to front of \e likelytrash list
|
||||||
vector<VarnodeData>::const_iterator trashEnd(void) const; ///< Get iterator to end of \e likelytrash list
|
vector<VarnodeData>::const_iterator trashEnd(void) const; ///< Get iterator to end of \e likelytrash list
|
||||||
|
vector<VarnodeData>::const_iterator internalBegin(void) const { return model->internalBegin(); } ///< Get iterator to front of \e internalstorage list
|
||||||
|
vector<VarnodeData>::const_iterator internalEnd(void) const { return model->internalEnd(); } ///< Get iterator to end of \e internalstorage list
|
||||||
int4 characterizeAsInputParam(const Address &addr,int4 size) const;
|
int4 characterizeAsInputParam(const Address &addr,int4 size) const;
|
||||||
int4 characterizeAsOutput(const Address &addr,int4 size) const;
|
int4 characterizeAsOutput(const Address &addr,int4 size) const;
|
||||||
bool possibleInputParam(const Address &addr,int4 size) const;
|
bool possibleInputParam(const Address &addr,int4 size) const;
|
||||||
|
|
|
@ -1972,6 +1972,8 @@ int4 AncestorRealistic::enterNode(void)
|
||||||
if (!vn->isDirectWrite())
|
if (!vn->isDirectWrite())
|
||||||
return pop_fail;
|
return pop_fail;
|
||||||
}
|
}
|
||||||
|
if (op->isStoreUnmapped())
|
||||||
|
return pop_fail;
|
||||||
op = vn->getDef();
|
op = vn->getDef();
|
||||||
if (op == (PcodeOp *)0) break;
|
if (op == (PcodeOp *)0) break;
|
||||||
OpCode opc = op->code();
|
OpCode opc = op->code();
|
||||||
|
|
|
@ -1266,6 +1266,6 @@ ElementId ELEM_VAL = ElementId("val",8);
|
||||||
ElementId ELEM_VALUE = ElementId("value",9);
|
ElementId ELEM_VALUE = ElementId("value",9);
|
||||||
ElementId ELEM_VOID = ElementId("void",10);
|
ElementId ELEM_VOID = ElementId("void",10);
|
||||||
|
|
||||||
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",286); // Number serves as next open index
|
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",287); // Number serves as next open index
|
||||||
|
|
||||||
} // End namespace ghidra
|
} // End namespace ghidra
|
||||||
|
|
|
@ -114,7 +114,8 @@ public:
|
||||||
stop_type_propagation = 0x40, ///< Stop data-type propagation into output from descendants
|
stop_type_propagation = 0x40, ///< Stop data-type propagation into output from descendants
|
||||||
hold_output = 0x80, ///< Output varnode (of call) should not be removed if it is unread
|
hold_output = 0x80, ///< Output varnode (of call) should not be removed if it is unread
|
||||||
concat_root = 0x100, ///< Output of \b this is root of a CONCAT tree
|
concat_root = 0x100, ///< Output of \b this is root of a CONCAT tree
|
||||||
no_indirect_collapse = 0x200 ///< Do not collapse \b this INDIRECT (via RuleIndirectCollapse)
|
no_indirect_collapse = 0x200, ///< Do not collapse \b this INDIRECT (via RuleIndirectCollapse)
|
||||||
|
store_unmapped = 0x400 ///< If STORE collapses to a stack Varnode, force it to be unmapped
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
TypeOp *opcode; ///< Pointer to class providing behavioral details of the operation
|
TypeOp *opcode; ///< Pointer to class providing behavioral details of the operation
|
||||||
|
@ -221,6 +222,8 @@ public:
|
||||||
void setStopCopyPropagation(void) { flags |= no_copy_propagation; } ///< Stop COPY propagation through inputs
|
void setStopCopyPropagation(void) { flags |= no_copy_propagation; } ///< Stop COPY propagation through inputs
|
||||||
bool noIndirectCollapse(void) const { return ((addlflags & no_indirect_collapse)!=0); } ///< Check if INDIRECT collapse is possible
|
bool noIndirectCollapse(void) const { return ((addlflags & no_indirect_collapse)!=0); } ///< Check if INDIRECT collapse is possible
|
||||||
void setNoIndirectCollapse(void) { addlflags |= no_indirect_collapse; } ///< Prevent collapse of INDIRECT
|
void setNoIndirectCollapse(void) { addlflags |= no_indirect_collapse; } ///< Prevent collapse of INDIRECT
|
||||||
|
bool isStoreUnmapped(void) const { return ((addlflags & store_unmapped)!=0); } ///< Is STORE location supposed to be unmapped
|
||||||
|
void setStoreUnmapped(void) const { addlflags |= store_unmapped; } ///< Mark that STORE location should be unmapped
|
||||||
/// \brief Return \b true if this LOADs or STOREs from a dynamic \e spacebase pointer
|
/// \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); }
|
bool usesSpacebasePtr(void) const { return ((flags&PcodeOp::spacebase_ptr)!=0); }
|
||||||
uintm getCseHash(void) const; ///< Return hash indicating possibility of common subexpression elimination
|
uintm getCseHash(void) const; ///< Return hash indicating possibility of common subexpression elimination
|
||||||
|
|
|
@ -4010,6 +4010,9 @@ int4 RuleStoreVarnode::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
data.opRemoveInput(op,1);
|
data.opRemoveInput(op,1);
|
||||||
data.opRemoveInput(op,0);
|
data.opRemoveInput(op,0);
|
||||||
data.opSetOpcode(op, CPUI_COPY );
|
data.opSetOpcode(op, CPUI_COPY );
|
||||||
|
if (op->isStoreUnmapped()) {
|
||||||
|
data.getScopeLocal()->markNotMapped(baseoff, offoff, size, false);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -831,6 +831,55 @@ bool Varnode::isConstantExtended(uint8 *val) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively check if the Varnode is either:
|
||||||
|
/// - Copied or extended from a constant
|
||||||
|
/// - The result of arithmetic or logical operations on constants
|
||||||
|
/// - Loaded from a pointer that is a constant
|
||||||
|
///
|
||||||
|
/// \param maxBinary is the maximum depth of binary operations to inspect (before giving up)
|
||||||
|
/// \param maxLoad is the maximum number of CPUI_LOAD operations to allow in a sequence
|
||||||
|
/// \return \b true if the Varnode (might) collapse to a constant
|
||||||
|
bool Varnode::isEventualConstant(int4 maxBinary,int4 maxLoad) const
|
||||||
|
|
||||||
|
{
|
||||||
|
const Varnode *curVn = this;
|
||||||
|
while(!curVn->isConstant()) {
|
||||||
|
if (!curVn->isWritten()) return false;
|
||||||
|
const PcodeOp *op = curVn->getDef();
|
||||||
|
switch(op->code()) {
|
||||||
|
case CPUI_LOAD:
|
||||||
|
if (maxLoad == 0) return false;
|
||||||
|
maxLoad -= 1;
|
||||||
|
curVn = op->getIn(1);
|
||||||
|
break;
|
||||||
|
case CPUI_INT_ADD:
|
||||||
|
case CPUI_INT_SUB:
|
||||||
|
case CPUI_INT_XOR:
|
||||||
|
case CPUI_INT_OR:
|
||||||
|
case CPUI_INT_AND:
|
||||||
|
if (maxBinary == 0) return false;
|
||||||
|
if (!op->getIn(0)->isEventualConstant(maxBinary-1,maxLoad))
|
||||||
|
return false;
|
||||||
|
return op->getIn(1)->isEventualConstant(maxBinary-1,maxLoad);
|
||||||
|
case CPUI_INT_ZEXT:
|
||||||
|
case CPUI_INT_SEXT:
|
||||||
|
case CPUI_COPY:
|
||||||
|
curVn = op->getIn(0);
|
||||||
|
break;
|
||||||
|
case CPUI_INT_LEFT:
|
||||||
|
case CPUI_INT_RIGHT:
|
||||||
|
case CPUI_INT_SRIGHT:
|
||||||
|
case CPUI_INT_MULT:
|
||||||
|
if (!op->getIn(1)->isConstant()) return false;
|
||||||
|
curVn = op->getIn(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Make an initial determination of the Datatype of this Varnode. If a Datatype is already
|
/// Make an initial determination of the Datatype of this Varnode. If a Datatype is already
|
||||||
/// set and locked return it. Otherwise look through all the read PcodeOps and the write PcodeOp
|
/// set and locked return it. Otherwise look through all the read PcodeOps and the write PcodeOp
|
||||||
/// to determine if the Varnode is getting used as an \b int, \b float, or \b pointer, etc.
|
/// to determine if the Varnode is getting used as an \b int, \b float, or \b pointer, etc.
|
||||||
|
|
|
@ -291,6 +291,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConstantExtended(uint8 *val) const; ///< Is \b this an (extended) constant
|
bool isConstantExtended(uint8 *val) const; ///< Is \b this an (extended) constant
|
||||||
|
bool isEventualConstant(int4 maxBinary,int4 maxLoad) const; ///< Will \b this Varnode ultimately collapse to a constant
|
||||||
|
|
||||||
/// Return \b true if this Varnode is linked into the SSA tree
|
/// Return \b true if this Varnode is linked into the SSA tree
|
||||||
bool isHeritageKnown(void) const { return ((flags&(Varnode::insert|Varnode::constant|Varnode::annotation))!=0); }
|
bool isHeritageKnown(void) const { return ((flags&(Varnode::insert|Varnode::constant|Varnode::annotation))!=0); }
|
||||||
bool isTypeLock(void) const { return ((flags&Varnode::typelock)!=0); } ///< Does \b this have a locked Datatype?
|
bool isTypeLock(void) const { return ((flags&Varnode::typelock)!=0); } ///< Does \b this have a locked Datatype?
|
||||||
|
|
35
Ghidra/Features/Decompiler/src/decompile/datatests/gp.xml
Normal file
35
Ghidra/Features/Decompiler/src/decompile/datatests/gp.xml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<decompilertest>
|
||||||
|
<!-- Example of MIPS -fPIC code storing the gp register on the stack.
|
||||||
|
The decompiler needs to be able to propagate the constant through the stack location.
|
||||||
|
This allows the printf call and "Hello" string to be recovered. -->
|
||||||
|
<binaryimage arch="MIPS:BE:32:default:default">
|
||||||
|
<bytechunk space="ram" offset="0x410040" readonly="true">
|
||||||
|
3c1c0041279c00200399e02127bdffd0
|
||||||
|
8f990094afbf0024afb00028afbc0020
|
||||||
|
008080250320f80927a400188fbc0020
|
||||||
|
020028258f8400908f99009800000000
|
||||||
|
0320f809248400008fbf00248fa2001c
|
||||||
|
8fb0002003e0000827bd0028
|
||||||
|
</bytechunk>
|
||||||
|
<bytechunk space="ram" offset="0x4100a0" readonly="true">
|
||||||
|
48656c6c6f00
|
||||||
|
</bytechunk>
|
||||||
|
<bytechunk space="ram" offset="0x4100b0" readonly="true">
|
||||||
|
004100a00041003000410024
|
||||||
|
</bytechunk>
|
||||||
|
<symbol space="ram" offset="0x410024" name="printf"/>
|
||||||
|
<symbol space="ram" offset="0x410030" name="populate"/>
|
||||||
|
<symbol space="ram" offset="0x410040" name="test_gp"/>
|
||||||
|
</binaryimage>
|
||||||
|
<script>
|
||||||
|
<com>option readonly on</com>
|
||||||
|
<com>set track t9 0x0 0x410040 0x410041</com>
|
||||||
|
<com>parse line extern void printf(char *,...);</com>
|
||||||
|
<com>lo fu test_gp</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>quit</com>
|
||||||
|
</script>
|
||||||
|
<stringmatch name="Gp Test #1" min="1" max="1">populate\(axStack_18\);</stringmatch>
|
||||||
|
<stringmatch name="Gp Test #2" min="1" max="1">printf\("Hello",param_1\);</stringmatch>
|
||||||
|
</decompilertest>
|
|
@ -544,6 +544,14 @@
|
||||||
</element>
|
</element>
|
||||||
</optional>
|
</optional>
|
||||||
|
|
||||||
|
<optional>
|
||||||
|
<element name="internal_storage">
|
||||||
|
<oneOrMore>
|
||||||
|
<ref name="varnode_tags_type"/>
|
||||||
|
</oneOrMore>
|
||||||
|
</element>
|
||||||
|
</optional>
|
||||||
|
|
||||||
<optional>
|
<optional>
|
||||||
<element name="localrange">
|
<element name="localrange">
|
||||||
<ref name="rangelist_type"/>
|
<ref name="rangelist_type"/>
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class PrototypeModel {
|
||||||
private Varnode[] killedbycall; // Memory ranges definitely affected by calls
|
private Varnode[] killedbycall; // Memory ranges definitely affected by calls
|
||||||
private Varnode[] returnaddress; // Memory used to store the return address
|
private Varnode[] returnaddress; // Memory used to store the return address
|
||||||
private Varnode[] likelytrash; // Memory likely to be meaningless on input
|
private Varnode[] likelytrash; // Memory likely to be meaningless on input
|
||||||
|
private Varnode[] internalstorage; // Registers holding internal compiler constants
|
||||||
private PrototypeModel compatModel; // The model this is an alias of
|
private PrototypeModel compatModel; // The model this is an alias of
|
||||||
private AddressSet localRange; // Range on the stack considered for local storage
|
private AddressSet localRange; // Range on the stack considered for local storage
|
||||||
private AddressSet paramRange; // Range on the stack considered for parameter storage
|
private AddressSet paramRange; // Range on the stack considered for parameter storage
|
||||||
|
@ -81,6 +82,7 @@ public class PrototypeModel {
|
||||||
killedbycall = model.killedbycall;
|
killedbycall = model.killedbycall;
|
||||||
returnaddress = model.returnaddress;
|
returnaddress = model.returnaddress;
|
||||||
likelytrash = model.likelytrash;
|
likelytrash = model.likelytrash;
|
||||||
|
internalstorage = model.internalstorage;
|
||||||
compatModel = model;
|
compatModel = model;
|
||||||
localRange = new AddressSet(model.localRange);
|
localRange = new AddressSet(model.localRange);
|
||||||
paramRange = new AddressSet(model.paramRange);
|
paramRange = new AddressSet(model.paramRange);
|
||||||
|
@ -101,6 +103,7 @@ public class PrototypeModel {
|
||||||
killedbycall = null;
|
killedbycall = null;
|
||||||
returnaddress = null;
|
returnaddress = null;
|
||||||
likelytrash = null;
|
likelytrash = null;
|
||||||
|
internalstorage = null;
|
||||||
compatModel = null;
|
compatModel = null;
|
||||||
localRange = null;
|
localRange = null;
|
||||||
paramRange = null;
|
paramRange = null;
|
||||||
|
@ -140,6 +143,16 @@ public class PrototypeModel {
|
||||||
return likelytrash;
|
return likelytrash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list of registers used to store internal compiler constants
|
||||||
|
*/
|
||||||
|
public Varnode[] getInternalStorage() {
|
||||||
|
if (internalstorage == null) {
|
||||||
|
internalstorage = new Varnode[0];
|
||||||
|
}
|
||||||
|
return internalstorage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list of registers/memory used to store the return address
|
* @return list of registers/memory used to store the return address
|
||||||
*/
|
*/
|
||||||
|
@ -456,6 +469,11 @@ public class PrototypeModel {
|
||||||
encodeVarnodes(encoder, likelytrash);
|
encodeVarnodes(encoder, likelytrash);
|
||||||
encoder.closeElement(ELEM_LIKELYTRASH);
|
encoder.closeElement(ELEM_LIKELYTRASH);
|
||||||
}
|
}
|
||||||
|
if (internalstorage != null) {
|
||||||
|
encoder.openElement(ELEM_INTERNAL_STORAGE);
|
||||||
|
encodeVarnodes(encoder, internalstorage);
|
||||||
|
encoder.closeElement(ELEM_INTERNAL_STORAGE);
|
||||||
|
}
|
||||||
if (returnaddress != null) {
|
if (returnaddress != null) {
|
||||||
encoder.openElement(ELEM_RETURNADDRESS);
|
encoder.openElement(ELEM_RETURNADDRESS);
|
||||||
encodeVarnodes(encoder, returnaddress);
|
encodeVarnodes(encoder, returnaddress);
|
||||||
|
@ -633,6 +651,9 @@ public class PrototypeModel {
|
||||||
else if (elName.equals("likelytrash")) {
|
else if (elName.equals("likelytrash")) {
|
||||||
likelytrash = readVarnodes(parser, cspec);
|
likelytrash = readVarnodes(parser, cspec);
|
||||||
}
|
}
|
||||||
|
else if (elName.equals("internal_storage")) {
|
||||||
|
internalstorage = readVarnodes(parser, cspec);
|
||||||
|
}
|
||||||
else if (elName.equals("localrange")) {
|
else if (elName.equals("localrange")) {
|
||||||
localRange = readAddressSet(parser, cspec);
|
localRange = readAddressSet(parser, cspec);
|
||||||
}
|
}
|
||||||
|
@ -741,6 +762,9 @@ public class PrototypeModel {
|
||||||
if (!SystemUtilities.isArrayEqual(likelytrash, obj.likelytrash)) {
|
if (!SystemUtilities.isArrayEqual(likelytrash, obj.likelytrash)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!SystemUtilities.isArrayEqual(internalstorage, obj.internalstorage)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
String compatName = (compatModel != null) ? compatModel.getName() : "";
|
String compatName = (compatModel != null) ? compatModel.getName() : "";
|
||||||
String compatNameOp2 = (obj.compatModel != null) ? obj.compatModel.getName() : "";
|
String compatNameOp2 = (obj.compatModel != null) ? obj.compatModel.getName() : "";
|
||||||
if (!compatName.equals(compatNameOp2)) {
|
if (!compatName.equals(compatNameOp2)) {
|
||||||
|
|
|
@ -268,6 +268,7 @@ public record ElementId(String name, int id) {
|
||||||
public static final ElementId ELEM_RETPARAM = new ElementId("retparam", 171);
|
public static final ElementId ELEM_RETPARAM = new ElementId("retparam", 171);
|
||||||
public static final ElementId ELEM_RETURNSYM = new ElementId("returnsym", 172);
|
public static final ElementId ELEM_RETURNSYM = new ElementId("returnsym", 172);
|
||||||
public static final ElementId ELEM_UNAFFECTED = new ElementId("unaffected", 173);
|
public static final ElementId ELEM_UNAFFECTED = new ElementId("unaffected", 173);
|
||||||
|
public static final ElementId ELEM_INTERNAL_STORAGE = new ElementId("internal_storage", 286);
|
||||||
|
|
||||||
// options
|
// options
|
||||||
public static final ElementId ELEM_ALIASBLOCK = new ElementId("aliasblock", 174);
|
public static final ElementId ELEM_ALIASBLOCK = new ElementId("aliasblock", 174);
|
||||||
|
@ -456,5 +457,5 @@ public record ElementId(String name, int id) {
|
||||||
new ElementId("join_per_primitive", 283);
|
new ElementId("join_per_primitive", 283);
|
||||||
public static final ElementId ELEM_JOIN_DUAL_CLASS = new ElementId("join_dual_class", 285);
|
public static final ElementId ELEM_JOIN_DUAL_CLASS = new ElementId("join_dual_class", 285);
|
||||||
|
|
||||||
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 286);
|
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 287);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,9 @@
|
||||||
<register name="f28"/>
|
<register name="f28"/>
|
||||||
<register name="f30"/>
|
<register name="f30"/>
|
||||||
</unaffected>
|
</unaffected>
|
||||||
|
<internal_storage>
|
||||||
|
<register name="gp"/> <!-- Compilers may save gp to the stack before a call and restore it afterward -->
|
||||||
|
</internal_storage>
|
||||||
<localrange>
|
<localrange>
|
||||||
<range space="stack" first="0xfff0bdc0" last="0xffffffff"/>
|
<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 -->
|
<range space="stack" first="0" last="15"/> <!-- This is backup storage space for register params, but we treat as locals -->
|
||||||
|
@ -101,6 +104,9 @@
|
||||||
<unaffected>
|
<unaffected>
|
||||||
<register name="sp"/>
|
<register name="sp"/>
|
||||||
</unaffected>
|
</unaffected>
|
||||||
|
<internal_storage>
|
||||||
|
<register name="gp"/> <!-- Compilers may save gp to the stack before a call and restore it afterward -->
|
||||||
|
</internal_storage>
|
||||||
</prototype>
|
</prototype>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,9 @@
|
||||||
<register name="f28"/>
|
<register name="f28"/>
|
||||||
<register name="f30"/>
|
<register name="f30"/>
|
||||||
</unaffected>
|
</unaffected>
|
||||||
|
<internal_storage>
|
||||||
|
<register name="gp"/> <!-- Compilers may save gp to the stack before a call and restore it afterward -->
|
||||||
|
</internal_storage>
|
||||||
<localrange>
|
<localrange>
|
||||||
<range space="stack" first="0xfff0bdc0" last="0xffffffff"/>
|
<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 -->
|
<range space="stack" first="0" last="15"/> <!-- This is backup storage space for register params, but we treat as locals -->
|
||||||
|
@ -101,6 +104,9 @@
|
||||||
<unaffected>
|
<unaffected>
|
||||||
<register name="sp"/>
|
<register name="sp"/>
|
||||||
</unaffected>
|
</unaffected>
|
||||||
|
<internal_storage>
|
||||||
|
<register name="gp"/> <!-- Compilers may save gp to the stack before a call and restore it afterward -->
|
||||||
|
</internal_storage>
|
||||||
</prototype>
|
</prototype>
|
||||||
|
|
||||||
</compiler_spec>
|
</compiler_spec>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue