{"record":{"id":"4559eb5552831e32","repo":"NationalSecurityAgency/ghidra","slug":"cannot-get-stack-change-for-indirect-call-op","errorCode":null,"errorMessage":"Cannot get stack change for indirect call: {op}","messagePattern":"Cannot get stack change for indirect call: (.+?)","errorType":"exception","errorClass":"PcodeExecutionException","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java","lineNumber":365,"sourceCode":"\t\tint purge = max == null ? 0 : (int) max.subtract(min);\n\t\t// AFAIK, this stdcall only applies to x86, so presume return address on stack\n\t\treturn purge + program.getLanguage().getProgramCounter().getNumBytes();\n\t}\n\n\t/**\n\t * Compute the stack change for an indirect call\n\t * \n\t * @param op the low p-code op\n\t * @return the depth change\n\t */\n\tprotected int computeStackChangeIndirect(PcodeOp op) {\n\t\tFunctionSignature sig = getSignatureOfIndirectCall(op);\n\t\tif (sig == null) {\n\t\t\tint extrapop = program.getCompilerSpec().getDefaultCallingConvention().getExtrapop();\n\t\t\tif (extrapop != PrototypeModel.UNKNOWN_EXTRAPOP) {\n\t\t\t\treturn extrapop;\n\t\t\t}\n\t\t\tthrow new PcodeExecutionException(\"Cannot get stack change for indirect call: \" + op);\n\t\t}\n\t\tPrototypeModel convention =\n\t\t\tprogram.getCompilerSpec().matchConvention(sig.getCallingConventionName());\n\t\tif (convention == null) {\n\t\t\twarnings.add(new UnspecifiedConventionStackUnwindWarning(null));\n\t\t\tconvention = program.getCompilerSpec().getDefaultCallingConvention();\n\t\t}\n\t\tint extrapop = convention.getExtrapop();\n\t\tif (extrapop != PrototypeModel.UNKNOWN_EXTRAPOP) {\n\t\t\treturn extrapop;\n\t\t}\n\t\treturn computeStdcallExtrapop(convention, sig);\n\t}\n\n\t/**\n\t * Apply the given stack change to the machine state\n\t * \n\t * <p>","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java#L347-L383","documentation":"Thrown during symbolic p-code execution of an indirect CALL when no function signature can be determined for the call target AND the default calling convention's extrapop value is UNKNOWN_EXTRAPOP. SymPcodeExecutor.computeStackChangeIndirect() tries to get the signature, falls back to the default convention's extrapop, and if that is also unknown, the stack depth change cannot be computed, halting unwinding.","triggerScenarios":"computeStackChangeIndirect() calls getSignatureOfIndirectCall(op) which returns null (no signature resolution), then checks program.getCompilerSpec().getDefaultCallingConvention().getExtrapop() which equals PrototypeModel.UNKNOWN_EXTRAPOP (-1). This combination means neither the call site nor the compiler spec provides stack adjustment info.","commonSituations":"Debugging/emulating code with indirect calls through function pointers or vtables where the target function isn't analyzed. The compiler spec for the processor doesn't define a default extrapop (common in non-x86 architectures or custom language definitions). The calling convention metadata is incomplete for the target platform.","solutions":["Analyze the indirect call target so getSignatureOfIndirectCall can resolve a signature (define the target function and its calling convention).","Set or correct the default calling convention's extrapop in the processor's .cspec file.","If the target is known, apply the correct function signature so the convention can be matched.","For custom architectures, ensure the PrototypeModel defines a concrete extrapop value instead of UNKNOWN_EXTRAPOP."],"exampleFix":"// In the .cspec for the language, ensure the default prototype model defines extrapop:\n// <prototype name=\"default\" ... extrapop=\"8\">\n// instead of extrapop=\"unknown\"","handlingStrategy":"validation","validationCode":"// Before emulating indirect calls, check if the convention has a known extrapop:\nPrototypeModel defaultConv = program.getCompilerSpec().getDefaultCallingConvention();\nif (defaultConv.getExtrapop() == PrototypeModel.UNKNOWN_EXTRAPOP) {\n    // warn: indirect calls with unknown signatures will fail stack change computation\n}","typeGuard":"private boolean canComputeIndirectStackChange(Program program) {\n    return program.getCompilerSpec()\n        .getDefaultCallingConvention()\n        .getExtrapop() != PrototypeModel.UNKNOWN_EXTRAPOP;\n}","tryCatchPattern":"try {\n    executor.execute(op, frame, library);\n} catch (PcodeExecutionException e) {\n    if (e.getMessage().startsWith(\"Cannot get stack change for indirect call\")) {\n        // fall back to manual stack adjustment or skip frame\n    } else { throw e; }\n}","preventionTips":["Define function signatures for indirect call targets so signatures can be resolved.","Ensure the .cspec default prototype model defines a concrete extrapop value.","Analyze indirect call sites with the decompiler to populate signature information.","For custom architectures, always set extrapop in the compiler spec."],"tags":["pcode","emulation","calling-convention","stack-depth","indirect-call"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}