src/arm/debugger/debugger.c (view raw)
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/isa-inlines.h>
12#include <mgba/internal/arm/debugger/memory-debugger.h>
13#include <mgba/internal/debugger/parser.h>
14
15DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
16DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
17
18static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
19 size_t i;
20 for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
21 if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
22 return ARMDebugBreakpointListGetPointer(breakpoints, i);
23 }
24 }
25 return 0;
26}
27
28static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
29 if (breakpoint->condition) {
30 parseFree(breakpoint->condition);
31 free(breakpoint->condition);
32 }
33}
34
35static void _destroyWatchpoint(struct ARMDebugWatchpoint* watchpoint) {
36 if (watchpoint->condition) {
37 parseFree(watchpoint->condition);
38 free(watchpoint->condition);
39 }
40}
41
42static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
43 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
44 int instructionLength;
45 enum ExecutionMode mode = debugger->cpu->cpsr.t;
46 if (mode == MODE_ARM) {
47 instructionLength = WORD_SIZE_ARM;
48 } else {
49 instructionLength = WORD_SIZE_THUMB;
50 }
51 struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
52 if (!breakpoint) {
53 return;
54 }
55 if (breakpoint->condition) {
56 int32_t value;
57 int segment;
58 if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) {
59 return;
60 }
61 }
62 struct mDebuggerEntryInfo info = {
63 .address = breakpoint->address,
64 .type.bp.breakType = BREAKPOINT_HARDWARE
65 };
66 mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
67}
68
69static void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform);
70static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
71
72static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
73
74static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
75static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
76static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
77static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
78static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition);
79static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
80static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
81static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
82static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
83static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
84static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
85
86struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
87 struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
88 platform->entered = ARMDebuggerEnter;
89 platform->init = ARMDebuggerInit;
90 platform->deinit = ARMDebuggerDeinit;
91 platform->setBreakpoint = ARMDebuggerSetBreakpoint;
92 platform->setConditionalBreakpoint = ARMDebuggerSetConditionalBreakpoint;
93 platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
94 platform->setWatchpoint = ARMDebuggerSetWatchpoint;
95 platform->setConditionalWatchpoint = ARMDebuggerSetConditionalWatchpoint;
96 platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
97 platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
98 platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
99 platform->trace = ARMDebuggerTrace;
100 platform->getRegister = ARMDebuggerGetRegister;
101 platform->setRegister = ARMDebuggerSetRegister;
102 return platform;
103}
104
105void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
106 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
107 debugger->cpu = cpu;
108 debugger->originalMemory = debugger->cpu->memory;
109 ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
110 ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
111 ARMDebugWatchpointListInit(&debugger->watchpoints, 0);
112}
113
114void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
115 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
116 if (debugger->clearSoftwareBreakpoint) {
117 // Clear the stack backwards in case any overlap
118 size_t b;
119 for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
120 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
121 debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
122 }
123 }
124 ARMDebuggerRemoveMemoryShim(debugger);
125
126 size_t i;
127 for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
128 _destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
129 }
130 ARMDebugBreakpointListDeinit(&debugger->breakpoints);
131
132 for (i = 0; i < ARMDebugWatchpointListSize(&debugger->watchpoints); ++i) {
133 _destroyWatchpoint(ARMDebugWatchpointListGetPointer(&debugger->watchpoints, i));
134 }
135 ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
136 ARMDebugWatchpointListDeinit(&debugger->watchpoints);
137}
138
139static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
140 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
141 struct ARMCore* cpu = debugger->cpu;
142 cpu->nextEvent = cpu->cycles;
143 if (reason == DEBUGGER_ENTER_BREAKPOINT) {
144 struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
145 if (breakpoint && breakpoint->isSw) {
146 info->address = breakpoint->address;
147 if (debugger->clearSoftwareBreakpoint) {
148 debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
149 }
150
151 ARMRunFake(cpu, breakpoint->sw.opcode);
152
153 if (debugger->setSoftwareBreakpoint) {
154 debugger->setSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, &breakpoint->sw.opcode);
155 }
156 }
157 }
158 if (debugger->d.p->entered) {
159 debugger->d.p->entered(debugger->d.p, reason, info);
160 }
161}
162
163bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
164 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
165 uint32_t opcode;
166 if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
167 return false;
168 }
169
170 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
171 breakpoint->address = address;
172 breakpoint->isSw = true;
173 breakpoint->sw.opcode = opcode;
174 breakpoint->sw.mode = mode;
175
176 return true;
177}
178
179void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
180 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
181 if (!debugger->clearSoftwareBreakpoint) {
182 return;
183 }
184
185 struct ARMDebugBreakpoint* breakpoint = NULL;
186 // Clear the stack backwards in case any overlap
187 size_t b;
188 for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
189 breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
190 if (breakpoint->address == address) {
191 break;
192 }
193 breakpoint = NULL;
194 }
195
196 if (breakpoint) {
197 debugger->clearSoftwareBreakpoint(debugger, address, breakpoint->sw.mode, breakpoint->sw.opcode);
198 }
199}
200
201static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
202 ARMDebuggerSetConditionalBreakpoint(d, address, segment, NULL);
203}
204
205static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
206 UNUSED(segment);
207 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
208 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
209 breakpoint->condition = condition;
210 breakpoint->address = address;
211 breakpoint->isSw = false;
212}
213
214static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
215 UNUSED(segment);
216 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
217 struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
218 size_t i;
219 for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
220 if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
221 _destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
222 ARMDebugBreakpointListShift(breakpoints, i, 1);
223 }
224 }
225}
226
227static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
228 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
229 return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints);
230}
231
232static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
233 ARMDebuggerSetConditionalWatchpoint(d, address, segment, type, NULL);
234}
235
236static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) {
237 UNUSED(segment);
238 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
239 if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
240 ARMDebuggerInstallMemoryShim(debugger);
241 }
242 struct ARMDebugWatchpoint* watchpoint = ARMDebugWatchpointListAppend(&debugger->watchpoints);
243 watchpoint->address = address;
244 watchpoint->type = type;
245 watchpoint->condition = condition;
246}
247
248static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
249 UNUSED(segment);
250 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
251 struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints;
252 size_t i;
253 for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) {
254 if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) {
255 _destroyWatchpoint(ARMDebugWatchpointListGetPointer(watchpoints, i));
256 ARMDebugWatchpointListShift(watchpoints, i, 1);
257 }
258 }
259 if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
260 ARMDebuggerRemoveMemoryShim(debugger);
261 }
262}
263
264static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
265 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
266 struct ARMCore* cpu = debugger->cpu;
267
268 char disassembly[64];
269
270 struct ARMInstructionInfo info;
271 if (cpu->executionMode == MODE_ARM) {
272 uint32_t instruction = cpu->prefetch[0];
273 sprintf(disassembly, "%08X: ", instruction);
274 ARMDecodeARM(instruction, &info);
275 ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
276 } else {
277 struct ARMInstructionInfo info2;
278 struct ARMInstructionInfo combined;
279 uint16_t instruction = cpu->prefetch[0];
280 uint16_t instruction2 = cpu->prefetch[1];
281 ARMDecodeThumb(instruction, &info);
282 ARMDecodeThumb(instruction2, &info2);
283 if (ARMDecodeThumbCombine(&info, &info2, &combined)) {
284 sprintf(disassembly, "%04X%04X: ", instruction, instruction2);
285 ARMDisassemble(&combined, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
286 } else {
287 sprintf(disassembly, " %04X: ", instruction);
288 ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
289 }
290 }
291
292 *length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X | %s",
293 cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3],
294 cpu->gprs[4], cpu->gprs[5], cpu->gprs[6], cpu->gprs[7],
295 cpu->gprs[8], cpu->gprs[9], cpu->gprs[10], cpu->gprs[11],
296 cpu->gprs[12], cpu->gprs[13], cpu->gprs[14], cpu->gprs[15],
297 cpu->cpsr.packed, disassembly);
298}
299
300bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
301 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
302 struct ARMCore* cpu = debugger->cpu;
303
304 if (strcmp(name, "sp") == 0) {
305 *value = cpu->gprs[ARM_SP];
306 return true;
307 }
308 if (strcmp(name, "lr") == 0) {
309 *value = cpu->gprs[ARM_LR];
310 return true;
311 }
312 if (strcmp(name, "pc") == 0) {
313 *value = cpu->gprs[ARM_PC];
314 return true;
315 }
316 if (strcmp(name, "cpsr") == 0) {
317 *value = cpu->cpsr.packed;
318 return true;
319 }
320 // TODO: test if mode has SPSR
321 if (strcmp(name, "spsr") == 0) {
322 *value = cpu->spsr.packed;
323 return true;
324 }
325 if (name[0] == 'r') {
326 char* end;
327 uint32_t reg = strtoul(&name[1], &end, 10);
328 if (reg <= ARM_PC) {
329 *value = cpu->gprs[reg];
330 return true;
331 }
332 }
333 return false;
334}
335
336bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
337 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
338 struct ARMCore* cpu = debugger->cpu;
339
340 if (strcmp(name, "sp") == 0) {
341 cpu->gprs[ARM_SP] = value;
342 return true;
343 }
344 if (strcmp(name, "lr") == 0) {
345 cpu->gprs[ARM_LR] = value;
346 return true;
347 }
348 if (strcmp(name, "pc") == 0) {
349 cpu->gprs[ARM_PC] = value;
350 int32_t currentCycles = 0;
351 if (cpu->executionMode == MODE_ARM) {
352 ARM_WRITE_PC;
353 } else {
354 THUMB_WRITE_PC;
355 }
356 return true;
357 }
358 if (name[0] == 'r') {
359 char* end;
360 uint32_t reg = strtoul(&name[1], &end, 10);
361 if (reg > ARM_PC) {
362 return false;
363 }
364 cpu->gprs[reg] = value;
365 if (reg == ARM_PC) {
366 int32_t currentCycles = 0;
367 if (cpu->executionMode == MODE_ARM) {
368 ARM_WRITE_PC;
369 } else {
370 THUMB_WRITE_PC;
371 }
372 }
373 return true;
374 }
375 return false;
376}