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