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