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, &regStringLen);
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