all repos — mgba @ 973f1a64a0f6fd8cc9f6eaa0515c6dd69257720e

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