all repos — mgba @ 0a48d4cc2f58423fa9fe91eaccca08cf080ca0cb

mGBA Game Boy Advance Emulator

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}