diff --git a/src/debugger/debugger.h b/src/debugger/debugger.h index 24290a89f..2ab3dfcdf 100644 --- a/src/debugger/debugger.h +++ b/src/debugger/debugger.h @@ -25,6 +25,7 @@ struct DebugMemoryShim { enum DebuggerEntryReason { DEBUGGER_ENTER_MANUAL, + DEBUGGER_ENTER_ATTACHED, DEBUGGER_ENTER_BREAKPOINT, DEBUGGER_ENTER_WATCHPOINT, DEBUGGER_ENTER_ILLEGAL_OP diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index 3f6ecac45..1060d8d48 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,8 @@ enum { MACH_O_ARM_V4T = 5 }; +static void _sendMessage(struct GDBStub* stub); + static void _gdbStubDeinit(struct ARMDebugger* debugger) { struct GDBStub* stub = (struct GDBStub*) debugger; 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) { char ack = '+'; send(stub->connection, &ack, 1, 0); @@ -115,6 +153,19 @@ static void _writeHostInfo(struct GDBStub* 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) { const char* readAddress = message; unsigned i; @@ -182,29 +233,79 @@ static void _readRegister(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); 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); } static void _processQWriteCommand(struct GDBStub* stub, const char* message) { 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); } -static void _processVMajCommand(struct GDBStub* stub, const char* message) { +static void _processVWriteCommand(struct GDBStub* stub, const char* message) { stub->outgoing[0] = '\0'; _sendMessage(stub); } -static void _processVMinCommand(struct GDBStub* stub, const char* message) { +static void _processVReadCommand(struct GDBStub* stub, const char* message) { 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); } +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) { uint8_t checksum = 0; int parsed = 1; @@ -219,6 +320,9 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) { case '$': ++message; break; + case '\x03': + ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL); + return parsed; default: _nak(stub); return parsed; @@ -254,9 +358,21 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) { _ack(stub); ++message; 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': _readGPRs(stub, message); 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': _readMemory(stub, message); break; @@ -270,10 +386,13 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) { _processQReadCommand(stub, message); break; case 'V': - _processVMajCommand(stub, message); + _processVWriteCommand(stub, message); break; case 'v': - _processVMinCommand(stub, message); + _processVReadCommand(stub, message); + break; + case 'Z': + _setBreakpoint(stub, message); break; default: _error(stub, GDB_UNSUPPORTED_COMMAND); @@ -287,7 +406,8 @@ void GDBStubCreate(struct GDBStub* stub) { stub->connection = -1; stub->d.init = 0; 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) { @@ -336,6 +456,9 @@ void GDBStubHangup(struct GDBStub* stub) { close(stub->connection); stub->connection = -1; } + if (stub->d.state == DEBUGGER_PAUSED) { + stub->d.state = DEBUGGER_RUNNING; + } } void GDBStubShutdown(struct GDBStub* stub) { @@ -349,16 +472,15 @@ void GDBStubShutdown(struct GDBStub* stub) { void GDBStubUpdate(struct GDBStub* stub) { if (stub->connection == -1) { stub->connection = accept(stub->socket, 0, 0); - if (errno == EWOULDBLOCK || errno == EAGAIN) { - return; - } if (stub->connection >= 0) { int flags = fcntl(stub->connection, F_GETFL); if (flags == -1) { goto connectionLost; } - 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 { goto connectionLost; }