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