all repos — mgba @ 2dc710feeb0e36fc2179ec7c5f90b3d0430dd00c

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