diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index 3bba734922..c3ddbb8d1f 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -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| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index adfb835e98..9f70f04449 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -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 &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::const_iterator iter = proto.internalBegin(); + vector::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::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) ); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index 71a281c6b8..9dabc37c2e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -555,7 +555,6 @@ class ActionDeadCode : public Action { static bool neverConsumed(Varnode *vn,Funcdata &data); static void markConsumedParameters(FuncCallSpecs *fc,vector &worklist); static uintb gatherConsumedReturn(Funcdata &data); - static bool isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount); static bool lastChanceLoad(Funcdata &data,vector &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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc index 341f231c98..247c90c598 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc @@ -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 &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 &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 ®List1,const vector ®List2) { vector newlist; int4 i=0; int4 j=0; - while((i &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::const_iterator iter; for(iter=model->localrange.begin();iter!=model->localrange.end();++iter) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh index d3e70953dc..55ec487467 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh @@ -58,6 +58,7 @@ extern ElementId ELEM_RESOLVEPROTOTYPE; ///< Marshaling element \ extern ElementId ELEM_RETURNSYM; ///< Marshaling element \ extern ElementId ELEM_UNAFFECTED; ///< Marshaling element \ +extern ElementId ELEM_INTERNAL_STORAGE; ///< Marshaling element \ /// \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 effectlist; ///< List of side-effects vector likelytrash; ///< Storage locations potentially carrying \e trash values + vector 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::const_iterator effectEnd(void) const { return effectlist.end(); } ///< Get an iterator to the last EffectRecord vector::const_iterator trashBegin(void) const { return likelytrash.begin(); } ///< Get an iterator to the first \e likelytrash vector::const_iterator trashEnd(void) const { return likelytrash.end(); } ///< Get an iterator to the last \e likelytrash + vector::const_iterator internalBegin(void) const { return internalstorage.begin(); } ///< Get an iterator to the first \e internalstorage + vector::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 modellist; ///< Constituent models being merged void intersectEffects(const vector &efflist); ///< Fold EffectRecords into \b this model - void intersectLikelyTrash(const vector &trashlist); ///< Fold \e likelytrash locations into \b this model + static void intersectRegisters(vector ®List1,const vector ®List2); public: ProtoModelMerged(Architecture *g) : ProtoModel(g) {} ///< Constructor virtual ~ProtoModelMerged(void) {} ///< Destructor @@ -1539,6 +1543,8 @@ public: vector::const_iterator effectEnd(void) const; ///< Get iterator to end of EffectRecord list vector::const_iterator trashBegin(void) const; ///< Get iterator to front of \e likelytrash list vector::const_iterator trashEnd(void) const; ///< Get iterator to end of \e likelytrash list + vector::const_iterator internalBegin(void) const { return model->internalBegin(); } ///< Get iterator to front of \e internalstorage list + vector::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; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc index 9cfd1b1b34..864472d70b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc @@ -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(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc index 22f216ce0b..9352092742 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh index b6c8cbe0d4..d2bf498d94 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index 6907c0d6a0..74ec9ea4b7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -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; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc index 6421700b44..4079c2661d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc @@ -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. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh index 487d8918b0..a881505f8d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh @@ -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? diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/gp.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/gp.xml new file mode 100644 index 0000000000..26d6f7e843 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/gp.xml @@ -0,0 +1,35 @@ + + + + +3c1c0041279c00200399e02127bdffd0 +8f990094afbf0024afb00028afbc0020 +008080250320f80927a400188fbc0020 +020028258f8400908f99009800000000 +0320f809248400008fbf00248fa2001c +8fb0002003e0000827bd0028 + + +48656c6c6f00 + + +004100a00041003000410024 + + + + + + +populate\(axStack_18\); +printf\("Hello",param_1\); + diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg index f1234a4b7b..faacc10022 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg @@ -544,6 +544,14 @@ + + + + + + + + diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModel.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModel.java index dcec0d9b5e..804105b28f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModel.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModel.java @@ -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)) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java index 430be5e85e..bd407a6426 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java @@ -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); } diff --git a/Ghidra/Processors/MIPS/data/languages/mips32be.cspec b/Ghidra/Processors/MIPS/data/languages/mips32be.cspec index 3c52fcde9d..c85f28c669 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32be.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32be.cspec @@ -78,6 +78,9 @@ + + + @@ -101,6 +104,9 @@ + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32le.cspec b/Ghidra/Processors/MIPS/data/languages/mips32le.cspec index c439a5d628..aa246286c7 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32le.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32le.cspec @@ -78,6 +78,9 @@ + + + @@ -101,6 +104,9 @@ + + +