all repos — mgba @ c5d243fca282cf11d7f2c38010fce18361babdc2

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