all repos — mgba @ 29623ecd0b025ac701dd3df10ea5a475154f025a

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