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