Debugger: parse memory operations correctly
Adam Higerd chighland@gmail.com
Mon, 10 Aug 2020 10:23:34 -0500
3 files changed,
54 insertions(+),
17 deletions(-)
M
include/mgba/internal/arm/decoder.h
→
include/mgba/internal/arm/decoder.h
@@ -217,6 +217,7 @@ void ARMDecodeThumb(uint16_t opcode, struct ARMInstructionInfo* info);
bool ARMDecodeThumbCombine(struct ARMInstructionInfo* info1, struct ARMInstructionInfo* info2, struct ARMInstructionInfo* out); int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen); +uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc); CXX_GUARD_END
M
src/arm/debugger/debugger.c
→
src/arm/debugger/debugger.c
@@ -15,6 +15,8 @@ #include <mgba/internal/debugger/parser.h>
#include <mgba/internal/debugger/stack-trace.h> #include <mgba-util/math.h> +#define FRAME_PRIV(FRAME) ((struct ARMRegisterFile*) FRAME->regs)->cpsr.priv + DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint); static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* info) {@@ -29,16 +31,6 @@ 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;@@ -46,8 +38,8 @@ struct ARMInstructionInfo info;
struct mStackTrace* stack = &d->p->stackTrace; struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); - int currentStack = ARMGetStack(&cpu->regs); - if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMGetStack(frame->regs)) { + enum RegisterBank currentStack = ARMSelectBank(cpu->cpsr.priv); + if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))) { // 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.@@ -56,7 +48,7 @@ do {
shouldBreak = shouldBreak || frame->breakWhenFinished; mStackTracePop(stack); frame = mStackTraceGetFrame(stack, 0); - } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMGetStack(frame->regs)); + } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))); if (shouldBreak) { struct mDebuggerEntryInfo debuggerInfo = { .address = pc,@@ -95,7 +87,7 @@ if (info.branchType == ARM_BRANCH_NONE && !interrupt) {
return false; } - bool isCall = (info.branchType & ARM_BRANCH_LINKED); + bool isCall = info.branchType & ARM_BRANCH_LINKED; uint32_t destAddress; if (interrupt && !isCall) {@@ -126,8 +118,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.branchType & ARM_BRANCH_INDIRECT) && info.op1.reg == ARM_PC && info.operandFormat & ARM_OPERAND_MEMORY_2) { + uint32_t ptrAddress = ARMResolveMemoryAccess(&info, &cpu->regs, pc); + destAddress = cpu->memory.load32(cpu, ptrAddress, 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.@@ -166,7 +159,7 @@ if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) {
return false; } } else if (!interrupt) { - if (frame && currentStack == ARMGetStack(frame->regs)) { + if (frame && currentStack == ARMSelectBank(FRAME_PRIV(frame))) { mStackTracePop(stack); } if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) {
M
src/arm/decoder.c
→
src/arm/decoder.c
@@ -488,3 +488,46 @@ }
buffer[blen - 1] = '\0'; return total; } + +uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc) { + uint32_t address = 0; + int32_t offset = 0; + if (info->memory.format & ARM_MEMORY_REGISTER_BASE) { + if (info->memory.baseReg == ARM_PC && info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) { + address = pc; + } else { + address = regs->gprs[info->memory.baseReg]; + } + } + if (info->memory.format & ARM_MEMORY_POST_INCREMENT) { + return address; + } + if (info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) { + offset = info->memory.offset.immediate; + } else if (info->memory.format & ARM_MEMORY_REGISTER_OFFSET) { + offset = info->memory.offset.reg == ARM_PC ? pc : regs->gprs[info->memory.offset.reg]; + } + if (info->memory.format & ARM_MEMORY_SHIFTED_OFFSET) { + uint8_t shiftSize = info->memory.offset.shifterImm; + switch (info->memory.offset.shifterOp) { + case ARM_SHIFT_LSL: + offset <<= shiftSize; + break; + case ARM_SHIFT_LSR: + offset = ((uint32_t) offset) >> shiftSize; + break; + case ARM_SHIFT_ASR: + offset >>= shiftSize; + break; + case ARM_SHIFT_ROR: + offset = ROR(offset, shiftSize); + break; + case ARM_SHIFT_RRX: + offset = (regs->cpsr.c << 31) | ((uint32_t) offset >> 1); + break; + default: + break; + }; + } + return address + (info->memory.format & ARM_MEMORY_OFFSET_SUBTRACT ? -offset : offset); +}