all repos — mgba @ 100eab76fe6fb1204f5d65600b05a0977648b6ed

mGBA Game Boy Advance Emulator

src/debugger/gdb-stub.c (view raw)

  1/* Copyright (c) 2013-2014 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "gdb-stub.h"
  7
  8#include "core/core.h"
  9#include "gba/memory.h"
 10
 11#include <signal.h>
 12
 13#ifndef SIGTRAP
 14#define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */
 15#endif
 16
 17#define SOCKET_TIMEOUT 50
 18
 19enum GDBError {
 20	GDB_NO_ERROR = 0x00,
 21	GDB_BAD_ARGUMENTS = 0x06,
 22	GDB_UNSUPPORTED_COMMAND = 0x07
 23};
 24
 25enum {
 26	MACH_O_ARM = 12,
 27	MACH_O_ARM_V4T = 5
 28};
 29
 30static void _sendMessage(struct GDBStub* stub);
 31
 32static void _gdbStubDeinit(struct mDebugger* debugger) {
 33	struct GDBStub* stub = (struct GDBStub*) debugger;
 34	if (!SOCKET_FAILED(stub->socket)) {
 35		GDBStubShutdown(stub);
 36	}
 37}
 38
 39static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
 40	struct GDBStub* stub = (struct GDBStub*) debugger;
 41	switch (reason) {
 42	case DEBUGGER_ENTER_MANUAL:
 43		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
 44		break;
 45	case DEBUGGER_ENTER_BREAKPOINT:
 46		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP); // TODO: Use hwbreak/swbreak if gdb supports it
 47		break;
 48	case DEBUGGER_ENTER_WATCHPOINT:
 49		if (info) {
 50			const char* type = 0;
 51			switch (info->watchType) {
 52			case WATCHPOINT_WRITE:
 53				if (info->newValue == info->oldValue) {
 54					if (stub->d.state == DEBUGGER_PAUSED) {
 55						stub->d.state = DEBUGGER_RUNNING;
 56					}
 57					return;
 58				}
 59				type = "watch";
 60				break;
 61			case WATCHPOINT_READ:
 62				type = "rwatch";
 63				break;
 64			case WATCHPOINT_RW:
 65				type = "awatch";
 66				break;
 67			}
 68			snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%s:%08x;", SIGTRAP, type, info->address);
 69		} else {
 70			snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
 71		}
 72		break;
 73	case DEBUGGER_ENTER_ILLEGAL_OP:
 74		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGILL);
 75		break;
 76	case DEBUGGER_ENTER_ATTACHED:
 77		return;
 78	}
 79	_sendMessage(stub);
 80}
 81
 82static void _gdbStubPoll(struct mDebugger* debugger) {
 83	struct GDBStub* stub = (struct GDBStub*) debugger;
 84	--stub->untilPoll;
 85	if (stub->untilPoll > 0) {
 86		return;
 87	}
 88	stub->untilPoll = GDB_STUB_INTERVAL;
 89	stub->shouldBlock = false;
 90	GDBStubUpdate(stub);
 91}
 92
 93static void _gdbStubWait(struct mDebugger* debugger) {
 94	struct GDBStub* stub = (struct GDBStub*) debugger;
 95	stub->shouldBlock = true;
 96	GDBStubUpdate(stub);
 97}
 98
 99static void _ack(struct GDBStub* stub) {
100	char ack = '+';
101	SocketSend(stub->connection, &ack, 1);
102}
103
104static void _nak(struct GDBStub* stub) {
105	char nak = '-';
106	mLOG(DEBUGGER, WARN, "Packet error");
107	SocketSend(stub->connection, &nak, 1);
108}
109
110static uint32_t _hex2int(const char* hex, int maxDigits) {
111	uint32_t value = 0;
112	uint8_t letter;
113
114	while (maxDigits--) {
115		letter = *hex - '0';
116		if (letter > 9) {
117			letter = *hex - 'a';
118			if (letter > 5) {
119				break;
120			}
121			value *= 0x10;
122			value += letter + 10;
123		} else {
124			value *= 0x10;
125			value += letter;
126		}
127		++hex;
128	}
129	return value;
130}
131
132static void _int2hex8(uint8_t value, char* out) {
133	static const char language[] = "0123456789abcdef";
134	out[0] = language[value >> 4];
135	out[1] = language[value & 0xF];
136}
137
138static void _int2hex32(uint32_t value, char* out) {
139	static const char language[] = "0123456789abcdef";
140	out[6] = language[value >> 28];
141	out[7] = language[(value >> 24) & 0xF];
142	out[4] = language[(value >> 20) & 0xF];
143	out[5] = language[(value >> 16) & 0xF];
144	out[2] = language[(value >> 12) & 0xF];
145	out[3] = language[(value >> 8) & 0xF];
146	out[0] = language[(value >> 4) & 0xF];
147	out[1] = language[value & 0xF];
148}
149
150static uint32_t _readHex(const char* in, unsigned* out) {
151	unsigned i;
152	for (i = 0; i < 8; ++i) {
153		if (in[i] == ',' || in[i] == ':' || in[i] == '=') {
154			break;
155		}
156	}
157	*out += i;
158	return _hex2int(in, i);
159}
160
161static void _sendMessage(struct GDBStub* stub) {
162	if (stub->lineAck != GDB_ACK_OFF) {
163		stub->lineAck = GDB_ACK_PENDING;
164	}
165	uint8_t checksum = 0;
166	int i = 1;
167	char buffer = stub->outgoing[0];
168	char swap;
169	stub->outgoing[0] = '$';
170	if (buffer) {
171		for (; i < GDB_STUB_MAX_LINE - 5; ++i) {
172			checksum += buffer;
173			swap = stub->outgoing[i];
174			stub->outgoing[i] = buffer;
175			buffer = swap;
176			if (!buffer) {
177				++i;
178				break;
179			}
180		}
181	}
182	stub->outgoing[i] = '#';
183	_int2hex8(checksum, &stub->outgoing[i + 1]);
184	stub->outgoing[i + 3] = 0;
185	mLOG(DEBUGGER, DEBUG, "> %s", stub->outgoing);
186	SocketSend(stub->connection, stub->outgoing, i + 3);
187}
188
189static void _error(struct GDBStub* stub, enum GDBError error) {
190	snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "E%02x", error);
191	_sendMessage(stub);
192}
193
194static void _writeHostInfo(struct GDBStub* stub) {
195	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);
196	_sendMessage(stub);
197}
198
199static void _continue(struct GDBStub* stub, const char* message) {
200	stub->d.state = DEBUGGER_CUSTOM;
201	stub->untilPoll = GDB_STUB_INTERVAL;
202	// TODO: parse message
203	UNUSED(message);
204}
205
206static void _step(struct GDBStub* stub, const char* message) {
207	stub->d.core->step(stub->d.core);
208	snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
209	_sendMessage(stub);
210	// TODO: parse message
211	UNUSED(message);
212}
213
214static void _writeMemoryBinary(struct GDBStub* stub, const char* message) {
215	const char* readAddress = message;
216	unsigned i = 0;
217	uint32_t address = _readHex(readAddress, &i);
218	readAddress += i + 1;
219
220	i = 0;
221	uint32_t size = _readHex(readAddress, &i);
222	readAddress += i + 1;
223
224	if (size > 512) {
225		_error(stub, GDB_BAD_ARGUMENTS);
226		return;
227	}
228
229	struct ARMCore* cpu = stub->d.core->cpu;
230	for (i = 0; i < size; i++) {
231		uint8_t byte = *readAddress;
232		++readAddress;
233
234		// Parse escape char
235		if (byte == 0x7D) {
236			byte = *readAddress ^ 0x20;
237			++readAddress;
238		}
239
240		GBAPatch8(cpu, address + i, byte, 0);
241	}
242
243	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
244	_sendMessage(stub);
245}
246
247
248static void _writeMemory(struct GDBStub* stub, const char* message) {
249	const char* readAddress = message;
250	unsigned i = 0;
251	uint32_t address = _readHex(readAddress, &i);
252	readAddress += i + 1;
253
254	i = 0;
255	uint32_t size = _readHex(readAddress, &i);
256	readAddress += i + 1;
257
258	if (size > 512) {
259		_error(stub, GDB_BAD_ARGUMENTS);
260		return;
261	}
262
263	struct ARMCore* cpu = stub->d.core->cpu;
264	for (i = 0; i < size; ++i, readAddress += 2) {
265		uint8_t byte = _hex2int(readAddress, 2);
266		GBAPatch8(cpu, address + i, byte, 0);
267	}
268
269	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
270	_sendMessage(stub);
271}
272
273static void _readMemory(struct GDBStub* stub, const char* message) {
274	const char* readAddress = message;
275	unsigned i = 0;
276	uint32_t address = _readHex(readAddress, &i);
277	readAddress += i + 1;
278	uint32_t size = _readHex(readAddress, &i);
279	if (size > 512) {
280		_error(stub, GDB_BAD_ARGUMENTS);
281		return;
282	}
283	struct ARMCore* cpu = stub->d.core->cpu;
284	int writeAddress = 0;
285	for (i = 0; i < size; ++i, writeAddress += 2) {
286		uint8_t byte = cpu->memory.load8(cpu, address + i, 0);
287		_int2hex8(byte, &stub->outgoing[writeAddress]);
288	}
289	stub->outgoing[writeAddress] = 0;
290	_sendMessage(stub);
291}
292
293static void _writeGPRs(struct GDBStub* stub, const char* message) {
294	struct ARMCore* cpu = stub->d.core->cpu;
295	const char* readAddress = message;
296
297	int r;
298	for (r = 0; r < 16; ++r) {
299		cpu->gprs[r] = _hex2int(readAddress, 8);
300		readAddress += 8;
301	}
302
303	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
304	_sendMessage(stub);
305}
306
307static void _readGPRs(struct GDBStub* stub, const char* message) {
308	struct ARMCore* cpu = stub->d.core->cpu;
309	UNUSED(message);
310	int r;
311	int i = 0;
312	for (r = 0; r < 16; ++r) {
313		_int2hex32(cpu->gprs[r], &stub->outgoing[i]);
314		i += 8;
315	}
316	stub->outgoing[i] = 0;
317	_sendMessage(stub);
318}
319
320static void _writeRegister(struct GDBStub* stub, const char* message) {
321	struct ARMCore* cpu = stub->d.core->cpu;
322	const char* readAddress = message;
323
324	unsigned i = 0;
325	uint32_t reg = _readHex(readAddress, &i);
326	readAddress += i + 1;
327
328	uint32_t value = _readHex(readAddress, &i);
329
330#ifdef _MSC_VER
331	value = _byteswap_ulong(value);
332#else
333	value = __builtin_bswap32(value);
334#endif
335
336	if (reg < 0x10) {
337		cpu->gprs[reg] = value;
338	} else if (reg == 0x19) {
339		cpu->cpsr.packed = value;
340	} else {
341		stub->outgoing[0] = '\0';
342		_sendMessage(stub);
343		return;
344	}
345
346	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
347	_sendMessage(stub);
348}
349
350static void _readRegister(struct GDBStub* stub, const char* message) {
351	struct ARMCore* cpu = stub->d.core->cpu;
352	const char* readAddress = message;
353	unsigned i = 0;
354	uint32_t reg = _readHex(readAddress, &i);
355	uint32_t value;
356	if (reg < 0x10) {
357		value = cpu->gprs[reg];
358	} else if (reg == 0x19) {
359		value = cpu->cpsr.packed;
360	} else {
361		stub->outgoing[0] = '\0';
362		_sendMessage(stub);
363		return;
364	}
365	_int2hex32(value, stub->outgoing);
366	stub->outgoing[8] = '\0';
367	_sendMessage(stub);
368}
369
370static void _processQReadCommand(struct GDBStub* stub, const char* message) {
371	stub->outgoing[0] = '\0';
372	if (!strncmp("HostInfo#", message, 9)) {
373		_writeHostInfo(stub);
374		return;
375	}
376	if (!strncmp("Attached#", message, 9)) {
377		strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
378	} else if (!strncmp("VAttachOrWaitSupported#", message, 23)) {
379		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
380	} else if (!strncmp("C#", message, 2)) {
381		strncpy(stub->outgoing, "QC1", GDB_STUB_MAX_LINE - 4);
382	} else if (!strncmp("fThreadInfo#", message, 12)) {
383		strncpy(stub->outgoing, "m1", GDB_STUB_MAX_LINE - 4);
384	} else if (!strncmp("sThreadInfo#", message, 12)) {
385		strncpy(stub->outgoing, "l", GDB_STUB_MAX_LINE - 4);
386	}
387	_sendMessage(stub);
388}
389
390static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
391	stub->outgoing[0] = '\0';
392	if (!strncmp("StartNoAckMode#", message, 16)) {
393		stub->lineAck = GDB_ACK_OFF;
394		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
395	}
396	_sendMessage(stub);
397}
398
399static void _processVWriteCommand(struct GDBStub* stub, const char* message) {
400	UNUSED(message);
401	stub->outgoing[0] = '\0';
402	_sendMessage(stub);
403}
404
405static void _processVReadCommand(struct GDBStub* stub, const char* message) {
406	stub->outgoing[0] = '\0';
407	if (!strncmp("Attach", message, 6)) {
408		strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
409		mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
410	}
411	_sendMessage(stub);
412}
413
414static void _setBreakpoint(struct GDBStub* stub, const char* message) {
415	const char* readAddress = &message[2];
416	unsigned i = 0;
417	uint32_t address = _readHex(readAddress, &i);
418	readAddress += i + 1;
419	uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
420	UNUSED(kind);
421
422	switch (message[0]) {
423	case '0': // Memory breakpoints are not currently supported
424	case '1':
425		stub->d.platform->setBreakpoint(stub->d.platform, address);
426		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
427		_sendMessage(stub);
428		break;
429	case '2':
430		stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_WRITE);
431		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
432		_sendMessage(stub);
433		break;
434	case '3':
435		stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_READ);
436		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
437		_sendMessage(stub);
438		break;
439	case '4':
440		stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_RW);
441		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
442		_sendMessage(stub);
443		break;
444	default:
445		stub->outgoing[0] = '\0';
446		_sendMessage(stub);
447		break;
448	}
449}
450
451static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
452	const char* readAddress = &message[2];
453	unsigned i = 0;
454	uint32_t address = _readHex(readAddress, &i);
455	switch (message[0]) {
456	case '0': // Memory breakpoints are not currently supported
457	case '1':
458		stub->d.platform->clearBreakpoint(stub->d.platform, address);
459		break;
460	case '2':
461	case '3':
462	case '4':
463		stub->d.platform->clearWatchpoint(stub->d.platform, address);
464		break;
465	default:
466		break;
467	}
468	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
469	_sendMessage(stub);
470}
471
472size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
473	uint8_t checksum = 0;
474	int parsed = 1;
475	switch (*message) {
476	case '+':
477		stub->lineAck = GDB_ACK_RECEIVED;
478		return parsed;
479	case '-':
480		stub->lineAck = GDB_NAK_RECEIVED;
481		return parsed;
482	case '$':
483		++message;
484		break;
485	case '\x03':
486		mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
487		return parsed;
488	default:
489		_nak(stub);
490		return parsed;
491	}
492
493	int i;
494	char messageType = message[0];
495	for (i = 0; message[i] != '#'; ++i, ++parsed) {
496		checksum += message[i];
497	}
498	if (!message[i]) {
499		_nak(stub);
500		return parsed;
501	}
502	++i;
503	++parsed;
504	if (!message[i]) {
505		_nak(stub);
506		return parsed;
507	} else if (!message[i + 1]) {
508		++parsed;
509		_nak(stub);
510		return parsed;
511	}
512	parsed += 2;
513	int networkChecksum = _hex2int(&message[i], 2);
514	if (networkChecksum != checksum) {
515		mLOG(DEBUGGER, WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum);
516		_nak(stub);
517		return parsed;
518	}
519
520	_ack(stub);
521	++message;
522	switch (messageType) {
523	case '?':
524		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
525		_sendMessage(stub);
526		break;
527	case 'c':
528		_continue(stub, message);
529		break;
530	case 'G':
531		_writeGPRs(stub, message);
532		break;
533	case 'g':
534		_readGPRs(stub, message);
535		break;
536	case 'H':
537		// This is faked because we only have one thread
538		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
539		_sendMessage(stub);
540		break;
541	case 'M':
542		_writeMemory(stub, message);
543		break;
544	case 'm':
545		_readMemory(stub, message);
546		break;
547	case 'P':
548		_writeRegister(stub, message);
549		break;
550	case 'p':
551		_readRegister(stub, message);
552		break;
553	case 'Q':
554		_processQWriteCommand(stub, message);
555		break;
556	case 'q':
557		_processQReadCommand(stub, message);
558		break;
559	case 's':
560		_step(stub, message);
561		break;
562	case 'V':
563		_processVWriteCommand(stub, message);
564		break;
565	case 'v':
566		_processVReadCommand(stub, message);
567		break;
568	case 'X':
569		_writeMemoryBinary(stub, message);
570                break;
571	case 'Z':
572		_setBreakpoint(stub, message);
573		break;
574	case 'z':
575		_clearBreakpoint(stub, message);
576		break;
577	default:
578		_error(stub, GDB_UNSUPPORTED_COMMAND);
579		break;
580	}
581	return parsed;
582}
583
584void GDBStubCreate(struct GDBStub* stub) {
585	stub->socket = INVALID_SOCKET;
586	stub->connection = INVALID_SOCKET;
587	stub->d.init = 0;
588	stub->d.deinit = _gdbStubDeinit;
589	stub->d.paused = _gdbStubWait;
590	stub->d.entered = _gdbStubEntered;
591	stub->d.custom = _gdbStubPoll;
592	stub->untilPoll = GDB_STUB_INTERVAL;
593	stub->lineAck = GDB_ACK_PENDING;
594	stub->shouldBlock = false;
595}
596
597bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) {
598	if (!SOCKET_FAILED(stub->socket)) {
599		GDBStubShutdown(stub);
600	}
601	stub->socket = SocketOpenTCP(port, bindAddress);
602	if (SOCKET_FAILED(stub->socket)) {
603		mLOG(DEBUGGER, ERROR, "Couldn't open socket");
604		return false;
605	}
606	if (!SocketSetBlocking(stub->socket, false)) {
607		goto cleanup;
608	}
609	int err = SocketListen(stub->socket, 1);
610	if (err) {
611		goto cleanup;
612	}
613
614	return true;
615
616cleanup:
617	mLOG(DEBUGGER, ERROR, "Couldn't listen on port");
618	SocketClose(stub->socket);
619	stub->socket = INVALID_SOCKET;
620	return false;
621}
622
623void GDBStubHangup(struct GDBStub* stub) {
624	if (!SOCKET_FAILED(stub->connection)) {
625		SocketClose(stub->connection);
626		stub->connection = INVALID_SOCKET;
627	}
628	if (stub->d.state == DEBUGGER_PAUSED) {
629		stub->d.state = DEBUGGER_RUNNING;
630	}
631}
632
633void GDBStubShutdown(struct GDBStub* stub) {
634	GDBStubHangup(stub);
635	if (!SOCKET_FAILED(stub->socket)) {
636		SocketClose(stub->socket);
637		stub->socket = INVALID_SOCKET;
638	}
639}
640
641void GDBStubUpdate(struct GDBStub* stub) {
642	if (stub->socket == INVALID_SOCKET) {
643		if (stub->d.state == DEBUGGER_PAUSED) {
644			stub->d.state = DEBUGGER_RUNNING;
645		}
646		return;
647	}
648	if (stub->connection == INVALID_SOCKET) {
649		if (stub->shouldBlock) {
650			Socket reads = stub->socket;
651			SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
652		}
653		stub->connection = SocketAccept(stub->socket, 0);
654		if (!SOCKET_FAILED(stub->connection)) {
655			if (!SocketSetBlocking(stub->connection, false)) {
656				goto connectionLost;
657			}
658			mDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED, 0);
659		} else if (SocketWouldBlock()) {
660			return;
661		} else {
662			goto connectionLost;
663		}
664	}
665	while (true) {
666		if (stub->shouldBlock) {
667			Socket reads = stub->connection;
668			SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
669		}
670		ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
671		if (messageLen == 0) {
672			goto connectionLost;
673		}
674		if (messageLen == -1) {
675			if (SocketWouldBlock()) {
676				return;
677			}
678			goto connectionLost;
679		}
680		stub->line[messageLen] = '\0';
681		mLOG(DEBUGGER, DEBUG, "< %s", stub->line);
682		ssize_t position = 0;
683		while (position < messageLen) {
684			position += _parseGDBMessage(stub, &stub->line[position]);
685		}
686	}
687
688connectionLost:
689	mLOG(DEBUGGER, WARN, "Connection lost");
690	GDBStubHangup(stub);
691}