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