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