all repos — mgba @ 325380989cec960668ba43d008f710c82e1cac11

mGBA Game Boy Advance Emulator

src/debugger/gdb-stub.c (view raw)

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