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