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