all repos — mgba @ 11e3bdc58577540dfe888b68c8b4249ed16e025c

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