Initial checkin of lldb code from internal Apple repo.

llvm-svn: 105619
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
new file mode 100644
index 0000000..3ac3ee9
--- /dev/null
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -0,0 +1,3187 @@
+//===-- RNBRemote.cpp -------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBRemote.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <mach/exception_types.h>
+#include <sys/sysctl.h>
+
+#include "DNB.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "RNBContext.h"
+#include "RNBServices.h"
+#include "RNBSocket.h"
+#include "StringExtractor.h"
+
+#include <iomanip>
+#include <sstream>
+
+#include <TargetConditionals.h> // for endianness predefines
+
+//----------------------------------------------------------------------
+// std::iostream formatting macros
+//----------------------------------------------------------------------
+#define RAW_HEXBASE     std::setfill('0') << std::hex << std::right
+#define HEXBASE         '0' << 'x' << RAW_HEXBASE
+#define RAWHEX8(x)      RAW_HEXBASE << std::setw(2) << ((uint32_t)((uint8_t)x))
+#define RAWHEX16        RAW_HEXBASE << std::setw(4)
+#define RAWHEX32        RAW_HEXBASE << std::setw(8)
+#define RAWHEX64        RAW_HEXBASE << std::setw(16)
+#define HEX8(x)         HEXBASE << std::setw(2) << ((uint32_t)(x))
+#define HEX16           HEXBASE << std::setw(4)
+#define HEX32           HEXBASE << std::setw(8)
+#define HEX64           HEXBASE << std::setw(16)
+#define RAW_HEX(x)      RAW_HEXBASE << std::setw(sizeof(x)*2) << (x)
+#define HEX(x)          HEXBASE << std::setw(sizeof(x)*2) << (x)
+#define RAWHEX_SIZE(x, sz)  RAW_HEXBASE << std::setw((sz)) << (x)
+#define HEX_SIZE(x, sz) HEXBASE << std::setw((sz)) << (x)
+#define STRING_WIDTH(w) std::setfill(' ') << std::setw(w)
+#define LEFT_STRING_WIDTH(s, w) std::left << std::setfill(' ') << std::setw(w) << (s) << std::right
+#define DECIMAL         std::dec << std::setfill(' ')
+#define DECIMAL_WIDTH(w) DECIMAL << std::setw(w)
+#define FLOAT(n, d)     std::setfill(' ') << std::setw((n)+(d)+1) << std::setprecision(d) << std::showpoint << std::fixed
+#define INDENT_WITH_SPACES(iword_idx)   std::setfill(' ') << std::setw((iword_idx)) << ""
+#define INDENT_WITH_TABS(iword_idx)     std::setfill('\t') << std::setw((iword_idx)) << ""
+// Class to handle communications via gdb remote protocol.
+
+extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args);
+
+RNBRemote::RNBRemote (bool use_native_regs) :
+    m_ctx(),
+    m_comm(),
+    m_extended_mode(false),
+    m_noack_mode(false),
+    m_continue_thread(-1),
+    m_thread(-1),
+    m_mutex(),
+    m_packets_recvd(0),
+    m_packets(),
+    m_rx_packets(),
+    m_rx_partial_data(),
+    m_rx_pthread(0),
+    m_breakpoints(),
+    m_max_payload_size(DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE - 4),
+    m_use_native_regs (use_native_regs)
+{
+    DNBLogThreadedIf (LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
+    CreatePacketTable ();
+}
+
+
+RNBRemote::~RNBRemote()
+{
+    DNBLogThreadedIf (LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
+    StopReadRemoteDataThread();
+}
+
+void
+RNBRemote::CreatePacketTable  ()
+{
+    // Step required to add new packets:
+    // 1 - Add new enumeration to RNBRemote::PacketEnum
+    // 2 - Create a the RNBRemote::HandlePacket_ function if a new function is needed
+    // 3 - Register the Packet definition with any needed callbacks in this fucntion
+    //          - If no response is needed for a command, then use NULL for the normal callback
+    //          - If the packet is not supported while the target is running, use NULL for the async callback
+    // 4 - If the packet is a standard packet (starts with a '$' character
+    //      followed by the payload and then '#' and checksum, then you are done
+    //      else go on to step 5
+    // 5 - if the packet is a fixed length packet:
+    //      - modify the switch statement for the first character in the payload
+    //        in RNBRemote::CommDataReceived so it doesn't reject the new packet
+    //        type as invalid
+    //      - modify the switch statement for the first character in the payload
+    //        in RNBRemote::GetPacketPayload and make sure the payload of the packet
+    //        is returned correctly
+
+    std::vector <Packet> &t = m_packets;
+    t.push_back (Packet (ack,                           NULL,                                   NULL, "+", "ACK"));
+    t.push_back (Packet (nack,                          NULL,                                   NULL, "-", "!ACK"));
+    t.push_back (Packet (read_memory,                   &RNBRemote::HandlePacket_m,             NULL, "m", "Read memory"));
+    t.push_back (Packet (read_register,                 &RNBRemote::HandlePacket_p,             NULL, "p", "Read one register"));
+    t.push_back (Packet (read_general_regs,             &RNBRemote::HandlePacket_g,             NULL, "g", "Read registers"));
+    t.push_back (Packet (write_memory,                  &RNBRemote::HandlePacket_M,             NULL, "M", "Write memory"));
+    t.push_back (Packet (write_register,                &RNBRemote::HandlePacket_P,             NULL, "P", "Write one register"));
+    t.push_back (Packet (write_general_regs,            &RNBRemote::HandlePacket_G,             NULL, "G", "Write registers"));
+    t.push_back (Packet (insert_mem_bp,                 &RNBRemote::HandlePacket_z,             NULL, "Z0", "Insert memory breakpoint"));
+    t.push_back (Packet (remove_mem_bp,                 &RNBRemote::HandlePacket_z,             NULL, "z0", "Remove memory breakpoint"));
+    t.push_back (Packet (single_step,                   &RNBRemote::HandlePacket_s,             NULL, "s", "Single step"));
+    t.push_back (Packet (cont,                          &RNBRemote::HandlePacket_c,             NULL, "c", "continue"));
+    t.push_back (Packet (single_step_with_sig,          &RNBRemote::HandlePacket_S,             NULL, "S", "Single step with signal"));
+    t.push_back (Packet (set_thread,                    &RNBRemote::HandlePacket_H,             NULL, "H", "Set thread"));
+    t.push_back (Packet (halt,                          &RNBRemote::HandlePacket_last_signal,   &RNBRemote::HandlePacket_stop_process, "\x03", "^C"));
+//  t.push_back (Packet (use_extended_mode,             &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "!", "Use extended mode"));
+    t.push_back (Packet (why_halted,                    &RNBRemote::HandlePacket_last_signal,   NULL, "?", "Why did target halt"));
+    t.push_back (Packet (set_argv,                      &RNBRemote::HandlePacket_A,             NULL, "A", "Set argv"));
+//  t.push_back (Packet (set_bp,                        &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "B", "Set/clear breakpoint"));
+    t.push_back (Packet (continue_with_sig,             &RNBRemote::HandlePacket_C,             NULL, "C", "Continue with signal"));
+    t.push_back (Packet (detach,                        &RNBRemote::HandlePacket_D,             NULL, "D", "Detach gdb from remote system"));
+//  t.push_back (Packet (step_inferior_one_cycle,       &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "i", "Step inferior by one clock cycle"));
+//  t.push_back (Packet (signal_and_step_inf_one_cycle, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "I", "Signal inferior, then step one clock cyle"));
+    t.push_back (Packet (kill,                          &RNBRemote::HandlePacket_k,             NULL, "k", "Kill"));
+//  t.push_back (Packet (restart,                       &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "R", "Restart inferior"));
+//  t.push_back (Packet (search_mem_backwards,          &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "t", "Search memory backwards"));
+    t.push_back (Packet (thread_alive_p,                &RNBRemote::HandlePacket_T,             NULL, "T", "Is thread alive"));
+    t.push_back (Packet (vattach,                       &RNBRemote::HandlePacket_v,             NULL, "vAttach", "Attach to a new process"));
+    t.push_back (Packet (vattachwait,                   &RNBRemote::HandlePacket_v,             NULL, "vAttachWait", "Wait for a process to start up then attach to it"));
+    t.push_back (Packet (vcont_list_actions,            &RNBRemote::HandlePacket_v,             NULL, "vCont;", "Verbose resume with thread actions"));
+    t.push_back (Packet (vcont_list_actions,            &RNBRemote::HandlePacket_v,             NULL, "vCont?", "List valid continue-with-thread-actions actions"));
+    // The X packet doesn't currently work. If/when it does, remove the line above and uncomment out the line below
+//  t.push_back (Packet (write_data_to_memory,          &RNBRemote::HandlePacket_X,             NULL, "X", "Write data to memory"));
+//  t.push_back (Packet (insert_hardware_bp,            &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z1", "Insert hardware breakpoint"));
+//  t.push_back (Packet (remove_hardware_bp,            &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z1", "Remove hardware breakpoint"));
+//  t.push_back (Packet (insert_write_watch_bp,         &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z2", "Insert write watchpoint"));
+//  t.push_back (Packet (remove_write_watch_bp,         &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z2", "Remove write watchpoint"));
+//  t.push_back (Packet (insert_read_watch_bp,          &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z3", "Insert read watchpoint"));
+//  t.push_back (Packet (remove_read_watch_bp,          &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z3", "Remove read watchpoint"));
+//  t.push_back (Packet (insert_access_watch_bp,        &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z4", "Insert access watchpoint"));
+//  t.push_back (Packet (remove_access_watch_bp,        &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z4", "Remove access watchpoint"));
+    t.push_back (Packet (query_current_thread_id,       &RNBRemote::HandlePacket_qC,            NULL, "qC", "Query current thread ID"));
+//  t.push_back (Packet (query_memory_crc,              &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qCRC:", "Compute CRC of memory region"));
+    t.push_back (Packet (query_thread_ids_first,        &RNBRemote::HandlePacket_qThreadInfo,   NULL, "qfThreadInfo", "Get list of active threads (first req)"));
+    t.push_back (Packet (query_thread_ids_subsequent,   &RNBRemote::HandlePacket_qThreadInfo,   NULL, "qsThreadInfo", "Get list of active threads (subsequent req)"));
+    // APPLE LOCAL: qThreadStopInfo
+    // syntax: qThreadStopInfoTTTT
+    //  TTTT is hex thread ID
+    t.push_back (Packet (query_thread_stop_info,        &RNBRemote::HandlePacket_qThreadStopInfo,   NULL, "qThreadStopInfo", "Get detailed info on why the specified thread stopped"));
+    t.push_back (Packet (query_thread_extra_info,       &RNBRemote::HandlePacket_qThreadExtraInfo,NULL, "qThreadExtraInfo", "Get printable status of a thread"));
+//  t.push_back (Packet (query_image_offsets,           &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qOffsets", "Report offset of loaded program"));
+    t.push_back (Packet (query_launch_success,          &RNBRemote::HandlePacket_qLaunchSuccess,NULL, "qLaunchSuccess", "Report the success or failure of the launch attempt"));
+    t.push_back (Packet (query_register_info,           &RNBRemote::HandlePacket_qRegisterInfo, NULL, "qRegisterInfo", "Dynamically discover remote register context information."));
+    t.push_back (Packet (query_shlib_notify_info_addr,  &RNBRemote::HandlePacket_qShlibInfoAddr,NULL, "qShlibInfoAddr", "Returns the address that contains info needed for getting shared library notifications"));
+    t.push_back (Packet (query_step_packet_supported,   &RNBRemote::HandlePacket_qStepPacketSupported,NULL, "qStepPacketSupported", "Replys with OK if the 's' packet is supported."));
+    t.push_back (Packet (query_host_info,               &RNBRemote::HandlePacket_qHostInfo,     NULL, "qHostInfo", "Replies with multiple 'key:value;' tuples appended to each other."));
+//  t.push_back (Packet (query_symbol_lookup,           &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qSymbol", "Notify that host debugger is ready to do symbol lookups"));
+    t.push_back (Packet (start_noack_mode,              &RNBRemote::HandlePacket_Q            , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets"));
+    t.push_back (Packet (set_logging_mode,              &RNBRemote::HandlePacket_Q            , NULL, "QSetLogging:", "Request that the " DEBUGSERVER_PROGRAM_NAME " set its logging mode bits"));
+    t.push_back (Packet (set_max_packet_size,           &RNBRemote::HandlePacket_Q            , NULL, "QSetMaxPacketSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized packet gdb can handle"));
+    t.push_back (Packet (set_max_payload_size,          &RNBRemote::HandlePacket_Q            , NULL, "QSetMaxPayloadSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized payload gdb can handle"));
+    t.push_back (Packet (set_environment_variable,      &RNBRemote::HandlePacket_Q            , NULL, "QEnvironment:", "Add an environment variable to the inferior's environment"));
+//  t.push_back (Packet (pass_signals_to_inferior,      &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "QPassSignals:", "Specify which signals are passed to the inferior"));
+    t.push_back (Packet (allocate_memory,               &RNBRemote::HandlePacket_AllocateMemory, NULL, "_M", "Allocate memory in the inferior process."));
+    t.push_back (Packet (deallocate_memory,             &RNBRemote::HandlePacket_DeallocateMemory, NULL, "_m", "Deallocate memory in the inferior process."));
+}
+
+
+void
+RNBRemote::FlushSTDIO ()
+{
+    if (m_ctx.HasValidProcessID())
+    {
+        nub_process_t pid = m_ctx.ProcessID();
+        char buf[256];
+        nub_size_t count;
+        do
+        {
+            count = DNBProcessGetAvailableSTDOUT(pid, buf, sizeof(buf));
+            if (count > 0)
+            {
+                SendSTDOUTPacket (buf, count);
+            }
+        } while (count > 0);
+
+        do
+        {
+            count = DNBProcessGetAvailableSTDERR(pid, buf, sizeof(buf));
+            if (count > 0)
+            {
+                SendSTDERRPacket (buf, count);
+            }
+        } while (count > 0);
+    }
+}
+
+rnb_err_t
+RNBRemote::SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer)
+{
+    std::ostringstream packet_sstrm;
+    // Append the header cstr if there was one
+    if (header && header[0])
+        packet_sstrm << header;
+    nub_size_t i;
+    const uint8_t *ubuf8 = (const uint8_t *)buf;
+    for (i=0; i<buf_len; i++)
+    {
+        packet_sstrm << RAWHEX8(ubuf8[i]);
+    }
+    // Append the footer cstr if there was one
+    if (footer && footer[0])
+        packet_sstrm << footer;
+
+    return SendPacket(packet_sstrm.str());
+}
+
+rnb_err_t
+RNBRemote::SendSTDOUTPacket (char *buf, nub_size_t buf_size)
+{
+    if (buf_size == 0)
+        return rnb_success;
+    return SendHexEncodedBytePacket("O", buf, buf_size, NULL);
+}
+
+rnb_err_t
+RNBRemote::SendSTDERRPacket (char *buf, nub_size_t buf_size)
+{
+    if (buf_size == 0)
+        return rnb_success;
+    return SendHexEncodedBytePacket("O", buf, buf_size, NULL);
+}
+
+rnb_err_t
+RNBRemote::SendPacket (const std::string &s)
+{
+    DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s (%s) called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, s.c_str());
+    std::string sendpacket = "$" + s + "#";
+    int cksum = 0;
+    char hexbuf[5];
+
+    if (m_noack_mode)
+    {
+        sendpacket += "00";
+    }
+    else
+    {
+        for (int i = 0; i != s.size(); ++i)
+            cksum += s[i];
+        snprintf (hexbuf, sizeof hexbuf, "%02x", cksum & 0xff);
+        sendpacket += hexbuf;
+    }
+
+    rnb_err_t err = m_comm.Write (sendpacket.c_str(), sendpacket.size());
+    if (err != rnb_success)
+        return err;
+
+    if (m_noack_mode)
+        return rnb_success;
+
+    std::string reply;
+    RNBRemote::Packet packet;
+    err = GetPacket (reply, packet, true);
+
+    if (err != rnb_success)
+    {
+        DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s (%s) got error trying to get reply...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, sendpacket.c_str());
+        return err;
+    }
+
+    DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s (%s) got reply: '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, sendpacket.c_str(), reply.c_str());
+
+    if (packet.type == ack)
+        return rnb_success;
+
+    // Should we try to resend the packet at this layer?
+    //  if (packet.command == nack)
+    return rnb_err;
+}
+
+/* Get a packet via gdb remote protocol.
+ Strip off the prefix/suffix, verify the checksum to make sure
+ a valid packet was received, send an ACK if they match.  */
+
+rnb_err_t
+RNBRemote::GetPacketPayload (std::string &return_packet)
+{
+    //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+
+    PThreadMutex::Locker locker(m_mutex);
+    if (m_rx_packets.empty())
+    {
+        // Only reset the remote command available event if we have no more packets
+        m_ctx.Events().ResetEvents ( RNBContext::event_read_packet_available );
+        //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s error: no packets available...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+        return rnb_err;
+    }
+
+    //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s has %u queued packets", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, m_rx_packets.size());
+    return_packet.swap(m_rx_packets.front());
+    m_rx_packets.pop_front();
+    locker.Reset(); // Release our lock on the mutex
+
+    if (m_rx_packets.empty())
+    {
+        // Reset the remote command available event if we have no more packets
+        m_ctx.Events().ResetEvents ( RNBContext::event_read_packet_available );
+    }
+
+    //DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s: '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str());
+
+    switch (return_packet[0])
+    {
+        case '+':
+        case '-':
+        case '\x03':
+            break;
+
+        case '$':
+        {
+            int packet_checksum = 0;
+            if (!m_noack_mode)
+            {
+                for (int i = return_packet.size() - 2; i < return_packet.size(); ++i)
+                {
+                    char checksum_char = tolower (return_packet[i]);
+                    if (!isxdigit (checksum_char))
+                    {
+                        m_comm.Write ("-", 1);
+                        DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s error: packet with invalid checksum characters: %s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str());
+                        return rnb_err;
+                    }
+                }
+                packet_checksum = strtol (&return_packet[return_packet.size() - 2], NULL, 16);
+            }
+
+            return_packet.erase(0,1);           // Strip the leading '$'
+            return_packet.erase(return_packet.size() - 3);// Strip the #XX checksum
+
+            if (!m_noack_mode)
+            {
+                // Compute the checksum
+                int computed_checksum = 0;
+                for (std::string::iterator it = return_packet.begin ();
+                     it != return_packet.end ();
+                     ++it)
+                {
+                    computed_checksum += *it;
+                }
+
+                if (packet_checksum == (computed_checksum & 0xff))
+                {
+                    //DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str());
+                    m_comm.Write ("+", 1);
+                }
+                else
+                {
+                    DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s' (error: packet checksum mismatch  (0x%2.2x != 0x%2.2x))",
+                                      (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+                                      __FUNCTION__,
+                                      return_packet.c_str(),
+                                      packet_checksum,
+                                      computed_checksum);
+                    m_comm.Write ("-", 1);
+                    return rnb_err;
+                }
+            }
+        }
+            break;
+
+        default:
+            DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s tossing unexpected packet???? %s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str());
+            if (!m_noack_mode)
+                m_comm.Write ("-", 1);
+            return rnb_err;
+    }
+
+    return rnb_success;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_UNIMPLEMENTED (const char* p)
+{
+    DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s(\"%s\")", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p ? p : "NULL");
+    return SendPacket ("");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_ILLFORMED (const char *description)
+{
+    DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s sending ILLFORMED", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+    return SendPacket ("E03");
+}
+
+rnb_err_t
+RNBRemote::GetPacket (std::string &packet_payload, RNBRemote::Packet& packet_info, bool wait)
+{
+    std::string payload;
+    rnb_err_t err = GetPacketPayload (payload);
+    if (err != rnb_success)
+    {
+        PThreadEvent& events = m_ctx.Events();
+        nub_event_t set_events = events.GetEventBits();
+        // TODO: add timeout version of GetPacket?? We would then need to pass
+        // that timeout value along to DNBProcessTimedWaitForEvent.
+        if (!wait || ((set_events & RNBContext::event_read_thread_running) == 0))
+            return err;
+
+        const nub_event_t events_to_wait_for = RNBContext::event_read_packet_available | RNBContext::event_read_thread_exiting;
+        set_events = 0;
+
+        while ((set_events = events.WaitForSetEvents(events_to_wait_for)) != 0)
+        {
+            if (set_events & RNBContext::event_read_packet_available)
+            {
+                // Try the queue again now that we got an event
+                err = GetPacketPayload (payload);
+                if (err == rnb_success)
+                    break;
+            }
+
+            if (set_events & RNBContext::event_read_thread_exiting)
+                err = rnb_not_connected;
+
+            if (err == rnb_not_connected)
+                return err;
+
+        } while (err == rnb_err);
+
+        if (set_events == 0)
+            err = rnb_not_connected;
+    }
+
+    if (err == rnb_success)
+    {
+        Packet::iterator it;
+        for (it = m_packets.begin (); it != m_packets.end (); ++it)
+        {
+            if (payload.compare (0, it->abbrev.size(), it->abbrev) == 0)
+                break;
+        }
+
+        // A packet we don't have an entry for. This can happen when we
+        // get a packet that we don't know about or support. We just reply
+        // accordingly and go on.
+        if (it == m_packets.end ())
+        {
+            DNBLogThreadedIf (LOG_RNB_PACKETS, "unimplemented packet: '%s'", payload.c_str());
+            HandlePacket_UNIMPLEMENTED(payload.c_str());
+            return rnb_err;
+        }
+        else
+        {
+            packet_info = *it;
+            packet_payload = payload;
+        }
+    }
+    return err;
+}
+
+rnb_err_t
+RNBRemote::HandleAsyncPacket(PacketEnum *type)
+{
+    DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+    static DNBTimer g_packetTimer(true);
+    rnb_err_t err = rnb_err;
+    std::string packet_data;
+    RNBRemote::Packet packet_info;
+    err = GetPacket (packet_data, packet_info, false);
+
+    if (err == rnb_success)
+    {
+        if (!packet_data.empty() && isprint(packet_data[0]))
+            DNBLogThreadedIf (LOG_RNB_REMOTE | LOG_RNB_PACKETS, "HandleAsyncPacket (\"%s\");", packet_data.c_str());
+        else
+            DNBLogThreadedIf (LOG_RNB_REMOTE | LOG_RNB_PACKETS, "HandleAsyncPacket (%s);", packet_info.printable_name.c_str());
+
+        HandlePacketCallback packet_callback = packet_info.async;
+        if (packet_callback != NULL)
+        {
+            if (type != NULL)
+                *type = packet_info.type;
+            return (this->*packet_callback)(packet_data.c_str());
+        }
+    }
+
+    return err;
+}
+
+rnb_err_t
+RNBRemote::HandleReceivedPacket(PacketEnum *type)
+{
+    static DNBTimer g_packetTimer(true);
+
+    //  DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+    rnb_err_t err = rnb_err;
+    std::string packet_data;
+    RNBRemote::Packet packet_info;
+    err = GetPacket (packet_data, packet_info, false);
+
+    if (err == rnb_success)
+    {
+        DNBLogThreadedIf (LOG_RNB_REMOTE, "HandleReceivedPacket (\"%s\");", packet_data.c_str());
+        HandlePacketCallback packet_callback = packet_info.normal;
+        if (packet_callback != NULL)
+        {
+            if (type != NULL)
+                *type = packet_info.type;
+            return (this->*packet_callback)(packet_data.c_str());
+        }
+        else
+        {
+            // Do not fall through to end of this function, if we have valid
+            // packet_info and it has a NULL callback, then we need to respect
+            // that it may not want any response or anything to be done.
+            return err;
+        }
+    }
+    return rnb_err;
+}
+
+void
+RNBRemote::CommDataReceived(const std::string& new_data)
+{
+    //  DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+    {
+        // Put the packet data into the buffer in a thread safe fashion
+        PThreadMutex::Locker locker(m_mutex);
+
+        std::string data;
+        // See if we have any left over data from a previous call to this
+        // function?
+        if (!m_rx_partial_data.empty())
+        {
+            // We do, so lets start with that data
+            data.swap(m_rx_partial_data);
+        }
+        // Append the new incoming data
+        data += new_data;
+
+        // Parse up the packets into gdb remote packets
+        uint32_t idx = 0;
+        const size_t data_size = data.size();
+
+        while (idx < data_size)
+        {
+            // end_idx must be one past the last valid packet byte. Start
+            // it off with an invalid value that is the same as the current
+            // index.
+            size_t end_idx = idx;
+
+            switch (data[idx])
+            {
+                case '+':       // Look for ack
+                case '-':       // Look for cancel
+                case '\x03':    // ^C to halt target
+                    end_idx = idx + 1;  // The command is one byte long...
+                    break;
+
+                case '$':
+                    // Look for a standard gdb packet?
+                    end_idx = data.find('#',  idx + 1);
+                    if (end_idx == std::string::npos || end_idx + 2 > data_size)
+                    {
+                        end_idx = std::string::npos;
+                    }
+                    else
+                    {
+                        // Add two for the checksum bytes
+                        end_idx += 4;
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+
+            if (end_idx == std::string::npos)
+            {
+                // Not all data may be here for the packet yet, save it for
+                // next time through this function.
+                m_rx_partial_data += data.substr(idx);
+                //DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s saving data for later[%u, npos): '%s'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, idx, m_rx_partial_data.c_str());
+                idx = end_idx;
+            }
+            else
+                if (idx < end_idx)
+                {
+                    m_packets_recvd++;
+                    // Hack to get rid of initial '+' ACK???
+                    if (m_packets_recvd == 1 && (end_idx == idx + 1) && data[idx] == '+')
+                    {
+                        //DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s throwing first ACK away....[%u, npos): '+'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, idx);
+                    }
+                    else
+                    {
+                        // We have a valid packet...
+                        m_rx_packets.push_back(data.substr(idx, end_idx - idx));
+                        DNBLogThreadedIf (LOG_RNB_PACKETS, "getpkt: %s", m_rx_packets.back().c_str());
+                    }
+                    idx = end_idx;
+                }
+                else
+                {
+                    DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s tossing junk byte at %c",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, data[idx]);
+                    idx = idx + 1;
+                }
+        }
+    }
+
+    if (!m_rx_packets.empty())
+    {
+        // Let the main thread know we have received a packet
+
+        //DNBLogThreadedIf (LOG_RNB_EVENTS, "%8d RNBRemote::%s   called events.SetEvent(RNBContext::event_read_packet_available)", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+        PThreadEvent& events = m_ctx.Events();
+        events.SetEvents (RNBContext::event_read_packet_available);
+    }
+}
+
+rnb_err_t
+RNBRemote::GetCommData ()
+{
+    //  DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+    std::string comm_data;
+    rnb_err_t err = m_comm.Read (comm_data);
+    if (err == rnb_success)
+    {
+        if (!comm_data.empty())
+            CommDataReceived (comm_data);
+    }
+    return err;
+}
+
+void
+RNBRemote::StartReadRemoteDataThread()
+{
+    DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+    PThreadEvent& events = m_ctx.Events();
+    if ((events.GetEventBits() & RNBContext::event_read_thread_running) == 0)
+    {
+        events.ResetEvents (RNBContext::event_read_thread_exiting);
+        int err = ::pthread_create (&m_rx_pthread, NULL, ThreadFunctionReadRemoteData, this);
+        if (err == 0)
+        {
+            // Our thread was successfully kicked off, wait for it to
+            // set the started event so we can safely continue
+            events.WaitForSetEvents (RNBContext::event_read_thread_running);
+        }
+        else
+        {
+            events.ResetEvents (RNBContext::event_read_thread_running);
+            events.SetEvents (RNBContext::event_read_thread_exiting);
+        }
+    }
+}
+
+void
+RNBRemote::StopReadRemoteDataThread()
+{
+    DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+    PThreadEvent& events = m_ctx.Events();
+    if ((events.GetEventBits() & RNBContext::event_read_thread_running) == RNBContext::event_read_thread_running)
+    {
+        m_comm.Disconnect(true);
+        struct timespec timeout_abstime;
+        DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0);
+
+        // Wait for 2 seconds for the remote data thread to exit
+        if (events.WaitForSetEvents(RNBContext::event_read_thread_exiting, &timeout_abstime) == 0)
+        {
+            // Kill the remote data thread???
+        }
+    }
+}
+
+
+void*
+RNBRemote::ThreadFunctionReadRemoteData(void *arg)
+{
+    // Keep a shared pointer reference so this doesn't go away on us before the thread is killed.
+    DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread starting...", __FUNCTION__, arg);
+    RNBRemoteSP remoteSP(g_remoteSP);
+    if (remoteSP.get() != NULL)
+    {
+        RNBRemote* remote = remoteSP.get();
+        PThreadEvent& events = remote->Context().Events();
+        events.SetEvents (RNBContext::event_read_thread_running);
+        // START: main receive remote command thread loop
+        bool done = false;
+        while (!done)
+        {
+            rnb_err_t err = remote->GetCommData();
+
+            switch (err)
+            {
+                case rnb_success:
+                    break;
+
+                default:
+                case rnb_err:
+                    DNBLogThreadedIf (LOG_RNB_REMOTE, "RNBSocket::GetCommData returned error %u", err);
+                    done = true;
+                    break;
+
+                case rnb_not_connected:
+                    DNBLogThreadedIf (LOG_RNB_REMOTE, "RNBSocket::GetCommData returned not connected...");
+                    done = true;
+                    break;
+            }
+        }
+        // START: main receive remote command thread loop
+        events.ResetEvents (RNBContext::event_read_thread_running);
+        events.SetEvents (RNBContext::event_read_thread_exiting);
+    }
+    DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread exiting...", __FUNCTION__, arg);
+    return NULL;
+}
+
+
+
+/* Read the bytes in STR which are GDB Remote Protocol binary encoded bytes
+ (8-bit bytes).
+ This encoding uses 0x7d ('}') as an escape character for 0x7d ('}'),
+ 0x23 ('#'), and 0x24 ('$').
+ LEN is the number of bytes to be processed.  If a character is escaped,
+ it is 2 characters for LEN.  A LEN of -1 means encode-until-nul-byte
+ (end of string).  */
+
+std::vector<uint8_t>
+decode_binary_data (const char *str, int len)
+{
+    std::vector<uint8_t> bytes;
+    if (len == 0)
+    {
+        return bytes;
+    }
+    if (len == -1)
+        len = strlen (str);
+
+    while (len--)
+    {
+        unsigned char c = *str;
+        if (c == 0x7d && len > 0)
+        {
+            len--;
+            str++;
+            c ^= 0x20;
+        }
+        bytes.push_back (c);
+    }
+    return bytes;
+}
+
+typedef struct register_map_entry
+{
+    uint32_t        gdb_regnum; // gdb register number
+    uint32_t        gdb_size;   // gdb register size in bytes (can be greater than or less than to debugnub size...)
+    const char *    gdb_name;   // gdb register name
+    DNBRegisterInfo nub_info;   // debugnub register info
+    const uint8_t*  fail_value; // Value to print if case we fail to reg this register (if this is NULL, we will return an error)
+    int             expedite;   // expedite delivery of this register in last stop reply packets
+} register_map_entry_t;
+
+
+
+// If the notion of registers differs from what is handed out by the
+// architecture, then flavors can be defined here.
+
+static const uint32_t MAX_REGISTER_BYTE_SIZE = 16;
+static const uint8_t k_zero_bytes[MAX_REGISTER_BYTE_SIZE] = {0};
+static std::vector<register_map_entry_t> g_dynamic_register_map;
+static register_map_entry_t *g_reg_entries = NULL;
+static size_t g_num_reg_entries = 0;
+
+static void
+RegisterEntryNotAvailable (register_map_entry_t *reg_entry)
+{
+    reg_entry->fail_value = k_zero_bytes;
+    reg_entry->nub_info.set = INVALID_NUB_REGNUM;
+    reg_entry->nub_info.reg = INVALID_NUB_REGNUM;
+    reg_entry->nub_info.name = NULL;
+    reg_entry->nub_info.alt = NULL;
+    reg_entry->nub_info.type = InvalidRegType;
+    reg_entry->nub_info.format = InvalidRegFormat;
+    reg_entry->nub_info.size = 0;
+    reg_entry->nub_info.offset = 0;
+    reg_entry->nub_info.reg_gcc = INVALID_NUB_REGNUM;
+    reg_entry->nub_info.reg_dwarf = INVALID_NUB_REGNUM;
+    reg_entry->nub_info.reg_generic = INVALID_NUB_REGNUM;
+    reg_entry->nub_info.reg_gdb = INVALID_NUB_REGNUM;
+}
+
+#if defined (__arm__)
+
+//----------------------------------------------------------------------
+// ARM regiseter sets as gdb knows them
+//----------------------------------------------------------------------
+
+register_map_entry_t
+g_gdb_register_map_arm[] =
+{
+    {  0,  4,  "r0",    {0}, NULL, 1},
+    {  1,  4,  "r1",    {0}, NULL, 1},
+    {  2,  4,  "r2",    {0}, NULL, 1},
+    {  3,  4,  "r3",    {0}, NULL, 1},
+    {  4,  4,  "r4",    {0}, NULL, 1},
+    {  5,  4,  "r5",    {0}, NULL, 1},
+    {  6,  4,  "r6",    {0}, NULL, 1},
+    {  7,  4,  "r7",    {0}, NULL, 1},
+    {  8,  4,  "r8",    {0}, NULL, 1},
+    {  9,  4,  "r9",    {0}, NULL, 1},
+    { 10,  4, "r10",    {0}, NULL, 1},
+    { 11,  4, "r11",    {0}, NULL, 1},
+    { 12,  4, "r12",    {0}, NULL, 1},
+    { 13,  4,  "sp",    {0}, NULL, 1},
+    { 14,  4,  "lr",    {0}, NULL, 1},
+    { 15,  4,  "pc",    {0}, NULL, 1},
+    { 16, 12,  "f0",    NULL, k_zero_bytes, 0},
+    { 17, 12,  "f1",    NULL, k_zero_bytes, 0},
+    { 18, 12,  "f2",    NULL, k_zero_bytes, 0},
+    { 19, 12,  "f3",    NULL, k_zero_bytes, 0},
+    { 20, 12,  "f4",    NULL, k_zero_bytes, 0},
+    { 21, 12,  "f5",    NULL, k_zero_bytes, 0},
+    { 22, 12,  "f6",    NULL, k_zero_bytes, 0},
+    { 23, 12,  "f7",    NULL, k_zero_bytes, 0},
+    { 24,  4, "fps",    {0}, NULL, 0},
+    { 25,  4,"cpsr",    {0}, NULL, 1},
+    { 26,  4,  "s0",    {0}, NULL, 0},
+    { 27,  4,  "s1",    {0}, NULL, 0},
+    { 28,  4,  "s2",    {0}, NULL, 0},
+    { 29,  4,  "s3",    {0}, NULL, 0},
+    { 30,  4,  "s4",    {0}, NULL, 0},
+    { 31,  4,  "s5",    {0}, NULL, 0},
+    { 32,  4,  "s6",    {0}, NULL, 0},
+    { 33,  4,  "s7",    {0}, NULL, 0},
+    { 34,  4,  "s8",    {0}, NULL, 0},
+    { 35,  4,  "s9",    {0}, NULL, 0},
+    { 36,  4, "s10",    {0}, NULL, 0},
+    { 37,  4, "s11",    {0}, NULL, 0},
+    { 38,  4, "s12",    {0}, NULL, 0},
+    { 39,  4, "s13",    {0}, NULL, 0},
+    { 40,  4, "s14",    {0}, NULL, 0},
+    { 41,  4, "s15",    {0}, NULL, 0},
+    { 42,  4, "s16",    {0}, NULL, 0},
+    { 43,  4, "s17",    {0}, NULL, 0},
+    { 44,  4, "s18",    {0}, NULL, 0},
+    { 45,  4, "s19",    {0}, NULL, 0},
+    { 46,  4, "s20",    {0}, NULL, 0},
+    { 47,  4, "s21",    {0}, NULL, 0},
+    { 48,  4, "s22",    {0}, NULL, 0},
+    { 49,  4, "s23",    {0}, NULL, 0},
+    { 50,  4, "s24",    {0}, NULL, 0},
+    { 51,  4, "s25",    {0}, NULL, 0},
+    { 52,  4, "s26",    {0}, NULL, 0},
+    { 53,  4, "s27",    {0}, NULL, 0},
+    { 54,  4, "s28",    {0}, NULL, 0},
+    { 55,  4, "s29",    {0}, NULL, 0},
+    { 56,  4, "s30",    {0}, NULL, 0},
+    { 57,  4, "s31",    {0}, NULL, 0},
+    { 58,  4, "fpscr",  {0}, NULL, 0}
+};
+
+void
+RNBRemote::InitializeRegisters (int use_native_registers)
+{
+    if (use_native_registers)
+    {
+        RNBRemote::InitializeNativeRegisters();
+    }
+    else
+    {
+        const size_t num_regs = sizeof (g_gdb_register_map_arm) / sizeof (register_map_entry_t);
+        for (uint32_t i=0; i<num_regs; ++i)
+        {
+            if (!DNBGetRegisterInfoByName (g_gdb_register_map_arm[i].gdb_name, &g_gdb_register_map_arm[i].nub_info))
+            {
+                RegisterEntryNotAvailable (&g_gdb_register_map_arm[i]);
+                assert (g_gdb_register_map_arm[i].gdb_size <= MAX_REGISTER_BYTE_SIZE);
+            }
+        }
+        g_reg_entries = g_gdb_register_map_arm;
+        g_num_reg_entries = sizeof (g_gdb_register_map_arm) / sizeof (register_map_entry_t);
+    }
+}
+
+
+#elif defined (__i386__)
+
+register_map_entry_t
+g_gdb_register_map_i386[] =
+{
+    {  0,   4, "eax"    , {0}, NULL, 0 },
+    {  1,   4, "ecx"    , {0}, NULL, 0 },
+    {  2,   4, "edx"    , {0}, NULL, 0 },
+    {  3,   4, "ebx"    , {0}, NULL, 0 },
+    {  4,   4, "esp"    , {0}, NULL, 1 },
+    {  5,   4, "ebp"    , {0}, NULL, 1 },
+    {  6,   4, "esi"    , {0}, NULL, 0 },
+    {  7,   4, "edi"    , {0}, NULL, 0 },
+    {  8,   4, "eip"    , {0}, NULL, 1 },
+    {  9,   4, "eflags" , {0}, NULL, 0 },
+    { 10,   4, "cs"     , {0}, NULL, 0 },
+    { 11,   4, "ss"     , {0}, NULL, 0 },
+    { 12,   4, "ds"     , {0}, NULL, 0 },
+    { 13,   4, "es"     , {0}, NULL, 0 },
+    { 14,   4, "fs"     , {0}, NULL, 0 },
+    { 15,   4, "gs"     , {0}, NULL, 0 },
+    { 16,  10, "stmm0"  , {0}, NULL, 0 },
+    { 17,  10, "stmm1"  , {0}, NULL, 0 },
+    { 18,  10, "stmm2"  , {0}, NULL, 0 },
+    { 19,  10, "stmm3"  , {0}, NULL, 0 },
+    { 20,  10, "stmm4"  , {0}, NULL, 0 },
+    { 21,  10, "stmm5"  , {0}, NULL, 0 },
+    { 22,  10, "stmm6"  , {0}, NULL, 0 },
+    { 23,  10, "stmm7"  , {0}, NULL, 0 },
+    { 24,   4, "fctrl"  , {0}, NULL, 0 },
+    { 25,   4, "fstat"  , {0}, NULL, 0 },
+    { 26,   4, "ftag"   , {0}, NULL, 0 },
+    { 27,   4, "fiseg"  , {0}, NULL, 0 },
+    { 28,   4, "fioff"  , {0}, NULL, 0 },
+    { 29,   4, "foseg"  , {0}, NULL, 0 },
+    { 30,   4, "fooff"  , {0}, NULL, 0 },
+    { 31,   4, "fop"    , {0}, NULL, 0 },
+    { 32,  16, "xmm0"   , {0}, NULL, 0 },
+    { 33,  16, "xmm1"   , {0}, NULL, 0 },
+    { 34,  16, "xmm2"   , {0}, NULL, 0 },
+    { 35,  16, "xmm3"   , {0}, NULL, 0 },
+    { 36,  16, "xmm4"   , {0}, NULL, 0 },
+    { 37,  16, "xmm5"   , {0}, NULL, 0 },
+    { 38,  16, "xmm6"   , {0}, NULL, 0 },
+    { 39,  16, "xmm7"   , {0}, NULL, 0 },
+    { 40,   4, "mxcsr"  , {0}, NULL, 0 },
+};
+
+void
+RNBRemote::InitializeRegisters (int use_native_registers)
+{
+    if (use_native_registers)
+    {
+        RNBRemote::InitializeNativeRegisters();
+    }
+    else
+    {
+        const size_t num_regs = sizeof (g_gdb_register_map_i386) / sizeof (register_map_entry_t);
+        for (uint32_t i=0; i<num_regs; ++i)
+        {
+            if (!DNBGetRegisterInfoByName (g_gdb_register_map_i386[i].gdb_name, &g_gdb_register_map_i386[i].nub_info))
+            {
+                RegisterEntryNotAvailable (&g_gdb_register_map_i386[i]);
+                assert (g_gdb_register_map_i386[i].gdb_size <= MAX_REGISTER_BYTE_SIZE);
+            }
+        }
+        g_reg_entries = g_gdb_register_map_i386;
+        g_num_reg_entries = sizeof (g_gdb_register_map_i386) / sizeof (register_map_entry_t);
+    }
+}
+
+
+#elif defined (__x86_64__)
+
+register_map_entry_t
+g_gdb_register_map_x86_64[] =
+{
+    {  0,   8, "rax"   , {0}, NULL, 0 },
+    {  1,   8, "rbx"   , {0}, NULL, 0 },
+    {  2,   8, "rcx"   , {0}, NULL, 0 },
+    {  3,   8, "rdx"   , {0}, NULL, 0 },
+    {  4,   8, "rsi"   , {0}, NULL, 0 },
+    {  5,   8, "rdi"   , {0}, NULL, 0 },
+    {  6,   8, "rbp"   , {0}, NULL, 1 },
+    {  7,   8, "rsp"   , {0}, NULL, 1 },
+    {  8,   8, "r8"    , {0}, NULL, 0 },
+    {  9,   8, "r9"    , {0}, NULL, 0 },
+    { 10,   8, "r10"   , {0}, NULL, 0 },
+    { 11,   8, "r11"   , {0}, NULL, 0 },
+    { 12,   8, "r12"   , {0}, NULL, 0 },
+    { 13,   8, "r13"   , {0}, NULL, 0 },
+    { 14,   8, "r14"   , {0}, NULL, 0 },
+    { 15,   8, "r15"   , {0}, NULL, 0 },
+    { 16,   8, "rip"   , {0}, NULL, 1 },
+    { 17,   4, "rflags", {0}, NULL, 0 },
+    { 18,   4, "cs"    , {0}, NULL, 0 },
+    { 19,   4, "ss"    , {0}, NULL, 0 },
+    { 20,   4, "ds"    , {0}, NULL, 0 },
+    { 21,   4, "es"    , {0}, NULL, 0 },
+    { 22,   4, "fs"    , {0}, NULL, 0 },
+    { 23,   4, "gs"    , {0}, NULL, 0 },
+    { 24,  10, "stmm0" , {0}, NULL, 0 },
+    { 25,  10, "stmm1" , {0}, NULL, 0 },
+    { 26,  10, "stmm2" , {0}, NULL, 0 },
+    { 27,  10, "stmm3" , {0}, NULL, 0 },
+    { 28,  10, "stmm4" , {0}, NULL, 0 },
+    { 29,  10, "stmm5" , {0}, NULL, 0 },
+    { 30,  10, "stmm6" , {0}, NULL, 0 },
+    { 31,  10, "stmm7" , {0}, NULL, 0 },
+    { 32,   4, "fctrl" , {0}, NULL, 0 },
+    { 33,   4, "fstat" , {0}, NULL, 0 },
+    { 34,   4, "ftag"  , {0}, NULL, 0 },
+    { 35,   4, "fiseg" , {0}, NULL, 0 },
+    { 36,   4, "fioff" , {0}, NULL, 0 },
+    { 37,   4, "foseg" , {0}, NULL, 0 },
+    { 38,   4, "fooff" , {0}, NULL, 0 },
+    { 39,   4, "fop"   , {0}, NULL, 0 },
+    { 40,  16, "xmm0"  , {0}, NULL, 0 },
+    { 41,  16, "xmm1"  , {0}, NULL, 0 },
+    { 42,  16, "xmm2"  , {0}, NULL, 0 },
+    { 43,  16, "xmm3"  , {0}, NULL, 0 },
+    { 44,  16, "xmm4"  , {0}, NULL, 0 },
+    { 45,  16, "xmm5"  , {0}, NULL, 0 },
+    { 46,  16, "xmm6"  , {0}, NULL, 0 },
+    { 47,  16, "xmm7"  , {0}, NULL, 0 },
+    { 48,  16, "xmm8"  , {0}, NULL, 0 },
+    { 49,  16, "xmm9"  , {0}, NULL, 0 },
+    { 50,  16, "xmm10" , {0}, NULL, 0 },
+    { 51,  16, "xmm11" , {0}, NULL, 0 },
+    { 52,  16, "xmm12" , {0}, NULL, 0 },
+    { 53,  16, "xmm13" , {0}, NULL, 0 },
+    { 54,  16, "xmm14" , {0}, NULL, 0 },
+    { 55,  16, "xmm15" , {0}, NULL, 0 },
+    { 56,   4, "mxcsr" , {0}, NULL, 0 }
+};
+
+void
+RNBRemote::InitializeRegisters (int use_native_registers)
+{
+    if (use_native_registers)
+    {
+        RNBRemote::InitializeNativeRegisters();
+    }
+    else
+    {
+        const size_t num_regs = sizeof (g_gdb_register_map_x86_64) / sizeof (register_map_entry_t);
+        for (uint32_t i=0; i<num_regs; ++i)
+        {
+            if (!DNBGetRegisterInfoByName (g_gdb_register_map_x86_64[i].gdb_name, &g_gdb_register_map_x86_64[i].nub_info))
+            {
+                RegisterEntryNotAvailable (&g_gdb_register_map_x86_64[i]);
+                assert (g_gdb_register_map_x86_64[i].gdb_size < MAX_REGISTER_BYTE_SIZE);
+            }
+        }
+        g_reg_entries = g_gdb_register_map_x86_64;
+        g_num_reg_entries = sizeof (g_gdb_register_map_x86_64) / sizeof (register_map_entry_t);
+    }
+}
+
+
+#else
+
+void
+RNBRemote::InitializeRegisters (int use_native_registers)
+{
+    // No choice, we don't have a GDB register definition for this arch.
+    RNBRemote::InitializeNativeRegisters();
+}
+
+#endif
+
+
+void
+RNBRemote::InitializeNativeRegisters()
+{
+    if (g_dynamic_register_map.empty())
+    {
+        nub_size_t num_reg_sets = 0;
+        const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo (&num_reg_sets);
+
+        assert (num_reg_sets > 0 && reg_sets != NULL);
+
+        uint32_t regnum = 0;
+        for (nub_size_t set = 0; set < num_reg_sets; ++set)
+        {
+            if (reg_sets[set].registers == NULL)
+                continue;
+
+            for (uint32_t reg=0; reg < reg_sets[set].num_registers; ++reg)
+            {
+                register_map_entry_t reg_entry = {
+                    regnum++,                           // register number starts at zero and goes up with no gaps
+                    reg_sets[set].registers[reg].size,  // register size in bytes
+                    reg_sets[set].registers[reg].name,  // register name
+                    reg_sets[set].registers[reg],       // DNBRegisterInfo
+                    NULL,                               // Value to print if case we fail to reg this register (if this is NULL, we will return an error)
+                    reg_sets[set].registers[reg].reg_generic != INVALID_NUB_REGNUM};
+
+                g_dynamic_register_map.push_back (reg_entry);
+            }
+        }
+        g_reg_entries = g_dynamic_register_map.data();
+        g_num_reg_entries = g_dynamic_register_map.size();
+    }
+}
+
+
+const register_map_entry_t *
+register_mapping_by_regname (const char *n)
+{
+    for (uint32_t reg = 0; reg < g_num_reg_entries; reg++)
+    {
+        if (strcmp (g_reg_entries[reg].gdb_name, n) == 0)
+            return &g_reg_entries[reg];
+    }
+    return NULL;
+}
+
+/* The inferior has stopped executing; send a packet
+ to gdb to let it know.  */
+
+void
+RNBRemote::NotifyThatProcessStopped (void)
+{
+    RNBRemote::HandlePacket_last_signal ("");
+    return;
+}
+
+
+/* `A arglen,argnum,arg,...'
+ Update the inferior context CTX with the program name and arg
+ list.
+ The documentation for this packet is underwhelming but my best reading
+ of this is that it is a series of (len, position #, arg)'s, one for
+ each argument with "arg" ``hex encoded'' (two 0-9a-f chars?).
+ Why we need BOTH a "len" and a hex encoded "arg" is beyond me - either
+ is sufficient to get around the "," position separator escape issue.
+
+ e.g. our best guess for a valid 'A' packet for "gdb -q a.out" is
+
+ 6,0,676462,4,1,2d71,10,2,612e6f7574
+
+ Note that "argnum" and "arglen" are numbers in base 10.  Again, that's
+ not documented either way but I'm assuming it's so.  */
+
+rnb_err_t
+RNBRemote::HandlePacket_A (const char *p)
+{
+    if (p == NULL || *p == '\0')
+    {
+        return HandlePacket_ILLFORMED ("Null packet for 'A' pkt");
+    }
+    p++;
+    if (p == '\0' || !isdigit (*p))
+    {
+        return HandlePacket_ILLFORMED ("arglen not specified on 'A' pkt");
+    }
+
+    /* I promise I don't modify it anywhere in this function.  strtoul()'s
+     2nd arg has to be non-const which makes it problematic to step
+     through the string easily.  */
+    char *buf = const_cast<char *>(p);
+
+    RNBContext& ctx = Context();
+
+    while (*buf != '\0')
+    {
+        int arglen, argnum;
+        std::string arg;
+        char *c;
+
+        errno = 0;
+        arglen = strtoul (buf, &c, 10);
+        if (errno != 0 && arglen == 0)
+        {
+            return HandlePacket_ILLFORMED ("arglen not a number on 'A' pkt");
+        }
+        if (*c != ',')
+        {
+            return HandlePacket_ILLFORMED ("arglen not followed by comma on 'A' pkt");
+        }
+        buf = c + 1;
+
+        errno = 0;
+        argnum = strtoul (buf, &c, 10);
+        if (errno != 0 && argnum == 0)
+        {
+            return HandlePacket_ILLFORMED ("argnum not a number on 'A' pkt");
+        }
+        if (*c != ',')
+        {
+            return HandlePacket_ILLFORMED ("arglen not followed by comma on 'A' pkt");
+        }
+        buf = c + 1;
+
+        c = buf;
+        buf = buf + arglen;
+        while (c < buf && *c != '\0' && c + 1 < buf && *(c + 1) != '\0')
+        {
+            char smallbuf[3];
+            smallbuf[0] = *c;
+            smallbuf[1] = *(c + 1);
+            smallbuf[2] = '\0';
+
+            errno = 0;
+            int ch = strtoul (smallbuf, NULL, 16);
+            if (errno != 0 && ch == 0)
+            {
+                return HandlePacket_ILLFORMED ("non-hex char in arg on 'A' pkt");
+            }
+
+            arg.push_back(ch);
+            c += 2;
+        }
+
+        ctx.PushArgument (arg.c_str());
+        if (*buf == ',')
+            buf++;
+    }
+    SendPacket ("OK");
+
+    return rnb_success;
+}
+
+/* `H c t'
+ Set the thread for subsequent actions; 'c' for step/continue ops,
+ 'g' for other ops.  -1 means all threads, 0 means any thread.  */
+
+rnb_err_t
+RNBRemote::HandlePacket_H (const char *p)
+{
+    p++;  // skip 'H'
+    if (*p != 'c' && *p != 'g')
+    {
+        return HandlePacket_ILLFORMED ("Missing 'c' or 'g' type in H packet");
+    }
+
+    if (!m_ctx.HasValidProcessID())
+    {
+        // We allow gdb to connect to a server that hasn't started running
+        // the target yet.  gdb still wants to ask questions about it and
+        // freaks out if it gets an error.  So just return OK here.
+    }
+
+    errno = 0;
+    nub_thread_t tid = strtoul (p + 1, NULL, 16);
+    if (errno != 0 && tid == 0)
+    {
+        return HandlePacket_ILLFORMED ("Invalid thread number in H packet");
+    }
+    if (*p == 'c')
+        SetContinueThread (tid);
+    if (*p == 'g')
+        SetCurrentThread (tid);
+
+    return SendPacket ("OK");
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_qLaunchSuccess (const char *p)
+{
+    if (m_ctx.HasValidProcessID() || m_ctx.LaunchStatus().Error() == 0)
+        return SendPacket("OK");
+    std::ostringstream ret_str;
+    std::string status_str;
+    ret_str << "E" << m_ctx.LaunchStatusAsString(status_str);
+
+    return SendPacket (ret_str.str());
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qShlibInfoAddr (const char *p)
+{
+    if (m_ctx.HasValidProcessID())
+    {
+        nub_addr_t shlib_info_addr = DNBProcessGetSharedLibraryInfoAddress(m_ctx.ProcessID());
+        if (shlib_info_addr != INVALID_NUB_ADDRESS)
+        {
+            std::ostringstream ostrm;
+            ostrm << RAW_HEXBASE << shlib_info_addr;
+            return SendPacket (ostrm.str ());
+        }
+    }
+    return SendPacket ("E44");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qStepPacketSupported (const char *p)
+{
+    // Normally the "s" packet is mandatory, yet in gdb when using ARM, they
+    // get around the need for this packet by implementing software single
+    // stepping from gdb. Current versions of debugserver do support the "s"
+    // packet, yet some older versions do not. We need a way to tell if this
+    // packet is supported so we can disable software single stepping in gdb
+    // for remote targets (so the "s" packet will get used).
+    return SendPacket("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qThreadStopInfo (const char *p)
+{
+    p += strlen ("qThreadStopInfo");
+    nub_thread_t tid = strtoul(p, 0, 16);
+    return SendStopReplyPacketForThread (tid);
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qThreadInfo (const char *p)
+{
+    // We allow gdb to connect to a server that hasn't started running
+    // the target yet.  gdb still wants to ask questions about it and
+    // freaks out if it gets an error.  So just return OK here.
+    nub_process_t pid = m_ctx.ProcessID();
+    if (pid == INVALID_NUB_PROCESS)
+        return SendPacket ("OK");
+
+    // Only "qfThreadInfo" and "qsThreadInfo" get into this function so
+    // we only need to check the second byte to tell which is which
+    if (p[1] == 'f')
+    {
+        nub_size_t numthreads = DNBProcessGetNumThreads (pid);
+        std::ostringstream ostrm;
+        ostrm << "m";
+        bool first = true;
+        for (nub_size_t i = 0; i < numthreads; ++i)
+        {
+            if (first)
+                first = false;
+            else
+                ostrm << ",";
+            nub_thread_t th = DNBProcessGetThreadAtIndex (pid, i);
+            ostrm << std::hex << th;
+        }
+        return SendPacket (ostrm.str ());
+    }
+    else
+    {
+        return SendPacket ("l");
+    }
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qThreadExtraInfo (const char *p)
+{
+    // We allow gdb to connect to a server that hasn't started running
+    // the target yet.  gdb still wants to ask questions about it and
+    // freaks out if it gets an error.  So just return OK here.
+    nub_process_t pid = m_ctx.ProcessID();
+    if (pid == INVALID_NUB_PROCESS)
+        return SendPacket ("OK");
+
+    /* This is supposed to return a string like 'Runnable' or
+     'Blocked on Mutex'.
+     The returned string is formatted like the "A" packet - a
+     sequence of letters encoded in as 2-hex-chars-per-letter.  */
+    p += strlen ("qThreadExtraInfo");
+    if (*p++ != ',')
+        return HandlePacket_ILLFORMED ("Ill formed qThreadExtraInfo packet");
+    errno = 0;
+    nub_thread_t tid = strtoul (p, NULL, 16);
+    if (errno != 0 && tid == 0)
+    {
+        return HandlePacket_ILLFORMED ("Invalid thread number in qThreadExtraInfo packet");
+    }
+
+    const char * threadInfo = DNBThreadGetInfo(pid, tid);
+    if (threadInfo != NULL && threadInfo[0])
+    {
+        return SendHexEncodedBytePacket(NULL, threadInfo, strlen(threadInfo), NULL);
+    }
+    else
+    {
+        // "OK" == 4f6b
+        // Return "OK" as a ASCII hex byte stream if things go wrong
+        return SendPacket ("4f6b");
+    }
+
+    return SendPacket ("");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qC (const char *p)
+{
+    nub_process_t pid;
+    std::ostringstream rep;
+    // If we haven't run the process yet, we tell the debugger the
+    // pid is 0.  That way it can know to tell use to run later on.
+    if (m_ctx.HasValidProcessID())
+        pid = m_ctx.ProcessID();
+    else
+        pid = 0;
+    rep << "QC" << std::hex << pid;
+    return SendPacket (rep.str());
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qRegisterInfo (const char *p)
+{
+    p += strlen ("qRegisterInfo");
+
+    nub_size_t num_reg_sets = 0;
+    const DNBRegisterSetInfo *reg_set_info = DNBGetRegisterSetInfo (&num_reg_sets);
+    uint32_t reg_num = strtoul(p, 0, 16);
+
+    if (reg_num < g_num_reg_entries)
+    {
+        const register_map_entry_t *reg_entry = &g_reg_entries[reg_num];
+        std::ostringstream ostrm;
+        ostrm << "name:" << reg_entry->gdb_name << ';';
+
+        if (reg_entry->nub_info.name && ::strcmp (reg_entry->gdb_name, reg_entry->nub_info.name))
+            ostrm << "alt-name:" << reg_entry->nub_info.name << ';';
+        else if (reg_entry->nub_info.alt && ::strcmp (reg_entry->gdb_name, reg_entry->nub_info.alt))
+            ostrm << "alt-name:" << reg_entry->nub_info.alt << ';';
+
+        ostrm << "bitsize:" << std::dec << reg_entry->gdb_size * 8 << ';';
+        ostrm << "offset:" << std::dec << reg_entry->nub_info.offset << ';';
+
+        switch (reg_entry->nub_info.type)
+        {
+            case Uint:      ostrm << "encoding:uint;"; break;
+            case Sint:      ostrm << "encoding:sint;"; break;
+            case IEEE754:   ostrm << "encoding:ieee754;"; break;
+            case Vector:    ostrm << "encoding:vector;"; break;
+        }
+
+        switch (reg_entry->nub_info.format)
+        {
+            case Binary:            ostrm << "format:binary;"; break;
+            case Decimal:           ostrm << "format:decimal;"; break;
+            case Hex:               ostrm << "format:hex;"; break;
+            case Float:             ostrm << "format:float;"; break;
+            case VectorOfSInt8:     ostrm << "format:vector-sint8;"; break;
+            case VectorOfUInt8:     ostrm << "format:vector-uint8;"; break;
+            case VectorOfSInt16:    ostrm << "format:vector-sint16;"; break;
+            case VectorOfUInt16:    ostrm << "format:vector-uint16;"; break;
+            case VectorOfSInt32:    ostrm << "format:vector-sint32;"; break;
+            case VectorOfUInt32:    ostrm << "format:vector-uint32;"; break;
+            case VectorOfFloat32:   ostrm << "format:vector-float32;"; break;
+            case VectorOfUInt128:   ostrm << "format:vector-uint128;"; break;
+        };
+
+        if (reg_set_info && reg_entry->nub_info.set < num_reg_sets)
+            ostrm << "set:" << reg_set_info[reg_entry->nub_info.set].name << ';';
+
+
+        if (g_reg_entries != g_dynamic_register_map.data())
+        {
+            if (reg_entry->nub_info.reg_gdb != INVALID_NUB_REGNUM && reg_entry->nub_info.reg_gdb != reg_num)
+            {
+                printf("register %s is getting gdb reg_num of %u when the register info says %u\n",
+                       reg_entry->gdb_name, reg_num, reg_entry->nub_info.reg_gdb);
+            }
+        }
+
+        if (reg_entry->nub_info.reg_gcc != INVALID_NUB_REGNUM)
+            ostrm << "gcc:" << std::dec << reg_entry->nub_info.reg_gcc << ';';
+
+        if (reg_entry->nub_info.reg_dwarf != INVALID_NUB_REGNUM)
+            ostrm << "dwarf:" << std::dec << reg_entry->nub_info.reg_dwarf << ';';
+
+
+        switch (reg_entry->nub_info.reg_generic)
+        {
+            case GENERIC_REGNUM_FP:     ostrm << "generic:fp;"; break;
+            case GENERIC_REGNUM_PC:     ostrm << "generic:pc;"; break;
+            case GENERIC_REGNUM_SP:     ostrm << "generic:sp;"; break;
+            case GENERIC_REGNUM_RA:     ostrm << "generic:ra;"; break;
+            case GENERIC_REGNUM_FLAGS:  ostrm << "generic:flags;"; break;
+            default: break;
+        }
+
+        return SendPacket (ostrm.str ());
+    }
+    return SendPacket ("E45");
+}
+
+
+/* This expects a packet formatted like
+
+ QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE;
+
+ with the "QSetLogging:" already removed from the start.  Maybe in the
+ future this packet will include other keyvalue pairs like
+
+ QSetLogging:bitmask=LOG_ALL;mode=asl;
+ */
+
+rnb_err_t
+set_logging (const char *p)
+{
+    int bitmask = 0;
+    while (p && *p != '\0')
+    {
+        if (strncmp (p, "bitmask=", sizeof ("bitmask=") - 1) == 0)
+        {
+            p += sizeof ("bitmask=") - 1;
+            while (p && *p != '\0' && *p != ';')
+            {
+                if (*p == '|')
+                    p++;
+                if (strncmp (p, "LOG_VERBOSE", sizeof ("LOG_VERBOSE") - 1) == 0)
+                {
+                    p += sizeof ("LOG_VERBOSE") - 1;
+                    bitmask |= LOG_VERBOSE;
+                }
+                else if (strncmp (p, "LOG_PROCESS", sizeof ("LOG_PROCESS") - 1) == 0)
+                {
+                    p += sizeof ("LOG_PROCESS") - 1;
+                    bitmask |= LOG_PROCESS;
+                }
+                else if (strncmp (p, "LOG_THREAD", sizeof ("LOG_THREAD") - 1) == 0)
+                {
+                    p += sizeof ("LOG_THREAD") - 1;
+                    bitmask |= LOG_THREAD;
+                }
+                else if (strncmp (p, "LOG_EXCEPTIONS", sizeof ("LOG_EXCEPTIONS") - 1) == 0)
+                {
+                    p += sizeof ("LOG_EXCEPTIONS") - 1;
+                    bitmask |= LOG_EXCEPTIONS;
+                }
+                else if (strncmp (p, "LOG_SHLIB", sizeof ("LOG_SHLIB") - 1) == 0)
+                {
+                    p += sizeof ("LOG_SHLIB") - 1;
+                    bitmask |= LOG_SHLIB;
+                }
+                else if (strncmp (p, "LOG_MEMORY", sizeof ("LOG_MEMORY") - 1) == 0)
+                {
+                    p += sizeof ("LOG_MEMORY") - 1;
+                    bitmask |= LOG_MEMORY;
+                }
+                else if (strncmp (p, "LOG_MEMORY_DATA_SHORT", sizeof ("LOG_MEMORY_DATA_SHORT") - 1) == 0)
+                {
+                    p += sizeof ("LOG_MEMORY_DATA_SHORT") - 1;
+                    bitmask |= LOG_MEMORY_DATA_SHORT;
+                }
+                else if (strncmp (p, "LOG_MEMORY_DATA_LONG", sizeof ("LOG_MEMORY_DATA_LONG") - 1) == 0)
+                {
+                    p += sizeof ("LOG_MEMORY_DATA_LONG") - 1;
+                    bitmask |= LOG_MEMORY_DATA_LONG;
+                }
+                else if (strncmp (p, "LOG_BREAKPOINTS", sizeof ("LOG_BREAKPOINTS") - 1) == 0)
+                {
+                    p += sizeof ("LOG_BREAKPOINTS") - 1;
+                    bitmask |= LOG_BREAKPOINTS;
+                }
+                else if (strncmp (p, "LOG_ALL", sizeof ("LOG_ALL") - 1) == 0)
+                {
+                    p += sizeof ("LOG_ALL") - 1;
+                    bitmask |= LOG_ALL;
+                }
+                else if (strncmp (p, "LOG_EVENTS", sizeof ("LOG_EVENTS") - 1) == 0)
+                {
+                    p += sizeof ("LOG_EVENTS") - 1;
+                    bitmask |= LOG_EVENTS;
+                }
+                else if (strncmp (p, "LOG_DEFAULT", sizeof ("LOG_DEFAULT") - 1) == 0)
+                {
+                    p += sizeof ("LOG_DEFAULT") - 1;
+                    bitmask |= LOG_DEFAULT;
+                }
+                else if (strncmp (p, "LOG_NONE", sizeof ("LOG_NONE") - 1) == 0)
+                {
+                    p += sizeof ("LOG_NONE") - 1;
+                    bitmask = 0;
+                }
+                else if (strncmp (p, "LOG_RNB_MINIMAL", sizeof ("LOG_RNB_MINIMAL") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_MINIMAL") - 1;
+                    bitmask |= LOG_RNB_MINIMAL;
+                }
+                else if (strncmp (p, "LOG_RNB_MEDIUM", sizeof ("LOG_RNB_MEDIUM") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_MEDIUM") - 1;
+                    bitmask |= LOG_RNB_MEDIUM;
+                }
+                else if (strncmp (p, "LOG_RNB_MAX", sizeof ("LOG_RNB_MAX") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_MAX") - 1;
+                    bitmask |= LOG_RNB_MAX;
+                }
+                else if (strncmp (p, "LOG_RNB_COMM", sizeof ("LOG_RNB_COMM") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_COMM") - 1;
+                    bitmask |= LOG_RNB_COMM;
+                }
+                else if (strncmp (p, "LOG_RNB_REMOTE", sizeof ("LOG_RNB_REMOTE") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_REMOTE") - 1;
+                    bitmask |= LOG_RNB_REMOTE;
+                }
+                else if (strncmp (p, "LOG_RNB_EVENTS", sizeof ("LOG_RNB_EVENTS") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_EVENTS") - 1;
+                    bitmask |= LOG_RNB_EVENTS;
+                }
+                else if (strncmp (p, "LOG_RNB_PROC", sizeof ("LOG_RNB_PROC") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_PROC") - 1;
+                    bitmask |= LOG_RNB_PROC;
+                }
+                else if (strncmp (p, "LOG_RNB_PACKETS", sizeof ("LOG_RNB_PACKETS") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_PACKETS") - 1;
+                    bitmask |= LOG_RNB_PACKETS;
+                }
+                else if (strncmp (p, "LOG_RNB_ALL", sizeof ("LOG_RNB_ALL") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_ALL") - 1;
+                    bitmask |= LOG_RNB_ALL;
+                }
+                else if (strncmp (p, "LOG_RNB_DEFAULT", sizeof ("LOG_RNB_DEFAULT") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_DEFAULT") - 1;
+                    bitmask |= LOG_RNB_DEFAULT;
+                }
+                else if (strncmp (p, "LOG_RNB_NONE", sizeof ("LOG_RNB_NONE") - 1) == 0)
+                {
+                    p += sizeof ("LOG_RNB_NONE") - 1;
+                    bitmask = 0;
+                }
+                else
+                {
+                    /* Unrecognized logging bit; ignore it.  */
+                    const char *c = strchr (p, '|');
+                    if (c)
+                    {
+                        p = c;
+                    }
+                    else
+                    {
+                        c = strchr (p, ';');
+                        if (c)
+                        {
+                            p = c;
+                        }
+                        else
+                        {
+                            // Improperly terminated word; just go to end of str
+                            p = strchr (p, '\0');
+                        }
+                    }
+                }
+            }
+            // Did we get a properly formatted logging bitmask?
+            if (*p == ';')
+            {
+                // Enable DNB logging
+                DNBLogSetLogCallback(ASLLogCallback, NULL);
+                DNBLogSetLogMask (bitmask);
+                p++;
+            }
+        }
+        // We're not going to support logging to a file for now.  All logging
+        // goes through ASL.
+#if 0
+        else if (strncmp (p, "mode=", sizeof ("mode=") - 1) == 0)
+        {
+            p += sizeof ("mode=") - 1;
+            if (strncmp (p, "asl;", sizeof ("asl;") - 1) == 0)
+            {
+                DNBLogToASL ();
+                p += sizeof ("asl;") - 1;
+            }
+            else if (strncmp (p, "file;", sizeof ("file;") - 1) == 0)
+            {
+                DNBLogToFile ();
+                p += sizeof ("file;") - 1;
+            }
+            else
+            {
+                // Ignore unknown argument
+                const char *c = strchr (p, ';');
+                if (c)
+                    p = c + 1;
+                else
+                    p = strchr (p, '\0');
+            }
+        }
+        else if (strncmp (p, "filename=", sizeof ("filename=") - 1) == 0)
+        {
+            p += sizeof ("filename=") - 1;
+            const char *c = strchr (p, ';');
+            if (c == NULL)
+            {
+                c = strchr (p, '\0');
+                continue;
+            }
+            char *fn = (char *) alloca (c - p + 1);
+            strncpy (fn, p, c - p);
+            fn[c - p] = '\0';
+
+            // A file name of "asl" is special and is another way to indicate
+            // that logging should be done via ASL, not by file.
+            if (strcmp (fn, "asl") == 0)
+            {
+                DNBLogToASL ();
+            }
+            else
+            {
+                FILE *f = fopen (fn, "w");
+                if (f)
+                {
+                    DNBLogSetLogFile (f);
+                    DNBEnableLogging (f, DNBLogGetLogMask ());
+                    DNBLogToFile ();
+                }
+            }
+            p = c + 1;
+        }
+#endif /* #if 0 to enforce ASL logging only.  */
+        else
+        {
+            // Ignore unknown argument
+            const char *c = strchr (p, ';');
+            if (c)
+                p = c + 1;
+            else
+                p = strchr (p, '\0');
+        }
+    }
+
+    return rnb_success;
+}
+
+
+
+rnb_err_t
+RNBRemote::HandlePacket_Q (const char *p)
+{
+    if (p == NULL || strlen (p) <= 1)
+    {
+        return HandlePacket_ILLFORMED ("No subtype specified in Q packet");
+    }
+
+    /* Switch to no-ack protocol mode after the "OK" packet is sent
+     and the ack for that comes back from gdb.  */
+
+    if (strcmp (p, "QStartNoAckMode") == 0)
+    {
+        rnb_err_t result = SendPacket ("OK");
+        m_noack_mode = true;
+        return result;
+    }
+
+    if (strncmp (p, "QSetLogging:", sizeof ("QSetLogging:") - 1) == 0)
+    {
+        p += sizeof ("QSetLogging:") - 1;
+        rnb_err_t result = set_logging (p);
+        if (result == rnb_success)
+            return SendPacket ("OK");
+        else
+            return SendPacket ("E35");
+    }
+
+    /* The number of characters in a packet payload that gdb is
+     prepared to accept.  The packet-start char, packet-end char,
+     2 checksum chars and terminating null character are not included
+     in this size.  */
+    if (strncmp (p, "QSetMaxPayloadSize:", sizeof ("QSetMaxPayloadSize:") - 1) == 0)
+    {
+        p += sizeof ("QSetMaxPayloadSize:") - 1;
+        errno = 0;
+        uint32_t size = strtoul (p, NULL, 16);
+        if (errno != 0 && size == 0)
+        {
+            return HandlePacket_ILLFORMED ("Invalid length in QSetMaxPayloadSize packet");
+        }
+        m_max_payload_size = size;
+        return SendPacket ("OK");
+    }
+
+    /* This tells us the largest packet that gdb can handle.
+     i.e. the size of gdb's packet-reading buffer.
+     QSetMaxPayloadSize is preferred because it is less ambiguous.  */
+
+    if (strncmp (p, "QSetMaxPacketSize:", sizeof ("QSetMaxPacketSize:") - 1) == 0)
+    {
+        p += sizeof ("QSetMaxPacketSize:") - 1;
+        errno = 0;
+        uint32_t size = strtoul (p, NULL, 16);
+        if (errno != 0 && size == 0)
+        {
+            return HandlePacket_ILLFORMED ("Invalid length in QSetMaxPacketSize packet");
+        }
+        m_max_payload_size = size - 5;
+        return SendPacket ("OK");
+    }
+
+    /* This sets the environment for the target program.  The packet is of the form:
+
+     QEnvironment:VARIABLE=VALUE
+
+     */
+
+    if (strncmp (p, "QEnvironment:", sizeof ("QEnvironment:") - 1) == 0)
+    {
+        DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironment: \"%s\"",
+                          (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p);
+
+        p += sizeof ("QEnvironment:") - 1;
+        RNBContext& ctx = Context();
+
+        ctx.PushEnvironment (p);
+        return SendPacket ("OK");
+    }
+
+    // Unrecognized Q packet
+    return SendPacket ("");
+}
+
+void
+append_hex_value (std::ostream& ostrm, const uint8_t* buf, size_t buf_size, bool swap)
+{
+    int i;
+    if (swap)
+    {
+        for (i = buf_size-1; i >= 0; i--)
+            ostrm << RAWHEX8(buf[i]);
+    }
+    else
+    {
+        for (i = 0; i < buf_size; i++)
+            ostrm << RAWHEX8(buf[i]);
+    }
+}
+
+
+void
+register_value_in_hex_fixed_width
+(
+ std::ostream& ostrm,
+ nub_process_t pid,
+ nub_thread_t tid,
+ const register_map_entry_t* reg
+ )
+{
+    if (reg != NULL)
+    {
+        DNBRegisterValue val;
+        if (DNBThreadGetRegisterValueByID (pid, tid, reg->nub_info.set, reg->nub_info.reg, &val))
+        {
+            append_hex_value (ostrm, val.value.v_uint8, reg->gdb_size, false);
+        }
+        else
+        {
+            // If we fail to read a regiser value, check if it has a default
+            // fail value. If it does, return this instead in case some of
+            // the registers are not available on the current system.
+            if (reg->gdb_size > 0)
+            {
+                if (reg->fail_value != NULL)
+                {
+                    append_hex_value (ostrm, reg->fail_value, reg->gdb_size, false);
+                }
+                else
+                {
+                    std::basic_string<uint8_t> zeros(reg->gdb_size, '\0');
+                    append_hex_value (ostrm, zeros.data(), zeros.size(), false);
+                }
+            }
+        }
+    }
+}
+
+
+void
+gdb_regnum_with_fixed_width_hex_register_value
+(
+ std::ostream& ostrm,
+ nub_process_t pid,
+ nub_thread_t tid,
+ const register_map_entry_t* reg
+ )
+{
+    // Output the register number as 'NN:VVVVVVVV;' where NN is a 2 bytes HEX
+    // gdb register number, and VVVVVVVV is the correct number of hex bytes
+    // as ASCII for the register value.
+    if (reg != NULL)
+    {
+        ostrm << RAWHEX8(reg->gdb_regnum) << ':';
+        register_value_in_hex_fixed_width (ostrm, pid, tid, reg);
+        ostrm << ';';
+    }
+}
+
+rnb_err_t
+RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid)
+{
+    const nub_process_t pid = m_ctx.ProcessID();
+    if (pid == INVALID_NUB_PROCESS)
+        return SendPacket("E50");
+
+    struct DNBThreadStopInfo tid_stop_info;
+
+    /* Fill the remaining space in this packet with as many registers
+     as we can stuff in there.  */
+
+    if (DNBThreadGetStopReason (pid, tid, &tid_stop_info))
+    {
+        std::ostringstream ostrm;
+        // Output the T packet with the thread
+        ostrm << 'T';
+        int signum = tid_stop_info.details.signal.signo;
+        DNBLogThreadedIf (LOG_RNB_PROC, "%8d %s got signal signo = %u, exc_type = %u", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, tid_stop_info.details.signal.signo, tid_stop_info.details.exception.type);
+
+        // Translate any mach exceptions to gdb versions, unless they are
+        // common exceptions like a breakpoint or a soft signal.
+        switch (tid_stop_info.details.exception.type)
+        {
+            default:                    signum = 0; break;
+            case EXC_BREAKPOINT:        signum = SIGTRAP; break;
+            case EXC_BAD_ACCESS:        signum = TARGET_EXC_BAD_ACCESS; break;
+            case EXC_BAD_INSTRUCTION:   signum = TARGET_EXC_BAD_INSTRUCTION; break;
+            case EXC_ARITHMETIC:        signum = TARGET_EXC_ARITHMETIC; break;
+            case EXC_EMULATION:         signum = TARGET_EXC_EMULATION; break;
+            case EXC_SOFTWARE:
+                if (tid_stop_info.details.exception.data_count == 2 &&
+                    tid_stop_info.details.exception.data[0] == EXC_SOFT_SIGNAL)
+                    signum = tid_stop_info.details.exception.data[1];
+                else
+                    signum = TARGET_EXC_SOFTWARE;
+                break;
+        }
+
+        ostrm << RAWHEX8(signum & 0xff);
+
+        ostrm << std::hex << "thread:" << tid << ';';
+
+        const char *thread_name = DNBThreadGetName (pid, tid);
+        if (thread_name && thread_name[0])
+            ostrm << std::hex << "name:" << thread_name << ';';
+
+        thread_identifier_info_data_t thread_ident_info;
+        if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info))
+        {
+            if (thread_ident_info.dispatch_qaddr != 0)
+                ostrm << std::hex << "dispatchqaddr:" << thread_ident_info.dispatch_qaddr << ';';
+        }
+        DNBRegisterValue reg_value;
+        for (uint32_t reg = 0; reg < g_num_reg_entries; reg++)
+        {
+            if (g_reg_entries[reg].expedite)
+            {
+                if (!DNBThreadGetRegisterValueByID (pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, &reg_value))
+                    continue;
+
+                gdb_regnum_with_fixed_width_hex_register_value (ostrm, pid, tid, &g_reg_entries[reg]);
+            }
+        }
+
+        if (tid_stop_info.details.exception.type)
+        {
+            ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type << ";";
+            ostrm << "mecount:" << std::hex << tid_stop_info.details.exception.data_count << ";";
+            for (int i = 0; i < tid_stop_info.details.exception.data_count; ++i)
+                ostrm << "medata:" << std::hex << tid_stop_info.details.exception.data[i] << ";";
+        }
+        return SendPacket (ostrm.str ());
+    }
+    return SendPacket("E51");
+}
+
+/* `?'
+ The stop reply packet - tell gdb what the status of the inferior is.
+ Often called the questionmark_packet.  */
+
+rnb_err_t
+RNBRemote::HandlePacket_last_signal (const char *unused)
+{
+    if (!m_ctx.HasValidProcessID())
+    {
+        // Inferior is not yet specified/running
+        return SendPacket ("E02");
+    }
+
+    nub_process_t pid = m_ctx.ProcessID();
+    nub_state_t pid_state = DNBProcessGetState (pid);
+
+    switch (pid_state)
+    {
+        case eStateAttaching:
+        case eStateLaunching:
+        case eStateRunning:
+        case eStateStepping:
+            return rnb_success;  // Ignore
+
+        case eStateSuspended:
+        case eStateStopped:
+        case eStateCrashed:
+        {
+            nub_thread_t tid = DNBProcessGetCurrentThread (pid);
+            // Make sure we set the current thread so g and p packets return
+            // the data the gdb will expect.
+            SetCurrentThread (tid);
+
+            SendStopReplyPacketForThread (tid);
+        }
+            break;
+
+        case eStateInvalid:
+        case eStateUnloaded:
+        case eStateExited:
+        {
+            char pid_exited_packet[16] = "";
+            int pid_status = 0;
+            // Process exited with exit status
+            if (!DNBProcessGetExitStatus(pid, &pid_status))
+                pid_status = 0;
+
+            if (pid_status)
+            {
+                if (WIFEXITED (pid_status))
+                    snprintf (pid_exited_packet, sizeof(pid_exited_packet), "W%02x", WEXITSTATUS (pid_status));
+                else if (WIFSIGNALED (pid_status))
+                    snprintf (pid_exited_packet, sizeof(pid_exited_packet), "X%02x", WEXITSTATUS (pid_status));
+                else if (WIFSTOPPED (pid_status))
+                    snprintf (pid_exited_packet, sizeof(pid_exited_packet), "S%02x", WSTOPSIG (pid_status));
+            }
+
+            // If we have an empty exit packet, lets fill one in to be safe.
+            if (!pid_exited_packet[0])
+            {
+                strncpy (pid_exited_packet, "W00", sizeof(pid_exited_packet)-1);
+                pid_exited_packet[sizeof(pid_exited_packet)-1] = '\0';
+            }
+
+            return SendPacket (pid_exited_packet);
+        }
+            break;
+    }
+    return rnb_success;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_M (const char *p)
+{
+    if (p == NULL || p[0] == '\0' || strlen (p) < 3)
+    {
+        return HandlePacket_ILLFORMED ("Too short M packet");
+    }
+
+    char *c;
+    p++;
+    errno = 0;
+    nub_addr_t addr = strtoull (p, &c, 16);
+    if (errno != 0 && addr == 0)
+    {
+        return HandlePacket_ILLFORMED ("Invalid address in M packet");
+    }
+    if (*c != ',')
+    {
+        return HandlePacket_ILLFORMED ("Comma sep missing in M packet");
+    }
+
+    /* Advance 'p' to the length part of the packet.  */
+    p += (c - p) + 1;
+
+    errno = 0;
+    uint32_t length = strtoul (p, &c, 16);
+    if (errno != 0 && length == 0)
+    {
+        return HandlePacket_ILLFORMED ("Invalid length in M packet");
+    }
+    if (length == 0)
+    {
+        return SendPacket ("OK");
+    }
+
+    if (*c != ':')
+    {
+        return HandlePacket_ILLFORMED ("Missing colon in M packet");
+    }
+    /* Advance 'p' to the data part of the packet.  */
+    p += (c - p) + 1;
+
+    int datalen = strlen (p);
+    if (datalen & 0x1)
+    {
+        return HandlePacket_ILLFORMED ("Uneven # of hex chars for data in M packet");
+    }
+    if (datalen == 0)
+    {
+        return SendPacket ("OK");
+    }
+
+    uint8_t *buf = (uint8_t *) alloca (datalen / 2);
+    uint8_t *i = buf;
+
+    while (*p != '\0' && *(p + 1) != '\0')
+    {
+        char hexbuf[3];
+        hexbuf[0] = *p;
+        hexbuf[1] = *(p + 1);
+        hexbuf[2] = '\0';
+        errno = 0;
+        uint8_t byte = strtoul (hexbuf, NULL, 16);
+        if (errno != 0 && byte == 0)
+        {
+            return HandlePacket_ILLFORMED ("Invalid hex byte in M packet");
+        }
+        *i++ = byte;
+        p += 2;
+    }
+
+    nub_size_t wrote = DNBProcessMemoryWrite (m_ctx.ProcessID(), addr, length, buf);
+    if (wrote != length)
+        return SendPacket ("E09");
+    else
+        return SendPacket ("OK");
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_m (const char *p)
+{
+    if (p == NULL || p[0] == '\0' || strlen (p) < 3)
+    {
+        return HandlePacket_ILLFORMED ("Too short m packet");
+    }
+
+    char *c;
+    p++;
+    errno = 0;
+    nub_addr_t addr = strtoull (p, &c, 16);
+    if (errno != 0 && addr == 0)
+    {
+        return HandlePacket_ILLFORMED ("Invalid address in m packet");
+    }
+    if (*c != ',')
+    {
+        return HandlePacket_ILLFORMED ("Comma sep missing in m packet");
+    }
+
+    /* Advance 'p' to the length part of the packet.  */
+    p += (c - p) + 1;
+
+    errno = 0;
+    uint32_t length = strtoul (p, NULL, 16);
+    if (errno != 0 && length == 0)
+    {
+        return HandlePacket_ILLFORMED ("Invalid length in m packet");
+    }
+    if (length == 0)
+    {
+        return SendPacket ("");
+    }
+
+    uint8_t buf[length];
+    int bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID(), addr, length, buf);
+    if (bytes_read == 0)
+    {
+        return SendPacket ("E08");
+    }
+
+    // "The reply may contain fewer bytes than requested if the server was able
+    //  to read only part of the region of memory."
+    length = bytes_read;
+
+    std::ostringstream ostrm;
+    for (int i = 0; i < length; i++)
+        ostrm << RAWHEX8(buf[i]);
+    return SendPacket (ostrm.str ());
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_X (const char *p)
+{
+    if (p == NULL || p[0] == '\0' || strlen (p) < 3)
+    {
+        return HandlePacket_ILLFORMED ("Too short X packet");
+    }
+
+    char *c;
+    p++;
+    errno = 0;
+    nub_addr_t addr = strtoull (p, &c, 16);
+    if (errno != 0 && addr == 0)
+    {
+        return HandlePacket_ILLFORMED ("Invalid address in X packet");
+    }
+    if (*c != ',')
+    {
+        return HandlePacket_ILLFORMED ("Comma sep missing in X packet");
+    }
+
+    /* Advance 'p' to the length part of the packet.  */
+    p += (c - p) + 1;
+
+    errno = 0;
+    int length = strtoul (p, NULL, 16);
+    if (errno != 0 && length == 0)
+    {
+        return HandlePacket_ILLFORMED ("Invalid length in m packet");
+    }
+
+    // I think gdb sends a zero length write request to test whether this
+    // packet is accepted.
+    if (length == 0)
+    {
+        return SendPacket ("OK");
+    }
+
+    std::vector<uint8_t> data = decode_binary_data (c, -1);
+    std::vector<uint8_t>::const_iterator it;
+    uint8_t *buf = (uint8_t *) alloca (data.size ());
+    uint8_t *i = buf;
+    for (it = data.begin (); it != data.end (); ++it)
+    {
+        *i++ = *it;
+    }
+
+    nub_size_t wrote = DNBProcessMemoryWrite (m_ctx.ProcessID(), addr, data.size(), buf);
+    if (wrote != data.size ())
+        return SendPacket ("E08");
+    return SendPacket ("OK");
+}
+
+/* `g' -- read registers
+ Get the contents of the registers for the current thread,
+ send them to gdb.
+ Should the setting of the Hg packet determine which thread's registers
+ are returned?  */
+
+rnb_err_t
+RNBRemote::HandlePacket_g (const char *p)
+{
+    std::ostringstream ostrm;
+    if (!m_ctx.HasValidProcessID())
+    {
+        return SendPacket ("E11");
+    }
+    nub_process_t pid = m_ctx.ProcessID ();
+    nub_thread_t tid = GetCurrentThread();
+
+    if (m_use_native_regs)
+    {
+        // Get the register context size first by calling with NULL buffer
+        nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0);
+        if (reg_ctx_size)
+        {
+            // Now allocate enough space for the entire register context
+            std::vector<uint8_t> reg_ctx;
+            reg_ctx.resize(reg_ctx_size);
+            // Now read the register context
+            reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, &reg_ctx[0], reg_ctx.size());
+            if (reg_ctx_size)
+            {
+                append_hex_value (ostrm, reg_ctx.data(), reg_ctx.size(), false);
+                return SendPacket (ostrm.str ());
+            }
+        }
+    }
+    
+    for (uint32_t reg = 0; reg < g_num_reg_entries; reg++)
+        register_value_in_hex_fixed_width (ostrm, pid, tid, &g_reg_entries[reg]);
+
+    return SendPacket (ostrm.str ());
+}
+
+/* `G XXX...' -- write registers
+ How is the thread for these specified, beyond "the current thread"?
+ Does gdb actually use the Hg packet to set this?  */
+
+rnb_err_t
+RNBRemote::HandlePacket_G (const char *p)
+{
+    if (!m_ctx.HasValidProcessID())
+    {
+        return SendPacket ("E11");
+    }
+    StringExtractor packet(p);
+    packet.SetFilePos(1); // Skip the 'G'
+    
+    nub_process_t pid = m_ctx.ProcessID();
+    nub_thread_t tid = GetCurrentThread();
+
+    if (m_use_native_regs)
+    {
+        // Get the register context size first by calling with NULL buffer
+        nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0);
+        if (reg_ctx_size)
+        {
+            // Now allocate enough space for the entire register context
+            std::vector<uint8_t> reg_ctx;
+            reg_ctx.resize(reg_ctx_size);
+            
+            if (packet.GetHexBytes (&reg_ctx[0], reg_ctx.size(), 0xcc) == reg_ctx.size())
+            {
+                // Now write the register context
+                reg_ctx_size = DNBThreadSetRegisterContext(pid, tid, reg_ctx.data(), reg_ctx.size());
+                if (reg_ctx_size == reg_ctx.size())
+                    return SendPacket ("OK");
+                else 
+                    return SendPacket ("E55");
+            }
+        }
+    }
+
+
+    DNBRegisterValue reg_value;
+    for (uint32_t reg = 0; reg < g_num_reg_entries; reg++)
+    {
+        const register_map_entry_t *reg_entry = &g_reg_entries[reg];
+
+        reg_value.info = reg_entry->nub_info;
+        if (packet.GetHexBytes (reg_value.value.v_sint8, reg_entry->gdb_size, 0xcc) != reg_entry->gdb_size)
+            break;
+
+        if (!DNBThreadSetRegisterValueByID (pid, tid, reg_entry->nub_info.set, reg_entry->nub_info.reg, &reg_value))
+            return SendPacket ("E15");
+    }
+    return SendPacket ("OK");
+}
+
+static bool
+RNBRemoteShouldCancelCallback (void *not_used)
+{
+    RNBRemoteSP remoteSP(g_remoteSP);
+    if (remoteSP.get() != NULL)
+    {
+        RNBRemote* remote = remoteSP.get();
+        if (remote->Comm().IsConnected())
+            return false;
+        else
+            return true;
+    }
+    return true;
+}
+
+
+// FORMAT: _MXXXXXX,PPP   
+//      XXXXXX: big endian hex chars
+//      PPP: permissions can be any combo of r w x chars
+//
+// RESPONSE: XXXXXX
+//      XXXXXX: hex address of the newly allocated memory
+//      EXX: error code
+//
+// EXAMPLES:
+//      _M123000,rw
+//      _M123000,rwx
+//      _M123000,xw
+
+rnb_err_t
+RNBRemote::HandlePacket_AllocateMemory (const char *p)
+{
+    StringExtractor packet (p);
+    packet.SetFilePos(2); // Skip the "_M"
+    
+    nub_addr_t size = packet.GetHexMaxU64 (StringExtractor::BigEndian, 0);
+    if (size != 0)
+    {
+        if (packet.GetChar() == ',')
+        {
+            uint32_t permissions = 0;
+            char ch;
+            bool success = true;
+            while (success && (ch = packet.GetChar()) != '\0')
+            {
+                switch (ch)
+                {
+                case 'r':   permissions |= eMemoryPermissionsReadable; break;
+                case 'w':   permissions |= eMemoryPermissionsWritable; break;
+                case 'x':   permissions |= eMemoryPermissionsExecutable; break;
+                default:    success = false; break;
+                }
+            }
+            
+            if (success)
+            {
+                nub_addr_t addr = DNBProcessMemoryAllocate (m_ctx.ProcessID(), size, permissions);
+                if (addr != INVALID_NUB_ADDRESS)
+                {
+                    std::ostringstream ostrm;
+                    ostrm << RAW_HEXBASE << addr;
+                    return SendPacket (ostrm.str ());
+                }
+            }
+        }
+    }
+    return SendPacket ("E53");
+}
+
+// FORMAT: _mXXXXXX   
+//      XXXXXX: address that was previosly allocated
+//
+// RESPONSE: XXXXXX
+//      OK: address was deallocated
+//      EXX: error code
+//
+// EXAMPLES: 
+//      _m123000
+
+rnb_err_t
+RNBRemote::HandlePacket_DeallocateMemory (const char *p)
+{
+    StringExtractor packet (p);
+    packet.SetFilePos(2); // Skip the "_m"
+    nub_addr_t addr = packet.GetHexMaxU64 (StringExtractor::BigEndian, INVALID_NUB_ADDRESS);
+
+    if (addr != INVALID_NUB_ADDRESS)
+    {
+        if (DNBProcessMemoryDeallocate (m_ctx.ProcessID(), addr))
+            return SendPacket ("OK");
+    }
+    return SendPacket ("E54");
+}
+
+/*
+ vAttach;pid
+
+ Attach to a new process with the specified process ID. pid is a hexadecimal integer
+ identifying the process. If the stub is currently controlling a process, it is
+ killed. The attached process is stopped.This packet is only available in extended
+ mode (see extended mode).
+
+ Reply:
+ "ENN"                      for an error
+ "Any Stop Reply Packet"     for success
+ */
+
+rnb_err_t
+RNBRemote::HandlePacket_v (const char *p)
+{
+    if (strcmp (p, "vCont;c") == 0)
+    {
+        // Simple continue
+        return RNBRemote::HandlePacket_c("c");
+    }
+    else if (strcmp (p, "vCont;s") == 0)
+    {
+        // Simple step
+        return RNBRemote::HandlePacket_s("s");
+    }
+    else if (strstr (p, "vCont") == p)
+    {
+        rnb_err_t rnb_err = rnb_success;
+        typedef struct
+        {
+            nub_thread_t tid;
+            char action;
+            int signal;
+        } vcont_action_t;
+
+        DNBThreadResumeActions thread_actions;
+        char *c = (char *)(p += strlen("vCont"));
+        char *c_end = c + strlen(c);
+        if (*c == '?')
+            return SendPacket ("vCont;c;C;s;S");
+
+        while (c < c_end && *c == ';')
+        {
+            ++c;    // Skip the semi-colon
+            DNBThreadResumeAction thread_action;
+            thread_action.tid = INVALID_NUB_THREAD;
+            thread_action.state = eStateInvalid;
+            thread_action.signal = 0;
+            thread_action.addr = INVALID_NUB_ADDRESS;
+
+            char action = *c++;
+
+            switch (action)
+            {
+                case 'C':
+                    errno = 0;
+                    thread_action.signal = strtoul (c, &c, 16);
+                    if (errno != 0)
+                        return HandlePacket_ILLFORMED ("Could not parse signal in vCont packet");
+                    // Fall through to next case...
+
+                case 'c':
+                    // Continue
+                    thread_action.state = eStateRunning;
+                    break;
+
+                case 'S':
+                    errno = 0;
+                    thread_action.signal = strtoul (c, &c, 16);
+                    if (errno != 0)
+                        return HandlePacket_ILLFORMED ("Could not parse signal in vCont packet");
+                    // Fall through to next case...
+
+                case 's':
+                    // Step
+                    thread_action.state = eStateStepping;
+                    break;
+
+                    break;
+
+                default:
+                    rnb_err = HandlePacket_ILLFORMED ("Unsupported action in vCont packet");
+                    break;
+            }
+            if (*c == ':')
+            {
+                errno = 0;
+                thread_action.tid = strtoul (++c, &c, 16);
+                if (errno != 0)
+                    return HandlePacket_ILLFORMED ("Could not parse thread number in vCont packet");
+            }
+
+            thread_actions.Append (thread_action);
+        }
+
+        // If a default action for all other threads wasn't mentioned
+        // then we should stop the threads
+        thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0);
+        DNBProcessResume(m_ctx.ProcessID(), thread_actions.GetFirst (), thread_actions.GetSize());
+        return rnb_success;
+    }
+    else if (strstr (p, "vAttach") == p)
+    {
+        nub_process_t attach_pid = INVALID_NUB_PROCESS;
+        char err_str[1024]={'\0'};
+        if (strstr (p, "vAttachWait;") == p)
+        {
+            p += strlen("vAttachWait;");
+            std::string attach_name;
+            while (*p != '\0')
+            {
+                char smallbuf[3];
+                smallbuf[0] = *p;
+                smallbuf[1] = *(p + 1);
+                smallbuf[2] = '\0';
+
+                errno = 0;
+                int ch = strtoul (smallbuf, NULL, 16);
+                if (errno != 0 && ch == 0)
+                {
+                    return HandlePacket_ILLFORMED ("non-hex char in arg on 'vAttachWait' pkt");
+                }
+
+                attach_name.push_back(ch);
+                p += 2;
+            }
+
+            attach_pid = DNBProcessAttachWait(attach_name.c_str (), m_ctx.LaunchFlavor(), NULL, 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback);
+
+        }
+        else if (strstr (p, "vAttach;") == p)
+        {
+            p += strlen("vAttach;");
+            char *end = NULL;
+            attach_pid = strtoul (p, &end, 16);    // PID will be in hex, so use base 16 to decode
+            if (p != end && *end == '\0')
+            {
+                // Wait at most 30 second for attach
+                struct timespec attach_timeout_abstime;
+                DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, 30, 0);
+                attach_pid = DNBProcessAttach(attach_pid, &attach_timeout_abstime, err_str, sizeof(err_str));
+            }
+        }
+        else
+            return HandlePacket_UNIMPLEMENTED(p);
+
+
+        if (attach_pid != INVALID_NUB_PROCESS)
+        {
+            if (m_ctx.ProcessID() != attach_pid)
+                m_ctx.SetProcessID(attach_pid);
+            // Send a stop reply packet to indicate we successfully attached!
+            NotifyThatProcessStopped ();
+            return rnb_success;
+        }
+        else
+        {
+            m_ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+            if (err_str[0])
+                m_ctx.LaunchStatus().SetErrorString(err_str);
+            else
+                m_ctx.LaunchStatus().SetErrorString("attach failed");
+            return SendPacket ("E01");  // E01 is our magic error value for attach failed.
+        }
+    }
+
+    // All other failures come through here
+    return HandlePacket_UNIMPLEMENTED(p);
+}
+
+/* `T XX' -- status of thread
+ Check if the specified thread is alive.
+ The thread number is in hex?  */
+
+rnb_err_t
+RNBRemote::HandlePacket_T (const char *p)
+{
+    p++;
+    if (p == NULL || *p == '\0')
+    {
+        return HandlePacket_ILLFORMED ("No thread specified in T packet");
+    }
+    if (!m_ctx.HasValidProcessID())
+    {
+        return SendPacket ("E15");
+    }
+    errno = 0;
+    nub_thread_t tid = strtoul (p, NULL, 16);
+    if (errno != 0 && tid == 0)
+    {
+        return HandlePacket_ILLFORMED ("Could not parse thread number in T packet");
+    }
+
+    nub_state_t state = DNBThreadGetState (m_ctx.ProcessID(), tid);
+    if (state == eStateInvalid || state == eStateExited || state == eStateCrashed)
+    {
+        return SendPacket ("E16");
+    }
+
+    return SendPacket ("OK");
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_z (const char *p)
+{
+    if (p == NULL || *p == '\0')
+        return HandlePacket_ILLFORMED ("No thread specified in z packet");
+
+    if (!m_ctx.HasValidProcessID())
+        return SendPacket ("E15");
+
+    char packet_cmd = *p++;
+    char break_type = *p++;
+
+    if (*p++ != ',')
+        return HandlePacket_ILLFORMED ("Comma separator missing in z packet");
+
+    char *c = NULL;
+    nub_process_t pid = m_ctx.ProcessID();
+    errno = 0;
+    nub_addr_t addr = strtoull (p, &c, 16);
+    if (errno != 0 && addr == 0)
+        return HandlePacket_ILLFORMED ("Invalid address in z packet");
+    p = c;
+    if (*p++ != ',')
+        return HandlePacket_ILLFORMED ("Comma separator missing in z packet");
+
+    errno = 0;
+    uint32_t byte_size = strtoul (p, &c, 16);
+    if (errno != 0 && byte_size == 0)
+        return HandlePacket_ILLFORMED ("Invalid length in z packet");
+
+    if (packet_cmd == 'Z')
+    {
+        // set
+        switch (break_type)
+        {
+            case '0':   // set software breakpoint
+            case '1':   // set hardware breakpoint
+            {
+                // gdb can send multiple Z packets for the same address and
+                // these calls must be ref counted.
+                bool hardware = (break_type == '1');
+
+                // Check if we currently have a breakpoint already set at this address
+                BreakpointMapIter pos = m_breakpoints.find(addr);
+                if (pos != m_breakpoints.end())
+                {
+                    // We do already have a breakpoint at this address, increment
+                    // its reference count and return OK
+                    pos->second.Retain();
+                    return SendPacket ("OK");
+                }
+                else
+                {
+                    // We do NOT already have a breakpoint at this address, So lets
+                    // create one.
+                    nub_break_t break_id = DNBBreakpointSet (pid, addr, byte_size, hardware);
+                    if (break_id != INVALID_NUB_BREAK_ID)
+                    {
+                        // We successfully created a breakpoint, now lets full out
+                        // a ref count structure with the breakID and add it to our
+                        // map.
+                        Breakpoint rnbBreakpoint(break_id);
+                        m_breakpoints[addr] = rnbBreakpoint;
+                        return SendPacket ("OK");
+                    }
+                    else
+                    {
+                        // We failed to set the software breakpoint
+                        return SendPacket ("E09");
+                    }
+                }
+            }
+                break;
+
+            case '2':   // set write watchpoint
+            case '3':   // set read watchpoint
+            case '4':   // set access watchpoint
+            {
+                bool hardware = true;
+                uint32_t watch_flags = 0;
+                if (break_type == '2')
+                    watch_flags = WATCH_TYPE_WRITE;
+                else if (break_type == '3')
+                    watch_flags = WATCH_TYPE_READ;
+                else
+                    watch_flags = WATCH_TYPE_READ | WATCH_TYPE_WRITE;
+
+                // Check if we currently have a watchpoint already set at this address
+                BreakpointMapIter pos = m_watchpoints.find(addr);
+                if (pos != m_watchpoints.end())
+                {
+                    // We do already have a watchpoint at this address, increment
+                    // its reference count and return OK
+                    pos->second.Retain();
+                    return SendPacket ("OK");
+                }
+                else
+                {
+                    // We do NOT already have a breakpoint at this address, So lets
+                    // create one.
+                    nub_watch_t watch_id = DNBWatchpointSet (pid, addr, byte_size, watch_flags, hardware);
+                    if (watch_id != INVALID_NUB_BREAK_ID)
+                    {
+                        // We successfully created a watchpoint, now lets full out
+                        // a ref count structure with the watch_id and add it to our
+                        // map.
+                        Breakpoint rnbWatchpoint(watch_id);
+                        m_watchpoints[addr] = rnbWatchpoint;
+                        return SendPacket ("OK");
+                    }
+                    else
+                    {
+                        // We failed to set the watchpoint
+                        return SendPacket ("E09");
+                    }
+                }
+            }
+                break;
+
+            default:
+                break;
+        }
+    }
+    else if (packet_cmd == 'z')
+    {
+        // remove
+        switch (break_type)
+        {
+            case '0':   // remove software breakpoint
+            case '1':   // remove hardware breakpoint
+            {
+                // gdb can send multiple z packets for the same address and
+                // these calls must be ref counted.
+                BreakpointMapIter pos = m_breakpoints.find(addr);
+                if (pos != m_breakpoints.end())
+                {
+                    // We currently have a breakpoint at address ADDR. Decrement
+                    // its reference count, and it that count is now zero we
+                    // can clear the breakpoint.
+                    pos->second.Release();
+                    if (pos->second.RefCount() == 0)
+                    {
+                        if (DNBBreakpointClear (pid, pos->second.BreakID()))
+                        {
+                            m_breakpoints.erase(pos);
+                            return SendPacket ("OK");
+                        }
+                        else
+                        {
+                            return SendPacket ("E08");
+                        }
+                    }
+                    else
+                    {
+                        // We still have references to this breakpoint don't
+                        // delete it, just decrementing the reference count
+                        // is enough.
+                        return SendPacket ("OK");
+                    }
+                }
+                else
+                {
+                    // We don't know about any breakpoints at this address
+                    return SendPacket ("E08");
+                }
+            }
+                break;
+
+            case '2':   // remove write watchpoint
+            case '3':   // remove read watchpoint
+            case '4':   // remove access watchpoint
+            {
+                // gdb can send multiple z packets for the same address and
+                // these calls must be ref counted.
+                BreakpointMapIter pos = m_watchpoints.find(addr);
+                if (pos != m_watchpoints.end())
+                {
+                    // We currently have a watchpoint at address ADDR. Decrement
+                    // its reference count, and it that count is now zero we
+                    // can clear the watchpoint.
+                    pos->second.Release();
+                    if (pos->second.RefCount() == 0)
+                    {
+                        if (DNBWatchpointClear (pid, pos->second.BreakID()))
+                        {
+                            m_watchpoints.erase(pos);
+                            return SendPacket ("OK");
+                        }
+                        else
+                        {
+                            return SendPacket ("E08");
+                        }
+                    }
+                    else
+                    {
+                        // We still have references to this watchpoint don't
+                        // delete it, just decrementing the reference count
+                        // is enough.
+                        return SendPacket ("OK");
+                    }
+                }
+                else
+                {
+                    // We don't know about any watchpoints at this address
+                    return SendPacket ("E08");
+                }
+            }
+                break;
+
+            default:
+                break;
+        }
+    }
+    return HandlePacket_UNIMPLEMENTED(p);
+}
+
+/* `p XX'
+ print the contents of register X */
+
+rnb_err_t
+RNBRemote::HandlePacket_p (const char *p)
+{
+    if (p == NULL || *p == '\0')
+    {
+        return HandlePacket_ILLFORMED ("No thread specified in p packet");
+    }
+    if (!m_ctx.HasValidProcessID())
+    {
+        return SendPacket ("E15");
+    }
+    nub_process_t pid = m_ctx.ProcessID();
+    errno = 0;
+    uint32_t reg = strtoul (p + 1, NULL, 16);
+    if (errno != 0 && reg == 0)
+    {
+        return HandlePacket_ILLFORMED ("Could not parse thread number in p packet");
+    }
+
+    const register_map_entry_t *reg_entry;
+
+    if (reg < g_num_reg_entries)
+        reg_entry = &g_reg_entries[reg];
+    else
+        reg_entry = NULL;
+
+    std::ostringstream ostrm;
+    if (reg_entry == NULL)
+    {
+        DNBLogError("RNBRemote::HandlePacket_p(%s): unknown register number %u requested\n", p, reg);
+        ostrm << "00000000";
+    }
+    else if (reg_entry->nub_info.reg == -1)
+    {
+        if (reg_entry->gdb_size > 0)
+        {
+            if (reg_entry->fail_value != NULL)
+            {
+                append_hex_value(ostrm, reg_entry->fail_value, reg_entry->gdb_size, false);
+            }
+            else
+            {
+                std::basic_string<uint8_t> zeros(reg_entry->gdb_size, '\0');
+                append_hex_value(ostrm, zeros.data(), zeros.size(), false);
+            }
+        }
+    }
+    else
+    {
+        nub_thread_t tid = GetCurrentThread();
+        register_value_in_hex_fixed_width (ostrm, pid, tid, reg_entry);
+    }
+    return SendPacket (ostrm.str());
+}
+
+/* `Pnn=rrrrr'
+ Set register number n to value r.
+ n and r are hex strings.  */
+
+rnb_err_t
+RNBRemote::HandlePacket_P (const char *p)
+{
+    if (p == NULL || *p == '\0')
+    {
+        return HandlePacket_ILLFORMED ("Empty P packet");
+    }
+    if (!m_ctx.HasValidProcessID())
+    {
+        return SendPacket ("E28");
+    }
+
+    nub_process_t pid = m_ctx.ProcessID();
+
+    StringExtractor packet (p);
+
+    const char cmd_char = packet.GetChar();
+    // Register ID is always in big endian
+    const uint32_t reg = packet.GetHexMaxU32 (false, UINT32_MAX);
+    const char equal_char = packet.GetChar();
+
+    if (cmd_char != 'P')
+        return HandlePacket_ILLFORMED ("Improperly formed P packet");
+
+    if (reg == UINT32_MAX)
+        return SendPacket ("E29");
+
+    if (equal_char != '=')
+        return SendPacket ("E30");
+
+    const register_map_entry_t *reg_entry;
+
+    if (reg >= g_num_reg_entries)
+        return SendPacket("E47");
+
+    reg_entry = &g_reg_entries[reg];
+
+    if (reg_entry->nub_info.set == -1 && reg_entry->nub_info.reg == -1)
+    {
+        DNBLogError("RNBRemote::HandlePacket_P(%s): unknown register number %u requested\n", p, reg);
+        return SendPacket("E48");
+    }
+
+    DNBRegisterValue reg_value;
+    reg_value.info = reg_entry->nub_info;
+    packet.GetHexBytes (reg_value.value.v_sint8, reg_entry->gdb_size, 0xcc);
+
+    nub_thread_t tid;
+    tid = GetCurrentThread ();
+
+    if (!DNBThreadSetRegisterValueByID (pid, tid, reg_entry->nub_info.set, reg_entry->nub_info.reg, &reg_value))
+    {
+        return SendPacket ("E32");
+    }
+    return SendPacket ("OK");
+}
+
+/* `c [addr]'
+ Continue, optionally from a specified address. */
+
+rnb_err_t
+RNBRemote::HandlePacket_c (const char *p)
+{
+    const nub_process_t pid = m_ctx.ProcessID();
+
+    if (pid == INVALID_NUB_PROCESS)
+        return SendPacket ("E23");
+
+    DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateRunning, 0, INVALID_NUB_ADDRESS };
+
+    if (*(p + 1) != '\0')
+    {
+        action.tid = GetContinueThread();
+        errno = 0;
+        action.addr = strtoull (p + 1, NULL, 16);
+        if (errno != 0 && action.addr == 0)
+            return HandlePacket_ILLFORMED ("Could not parse address in c packet");
+    }
+
+    DNBThreadResumeActions thread_actions;
+    thread_actions.Append(action);
+    thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
+    if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize()))
+        return SendPacket ("E25");
+    // Don't send an "OK" packet; response is the stopped/exited message.
+    return rnb_success;
+}
+
+/* `C sig [;addr]'
+ Resume with signal sig, optionally at address addr.  */
+
+rnb_err_t
+RNBRemote::HandlePacket_C (const char *p)
+{
+    const nub_process_t pid = m_ctx.ProcessID();
+
+    if (pid == INVALID_NUB_PROCESS)
+        return SendPacket ("E36");
+
+    DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateRunning, 0, INVALID_NUB_ADDRESS };
+    int process_signo = -1;
+    if (*(p + 1) != '\0')
+    {
+        action.tid = GetContinueThread();
+        char *end = NULL;
+        errno = 0;
+        process_signo = strtoul (p + 1, &end, 16);
+        if (errno != 0)
+            return HandlePacket_ILLFORMED ("Could not parse signal in C packet");
+        else if (*end == ';')
+        {
+            errno = 0;
+            action.addr = strtoull (end + 1, NULL, 16);
+            if (errno != 0 && action.addr == 0)
+                return HandlePacket_ILLFORMED ("Could not parse address in C packet");
+        }
+    }
+
+    DNBThreadResumeActions thread_actions;
+    thread_actions.Append (action);
+    thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, action.signal);
+    if (!DNBProcessSignal(pid, process_signo))
+        return SendPacket ("E52");
+    if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize()))
+        return SendPacket ("E38");
+    /* Don't send an "OK" packet; response is the stopped/exited message.  */
+    return rnb_success;
+}
+
+//----------------------------------------------------------------------
+// 'D' packet
+// Detach from gdb.
+//----------------------------------------------------------------------
+rnb_err_t
+RNBRemote::HandlePacket_D (const char *p)
+{
+    SendPacket ("OK");
+    if (m_ctx.HasValidProcessID())
+        DNBProcessDetach(m_ctx.ProcessID());
+    return rnb_success;
+}
+
+/* `k'
+ Kill the inferior process.  */
+
+rnb_err_t
+RNBRemote::HandlePacket_k (const char *p)
+{
+    if (!m_ctx.HasValidProcessID())
+        return SendPacket ("E26");
+    if (!DNBProcessKill (m_ctx.ProcessID()))
+        return SendPacket ("E27");
+    return SendPacket ("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_stop_process (const char *p)
+{
+    DNBProcessSignal (m_ctx.ProcessID(), SIGSTOP);
+    //DNBProcessSignal (m_ctx.ProcessID(), SIGINT);
+    // Do not send any response packet! Wait for the stop reply packet to naturally happen
+    return rnb_success;
+}
+
+/* `s'
+ Step the inferior process.  */
+
+rnb_err_t
+RNBRemote::HandlePacket_s (const char *p)
+{
+    const nub_process_t pid = m_ctx.ProcessID();
+    if (pid == INVALID_NUB_PROCESS)
+        return SendPacket ("E32");
+
+    // Hardware supported stepping not supported on arm
+    nub_thread_t tid = GetContinueThread ();
+    if (tid == 0 || tid == -1)
+        tid = GetCurrentThread();
+
+    if (tid == INVALID_NUB_THREAD)
+        return SendPacket ("E33");
+
+    DNBThreadResumeActions thread_actions;
+    thread_actions.AppendAction(tid, eStateStepping);
+
+    // Make all other threads stop when we are stepping
+    thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0);
+    if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize()))
+        return SendPacket ("E49");
+    // Don't send an "OK" packet; response is the stopped/exited message.
+    return rnb_success;
+}
+
+/* `S sig [;addr]'
+ Step with signal sig, optionally at address addr.  */
+
+rnb_err_t
+RNBRemote::HandlePacket_S (const char *p)
+{
+    const nub_process_t pid = m_ctx.ProcessID();
+    if (pid == INVALID_NUB_PROCESS)
+        return SendPacket ("E36");
+
+    DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateStepping, 0, INVALID_NUB_ADDRESS };
+
+    if (*(p + 1) != '\0')
+    {
+        char *end = NULL;
+        errno = 0;
+        action.signal = strtoul (p + 1, &end, 16);
+        if (errno != 0)
+            return HandlePacket_ILLFORMED ("Could not parse signal in S packet");
+        else if (*end == ';')
+        {
+            errno = 0;
+            action.addr = strtoull (end + 1, NULL, 16);
+            if (errno != 0 && action.addr == 0)
+            {
+                return HandlePacket_ILLFORMED ("Could not parse address in S packet");
+            }
+        }
+    }
+
+    action.tid = GetContinueThread ();
+    if (action.tid == 0 || action.tid == -1)
+        return SendPacket ("E40");
+
+    nub_state_t tstate = DNBThreadGetState (pid, action.tid);
+    if (tstate == eStateInvalid || tstate == eStateExited)
+        return SendPacket ("E37");
+
+
+    DNBThreadResumeActions thread_actions;
+    thread_actions.Append (action);
+
+    // Make all other threads stop when we are stepping
+    thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
+    if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize()))
+        return SendPacket ("E39");
+
+    // Don't send an "OK" packet; response is the stopped/exited message.
+    return rnb_success;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qHostInfo (const char *p)
+{
+    std::ostringstream strm;
+
+    uint32_t cputype, is_64_bit_capable;
+    size_t len = sizeof(cputype);
+    bool promoted_to_64 = false;
+    if  (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0)
+    {
+        len = sizeof (is_64_bit_capable);
+        if  (::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0) == 0)
+        {
+            if (is_64_bit_capable && ((cputype & CPU_ARCH_ABI64) == 0))
+            {
+                promoted_to_64 = true;
+                cputype |= CPU_ARCH_ABI64;
+            }
+        }
+        
+        strm << "cputype:" << std::dec << cputype << ';';
+    }
+
+    uint32_t cpusubtype;
+    len = sizeof(cpusubtype);
+    if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
+    {
+        if (promoted_to_64 && 
+            cputype == CPU_TYPE_X86_64 && 
+            cpusubtype == CPU_SUBTYPE_486)
+            cpusubtype = CPU_SUBTYPE_X86_64_ALL;
+
+        strm << "cpusubtype:" << std::dec << cpusubtype << ';';
+    }
+
+    char ostype[64];
+    len = sizeof(ostype);
+    if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0)
+        strm << "ostype:" << std::dec << ostype << ';';
+
+    strm << "vendor:apple;";
+
+#if defined (__LITTLE_ENDIAN__)
+    strm << "endian:little;";
+#elif defined (__BIG_ENDIAN__)
+    strm << "endian:big;";
+#elif defined (__PDP_ENDIAN__)
+    strm << "endian:pdp;";
+#endif
+
+    strm << "ptrsize:" << std::dec << sizeof(void *) << ';';
+    return SendPacket (strm.str());
+}
+