diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc index c59ebdf616..ff775928f5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc @@ -753,23 +753,16 @@ void FlowInfo::generateOps(void) do { bool collapsed_jumptable = false; while(!tablelist.empty()) { // For each jumptable found - PcodeOp *op = tablelist.back(); - tablelist.pop_back(); - int4 failuremode; - JumpTable *jt = data.recoverJumpTable(op,this,failuremode); // Recover it - if (jt == (JumpTable *)0) { // Could not recover jumptable - if ((failuremode == 3) && (!tablelist.empty()) && (!isInArray(notreached,op))) { - // If the indirect op was not reachable with current flow AND there is more flow to generate, - // AND we haven't tried to recover this table before - notreached.push_back(op); // Save this op so we can try to recovery table again later - } - else if (!isFlowForInline()) // Unless this flow is being inlined for something else - truncateIndirectJump(op,failuremode); // Treat the indirect jump as a call - } - else { + vector newTables; + recoverJumpTables(newTables, notreached); + tablelist.clear(); + for(int4 i=0;inumEntries(); for(int4 i=0;igetAddressByIndex(i)); + newAddress(jt->getIndirectOp(),jt->getAddressByIndex(i)); if (jt->isPossibleMultistage()) collapsed_jumptable = true; while(!addrlist.empty()) // Try to fill in as much more as possible @@ -1384,3 +1377,35 @@ void FlowInfo::checkMultistageJumptables(void) tablelist.push_back(jt->getIndirectOp()); } } + +/// \brief Recover jumptables for current set of BRANCHIND ops using existing flow +/// +/// \param newTables will hold one JumpTable pointer for each BRANCHIND in \b tablelist +void FlowInfo::recoverJumpTables(vector &newTables,vector ¬reached) + +{ + PcodeOp *op = tablelist[0]; + ostringstream s1; + s1 << data.getName() << "@@jump@"; + op->getAddr().printRaw(s1); + + // Prepare partial Funcdata object for analysis if necessary + Funcdata partial(s1.str(),data.getScopeLocal()->getParent(),data.getAddress(),(FunctionSymbol *)0); + + for(int4 i=0;i 1) && (!isInArray(notreached,op))) { + // If the indirect op was not reachable with current flow AND there is more flow to generate, + // AND we haven't tried to recover this table before + notreached.push_back(op); // Save this op so we can try to recovery table again later + } + else if (!isFlowForInline()) // Unless this flow is being inlined for something else + truncateIndirectJump(op,failuremode); // Treat the indirect jump as a call + } + newTables.push_back(jt); + } +} + diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.hh index 7362e4b189..19d3b47bf6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.hh @@ -133,6 +133,7 @@ private: bool injectSubFunction(FuncCallSpecs *fc); ///< Perform \e injection replacing the CALL at the given call site void checkContainedCall(void); void checkMultistageJumptables(void); + void recoverJumpTables(vector &newTables,vector ¬reached); void deleteCallSpec(FuncCallSpecs *fc); ///< Remove the given call site from the list for \b this function void truncateIndirectJump(PcodeOp *op,int4 failuremode); ///< Treat indirect jump as indirect call that never returns static bool isInArray(vector &array,PcodeOp *op); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 24a7a634be..980c5ff135 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -115,7 +115,7 @@ class Funcdata { void pushMultiequals(BlockBasic *bb); ///< Push MULTIEQUAL Varnodes of the given block into the output block void clearBlocks(void); ///< Clear all basic blocks void structureReset(void); ///< Calculate initial basic block structures (after a control-flow change) - int4 stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow); + int4 stageJumpTable(Funcdata &partial,JumpTable *jt,PcodeOp *op,FlowInfo *flow); void switchOverJumpTables(const FlowInfo &flow); ///< Convert jump-table addresses to basic block indices void clearJumpTables(void); ///< Clear any jump-table information @@ -516,7 +516,7 @@ public: JumpTable *linkJumpTable(PcodeOp *op); ///< Link jump-table with a given BRANCHIND JumpTable *findJumpTable(const PcodeOp *op) const; ///< Find a jump-table associated with a given BRANCHIND JumpTable *installJumpTable(const Address &addr); ///< Install a new jump-table for the given Address - JumpTable *recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremode); + JumpTable *recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *flow,int4 &failuremode); int4 numJumpTables(void) const { return jumpvec.size(); } ///< Get the number of jump-tables for \b this function JumpTable *getJumpTable(int4 i) { return jumpvec[i]; } ///< Get the i-th jump-table void removeJumpTable(JumpTable *jt); ///< Remove/delete the given jump-table diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc index a7a2756e2d..2ddda563fc 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc @@ -481,47 +481,48 @@ JumpTable *Funcdata::installJumpTable(const Address &addr) /// - 2 = \b likely \b thunk failure /// - 3 = no legal flows to the BRANCHIND failure /// +/// \param partial is a function object for caching analysis /// \param jt is the jump-table object to populate /// \param op is the BRANCHIND p-code op to analyze /// \param flow is the existing flow information /// \return the success/failure code -int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow) +int4 Funcdata::stageJumpTable(Funcdata &partial,JumpTable *jt,PcodeOp *op,FlowInfo *flow) { - PcodeOp *partop = (PcodeOp *)0; - string oldactname; + if (!partial.isJumptableRecoveryOn()) { + // Do full analysis on the table if we haven't before + partial.flags |= jumptablerecovery_on; // Mark that this Funcdata object is dedicated to jumptable recovery + partial.truncatedFlow(this,flow); - ostringstream s1; - s1 << name << "@@jump@"; - op->getAddr().printRaw(s1); - - Funcdata partial(s1.str(),localmap->getParent(),baseaddr,(FunctionSymbol *)0); - partial.flags |= jumptablerecovery_on; // Mark that this Funcdata object is dedicated to jumptable recovery - partial.truncatedFlow(this,flow); - - partop = partial.findOp(op->getSeqNum()); - - if ((partop==(PcodeOp *)0) || - (partop->code() != CPUI_BRANCHIND)|| - (partop->getAddr() != op->getAddr())) - throw LowlevelError("Error recovering jumptable: Bad partial clone"); - - oldactname = glb->allacts.getCurrentName(); // Save off old action - glb->allacts.setCurrent("jumptable"); - try { + string oldactname = glb->allacts.getCurrentName(); // Save off old action + try { + glb->allacts.setCurrent("jumptable"); #ifdef OPACTION_DEBUG - if (jtcallback != (void (*)(Funcdata &orig,Funcdata &fd))0) - (*jtcallback)(*this,partial); // Alternative reset/perform - else { + if (jtcallback != (void (*)(Funcdata &orig,Funcdata &fd))0) + (*jtcallback)(*this,partial); // Alternative reset/perform + else { #endif - glb->allacts.getCurrent()->reset( partial ); - glb->allacts.getCurrent()->perform( partial ); // Simplify the partial function + glb->allacts.getCurrent()->reset( partial ); + glb->allacts.getCurrent()->perform( partial ); // Simplify the partial function #ifdef OPACTION_DEBUG + } +#endif + glb->allacts.setCurrent(oldactname); // Restore old action } -#endif - glb->allacts.setCurrent(oldactname); // Restore old action - if (partop->isDead()) // Indirectop we were trying to recover was eliminated as dead code (unreachable) - return 0; // Return jumptable as + catch(LowlevelError &err) { + glb->allacts.setCurrent(oldactname); + warning(err.explain,op->getAddr()); + return 1; + } + } + PcodeOp *partop = partial.findOp(op->getSeqNum()); + + if (partop==(PcodeOp *)0 || partop->code() != CPUI_BRANCHIND || partop->getAddr() != op->getAddr()) + throw LowlevelError("Error recovering jumptable: Bad partial clone"); + if (partop->isDead()) // Indirectop we were trying to recover was eliminated as dead code (unreachable) + return 0; // Return jumptable as + + try { jt->setLoadCollect(flow->doesJumpRecord()); jt->setIndirectOp(partop); if (jt->getStage()>0) @@ -529,16 +530,13 @@ int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow) else jt->recoverAddresses(&partial); // Analyze partial to recover jumptable addresses } - catch(JumptableNotReachableError &err) { - glb->allacts.setCurrent(oldactname); + catch(JumptableNotReachableError &err) { // Thrown by recoverAddresses return 3; } - catch(JumptableThunkError &err) { - glb->allacts.setCurrent(oldactname); + catch(JumptableThunkError &err) { // Thrown by recoverAddresses return 2; } catch(LowlevelError &err) { - glb->allacts.setCurrent(oldactname); warning(err.explain,op->getAddr()); return 1; } @@ -552,11 +550,12 @@ int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow) /// to the copy, and the resulting data-flow tree is examined to enumerate possible values /// of the input Varnode to the given BRANCHIND PcodeOp. This information is stored in a /// JumpTable object. +/// \param partial is the Funcdata copy to perform analysis on if necessary /// \param op is the given BRANCHIND PcodeOp /// \param flow is current flow information for \b this function /// \param failuremode will hold the final success/failure code (0=success) /// \return the recovered JumpTable or NULL if there was no success -JumpTable *Funcdata::recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremode) +JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *flow,int4 &failuremode) { JumpTable *jt; @@ -568,7 +567,7 @@ JumpTable *Funcdata::recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremo if (jt->getStage() != 1) return jt; // Previously calculated jumptable (NOT an override and NOT incomplete) } - failuremode = stageJumpTable(jt,op,flow); // Recover based on override information + failuremode = stageJumpTable(partial,jt,op,flow); // Recover based on override information if (failuremode != 0) return (JumpTable *)0; jt->setIndirectOp(op); // Relink table back to original op @@ -578,7 +577,7 @@ JumpTable *Funcdata::recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremo if ((flags & jumptablerecovery_dont)!=0) return (JumpTable *)0; // Explicitly told not to recover jumptables JumpTable trialjt(glb); - failuremode = stageJumpTable(&trialjt,op,flow); + failuremode = stageJumpTable(partial,&trialjt,op,flow); if (failuremode != 0) return (JumpTable *)0; // if (trialjt.is_twostage())