diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index dcc5afa567..48719dae14 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -25,6 +25,7 @@ src/decompile/datatests/forloop_varused.xml||GHIDRA||||END| src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END| src/decompile/datatests/impliedfield.xml||GHIDRA||||END| src/decompile/datatests/indproto.xml||GHIDRA||||END| +src/decompile/datatests/injectoverride.xml||GHIDRA||||END| src/decompile/datatests/loopcomment.xml||GHIDRA||||END| src/decompile/datatests/mixfloatint.xml||GHIDRA||||END| src/decompile/datatests/modulo.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc index 178047f043..c59ebdf616 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc @@ -323,7 +323,7 @@ PcodeOp *FlowInfo::xrefControlFlow(list::const_iterator oiter,bool &s --oiter; // Backup one op, to pickup halt break; case CPUI_CALLIND: - if (setupCallindSpecs(op,true,fc)) + if (setupCallindSpecs(op,fc)) --oiter; // Backup one op, to pickup halt break; case CPUI_CALLOTHER: @@ -685,27 +685,20 @@ bool FlowInfo::setupCallSpecs(PcodeOp *op,FuncCallSpecs *fc) /// The new FuncCallSpecs object is created and initialized based on /// the CALLIND op at the site. Any overriding prototype or control-flow may be examined and applied. /// \param op is the given CALLIND op -/// \param tryoverride is \b true is overrides should be applied for the call site /// \param fc is non-NULL if \e injection is in progress and a cycle check needs to be made /// \return \b true if it is discovered the sub-function never returns -bool FlowInfo::setupCallindSpecs(PcodeOp *op,bool tryoverride,FuncCallSpecs *fc) +bool FlowInfo::setupCallindSpecs(PcodeOp *op,FuncCallSpecs *fc) { FuncCallSpecs *res; res = new FuncCallSpecs(op); qlst.push_back(res); - if (tryoverride) { - data.getOverride().applyIndirect(data,*res); - data.getOverride().applyPrototype(data,*res); - } + data.getOverride().applyIndirect(data,*res); + if (fc != (FuncCallSpecs *)0 && fc->getEntryAddress() == res->getEntryAddress()) + res->setAddress(Address()); // Cancel any indirect override + data.getOverride().applyPrototype(data,*res); queryCall(*res); - if (fc != (FuncCallSpecs *)0) { - if (fc->getEntryAddress() == res->getEntryAddress()) { - res->cancelInjectId(); - res->setAddress(Address()); // Cancel any indirect override - } - } if (!res->getEntryAddress().isInvalid()) { // If we are overridden to a direct call // Change indirect pcode call into a normal pcode call @@ -721,9 +714,9 @@ void FlowInfo::truncateIndirectJump(PcodeOp *op,int4 failuremode) { data.opSetOpcode(op,CPUI_CALLIND); // Turn jump into call - bool tryoverride = (failuremode == 2); - setupCallindSpecs(op,tryoverride,(FuncCallSpecs *)0); - data.getCallSpecs(op)->setBadJumpTable(true); + setupCallindSpecs(op,(FuncCallSpecs *)0); + if (failuremode != 2) // Unless the switch was a thunk mechanism + data.getCallSpecs(op)->setBadJumpTable(true); // Consider using special name for switch variable // Create an artificial return PcodeOp *truncop = artificialHalt(op->getAddr(),0); @@ -1031,7 +1024,7 @@ void FlowInfo::xrefInlinedBranch(PcodeOp *op) if (op->code() == CPUI_CALL) setupCallSpecs(op,(FuncCallSpecs *)0); else if (op->code() == CPUI_CALLIND) - setupCallindSpecs(op,true,(FuncCallSpecs *)0); + setupCallindSpecs(op,(FuncCallSpecs *)0); else if (op->code() == CPUI_BRANCHIND) { JumpTable *jt = data.linkJumpTable(op); if (jt == (JumpTable *)0) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.hh index d6a4b18f00..7362e4b189 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.hh @@ -125,7 +125,7 @@ private: bool checkForFlowModification(FuncCallSpecs &fspecs); void queryCall(FuncCallSpecs &fspecs); ///< Try to recover the Funcdata object corresponding to a given call bool setupCallSpecs(PcodeOp *op,FuncCallSpecs *fc); ///< Set up the FuncCallSpecs object for a new call site - bool setupCallindSpecs(PcodeOp *op,bool tryoverride,FuncCallSpecs *fc); + bool setupCallindSpecs(PcodeOp *op,FuncCallSpecs *fc); void xrefInlinedBranch(PcodeOp *op); ///< Check for control-flow in a new injected p-code op void doInjection(InjectPayload *payload,InjectContext &icontext,PcodeOp *op,FuncCallSpecs *fc); void injectUserOp(PcodeOp *op); ///< Perform \e injection for a given user-defined p-code op diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc index 4309aedf4d..19ed41c636 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc @@ -3743,6 +3743,19 @@ void FuncProto::clearInput(void) flags &= ~((uint4)voidinputlock); // If a void was locked in clear it } +/// Set the id directly. +/// \param id is the new id +void FuncProto::setInjectId(int4 id) + +{ + if (id < 0) + cancelInjectId(); + else { + injectid = id; + flags |= is_inline; + } +} + void FuncProto::cancelInjectId(void) { @@ -5098,8 +5111,6 @@ void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd) Varnode *vn = data.newVarnodeCallSpecs(this); data.opSetInput(op,vn,0); data.opSetOpcode(op,CPUI_CALL); - if (isOverride()) // If we are overridden at the call-site - return; // Don't use the discovered function prototype data.getOverride().insertIndirectOverride(op->getAddr(),entryaddress); @@ -5108,14 +5119,17 @@ void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd) vector newinput; Varnode *newoutput; FuncProto &newproto( newfd->getFuncProto() ); - if ((!newproto.isNoReturn())&&(!newproto.isInline())&& - lateRestriction(newproto,newinput,newoutput)) { - commitNewInputs(data,newinput); - commitNewOutputs(data,newoutput); - } - else { - data.setRestartPending(true); + if ((!newproto.isNoReturn())&&(!newproto.isInline())) { + if (isOverride()) // If we are overridden at the call-site + return; // Don't use the discovered function prototype + + if (lateRestriction(newproto,newinput,newoutput)) { + commitNewInputs(data,newinput); + commitNewOutputs(data,newoutput); + return; // We have successfully updated the prototype, don't restart + } } + data.setRestartPending(true); } /// \brief Force a more restrictive prototype on \b this call site diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh index abbde51fa3..08487fa4e1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh @@ -1428,6 +1428,7 @@ public: void clearUnlockedInput(void); ///< Clear input parameters that have not been locked void clearUnlockedOutput(void); ///< Clear the return value if it has not been locked void clearInput(void); ///< Clear all input parameters regardless of lock + void setInjectId(int4 id); ///< Associate a given injection with \b this prototype void cancelInjectId(void); ///< Turn-off any in-lining for this function void resolveModel(ParamActive *active); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc index b8d8afee03..5e035d4a2d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc @@ -125,6 +125,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status) status->registerCom(new IfcCallGraphList(),"callgraph","list"); status->registerCom(new IfcCallFixup(),"fixup","call"); status->registerCom(new IfcCallOtherFixup(),"fixup","callother"); + status->registerCom(new IfcFixupApply(),"fixup","apply"); status->registerCom(new IfcVolatile(),"volatile"); status->registerCom(new IfcReadonly(),"readonly"); status->registerCom(new IfcPointerSetting(),"pointer","setting"); @@ -2883,6 +2884,43 @@ void IfcCallOtherFixup::execute(istream &s) *status->optr << "Successfully registered callotherfixup" << endl; } +/// \class IfcFixupApply +/// \brief Apply a call-fixup to a particular function: `fixup apply ` +/// +/// The call-fixup and function are named from the command-line. If they both exist, +/// the fixup is set on the function's prototype. +void IfcFixupApply::execute(istream &s) + +{ + if (dcp->conf == (Architecture *)0) + throw IfaceExecutionError("No load image present"); + + string fixupName,funcName; + + s >> ws; + if (s.eof()) + throw IfaceParseError("Missing fixup name"); + s >> fixupName >> ws; + if (s.eof()) + throw IfaceParseError("Missing function name"); + s >> funcName; + + int4 injectid = dcp->conf->pcodeinjectlib->getPayloadId(InjectPayload::CALLFIXUP_TYPE, fixupName); + if (injectid < 0) + throw IfaceExecutionError("Unknown fixup: " + fixupName); + + string basename; + Scope *funcscope = dcp->conf->symboltab->resolveScopeFromSymbolName(funcName,"::",basename,(Scope *)0); + if (funcscope == (Scope *)0) + throw IfaceExecutionError("Bad namespace: "+funcName); + Funcdata *fd = funcscope->queryFunction( basename ); // Is function already in database + if (fd == (Funcdata *)0) + throw IfaceExecutionError("Unknown function name: "+funcName); + + fd->getFuncProto().setInjectId(injectid); + *status->optr << "Successfully applied callfixup" << endl; +} + /// \class IfcVolatile /// \brief Mark a memory range as volatile: `volatile ` /// diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh index 8c3386c5f8..73f476e20b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh @@ -531,6 +531,11 @@ public: virtual void execute(istream &s); }; +class IfcFixupApply : public IfaceDecompCommand { +public: + virtual void execute(istream &s); +}; + class IfcCountPcode : public IfaceDecompCommand { public: virtual void execute(istream &s); diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/injectoverride.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/injectoverride.xml new file mode 100644 index 0000000000..070958d6f5 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/injectoverride.xml @@ -0,0 +1,32 @@ + + + + +534883ec20ff1516000000c300000000 +48b830c000800100000066e80200c300 +c320c0008001000000 + + + + + + + +\(\*in_RAX\)\(param_1,param_2\); +collapsed\(param_1\); + diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/DeletePrototypeOverrideAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/DeletePrototypeOverrideAction.java index 03ce6963a9..1c5ef22bb2 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/DeletePrototypeOverrideAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/DeletePrototypeOverrideAction.java @@ -20,10 +20,10 @@ import ghidra.app.decompiler.ClangToken; import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.util.HelpTopics; import ghidra.program.database.symbol.CodeSymbol; -import ghidra.program.model.address.Address; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.HighFunction; +import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.symbol.*; import ghidra.util.*; @@ -39,16 +39,17 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction { if (tokenAtCursor == null) { return null; } - Address addr = tokenAtCursor.getMinAddress(); - if (addr == null) { - return null; - } + Namespace overspace = HighFunction.findOverrideSpace(func); if (overspace == null) { return null; } + PcodeOp op = OverridePrototypeAction.getCallOp(func.getProgram(), tokenAtCursor); + if (op == null) { + return null; + } SymbolTable symtab = func.getProgram().getSymbolTable(); - SymbolIterator iter = symtab.getSymbols(overspace); + SymbolIterator iter = symtab.getSymbolsAsIterator(op.getSeqnum().getTarget()); while (iter.hasNext()) { Symbol sym = iter.next(); if (!sym.getName().startsWith("prt")) { @@ -57,7 +58,7 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction { if (!(sym instanceof CodeSymbol)) { continue; } - if (!sym.getAddress().equals(addr)) { + if (!sym.getParentNamespace().equals(overspace)) { continue; } return (CodeSymbol) sym; diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/OverridePrototypeAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/OverridePrototypeAction.java index 1d133909e2..2c22a7f3e3 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/OverridePrototypeAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/OverridePrototypeAction.java @@ -47,7 +47,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction { * @param tokenAtCursor is the point in the window the user has selected * @return the PcodeOp or null */ - private static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) { + public static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) { if (tokenAtCursor == null) { return null; }