mirror of https://github.com/mgba-emu/mgba.git
LLDB compatibility
This commit is contained in:
parent
1541e6e0b0
commit
ea6b129509
|
@ -25,6 +25,7 @@ struct DebugMemoryShim {
|
||||||
|
|
||||||
enum DebuggerEntryReason {
|
enum DebuggerEntryReason {
|
||||||
DEBUGGER_ENTER_MANUAL,
|
DEBUGGER_ENTER_MANUAL,
|
||||||
|
DEBUGGER_ENTER_ATTACHED,
|
||||||
DEBUGGER_ENTER_BREAKPOINT,
|
DEBUGGER_ENTER_BREAKPOINT,
|
||||||
DEBUGGER_ENTER_WATCHPOINT,
|
DEBUGGER_ENTER_WATCHPOINT,
|
||||||
DEBUGGER_ENTER_ILLEGAL_OP
|
DEBUGGER_ENTER_ILLEGAL_OP
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
@ -19,6 +20,8 @@ enum {
|
||||||
MACH_O_ARM_V4T = 5
|
MACH_O_ARM_V4T = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void _sendMessage(struct GDBStub* stub);
|
||||||
|
|
||||||
static void _gdbStubDeinit(struct ARMDebugger* debugger) {
|
static void _gdbStubDeinit(struct ARMDebugger* debugger) {
|
||||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||||
if (stub->socket >= 0) {
|
if (stub->socket >= 0) {
|
||||||
|
@ -26,6 +29,41 @@ static void _gdbStubDeinit(struct ARMDebugger* debugger) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
|
||||||
|
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||||
|
switch (reason) {
|
||||||
|
case DEBUGGER_ENTER_MANUAL:
|
||||||
|
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
|
||||||
|
break;
|
||||||
|
case DEBUGGER_ENTER_BREAKPOINT:
|
||||||
|
case DEBUGGER_ENTER_WATCHPOINT: // TODO: Make watchpoints raise with address
|
||||||
|
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
|
||||||
|
break;
|
||||||
|
case DEBUGGER_ENTER_ILLEGAL_OP:
|
||||||
|
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGILL);
|
||||||
|
break;
|
||||||
|
case DEBUGGER_ENTER_ATTACHED:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_sendMessage(stub);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _gdbStubPoll(struct ARMDebugger* debugger) {
|
||||||
|
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||||
|
int flags;
|
||||||
|
while (stub->d.state == DEBUGGER_PAUSED) {
|
||||||
|
if (stub->connection >= 0) {
|
||||||
|
flags = fcntl(stub->connection, F_GETFL);
|
||||||
|
if (flags == -1) {
|
||||||
|
GDBStubHangup(stub);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fcntl(stub->connection, F_SETFL, flags & ~O_NONBLOCK);
|
||||||
|
}
|
||||||
|
GDBStubUpdate(stub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _ack(struct GDBStub* stub) {
|
static void _ack(struct GDBStub* stub) {
|
||||||
char ack = '+';
|
char ack = '+';
|
||||||
send(stub->connection, &ack, 1, 0);
|
send(stub->connection, &ack, 1, 0);
|
||||||
|
@ -115,6 +153,19 @@ static void _writeHostInfo(struct GDBStub* stub) {
|
||||||
_sendMessage(stub);
|
_sendMessage(stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _continue(struct GDBStub* stub, const char* message) {
|
||||||
|
stub->d.state = DEBUGGER_RUNNING;
|
||||||
|
if (stub->connection >= 0) {
|
||||||
|
int flags = fcntl(stub->connection, F_GETFL);
|
||||||
|
if (flags == -1) {
|
||||||
|
GDBStubHangup(stub);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fcntl(stub->connection, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
// TODO: parse message
|
||||||
|
}
|
||||||
|
|
||||||
static void _readMemory(struct GDBStub* stub, const char* message) {
|
static void _readMemory(struct GDBStub* stub, const char* message) {
|
||||||
const char* readAddress = message;
|
const char* readAddress = message;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
@ -182,29 +233,79 @@ static void _readRegister(struct GDBStub* stub, const char* message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _processQReadCommand(struct GDBStub* stub, const char* message) {
|
static void _processQReadCommand(struct GDBStub* stub, const char* message) {
|
||||||
if (!strncmp("HostInfo", message, 8)) {
|
stub->outgoing[0] = '\0';
|
||||||
|
if (!strncmp("HostInfo#", message, 9)) {
|
||||||
_writeHostInfo(stub);
|
_writeHostInfo(stub);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stub->outgoing[0] = '\0';
|
if (!strncmp("Attached#", message, 9)) {
|
||||||
|
strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
|
||||||
|
} else if (!strncmp("VAttachOrWaitSupported#", message, 23)) {
|
||||||
|
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||||
|
} else if (!strncmp("C#", message, 2)) {
|
||||||
|
strncpy(stub->outgoing, "QC1", GDB_STUB_MAX_LINE - 4);
|
||||||
|
} else if (!strncmp("fThreadInfo#", message, 12)) {
|
||||||
|
strncpy(stub->outgoing, "m1", GDB_STUB_MAX_LINE - 4);
|
||||||
|
} else if (!strncmp("sThreadInfo#", message, 12)) {
|
||||||
|
strncpy(stub->outgoing, "l", GDB_STUB_MAX_LINE - 4);
|
||||||
|
}
|
||||||
_sendMessage(stub);
|
_sendMessage(stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
|
static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
|
||||||
stub->outgoing[0] = '\0';
|
stub->outgoing[0] = '\0';
|
||||||
|
if (!strncmp("StartNoAckMode#", message, 16)) {
|
||||||
|
stub->lineAck = GDB_ACK_OFF;
|
||||||
|
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||||
|
}
|
||||||
_sendMessage(stub);
|
_sendMessage(stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _processVMajCommand(struct GDBStub* stub, const char* message) {
|
static void _processVWriteCommand(struct GDBStub* stub, const char* message) {
|
||||||
stub->outgoing[0] = '\0';
|
stub->outgoing[0] = '\0';
|
||||||
_sendMessage(stub);
|
_sendMessage(stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _processVMinCommand(struct GDBStub* stub, const char* message) {
|
static void _processVReadCommand(struct GDBStub* stub, const char* message) {
|
||||||
stub->outgoing[0] = '\0';
|
stub->outgoing[0] = '\0';
|
||||||
|
if (!strncmp("Attach", message, 6)) {
|
||||||
|
strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
|
||||||
|
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL);
|
||||||
|
}
|
||||||
_sendMessage(stub);
|
_sendMessage(stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _setBreakpoint(struct GDBStub* stub, const char* message) {
|
||||||
|
switch (message[0]) {
|
||||||
|
case '0': // Memory breakpoints are not currently supported
|
||||||
|
case '1': {
|
||||||
|
const char* readAddress = &message[2];
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
if (readAddress[i] == ',') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t address = _hex2int(readAddress, i);
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
if (readAddress[i] == '#') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t kind = _hex2int(readAddress, i); // We don't use this in hardware watchpoints
|
||||||
|
ARMDebuggerSetBreakpoint(&stub->d, address);
|
||||||
|
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||||
|
_sendMessage(stub);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
// TODO: Watchpoints
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
|
size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
|
||||||
uint8_t checksum = 0;
|
uint8_t checksum = 0;
|
||||||
int parsed = 1;
|
int parsed = 1;
|
||||||
|
@ -219,6 +320,9 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
|
||||||
case '$':
|
case '$':
|
||||||
++message;
|
++message;
|
||||||
break;
|
break;
|
||||||
|
case '\x03':
|
||||||
|
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL);
|
||||||
|
return parsed;
|
||||||
default:
|
default:
|
||||||
_nak(stub);
|
_nak(stub);
|
||||||
return parsed;
|
return parsed;
|
||||||
|
@ -254,9 +358,21 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
|
||||||
_ack(stub);
|
_ack(stub);
|
||||||
++message;
|
++message;
|
||||||
switch (messageType) {
|
switch (messageType) {
|
||||||
|
case '?':
|
||||||
|
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
|
||||||
|
_sendMessage(stub);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
_continue(stub, message);
|
||||||
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
_readGPRs(stub, message);
|
_readGPRs(stub, message);
|
||||||
break;
|
break;
|
||||||
|
case 'H':
|
||||||
|
// This is faked because we only have one thread
|
||||||
|
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||||
|
_sendMessage(stub);
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
_readMemory(stub, message);
|
_readMemory(stub, message);
|
||||||
break;
|
break;
|
||||||
|
@ -270,10 +386,13 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
|
||||||
_processQReadCommand(stub, message);
|
_processQReadCommand(stub, message);
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
_processVMajCommand(stub, message);
|
_processVWriteCommand(stub, message);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
_processVMinCommand(stub, message);
|
_processVReadCommand(stub, message);
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
_setBreakpoint(stub, message);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_error(stub, GDB_UNSUPPORTED_COMMAND);
|
_error(stub, GDB_UNSUPPORTED_COMMAND);
|
||||||
|
@ -287,7 +406,8 @@ void GDBStubCreate(struct GDBStub* stub) {
|
||||||
stub->connection = -1;
|
stub->connection = -1;
|
||||||
stub->d.init = 0;
|
stub->d.init = 0;
|
||||||
stub->d.deinit = _gdbStubDeinit;
|
stub->d.deinit = _gdbStubDeinit;
|
||||||
stub->d.paused = 0;
|
stub->d.paused = _gdbStubPoll;
|
||||||
|
stub->d.entered = _gdbStubEntered;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) {
|
int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) {
|
||||||
|
@ -336,6 +456,9 @@ void GDBStubHangup(struct GDBStub* stub) {
|
||||||
close(stub->connection);
|
close(stub->connection);
|
||||||
stub->connection = -1;
|
stub->connection = -1;
|
||||||
}
|
}
|
||||||
|
if (stub->d.state == DEBUGGER_PAUSED) {
|
||||||
|
stub->d.state = DEBUGGER_RUNNING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDBStubShutdown(struct GDBStub* stub) {
|
void GDBStubShutdown(struct GDBStub* stub) {
|
||||||
|
@ -349,16 +472,15 @@ void GDBStubShutdown(struct GDBStub* stub) {
|
||||||
void GDBStubUpdate(struct GDBStub* stub) {
|
void GDBStubUpdate(struct GDBStub* stub) {
|
||||||
if (stub->connection == -1) {
|
if (stub->connection == -1) {
|
||||||
stub->connection = accept(stub->socket, 0, 0);
|
stub->connection = accept(stub->socket, 0, 0);
|
||||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stub->connection >= 0) {
|
if (stub->connection >= 0) {
|
||||||
int flags = fcntl(stub->connection, F_GETFL);
|
int flags = fcntl(stub->connection, F_GETFL);
|
||||||
if (flags == -1) {
|
if (flags == -1) {
|
||||||
goto connectionLost;
|
goto connectionLost;
|
||||||
}
|
}
|
||||||
flags |= O_NONBLOCK;
|
|
||||||
fcntl(stub->connection, F_SETFL, flags | O_NONBLOCK);
|
fcntl(stub->connection, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
|
||||||
|
} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
goto connectionLost;
|
goto connectionLost;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue