src/debugger/gdb-stub.c (view raw)
1/* Copyright (c) 2013-2014 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 "gdb-stub.h"
7
8#include "core/core.h"
9#include "gba/memory.h"
10
11#include <signal.h>
12
13#ifndef SIGTRAP
14#define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */
15#endif
16
17#define SOCKET_TIMEOUT 50
18
19enum GDBError {
20 GDB_NO_ERROR = 0x00,
21 GDB_BAD_ARGUMENTS = 0x06,
22 GDB_UNSUPPORTED_COMMAND = 0x07
23};
24
25enum {
26 MACH_O_ARM = 12,
27 MACH_O_ARM_V4T = 5
28};
29
30static void _sendMessage(struct GDBStub* stub);
31
32static void _gdbStubDeinit(struct mDebugger* debugger) {
33 struct GDBStub* stub = (struct GDBStub*) debugger;
34 if (!SOCKET_FAILED(stub->socket)) {
35 GDBStubShutdown(stub);
36 }
37}
38
39static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
40 struct GDBStub* stub = (struct GDBStub*) debugger;
41 switch (reason) {
42 case DEBUGGER_ENTER_MANUAL:
43 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
44 break;
45 case DEBUGGER_ENTER_BREAKPOINT:
46 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP); // TODO: Use hwbreak/swbreak if gdb supports it
47 break;
48 case DEBUGGER_ENTER_WATCHPOINT:
49 if (info) {
50 const char* type = 0;
51 switch (info->watchType) {
52 case WATCHPOINT_WRITE:
53 if (info->newValue == info->oldValue) {
54 if (stub->d.state == DEBUGGER_PAUSED) {
55 stub->d.state = DEBUGGER_RUNNING;
56 }
57 return;
58 }
59 type = "watch";
60 break;
61 case WATCHPOINT_READ:
62 type = "rwatch";
63 break;
64 case WATCHPOINT_RW:
65 type = "awatch";
66 break;
67 }
68 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%s:%08x;", SIGTRAP, type, info->address);
69 } else {
70 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
71 }
72 break;
73 case DEBUGGER_ENTER_ILLEGAL_OP:
74 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGILL);
75 break;
76 case DEBUGGER_ENTER_ATTACHED:
77 return;
78 }
79 _sendMessage(stub);
80}
81
82static void _gdbStubPoll(struct mDebugger* debugger) {
83 struct GDBStub* stub = (struct GDBStub*) debugger;
84 --stub->untilPoll;
85 if (stub->untilPoll > 0) {
86 return;
87 }
88 stub->untilPoll = GDB_STUB_INTERVAL;
89 stub->shouldBlock = false;
90 GDBStubUpdate(stub);
91}
92
93static void _gdbStubWait(struct mDebugger* debugger) {
94 struct GDBStub* stub = (struct GDBStub*) debugger;
95 stub->shouldBlock = true;
96 GDBStubUpdate(stub);
97}
98
99static void _ack(struct GDBStub* stub) {
100 char ack = '+';
101 SocketSend(stub->connection, &ack, 1);
102}
103
104static void _nak(struct GDBStub* stub) {
105 char nak = '-';
106 mLOG(DEBUGGER, WARN, "Packet error");
107 SocketSend(stub->connection, &nak, 1);
108}
109
110static uint32_t _hex2int(const char* hex, int maxDigits) {
111 uint32_t value = 0;
112 uint8_t letter;
113
114 while (maxDigits--) {
115 letter = *hex - '0';
116 if (letter > 9) {
117 letter = *hex - 'a';
118 if (letter > 5) {
119 break;
120 }
121 value *= 0x10;
122 value += letter + 10;
123 } else {
124 value *= 0x10;
125 value += letter;
126 }
127 ++hex;
128 }
129 return value;
130}
131
132static void _int2hex8(uint8_t value, char* out) {
133 static const char language[] = "0123456789abcdef";
134 out[0] = language[value >> 4];
135 out[1] = language[value & 0xF];
136}
137
138static void _int2hex32(uint32_t value, char* out) {
139 static const char language[] = "0123456789abcdef";
140 out[6] = language[value >> 28];
141 out[7] = language[(value >> 24) & 0xF];
142 out[4] = language[(value >> 20) & 0xF];
143 out[5] = language[(value >> 16) & 0xF];
144 out[2] = language[(value >> 12) & 0xF];
145 out[3] = language[(value >> 8) & 0xF];
146 out[0] = language[(value >> 4) & 0xF];
147 out[1] = language[value & 0xF];
148}
149
150static uint32_t _readHex(const char* in, unsigned* out) {
151 unsigned i;
152 for (i = 0; i < 8; ++i) {
153 if (in[i] == ',' || in[i] == ':' || in[i] == '=') {
154 break;
155 }
156 }
157 *out += i;
158 return _hex2int(in, i);
159}
160
161static void _sendMessage(struct GDBStub* stub) {
162 if (stub->lineAck != GDB_ACK_OFF) {
163 stub->lineAck = GDB_ACK_PENDING;
164 }
165 uint8_t checksum = 0;
166 int i = 1;
167 char buffer = stub->outgoing[0];
168 char swap;
169 stub->outgoing[0] = '$';
170 if (buffer) {
171 for (; i < GDB_STUB_MAX_LINE - 5; ++i) {
172 checksum += buffer;
173 swap = stub->outgoing[i];
174 stub->outgoing[i] = buffer;
175 buffer = swap;
176 if (!buffer) {
177 ++i;
178 break;
179 }
180 }
181 }
182 stub->outgoing[i] = '#';
183 _int2hex8(checksum, &stub->outgoing[i + 1]);
184 stub->outgoing[i + 3] = 0;
185 mLOG(DEBUGGER, DEBUG, "> %s", stub->outgoing);
186 SocketSend(stub->connection, stub->outgoing, i + 3);
187}
188
189static void _error(struct GDBStub* stub, enum GDBError error) {
190 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "E%02x", error);
191 _sendMessage(stub);
192}
193
194static void _writeHostInfo(struct GDBStub* stub) {
195 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "cputype:%u;cpusubtype:%u:ostype:none;vendor:none;endian:little;ptrsize:4;", MACH_O_ARM, MACH_O_ARM_V4T);
196 _sendMessage(stub);
197}
198
199static void _continue(struct GDBStub* stub, const char* message) {
200 stub->d.state = DEBUGGER_CUSTOM;
201 stub->untilPoll = GDB_STUB_INTERVAL;
202 // TODO: parse message
203 UNUSED(message);
204}
205
206static void _step(struct GDBStub* stub, const char* message) {
207 stub->d.core->step(stub->d.core);
208 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
209 _sendMessage(stub);
210 // TODO: parse message
211 UNUSED(message);
212}
213
214static void _writeMemoryBinary(struct GDBStub* stub, const char* message) {
215 const char* readAddress = message;
216 unsigned i = 0;
217 uint32_t address = _readHex(readAddress, &i);
218 readAddress += i + 1;
219
220 i = 0;
221 uint32_t size = _readHex(readAddress, &i);
222 readAddress += i + 1;
223
224 if (size > 512) {
225 _error(stub, GDB_BAD_ARGUMENTS);
226 return;
227 }
228
229 struct ARMCore* cpu = stub->d.core->cpu;
230 for (i = 0; i < size; i++) {
231 uint8_t byte = *readAddress;
232 ++readAddress;
233
234 // Parse escape char
235 if (byte == 0x7D) {
236 byte = *readAddress ^ 0x20;
237 ++readAddress;
238 }
239
240 GBAPatch8(cpu, address + i, byte, 0);
241 }
242
243 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
244 _sendMessage(stub);
245}
246
247
248static void _writeMemory(struct GDBStub* stub, const char* message) {
249 const char* readAddress = message;
250 unsigned i = 0;
251 uint32_t address = _readHex(readAddress, &i);
252 readAddress += i + 1;
253
254 i = 0;
255 uint32_t size = _readHex(readAddress, &i);
256 readAddress += i + 1;
257
258 if (size > 512) {
259 _error(stub, GDB_BAD_ARGUMENTS);
260 return;
261 }
262
263 struct ARMCore* cpu = stub->d.core->cpu;
264 for (i = 0; i < size; ++i, readAddress += 2) {
265 uint8_t byte = _hex2int(readAddress, 2);
266 GBAPatch8(cpu, address + i, byte, 0);
267 }
268
269 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
270 _sendMessage(stub);
271}
272
273static void _readMemory(struct GDBStub* stub, const char* message) {
274 const char* readAddress = message;
275 unsigned i = 0;
276 uint32_t address = _readHex(readAddress, &i);
277 readAddress += i + 1;
278 uint32_t size = _readHex(readAddress, &i);
279 if (size > 512) {
280 _error(stub, GDB_BAD_ARGUMENTS);
281 return;
282 }
283 struct ARMCore* cpu = stub->d.core->cpu;
284 int writeAddress = 0;
285 for (i = 0; i < size; ++i, writeAddress += 2) {
286 uint8_t byte = cpu->memory.load8(cpu, address + i, 0);
287 _int2hex8(byte, &stub->outgoing[writeAddress]);
288 }
289 stub->outgoing[writeAddress] = 0;
290 _sendMessage(stub);
291}
292
293static void _writeGPRs(struct GDBStub* stub, const char* message) {
294 struct ARMCore* cpu = stub->d.core->cpu;
295 const char* readAddress = message;
296
297 int r;
298 for (r = 0; r < 16; ++r) {
299 cpu->gprs[r] = _hex2int(readAddress, 8);
300 readAddress += 8;
301 }
302
303 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
304 _sendMessage(stub);
305}
306
307static void _readGPRs(struct GDBStub* stub, const char* message) {
308 struct ARMCore* cpu = stub->d.core->cpu;
309 UNUSED(message);
310 int r;
311 int i = 0;
312 for (r = 0; r < 16; ++r) {
313 _int2hex32(cpu->gprs[r], &stub->outgoing[i]);
314 i += 8;
315 }
316 stub->outgoing[i] = 0;
317 _sendMessage(stub);
318}
319
320static void _writeRegister(struct GDBStub* stub, const char* message) {
321 struct ARMCore* cpu = stub->d.core->cpu;
322 const char* readAddress = message;
323
324 unsigned i = 0;
325 uint32_t reg = _readHex(readAddress, &i);
326 readAddress += i + 1;
327
328 uint32_t value = _readHex(readAddress, &i);
329
330#ifdef _MSC_VER
331 value = _byteswap_ulong(value);
332#else
333 value = __builtin_bswap32(value);
334#endif
335
336 if (reg < 0x10) {
337 cpu->gprs[reg] = value;
338 } else if (reg == 0x19) {
339 cpu->cpsr.packed = value;
340 } else {
341 stub->outgoing[0] = '\0';
342 _sendMessage(stub);
343 return;
344 }
345
346 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
347 _sendMessage(stub);
348}
349
350static void _readRegister(struct GDBStub* stub, const char* message) {
351 struct ARMCore* cpu = stub->d.core->cpu;
352 const char* readAddress = message;
353 unsigned i = 0;
354 uint32_t reg = _readHex(readAddress, &i);
355 uint32_t value;
356 if (reg < 0x10) {
357 value = cpu->gprs[reg];
358 } else if (reg == 0x19) {
359 value = cpu->cpsr.packed;
360 } else {
361 stub->outgoing[0] = '\0';
362 _sendMessage(stub);
363 return;
364 }
365 _int2hex32(value, stub->outgoing);
366 stub->outgoing[8] = '\0';
367 _sendMessage(stub);
368}
369
370static void _processQReadCommand(struct GDBStub* stub, const char* message) {
371 stub->outgoing[0] = '\0';
372 if (!strncmp("HostInfo#", message, 9)) {
373 _writeHostInfo(stub);
374 return;
375 }
376 if (!strncmp("Attached#", message, 9)) {
377 strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
378 } else if (!strncmp("VAttachOrWaitSupported#", message, 23)) {
379 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
380 } else if (!strncmp("C#", message, 2)) {
381 strncpy(stub->outgoing, "QC1", GDB_STUB_MAX_LINE - 4);
382 } else if (!strncmp("fThreadInfo#", message, 12)) {
383 strncpy(stub->outgoing, "m1", GDB_STUB_MAX_LINE - 4);
384 } else if (!strncmp("sThreadInfo#", message, 12)) {
385 strncpy(stub->outgoing, "l", GDB_STUB_MAX_LINE - 4);
386 }
387 _sendMessage(stub);
388}
389
390static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
391 stub->outgoing[0] = '\0';
392 if (!strncmp("StartNoAckMode#", message, 16)) {
393 stub->lineAck = GDB_ACK_OFF;
394 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
395 }
396 _sendMessage(stub);
397}
398
399static void _processVWriteCommand(struct GDBStub* stub, const char* message) {
400 UNUSED(message);
401 stub->outgoing[0] = '\0';
402 _sendMessage(stub);
403}
404
405static void _processVReadCommand(struct GDBStub* stub, const char* message) {
406 stub->outgoing[0] = '\0';
407 if (!strncmp("Attach", message, 6)) {
408 strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
409 mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
410 }
411 _sendMessage(stub);
412}
413
414static void _setBreakpoint(struct GDBStub* stub, const char* message) {
415 const char* readAddress = &message[2];
416 unsigned i = 0;
417 uint32_t address = _readHex(readAddress, &i);
418 readAddress += i + 1;
419 uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
420 UNUSED(kind);
421
422 switch (message[0]) {
423 case '0': // Memory breakpoints are not currently supported
424 case '1':
425 stub->d.platform->setBreakpoint(stub->d.platform, address);
426 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
427 _sendMessage(stub);
428 break;
429 case '2':
430 stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_WRITE);
431 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
432 _sendMessage(stub);
433 break;
434 case '3':
435 stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_READ);
436 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
437 _sendMessage(stub);
438 break;
439 case '4':
440 stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_RW);
441 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
442 _sendMessage(stub);
443 break;
444 default:
445 stub->outgoing[0] = '\0';
446 _sendMessage(stub);
447 break;
448 }
449}
450
451static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
452 const char* readAddress = &message[2];
453 unsigned i = 0;
454 uint32_t address = _readHex(readAddress, &i);
455 switch (message[0]) {
456 case '0': // Memory breakpoints are not currently supported
457 case '1':
458 stub->d.platform->clearBreakpoint(stub->d.platform, address);
459 break;
460 case '2':
461 case '3':
462 case '4':
463 stub->d.platform->clearWatchpoint(stub->d.platform, address);
464 break;
465 default:
466 break;
467 }
468 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
469 _sendMessage(stub);
470}
471
472size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
473 uint8_t checksum = 0;
474 int parsed = 1;
475 switch (*message) {
476 case '+':
477 stub->lineAck = GDB_ACK_RECEIVED;
478 return parsed;
479 case '-':
480 stub->lineAck = GDB_NAK_RECEIVED;
481 return parsed;
482 case '$':
483 ++message;
484 break;
485 case '\x03':
486 mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
487 return parsed;
488 default:
489 _nak(stub);
490 return parsed;
491 }
492
493 int i;
494 char messageType = message[0];
495 for (i = 0; message[i] != '#'; ++i, ++parsed) {
496 checksum += message[i];
497 }
498 if (!message[i]) {
499 _nak(stub);
500 return parsed;
501 }
502 ++i;
503 ++parsed;
504 if (!message[i]) {
505 _nak(stub);
506 return parsed;
507 } else if (!message[i + 1]) {
508 ++parsed;
509 _nak(stub);
510 return parsed;
511 }
512 parsed += 2;
513 int networkChecksum = _hex2int(&message[i], 2);
514 if (networkChecksum != checksum) {
515 mLOG(DEBUGGER, WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum);
516 _nak(stub);
517 return parsed;
518 }
519
520 _ack(stub);
521 ++message;
522 switch (messageType) {
523 case '?':
524 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
525 _sendMessage(stub);
526 break;
527 case 'c':
528 _continue(stub, message);
529 break;
530 case 'G':
531 _writeGPRs(stub, message);
532 break;
533 case 'g':
534 _readGPRs(stub, message);
535 break;
536 case 'H':
537 // This is faked because we only have one thread
538 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
539 _sendMessage(stub);
540 break;
541 case 'M':
542 _writeMemory(stub, message);
543 break;
544 case 'm':
545 _readMemory(stub, message);
546 break;
547 case 'P':
548 _writeRegister(stub, message);
549 break;
550 case 'p':
551 _readRegister(stub, message);
552 break;
553 case 'Q':
554 _processQWriteCommand(stub, message);
555 break;
556 case 'q':
557 _processQReadCommand(stub, message);
558 break;
559 case 's':
560 _step(stub, message);
561 break;
562 case 'V':
563 _processVWriteCommand(stub, message);
564 break;
565 case 'v':
566 _processVReadCommand(stub, message);
567 break;
568 case 'X':
569 _writeMemoryBinary(stub, message);
570 break;
571 case 'Z':
572 _setBreakpoint(stub, message);
573 break;
574 case 'z':
575 _clearBreakpoint(stub, message);
576 break;
577 default:
578 _error(stub, GDB_UNSUPPORTED_COMMAND);
579 break;
580 }
581 return parsed;
582}
583
584void GDBStubCreate(struct GDBStub* stub) {
585 stub->socket = INVALID_SOCKET;
586 stub->connection = INVALID_SOCKET;
587 stub->d.init = 0;
588 stub->d.deinit = _gdbStubDeinit;
589 stub->d.paused = _gdbStubWait;
590 stub->d.entered = _gdbStubEntered;
591 stub->d.custom = _gdbStubPoll;
592 stub->untilPoll = GDB_STUB_INTERVAL;
593 stub->lineAck = GDB_ACK_PENDING;
594 stub->shouldBlock = false;
595}
596
597bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) {
598 if (!SOCKET_FAILED(stub->socket)) {
599 GDBStubShutdown(stub);
600 }
601 stub->socket = SocketOpenTCP(port, bindAddress);
602 if (SOCKET_FAILED(stub->socket)) {
603 mLOG(DEBUGGER, ERROR, "Couldn't open socket");
604 return false;
605 }
606 if (!SocketSetBlocking(stub->socket, false)) {
607 goto cleanup;
608 }
609 int err = SocketListen(stub->socket, 1);
610 if (err) {
611 goto cleanup;
612 }
613
614 return true;
615
616cleanup:
617 mLOG(DEBUGGER, ERROR, "Couldn't listen on port");
618 SocketClose(stub->socket);
619 stub->socket = INVALID_SOCKET;
620 return false;
621}
622
623void GDBStubHangup(struct GDBStub* stub) {
624 if (!SOCKET_FAILED(stub->connection)) {
625 SocketClose(stub->connection);
626 stub->connection = INVALID_SOCKET;
627 }
628 if (stub->d.state == DEBUGGER_PAUSED) {
629 stub->d.state = DEBUGGER_RUNNING;
630 }
631}
632
633void GDBStubShutdown(struct GDBStub* stub) {
634 GDBStubHangup(stub);
635 if (!SOCKET_FAILED(stub->socket)) {
636 SocketClose(stub->socket);
637 stub->socket = INVALID_SOCKET;
638 }
639}
640
641void GDBStubUpdate(struct GDBStub* stub) {
642 if (stub->socket == INVALID_SOCKET) {
643 if (stub->d.state == DEBUGGER_PAUSED) {
644 stub->d.state = DEBUGGER_RUNNING;
645 }
646 return;
647 }
648 if (stub->connection == INVALID_SOCKET) {
649 if (stub->shouldBlock) {
650 Socket reads = stub->socket;
651 SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
652 }
653 stub->connection = SocketAccept(stub->socket, 0);
654 if (!SOCKET_FAILED(stub->connection)) {
655 if (!SocketSetBlocking(stub->connection, false)) {
656 goto connectionLost;
657 }
658 mDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED, 0);
659 } else if (SocketWouldBlock()) {
660 return;
661 } else {
662 goto connectionLost;
663 }
664 }
665 while (true) {
666 if (stub->shouldBlock) {
667 Socket reads = stub->connection;
668 SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
669 }
670 ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
671 if (messageLen == 0) {
672 goto connectionLost;
673 }
674 if (messageLen == -1) {
675 if (SocketWouldBlock()) {
676 return;
677 }
678 goto connectionLost;
679 }
680 stub->line[messageLen] = '\0';
681 mLOG(DEBUGGER, DEBUG, "< %s", stub->line);
682 ssize_t position = 0;
683 while (position < messageLen) {
684 position += _parseGDBMessage(stub, &stub->line[position]);
685 }
686 }
687
688connectionLost:
689 mLOG(DEBUGGER, WARN, "Connection lost");
690 GDBStubHangup(stub);
691}