diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc index 38c0d5c825..a7db1774d3 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc @@ -722,14 +722,37 @@ void FlowInfo::truncateIndirectJump(PcodeOp *op,JumpTable::RecoveryMode mode) else { data.opSetOpcode(op,CPUI_CALLIND); // Turn jump into call setupCallindSpecs(op,(FuncCallSpecs *)0); - if (mode != JumpTable::fail_thunk) // Unless the switch was a thunk mechanism - data.getCallSpecs(op)->setBadJumpTable(true); // Consider using special name for switch variable + FuncCallSpecs *fc = data.getCallSpecs(op); + uint4 returnType; + bool noParams; + + if (mode == JumpTable::fail_thunk) { + returnType = 0; + noParams = false; + } + else if (mode == JumpTable::fail_callother) { + returnType = PcodeOp::noreturn; + fc->setNoReturn(true); + data.warning("Does not return", op->getAddr()); + noParams = true; + } + else { + returnType = 0; + noParams = false; + fc->setBadJumpTable(true); // Consider using special name for switch variable + data.warning("Treating indirect jump as call",op->getAddr()); + } + if (noParams) { + if (!fc->hasModel()) { + fc->setInternal(glb->defaultfp, glb->types->getTypeVoid()); + fc->setInputLock(true); + fc->setOutputLock(true); + } + } // Create an artificial return - PcodeOp *truncop = artificialHalt(op->getAddr(),0); + PcodeOp *truncop = artificialHalt(op->getAddr(),returnType); data.opDeadInsertAfter(truncop,op); - - data.warning("Treating indirect jump as call",op->getAddr()); } } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index 84a46b4240..327294571c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -521,7 +521,7 @@ public: 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(Funcdata &partial,PcodeOp *op,FlowInfo *flow,JumpTable::RecoveryMode &mode); - bool earlyJumpTableFail(PcodeOp *op); ///< Try to determine, early, if jump-table analysis will fail + JumpTable::RecoveryMode earlyJumpTableFail(PcodeOp *op); ///< Try to determine, early, if jump-table analysis will fail 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 80e40074ef..a58daed29a 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_block.cc @@ -548,10 +548,10 @@ JumpTable::RecoveryMode Funcdata::stageJumpTable(Funcdata &partial,JumpTable *jt /// Backtrack from the BRANCHIND, looking for ops that might affect the destination. /// If a CALLOTHER, which is not injected/inlined in some way, is in the flow path of -/// the destination calculation, we know the jump-table analysis will fail and return \b true. +/// the destination calculation, we know the jump-table analysis will fail and the failure mode is returned. /// \param op is the BRANCHIND op -/// \return \b true if jump-table analysis is guaranteed to fail -bool Funcdata::earlyJumpTableFail(PcodeOp *op) +/// \return \b success if there is no early failure, or the failure mode otherwise +JumpTable::RecoveryMode Funcdata::earlyJumpTableFail(PcodeOp *op) { Varnode *vn = op->getIn(0); @@ -559,9 +559,9 @@ bool Funcdata::earlyJumpTableFail(PcodeOp *op) list::const_iterator startiter = beginOpDead(); int4 countMax = 8; while(iter != startiter) { - if (vn->getSize() == 1) return false; + if (vn->getSize() == 1) return JumpTable::success; countMax -= 1; - if (countMax < 0) return false; // Don't iterate too many times + if (countMax < 0) return JumpTable::success; // Don't iterate too many times --iter; op = *iter; Varnode *outvn = op->getOut(); @@ -575,33 +575,33 @@ bool Funcdata::earlyJumpTableFail(PcodeOp *op) int4 id = (int4)op->getIn(0)->getOffset(); UserPcodeOp *userOp = glb->userops.getOp(id); if (dynamic_cast(userOp) != (InjectedUserOp *)0) - return false; // Don't try to back track through injection + return JumpTable::success; // Don't try to back track through injection if (dynamic_cast(userOp) != (JumpAssistOp *)0) - return false; + return JumpTable::success; if (dynamic_cast(userOp) != (SegmentOp *)0) - return false; + return JumpTable::success; if (outhit) - return true; // Address formed via uninjected CALLOTHER, analysis will fail + return JumpTable::fail_callother; // Address formed via uninjected CALLOTHER, analysis will fail // Assume CALLOTHER will not interfere with address and continue backtracking } else { // CALL or CALLIND - Output has not been established yet - return false; // Don't try to back track through CALL + return JumpTable::success; // Don't try to back track through CALL } } else if (op->isBranch()) - return false; // Don't try to back track further + return JumpTable::success; // Don't try to back track further else { - if (op->code() == CPUI_STORE) return false; // Don't try to back track through STORE + if (op->code() == CPUI_STORE) return JumpTable::success; // Don't try to back track through STORE if (outhit) - return false; // Some special op (CPOOLREF, NEW, etc) generates address, don't assume failure + return JumpTable::success; // Some special op (CPOOLREF, NEW, etc) generates address, don't assume failure // Assume special will not interfere with address and continue backtracking } } else if (op->getEvalType() == PcodeOp::unary) { if (outhit) { Varnode *invn = op->getIn(0); - if (invn->getSize() != vn->getSize()) return false; + if (invn->getSize() != vn->getSize()) return JumpTable::success; vn = invn; // Treat input as address } // Continue backtracking @@ -610,20 +610,20 @@ bool Funcdata::earlyJumpTableFail(PcodeOp *op) if (outhit) { OpCode opc = op->code(); if (opc != CPUI_INT_ADD && opc != CPUI_INT_SUB && opc != CPUI_INT_XOR) - return false; - if (!op->getIn(1)->isConstant()) return false; // Don't back-track thru binary op, don't assume failure + return JumpTable::success; + if (!op->getIn(1)->isConstant()) return JumpTable::success; // Don't back-track thru binary op, don't assume failure Varnode *invn = op->getIn(0); - if (invn->getSize() != vn->getSize()) return false; + if (invn->getSize() != vn->getSize()) return JumpTable::success; vn = invn; // Treat input as address } // Continue backtracking } else { if (outhit) - return false; + return JumpTable::success; } } - return false; + return JumpTable::success; } /// \brief Recover control-flow destinations for a BRANCHIND @@ -657,7 +657,8 @@ JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *fl if ((flags & jumptablerecovery_dont)!=0) return (JumpTable *)0; // Explicitly told not to recover jumptables - if (earlyJumpTableFail(op)) + mode = earlyJumpTableFail(op); + if (mode != JumpTable::success) return (JumpTable *)0; JumpTable trialjt(glb); mode = stageJumpTable(partial,&trialjt,op,flow); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh index f550d24325..d3b1f4fbe4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh @@ -528,11 +528,12 @@ class JumpTable { public: /// \brief Recovery status for a specific JumpTable enum RecoveryMode { - success = 0, ///< JumpTable is fully recovered + success = 0, ///< JumpTable is fully recovered fail_normal = 1, ///< Normal failure to recover fail_thunk = 2, ///< Likely \b thunk fail_noflow = 3, ///< No legal flow to BRANCHIND - fail_return = 4 ///< Likely \b return operation + fail_return = 4, ///< Likely \b return operation + fail_callother = 5 ///< Address formed by CALLOTHER }; private: /// \brief An address table index and its corresponding out-edge