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