all repos — mgba @ db6113d970d80ea271d3414ad226d3222fd361dd

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 ARMDebugger* debugger) {
 30	struct GDBStub* stub = (struct GDBStub*) debugger;
 31	if (!SOCKET_FAILED(stub->socket)) {
 32		GDBStubShutdown(stub);
 33	}
 34}
 35
 36static void _gdbStubEntered(struct ARMDebugger* 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				type = "watch";
 51				break;
 52			case WATCHPOINT_READ:
 53				type = "rwatch";
 54				break;
 55			case WATCHPOINT_RW:
 56				type = "awatch";
 57				break;
 58			}
 59			snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%s:%08x;", SIGTRAP, type, info->address);
 60		} else {
 61			snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
 62		}
 63		break;
 64	case DEBUGGER_ENTER_ILLEGAL_OP:
 65		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGILL);
 66		break;
 67	case DEBUGGER_ENTER_ATTACHED:
 68		return;
 69	}
 70	_sendMessage(stub);
 71}
 72
 73static void _gdbStubPoll(struct ARMDebugger* debugger) {
 74	struct GDBStub* stub = (struct GDBStub*) debugger;
 75	--stub->untilPoll;
 76	if (stub->untilPoll > 0) {
 77		return;
 78	}
 79	stub->untilPoll = GDB_STUB_INTERVAL;
 80	stub->shouldBlock = false;
 81	GDBStubUpdate(stub);
 82}
 83
 84static void _gdbStubWait(struct ARMDebugger* debugger) {
 85	struct GDBStub* stub = (struct GDBStub*) debugger;
 86	stub->shouldBlock = true;
 87	GDBStubUpdate(stub);
 88}
 89
 90static void _ack(struct GDBStub* stub) {
 91	char ack = '+';
 92	SocketSend(stub->connection, &ack, 1);
 93}
 94
 95static void _nak(struct GDBStub* stub) {
 96	char nak = '-';
 97	if (stub->d.log) {
 98		stub->d.log(&stub->d, DEBUGGER_LOG_WARN, "Packet error");
 99	}
100	SocketSend(stub->connection, &nak, 1);
101}
102
103static uint32_t _hex2int(const char* hex, int maxDigits) {
104	uint32_t value = 0;
105	uint8_t letter;
106
107	while (maxDigits--) {
108		letter = *hex - '0';
109		if (letter > 9) {
110			letter = *hex - 'a';
111			if (letter > 5) {
112				break;
113			}
114			value *= 0x10;
115			value += letter + 10;
116		} else {
117			value *= 0x10;
118			value += letter;
119		}
120		++hex;
121	}
122	return value;
123}
124
125static void _int2hex8(uint8_t value, char* out) {
126	static const char language[] = "0123456789abcdef";
127	out[0] = language[value >> 4];
128	out[1] = language[value & 0xF];
129}
130
131static void _int2hex32(uint32_t value, char* out) {
132	static const char language[] = "0123456789abcdef";
133	out[6] = language[value >> 28];
134	out[7] = language[(value >> 24) & 0xF];
135	out[4] = language[(value >> 20) & 0xF];
136	out[5] = language[(value >> 16) & 0xF];
137	out[2] = language[(value >> 12) & 0xF];
138	out[3] = language[(value >> 8) & 0xF];
139	out[0] = language[(value >> 4) & 0xF];
140	out[1] = language[value & 0xF];
141}
142
143static uint32_t _readHex(const char* in, unsigned* out) {
144	unsigned i;
145	for (i = 0; i < 8; ++i) {
146		if (in[i] == ',') {
147			break;
148		}
149	}
150	*out += i;
151	return _hex2int(in, i);
152}
153
154static void _sendMessage(struct GDBStub* stub) {
155	if (stub->lineAck != GDB_ACK_OFF) {
156		stub->lineAck = GDB_ACK_PENDING;
157	}
158	uint8_t checksum = 0;
159	int i = 1;
160	char buffer = stub->outgoing[0];
161	char swap;
162	stub->outgoing[0] = '$';
163	if (buffer) {
164		for (; i < GDB_STUB_MAX_LINE - 5; ++i) {
165			checksum += buffer;
166			swap = stub->outgoing[i];
167			stub->outgoing[i] = buffer;
168			buffer = swap;
169			if (!buffer) {
170				++i;
171				break;
172			}
173		}
174	}
175	stub->outgoing[i] = '#';
176	_int2hex8(checksum, &stub->outgoing[i + 1]);
177	stub->outgoing[i + 3] = 0;
178	if (stub->d.log) {
179		stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "> %s", stub->outgoing);
180	}
181	SocketSend(stub->connection, stub->outgoing, i + 3);
182}
183
184static void _error(struct GDBStub* stub, enum GDBError error) {
185	snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "E%02x", error);
186	_sendMessage(stub);
187}
188
189static void _writeHostInfo(struct GDBStub* stub) {
190	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);
191	_sendMessage(stub);
192}
193
194static void _continue(struct GDBStub* stub, const char* message) {
195	stub->d.state = DEBUGGER_CUSTOM;
196	stub->untilPoll = GDB_STUB_INTERVAL;
197	// TODO: parse message
198	UNUSED(message);
199}
200
201static void _step(struct GDBStub* stub, const char* message) {
202	ARMRun(stub->d.cpu);
203	snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
204	_sendMessage(stub);
205	// TODO: parse message
206	UNUSED(message);
207}
208
209static void _readMemory(struct GDBStub* stub, const char* message) {
210	const char* readAddress = message;
211	unsigned i = 0;
212	uint32_t address = _readHex(readAddress, &i);
213	readAddress += i + 1;
214	uint32_t size = _readHex(readAddress, &i);
215	if (size > 512) {
216		_error(stub, GDB_BAD_ARGUMENTS);
217		return;
218	}
219	struct ARMCore* cpu = stub->d.cpu;
220	int writeAddress = 0;
221	for (i = 0; i < size; ++i, writeAddress += 2) {
222		uint8_t byte = cpu->memory.load8(cpu, address + i, 0);
223		_int2hex8(byte, &stub->outgoing[writeAddress]);
224	}
225	stub->outgoing[writeAddress] = 0;
226	_sendMessage(stub);
227}
228
229static void _readGPRs(struct GDBStub* stub, const char* message) {
230	UNUSED(message);
231	int r;
232	int i = 0;
233	for (r = 0; r < 16; ++r) {
234		_int2hex32(stub->d.cpu->gprs[r], &stub->outgoing[i]);
235		i += 8;
236	}
237	stub->outgoing[i] = 0;
238	_sendMessage(stub);
239}
240
241static void _readRegister(struct GDBStub* stub, const char* message) {
242	const char* readAddress = message;
243	unsigned i = 0;
244	uint32_t reg = _readHex(readAddress, &i);
245	uint32_t value;
246	if (reg < 0x10) {
247		value = stub->d.cpu->gprs[reg];
248	} else if (reg == 0x19) {
249		value = stub->d.cpu->cpsr.packed;
250	} else {
251		stub->outgoing[0] = '\0';
252		_sendMessage(stub);
253		return;
254	}
255	_int2hex32(value, stub->outgoing);
256	stub->outgoing[8] = '\0';
257	_sendMessage(stub);
258}
259
260static void _processQReadCommand(struct GDBStub* stub, const char* message) {
261	stub->outgoing[0] = '\0';
262	if (!strncmp("HostInfo#", message, 9)) {
263		_writeHostInfo(stub);
264		return;
265	}
266	if (!strncmp("Attached#", message, 9)) {
267		strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
268	} else if (!strncmp("VAttachOrWaitSupported#", message, 23)) {
269		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
270	} else if (!strncmp("C#", message, 2)) {
271		strncpy(stub->outgoing, "QC1", GDB_STUB_MAX_LINE - 4);
272	} else if (!strncmp("fThreadInfo#", message, 12)) {
273		strncpy(stub->outgoing, "m1", GDB_STUB_MAX_LINE - 4);
274	} else if (!strncmp("sThreadInfo#", message, 12)) {
275		strncpy(stub->outgoing, "l", GDB_STUB_MAX_LINE - 4);
276	}
277	_sendMessage(stub);
278}
279
280static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
281	stub->outgoing[0] = '\0';
282	if (!strncmp("StartNoAckMode#", message, 16)) {
283		stub->lineAck = GDB_ACK_OFF;
284		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
285	}
286	_sendMessage(stub);
287}
288
289static void _processVWriteCommand(struct GDBStub* stub, const char* message) {
290	UNUSED(message);
291	stub->outgoing[0] = '\0';
292	_sendMessage(stub);
293}
294
295static void _processVReadCommand(struct GDBStub* stub, const char* message) {
296	stub->outgoing[0] = '\0';
297	if (!strncmp("Attach", message, 6)) {
298		strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
299		ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
300	}
301	_sendMessage(stub);
302}
303
304static void _setBreakpoint(struct GDBStub* stub, const char* message) {
305	const char* readAddress = &message[2];
306	unsigned i = 0;
307	uint32_t address = _readHex(readAddress, &i);
308	readAddress += i + 1;
309	uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
310	UNUSED(kind);
311
312	switch (message[0]) {
313	case '0': // Memory breakpoints are not currently supported
314	case '1':
315		ARMDebuggerSetBreakpoint(&stub->d, address);
316		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
317		_sendMessage(stub);
318		break;
319	case '2':
320		ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_WRITE);
321		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
322		_sendMessage(stub);
323		break;
324	case '3':
325		ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_READ);
326		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
327		_sendMessage(stub);
328		break;
329	case '4':
330		ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_RW);
331		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
332		_sendMessage(stub);
333		break;
334	default:
335		stub->outgoing[0] = '\0';
336		_sendMessage(stub);
337		break;
338	}
339}
340
341static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
342	const char* readAddress = &message[2];
343	unsigned i = 0;
344	uint32_t address = _readHex(readAddress, &i);
345	switch (message[0]) {
346	case '0': // Memory breakpoints are not currently supported
347	case '1':
348		ARMDebuggerClearBreakpoint(&stub->d, address);
349		break;
350	case '2':
351	case '3':
352	case '4':
353		ARMDebuggerClearWatchpoint(&stub->d, address);
354		break;
355	default:
356		break;
357	}
358	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
359	_sendMessage(stub);
360}
361
362size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
363	uint8_t checksum = 0;
364	int parsed = 1;
365	switch (*message) {
366	case '+':
367		stub->lineAck = GDB_ACK_RECEIVED;
368		return parsed;
369	case '-':
370		stub->lineAck = GDB_NAK_RECEIVED;
371		return parsed;
372	case '$':
373		++message;
374		break;
375	case '\x03':
376		ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
377		return parsed;
378	default:
379		_nak(stub);
380		return parsed;
381	}
382
383	int i;
384	char messageType = message[0];
385	for (i = 0; message[i] && message[i] != '#'; ++i, ++parsed) {
386		checksum += message[i];
387	}
388	if (!message[i]) {
389		_nak(stub);
390		return parsed;
391	}
392	++i;
393	++parsed;
394	if (!message[i]) {
395		_nak(stub);
396		return parsed;
397	} else if (!message[i + 1]) {
398		++parsed;
399		_nak(stub);
400		return parsed;
401	}
402	parsed += 2;
403	int networkChecksum = _hex2int(&message[i], 2);
404	if (networkChecksum != checksum) {
405		if (stub->d.log) {
406			stub->d.log(&stub->d, DEBUGGER_LOG_WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum);
407		}
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	ARMDebuggerCreate(&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->d.log = 0;
474	stub->untilPoll = GDB_STUB_INTERVAL;
475	stub->lineAck = GDB_ACK_PENDING;
476	stub->shouldBlock = false;
477}
478
479bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) {
480	if (!SOCKET_FAILED(stub->socket)) {
481		GDBStubShutdown(stub);
482	}
483	stub->socket = SocketOpenTCP(port, bindAddress);
484	if (SOCKET_FAILED(stub->socket)) {
485		if (stub->d.log) {
486			stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket");
487		}
488		return false;
489	}
490	if (!SocketSetBlocking(stub->socket, false)) {
491		goto cleanup;
492	}
493	int err = SocketListen(stub->socket, 1);
494	if (err) {
495		goto cleanup;
496	}
497
498	return true;
499
500cleanup:
501	if (stub->d.log) {
502		stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port");
503	}
504	SocketClose(stub->socket);
505	stub->socket = INVALID_SOCKET;
506	return false;
507}
508
509void GDBStubHangup(struct GDBStub* stub) {
510	if (!SOCKET_FAILED(stub->connection)) {
511		SocketClose(stub->connection);
512		stub->connection = INVALID_SOCKET;
513	}
514	if (stub->d.state == DEBUGGER_PAUSED) {
515		stub->d.state = DEBUGGER_RUNNING;
516	}
517}
518
519void GDBStubShutdown(struct GDBStub* stub) {
520	GDBStubHangup(stub);
521	if (!SOCKET_FAILED(stub->socket)) {
522		SocketClose(stub->socket);
523		stub->socket = INVALID_SOCKET;
524	}
525}
526
527void GDBStubUpdate(struct GDBStub* stub) {
528	if (stub->socket == INVALID_SOCKET) {
529		if (stub->d.state == DEBUGGER_PAUSED) {
530			stub->d.state = DEBUGGER_RUNNING;
531		}
532		return;
533	}
534	if (stub->connection == INVALID_SOCKET) {
535		if (stub->shouldBlock) {
536			Socket reads = stub->socket;
537			SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
538		}
539		stub->connection = SocketAccept(stub->socket, 0);
540		if (!SOCKET_FAILED(stub->connection)) {
541			if (!SocketSetBlocking(stub->connection, false)) {
542				goto connectionLost;
543			}
544			ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED, 0);
545		} else if (SocketWouldBlock()) {
546			return;
547		} else {
548			goto connectionLost;
549		}
550	}
551	while (true) {
552		if (stub->shouldBlock) {
553			Socket reads = stub->connection;
554			SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
555		}
556		ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
557		if (messageLen == 0) {
558			goto connectionLost;
559		}
560		if (messageLen == -1) {
561			if (SocketWouldBlock()) {
562				return;
563			}
564			goto connectionLost;
565		}
566		stub->line[messageLen] = '\0';
567		if (stub->d.log) {
568			stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "< %s", stub->line);
569		}
570		ssize_t position = 0;
571		while (position < messageLen) {
572			position += _parseGDBMessage(stub, &stub->line[position]);
573		}
574	}
575
576connectionLost:
577	if (stub->d.log) {
578		stub->d.log(&stub->d, DEBUGGER_LOG_INFO, "Connection lost");
579	}
580	GDBStubHangup(stub);
581}