diff --git a/Ghidra/Features/Decompiler/build.gradle b/Ghidra/Features/Decompiler/build.gradle index 23027526ae..55b09c0f2d 100644 --- a/Ghidra/Features/Decompiler/build.gradle +++ b/Ghidra/Features/Decompiler/build.gradle @@ -295,6 +295,7 @@ model { include "rangeutil.cc" include "ruleaction.cc" include "subflow.cc" + include "transform.cc" include "blockaction.cc" include "merge.cc" include "double.cc" diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/Makefile b/Ghidra/Features/Decompiler/src/decompile/cpp/Makefile index c44d44ebe7..e6d32c79c1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/Makefile +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/Makefile @@ -79,7 +79,7 @@ DECCORE=capability architecture options graph cover block cast typeop database c type variable varmap jumptable emulate emulateutil flow userop \ funcdata funcdata_block funcdata_op funcdata_varnode pcodeinject \ heritage prefersplit rangeutil ruleaction subflow blockaction merge double \ - coreaction condexe override dynamic crc32 prettyprint \ + transform coreaction condexe override dynamic crc32 prettyprint \ printlanguage printc printjava memstate opbehavior paramid $(COREEXT_NAMES) # Files used for any project that use the sleigh decoder SLEIGH= sleigh pcodeparse pcodecompile sleighbase slghsymbol \ diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index 0af0a3670c..a2abeb6b44 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -220,6 +220,42 @@ AddrSpace *Architecture::getSpaceBySpacebase(const Address &loc,int4 size) const return (AddrSpace *)0; } +/// Look-up the laned register record associated with a specific storage location. Currently, the +/// record is only associated with the \e size of the storage, not its address. If there is no +/// associated record, null is returned. +/// \param loc is the starting address of the storage location +/// \param size is the size of the storage in bytes +/// \return the matching LanedRegister record or null +const LanedRegister *Architecture::getLanedRegister(const Address &loc,int4 size) const + +{ + int4 min = 0; + int4 max = lanerecords.size() - 1; + while(min <= max) { + int4 mid = (min + max) / 2; + int4 sz = lanerecords[mid].getWholeSize(); + if (sz < size) + min = mid + 1; + else if (size < sz) + max = mid - 1; + else + return &lanerecords[mid]; + } + return (const LanedRegister *)0; +} + +/// Return a size intended for comparison with a Varnode size to immediately determine if +/// the Varnode is a potential laned register. If there are no laned registers for the architecture, +/// -1 is returned. +/// \return the size in bytes of the smallest laned register or -1. +int4 Architecture::getMinimumLanedRegisterSize(void) const + +{ + if (lanerecords.empty()) + return -1; + return lanerecords[0].getWholeSize(); +} + /// The default model is used whenever an explicit model is not known /// or can't be determined. /// \param nm is the name of the model to set @@ -811,6 +847,32 @@ void Architecture::parseIncidentalCopy(const Element *el) } } +/// Look for \ tags that have a \e vector_lane_size attribute. +/// Record these so that the decompiler can split large registers into appropriate lane size pieces. +/// \param el is the XML element +void Architecture::parseLaneSizes(const Element *el) + +{ + vector maskList; + const List &childList(el->getChildren()); + List::const_iterator iter; + + LanedRegister lanedRegister; // Only allocate once + for(iter=childList.begin();iter!=childList.end();++iter) { + if (lanedRegister.restoreXml(*iter, this)) { + int4 sizeIndex = lanedRegister.getWholeSize(); + while (maskList.size() <= sizeIndex) + maskList.push_back(0); + maskList[sizeIndex] |= lanedRegister.getSizeBitMask(); + } + } + lanerecords.clear(); + for(int4 i=0;i element /// \param el is the XML element void Architecture::parseStackPointer(const Element *el) @@ -976,6 +1038,7 @@ void Architecture::parseProcessorConfig(DocumentStorage &store) else if (elname == "segmentop") userops.parseSegmentOp(*iter,this); else if (elname == "register_data") { + parseLaneSizes(*iter); } else if (elname == "segmented_address") { } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index cb361c36bd..7676d3278c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -30,6 +30,7 @@ #include "comment.hh" #include "userop.hh" #include "options.hh" +#include "transform.hh" #include "prefersplit.hh" #ifdef CPUI_STATISTICS @@ -151,6 +152,7 @@ public: vector inst; ///< Registered p-code instructions UserOpManage userops; ///< Specifically registered user-defined p-code ops vector splitrecords; ///< registers that we would prefer to see split for this processor + vector lanerecords; ///< Vector registers that have preferred lane sizes ActionDatabase allacts; ///< Actions that can be applied in this architecture bool loadersymbols_parsed; ///< True if loader symbols have been read #ifdef CPUI_STATISTICS @@ -165,6 +167,8 @@ public: bool hasModel(const string &nm) const; ///< Does this Architecture have a specific PrototypeModel bool highPtrPossible(const Address &loc,int4 size) const; ///< Are pointers possible to the given location? AddrSpace *getSpaceBySpacebase(const Address &loc,int4 size) const; ///< Get space associated with a \e spacebase register + const LanedRegister *getLanedRegister(const Address &loc,int4 size) const; ///< Get LanedRegister associated with storage + int4 getMinimumLanedRegisterSize(void) const; ///< Get the minimum size of a laned register in bytes void setDefaultModel(const string &nm); ///< Set the default PrototypeModel void clearAnalysis(Funcdata *fd); ///< Clear analysis specific to a function void readLoaderSymbols(void); ///< Read any symbols from loader into database @@ -255,11 +259,12 @@ protected: void parseProtoEval(const Element *el); ///< Apply prototype evaluation configuration void parseDefaultProto(const Element *el); ///< Apply default prototype model configuration void parseGlobal(const Element *el); ///< Apply global space configuration - void addOtherSpace(void); ////add OTHER space and all of its overlays to the symboltab + void addOtherSpace(void); ///< Add OTHER space and all of its overlays to the symboltab void parseReadOnly(const Element *el); ///< Apply read-only region configuration void parseVolatile(const Element *el); ///< Apply volatile region configuration void parseReturnAddress(const Element *el); ///< Apply return address configuration void parseIncidentalCopy(const Element *el); ///< Apply incidental copy configuration + void parseLaneSizes(const Element *el); ///< Apply lane size configuration void parseStackPointer(const Element *el); ///< Apply stack pointer configuration void parseDeadcodeDelay(const Element *el); ///< Apply dead-code delay configuration void parseFuncPtrAlign(const Element *el); ///< Apply function pointer alignment configuration diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index be35424a27..3f1f9a9ef0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -16,6 +16,7 @@ #include "coreaction.hh" #include "condexe.hh" #include "double.hh" +#include "subflow.hh" /// \brief A stack equation struct StackEqn { @@ -494,6 +495,121 @@ int4 ActionStackPtrFlow::apply(Funcdata &data) return 0; } +/// \brief Examine the PcodeOps using the given Varnode to determine possible lane sizes +/// +/// Run through the defining op and any descendant ops of the given Varnode, looking for +/// CPUI_PIECE and CPUI_SUBPIECE. Use these to determine possible lane sizes and +/// register them with the given LanedRegister object. +/// \param vn is the given Varnode +/// \param allowedLanes is used to determine if a putative lane size is allowed +/// \param checkLanes collects the possible lane sizes +void ActionLaneDivide::collectLaneSizes(Varnode *vn,const LanedRegister &allowedLanes,LanedRegister &checkLanes) + +{ + list::const_iterator iter = vn->beginDescend(); + int4 step = 0; // 0 = descendants, 1 = def, 2 = done + if (iter == vn->endDescend()) { + step = 1; + } + while(step < 2) { + int4 curSize; // Putative lane size + if (step == 0) { + PcodeOp *op = *iter; + ++iter; + if (iter == vn->endDescend()) + step = 1; + if (op->code() != CPUI_SUBPIECE) continue; // Is the big register split into pieces + curSize = op->getOut()->getSize(); + } + else { + step = 2; + if (!vn->isWritten()) continue; + PcodeOp *op = vn->getDef(); + if (op->code() != CPUI_PIECE) continue; // Is the big register formed from smaller pieces + curSize = op->getIn(0)->getSize(); + int4 tmpSize = op->getIn(1)->getSize(); + if (tmpSize < curSize) + curSize = tmpSize; + } + if (allowedLanes.allowedLane(curSize)) + checkLanes.addLaneSize(curSize); // Register this possible size + } +} + +/// \brief Search for a likely lane size and try to divide a single Varnode into these lanes +/// +/// There are different ways to search for a lane size: +/// +/// Mode 0: Collect putative lane sizes based on the local ops using the Varnode. Attempt +/// to divide based on each of those lane sizes in turn. +/// +/// Mode 1: Similar to mode 0, except we allow for SUBPIECE operations that truncate to +/// variables that are smaller than the lane size. +/// +/// Mode 2: Attempt to divide based on a default lane size. +/// \param data is the function being transformed +/// \param vn is the given single Varnode +/// \param lanedRegister is acceptable set of lane sizes for the Varnode +/// \param mode is the lane size search mode (0, 1, or 2) +/// \return \b true if the Varnode (and its data-flow) was successfully split +bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,int4 mode) + +{ + LanedRegister checkLanes; // Lanes we are going to try, initialized to no lanes + bool allowDowncast = (mode > 0); + if (mode < 2) + collectLaneSizes(vn,lanedRegister,checkLanes); + else { + checkLanes.addLaneSize(4); // Default lane size + } + LanedRegister::const_iterator enditer = checkLanes.end(); + for(LanedRegister::const_iterator iter=checkLanes.begin();iter!=enditer;++iter) { + int4 curSize = *iter; + LaneDescription description(lanedRegister.getWholeSize(),curSize); // Lane scheme dictated by curSize + LaneDivide laneDivide(&data,vn,description,allowDowncast); + if (laneDivide.doTrace()) { + laneDivide.apply(); + count += 1; // Indicate a change was made + return true; + } + } + return false; +} + +int4 ActionLaneDivide::apply(Funcdata &data) + +{ + map::const_iterator iter; + for(int4 mode=0;mode<3;++mode) { + bool allStorageProcessed = true; + for(iter=data.beginLaneAccess();iter!=data.endLaneAccess();++iter) { + const LanedRegister *lanedReg = (*iter).second; + Address addr = (*iter).first.getAddr(); + int4 sz = (*iter).first.size; + VarnodeLocSet::const_iterator viter = data.beginLoc(sz,addr); + VarnodeLocSet::const_iterator venditer = data.endLoc(sz,addr); + bool allVarnodesProcessed = true; + while(viter != venditer) { + Varnode *vn = *viter; + if (processVarnode(data, vn, *lanedReg, mode)) { + viter = data.beginLoc(sz,addr); + venditer = data.endLoc(sz, addr); // Recalculate bounds + allVarnodesProcessed = true; + } + else { + ++viter; + allVarnodesProcessed = false; + } + } + if (!allVarnodesProcessed) + allStorageProcessed = false; + } + if (allStorageProcessed) break; + } + data.clearLanedAccessMap(); + return 0; +} + int4 ActionSegmentize::apply(Funcdata &data) { @@ -820,12 +936,16 @@ int4 ActionShadowVar::apply(Funcdata &data) /// \brief Determine if given Varnode might be a pointer constant. /// -/// If it is a pointer, return the symbol it points to, or NULL otherwise. +/// If it is a pointer, return the symbol it points to, or NULL otherwise. If it is determined +/// that the Varnode is a pointer to a specific symbol, the encoding of the full pointer is passed back. +/// Usually this is just the constant value of the Varnode, but in this case of partial pointers +/// (like \e near pointers) the full pointer may contain additional information. /// \param spc is the address space being pointed to /// \param vn is the given Varnode /// \param op is the lone descendant of the Varnode /// \param slot is the slot index of the Varnode /// \param rampoint will hold the Address of the resolved symbol +/// \param fullEncoding will hold the full pointer encoding being passed back /// \param data is the function being analyzed /// \return the recovered symbol or NULL SymbolEntry *ActionConstantPtr::isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op,int4 slot, @@ -1025,14 +1145,30 @@ int4 ActionDeindirect::apply(Funcdata &data) return 0; } +/// Check if the given Varnode has a matching LanedRegister record. If so, add its +/// storage location to the given function's laned access list. +/// \param data is the given function +/// \param vn is the given Varnode +void ActionVarnodeProps::markLanedVarnode(Funcdata &data,Varnode *vn) + +{ + if (vn->isConstant()) return; + Architecture *glb = data.getArch(); + const LanedRegister *lanedRegister = glb->getLanedRegister(vn->getAddr(),vn->getSize()); + if (lanedRegister != (const LanedRegister *)0) + data.markLanedVarnode(vn,lanedRegister); +} + int4 ActionVarnodeProps::apply(Funcdata &data) { Architecture *glb = data.getArch(); bool cachereadonly = glb->readonlypropagate; - if (glb->userops.getVolatileRead() == (VolatileReadOp *)0) { - if (!cachereadonly) - return 0; // Nothing to do to special properties + int4 minLanedSize = 1000000; // Default size meant to filter no Varnodes + if (!data.isLanedRegComplete()) { + int4 sz = glb->getMinimumLanedRegisterSize(); + if (sz > 0) + minLanedSize = sz; } VarnodeLocSet::const_iterator iter; Varnode *vn; @@ -1041,6 +1177,9 @@ int4 ActionVarnodeProps::apply(Funcdata &data) while(iter != data.endLoc()) { vn = *iter++; // Advance iterator in case vn is deleted if (vn->isAnnotation()) continue; + int4 vnSize = vn->getSize(); + if (vnSize >= minLanedSize) + markLanedVarnode(data, vn); if (vn->hasActionProperty()) { if (cachereadonly&&vn->isReadOnly()) { if (data.fillinReadOnly(vn)) // Try to replace vn with its lookup in LoadImage @@ -1050,7 +1189,7 @@ int4 ActionVarnodeProps::apply(Funcdata &data) if (data.replaceVolatile(vn)) count += 1; // Try to replace vn with pcode op } - else if (((vn->getNZMask() & vn->getConsume())==0)&&(vn->getSize()<=sizeof(uintb))) { + else if (((vn->getNZMask() & vn->getConsume())==0)&&(vnSize<=sizeof(uintb))) { // FIXME: uintb should be arbitrary precision if (vn->isConstant()) continue; // Don't replace a constant if (vn->isWritten()) { @@ -1070,6 +1209,7 @@ int4 ActionVarnodeProps::apply(Funcdata &data) } } } + data.setLanedRegGenerated(); return 0; } @@ -1860,8 +2000,11 @@ int4 ActionLikelyTrash::apply(Funcdata &data) for(uint4 i=0;icode() == CPUI_INDIRECT) - data.truncateIndirect(indlist[i]); + if (op->code() == CPUI_INDIRECT) { + // Trucate data-flow through INDIRECT, turning it into indirect creation + data.opSetInput(op,data.newConstant(op->getOut()->getSize(), 0),0); + data.markIndirectCreation(op,false); + } else if (op->code() == CPUI_INT_AND) { data.opSetInput(op,data.newConstant(op->getIn(1)->getSize(),0),1); } @@ -4628,6 +4771,7 @@ void universal_action(Architecture *conf) conf->extra_pool_rules.clear(); // Rules are now absorbed into universal } actstackstall->addAction( actprop ); + actstackstall->addAction( new ActionLaneDivide("base") ); actstackstall->addAction( new ActionMultiCse("analysis") ); actstackstall->addAction( new ActionShadowVar("analysis") ); actstackstall->addAction( new ActionDeindirect("deindirect") ); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index 07cc18900d..deeabd785d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -97,6 +97,24 @@ public: virtual int4 apply(Funcdata &data); }; +/// \brief Find Varnodes with a vectorized lane scheme and attempt to split the lanes +/// +/// The Architecture lists (vector) registers that may be used to perform parallelized operations +/// on \b lanes within the register. This action looks for these registers as Varnodes, determines +/// if a particular lane scheme makes sense in terms of the function's data-flow, and then +/// rewrites the data-flow so that the lanes become explicit Varnodes. +class ActionLaneDivide : public Action { + void collectLaneSizes(Varnode *vn,const LanedRegister &allowedLanes,LanedRegister &checkLanes); + bool processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,int4 mode); +public: + ActionLaneDivide(const string &g) : Action(rule_onceperfunc,"lanedivide",g) {} ///< Constructor + virtual Action *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Action *)0; + return new ActionLaneDivide(getGroup()); + } + virtual int4 apply(Funcdata &data); +}; + /// \brief Make sure pointers into segmented spaces have the correct form. /// /// Convert user-defined ops defined as segment p-code ops by a cspec tag into the internal CPUI_SEGMENTOP @@ -185,8 +203,15 @@ public: virtual int4 apply(Funcdata &data); }; -/// \brief Transform read-only variables to constants +/// \brief Transform based on Varnode properties, such as \e read-only and \e volatile +/// +/// This performs various transforms that are based on Varnode properties. +/// - Read-only Varnodes are converted to the underlying constant +/// - Volatile Varnodes are converted read/write functions +/// - Varnodes whose values are not consumed are replaced with constant 0 Varnodes +/// - Large Varnodes are flagged for lane analysis class ActionVarnodeProps : public Action { + void markLanedVarnode(Funcdata &data,Varnode *vn); ///< Mark possible laned register storage public: ActionVarnodeProps(const string &g) : Action(0,"varnodeprops",g) {} ///< Constructor virtual Action *clone(const ActionGroupList &grouplist) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 137b23c5e7..1be92c13e0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -56,7 +56,8 @@ class Funcdata { restart_pending = 0x200, ///< Analysis must be restarted (because of new override info) unimplemented_present = 0x400, ///< Set if function contains unimplemented instructions baddata_present = 0x800, ///< Set if function flowed into bad data - double_precis_on = 0x1000 ///< Set if we are performing double precision recovery + double_precis_on = 0x1000, ///< Set if we are performing double precision recovery + big_varnodes_generated = 0x2000 ///< Set when search for laned registers is complete }; uint4 flags; ///< Boolean properties associated with \b this function uint4 clean_up_index; ///< Creation index of first Varnode created after start of cleanup @@ -80,6 +81,7 @@ class Funcdata { Merge covermerge; ///< Variable range intersection algorithms ParamActive *activeoutput; ///< Data for assessing which parameters are passed to \b this function Override localoverride; ///< Overrides of data-flow, prototypes, etc. that are local to \b this function + map lanedMap; ///< Current storage locations which may be laned registers // Low level Varnode functions void setVarnodeProperties(Varnode *vn) const; ///< Look-up boolean properties and data-type information @@ -130,6 +132,8 @@ public: bool isTypeRecoveryOn(void) const { return ((flags&typerecovery_on)!=0); } ///< Has data-type recovery processes started bool hasNoCode(void) const { return ((flags & no_code)!=0); } ///< Return \b true if \b this function has no code body void setNoCode(bool val) { if (val) flags |= no_code; else flags &= ~no_code; } ///< Toggle whether \b this has a body + bool isLanedRegComplete(void) const { return ((flags&big_varnodes_generated)!=0); } ///< Have potential laned registers been generated + void setLanedRegGenerated(void) { flags |= big_varnodes_generated; } ///< Mark that laned registers have been collected /// \brief Toggle whether \b this is being used for jump-table recovery /// @@ -346,13 +350,18 @@ public: /// \brief End of (input or free) Varnodes at a given storage address VarnodeDefSet::const_iterator endDef(uint4 fl,const Address &addr) const { return vbank.endDef(fl,addr); } + void markLanedVarnode(Varnode *vn,const LanedRegister *lanedReg); ///< Mark Varnode as potential laned register + map::const_iterator beginLaneAccess(void) const { return lanedMap.begin(); } ///< Beginning iterator over laned accesses + map::const_iterator endLaneAccess(void) const { return lanedMap.end(); } ///< Ending iterator over laned accesses + void clearLanedAccessMap(void) { lanedMap.clear(); } ///< Clear records from the laned access list + HighVariable *findHigh(const string &name) const; ///< Find a high-level variable by name void mapGlobals(void); ///< Make sure there is a Symbol entry for all global Varnodes bool checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,const ParamTrial &trial) const; bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial) const; bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial) const; bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes); - void splitVarnode(Varnode *vn,int4 lowsize,Varnode *&vnlo,Varnode *& vnhi); + void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset); bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image bool replaceVolatile(Varnode *vn); ///< Replace accesses of the given Varnode with \e volatile operations void markIndirectOnly(void); ///< Mark \e illegal \e input Varnodes used only in INDIRECTs @@ -385,9 +394,8 @@ public: PcodeOp *cloneOp(const PcodeOp *op,const SeqNum &seq); /// Clone a PcodeOp into \b this function PcodeOp *canonicalReturnOp(void) const; /// Find a representative CPUI_RETURN op for \b this function PcodeOp *newIndirectOp(PcodeOp *indeffect,const Address &addr,int4 size,uint4 extraFlags); - void setIndirectCreation(PcodeOp *op,PcodeOp *indeffect,Varnode *outvn,bool possibleout); PcodeOp *newIndirectCreation(PcodeOp *indeffect,const Address &addr,int4 size,bool possibleout); - void truncateIndirect(PcodeOp *indop); ///< Convert CPUI_INDIRECT into an \e indirect \e creation + void markIndirectCreation(PcodeOp *indop,bool possibleOutput); ///< Convert CPUI_INDIRECT into an \e indirect \e creation PcodeOp *findOp(const SeqNum &sq) { return obank.findOp(sq); } ///< Find PcodeOp with given sequence number void opInsertBefore(PcodeOp *op,PcodeOp *follow); ///< Insert given PcodeOp before a specific op void opInsertAfter(PcodeOp *op,PcodeOp *prev); ///< Insert given PcodeOp after a specific op diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc index a4ec654f6b..c76eb65df9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc @@ -663,31 +663,6 @@ PcodeOp *Funcdata::newIndirectOp(PcodeOp *indeffect,const Address &addr,int4 siz return newop; } -/// \brief Turn given PcodeOp into a CPUI_INDIRECT that \e indirectly \e creates a Varnode -/// -/// An \e indirectly \e created Varnode effectively has no data-flow before the INDIRECT op -/// that defines it, and the value contained by the Varnode is not explicitly calculable. -/// \param op is the given PcodeOp to convert to a CPUI_INDIRECT -/// \param indeffect is the p-code causing the indirect effect -/// \param outvn is the (preexisting) Varnode that will be marked as \e created by the INDIRECT -/// \param possibleout is \b true if the output should be treated as a \e directwrite. -void Funcdata::setIndirectCreation(PcodeOp *op,PcodeOp *indeffect,Varnode *outvn,bool possibleout) - -{ - Varnode *newin; - - newin = newConstant(outvn->getSize(),0); - op->flags |= PcodeOp::indirect_creation; - opSetOutput(op,outvn); - if (!possibleout) - newin->flags |= Varnode::indirect_creation; - outvn->flags |= Varnode::indirect_creation; - opSetOpcode(op,CPUI_INDIRECT); - opSetInput(op,newin,0); - opSetInput(op,newVarnodeIop(indeffect),1); - opInsertBefore(op,indeffect); -} - /// \brief Build a CPUI_INDIRECT op that \e indirectly \e creates a Varnode /// /// An \e indirectly \e created Varnode effectively has no data-flow before the INDIRECT op @@ -718,22 +693,24 @@ PcodeOp *Funcdata::newIndirectCreation(PcodeOp *indeffect,const Address &addr,in return newop; } -/// Data-flow through the given CPUI_INDIRECT op is truncated causing the output Varnode -/// to be \e indirectly \e created. +/// Data-flow through the given CPUI_INDIRECT op is marked so that the output Varnode +/// is considered \e indirectly \e created. /// An \e indirectly \e created Varnode effectively has no data-flow before the INDIRECT op /// that defines it, and the value contained by the Varnode is not explicitly calculable. /// \param indop is the given CPUI_INDIRECT op -void Funcdata::truncateIndirect(PcodeOp *indop) +/// \param possibleOutput is \b true if INDIRECT should be marked as a possible call output +void Funcdata::markIndirectCreation(PcodeOp *indop,bool possibleOutput) { Varnode *outvn = indop->getOut(); - Varnode *newin = newConstant(outvn->getSize(),0); + Varnode *in0 = indop->getIn(0); indop->flags |= PcodeOp::indirect_creation; - newin->flags |= Varnode::indirect_creation; + if (!in0->isConstant()) + throw LowlevelError("Indirect creation not properly formed"); + if (!possibleOutput) + in0->flags |= Varnode::indirect_creation; outvn->flags |= Varnode::indirect_creation; - - opSetInput(indop,newin,0); } /// \brief Generate raw p-code for the function diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc index 65a6e788db..4ec6fd511c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc @@ -286,6 +286,21 @@ void Funcdata::destroyVarnode(Varnode *vn) vbank.destroy(vn); } +/// Record the given Varnode as a potential laned register access. +/// The address and size of the Varnode is recorded, anticipating that new +/// Varnodes at the same storage location may be created +/// \param vn is the given Varnode to mark +/// \param lanedReg is the laned register record to associate with the Varnode +void Funcdata::markLanedVarnode(Varnode *vn,const LanedRegister *lanedReg) + +{ + VarnodeData storage; + storage.space = vn->getSpace(); + storage.offset = vn->getOffset(); + storage.size = vn->getSize(); + lanedMap[storage] = lanedReg; +} + /// Look up the Symbol visible in \b this function's Scope and return the HighVariable /// associated with it. If the Symbol doesn't exist or there is no Varnode holding at least /// part of the value of the Symbol, NULL is returned. @@ -479,34 +494,22 @@ void Funcdata::setHighLevel(void) assignHigh(*iter); } -/// \brief Create two new Varnodes which split the given Varnode +/// \brief Copy properties from an existing Varnode to a new Varnode /// -/// Attributes are copied from the original into the split pieces if appropriate -/// \param vn is the given Varnode -/// \param lowsize is the desired size in bytes of the least significant portion -/// \param vnlo will hold the least significant portion -/// \param vnhi will hold the most significant portion -void Funcdata::splitVarnode(Varnode *vn,int4 lowsize,Varnode *& vnlo,Varnode *& vnhi) +/// The new Varnode is assumed to overlap the storage of the existing Varnode. +/// Properties like boolean flags and \e consume bits are copied as appropriate. +/// \param vn is the existing Varnode +/// \param newVn is the new Varnode that has its properties set +/// \param lsbOffset is the significance offset of the new Varnode within the exising +void Funcdata::transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset) { - int4 highsize = vn->getSize() - lowsize; - Address addrhi = vn->getAddr(); - Address addrlo = addrhi; - uintb consumehi = vn->getConsume() >> 8*lowsize; - uintb consumelo = vn->getConsume() & calc_mask(lowsize); - if (vn->getSpace()->isBigEndian()) - addrlo = addrhi + highsize; - else - addrhi = addrhi + lowsize; + uintb newConsume = (vn->getConsume() >> 8*lsbOffset) & calc_mask(newVn->getSize()); - uint4 vnflags = vn->getFlags() & (Varnode::directwrite|Varnode::addrforce|Varnode::auto_live); - vnhi = newVarnode(highsize,addrhi); - vnlo = newVarnode(lowsize,addrlo); + uint4 vnFlags = vn->getFlags() & (Varnode::directwrite|Varnode::addrforce|Varnode::auto_live); - vnhi->setFlags(vnflags); // Preserve addrforce setting - vnlo->setFlags(vnflags); - vnhi->setConsume(consumehi); - vnlo->setConsume(consumelo); + newVn->setFlags(vnFlags); // Preserve addrforce setting + newVn->setConsume(newConsume); } /// Treat the given Varnode as read-only, look up its value in LoadImage @@ -535,6 +538,9 @@ bool Funcdata::fillinReadOnly(Varnode *vn) return false; // No change was made } + if (vn->getSize() > sizeof(uintb)) + return false; // Constant will exceed precision + uintb res; uint1 bytes[32]; try { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh index dd30309fc7..00db843979 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.hh @@ -92,7 +92,7 @@ class HeritageInfo { bool warningissued; ///< \b true if warning issued previously void set(AddrSpace *spc,int4 dl,int4 dcdl) { space=spc; delay=dl; deadcodedelay=dcdl; deadremoved=0; warningissued=false; loadGuardSearch = false; } ///< Set all fields - bool isHeritaged(void) const { return (space != (AddrSpace *)0); } + bool isHeritaged(void) const { return (space != (AddrSpace *)0); } ///< Return \b true if heritage is performed on this space void reset(void) { deadremoved = 0; deadcodedelay = delay; warningissued = false; loadGuardSearch = false; } ///< Reset }; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc index 2e325c69e5..cc3d2c0b10 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc @@ -42,3 +42,14 @@ void VarnodeData::restoreXml(const Element *el,const AddrSpaceManager *manage) } } +/// Return \b true, if \b this, as an address range, contains the other address range +/// \param op2 is the other VarnodeData to test for containment +/// \return \b true if \b this contains the other +bool VarnodeData::contains(const VarnodeData &op2) const + +{ + if (space != op2.space) return false; + if (op2.offset < offset) return false; + if ((offset + (size-1)) < (op2.offset + (op2.size-1))) return false; + return true; +} diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.hh index f068ae94b4..f4bd1f45a5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.hh @@ -43,6 +43,9 @@ struct VarnodeData { /// Recover this object from an XML tag void restoreXml(const Element *el,const AddrSpaceManager *manage); + + /// Does \b this container another given VarnodeData + bool contains(const VarnodeData &op2) const; }; /// VarnodeData can be sorted in terms of the space its in diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc index d91c59a0c9..7ccb7d48dc 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc @@ -263,7 +263,6 @@ void PrintLanguage::pushVnLHS(const Varnode *vn,const PcodeOp *op) /// ending with the given operator token, needs to be surrounded by parentheses to convey /// the proper meaning. /// \param op2 is the input token to \b this operator -/// \param stage is the stage of \b this operator currently being printed /// \return \b true if \b op2 (as input to \b this) should be parenthesized bool PrintLanguage::parentheses(const OpToken *op2) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index f919ff7157..be5bf04533 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -543,6 +543,7 @@ int4 RuleShiftBitops::applyOp(PcodeOp *op,Funcdata &data) if (!constvn->isConstant()) return 0; // Must be a constant shift Varnode *vn = op->getIn(0); if (!vn->isWritten()) return 0; + if (vn->getSize() > sizeof(uintb)) return 0; // FIXME: Can't exceed uintb precision int4 sa; bool leftshift; @@ -3216,7 +3217,7 @@ int4 RuleSignShift::applyOp(PcodeOp *op,Funcdata &data) return 1; } -/// \class RuleSignShift +/// \class RuleTestSign /// \brief Convert sign-bit test to signed comparison: `(V s>> 0x1f) != 0 => V s< 0` void RuleTestSign::getOpList(vector &oplist) const @@ -4972,6 +4973,7 @@ int4 RuleEmbed::applyOp(PcodeOp *op,Funcdata &data) PcodeOp *subop; int4 i; + if (op->getOut()->getSize() > sizeof(uintb)) return 0; // FIXME: Can't exceed uintb precision for(i=0;i<2;++i) { subout = op->getIn(i); if (!subout->isWritten()) continue; @@ -5001,8 +5003,6 @@ int4 RuleEmbed::applyOp(PcodeOp *op,Funcdata &data) } } - // Be careful of precision limit when constructing mask - if (subout->getSize() + c > sizeof(uintb)) continue; uintb mask = calc_mask(subout->getSize()); mask <<= 8*c; @@ -7334,7 +7334,7 @@ int4 RuleSplitFlow::applyOp(PcodeOp *op,Funcdata &data) return 0; SplitFlow splitFlow(&data,vn,loSize); if (!splitFlow.doTrace()) return 0; - splitFlow.doReplacement(); + splitFlow.apply(); return 1; } @@ -7699,13 +7699,13 @@ int4 RuleSubfloatConvert::applyOp(PcodeOp *op,Funcdata &data) if (outsize > insize) { SubfloatFlow subflow(&data,outvn,insize); if (!subflow.doTrace()) return 0; - subflow.doReplacement(); + subflow.apply(); return 1; } else { SubfloatFlow subflow(&data,invn,outsize); if (!subflow.doTrace()) return 0; - subflow.doReplacement(); + subflow.apply(); return 1; } return 0; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc index ce47c37d2b..f60902cfe2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc @@ -170,10 +170,11 @@ SubvariableFlow::ReplaceOp *SubvariableFlow::createOp(OpCode opc,int4 numparam,R } -/// \brief Create a logical subraph operator node given one of its input variable nodes +/// \brief Create a logical subgraph operator node given one of its input variable nodes /// /// \param opc is the opcode of the new logical operator /// \param numparam is the number of parameters in the new operator +/// \param op is the original PcodeOp being replaced /// \param inrvn is the given input variable node /// \param slot is the input slot of the variable node /// \return the new logical subgraph operator objects @@ -206,7 +207,12 @@ void SubvariableFlow::patchIndirect(PcodeOp *newop,PcodeOp *oldop, ReplaceVarnod PcodeOp *indop = PcodeOp::getOpFromConst(oldop->getIn(1)->getAddr()); bool possibleout = !oldop->getIn(0)->isIndirectZero(); Varnode *outvn = getReplaceVarnode(out); - fd->setIndirectCreation(newop,indop,outvn,possibleout); + fd->opSetOutput(newop,outvn); + fd->opSetOpcode(newop, CPUI_INDIRECT); + fd->opSetInput(newop,fd->newConstant(outvn->getSize(),0),0); + fd->opSetInput(newop,fd->newVarnodeIop(indop),1); + fd->markIndirectCreation(newop,possibleout); + fd->opInsertBefore(newop, indop); FuncCallSpecs *fc = fd->getCallSpecs(indop); if (fc == (FuncCallSpecs *)0) return; if (fc->isOutputActive()) { @@ -1040,7 +1046,7 @@ void SubvariableFlow::addTerminalPatchSameOp(PcodeOp *pullop,ReplaceVarnode *rvn /// /// This doesn't count as a Varnode holding a logical value that needs to be patched (by itself). /// A PatchRecord terminating the logical subgraph along the given edge is created. -/// \param pullup is the operation taking the boolean input +/// \param pullop is the operation taking the boolean input /// \param rvn is the given bit variable /// \param slot is the input slot of the variable to the operation void SubvariableFlow::addBooleanPatch(PcodeOp *pullop,ReplaceVarnode *rvn,int4 slot) @@ -1201,6 +1207,11 @@ bool SubvariableFlow::processNextWork(void) return traceForward(rvn); } +/// \param f is the function to attempt the subvariable transform on +/// \param root is a starting Varnode containing a smaller logical value +/// \param mask is a mask where 1 bits indicate the position of the logical value within the \e root Varnode +/// \param aggr is \b true if we should use aggressive (less restrictive) tests during the trace +/// \param sext is \b true if we should assume sign extensions from the logical value into its container SubvariableFlow::SubvariableFlow(Funcdata *f,Varnode *root,uintb mask,bool aggr,bool sext) { @@ -1228,9 +1239,13 @@ SubvariableFlow::SubvariableFlow(Funcdata *f,Varnode *root,uintb mask,bool aggr, createLink((ReplaceOp *)0,mask,0,root); } +/// Push the logical value around, setting up explicit transforms as we go that convert them +/// into explicit Varnodes. If at any point, we cannot naturally interpret the flow of the +/// logical value, return \b false. +/// \return \b true if a full transform has been constructed that can make logical values into explicit Varnodes bool SubvariableFlow::doTrace(void) -{ // Process worklist until its done +{ pullcount = 0; bool retval = false; if (fd != (Funcdata *)0) { @@ -1255,7 +1270,7 @@ bool SubvariableFlow::doTrace(void) void SubvariableFlow::doReplacement(void) -{ // Create the actual replacement data-flow with -fd- +{ list::iterator iter; // Define all the outputs first @@ -1337,253 +1352,100 @@ void SubvariableFlow::doReplacement(void) } } -SplitFlow::ReplaceVarnode::ReplaceVarnode(void) +/// \brief Find or build the placeholder objects for a Varnode that needs to be split +/// +/// Mark the Varnode so it doesn't get revisited. +/// Decide if the Varnode needs to go into the worklist. +/// \param vn is the Varnode that needs to be split +/// \return the array of placeholders describing the split or null +TransformVar *SplitFlow::setReplacement(Varnode *vn) { - replaceLo = (Varnode *)0; - replaceHi = (Varnode *)0; - defTraversed = false; -} - -SplitFlow::ReplaceOp::ReplaceOp(bool isLogic,PcodeOp *o,OpCode opc,int4 num) - -{ - op = o; - opcode = opc; - loOp = (PcodeOp *)0; - hiOp = (PcodeOp *)0; - numParams = num; - doDelete = false; - isLogicalInput = isLogic; - output = (ReplaceVarnode *)0; -} - -void SplitFlow::assignReplaceOp(bool isLogicalInput,PcodeOp *op,OpCode opc,int4 numParam,ReplaceVarnode *outrvn) - -{ - if (outrvn != (ReplaceVarnode *)0) { - if (!outrvn->defTraversed) { - oplist.push_back(ReplaceOp(isLogicalInput,op,opc,numParam)); - oplist.back().output = outrvn; - outrvn->defTraversed = true; - } - } - else { - oplist.push_back(ReplaceOp(isLogicalInput,op,opc,numParam)); - } -} - -void SplitFlow::assignLogicalPieces(ReplaceVarnode *rvn) - -{ // Create the logical pieces of -rvn- as actual Varnodes - if (rvn->replaceLo != (Varnode *)0) return; - if (rvn->vn->isConstant()) { - uintb val1 = rvn->vn->getOffset() & calc_mask(loSize); - uintb val2 = (rvn->vn->getOffset() >> (loSize * 8)) & calc_mask(hiSize); - rvn->replaceLo = fd->newConstant(loSize,val1); - rvn->replaceHi = fd->newConstant(hiSize,val2); - return; - } - if (rvn->vn->getSpace()->getType() == IPTR_INTERNAL) { - rvn->replaceLo = fd->newUnique(loSize); - rvn->replaceHi = fd->newUnique(hiSize); - return; - } - fd->splitVarnode(rvn->vn,loSize,rvn->replaceLo,rvn->replaceHi); - if (rvn->vn->isInput()) { // Right now this shouldn't happen - fd->setInputVarnode(rvn->replaceLo); - fd->setInputVarnode(rvn->replaceHi); - } -} - -void SplitFlow::buildReplaceOutputs(ReplaceOp *rop) - -{ - if (rop->output == (ReplaceVarnode *)0) return; - assignLogicalPieces(rop->output); - rop->loOp = fd->newOp(rop->numParams,rop->op->getAddr()); - rop->hiOp = fd->newOp(rop->numParams,rop->op->getAddr()); - fd->opSetOpcode(rop->loOp,rop->opcode); - fd->opSetOpcode(rop->hiOp,rop->opcode); - fd->opSetOutput(rop->loOp,rop->output->replaceLo); - fd->opSetOutput(rop->hiOp,rop->output->replaceHi); -} - -void SplitFlow::replacePiece(ReplaceOp *rop) - -{ // Finish replacing the CPUI_PIECE operation with two COPY operations - PcodeOp *op = rop->op; - Varnode *invn0 = op->getIn(0); - Varnode *invn1 = op->getIn(1); - fd->opUnsetInput(op,0); - fd->opUnsetInput(op,1); - fd->opSetInput(rop->loOp,invn1,0); - fd->opSetInput(rop->hiOp,invn0,0); - fd->opInsertBefore(rop->loOp,op); // insert at the same place as original op - fd->opInsertBefore(rop->hiOp,op); - rop->doDelete = true; // Mark this op to be deleted -} - -void SplitFlow::replaceZext(ReplaceOp *rop) - -{ // Finish replacing the CPUI_INT_ZEXT operation with a COPY and a COPY zero - PcodeOp *op = rop->op; - Varnode *invn0 = op->getIn(0); - fd->opUnsetInput(op,0); - fd->opSetInput(rop->loOp,invn0,0); // Input to first COPY is original input to ZEXT - fd->opSetInput(rop->hiOp,fd->newConstant(hiSize,0),0); // Input to second COPY is 0 constant - fd->opInsertBefore(rop->loOp,op); // insert at the same place as original op - fd->opInsertBefore(rop->hiOp,op); - rop->doDelete = true; // Mark this op to be deleted -} - -void SplitFlow::replaceLeftInput(ReplaceOp *rop) - -{ - PcodeOp *op = rop->op; - // Presence of ZEXT operation has already been verified - Varnode *invn0 = op->getIn(0)->getDef()->getIn(0); // Grab the input to ZEXT - fd->opUnsetInput(op,0); - fd->opSetInput(rop->loOp,fd->newConstant(loSize,0),0); // Input to first COPY is 0 constant - fd->opSetInput(rop->hiOp,invn0,0); // Input to second COPY is original input to ZEXT - fd->opInsertBefore(rop->loOp,op); // insert at the same place as original op - fd->opInsertBefore(rop->hiOp,op); - rop->doDelete = true; -} - -void SplitFlow::replaceLeftTerminal(ReplaceOp *rop) - -{ - PcodeOp *op = rop->op; - ReplaceVarnode *rvn1 = &varmap[op->getIn(0)]; - assignLogicalPieces(rvn1); - PcodeOp *otherOp = fd->newOp(1,op->getAddr()); - Varnode *otherVn = fd->newUniqueOut(concatSize,otherOp); - fd->opSetOpcode(otherOp,CPUI_INT_ZEXT); // Extension of low piece - fd->opSetInput(otherOp,rvn1->replaceLo,0); - fd->opInsertBefore(otherOp,op); - fd->opSetInput(op,otherVn,0); // Original shift is unchanged -} - -void SplitFlow::replaceOp(ReplaceOp *rop) - -{ // Finish splitting -rop- into two separate operations on the logical pieces at the same point in the code - // Build the logical Varnodes or reuse previously built ones as necessary - // going through ReplaceVarnodes and -varmap- - vector inputs; - - PcodeOp *op = rop->op; - int4 numParam = op->numInput(); - if (op->code() == CPUI_INDIRECT) // Slightly special handling if this is an INDIRECT - numParam = 1; // We don't split the "indirect effect" varnode - for(int4 i=0;igetIn(i)]; - assignLogicalPieces(invn); // Make sure logical pieces are built - inputs.push_back(invn); - } - for(int4 i=0;iopSetInput(rop->loOp,invn->replaceLo,i); // Set inputs of component ops - fd->opSetInput(rop->hiOp,invn->replaceHi,i); - } - if (op->code() == CPUI_INDIRECT) { - PcodeOp *indeffect = PcodeOp::getOpFromConst(op->getIn(1)->getAddr()); - fd->opSetInput(rop->loOp,fd->newVarnodeIop(indeffect),1); // Add in the "indirect effect" parameter - fd->opSetInput(rop->hiOp,fd->newVarnodeIop(indeffect),1); - fd->opInsertBefore(rop->loOp,indeffect); // Insert right before the indirect effect - fd->opInsertBefore(rop->hiOp,indeffect); - } - else if (op->code() == CPUI_MULTIEQUAL) { - BlockBasic *bb = op->getParent(); // Make sure MULTIEQUALs get inserted at the beginning of the block - fd->opInsertBegin(rop->loOp,bb); - fd->opInsertBegin(rop->hiOp,bb); - } - else { - fd->opInsertBefore(rop->loOp,op); // Otherwise, insert at the same place as original op - fd->opInsertBefore(rop->hiOp,op); - } - rop->doDelete = true; // Mark this op to be deleted -} - -SplitFlow::ReplaceVarnode *SplitFlow::setReplacement(Varnode *vn,bool &inworklist) - -{ // Find the matching placeholder object for a varnode that needs to be split, OR build the placeholder object - // Mark the varnode so it doesn't get revisited - // Decide if the varnode needs to go into the worklist by setting -inworklist- - // Return null if this won't work - ReplaceVarnode *res; + TransformVar *res; if (vn->isMark()) { // Already seen before - map::iterator iter; - iter = varmap.find(vn); - res = &(*iter).second; - inworklist = false; + res = getSplit(vn, laneDescription); return res; } if (vn->isTypeLock()) - return (ReplaceVarnode *)0; + return (TransformVar *)0; if (vn->isInput()) - return (ReplaceVarnode *)0; // Right now we can't split inputs + return (TransformVar *)0; // Right now we can't split inputs if (vn->isFree() && (!vn->isConstant())) - return (ReplaceVarnode *)0; // Abort + return (TransformVar *)0; // Abort - res = & varmap[ vn ]; // Create new ReplaceVarnode and put it in map + res = newSplit(vn, laneDescription); // Create new ReplaceVarnode and put it in map vn->setMark(); - res->vn = vn; - inworklist = !vn->isConstant(); + if (!vn->isConstant()) + worklist.push_back(res); return res; } -bool SplitFlow::addOpOutput(PcodeOp *op) +/// \brief Split given op into its lanes. +/// +/// We assume op is a logical operation, or a COPY, or an INDIRECT. It must have an output. +/// All inputs and output have their placeholders generated and added to the worklist +/// if appropriate. +/// \param op is the given op +/// \param rvn is a known parameter of the op +/// \param slot is the incoming slot of the known parameter (-1 means parameter is output) +/// \return \b true if the op is successfully split +bool SplitFlow::addOp(PcodeOp *op,TransformVar *rvn,int4 slot) -{ // Save off -op- for replacement - // Make sure the output will be replaced and add it to the worklist - // Return false if this is not possible - bool inworklist; - ReplaceVarnode *newvn = setReplacement(op->getOut(),inworklist); - if (newvn == (ReplaceVarnode *)0) - return false; - assignReplaceOp(false,op,op->code(),op->numInput(),newvn); - if (inworklist) - worklist.push_back(newvn); - return true; -} - -bool SplitFlow::addOpInputs(PcodeOp *op,ReplaceVarnode *outrvn,int4 numParam) - -{ // Save off -op- for replacement - // Make sure the inputs will be replaced and add them to the worklist - // Return false if this is not possible - bool inworklist; - ReplaceVarnode *newvn; - - for(int4 i=0;igetIn(i); - newvn = setReplacement(vn,inworklist); - if (newvn == (ReplaceVarnode *)0) +{ + TransformVar *outvn; + if (slot == -1) + outvn = rvn; + else { + outvn = setReplacement(op->getOut()); + if (outvn == (TransformVar *)0) return false; - if (inworklist) - worklist.push_back(newvn); } - assignReplaceOp(false,op,op->code(),op->numInput(),outrvn); + + if (outvn->getDef() != (TransformOp *)0) + return true; // Already traversed + + TransformOp *loOp = newOpReplace(op->numInput(), op->code(), op); + TransformOp *hiOp = newOpReplace(op->numInput(), op->code(), op); + int4 numParam = op->numInput(); + if (op->code() == CPUI_INDIRECT) { + opSetInput(loOp,newIop(op->getIn(1)),1); + opSetInput(hiOp,newIop(op->getIn(1)),1); + numParam = 1; + } + for(int4 i=0;igetIn(i)); + if (invn == (TransformVar *)0) + return false; + } + opSetInput(loOp,invn,i); // Low piece with low op + opSetInput(hiOp,invn+1,i); // High piece with high op + } + opSetOutput(loOp,outvn); + opSetOutput(hiOp,outvn+1); return true; } -bool SplitFlow::traceForward(ReplaceVarnode *rvn) - -{ // Try to trace pieces of -rvn- forward, through reading ops, update worklist - // Return true if logical pieces can be naturally traced, false otherwise - PcodeOp *op; - Varnode *outvn,*tmpvn; - uintb val; +/// \brief Try to trace the pair of logical values, forward, through ops that read them +/// +/// Try to trace pieces of TransformVar pair forward, through reading ops, update worklist +/// \param rvn is the TransformVar pair to trace, as an array +/// \return \b true if logical pieces can be naturally traced, \b false otherwise +bool SplitFlow::traceForward(TransformVar *rvn) +{ + Varnode *origvn = rvn->getOriginal(); list::const_iterator iter,enditer; - iter = rvn->vn->beginDescend(); - enditer = rvn->vn->endDescend(); + iter = origvn->beginDescend(); + enditer = origvn->endDescend(); while(iter != enditer) { - op = *iter++; - outvn = op->getOut(); + PcodeOp *op = *iter++; + Varnode *outvn = op->getOut(); if ((outvn!=(Varnode *)0)&&(outvn->isMark())) continue; switch(op->code()) { @@ -1594,37 +1456,65 @@ bool SplitFlow::traceForward(ReplaceVarnode *rvn) case CPUI_INT_OR: case CPUI_INT_XOR: // case CPUI_INT_NEGATE: - if (!addOpOutput(op)) + if (!addOp(op,rvn,op->getSlot(origvn))) return false; break; case CPUI_SUBPIECE: - val = op->getIn(1)->getOffset(); - if ((val==0)&&(outvn->getSize() == loSize)) - assignReplaceOp(false,op,CPUI_COPY,1,(ReplaceVarnode *)0); // Grabs the low piece - else if ((val == loSize)&&(outvn->getSize() == hiSize)) - assignReplaceOp(false,op,CPUI_COPY,1,(ReplaceVarnode *)0); // Grabs the high piece + { + uintb val = op->getIn(1)->getOffset(); + if ((val==0)&&(outvn->getSize() == laneDescription.getSize(0))) { + TransformOp *rop = newPreexistingOp(1,CPUI_COPY,op); // Grabs the low piece + opSetInput(rop, rvn, 0); + } + else if ((val == laneDescription.getSize(0))&&(outvn->getSize() == laneDescription.getSize(1))) { + TransformOp *rop = newPreexistingOp(1,CPUI_COPY,op); // Grabs the high piece + opSetInput(rop, rvn+1, 0); + } else return false; break; + } case CPUI_INT_LEFT: - tmpvn = op->getIn(1); + { + Varnode *tmpvn = op->getIn(1); if (!tmpvn->isConstant()) return false; - val = tmpvn->getOffset(); - if (val < hiSize * 8) + uintb val = tmpvn->getOffset(); + if (val < laneDescription.getSize(1) * 8) return false; // Must obliterate all high bits - assignReplaceOp(false,op,CPUI_INT_LEFT,2,(ReplaceVarnode *)0); // Good, but terminating op + TransformOp *rop = newPreexistingOp(2,CPUI_INT_LEFT,op); // Keep original shift + TransformOp *zextrop = newOp(1, CPUI_INT_ZEXT, rop); + opSetInput(zextrop, rvn, 0); // Input is just the low piece + opSetOutput(zextrop, newUnique(laneDescription.getWholeSize())); + opSetInput(rop, zextrop->getOut(), 0); + opSetInput(rop, newConstant(op->getIn(1)->getSize(), 0, op->getIn(1)->getOffset()), 1); // Original shift amount break; + } case CPUI_INT_SRIGHT: case CPUI_INT_RIGHT: - tmpvn = op->getIn(1); + { + Varnode *tmpvn = op->getIn(1); if (!tmpvn->isConstant()) return false; - val = tmpvn->getOffset(); - if (val < loSize * 8) + uintb val = tmpvn->getOffset(); + if (val < laneDescription.getSize(0) * 8) return false; - assignReplaceOp(false,op,(op->code() == CPUI_INT_RIGHT) ? CPUI_INT_ZEXT : CPUI_INT_SEXT,2,(ReplaceVarnode *)0); // Good, but terminating op + OpCode extOpCode = (op->code() == CPUI_INT_RIGHT) ? CPUI_INT_ZEXT : CPUI_INT_SEXT; + if (val == laneDescription.getSize(0) * 8) { // Shift of exactly loSize bytes + TransformOp *rop = newPreexistingOp(1,extOpCode,op); + opSetInput(rop, rvn+1, 0); // Input is the high piece + } + else { + uintb remainShift = val - laneDescription.getSize(0) * 8; + TransformOp *rop = newPreexistingOp(2,op->code(),op); + TransformOp *extrop = newOp(1, extOpCode, rop); + opSetInput(extrop, rvn+1, 0); // Input is the high piece + opSetOutput(extrop, newUnique(laneDescription.getWholeSize())); + opSetInput(rop, extrop->getOut(), 0); + opSetInput(rop, newConstant(op->getIn(1)->getSize(), 0, remainShift), 1); // Shift any remaining bits + } break; + } default: return false; } @@ -1632,12 +1522,15 @@ bool SplitFlow::traceForward(ReplaceVarnode *rvn) return true; } -bool SplitFlow::traceBackward(ReplaceVarnode *rvn) +/// \brief Try to trace the pair of logical values, backward, through the defining op +/// +/// Create part of transform related to the defining op, and update the worklist as necessary. +/// \param rvn is the logical value to examine +/// \return \b false if the trace is not possible +bool SplitFlow::traceBackward(TransformVar *rvn) -{ // Try to trace the pair of logical values, backward, through the op defining -rvn- - // Update list of Varnodes and PcodeOps to replace and the worklist as necessary - // Return false if this is not possible - PcodeOp *op = rvn->vn->getDef(); +{ + PcodeOp *op = rvn->getOriginal()->getDef(); if (op == (PcodeOp *)0) return true; // If vn is input switch(op->code()) { @@ -1646,43 +1539,59 @@ bool SplitFlow::traceBackward(ReplaceVarnode *rvn) case CPUI_INT_AND: case CPUI_INT_OR: case CPUI_INT_XOR: -// case CPUI_INT_NEGATE: - if (!addOpInputs(op,rvn,op->numInput())) - return false; - break; case CPUI_INDIRECT: - if (!addOpInputs(op,rvn,1)) // Only backtrack through the first input +// case CPUI_INT_NEGATE: + if (!addOp(op,rvn,-1)) return false; break; case CPUI_PIECE: - if (op->getIn(0)->getSize() != hiSize) + { + if (op->getIn(0)->getSize() != laneDescription.getSize(1)) return false; - if (op->getIn(1)->getSize() != loSize) + if (op->getIn(1)->getSize() != laneDescription.getSize(0)) return false; - assignReplaceOp(true,op,CPUI_COPY,1,rvn); + TransformOp *loOp = newOpReplace(1, CPUI_COPY, op); + TransformOp *hiOp = newOpReplace(1, CPUI_COPY, op); + opSetInput(loOp,getPreexistingVarnode(op->getIn(1)),0); + opSetOutput(loOp,rvn); // Least sig -> low + opSetInput(hiOp,getPreexistingVarnode(op->getIn(0)),0); + opSetOutput(hiOp,rvn+1); // Most sig -> high break; + } case CPUI_INT_ZEXT: - if (op->getIn(0)->getSize() != loSize) + { + if (op->getIn(0)->getSize() != laneDescription.getSize(0)) return false; - if (op->getOut()->getSize() != (loSize + hiSize)) + if (op->getOut()->getSize() != laneDescription.getWholeSize()) return false; - assignReplaceOp(true,op,CPUI_COPY,1,rvn); + TransformOp *loOp = newOpReplace(1, CPUI_COPY, op); + TransformOp *hiOp = newOpReplace(1, CPUI_COPY, op); + opSetInput(loOp,getPreexistingVarnode(op->getIn(0)),0); + opSetOutput(loOp,rvn); // ZEXT input -> low + opSetInput(hiOp,newConstant(laneDescription.getSize(1), 0, 0), 0); + opSetOutput(hiOp,rvn+1); // zero -> high break; + } case CPUI_INT_LEFT: - { - Varnode *cvn = op->getIn(1); - if (!cvn->isConstant()) return false; - if (cvn->getOffset() != loSize * 8) return false; - Varnode *invn = op->getIn(0); - if (!invn->isWritten()) return false; - PcodeOp *zextOp = invn->getDef(); - if (zextOp->code() != CPUI_INT_ZEXT) return false; - invn = zextOp->getIn(0); - if (invn->getSize() != hiSize) return false; - if (invn->isFree()) return false; - assignReplaceOp(true,op,CPUI_COPY,1,rvn); - } + { + Varnode *cvn = op->getIn(1); + if (!cvn->isConstant()) return false; + if (cvn->getOffset() != laneDescription.getSize(0) * 8) return false; + Varnode *invn = op->getIn(0); + if (!invn->isWritten()) return false; + PcodeOp *zextOp = invn->getDef(); + if (zextOp->code() != CPUI_INT_ZEXT) return false; + invn = zextOp->getIn(0); + if (invn->getSize() != laneDescription.getSize(1)) return false; + if (invn->isFree()) return false; + TransformOp *loOp = newOpReplace(1, CPUI_COPY, op); + TransformOp *hiOp = newOpReplace(1, CPUI_COPY, op); + opSetInput(loOp,newConstant(laneDescription.getSize(0), 0, 0), 0); + opSetOutput(loOp, rvn); // zero -> low + opSetInput(hiOp,getPreexistingVarnode(invn), 0); + opSetOutput(hiOp, rvn+1); // invn -> high break; + } // case CPUI_LOAD: // We could split into two different loads default: return false; @@ -1690,10 +1599,11 @@ bool SplitFlow::traceBackward(ReplaceVarnode *rvn) return true; } +/// \return \b true if the logical split was successfully pushed through its local operators bool SplitFlow::processNextWork(void) { - ReplaceVarnode *rvn = worklist.back(); + TransformVar *rvn = worklist.back(); worklist.pop_back(); @@ -1702,243 +1612,100 @@ bool SplitFlow::processNextWork(void) } SplitFlow::SplitFlow(Funcdata *f,Varnode *root,int4 lowSize) + : TransformManager(f), laneDescription(root->getSize(),lowSize,root->getSize()-lowSize) { - fd = f; - concatSize = root->getSize(); - loSize = lowSize; - hiSize = concatSize - loSize; - bool inworklist; - ReplaceVarnode *rvn = setReplacement(root,inworklist); - if (rvn == (ReplaceVarnode *)0) - return; - if (inworklist) - worklist.push_back(rvn); -} - -void SplitFlow::doReplacement(void) - -{ - ReplaceVarnode *rvn1; - - list::iterator iter; - for(iter=oplist.begin();iter!=oplist.end();++iter) { - buildReplaceOutputs(&(*iter)); // Build the raw replacement ops for anything needing an output - } - - for(iter=oplist.begin();iter!=oplist.end();++iter) { - ReplaceOp *rop = &(*iter); - PcodeOp *op = rop->op; - switch(op->code()) { - case CPUI_SUBPIECE: - rvn1 = &varmap[op->getIn(0)]; - assignLogicalPieces(rvn1); - fd->opSetOpcode(op,CPUI_COPY); // This becomes a COPY - if (op->getIn(1)->getOffset() == 0) // Grabbing the low piece - fd->opSetInput(op,rvn1->replaceLo,0); - else - fd->opSetInput(op,rvn1->replaceHi,0); // Grabbing the high piece - fd->opRemoveInput(op,1); - break; - case CPUI_INT_LEFT: - if (rop->isLogicalInput) - replaceLeftInput(rop); - else - replaceLeftTerminal(rop); - break; - case CPUI_INT_RIGHT: - case CPUI_INT_SRIGHT: - rvn1 = &varmap[op->getIn(0)]; - assignLogicalPieces(rvn1); - if (op->getIn(1)->getOffset() == loSize * 8) { // Shift of exactly loSize bytes - fd->opSetOpcode(op,rop->opcode); // is equivalent to an extension - fd->opRemoveInput(op,1); - fd->opSetInput(op,rvn1->replaceHi,0); // of the high part - } - else { - PcodeOp *otherOp = fd->newOp(1,op->getAddr()); - Varnode *otherVn = fd->newUniqueOut(concatSize,otherOp); - uintb remainShift = op->getIn(1)->getOffset() - loSize * 8; - fd->opSetOpcode(otherOp,rop->opcode); // Extension of high piece - fd->opSetInput(otherOp,rvn1->replaceHi,0); // Equivalent of INT_RIGHT by loSize * 8 - fd->opInsertBefore(otherOp,op); - fd->opSetInput(op,otherVn,0); // Original shift - fd->opSetInput(op,fd->newConstant(4,remainShift),1); // now shifts any remaining bits - } - break; - case CPUI_PIECE: - replacePiece(rop); - break; - case CPUI_INT_ZEXT: - replaceZext(rop); - break; - default: - replaceOp(rop); - break; - } - } - - for(iter=oplist.begin();iter!=oplist.end();++iter) { - if ((*iter).doDelete) { // Marked for deletion - fd->opDestroy((*iter).op); - } - } + setReplacement(root); } +/// Push the logical split around, setting up the explicit transforms as we go. +/// If at any point, the split cannot be naturally pushed, return \b false. +/// \return \b true if a full transform has been constructed that can perform the split bool SplitFlow::doTrace(void) -{ // Process worklist until its done +{ if (worklist.empty()) return false; // Nothing to do - bool retval = false; - if (fd != (Funcdata *)0) { - retval = true; - while(!worklist.empty()) { - if (!processNextWork()) { - retval = false; - break; - } + bool retval = true; + while(!worklist.empty()) { // Process the worklist until its done + if (!processNextWork()) { + retval = false; + break; } } - // Clear marks - map::iterator iter; - for(iter=varmap.begin();iter!=varmap.end();++iter) - (*iter).first->clearMark(); - + clearVarnodeMarks(); if (!retval) return false; return true; } -SubfloatFlow::ReplaceVarnode *SubfloatFlow::setReplacement(Varnode *vn,bool &inworklist) +/// \brief Create and return a placeholder associated with the given Varnode +/// +/// Add the placeholder to the worklist if it hasn't been visited before +/// \param vn is the given Varnode +/// \return the placeholder or null if the Varnode is not suitable for replacement +TransformVar *SubfloatFlow::setReplacement(Varnode *vn) -{ // Create and return a ReplaceVarnode associated with vn, if vn is suitable for replacement - // Set inworklist to true if the varnode has not been in the worklist before - // Return NULL if the vn is not suitable for replacement - ReplaceVarnode *res; - if (vn->isMark()) { // Already seen before - map::iterator iter; - iter = varmap.find(vn); - res = &(*iter).second; - inworklist = false; - return res; - } +{ + if (vn->isMark()) // Already seen before + return getPiece(vn, precision*8, 0); if (vn->isConstant()) { - inworklist = false; - return addConstant(vn); + const FloatFormat *form2 = getFunction()->getArch()->translate->getFloatFormat(vn->getSize()); + if (form2 == (const FloatFormat *)0) + return (TransformVar *)0; // Unsupported constant format + // Return the converted form of the constant + return newConstant(precision, 0, format->convertEncoding(vn->getOffset(),form2)); } if (vn->isFree()) - return (ReplaceVarnode *)0; // Abort + return (TransformVar *)0; // Abort if (vn->isAddrForce() && (vn->getSize() != precision)) - return (ReplaceVarnode *)0; + return (TransformVar *)0; if (vn->isTypeLock()) { int4 sz = vn->getType()->getSize(); if (sz != precision) - return (ReplaceVarnode *)0; + return (TransformVar *)0; } if (vn->isInput()) { // Must be careful with inputs - if (vn->getSize() != precision) return (ReplaceVarnode *)0; + if (vn->getSize() != precision) return (TransformVar *)0; } - res = & varmap[ vn ]; vn->setMark(); - res->vn = vn; - res->replacement = (Varnode *)0; - res->def = (ReplaceOp *)0; - inworklist = true; + TransformVar *res; // Check if vn already represents the logical variable being traced - if (vn->getSize() == precision) { - inworklist = false; - res->replacement = vn; + if (vn->getSize() == precision) + res = newPreexistingVarnode(vn); + else { + res = newPiece(vn, precision*8, 0); + worklist.push_back(res); } return res; } -SubfloatFlow::ReplaceVarnode *SubfloatFlow::setReplacementNoFlow(Varnode *vn) - -{ // Create and return a ReplaceVarnode associated with vn, where we assume -vn- is not going to change - // and there will be no further logical flow through -vn- - ReplaceVarnode *res; - if (vn->isMark()) { // Already seen before - map::iterator iter; - iter = varmap.find(vn); - res = &(*iter).second; - return res; - } - - if (!vn->isConstant()) { - if (vn->isFree()) // If we have an unheritaged value - return (ReplaceVarnode *)0; // Abort - } - - res = &varmap[ vn ]; - vn->setMark(); - res->vn = vn; - res->replacement = vn; // NOTE: we set replacement as itself, even if it is a constant - res->def = (ReplaceOp *)0; - return res; -} - -SubfloatFlow::ReplaceOp *SubfloatFlow::createOp(OpCode opc,int4 numparam,ReplaceVarnode *outrvn) - -{ // Create record for replacement op, given its replacement varnode output - if (outrvn->def != (ReplaceOp *)0) - return outrvn->def; - oplist.push_back(ReplaceOp()); - ReplaceOp *rop = &oplist.back(); - outrvn->def = rop; - rop->op = outrvn->vn->getDef(); - rop->numparams = numparam; - rop->opc = opc; - rop->output = outrvn; - - return rop; -} - -SubfloatFlow::ReplaceOp *SubfloatFlow::createOpDown(OpCode opc,int4 numparam,PcodeOp *op,ReplaceVarnode *inrvn,int4 slot) - -{ // Create record for replacement op, given one of its input replacement varnodes - oplist.push_back(ReplaceOp()); - ReplaceOp *rop = &oplist.back(); - rop->op = op; - rop->opc = opc; - rop->numparams = numparam; - rop->output = (ReplaceVarnode *)0; - while(rop->input.size() <= slot) - rop->input.push_back((ReplaceVarnode *)0); - rop->input[slot] = inrvn; - return rop; -} - -bool SubfloatFlow::traceForward(ReplaceVarnode *rvn) - -{ // Try to trace logical variable through descendant varnodes - // updating list/map of replace_ops and replace_varnodes - // and the worklist - ReplaceVarnode *rvn2; - ReplaceOp *rop; - PcodeOp *op; - Varnode *outvn; - int4 slot; - bool inworklist; - int4 dcount = 0; - int4 hcount = 0; +/// \brief Try to trace logical variable through descendant Varnodes +/// +/// Given a Varnode placeholder, look at all descendent PcodeOps and create +/// placeholders for the op and its output Varnode. If appropriate add the +/// output placeholder to the worklist. +/// \param rvn is the given Varnode placeholder +/// \return \b true if tracing the logical variable forward was possible +bool SubfloatFlow::traceForward(TransformVar *rvn) +{ list::const_iterator iter,enditer; - iter = rvn->vn->beginDescend(); - enditer = rvn->vn->endDescend(); + Varnode *vn = rvn->getOriginal(); + iter = vn->beginDescend(); + enditer = vn->endDescend(); while(iter != enditer) { - op = *iter++; - outvn = op->getOut(); + PcodeOp *op = *iter++; + Varnode *outvn = op->getOut(); if ((outvn!=(Varnode *)0)&&(outvn->isMark())) continue; - dcount += 1; // Count this descendant - slot = op->getSlot(rvn->vn); + int4 slot = op->getSlot(vn); switch(op->code()) { case CPUI_COPY: case CPUI_FLOAT_CEIL: @@ -1952,54 +1719,69 @@ bool SubfloatFlow::traceForward(ReplaceVarnode *rvn) case CPUI_FLOAT_MULT: case CPUI_FLOAT_DIV: case CPUI_MULTIEQUAL: - rop = createOpDown(op->code(),op->numInput(),op,rvn,slot); - if (!createLink(rop,-1,outvn)) return false; - hcount += 1; // Dealt with this descendant + { + TransformOp *rop = newOpReplace(op->numInput(), op->code(), op); + TransformVar *outrvn = setReplacement(outvn); + if (outrvn == (TransformVar *)0) return false; + opSetInput(rop,rvn,slot); + opSetOutput(rop,outrvn); break; + } case CPUI_FLOAT_FLOAT2FLOAT: + { if (outvn->getSize() < precision) return false; - addtopulllist(op,rvn); - hcount += 1; // Dealt with this descendant + TransformOp *rop = newPreexistingOp(1, (outvn->getSize() == precision) ? CPUI_COPY : CPUI_FLOAT_FLOAT2FLOAT, op); + opSetInput(rop,rvn,0); + terminatorCount += 1; break; + } case CPUI_FLOAT_EQUAL: case CPUI_FLOAT_NOTEQUAL: case CPUI_FLOAT_LESS: case CPUI_FLOAT_LESSEQUAL: - rvn2 = setReplacement(op->getIn(1-slot),inworklist); - if (rvn2 == (ReplaceVarnode *)0) return false; - if (inworklist) - worklist.push_back(rvn2); - if (slot == 0) - addtocomplist(rvn,rvn2,op); - else - addtocomplist(rvn2,rvn,op); - hcount += 1; // Dealt with this descendant + { + TransformVar *rvn2 = setReplacement(op->getIn(1-slot)); + if (rvn2 == (TransformVar *)0) return false; + TransformOp *rop = newPreexistingOp(2, op->code(), op); + if (slot == 0) { + opSetInput(rop,rvn,0); + opSetInput(rop,rvn2,1); + } + else { + opSetInput(rop,rvn2,0); + opSetInput(rop,rvn,1); + } + terminatorCount += 1; break; + } case CPUI_FLOAT_TRUNC: case CPUI_FLOAT_NAN: - addtopulllist(op,rvn); - hcount += 1; + { + TransformOp *rop = newPreexistingOp(1,op->code(), op); + opSetInput(rop,rvn,0); + terminatorCount += 1; break; + } default: return false; } } - if (dcount != hcount) { - // Must account for all descendants of an input - if (rvn->vn->isInput()) return false; - } return true; } -bool SubfloatFlow::traceBackward(ReplaceVarnode *rvn) +/// \brief Trace a logical value backward through defining op one level +/// +/// Given an existing variable placeholder look at the op defining it and +/// define placeholder variables for all its inputs. Put the new placeholders +/// onto the worklist if appropriate. +/// \param rvn is the given variable placeholder +/// \return \b true if the logical value can be traced properly +bool SubfloatFlow::traceBackward(TransformVar *rvn) -{ // Trace backward through defining op one level - // Update worklist, varmap, and oplist - // return false if the trace is aborted - PcodeOp *op = rvn->vn->getDef(); +{ + PcodeOp *op = rvn->getOriginal()->getDef(); if (op == (PcodeOp *)0) return true; // If vn is input - ReplaceOp *rop; switch(op->code()) { case CPUI_COPY: @@ -2014,20 +1796,59 @@ bool SubfloatFlow::traceBackward(ReplaceVarnode *rvn) case CPUI_FLOAT_MULT: case CPUI_FLOAT_DIV: case CPUI_MULTIEQUAL: - rop = createOp(op->code(),op->numInput(),rvn); - for(int4 i=0;inumInput();++i) - if (!createLink(rop,i,op->getIn(i))) // Same inputs and mask - return false; + { + TransformOp *rop = rvn->getDef(); + if (rop == (TransformOp *)0) { + rop = newOpReplace(op->numInput(), op->code(), op); + opSetOutput(rop, rvn); + } + for(int4 i=0;inumInput();++i) { + TransformVar *newvar = rop->getIn(i); + if (newvar == (TransformVar *)0) { + newvar = setReplacement(op->getIn(i)); + if (newvar == (TransformVar *)0) + return false; + opSetInput(rop,newvar,i); + } + } return true; + } case CPUI_FLOAT_INT2FLOAT: - if (addtopushlist(op,rvn)) - return true; - break; + { + Varnode *vn = op->getIn(0); + if (!vn->isConstant() && vn->isFree()) + return false; + TransformOp *rop = newOpReplace(1, CPUI_FLOAT_INT2FLOAT, op); + opSetOutput(rop, rvn); + TransformVar *newvar = getPreexistingVarnode(vn); + opSetInput(rop,newvar,0); + return true; + } case CPUI_FLOAT_FLOAT2FLOAT: - // if ((op->getIn(0)->getSize() <= precision)||op->getIn(0)->isConstant()) - if (addtopushlist(op,rvn)) - return true; - break; + { + Varnode *vn = op->getIn(0); + TransformVar *newvar; + OpCode opc; + if (vn->isConstant()) { + opc = CPUI_COPY; + if (vn->getSize() == precision) + newvar = newConstant(precision, 0, vn->getOffset()); + else { + newvar = setReplacement(vn); // Convert constant to precision size + if (newvar == (TransformVar *)0) + return false; // Unsupported float format + } + } + else { + if (vn->isFree()) return false; + opc = (vn->getSize() == precision) ? CPUI_COPY : CPUI_FLOAT_FLOAT2FLOAT; + newvar = getPreexistingVarnode(vn); + } + TransformOp *rop = newOpReplace(1, opc, op); + opSetOutput(rop, rvn); + opSetInput(rop,newvar,0); + return true; + } default: break; // Everything else we abort } @@ -2035,143 +1856,16 @@ bool SubfloatFlow::traceBackward(ReplaceVarnode *rvn) return false; } -bool SubfloatFlow::createLink(ReplaceOp *rop,int4 slot,Varnode *vn) - -{ // Add a new varnode (and the edge which traced to it) to the worklist - bool inworklist; - ReplaceVarnode *rep = setReplacement(vn,inworklist); - if (rep == (ReplaceVarnode *)0) return false; - - if (rop != (ReplaceOp *)0) { - if (slot == -1) { - rop->output = rep; - rep->def = rop; - } - else { - while(rop->input.size() <= slot) - rop->input.push_back((ReplaceVarnode *)0); - rop->input[slot] = rep; - } - } - - if (inworklist) - worklist.push_back(rep); - return true; -} - -SubfloatFlow::ReplaceVarnode *SubfloatFlow::addConstant(Varnode *vn) - -{ // Add a constant to the replacement tree - const FloatFormat *form2 = fd->getArch()->translate->getFloatFormat(vn->getSize()); - if (form2 == (const FloatFormat *)0) - return (ReplaceVarnode *)0; // Unsupported constant format - newvarlist.push_back(ReplaceVarnode()); - ReplaceVarnode *res = &newvarlist.back(); - res->vn = vn; - res->replacement = (Varnode *)0; - res->def = (ReplaceOp *)0; - return res; -} - -void SubfloatFlow::addtopulllist(PcodeOp *pullop,ReplaceVarnode *rvn) - -{ // Exit point of the logical flow - // Add a reference to the logical variable getting pulled - // out of container flow - pulllist.push_back(PulloutRecord()); - pulllist.back().pullop = pullop; // Operation pulling the variable out - if (pullop->code() == CPUI_FLOAT_FLOAT2FLOAT) { - if (pullop->getOut()->getSize() == precision) - pulllist.back().opc = CPUI_COPY; - else - pulllist.back().opc = CPUI_FLOAT_FLOAT2FLOAT; - } - else - pulllist.back().opc = pullop->code(); - pulllist.back().input = rvn; // Point in container flow for pull -} - -bool SubfloatFlow::addtopushlist(PcodeOp *pushop,ReplaceVarnode *rvn) - -{ // Entry point of the logical flow - Varnode *invn = pushop->getIn(0); - OpCode opc = pushop->code(); - if (opc == CPUI_FLOAT_FLOAT2FLOAT) { - if ((invn->getSize() == precision)||invn->isConstant()) - opc = CPUI_COPY; - } - ReplaceOp *rop = createOp(opc,1,rvn); - if ((opc == CPUI_FLOAT_INT2FLOAT)|| - ((opc == CPUI_FLOAT_FLOAT2FLOAT)&&(invn->getSize() > precision))) { - // We do not want to create a new input replacement, but want to keep the old - ReplaceVarnode *rvn = setReplacementNoFlow(invn); - if (rvn == (ReplaceVarnode *)0) - return false; - rop->input.push_back(rvn); - return true; - } - return createLink(rop,0,invn); -} - -void SubfloatFlow::addtocomplist(ReplaceVarnode *in1,ReplaceVarnode *in2,PcodeOp *op) - -{ - complist.push_back(CompareRecord()); - complist.back().in1 = in1; - complist.back().in2 = in2; - complist.back().compop = op; -} - -void SubfloatFlow::replaceInput(ReplaceVarnode *rvn) - -{ // Replace ORIGINAL input in the subgraph with temporaries, so - // we don't get overlapping varnode errors - Varnode *newvn = fd->newUnique(rvn->vn->getSize()); - newvn = fd->setInputVarnode(newvn); - fd->totalReplace(rvn->vn,newvn); - fd->deleteVarnode(rvn->vn); - rvn->vn = newvn; -} - -Varnode *SubfloatFlow::getReplaceVarnode(ReplaceVarnode *rvn) - -{ // Get the actual varnode associated with a replacement varnode - // either by recycling a previously built one or creating - // one on the spot - if (rvn->replacement != (Varnode *)0) { - if (!rvn->replacement->isConstant()) - return rvn->replacement; - // if replacement is a constant (this was generated in setReplacementNoFlow) create copy of original constant - return fd->newConstant(rvn->replacement->getSize(),rvn->replacement->getOffset()); - } - if (rvn->vn->isConstant()) { // A constant - const FloatFormat *formin = fd->getArch()->translate->getFloatFormat(rvn->vn->getSize()); - // addConstant makes sure that formin is not null - return fd->newConstant(precision,format->convertEncoding(rvn->vn->getOffset(),formin)); - } - - bool isinput = rvn->vn->isInput(); - if (isinput) { - Address addr = rvn->vn->getAddr(); - // This is sort of fundemental problem: how do we represent an input variable that - // is lower precision than its storage location - // Here we artificially truncate the location, which isn't realistic - if (addr.isBigEndian()) - addr = addr + (rvn->vn->getSize() - precision); - replaceInput(rvn); // Replace input to avoid overlap errors - rvn->replacement = fd->newVarnode(precision,addr); - } - else - rvn->replacement = fd->newUnique(precision); - if (isinput) // Is this an input - rvn->replacement = fd->setInputVarnode(rvn->replacement); - return rvn->replacement; -} - +/// \brief Push the trace one hop from the placeholder at the top of the worklist +/// +/// The logical value for the value on top of the worklist stack is pushed back +/// to the input Varnodes of the operation defining it. Then the value is pushed +/// forward through all operations that read it. +/// \return \b true if the trace is successfully pushed bool SubfloatFlow::processNextWork(void) { - ReplaceVarnode *rvn = worklist.back(); + TransformVar *rvn = worklist.back(); worklist.pop_back(); @@ -2179,84 +1873,550 @@ bool SubfloatFlow::processNextWork(void) return traceForward(rvn); } +/// \param f is the function being transformed +/// \param root is the start Varnode containing the logical value +/// \param prec is the precision to assume for the logical value SubfloatFlow::SubfloatFlow(Funcdata *f,Varnode *root,int4 prec) - + : TransformManager(f) { - fd = f; precision = prec; - format = fd->getArch()->translate->getFloatFormat(precision); - createLink((ReplaceOp *)0,0,root); + format = f->getArch()->translate->getFloatFormat(precision); + if (format == (const FloatFormat *)0) + return; + setReplacement(root); } +bool SubfloatFlow::preserveAddress(Varnode *vn,int4 bitSize,int4 lsbOffset) const + +{ + return vn->isInput(); // Only try to preserve address for input varnodes +} + +/// The interpretation that the root Varnode contains a logical value with +/// smaller precision is pushed through the data-flow. If the interpretation is +/// inconsistent, \b false is returned. Otherwise a transform is constructed that +/// makes the smaller precision the explicit size of Varnodes within the data-flow. +/// \return \b true if a transform consistent with the given precision can be built bool SubfloatFlow::doTrace(void) -{ // Process worklist until its done - bool retval = false; - if ((fd != (Funcdata *)0)&& - (format != (const FloatFormat *)0)) { - retval = true; - while(!worklist.empty()) { - if (!processNextWork()) { - retval = false; - break; - } +{ + if (format == (const FloatFormat *)0) + return false; + terminatorCount = 0; // Have seen no terminators + bool retval = true; + while(!worklist.empty()) { + if (!processNextWork()) { + retval = false; + break; } } - // Clear marks - map::iterator iter; - for(iter=varmap.begin();iter!=varmap.end();++iter) - (*iter).first->clearMark(); + clearVarnodeMarks(); if (!retval) return false; - if (pulllist.empty()&&complist.empty()) return false; + if (terminatorCount == 0) return false; // Must see at least 1 terminator return true; } -void SubfloatFlow::doReplacement(void) +/// \brief Find or build the placeholder objects for a Varnode that needs to be split into lanes +/// +/// The Varnode is split based on the given subset of the lane description. +/// Constants can be split. Decide if the Varnode needs to go into the work list. +/// If the Varnode cannot be acceptably split, return null. +/// \param vn is the Varnode that needs to be split +/// \param numLanes is the number of lanes in the subset +/// \param skipLanes is the start (least significant) lane in the subset +/// \return the array of placeholders describing the split or null +TransformVar *LaneDivide::setReplacement(Varnode *vn,int4 numLanes,int4 skipLanes) -{ // Create the actual replacement data-flow with -fd- - list::iterator iter; +{ + if (vn->isMark()) // Already seen before + return getSplit(vn, description, numLanes, skipLanes); - // Define all the outputs first - for(iter=oplist.begin();iter!=oplist.end();++iter) { - PcodeOp *newop = fd->newOp((*iter).numparams,(*iter).op->getAddr()); - (*iter).replacement = newop; - fd->opSetOpcode(newop,(*iter).opc); - ReplaceVarnode *rout = (*iter).output; - if (rout != (ReplaceVarnode *)0) { - if (rout->replacement == (Varnode *)0) - rout->replacement = fd->newUniqueOut(precision,newop); - else - fd->opSetOutput(newop,rout->replacement); - } - fd->opInsertAfter(newop,(*iter).op); + if (vn->isConstant()) { + return newSplit(vn,description, numLanes, skipLanes); } - // Set all the inputs - for(iter=oplist.begin();iter!=oplist.end();++iter) { - PcodeOp *newop = (*iter).replacement; - for(uint4 i=0;i<(*iter).input.size();++i) - fd->opSetInput(newop,getReplaceVarnode((*iter).input[i]),i); - } + // Allow free varnodes to be split +// if (vn->isFree()) +// return (TransformVar *)0; - // These are operations that carry flow from the small variable into an existing - // variable of the correct size - list::iterator piter; - for(piter=pulllist.begin();piter!=pulllist.end();++piter) { - PcodeOp *pullop = (*piter).pullop; - while(pullop->numInput() > 1) - fd->opRemoveInput(pullop,pullop->numInput()-1); - fd->opSetInput(pullop,getReplaceVarnode((*piter).input),0); - if (pullop->code() != (*piter).opc) - fd->opSetOpcode(pullop,(*piter).opc); - } + if (vn->isTypeLock()) + return (TransformVar *)0; - list::iterator citer; - for(citer=complist.begin();citer!=complist.end();++citer) { - PcodeOp *op = (*citer).compop; - fd->opSetInput(op,getReplaceVarnode((*citer).in1),0); - fd->opSetInput(op,getReplaceVarnode((*citer).in2),1); + vn->setMark(); + TransformVar *res = newSplit(vn, description, numLanes, skipLanes); + if (!vn->isFree()) { + workList.push_back(WorkNode()); + workList.back().lanes = res; + workList.back().numLanes = numLanes; + workList.back().skipLanes = skipLanes; + } + return res; +} + +/// \brief Build unary op placeholders with the same opcode across a set of lanes +/// +/// We assume the input and output placeholder variables have already been collected +/// \param opc is the desired opcode for the new op placeholders +/// \param op is the PcodeOp getting replaced +/// \param inVars is the array of input variables, 1 for each unary op +/// \param outVars is the array of output variables, 1 for each unary op +/// \param numLanes is the number of unary ops to create +void LaneDivide::buildUnaryOp(OpCode opc,PcodeOp *op,TransformVar *inVars,TransformVar *outVars,int4 numLanes) + +{ + for(int4 i=0;igetIn(0); + Varnode *lowVn = op->getIn(1); + + if (!description.restriction(numLanes,skipLanes,lowVn->getSize(),highVn->getSize(),highLanes,highSkip)) + return false; + if (!description.restriction(numLanes,skipLanes,0,lowVn->getSize(),lowLanes,lowSkip)) + return false; + if (highLanes == 1) { + TransformVar *highRvn = getPreexistingVarnode(highVn); + TransformOp *rop = newOpReplace(1, CPUI_COPY, op); + opSetInput(rop,highRvn,0); + opSetOutput(rop,outVars + (numLanes-1)); + } + else { // Multi-lane high + TransformVar *highRvn = setReplacement(highVn, highLanes, highSkip); + if (highRvn == (TransformVar *)0) return false; + int4 outHighStart = numLanes - highLanes; + for(int4 i=0;i inVarSets; + int4 numInput = op->numInput(); + for(int4 i=0;igetIn(i), numLanes, skipLanes); + if (inVn == (TransformVar *)0) return false; + inVarSets.push_back(inVn); + } + for(int4 i=0;igetIn(2), numLanes, skipLanes); + if (inVars == (TransformVar *)0) return false; + uintb spaceConst = op->getIn(0)->getOffset(); + int4 spaceConstSize = op->getIn(0)->getSize(); + AddrSpace *spc = Address::getSpaceFromConst(op->getIn(0)->getAddr()); // Address space being stored to + Varnode *origPtr = op->getIn(1); + if (origPtr->isFree()) { + if (!origPtr->isConstant()) return false; + } + TransformVar *basePtr = getPreexistingVarnode(origPtr); + int4 ptrSize = origPtr->getSize(); + Varnode *valueVn = op->getIn(2); + for(int4 i=0;iisBigEndian()) + bytePos = valueVn->getSize() - (bytePos + sz); // Convert position to address order + + // Construct the pointer + TransformVar *ptrVn; + if (bytePos == 0) + ptrVn = basePtr; + else { + ptrVn = newUnique(ptrSize); + TransformOp *addOp = newOp(2, CPUI_INT_ADD, ropStore); + opSetOutput(addOp,ptrVn); + opSetInput(addOp,basePtr,0); + opSetInput(addOp,newConstant(ptrSize, 0, bytePos), 1); + } + + opSetInput(ropStore,newConstant(spaceConstSize,0,spaceConst),0); + opSetInput(ropStore,ptrVn,1); + opSetInput(ropStore,inVars+i,2); + } + return true; +} + +/// \brief Split a given CPUI_LOAD operation into a sequence of LOADs of individual lanes +/// +/// A new pointer is constructed for each individual lane into a temporary, then a +/// LOAD is created using the pointer that loads an individual lane. +/// \param op is the given CPUI_LOAD PcodeOp +/// \param outVars is the output placeholders for the LOAD +/// \param numLanes is the number of lanes the LOAD is split into +/// \param skipLanes is the starting lane (within the global description) of the value being loaded +/// \return \b true if the CPUI_LOAD was successfully modeled on lanes +bool LaneDivide::buildLoad(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes) + +{ + uintb spaceConst = op->getIn(0)->getOffset(); + int4 spaceConstSize = op->getIn(0)->getSize(); + AddrSpace *spc = Address::getSpaceFromConst(op->getIn(0)->getAddr()); // Address space being stored to + Varnode *origPtr = op->getIn(1); + if (origPtr->isFree()) { + if (!origPtr->isConstant()) return false; + } + TransformVar *basePtr = getPreexistingVarnode(origPtr); + int4 ptrSize = origPtr->getSize(); + int4 outSize = op->getOut()->getSize(); + for(int4 i=0;iisBigEndian()) + bytePos = outSize - (bytePos + sz); // Convert position to address order + + // Construct the pointer + TransformVar *ptrVn; + if (bytePos == 0) + ptrVn = basePtr; + else { + ptrVn = newUnique(ptrSize); + TransformOp *addOp = newOp(2, CPUI_INT_ADD, ropLoad); + opSetOutput(addOp,ptrVn); + opSetInput(addOp,basePtr,0); + opSetInput(addOp,newConstant(ptrSize, 0, bytePos), 1); + } + + opSetInput(ropLoad,newConstant(spaceConstSize,0,spaceConst),0); + opSetInput(ropLoad,ptrVn,1); + opSetOutput(ropLoad,outVars+i); + } + return true; +} + +/// \brief Check that a CPUI_INT_RIGHT respects the lanes then generate lane placeholders +/// +/// For the given lane scheme, check that the RIGHT shift is copying whole lanes to each other. +/// If so, generate the placeholder COPYs that model the shift. +/// \param op is the given CPUI_INT_RIGHT PcodeOp +/// \param outVars is the output placeholders for the RIGHT shift +/// \param numLanes is the number of lanes the shift is split into +/// \param skipLanes is the starting lane (within the global description) of the value being loaded +/// \return \b true if the CPUI_INT_RIGHT was successfully modeled on lanes +bool LaneDivide::buildRightShift(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes) + +{ + if (!op->getIn(1)->isConstant()) return false; + int4 shiftSize = (int4)op->getIn(1)->getOffset(); + if ((shiftSize & 7) != 0) return false; // Not a multiple of 8 + shiftSize /= 8; + int4 startPos = shiftSize + description.getPosition(skipLanes); + int4 startLane = description.getBoundary(startPos); + if (startLane < 0) return false; // Shift does not end on a lane boundary + int4 srcLane = startLane; + int4 destLane = skipLanes; + while(srcLane - skipLanes < numLanes) { + if (description.getSize(srcLane) != description.getSize(destLane)) return false; + srcLane += 1; + destLane += 1; + } + TransformVar *inVars = setReplacement(op->getIn(0), numLanes, skipLanes); + if (inVars == (TransformVar *)0) return false; + buildUnaryOp(CPUI_COPY, op, inVars + (startLane - skipLanes), outVars, numLanes - (startLane - skipLanes)); + for(int4 zeroLane=numLanes - (startLane - skipLanes);zeroLane < numLanes;++zeroLane) { + TransformOp *rop = newOpReplace(1, CPUI_COPY, op); + opSetOutput(rop,outVars + zeroLane); + opSetInput(rop,newConstant(description.getSize(zeroLane), 0, 0),0); + } + return true; +} + +/// \brief Push the logical lanes forward through any PcodeOp reading the given variable +/// +/// Determine if the logical lanes can be pushed forward naturally, and create placeholder +/// variables and ops representing the logical data-flow. Update the worklist with any +/// new Varnodes that the lanes get pushed into. +/// \param rvn is the placeholder variable to push forward from +/// \param numLanes is the number of lanes represented by the placeholder variable +/// \param skipLanes is the index of the starting lane within the global description of the placeholder variable +/// \return \b true if the lanes can be naturally pushed forward +bool LaneDivide::traceForward(TransformVar *rvn,int4 numLanes,int4 skipLanes) + +{ + Varnode *origvn = rvn->getOriginal(); + list::const_iterator iter,enditer; + iter = origvn->beginDescend(); + enditer = origvn->endDescend(); + while(iter != enditer) { + PcodeOp *op = *iter++; + Varnode *outvn = op->getOut(); + if ((outvn!=(Varnode *)0)&&(outvn->isMark())) + continue; + switch(op->code()) { + case CPUI_SUBPIECE: + { + int4 bytePos = (int4)op->getIn(1)->getOffset(); + int4 outLanes,outSkip; + if (!description.restriction(numLanes, skipLanes, bytePos, outvn->getSize(), outLanes, outSkip)) { + if (allowSubpieceTerminator) { + int4 laneIndex = description.getBoundary(bytePos); + if (laneIndex < 0 || laneIndex >= description.getNumLanes()) // Does piece start on lane boundary? + return false; + if (description.getSize(laneIndex) <= outvn->getSize()) // Is the piece smaller than a lane? + return false; + // Treat SUBPIECE as terminating + TransformOp *rop = newPreexistingOp(2, CPUI_SUBPIECE, op); + opSetInput(rop, rvn + (laneIndex - skipLanes), 0); + opSetInput(rop, newConstant(4, 0, 0), 1); + break; + } + return false; + } + if (outLanes == 1) { + TransformOp *rop = newPreexistingOp(1, CPUI_COPY, op); + opSetInput(rop,rvn + (outSkip-skipLanes), 0); + } + else { + TransformVar *outRvn = setReplacement(outvn,outLanes,outSkip); + if (outRvn == (TransformVar *)0) return false; + // Don't create the placeholder ops, let traceBackward make them + } + break; + } + case CPUI_PIECE: + { + int4 outLanes,outSkip; + int4 bytePos = (op->getIn(0) == origvn) ? op->getIn(1)->getSize() : 0; + if (!description.extension(numLanes, skipLanes, bytePos, outvn->getSize(), outLanes, outSkip)) + return false; + TransformVar *outRvn = setReplacement(outvn,outLanes,outSkip); + if (outRvn == (TransformVar *)0) return false; + // Don't create the placeholder ops, let traceBackward make them + break; + } + case CPUI_COPY: + case CPUI_INT_NEGATE: + case CPUI_INT_AND: + case CPUI_INT_OR: + case CPUI_INT_XOR: + case CPUI_MULTIEQUAL: + { + TransformVar *outRvn = setReplacement(outvn,numLanes,skipLanes); + if (outRvn == (TransformVar *)0) return false; + // Don't create the placeholder ops, let traceBackward make them + break; + } + case CPUI_INT_RIGHT: + { + if (!op->getIn(1)->isConstant()) return false; // Trace must come through op->getIn(0) + TransformVar *outRvn = setReplacement(outvn, numLanes, skipLanes); + if (outRvn == (TransformVar *)0) return false; + // Don't create the placeholder ops, let traceBackward make them + break; + } + case CPUI_STORE: + if (op->getIn(2) != origvn) return false; // Can only propagate through value being stored + if (!buildStore(op,numLanes,skipLanes)) + return false; + break; + default: + return false; + } + } + return true; +} + +/// \brief Pull the logical lanes back through the defining PcodeOp of the given variable +/// +/// Determine if the logical lanes can be pulled back naturally, and create placeholder +/// variables and ops representing the logical data-flow. Update the worklist with any +/// new Varnodes that the lanes get pulled back into. +/// \param rvn is the placeholder variable to pull back +/// \param numLanes is the number of lanes represented by the placeholder variable +/// \param skipLanes is the index of the starting lane within the global description of the placeholder variable +/// \return \b true if the lanes can be naturally pulled back +bool LaneDivide::traceBackward(TransformVar *rvn,int4 numLanes,int4 skipLanes) + +{ + PcodeOp *op = rvn->getOriginal()->getDef(); + if (op == (PcodeOp *)0) return true; // If vn is input + + switch(op->code()) { + case CPUI_INT_NEGATE: + case CPUI_COPY: + { + TransformVar *inVars = setReplacement(op->getIn(0),numLanes,skipLanes); + if (inVars == (TransformVar *)0) return false; + buildUnaryOp(op->code(), op, inVars, rvn, numLanes); + break; + } + case CPUI_INT_AND: + case CPUI_INT_OR: + case CPUI_INT_XOR: + { + TransformVar *in0Vars = setReplacement(op->getIn(0),numLanes,skipLanes); + if (in0Vars == (TransformVar *)0) return false; + TransformVar *in1Vars = setReplacement(op->getIn(1),numLanes,skipLanes); + if (in1Vars == (TransformVar *)0) return false; + buildBinaryOp(op->code(),op,in0Vars,in1Vars,rvn,numLanes); + break; + } + case CPUI_MULTIEQUAL: + if (!buildMultiequal(op, rvn, numLanes, skipLanes)) + return false; + break; + case CPUI_SUBPIECE: + { + Varnode *inVn = op->getIn(0); + int4 bytePos = (int4)op->getIn(1)->getOffset(); + int4 inLanes,inSkip; + if (!description.extension(numLanes, skipLanes, bytePos, inVn->getSize(), inLanes, inSkip)) + return false; + TransformVar *inVars = setReplacement(inVn,inLanes,inSkip); + if (inVars == (TransformVar *)0) return false; + buildUnaryOp(CPUI_COPY,op,inVars + (skipLanes - inSkip), rvn, numLanes); + break; + } + case CPUI_PIECE: + if (!buildPiece(op, rvn, numLanes, skipLanes)) + return false; + break; + case CPUI_LOAD: + if (!buildLoad(op, rvn, numLanes, skipLanes)) + return false; + break; + case CPUI_INT_RIGHT: + if (!buildRightShift(op, rvn, numLanes, skipLanes)) + return false; + break; + default: + return false; + } + return true; +} + +/// \return \b true if the lane split for the top Varnode on the work list is propagated through local operators +bool LaneDivide::processNextWork(void) + +{ + TransformVar *rvn = workList.back().lanes; + int4 numLanes = workList.back().numLanes; + int4 skipLanes = workList.back().skipLanes; + + workList.pop_back(); + + if (!traceBackward(rvn,numLanes,skipLanes)) return false; + return traceForward(rvn,numLanes,skipLanes); +} + +/// \param f is the function being transformed +/// \param root is the root Varnode to start tracing lanes from +/// \param desc is a description of the lanes on the root Varnode +/// \param allowDowncast is \b true if we all SUBPIECE to be treated as terminating +LaneDivide::LaneDivide(Funcdata *f,Varnode *root,const LaneDescription &desc,bool allowDowncast) + : TransformManager(f), description(desc) +{ + allowSubpieceTerminator = allowDowncast; + setReplacement(root, desc.getNumLanes(), 0); +} + +/// Push the lanes around from the root, setting up the explicit transforms as we go. +/// If at any point, the lanes cannot be naturally pushed, return \b false. +/// \return \b true if a full transform has been constructed that can split into explicit lanes +bool LaneDivide::doTrace(void) + +{ + if (workList.empty()) + return false; // Nothing to do + bool retval = true; + while(!workList.empty()) { // Process the work list until its done + if (!processNextWork()) { + retval = false; + break; + } + } + + clearVarnodeMarks(); + if (!retval) return false; + return true; +} diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh index a8700d8435..543ba93826 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh @@ -109,130 +109,78 @@ class SubvariableFlow { Varnode *getReplaceVarnode(ReplaceVarnode *rvn); bool processNextWork(void); ///< Extend the subgraph from the next node in the worklist public: - SubvariableFlow(Funcdata *f,Varnode *root,uintb mask,bool aggr,bool sext); - bool doTrace(void); - void doReplacement(void); + SubvariableFlow(Funcdata *f,Varnode *root,uintb mask,bool aggr,bool sext); ///< Constructor + bool doTrace(void); ///< Trace logical value through data-flow, constructing transform + void doReplacement(void); ///< Perform the discovered transform, making logical values explicit }; -// Class for splitting up varnodes that hold 2 logical variables -class SplitFlow { - class ReplaceVarnode { - friend class SplitFlow; - Varnode *vn; // Varnode being split - Varnode *replaceLo; // Replacement holding least significant part of original - Varnode *replaceHi; // Replacement holding most significant part - bool defTraversed; // Has the defining op been traversed - public: - ReplaceVarnode(void); - }; - class ReplaceOp { - friend class SplitFlow; - PcodeOp *op; // Original op being split - OpCode opcode; // Replacement opcode - PcodeOp *loOp; // Replacement for least sig part - PcodeOp *hiOp; // Replacement for most sig part - int4 numParams; - bool doDelete; // Original operation should be deleted - bool isLogicalInput; // Op is putting a logical value into the whole, as opposed to pulling one out - ReplaceVarnode *output; // Output varnode(s) if needed - public: - ReplaceOp(bool isLogic,PcodeOp *o,OpCode opc,int4 num); - }; - int4 concatSize; // Size of combined logicals - int4 loSize; // Size of logical piece in least sig part of combined - int4 hiSize; // Size of logical piece in most sig part of combined - Funcdata *fd; - map varmap; - list oplist; - vector worklist; - void assignReplaceOp(bool isLogicalInput,PcodeOp *op,OpCode opc,int4 numParam,ReplaceVarnode *outrvn); - void assignLogicalPieces(ReplaceVarnode *rvn); - void buildReplaceOutputs(ReplaceOp *rop); - void replacePiece(ReplaceOp *rop); - void replaceZext(ReplaceOp *rop); - void replaceLeftInput(ReplaceOp *rop); - void replaceLeftTerminal(ReplaceOp *rop); - void replaceOp(ReplaceOp *rop); - ReplaceVarnode *setReplacement(Varnode *vn,bool &inworklist); - bool addOpOutput(PcodeOp *op); - bool addOpInputs(PcodeOp *op,ReplaceVarnode *outrvn,int4 numParam); - bool traceForward(ReplaceVarnode *rvn); - bool traceBackward(ReplaceVarnode *rvn); - bool processNextWork(void); +/// \brief Class for splitting up Varnodes that hold 2 logical variables +/// +/// Starting from a \e root Varnode provided to the constructor, \b this class looks for data-flow +/// that consistently holds 2 logical values in a single Varnode. If doTrace() returns \b true, +/// a consistent view has been created and invoking apply() will split all Varnodes and PcodeOps +/// involved in the data-flow into their logical pieces. +class SplitFlow : public TransformManager { + LaneDescription laneDescription; ///< Description of how to split Varnodes + vector worklist; ///< Pending work list of Varnodes to push the split through + TransformVar *setReplacement(Varnode *vn); + bool addOp(PcodeOp *op,TransformVar *rvn,int4 slot); + bool traceForward(TransformVar *rvn); + bool traceBackward(TransformVar *rvn); + bool processNextWork(void); ///< Process the next logical value on the worklist public: - SplitFlow(Funcdata *f,Varnode *root,int4 lowSize); - void doReplacement(void); - bool doTrace(void); + SplitFlow(Funcdata *f,Varnode *root,int4 lowSize); ///< Constructor + bool doTrace(void); ///< Trace split through data-flow, constructing transform }; -// Structures for tracing floating point variables if they are -// stored at points in a higher precision encoding. This is nearly identical -// in spirit to the SubvariableFlow class, but it performs on floating point -// variables contained in higher precision storage, rather than integers stored -// as a subfield of a bigger integer - -// This the floating point version of SubvariablFlow, it follows the flow of a logical lower -// precision value stored in higher precision locations -class SubfloatFlow { - class ReplaceOp; - class ReplaceVarnode { - friend class SubfloatFlow; - Varnode *vn; // Varnode being split - Varnode *replacement; // The new subvariable varnode - ReplaceOp *def; // Defining op for new varnode - }; - - class ReplaceOp { - friend class SubfloatFlow; - PcodeOp *op; // op getting paralleled - PcodeOp *replacement; // The new op - OpCode opc; // type of new op - int4 numparams; - ReplaceVarnode *output; // varnode output - vector input; // varnode inputs - }; - - class PulloutRecord { // Node where logical variable is getting pulled out into a real varnode - friend class SubfloatFlow; - OpCode opc; // (possibly) new opcode - PcodeOp *pullop; // Op producing the real output - ReplaceVarnode *input; // The logical variable input - }; - - class CompareRecord { - friend class SubfloatFlow; - ReplaceVarnode *in1; - ReplaceVarnode *in2; - PcodeOp *compop; - }; - - int4 precision; // Number of bytes of precision in the logical flow - Funcdata *fd; - const FloatFormat *format; - map varmap; - list newvarlist; - list oplist; - list pulllist; - list complist; - vector worklist; - ReplaceVarnode *setReplacement(Varnode *vn,bool &inworklist); - ReplaceVarnode *setReplacementNoFlow(Varnode *vn); - ReplaceOp *createOp(OpCode opc,int4 numparam,ReplaceVarnode *outrvn); - ReplaceOp *createOpDown(OpCode opc,int4 numparam,PcodeOp *op,ReplaceVarnode *inrvn,int4 slot); - bool traceForward(ReplaceVarnode *rvn); - bool traceBackward(ReplaceVarnode *rvn); - bool createLink(ReplaceOp *rop,int4 slot,Varnode *vn); - void addtopulllist(PcodeOp *pullop,ReplaceVarnode *rvn); - bool addtopushlist(PcodeOp *pushop,ReplaceVarnode *rvn); - void addtocomplist(ReplaceVarnode *in1,ReplaceVarnode *in2,PcodeOp *op); - ReplaceVarnode *addConstant(Varnode *vn); - void replaceInput(ReplaceVarnode *rvn); - Varnode *getReplaceVarnode(ReplaceVarnode *rvn); +/// \brief Class for tracing changes of precision in floating point variables +/// +/// It follows the flow of a logical lower precision value stored in higher precision locations +/// and then rewrites the data-flow in terms of the lower precision, eliminating the +/// precision conversions. +class SubfloatFlow : public TransformManager { + int4 precision; ///< Number of bytes of precision in the logical flow + int4 terminatorCount; ///< Number of terminating nodes reachable via the root + const FloatFormat *format; ///< The floating-point format of the logical value + vector worklist; ///< Current list of placeholders that still need to be traced + TransformVar *setReplacement(Varnode *vn); + bool traceForward(TransformVar *rvn); + bool traceBackward(TransformVar *rvn); bool processNextWork(void); public: SubfloatFlow(Funcdata *f,Varnode *root,int4 prec); - bool doTrace(void); - void doReplacement(void); + virtual bool preserveAddress(Varnode *vn,int4 bitSize,int4 lsbOffset) const; + bool doTrace(void); ///< Trace logical value as far as possible +}; + +class LaneDivide : public TransformManager { + /// \brief Description of a large Varnode that needs to be traced (in the worklist) + class WorkNode { + friend class LaneDivide; + Varnode *vn; ///< The underlying Varnode with lanes + TransformVar *lanes; ///< Lane placeholders for underyling Varnode + int4 numLanes; ///< Number of lanes in the particular Varnode + int4 skipLanes; ///< Number of lanes to skip in the global description + }; + + LaneDescription description; ///< Global description of lanes that need to be split + vector workList; ///< List of Varnodes still left to trace + bool allowSubpieceTerminator; ///< \b true if we allow lanes to be cast (via SUBPIECE) to a smaller integer size + + TransformVar *setReplacement(Varnode *vn,int4 numLanes,int4 skipLanes); + void buildUnaryOp(OpCode opc,PcodeOp *op,TransformVar *inVars,TransformVar *outVars,int4 numLanes); + void buildBinaryOp(OpCode opc,PcodeOp *op,TransformVar *in0Vars,TransformVar *in1Vars,TransformVar *outVars,int4 numLanes); + bool buildPiece(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes); + bool buildMultiequal(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes); + bool buildStore(PcodeOp *op,int4 numLanes,int4 skipLanes); + bool buildLoad(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes); + bool buildRightShift(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes); + bool traceForward(TransformVar *rvn,int4 numLanes,int4 skipLanes); + bool traceBackward(TransformVar *rvn,int4 numLanes,int4 skipLanes); + bool processNextWork(void); ///< Process the next Varnode on the work list +public: + LaneDivide(Funcdata *f,Varnode *root,const LaneDescription &desc,bool allowDowncast); ///< Constructor + bool doTrace(void); ///< Trace lanes as far as possible from the root Varnode }; #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/transform.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.cc new file mode 100644 index 0000000000..010d8f54db --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.cc @@ -0,0 +1,746 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "transform.hh" +#include "funcdata.hh" + +/// \param op2 is the lane description to copy from +LaneDescription::LaneDescription(const LaneDescription &op2) + +{ + wholeSize = op2.wholeSize; + laneSize = op2.laneSize; + lanePosition = op2.lanePosition; +} + +/// Create lanes that are all the same size +/// \param origSize is the size of the whole in bytes +/// \param sz is the size of a lane in bytes +LaneDescription::LaneDescription(int4 origSize,int4 sz) + +{ + wholeSize = origSize; + int4 numLanes = origSize / sz; + laneSize.resize(numLanes); + lanePosition.resize(numLanes); + int4 pos = 0; + for(int4 i=0;i newLaneSize; + lanePosition.clear(); + int4 newPosition = 0; + for(int4 i=firstLane;i wholeSize) + return -1; + if (bytePos == wholeSize) + return lanePosition.size(); + int4 min = 0; + int4 max = lanePosition.size() - 1; + while(min <= max) { + int4 index = (min + max) / 2; + int4 pos = lanePosition[index]; + if (pos == bytePos) return index; + if (pos < bytePos) + min = index + 1; + else + max = index - 1; + } + return -1; +} + +/// \brief Decide if a given truncation is natural for \b this description +/// +/// A subset of lanes are specified and a truncation (given by a byte position and byte size). +/// If the truncation, relative to the subset, contains at least 1 lane and does not split any +/// lanes, then return \b true and pass back the number of lanes and starting lane of the truncation. +/// \param numLanes is the number of lanes in the original subset +/// \param skipLanes is the starting (least significant) lane index of the original subset +/// \param bytePos is the number of bytes to truncate from the front (least significant portion) of the subset +/// \param size is the number of bytes to include in the truncation +/// \param resNumLanes will hold the number of lanes in the truncation +/// \param resSkipLanes will hold the starting lane in the truncation +/// \return \b true if the truncation is natural +bool LaneDescription::restriction(int4 numLanes,int4 skipLanes,int4 bytePos,int4 size, + int4 &resNumLanes,int4 &resSkipLanes) const + +{ + resSkipLanes = getBoundary(lanePosition[skipLanes] + bytePos); + if (resSkipLanes < 0) return false; + int4 finalIndex = getBoundary(lanePosition[skipLanes] + bytePos + size); + if (finalIndex < 0) return false; + resNumLanes = finalIndex - resSkipLanes; + return (resNumLanes != 0); +} + +/// \brief Decide if a given subset of lanes can be extended naturally for \b this description +/// +/// A subset of lanes are specified and their position within an extension (given by a byte position). +/// The size in bytes of the extension is also given. If the extension is contained within \b this description, +/// and the boundaries of the extension don't split any lanes, then return \b true and pass back +/// the number of lanes and starting lane of the extension. +/// \param numLanes is the number of lanes in the original subset +/// \param skipLanes is the starting (least significant) lane index of the original subset +/// \param bytePos is the number of bytes to truncate from the front (least significant portion) of the extension +/// \param size is the number of bytes in the extension +/// \param resNumLanes will hold the number of lanes in the extension +/// \param resSkipLanes will hold the starting lane in the extension +/// \return \b true if the extension is natural +bool LaneDescription::extension(int4 numLanes,int4 skipLanes,int4 bytePos,int4 size, + int4 &resNumLanes,int4 &resSkipLanes) const + +{ + resSkipLanes = getBoundary(lanePosition[skipLanes] - bytePos); + if (resSkipLanes < 0) return false; + int4 finalIndex = getBoundary(lanePosition[skipLanes] - bytePos + size); + if (finalIndex < 0) return false; + resNumLanes = finalIndex - resSkipLanes; + return (resNumLanes != 0); +} + +/// Create the Varnode object (constant, unique, vector piece) described by the +/// given placeholder. If the Varnode is an output, assume the op already exists +/// and create the Varnode as an output. Set the \b replacement field with the +/// new Varnode. +/// \param fd is the function in which to create the replacement +void TransformVar::createReplacement(Funcdata *fd) + +{ + if (replacement != (Varnode *)0) + return; // Replacement already created + switch(type) { + case TransformVar::preexisting: + replacement = vn; + break; + case TransformVar::constant: + replacement = fd->newConstant(byteSize,val); + break; + case TransformVar::normal_temp: + case TransformVar::piece_temp: + if (def == (TransformOp *)0) + replacement = fd->newUnique(byteSize); + else + replacement = fd->newUniqueOut(byteSize,def->replacement); + break; + case TransformVar::piece: + { + int4 bytePos = (int4)val; + if ((bytePos & 7) != 0) + throw LowlevelError("Varnode piece is not byte aligned"); + bytePos >>= 3; + if (vn->getSpace()->isBigEndian()) + bytePos = vn->getSize() - bytePos - byteSize; + Address addr = vn->getAddr() + bytePos; + if (def == (TransformOp *)0) + replacement = fd->newVarnode(byteSize,addr); + else + replacement = fd->newVarnodeOut(byteSize, addr, def->replacement); + fd->transferVarnodeProperties(vn,replacement,bytePos); + break; + } + case TransformVar::constant_iop: + { + PcodeOp *indeffect = PcodeOp::getOpFromConst(Address(fd->getArch()->getIopSpace(),val)); + replacement = fd->newVarnodeIop(indeffect); + break; + } + default: + throw LowlevelError("Bad TransformVar type"); + } +} + +/// Create a new PcodeOp or modify an existing one so that it matches this placeholder description. +/// Go ahead an insert the new PcodeOp into the basic block if possible +/// \param fd is the function in which to make the modifications +void TransformOp::createReplacement(Funcdata *fd) + +{ + if ((special & TransformOp::op_preexisting)!=0) { + replacement = op; + fd->opSetOpcode(op, opc); + while(input.size() < op->numInput()) + fd->opRemoveInput(op, op->numInput()-1); + while(op->numInput() < input.size()) + fd->opInsertInput(op, (Varnode *)0, op->numInput()-1); + } + else { + replacement = fd->newOp(input.size(),op->getAddr()); + fd->opSetOpcode(replacement, opc); + if (output != (TransformVar *)0) + output->createReplacement(fd); + if (follow == (TransformOp *)0) { // Can be inserted immediately + if (opc == CPUI_MULTIEQUAL) + fd->opInsertBegin(replacement, op->getParent()); + else + fd->opInsertBefore(replacement, op); + } + } +} + +/// \param fd is the function into which the PcodeOp will be inserted +/// \return \b true if the op is successfully inserted or already inserted +bool TransformOp::attemptInsertion(Funcdata *fd) + +{ + if (follow != (TransformOp *)0) { + if (follow->follow == (TransformOp *)0) { // Check if the follow is inserted + if (opc == CPUI_MULTIEQUAL) + fd->opInsertBegin(replacement, follow->replacement->getParent()); + else + fd->opInsertBefore(replacement,follow->replacement); + follow = (TransformOp *)0; // Mark that this has been inserted + return true; + } + return false; + } + return true; // Already inserted +} + +void LanedRegister::LanedIterator::normalize(void) + +{ + uint4 flag = 1; + flag <<= size; + while(flag <= mask) { + if ((flag & mask) != 0) return; // Found a valid lane size + size += 1; + flag <<= 1; + } + size = -1; // Indicate ending iterator +} + +/// Read XML of the form \ +/// \param el is the particular \e register tag +/// \param manage is used to map register names to storage info +/// \return \b true if the XML description provides lane sizes +bool LanedRegister::restoreXml(const Element *el,const AddrSpaceManager *manage) + +{ + string laneSizes; + for(int4 i=0;igetNumAttributes();++i) { + if (el->getAttributeName(i) == "vector_lane_sizes") { + laneSizes = el->getAttributeValue(i); + break; + } + } + if (laneSizes.empty()) return false; + VarnodeData storage; + storage.space = (AddrSpace *)0; + storage.restoreXml(el, manage); + wholeSize = storage.size; + sizeBitMask = 0; + string::size_type pos = 0; + while(pos != string::npos) { + string::size_type nextPos = laneSizes.find(',',pos); + string value; + if (nextPos == string::npos) { + value = laneSizes.substr(pos); // To the end of the string + pos = nextPos; + } + else { + value = laneSizes.substr(pos,(nextPos - pos)); + pos = nextPos + 1; + if (pos >= laneSizes.size()) + pos = string::npos; + } + istringstream s(value); + s.unsetf(ios::dec | ios::hex | ios::oct); + int4 sz = -1; + s >> sz; + if (sz < 0 || sz > 16) + throw LowlevelError("Bad lane size: " + value); + addLaneSize(sz); + } + return true; +} + +TransformManager::~TransformManager(void) + +{ + map::iterator iter; + for(iter=pieceMap.begin();iter!=pieceMap.end();++iter) { + delete [] (*iter).second; + } +} + +/// \brief Should the address of the given Varnode be preserved when constructing a piece +/// +/// A new Varnode will be created that represents a logical piece of the given Varnode. +/// This routine determines whether the new Varnode should be constructed using +/// storage which overlaps the given Varnode. It returns \b true if overlapping storage +/// should be used, \b false if the new Varnode should be constructed as a unique temporary. +/// \param vn is the given Varnode +/// \param bitSize is the logical size of the Varnode piece being constructed +/// \param lsbOffset is the least significant bit position of the logical value within the given Varnode +/// \return \b true if overlapping storage should be used in construction +bool TransformManager::preserveAddress(Varnode *vn,int4 bitSize,int4 lsbOffset) const + +{ + if ((lsbOffset & 7) != 0) return false; // Logical value not aligned + if (vn->getSpace()->getType() == IPTR_INTERNAL) return false; + return true; +} + +void TransformManager::clearVarnodeMarks(void) + +{ + map::const_iterator iter; + for(iter=pieceMap.begin();iter!=pieceMap.end();++iter) { + Varnode *vn = (*iter).second->vn; + if (vn == (Varnode *)0) + continue; + vn->clearMark(); + } +} + +/// \param vn is the preexisting Varnode to create a placeholder for +/// \return the new placeholder node +TransformVar *TransformManager::newPreexistingVarnode(Varnode *vn) + +{ + TransformVar *res = new TransformVar[1]; + pieceMap[vn->getCreateIndex()] = res; // Enter preexisting Varnode into map, so we don't make another placeholder + + // value of 0 treats this as "piece" of itself at offset 0, allows getPiece() to find it + res->initialize(TransformVar::preexisting,vn,vn->getSize()*8,vn->getSize(),0); + res->flags = TransformVar::split_terminator; + return res; +} + +/// \param size is the size in bytes of the new unique Varnode +/// \return the new placeholder node +TransformVar *TransformManager::newUnique(int4 size) + +{ + newVarnodes.push_back(TransformVar()); + TransformVar *res = &newVarnodes.back(); + res->initialize(TransformVar::normal_temp,(Varnode *)0,size*8,size,0); + return res; +} + +/// Create a new constant in the transform view. A piece of an existing constant +/// can be created by giving the existing value and the least significant offset. +/// \param size is the size in bytes of the new constant +/// \param lsbOffset is the number of bits to strip off of the existing value +/// \param val is the value of the constant +/// \return the new placeholder node +TransformVar *TransformManager::newConstant(int4 size,int4 lsbOffset,uintb val) + +{ + newVarnodes.push_back(TransformVar()); + TransformVar *res = &newVarnodes.back(); + res->initialize(TransformVar::constant,(Varnode *)0,size*8,size,(val >> lsbOffset) & calc_mask(size)); + return res; +} + +/// Used for creating INDIRECT placeholders. +/// \param vn is the original iop parameter to the INDIRECT +/// \return the new placeholder node +TransformVar *TransformManager::newIop(Varnode *vn) + +{ + newVarnodes.push_back(TransformVar()); + TransformVar *res = &newVarnodes.back(); + res->initialize(TransformVar::constant_iop,(Varnode *)0,vn->getSize()*8,vn->getSize(),vn->getOffset()); + return res; +} + +/// Given a single logical value within a larger Varnode, create a placeholder for +/// that logical value. +/// \param vn is the large Varnode +/// \param bitSize is the size of the logical value in bits +/// \param lsbOffset is the number of least significant bits of the Varnode dropped from the value +/// \return the placeholder variable +TransformVar *TransformManager::newPiece(Varnode *vn,int4 bitSize,int4 lsbOffset) + +{ + TransformVar *res = new TransformVar[1]; + pieceMap[vn->getCreateIndex()] = res; + int4 byteSize = (bitSize + 7) / 8; + uint4 type = preserveAddress(vn, bitSize, lsbOffset) ? TransformVar::piece : TransformVar::piece_temp; + res->initialize(type, vn, bitSize, byteSize, lsbOffset); + res->flags = TransformVar::split_terminator; + return res; +} + +/// \brief Create placeholder nodes splitting a Varnode into its lanes +/// +/// Given a big Varnode and a lane description, create placeholders for all the explicit pieces +/// that the big Varnode will be split into. +/// \param vn is the big Varnode to split +/// \param description shows how the big Varnode will be split +/// \return an array of the new TransformVar placeholders from least to most significant +TransformVar *TransformManager::newSplit(Varnode *vn,const LaneDescription &description) + +{ + int4 num = description.getNumLanes(); + TransformVar *res = new TransformVar[num]; + pieceMap[vn->getCreateIndex()] = res; + for(int4 i=0;iisConstant()) + newVar->initialize(TransformVar::constant,vn,byteSize * 8,byteSize, (vn->getOffset() >> bitpos) & calc_mask(byteSize)); + else { + uint4 type = preserveAddress(vn, byteSize * 8, bitpos) ? TransformVar::piece : TransformVar::piece_temp; + newVar->initialize(type,vn,byteSize * 8, byteSize, bitpos); + } + } + res[num-1].flags = TransformVar::split_terminator; + return res; +} + +/// \brief Create placeholder nodes splitting a Varnode into a subset of lanes in the given description +/// +/// Given a big Varnode and specific subset of a lane description, create placeholders for all +/// the explicit pieces that the big Varnode will be split into. +/// \param vn is the big Varnode to split +/// \param description gives a list of potentional lanes +/// \param numLanes is the number of lanes in the subset +/// \param startLane is the starting (least significant) lane in the subset +/// \return an array of the new TransformVar placeholders from least to most significant +TransformVar *TransformManager::newSplit(Varnode *vn,const LaneDescription &description,int4 numLanes,int4 startLane) + +{ + TransformVar *res = new TransformVar[numLanes]; + pieceMap[vn->getCreateIndex()] = res; + int4 baseBitPos = description.getPosition(startLane) * 8; + for(int4 i=0;iisConstant()) + newVar->initialize(TransformVar::constant,vn,byteSize * 8, byteSize, (vn->getOffset() >> bitpos) & calc_mask(byteSize)); + else { + uint4 type = preserveAddress(vn, byteSize * 8, bitpos) ? TransformVar::piece : TransformVar::piece_temp; + newVar->initialize(type,vn,byteSize * 8, byteSize, bitpos); + } + } + res[numLanes-1].flags = TransformVar::split_terminator; + return res; +} + +/// \brief Create a new placeholder op intended to replace an existing op +/// +/// An uninitialized placeholder for the new op is created. +/// \param numParams is the number of Varnode inputs intended for the new op +/// \param opc is the opcode of the new op +/// \param replace is the existing op the new op will replace +/// \return the new placeholder node +TransformOp *TransformManager::newOpReplace(int4 numParams,OpCode opc,PcodeOp *replace) + +{ + newOps.push_back(TransformOp()); + TransformOp &rop(newOps.back()); + rop.op = replace; + rop.replacement = (PcodeOp *)0; + rop.opc = opc; + rop.special = TransformOp::op_replacement; + rop.output = (TransformVar *)0; + rop.follow = (TransformOp *)0; + rop.input.resize(numParams,(TransformVar *)0); + return &rop; +} + +/// \brief Create a new placeholder op that will not replace an existing op +/// +/// An uninitialized placeholder for the new op is created. When (if) the new op is created +/// it will not replace an existing op. The op that follows it must be given. +/// \param numParams is the number of Varnode inputs intended for the new op +/// \param opc is the opcode of the new op +/// \param follow is the placeholder for the op that follow the new op when it is created +/// \return the new placeholder node +TransformOp *TransformManager::newOp(int4 numParams,OpCode opc,TransformOp *follow) + +{ + newOps.push_back(TransformOp()); + TransformOp &rop(newOps.back()); + rop.op = follow->op; + rop.replacement = (PcodeOp *)0; + rop.opc = opc; + rop.special = 0; + rop.output = (TransformVar *)0; + rop.follow = follow; + rop.input.resize(numParams,(TransformVar *)0); + return &rop; +} + +/// \brief Create a new placeholder op for an existing PcodeOp +/// +/// An uninitialized placeholder for the existing op is created. When applied, this causes +/// the op to be transformed as described by the placeholder, changing its opcode and +/// inputs. The output however is unaffected. +/// \param numParams is the number of Varnode inputs intended for the transformed op +/// \param opc is the opcode of the transformed op +/// \param originalOp is the preexisting PcodeOp +/// \return the new placeholder node +TransformOp *TransformManager::newPreexistingOp(int4 numParams,OpCode opc,PcodeOp *originalOp) + +{ + newOps.push_back(TransformOp()); + TransformOp &rop(newOps.back()); + rop.op = originalOp; + rop.replacement = (PcodeOp *)0; + rop.opc = opc; + rop.special = TransformOp::op_preexisting; + rop.output = (TransformVar *)0; + rop.follow = (TransformOp *)0; + rop.input.resize(numParams,(TransformVar *)0); + return &rop; +} + +/// Check if a placeholder node was created for the preexisting Varnode for, +/// otherwise create a new one. +/// \param vn is the preexisting Varnode to find a placeholder for +/// \return the placeholder node +TransformVar *TransformManager::getPreexistingVarnode(Varnode *vn) + +{ + if (vn->isConstant()) + return newConstant(vn->getSize(), 0, vn->getOffset()); + map::const_iterator iter; + iter = pieceMap.find(vn->getCreateIndex()); + if (iter != pieceMap.end()) + return (*iter).second; + return newPreexistingVarnode(vn); +} + +/// Given a big Varnode, find the placeholder corresponding to the logical value +/// given by a size and significance offset. If it doesn't exist, create it. +/// \param vn is the big Varnode containing the logical value +/// \param bitSize is the size of the logical value in bytes +/// \param lsbOffset is the signficance offset of the logical value within the Varnode +/// \return the found/created placeholder +TransformVar *TransformManager::getPiece(Varnode *vn,int4 bitSize,int4 lsbOffset) + +{ + map::const_iterator iter; + iter = pieceMap.find(vn->getCreateIndex()); + if (iter != pieceMap.end()) { + TransformVar *res = (*iter).second; + if (res->bitSize != bitSize || res->val != lsbOffset) + throw LowlevelError("Cannot create multiple pieces for one Varnode through getPiece"); + return res; + } + return newPiece(vn,bitSize,lsbOffset); +} + +/// \brief Find (or create) placeholder nodes splitting a Varnode into its lanes +/// +/// Given a big Varnode and a lane description, look up placeholders for all its +/// explicit pieces. If they don't exist, create them. +/// \param vn is the big Varnode to split +/// \param description shows how the big Varnode will be split +/// \return an array of the TransformVar placeholders from least to most significant +TransformVar *TransformManager::getSplit(Varnode *vn,const LaneDescription &description) + +{ + map::const_iterator iter; + iter = pieceMap.find(vn->getCreateIndex()); + if (iter != pieceMap.end()) { + return (*iter).second; + } + return newSplit(vn,description); +} + +/// \brief Find (or create) placeholder nodes splitting a Varnode into a subset of lanes from a description +/// +/// Given a big Varnode and a specific subset of a lane description, look up placeholders +/// for all the explicit pieces. If they don't exist, create them. +/// \param vn is the big Varnode to split +/// \param description describes all the possible lanes +/// \param numLanes is the number of lanes in the subset +/// \param startLane is the starting (least significant) lane in the subset +/// \return an array of the TransformVar placeholders from least to most significant +TransformVar *TransformManager::getSplit(Varnode *vn,const LaneDescription &description,int4 numLanes,int4 startLane) + +{ + map::const_iterator iter; + iter = pieceMap.find(vn->getCreateIndex()); + if (iter != pieceMap.end()) { + return (*iter).second; + } + return newSplit(vn,description,numLanes,startLane); +} + +/// \brief Handle some special PcodeOp marking +/// If a PcodeOp is an INDIRECT creation, we need to do special marking of the op and Varnodes +/// \param rop is the placeholder op with the special requirement +void TransformManager::specialHandling(TransformOp &rop) +{ + if ((rop.special & TransformOp::indirect_creation) != 0) + fd->markIndirectCreation(rop.replacement, false); + else if ((rop.special & TransformOp::indirect_creation_possible_out) != 0) + fd->markIndirectCreation(rop.replacement, true); +} + +/// Run through the list of TransformOp placeholders and create the actual PcodeOp object. +/// If the op has an output Varnode, create it. Make sure all the new ops are inserted in +/// control flow. +void TransformManager::createOps(void) + +{ + list::iterator iter; + for(iter=newOps.begin();iter!=newOps.end();++iter) + (*iter).createReplacement(fd); + + int4 followCount; + do { + followCount = 0; + for(iter=newOps.begin();iter!=newOps.end();++iter) { + if (!(*iter).attemptInsertion(fd)) + followCount += 1; + } + } while(followCount != 0); +} + +/// Record any input vars in the given container +/// \param inputList will hold any inputs +void TransformManager::createVarnodes(vector &inputList) + +{ + map::iterator piter; + for(piter=pieceMap.begin();piter!=pieceMap.end();++piter) { + TransformVar *vArray = (*piter).second; + for(int4 i=0;;++i) { + TransformVar *rvn = vArray + i; + if (rvn->type == TransformVar::piece) { + Varnode *vn = rvn->vn; + if (vn->isInput()) { + inputList.push_back(rvn); + if (vn->isMark()) + rvn->flags |= TransformVar::input_duplicate; + else + vn->setMark(); + } + } + rvn->createReplacement(fd); + if ((rvn->flags & TransformVar::split_terminator)!=0) + break; + } + } + list::iterator iter; + for(iter=newVarnodes.begin();iter!=newVarnodes.end();++iter) { + (*iter).createReplacement(fd); + } +} + +void TransformManager::removeOld(void) + +{ + list::iterator iter; + for(iter=newOps.begin();iter!=newOps.end();++iter) { + TransformOp &rop(*iter); + if ((rop.special & TransformOp::op_replacement) != 0) { + if (!rop.op->isDead()) + fd->opDestroy(rop.op); // Destroy old op (and its output Varnode) + } + } +} + +/// Remove all input Varnodes from the given container. +/// Mark all the replacement Varnodes as inputs. +/// \param inputList is the given container of input placeholders +void TransformManager::transformInputVarnodes(vector &inputList) + +{ + for(int4 i=0;iflags & TransformVar::input_duplicate)==0) + fd->deleteVarnode(rvn->vn); + rvn->replacement = fd->setInputVarnode(rvn->replacement); + } +} + +void TransformManager::placeInputs(void) + +{ + list::iterator iter; + for(iter=newOps.begin();iter!=newOps.end();++iter) { + TransformOp &rop(*iter); + PcodeOp *op = rop.replacement; + for(int4 i=0;ireplacement; + fd->opSetInput(op, vn, i); + } + specialHandling(rop); + } +} + +void TransformManager::apply(void) + +{ + vector inputList; + createOps(); + createVarnodes(inputList); + removeOld(); + transformInputVarnodes(inputList); + placeInputs(); +} diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh new file mode 100644 index 0000000000..671ffd70ba --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh @@ -0,0 +1,229 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/// \file transform.hh +/// \brief Classes for building large scale transforms of function data-flow +#ifndef __TRANSFORM__ +#define __TRANSFORM__ + +#include "varnode.hh" +class Funcdata; // Forward declaration +class TransformOp; + +/// \brief Placeholder node for Varnode that will exist after a transform is applied to a function +class TransformVar { + friend class TransformManager; + friend class TransformOp; +public: + /// \brief Types of replacement Varnodes + enum { + piece = 1, ///< New Varnode is a piece of an original Varnode + preexisting = 2, ///< Varnode preexisted in the original data-flow + normal_temp = 3, ///< A new temporary (unique space) Varnode + piece_temp = 4, ///< A temporary representing a piece of an original Varnode + constant = 5, ///< A new constant Varnode + constant_iop = 6, ///< Special iop constant encoding a PcodeOp reference + }; + /// \brief Flags for a TransformVar + enum { + split_terminator = 1, ///< The last (most significant piece) of a split array + input_duplicate = 2 ///< This is a piece of an input that has already been visited + }; +private: + Varnode *vn; ///< Original \b big Varnode of which \b this is a component + Varnode *replacement; ///< The new explicit lane Varnode + uint4 type; ///< Type of new Varnode + uint4 flags; ///< Boolean properties of the placeholder + int4 byteSize; ///< Size of the lane Varnode in bytes + int4 bitSize; ///< Size of the logical value in bits + uintb val; ///< Value of constant or (bit) position within the original big Varnode + TransformOp *def; ///< Defining op for new Varnode + void createReplacement(Funcdata *fd); ///< Create the new/modified variable this placeholder represents + void initialize(uint4 tp,Varnode *v,int4 bits,int4 bytes,uintb value); +public: + Varnode *getOriginal(void) const { return vn; } ///< Get the original Varnode \b this placeholder models + TransformOp *getDef(void) const { return def; } ///< Get the operator that defines this placeholder variable +}; + +/// \brief Placeholder node for PcodeOp that will exist after a transform is applied to a function +class TransformOp { + friend class TransformManager; + friend class TransformVar; +public: + /// Special annotations on new pcode ops + enum { + op_replacement = 1, ///< Op replaces an existing op + op_preexisting = 2, ///< Op already exists (but will be transformed) + indirect_creation = 4, ///< Mark op as indirect creation + indirect_creation_possible_out = 8 ///< Mark op as indirect creation and possible call output + }; +private: + PcodeOp *op; ///< Original op which \b this is splitting (or null) + PcodeOp *replacement; ///< The new replacement op + OpCode opc; ///< Opcode of the new op + uint4 special; ///< Special handling code when creating + TransformVar *output; ///< Varnode output + vector input; ///< Varnode inputs + TransformOp *follow; ///< The following op after \b this (if not null) + void createReplacement(Funcdata *fd); ///< Create the new/modified op this placeholder represents + bool attemptInsertion(Funcdata *fd); ///< Try to put the new PcodeOp into its basic block +public: + TransformVar *getOut(void) const { return output; } ///< Get the output placeholder variable for \b this operator + TransformVar *getIn(int4 i) const { return input[i]; } ///< Get the i-th input placeholder variable for \b this +}; + +/// \brief Describes a (register) storage location and the ways it might be split into lanes +class LanedRegister { + friend class LanedIterator; +public: + /// \brief Class for iterating over possible lane sizes + class LanedIterator { + int4 size; ///< Current lane size + uint4 mask; ///< Collection being iterated over + void normalize(void); ///< Normalize the iterator, after increment or initialization + public: + LanedIterator(const LanedRegister *lanedR) { size = 0; mask = lanedR->sizeBitMask; normalize(); } ///< Constructor + LanedIterator(void) { size = -1; mask = 0; } ///< Constructor for ending iterator + LanedIterator &operator++(void) { size += 1; normalize(); return *this; } ///< Preincrement operator + int4 operator*(void) const { return size; } /// Dereference operator + LanedIterator &operator=(const LanedIterator &op2) { size = op2.size; mask = op2.mask; return *this; } ///< Assignment + bool operator==(const LanedIterator &op2) const { return (size == op2.size); } ///< Equal operator + bool operator!=(const LanedIterator &op2) const { return (size != op2.size); } ///< Not-equal operator + }; + typedef LanedIterator const_iterator; +private: + int4 wholeSize; ///< Size of the whole register + uint4 sizeBitMask; ///< A 1-bit for every permissible lane size +public: + LanedRegister(void) { wholeSize = 0; sizeBitMask = 0; } ///< Constructor for use with restoreXml + LanedRegister(int4 sz,uint4 mask) { wholeSize = sz; sizeBitMask = mask; } ///< Constructor + bool restoreXml(const Element *el,const AddrSpaceManager *manage); ///< Restore object from XML stream + int4 getWholeSize(void) const { return wholeSize; } ///< Get the size in bytes of the whole laned register + uint4 getSizeBitMask(void) const { return sizeBitMask; } ///< Get the bit mask of possible lane sizes + void addLaneSize(int4 size) { sizeBitMask |= ((uint4)1 << size); } ///< Add a new \e size to the allowed list + bool allowedLane(int4 size) const { return (((sizeBitMask >> size) & 1) != 0); } ///< Is \e size among the allowed lane sizes + const_iterator begin(void) const { return LanedIterator(this); } ///< Starting iterator over possible lane sizes + const_iterator end(void) const { return LanedIterator(); } ///< Ending iterator over possible lane sizes +}; + +/// \brief Description of logical lanes within a \b big Varnode +/// +/// A \b lane is a byte offset and size within a Varnode. Lanes within a +/// Varnode are disjoint. In general, we expect a Varnode to be tiled with +/// lanes all of the same size, but the API allows for possibly non-uniform lanes. +class LaneDescription { + int4 wholeSize; ///< Size of the region being split in bytes + vector laneSize; ///< Size of lanes in bytes + vector lanePosition; ///< Significance positions of lanes in bytes +public: + LaneDescription(const LaneDescription &op2); ///< Copy constructor + LaneDescription(int4 origSize,int4 sz); ///< Construct uniform lanes + LaneDescription(int4 origSize,int4 lo,int4 hi); ///< Construct two lanes of arbitrary size + bool subset(int4 lsbOffset,int4 size); ///< Trim \b this to a subset of the original lanes + int4 getNumLanes(void) const { return laneSize.size(); } ///< Get the total number of lanes + int4 getWholeSize(void) const { return wholeSize; } ///< Get the size of the region being split + int4 getSize(int4 i) const { return laneSize[i]; } ///< Get the size of the i-th lane + int4 getPosition(int4 i) const { return lanePosition[i]; } ///< Get the significance offset of the i-th lane + int4 getBoundary(int4 bytePos) const; ///< Get index of lane that starts at the given byte position + bool restriction(int4 numLanes,int4 skipLanes,int4 bytePos,int4 size,int4 &resNumLanes,int4 &resSkipLanes) const; + bool extension(int4 numLanes,int4 skipLanes,int4 bytePos,int4 size,int4 &resNumLanes,int4 &resSkipLanes) const; +}; + +/// \brief Class for splitting larger registers holding smaller logical lanes +/// +/// Given a starting Varnode in the data-flow, look for evidence of the Varnode +/// being interpreted as disjoint logical values concatenated together (lanes). +/// If the interpretation is consistent for data-flow involving the Varnode, split +/// Varnode and data-flow into explicit operations on the lanes. +class TransformManager { + Funcdata *fd; ///< Function being operated on + map pieceMap; ///< Map from large Varnodes to their new pieces + list newVarnodes; ///< Storage for Varnode placeholder nodes + list newOps; ///< Storage for PcodeOp placeholder nodes + + void specialHandling(TransformOp &rop); + void createOps(void); ///< Create a new op for each placeholder + void createVarnodes(vector &inputList); ///< Create a Varnode for each placeholder + void removeOld(void); ///< Remove old preexisting PcodeOps and Varnodes that are now obsolete + void transformInputVarnodes(vector &inputList); ///< Remove old input Varnodes, mark new input Varnodes + void placeInputs(void); ///< Set input Varnodes for all new ops +public: + TransformManager(Funcdata *f) { fd = f; } ///< Constructor + virtual ~TransformManager(void); ///< Destructor + virtual bool preserveAddress(Varnode *vn,int4 bitSize,int4 lsbOffset) const; + Funcdata *getFunction(void) const { return fd; } ///< Get function being transformed + void clearVarnodeMarks(void); ///< Clear mark for all Varnodes in the map + TransformVar *newPreexistingVarnode(Varnode *vn); ///< Make placeholder for preexisting Varnode + TransformVar *newUnique(int4 size); ///< Make placeholder for new unique space Varnode + TransformVar *newConstant(int4 size,int4 lsbOffset,uintb val); ///< Make placeholder for constant Varnode + TransformVar *newIop(Varnode *vn); ///< Make placeholder for special iop constant + TransformVar *newPiece(Varnode *vn,int4 bitSize,int4 lsbOffset); ///< Make placeholder for piece of a Varnode + TransformVar *newSplit(Varnode *vn,const LaneDescription &description); + TransformVar *newSplit(Varnode *vn,const LaneDescription &description,int4 numLanes,int4 startLane); + TransformOp *newOpReplace(int4 numParams,OpCode opc,PcodeOp *replace); + TransformOp *newOp(int4 numParams,OpCode opc,TransformOp *follow); + TransformOp *newPreexistingOp(int4 numParams,OpCode opc,PcodeOp *originalOp); + + TransformVar *getPreexistingVarnode(Varnode *vn); ///< Get (or create) placeholder for preexisting Varnode + TransformVar *getPiece(Varnode *vn,int4 bitSize,int4 lsbOffset); ///< Get (or create) placeholder piece + TransformVar *getSplit(Varnode *vn,const LaneDescription &description); + TransformVar *getSplit(Varnode *vn,const LaneDescription &description,int4 numLanes,int4 startLane); + void opSetInput(TransformOp *rop,TransformVar *rvn,int4 slot); ///< Mark given variable as input to given op + void opSetOutput(TransformOp *rop,TransformVar *rvn); ///< Mark given variable as output of given op + + void apply(void); ///< Apply the full transform to the function +}; + +/// \brief Initialize \b this variable from raw data +/// +/// \param tp is the type of variable to create +/// \param v is the underlying Varnode of which this is a piece (may be null) +/// \param bits is the number of bits in the variable +/// \param bytes is the number of bytes in the variable +/// \param value is the associated value +inline void TransformVar::initialize(uint4 tp,Varnode *v,int4 bits,int4 bytes,uintb value) + +{ + type = tp; + vn = v; + val = value; + bitSize = bits; + byteSize = bytes; + flags = 0; + def = (TransformOp *)0; + replacement = (Varnode *)0; +} + +/// \param rop is the given placeholder op whose input is set +/// \param rvn is the placeholder variable to set +/// \param slot is the input position to set +inline void TransformManager::opSetInput(TransformOp *rop,TransformVar *rvn,int4 slot) + +{ + rop->input[slot] = rvn; +} + +/// Establish that the given op produces the given var as output. +/// Mark both the \e output field of the TransformOp and the \e def field of the TransformVar. +/// \param rop is the given op +/// \param rvn is the given variable +inline void TransformManager::opSetOutput(TransformOp *rop,TransformVar *rvn) + +{ + rop->output = rvn; + rvn->def = rop; +} + +#endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh index 908c6bf1c0..9aaca41aa6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh @@ -137,9 +137,9 @@ private: list descend; ///< List of every op using this varnode as input mutable Cover *cover; ///< Addresses covered by the def->use of this Varnode mutable union { - Datatype *dataType; ///< For type propagate algorithm - ValueSet *valueSet; - } temp; + Datatype *dataType; ///< Temporary data-type associated with \b this for use in type propagate algorithm + ValueSet *valueSet; ///< Value set associated with \b this when performing Value Set Analysis + } temp; ///< Temporary storage for analysis algorithms uintb consumed; ///< What parts of this varnode are used uintb nzm; ///< Which bits do we know are zero friend class VarnodeBank;