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