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