1 /* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <mgba/internal/arm/debugger/debugger.h>
7
8 #include <mgba/core/core.h>
9 #include <mgba/internal/arm/arm.h>
10 #include <mgba/internal/arm/decoder.h>
11 #include <mgba/internal/arm/decoder-inlines.h>
12 #include <mgba/internal/arm/isa-inlines.h>
13 #include <mgba/internal/arm/debugger/memory-debugger.h>
14 #include <mgba/internal/debugger/parser.h>
15 #include <mgba/internal/debugger/stack-trace.h>
16 #include <mgba-util/math.h>
17
18 #define FRAME_PRIV(FRAME) ((struct ARMRegisterFile*) FRAME->regs)->cpsr.priv
19
20 DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
21
ARMDecodeCombined(struct ARMCore * cpu,struct ARMInstructionInfo * info)22 static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* info) {
23 if (cpu->executionMode == MODE_ARM) {
24 ARMDecodeARM(cpu->prefetch[0], info);
25 return true;
26 } else {
27 struct ARMInstructionInfo info2;
28 ARMDecodeThumb(cpu->prefetch[0], info);
29 ARMDecodeThumb(cpu->prefetch[1], &info2);
30 return ARMDecodeThumbCombine(info, &info2, info);
31 }
32 }
33
ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform * d,uint32_t pc)34 static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uint32_t pc) {
35 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
36 struct ARMCore* cpu = debugger->cpu;
37 struct ARMInstructionInfo info;
38 struct mStackTrace* stack = &d->p->stackTrace;
39
40 struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
41 enum RegisterBank currentStack = ARMSelectBank(cpu->cpsr.priv);
42 if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))) {
43 // The stack frame has been popped off the stack. This means the function
44 // has been returned from, or that the stack pointer has been otherwise
45 // manipulated. Either way, the function is done executing.
46 bool shouldBreak = debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN;
47 do {
48 shouldBreak = shouldBreak || frame->breakWhenFinished;
49 mStackTracePop(stack);
50 frame = mStackTraceGetFrame(stack, 0);
51 } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame)));
52 if (shouldBreak) {
53 struct mDebuggerEntryInfo debuggerInfo = {
54 .address = pc,
55 .type.st.traceType = STACK_TRACE_BREAK_ON_RETURN,
56 .pointId = 0
57 };
58 mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo);
59 return true;
60 } else {
61 return false;
62 }
63 }
64
65 bool interrupt = false;
66 bool isWideInstruction = ARMDecodeCombined(cpu, &info);
67 if (!isWideInstruction && info.mnemonic == ARM_MN_BL) {
68 return false;
69 }
70 if (!ARMTestCondition(cpu, info.condition)) {
71 return false;
72 }
73
74 if (_ARMModeHasSPSR(cpu->cpsr.priv)) {
75 struct mStackFrame* irqFrame = mStackTraceGetFrame(stack, 0);
76 // TODO: uint32_t ivtBase = ARMControlRegIsVE(cpu->cp15.r1.c0) ? 0xFFFF0000 : 0x00000000;
77 uint32_t ivtBase = 0x00000000;
78 if (ivtBase <= pc && pc < ivtBase + 0x20 && !(irqFrame && _ARMModeHasSPSR(((struct ARMRegisterFile*) irqFrame->regs)->cpsr.priv))) {
79 // TODO: Potential enhancement opportunity: add break-on-exception mode
80 irqFrame = mStackTracePush(stack, pc, pc, cpu->gprs[ARM_SP], &cpu->regs);
81 irqFrame->interrupt = true;
82 interrupt = true;
83 }
84 }
85
86 if (info.branchType == ARM_BRANCH_NONE && !interrupt) {
87 return false;
88 }
89
90 bool isCall = info.branchType & ARM_BRANCH_LINKED;
91 uint32_t destAddress;
92
93 if (interrupt && !isCall) {
94 // The stack frame was already pushed up above, so there's no
95 // action necessary here, but we still want to check for a
96 // breakpoint down below.
97 //
98 // The first instruction could possibly be a call, which would
99 // need ANOTHER stack frame, so only skip if it's not.
100 destAddress = pc;
101 } else if (info.operandFormat & ARM_OPERAND_MEMORY_1) {
102 // This is most likely ldmia ..., {..., pc}, which is a function return.
103 // To find which stack slot holds the return address, count the number of set bits.
104 int regCount = popcount32(info.op1.immediate);
105 uint32_t baseAddress = cpu->gprs[info.memory.baseReg] + ((regCount - 1) << 2);
106 destAddress = cpu->memory.load32(cpu, baseAddress, NULL);
107 } else if (info.operandFormat & ARM_OPERAND_IMMEDIATE_1) {
108 if (!isCall) {
109 return false;
110 }
111 destAddress = info.op1.immediate + cpu->gprs[ARM_PC];
112 } else if (info.operandFormat & ARM_OPERAND_REGISTER_1) {
113 if (isCall) {
114 destAddress = cpu->gprs[info.op1.reg];
115 } else {
116 bool isExceptionReturn = _ARMModeHasSPSR(cpu->cpsr.priv) && info.affectsCPSR && info.op1.reg == ARM_PC;
117 bool isMovPcLr = (info.operandFormat & ARM_OPERAND_REGISTER_2) && info.op1.reg == ARM_PC && info.op2.reg == ARM_LR;
118 bool isBranch = ARMInstructionIsBranch(info.mnemonic);
119 int reg = (isBranch ? info.op1.reg : info.op2.reg);
120 destAddress = cpu->gprs[reg];
121 if (!isBranch && (info.branchType & ARM_BRANCH_INDIRECT) && info.op1.reg == ARM_PC && info.operandFormat & ARM_OPERAND_MEMORY_2) {
122 uint32_t ptrAddress = ARMResolveMemoryAccess(&info, &cpu->regs, pc);
123 destAddress = cpu->memory.load32(cpu, ptrAddress, NULL);
124 }
125 if (isBranch || (info.op1.reg == ARM_PC && !isMovPcLr)) {
126 // ARMv4 doesn't have the BLX opcode, so it uses an assignment to LR before a BX for that purpose.
127 struct ARMInstructionInfo prevInfo;
128 if (cpu->executionMode == MODE_ARM) {
129 ARMDecodeARM(cpu->memory.load32(cpu, pc - 4, NULL), &prevInfo);
130 } else {
131 ARMDecodeThumb(cpu->memory.load16(cpu, pc - 2, NULL), &prevInfo);
132 }
133 if ((prevInfo.operandFormat & (ARM_OPERAND_REGISTER_1 | ARM_OPERAND_AFFECTED_1)) == (ARM_OPERAND_REGISTER_1 | ARM_OPERAND_AFFECTED_1) && prevInfo.op1.reg == ARM_LR) {
134 isCall = true;
135 } else if ((isBranch ? info.op1.reg : info.op2.reg) == ARM_LR) {
136 isBranch = true;
137 } else if (frame && frame->frameBaseAddress == (uint32_t) cpu->gprs[ARM_SP]) {
138 // A branch to something that isn't LR isn't a standard function return, but it might potentially
139 // be a nonstandard one. As a heuristic, if the stack pointer and the destination address match
140 // where we came from, consider it to be a function return.
141 isBranch = (destAddress > frame->callAddress + 1 && destAddress <= frame->callAddress + 5);
142 } else {
143 isBranch = false;
144 }
145 }
146 if (!isCall && !isBranch && !isExceptionReturn && !isMovPcLr) {
147 return false;
148 }
149 }
150 } else {
151 mLOG(DEBUGGER, ERROR, "Unknown branch operand in stack trace");
152 return false;
153 }
154
155 if (interrupt || isCall) {
156 if (isCall) {
157 int instructionLength = isWideInstruction ? WORD_SIZE_ARM : WORD_SIZE_THUMB;
158 frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs);
159 }
160 if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) {
161 return false;
162 }
163 } else {
164 if (frame && currentStack == ARMSelectBank(FRAME_PRIV(frame))) {
165 mStackTracePop(stack);
166 }
167 if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) {
168 return false;
169 }
170 }
171 struct mDebuggerEntryInfo debuggerInfo = {
172 .address = pc,
173 .type.st.traceType = (interrupt || isCall) ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN,
174 .pointId = 0
175 };
176 mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo);
177 return true;
178 }
179
_lookupBreakpoint(struct ARMDebugBreakpointList * breakpoints,uint32_t address)180 static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
181 size_t i;
182 for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
183 if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.address == address) {
184 return ARMDebugBreakpointListGetPointer(breakpoints, i);
185 }
186 }
187 return 0;
188 }
189
_destroyBreakpoint(struct ARMDebugBreakpoint * breakpoint)190 static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
191 if (breakpoint->d.condition) {
192 parseFree(breakpoint->d.condition);
193 free(breakpoint->d.condition);
194 }
195 }
196
_destroyWatchpoint(struct mWatchpoint * watchpoint)197 static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
198 if (watchpoint->condition) {
199 parseFree(watchpoint->condition);
200 free(watchpoint->condition);
201 }
202 }
203
ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform * d)204 static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
205 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
206 int instructionLength = _ARMInstructionLength(debugger->cpu);
207 uint32_t pc = debugger->cpu->gprs[ARM_PC] - instructionLength;
208 if (debugger->stackTraceMode != STACK_TRACE_DISABLED && ARMDebuggerUpdateStackTraceInternal(d, pc)) {
209 return;
210 }
211 struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, pc);
212 if (!breakpoint) {
213 return;
214 }
215 if (breakpoint->d.condition) {
216 int32_t value;
217 int segment;
218 if (!mDebuggerEvaluateParseTree(d->p, breakpoint->d.condition, &value, &segment) || !(value || segment >= 0)) {
219 return;
220 }
221 }
222 struct mDebuggerEntryInfo info = {
223 .address = breakpoint->d.address,
224 .type.bp.breakType = BREAKPOINT_HARDWARE,
225 .pointId = breakpoint->d.id
226 };
227 mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
228 }
229
230 static void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform);
231 static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
232
233 static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
234
235 static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, const struct mBreakpoint*);
236 static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, ssize_t id);
237 static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform*, struct mBreakpointList*);
238 static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, const struct mWatchpoint*);
239 static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatchpointList*);
240 static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
241 static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
242 static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
243 static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* out, size_t* length);
244 static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length);
245 static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
246 static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
247 static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform*);
248 static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform*, uint32_t);
249 static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d);
250
ARMDebuggerPlatformCreate(void)251 struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
252 struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
253 platform->entered = ARMDebuggerEnter;
254 platform->init = ARMDebuggerInit;
255 platform->deinit = ARMDebuggerDeinit;
256 platform->setBreakpoint = ARMDebuggerSetBreakpoint;
257 platform->listBreakpoints = ARMDebuggerListBreakpoints;
258 platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
259 platform->setWatchpoint = ARMDebuggerSetWatchpoint;
260 platform->listWatchpoints = ARMDebuggerListWatchpoints;
261 platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
262 platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
263 platform->trace = ARMDebuggerTrace;
264 platform->getRegister = ARMDebuggerGetRegister;
265 platform->setRegister = ARMDebuggerSetRegister;
266 platform->getStackTraceMode = ARMDebuggerGetStackTraceMode;
267 platform->setStackTraceMode = ARMDebuggerSetStackTraceMode;
268 platform->updateStackTrace = ARMDebuggerUpdateStackTrace;
269 return platform;
270 }
271
ARMDebuggerInit(void * cpu,struct mDebuggerPlatform * platform)272 void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
273 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
274 debugger->cpu = cpu;
275 debugger->originalMemory = debugger->cpu->memory;
276 debugger->nextId = 1;
277 debugger->stackTraceMode = STACK_TRACE_DISABLED;
278 ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
279 ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
280 mWatchpointListInit(&debugger->watchpoints, 0);
281 struct mStackTrace* stack = &platform->p->stackTrace;
282 mStackTraceInit(stack, sizeof(struct ARMRegisterFile));
283 stack->formatRegisters = ARMDebuggerFrameFormatRegisters;
284 }
285
ARMDebuggerDeinit(struct mDebuggerPlatform * platform)286 void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
287 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
288 if (debugger->clearSoftwareBreakpoint) {
289 // Clear the stack backwards in case any overlap
290 size_t b;
291 for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
292 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
293 debugger->clearSoftwareBreakpoint(debugger, breakpoint);
294 }
295 }
296 ARMDebuggerRemoveMemoryShim(debugger);
297
298 size_t i;
299 for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
300 _destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
301 }
302 ARMDebugBreakpointListDeinit(&debugger->breakpoints);
303
304 for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
305 _destroyWatchpoint(mWatchpointListGetPointer(&debugger->watchpoints, i));
306 }
307 ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
308 mWatchpointListDeinit(&debugger->watchpoints);
309 mStackTraceDeinit(&platform->p->stackTrace);
310 }
311
ARMDebuggerEnter(struct mDebuggerPlatform * platform,enum mDebuggerEntryReason reason,struct mDebuggerEntryInfo * info)312 static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
313 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
314 struct ARMCore* cpu = debugger->cpu;
315 cpu->nextEvent = cpu->cycles;
316 if (reason == DEBUGGER_ENTER_BREAKPOINT) {
317 struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
318 if (breakpoint && breakpoint->d.type == BREAKPOINT_SOFTWARE) {
319 info->address = breakpoint->d.address;
320 info->pointId = breakpoint->d.id;
321 if (debugger->clearSoftwareBreakpoint) {
322 debugger->clearSoftwareBreakpoint(debugger, breakpoint);
323 }
324
325 ARMRunFake(cpu, breakpoint->sw.opcode);
326
327 if (debugger->setSoftwareBreakpoint) {
328 debugger->setSoftwareBreakpoint(debugger, breakpoint->d.address, breakpoint->sw.mode, &breakpoint->sw.opcode);
329 }
330 }
331 }
332 if (debugger->d.p->entered) {
333 debugger->d.p->entered(debugger->d.p, reason, info);
334 }
335 }
336
ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform * d,uint32_t address,enum ExecutionMode mode)337 ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
338 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
339 uint32_t opcode;
340 if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
341 return -1;
342 }
343
344 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
345 ssize_t id = debugger->nextId;
346 ++debugger->nextId;
347 breakpoint->d.id = id;
348 breakpoint->d.address = address & ~1; // Clear Thumb bit since it's not part of a valid address
349 breakpoint->d.segment = -1;
350 breakpoint->d.condition = NULL;
351 breakpoint->d.type = BREAKPOINT_SOFTWARE;
352 breakpoint->sw.opcode = opcode;
353 breakpoint->sw.mode = mode;
354
355 return id;
356 }
357
ARMDebuggerSetBreakpoint(struct mDebuggerPlatform * d,const struct mBreakpoint * info)358 static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struct mBreakpoint* info) {
359 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
360 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
361 ssize_t id = debugger->nextId;
362 ++debugger->nextId;
363 breakpoint->d = *info;
364 breakpoint->d.address &= ~1; // Clear Thumb bit since it's not part of a valid address
365 breakpoint->d.id = id;
366 if (info->type == BREAKPOINT_SOFTWARE) {
367 // TODO
368 abort();
369 }
370 return id;
371 }
372
ARMDebuggerClearBreakpoint(struct mDebuggerPlatform * d,ssize_t id)373 static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id) {
374 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
375 size_t i;
376
377 struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
378 for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
379 if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.id == id) {
380 _destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
381 ARMDebugBreakpointListShift(breakpoints, i, 1);
382 return true;
383 }
384 }
385
386 struct ARMDebugBreakpointList* swBreakpoints = &debugger->swBreakpoints;
387 if (debugger->clearSoftwareBreakpoint) {
388 for (i = 0; i < ARMDebugBreakpointListSize(swBreakpoints); ++i) {
389 if (ARMDebugBreakpointListGetPointer(swBreakpoints, i)->d.id == id) {
390 debugger->clearSoftwareBreakpoint(debugger, ARMDebugBreakpointListGetPointer(swBreakpoints, i));
391 ARMDebugBreakpointListShift(swBreakpoints, i, 1);
392 return true;
393 }
394 }
395 }
396
397 struct mWatchpointList* watchpoints = &debugger->watchpoints;
398 for (i = 0; i < mWatchpointListSize(watchpoints); ++i) {
399 if (mWatchpointListGetPointer(watchpoints, i)->id == id) {
400 _destroyWatchpoint(mWatchpointListGetPointer(watchpoints, i));
401 mWatchpointListShift(watchpoints, i, 1);
402 if (!mWatchpointListSize(&debugger->watchpoints)) {
403 ARMDebuggerRemoveMemoryShim(debugger);
404 }
405 return true;
406 }
407 }
408 return false;
409 }
410
ARMDebuggerListBreakpoints(struct mDebuggerPlatform * d,struct mBreakpointList * list)411 static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBreakpointList* list) {
412 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
413 mBreakpointListClear(list);
414 size_t i, s;
415 for (i = 0, s = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints) || s < ARMDebugBreakpointListSize(&debugger->swBreakpoints);) {
416 struct ARMDebugBreakpoint* hw = NULL;
417 struct ARMDebugBreakpoint* sw = NULL;
418 if (i < ARMDebugBreakpointListSize(&debugger->breakpoints)) {
419 hw = ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i);
420 }
421 if (s < ARMDebugBreakpointListSize(&debugger->swBreakpoints)) {
422 sw = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, s);
423 }
424 struct mBreakpoint* b = mBreakpointListAppend(list);
425 if (hw && sw) {
426 if (hw->d.id < sw->d.id) {
427 *b = hw->d;
428 ++i;
429 } else {
430 *b = sw->d;
431 ++s;
432 }
433 } else if (hw) {
434 *b = hw->d;
435 ++i;
436 } else if (sw) {
437 *b = sw->d;
438 ++s;
439 } else {
440 abort(); // Should be unreachable
441 }
442 }
443 }
444
ARMDebuggerHasBreakpoints(struct mDebuggerPlatform * d)445 static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
446 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
447 return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints) || debugger->stackTraceMode != STACK_TRACE_DISABLED;
448 }
449
ARMDebuggerSetWatchpoint(struct mDebuggerPlatform * d,const struct mWatchpoint * info)450 static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) {
451 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
452 if (!mWatchpointListSize(&debugger->watchpoints)) {
453 ARMDebuggerInstallMemoryShim(debugger);
454 }
455 struct mWatchpoint* watchpoint = mWatchpointListAppend(&debugger->watchpoints);
456 ssize_t id = debugger->nextId;
457 ++debugger->nextId;
458 *watchpoint = *info;
459 watchpoint->id = id;
460 return id;
461 }
462
ARMDebuggerListWatchpoints(struct mDebuggerPlatform * d,struct mWatchpointList * list)463 static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform* d, struct mWatchpointList* list) {
464 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
465 mWatchpointListClear(list);
466 mWatchpointListCopy(list, &debugger->watchpoints);
467 }
468
ARMDebuggerTrace(struct mDebuggerPlatform * d,char * out,size_t * length)469 static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
470 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
471 struct ARMCore* cpu = debugger->cpu;
472 struct mCore* core = d->p->core;
473
474 char disassembly[64];
475
476 struct ARMInstructionInfo info;
477 bool isWideInstruction = ARMDecodeCombined(cpu, &info);
478 if (cpu->executionMode == MODE_ARM) {
479 uint32_t instruction = cpu->prefetch[0];
480 sprintf(disassembly, "%08X: ", instruction);
481 ARMDisassemble(&info, cpu, core->symbolTable, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
482 } else {
483 uint16_t instruction = cpu->prefetch[0];
484 ARMDecodeThumb(instruction, &info);
485 if (isWideInstruction) {
486 uint16_t instruction2 = cpu->prefetch[1];
487 sprintf(disassembly, "%04X%04X: ", instruction, instruction2);
488 ARMDisassemble(&info, cpu, core->symbolTable, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
489 } else {
490 sprintf(disassembly, " %04X: ", instruction);
491 ARMDisassemble(&info, cpu, core->symbolTable, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
492 }
493 }
494
495 size_t regStringLen = *length;
496 ARMDebuggerFormatRegisters(&cpu->regs, out, ®StringLen);
497 regStringLen += snprintf(out + regStringLen, *length - regStringLen, " | %s", disassembly);
498 *length = regStringLen;
499 }
500
ARMDebuggerFormatRegisters(struct ARMRegisterFile * regs,char * out,size_t * length)501 static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* out, size_t* length) {
502 *length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X",
503 regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3],
504 regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7],
505 regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11],
506 regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15],
507 regs->cpsr.packed);
508 }
509
ARMDebuggerFrameFormatRegisters(struct mStackFrame * frame,char * out,size_t * length)510 static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length) {
511 ARMDebuggerFormatRegisters(frame->regs, out, length);
512 }
513
ARMDebuggerGetRegister(struct mDebuggerPlatform * d,const char * name,int32_t * value)514 bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
515 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
516 struct ARMCore* cpu = debugger->cpu;
517
518 if (strcmp(name, "sp") == 0) {
519 *value = cpu->gprs[ARM_SP];
520 return true;
521 }
522 if (strcmp(name, "lr") == 0) {
523 *value = cpu->gprs[ARM_LR];
524 return true;
525 }
526 if (strcmp(name, "pc") == 0) {
527 *value = cpu->gprs[ARM_PC];
528 return true;
529 }
530 if (strcmp(name, "cpsr") == 0) {
531 *value = cpu->cpsr.packed;
532 return true;
533 }
534 // TODO: test if mode has SPSR
535 if (strcmp(name, "spsr") == 0) {
536 *value = cpu->spsr.packed;
537 return true;
538 }
539 if (name[0] == 'r') {
540 char* end;
541 uint32_t reg = strtoul(&name[1], &end, 10);
542 if (reg <= ARM_PC) {
543 *value = cpu->gprs[reg];
544 return true;
545 }
546 }
547 return false;
548 }
549
ARMDebuggerSetRegister(struct mDebuggerPlatform * d,const char * name,int32_t value)550 bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
551 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
552 struct ARMCore* cpu = debugger->cpu;
553
554 if (strcmp(name, "sp") == 0) {
555 cpu->gprs[ARM_SP] = value;
556 return true;
557 }
558 if (strcmp(name, "lr") == 0) {
559 cpu->gprs[ARM_LR] = value;
560 return true;
561 }
562 if (strcmp(name, "pc") == 0) {
563 cpu->gprs[ARM_PC] = value;
564 if (cpu->executionMode == MODE_ARM) {
565 ARMWritePC(cpu);
566 } else {
567 ThumbWritePC(cpu);
568 }
569 return true;
570 }
571 if (name[0] == 'r') {
572 char* end;
573 uint32_t reg = strtoul(&name[1], &end, 10);
574 if (reg > ARM_PC) {
575 return false;
576 }
577 cpu->gprs[reg] = value;
578 if (reg == ARM_PC) {
579 if (cpu->executionMode == MODE_ARM) {
580 ARMWritePC(cpu);
581 } else {
582 ThumbWritePC(cpu);
583 }
584 }
585 return true;
586 }
587 return false;
588 }
589
ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform * d)590 static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform* d) {
591 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
592 return debugger->stackTraceMode;
593 }
594
ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform * d,uint32_t mode)595 static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform* d, uint32_t mode) {
596 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
597 struct mStackTrace* stack = &d->p->stackTrace;
598 if (mode == STACK_TRACE_DISABLED && debugger->stackTraceMode != STACK_TRACE_DISABLED) {
599 mStackTraceClear(stack);
600 }
601 debugger->stackTraceMode = mode;
602 }
603
ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform * d)604 static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d) {
605 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
606 int instructionLength = _ARMInstructionLength(debugger->cpu);
607 uint32_t pc = debugger->cpu->gprs[ARM_PC] - instructionLength;
608 if (debugger->stackTraceMode != STACK_TRACE_DISABLED) {
609 return ARMDebuggerUpdateStackTraceInternal(d, pc);
610 } else {
611 return false;
612 }
613 }
614