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