diff options
Diffstat (limited to 'src/base')
-rw-r--r-- | src/base/remote_gdb.cc | 834 | ||||
-rw-r--r-- | src/base/remote_gdb.hh | 125 |
2 files changed, 481 insertions, 478 deletions
diff --git a/src/base/remote_gdb.cc b/src/base/remote_gdb.cc index 57fccd978..f7b0253a5 100644 --- a/src/base/remote_gdb.cc +++ b/src/base/remote_gdb.cc @@ -147,6 +147,13 @@ using namespace std; using namespace TheISA; +static const char GDBStart = '$'; +static const char GDBEnd = '#'; +static const char GDBGoodP = '+'; +static const char GDBBadP = '-'; + +static const int GDBPacketBufLen = 1024; + #ifndef NDEBUG vector<BaseRemoteGDB *> debuggers; @@ -188,8 +195,7 @@ GDBListener::GDBListener(BaseRemoteGDB *g, int p) GDBListener::~GDBListener() { - if (inputEvent) - delete inputEvent; + delete inputEvent; } string @@ -319,6 +325,8 @@ void BaseRemoteGDB::detach() { attached = false; + active = false; + clearSingleStep(); close(fd); fd = -1; @@ -326,75 +334,31 @@ BaseRemoteGDB::detach() DPRINTFN("remote gdb detached\n"); } -const char * -BaseRemoteGDB::gdb_command(char cmd) -{ - switch (cmd) { - case GDBSignal: return "KGDB_SIGNAL"; - case GDBSetBaud: return "KGDB_SET_BAUD"; - case GDBSetBreak: return "KGDB_SET_BREAK"; - case GDBCont: return "KGDB_CONT"; - case GDBAsyncCont: return "KGDB_ASYNC_CONT"; - case GDBDebug: return "KGDB_DEBUG"; - case GDBDetach: return "KGDB_DETACH"; - case GDBRegR: return "KGDB_REG_R"; - case GDBRegW: return "KGDB_REG_W"; - case GDBSetThread: return "KGDB_SET_THREAD"; - case GDBCycleStep: return "KGDB_CYCLE_STEP"; - case GDBSigCycleStep: return "KGDB_SIG_CYCLE_STEP"; - case GDBKill: return "KGDB_KILL"; - case GDBMemW: return "KGDB_MEM_W"; - case GDBMemR: return "KGDB_MEM_R"; - case GDBSetReg: return "KGDB_SET_REG"; - case GDBReadReg: return "KGDB_READ_REG"; - case GDBQueryVar: return "KGDB_QUERY_VAR"; - case GDBSetVar: return "KGDB_SET_VAR"; - case GDBReset: return "KGDB_RESET"; - case GDBStep: return "KGDB_STEP"; - case GDBAsyncStep: return "KGDB_ASYNC_STEP"; - case GDBThreadAlive: return "KGDB_THREAD_ALIVE"; - case GDBTargetExit: return "KGDB_TARGET_EXIT"; - case GDBBinaryDload: return "KGDB_BINARY_DLOAD"; - case GDBClrHwBkpt: return "KGDB_CLR_HW_BKPT"; - case GDBSetHwBkpt: return "KGDB_SET_HW_BKPT"; - case GDBStart: return "KGDB_START"; - case GDBEnd: return "KGDB_END"; - case GDBGoodP: return "KGDB_GOODP"; - case GDBBadP: return "KGDB_BADP"; - default: return "KGDB_UNKNOWN"; - } -} - ///////////////////////// // // -bool -BaseRemoteGDB::getbyte(uint8_t &b) +uint8_t +BaseRemoteGDB::getbyte() { - if (::read(fd, &b, sizeof(b)) == sizeof(b)) { - return true; - } else { - warn("Couldn't read data from debugger, detaching."); - detach(); - return false; - } + uint8_t b; + if (::read(fd, &b, sizeof(b)) == sizeof(b)) + return b; + + throw BadClient("Couldn't read data from debugger."); } -bool +void BaseRemoteGDB::putbyte(uint8_t b) { - if (::write(fd, &b, sizeof(b)) == sizeof(b)) { - return true; - } else { - warn("Couldn't write data to the debugger, detaching."); - detach(); - return false; - } + if (::write(fd, &b, sizeof(b)) == sizeof(b)) + return; + + throw BadClient("Couldn't write data to the debugger."); } // Send a packet to gdb -ssize_t +void BaseRemoteGDB::send(const char *bp) { const char *p; @@ -404,28 +368,22 @@ BaseRemoteGDB::send(const char *bp) do { p = bp; - //Start sending a packet - if (!putbyte(GDBStart)) - return -1; - //Send the contents, and also keep a check sum. + // Start sending a packet + putbyte(GDBStart); + // Send the contents, and also keep a check sum. for (csum = 0; (c = *p); p++) { - if (!putbyte(c)) - return -1; + putbyte(c); csum += c; } - if (//Send the ending character. - !putbyte(GDBEnd) || - //Sent the checksum. - !putbyte(i2digit(csum >> 4)) || - !putbyte(i2digit(csum))) { - return -1; - } - //Try transmitting over and over again until the other end doesn't - //send an error back. - if (!getbyte(c)) - return -1; + // Send the ending character. + putbyte(GDBEnd); + // Send the checksum. + putbyte(i2digit(csum >> 4)); + putbyte(i2digit(csum)); + // Try transmitting over and over again until the other end doesn't + // send an error back. + c = getbyte(); } while ((c & 0x7f) == GDBBadP); - return 0; } // Receive a packet from gdb @@ -440,17 +398,13 @@ BaseRemoteGDB::recv(char *bp, int maxlen) do { p = bp; csum = len = 0; - //Find the beginning of a packet - do { - if (!getbyte(c)) - return -1; - } while (c != GDBStart); - - //Read until you find the end of the data in the packet, and keep - //track of the check sum. + // Find the beginning of a packet + while ((c = getbyte()) != GDBStart); + + // Read until you find the end of the data in the packet, and keep + // track of the check sum. while (len < maxlen) { - if (!getbyte(c)) - return -1; + c = getbyte(); if (c == GDBEnd) break; c &= 0x7f; @@ -459,46 +413,40 @@ BaseRemoteGDB::recv(char *bp, int maxlen) len++; } - //Mask the check sum, and terminate the command string. + // Mask the check sum, and terminate the command string. csum &= 0xff; *p = '\0'; - //If the command was too long, report an error. + // If the command was too long, report an error. if (len >= maxlen) { - if (!putbyte(GDBBadP)) - return -1; + putbyte(GDBBadP); continue; } - //Bring in the checksum. If the check sum matches, csum will be 0. - uint8_t csum1, csum2; - if (!getbyte(csum1) || !getbyte(csum2)) - return -1; - csum -= digit2i(csum1) * 16; - csum -= digit2i(csum2); + // Bring in the checksum. If the check sum matches, csum will be 0. + csum -= digit2i(getbyte()) * 16; + csum -= digit2i(getbyte()); - //If the check sum was correct + // If the check sum was correct if (csum == 0) { - //Report that the packet was received correctly - if (!putbyte(GDBGoodP)) - return -1; + // Report that the packet was received correctly + putbyte(GDBGoodP); // Sequence present? if (bp[2] == ':') { - if (!putbyte(bp[0]) || !putbyte(bp[1])) - return -1; + putbyte(bp[0]); + putbyte(bp[1]); len -= 3; memcpy(bp, bp+3, len); } break; } - //Otherwise, report that there was a mistake. - if (!putbyte(GDBBadP)) - return -1; + // Otherwise, report that there was a mistake. + putbyte(GDBBadP); } while (1); - DPRINTF(GDBRecv, "recv: %s: %s\n", gdb_command(*bp), bp); + DPRINTF(GDBRecv, "recv: %s\n", bp); - return (len); + return len; } // Read bytes from kernel address space for debugger. @@ -632,60 +580,56 @@ BaseRemoteGDB::HardBreakpoint::process(ThreadContext *tc) gdb->trap(SIGTRAP); } -bool +void BaseRemoteGDB::insertSoftBreak(Addr addr, size_t len) { if (!checkBpLen(len)) - panic("invalid length\n"); + throw BadClient("Invalid breakpoint length\n"); return insertHardBreak(addr, len); } -bool +void BaseRemoteGDB::removeSoftBreak(Addr addr, size_t len) { if (!checkBpLen(len)) - panic("invalid length\n"); + throw BadClient("Invalid breakpoint length.\n"); return removeHardBreak(addr, len); } -bool +void BaseRemoteGDB::insertHardBreak(Addr addr, size_t len) { if (!checkBpLen(len)) - panic("invalid length\n"); + throw BadClient("Invalid breakpoint length\n"); - DPRINTF(GDBMisc, "inserting hardware breakpoint at %#x\n", addr); + DPRINTF(GDBMisc, "Inserting hardware breakpoint at %#x\n", addr); HardBreakpoint *&bkpt = hardBreakMap[addr]; if (bkpt == 0) bkpt = new HardBreakpoint(this, addr); bkpt->refcount++; - - return true; } -bool +void BaseRemoteGDB::removeHardBreak(Addr addr, size_t len) { if (!checkBpLen(len)) - panic("invalid length\n"); + throw BadClient("Invalid breakpoint length\n"); - DPRINTF(GDBMisc, "removing hardware breakpoint at %#x\n", addr); + DPRINTF(GDBMisc, "Removing hardware breakpoint at %#x\n", addr); break_iter_t i = hardBreakMap.find(addr); if (i == hardBreakMap.end()) - return false; + throw CmdError("E0C"); HardBreakpoint *hbp = (*i).second; if (--hbp->refcount == 0) { delete hbp; hardBreakMap.erase(i); } - - return true; } void @@ -703,19 +647,316 @@ BaseRemoteGDB::clearTempBreakpoint(Addr &bkpt) bkpt = 0; } +enum GdbBreakpointType { + GdbSoftBp = '0', + GdbHardBp = '1', + GdbWriteWp = '2', + GdbReadWp = '3', + GdbAccWp = '4', +}; + const char * BaseRemoteGDB::break_type(char c) { switch(c) { - case '0': return "software breakpoint"; - case '1': return "hardware breakpoint"; - case '2': return "write watchpoint"; - case '3': return "read watchpoint"; - case '4': return "access watchpoint"; + case GdbSoftBp: return "software breakpoint"; + case GdbHardBp: return "hardware breakpoint"; + case GdbWriteWp: return "write watchpoint"; + case GdbReadWp: return "read watchpoint"; + case GdbAccWp: return "access watchpoint"; default: return "unknown breakpoint/watchpoint"; } } +std::map<char, GdbCommand> BaseRemoteGDB::command_map = { + // last signal + { '?', { "KGDB_SIGNAL", &BaseRemoteGDB::cmd_signal } }, + // set baud (deprecated) + { 'b', { "KGDB_SET_BAUD", &BaseRemoteGDB::cmd_unsupported } }, + // set breakpoint (deprecated) + { 'B', { "KGDB_SET_BREAK", &BaseRemoteGDB::cmd_unsupported } }, + // resume + { 'c', { "KGDB_CONT", &BaseRemoteGDB::cmd_cont } }, + // continue with signal + { 'C', { "KGDB_ASYNC_CONT", &BaseRemoteGDB::cmd_async_cont } }, + // toggle debug flags (deprecated) + { 'd', { "KGDB_DEBUG", &BaseRemoteGDB::cmd_unsupported } }, + // detach remote gdb + { 'D', { "KGDB_DETACH", &BaseRemoteGDB::cmd_detach } }, + // read general registers + { 'g', { "KGDB_REG_R", &BaseRemoteGDB::cmd_reg_r } }, + // write general registers + { 'G', { "KGDB_REG_W", &BaseRemoteGDB::cmd_reg_w } }, + // set thread + { 'H', { "KGDB_SET_THREAD", &BaseRemoteGDB::cmd_set_thread } }, + // step a single cycle + { 'i', { "KGDB_CYCLE_STEP", &BaseRemoteGDB::cmd_unsupported } }, + // signal then cycle step + { 'I', { "KGDB_SIG_CYCLE_STEP", &BaseRemoteGDB::cmd_unsupported } }, + // kill program + { 'k', { "KGDB_KILL", &BaseRemoteGDB::cmd_detach } }, + // read memory + { 'm', { "KGDB_MEM_R", &BaseRemoteGDB::cmd_mem_r } }, + // write memory + { 'M', { "KGDB_MEM_W", &BaseRemoteGDB::cmd_mem_w } }, + // read register + { 'p', { "KGDB_READ_REG", &BaseRemoteGDB::cmd_unsupported } }, + // write register + { 'P', { "KGDB_SET_REG", &BaseRemoteGDB::cmd_unsupported } }, + // query variable + { 'q', { "KGDB_QUERY_VAR", &BaseRemoteGDB::cmd_query_var } }, + // set variable + { 'Q', { "KGDB_SET_VAR", &BaseRemoteGDB::cmd_unsupported } }, + // reset system (deprecated) + { 'r', { "KGDB_RESET", &BaseRemoteGDB::cmd_unsupported } }, + // step + { 's', { "KGDB_STEP", &BaseRemoteGDB::cmd_step } }, + // signal and step + { 'S', { "KGDB_ASYNC_STEP", &BaseRemoteGDB::cmd_async_step } }, + // find out if the thread is alive + { 'T', { "KGDB_THREAD_ALIVE", &BaseRemoteGDB::cmd_unsupported } }, + // target exited + { 'W', { "KGDB_TARGET_EXIT", &BaseRemoteGDB::cmd_unsupported } }, + // write memory + { 'X', { "KGDB_BINARY_DLOAD", &BaseRemoteGDB::cmd_unsupported } }, + // remove breakpoint or watchpoint + { 'z', { "KGDB_CLR_HW_BKPT", &BaseRemoteGDB::cmd_clr_hw_bkpt } }, + // insert breakpoint or watchpoint + { 'Z', { "KGDB_SET_HW_BKPT", &BaseRemoteGDB::cmd_set_hw_bkpt } }, +}; + + +bool +BaseRemoteGDB::cmd_unsupported(GdbCommand::Context &ctx) +{ + DPRINTF(GDBMisc, "Unsupported command: %s\n", ctx.cmd->name); + DDUMP(GDBMisc, ctx.data, ctx.len); + throw Unsupported(); +} + + +bool +BaseRemoteGDB::cmd_signal(GdbCommand::Context &ctx) +{ + send(csprintf("S%02x", ctx.type).c_str()); + return true; +} + +bool +BaseRemoteGDB::cmd_cont(GdbCommand::Context &ctx) +{ + const char *p = ctx.data; + if (ctx.len) { + Addr newPc = hex2i(&p); + context->pcState(newPc); + } + clearSingleStep(); + return false; +} + +bool +BaseRemoteGDB::cmd_async_cont(GdbCommand::Context &ctx) +{ + const char *p = ctx.data; + hex2i(&p); + if (*p++ == ';') { + Addr newPc = hex2i(&p); + context->pcState(newPc); + } + clearSingleStep(); + return false; +} + +bool +BaseRemoteGDB::cmd_detach(GdbCommand::Context &ctx) +{ + detach(); + return false; +} + +bool +BaseRemoteGDB::cmd_reg_r(GdbCommand::Context &ctx) +{ + char buf[2 * regCachePtr->size() + 1]; + buf[2 * regCachePtr->size()] = '\0'; + mem2hex(buf, regCachePtr->data(), regCachePtr->size()); + send(buf); + return true; +} + +bool +BaseRemoteGDB::cmd_reg_w(GdbCommand::Context &ctx) +{ + const char *p = ctx.data; + p = hex2mem(regCachePtr->data(), p, regCachePtr->size()); + if (p == NULL || *p != '\0') + throw CmdError("E01"); + + regCachePtr->setRegs(context); + send("OK"); + + return true; +} + +bool +BaseRemoteGDB::cmd_set_thread(GdbCommand::Context &ctx) +{ + const char *p = ctx.data + 1; // Ignore the subcommand byte. + if (hex2i(&p) != 0) + throw CmdError("E01"); + send("OK"); + return true; +} + +bool +BaseRemoteGDB::cmd_mem_r(GdbCommand::Context &ctx) +{ + const char *p = ctx.data; + Addr addr = hex2i(&p); + if (*p++ != ',') + throw CmdError("E02"); + size_t len = hex2i(&p); + if (*p != '\0') + throw CmdError("E03"); + if (!acc(addr, len)) + throw CmdError("E05"); + + char buf[len]; + if (!read(addr, len, buf)) + throw CmdError("E05"); + + char temp[2 * len + 1]; + temp[2 * len] = '\0'; + mem2hex(temp, buf, len); + send(temp); + return true; +} + +bool +BaseRemoteGDB::cmd_mem_w(GdbCommand::Context &ctx) +{ + const char *p = ctx.data; + Addr addr = hex2i(&p); + if (*p++ != ',') + throw CmdError("E06"); + size_t len = hex2i(&p); + if (*p++ != ':') + throw CmdError("E07"); + if (len * 2 > ctx.len - (p - ctx.data)) + throw CmdError("E08"); + char buf[len]; + p = (char *)hex2mem(buf, p, len); + if (p == NULL) + throw CmdError("E09"); + if (!acc(addr, len)) + throw CmdError("E0A"); + if (!write(addr, len, buf)) + throw CmdError("E0B"); + send("OK"); + return true; +} + +bool +BaseRemoteGDB::cmd_query_var(GdbCommand::Context &ctx) +{ + if (string(ctx.data, ctx.len - 1) != "C") + throw Unsupported(); + send("QC0"); + return true; +} + +bool +BaseRemoteGDB::cmd_async_step(GdbCommand::Context &ctx) +{ + const char *p = ctx.data; + hex2i(&p); // Ignore the subcommand byte. + if (*p++ == ';') { + Addr newPc = hex2i(&p); + context->pcState(newPc); + } + setSingleStep(); + return false; +} + +bool +BaseRemoteGDB::cmd_step(GdbCommand::Context &ctx) +{ + if (ctx.len) { + const char *p = ctx.data; + Addr newPc = hex2i(&p); + context->pcState(newPc); + } + setSingleStep(); + return false; +} + +bool +BaseRemoteGDB::cmd_clr_hw_bkpt(GdbCommand::Context &ctx) +{ + const char *p = ctx.data; + char subcmd = *p++; + if (*p++ != ',') + throw CmdError("E0D"); + Addr addr = hex2i(&p); + if (*p++ != ',') + throw CmdError("E0D"); + size_t len = hex2i(&p); + + DPRINTF(GDBMisc, "clear %s, addr=%#x, len=%d\n", + break_type(subcmd), addr, len); + + switch (subcmd) { + case GdbSoftBp: + removeSoftBreak(addr, len); + break; + case GdbHardBp: + removeHardBreak(addr, len); + break; + case GdbWriteWp: + case GdbReadWp: + case GdbAccWp: + default: // unknown + throw Unsupported(); + } + send("OK"); + + return true; +} + +bool +BaseRemoteGDB::cmd_set_hw_bkpt(GdbCommand::Context &ctx) +{ + const char *p = ctx.data; + char subcmd = *p++; + if (*p++ != ',') + throw CmdError("E0D"); + Addr addr = hex2i(&p); + if (*p++ != ',') + throw CmdError("E0D"); + size_t len = hex2i(&p); + + DPRINTF(GDBMisc, "set %s, addr=%#x, len=%d\n", + break_type(subcmd), addr, len); + + switch (subcmd) { + case GdbSoftBp: + insertSoftBreak(addr, len); + break; + case GdbHardBp: + insertHardBreak(addr, len); + break; + case GdbWriteWp: + case GdbReadWp: + case GdbAccWp: + default: // unknown + throw Unsupported(); + } + send("OK"); + + return true; +} + + // This function does all command processing for interfacing to a // remote gdb. Note that the error codes are ignored by gdb at // present, but might eventually become meaningful. (XXX) It might @@ -724,24 +965,10 @@ BaseRemoteGDB::break_type(char c) bool BaseRemoteGDB::trap(int type) { - uint64_t val; - size_t datalen, len; - char data[GDBPacketBufLen + 1]; - size_t bufferSize; - const char *p; - char command, subcmd; - string var; - bool ret; if (!attached) return false; - unique_ptr<BaseRemoteGDB::BaseGdbRegCache> regCache(gdbRegs()); - - bufferSize = regCache->size() * 2 + 256; - unique_ptr<char[]> buffer_mem(new char[bufferSize]); - char *buffer = buffer_mem.get(); - DPRINTF(GDBMisc, "trap: PC=%s\n", context->pcState()); clearSingleStep(); @@ -760,298 +987,50 @@ BaseRemoteGDB::trap(int type) active = true; } else { // Tell remote host that an exception has occurred. - snprintf(buffer, bufferSize, "S%02x", type); - if (send(buffer) < 0) - return true; + send(csprintf("S%02x", type).c_str()); } // Stick frame regs into our reg cache. - regCache->getRegs(context); - - for (;;) { - int recved = recv(data, sizeof(data)); - if (recved < 0) - return true; - datalen = recved; - data[sizeof(data) - 1] = 0; // Sentinel - command = data[0]; - subcmd = 0; - p = data + 1; - switch (command) { - - case GDBSignal: - // if this command came from a running gdb, answer it -- - // the other guy has no way of knowing if we're in or out - // of this loop when he issues a "remote-signal". - snprintf(buffer, bufferSize, - "S%02x", type); - if (send(buffer) < 0) - return true; - continue; - - case GDBRegR: - if (2 * regCache->size() > bufferSize) - panic("buffer too small"); - - mem2hex(buffer, regCache->data(), regCache->size()); - if (send(buffer) < 0) - return true; - continue; - - case GDBRegW: - p = hex2mem(regCache->data(), p, regCache->size()); - if (p == NULL || *p != '\0') { - if (send("E01") < 0) - return true; - } else { - regCache->setRegs(context); - if (send("OK") < 0) - return true; - } - continue; - - case GDBMemR: - val = hex2i(&p); - if (*p++ != ',') { - if (send("E02") < 0) - return true; - continue; - } - len = hex2i(&p); - if (*p != '\0') { - if (send("E03") < 0) - return true; - continue; - } - if (len > bufferSize) { - if (send("E04") < 0) - return true; - continue; - } - if (!acc(val, len)) { - if (send("E05") < 0) - return true; - continue; - } - - if (read(val, (size_t)len, buffer)) { - // variable length array would be nice, but C++ doesn't - // officially support those... - char *temp = new char[2*len+1]; - mem2hex(temp, buffer, len); - if (send(temp) < 0) { - delete [] temp; - return true; - } - delete [] temp; - } else { - if (send("E05") < 0) - return true; - } - continue; + regCachePtr = gdbRegs(); + regCachePtr->getRegs(context); - case GDBMemW: - val = hex2i(&p); - if (*p++ != ',') { - if (send("E06") < 0) - return true; - continue; - } - len = hex2i(&p); - if (*p++ != ':') { - if (send("E07") < 0) - return true; - continue; - } - if (len > datalen - (p - data)) { - if (send("E08") < 0) - return true; - continue; - } - p = hex2mem(buffer, p, bufferSize); - if (p == NULL) { - if (send("E09") < 0) - return true; - continue; - } - if (!acc(val, len)) { - if (send("E0A") < 0) - return true; - continue; - } - if (write(val, (size_t)len, buffer)) { - if (send("OK") < 0) - return true; - } else { - if (send("E0B") < 0) - return true; - } - continue; - - case GDBSetThread: - subcmd = *p++; - val = hex2i(&p); - if (val == 0) { - if (send("OK") < 0) - return true; - } else { - if (send("E01") < 0) - return true; - } - continue; - - case GDBDetach: - case GDBKill: - active = false; - clearSingleStep(); - detach(); - return true; - - case GDBAsyncCont: - subcmd = hex2i(&p); - if (*p++ == ';') { - val = hex2i(&p); - context->pcState(val); - } - clearSingleStep(); - return true; - - case GDBCont: - if (p - data < (ptrdiff_t)datalen) { - val = hex2i(&p); - context->pcState(val); - } - clearSingleStep(); - return true; - - case GDBAsyncStep: - subcmd = hex2i(&p); - if (*p++ == ';') { - val = hex2i(&p); - context->pcState(val); - } - setSingleStep(); - return true; - - case GDBStep: - if (p - data < (ptrdiff_t)datalen) { - val = hex2i(&p); - context->pcState(val); - } - setSingleStep(); - return true; - - case GDBClrHwBkpt: - subcmd = *p++; - if (*p++ != ',' && send("E0D") < 0) - return true; - val = hex2i(&p); - if (*p++ != ',' && send("E0D") < 0) - return true; - len = hex2i(&p); - - DPRINTF(GDBMisc, "clear %s, addr=%#x, len=%d\n", - break_type(subcmd), val, len); - - ret = false; - - switch (subcmd) { - case '0': // software breakpoint - ret = removeSoftBreak(val, len); - break; - - case '1': // hardware breakpoint - ret = removeHardBreak(val, len); - break; + char data[GDBPacketBufLen + 1]; + GdbCommand::Context cmdCtx; + cmdCtx.type = type; + cmdCtx.data = &data[1]; - case '2': // write watchpoint - case '3': // read watchpoint - case '4': // access watchpoint - default: // unknown - if (send("") < 0) - return true; - break; + for (;;) { + try { + size_t datalen = recv(data, sizeof(data)); + if (datalen < 1) + throw BadClient(); + + data[datalen] = 0; // Sentinel + cmdCtx.cmd_byte = data[0]; + cmdCtx.len = datalen - 1; + + auto cmdIt = command_map.find(cmdCtx.cmd_byte); + if (cmdIt == command_map.end()) { + DPRINTF(GDBMisc, "Unknown command: %c(%#x)\n", + cmdCtx.cmd_byte, cmdCtx.cmd_byte); + throw Unsupported(); } + cmdCtx.cmd = &(cmdIt->second); - if (send(ret ? "OK" : "E0C") < 0) - return true; - continue; - - case GDBSetHwBkpt: - subcmd = *p++; - if (*p++ != ',' && send("E0D") < 0) - return true; - val = hex2i(&p); - if (*p++ != ',' && send("E0D") < 0) - return true; - len = hex2i(&p); - - DPRINTF(GDBMisc, "set %s, addr=%#x, len=%d\n", - break_type(subcmd), val, len); - - ret = false; - - switch (subcmd) { - case '0': // software breakpoint - ret = insertSoftBreak(val, len); + if (!(this->*(cmdCtx.cmd->func))(cmdCtx)) break; - case '1': // hardware breakpoint - ret = insertHardBreak(val, len); - break; - - case '2': // write watchpoint - case '3': // read watchpoint - case '4': // access watchpoint - default: // unknown - if (send("") < 0) - return true; - break; - } - - if (send(ret ? "OK" : "E0C") < 0) - return true; - continue; - - case GDBQueryVar: - var = string(p, datalen - 1); - if (var == "C") { - if (send("QC0") < 0) - return true; - } else { - if (send("") < 0) - return true; - } - continue; - - case GDBSetBaud: - case GDBSetBreak: - case GDBDebug: - case GDBCycleStep: - case GDBSigCycleStep: - case GDBReadReg: - case GDBSetVar: - case GDBReset: - case GDBThreadAlive: - case GDBTargetExit: - case GDBBinaryDload: - // Unsupported command - DPRINTF(GDBMisc, "Unsupported command: %s\n", - gdb_command(command)); - DDUMP(GDBMisc, (uint8_t *)data, datalen); - if (send("") < 0) - return true; - continue; - - default: - // Unknown command. - DPRINTF(GDBMisc, "Unknown command: %c(%#x)\n", - command, command); - if (send("") < 0) - return true; - continue; - - + } catch (BadClient &e) { + if (e.warning) + warn(e.warning); + detach(); + break; + } catch (Unsupported &e) { + send(""); + } catch (CmdError &e) { + send(e.error.c_str()); + } catch (...) { + panic("Unrecognzied GDB exception."); } } @@ -1068,7 +1047,6 @@ BaseRemoteGDB::digit2i(char c) else if (c >= 'a' && c <= 'f') return (c - 'a' + 10); else if (c >= 'A' && c <= 'F') - return (c - 'A' + 10); else return (-1); @@ -1114,7 +1092,7 @@ BaseRemoteGDB::hex2mem(char *vdst, const char *src, int maxlen) return (NULL); *dst++ = (msb << 4) | lsb; } - return (src); + return src; } // Convert an hex string into an integer. @@ -1133,6 +1111,6 @@ BaseRemoteGDB::hex2i(const char **srcp) src++; } *srcp = src; - return (r); + return r; } diff --git a/src/base/remote_gdb.hh b/src/base/remote_gdb.hh index b3a2b5c60..b860f5d33 100644 --- a/src/base/remote_gdb.hh +++ b/src/base/remote_gdb.hh @@ -36,7 +36,9 @@ #include <sys/signal.h> +#include <exception> #include <map> +#include <string> #include "arch/types.hh" #include "base/intmath.hh" @@ -49,43 +51,28 @@ class ThreadContext; class GDBListener; -enum GDBCommands +class BaseRemoteGDB; + +struct GdbCommand { - GDBSignal = '?', // last signal - GDBSetBaud = 'b', // set baud (depracated) - GDBSetBreak = 'B', // set breakpoint (depracated) - GDBCont = 'c', // resume - GDBAsyncCont = 'C', // continue with signal - GDBDebug = 'd', // toggle debug flags (deprecated) - GDBDetach = 'D', // detach remote gdb - GDBRegR = 'g', // read general registers - GDBRegW = 'G', // write general registers - GDBSetThread = 'H', // set thread - GDBCycleStep = 'i', // step a single cycle - GDBSigCycleStep = 'I', // signal then cycle step - GDBKill = 'k', // kill program - GDBMemR = 'm', // read memory - GDBMemW = 'M', // write memory - GDBReadReg = 'p', // read register - GDBSetReg = 'P', // write register - GDBQueryVar = 'q', // query variable - GDBSetVar = 'Q', // set variable - GDBReset = 'r', // reset system. (Deprecated) - GDBStep = 's', // step - GDBAsyncStep = 'S', // signal and step - GDBThreadAlive = 'T', // find out if the thread is alive - GDBTargetExit = 'W', // target exited - GDBBinaryDload = 'X', // write memory - GDBClrHwBkpt = 'z', // remove breakpoint or watchpoint - GDBSetHwBkpt = 'Z' // insert breakpoint or watchpoint -}; + public: + struct Context + { + const GdbCommand *cmd; + char cmd_byte; + int type; + char *data; + int len; + }; -const char GDBStart = '$'; -const char GDBEnd = '#'; -const char GDBGoodP = '+'; -const char GDBBadP = '-'; + typedef bool (BaseRemoteGDB::*Func)(Context &ctx); -const int GDBPacketBufLen = 1024; + const char * const name; + const Func func; + + GdbCommand(const char *_name, Func _func) : name(_name), func(_func) + {} +}; class BaseRemoteGDB { @@ -93,18 +80,55 @@ class BaseRemoteGDB friend void debugger(); friend class GDBListener; - //Helper functions + protected: + /// Exception to throw when the connection to the client is broken. + struct BadClient + { + const char *warning; + BadClient(const char *_warning=NULL) : warning(_warning) + {} + }; + /// Exception to throw when an error needs to be reported to the client. + struct CmdError + { + std::string error; + CmdError(std::string _error) : error(_error) + {} + }; + /// Exception to throw when something isn't supported. + class Unsupported {}; + + // Helper functions protected: int digit2i(char); char i2digit(int); Addr hex2i(const char **); - //Address formats, break types, and gdb commands may change - //between architectures, so they're defined as virtual - //functions. + // Address formats, break types, and gdb commands may change + // between architectures, so they're defined as virtual + // functions. virtual void mem2hex(char *, const char *, int); virtual const char * hex2mem(char *, const char *, int); virtual const char * break_type(char c); - virtual const char * gdb_command(char cmd); + + protected: + static std::map<char, GdbCommand> command_map; + + bool cmd_unsupported(GdbCommand::Context &ctx); + + bool cmd_signal(GdbCommand::Context &ctx); + bool cmd_cont(GdbCommand::Context &ctx); + bool cmd_async_cont(GdbCommand::Context &ctx); + bool cmd_detach(GdbCommand::Context &ctx); + bool cmd_reg_r(GdbCommand::Context &ctx); + bool cmd_reg_w(GdbCommand::Context &ctx); + bool cmd_set_thread(GdbCommand::Context &ctx); + bool cmd_mem_r(GdbCommand::Context &ctx); + bool cmd_mem_w(GdbCommand::Context &ctx); + bool cmd_query_var(GdbCommand::Context &ctx); + bool cmd_step(GdbCommand::Context &ctx); + bool cmd_async_step(GdbCommand::Context &ctx); + bool cmd_clr_hw_bkpt(GdbCommand::Context &ctx); + bool cmd_set_hw_bkpt(GdbCommand::Context &ctx); protected: class InputEvent : public PollEvent @@ -138,13 +162,10 @@ class BaseRemoteGDB int number; protected: - //The socket commands come in through + // The socket commands come in through int fd; protected: -#ifdef notyet - label_t recover; -#endif bool active; bool attached; @@ -197,17 +218,21 @@ class BaseRemoteGDB BaseGdbRegCache(BaseRemoteGDB *g) : gdb(g) {} + virtual ~BaseGdbRegCache() + {} protected: BaseRemoteGDB *gdb; }; + BaseGdbRegCache *regCachePtr; + protected: - bool getbyte(uint8_t &b); - bool putbyte(uint8_t b); + uint8_t getbyte(); + void putbyte(uint8_t b); int recv(char *data, int len); - ssize_t send(const char *data); + void send(const char *data); protected: // Machine memory @@ -284,10 +309,10 @@ class BaseRemoteGDB typedef break_map_t::iterator break_iter_t; break_map_t hardBreakMap; - bool insertSoftBreak(Addr addr, size_t len); - bool removeSoftBreak(Addr addr, size_t len); - virtual bool insertHardBreak(Addr addr, size_t len); - bool removeHardBreak(Addr addr, size_t len); + void insertSoftBreak(Addr addr, size_t len); + void removeSoftBreak(Addr addr, size_t len); + virtual void insertHardBreak(Addr addr, size_t len); + void removeHardBreak(Addr addr, size_t len); protected: void clearTempBreakpoint(Addr &bkpt); |