Stack traces: fix tracing of indirect jumps and interrupt handlers

This commit is contained in:
Adam Higerd 2020-08-09 23:36:46 -05:00 committed by Vicki Pfau
parent 0c51bdf618
commit e68d3ed00f
2 changed files with 30 additions and 12 deletions

View File

@ -29,6 +29,16 @@ static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* in
} }
} }
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) { static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uint32_t pc) {
struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMDebugger* debugger = (struct ARMDebugger*) d;
struct ARMCore* cpu = debugger->cpu; struct ARMCore* cpu = debugger->cpu;
@ -36,7 +46,8 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
struct mStackTrace* stack = &d->p->stackTrace; struct mStackTrace* stack = &d->p->stackTrace;
struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); 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 // 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 // has been returned from, or that the stack pointer has been otherwise
// manipulated. Either way, the function is done executing. // manipulated. Either way, the function is done executing.
@ -45,7 +56,7 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
shouldBreak = shouldBreak || frame->breakWhenFinished; shouldBreak = shouldBreak || frame->breakWhenFinished;
mStackTracePop(stack); mStackTracePop(stack);
frame = mStackTraceGetFrame(stack, 0); 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) { if (shouldBreak) {
struct mDebuggerEntryInfo debuggerInfo = { struct mDebuggerEntryInfo debuggerInfo = {
.address = pc, .address = pc,
@ -84,10 +95,10 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
return false; return false;
} }
bool isCall = interrupt || (info.branchType & ARM_BRANCH_LINKED); bool isCall = (info.branchType & ARM_BRANCH_LINKED);
uint32_t destAddress; 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 // The stack frame was already pushed up above, so there's no
// action necessary here, but we still want to check for a // action necessary here, but we still want to check for a
// breakpoint down below. // breakpoint down below.
@ -115,6 +126,9 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
bool isBranch = ARMInstructionIsBranch(info.mnemonic); bool isBranch = ARMInstructionIsBranch(info.mnemonic);
int reg = (isBranch ? info.op1.reg : info.op2.reg); int reg = (isBranch ? info.op1.reg : info.op2.reg);
destAddress = cpu->gprs[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)) { 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. // ARMv4 doesn't have the BLX opcode, so it uses an assignment to LR before a BX for that purpose.
struct ARMInstructionInfo prevInfo; struct ARMInstructionInfo prevInfo;
@ -145,25 +159,23 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
return false; return false;
} }
if (info.branchType & ARM_BRANCH_INDIRECT) {
destAddress = cpu->memory.load32(cpu, destAddress, NULL);
}
if (isCall) { if (isCall) {
int instructionLength = isWideInstruction ? WORD_SIZE_ARM : WORD_SIZE_THUMB; int instructionLength = isWideInstruction ? WORD_SIZE_ARM : WORD_SIZE_THUMB;
frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs); frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs);
if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) { if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) {
return false; return false;
} }
} else { } else if (!interrupt) {
mStackTracePop(stack); if (frame && currentStack == ARMGetStack(frame->regs)) {
mStackTracePop(stack);
}
if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) { if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) {
return false; return false;
} }
} }
struct mDebuggerEntryInfo debuggerInfo = { struct mDebuggerEntryInfo debuggerInfo = {
.address = pc, .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 .pointId = 0
}; };
mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo); mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo);

View File

@ -1035,7 +1035,13 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r
case DEBUGGER_ENTER_STACK: case DEBUGGER_ENTER_STACK:
if (info) { if (info) {
if (info->type.st.traceType == STACK_TRACE_BREAK_ON_CALL) { 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 { } else {
cliDebugger->backend->printf(cliDebugger->backend, "Hit function return at at 0x%08X\n", info->address); cliDebugger->backend->printf(cliDebugger->backend, "Hit function return at at 0x%08X\n", info->address);
} }