all repos — mgba @ e6ea94d2296eae963a48a18d009217a38d92bf9b

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	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	UNUSED(kind);
288
289	switch (message[0]) {
290	case '0': // Memory breakpoints are not currently supported
291	case '1':
292		ARMDebuggerSetBreakpoint(&stub->d, address);
293		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
294		_sendMessage(stub);
295		break;
296	case '2':
297	case '3':
298	case '4':
299		ARMDebuggerSetWatchpoint(&stub->d, address);
300		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
301		_sendMessage(stub);
302		break;
303	default:
304		stub->outgoing[0] = '\0';
305		_sendMessage(stub);
306		break;
307	}
308}
309
310static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
311	const char* readAddress = &message[2];
312	unsigned i = 0;
313	uint32_t address = _readHex(readAddress, &i);
314	switch (message[0]) {
315	case '0': // Memory breakpoints are not currently supported
316	case '1':
317		ARMDebuggerClearBreakpoint(&stub->d, address);
318		break;
319	case '2':
320	case '3':
321	case '4':
322		ARMDebuggerClearWatchpoint(&stub->d, address);
323		break;
324	default:
325		break;
326	}
327	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
328	_sendMessage(stub);
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	ARMDebuggerCreate(&stub->d);
435	stub->socket = INVALID_SOCKET;
436	stub->connection = INVALID_SOCKET;
437	stub->d.init = 0;
438	stub->d.deinit = _gdbStubDeinit;
439	stub->d.paused = _gdbStubPoll;
440	stub->d.entered = _gdbStubEntered;
441	stub->d.log = 0;
442}
443
444int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) {
445	if (!SOCKET_FAILED(stub->socket)) {
446		GDBStubShutdown(stub);
447	}
448	// TODO: support IPv6
449	stub->socket = SocketOpenTCP(port, bindAddress);
450	if (SOCKET_FAILED(stub->socket)) {
451		if (stub->d.log) {
452			stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket");
453		}
454		return 0;
455	}
456	int err = SocketListen(stub->socket, 1);
457	if (err) {
458		goto cleanup;
459	}
460	if (!SocketSetBlocking(stub->socket, 0)) {
461		goto cleanup;
462	}
463
464	return 1;
465
466cleanup:
467	if (stub->d.log) {
468		stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port");
469	}
470	SocketClose(stub->socket);
471	stub->socket = -1;
472	return 0;
473}
474
475void GDBStubHangup(struct GDBStub* stub) {
476	if (!SOCKET_FAILED(stub->connection)) {
477		SocketClose(stub->connection);
478		stub->connection = -1;
479	}
480	if (stub->d.state == DEBUGGER_PAUSED) {
481		stub->d.state = DEBUGGER_RUNNING;
482	}
483}
484
485void GDBStubShutdown(struct GDBStub* stub) {
486	GDBStubHangup(stub);
487	if (!SOCKET_FAILED(stub->socket)) {
488		SocketClose(stub->socket);
489		stub->socket = -1;
490	}
491}
492
493void GDBStubUpdate(struct GDBStub* stub) {
494	if (stub->socket == INVALID_SOCKET) {
495		return;
496	}
497	if (stub->connection == INVALID_SOCKET) {
498		stub->connection = SocketAccept(stub->socket, 0, 0);
499		if (!SOCKET_FAILED(stub->connection)) {
500			if (!SocketSetBlocking(stub->connection, 0)) {
501				goto connectionLost;
502			}
503			ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
504		} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
505			return;
506		} else {
507			goto connectionLost;
508		}
509	}
510	while (true) {
511		ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
512		if (messageLen == 0) {
513			goto connectionLost;
514		}
515		if (messageLen == -1) {
516			if (errno == EWOULDBLOCK || errno == EAGAIN) {
517				return;
518			}
519			goto connectionLost;
520		}
521		stub->line[messageLen] = '\0';
522		if (stub->d.log) {
523			stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "< %s", stub->line);
524		}
525		ssize_t position = 0;
526		while (position < messageLen) {
527			position += _parseGDBMessage(stub, &stub->line[position]);
528		}
529	}
530
531connectionLost:
532	if (stub->d.log) {
533		stub->d.log(&stub->d, DEBUGGER_LOG_INFO, "Connection lost");
534	}
535	GDBStubHangup(stub);
536}