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