all repos — mgba @ 775e417cc6781ceb30520c85c968d198efb87429

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
  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 ARMMemory* memory = stub->d.memoryShim.original;
193	int writeAddress = 0;
194	for (i = 0; i < size; ++i, writeAddress += 2) {
195		uint8_t byte = memory->load8(memory, 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	stub->socket = -1;
424	stub->connection = -1;
425	stub->d.init = 0;
426	stub->d.deinit = _gdbStubDeinit;
427	stub->d.paused = _gdbStubPoll;
428	stub->d.entered = _gdbStubEntered;
429	stub->d.log = 0;
430}
431
432int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) {
433	if (stub->socket >= 0) {
434		GDBStubShutdown(stub);
435	}
436	// TODO: support IPv6
437	stub->socket = SocketOpenTCP(port, bindAddress);
438	if (stub->socket < 0) {
439		if (stub->d.log) {
440			stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket");
441		}
442		return 0;
443	}
444	int err = SocketListen(stub->socket, 1);
445	if (err) {
446		goto cleanup;
447	}
448	if (!SocketSetBlocking(stub->socket, 0)) {
449		goto cleanup;
450	}
451
452	return 1;
453
454cleanup:
455	if (stub->d.log) {
456		stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port");
457	}
458	SocketClose(stub->socket);
459	stub->socket = -1;
460	return 0;
461}
462
463void GDBStubHangup(struct GDBStub* stub) {
464	if (stub->connection >= 0) {
465		SocketClose(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		SocketClose(stub->socket);
477		stub->socket = -1;
478	}
479}
480
481void GDBStubUpdate(struct GDBStub* stub) {
482	if (stub->socket == -1) {
483		return;
484	}
485	if (stub->connection == -1) {
486		stub->connection = SocketAccept(stub->socket, 0, 0);
487		if (stub->connection >= 0) {
488			if (!SocketSetBlocking(stub->connection, 0)) {
489				goto connectionLost;
490			}
491			ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
492		} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
493			return;
494		} else {
495			goto connectionLost;
496		}
497	}
498	while (1) {
499		ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
500		if (messageLen == 0) {
501			goto connectionLost;
502		}
503		if (messageLen == -1) {
504			if (errno == EWOULDBLOCK || errno == EAGAIN) {
505				return;
506			}
507			goto connectionLost;
508		}
509		stub->line[messageLen] = '\0';
510		if (stub->d.log) {
511			stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "< %s", stub->line);
512		}
513		ssize_t position = 0;
514		while (position < messageLen) {
515			position += _parseGDBMessage(stub, &stub->line[position]);
516		}
517	}
518
519connectionLost:
520	if (stub->d.log) {
521		stub->d.log(&stub->d, DEBUGGER_LOG_INFO, "Connection lost");
522	}
523	GDBStubHangup(stub);
524}