mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +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_varused.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/impliedfield.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/indproto.xml||GHIDRA||||END|
|
||||
|
|
|
@ -3857,54 +3857,6 @@ uintb ActionDeadCode::gatherConsumedReturn(Funcdata &data)
|
|||
return consumeVal;
|
||||
}
|
||||
|
||||
/// \brief Determine if the given Varnode may eventually collapse to a constant
|
||||
///
|
||||
/// Recursively check if the Varnode is either:
|
||||
/// - Copied from a constant
|
||||
/// - The result of adding constants
|
||||
/// - Loaded from a pointer that is a constant
|
||||
///
|
||||
/// \param vn is the given Varnode
|
||||
/// \param addCount is the number of CPUI_INT_ADD operations seen so far
|
||||
/// \param loadCount is the number of CPUI_LOAD operations seen so far
|
||||
/// \return \b true if the Varnode (might) collapse to a constant
|
||||
bool ActionDeadCode::isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount)
|
||||
|
||||
{
|
||||
if (vn->isConstant()) return true;
|
||||
if (!vn->isWritten()) return false;
|
||||
PcodeOp *op = vn->getDef();
|
||||
while(op->code() == CPUI_COPY) {
|
||||
vn = op->getIn(0);
|
||||
if (vn->isConstant()) return true;
|
||||
if (!vn->isWritten()) return false;
|
||||
op = vn->getDef();
|
||||
}
|
||||
switch(op->code()) {
|
||||
case CPUI_INT_ADD:
|
||||
if (addCount > 0) return false;
|
||||
if (!isEventualConstant(op->getIn(0),addCount+1,loadCount))
|
||||
return false;
|
||||
return isEventualConstant(op->getIn(1),addCount+1,loadCount);
|
||||
case CPUI_LOAD:
|
||||
if (loadCount > 0) return false;
|
||||
return isEventualConstant(op->getIn(1),0,loadCount+1);
|
||||
case CPUI_INT_LEFT:
|
||||
case CPUI_INT_RIGHT:
|
||||
case CPUI_INT_SRIGHT:
|
||||
case CPUI_INT_MULT:
|
||||
if (!op->getIn(1)->isConstant())
|
||||
return false;
|
||||
return isEventualConstant(op->getIn(0),addCount,loadCount);
|
||||
case CPUI_INT_ZEXT:
|
||||
case CPUI_INT_SEXT:
|
||||
return isEventualConstant(op->getIn(0),addCount,loadCount);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Check if there are any unconsumed LOADs that may be from volatile addresses.
|
||||
///
|
||||
/// It may be too early to remove certain LOAD operations even though their result isn't
|
||||
|
@ -3927,7 +3879,7 @@ bool ActionDeadCode::lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist)
|
|||
if (op->isDead()) continue;
|
||||
Varnode *vn = op->getOut();
|
||||
if (vn->isConsumeVacuous()) continue;
|
||||
if (isEventualConstant(op->getIn(1), 0, 0)) {
|
||||
if (op->getIn(1)->isEventualConstant(3, 1)) {
|
||||
pushConsumed(~(uintb)0, vn, worklist);
|
||||
vn->setAutoLiveHold();
|
||||
res = true;
|
||||
|
@ -4817,6 +4769,37 @@ int4 ActionPrototypeWarnings::apply(Funcdata &data)
|
|||
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
|
||||
/// \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 ActionParamDouble("protorecovery") );
|
||||
actmainloop->addAction( new ActionSegmentize("base"));
|
||||
actmainloop->addAction( new ActionInternalStorage("base") );
|
||||
actmainloop->addAction( new ActionForceGoto("blockrecovery") );
|
||||
actmainloop->addAction( new ActionDirectWrite("protorecovery_a", true) );
|
||||
actmainloop->addAction( new ActionDirectWrite("protorecovery_b", false) );
|
||||
|
|
|
@ -555,7 +555,6 @@ class ActionDeadCode : public Action {
|
|||
static bool neverConsumed(Varnode *vn,Funcdata &data);
|
||||
static void markConsumedParameters(FuncCallSpecs *fc,vector<Varnode *> &worklist);
|
||||
static uintb gatherConsumedReturn(Funcdata &data);
|
||||
static bool isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount);
|
||||
static bool lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist);
|
||||
public:
|
||||
ActionDeadCode(const string &g) : Action(0,"deadcode",g) {} ///< Constructor
|
||||
|
@ -1036,6 +1035,19 @@ public:
|
|||
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
|
||||
///
|
||||
/// 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_RETURNSYM = ElementId("returnsym",172);
|
||||
ElementId ELEM_UNAFFECTED = ElementId("unaffected",173);
|
||||
ElementId ELEM_INTERNAL_STORAGE = ElementId("internal_storage",286);
|
||||
|
||||
/// \brief Find a ParamEntry matching the given storage Varnode
|
||||
///
|
||||
|
@ -2324,6 +2325,7 @@ ProtoModel::ProtoModel(const string &nm,const ProtoModel &op2)
|
|||
|
||||
effectlist = op2.effectlist;
|
||||
likelytrash = op2.likelytrash;
|
||||
internalstorage = op2.internalstorage;
|
||||
|
||||
injectUponEntry = op2.injectUponEntry;
|
||||
injectUponReturn = op2.injectUponReturn;
|
||||
|
@ -2515,6 +2517,7 @@ void ProtoModel::decode(Decoder &decoder)
|
|||
injectUponEntry = -1;
|
||||
injectUponReturn = -1;
|
||||
likelytrash.clear();
|
||||
internalstorage.clear();
|
||||
uint4 elemId = decoder.openElement(ELEM_PROTOTYPE);
|
||||
for(;;) {
|
||||
uint4 attribId = decoder.getNextAttributeId();
|
||||
|
@ -2612,6 +2615,14 @@ void ProtoModel::decode(Decoder &decoder)
|
|||
}
|
||||
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) {
|
||||
int4 injectId = glb->pcodeinjectlib->decodeInject("Protomodel : "+name, name,
|
||||
InjectPayload::CALLMECHANISM_TYPE,decoder);
|
||||
|
@ -2631,6 +2642,7 @@ void ProtoModel::decode(Decoder &decoder)
|
|||
}
|
||||
sort(effectlist.begin(),effectlist.end(),EffectRecord::compareByAddress);
|
||||
sort(likelytrash.begin(),likelytrash.end());
|
||||
sort(internalstorage.begin(),internalstorage.end());
|
||||
if (!sawlocalrange)
|
||||
defaultLocalRange();
|
||||
if (!sawparamrange)
|
||||
|
@ -2740,19 +2752,20 @@ void ProtoModelMerged::intersectEffects(const vector<EffectRecord> &efflist)
|
|||
effectlist.swap(newlist);
|
||||
}
|
||||
|
||||
/// The \e likely-trash locations are intersected. Anything in \b this that is not also in the
|
||||
/// given \e likely-trash list is removed.
|
||||
/// \param trashlist is the given \e likely-trash list
|
||||
void ProtoModelMerged::intersectLikelyTrash(const vector<VarnodeData> &trashlist)
|
||||
/// The intersection of two containers of register Varnodes is calculated, and the result is
|
||||
/// placed in the first container, replacing the original contents. The containers must already be sorted.
|
||||
/// \param regList1 is the first container
|
||||
/// \param regList2 is the second container
|
||||
void ProtoModelMerged::intersectRegisters(vector<VarnodeData> ®List1,const vector<VarnodeData> ®List2)
|
||||
|
||||
{
|
||||
vector<VarnodeData> newlist;
|
||||
|
||||
int4 i=0;
|
||||
int4 j=0;
|
||||
while((i<likelytrash.size())&&(j<trashlist.size())) {
|
||||
const VarnodeData &trs1( likelytrash[i] );
|
||||
const VarnodeData &trs2( trashlist[j] );
|
||||
while((i<regList1.size())&&(j<regList2.size())) {
|
||||
const VarnodeData &trs1( regList1[i] );
|
||||
const VarnodeData &trs2( regList2[j] );
|
||||
|
||||
if (trs1 < trs2)
|
||||
i += 1;
|
||||
|
@ -2764,7 +2777,7 @@ void ProtoModelMerged::intersectLikelyTrash(const vector<VarnodeData> &trashlist
|
|||
j += 1;
|
||||
}
|
||||
}
|
||||
likelytrash = newlist;
|
||||
regList1.swap(newlist);
|
||||
}
|
||||
|
||||
/// \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))
|
||||
throw LowlevelError("Cannot merge prototype models with different inject ids");
|
||||
intersectEffects(model->effectlist);
|
||||
intersectLikelyTrash(model->likelytrash);
|
||||
intersectRegisters(likelytrash,model->likelytrash);
|
||||
intersectRegisters(internalstorage,model->internalstorage);
|
||||
// Take the union of the localrange and paramrange
|
||||
set<Range>::const_iterator 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_RETURNSYM; ///< Marshaling element \<returnsym>
|
||||
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
|
||||
struct ParamUnassignedError : public LowlevelError {
|
||||
|
@ -749,6 +750,7 @@ class ProtoModel {
|
|||
const ProtoModel *compatModel; ///< The model \b this is a copy of
|
||||
vector<EffectRecord> effectlist; ///< List of side-effects
|
||||
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 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
|
||||
|
@ -834,6 +836,8 @@ public:
|
|||
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 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
|
||||
///
|
||||
|
@ -1068,7 +1072,7 @@ public:
|
|||
class ProtoModelMerged : public ProtoModel {
|
||||
vector<ProtoModel *> modellist; ///< Constituent models being merged
|
||||
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:
|
||||
ProtoModelMerged(Architecture *g) : ProtoModel(g) {} ///< Constructor
|
||||
virtual ~ProtoModelMerged(void) {} ///< Destructor
|
||||
|
@ -1539,6 +1543,8 @@ public:
|
|||
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 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 characterizeAsOutput(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())
|
||||
return pop_fail;
|
||||
}
|
||||
if (op->isStoreUnmapped())
|
||||
return pop_fail;
|
||||
op = vn->getDef();
|
||||
if (op == (PcodeOp *)0) break;
|
||||
OpCode opc = op->code();
|
||||
|
|
|
@ -1266,6 +1266,6 @@ ElementId ELEM_VAL = ElementId("val",8);
|
|||
ElementId ELEM_VALUE = ElementId("value",9);
|
||||
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
|
||||
|
|
|
@ -114,7 +114,8 @@ public:
|
|||
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
|
||||
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:
|
||||
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
|
||||
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
|
||||
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
|
||||
bool usesSpacebasePtr(void) const { return ((flags&PcodeOp::spacebase_ptr)!=0); }
|
||||
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,0);
|
||||
data.opSetOpcode(op, CPUI_COPY );
|
||||
if (op->isStoreUnmapped()) {
|
||||
data.getScopeLocal()->markNotMapped(baseoff, offoff, size, false);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -831,6 +831,55 @@ bool Varnode::isConstantExtended(uint8 *val) const
|
|||
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
|
||||
/// 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.
|
||||
|
|
|
@ -291,6 +291,8 @@ public:
|
|||
}
|
||||
|
||||
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
|
||||
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?
|
||||
|
|
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>
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<element name="internal_storage">
|
||||
<oneOrMore>
|
||||
<ref name="varnode_tags_type"/>
|
||||
</oneOrMore>
|
||||
</element>
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<element name="localrange">
|
||||
<ref name="rangelist_type"/>
|
||||
|
|
|
@ -50,6 +50,7 @@ public class PrototypeModel {
|
|||
private Varnode[] killedbycall; // Memory ranges definitely affected by calls
|
||||
private Varnode[] returnaddress; // Memory used to store the return address
|
||||
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 AddressSet localRange; // Range on the stack considered for local storage
|
||||
private AddressSet paramRange; // Range on the stack considered for parameter storage
|
||||
|
@ -81,6 +82,7 @@ public class PrototypeModel {
|
|||
killedbycall = model.killedbycall;
|
||||
returnaddress = model.returnaddress;
|
||||
likelytrash = model.likelytrash;
|
||||
internalstorage = model.internalstorage;
|
||||
compatModel = model;
|
||||
localRange = new AddressSet(model.localRange);
|
||||
paramRange = new AddressSet(model.paramRange);
|
||||
|
@ -101,6 +103,7 @@ public class PrototypeModel {
|
|||
killedbycall = null;
|
||||
returnaddress = null;
|
||||
likelytrash = null;
|
||||
internalstorage = null;
|
||||
compatModel = null;
|
||||
localRange = null;
|
||||
paramRange = null;
|
||||
|
@ -140,6 +143,16 @@ public class PrototypeModel {
|
|||
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
|
||||
*/
|
||||
|
@ -456,6 +469,11 @@ public class PrototypeModel {
|
|||
encodeVarnodes(encoder, likelytrash);
|
||||
encoder.closeElement(ELEM_LIKELYTRASH);
|
||||
}
|
||||
if (internalstorage != null) {
|
||||
encoder.openElement(ELEM_INTERNAL_STORAGE);
|
||||
encodeVarnodes(encoder, internalstorage);
|
||||
encoder.closeElement(ELEM_INTERNAL_STORAGE);
|
||||
}
|
||||
if (returnaddress != null) {
|
||||
encoder.openElement(ELEM_RETURNADDRESS);
|
||||
encodeVarnodes(encoder, returnaddress);
|
||||
|
@ -633,6 +651,9 @@ public class PrototypeModel {
|
|||
else if (elName.equals("likelytrash")) {
|
||||
likelytrash = readVarnodes(parser, cspec);
|
||||
}
|
||||
else if (elName.equals("internal_storage")) {
|
||||
internalstorage = readVarnodes(parser, cspec);
|
||||
}
|
||||
else if (elName.equals("localrange")) {
|
||||
localRange = readAddressSet(parser, cspec);
|
||||
}
|
||||
|
@ -741,6 +762,9 @@ public class PrototypeModel {
|
|||
if (!SystemUtilities.isArrayEqual(likelytrash, obj.likelytrash)) {
|
||||
return false;
|
||||
}
|
||||
if (!SystemUtilities.isArrayEqual(internalstorage, obj.internalstorage)) {
|
||||
return false;
|
||||
}
|
||||
String compatName = (compatModel != null) ? compatModel.getName() : "";
|
||||
String compatNameOp2 = (obj.compatModel != null) ? obj.compatModel.getName() : "";
|
||||
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_RETURNSYM = new ElementId("returnsym", 172);
|
||||
public static final ElementId ELEM_UNAFFECTED = new ElementId("unaffected", 173);
|
||||
public static final ElementId ELEM_INTERNAL_STORAGE = new ElementId("internal_storage", 286);
|
||||
|
||||
// options
|
||||
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);
|
||||
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="f30"/>
|
||||
</unaffected>
|
||||
<internal_storage>
|
||||
<register name="gp"/> <!-- Compilers may save gp to the stack before a call and restore it afterward -->
|
||||
</internal_storage>
|
||||
<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 -->
|
||||
|
@ -101,6 +104,9 @@
|
|||
<unaffected>
|
||||
<register name="sp"/>
|
||||
</unaffected>
|
||||
<internal_storage>
|
||||
<register name="gp"/> <!-- Compilers may save gp to the stack before a call and restore it afterward -->
|
||||
</internal_storage>
|
||||
</prototype>
|
||||
|
||||
|
||||
|
|
|
@ -78,6 +78,9 @@
|
|||
<register name="f28"/>
|
||||
<register name="f30"/>
|
||||
</unaffected>
|
||||
<internal_storage>
|
||||
<register name="gp"/> <!-- Compilers may save gp to the stack before a call and restore it afterward -->
|
||||
</internal_storage>
|
||||
<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 -->
|
||||
|
@ -101,6 +104,9 @@
|
|||
<unaffected>
|
||||
<register name="sp"/>
|
||||
</unaffected>
|
||||
<internal_storage>
|
||||
<register name="gp"/> <!-- Compilers may save gp to the stack before a call and restore it afterward -->
|
||||
</internal_storage>
|
||||
</prototype>
|
||||
|
||||
</compiler_spec>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue