all repos — mgba @ 3c50adc0257bd8bfec508f93062f833b9997ef30

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