to the DoHalt down in ProcessGDBRemote. I also moved the functionality that was in ProcessGDBRemote::DoHalt up into Process::Halt so not every class has to implement a tricky halt/resume on the internal state thread. The functionality is the same as it was before with two changes: - when we eat the event we now just reuse the event we consume when the private state thread is paused and set the interrupted bool on the event if needed - we also properly update the Process::m_public_state with the state of the event we consume. Prior to this, if you issued a "process halt" it would eat the event, not update the process state, and then produce a new event with the interrupted bit set and send it. Anyone listening to the event would get the stopped event with a process that whose state was set to "running". Fixed debugserver to not have to be spawned with the architecture of the inferior process. This worked fine for launching processes, but when attaching to processes by name or pid without a file in lldb, it would fail. Now debugserver can support multiple architectures for a native debug session on the current host. This currently means i386 and x86_64 are supported in the same binary and a x86_64 debugserver can attach to a i386 executable. This change involved a lot of changes to make sure we dynamically detect the correct registers for the inferior process. llvm-svn: 119680
312 lines
12 KiB
C++
312 lines
12 KiB
C++
//===-- RNBRemote.h ---------------------------------------------*- 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.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef __RNBRemote_h__
|
|
#define __RNBRemote_h__
|
|
|
|
#include "RNBDefs.h"
|
|
#include "DNB.h"
|
|
#include "RNBContext.h"
|
|
#include "RNBSocket.h"
|
|
#include "PThreadMutex.h"
|
|
#include <string>
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <map>
|
|
|
|
class RNBSocket;
|
|
class RNBContext;
|
|
class PThreadEvents;
|
|
|
|
enum event_loop_mode { debug_nub, gdb_remote_protocol, done };
|
|
|
|
class RNBRemote
|
|
{
|
|
public:
|
|
|
|
typedef enum {
|
|
invalid_packet = 0,
|
|
ack, // '+'
|
|
nack, // '-'
|
|
halt, // ^C (async halt)
|
|
use_extended_mode, // '!'
|
|
why_halted, // '?'
|
|
set_argv, // 'A'
|
|
set_bp, // 'B'
|
|
cont, // 'c'
|
|
continue_with_sig, // 'C'
|
|
detach, // 'D'
|
|
read_general_regs, // 'g'
|
|
write_general_regs, // 'G'
|
|
set_thread, // 'H'
|
|
step_inferior_one_cycle, // 'i'
|
|
signal_and_step_inf_one_cycle, // 'I'
|
|
kill, // 'k'
|
|
read_memory, // 'm'
|
|
write_memory, // 'M'
|
|
read_register, // 'p'
|
|
write_register, // 'P'
|
|
restart, // 'R'
|
|
single_step, // 's'
|
|
single_step_with_sig, // 'S'
|
|
search_mem_backwards, // 't'
|
|
thread_alive_p, // 'T'
|
|
vattach, // 'vAttach;pid'
|
|
vattachwait, // 'vAttachWait:XX...' where XX is one or more hex encoded process name ASCII bytes
|
|
vattachname, // 'vAttachName:XX...' where XX is one or more hex encoded process name ASCII bytes
|
|
vcont, // 'vCont'
|
|
vcont_list_actions, // 'vCont?'
|
|
write_data_to_memory, // 'X'
|
|
insert_mem_bp, // 'Z0'
|
|
remove_mem_bp, // 'z0'
|
|
insert_hardware_bp, // 'Z1'
|
|
remove_hardware_bp, // 'z1'
|
|
insert_write_watch_bp, // 'Z2'
|
|
remove_write_watch_bp, // 'z2'
|
|
insert_read_watch_bp, // 'Z3'
|
|
remove_read_watch_bp, // 'z3'
|
|
insert_access_watch_bp, // 'Z4'
|
|
remove_access_watch_bp, // 'z4'
|
|
|
|
query_current_thread_id, // 'qC'
|
|
query_memory_crc, // 'qCRC:'
|
|
query_thread_ids_first, // 'qfThreadInfo'
|
|
query_thread_ids_subsequent, // 'qsThreadInfo'
|
|
query_thread_extra_info, // 'qThreadExtraInfo'
|
|
query_thread_stop_info, // 'qThreadStopInfo'
|
|
query_image_offsets, // 'qOffsets'
|
|
query_symbol_lookup, // 'gSymbols'
|
|
query_launch_success, // 'qLaunchSuccess'
|
|
query_register_info, // 'qRegisterInfo'
|
|
query_shlib_notify_info_addr, // 'qShlibInfoAddr'
|
|
query_step_packet_supported, // 'qStepPacketSupported'
|
|
query_host_info, // 'qHostInfo'
|
|
pass_signals_to_inferior, // 'QPassSignals'
|
|
start_noack_mode, // 'QStartNoAckMode'
|
|
set_logging_mode, // 'QSetLogging:'
|
|
set_max_packet_size, // 'QSetMaxPacketSize:'
|
|
set_max_payload_size, // 'QSetMaxPayloadSize:'
|
|
set_environment_variable, // 'QEnvironment:'
|
|
set_disable_aslr, // 'QSetDisableASLR:'
|
|
allocate_memory, // '_M'
|
|
deallocate_memory, // '_m'
|
|
|
|
unknown_type,
|
|
} PacketEnum;
|
|
|
|
typedef rnb_err_t (RNBRemote::*HandlePacketCallback)(const char *p);
|
|
|
|
RNBRemote (bool use_native_regs, const char *arch);
|
|
~RNBRemote ();
|
|
|
|
void Initialize();
|
|
|
|
bool InitializeRegisters ();
|
|
|
|
rnb_err_t HandleAsyncPacket(PacketEnum *type = NULL);
|
|
rnb_err_t HandleReceivedPacket(PacketEnum *type = NULL);
|
|
|
|
nub_thread_t GetContinueThread () const
|
|
{
|
|
return m_continue_thread;
|
|
}
|
|
|
|
void SetContinueThread (nub_thread_t tid)
|
|
{
|
|
m_continue_thread = tid;
|
|
}
|
|
|
|
nub_thread_t GetCurrentThread () const
|
|
{
|
|
if (m_thread == 0 || m_thread == -1)
|
|
return DNBProcessGetCurrentThread (m_ctx.ProcessID());
|
|
return m_thread;
|
|
}
|
|
|
|
void SetCurrentThread (nub_thread_t tid)
|
|
{
|
|
DNBProcessSetCurrentThread (m_ctx.ProcessID(), tid);
|
|
m_thread = tid;
|
|
}
|
|
|
|
static void* ThreadFunctionReadRemoteData(void *arg);
|
|
void StartReadRemoteDataThread ();
|
|
void StopReadRemoteDataThread ();
|
|
|
|
void NotifyThatProcessStopped (void);
|
|
|
|
rnb_err_t HandlePacket_A (const char *p);
|
|
rnb_err_t HandlePacket_H (const char *p);
|
|
rnb_err_t HandlePacket_qC (const char *p);
|
|
rnb_err_t HandlePacket_qLaunchSuccess (const char *p);
|
|
rnb_err_t HandlePacket_qRegisterInfo (const char *p);
|
|
rnb_err_t HandlePacket_qShlibInfoAddr (const char *p);
|
|
rnb_err_t HandlePacket_qStepPacketSupported (const char *p);
|
|
rnb_err_t HandlePacket_qThreadInfo (const char *p);
|
|
rnb_err_t HandlePacket_qThreadExtraInfo (const char *p);
|
|
rnb_err_t HandlePacket_qThreadStopInfo (const char *p);
|
|
rnb_err_t HandlePacket_qHostInfo (const char *p);
|
|
rnb_err_t HandlePacket_Q (const char *p);
|
|
rnb_err_t HandlePacket_last_signal (const char *p);
|
|
rnb_err_t HandlePacket_m (const char *p);
|
|
rnb_err_t HandlePacket_M (const char *p);
|
|
rnb_err_t HandlePacket_X (const char *p);
|
|
rnb_err_t HandlePacket_g (const char *p);
|
|
rnb_err_t HandlePacket_G (const char *p);
|
|
rnb_err_t HandlePacket_z (const char *p);
|
|
rnb_err_t HandlePacket_T (const char *p);
|
|
rnb_err_t HandlePacket_p (const char *p);
|
|
rnb_err_t HandlePacket_P (const char *p);
|
|
rnb_err_t HandlePacket_c (const char *p);
|
|
rnb_err_t HandlePacket_C (const char *p);
|
|
rnb_err_t HandlePacket_D (const char *p);
|
|
rnb_err_t HandlePacket_k (const char *p);
|
|
rnb_err_t HandlePacket_s (const char *p);
|
|
rnb_err_t HandlePacket_S (const char *p);
|
|
rnb_err_t HandlePacket_v (const char *p);
|
|
rnb_err_t HandlePacket_UNIMPLEMENTED (const char *p);
|
|
rnb_err_t HandlePacket_ILLFORMED (const char *description);
|
|
rnb_err_t HandlePacket_AllocateMemory (const char *p);
|
|
rnb_err_t HandlePacket_DeallocateMemory (const char *p);
|
|
|
|
rnb_err_t HandlePacket_stop_process (const char *p);
|
|
|
|
rnb_err_t SendStopReplyPacketForThread (nub_thread_t tid);
|
|
rnb_err_t SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer);
|
|
rnb_err_t SendSTDOUTPacket (char *buf, nub_size_t buf_size);
|
|
rnb_err_t SendSTDERRPacket (char *buf, nub_size_t buf_size);
|
|
void FlushSTDIO ();
|
|
|
|
RNBContext& Context() { return m_ctx; }
|
|
RNBSocket& Comm() { return m_comm; }
|
|
|
|
private:
|
|
// Outlaw some contructors
|
|
RNBRemote (const RNBRemote &);
|
|
|
|
protected:
|
|
|
|
rnb_err_t GetCommData ();
|
|
void CommDataReceived(const std::string& data);
|
|
struct Packet
|
|
{
|
|
typedef std::vector<Packet> collection;
|
|
typedef collection::iterator iterator;
|
|
typedef collection::const_iterator const_iterator;
|
|
PacketEnum type;
|
|
HandlePacketCallback normal; // Function to call when inferior is halted
|
|
HandlePacketCallback async; // Function to call when inferior is running
|
|
std::string abbrev;
|
|
std::string printable_name;
|
|
Packet() :
|
|
type(invalid_packet),
|
|
normal (NULL),
|
|
async (NULL),
|
|
abbrev (),
|
|
printable_name ()
|
|
{
|
|
}
|
|
|
|
Packet( PacketEnum in_type,
|
|
HandlePacketCallback in_normal,
|
|
HandlePacketCallback in_async,
|
|
const char *in_abbrev,
|
|
const char *in_printable_name) :
|
|
type (in_type),
|
|
normal (in_normal),
|
|
async (in_async),
|
|
abbrev (in_abbrev),
|
|
printable_name (in_printable_name)
|
|
{
|
|
}
|
|
};
|
|
|
|
rnb_err_t GetPacket (std::string &packet_data, RNBRemote::Packet& packet_info, bool wait);
|
|
rnb_err_t SendPacket (const std::string &);
|
|
|
|
void CreatePacketTable ();
|
|
rnb_err_t GetPacketPayload (std::string &);
|
|
|
|
// gdb can send multiple Z/z packets for the same address and
|
|
// these calls must be ref counted.
|
|
struct Breakpoint
|
|
{
|
|
Breakpoint(nub_break_t breakID) :
|
|
m_breakID(breakID),
|
|
m_refCount(1)
|
|
{
|
|
}
|
|
|
|
Breakpoint() :
|
|
m_breakID(INVALID_NUB_BREAK_ID),
|
|
m_refCount(0)
|
|
{
|
|
}
|
|
|
|
Breakpoint(const Breakpoint& rhs) :
|
|
m_breakID(rhs.m_breakID),
|
|
m_refCount(rhs.m_refCount)
|
|
{
|
|
}
|
|
|
|
nub_break_t BreakID() const { return m_breakID; }
|
|
uint32_t RefCount() const { return m_refCount; }
|
|
void Release() { if (m_refCount > 0) --m_refCount; }
|
|
void Retain() { ++m_refCount; }
|
|
|
|
nub_break_t m_breakID;
|
|
uint32_t m_refCount;
|
|
};
|
|
typedef std::map<nub_addr_t, Breakpoint> BreakpointMap;
|
|
typedef BreakpointMap::iterator BreakpointMapIter;
|
|
typedef BreakpointMap::const_iterator BreakpointMapConstIter;
|
|
RNBContext m_ctx; // process context
|
|
RNBSocket m_comm; // communication port
|
|
std::string m_arch;
|
|
nub_thread_t m_continue_thread; // thread to continue; 0 for any, -1 for all
|
|
nub_thread_t m_thread; // thread for other ops; 0 for any, -1 for all
|
|
PThreadMutex m_mutex; // Mutex that protects
|
|
uint32_t m_packets_recvd;
|
|
Packet::collection m_packets;
|
|
std::deque<std::string> m_rx_packets;
|
|
std::string m_rx_partial_data; // For packets that may come in more than one batch, anything left over can be left here
|
|
pthread_t m_rx_pthread;
|
|
BreakpointMap m_breakpoints;
|
|
BreakpointMap m_watchpoints;
|
|
uint32_t m_max_payload_size; // the maximum sized payload we should send to gdb
|
|
bool m_extended_mode:1, // are we in extended mode?
|
|
m_noack_mode:1, // are we in no-ack mode?
|
|
m_use_native_regs:1; // Use native registers by querying DNB layer for register definitions?
|
|
};
|
|
|
|
/* We translate the /usr/include/mach/exception_types.h exception types
|
|
(e.g. EXC_BAD_ACCESS) to the fake BSD signal numbers that gdb uses
|
|
in include/gdb/signals.h (e.g. TARGET_EXC_BAD_ACCESS). These hard
|
|
coded values for TARGET_EXC_BAD_ACCESS et al must match the gdb
|
|
values in its include/gdb/signals.h. */
|
|
|
|
#define TARGET_EXC_BAD_ACCESS 0x91
|
|
#define TARGET_EXC_BAD_INSTRUCTION 0x92
|
|
#define TARGET_EXC_ARITHMETIC 0x93
|
|
#define TARGET_EXC_EMULATION 0x94
|
|
#define TARGET_EXC_SOFTWARE 0x95
|
|
#define TARGET_EXC_BREAKPOINT 0x96
|
|
|
|
/* Generally speaking, you can't assume gdb can receive more than 399 bytes
|
|
at a time with a random gdb. This bufsize constant is only specifying
|
|
how many bytes gdb can *receive* from debugserver -- it tells us nothing
|
|
about how many bytes gdb might try to send in a single packet. */
|
|
#define DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE 399
|
|
|
|
#endif // #ifndef __RNBRemote_h__
|