all repos — mgba @ 1cc0bdeec140cbe411020b8239f3dd47852a9e2f

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