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