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 LOAD_32BE(value, 0, &value);
366
367 if (reg <= ARM_PC) {
368 cpu->gprs[reg] = value;
369 if (reg == ARM_PC) {
370 if (cpu->executionMode == MODE_ARM) {
371 ARMWritePC(cpu);
372 } else {
373 ThumbWritePC(cpu);
374 }
375 }
376 } else if (reg == 0x19) {
377 cpu->cpsr.packed = value;
378 } else {
379 stub->outgoing[0] = '\0';
380 _sendMessage(stub);
381 return;
382 }
383
384 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
385 _sendMessage(stub);
386}
387
388static void _readRegister(struct GDBStub* stub, const char* message) {
389 struct ARMCore* cpu = stub->d.core->cpu;
390 const char* readAddress = message;
391 unsigned i = 0;
392 uint32_t reg = _readHex(readAddress, &i);
393 uint32_t value;
394 if (reg < 0x10) {
395 value = cpu->gprs[reg];
396 } else if (reg == 0x19) {
397 value = cpu->cpsr.packed;
398 } else {
399 stub->outgoing[0] = '\0';
400 _sendMessage(stub);
401 return;
402 }
403 _int2hex32(value, stub->outgoing);
404 stub->outgoing[8] = '\0';
405 _sendMessage(stub);
406}
407
408static void _processQSupportedCommand(struct GDBStub* stub, const char* message) {
409 const char* terminator = strrchr(message, '#');
410 stub->supportsSwbreak = false;
411 stub->supportsHwbreak = false;
412 while (message < terminator) {
413 const char* end = strchr(message, ';');
414 size_t len;
415 if (end && end < terminator) {
416 len = end - message;
417 } else {
418 len = terminator - message;
419 }
420 if (!strncmp(message, "swbreak+", len)) {
421 stub->supportsSwbreak = true;
422 } else if (!strncmp(message, "hwbreak+", len)) {
423 stub->supportsHwbreak = true;
424 } else if (!strncmp(message, "swbreak-", len)) {
425 stub->supportsSwbreak = false;
426 } else if (!strncmp(message, "hwbreak-", len)) {
427 stub->supportsHwbreak = false;
428 }
429 if (!end) {
430 break;
431 }
432 message = end + 1;
433 }
434 strncpy(stub->outgoing, "swbreak+;hwbreak+", GDB_STUB_MAX_LINE - 4);
435}
436
437static void _processQReadCommand(struct GDBStub* stub, const char* message) {
438 stub->outgoing[0] = '\0';
439 if (!strncmp("HostInfo#", message, 9)) {
440 _writeHostInfo(stub);
441 return;
442 }
443 if (!strncmp("Attached#", message, 9)) {
444 strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
445 } else if (!strncmp("VAttachOrWaitSupported#", message, 23)) {
446 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
447 } else if (!strncmp("C#", message, 2)) {
448 strncpy(stub->outgoing, "QC1", GDB_STUB_MAX_LINE - 4);
449 } else if (!strncmp("fThreadInfo#", message, 12)) {
450 strncpy(stub->outgoing, "m1", GDB_STUB_MAX_LINE - 4);
451 } else if (!strncmp("sThreadInfo#", message, 12)) {
452 strncpy(stub->outgoing, "l", GDB_STUB_MAX_LINE - 4);
453 } else if (!strncmp("Supported:", message, 10)) {
454 _processQSupportedCommand(stub, message + 10);
455 }
456 _sendMessage(stub);
457}
458
459static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
460 stub->outgoing[0] = '\0';
461 if (!strncmp("StartNoAckMode#", message, 16)) {
462 stub->lineAck = GDB_ACK_OFF;
463 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
464 }
465 _sendMessage(stub);
466}
467
468static void _processVWriteCommand(struct GDBStub* stub, const char* message) {
469 UNUSED(message);
470 stub->outgoing[0] = '\0';
471 _sendMessage(stub);
472}
473
474static void _processVReadCommand(struct GDBStub* stub, const char* message) {
475 stub->outgoing[0] = '\0';
476 if (!strncmp("Attach", message, 6)) {
477 strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
478 mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
479 }
480 _sendMessage(stub);
481}
482
483static void _setBreakpoint(struct GDBStub* stub, const char* message) {
484 const char* readAddress = &message[2];
485 unsigned i = 0;
486 uint32_t address = _readHex(readAddress, &i);
487 readAddress += i + 1;
488 uint32_t kind = _readHex(readAddress, &i);
489
490 struct mBreakpoint breakpoint = {
491 .address = address,
492 .type = BREAKPOINT_HARDWARE
493 };
494 struct mWatchpoint watchpoint = {
495 .address = address
496 };
497
498 switch (message[0]) {
499 case '0':
500 ARMDebuggerSetSoftwareBreakpoint(stub->d.platform, address, kind == 2 ? MODE_THUMB : MODE_ARM);
501 break;
502 case '1':
503 stub->d.platform->setBreakpoint(stub->d.platform, &breakpoint);
504 break;
505 case '2':
506 watchpoint.type = WATCHPOINT_WRITE_CHANGE;
507 stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
508 break;
509 case '3':
510 watchpoint.type = WATCHPOINT_READ;
511 stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
512 break;
513 case '4':
514 watchpoint.type = WATCHPOINT_RW;
515 stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
516 break;
517 default:
518 stub->outgoing[0] = '\0';
519 _sendMessage(stub);
520 return;
521 }
522 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
523 _sendMessage(stub);
524}
525
526static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
527 const char* readAddress = &message[2];
528 unsigned i = 0;
529 uint32_t address = _readHex(readAddress, &i);
530 struct mBreakpointList breakpoints;
531 struct mWatchpointList watchpoints;
532
533 size_t index;
534 switch (message[0]) {
535 case '0':
536 case '1':
537 mBreakpointListInit(&breakpoints, 0);
538 stub->d.platform->listBreakpoints(stub->d.platform, &breakpoints);
539 for (index = 0; index < mBreakpointListSize(&breakpoints); ++index) {
540 if (mBreakpointListGetPointer(&breakpoints, index)->address != address) {
541 continue;
542 }
543 stub->d.platform->clearBreakpoint(stub->d.platform, mBreakpointListGetPointer(&breakpoints, index)->id);
544 }
545 mBreakpointListDeinit(&breakpoints);
546 break;
547 case '2':
548 case '3':
549 case '4':
550 mWatchpointListInit(&watchpoints, 0);
551 stub->d.platform->listWatchpoints(stub->d.platform, &watchpoints);
552 for (index = 0; index < mWatchpointListSize(&watchpoints); ++index) {
553 if (mWatchpointListGetPointer(&watchpoints, index)->address != address) {
554 continue;
555 }
556 stub->d.platform->clearBreakpoint(stub->d.platform, mWatchpointListGetPointer(&watchpoints, index)->id);
557 }
558 mWatchpointListDeinit(&watchpoints);
559 break;
560 default:
561 break;
562 }
563 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
564 _sendMessage(stub);
565}
566
567size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
568 uint8_t checksum = 0;
569 int parsed = 1;
570 switch (*message) {
571 case '+':
572 stub->lineAck = GDB_ACK_RECEIVED;
573 return parsed;
574 case '-':
575 stub->lineAck = GDB_NAK_RECEIVED;
576 return parsed;
577 case '$':
578 ++message;
579 break;
580 case '\x03':
581 mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
582 return parsed;
583 default:
584 _nak(stub);
585 return parsed;
586 }
587
588 int i;
589 char messageType = message[0];
590 for (i = 0; message[i] != '#'; ++i, ++parsed) {
591 checksum += message[i];
592 }
593 if (!message[i]) {
594 _nak(stub);
595 return parsed;
596 }
597 ++i;
598 ++parsed;
599 if (!message[i]) {
600 _nak(stub);
601 return parsed;
602 } else if (!message[i + 1]) {
603 ++parsed;
604 _nak(stub);
605 return parsed;
606 }
607 parsed += 2;
608 int networkChecksum = _hex2int(&message[i], 2);
609 if (networkChecksum != checksum) {
610 mLOG(DEBUGGER, WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum);
611 _nak(stub);
612 return parsed;
613 }
614
615 _ack(stub);
616 ++message;
617 switch (messageType) {
618 case '?':
619 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
620 _sendMessage(stub);
621 break;
622 case 'c':
623 _continue(stub, message);
624 break;
625 case 'G':
626 _writeGPRs(stub, message);
627 break;
628 case 'g':
629 _readGPRs(stub, message);
630 break;
631 case 'H':
632 // This is faked because we only have one thread
633 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
634 _sendMessage(stub);
635 break;
636 case 'M':
637 _writeMemory(stub, message);
638 break;
639 case 'm':
640 _readMemory(stub, message);
641 break;
642 case 'P':
643 _writeRegister(stub, message);
644 break;
645 case 'p':
646 _readRegister(stub, message);
647 break;
648 case 'Q':
649 _processQWriteCommand(stub, message);
650 break;
651 case 'q':
652 _processQReadCommand(stub, message);
653 break;
654 case 's':
655 _step(stub, message);
656 break;
657 case 'V':
658 _processVWriteCommand(stub, message);
659 break;
660 case 'v':
661 _processVReadCommand(stub, message);
662 break;
663 case 'X':
664 _writeMemoryBinary(stub, message);
665 break;
666 case 'Z':
667 _setBreakpoint(stub, message);
668 break;
669 case 'z':
670 _clearBreakpoint(stub, message);
671 break;
672 default:
673 _error(stub, GDB_UNSUPPORTED_COMMAND);
674 break;
675 }
676 return parsed;
677}
678
679void GDBStubCreate(struct GDBStub* stub) {
680 stub->socket = INVALID_SOCKET;
681 stub->connection = INVALID_SOCKET;
682 stub->d.init = 0;
683 stub->d.deinit = _gdbStubDeinit;
684 stub->d.paused = _gdbStubWait;
685 stub->d.entered = _gdbStubEntered;
686 stub->d.custom = _gdbStubPoll;
687 stub->d.type = DEBUGGER_GDB;
688 stub->untilPoll = GDB_STUB_INTERVAL;
689 stub->lineAck = GDB_ACK_PENDING;
690 stub->shouldBlock = false;
691}
692
693bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) {
694 if (!SOCKET_FAILED(stub->socket)) {
695 GDBStubShutdown(stub);
696 }
697 stub->socket = SocketOpenTCP(port, bindAddress);
698 if (SOCKET_FAILED(stub->socket)) {
699 mLOG(DEBUGGER, ERROR, "Couldn't open socket");
700 return false;
701 }
702 if (!SocketSetBlocking(stub->socket, false)) {
703 goto cleanup;
704 }
705 int err = SocketListen(stub->socket, 1);
706 if (err) {
707 goto cleanup;
708 }
709
710 return true;
711
712cleanup:
713 mLOG(DEBUGGER, ERROR, "Couldn't listen on port");
714 SocketClose(stub->socket);
715 stub->socket = INVALID_SOCKET;
716 return false;
717}
718
719void GDBStubHangup(struct GDBStub* stub) {
720 if (!SOCKET_FAILED(stub->connection)) {
721 SocketClose(stub->connection);
722 stub->connection = INVALID_SOCKET;
723 }
724 if (stub->d.state == DEBUGGER_PAUSED) {
725 stub->d.state = DEBUGGER_RUNNING;
726 }
727}
728
729void GDBStubShutdown(struct GDBStub* stub) {
730 GDBStubHangup(stub);
731 if (!SOCKET_FAILED(stub->socket)) {
732 SocketClose(stub->socket);
733 stub->socket = INVALID_SOCKET;
734 }
735}
736
737void GDBStubUpdate(struct GDBStub* stub) {
738 if (stub->socket == INVALID_SOCKET) {
739 if (stub->d.state == DEBUGGER_PAUSED) {
740 stub->d.state = DEBUGGER_RUNNING;
741 }
742 return;
743 }
744 if (stub->connection == INVALID_SOCKET) {
745 if (stub->shouldBlock) {
746 Socket reads = stub->socket;
747 SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
748 }
749 stub->connection = SocketAccept(stub->socket, 0);
750 if (!SOCKET_FAILED(stub->connection)) {
751 if (!SocketSetBlocking(stub->connection, false)) {
752 goto connectionLost;
753 }
754 mDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED, 0);
755 } else if (SocketWouldBlock()) {
756 return;
757 } else {
758 goto connectionLost;
759 }
760 }
761 while (true) {
762 if (stub->shouldBlock) {
763 Socket reads = stub->connection;
764 SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
765 }
766 ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
767 if (messageLen == 0) {
768 goto connectionLost;
769 }
770 if (messageLen == -1) {
771 if (SocketWouldBlock()) {
772 return;
773 }
774 goto connectionLost;
775 }
776 stub->line[messageLen] = '\0';
777 mLOG(DEBUGGER, DEBUG, "< %s", stub->line);
778 ssize_t position = 0;
779 while (position < messageLen) {
780 position += _parseGDBMessage(stub, &stub->line[position]);
781 }
782 }
783
784connectionLost:
785 mLOG(DEBUGGER, WARN, "Connection lost");
786 GDBStubHangup(stub);
787}