From dddcf4c715c0700b9bdb7fe2cbd47fb6b44f4672 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Wed, 6 Nov 2019 10:02:07 -0500 Subject: [PATCH] ActionCollectLanedAccess --- .../src/decompile/cpp/coreaction.cc | 207 +++++++++++++----- .../src/decompile/cpp/coreaction.hh | 23 +- .../Decompiler/src/decompile/cpp/funcdata.hh | 5 + .../src/decompile/cpp/funcdata_op.cc | 13 ++ .../Decompiler/src/decompile/cpp/transform.hh | 14 ++ 5 files changed, 202 insertions(+), 60 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 33be08554e..77b6a37c40 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -495,6 +495,127 @@ int4 ActionStackPtrFlow::apply(Funcdata &data) return 0; } +/// Mark every PcodeOp that is reachable from the given Varnode and create a LanedAccess record. +/// \param data is the function to trace through +/// \param base is the description of the laned register being traced from +/// \param vn is the Varnode to trace +/// \param pos is the significance position of the Varnode within the laned register +void ActionCollectLanedAccess::traceVarnode(Funcdata &data,const LanedRegister *base,Varnode *vn,int4 pos) + +{ + list::const_iterator iter = vn->beginDescend(); + int4 step = 0; + while(step < 2) { + PcodeOp *op; + if (step == 0) { + op = *iter; + ++iter; + if (iter == vn->endDescend()) + step = 1; + } + else { + step = 2; + if (!vn->isWritten()) continue; + op = vn->getDef(); + } + if (op->isMark()) continue; + // Make sure op is associated with a lane access records describing its biggest Varnode + switch(op->code()) { + case CPUI_LOAD: + case CPUI_PIECE: + case CPUI_INT_ZEXT: + case CPUI_INT_SEXT: + if (vn != op->getOut()) continue; // Biggest Varnode is the output + break; + case CPUI_SUBPIECE: + if (vn != op->getIn(0)) continue; // Biggest Varnode is first input + break; + case CPUI_STORE: + if (vn != op->getIn(2)) continue; + break; + default: + break; + } + op->setMark(); + data.opMarkLanedAccess(base, op, vn->getSize(), pos); + } +} + +/// \brief Search for Varnodes that match the given laned vector register +/// +/// All varnodes (bigger than 8 bytes) that are contained in the vector register are processed, +/// looking for a working lane scheme. Data-flow from these Varnodes is traces and LanedAccess +/// records are created for each PcodeOp flowed through. +/// \param data is the function being traced +/// \param lanedRegister is the given register and acceptable lane schemes +/// \param iter is an iterator to the first Varnode that matches the vector register +void ActionCollectLanedAccess::processLane(Funcdata &data,const LanedRegister &lanedRegister, + VarnodeLocSet::const_iterator iter) +{ + int4 fullSize = lanedRegister.getStorage().size; + Address startAddress(lanedRegister.getStorage().getAddr()); + Address lastAddress(startAddress + (fullSize-1)); + VarnodeLocSet::const_iterator enditer = data.endLoc(); + while(iter != enditer) { + Varnode *vn = *iter; + ++iter; + if (lastAddress < vn->getAddr()) + break; + if (vn->getSize() <= 8) // Varnode not big enough to be vector register + continue; + int4 diff = (int4)(vn->getOffset() - startAddress.getOffset()); + if (diff + vn->getSize() > fullSize) // Must be contained by full register + continue; + if (vn->getSpace()->isBigEndian()) + diff = fullSize - (diff + vn->getSize()); + traceVarnode(data, &lanedRegister, vn, diff); + } +} + +/// Give each PcodeOp in the laned access list a chance to propagate to new PcodeOps, +/// which will have new records added to the list. +/// \param data is the function being traced +void ActionCollectLanedAccess::propagate(Funcdata &data) + +{ + list::const_iterator iter = data.beginLaneAccess(); + while(iter != data.endLaneAccess()) { + const LanedAccess lanedAccess( *iter ); + PcodeOp *op = lanedAccess.getOp(); + int4 sz = lanedAccess.getSize(); + for(int4 i=0;inumInput();++i) { + Varnode *vn = op->getIn(i); + if (vn->getSize() != sz) continue; // Size must match exactly + if (vn->isConstant() || vn->isAnnotation()) continue; + traceVarnode(data, lanedAccess.getBase(), vn, lanedAccess.getBytePos()); + } + ++iter; + } +} + +int4 ActionCollectLanedAccess::apply(Funcdata &data) + +{ + if (data.getArch()->lanerecords.empty()) + return 0; + const LanedRegister &lastRecord( data.getArch()->lanerecords.back() ); + Address lastAddress( lastRecord.getStorage().getAddr() + lastRecord.getStorage().size - 1); + list::const_iterator iter; + for(iter=data.getArch()->lanerecords.begin();iter!=data.getArch()->lanerecords.end();++iter) { + const LanedRegister &lanedRegister( *iter ); + VarnodeLocSet::const_iterator viter = data.beginLoc(lanedRegister.getStorage().getAddr()); + if (viter == data.endLoc()) break; + Varnode *vn = *viter; + if (lastAddress < vn->getAddr()) break; + processLane(data,lanedRegister,viter); + } + propagate(data); + list::const_iterator aiter; + for(aiter=data.beginLaneAccess();aiter!=data.endLaneAccess();++aiter) + (*aiter).getOp()->clearMark(); + return 0; +} + /// \brief Try to divide a single Varnode into lanes /// /// Look for a CPUI_SUBPIECE op that takes the given Varnode as the input or a @@ -505,8 +626,10 @@ int4 ActionStackPtrFlow::apply(Funcdata &data) /// \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 bytePos is the significance position of the Varnode within the laned register /// \param allowDowncast is \b true if we allow lane systems with SUBPIECE terminators -bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,bool allowDowncast) +bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,int4 bytePos, + bool allowDowncast) { list::const_iterator iter = vn->beginDescend(); @@ -536,10 +659,6 @@ bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegi if (checkedLanes.allowedLane(curSize)) continue; checkedLanes.addSize(curSize); // Only check this scheme once LaneDescription description(lanedRegister.getStorage().size,curSize); // Lane scheme dictated by curSize - int4 bytePos = (int4)(lanedRegister.getStorage().offset - vn->getOffset()); - if (lanedRegister.getStorage().space->isBigEndian()) { - bytePos = lanedRegister.getStorage().size - (bytePos + vn->getSize()); // Convert to significance order - } if (!description.subset(bytePos,vn->getSize())) // Try to restrict lane scheme to actual Varnode continue; LaneDivide laneDivide(&data,vn,description,allowDowncast); @@ -553,63 +672,34 @@ bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegi return false; } -/// \brief Search for and attempt to split Varnodes that match the given laned vector register -/// -/// All varnodes (bigger than 8 bytes) that are contained in the vector register are processed, -/// looking for a working lane scheme, and splitting based on that scheme. Return \b false if there -/// is at least one eligible Varnode that could not be split. -/// \param data is the function being modified -/// \param lanedRegister is the given register and acceptable lane schemes -/// \param allowDowncast is \b true if SUBPIECE terminators are allowed in the schemes -/// \param iter is an iterator to the first Varnode that matches the vector register -/// \return \b true if all varnodes were successfully split -bool ActionLaneDivide::processLane(Funcdata &data,const LanedRegister &lanedRegister,bool allowDowncast, - VarnodeLocSet::const_iterator iter) - -{ - int4 fullSize = lanedRegister.getStorage().size; - Address startAddress(lanedRegister.getStorage().getAddr()); - Address lastAddress(startAddress + (fullSize-1)); - VarnodeLocSet::const_iterator enditer = data.endLoc(); - bool res = true; - while(iter != enditer) { - Varnode *vn = *iter; - ++iter; - if (lastAddress < vn->getAddr()) - break; - if (vn->getSize() <= 8) // Varnode not big enough to be vector register - continue; - int4 diff = (int4)(vn->getOffset() - startAddress.getOffset()); - if (diff + vn->getSize() > fullSize) // Must be contained by full register - continue; - if (processVarnode(data,vn,lanedRegister,allowDowncast)) { - // If changes were made, iterator may no longer be valid, generate a new one - iter = data.beginLoc(startAddress); - res = true; // We may have eliminated a previous failure - } - else - res = false; // Note the failure - } - return res; -} - int4 ActionLaneDivide::apply(Funcdata &data) { - if (data.getArch()->lanerecords.empty()) - return 0; - const LanedRegister &lastRecord( data.getArch()->lanerecords.back() ); - Address lastAddress( lastRecord.getStorage().getAddr() + lastRecord.getStorage().size - 1); - list::const_iterator iter; - for(iter=data.getArch()->lanerecords.begin();iter!=data.getArch()->lanerecords.end();++iter) { - const LanedRegister &lanedRegister( *iter ); - VarnodeLocSet::const_iterator viter = data.beginLoc(lanedRegister.getStorage().getAddr()); - if (viter == data.endLoc()) break; - Varnode *vn = *viter; - if (lastAddress < vn->getAddr()) break; - if (!processLane(data,lanedRegister,false,viter)) // Try without SUBPIECE terminators - processLane(data,lanedRegister,true,viter); // If we fail, try with SUBPIECE terminators + list::const_iterator iter; + bool allowDowncast = false; + for(int4 i=0;i<2;++i) { + for(iter=data.beginLaneAccess();iter!=data.endLaneAccess();++iter) { + const LanedAccess &lanedAccess(*iter); + PcodeOp *op = lanedAccess.getOp(); + if (op->isDead()) continue; + Varnode *vn = op->getOut(); + if (vn != (Varnode *)0 && vn->getSize() == lanedAccess.getSize()) + processVarnode(data, vn, *lanedAccess.getBase(), lanedAccess.getBytePos(), allowDowncast); + else { + for(int4 j=0;jnumInput();++j) { + vn = op->getIn(j); + if (vn->getSize() == lanedAccess.getSize()) { + if (!vn->isConstant() && !vn->isAnnotation() && !vn->isWritten()) { + processVarnode(data, vn, *lanedAccess.getBase(), lanedAccess.getBytePos(), allowDowncast); + break; + } + } + } + } + } + allowDowncast = true; } + data.clearLanedAccessList(); return 0; } @@ -4624,6 +4714,7 @@ void universal_action(Architecture *conf) // actmainloop->addAction( new ActionParamShiftStop("paramshift") ); actmainloop->addAction( new ActionRestrictLocal("localrecovery") ); // Do before dead code removed actmainloop->addAction( new ActionDeadCode("deadcode") ); + actmainloop->addAction( new ActionCollectLanedAccess("base") ); actmainloop->addAction( new ActionDynamicMapping("dynamic") ); // Must come before restructurevarnode and infertypes actmainloop->addAction( new ActionRestructureVarnode("localrecovery") ); actmainloop->addAction( new ActionSpacebase("base") ); // Must come before infertypes and nonzeromask diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index 02a843f8a7..9e76efb3ae 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -97,6 +97,26 @@ public: virtual int4 apply(Funcdata &data); }; +/// \brief Find PcodeOps that are accessing values from laned registers +/// +/// This looks for Varnodes that match any of the LanedRegister records associated with +/// the architecture. A record is created in the function's LanedAccess list for every +/// PcodeOp that accesses one of these Varnodes. These are later examined by ActionLaneDivide. +/// The LanedRegister is the key for finding the Varnode, but the \e big property can propagate +/// through data-flow to other Varnodes that are not necessarily using the same storage (i.e. uniques). +class ActionCollectLanedAccess : public Action { + void traceVarnode(Funcdata &data,const LanedRegister *base,Varnode *vn,int4 pos); ///< Trace a big Varnode instance to other big Varnodes + void processLane(Funcdata &data,const LanedRegister &lanedRegister,VarnodeLocSet::const_iterator iter); + void propagate(Funcdata &data); ///< Discover other PcodeOps that use laned values +public: + ActionCollectLanedAccess(const string &g) : Action(rule_onceperfunc,"collectlanedaccess",g) {} ///< Constructor + virtual Action *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Action *)0; + return new ActionCollectLanedAccess(getGroup()); + } + 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 @@ -104,8 +124,7 @@ public: /// 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 { - bool processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,bool allowDowncast); - bool processLane(Funcdata &data,const LanedRegister &lanedRegister,bool allowDowncast,VarnodeLocSet::const_iterator iter); + bool processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,int4 bytePos,bool allowDowncast); public: ActionLaneDivide(const string &g) : Action(rule_onceperfunc,"lanedivide",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 b29c96f8bb..d7bb4760a4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -80,6 +80,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 + list lanedList; ///< List of ops that are accessing potentially laned registers // Low level Varnode functions void setVarnodeProperties(Varnode *vn) const; ///< Look-up boolean properties and data-type information @@ -428,6 +429,10 @@ public: Varnode *opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Varnode *stackptr,bool insertafter); PcodeOp *opStackStore(AddrSpace *spc,uintb off,PcodeOp *op,bool insertafter); void opUndoPtradd(PcodeOp *op,bool finalize); ///< Convert a CPUI_PTRADD back into a CPUI_INT_ADD + void opMarkLanedAccess(const LanedRegister *base,PcodeOp *op,int4 sz,int4 pos); ///< Mark op as using laned register + list::const_iterator beginLaneAccess(void) const { return lanedList.begin(); } ///< Beginning iterator over laned accesses + list::const_iterator endLaneAccess(void) const { return lanedList.end(); } ///< Ending iterator over laned accesses + void clearLanedAccessList(void) { lanedList.clear(); } ///< Clear records from the laned access list /// \brief Start of PcodeOp objects with the given op-code list::const_iterator beginOp(OpCode opc) const { return obank.begin(opc); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc index c76eb65df9..14fb27e935 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc @@ -557,6 +557,19 @@ void Funcdata::opUndoPtradd(PcodeOp *op,bool finalize) opInsertBefore(multOp,op); } +/// Store a record indicating a potential use of a large laned register. Generally, the output +/// of the op is the use, except in the case of SUBPIECE or STORE, where getIn(0) and getIn(2) +/// respectively are the Varnodes being used. +/// \param base is the description of the laned register that is triggering this record +/// \param op is the PcodeOp using the large Varnode +/// \param sz is the size of the large Varnode in bytes +/// \param pos is the significance position of the Varnode, relative to the LanedRegister description +void Funcdata::opMarkLanedAccess(const LanedRegister *base,PcodeOp *op,int4 sz,int4 pos) + +{ + lanedList.push_back(LanedAccess(base,op,sz,pos)); +} + /// Make a clone of the given PcodeOp, copying control-flow properties as well. The data-type /// is \e not cloned. /// \param op is the PcodeOp to clone diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh index 87e1251295..2d61e67a62 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh @@ -98,6 +98,20 @@ public: bool operator<(const LanedRegister &op2) const { return (storage < op2.storage); } ///< Compare based on VarnodeData }; +/// \brief Record describing the access of a large Varnode that can be traced to a LanedRegister +class LanedAccess { + const LanedRegister *base; ///< Base register dictating the lane scheme + PcodeOp *op; ///< Operation using the big register + int4 size; ///< Size of the register in bytes + int4 bytePos; ///< Significance position relative to the laned register +public: + LanedAccess(const LanedRegister *b,PcodeOp *o,int4 sz,int4 pos) { base=b; op=o; size=sz; bytePos=pos; } ///< Constructor + const LanedRegister *getBase(void) const { return base; } ///< Get the base LanedRegister being traced + PcodeOp *getOp(void) const { return op; } ///< Get the PcodeOp using the large Varnode + int4 getSize(void) const { return size; } ///< Get the size of the Varnode being accessed + int4 getBytePos(void) const { return bytePos; } ///< Get the significance position relative to the laned register +}; + /// \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