Stack traces: fix tracing of indirect jumps and interrupt handlers
Adam Higerd chighland@gmail.com
Sun, 09 Aug 2020 23:36:46 -0500
2 files changed,
30 insertions(+),
12 deletions(-)
M
src/arm/debugger/debugger.c
→
src/arm/debugger/debugger.c
@@ -29,6 +29,16 @@ return ARMDecodeThumbCombine(info, &info2, info);
} } +static inline int ARMGetStack(struct ARMRegisterFile* regs) { + if (!regs) { + return MODE_USER; + } + if (regs->cpsr.priv == MODE_SYSTEM) { + return MODE_USER; + } + return regs->cpsr.priv; +} + static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uint32_t pc) { struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMCore* cpu = debugger->cpu;@@ -36,7 +46,8 @@ struct ARMInstructionInfo info;
struct mStackTrace* stack = &d->p->stackTrace; struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); - if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP]) { + int currentStack = ARMGetStack(&cpu->regs); + if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMGetStack(frame->regs)) { // The stack frame has been popped off the stack. This means the function // has been returned from, or that the stack pointer has been otherwise // manipulated. Either way, the function is done executing.@@ -45,7 +56,7 @@ do {
shouldBreak = shouldBreak || frame->breakWhenFinished; mStackTracePop(stack); frame = mStackTraceGetFrame(stack, 0); - } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP]); + } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMGetStack(frame->regs)); if (shouldBreak) { struct mDebuggerEntryInfo debuggerInfo = { .address = pc,@@ -84,10 +95,10 @@ if (info.branchType == ARM_BRANCH_NONE && !interrupt) {
return false; } - bool isCall = interrupt || (info.branchType & ARM_BRANCH_LINKED); + bool isCall = (info.branchType & ARM_BRANCH_LINKED); uint32_t destAddress; - if (interrupt && info.branchType == ARM_BRANCH_NONE) { + if (interrupt && !isCall) { // The stack frame was already pushed up above, so there's no // action necessary here, but we still want to check for a // breakpoint down below.@@ -115,6 +126,9 @@ bool isMovPcLr = (info.operandFormat & ARM_OPERAND_REGISTER_2) && info.op1.reg == ARM_PC && info.op2.reg == ARM_LR;
bool isBranch = ARMInstructionIsBranch(info.mnemonic); int reg = (isBranch ? info.op1.reg : info.op2.reg); destAddress = cpu->gprs[reg]; + if ((info.operandFormat & ARM_OPERAND_MEMORY_2) && (info.branchType & ARM_BRANCH_INDIRECT)) { + destAddress = cpu->memory.load32(cpu, destAddress, NULL); + } if (isBranch || (info.op1.reg == ARM_PC && !isMovPcLr)) { // ARMv4 doesn't have the BLX opcode, so it uses an assignment to LR before a BX for that purpose. struct ARMInstructionInfo prevInfo;@@ -143,10 +157,6 @@ }
} else { mLOG(DEBUGGER, ERROR, "Unknown branch operand in stack trace"); return false; - } - - if (info.branchType & ARM_BRANCH_INDIRECT) { - destAddress = cpu->memory.load32(cpu, destAddress, NULL); } if (isCall) {@@ -155,15 +165,17 @@ frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs);
if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) { return false; } - } else { - mStackTracePop(stack); + } else if (!interrupt) { + if (frame && currentStack == ARMGetStack(frame->regs)) { + mStackTracePop(stack); + } if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) { return false; } } struct mDebuggerEntryInfo debuggerInfo = { .address = pc, - .type.st.traceType = isCall ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN, + .type.st.traceType = (interrupt || isCall) ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN, .pointId = 0 }; mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo);
M
src/debugger/cli-debugger.c
→
src/debugger/cli-debugger.c
@@ -1035,7 +1035,13 @@ break;
case DEBUGGER_ENTER_STACK: if (info) { if (info->type.st.traceType == STACK_TRACE_BREAK_ON_CALL) { - cliDebugger->backend->printf(cliDebugger->backend, "Hit function call at at 0x%08X\n", info->address); + struct mStackTrace* stack = &cliDebugger->d.stackTrace; + struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); + if (frame->interrupt) { + cliDebugger->backend->printf(cliDebugger->backend, "Hit interrupt at at 0x%08X\n", info->address); + } else { + cliDebugger->backend->printf(cliDebugger->backend, "Hit function call at at 0x%08X\n", info->address); + } } else { cliDebugger->backend->printf(cliDebugger->backend, "Hit function return at at 0x%08X\n", info->address); }