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 (void) (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", SIGINT);
181 _sendMessage(stub);
182 // TODO: parse message
183 (void) (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 (void) (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 (void) (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 switch (message[0]) {
283 case '0': // Memory breakpoints are not currently supported
284 case '1': {
285 const char* readAddress = &message[2];
286 unsigned i = 0;
287 uint32_t address = _readHex(readAddress, &i);
288 readAddress += i + 1;
289 uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
290 (void) (kind);
291 ARMDebuggerSetBreakpoint(&stub->d, address);
292 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
293 _sendMessage(stub);
294 break;
295 }
296 case '2':
297 case '3':
298 // TODO: Watchpoints
299 default:
300 break;
301 }
302}
303
304static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
305 switch (message[0]) {
306 case '0': // Memory breakpoints are not currently supported
307 case '1': {
308 const char* readAddress = &message[2];
309 unsigned i = 0;
310 uint32_t address = _readHex(readAddress, &i);
311 ARMDebuggerClearBreakpoint(&stub->d, address);
312 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
313 _sendMessage(stub);
314 break;
315 }
316 case '2':
317 case '3':
318 // TODO: Watchpoints
319 default:
320 break;
321 }
322}
323
324size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
325 uint8_t checksum = 0;
326 int parsed = 1;
327 switch (*message) {
328 case '+':
329 stub->lineAck = GDB_ACK_RECEIVED;
330 return parsed;
331 case '-':
332 stub->lineAck = GDB_NAK_RECEIVED;
333 return parsed;
334 case '$':
335 ++message;
336 break;
337 case '\x03':
338 ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL);
339 return parsed;
340 default:
341 _nak(stub);
342 return parsed;
343 }
344
345 int i;
346 char messageType = message[0];
347 for (i = 0; message[i] && message[i] != '#'; ++i, ++parsed) {
348 checksum += message[i];
349 }
350 if (!message[i]) {
351 _nak(stub);
352 return parsed;
353 }
354 ++i;
355 ++parsed;
356 if (!message[i]) {
357 _nak(stub);
358 return parsed;
359 } else if (!message[i + 1]) {
360 ++parsed;
361 _nak(stub);
362 return parsed;
363 }
364 parsed += 2;
365 int networkChecksum = _hex2int(&message[i], 2);
366 if (networkChecksum != checksum) {
367 if (stub->d.log) {
368 stub->d.log(&stub->d, DEBUGGER_LOG_WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum);
369 }
370 _nak(stub);
371 return parsed;
372 }
373
374 _ack(stub);
375 ++message;
376 switch (messageType) {
377 case '?':
378 snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
379 _sendMessage(stub);
380 break;
381 case 'c':
382 _continue(stub, message);
383 break;
384 case 'g':
385 _readGPRs(stub, message);
386 break;
387 case 'H':
388 // This is faked because we only have one thread
389 strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
390 _sendMessage(stub);
391 break;
392 case 'm':
393 _readMemory(stub, message);
394 break;
395 case 'p':
396 _readRegister(stub, message);
397 break;
398 case 'Q':
399 _processQWriteCommand(stub, message);
400 break;
401 case 'q':
402 _processQReadCommand(stub, message);
403 break;
404 case 's':
405 _step(stub, message);
406 break;
407 case 'V':
408 _processVWriteCommand(stub, message);
409 break;
410 case 'v':
411 _processVReadCommand(stub, message);
412 break;
413 case 'Z':
414 _setBreakpoint(stub, message);
415 break;
416 case 'z':
417 _clearBreakpoint(stub, message);
418 break;
419 default:
420 _error(stub, GDB_UNSUPPORTED_COMMAND);
421 break;
422 }
423 return parsed;
424}
425
426void GDBStubCreate(struct GDBStub* stub) {
427 ARMDebuggerCreate(&stub->d);
428 stub->socket = INVALID_SOCKET;
429 stub->connection = INVALID_SOCKET;
430 stub->d.init = 0;
431 stub->d.deinit = _gdbStubDeinit;
432 stub->d.paused = _gdbStubPoll;
433 stub->d.entered = _gdbStubEntered;
434 stub->d.log = 0;
435}
436
437int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) {
438 if (!SOCKET_FAILED(stub->socket)) {
439 GDBStubShutdown(stub);
440 }
441 // TODO: support IPv6
442 stub->socket = SocketOpenTCP(port, bindAddress);
443 if (SOCKET_FAILED(stub->socket)) {
444 if (stub->d.log) {
445 stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket");
446 }
447 return 0;
448 }
449 int err = SocketListen(stub->socket, 1);
450 if (err) {
451 goto cleanup;
452 }
453 if (!SocketSetBlocking(stub->socket, 0)) {
454 goto cleanup;
455 }
456
457 return 1;
458
459cleanup:
460 if (stub->d.log) {
461 stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port");
462 }
463 SocketClose(stub->socket);
464 stub->socket = -1;
465 return 0;
466}
467
468void GDBStubHangup(struct GDBStub* stub) {
469 if (!SOCKET_FAILED(stub->connection)) {
470 SocketClose(stub->connection);
471 stub->connection = -1;
472 }
473 if (stub->d.state == DEBUGGER_PAUSED) {
474 stub->d.state = DEBUGGER_RUNNING;
475 }
476}
477
478void GDBStubShutdown(struct GDBStub* stub) {
479 GDBStubHangup(stub);
480 if (!SOCKET_FAILED(stub->socket)) {
481 SocketClose(stub->socket);
482 stub->socket = -1;
483 }
484}
485
486void GDBStubUpdate(struct GDBStub* stub) {
487 if (stub->socket == INVALID_SOCKET) {
488 return;
489 }
490 if (stub->connection == INVALID_SOCKET) {
491 stub->connection = SocketAccept(stub->socket, 0, 0);
492 if (!SOCKET_FAILED(stub->connection)) {
493 if (!SocketSetBlocking(stub->connection, 0)) {
494 goto connectionLost;
495 }
496 ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
497 } else if (errno == EWOULDBLOCK || errno == EAGAIN) {
498 return;
499 } else {
500 goto connectionLost;
501 }
502 }
503 while (1) {
504 ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
505 if (messageLen == 0) {
506 goto connectionLost;
507 }
508 if (messageLen == -1) {
509 if (errno == EWOULDBLOCK || errno == EAGAIN) {
510 return;
511 }
512 goto connectionLost;
513 }
514 stub->line[messageLen] = '\0';
515 if (stub->d.log) {
516 stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "< %s", stub->line);
517 }
518 ssize_t position = 0;
519 while (position < messageLen) {
520 position += _parseGDBMessage(stub, &stub->line[position]);
521 }
522 }
523
524connectionLost:
525 if (stub->d.log) {
526 stub->d.log(&stub->d, DEBUGGER_LOG_INFO, "Connection lost");
527 }
528 GDBStubHangup(stub);
529}