/* * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Nathan Binkert */ /* * Copyright (c) 1990, 1993 The Regents of the University of California * All rights reserved * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratories. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kgdb_stub.c 8.4 (Berkeley) 1/12/94 */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * $NetBSD: kgdb_stub.c,v 1.8 2001/07/07 22:58:00 wdk Exp $ * * Taken from NetBSD * * "Stub" to allow remote cpu to debug over a serial line using gdb. */ #include #include #include #include #include "config/full_system.hh" #if FULL_SYSTEM #include "arch/vtophys.hh" #include "mem/vport.hh" #endif #include "base/intmath.hh" #include "base/remote_gdb.hh" #include "base/socket.hh" #include "base/trace.hh" #include "config/the_isa.hh" #include "cpu/static_inst.hh" #include "cpu/thread_context.hh" #include "debug/GDBAll.hh" #include "mem/port.hh" #include "mem/translating_port.hh" #include "sim/system.hh" using namespace std; using namespace Debug; using namespace TheISA; #ifndef NDEBUG vector debuggers; void debugger() { static int current_debugger = -1; if (current_debugger >= 0 && current_debugger < (int)debuggers.size()) { BaseRemoteGDB *gdb = debuggers[current_debugger]; if (!gdb->isattached()) gdb->listener->accept(); if (gdb->isattached()) gdb->trap(SIGILL); } } #endif /////////////////////////////////////////////////////////// // // // GDBListener::Event::Event(GDBListener *l, int fd, int e) : PollEvent(fd, e), listener(l) {} void GDBListener::Event::process(int revent) { listener->accept(); } GDBListener::GDBListener(BaseRemoteGDB *g, int p) : event(NULL), gdb(g), port(p) { assert(!gdb->listener); gdb->listener = this; } GDBListener::~GDBListener() { if (event) delete event; } string GDBListener::name() { return gdb->name() + ".listener"; } void GDBListener::listen() { if (ListenSocket::allDisabled()) { warn_once("Sockets disabled, not accepting gdb connections"); return; } while (!listener.listen(port, true)) { DPRINTF(GDBMisc, "Can't bind port %d\n", port); port++; } event = new Event(this, listener.getfd(), POLLIN); pollQueue.schedule(event); #ifndef NDEBUG gdb->number = debuggers.size(); debuggers.push_back(gdb); #endif #ifndef NDEBUG ccprintf(cerr, "%d: %s: listening for remote gdb #%d on port %d\n", curTick(), name(), gdb->number, port); #else ccprintf(cerr, "%d: %s: listening for remote gdb on port %d\n", curTick(), name(), port); #endif } void GDBListener::accept() { if (!listener.islistening()) panic("GDBListener::accept(): cannot accept if we're not listening!"); int sfd = listener.accept(true); if (sfd != -1) { if (gdb->isattached()) close(sfd); else gdb->attach(sfd); } } BaseRemoteGDB::Event::Event(BaseRemoteGDB *g, int fd, int e) : PollEvent(fd, e), gdb(g) {} void BaseRemoteGDB::Event::process(int revent) { if (revent & POLLIN) gdb->trap(SIGILL); else if (revent & POLLNVAL) gdb->detach(); } BaseRemoteGDB::BaseRemoteGDB(System *_system, ThreadContext *c, size_t cacheSize) : event(NULL), listener(NULL), number(-1), fd(-1), active(false), attached(false), system(_system), pmem(_system->physmem), context(c), gdbregs(cacheSize) { memset(gdbregs.regs, 0, gdbregs.bytes()); } BaseRemoteGDB::~BaseRemoteGDB() { if (event) delete event; } string BaseRemoteGDB::name() { return system->name() + ".remote_gdb"; } bool BaseRemoteGDB::isattached() { return attached; } void BaseRemoteGDB::attach(int f) { fd = f; event = new Event(this, fd, POLLIN); pollQueue.schedule(event); attached = true; DPRINTFN("remote gdb attached\n"); } void BaseRemoteGDB::detach() { attached = false; close(fd); fd = -1; pollQueue.remove(event); 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"; } } ///////////////////////// // // uint8_t BaseRemoteGDB::getbyte() { uint8_t b; if (::read(fd, &b, 1) != 1) warn("could not read byte from debugger"); return b; } void BaseRemoteGDB::putbyte(uint8_t b) { if (::write(fd, &b, 1) != 1) warn("could not write byte to debugger"); } // Send a packet to gdb void BaseRemoteGDB::send(const char *bp) { const char *p; uint8_t csum, c; DPRINTF(GDBSend, "send: %s\n", bp); do { p = bp; //Start sending a packet putbyte(GDBStart); //Send the contents, and also keep a check sum. for (csum = 0; (c = *p); p++) { putbyte(c); csum += c; } //Send the ending character. putbyte(GDBEnd); //Sent 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. } while ((c = getbyte() & 0x7f) == GDBBadP); } // Receive a packet from gdb int BaseRemoteGDB::recv(char *bp, int maxlen) { char *p; int c, csum; int len; do { p = bp; csum = len = 0; //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 ((c = getbyte()) != GDBEnd && len < maxlen) { c &= 0x7f; csum += c; *p++ = c; len++; } //Mask the check sum, and terminate the command string. csum &= 0xff; *p = '\0'; //If the command was too long, report an error. if (len >= maxlen) { putbyte(GDBBadP); continue; } //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 (csum == 0) { //Report that the packet was received correctly putbyte(GDBGoodP); // Sequence present? if (bp[2] == ':') { putbyte(bp[0]); putbyte(bp[1]); len -= 3; memcpy(bp, bp+3, len); } break; } //Otherwise, report that there was a mistake. putbyte(GDBBadP); } while (1); DPRINTF(GDBRecv, "recv: %s: %s\n", gdb_command(*bp), bp); return (len); } // Read bytes from kernel address space for debugger. bool BaseRemoteGDB::read(Addr vaddr, size_t size, char *data) { static Addr lastaddr = 0; static size_t lastsize = 0; if (vaddr < 10) { DPRINTF(GDBRead, "read: reading memory location zero!\n"); vaddr = lastaddr + lastsize; } DPRINTF(GDBRead, "read: addr=%#x, size=%d", vaddr, size); #if FULL_SYSTEM VirtualPort *port = context->getVirtPort(); #else TranslatingPort *port = context->getMemPort(); #endif port->readBlob(vaddr, (uint8_t*)data, size); #if TRACING_ON if (DTRACE(GDBRead)) { if (DTRACE(GDBExtra)) { char buf[1024]; mem2hex(buf, data, size); DPRINTFNR(": %s\n", buf); } else DPRINTFNR("\n"); } #endif return true; } // Write bytes to kernel address space for debugger. bool BaseRemoteGDB::write(Addr vaddr, size_t size, const char *data) { static Addr lastaddr = 0; static size_t lastsize = 0; if (vaddr < 10) { DPRINTF(GDBWrite, "write: writing memory location zero!\n"); vaddr = lastaddr + lastsize; } if (DTRACE(GDBWrite)) { DPRINTFN("write: addr=%#x, size=%d", vaddr, size); if (DTRACE(GDBExtra)) { char buf[1024]; mem2hex(buf, data, size); DPRINTFNR(": %s\n", buf); } else DPRINTFNR("\n"); } #if FULL_SYSTEM VirtualPort *port = context->getVirtPort(); #else TranslatingPort *port = context->getMemPort(); #endif port->writeBlob(vaddr, (uint8_t*)data, size); #if !FULL_SYSTEM delete port; #endif return true; } PCEventQueue *BaseRemoteGDB::getPcEventQueue() { return &system->pcEventQueue; } BaseRemoteGDB::HardBreakpoint::HardBreakpoint(BaseRemoteGDB *_gdb, Addr pc) : PCEvent(_gdb->getPcEventQueue(), "HardBreakpoint Event", pc), gdb(_gdb), refcount(0) { DPRINTF(GDBMisc, "creating hardware breakpoint at %#x\n", evpc); } void BaseRemoteGDB::HardBreakpoint::process(ThreadContext *tc) { DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc()); if (tc == gdb->context) gdb->trap(SIGTRAP); } bool BaseRemoteGDB::insertSoftBreak(Addr addr, size_t len) { if (len != sizeof(TheISA::MachInst)) panic("invalid length\n"); return insertHardBreak(addr, len); } bool BaseRemoteGDB::removeSoftBreak(Addr addr, size_t len) { if (len != sizeof(MachInst)) panic("invalid length\n"); return removeHardBreak(addr, len); } bool BaseRemoteGDB::insertHardBreak(Addr addr, size_t len) { if (len != sizeof(MachInst)) panic("invalid length\n"); 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 BaseRemoteGDB::removeHardBreak(Addr addr, size_t len) { if (len != sizeof(MachInst)) panic("invalid length\n"); DPRINTF(GDBMisc, "removing hardware breakpoint at %#x\n", addr); break_iter_t i = hardBreakMap.find(addr); if (i == hardBreakMap.end()) return false; HardBreakpoint *hbp = (*i).second; if (--hbp->refcount == 0) { delete hbp; hardBreakMap.erase(i); } return true; } void BaseRemoteGDB::setTempBreakpoint(Addr bkpt) { DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt); insertHardBreak(bkpt, sizeof(TheISA::MachInst)); } void BaseRemoteGDB::clearTempBreakpoint(Addr &bkpt) { DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt); removeHardBreak(bkpt, sizeof(TheISA::MachInst)); bkpt = 0; } 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"; default: return "unknown breakpoint/watchpoint"; } } // 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 // makes sense to use POSIX errno values, because that is what the // gdb/remote.c functions want to return. bool BaseRemoteGDB::trap(int type) { uint64_t val; size_t datalen, len; char data[GDBPacketBufLen + 1]; char *buffer; size_t bufferSize; const char *p; char command, subcmd; string var; bool ret; if (!attached) return false; bufferSize = gdbregs.bytes() * 2 + 256; buffer = (char*)malloc(bufferSize); TheISA::PCState pc = context->pcState(); DPRINTF(GDBMisc, "trap: PC=%s\n", pc); clearSingleStep(); /* * The first entry to this function is normally through * a breakpoint trap in kgdb_connect(), in which case we * must advance past the breakpoint because gdb will not. * * On the first entry here, we expect that gdb is not yet * listening to us, so just enter the interaction loop. * After the debugger is "active" (connected) it will be * waiting for a "signaled" message from us. */ if (!active) active = true; else // Tell remote host that an exception has occurred. snprintf((char *)buffer, bufferSize, "S%02x", type); send(buffer); // Stick frame regs into our reg cache. getregs(); for (;;) { datalen = recv(data, sizeof(data)); 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((char *)buffer, bufferSize, "S%02x", type); send(buffer); continue; case GDBRegR: if (2 * gdbregs.bytes() > bufferSize) panic("buffer too small"); mem2hex(buffer, gdbregs.regs, gdbregs.bytes()); send(buffer); continue; case GDBRegW: p = hex2mem(gdbregs.regs, p, gdbregs.bytes()); if (p == NULL || *p != '\0') send("E01"); else { setregs(); send("OK"); } continue; #if 0 case GDBSetReg: val = hex2i(&p); if (*p++ != '=') { send("E01"); continue; } if (val < 0 && val >= KGDB_NUMREGS) { send("E01"); continue; } gdbregs.regs[val] = hex2i(&p); setregs(); send("OK"); continue; #endif case GDBMemR: val = hex2i(&p); if (*p++ != ',') { send("E02"); continue; } len = hex2i(&p); if (*p != '\0') { send("E03"); continue; } if (len > bufferSize) { send("E04"); continue; } if (!acc(val, len)) { send("E05"); continue; } if (read(val, (size_t)len, (char *)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); send(temp); delete [] temp; } else { send("E05"); } continue; case GDBMemW: val = hex2i(&p); if (*p++ != ',') { send("E06"); continue; } len = hex2i(&p); if (*p++ != ':') { send("E07"); continue; } if (len > datalen - (p - data)) { send("E08"); continue; } p = hex2mem(buffer, p, bufferSize); if (p == NULL) { send("E09"); continue; } if (!acc(val, len)) { send("E0A"); continue; } if (write(val, (size_t)len, (char *)buffer)) send("OK"); else send("E0B"); continue; case GDBSetThread: subcmd = *p++; val = hex2i(&p); if (val == 0) send("OK"); else send("E01"); continue; case GDBDetach: case GDBKill: active = false; clearSingleStep(); detach(); goto out; case GDBAsyncCont: subcmd = hex2i(&p); if (*p++ == ';') { val = hex2i(&p); context->pcState(val); } clearSingleStep(); goto out; case GDBCont: if (p - data < (ptrdiff_t)datalen) { val = hex2i(&p); context->pcState(val); } clearSingleStep(); goto out; case GDBAsyncStep: subcmd = hex2i(&p); if (*p++ == ';') { val = hex2i(&p); context->pcState(val); } setSingleStep(); goto out; case GDBStep: if (p - data < (ptrdiff_t)datalen) { val = hex2i(&p); context->pcState(val); } setSingleStep(); goto out; case GDBClrHwBkpt: subcmd = *p++; if (*p++ != ',') send("E0D"); val = hex2i(&p); if (*p++ != ',') send("E0D"); 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; case '2': // write watchpoint case '3': // read watchpoint case '4': // access watchpoint default: // unknown send(""); break; } send(ret ? "OK" : "E0C"); continue; case GDBSetHwBkpt: subcmd = *p++; if (*p++ != ',') send("E0D"); val = hex2i(&p); if (*p++ != ',') send("E0D"); 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); break; case '1': // hardware breakpoint ret = insertHardBreak(val, len); break; case '2': // write watchpoint case '3': // read watchpoint case '4': // access watchpoint default: // unknown send(""); break; } send(ret ? "OK" : "E0C"); continue; case GDBQueryVar: var = string(p, datalen - 1); if (var == "C") send("QC0"); else send(""); 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); send(""); continue; default: // Unknown command. DPRINTF(GDBMisc, "Unknown command: %c(%#x)\n", command, command); send(""); continue; } } out: free(buffer); return true; } // Convert a hex digit into an integer. // This returns -1 if the argument passed is no valid hex digit. int BaseRemoteGDB::digit2i(char c) { if (c >= '0' && c <= '9') return (c - '0'); else if (c >= 'a' && c <= 'f') return (c - 'a' + 10); else if (c >= 'A' && c <= 'F') return (c - 'A' + 10); else return (-1); } // Convert the low 4 bits of an integer into an hex digit. char BaseRemoteGDB::i2digit(int n) { return ("0123456789abcdef"[n & 0x0f]); } // Convert a byte array into an hex string. void BaseRemoteGDB::mem2hex(void *vdst, const void *vsrc, int len) { char *dst = (char *)vdst; const char *src = (const char *)vsrc; while (len--) { *dst++ = i2digit(*src >> 4); *dst++ = i2digit(*src++); } *dst = '\0'; } // Convert an hex string into a byte array. // This returns a pointer to the character following the last valid // hex digit. If the string ends in the middle of a byte, NULL is // returned. const char * BaseRemoteGDB::hex2mem(void *vdst, const char *src, int maxlen) { char *dst = (char *)vdst; int msb, lsb; while (*src && maxlen--) { msb = digit2i(*src++); if (msb < 0) return (src - 1); lsb = digit2i(*src++); if (lsb < 0) return (NULL); *dst++ = (msb << 4) | lsb; } return (src); } // Convert an hex string into an integer. // This returns a pointer to the character following the last valid // hex digit. Addr BaseRemoteGDB::hex2i(const char **srcp) { const char *src = *srcp; Addr r = 0; int nibble; while ((nibble = digit2i(*src)) >= 0) { r *= 16; r += nibble; src++; } *srcp = src; return (r); }