diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh index 8287f9edd0..8da8bba4f3 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh @@ -484,6 +484,24 @@ inline uintb pcode_left(uintb val,int4 sa) { return val << sa; } +/// \brief Calculate smallest mask that covers the given value +/// +/// Calculcate a mask that covers either the least significant byte, uint2, uint4, or uint8, +/// whatever is smallest. +/// \param val is the given value +/// \return the minimal mask +inline uintb minimalmask(uintb val) + +{ + if (val > 0xffffffff) + return ~((uintb)0); + if (val > 0xffff) + return 0xffffffff; + if (val > 0xff) + return 0xffff; + return 0xff; +} + extern bool signbit_negative(uintb val,int4 size); ///< Return true if the sign-bit is set extern uintb calc_mask(int4 size); ///< Calculate a mask for a given byte size extern uintb uintb_negate(uintb in,int4 size); ///< Negate the \e sized value diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index cedc769720..84f9e731d0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -3373,7 +3373,7 @@ void ActionDeadCode::markConsumedParameters(FuncCallSpecs *fc,vector if (vn->isAutoLive()) consumeVal = ~((uintb)0); else - consumeVal = minimalMask(vn->getNZMask()); + consumeVal = minimalmask(vn->getNZMask()); pushConsumed(consumeVal,vn,worklist); } } @@ -3398,7 +3398,7 @@ uintb ActionDeadCode::gatherConsumedReturn(Funcdata &data) if (returnOp->isDead()) continue; if (returnOp->numInput() > 1) { Varnode *vn = returnOp->getIn(1); - consumeVal |= minimalMask(vn->getNZMask()); + consumeVal |= minimalmask(vn->getNZMask()); } } return consumeVal; @@ -3455,11 +3455,21 @@ int4 ActionDeadCode::apply(Funcdata &data) continue; } else if (!op->isAssignment()) { - if (op->code() == CPUI_RETURN) { + OpCode opc = op->code(); + if (opc == CPUI_RETURN) { pushConsumed(~((uintb)0),op->getIn(0),worklist); for(i=1;inumInput();++i) pushConsumed(returnConsume,op->getIn(i),worklist); } + else if (opc == CPUI_BRANCHIND) { + JumpTable *jt = data.findJumpTable(op); + uintb mask; + if (jt != (JumpTable *)0) + mask = jt->getSwitchVarConsume(); + else + mask = ~((uintb)0); + pushConsumed(mask,op->getIn(0),worklist); + } else { for(i=0;inumInput();++i) pushConsumed(~((uintb)0),op->getIn(i),worklist); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index 30a6974498..46dc862ead 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -543,7 +543,6 @@ class ActionDeadCode : public Action { static bool neverConsumed(Varnode *vn,Funcdata &data); static void markConsumedParameters(FuncCallSpecs *fc,vector &worklist); static uintb gatherConsumedReturn(Funcdata &data); - static uintb minimalMask(uintb val); ///< Calculate smallest mask that covers the given value public: ActionDeadCode(const string &g) : Action(0,"deadcode",g) {} ///< Constructor virtual Action *clone(const ActionGroupList &grouplist) const { @@ -1077,20 +1076,4 @@ public: inline bool TermOrder::additiveCompare(const PcodeOpEdge *op1,const PcodeOpEdge *op2) { return (-1 == op1->getVarnode()->termOrder(op2->getVarnode())); } -/// Calculcate a mask that covers either the least significant byte, uint2, uint4, or uint8, -/// whatever is smallest. -/// \param val is the given value -/// \return the minimal mask -inline uintb ActionDeadCode::minimalMask(uintb val) - -{ - if (val > 0xffffffff) - return ~((uintb)0); - if (val > 0xffff) - return 0xffffffff; - if (val > 0xff) - return 0xffff; - return 0xff; -} - #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc index d1af1a4d67..924e071225 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc @@ -1219,23 +1219,18 @@ void JumpBasic::buildLabels(Funcdata *fd,vector
&addresstable,vectoropSetInput(indop,switchvn,0); + return switchvn; } bool JumpBasic::foldInGuards(Funcdata *fd,JumpTable *jump) -{ // We now think of the BRANCHIND as encompassing - // the guard function, so we "disarm" the guard - // instructions by making the guard condition - // always false. If the simplification removes - // the unusable branches, we are left with only - // one path through the switch +{ bool change = false; for(int4 i=0;i &addresstable,vector label.push_back(0xBAD1ABE1); // Add fake label to match the defaultAddress } -void JumpAssisted::foldInNormalization(Funcdata *fd,PcodeOp *indop) +Varnode *JumpAssisted::foldInNormalization(Funcdata *fd,PcodeOp *indop) { // Replace all outputs of jumpassist op with switchvn (including BRANCHIND) @@ -1837,6 +1832,7 @@ void JumpAssisted::foldInNormalization(Funcdata *fd,PcodeOp *indop) fd->opSetInput(op,switchvn,0); } fd->opDestroy(assistOp); // Get rid of the assist op (it has served its purpose) + return switchvn; } bool JumpAssisted::foldInGuards(Funcdata *fd,JumpTable *jump) @@ -1972,6 +1968,7 @@ JumpTable::JumpTable(Architecture *g,Address ad) jmodel = (JumpModel *)0; origmodel = (JumpModel *)0; indirect = (PcodeOp *)0; + switchVarConsume = ~((uintb)0); mostcommon = ~((uint4)0); maxtablesize = 1024; maxaddsub = 1; @@ -1988,6 +1985,7 @@ JumpTable::JumpTable(const JumpTable *op2) jmodel = (JumpModel *)0; origmodel = (JumpModel *)0; indirect = (PcodeOp *)0; + switchVarConsume = ~((uintb)0); mostcommon = ~((uint4)0); maxtablesize = op2->maxtablesize; maxaddsub = op2->maxaddsub; @@ -2081,7 +2079,7 @@ void JumpTable::addBlockToSwitch(BlockBasic *bl,uintb lab) void JumpTable::switchOver(const FlowInfo &flow) -{ // Convert absolute addresses to block indices +{ FlowBlock *parent,*tmpbl; uint4 pos; int4 i,j,count,maxcount; @@ -2116,6 +2114,28 @@ void JumpTable::switchOver(const FlowInfo &flow) } } +/// Eliminate any code involved in actually computing the destination address so +/// it looks like the CPUI_BRANCHIND operation does it all internally. +/// \param fd is the function containing \b this switch +void JumpTable::foldInNormalization(Funcdata *fd) + +{ + Varnode *switchvn = jmodel->foldInNormalization(fd,indirect); + if (switchvn != (Varnode *)0) { + // If possible, mark up the switch variable as not fully consumed so that + // subvariable flow can truncate it. + switchVarConsume = minimalmask(switchvn->getNZMask()); + if (switchVarConsume >= calc_mask(switchvn->getSize())) { // If mask covers everything + if (switchvn->isWritten()) { + PcodeOp *op = switchvn->getDef(); + if (op->code() == CPUI_INT_SEXT) { // Check for a signed extension + switchVarConsume = calc_mask(op->getIn(0)->getSize()); // Assume the extension is not consumed + } + } + } + } +} + void JumpTable::trivialSwitchOver(void) { @@ -2259,6 +2279,7 @@ void JumpTable::clear(void) label.clear(); loadpoints.clear(); indirect = (PcodeOp *)0; + switchVarConsume = ~((uintb)0); recoverystage = 0; // -opaddress- -maxtablesize- -maxaddsub- -maxleftright- -maxext- -collectloads- are permanent } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh index b8da3ea37b..134dae5400 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh @@ -192,7 +192,22 @@ public: virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const=0; virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext)=0; virtual void buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const=0; - virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop)=0; + + /// \brief Do normalization of the given switch specific to \b this model. + /// + /// The PcodeOp machinery is removed so it looks like the CPUI_BRANCHIND simply takes the + /// switch variable as an input Varnode and automatically interprets its values to reach + /// the correct destination. + /// \param fd is the function containing the switch + /// \param indop is the given switch as a CPUI_BRANCHIND + /// \return the Varnode holding the final unnormalized switch variable + virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop)=0; + + /// \brief Eliminate any \e guard code involved in computing the switch destination + /// + /// We now think of the BRANCHIND as encompassing any guard function. + /// \param fd is the function containing the switch + /// \param jump is the JumpTable owning \b this model. virtual bool foldInGuards(Funcdata *fd,JumpTable *jump)=0; virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable)=0; virtual JumpModel *clone(JumpTable *jt) const=0; @@ -213,7 +228,7 @@ public: virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const; virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {} virtual void buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const; - virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop) {} + virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop) { return (Varnode *)0; } virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; } virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable) { return true; } virtual JumpModel *clone(JumpTable *jt) const; @@ -241,6 +256,16 @@ protected: void findSmallestNormal(uint4 matchsize); void findNormalized(Funcdata *fd,BlockBasic *rootbl,int4 pathout,uint4 matchsize,uint4 maxtablesize); void markFoldableGuards(); + + /// \brief Eliminate the given guard to \b this switch + /// + /// We \e disarm the guard instructions by making the guard condition + /// always \b false. If the simplification removes the unusable branches, + /// we are left with only one path through the switch. + /// \param fd is the function containing the switch + /// \param guard is a description of the particular guard mechanism + /// \param jump is the JumpTable owning \b this model + /// \return \b true if a change was made to data-flow virtual bool foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump); public: JumpBasic(JumpTable *jt) : JumpModel(jt) { jrange = (JumpValuesRange *)0; } @@ -253,7 +278,7 @@ public: virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const; virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext); virtual void buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const; - virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop); + virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop); virtual bool foldInGuards(Funcdata *fd,JumpTable *jump); virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable); virtual JumpModel *clone(JumpTable *jt) const; @@ -341,45 +366,54 @@ public: virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector
&addresstable,vector *loadpoints) const; virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {} virtual void buildLabels(Funcdata *fd,vector
&addresstable,vector &label,const JumpModel *orig) const; - virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop); + virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop); virtual bool foldInGuards(Funcdata *fd,JumpTable *jump); virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector
&addresstable) { return true; } virtual JumpModel *clone(JumpTable *jt) const; virtual void clear(void) { assistOp = (PcodeOp *)0; switchvn = (Varnode *)0; } }; +/// \brief A map from values to control-flow targets within a function +/// +/// A JumpTable is attached to a specific CPUI_BRANCHIND and encapsulates all +/// the information necessary to model the indirect jump as a \e switch statement. +/// It knows how to map from specific switch variable values to the destination +/// \e case block and how to label the value. class JumpTable { - Architecture *glb; // Architecture under which this jumptable operates - JumpModel *jmodel,*origmodel; - vector
addresstable; // Raw addresses in the jumptable - vector blocktable; // Addresses converted to basic blocks - vector label; - vector loadpoints; - Address opaddress; // Absolute address of op - PcodeOp *indirect; // INDIRECT op referring to this jump table - uint4 mostcommon; // Most common position in table - uint4 maxtablesize; // Maximum table size we allow to be built (sanity check) - uint4 maxaddsub; // Maximum ADDs or SUBs to normalize - uint4 maxleftright; // Maximum shifts to normalize - uint4 maxext; // Maximum extensions to normalize - int4 recoverystage; // 0=no stages, 1=needs additional stage, 2=complete - bool collectloads; - void recoverModel(Funcdata *fd); + Architecture *glb; ///< Architecture under which this jump-table operates + JumpModel *jmodel; ///< Current model of how the jump table is implemented in code + JumpModel *origmodel; ///< Initial jump table model, which may be incomplete + vector
addresstable; ///< Raw addresses in the jump-table + vector blocktable; ///< Addresses converted to basic blocks + vector label; ///< The case label for each explicit target + vector loadpoints; ///< Any recovered in-memory data for the jump-table + Address opaddress; ///< Absolute address of the INDIRECT jump + PcodeOp *indirect; ///< CPUI_INDIRECT op referring linked to \b this jump-table + uintb switchVarConsume; ///< Bits of the switch variable being consumed + uint4 mostcommon; ///< Index of the most common position in table, prior to deduping + uint4 maxtablesize; ///< Maximum table size we allow to be built (sanity check) + uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize + uint4 maxleftright; ///< Maximum shifts to normalize + uint4 maxext; ///< Maximum extensions to normalize + int4 recoverystage; ///< 0=no stages, 1=needs additional stage, 2=complete + bool collectloads; ///< Set to \b true if information about in-memory model data is/should be collected + void recoverModel(Funcdata *fd); ///< Attempt recovery of the jump-table model void trivialSwitchOver(void); - void sanityCheck(Funcdata *fd); + void sanityCheck(Funcdata *fd); ///< Perform sanity check on recovered address targets uint4 block2Position(const FlowBlock *bl) const; static bool isReachable(PcodeOp *op); public: - JumpTable(Architecture *g,Address ad=Address()); - JumpTable(const JumpTable *op2); - ~JumpTable(void); - bool isSwitchedOver(void) const { return !blocktable.empty(); } - bool isRecovered(void) const { return !addresstable.empty(); } - bool isLabelled(void) const { return !label.empty(); } - bool isOverride(void) const; + JumpTable(Architecture *g,Address ad=Address()); ///< Constructor + JumpTable(const JumpTable *op2); ///< Copy constructor + ~JumpTable(void); ///< Destructor + bool isSwitchedOver(void) const { return !blocktable.empty(); } ///< Return \b true if addresses converted to basic-blocks + bool isRecovered(void) const { return !addresstable.empty(); } ///< Return \b true if a model has been recovered + bool isLabelled(void) const { return !label.empty(); } ///< Return \b true if \e case labels are computed + bool isOverride(void) const; ///< Return \b true if \b this table was manually overridden bool isPossibleMultistage(void) const { return (addresstable.size()==1); } int4 getStage(void) const { return recoverystage; } int4 numEntries(void) const { return addresstable.size(); } + uintb getSwitchVarConsume(void) const { return switchVarConsume; } ///< Get bits of switch variable consumed by \b this table int4 getMostCommon(void) const { return mostcommon; } const Address &getOpAddress(void) const { return opaddress; } PcodeOp *getIndirectOp(void) const { return indirect; } @@ -395,10 +429,10 @@ public: void setMostCommonBlock(uint4 bl) { mostcommon = bl; } void setLoadCollect(bool val) { collectloads = val; } void addBlockToSwitch(BlockBasic *bl,uintb lab); - void switchOver(const FlowInfo &flow); - uintb getLabelByIndex(int4 index) const { return label[index]; } - void foldInNormalization(Funcdata *fd) { jmodel->foldInNormalization(fd,indirect); } - bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); } + void switchOver(const FlowInfo &flow); ///< Convert absolute addresses to block indices + uintb getLabelByIndex(int4 index) const { return label[index]; } ///< Given a \e case index, get its label + void foldInNormalization(Funcdata *fd); ///< Hide the normalization code for the switch + bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); } ///< Hide any guard code for \b this switch void recoverAddresses(Funcdata *fd); void recoverMultistage(Funcdata *fd); bool recoverLabels(Funcdata *fd); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc index 0ebcb283be..a3d6b09291 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc @@ -340,6 +340,28 @@ bool SubvariableFlow::tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn) return true; } +/// \brief Determine if the subgraph variable can act as a switch variable for the given BRANCHIND +/// +/// We query the JumpTable associated with the BRANCHIND to see if its switch variable +/// can be trimmed as indicated by the logical flow. +/// \param op is the given BRANCHIND op +/// \param rvn is the subgraph variable flowing to the BRANCHIND +/// \return \b true if the switch variable can be successfully trimmed to its logical size +bool SubvariableFlow::trySwitchPull(PcodeOp *op,ReplaceVarnode *rvn) + +{ + if ((rvn->mask & 1) == 0) return false; // Logical value must be justified + if ((rvn->vn->getConsume()&~rvn->mask)!=0) // If there's something outside the mask being consumed + return false; // we can't trim + patchlist.push_back(PatchRecord()); + patchlist.back().type = 2; + patchlist.back().pullop = op; + patchlist.back().in1 = rvn; + patchlist.back().slot = 0; + pullcount += 1; // A true terminal modification + return true; +} + /// Try to trace the logical variable through descendant Varnodes /// creating new nodes in the logical subgraph and updating the worklist. /// \param rvn is the given subgraph variable to trace @@ -573,6 +595,10 @@ bool SubvariableFlow::traceForward(ReplaceVarnode *rvn) if (!tryReturnPull(op,rvn,slot)) return false; hcount += 1; break; + case CPUI_BRANCHIND: + if (!trySwitchPull(op, rvn)) return false; + hcount += 1; + break; case CPUI_BOOL_NEGATE: case CPUI_BOOL_AND: case CPUI_BOOL_OR: @@ -850,6 +876,10 @@ bool SubvariableFlow::traceForwardSext(ReplaceVarnode *rvn) if (!tryReturnPull(op,rvn,slot)) return false; hcount += 1; break; + case CPUI_BRANCHIND: + if (!trySwitchPull(op,rvn)) return false; + hcount += 1; + break; default: return false; } @@ -1331,7 +1361,7 @@ void SubvariableFlow::doReplacement(void) fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),0); fd->opSetInput(pullop,getReplaceVarnode((*piter).in2),1); } - else if (type == 2) { // A call parameter or return value + else if (type == 2) { // A call parameter, return value, or switch variable fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),(*piter).slot); } else if (type == 3) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh index b5655c9349..06a04d077c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh @@ -62,7 +62,7 @@ class SubvariableFlow { /// \brief Operation with a new logical value as (part of) input, but output Varnode is unchanged class PatchRecord { friend class SubvariableFlow; - int4 type; ///< 0=COPY 1=compare 2=call 3=AND/SHIFT + int4 type; ///< 0=COPY 1=compare 2=call/return/branchind 3=AND/SHIFT PcodeOp *pullop; ///< Op being affected ReplaceVarnode *in1; ///< The logical variable input ReplaceVarnode *in2; ///< (optional second parameter) @@ -91,6 +91,7 @@ class SubvariableFlow { bool tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot); bool tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot); bool tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn); + bool trySwitchPull(PcodeOp *op,ReplaceVarnode *rvn); bool traceForward(ReplaceVarnode *rvn); ///< Trace the logical data-flow forward for the given subgraph variable bool traceBackward(ReplaceVarnode *rvn); ///< Trace the logical data-flow backward for the given subgraph variable bool traceForwardSext(ReplaceVarnode *rvn); ///< Trace logical data-flow forward assuming sign-extensions