all repos — mgba @ 21437e91f6d73c58f7980a12f1bc381be4f9471b

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	UNUSED(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", SIGTRAP);
181	_sendMessage(stub);
182	// TODO: parse message
183	UNUSED(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	UNUSED(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	UNUSED(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		UNUSED(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		stub->outgoing[0] = '\0';
301		_sendMessage(stub);
302		break;
303	}
304}
305
306static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
307	switch (message[0]) {
308	case '0': // Memory breakpoints are not currently supported
309	case '1': {
310		const char* readAddress = &message[2];
311		unsigned i = 0;
312		uint32_t address = _readHex(readAddress, &i);
313		ARMDebuggerClearBreakpoint(&stub->d, address);
314		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
315		_sendMessage(stub);
316		break;
317	}
318	case '2':
319	case '3':
320		// TODO: Watchpoints
321	default:
322		break;
323	}
324}
325
326size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
327	uint8_t checksum = 0;
328	int parsed = 1;
329	switch (*message) {
330	case '+':
331		stub->lineAck = GDB_ACK_RECEIVED;
332		return parsed;
333	case '-':
334		stub->lineAck = GDB_NAK_RECEIVED;
335		return parsed;
336	case '$':
337		++message;
338		break;
339	case '\x03':
340		ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL);
341		return parsed;
342	default:
343		_nak(stub);
344		return parsed;
345	}
346
347	int i;
348	char messageType = message[0];
349	for (i = 0; message[i] && message[i] != '#'; ++i, ++parsed) {
350		checksum += message[i];
351	}
352	if (!message[i]) {
353		_nak(stub);
354		return parsed;
355	}
356	++i;
357	++parsed;
358	if (!message[i]) {
359		_nak(stub);
360		return parsed;
361	} else if (!message[i + 1]) {
362		++parsed;
363		_nak(stub);
364		return parsed;
365	}
366	parsed += 2;
367	int networkChecksum = _hex2int(&message[i], 2);
368	if (networkChecksum != checksum) {
369		if (stub->d.log) {
370			stub->d.log(&stub->d, DEBUGGER_LOG_WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum);
371		}
372		_nak(stub);
373		return parsed;
374	}
375
376	_ack(stub);
377	++message;
378	switch (messageType) {
379	case '?':
380		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
381		_sendMessage(stub);
382		break;
383	case 'c':
384		_continue(stub, message);
385		break;
386	case 'g':
387		_readGPRs(stub, message);
388		break;
389	case 'H':
390		// This is faked because we only have one thread
391		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
392		_sendMessage(stub);
393		break;
394	case 'm':
395		_readMemory(stub, message);
396		break;
397	case 'p':
398		_readRegister(stub, message);
399		break;
400	case 'Q':
401		_processQWriteCommand(stub, message);
402		break;
403	case 'q':
404		_processQReadCommand(stub, message);
405		break;
406	case 's':
407		_step(stub, message);
408		break;
409	case 'V':
410		_processVWriteCommand(stub, message);
411		break;
412	case 'v':
413		_processVReadCommand(stub, message);
414		break;
415	case 'Z':
416		_setBreakpoint(stub, message);
417		break;
418	case 'z':
419		_clearBreakpoint(stub, message);
420		break;
421	default:
422		_error(stub, GDB_UNSUPPORTED_COMMAND);
423		break;
424	}
425	return parsed;
426}
427
428void GDBStubCreate(struct GDBStub* stub) {
429	ARMDebuggerCreate(&stub->d);
430	stub->socket = INVALID_SOCKET;
431	stub->connection = INVALID_SOCKET;
432	stub->d.init = 0;
433	stub->d.deinit = _gdbStubDeinit;
434	stub->d.paused = _gdbStubPoll;
435	stub->d.entered = _gdbStubEntered;
436	stub->d.log = 0;
437}
438
439int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) {
440	if (!SOCKET_FAILED(stub->socket)) {
441		GDBStubShutdown(stub);
442	}
443	// TODO: support IPv6
444	stub->socket = SocketOpenTCP(port, bindAddress);
445	if (SOCKET_FAILED(stub->socket)) {
446		if (stub->d.log) {
447			stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket");
448		}
449		return 0;
450	}
451	int err = SocketListen(stub->socket, 1);
452	if (err) {
453		goto cleanup;
454	}
455	if (!SocketSetBlocking(stub->socket, 0)) {
456		goto cleanup;
457	}
458
459	return 1;
460
461cleanup:
462	if (stub->d.log) {
463		stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port");
464	}
465	SocketClose(stub->socket);
466	stub->socket = -1;
467	return 0;
468}
469
470void GDBStubHangup(struct GDBStub* stub) {
471	if (!SOCKET_FAILED(stub->connection)) {
472		SocketClose(stub->connection);
473		stub->connection = -1;
474	}
475	if (stub->d.state == DEBUGGER_PAUSED) {
476		stub->d.state = DEBUGGER_RUNNING;
477	}
478}
479
480void GDBStubShutdown(struct GDBStub* stub) {
481	GDBStubHangup(stub);
482	if (!SOCKET_FAILED(stub->socket)) {
483		SocketClose(stub->socket);
484		stub->socket = -1;
485	}
486}
487
488void GDBStubUpdate(struct GDBStub* stub) {
489	if (stub->socket == INVALID_SOCKET) {
490		return;
491	}
492	if (stub->connection == INVALID_SOCKET) {
493		stub->connection = SocketAccept(stub->socket, 0, 0);
494		if (!SOCKET_FAILED(stub->connection)) {
495			if (!SocketSetBlocking(stub->connection, 0)) {
496				goto connectionLost;
497			}
498			ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
499		} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
500			return;
501		} else {
502			goto connectionLost;
503		}
504	}
505	while (true) {
506		ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
507		if (messageLen == 0) {
508			goto connectionLost;
509		}
510		if (messageLen == -1) {
511			if (errno == EWOULDBLOCK || errno == EAGAIN) {
512				return;
513			}
514			goto connectionLost;
515		}
516		stub->line[messageLen] = '\0';
517		if (stub->d.log) {
518			stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "< %s", stub->line);
519		}
520		ssize_t position = 0;
521		while (position < messageLen) {
522			position += _parseGDBMessage(stub, &stub->line[position]);
523		}
524	}
525
526connectionLost:
527	if (stub->d.log) {
528		stub->d.log(&stub->d, DEBUGGER_LOG_INFO, "Connection lost");
529	}
530	GDBStubHangup(stub);
531}