summaryrefslogtreecommitdiffstats
path: root/source/Plugins/Process
diff options
context:
space:
mode:
authoremaste <emaste@FreeBSD.org>2013-08-23 17:46:38 +0000
committeremaste <emaste@FreeBSD.org>2013-08-23 17:46:38 +0000
commitdcd15f81789e389c1cb27d264fcdddfd0a6002bd (patch)
treef561dabc721ad515599172c16da3a4400b7f4aec /source/Plugins/Process
downloadFreeBSD-src-dcd15f81789e389c1cb27d264fcdddfd0a6002bd.zip
FreeBSD-src-dcd15f81789e389c1cb27d264fcdddfd0a6002bd.tar.gz
Import lldb as of SVN r188801
(A number of files not required for the FreeBSD build have been removed.) Sponsored by: DARPA, AFRL
Diffstat (limited to 'source/Plugins/Process')
-rw-r--r--source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp132
-rw-r--r--source/Plugins/Process/FreeBSD/ProcessFreeBSD.h82
-rw-r--r--source/Plugins/Process/FreeBSD/ProcessMonitor.cpp1677
-rw-r--r--source/Plugins/Process/FreeBSD/ProcessMonitor.h322
-rw-r--r--source/Plugins/Process/POSIX/POSIXStopInfo.cpp89
-rw-r--r--source/Plugins/Process/POSIX/POSIXStopInfo.h120
-rw-r--r--source/Plugins/Process/POSIX/POSIXThread.cpp578
-rw-r--r--source/Plugins/Process/POSIX/POSIXThread.h133
-rw-r--r--source/Plugins/Process/POSIX/ProcessMessage.cpp258
-rw-r--r--source/Plugins/Process/POSIX/ProcessMessage.h207
-rw-r--r--source/Plugins/Process/POSIX/ProcessPOSIX.cpp911
-rw-r--r--source/Plugins/Process/POSIX/ProcessPOSIX.h211
-rw-r--r--source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp193
-rw-r--r--source/Plugins/Process/POSIX/ProcessPOSIXLog.h111
-rw-r--r--source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp136
-rw-r--r--source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h32
-rw-r--r--source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp180
-rw-r--r--source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h32
-rw-r--r--source/Plugins/Process/POSIX/RegisterContextPOSIX.h70
-rw-r--r--source/Plugins/Process/POSIX/RegisterContext_i386.cpp551
-rw-r--r--source/Plugins/Process/POSIX/RegisterContext_i386.h169
-rw-r--r--source/Plugins/Process/POSIX/RegisterContext_x86.h110
-rw-r--r--source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp1563
-rw-r--r--source/Plugins/Process/POSIX/RegisterContext_x86_64.h347
-rw-r--r--source/Plugins/Process/Utility/ARMDefines.h110
-rw-r--r--source/Plugins/Process/Utility/ARMUtils.h394
-rw-r--r--source/Plugins/Process/Utility/DynamicRegisterInfo.cpp279
-rw-r--r--source/Plugins/Process/Utility/DynamicRegisterInfo.h85
-rw-r--r--source/Plugins/Process/Utility/InferiorCallPOSIX.cpp274
-rw-r--r--source/Plugins/Process/Utility/InferiorCallPOSIX.h43
-rw-r--r--source/Plugins/Process/Utility/InstructionUtils.h136
-rw-r--r--source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp1226
-rw-r--r--source/Plugins/Process/Utility/RegisterContextDarwin_arm.h333
-rw-r--r--source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp980
-rw-r--r--source/Plugins/Process/Utility/RegisterContextDarwin_i386.h269
-rw-r--r--source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp1066
-rw-r--r--source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h274
-rw-r--r--source/Plugins/Process/Utility/RegisterContextDummy.cpp137
-rw-r--r--source/Plugins/Process/Utility/RegisterContextDummy.h77
-rw-r--r--source/Plugins/Process/Utility/RegisterContextLLDB.cpp1541
-rw-r--r--source/Plugins/Process/Utility/RegisterContextLLDB.h212
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp206
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h77
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMach_arm.cpp87
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMach_arm.h56
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMach_i386.cpp72
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMach_i386.h49
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp72
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMach_x86_64.h49
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMemory.cpp174
-rw-r--r--source/Plugins/Process/Utility/RegisterContextMemory.h102
-rw-r--r--source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp261
-rw-r--r--source/Plugins/Process/Utility/RegisterContextThreadMemory.h114
-rw-r--r--source/Plugins/Process/Utility/StopInfoMachException.cpp482
-rw-r--r--source/Plugins/Process/Utility/StopInfoMachException.h77
-rw-r--r--source/Plugins/Process/Utility/ThreadMemory.cpp140
-rw-r--r--source/Plugins/Process/Utility/ThreadMemory.h152
-rw-r--r--source/Plugins/Process/Utility/UnwindLLDB.cpp322
-rw-r--r--source/Plugins/Process/Utility/UnwindLLDB.h125
-rw-r--r--source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp275
-rw-r--r--source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h74
-rw-r--r--source/Plugins/Process/elf-core/ProcessElfCore.cpp619
-rw-r--r--source/Plugins/Process/elf-core/ProcessElfCore.h171
-rw-r--r--source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.cpp68
-rw-r--r--source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.h47
-rw-r--r--source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.cpp68
-rw-r--r--source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.h54
-rw-r--r--source/Plugins/Process/elf-core/ThreadElfCore.cpp176
-rw-r--r--source/Plugins/Process/elf-core/ThreadElfCore.h174
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp643
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h268
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp2348
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h430
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp839
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h147
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp971
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h311
-rw-r--r--source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp3291
-rw-r--r--source/Plugins/Process/gdb-remote/ProcessGDBRemote.h396
-rw-r--r--source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp196
-rw-r--r--source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h57
-rw-r--r--source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp214
-rw-r--r--source/Plugins/Process/gdb-remote/ThreadGDBRemote.h107
83 files changed, 30211 insertions, 0 deletions
diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
new file mode 100644
index 0000000..ea26d97
--- /dev/null
+++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
@@ -0,0 +1,132 @@
+//===-- ProcessFreeBSD.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <errno.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/State.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Target.h"
+
+#include "ProcessFreeBSD.h"
+#include "ProcessPOSIXLog.h"
+#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
+#include "ProcessMonitor.h"
+#include "POSIXThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//------------------------------------------------------------------------------
+// Static functions.
+
+lldb::ProcessSP
+ProcessFreeBSD::CreateInstance(Target& target,
+ Listener &listener,
+ const FileSpec *crash_file_path)
+{
+ lldb::ProcessSP process_sp;
+ if (crash_file_path == NULL)
+ process_sp.reset(new ProcessFreeBSD (target, listener));
+ return process_sp;
+}
+
+void
+ProcessFreeBSD::Initialize()
+{
+ static bool g_initialized = false;
+
+ if (!g_initialized)
+ {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ CreateInstance);
+ Log::Callbacks log_callbacks = {
+ ProcessPOSIXLog::DisableLog,
+ ProcessPOSIXLog::EnableLog,
+ ProcessPOSIXLog::ListLogCategories
+ };
+
+ Log::RegisterLogChannel (ProcessFreeBSD::GetPluginNameStatic(), log_callbacks);
+ ProcessPOSIXLog::RegisterPluginName(GetPluginNameStatic());
+ g_initialized = true;
+ }
+}
+
+lldb_private::ConstString
+ProcessFreeBSD::GetPluginNameStatic()
+{
+ static ConstString g_name("freebsd");
+ return g_name;
+}
+
+const char *
+ProcessFreeBSD::GetPluginDescriptionStatic()
+{
+ return "Process plugin for FreeBSD";
+}
+
+//------------------------------------------------------------------------------
+// ProcessInterface protocol.
+
+lldb_private::ConstString
+ProcessFreeBSD::GetPluginName()
+{
+ return GetPluginNameStatic();
+}
+
+uint32_t
+ProcessFreeBSD::GetPluginVersion()
+{
+ return 1;
+}
+
+void
+ProcessFreeBSD::GetPluginCommandHelp(const char *command, Stream *strm)
+{
+}
+
+Error
+ProcessFreeBSD::ExecutePluginCommand(Args &command, Stream *strm)
+{
+ return Error(1, eErrorTypeGeneric);
+}
+
+Log *
+ProcessFreeBSD::EnablePluginLogging(Stream *strm, Args &command)
+{
+ return NULL;
+}
+
+//------------------------------------------------------------------------------
+// Constructors and destructors.
+
+ProcessFreeBSD::ProcessFreeBSD(Target& target, Listener &listener)
+ : ProcessPOSIX(target, listener)
+{
+}
+
+void
+ProcessFreeBSD::Terminate()
+{
+}
+
+bool
+ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list)
+{
+ // XXX haxx
+ new_thread_list = old_thread_list;
+
+ return false;
+}
diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
new file mode 100644
index 0000000..5f79b74
--- /dev/null
+++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
@@ -0,0 +1,82 @@
+//===-- ProcessFreeBSD.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessFreeBSD_H_
+#define liblldb_ProcessFreeBSD_H_
+
+// C Includes
+
+// C++ Includes
+#include <queue>
+
+// Other libraries and framework includes
+#include "lldb/Target/Process.h"
+#include "lldb/Target/ThreadList.h"
+#include "ProcessMessage.h"
+#include "ProcessPOSIX.h"
+
+class ProcessMonitor;
+
+class ProcessFreeBSD :
+ public ProcessPOSIX
+{
+
+public:
+ //------------------------------------------------------------------
+ // Static functions.
+ //------------------------------------------------------------------
+ static lldb::ProcessSP
+ CreateInstance(lldb_private::Target& target,
+ lldb_private::Listener &listener,
+ const lldb_private::FileSpec *crash_file_path);
+
+ static void
+ Initialize();
+
+ static void
+ Terminate();
+
+ static lldb_private::ConstString
+ GetPluginNameStatic();
+
+ static const char *
+ GetPluginDescriptionStatic();
+
+ //------------------------------------------------------------------
+ // Constructors and destructors
+ //------------------------------------------------------------------
+ ProcessFreeBSD(lldb_private::Target& target,
+ lldb_private::Listener &listener);
+
+ virtual bool
+ UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list);
+
+ //------------------------------------------------------------------
+ // PluginInterface protocol
+ //------------------------------------------------------------------
+ virtual lldb_private::ConstString
+ GetPluginName();
+
+ virtual uint32_t
+ GetPluginVersion();
+
+ virtual void
+ GetPluginCommandHelp(const char *command, lldb_private::Stream *strm);
+
+ virtual lldb_private::Error
+ ExecutePluginCommand(lldb_private::Args &command,
+ lldb_private::Stream *strm);
+
+ virtual lldb_private::Log *
+ EnablePluginLogging(lldb_private::Stream *strm,
+ lldb_private::Args &command);
+
+};
+
+#endif // liblldb_MacOSXProcess_H_
diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
new file mode 100644
index 0000000..9fd51d2
--- /dev/null
+++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
@@ -0,0 +1,1677 @@
+//===-- ProcessMonitor.cpp ------------------------------------ -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/PseudoTerminal.h"
+
+
+#include "POSIXThread.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessPOSIXLog.h"
+#include "ProcessMonitor.h"
+
+extern "C" {
+ extern char ** environ;
+ }
+
+using namespace lldb;
+using namespace lldb_private;
+
+// We disable the tracing of ptrace calls for integration builds to
+// avoid the additional indirection and checks.
+#ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+// Wrapper for ptrace to catch errors and log calls.
+
+const char *
+Get_PT_IO_OP(int op)
+{
+ switch (op) {
+ case PIOD_READ_D: return "READ_D";
+ case PIOD_WRITE_D: return "WRITE_D";
+ case PIOD_READ_I: return "READ_I";
+ case PIOD_WRITE_I: return "WRITE_I";
+ default: return "Unknown op";
+ }
+}
+
+// Wrapper for ptrace to catch errors and log calls.
+// Note that ptrace sets errno on error because -1 is reserved as a valid result.
+extern long
+PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data,
+ const char* reqName, const char* file, int line)
+{
+ long int result;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ if (log) {
+ log->Printf("ptrace(%s, %lu, %p, %x) called from file %s line %d",
+ reqName, pid, addr, data, file, line);
+ if (req == PT_IO) {
+ struct ptrace_io_desc *pi = (struct ptrace_io_desc *) addr;
+
+ log->Printf("PT_IO: op=%s offs=%zx size=%ld",
+ Get_PT_IO_OP(pi->piod_op), (size_t)pi->piod_offs, pi->piod_len);
+ }
+ }
+
+ //PtraceDisplayBytes(req, data);
+
+ errno = 0;
+ result = ptrace(req, pid, (caddr_t) addr, data);
+
+ //PtraceDisplayBytes(req, data);
+
+ if (log && errno != 0)
+ {
+ const char* str;
+ switch (errno)
+ {
+ case ESRCH: str = "ESRCH"; break;
+ case EINVAL: str = "EINVAL"; break;
+ case EBUSY: str = "EBUSY"; break;
+ case EPERM: str = "EPERM"; break;
+ default: str = "<unknown>";
+ }
+ log->Printf("ptrace() failed; errno=%d (%s)", errno, str);
+ }
+
+#ifdef __amd64__
+ if (log) {
+ if (req == PT_GETREGS) {
+ struct reg *r = (struct reg *) addr;
+
+ log->Printf("PT_GETREGS: ip=0x%lx", r->r_rip);
+ log->Printf("PT_GETREGS: sp=0x%lx", r->r_rsp);
+ log->Printf("PT_GETREGS: bp=0x%lx", r->r_rbp);
+ log->Printf("PT_GETREGS: ax=0x%lx", r->r_rax);
+ }
+ }
+#endif
+
+ return result;
+}
+
+// Wrapper for ptrace when logging is not required.
+// Sets errno to 0 prior to calling ptrace.
+extern long
+PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data)
+{
+ long result = 0;
+ errno = 0;
+ result = ptrace(req, pid, (caddr_t)addr, data);
+ return result;
+}
+
+#define PTRACE(req, pid, addr, data) \
+ PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__)
+#else
+ PtraceWrapper((req), (pid), (addr), (data))
+#endif
+
+//------------------------------------------------------------------------------
+// Static implementations of ProcessMonitor::ReadMemory and
+// ProcessMonitor::WriteMemory. This enables mutual recursion between these
+// functions without needed to go thru the thread funnel.
+
+static size_t
+DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf, size_t size,
+ Error &error)
+{
+ struct ptrace_io_desc pi_desc;
+
+ pi_desc.piod_op = PIOD_READ_D;
+ pi_desc.piod_offs = (void *)vm_addr;
+ pi_desc.piod_addr = buf;
+ pi_desc.piod_len = size;
+
+ if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0)
+ error.SetErrorToErrno();
+ return pi_desc.piod_len;
+}
+
+static size_t
+DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr, const void *buf,
+ size_t size, Error &error)
+{
+ struct ptrace_io_desc pi_desc;
+
+ pi_desc.piod_op = PIOD_WRITE_D;
+ pi_desc.piod_offs = (void *)vm_addr;
+ pi_desc.piod_addr = (void *)buf;
+ pi_desc.piod_len = size;
+
+ if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0)
+ error.SetErrorToErrno();
+ return pi_desc.piod_len;
+}
+
+// Simple helper function to ensure flags are enabled on the given file
+// descriptor.
+static bool
+EnsureFDFlags(int fd, int flags, Error &error)
+{
+ int status;
+
+ if ((status = fcntl(fd, F_GETFL)) == -1)
+ {
+ error.SetErrorToErrno();
+ return false;
+ }
+
+ if (fcntl(fd, F_SETFL, status | flags) == -1)
+ {
+ error.SetErrorToErrno();
+ return false;
+ }
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+/// @class Operation
+/// @brief Represents a ProcessMonitor operation.
+///
+/// Under FreeBSD, it is not possible to ptrace() from any other thread but the
+/// one that spawned or attached to the process from the start. Therefore, when
+/// a ProcessMonitor is asked to deliver or change the state of an inferior
+/// process the operation must be "funneled" to a specific thread to perform the
+/// task. The Operation class provides an abstract base for all services the
+/// ProcessMonitor must perform via the single virtual function Execute, thus
+/// encapsulating the code that needs to run in the privileged context.
+class Operation
+{
+public:
+ virtual ~Operation() {}
+ virtual void Execute(ProcessMonitor *monitor) = 0;
+};
+
+//------------------------------------------------------------------------------
+/// @class ReadOperation
+/// @brief Implements ProcessMonitor::ReadMemory.
+class ReadOperation : public Operation
+{
+public:
+ ReadOperation(lldb::addr_t addr, void *buff, size_t size,
+ Error &error, size_t &result)
+ : m_addr(addr), m_buff(buff), m_size(size),
+ m_error(error), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::addr_t m_addr;
+ void *m_buff;
+ size_t m_size;
+ Error &m_error;
+ size_t &m_result;
+};
+
+void
+ReadOperation::Execute(ProcessMonitor *monitor)
+{
+ lldb::pid_t pid = monitor->GetPID();
+
+ m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error);
+}
+
+//------------------------------------------------------------------------------
+/// @class WriteOperation
+/// @brief Implements ProcessMonitor::WriteMemory.
+class WriteOperation : public Operation
+{
+public:
+ WriteOperation(lldb::addr_t addr, const void *buff, size_t size,
+ Error &error, size_t &result)
+ : m_addr(addr), m_buff(buff), m_size(size),
+ m_error(error), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::addr_t m_addr;
+ const void *m_buff;
+ size_t m_size;
+ Error &m_error;
+ size_t &m_result;
+};
+
+void
+WriteOperation::Execute(ProcessMonitor *monitor)
+{
+ lldb::pid_t pid = monitor->GetPID();
+
+ m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error);
+}
+
+//------------------------------------------------------------------------------
+/// @class ReadRegOperation
+/// @brief Implements ProcessMonitor::ReadRegisterValue.
+class ReadRegOperation : public Operation
+{
+public:
+ ReadRegOperation(lldb::tid_t tid, unsigned offset, unsigned size,
+ RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_size(size),
+ m_value(value), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ unsigned m_size;
+ RegisterValue &m_value;
+ bool &m_result;
+};
+
+void
+ReadRegOperation::Execute(ProcessMonitor *monitor)
+{
+ struct reg regs;
+ int rc;
+
+ if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)&regs, 0)) < 0) {
+ m_result = false;
+ } else {
+ if (m_size == sizeof(uintptr_t))
+ m_value = *(uintptr_t *)(((caddr_t)&regs) + m_offset);
+ else
+ memcpy(&m_value, (((caddr_t)&regs) + m_offset), m_size);
+ m_result = true;
+ }
+}
+
+//------------------------------------------------------------------------------
+/// @class WriteRegOperation
+/// @brief Implements ProcessMonitor::WriteRegisterValue.
+class WriteRegOperation : public Operation
+{
+public:
+ WriteRegOperation(lldb::tid_t tid, unsigned offset,
+ const RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset),
+ m_value(value), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ const RegisterValue &m_value;
+ bool &m_result;
+};
+
+void
+WriteRegOperation::Execute(ProcessMonitor *monitor)
+{
+ struct reg regs;
+
+ if (PTRACE(PT_GETREGS, m_tid, (caddr_t)&regs, 0) < 0) {
+ m_result = false;
+ return;
+ }
+ *(uintptr_t *)(((caddr_t)&regs) + m_offset) = (uintptr_t)m_value.GetAsUInt64();
+ if (PTRACE(PT_SETREGS, m_tid, (caddr_t)&regs, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class ReadGPROperation
+/// @brief Implements ProcessMonitor::ReadGPR.
+class ReadGPROperation : public Operation
+{
+public:
+ ReadGPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void
+ReadGPROperation::Execute(ProcessMonitor *monitor)
+{
+ int rc;
+
+ errno = 0;
+ rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)m_buf, 0);
+ if (errno != 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class ReadFPROperation
+/// @brief Implements ProcessMonitor::ReadFPR.
+class ReadFPROperation : public Operation
+{
+public:
+ ReadFPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void
+ReadFPROperation::Execute(ProcessMonitor *monitor)
+{
+ if (PTRACE(PT_GETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class WriteGPROperation
+/// @brief Implements ProcessMonitor::WriteGPR.
+class WriteGPROperation : public Operation
+{
+public:
+ WriteGPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void
+WriteGPROperation::Execute(ProcessMonitor *monitor)
+{
+ if (PTRACE(PT_SETREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class WriteFPROperation
+/// @brief Implements ProcessMonitor::WriteFPR.
+class WriteFPROperation : public Operation
+{
+public:
+ WriteFPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void
+WriteFPROperation::Execute(ProcessMonitor *monitor)
+{
+ if (PTRACE(PT_SETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class ResumeOperation
+/// @brief Implements ProcessMonitor::Resume.
+class ResumeOperation : public Operation
+{
+public:
+ ResumeOperation(lldb::tid_t tid, uint32_t signo, bool &result) :
+ m_tid(tid), m_signo(signo), m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ uint32_t m_signo;
+ bool &m_result;
+};
+
+void
+ResumeOperation::Execute(ProcessMonitor *monitor)
+{
+ int data = 0;
+
+ if (m_signo != LLDB_INVALID_SIGNAL_NUMBER)
+ data = m_signo;
+
+ if (PTRACE(PT_CONTINUE, m_tid, (caddr_t)1, data))
+ {
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("ResumeOperation (%" PRIu64 ") failed: %s", m_tid, strerror(errno));
+ m_result = false;
+ }
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class SingleStepOperation
+/// @brief Implements ProcessMonitor::SingleStep.
+class SingleStepOperation : public Operation
+{
+public:
+ SingleStepOperation(lldb::tid_t tid, uint32_t signo, bool &result)
+ : m_tid(tid), m_signo(signo), m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ uint32_t m_signo;
+ bool &m_result;
+};
+
+void
+SingleStepOperation::Execute(ProcessMonitor *monitor)
+{
+ int data = 0;
+
+ if (m_signo != LLDB_INVALID_SIGNAL_NUMBER)
+ data = m_signo;
+
+ if (PTRACE(PT_STEP, m_tid, NULL, data))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class LwpInfoOperation
+/// @brief Implements ProcessMonitor::GetLwpInfo.
+class LwpInfoOperation : public Operation
+{
+public:
+ LwpInfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err)
+ : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_info;
+ bool &m_result;
+ int &m_err;
+};
+
+void
+LwpInfoOperation::Execute(ProcessMonitor *monitor)
+{
+ struct ptrace_lwpinfo plwp;
+
+ if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) {
+ m_result = false;
+ m_err = errno;
+ } else {
+ memcpy(m_info, &plwp, sizeof(plwp));
+ m_result = true;
+ }
+}
+
+//------------------------------------------------------------------------------
+/// @class EventMessageOperation
+/// @brief Implements ProcessMonitor::GetEventMessage.
+class EventMessageOperation : public Operation
+{
+public:
+ EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result)
+ : m_tid(tid), m_message(message), m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned long *m_message;
+ bool &m_result;
+};
+
+void
+EventMessageOperation::Execute(ProcessMonitor *monitor)
+{
+ struct ptrace_lwpinfo plwp;
+
+ if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp)))
+ m_result = false;
+ else {
+ if (plwp.pl_flags & PL_FLAG_FORKED) {
+ m_message = (unsigned long *)plwp.pl_child_pid;
+ m_result = true;
+ } else
+ m_result = false;
+ }
+}
+
+//------------------------------------------------------------------------------
+/// @class KillOperation
+/// @brief Implements ProcessMonitor::BringProcessIntoLimbo.
+class KillOperation : public Operation
+{
+public:
+ KillOperation(bool &result) : m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ bool &m_result;
+};
+
+void
+KillOperation::Execute(ProcessMonitor *monitor)
+{
+ lldb::pid_t pid = monitor->GetPID();
+
+ if (PTRACE(PT_KILL, pid, NULL, 0))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class DetachOperation
+/// @brief Implements ProcessMonitor::BringProcessIntoLimbo.
+class DetachOperation : public Operation
+{
+public:
+ DetachOperation(Error &result) : m_error(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ Error &m_error;
+};
+
+void
+DetachOperation::Execute(ProcessMonitor *monitor)
+{
+ lldb::pid_t pid = monitor->GetPID();
+
+ if (PTRACE(PT_DETACH, pid, NULL, 0) < 0)
+ m_error.SetErrorToErrno();
+
+}
+
+ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor)
+ : m_monitor(monitor)
+{
+ sem_init(&m_semaphore, 0, 0);
+}
+
+ProcessMonitor::OperationArgs::~OperationArgs()
+{
+ sem_destroy(&m_semaphore);
+}
+
+ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor,
+ lldb_private::Module *module,
+ char const **argv,
+ char const **envp,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ const char *working_dir)
+ : OperationArgs(monitor),
+ m_module(module),
+ m_argv(argv),
+ m_envp(envp),
+ m_stdin_path(stdin_path),
+ m_stdout_path(stdout_path),
+ m_stderr_path(stderr_path),
+ m_working_dir(working_dir) { }
+
+ProcessMonitor::LaunchArgs::~LaunchArgs()
+{ }
+
+ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor,
+ lldb::pid_t pid)
+ : OperationArgs(monitor), m_pid(pid) { }
+
+ProcessMonitor::AttachArgs::~AttachArgs()
+{ }
+
+//------------------------------------------------------------------------------
+/// The basic design of the ProcessMonitor is built around two threads.
+///
+/// One thread (@see SignalThread) simply blocks on a call to waitpid() looking
+/// for changes in the debugee state. When a change is detected a
+/// ProcessMessage is sent to the associated ProcessFreeBSD instance. This thread
+/// "drives" state changes in the debugger.
+///
+/// The second thread (@see OperationThread) is responsible for two things 1)
+/// launching or attaching to the inferior process, and then 2) servicing
+/// operations such as register reads/writes, stepping, etc. See the comments
+/// on the Operation class for more info as to why this is needed.
+ProcessMonitor::ProcessMonitor(ProcessPOSIX *process,
+ Module *module,
+ const char *argv[],
+ const char *envp[],
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ const char *working_dir,
+ lldb_private::Error &error)
+ : m_process(static_cast<ProcessFreeBSD *>(process)),
+ m_operation_thread(LLDB_INVALID_HOST_THREAD),
+ m_monitor_thread(LLDB_INVALID_HOST_THREAD),
+ m_pid(LLDB_INVALID_PROCESS_ID),
+ m_server_mutex(Mutex::eMutexTypeRecursive),
+ m_terminal_fd(-1),
+ m_client_fd(-1),
+ m_server_fd(-1)
+{
+ std::unique_ptr<LaunchArgs> args;
+
+ args.reset(new LaunchArgs(this, module, argv, envp,
+ stdin_path, stdout_path, stderr_path, working_dir));
+
+
+ // Server/client descriptors.
+ if (!EnableIPC())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Monitor failed to initialize.");
+ }
+
+ StartLaunchOpThread(args.get(), error);
+ if (!error.Success())
+ return;
+
+WAIT_AGAIN:
+ // Wait for the operation thread to initialize.
+ if (sem_wait(&args->m_semaphore))
+ {
+ if (errno == EINTR)
+ goto WAIT_AGAIN;
+ else
+ {
+ error.SetErrorToErrno();
+ return;
+ }
+ }
+
+ // Check that the launch was a success.
+ if (!args->m_error.Success())
+ {
+ StopOpThread();
+ error = args->m_error;
+ return;
+ }
+
+ // Finally, start monitoring the child process for change in state.
+ m_monitor_thread = Host::StartMonitoringChildProcess(
+ ProcessMonitor::MonitorCallback, this, GetPID(), true);
+ if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Process launch failed.");
+ return;
+ }
+}
+
+ProcessMonitor::ProcessMonitor(ProcessPOSIX *process,
+ lldb::pid_t pid,
+ lldb_private::Error &error)
+ : m_process(static_cast<ProcessFreeBSD *>(process)),
+ m_operation_thread(LLDB_INVALID_HOST_THREAD),
+ m_monitor_thread(LLDB_INVALID_HOST_THREAD),
+ m_pid(pid),
+ m_server_mutex(Mutex::eMutexTypeRecursive),
+ m_terminal_fd(-1),
+ m_client_fd(-1),
+ m_server_fd(-1)
+{
+ std::unique_ptr<AttachArgs> args;
+
+ args.reset(new AttachArgs(this, pid));
+
+ // Server/client descriptors.
+ if (!EnableIPC())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Monitor failed to initialize.");
+ }
+
+ StartAttachOpThread(args.get(), error);
+ if (!error.Success())
+ return;
+
+WAIT_AGAIN:
+ // Wait for the operation thread to initialize.
+ if (sem_wait(&args->m_semaphore))
+ {
+ if (errno == EINTR)
+ goto WAIT_AGAIN;
+ else
+ {
+ error.SetErrorToErrno();
+ return;
+ }
+ }
+
+ // Check that the attach was a success.
+ if (!args->m_error.Success())
+ {
+ StopOpThread();
+ error = args->m_error;
+ return;
+ }
+
+ // Finally, start monitoring the child process for change in state.
+ m_monitor_thread = Host::StartMonitoringChildProcess(
+ ProcessMonitor::MonitorCallback, this, GetPID(), true);
+ if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Process attach failed.");
+ return;
+ }
+}
+
+ProcessMonitor::~ProcessMonitor()
+{
+ StopMonitor();
+}
+
+//------------------------------------------------------------------------------
+// Thread setup and tear down.
+void
+ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Error &error)
+{
+ static const char *g_thread_name = "lldb.process.freebsd.operation";
+
+ if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread))
+ return;
+
+ m_operation_thread =
+ Host::ThreadCreate(g_thread_name, LaunchOpThread, args, &error);
+}
+
+void *
+ProcessMonitor::LaunchOpThread(void *arg)
+{
+ LaunchArgs *args = static_cast<LaunchArgs*>(arg);
+
+ if (!Launch(args)) {
+ sem_post(&args->m_semaphore);
+ return NULL;
+ }
+
+ ServeOperation(args);
+ return NULL;
+}
+
+bool
+ProcessMonitor::Launch(LaunchArgs *args)
+{
+ ProcessMonitor *monitor = args->m_monitor;
+ ProcessFreeBSD &process = monitor->GetProcess();
+ lldb::ProcessSP processSP = process.shared_from_this();
+ const char **argv = args->m_argv;
+ const char **envp = args->m_envp;
+ const char *stdin_path = args->m_stdin_path;
+ const char *stdout_path = args->m_stdout_path;
+ const char *stderr_path = args->m_stderr_path;
+ const char *working_dir = args->m_working_dir;
+
+ lldb_utility::PseudoTerminal terminal;
+ const size_t err_len = 1024;
+ char err_str[err_len];
+ lldb::pid_t pid;
+
+ lldb::ThreadSP inferior;
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+
+ // Propagate the environment if one is not supplied.
+ if (envp == NULL || envp[0] == NULL)
+ envp = const_cast<const char **>(environ);
+
+ // Pseudo terminal setup.
+ if (!terminal.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, err_str, err_len))
+ {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Could not open controlling TTY.");
+ goto FINISH;
+ }
+
+ if ((pid = terminal.Fork(err_str, err_len)) == -1)
+ {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Process fork failed.");
+ goto FINISH;
+ }
+
+ // Recognized child exit status codes.
+ enum {
+ ePtraceFailed = 1,
+ eDupStdinFailed,
+ eDupStdoutFailed,
+ eDupStderrFailed,
+ eChdirFailed,
+ eExecFailed
+ };
+
+ // Child process.
+ if (pid == 0)
+ {
+ // Trace this process.
+ if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0)
+ exit(ePtraceFailed);
+
+ // Do not inherit setgid powers.
+ setgid(getgid());
+
+ // Let us have our own process group.
+ setpgid(0, 0);
+
+ // Dup file descriptors if needed.
+ //
+ // FIXME: If two or more of the paths are the same we needlessly open
+ // the same file multiple times.
+ if (stdin_path != NULL && stdin_path[0])
+ if (!DupDescriptor(stdin_path, STDIN_FILENO, O_RDONLY))
+ exit(eDupStdinFailed);
+
+ if (stdout_path != NULL && stdout_path[0])
+ if (!DupDescriptor(stdout_path, STDOUT_FILENO, O_WRONLY | O_CREAT))
+ exit(eDupStdoutFailed);
+
+ if (stderr_path != NULL && stderr_path[0])
+ if (!DupDescriptor(stderr_path, STDERR_FILENO, O_WRONLY | O_CREAT))
+ exit(eDupStderrFailed);
+
+ // Change working directory
+ if (working_dir != NULL && working_dir[0])
+ if (0 != ::chdir(working_dir))
+ exit(eChdirFailed);
+
+ // Execute. We should never return.
+ execve(argv[0],
+ const_cast<char *const *>(argv),
+ const_cast<char *const *>(envp));
+ exit(eExecFailed);
+ }
+
+ // Wait for the child process to to trap on its call to execve.
+ ::pid_t wpid;
+ int status;
+ if ((wpid = waitpid(pid, &status, 0)) < 0)
+ {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ }
+ else if (WIFEXITED(status))
+ {
+ // open, dup or execve likely failed for some reason.
+ args->m_error.SetErrorToGenericError();
+ switch (WEXITSTATUS(status))
+ {
+ case ePtraceFailed:
+ args->m_error.SetErrorString("Child ptrace failed.");
+ break;
+ case eDupStdinFailed:
+ args->m_error.SetErrorString("Child open stdin failed.");
+ break;
+ case eDupStdoutFailed:
+ args->m_error.SetErrorString("Child open stdout failed.");
+ break;
+ case eDupStderrFailed:
+ args->m_error.SetErrorString("Child open stderr failed.");
+ break;
+ case eChdirFailed:
+ args->m_error.SetErrorString("Child failed to set working directory.");
+ break;
+ case eExecFailed:
+ args->m_error.SetErrorString("Child exec failed.");
+ break;
+ default:
+ args->m_error.SetErrorString("Child returned unknown exit status.");
+ break;
+ }
+ goto FINISH;
+ }
+ assert(WIFSTOPPED(status) && wpid == pid &&
+ "Could not sync with inferior process.");
+
+#ifdef notyet
+ // Have the child raise an event on exit. This is used to keep the child in
+ // limbo until it is destroyed.
+ if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0)
+ {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ }
+#endif
+ // Release the master terminal descriptor and pass it off to the
+ // ProcessMonitor instance. Similarly stash the inferior pid.
+ monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor();
+ monitor->m_pid = pid;
+
+ // Set the terminal fd to be in non blocking mode (it simplifies the
+ // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking
+ // descriptor to read from).
+ if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error))
+ goto FINISH;
+
+ // Update the process thread list with this new thread.
+ inferior.reset(process.CreateNewPOSIXThread(*processSP, pid));
+ if (log)
+ log->Printf ("ProcessMonitor::%s() adding pid = %" PRIu64, __FUNCTION__, pid);
+ process.GetThreadList().AddThread(inferior);
+
+ // Let our process instance know the thread has stopped.
+ process.SendMessage(ProcessMessage::Trace(pid));
+
+FINISH:
+ return args->m_error.Success();
+}
+
+bool
+ProcessMonitor::EnableIPC()
+{
+ int fd[2];
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd))
+ return false;
+
+ m_client_fd = fd[0];
+ m_server_fd = fd[1];
+ return true;
+}
+
+void
+ProcessMonitor::StartAttachOpThread(AttachArgs *args, lldb_private::Error &error)
+{
+ static const char *g_thread_name = "lldb.process.freebsd.operation";
+
+ if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread))
+ return;
+
+ m_operation_thread =
+ Host::ThreadCreate(g_thread_name, AttachOpThread, args, &error);
+}
+
+void *
+ProcessMonitor::AttachOpThread(void *arg)
+{
+ AttachArgs *args = static_cast<AttachArgs*>(arg);
+
+ if (!Attach(args))
+ return NULL;
+
+ ServeOperation(args);
+ return NULL;
+}
+
+bool
+ProcessMonitor::Attach(AttachArgs *args)
+{
+ lldb::pid_t pid = args->m_pid;
+
+ ProcessMonitor *monitor = args->m_monitor;
+ ProcessFreeBSD &process = monitor->GetProcess();
+ lldb::ProcessSP processSP = process.shared_from_this();
+ ThreadList &tl = process.GetThreadList();
+ lldb::ThreadSP inferior;
+
+ if (pid <= 1)
+ {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Attaching to process 1 is not allowed.");
+ goto FINISH;
+ }
+
+ // Attach to the requested process.
+ if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0)
+ {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ }
+
+ int status;
+ if ((status = waitpid(pid, NULL, 0)) < 0)
+ {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ }
+
+ // Update the process thread list with the attached thread.
+ inferior.reset(process.CreateNewPOSIXThread(*processSP, pid));
+ tl.AddThread(inferior);
+
+ // Let our process instance know the thread has stopped.
+ process.SendMessage(ProcessMessage::Trace(pid));
+
+ FINISH:
+ return args->m_error.Success();
+}
+
+bool
+ProcessMonitor::MonitorCallback(void *callback_baton,
+ lldb::pid_t pid,
+ bool exited,
+ int signal,
+ int status)
+{
+ ProcessMessage message;
+ ProcessMonitor *monitor = static_cast<ProcessMonitor*>(callback_baton);
+ ProcessFreeBSD *process = monitor->m_process;
+ assert(process);
+ bool stop_monitoring;
+ struct ptrace_lwpinfo plwp;
+ int ptrace_err;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+
+ if (exited)
+ {
+ if (log)
+ log->Printf ("ProcessMonitor::%s() got exit signal, tid = %" PRIu64, __FUNCTION__, pid);
+ message = ProcessMessage::Exit(pid, status);
+ process->SendMessage(message);
+ return pid == process->GetID();
+ }
+
+ if (!monitor->GetLwpInfo(pid, &plwp, ptrace_err))
+ stop_monitoring = true; // pid is gone. Bail.
+ else {
+ switch (plwp.pl_siginfo.si_signo)
+ {
+ case SIGTRAP:
+ message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, pid);
+ break;
+
+ default:
+ message = MonitorSignal(monitor, &plwp.pl_siginfo, pid);
+ break;
+ }
+
+ process->SendMessage(message);
+ stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage;
+ }
+
+ return stop_monitoring;
+}
+
+ProcessMessage
+ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor,
+ const siginfo_t *info, lldb::pid_t pid)
+{
+ ProcessMessage message;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+
+ assert(monitor);
+ assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!");
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "Unexpected SIGTRAP code!");
+ break;
+
+ case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */):
+ {
+ // The inferior process is about to exit. Maintain the process in a
+ // state of "limbo" until we are explicitly commanded to detach,
+ // destroy, resume, etc.
+ unsigned long data = 0;
+ if (!monitor->GetEventMessage(pid, &data))
+ data = -1;
+ if (log)
+ log->Printf ("ProcessMonitor::%s() received exit? event, data = %lx, pid = %" PRIu64, __FUNCTION__, data, pid);
+ message = ProcessMessage::Limbo(pid, (data >> 8));
+ break;
+ }
+
+ case 0:
+ case TRAP_TRACE:
+ if (log)
+ log->Printf ("ProcessMonitor::%s() received trace event, pid = %" PRIu64, __FUNCTION__, pid);
+ message = ProcessMessage::Trace(pid);
+ break;
+
+ case SI_KERNEL:
+ case TRAP_BRKPT:
+ if (log)
+ log->Printf ("ProcessMonitor::%s() received breakpoint event, pid = %" PRIu64, __FUNCTION__, pid);
+ message = ProcessMessage::Break(pid);
+ break;
+ }
+
+ return message;
+}
+
+ProcessMessage
+ProcessMonitor::MonitorSignal(ProcessMonitor *monitor,
+ const siginfo_t *info, lldb::pid_t pid)
+{
+ ProcessMessage message;
+ int signo = info->si_signo;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+
+ // POSIX says that process behaviour is undefined after it ignores a SIGFPE,
+ // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a
+ // kill(2) or raise(3). Similarly for tgkill(2) on FreeBSD.
+ //
+ // IOW, user generated signals never generate what we consider to be a
+ // "crash".
+ //
+ // Similarly, ACK signals generated by this monitor.
+ if (info->si_code == SI_USER)
+ {
+ if (log)
+ log->Printf ("ProcessMonitor::%s() received signal %s with code %s, pid = %d",
+ __FUNCTION__,
+ monitor->m_process->GetUnixSignals().GetSignalAsCString (signo),
+ "SI_USER",
+ info->si_pid);
+ if (info->si_pid == getpid())
+ return ProcessMessage::SignalDelivered(pid, signo);
+ else
+ return ProcessMessage::Signal(pid, signo);
+ }
+
+ if (log)
+ log->Printf ("ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals().GetSignalAsCString (signo));
+
+ if (signo == SIGSEGV) {
+ lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+ ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info);
+ return ProcessMessage::Crash(pid, reason, signo, fault_addr);
+ }
+
+ if (signo == SIGILL) {
+ lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+ ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info);
+ return ProcessMessage::Crash(pid, reason, signo, fault_addr);
+ }
+
+ if (signo == SIGFPE) {
+ lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+ ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info);
+ return ProcessMessage::Crash(pid, reason, signo, fault_addr);
+ }
+
+ if (signo == SIGBUS) {
+ lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+ ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info);
+ return ProcessMessage::Crash(pid, reason, signo, fault_addr);
+ }
+
+ // Everything else is "normal" and does not require any special action on
+ // our part.
+ return ProcessMessage::Signal(pid, signo);
+}
+
+ProcessMessage::CrashReason
+ProcessMonitor::GetCrashReasonForSIGSEGV(const siginfo_t *info)
+{
+ ProcessMessage::CrashReason reason;
+ assert(info->si_signo == SIGSEGV);
+
+ reason = ProcessMessage::eInvalidCrashReason;
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "unexpected si_code for SIGSEGV");
+ break;
+ case SEGV_MAPERR:
+ reason = ProcessMessage::eInvalidAddress;
+ break;
+ case SEGV_ACCERR:
+ reason = ProcessMessage::ePrivilegedAddress;
+ break;
+ }
+
+ return reason;
+}
+
+ProcessMessage::CrashReason
+ProcessMonitor::GetCrashReasonForSIGILL(const siginfo_t *info)
+{
+ ProcessMessage::CrashReason reason;
+ assert(info->si_signo == SIGILL);
+
+ reason = ProcessMessage::eInvalidCrashReason;
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "unexpected si_code for SIGILL");
+ break;
+ case ILL_ILLOPC:
+ reason = ProcessMessage::eIllegalOpcode;
+ break;
+ case ILL_ILLOPN:
+ reason = ProcessMessage::eIllegalOperand;
+ break;
+ case ILL_ILLADR:
+ reason = ProcessMessage::eIllegalAddressingMode;
+ break;
+ case ILL_ILLTRP:
+ reason = ProcessMessage::eIllegalTrap;
+ break;
+ case ILL_PRVOPC:
+ reason = ProcessMessage::ePrivilegedOpcode;
+ break;
+ case ILL_PRVREG:
+ reason = ProcessMessage::ePrivilegedRegister;
+ break;
+ case ILL_COPROC:
+ reason = ProcessMessage::eCoprocessorError;
+ break;
+ case ILL_BADSTK:
+ reason = ProcessMessage::eInternalStackError;
+ break;
+ }
+
+ return reason;
+}
+
+ProcessMessage::CrashReason
+ProcessMonitor::GetCrashReasonForSIGFPE(const siginfo_t *info)
+{
+ ProcessMessage::CrashReason reason;
+ assert(info->si_signo == SIGFPE);
+
+ reason = ProcessMessage::eInvalidCrashReason;
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "unexpected si_code for SIGFPE");
+ break;
+ case FPE_INTDIV:
+ reason = ProcessMessage::eIntegerDivideByZero;
+ break;
+ case FPE_INTOVF:
+ reason = ProcessMessage::eIntegerOverflow;
+ break;
+ case FPE_FLTDIV:
+ reason = ProcessMessage::eFloatDivideByZero;
+ break;
+ case FPE_FLTOVF:
+ reason = ProcessMessage::eFloatOverflow;
+ break;
+ case FPE_FLTUND:
+ reason = ProcessMessage::eFloatUnderflow;
+ break;
+ case FPE_FLTRES:
+ reason = ProcessMessage::eFloatInexactResult;
+ break;
+ case FPE_FLTINV:
+ reason = ProcessMessage::eFloatInvalidOperation;
+ break;
+ case FPE_FLTSUB:
+ reason = ProcessMessage::eFloatSubscriptRange;
+ break;
+ }
+
+ return reason;
+}
+
+ProcessMessage::CrashReason
+ProcessMonitor::GetCrashReasonForSIGBUS(const siginfo_t *info)
+{
+ ProcessMessage::CrashReason reason;
+ assert(info->si_signo == SIGBUS);
+
+ reason = ProcessMessage::eInvalidCrashReason;
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "unexpected si_code for SIGBUS");
+ break;
+ case BUS_ADRALN:
+ reason = ProcessMessage::eIllegalAlignment;
+ break;
+ case BUS_ADRERR:
+ reason = ProcessMessage::eIllegalAddress;
+ break;
+ case BUS_OBJERR:
+ reason = ProcessMessage::eHardwareError;
+ break;
+ }
+
+ return reason;
+}
+
+void
+ProcessMonitor::ServeOperation(OperationArgs *args)
+{
+ int status;
+ pollfd fdset;
+
+ ProcessMonitor *monitor = args->m_monitor;
+
+ fdset.fd = monitor->m_server_fd;
+ fdset.events = POLLIN | POLLPRI;
+ fdset.revents = 0;
+
+ // We are finised with the arguments and are ready to go. Sync with the
+ // parent thread and start serving operations on the inferior.
+ sem_post(&args->m_semaphore);
+
+ for (;;)
+ {
+ if ((status = poll(&fdset, 1, -1)) < 0)
+ {
+ switch (errno)
+ {
+ default:
+ assert(false && "Unexpected poll() failure!");
+ continue;
+
+ case EINTR: continue; // Just poll again.
+ case EBADF: return; // Connection terminated.
+ }
+ }
+
+ assert(status == 1 && "Too many descriptors!");
+
+ if (fdset.revents & POLLIN)
+ {
+ Operation *op = NULL;
+
+ READ_AGAIN:
+ if ((status = read(fdset.fd, &op, sizeof(op))) < 0)
+ {
+ // There is only one acceptable failure.
+ assert(errno == EINTR);
+ goto READ_AGAIN;
+ }
+ if (status == 0)
+ continue; // Poll again. The connection probably terminated.
+ assert(status == sizeof(op));
+ op->Execute(monitor);
+ write(fdset.fd, &op, sizeof(op));
+ }
+ }
+}
+
+void
+ProcessMonitor::DoOperation(Operation *op)
+{
+ int status;
+ Operation *ack = NULL;
+ Mutex::Locker lock(m_server_mutex);
+
+ // FIXME: Do proper error checking here.
+ write(m_client_fd, &op, sizeof(op));
+
+READ_AGAIN:
+ if ((status = read(m_client_fd, &ack, sizeof(ack))) < 0)
+ {
+ // If interrupted by a signal handler try again. Otherwise the monitor
+ // thread probably died and we have a stale file descriptor -- abort the
+ // operation.
+ if (errno == EINTR)
+ goto READ_AGAIN;
+ return;
+ }
+
+ assert(status == sizeof(ack));
+ assert(ack == op && "Invalid monitor thread response!");
+}
+
+size_t
+ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Error &error)
+{
+ size_t result;
+ ReadOperation op(vm_addr, buf, size, error, result);
+ DoOperation(&op);
+ return result;
+}
+
+size_t
+ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
+ lldb_private::Error &error)
+{
+ size_t result;
+ WriteOperation op(vm_addr, buf, size, error, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char* reg_name,
+ unsigned size, RegisterValue &value)
+{
+ bool result;
+ ReadRegOperation op(tid, offset, size, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char* reg_name, const RegisterValue &value)
+{
+ bool result;
+ WriteRegOperation op(tid, offset, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size)
+{
+ bool result;
+ ReadGPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size)
+{
+ bool result;
+ ReadFPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset)
+{
+ return false;
+}
+
+bool
+ProcessMonitor::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size)
+{
+ bool result;
+ WriteGPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size)
+{
+ bool result;
+ WriteFPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset)
+{
+ return false;
+}
+
+bool
+ProcessMonitor::Resume(lldb::tid_t tid, uint32_t signo)
+{
+ bool result;
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("ProcessMonitor::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid,
+ m_process->GetUnixSignals().GetSignalAsCString (signo));
+ ResumeOperation op(tid, signo, result);
+ DoOperation(&op);
+ if (log)
+ log->Printf ("ProcessMonitor::%s() resuming result = %s", __FUNCTION__, result ? "true" : "false");
+ return result;
+}
+
+bool
+ProcessMonitor::SingleStep(lldb::tid_t tid, uint32_t signo)
+{
+ bool result;
+ SingleStepOperation op(tid, signo, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::BringProcessIntoLimbo()
+{
+ bool result;
+ KillOperation op(result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &ptrace_err)
+{
+ bool result;
+ LwpInfoOperation op(tid, lwpinfo, result, ptrace_err);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message)
+{
+ bool result;
+ EventMessageOperation op(tid, message, result);
+ DoOperation(&op);
+ return result;
+}
+
+lldb_private::Error
+ProcessMonitor::Detach(lldb::tid_t tid)
+{
+ lldb_private::Error error;
+ if (tid != LLDB_INVALID_THREAD_ID)
+ {
+ DetachOperation op(error);
+ DoOperation(&op);
+ }
+ return error;
+}
+
+bool
+ProcessMonitor::DupDescriptor(const char *path, int fd, int flags)
+{
+ int target_fd = open(path, flags, 0666);
+
+ if (target_fd == -1)
+ return false;
+
+ return (dup2(target_fd, fd) == -1) ? false : true;
+}
+
+void
+ProcessMonitor::StopMonitoringChildProcess()
+{
+ lldb::thread_result_t thread_result;
+
+ if (IS_VALID_LLDB_HOST_THREAD(m_monitor_thread))
+ {
+ Host::ThreadCancel(m_monitor_thread, NULL);
+ Host::ThreadJoin(m_monitor_thread, &thread_result, NULL);
+ m_monitor_thread = LLDB_INVALID_HOST_THREAD;
+ }
+}
+
+void
+ProcessMonitor::StopMonitor()
+{
+ StopMonitoringChildProcess();
+ StopOpThread();
+ CloseFD(m_terminal_fd);
+ CloseFD(m_client_fd);
+ CloseFD(m_server_fd);
+}
+
+void
+ProcessMonitor::StopOpThread()
+{
+ lldb::thread_result_t result;
+
+ if (!IS_VALID_LLDB_HOST_THREAD(m_operation_thread))
+ return;
+
+ Host::ThreadCancel(m_operation_thread, NULL);
+ Host::ThreadJoin(m_operation_thread, &result, NULL);
+ m_operation_thread = LLDB_INVALID_HOST_THREAD;
+}
+
+void
+ProcessMonitor::CloseFD(int &fd)
+{
+ if (fd != -1)
+ {
+ close(fd);
+ fd = -1;
+ }
+}
diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/source/Plugins/Process/FreeBSD/ProcessMonitor.h
new file mode 100644
index 0000000..ce66c03
--- /dev/null
+++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.h
@@ -0,0 +1,322 @@
+//===-- ProcessMonitor.h -------------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessMonitor_H_
+#define liblldb_ProcessMonitor_H_
+
+// C Includes
+#include <semaphore.h>
+#include <signal.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/lldb-types.h"
+#include "lldb/Host/Mutex.h"
+
+namespace lldb_private
+{
+class Error;
+class Module;
+class Scalar;
+} // End lldb_private namespace.
+
+class ProcessFreeBSD;
+class Operation;
+
+/// @class ProcessMonitor
+/// @brief Manages communication with the inferior (debugee) process.
+///
+/// Upon construction, this class prepares and launches an inferior process for
+/// debugging.
+///
+/// Changes in the inferior process state are propagated to the associated
+/// ProcessFreeBSD instance by calling ProcessFreeBSD::SendMessage with the
+/// appropriate ProcessMessage events.
+///
+/// A purposely minimal set of operations are provided to interrogate and change
+/// the inferior process state.
+class ProcessMonitor
+{
+public:
+
+ /// Launches an inferior process ready for debugging. Forms the
+ /// implementation of Process::DoLaunch.
+ ProcessMonitor(ProcessPOSIX *process,
+ lldb_private::Module *module,
+ char const *argv[],
+ char const *envp[],
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ const char *working_dir,
+ lldb_private::Error &error);
+
+ ProcessMonitor(ProcessPOSIX *process,
+ lldb::pid_t pid,
+ lldb_private::Error &error);
+
+ ~ProcessMonitor();
+
+ /// Provides the process number of debugee.
+ lldb::pid_t
+ GetPID() const { return m_pid; }
+
+ /// Returns the process associated with this ProcessMonitor.
+ ProcessFreeBSD &
+ GetProcess() { return *m_process; }
+
+ /// Returns a file descriptor to the controlling terminal of the inferior
+ /// process.
+ ///
+ /// Reads from this file descriptor yield both the standard output and
+ /// standard error of this debugee. Even if stderr and stdout were
+ /// redirected on launch it may still happen that data is available on this
+ /// descriptor (if the inferior process opens /dev/tty, for example).
+ ///
+ /// If this monitor was attached to an existing process this method returns
+ /// -1.
+ int
+ GetTerminalFD() const { return m_terminal_fd; }
+
+ /// Reads @p size bytes from address @vm_adder in the inferior process
+ /// address space.
+ ///
+ /// This method is provided to implement Process::DoReadMemory.
+ size_t
+ ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ lldb_private::Error &error);
+
+ /// Writes @p size bytes from address @p vm_adder in the inferior process
+ /// address space.
+ ///
+ /// This method is provided to implement Process::DoWriteMemory.
+ size_t
+ WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
+ lldb_private::Error &error);
+
+ /// Reads the contents from the register identified by the given (architecture
+ /// dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ /// FIXME: The FreeBSD implementation of this function should use tid in order
+ /// to enable support for debugging threaded programs.
+ bool
+ ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name,
+ unsigned size, lldb_private::RegisterValue &value);
+
+ /// Writes the given value to the register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ /// FIXME: The FreeBSD implementation of this function should use tid in order
+ /// to enable support for debugging threaded programs.
+ bool
+ WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name,
+ const lldb_private::RegisterValue &value);
+
+ /// Reads all general purpose registers into the specified buffer.
+ /// FIXME: The FreeBSD implementation of this function should use tid in order
+ /// to enable support for debugging threaded programs.
+ bool
+ ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Reads all floating point registers into the specified buffer.
+ /// FIXME: The FreeBSD implementation of this function should use tid in order
+ /// to enable support for debugging threaded programs.
+ bool
+ ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Reads the specified register set into the specified buffer.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ /// FIXME: The FreeBSD implementation of this function should use tid in order
+ /// to enable support for debugging threaded programs.
+ bool
+ ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset);
+
+ /// Writes all general purpose registers into the specified buffer.
+ /// FIXME: The FreeBSD implementation of this function should use tid in order
+ /// to enable support for debugging threaded programs.
+ bool
+ WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Writes all floating point registers into the specified buffer.
+ /// FIXME: The FreeBSD implementation of this function should use tid in order
+ /// to enable support for debugging threaded programs.
+ bool
+ WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Writes the specified register set into the specified buffer.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ /// FIXME: The FreeBSD implementation of this function should use tid in order
+ /// to enable support for debugging threaded programs.
+ bool
+ WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset);
+
+ /// Writes a ptrace_lwpinfo structure corresponding to the given thread ID
+ /// to the memory region pointed to by @p lwpinfo.
+ bool
+ GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &error_no);
+
+ /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
+ /// corresponding to the given thread IDto the memory pointed to by @p
+ /// message.
+ bool
+ GetEventMessage(lldb::tid_t tid, unsigned long *message);
+
+ /// Resumes the given thread. If @p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+ bool
+ Resume(lldb::tid_t tid, uint32_t signo);
+
+ /// Single steps the given thread. If @p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+ bool
+ SingleStep(lldb::tid_t tid, uint32_t signo);
+
+ /// Sends the inferior process a PTRACE_KILL signal. The inferior will
+ /// still exists and can be interrogated. Once resumed it will exit as
+ /// though it received a SIGKILL.
+ bool
+ BringProcessIntoLimbo();
+
+ lldb_private::Error
+ Detach(lldb::tid_t tid);
+
+ void
+ StopMonitor();
+
+private:
+ ProcessFreeBSD *m_process;
+
+ lldb::thread_t m_operation_thread;
+ lldb::thread_t m_monitor_thread;
+ lldb::pid_t m_pid;
+
+
+ lldb_private::Mutex m_server_mutex;
+ int m_terminal_fd;
+ int m_client_fd;
+ int m_server_fd;
+
+ struct OperationArgs
+ {
+ OperationArgs(ProcessMonitor *monitor);
+
+ ~OperationArgs();
+
+ ProcessMonitor *m_monitor; // The monitor performing the attach.
+ sem_t m_semaphore; // Posted to once operation complete.
+ lldb_private::Error m_error; // Set if process operation failed.
+ };
+
+ /// @class LauchArgs
+ ///
+ /// @brief Simple structure to pass data to the thread responsible for
+ /// launching a child process.
+ struct LaunchArgs : OperationArgs
+ {
+ LaunchArgs(ProcessMonitor *monitor,
+ lldb_private::Module *module,
+ char const **argv,
+ char const **envp,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ const char *working_dir);
+
+ ~LaunchArgs();
+
+ lldb_private::Module *m_module; // The executable image to launch.
+ char const **m_argv; // Process arguments.
+ char const **m_envp; // Process environment.
+ const char *m_stdin_path; // Redirect stdin or NULL.
+ const char *m_stdout_path; // Redirect stdout or NULL.
+ const char *m_stderr_path; // Redirect stderr or NULL.
+ const char *m_working_dir; // Working directory or NULL.
+ };
+
+ void
+ StartLaunchOpThread(LaunchArgs *args, lldb_private::Error &error);
+
+ static void *
+ LaunchOpThread(void *arg);
+
+ static bool
+ Launch(LaunchArgs *args);
+
+ bool
+ EnableIPC();
+
+ struct AttachArgs : OperationArgs
+ {
+ AttachArgs(ProcessMonitor *monitor,
+ lldb::pid_t pid);
+
+ ~AttachArgs();
+
+ lldb::pid_t m_pid; // pid of the process to be attached.
+ };
+
+ void
+ StartAttachOpThread(AttachArgs *args, lldb_private::Error &error);
+
+ static void *
+ AttachOpThread(void *args);
+
+ static bool
+ Attach(AttachArgs *args);
+
+ static void
+ ServeOperation(OperationArgs *args);
+
+ static bool
+ DupDescriptor(const char *path, int fd, int flags);
+
+ static bool
+ MonitorCallback(void *callback_baton,
+ lldb::pid_t pid, bool exited, int signal, int status);
+
+ static ProcessMessage
+ MonitorSIGTRAP(ProcessMonitor *monitor,
+ const siginfo_t *info, lldb::pid_t pid);
+
+ static ProcessMessage
+ MonitorSignal(ProcessMonitor *monitor,
+ const siginfo_t *info, lldb::pid_t pid);
+
+ static ProcessMessage::CrashReason
+ GetCrashReasonForSIGSEGV(const siginfo_t *info);
+
+ static ProcessMessage::CrashReason
+ GetCrashReasonForSIGILL(const siginfo_t *info);
+
+ static ProcessMessage::CrashReason
+ GetCrashReasonForSIGFPE(const siginfo_t *info);
+
+ static ProcessMessage::CrashReason
+ GetCrashReasonForSIGBUS(const siginfo_t *info);
+
+ void
+ DoOperation(Operation *op);
+
+ /// Stops the child monitor thread.
+ void
+ StopMonitoringChildProcess();
+
+ /// Stops the operation thread used to attach/launch a process.
+ void
+ StopOpThread();
+
+ void
+ CloseFD(int &fd);
+};
+
+#endif // #ifndef liblldb_ProcessMonitor_H_
diff --git a/source/Plugins/Process/POSIX/POSIXStopInfo.cpp b/source/Plugins/Process/POSIX/POSIXStopInfo.cpp
new file mode 100644
index 0000000..6e2c140
--- /dev/null
+++ b/source/Plugins/Process/POSIX/POSIXStopInfo.cpp
@@ -0,0 +1,89 @@
+//===-- POSIXStopInfo.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "POSIXStopInfo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+//===----------------------------------------------------------------------===//
+// POSIXLimboStopInfo
+
+POSIXLimboStopInfo::~POSIXLimboStopInfo() { }
+
+lldb::StopReason
+POSIXLimboStopInfo::GetStopReason() const
+{
+ return lldb::eStopReasonThreadExiting;
+}
+
+const char *
+POSIXLimboStopInfo::GetDescription()
+{
+ return "thread exiting";
+}
+
+bool
+POSIXLimboStopInfo::ShouldStop(Event *event_ptr)
+{
+ return false;
+}
+
+bool
+POSIXLimboStopInfo::ShouldNotify(Event *event_ptr)
+{
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// POSIXCrashStopInfo
+
+POSIXCrashStopInfo::~POSIXCrashStopInfo() { }
+
+lldb::StopReason
+POSIXCrashStopInfo::GetStopReason() const
+{
+ return lldb::eStopReasonException;
+}
+
+const char *
+POSIXCrashStopInfo::GetDescription()
+{
+ return ProcessMessage::GetCrashReasonString(m_crash_reason, m_fault_addr);
+}
+
+//===----------------------------------------------------------------------===//
+// POSIXNewThreadStopInfo
+
+POSIXNewThreadStopInfo::~POSIXNewThreadStopInfo() { }
+
+lldb::StopReason
+POSIXNewThreadStopInfo::GetStopReason() const
+{
+ return lldb::eStopReasonNone;
+}
+
+const char *
+POSIXNewThreadStopInfo::GetDescription()
+{
+ return "thread spawned";
+}
+
+bool
+POSIXNewThreadStopInfo::ShouldStop(Event *event_ptr)
+{
+ return false;
+}
+
+bool
+POSIXNewThreadStopInfo::ShouldNotify(Event *event_ptr)
+{
+ return false;
+}
diff --git a/source/Plugins/Process/POSIX/POSIXStopInfo.h b/source/Plugins/Process/POSIX/POSIXStopInfo.h
new file mode 100644
index 0000000..cbf309e
--- /dev/null
+++ b/source/Plugins/Process/POSIX/POSIXStopInfo.h
@@ -0,0 +1,120 @@
+//===-- POSIXStopInfo.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_POSIXStopInfo_H_
+#define liblldb_POSIXStopInfo_H_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Target/StopInfo.h"
+
+#include "POSIXThread.h"
+#include "ProcessMessage.h"
+
+//===----------------------------------------------------------------------===//
+/// @class POSIXStopInfo
+/// @brief Simple base class for all POSIX-specific StopInfo objects.
+///
+class POSIXStopInfo
+ : public lldb_private::StopInfo
+{
+public:
+ POSIXStopInfo(lldb_private::Thread &thread, uint32_t status)
+ : StopInfo(thread, status)
+ { }
+};
+
+//===----------------------------------------------------------------------===//
+/// @class POSIXLimboStopInfo
+/// @brief Represents the stop state of a process ready to exit.
+///
+class POSIXLimboStopInfo
+ : public POSIXStopInfo
+{
+public:
+ POSIXLimboStopInfo(POSIXThread &thread)
+ : POSIXStopInfo(thread, 0)
+ { }
+
+ ~POSIXLimboStopInfo();
+
+ lldb::StopReason
+ GetStopReason() const;
+
+ const char *
+ GetDescription();
+
+ bool
+ ShouldStop(lldb_private::Event *event_ptr);
+
+ bool
+ ShouldNotify(lldb_private::Event *event_ptr);
+};
+
+
+//===----------------------------------------------------------------------===//
+/// @class POSIXCrashStopInfo
+/// @brief Represents the stop state of process that is ready to crash.
+///
+class POSIXCrashStopInfo
+ : public POSIXStopInfo
+{
+public:
+ POSIXCrashStopInfo(POSIXThread &thread, uint32_t status,
+ ProcessMessage::CrashReason reason,
+ lldb::addr_t fault_addr)
+ : POSIXStopInfo(thread, status),
+ m_crash_reason(reason),
+ m_fault_addr(fault_addr)
+ { }
+
+ ~POSIXCrashStopInfo();
+
+ lldb::StopReason
+ GetStopReason() const;
+
+ const char *
+ GetDescription();
+
+private:
+ ProcessMessage::CrashReason m_crash_reason;
+ lldb::addr_t m_fault_addr;
+};
+
+//===----------------------------------------------------------------------===//
+/// @class POSIXNewThreadStopInfo
+/// @brief Represents the stop state of process when a new thread is spawned.
+///
+
+class POSIXNewThreadStopInfo
+ : public POSIXStopInfo
+{
+public:
+ POSIXNewThreadStopInfo (POSIXThread &thread)
+ : POSIXStopInfo (thread, 0)
+ { }
+
+ ~POSIXNewThreadStopInfo();
+
+ lldb::StopReason
+ GetStopReason() const;
+
+ const char *
+ GetDescription();
+
+ bool
+ ShouldStop(lldb_private::Event *event_ptr);
+
+ bool
+ ShouldNotify(lldb_private::Event *event_ptr);
+};
+
+#endif
diff --git a/source/Plugins/Process/POSIX/POSIXThread.cpp b/source/Plugins/Process/POSIX/POSIXThread.cpp
new file mode 100644
index 0000000..93c2966
--- /dev/null
+++ b/source/Plugins/Process/POSIX/POSIXThread.cpp
@@ -0,0 +1,578 @@
+//===-- POSIXThread.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-python.h"
+
+// C Includes
+#include <errno.h>
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/State.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "POSIXStopInfo.h"
+#include "POSIXThread.h"
+#include "ProcessPOSIX.h"
+#include "ProcessPOSIXLog.h"
+#include "ProcessMonitor.h"
+#include "RegisterContext_i386.h"
+#include "RegisterContext_x86_64.h"
+#include "RegisterContextPOSIX.h"
+#include "RegisterContextLinux_x86_64.h"
+#include "RegisterContextFreeBSD_x86_64.h"
+
+#include "UnwindLLDB.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+POSIXThread::POSIXThread(Process &process, lldb::tid_t tid)
+ : Thread(process, tid),
+ m_frame_ap (),
+ m_breakpoint (),
+ m_thread_name_valid (false),
+ m_thread_name ()
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+ if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
+ log->Printf ("POSIXThread::%s (tid = %" PRIi64 ")", __FUNCTION__, tid);
+
+ // Set the current watchpoints for this thread.
+ Target &target = GetProcess()->GetTarget();
+ const WatchpointList &wp_list = target.GetWatchpointList();
+ size_t wp_size = wp_list.GetSize();
+
+ for (uint32_t wp_idx = 0; wp_idx < wp_size; wp_idx++)
+ {
+ lldb::WatchpointSP wp = wp_list.GetByIndex(wp_idx);
+ if (wp.get() && wp->IsEnabled())
+ {
+ assert(EnableHardwareWatchpoint(wp.get()));
+ }
+ }
+}
+
+POSIXThread::~POSIXThread()
+{
+ DestroyThread();
+}
+
+ProcessMonitor &
+POSIXThread::GetMonitor()
+{
+ ProcessSP base = GetProcess();
+ ProcessPOSIX &process = static_cast<ProcessPOSIX&>(*base);
+ return process.GetMonitor();
+}
+
+void
+POSIXThread::RefreshStateAfterStop()
+{
+ // Invalidate all registers in our register context. We don't set "force" to
+ // true because the stop reply packet might have had some register values
+ // that were expedited and these will already be copied into the register
+ // context by the time this function gets called. The KDPRegisterContext
+ // class has been made smart enough to detect when it needs to invalidate
+ // which registers are valid by putting hooks in the register read and
+ // register supply functions where they check the process stop ID and do
+ // the right thing.
+ //if (StateIsStoppedState(GetState())
+ {
+ const bool force = false;
+ GetRegisterContext()->InvalidateIfNeeded (force);
+ }
+ // FIXME: This should probably happen somewhere else.
+ SetResumeState(eStateRunning);
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+ if (log)
+ log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to running", __FUNCTION__, GetID());
+}
+
+const char *
+POSIXThread::GetInfo()
+{
+ return NULL;
+}
+
+void
+POSIXThread::SetName (const char *name)
+{
+ m_thread_name_valid = (name && name[0]);
+ if (m_thread_name_valid)
+ m_thread_name.assign (name);
+ else
+ m_thread_name.clear();
+}
+
+const char *
+POSIXThread::GetName ()
+{
+ if (!m_thread_name_valid)
+ {
+ SetName(Host::GetThreadName(GetProcess()->GetID(), GetID()).c_str());
+ m_thread_name_valid = true;
+ }
+
+ if (m_thread_name.empty())
+ return NULL;
+ return m_thread_name.c_str();
+}
+
+lldb::RegisterContextSP
+POSIXThread::GetRegisterContext()
+{
+ if (!m_reg_context_sp)
+ {
+ ArchSpec arch = Host::GetArchitecture();
+
+ switch (arch.GetCore())
+ {
+ default:
+ assert(false && "CPU type not supported!");
+ break;
+
+ case ArchSpec::eCore_x86_32_i386:
+ case ArchSpec::eCore_x86_32_i486:
+ case ArchSpec::eCore_x86_32_i486sx:
+ m_reg_context_sp.reset(new RegisterContext_i386(*this, 0));
+ break;
+
+ case ArchSpec::eCore_x86_64_x86_64:
+ switch (arch.GetTriple().getOS())
+ {
+ case llvm::Triple::FreeBSD:
+ m_reg_context_sp.reset(new RegisterContextFreeBSD_x86_64(*this, 0));
+ break;
+ case llvm::Triple::Linux:
+ m_reg_context_sp.reset(new RegisterContextLinux_x86_64(*this, 0));
+ break;
+ default:
+ assert(false && "OS not supported");
+ break;
+ }
+ break;
+ }
+ }
+ return m_reg_context_sp;
+}
+
+lldb::RegisterContextSP
+POSIXThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame)
+{
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+ if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
+ log->Printf ("POSIXThread::%s ()", __FUNCTION__);
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0)
+ reg_ctx_sp = GetRegisterContext();
+ else
+ {
+ assert(GetUnwinder());
+ reg_ctx_sp = GetUnwinder()->CreateRegisterContextForFrame(frame);
+ }
+
+ return reg_ctx_sp;
+}
+
+bool
+POSIXThread::CalculateStopInfo()
+{
+ SetStopInfo (m_stop_info_sp);
+ return true;
+}
+
+Unwind *
+POSIXThread::GetUnwinder()
+{
+ if (m_unwinder_ap.get() == NULL)
+ m_unwinder_ap.reset(new UnwindLLDB(*this));
+
+ return m_unwinder_ap.get();
+}
+
+void
+POSIXThread::WillResume(lldb::StateType resume_state)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+ if (log)
+ log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to %s", __FUNCTION__, GetID(), StateAsCString(resume_state));
+ // TODO: the line below shouldn't really be done, but
+ // the POSIXThread might rely on this so I will leave this in for now
+ SetResumeState(resume_state);
+}
+
+void
+POSIXThread::DidStop()
+{
+ // Don't set the thread state to stopped unless we really stopped.
+}
+
+bool
+POSIXThread::Resume()
+{
+ lldb::StateType resume_state = GetResumeState();
+ ProcessMonitor &monitor = GetMonitor();
+ bool status;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+ if (log)
+ log->Printf ("POSIXThread::%s (), resume_state = %s", __FUNCTION__,
+ StateAsCString(resume_state));
+
+ switch (resume_state)
+ {
+ default:
+ assert(false && "Unexpected state for resume!");
+ status = false;
+ break;
+
+ case lldb::eStateRunning:
+ SetState(resume_state);
+ status = monitor.Resume(GetID(), GetResumeSignal());
+ break;
+
+ case lldb::eStateStepping:
+ SetState(resume_state);
+ status = monitor.SingleStep(GetID(), GetResumeSignal());
+ break;
+ case lldb::eStateStopped:
+ case lldb::eStateSuspended:
+ status = true;
+ break;
+ }
+
+ return status;
+}
+
+void
+POSIXThread::Notify(const ProcessMessage &message)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+ if (log)
+ log->Printf ("POSIXThread::%s () message kind = '%s' for tid %" PRIu64,
+ __FUNCTION__, message.PrintKind(), GetID());
+
+ switch (message.GetKind())
+ {
+ default:
+ assert(false && "Unexpected message kind!");
+ break;
+
+ case ProcessMessage::eExitMessage:
+ // Nothing to be done.
+ break;
+
+ case ProcessMessage::eLimboMessage:
+ LimboNotify(message);
+ break;
+
+ case ProcessMessage::eSignalMessage:
+ SignalNotify(message);
+ break;
+
+ case ProcessMessage::eSignalDeliveredMessage:
+ SignalDeliveredNotify(message);
+ break;
+
+ case ProcessMessage::eTraceMessage:
+ TraceNotify(message);
+ break;
+
+ case ProcessMessage::eBreakpointMessage:
+ BreakNotify(message);
+ break;
+
+ case ProcessMessage::eWatchpointMessage:
+ WatchNotify(message);
+ break;
+
+ case ProcessMessage::eCrashMessage:
+ CrashNotify(message);
+ break;
+
+ case ProcessMessage::eNewThreadMessage:
+ ThreadNotify(message);
+ break;
+ }
+}
+
+bool
+POSIXThread::EnableHardwareWatchpoint(Watchpoint *wp)
+{
+ bool wp_set = false;
+ if (wp)
+ {
+ addr_t wp_addr = wp->GetLoadAddress();
+ size_t wp_size = wp->GetByteSize();
+ bool wp_read = wp->WatchpointRead();
+ bool wp_write = wp->WatchpointWrite();
+ uint32_t wp_hw_index = wp->GetHardwareIndex();
+ RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX();
+ if (reg_ctx)
+ wp_set = reg_ctx->SetHardwareWatchpointWithIndex(wp_addr, wp_size,
+ wp_read, wp_write,
+ wp_hw_index);
+ }
+ return wp_set;
+}
+
+bool
+POSIXThread::DisableHardwareWatchpoint(Watchpoint *wp)
+{
+ bool result = false;
+ if (wp)
+ {
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ result = reg_ctx_sp->ClearHardwareWatchpoint(wp->GetHardwareIndex());
+ }
+ return result;
+}
+
+uint32_t
+POSIXThread::NumSupportedHardwareWatchpoints()
+{
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ return reg_ctx_sp->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t
+POSIXThread::FindVacantWatchpointIndex()
+{
+ uint32_t hw_index = LLDB_INVALID_INDEX32;
+ uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX();
+ if (reg_ctx)
+ {
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++)
+ {
+ if (reg_ctx->IsWatchpointVacant(wp_idx))
+ {
+ hw_index = wp_idx;
+ break;
+ }
+ }
+ }
+ return hw_index;
+}
+
+void
+POSIXThread::BreakNotify(const ProcessMessage &message)
+{
+ bool status;
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+
+ assert(GetRegisterContext());
+ status = GetRegisterContextPOSIX()->UpdateAfterBreakpoint();
+ assert(status && "Breakpoint update failed!");
+
+ // With our register state restored, resolve the breakpoint object
+ // corresponding to our current PC.
+ assert(GetRegisterContext());
+ lldb::addr_t pc = GetRegisterContext()->GetPC();
+ if (log)
+ log->Printf ("POSIXThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc);
+ lldb::BreakpointSiteSP bp_site(GetProcess()->GetBreakpointSiteList().FindByAddress(pc));
+
+ // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
+ // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
+ // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
+ if (bp_site && bp_site->ValidForThisThread(this))
+ {
+ lldb::break_id_t bp_id = bp_site->GetID();
+ if (GetProcess()->GetThreadList().SetSelectedThreadByID(GetID()))
+ SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id));
+ else
+ assert(false && "Invalid thread ID during BreakNotify.");
+ }
+ else
+ {
+ const ThreadSpec *spec = bp_site ?
+ bp_site->GetOwnerAtIndex(0)->GetOptionsNoCreate()->GetThreadSpecNoCreate() : 0;
+
+ if (spec && spec->TIDMatches(*this))
+ assert(false && "BreakpointSite is invalid for the current ThreadSpec.");
+ else
+ {
+ if (!m_stop_info_sp) {
+ StopInfoSP invalid_stop_info_sp;
+ SetStopInfo (invalid_stop_info_sp);
+ }
+ }
+ }
+}
+
+void
+POSIXThread::WatchNotify(const ProcessMessage &message)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+
+ lldb::addr_t halt_addr = message.GetHWAddress();
+ if (log)
+ log->Printf ("POSIXThread::%s () Hardware Watchpoint Address = 0x%8.8"
+ PRIx64, __FUNCTION__, halt_addr);
+
+ RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX();
+ if (reg_ctx)
+ {
+ uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++)
+ {
+ if (reg_ctx->IsWatchpointHit(wp_idx))
+ {
+ // Clear the watchpoint hit here
+ reg_ctx->ClearWatchpointHits();
+ break;
+ }
+ }
+
+ if (wp_idx == num_hw_wps)
+ return;
+
+ Target &target = GetProcess()->GetTarget();
+ lldb::addr_t wp_monitor_addr = reg_ctx->GetWatchpointAddress(wp_idx);
+ const WatchpointList &wp_list = target.GetWatchpointList();
+ lldb::WatchpointSP wp_sp = wp_list.FindByAddress(wp_monitor_addr);
+
+ assert(wp_sp.get() && "No watchpoint found");
+ SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID(*this,
+ wp_sp->GetID()));
+ }
+}
+
+void
+POSIXThread::TraceNotify(const ProcessMessage &message)
+{
+ SetStopInfo (StopInfo::CreateStopReasonToTrace(*this));
+}
+
+void
+POSIXThread::LimboNotify(const ProcessMessage &message)
+{
+ SetStopInfo (lldb::StopInfoSP(new POSIXLimboStopInfo(*this)));
+}
+
+void
+POSIXThread::SignalNotify(const ProcessMessage &message)
+{
+ int signo = message.GetSignal();
+
+ SetStopInfo (StopInfo::CreateStopReasonWithSignal(*this, signo));
+ SetResumeSignal(signo);
+}
+
+void
+POSIXThread::SignalDeliveredNotify(const ProcessMessage &message)
+{
+ int signo = message.GetSignal();
+
+ SetStopInfo (StopInfo::CreateStopReasonWithSignal(*this, signo));
+ SetResumeSignal(signo);
+}
+
+void
+POSIXThread::CrashNotify(const ProcessMessage &message)
+{
+ // FIXME: Update stop reason as per bugzilla 14598
+ int signo = message.GetSignal();
+
+ assert(message.GetKind() == ProcessMessage::eCrashMessage);
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+ if (log)
+ log->Printf ("POSIXThread::%s () signo = %i, reason = '%s'",
+ __FUNCTION__, signo, message.PrintCrashReason());
+
+ SetStopInfo (lldb::StopInfoSP(new POSIXCrashStopInfo(*this, signo,
+ message.GetCrashReason(),
+ message.GetFaultAddress())));
+ SetResumeSignal(signo);
+}
+
+void
+POSIXThread::ThreadNotify(const ProcessMessage &message)
+{
+ SetStopInfo (lldb::StopInfoSP(new POSIXNewThreadStopInfo(*this)));
+}
+
+unsigned
+POSIXThread::GetRegisterIndexFromOffset(unsigned offset)
+{
+ unsigned reg = LLDB_INVALID_REGNUM;
+ ArchSpec arch = Host::GetArchitecture();
+
+ switch (arch.GetCore())
+ {
+ default:
+ llvm_unreachable("CPU type not supported!");
+ break;
+
+ case ArchSpec::eCore_x86_32_i386:
+ case ArchSpec::eCore_x86_32_i486:
+ case ArchSpec::eCore_x86_32_i486sx:
+ case ArchSpec::eCore_x86_64_x86_64:
+ {
+ RegisterContextSP base = GetRegisterContext();
+ if (base) {
+ RegisterContextPOSIX &context = static_cast<RegisterContextPOSIX &>(*base);
+ reg = context.GetRegisterIndexFromOffset(offset);
+ }
+ }
+ break;
+ }
+ return reg;
+}
+
+const char *
+POSIXThread::GetRegisterName(unsigned reg)
+{
+ const char * name = nullptr;
+ ArchSpec arch = Host::GetArchitecture();
+
+ switch (arch.GetCore())
+ {
+ default:
+ assert(false && "CPU type not supported!");
+ break;
+
+ case ArchSpec::eCore_x86_32_i386:
+ case ArchSpec::eCore_x86_32_i486:
+ case ArchSpec::eCore_x86_32_i486sx:
+ case ArchSpec::eCore_x86_64_x86_64:
+ name = GetRegisterContext()->GetRegisterName(reg);
+ break;
+ }
+ return name;
+}
+
+const char *
+POSIXThread::GetRegisterNameFromOffset(unsigned offset)
+{
+ return GetRegisterName(GetRegisterIndexFromOffset(offset));
+}
+
diff --git a/source/Plugins/Process/POSIX/POSIXThread.h b/source/Plugins/Process/POSIX/POSIXThread.h
new file mode 100644
index 0000000..d051d23
--- /dev/null
+++ b/source/Plugins/Process/POSIX/POSIXThread.h
@@ -0,0 +1,133 @@
+//===-- POSIXThread.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_POSIXThread_H_
+#define liblldb_POSIXThread_H_
+
+// C Includes
+// C++ Includes
+#include <memory>
+#include <string>
+
+// Other libraries and framework includes
+#include "lldb/Target/Thread.h"
+#include "RegisterContextPOSIX.h"
+
+class ProcessMessage;
+class ProcessMonitor;
+class RegisterContextPOSIX;
+
+//------------------------------------------------------------------------------
+// @class POSIXThread
+// @brief Abstraction of a POSIX thread.
+class POSIXThread
+ : public lldb_private::Thread
+{
+public:
+ POSIXThread(lldb_private::Process &process, lldb::tid_t tid);
+
+ virtual ~POSIXThread();
+
+ void
+ RefreshStateAfterStop();
+
+ virtual void
+ WillResume(lldb::StateType resume_state);
+
+ // This notifies the thread when a private stop occurs.
+ virtual void
+ DidStop ();
+
+ const char *
+ GetInfo();
+
+ void
+ SetName (const char *name);
+
+ const char *
+ GetName ();
+
+ virtual lldb::RegisterContextSP
+ GetRegisterContext();
+
+ virtual lldb::RegisterContextSP
+ CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ //--------------------------------------------------------------------------
+ // These functions provide a mapping from the register offset
+ // back to the register index or name for use in debugging or log
+ // output.
+
+ unsigned
+ GetRegisterIndexFromOffset(unsigned offset);
+
+ const char *
+ GetRegisterName(unsigned reg);
+
+ const char *
+ GetRegisterNameFromOffset(unsigned offset);
+
+ //--------------------------------------------------------------------------
+ // These methods form a specialized interface to POSIX threads.
+ //
+ bool Resume();
+
+ void Notify(const ProcessMessage &message);
+
+ //--------------------------------------------------------------------------
+ // These methods provide an interface to watchpoints
+ //
+ bool EnableHardwareWatchpoint(lldb_private::Watchpoint *wp);
+
+ bool DisableHardwareWatchpoint(lldb_private::Watchpoint *wp);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+ uint32_t FindVacantWatchpointIndex();
+
+protected:
+ RegisterContextPOSIX *
+ GetRegisterContextPOSIX ()
+ {
+ if (!m_reg_context_sp)
+ m_reg_context_sp = GetRegisterContext();
+#if 0
+ return dynamic_cast<RegisterContextPOSIX*>(m_reg_context_sp.get());
+#endif
+ return (RegisterContextPOSIX *)m_reg_context_sp.get();
+ }
+
+ std::unique_ptr<lldb_private::StackFrame> m_frame_ap;
+
+ lldb::BreakpointSiteSP m_breakpoint;
+
+ bool m_thread_name_valid;
+ std::string m_thread_name;
+
+ ProcessMonitor &
+ GetMonitor();
+
+ virtual bool
+ CalculateStopInfo();
+
+ void BreakNotify(const ProcessMessage &message);
+ void WatchNotify(const ProcessMessage &message);
+ virtual void TraceNotify(const ProcessMessage &message);
+ void LimboNotify(const ProcessMessage &message);
+ void SignalNotify(const ProcessMessage &message);
+ void SignalDeliveredNotify(const ProcessMessage &message);
+ void CrashNotify(const ProcessMessage &message);
+ void ThreadNotify(const ProcessMessage &message);
+ void ExitNotify(const ProcessMessage &message);
+
+ lldb_private::Unwind *
+ GetUnwinder();
+};
+
+#endif // #ifndef liblldb_POSIXThread_H_
diff --git a/source/Plugins/Process/POSIX/ProcessMessage.cpp b/source/Plugins/Process/POSIX/ProcessMessage.cpp
new file mode 100644
index 0000000..60a29e0
--- /dev/null
+++ b/source/Plugins/Process/POSIX/ProcessMessage.cpp
@@ -0,0 +1,258 @@
+//===-- ProcessMessage.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessMessage.h"
+
+#include <sstream>
+
+using namespace lldb_private;
+
+namespace {
+
+inline void AppendFaultAddr(std::string& str, lldb::addr_t addr)
+{
+ std::stringstream ss;
+ ss << " (fault address: 0x" << std::hex << addr << ")";
+ str += ss.str();
+}
+
+}
+
+const char *
+ProcessMessage::GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr)
+{
+ static std::string str;
+
+ switch (reason)
+ {
+ default:
+ assert(false && "invalid CrashReason");
+ break;
+
+ case eInvalidAddress:
+ str = "invalid address";
+ AppendFaultAddr(str, fault_addr);
+ break;
+ case ePrivilegedAddress:
+ str = "address access protected";
+ AppendFaultAddr(str, fault_addr);
+ break;
+ case eIllegalOpcode:
+ str = "illegal instruction";
+ break;
+ case eIllegalOperand:
+ str = "illegal instruction operand";
+ break;
+ case eIllegalAddressingMode:
+ str = "illegal addressing mode";
+ break;
+ case eIllegalTrap:
+ str = "illegal trap";
+ break;
+ case ePrivilegedOpcode:
+ str = "privileged instruction";
+ break;
+ case ePrivilegedRegister:
+ str = "privileged register";
+ break;
+ case eCoprocessorError:
+ str = "coprocessor error";
+ break;
+ case eInternalStackError:
+ str = "internal stack error";
+ break;
+ case eIllegalAlignment:
+ str = "illegal alignment";
+ break;
+ case eIllegalAddress:
+ str = "illegal address";
+ break;
+ case eHardwareError:
+ str = "hardware error";
+ break;
+ case eIntegerDivideByZero:
+ str = "integer divide by zero";
+ break;
+ case eIntegerOverflow:
+ str = "integer overflow";
+ break;
+ case eFloatDivideByZero:
+ str = "floating point divide by zero";
+ break;
+ case eFloatOverflow:
+ str = "floating point overflow";
+ break;
+ case eFloatUnderflow:
+ str = "floating point underflow";
+ break;
+ case eFloatInexactResult:
+ str = "inexact floating point result";
+ break;
+ case eFloatInvalidOperation:
+ str = "invalid floating point operation";
+ break;
+ case eFloatSubscriptRange:
+ str = "invalid floating point subscript range";
+ break;
+ }
+
+ return str.c_str();
+}
+
+const char *
+ProcessMessage::PrintCrashReason(CrashReason reason)
+{
+#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+ // Just return the code in asci for integration builds.
+ chcar str[8];
+ sprintf(str, "%d", reason);
+#else
+ const char *str = NULL;
+
+ switch (reason)
+ {
+ case eInvalidCrashReason:
+ str = "eInvalidCrashReason";
+ break;
+
+ // SIGSEGV crash reasons.
+ case eInvalidAddress:
+ str = "eInvalidAddress";
+ break;
+ case ePrivilegedAddress:
+ str = "ePrivilegedAddress";
+ break;
+
+ // SIGILL crash reasons.
+ case eIllegalOpcode:
+ str = "eIllegalOpcode";
+ break;
+ case eIllegalOperand:
+ str = "eIllegalOperand";
+ break;
+ case eIllegalAddressingMode:
+ str = "eIllegalAddressingMode";
+ break;
+ case eIllegalTrap:
+ str = "eIllegalTrap";
+ break;
+ case ePrivilegedOpcode:
+ str = "ePrivilegedOpcode";
+ break;
+ case ePrivilegedRegister:
+ str = "ePrivilegedRegister";
+ break;
+ case eCoprocessorError:
+ str = "eCoprocessorError";
+ break;
+ case eInternalStackError:
+ str = "eInternalStackError";
+ break;
+
+ // SIGBUS crash reasons:
+ case eIllegalAlignment:
+ str = "eIllegalAlignment";
+ break;
+ case eIllegalAddress:
+ str = "eIllegalAddress";
+ break;
+ case eHardwareError:
+ str = "eHardwareError";
+ break;
+
+ // SIGFPE crash reasons:
+ case eIntegerDivideByZero:
+ str = "eIntegerDivideByZero";
+ break;
+ case eIntegerOverflow:
+ str = "eIntegerOverflow";
+ break;
+ case eFloatDivideByZero:
+ str = "eFloatDivideByZero";
+ break;
+ case eFloatOverflow:
+ str = "eFloatOverflow";
+ break;
+ case eFloatUnderflow:
+ str = "eFloatUnderflow";
+ break;
+ case eFloatInexactResult:
+ str = "eFloatInexactResult";
+ break;
+ case eFloatInvalidOperation:
+ str = "eFloatInvalidOperation";
+ break;
+ case eFloatSubscriptRange:
+ str = "eFloatSubscriptRange";
+ break;
+ }
+#endif
+
+ return str;
+}
+
+const char *
+ProcessMessage::PrintCrashReason() const
+{
+ return PrintCrashReason(m_crash_reason);
+}
+
+const char *
+ProcessMessage::PrintKind(Kind kind)
+{
+#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+ // Just return the code in asci for integration builds.
+ chcar str[8];
+ sprintf(str, "%d", reason);
+#else
+ const char *str = NULL;
+
+ switch (kind)
+ {
+ case eInvalidMessage:
+ str = "eInvalidMessage";
+ break;
+ case eExitMessage:
+ str = "eExitMessage";
+ break;
+ case eLimboMessage:
+ str = "eLimboMessage";
+ break;
+ case eSignalMessage:
+ str = "eSignalMessage";
+ break;
+ case eSignalDeliveredMessage:
+ str = "eSignalDeliveredMessage";
+ break;
+ case eTraceMessage:
+ str = "eTraceMessage";
+ break;
+ case eBreakpointMessage:
+ str = "eBreakpointMessage";
+ break;
+ case eWatchpointMessage:
+ str = "eWatchpointMessage";
+ break;
+ case eCrashMessage:
+ str = "eCrashMessage";
+ break;
+ case eNewThreadMessage:
+ str = "eNewThreadMessage";
+ break;
+ }
+#endif
+
+ return str;
+}
+
+const char *
+ProcessMessage::PrintKind() const
+{
+ return PrintKind(m_kind);
+}
diff --git a/source/Plugins/Process/POSIX/ProcessMessage.h b/source/Plugins/Process/POSIX/ProcessMessage.h
new file mode 100644
index 0000000..c6c460c
--- /dev/null
+++ b/source/Plugins/Process/POSIX/ProcessMessage.h
@@ -0,0 +1,207 @@
+//===-- ProcessMessage.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessMessage_H_
+#define liblldb_ProcessMessage_H_
+
+#include <cassert>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+class ProcessMessage
+{
+public:
+
+ /// The type of signal this message can correspond to.
+ enum Kind
+ {
+ eInvalidMessage,
+ eExitMessage,
+ eLimboMessage,
+ eSignalMessage,
+ eSignalDeliveredMessage,
+ eTraceMessage,
+ eBreakpointMessage,
+ eWatchpointMessage,
+ eCrashMessage,
+ eNewThreadMessage
+ };
+
+ enum CrashReason
+ {
+ eInvalidCrashReason,
+
+ // SIGSEGV crash reasons.
+ eInvalidAddress,
+ ePrivilegedAddress,
+
+ // SIGILL crash reasons.
+ eIllegalOpcode,
+ eIllegalOperand,
+ eIllegalAddressingMode,
+ eIllegalTrap,
+ ePrivilegedOpcode,
+ ePrivilegedRegister,
+ eCoprocessorError,
+ eInternalStackError,
+
+ // SIGBUS crash reasons,
+ eIllegalAlignment,
+ eIllegalAddress,
+ eHardwareError,
+
+ // SIGFPE crash reasons,
+ eIntegerDivideByZero,
+ eIntegerOverflow,
+ eFloatDivideByZero,
+ eFloatOverflow,
+ eFloatUnderflow,
+ eFloatInexactResult,
+ eFloatInvalidOperation,
+ eFloatSubscriptRange
+ };
+
+ ProcessMessage()
+ : m_tid(LLDB_INVALID_PROCESS_ID),
+ m_kind(eInvalidMessage),
+ m_crash_reason(eInvalidCrashReason),
+ m_status(0),
+ m_addr(0) { }
+
+ Kind GetKind() const { return m_kind; }
+
+ lldb::tid_t GetTID() const { return m_tid; }
+
+ /// Indicates that the thread @p tid is about to exit with status @p status.
+ static ProcessMessage Limbo(lldb::tid_t tid, int status) {
+ return ProcessMessage(tid, eLimboMessage, status);
+ }
+
+ /// Indicates that the thread @p tid had the signal @p signum delivered.
+ static ProcessMessage Signal(lldb::tid_t tid, int signum) {
+ return ProcessMessage(tid, eSignalMessage, signum);
+ }
+
+ /// Indicates that a signal @p signum generated by the debugging process was
+ /// delivered to the thread @p tid.
+ static ProcessMessage SignalDelivered(lldb::tid_t tid, int signum) {
+ return ProcessMessage(tid, eSignalDeliveredMessage, signum);
+ }
+
+ /// Indicates that the thread @p tid encountered a trace point.
+ static ProcessMessage Trace(lldb::tid_t tid) {
+ return ProcessMessage(tid, eTraceMessage);
+ }
+
+ /// Indicates that the thread @p tid encountered a break point.
+ static ProcessMessage Break(lldb::tid_t tid) {
+ return ProcessMessage(tid, eBreakpointMessage);
+ }
+
+ static ProcessMessage Watch(lldb::tid_t tid, lldb::addr_t wp_addr) {
+ return ProcessMessage(tid, eWatchpointMessage, 0, wp_addr);
+ }
+
+ /// Indicates that the thread @p tid crashed.
+ static ProcessMessage Crash(lldb::pid_t pid, CrashReason reason,
+ int signo, lldb::addr_t fault_addr) {
+ ProcessMessage message(pid, eCrashMessage, signo, fault_addr);
+ message.m_crash_reason = reason;
+ return message;
+ }
+
+ /// Indicates that the thread @p child_tid was spawned.
+ static ProcessMessage NewThread(lldb::tid_t parent_tid, lldb::tid_t child_tid) {
+ return ProcessMessage(parent_tid, eNewThreadMessage, child_tid);
+ }
+
+ /// Indicates that the thread @p tid is about to exit with status @p status.
+ static ProcessMessage Exit(lldb::tid_t tid, int status) {
+ return ProcessMessage(tid, eExitMessage, status);
+ }
+
+ int GetExitStatus() const {
+ assert(GetKind() == eExitMessage || GetKind() == eLimboMessage);
+ return m_status;
+ }
+
+ int GetSignal() const {
+ assert(GetKind() == eSignalMessage || GetKind() == eCrashMessage ||
+ GetKind() == eSignalDeliveredMessage);
+ return m_status;
+ }
+
+ int GetStopStatus() const {
+ assert(GetKind() == eSignalMessage);
+ return m_status;
+ }
+
+ CrashReason GetCrashReason() const {
+ assert(GetKind() == eCrashMessage);
+ return m_crash_reason;
+ }
+
+ lldb::addr_t GetFaultAddress() const {
+ assert(GetKind() == eCrashMessage);
+ return m_addr;
+ }
+
+ lldb::addr_t GetHWAddress() const {
+ assert(GetKind() == eWatchpointMessage || GetKind() == eTraceMessage);
+ return m_addr;
+ }
+
+ lldb::tid_t GetChildTID() const {
+ assert(GetKind() == eNewThreadMessage);
+ return m_child_tid;
+ }
+
+ static const char *
+ GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr);
+
+ const char *
+ PrintCrashReason() const;
+
+ static const char *
+ PrintCrashReason(CrashReason reason);
+
+ const char *
+ PrintKind() const;
+
+ static const char *
+ PrintKind(Kind);
+
+private:
+ ProcessMessage(lldb::tid_t tid, Kind kind,
+ int status = 0, lldb::addr_t addr = 0)
+ : m_tid(tid),
+ m_kind(kind),
+ m_crash_reason(eInvalidCrashReason),
+ m_status(status),
+ m_addr(addr),
+ m_child_tid(0) { }
+
+ ProcessMessage(lldb::tid_t tid, Kind kind, lldb::tid_t child_tid)
+ : m_tid(tid),
+ m_kind(kind),
+ m_crash_reason(eInvalidCrashReason),
+ m_status(0),
+ m_addr(0),
+ m_child_tid(child_tid) { }
+
+ lldb::tid_t m_tid;
+ Kind m_kind : 8;
+ CrashReason m_crash_reason : 8;
+ int m_status;
+ lldb::addr_t m_addr;
+ lldb::tid_t m_child_tid;
+};
+
+#endif // #ifndef liblldb_ProcessMessage_H_
diff --git a/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/source/Plugins/Process/POSIX/ProcessPOSIX.cpp
new file mode 100644
index 0000000..f04631d
--- /dev/null
+++ b/source/Plugins/Process/POSIX/ProcessPOSIX.cpp
@@ -0,0 +1,911 @@
+//===-- ProcessPOSIX.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-python.h"
+
+// C Includes
+#include <errno.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/State.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Target.h"
+
+#include "ProcessPOSIX.h"
+#include "ProcessPOSIXLog.h"
+#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
+#include "ProcessMonitor.h"
+#include "POSIXThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//------------------------------------------------------------------------------
+// Static functions.
+#if 0
+Process*
+ProcessPOSIX::CreateInstance(Target& target, Listener &listener)
+{
+ return new ProcessPOSIX(target, listener);
+}
+
+
+void
+ProcessPOSIX::Initialize()
+{
+ static bool g_initialized = false;
+
+ if (!g_initialized)
+ {
+ g_initialized = true;
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ CreateInstance);
+
+ Log::Callbacks log_callbacks = {
+ ProcessPOSIXLog::DisableLog,
+ ProcessPOSIXLog::EnableLog,
+ ProcessPOSIXLog::ListLogCategories
+ };
+
+ Log::RegisterLogChannel (ProcessPOSIX::GetPluginNameStatic(), log_callbacks);
+ }
+}
+#endif
+
+//------------------------------------------------------------------------------
+// Constructors and destructors.
+
+ProcessPOSIX::ProcessPOSIX(Target& target, Listener &listener)
+ : Process(target, listener),
+ m_byte_order(lldb::endian::InlHostByteOrder()),
+ m_monitor(NULL),
+ m_module(NULL),
+ m_message_mutex (Mutex::eMutexTypeRecursive),
+ m_exit_now(false),
+ m_seen_initial_stop()
+{
+ // FIXME: Putting this code in the ctor and saving the byte order in a
+ // member variable is a hack to avoid const qual issues in GetByteOrder.
+ lldb::ModuleSP module = GetTarget().GetExecutableModule();
+ if (module && module->GetObjectFile())
+ m_byte_order = module->GetObjectFile()->GetByteOrder();
+}
+
+ProcessPOSIX::~ProcessPOSIX()
+{
+ delete m_monitor;
+}
+
+//------------------------------------------------------------------------------
+// Process protocol.
+void
+ProcessPOSIX::Finalize()
+{
+ Process::Finalize();
+
+ if (m_monitor)
+ m_monitor->StopMonitor();
+}
+
+bool
+ProcessPOSIX::CanDebug(Target &target, bool plugin_specified_by_name)
+{
+ // For now we are just making sure the file exists for a given module
+ ModuleSP exe_module_sp(target.GetExecutableModule());
+ if (exe_module_sp.get())
+ return exe_module_sp->GetFileSpec().Exists();
+ // If there is no executable module, we return true since we might be preparing to attach.
+ return true;
+}
+
+Error
+ProcessPOSIX::DoAttachToProcessWithID(lldb::pid_t pid)
+{
+ Error error;
+ assert(m_monitor == NULL);
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+ if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
+ log->Printf ("ProcessPOSIX::%s(pid = %" PRIi64 ")", __FUNCTION__, GetID());
+
+ m_monitor = new ProcessMonitor(this, pid, error);
+
+ if (!error.Success())
+ return error;
+
+ PlatformSP platform_sp (m_target.GetPlatform ());
+ assert (platform_sp.get());
+ if (!platform_sp)
+ return error; // FIXME: Detatch?
+
+ // Find out what we can about this process
+ ProcessInstanceInfo process_info;
+ platform_sp->GetProcessInfo (pid, process_info);
+
+ // Resolve the executable module
+ ModuleSP exe_module_sp;
+ FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths());
+ error = platform_sp->ResolveExecutable(process_info.GetExecutableFile(),
+ m_target.GetArchitecture(),
+ exe_module_sp,
+ executable_search_paths.GetSize() ? &executable_search_paths : NULL);
+ if (!error.Success())
+ return error;
+
+ // Fix the target architecture if necessary
+ const ArchSpec &module_arch = exe_module_sp->GetArchitecture();
+ if (module_arch.IsValid() && !m_target.GetArchitecture().IsExactMatch(module_arch))
+ m_target.SetArchitecture(module_arch);
+
+ // Initialize the target module list
+ m_target.SetExecutableModule (exe_module_sp, true);
+
+ SetSTDIOFileDescriptor(m_monitor->GetTerminalFD());
+
+ SetID(pid);
+
+ return error;
+}
+
+Error
+ProcessPOSIX::DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info)
+{
+ return DoAttachToProcessWithID(pid);
+}
+
+Error
+ProcessPOSIX::WillLaunch(Module* module)
+{
+ Error error;
+ return error;
+}
+
+const char *
+ProcessPOSIX::GetFilePath(
+ const lldb_private::ProcessLaunchInfo::FileAction *file_action,
+ const char *default_path)
+{
+ const char *pts_name = "/dev/pts/";
+ const char *path = NULL;
+
+ if (file_action)
+ {
+ if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen)
+ path = file_action->GetPath();
+ // By default the stdio paths passed in will be pseudo-terminal
+ // (/dev/pts). If so, convert to using a different default path
+ // instead to redirect I/O to the debugger console. This should
+ // also handle user overrides to /dev/null or a different file.
+ if (::strncmp(path, pts_name, ::strlen(pts_name)) == 0)
+ path = default_path;
+ }
+
+ return path;
+}
+
+Error
+ProcessPOSIX::DoLaunch (Module *module,
+ const ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ assert(m_monitor == NULL);
+
+ const char* working_dir = launch_info.GetWorkingDirectory();
+ if (working_dir) {
+ FileSpec WorkingDir(working_dir, true);
+ if (!WorkingDir || WorkingDir.GetFileType() != FileSpec::eFileTypeDirectory)
+ {
+ error.SetErrorStringWithFormat("No such file or directory: %s", working_dir);
+ return error;
+ }
+ }
+
+ SetPrivateState(eStateLaunching);
+
+ const lldb_private::ProcessLaunchInfo::FileAction *file_action;
+
+ // Default of NULL will mean to use existing open file descriptors
+ const char *stdin_path = NULL;
+ const char *stdout_path = NULL;
+ const char *stderr_path = NULL;
+
+ file_action = launch_info.GetFileActionForFD (STDIN_FILENO);
+ stdin_path = GetFilePath(file_action, stdin_path);
+
+ file_action = launch_info.GetFileActionForFD (STDOUT_FILENO);
+ stdout_path = GetFilePath(file_action, stdout_path);
+
+ file_action = launch_info.GetFileActionForFD (STDERR_FILENO);
+ stderr_path = GetFilePath(file_action, stderr_path);
+
+ m_monitor = new ProcessMonitor (this,
+ module,
+ launch_info.GetArguments().GetConstArgumentVector(),
+ launch_info.GetEnvironmentEntries().GetConstArgumentVector(),
+ stdin_path,
+ stdout_path,
+ stderr_path,
+ working_dir,
+ error);
+
+ m_module = module;
+
+ if (!error.Success())
+ return error;
+
+ SetSTDIOFileDescriptor(m_monitor->GetTerminalFD());
+
+ SetID(m_monitor->GetPID());
+ return error;
+}
+
+void
+ProcessPOSIX::DidLaunch()
+{
+}
+
+Error
+ProcessPOSIX::DoResume()
+{
+ StateType state = GetPrivateState();
+
+ assert(state == eStateStopped);
+
+ SetPrivateState(eStateRunning);
+
+ bool did_resume = false;
+
+ Mutex::Locker lock(m_thread_list.GetMutex());
+
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i)
+ {
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ did_resume = thread->Resume() || did_resume;
+ }
+ assert(did_resume && "Process resume failed!");
+
+ return Error();
+}
+
+addr_t
+ProcessPOSIX::GetImageInfoAddress()
+{
+ Target *target = &GetTarget();
+ ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress();
+
+ if (addr.IsValid())
+ return addr.GetLoadAddress(target);
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+Error
+ProcessPOSIX::DoHalt(bool &caused_stop)
+{
+ Error error;
+
+ if (IsStopped())
+ {
+ caused_stop = false;
+ }
+ else if (kill(GetID(), SIGSTOP))
+ {
+ caused_stop = false;
+ error.SetErrorToErrno();
+ }
+ else
+ {
+ caused_stop = true;
+ }
+ return error;
+}
+
+Error
+ProcessPOSIX::DoDetach(bool keep_stopped)
+{
+ Error error;
+ if (keep_stopped)
+ {
+ // FIXME: If you want to implement keep_stopped,
+ // this would be the place to do it.
+ error.SetErrorString("Detaching with keep_stopped true is not currently supported on this platform.");
+ return error;
+ }
+
+ Mutex::Locker lock(m_thread_list.GetMutex());
+
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i)
+ {
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ error = m_monitor->Detach(thread->GetID());
+ }
+
+ if (error.Success())
+ SetPrivateState(eStateDetached);
+
+ return error;
+}
+
+Error
+ProcessPOSIX::DoSignal(int signal)
+{
+ Error error;
+
+ if (kill(GetID(), signal))
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Error
+ProcessPOSIX::DoDestroy()
+{
+ Error error;
+
+ if (!HasExited())
+ {
+ // Drive the exit event to completion (do not keep the inferior in
+ // limbo).
+ m_exit_now = true;
+
+ if ((m_monitor == NULL || kill(m_monitor->GetPID(), SIGKILL)) && error.Success())
+ {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ SetPrivateState(eStateExited);
+ }
+
+ return error;
+}
+
+void
+ProcessPOSIX::SendMessage(const ProcessMessage &message)
+{
+ Mutex::Locker lock(m_message_mutex);
+
+ Mutex::Locker thread_lock(m_thread_list.GetMutex());
+
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.FindThreadByID(message.GetTID(), false).get());
+
+ switch (message.GetKind())
+ {
+ case ProcessMessage::eInvalidMessage:
+ return;
+
+ case ProcessMessage::eLimboMessage:
+ assert(thread);
+ thread->SetState(eStateStopped);
+ if (message.GetTID() == GetID())
+ {
+ m_exit_status = message.GetExitStatus();
+ if (m_exit_now)
+ {
+ SetPrivateState(eStateExited);
+ m_monitor->Detach(GetID());
+ }
+ else
+ {
+ StopAllThreads(message.GetTID());
+ SetPrivateState(eStateStopped);
+ }
+ }
+ else
+ {
+ StopAllThreads(message.GetTID());
+ SetPrivateState(eStateStopped);
+ }
+ break;
+
+ case ProcessMessage::eExitMessage:
+ assert(thread);
+ thread->SetState(eStateExited);
+ // FIXME: I'm not sure we need to do this.
+ if (message.GetTID() == GetID())
+ {
+ m_exit_status = message.GetExitStatus();
+ SetExitStatus(m_exit_status, NULL);
+ }
+ else if (!IsAThreadRunning())
+ SetPrivateState(eStateStopped);
+ break;
+
+ case ProcessMessage::eSignalMessage:
+ case ProcessMessage::eSignalDeliveredMessage:
+ if (message.GetSignal() == SIGSTOP &&
+ AddThreadForInitialStopIfNeeded(message.GetTID()))
+ return;
+ // Intentional fall-through
+
+ case ProcessMessage::eBreakpointMessage:
+ case ProcessMessage::eTraceMessage:
+ case ProcessMessage::eWatchpointMessage:
+ case ProcessMessage::eNewThreadMessage:
+ case ProcessMessage::eCrashMessage:
+ assert(thread);
+ thread->SetState(eStateStopped);
+ StopAllThreads(message.GetTID());
+ SetPrivateState(eStateStopped);
+ break;
+ }
+
+ m_message_queue.push(message);
+}
+
+void
+ProcessPOSIX::StopAllThreads(lldb::tid_t stop_tid)
+{
+ // FIXME: Will this work the same way on FreeBSD and Linux?
+}
+
+bool
+ProcessPOSIX::AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid)
+{
+ bool added_to_set = false;
+ ThreadStopSet::iterator it = m_seen_initial_stop.find(stop_tid);
+ if (it == m_seen_initial_stop.end())
+ {
+ m_seen_initial_stop.insert(stop_tid);
+ added_to_set = true;
+ }
+ return added_to_set;
+}
+
+POSIXThread *
+ProcessPOSIX::CreateNewPOSIXThread(lldb_private::Process &process, lldb::tid_t tid)
+{
+ return new POSIXThread(process, tid);
+}
+
+void
+ProcessPOSIX::RefreshStateAfterStop()
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+ if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
+ log->Printf ("ProcessPOSIX::%s(), message_queue size = %d", __FUNCTION__, (int)m_message_queue.size());
+
+ Mutex::Locker lock(m_message_mutex);
+
+ // This method used to only handle one message. Changing it to loop allows
+ // it to handle the case where we hit a breakpoint while handling a different
+ // breakpoint.
+ while (!m_message_queue.empty())
+ {
+ ProcessMessage &message = m_message_queue.front();
+
+ // Resolve the thread this message corresponds to and pass it along.
+ lldb::tid_t tid = message.GetTID();
+ if (log)
+ log->Printf ("ProcessPOSIX::%s(), message_queue size = %d, pid = %" PRIi64, __FUNCTION__, (int)m_message_queue.size(), tid);
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ GetThreadList().FindThreadByID(tid, false).get());
+
+ if (message.GetKind() == ProcessMessage::eNewThreadMessage)
+ {
+ if (log)
+ log->Printf ("ProcessPOSIX::%s() adding thread, tid = %" PRIi64, __FUNCTION__, message.GetChildTID());
+ lldb::tid_t child_tid = message.GetChildTID();
+ ThreadSP thread_sp;
+ thread_sp.reset(CreateNewPOSIXThread(*this, child_tid));
+
+ Mutex::Locker lock(m_thread_list.GetMutex());
+
+ m_thread_list.AddThread(thread_sp);
+ }
+
+ m_thread_list.RefreshStateAfterStop();
+
+ if (thread)
+ thread->Notify(message);
+
+ if (message.GetKind() == ProcessMessage::eExitMessage)
+ {
+ // FIXME: We should tell the user about this, but the limbo message is probably better for that.
+ if (log)
+ log->Printf ("ProcessPOSIX::%s() removing thread, tid = %" PRIi64, __FUNCTION__, tid);
+
+ Mutex::Locker lock(m_thread_list.GetMutex());
+
+ ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false);
+ thread_sp.reset();
+ m_seen_initial_stop.erase(tid);
+ }
+
+ m_message_queue.pop();
+ }
+}
+
+bool
+ProcessPOSIX::IsAlive()
+{
+ StateType state = GetPrivateState();
+ return state != eStateDetached
+ && state != eStateExited
+ && state != eStateInvalid
+ && state != eStateUnloaded;
+}
+
+size_t
+ProcessPOSIX::DoReadMemory(addr_t vm_addr,
+ void *buf, size_t size, Error &error)
+{
+ assert(m_monitor);
+ return m_monitor->ReadMemory(vm_addr, buf, size, error);
+}
+
+size_t
+ProcessPOSIX::DoWriteMemory(addr_t vm_addr, const void *buf, size_t size,
+ Error &error)
+{
+ assert(m_monitor);
+ return m_monitor->WriteMemory(vm_addr, buf, size, error);
+}
+
+addr_t
+ProcessPOSIX::DoAllocateMemory(size_t size, uint32_t permissions,
+ Error &error)
+{
+ addr_t allocated_addr = LLDB_INVALID_ADDRESS;
+
+ unsigned prot = 0;
+ if (permissions & lldb::ePermissionsReadable)
+ prot |= eMmapProtRead;
+ if (permissions & lldb::ePermissionsWritable)
+ prot |= eMmapProtWrite;
+ if (permissions & lldb::ePermissionsExecutable)
+ prot |= eMmapProtExec;
+
+ if (InferiorCallMmap(this, allocated_addr, 0, size, prot,
+ eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) {
+ m_addr_to_mmap_size[allocated_addr] = size;
+ error.Clear();
+ } else {
+ allocated_addr = LLDB_INVALID_ADDRESS;
+ error.SetErrorStringWithFormat("unable to allocate %zu bytes of memory with permissions %s", size, GetPermissionsAsCString (permissions));
+ }
+
+ return allocated_addr;
+}
+
+Error
+ProcessPOSIX::DoDeallocateMemory(lldb::addr_t addr)
+{
+ Error error;
+ MMapMap::iterator pos = m_addr_to_mmap_size.find(addr);
+ if (pos != m_addr_to_mmap_size.end() &&
+ InferiorCallMunmap(this, addr, pos->second))
+ m_addr_to_mmap_size.erase (pos);
+ else
+ error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr);
+
+ return error;
+}
+
+addr_t
+ProcessPOSIX::ResolveIndirectFunction(const Address *address, Error &error)
+{
+ addr_t function_addr = LLDB_INVALID_ADDRESS;
+ if (address == NULL) {
+ error.SetErrorStringWithFormat("unable to determine direct function call for NULL address");
+ } else if (!InferiorCall(this, address, function_addr)) {
+ function_addr = LLDB_INVALID_ADDRESS;
+ error.SetErrorStringWithFormat("unable to determine direct function call for indirect function %s",
+ address->CalculateSymbolContextSymbol()->GetName().AsCString());
+ }
+ return function_addr;
+}
+
+size_t
+ProcessPOSIX::GetSoftwareBreakpointTrapOpcode(BreakpointSite* bp_site)
+{
+ static const uint8_t g_i386_opcode[] = { 0xCC };
+
+ ArchSpec arch = GetTarget().GetArchitecture();
+ const uint8_t *opcode = NULL;
+ size_t opcode_size = 0;
+
+ switch (arch.GetCore())
+ {
+ default:
+ assert(false && "CPU type not supported!");
+ break;
+
+ case ArchSpec::eCore_x86_32_i386:
+ case ArchSpec::eCore_x86_64_x86_64:
+ opcode = g_i386_opcode;
+ opcode_size = sizeof(g_i386_opcode);
+ break;
+ }
+
+ bp_site->SetTrapOpcode(opcode, opcode_size);
+ return opcode_size;
+}
+
+Error
+ProcessPOSIX::EnableBreakpointSite(BreakpointSite *bp_site)
+{
+ return EnableSoftwareBreakpoint(bp_site);
+}
+
+Error
+ProcessPOSIX::DisableBreakpointSite(BreakpointSite *bp_site)
+{
+ return DisableSoftwareBreakpoint(bp_site);
+}
+
+Error
+ProcessPOSIX::EnableWatchpoint(Watchpoint *wp, bool notify)
+{
+ Error error;
+ if (wp)
+ {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("ProcessPOSIX::EnableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (wp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessPOSIX::EnableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.",
+ watchID, (uint64_t)addr);
+ return error;
+ }
+
+ // Try to find a vacant watchpoint slot in the inferiors' main thread
+ uint32_t wp_hw_index = LLDB_INVALID_INDEX32;
+ Mutex::Locker lock(m_thread_list.GetMutex());
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(0, false).get());
+
+ if (thread)
+ wp_hw_index = thread->FindVacantWatchpointIndex();
+
+ if (wp_hw_index == LLDB_INVALID_INDEX32)
+ {
+ error.SetErrorString("Setting hardware watchpoint failed.");
+ }
+ else
+ {
+ wp->SetHardwareIndex(wp_hw_index);
+ bool wp_enabled = true;
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i)
+ {
+ thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ if (thread)
+ wp_enabled &= thread->EnableHardwareWatchpoint(wp);
+ else
+ wp_enabled = false;
+ }
+ if (wp_enabled)
+ {
+ wp->SetEnabled(true, notify);
+ return error;
+ }
+ else
+ {
+ // Watchpoint enabling failed on at least one
+ // of the threads so roll back all of them
+ DisableWatchpoint(wp, false);
+ error.SetErrorString("Setting hardware watchpoint failed");
+ }
+ }
+ }
+ else
+ error.SetErrorString("Watchpoint argument was NULL.");
+ return error;
+}
+
+Error
+ProcessPOSIX::DisableWatchpoint(Watchpoint *wp, bool notify)
+{
+ Error error;
+ if (wp)
+ {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("ProcessPOSIX::DisableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (!wp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessPOSIX::DisableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already disabled.",
+ watchID, (uint64_t)addr);
+ // This is needed (for now) to keep watchpoints disabled correctly
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+
+ if (wp->IsHardware())
+ {
+ bool wp_disabled = true;
+ Mutex::Locker lock(m_thread_list.GetMutex());
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i)
+ {
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ if (thread)
+ wp_disabled &= thread->DisableHardwareWatchpoint(wp);
+ else
+ wp_disabled = false;
+ }
+ if (wp_disabled)
+ {
+ wp->SetHardwareIndex(LLDB_INVALID_INDEX32);
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+ else
+ error.SetErrorString("Disabling hardware watchpoint failed");
+ }
+ }
+ else
+ error.SetErrorString("Watchpoint argument was NULL.");
+ return error;
+}
+
+Error
+ProcessPOSIX::GetWatchpointSupportInfo(uint32_t &num)
+{
+ Error error;
+ Mutex::Locker lock(m_thread_list.GetMutex());
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(0, false).get());
+ if (thread)
+ num = thread->NumSupportedHardwareWatchpoints();
+ else
+ error.SetErrorString("Process does not exist.");
+ return error;
+}
+
+Error
+ProcessPOSIX::GetWatchpointSupportInfo(uint32_t &num, bool &after)
+{
+ Error error = GetWatchpointSupportInfo(num);
+ // Watchpoints trigger and halt the inferior after
+ // the corresponding instruction has been executed.
+ after = true;
+ return error;
+}
+
+uint32_t
+ProcessPOSIX::UpdateThreadListIfNeeded()
+{
+ Mutex::Locker lock(m_thread_list.GetMutex());
+ // Do not allow recursive updates.
+ return m_thread_list.GetSize(false);
+}
+
+bool
+ProcessPOSIX::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+ if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
+ log->Printf ("ProcessPOSIX::%s() (pid = %" PRIi64 ")", __FUNCTION__, GetID());
+
+ // Update the process thread list with this new thread.
+ // FIXME: We should be using tid, not pid.
+ assert(m_monitor);
+ ThreadSP thread_sp (old_thread_list.FindThreadByID (GetID(), false));
+ if (!thread_sp) {
+ thread_sp.reset(CreateNewPOSIXThread(*this, GetID()));
+ }
+
+ if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
+ log->Printf ("ProcessPOSIX::%s() updated pid = %" PRIi64, __FUNCTION__, GetID());
+ new_thread_list.AddThread(thread_sp);
+
+ return new_thread_list.GetSize(false) > 0;
+}
+
+ByteOrder
+ProcessPOSIX::GetByteOrder() const
+{
+ // FIXME: We should be able to extract this value directly. See comment in
+ // ProcessPOSIX().
+ return m_byte_order;
+}
+
+size_t
+ProcessPOSIX::PutSTDIN(const char *buf, size_t len, Error &error)
+{
+ ssize_t status;
+ if ((status = write(m_monitor->GetTerminalFD(), buf, len)) < 0)
+ {
+ error.SetErrorToErrno();
+ return 0;
+ }
+ return status;
+}
+
+UnixSignals &
+ProcessPOSIX::GetUnixSignals()
+{
+ return m_signals;
+}
+
+//------------------------------------------------------------------------------
+// Utility functions.
+
+bool
+ProcessPOSIX::HasExited()
+{
+ switch (GetPrivateState())
+ {
+ default:
+ break;
+
+ case eStateDetached:
+ case eStateExited:
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ProcessPOSIX::IsStopped()
+{
+ switch (GetPrivateState())
+ {
+ default:
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ProcessPOSIX::IsAThreadRunning()
+{
+ bool is_running = false;
+ Mutex::Locker lock(m_thread_list.GetMutex());
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i)
+ {
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ StateType thread_state = thread->GetState();
+ if (thread_state == eStateRunning || thread_state == eStateStepping)
+ {
+ is_running = true;
+ break;
+ }
+ }
+ return is_running;
+}
diff --git a/source/Plugins/Process/POSIX/ProcessPOSIX.h b/source/Plugins/Process/POSIX/ProcessPOSIX.h
new file mode 100644
index 0000000..48b19ba
--- /dev/null
+++ b/source/Plugins/Process/POSIX/ProcessPOSIX.h
@@ -0,0 +1,211 @@
+//===-- ProcessPOSIX.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessPOSIX_H_
+#define liblldb_ProcessPOSIX_H_
+
+// C Includes
+
+// C++ Includes
+#include <queue>
+#include <set>
+
+// Other libraries and framework includes
+#include "lldb/Target/Process.h"
+#include "lldb/Target/UnixSignals.h"
+#include "ProcessMessage.h"
+
+class ProcessMonitor;
+class POSIXThread;
+
+class ProcessPOSIX :
+ public lldb_private::Process
+{
+public:
+
+ //------------------------------------------------------------------
+ // Constructors and destructors
+ //------------------------------------------------------------------
+ ProcessPOSIX(lldb_private::Target& target,
+ lldb_private::Listener &listener);
+
+ virtual
+ ~ProcessPOSIX();
+
+ //------------------------------------------------------------------
+ // Process protocol.
+ //------------------------------------------------------------------
+ virtual void
+ Finalize();
+
+ virtual bool
+ CanDebug(lldb_private::Target &target, bool plugin_specified_by_name);
+
+ virtual lldb_private::Error
+ WillLaunch(lldb_private::Module *module);
+
+ virtual lldb_private::Error
+ DoAttachToProcessWithID(lldb::pid_t pid);
+
+ virtual lldb_private::Error
+ DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info);
+
+ virtual lldb_private::Error
+ DoLaunch (lldb_private::Module *exe_module,
+ const lldb_private::ProcessLaunchInfo &launch_info);
+
+ virtual void
+ DidLaunch();
+
+ virtual lldb_private::Error
+ DoResume();
+
+ virtual lldb_private::Error
+ DoHalt(bool &caused_stop);
+
+ virtual lldb_private::Error
+ DoDetach(bool keep_stopped);
+
+ virtual lldb_private::Error
+ DoSignal(int signal);
+
+ virtual lldb_private::Error
+ DoDestroy();
+
+ virtual void
+ RefreshStateAfterStop();
+
+ virtual bool
+ IsAlive();
+
+ virtual size_t
+ DoReadMemory(lldb::addr_t vm_addr,
+ void *buf,
+ size_t size,
+ lldb_private::Error &error);
+
+ virtual size_t
+ DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
+ lldb_private::Error &error);
+
+ virtual lldb::addr_t
+ DoAllocateMemory(size_t size, uint32_t permissions,
+ lldb_private::Error &error);
+
+ virtual lldb_private::Error
+ DoDeallocateMemory(lldb::addr_t ptr);
+
+ virtual lldb::addr_t
+ ResolveIndirectFunction(const lldb_private::Address *address, lldb_private::Error &error);
+
+ virtual size_t
+ GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site);
+
+ virtual lldb_private::Error
+ EnableBreakpointSite(lldb_private::BreakpointSite *bp_site);
+
+ virtual lldb_private::Error
+ DisableBreakpointSite(lldb_private::BreakpointSite *bp_site);
+
+ virtual lldb_private::Error
+ EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true);
+
+ virtual lldb_private::Error
+ DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true);
+
+ virtual lldb_private::Error
+ GetWatchpointSupportInfo(uint32_t &num);
+
+ virtual lldb_private::Error
+ GetWatchpointSupportInfo(uint32_t &num, bool &after);
+
+ virtual uint32_t
+ UpdateThreadListIfNeeded();
+
+ virtual bool
+ UpdateThreadList(lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &new_thread_list) = 0;
+
+ virtual lldb::ByteOrder
+ GetByteOrder() const;
+
+ virtual lldb::addr_t
+ GetImageInfoAddress();
+
+ virtual size_t
+ PutSTDIN(const char *buf, size_t len, lldb_private::Error &error);
+
+ //--------------------------------------------------------------------------
+ // ProcessPOSIX internal API.
+
+ /// Registers the given message with this process.
+ void SendMessage(const ProcessMessage &message);
+
+ ProcessMonitor &
+ GetMonitor() { assert(m_monitor); return *m_monitor; }
+
+ lldb_private::UnixSignals &
+ GetUnixSignals();
+
+ const char *
+ GetFilePath(const lldb_private::ProcessLaunchInfo::FileAction *file_action,
+ const char *default_path);
+
+ /// Stops all threads in the process.
+ /// The \p stop_tid parameter indicates the thread which initiated the stop.
+ virtual void
+ StopAllThreads(lldb::tid_t stop_tid);
+
+ /// Adds the thread to the list of threads for which we have received the initial stopping signal.
+ /// The \p stop_tid paramter indicates the thread which the stop happened for.
+ bool
+ AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid);
+
+ virtual POSIXThread *
+ CreateNewPOSIXThread(lldb_private::Process &process, lldb::tid_t tid);
+
+protected:
+ /// Target byte order.
+ lldb::ByteOrder m_byte_order;
+
+ /// Process monitor;
+ ProcessMonitor *m_monitor;
+
+ /// The module we are executing.
+ lldb_private::Module *m_module;
+
+ /// Message queue notifying this instance of inferior process state changes.
+ lldb_private::Mutex m_message_mutex;
+ std::queue<ProcessMessage> m_message_queue;
+
+ /// Drive any exit events to completion.
+ bool m_exit_now;
+
+ /// OS-specific signal set.
+ lldb_private::UnixSignals m_signals;
+
+ /// Returns true if the process has exited.
+ bool HasExited();
+
+ /// Returns true if the process is stopped.
+ bool IsStopped();
+
+ /// Returns true if at least one running is currently running
+ bool IsAThreadRunning();
+
+ typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
+ MMapMap m_addr_to_mmap_size;
+
+ typedef std::set<lldb::tid_t> ThreadStopSet;
+ /// Every thread begins with a stop signal. This keeps track
+ /// of the threads for which we have received the stop signal.
+ ThreadStopSet m_seen_initial_stop;
+};
+
+#endif // liblldb_MacOSXProcess_H_
diff --git a/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp b/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp
new file mode 100644
index 0000000..624ca87
--- /dev/null
+++ b/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp
@@ -0,0 +1,193 @@
+//===-- ProcessPOSIXLog.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessPOSIXLog.h"
+
+#include "lldb/Interpreter/Args.h"
+#include "lldb/Core/StreamFile.h"
+
+#include "ProcessPOSIX.h"
+#include "ProcessPOSIXLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+// We want to avoid global constructors where code needs to be run so here we
+// control access to our static g_log_sp by hiding it in a singleton function
+// that will construct the static g_log_sp the first time this function is
+// called.
+static bool g_log_enabled = false;
+static Log * g_log = NULL;
+static Log *
+GetLog ()
+{
+ if (!g_log_enabled)
+ return NULL;
+ return g_log;
+}
+
+
+Log *
+ProcessPOSIXLog::GetLogIfAllCategoriesSet (uint32_t mask)
+{
+ Log *log(GetLog ());
+ if (log && mask)
+ {
+ uint32_t log_mask = log->GetMask().Get();
+ if ((log_mask & mask) != mask)
+ return NULL;
+ }
+ return log;
+}
+
+static uint32_t
+GetFlagBits (const char *arg)
+{
+ if (::strcasecmp (arg, "all") == 0 ) return POSIX_LOG_ALL;
+ else if (::strcasecmp (arg, "async") == 0 ) return POSIX_LOG_ASYNC;
+ else if (::strncasecmp (arg, "break", 5) == 0 ) return POSIX_LOG_BREAKPOINTS;
+ else if (::strncasecmp (arg, "comm", 4) == 0 ) return POSIX_LOG_COMM;
+ else if (::strcasecmp (arg, "default") == 0 ) return POSIX_LOG_DEFAULT;
+ else if (::strcasecmp (arg, "packets") == 0 ) return POSIX_LOG_PACKETS;
+ else if (::strcasecmp (arg, "memory") == 0 ) return POSIX_LOG_MEMORY;
+ else if (::strcasecmp (arg, "data-short") == 0 ) return POSIX_LOG_MEMORY_DATA_SHORT;
+ else if (::strcasecmp (arg, "data-long") == 0 ) return POSIX_LOG_MEMORY_DATA_LONG;
+ else if (::strcasecmp (arg, "process") == 0 ) return POSIX_LOG_PROCESS;
+ else if (::strcasecmp (arg, "ptrace") == 0 ) return POSIX_LOG_PTRACE;
+ else if (::strcasecmp (arg, "registers") == 0 ) return POSIX_LOG_REGISTERS;
+ else if (::strcasecmp (arg, "step") == 0 ) return POSIX_LOG_STEP;
+ else if (::strcasecmp (arg, "thread") == 0 ) return POSIX_LOG_THREAD;
+ else if (::strcasecmp (arg, "verbose") == 0 ) return POSIX_LOG_VERBOSE;
+ else if (::strncasecmp (arg, "watch", 5) == 0 ) return POSIX_LOG_WATCHPOINTS;
+ return 0;
+}
+
+void
+ProcessPOSIXLog::DisableLog (const char **args, Stream *feedback_strm)
+{
+ Log *log (GetLog ());
+ if (log)
+ {
+ uint32_t flag_bits = 0;
+
+ flag_bits = log->GetMask().Get();
+ for (; args[0]; args++)
+ {
+ const char *arg = args[0];
+ uint32_t bits = GetFlagBits(arg);
+
+ if (bits)
+ {
+ flag_bits &= ~bits;
+ }
+ else
+ {
+ feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
+ ListLogCategories (feedback_strm);
+ }
+ }
+
+ log->GetMask().Reset (flag_bits);
+ if (flag_bits == 0)
+ g_log_enabled = false;
+ }
+
+ return;
+}
+
+Log *
+ProcessPOSIXLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const char **args, Stream *feedback_strm)
+{
+ // Try see if there already is a log - that way we can reuse its settings.
+ // We could reuse the log in toto, but we don't know that the stream is the same.
+ uint32_t flag_bits = 0;
+ if (g_log)
+ flag_bits = g_log->GetMask().Get();
+
+ // Now make a new log with this stream if one was provided
+ if (log_stream_sp)
+ {
+ if (g_log)
+ g_log->SetStream(log_stream_sp);
+ else
+ g_log = new Log(log_stream_sp);
+ }
+
+ if (g_log)
+ {
+ bool got_unknown_category = false;
+ for (; args[0]; args++)
+ {
+ const char *arg = args[0];
+ uint32_t bits = GetFlagBits(arg);
+
+ if (bits)
+ {
+ flag_bits |= bits;
+ }
+ else
+ {
+ feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
+ if (got_unknown_category == false)
+ {
+ got_unknown_category = true;
+ ListLogCategories (feedback_strm);
+ }
+ }
+ }
+ if (flag_bits == 0)
+ flag_bits = POSIX_LOG_DEFAULT;
+ g_log->GetMask().Reset(flag_bits);
+ g_log->GetOptions().Reset(log_options);
+ g_log_enabled = true;
+ }
+ return g_log;
+}
+
+void
+ProcessPOSIXLog::ListLogCategories (Stream *strm)
+{
+ strm->Printf ("Logging categories for '%s':\n"
+ " all - turn on all available logging categories\n"
+ " async - log asynchronous activity\n"
+ " break - log breakpoints\n"
+ " communication - log communication activity\n"
+ " default - enable the default set of logging categories for liblldb\n"
+ " packets - log gdb remote packets\n"
+ " memory - log memory reads and writes\n"
+ " data-short - log memory bytes for memory reads and writes for short transactions only\n"
+ " data-long - log memory bytes for memory reads and writes for all transactions\n"
+ " process - log process events and activities\n"
+#ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+ " ptrace - log all calls to ptrace\n"
+#endif
+ " registers - log register read/writes\n"
+ " thread - log thread events and activities\n"
+ " step - log step related activities\n"
+ " verbose - enable verbose logging\n"
+ " watch - log watchpoint related activities\n", ProcessPOSIXLog::m_pluginname);
+}
+
+
+void
+ProcessPOSIXLog::LogIf (uint32_t mask, const char *format, ...)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (mask));
+ if (log)
+ {
+ va_list args;
+ va_start (args, format);
+ log->VAPrintf (format, args);
+ va_end (args);
+ }
+}
+
+int ProcessPOSIXLog::m_nestinglevel;
+const char *ProcessPOSIXLog::m_pluginname = "";
diff --git a/source/Plugins/Process/POSIX/ProcessPOSIXLog.h b/source/Plugins/Process/POSIX/ProcessPOSIXLog.h
new file mode 100644
index 0000000..a1e2e37
--- /dev/null
+++ b/source/Plugins/Process/POSIX/ProcessPOSIXLog.h
@@ -0,0 +1,111 @@
+//===-- ProcessPOSIXLog.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessPOSIXLog_h_
+#define liblldb_ProcessPOSIXLog_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+
+// Project includes
+#include "lldb/Core/Log.h"
+
+#define POSIX_LOG_VERBOSE (1u << 0)
+#define POSIX_LOG_PROCESS (1u << 1)
+#define POSIX_LOG_THREAD (1u << 2)
+#define POSIX_LOG_PACKETS (1u << 3)
+#define POSIX_LOG_MEMORY (1u << 4) // Log memory reads/writes calls
+#define POSIX_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes
+#define POSIX_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes
+#define POSIX_LOG_BREAKPOINTS (1u << 7)
+#define POSIX_LOG_WATCHPOINTS (1u << 8)
+#define POSIX_LOG_STEP (1u << 9)
+#define POSIX_LOG_COMM (1u << 10)
+#define POSIX_LOG_ASYNC (1u << 11)
+#define POSIX_LOG_PTRACE (1u << 12)
+#define POSIX_LOG_REGISTERS (1u << 13)
+#define POSIX_LOG_ALL (UINT32_MAX)
+#define POSIX_LOG_DEFAULT POSIX_LOG_PACKETS
+
+// The size which determines "short memory reads/writes".
+#define POSIX_LOG_MEMORY_SHORT_BYTES (4 * sizeof(ptrdiff_t))
+
+class ProcessPOSIXLog
+{
+ static int m_nestinglevel;
+ static const char *m_pluginname;
+
+public:
+ static void
+ RegisterPluginName(const char *pluginName)
+ {
+ m_pluginname = pluginName;
+ }
+
+ static void
+ RegisterPluginName(lldb_private::ConstString pluginName)
+ {
+ m_pluginname = pluginName.GetCString();
+ }
+
+ static lldb_private::Log *
+ GetLogIfAllCategoriesSet(uint32_t mask = 0);
+
+ static void
+ DisableLog (const char **args, lldb_private::Stream *feedback_strm);
+
+ static lldb_private::Log *
+ EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options,
+ const char **args, lldb_private::Stream *feedback_strm);
+
+ static void
+ ListLogCategories (lldb_private::Stream *strm);
+
+ static void
+ LogIf (uint32_t mask, const char *format, ...);
+
+ // The following functions can be used to enable the client to limit
+ // logging to only the top level function calls. This is useful for
+ // recursive functions. FIXME: not thread safe!
+ // Example:
+ // void NestingFunc() {
+ // LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_ALL));
+ // if (log)
+ // {
+ // ProcessPOSIXLog::IncNestLevel();
+ // if (ProcessPOSIXLog::AtTopNestLevel())
+ // log->Print(msg);
+ // }
+ // NestingFunc();
+ // if (log)
+ // ProcessPOSIXLog::DecNestLevel();
+ // }
+
+ static bool
+ AtTopNestLevel()
+ {
+ return m_nestinglevel == 1;
+ }
+
+ static void
+ IncNestLevel()
+ {
+ ++m_nestinglevel;
+ }
+
+ static void
+ DecNestLevel()
+ {
+ --m_nestinglevel;
+ assert(m_nestinglevel >= 0);
+ }
+};
+
+#endif // liblldb_ProcessPOSIXLog_h_
diff --git a/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp b/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp
new file mode 100644
index 0000000..0fb9dc1
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp
@@ -0,0 +1,136 @@
+//===-- RegisterContextFreeBSD_x86_64.h ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextFreeBSD_x86_64.h"
+#include <vector>
+
+using namespace lldb_private;
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) \
+ (offsetof(GPR, regname))
+
+// Update the FreeBSD specific information (offset and size).
+#define UPDATE_GPR_INFO(reg) \
+do { \
+ GetRegisterContext()[gpr_##reg].byte_size = sizeof(GPR::reg); \
+ GetRegisterContext()[gpr_##reg].byte_offset = GPR_OFFSET(reg); \
+} while(false);
+
+#define UPDATE_I386_GPR_INFO(i386_reg, reg) \
+do { \
+ GetRegisterContext()[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \
+} while(false);
+
+typedef struct _GPR
+{
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t rdx;
+ uint64_t rcx;
+ uint64_t rax;
+ uint32_t trapno;
+ uint16_t fs;
+ uint16_t gs;
+ uint32_t err;
+ uint16_t es;
+ uint16_t ds;
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t rflags;
+ uint64_t rsp;
+ uint64_t ss;
+} GPR;
+
+// Use a singleton function to avoid global constructors in shared libraries.
+static std::vector<RegisterInfo> & GetRegisterContext () {
+ static std::vector<RegisterInfo> g_register_infos;
+ return g_register_infos;
+}
+
+
+RegisterContextFreeBSD_x86_64::RegisterContextFreeBSD_x86_64(Thread &thread, uint32_t concrete_frame_idx):
+ RegisterContext_x86_64(thread, concrete_frame_idx)
+{
+}
+
+size_t
+RegisterContextFreeBSD_x86_64::GetGPRSize()
+{
+ return sizeof(GPR);
+}
+
+const RegisterInfo *
+RegisterContextFreeBSD_x86_64::GetRegisterInfo()
+{
+ // Allocate RegisterInfo only once
+ if (GetRegisterContext().empty())
+ {
+ // Copy the register information from base class
+ const RegisterInfo *base_info = RegisterContext_x86_64::GetRegisterInfo();
+ if (base_info)
+ {
+ GetRegisterContext().insert(GetRegisterContext().end(), &base_info[0], &base_info[k_num_registers]);
+ // Update the FreeBSD specific register information (offset and size).
+ UpdateRegisterInfo();
+ }
+ }
+ return &GetRegisterContext()[0];
+}
+
+void
+RegisterContextFreeBSD_x86_64::UpdateRegisterInfo()
+{
+ UPDATE_GPR_INFO(rax);
+ UPDATE_GPR_INFO(rbx);
+ UPDATE_GPR_INFO(rcx);
+ UPDATE_GPR_INFO(rdx);
+ UPDATE_GPR_INFO(rdi);
+ UPDATE_GPR_INFO(rsi);
+ UPDATE_GPR_INFO(rbp);
+ UPDATE_GPR_INFO(rsp);
+ UPDATE_GPR_INFO(r8);
+ UPDATE_GPR_INFO(r9);
+ UPDATE_GPR_INFO(r10);
+ UPDATE_GPR_INFO(r11);
+ UPDATE_GPR_INFO(r12);
+ UPDATE_GPR_INFO(r13);
+ UPDATE_GPR_INFO(r14);
+ UPDATE_GPR_INFO(r15);
+ UPDATE_GPR_INFO(rip);
+ UPDATE_GPR_INFO(rflags);
+ UPDATE_GPR_INFO(cs);
+ UPDATE_GPR_INFO(fs);
+ UPDATE_GPR_INFO(gs);
+ UPDATE_GPR_INFO(ss);
+ UPDATE_GPR_INFO(ds);
+ UPDATE_GPR_INFO(es);
+
+ UPDATE_I386_GPR_INFO(eax, rax);
+ UPDATE_I386_GPR_INFO(ebx, rbx);
+ UPDATE_I386_GPR_INFO(ecx, rcx);
+ UPDATE_I386_GPR_INFO(edx, rdx);
+ UPDATE_I386_GPR_INFO(edi, rdi);
+ UPDATE_I386_GPR_INFO(esi, rsi);
+ UPDATE_I386_GPR_INFO(ebp, rbp);
+ UPDATE_I386_GPR_INFO(esp, rsp);
+ UPDATE_I386_GPR_INFO(eip, rip);
+ UPDATE_I386_GPR_INFO(eflags, rflags);
+}
+
diff --git a/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h b/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h
new file mode 100644
index 0000000..ffff40a
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h
@@ -0,0 +1,32 @@
+//===-- RegisterContextFreeBSD_x86_64.h ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextFreeBSD_x86_64_H_
+#define liblldb_RegisterContextFreeBSD_x86_64_H_
+
+#include "Plugins/Process/POSIX/RegisterContext_x86_64.h"
+
+class RegisterContextFreeBSD_x86_64:
+ public RegisterContext_x86_64
+{
+public:
+ RegisterContextFreeBSD_x86_64(lldb_private::Thread &thread, uint32_t concrete_frame_idx);
+
+ size_t
+ GetGPRSize();
+
+protected:
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfo();
+
+ virtual void
+ UpdateRegisterInfo();
+};
+
+#endif
diff --git a/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp b/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp
new file mode 100644
index 0000000..c1aea2a
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp
@@ -0,0 +1,180 @@
+//===-- RegisterContextLinux_x86_64.h --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "llvm/Support/Compiler.h"
+#include "RegisterContextLinux_x86_64.h"
+#include <vector>
+
+using namespace lldb_private;
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) \
+ (offsetof(GPR, regname))
+
+// Update the Linux specific information (offset and size).
+#define UPDATE_GPR_INFO(reg) \
+do { \
+ GetRegisterContext()[gpr_##reg].byte_size = sizeof(GPR::reg); \
+ GetRegisterContext()[gpr_##reg].byte_offset = GPR_OFFSET(reg); \
+} while(false);
+
+#define UPDATE_I386_GPR_INFO(i386_reg, reg) \
+do { \
+ GetRegisterContext()[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \
+} while(false);
+
+#define DR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, u_debugreg[reg_index]))
+
+#define UPDATE_DR_INFO(reg_index) \
+do { \
+ GetRegisterContext()[dr##reg_index].byte_size = sizeof(UserArea::u_debugreg[0]); \
+ GetRegisterContext()[dr##reg_index].byte_offset = DR_OFFSET(reg_index); \
+} while(false);
+
+typedef struct _GPR
+{
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t orig_ax;
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t rflags;
+ uint64_t rsp;
+ uint64_t ss;
+ uint64_t fs_base;
+ uint64_t gs_base;
+ uint64_t ds;
+ uint64_t es;
+ uint64_t fs;
+ uint64_t gs;
+} GPR;
+
+typedef RegisterContext_x86_64::FXSAVE FXSAVE;
+
+struct UserArea
+{
+ GPR gpr; // General purpose registers.
+ int32_t fpvalid; // True if FPU is being used.
+ int32_t pad0;
+ FXSAVE i387; // General purpose floating point registers (see FPR for extended register sets).
+ uint64_t tsize; // Text segment size.
+ uint64_t dsize; // Data segment size.
+ uint64_t ssize; // Stack segment size.
+ uint64_t start_code; // VM address of text.
+ uint64_t start_stack; // VM address of stack bottom (top in rsp).
+ int64_t signal; // Signal causing core dump.
+ int32_t reserved; // Unused.
+ int32_t pad1;
+ uint64_t ar0; // Location of GPR's.
+ FXSAVE* fpstate; // Location of FPR's.
+ uint64_t magic; // Identifier for core dumps.
+ char u_comm[32]; // Command causing core dump.
+ uint64_t u_debugreg[8]; // Debug registers (DR0 - DR7).
+ uint64_t error_code; // CPU error code.
+ uint64_t fault_address; // Control register CR3.
+};
+
+// Use a singleton function to avoid global constructors in shared libraries.
+static std::vector<RegisterInfo> & GetRegisterContext () {
+ static std::vector<RegisterInfo> g_register_infos;
+ return g_register_infos;
+}
+
+RegisterContextLinux_x86_64::RegisterContextLinux_x86_64(Thread &thread, uint32_t concrete_frame_idx):
+ RegisterContext_x86_64(thread, concrete_frame_idx)
+{
+}
+
+size_t
+RegisterContextLinux_x86_64::GetGPRSize()
+{
+ return sizeof(GPR);
+}
+
+const RegisterInfo *
+RegisterContextLinux_x86_64::GetRegisterInfo()
+{
+ // Allocate RegisterInfo only once
+ if (GetRegisterContext().empty())
+ {
+ // Copy the register information from base class
+ const RegisterInfo *base_info = RegisterContext_x86_64::GetRegisterInfo();
+ if (base_info)
+ {
+ GetRegisterContext().insert(GetRegisterContext().end(), &base_info[0], &base_info[k_num_registers]);
+ // Update the Linux specific register information (offset and size).
+ UpdateRegisterInfo();
+ }
+ }
+ return &GetRegisterContext()[0];
+}
+
+void
+RegisterContextLinux_x86_64::UpdateRegisterInfo()
+{
+ UPDATE_GPR_INFO(rax);
+ UPDATE_GPR_INFO(rbx);
+ UPDATE_GPR_INFO(rcx);
+ UPDATE_GPR_INFO(rdx);
+ UPDATE_GPR_INFO(rdi);
+ UPDATE_GPR_INFO(rsi);
+ UPDATE_GPR_INFO(rbp);
+ UPDATE_GPR_INFO(rsp);
+ UPDATE_GPR_INFO(r8);
+ UPDATE_GPR_INFO(r9);
+ UPDATE_GPR_INFO(r10);
+ UPDATE_GPR_INFO(r11);
+ UPDATE_GPR_INFO(r12);
+ UPDATE_GPR_INFO(r13);
+ UPDATE_GPR_INFO(r14);
+ UPDATE_GPR_INFO(r15);
+ UPDATE_GPR_INFO(rip);
+ UPDATE_GPR_INFO(rflags);
+ UPDATE_GPR_INFO(cs);
+ UPDATE_GPR_INFO(fs);
+ UPDATE_GPR_INFO(gs);
+ UPDATE_GPR_INFO(ss);
+ UPDATE_GPR_INFO(ds);
+ UPDATE_GPR_INFO(es);
+
+ UPDATE_I386_GPR_INFO(eax, rax);
+ UPDATE_I386_GPR_INFO(ebx, rbx);
+ UPDATE_I386_GPR_INFO(ecx, rcx);
+ UPDATE_I386_GPR_INFO(edx, rdx);
+ UPDATE_I386_GPR_INFO(edi, rdi);
+ UPDATE_I386_GPR_INFO(esi, rsi);
+ UPDATE_I386_GPR_INFO(ebp, rbp);
+ UPDATE_I386_GPR_INFO(esp, rsp);
+ UPDATE_I386_GPR_INFO(eip, rip);
+ UPDATE_I386_GPR_INFO(eflags, rflags);
+
+ UPDATE_DR_INFO(0);
+ UPDATE_DR_INFO(1);
+ UPDATE_DR_INFO(2);
+ UPDATE_DR_INFO(3);
+ UPDATE_DR_INFO(4);
+ UPDATE_DR_INFO(5);
+ UPDATE_DR_INFO(6);
+ UPDATE_DR_INFO(7);
+}
+
diff --git a/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h b/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h
new file mode 100644
index 0000000..1509ef5
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h
@@ -0,0 +1,32 @@
+//===-- RegisterContextLinux_x86_64.h ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_x86_64_H_
+#define liblldb_RegisterContextLinux_x86_64_H_
+
+#include "Plugins/Process/POSIX/RegisterContext_x86_64.h"
+
+class RegisterContextLinux_x86_64:
+ public RegisterContext_x86_64
+{
+public:
+ RegisterContextLinux_x86_64(lldb_private::Thread &thread, uint32_t concrete_frame_idx);
+
+ size_t
+ GetGPRSize();
+
+protected:
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfo();
+
+ virtual void
+ UpdateRegisterInfo();
+};
+
+#endif
diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIX.h b/source/Plugins/Process/POSIX/RegisterContextPOSIX.h
new file mode 100644
index 0000000..63ae01e
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContextPOSIX.h
@@ -0,0 +1,70 @@
+//===-- RegisterContextPOSIX.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_H_
+#define liblldb_RegisterContextPOSIX_H_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Target/RegisterContext.h"
+
+//------------------------------------------------------------------------------
+/// @class RegisterContextPOSIX
+///
+/// @brief Extends RegisterClass with a few virtual operations useful on POSIX.
+class RegisterContextPOSIX
+ : public lldb_private::RegisterContext
+{
+public:
+ RegisterContextPOSIX(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx)
+ { m_watchpoints_initialized = false; }
+
+ /// Updates the register state of the associated thread after hitting a
+ /// breakpoint (if that make sense for the architecture). Default
+ /// implementation simply returns true for architectures which do not
+ /// require any update.
+ ///
+ /// @return
+ /// True if the operation succeeded and false otherwise.
+ virtual bool UpdateAfterBreakpoint() { return true; }
+
+ /// Determines the index in lldb's register file given a kernel byte offset.
+ virtual unsigned
+ GetRegisterIndexFromOffset(unsigned offset) { return LLDB_INVALID_REGNUM; }
+
+ // Checks to see if a watchpoint specified by hw_index caused the inferior
+ // to stop.
+ virtual bool
+ IsWatchpointHit (uint32_t hw_index) { return false; }
+
+ // Resets any watchpoints that have been hit.
+ virtual bool
+ ClearWatchpointHits () { return false; }
+
+ // Returns the watchpoint address associated with a watchpoint hardware
+ // index.
+ virtual lldb::addr_t
+ GetWatchpointAddress (uint32_t hw_index) { return LLDB_INVALID_ADDRESS; }
+
+ virtual bool
+ IsWatchpointVacant (uint32_t hw_index) { return false; }
+
+ virtual bool
+ SetHardwareWatchpointWithIndex (lldb::addr_t addr, size_t size,
+ bool read, bool write,
+ uint32_t hw_index) { return false; }
+
+protected:
+ bool m_watchpoints_initialized;
+};
+
+#endif // #ifndef liblldb_RegisterContextPOSIX_H_
diff --git a/source/Plugins/Process/POSIX/RegisterContext_i386.cpp b/source/Plugins/Process/POSIX/RegisterContext_i386.cpp
new file mode 100644
index 0000000..49676bd
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContext_i386.cpp
@@ -0,0 +1,551 @@
+//===-- RegisterContextPOSIX_i386.cpp ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Host/Endian.h"
+#include "llvm/Support/Compiler.h"
+
+#include "ProcessPOSIX.h"
+#include "ProcessPOSIXLog.h"
+#include "ProcessMonitor.h"
+#include "RegisterContext_i386.h"
+#include "RegisterContext_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+enum
+{
+ k_first_gpr,
+ gpr_eax = k_first_gpr,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_ss,
+ gpr_eflags,
+#ifdef __FreeBSD__
+ gpr_orig_ax,
+#endif
+ gpr_eip,
+ gpr_cs,
+ gpr_ds,
+ gpr_es,
+ gpr_fs,
+ gpr_gs,
+ k_last_gpr = gpr_gs,
+
+ k_first_fpr,
+ fpu_fcw = k_first_fpr,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_foo,
+ fpu_fos,
+ fpu_mxcsr,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ k_last_fpr = fpu_xmm7,
+
+ k_num_registers,
+ k_num_gpr_registers = k_last_gpr - k_first_gpr + 1,
+ k_num_fpu_registers = k_last_fpr - k_first_fpr + 1
+};
+
+// Number of register sets provided by this context.
+enum
+{
+ k_num_register_sets = 2
+};
+
+static const
+uint32_t g_gpr_regnums[k_num_gpr_registers] =
+{
+ gpr_eax,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_ss,
+ gpr_eflags,
+#ifdef __FreeBSD__
+ gpr_orig_ax,
+#endif
+ gpr_eip,
+ gpr_cs,
+ gpr_ds,
+ gpr_es,
+ gpr_fs,
+ gpr_gs,
+};
+
+static const uint32_t
+g_fpu_regnums[k_num_fpu_registers] =
+{
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_foo,
+ fpu_fos,
+ fpu_mxcsr,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+};
+
+static const RegisterSet
+g_reg_sets[k_num_register_sets] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums },
+ { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }
+};
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) \
+ (offsetof(RegisterContext_i386::UserArea, regs) + \
+ offsetof(RegisterContext_i386::GPR, regname))
+
+// Computes the offset of the given FPR in the user data area.
+#define FPR_OFFSET(regname) \
+ (offsetof(RegisterContext_i386::UserArea, i387) + \
+ offsetof(RegisterContext_i386::FPU, regname))
+
+// Number of bytes needed to represent a GPR.
+#define GPR_SIZE(reg) sizeof(((RegisterContext_i386::GPR*)NULL)->reg)
+
+// Number of bytes needed to represent a FPR.
+#define FPR_SIZE(reg) sizeof(((RegisterContext_i386::FPU*)NULL)->reg)
+
+// Number of bytes needed to represent the i'th FP register.
+#define FP_SIZE sizeof(((RegisterContext_i386::MMSReg*)NULL)->bytes)
+
+// Number of bytes needed to represent an XMM register.
+#define XMM_SIZE sizeof(RegisterContext_i386::XMMReg)
+
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, \
+ eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg }, NULL, NULL }
+
+#define DEFINE_FPR(reg, kind1, kind2, kind3, kind4) \
+ { #reg, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \
+ eFormatHex, { kind1, kind2, kind3, kind4, fpu_##reg }, NULL, NULL }
+
+#define DEFINE_FP(reg, i) \
+ { #reg#i, NULL, FP_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \
+ eEncodingVector, eFormatVectorOfUInt8, \
+ { dwarf_##reg##i, dwarf_##reg##i, \
+ LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i }, NULL, NULL }
+
+#define DEFINE_XMM(reg, i) \
+ { #reg#i, NULL, XMM_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \
+ eEncodingVector, eFormatVectorOfUInt8, \
+ { dwarf_##reg##i, dwarf_##reg##i, \
+ LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i }, NULL, NULL }
+
+static RegisterInfo
+g_register_infos[k_num_registers] =
+{
+ // General purpose registers.
+ DEFINE_GPR(eax, NULL, gcc_eax, dwarf_eax, LLDB_INVALID_REGNUM, gdb_eax),
+ DEFINE_GPR(ebx, NULL, gcc_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, gdb_ebx),
+ DEFINE_GPR(ecx, NULL, gcc_ecx, dwarf_ecx, LLDB_INVALID_REGNUM, gdb_ecx),
+ DEFINE_GPR(edx, NULL, gcc_edx, dwarf_edx, LLDB_INVALID_REGNUM, gdb_edx),
+ DEFINE_GPR(edi, NULL, gcc_edi, dwarf_edi, LLDB_INVALID_REGNUM, gdb_edi),
+ DEFINE_GPR(esi, NULL, gcc_esi, dwarf_esi, LLDB_INVALID_REGNUM, gdb_esi),
+ DEFINE_GPR(ebp, "fp", gcc_ebp, dwarf_ebp, LLDB_INVALID_REGNUM, gdb_ebp),
+ DEFINE_GPR(esp, "sp", gcc_esp, dwarf_esp, LLDB_INVALID_REGNUM, gdb_esp),
+ DEFINE_GPR(ss, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ss),
+ DEFINE_GPR(eflags, "flags", gcc_eflags, dwarf_eflags, LLDB_INVALID_REGNUM, gdb_eflags),
+ DEFINE_GPR(eip, "pc", gcc_eip, dwarf_eip, LLDB_INVALID_REGNUM, gdb_eip),
+ DEFINE_GPR(cs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_cs),
+ DEFINE_GPR(ds, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ds),
+ DEFINE_GPR(es, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_es),
+ DEFINE_GPR(fs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fs),
+ DEFINE_GPR(gs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gs),
+
+ // Floating point registers.
+ DEFINE_FPR(fcw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fcw),
+ DEFINE_FPR(fsw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fsw),
+ DEFINE_FPR(ftw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ftw),
+ DEFINE_FPR(fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fop),
+ DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ip),
+ DEFINE_FPR(cs, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_cs),
+ DEFINE_FPR(foo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_dp),
+ DEFINE_FPR(fos, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ds),
+ DEFINE_FPR(mxcsr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_mxcsr),
+
+ DEFINE_FP(stmm, 0),
+ DEFINE_FP(stmm, 1),
+ DEFINE_FP(stmm, 2),
+ DEFINE_FP(stmm, 3),
+ DEFINE_FP(stmm, 4),
+ DEFINE_FP(stmm, 5),
+ DEFINE_FP(stmm, 6),
+ DEFINE_FP(stmm, 7),
+
+ // XMM registers
+ DEFINE_XMM(xmm, 0),
+ DEFINE_XMM(xmm, 1),
+ DEFINE_XMM(xmm, 2),
+ DEFINE_XMM(xmm, 3),
+ DEFINE_XMM(xmm, 4),
+ DEFINE_XMM(xmm, 5),
+ DEFINE_XMM(xmm, 6),
+ DEFINE_XMM(xmm, 7),
+
+};
+
+#ifndef NDEBUG
+static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo));
+#endif
+
+static unsigned GetRegOffset(unsigned reg)
+{
+ assert(reg < k_num_registers && "Invalid register number.");
+ return g_register_infos[reg].byte_offset;
+}
+
+static unsigned GetRegSize(unsigned reg)
+{
+ assert(reg < k_num_registers && "Invalid register number.");
+ return g_register_infos[reg].byte_size;
+}
+
+RegisterContext_i386::RegisterContext_i386(Thread &thread,
+ uint32_t concrete_frame_idx)
+ : RegisterContextPOSIX(thread, concrete_frame_idx)
+{
+}
+
+RegisterContext_i386::~RegisterContext_i386()
+{
+}
+
+ProcessMonitor &
+RegisterContext_i386::GetMonitor()
+{
+ ProcessSP base = CalculateProcess();
+ ProcessPOSIX *process = static_cast<ProcessPOSIX*>(base.get());
+ return process->GetMonitor();
+}
+
+void
+RegisterContext_i386::Invalidate()
+{
+}
+
+void
+RegisterContext_i386::InvalidateAllRegisters()
+{
+}
+
+size_t
+RegisterContext_i386::GetRegisterCount()
+{
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContext_i386::GetRegisterInfoAtIndex(size_t reg)
+{
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ else
+ return NULL;
+}
+
+size_t
+RegisterContext_i386::GetRegisterSetCount()
+{
+ return k_num_register_sets;
+}
+
+const RegisterSet *
+RegisterContext_i386::GetRegisterSet(size_t set)
+{
+ if (set < k_num_register_sets)
+ return &g_reg_sets[set];
+ else
+ return NULL;
+}
+
+unsigned
+RegisterContext_i386::GetRegisterIndexFromOffset(unsigned offset)
+{
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers; reg++)
+ {
+ if (g_register_infos[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers && "Invalid register offset.");
+ return reg;
+}
+
+const char *
+RegisterContext_i386::GetRegisterName(unsigned reg)
+{
+ assert(reg < k_num_registers && "Invalid register offset.");
+ return g_register_infos[reg].name;
+}
+
+bool
+RegisterContext_i386::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegOffset(reg),
+ GetRegisterName(reg), GetRegSize(reg), value);
+}
+
+bool
+RegisterContext_i386::ReadAllRegisterValues(DataBufferSP &data_sp)
+{
+ return false;
+}
+
+bool RegisterContext_i386::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(m_thread.GetID(), GetRegOffset(reg),
+ GetRegisterName(reg), value);
+}
+
+bool
+RegisterContext_i386::WriteAllRegisterValues(const DataBufferSP &data)
+{
+ return false;
+}
+
+bool
+RegisterContext_i386::UpdateAfterBreakpoint()
+{
+ // PC points one byte past the int3 responsible for the breakpoint.
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ SetPC(pc - 1);
+ return true;
+}
+
+uint32_t
+RegisterContext_i386::ConvertRegisterKindToRegisterNumber(uint32_t kind,
+ uint32_t num)
+{
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (num)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_eip;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_esp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_ebp;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+
+ if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
+ {
+ switch (num)
+ {
+ case dwarf_eax: return gpr_eax;
+ case dwarf_edx: return gpr_edx;
+ case dwarf_ecx: return gpr_ecx;
+ case dwarf_ebx: return gpr_ebx;
+ case dwarf_esi: return gpr_esi;
+ case dwarf_edi: return gpr_edi;
+ case dwarf_ebp: return gpr_ebp;
+ case dwarf_esp: return gpr_esp;
+ case dwarf_eip: return gpr_eip;
+ case dwarf_xmm0: return fpu_xmm0;
+ case dwarf_xmm1: return fpu_xmm1;
+ case dwarf_xmm2: return fpu_xmm2;
+ case dwarf_xmm3: return fpu_xmm3;
+ case dwarf_xmm4: return fpu_xmm4;
+ case dwarf_xmm5: return fpu_xmm5;
+ case dwarf_xmm6: return fpu_xmm6;
+ case dwarf_xmm7: return fpu_xmm7;
+ case dwarf_stmm0: return fpu_stmm0;
+ case dwarf_stmm1: return fpu_stmm1;
+ case dwarf_stmm2: return fpu_stmm2;
+ case dwarf_stmm3: return fpu_stmm3;
+ case dwarf_stmm4: return fpu_stmm4;
+ case dwarf_stmm5: return fpu_stmm5;
+ case dwarf_stmm6: return fpu_stmm6;
+ case dwarf_stmm7: return fpu_stmm7;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+
+ if (kind == eRegisterKindGDB)
+ {
+ switch (num)
+ {
+ case gdb_eax : return gpr_eax;
+ case gdb_ebx : return gpr_ebx;
+ case gdb_ecx : return gpr_ecx;
+ case gdb_edx : return gpr_edx;
+ case gdb_esi : return gpr_esi;
+ case gdb_edi : return gpr_edi;
+ case gdb_ebp : return gpr_ebp;
+ case gdb_esp : return gpr_esp;
+ case gdb_eip : return gpr_eip;
+ case gdb_eflags : return gpr_eflags;
+ case gdb_cs : return gpr_cs;
+ case gdb_ss : return gpr_ss;
+ case gdb_ds : return gpr_ds;
+ case gdb_es : return gpr_es;
+ case gdb_fs : return gpr_fs;
+ case gdb_gs : return gpr_gs;
+ case gdb_stmm0 : return fpu_stmm0;
+ case gdb_stmm1 : return fpu_stmm1;
+ case gdb_stmm2 : return fpu_stmm2;
+ case gdb_stmm3 : return fpu_stmm3;
+ case gdb_stmm4 : return fpu_stmm4;
+ case gdb_stmm5 : return fpu_stmm5;
+ case gdb_stmm6 : return fpu_stmm6;
+ case gdb_stmm7 : return fpu_stmm7;
+ case gdb_fcw : return fpu_fcw;
+ case gdb_fsw : return fpu_fsw;
+ case gdb_ftw : return fpu_ftw;
+ case gdb_fpu_cs : return fpu_cs;
+ case gdb_ip : return fpu_ip;
+ case gdb_fpu_ds : return fpu_fos;
+ case gdb_dp : return fpu_foo;
+ case gdb_fop : return fpu_fop;
+ case gdb_xmm0 : return fpu_xmm0;
+ case gdb_xmm1 : return fpu_xmm1;
+ case gdb_xmm2 : return fpu_xmm2;
+ case gdb_xmm3 : return fpu_xmm3;
+ case gdb_xmm4 : return fpu_xmm4;
+ case gdb_xmm5 : return fpu_xmm5;
+ case gdb_xmm6 : return fpu_xmm6;
+ case gdb_xmm7 : return fpu_xmm7;
+ case gdb_mxcsr : return fpu_mxcsr;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+ else if (kind == eRegisterKindLLDB)
+ {
+ return num;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
+
+bool
+RegisterContext_i386::HardwareSingleStep(bool enable)
+{
+ enum { TRACE_BIT = 0x100 };
+ uint64_t eflags;
+
+ if ((eflags = ReadRegisterAsUnsigned(gpr_eflags, -1UL)) == -1UL)
+ return false;
+
+ if (enable)
+ {
+ if (eflags & TRACE_BIT)
+ return true;
+
+ eflags |= TRACE_BIT;
+ }
+ else
+ {
+ if (!(eflags & TRACE_BIT))
+ return false;
+
+ eflags &= ~TRACE_BIT;
+ }
+
+ return WriteRegisterFromUnsigned(gpr_eflags, eflags);
+}
+
+void
+RegisterContext_i386::LogGPR(const char *title)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS));
+ if (log)
+ {
+ if (title)
+ log->Printf ("%s", title);
+ for (uint32_t i=0; i<k_num_gpr_registers; i++)
+ {
+ uint32_t reg = gpr_eax + i;
+ log->Printf("%12s = 0x%8.8" PRIx64, g_register_infos[reg].name, ((uint64_t*)&user.regs)[reg]);
+ }
+ }
+}
+
+bool
+RegisterContext_i386::ReadGPR()
+{
+ bool result;
+
+ ProcessMonitor &monitor = GetMonitor();
+ result = monitor.ReadGPR(m_thread.GetID(), &user.regs, sizeof(user.regs));
+ LogGPR("RegisterContext_i386::ReadGPR()");
+ return result;
+}
+
+bool
+RegisterContext_i386::ReadFPR()
+{
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadFPR(m_thread.GetID(), &user.i387, sizeof(user.i387));
+}
diff --git a/source/Plugins/Process/POSIX/RegisterContext_i386.h b/source/Plugins/Process/POSIX/RegisterContext_i386.h
new file mode 100644
index 0000000..96066c4
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContext_i386.h
@@ -0,0 +1,169 @@
+//===-- RegisterContext_i386.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContext_i386_h_
+#define liblldb_RegisterContext_i386_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Log.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContext_i386 : public RegisterContextPOSIX
+{
+public:
+ RegisterContext_i386(lldb_private::Thread &thread,
+ uint32_t concreate_frame_idx);
+
+ ~RegisterContext_i386();
+
+ void
+ Invalidate();
+
+ void
+ InvalidateAllRegisters();
+
+ size_t
+ GetRegisterCount();
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex(size_t reg);
+
+ size_t
+ GetRegisterSetCount();
+
+ const lldb_private::RegisterSet *
+ GetRegisterSet(size_t set);
+
+ unsigned
+ GetRegisterIndexFromOffset(unsigned offset);
+
+ const char *
+ GetRegisterName(unsigned reg);
+
+ bool
+ ReadRegisterValue(uint32_t reg, lldb_private::Scalar &value);
+
+ bool
+ ReadRegisterBytes(uint32_t reg, lldb_private::DataExtractor &data);
+
+ virtual bool
+ ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool
+ ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool
+ WriteRegisterValue(uint32_t reg, const lldb_private::Scalar &value);
+
+ bool
+ WriteRegisterBytes(uint32_t reg, lldb_private::DataExtractor &data,
+ uint32_t data_offset = 0);
+
+ virtual bool
+ WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool
+ WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t
+ ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num);
+
+ bool
+ HardwareSingleStep(bool enable);
+
+ bool
+ UpdateAfterBreakpoint();
+
+ struct GPR
+ {
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t eax;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+ uint32_t orig_ax;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+ };
+
+ struct MMSReg
+ {
+ uint8_t bytes[8];
+ };
+
+ struct XMMReg
+ {
+ uint8_t bytes[16];
+ };
+
+ struct FPU
+ {
+ uint16_t fcw;
+ uint16_t fsw;
+ uint16_t ftw;
+ uint16_t fop;
+ uint32_t ip;
+ uint32_t cs;
+ uint32_t foo;
+ uint32_t fos;
+ uint32_t mxcsr;
+ uint32_t reserved;
+ MMSReg stmm[8];
+ XMMReg xmm[8];
+ uint32_t pad[56];
+ };
+
+ // A user area like this no longer exists on FreeBSD
+ // making this a Linux artifact. Nonetheless, it is safe
+ // leaving it here while the code is being cleaned up and generalized.
+
+ struct UserArea
+ {
+ GPR regs; // General purpose registers.
+ int32_t fpvalid; // True if FPU is being used.
+ FPU i387; // FPU registers.
+ uint32_t tsize; // Text segment size.
+ uint32_t dsize; // Data segment size.
+ uint32_t ssize; // Stack segment size.
+ uint32_t start_code; // VM address of text.
+ uint32_t start_stack; // VM address of stack bottom (top in rsp).
+ int32_t signal; // Signal causing core dump.
+ int32_t reserved; // Unused.
+ uint32_t ar0; // Location of GPR's.
+ FPU* fpstate; // Location of FPR's.
+ uint32_t magic; // Identifier for core dumps.
+ char u_comm[32]; // Command causing core dump.
+ uint32_t u_debugreg[8]; // Debug registers (DR0 - DR7).
+ };
+private:
+ UserArea user;
+
+ ProcessMonitor &GetMonitor();
+
+ void LogGPR(const char *title);
+
+ bool ReadGPR();
+ bool ReadFPR();
+};
+
+#endif // #ifndef liblldb_RegisterContext_i386_h_
diff --git a/source/Plugins/Process/POSIX/RegisterContext_x86.h b/source/Plugins/Process/POSIX/RegisterContext_x86.h
new file mode 100644
index 0000000..61a25c4
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContext_x86.h
@@ -0,0 +1,110 @@
+//===-- RegisterContext_x86.h ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContext_x86_H_
+#define liblldb_RegisterContext_x86_H_
+
+enum
+{
+ gcc_eax = 0,
+ gcc_ecx,
+ gcc_edx,
+ gcc_ebx,
+ gcc_ebp,
+ gcc_esp,
+ gcc_esi,
+ gcc_edi,
+ gcc_eip,
+ gcc_eflags
+};
+
+enum
+{
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+ dwarf_stmm0 = 11,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_xmm0 = 21,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7
+};
+
+enum
+{
+ gdb_eax = 0,
+ gdb_ecx = 1,
+ gdb_edx = 2,
+ gdb_ebx = 3,
+ gdb_esp = 4,
+ gdb_ebp = 5,
+ gdb_esi = 6,
+ gdb_edi = 7,
+ gdb_eip = 8,
+ gdb_eflags = 9,
+ gdb_cs = 10,
+ gdb_ss = 11,
+ gdb_ds = 12,
+ gdb_es = 13,
+ gdb_fs = 14,
+ gdb_gs = 15,
+ gdb_stmm0 = 16,
+ gdb_stmm1 = 17,
+ gdb_stmm2 = 18,
+ gdb_stmm3 = 19,
+ gdb_stmm4 = 20,
+ gdb_stmm5 = 21,
+ gdb_stmm6 = 22,
+ gdb_stmm7 = 23,
+ gdb_fcw = 24,
+ gdb_fsw = 25,
+ gdb_ftw = 26,
+ gdb_fpu_cs = 27,
+ gdb_ip = 28,
+ gdb_fpu_ds = 29,
+ gdb_dp = 30,
+ gdb_fop = 31,
+ gdb_xmm0 = 32,
+ gdb_xmm1 = 33,
+ gdb_xmm2 = 34,
+ gdb_xmm3 = 35,
+ gdb_xmm4 = 36,
+ gdb_xmm5 = 37,
+ gdb_xmm6 = 38,
+ gdb_xmm7 = 39,
+ gdb_mxcsr = 40,
+ gdb_mm0 = 41,
+ gdb_mm1 = 42,
+ gdb_mm2 = 43,
+ gdb_mm3 = 44,
+ gdb_mm4 = 45,
+ gdb_mm5 = 46,
+ gdb_mm6 = 47,
+ gdb_mm7 = 48
+};
+
+#endif
diff --git a/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp b/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp
new file mode 100644
index 0000000..617b184
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp
@@ -0,0 +1,1563 @@
+//===-- RegisterContext_x86_64.cpp -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Host/Endian.h"
+#include "llvm/Support/Compiler.h"
+
+#include "ProcessPOSIX.h"
+#if defined(__linux__) or defined(__FreeBSD__)
+#include "ProcessMonitor.h"
+#endif
+#include "RegisterContext_i386.h"
+#include "RegisterContext_x86.h"
+#include "RegisterContext_x86_64.h"
+#include "Plugins/Process/elf-core/ProcessElfCore.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// Support ptrace extensions even when compiled without required kernel support
+#ifndef NT_X86_XSTATE
+ #define NT_X86_XSTATE 0x202
+#endif
+
+enum
+{
+ gcc_dwarf_gpr_rax = 0,
+ gcc_dwarf_gpr_rdx,
+ gcc_dwarf_gpr_rcx,
+ gcc_dwarf_gpr_rbx,
+ gcc_dwarf_gpr_rsi,
+ gcc_dwarf_gpr_rdi,
+ gcc_dwarf_gpr_rbp,
+ gcc_dwarf_gpr_rsp,
+ gcc_dwarf_gpr_r8,
+ gcc_dwarf_gpr_r9,
+ gcc_dwarf_gpr_r10,
+ gcc_dwarf_gpr_r11,
+ gcc_dwarf_gpr_r12,
+ gcc_dwarf_gpr_r13,
+ gcc_dwarf_gpr_r14,
+ gcc_dwarf_gpr_r15,
+ gcc_dwarf_gpr_rip,
+ gcc_dwarf_fpu_xmm0,
+ gcc_dwarf_fpu_xmm1,
+ gcc_dwarf_fpu_xmm2,
+ gcc_dwarf_fpu_xmm3,
+ gcc_dwarf_fpu_xmm4,
+ gcc_dwarf_fpu_xmm5,
+ gcc_dwarf_fpu_xmm6,
+ gcc_dwarf_fpu_xmm7,
+ gcc_dwarf_fpu_xmm8,
+ gcc_dwarf_fpu_xmm9,
+ gcc_dwarf_fpu_xmm10,
+ gcc_dwarf_fpu_xmm11,
+ gcc_dwarf_fpu_xmm12,
+ gcc_dwarf_fpu_xmm13,
+ gcc_dwarf_fpu_xmm14,
+ gcc_dwarf_fpu_xmm15,
+ gcc_dwarf_fpu_stmm0,
+ gcc_dwarf_fpu_stmm1,
+ gcc_dwarf_fpu_stmm2,
+ gcc_dwarf_fpu_stmm3,
+ gcc_dwarf_fpu_stmm4,
+ gcc_dwarf_fpu_stmm5,
+ gcc_dwarf_fpu_stmm6,
+ gcc_dwarf_fpu_stmm7,
+ gcc_dwarf_fpu_ymm0,
+ gcc_dwarf_fpu_ymm1,
+ gcc_dwarf_fpu_ymm2,
+ gcc_dwarf_fpu_ymm3,
+ gcc_dwarf_fpu_ymm4,
+ gcc_dwarf_fpu_ymm5,
+ gcc_dwarf_fpu_ymm6,
+ gcc_dwarf_fpu_ymm7,
+ gcc_dwarf_fpu_ymm8,
+ gcc_dwarf_fpu_ymm9,
+ gcc_dwarf_fpu_ymm10,
+ gcc_dwarf_fpu_ymm11,
+ gcc_dwarf_fpu_ymm12,
+ gcc_dwarf_fpu_ymm13,
+ gcc_dwarf_fpu_ymm14,
+ gcc_dwarf_fpu_ymm15
+};
+
+enum
+{
+ gdb_gpr_rax = 0,
+ gdb_gpr_rbx = 1,
+ gdb_gpr_rcx = 2,
+ gdb_gpr_rdx = 3,
+ gdb_gpr_rsi = 4,
+ gdb_gpr_rdi = 5,
+ gdb_gpr_rbp = 6,
+ gdb_gpr_rsp = 7,
+ gdb_gpr_r8 = 8,
+ gdb_gpr_r9 = 9,
+ gdb_gpr_r10 = 10,
+ gdb_gpr_r11 = 11,
+ gdb_gpr_r12 = 12,
+ gdb_gpr_r13 = 13,
+ gdb_gpr_r14 = 14,
+ gdb_gpr_r15 = 15,
+ gdb_gpr_rip = 16,
+ gdb_gpr_rflags = 17,
+ gdb_gpr_cs = 18,
+ gdb_gpr_ss = 19,
+ gdb_gpr_ds = 20,
+ gdb_gpr_es = 21,
+ gdb_gpr_fs = 22,
+ gdb_gpr_gs = 23,
+ gdb_fpu_stmm0 = 24,
+ gdb_fpu_stmm1 = 25,
+ gdb_fpu_stmm2 = 26,
+ gdb_fpu_stmm3 = 27,
+ gdb_fpu_stmm4 = 28,
+ gdb_fpu_stmm5 = 29,
+ gdb_fpu_stmm6 = 30,
+ gdb_fpu_stmm7 = 31,
+ gdb_fpu_fcw = 32,
+ gdb_fpu_fsw = 33,
+ gdb_fpu_ftw = 34,
+ gdb_fpu_cs_64 = 35,
+ gdb_fpu_ip = 36,
+ gdb_fpu_ds_64 = 37,
+ gdb_fpu_dp = 38,
+ gdb_fpu_fop = 39,
+ gdb_fpu_xmm0 = 40,
+ gdb_fpu_xmm1 = 41,
+ gdb_fpu_xmm2 = 42,
+ gdb_fpu_xmm3 = 43,
+ gdb_fpu_xmm4 = 44,
+ gdb_fpu_xmm5 = 45,
+ gdb_fpu_xmm6 = 46,
+ gdb_fpu_xmm7 = 47,
+ gdb_fpu_xmm8 = 48,
+ gdb_fpu_xmm9 = 49,
+ gdb_fpu_xmm10 = 50,
+ gdb_fpu_xmm11 = 51,
+ gdb_fpu_xmm12 = 52,
+ gdb_fpu_xmm13 = 53,
+ gdb_fpu_xmm14 = 54,
+ gdb_fpu_xmm15 = 55,
+ gdb_fpu_mxcsr = 56,
+ gdb_fpu_ymm0 = 57,
+ gdb_fpu_ymm1 = 58,
+ gdb_fpu_ymm2 = 59,
+ gdb_fpu_ymm3 = 60,
+ gdb_fpu_ymm4 = 61,
+ gdb_fpu_ymm5 = 62,
+ gdb_fpu_ymm6 = 63,
+ gdb_fpu_ymm7 = 64,
+ gdb_fpu_ymm8 = 65,
+ gdb_fpu_ymm9 = 66,
+ gdb_fpu_ymm10 = 67,
+ gdb_fpu_ymm11 = 68,
+ gdb_fpu_ymm12 = 69,
+ gdb_fpu_ymm13 = 70,
+ gdb_fpu_ymm14 = 71,
+ gdb_fpu_ymm15 = 72
+};
+
+static const
+uint32_t g_gpr_regnums[k_num_gpr_registers] =
+{
+ gpr_rax,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs,
+ gpr_ss,
+ gpr_ds,
+ gpr_es,
+ gpr_eax,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_eip,
+ gpr_eflags
+};
+
+static const uint32_t
+g_fpu_regnums[k_num_fpr_registers] =
+{
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15
+};
+
+static const uint32_t
+g_avx_regnums[k_num_avx_registers] =
+{
+ fpu_ymm0,
+ fpu_ymm1,
+ fpu_ymm2,
+ fpu_ymm3,
+ fpu_ymm4,
+ fpu_ymm5,
+ fpu_ymm6,
+ fpu_ymm7,
+ fpu_ymm8,
+ fpu_ymm9,
+ fpu_ymm10,
+ fpu_ymm11,
+ fpu_ymm12,
+ fpu_ymm13,
+ fpu_ymm14,
+ fpu_ymm15
+};
+
+// Number of register sets provided by this context.
+enum
+{
+ k_num_extended_register_sets = 1,
+ k_num_register_sets = 3
+};
+
+static const RegisterSet
+g_reg_sets[k_num_register_sets] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums },
+ { "Floating Point Registers", "fpu", k_num_fpr_registers, g_fpu_regnums },
+ { "Advanced Vector Extensions", "avx", k_num_avx_registers, g_avx_regnums }
+};
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (offsetof(RegisterContext_x86_64::FPR, xstate) + \
+ offsetof(RegisterContext_x86_64::FXSAVE, regname))
+
+// Computes the offset of the YMM register assembled from register halves.
+#define YMM_OFFSET(regname) \
+ (offsetof(RegisterContext_x86_64::YMM, regname))
+
+// Number of bytes needed to represent a i386 GPR
+#define GPR_i386_SIZE(reg) sizeof(((RegisterContext_i386::GPR*)NULL)->reg)
+
+// Number of bytes needed to represent a FPR.
+#define FPR_SIZE(reg) sizeof(((RegisterContext_x86_64::FXSAVE*)NULL)->reg)
+
+// Number of bytes needed to represent the i'th FP register.
+#define FP_SIZE sizeof(((RegisterContext_x86_64::MMSReg*)NULL)->bytes)
+
+// Number of bytes needed to represent an XMM register.
+#define XMM_SIZE sizeof(RegisterContext_x86_64::XMMReg)
+
+// Number of bytes needed to represent a YMM register.
+#define YMM_SIZE sizeof(RegisterContext_x86_64::YMMReg)
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { #reg, alt, 0, 0, eEncodingUint, \
+ eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg }, NULL, NULL }
+
+// Dummy data for RegisterInfo::value_regs as expected by DumpRegisterSet.
+static uint32_t value_regs = LLDB_INVALID_REGNUM;
+
+#define DEFINE_GPR_i386(reg_i386, reg_x86_64, alt, kind1, kind2, kind3, kind4) \
+ { #reg_i386, alt, GPR_i386_SIZE(reg_i386), 0, eEncodingUint, \
+ eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg_i386 }, &value_regs, NULL }
+
+#define DEFINE_FPR(reg, kind1, kind2, kind3, kind4) \
+ { #reg, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \
+ eFormatHex, { kind1, kind2, kind3, kind4, fpu_##reg }, NULL, NULL }
+
+#define DEFINE_FP(reg, i) \
+ { #reg#i, NULL, FP_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \
+ eEncodingVector, eFormatVectorOfUInt8, \
+ { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \
+ LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL }
+
+#define DEFINE_XMM(reg, i) \
+ { #reg#i, NULL, XMM_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \
+ eEncodingVector, eFormatVectorOfUInt8, \
+ { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \
+ LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL }
+
+#define DEFINE_YMM(reg, i) \
+ { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(reg[i]), \
+ eEncodingVector, eFormatVectorOfUInt8, \
+ { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \
+ LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL }
+
+#define DEFINE_DR(reg, i) \
+ { #reg#i, NULL, 0, 0, eEncodingUint, eFormatHex, \
+ { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL }
+
+#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(RegisterContext_x86_64::FPR))
+
+static RegisterInfo
+g_register_infos[k_num_registers] =
+{
+ // General purpose registers.
+ DEFINE_GPR(rax, NULL, gcc_dwarf_gpr_rax, gcc_dwarf_gpr_rax, LLDB_INVALID_REGNUM, gdb_gpr_rax),
+ DEFINE_GPR(rbx, NULL, gcc_dwarf_gpr_rbx, gcc_dwarf_gpr_rbx, LLDB_INVALID_REGNUM, gdb_gpr_rbx),
+ DEFINE_GPR(rcx, NULL, gcc_dwarf_gpr_rcx, gcc_dwarf_gpr_rcx, LLDB_INVALID_REGNUM, gdb_gpr_rcx),
+ DEFINE_GPR(rdx, NULL, gcc_dwarf_gpr_rdx, gcc_dwarf_gpr_rdx, LLDB_INVALID_REGNUM, gdb_gpr_rdx),
+ DEFINE_GPR(rdi, NULL, gcc_dwarf_gpr_rdi, gcc_dwarf_gpr_rdi, LLDB_INVALID_REGNUM, gdb_gpr_rdi),
+ DEFINE_GPR(rsi, NULL, gcc_dwarf_gpr_rsi, gcc_dwarf_gpr_rsi, LLDB_INVALID_REGNUM, gdb_gpr_rsi),
+ DEFINE_GPR(rbp, "fp", gcc_dwarf_gpr_rbp, gcc_dwarf_gpr_rbp, LLDB_REGNUM_GENERIC_FP, gdb_gpr_rbp),
+ DEFINE_GPR(rsp, "sp", gcc_dwarf_gpr_rsp, gcc_dwarf_gpr_rsp, LLDB_REGNUM_GENERIC_SP, gdb_gpr_rsp),
+ DEFINE_GPR(r8, NULL, gcc_dwarf_gpr_r8, gcc_dwarf_gpr_r8, LLDB_INVALID_REGNUM, gdb_gpr_r8),
+ DEFINE_GPR(r9, NULL, gcc_dwarf_gpr_r9, gcc_dwarf_gpr_r9, LLDB_INVALID_REGNUM, gdb_gpr_r9),
+ DEFINE_GPR(r10, NULL, gcc_dwarf_gpr_r10, gcc_dwarf_gpr_r10, LLDB_INVALID_REGNUM, gdb_gpr_r10),
+ DEFINE_GPR(r11, NULL, gcc_dwarf_gpr_r11, gcc_dwarf_gpr_r11, LLDB_INVALID_REGNUM, gdb_gpr_r11),
+ DEFINE_GPR(r12, NULL, gcc_dwarf_gpr_r12, gcc_dwarf_gpr_r12, LLDB_INVALID_REGNUM, gdb_gpr_r12),
+ DEFINE_GPR(r13, NULL, gcc_dwarf_gpr_r13, gcc_dwarf_gpr_r13, LLDB_INVALID_REGNUM, gdb_gpr_r13),
+ DEFINE_GPR(r14, NULL, gcc_dwarf_gpr_r14, gcc_dwarf_gpr_r14, LLDB_INVALID_REGNUM, gdb_gpr_r14),
+ DEFINE_GPR(r15, NULL, gcc_dwarf_gpr_r15, gcc_dwarf_gpr_r15, LLDB_INVALID_REGNUM, gdb_gpr_r15),
+ DEFINE_GPR(rip, "pc", gcc_dwarf_gpr_rip, gcc_dwarf_gpr_rip, LLDB_REGNUM_GENERIC_PC, gdb_gpr_rip),
+ DEFINE_GPR(rflags, "flags", LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, gdb_gpr_rflags),
+ DEFINE_GPR(cs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_cs),
+ DEFINE_GPR(fs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_fs),
+ DEFINE_GPR(gs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_gs),
+ DEFINE_GPR(ss, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_ss),
+ DEFINE_GPR(ds, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_ds),
+ DEFINE_GPR(es, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_es),
+ // i386 registers
+ DEFINE_GPR_i386(eax, rax, NULL, gcc_eax, dwarf_eax, LLDB_INVALID_REGNUM, gdb_eax),
+ DEFINE_GPR_i386(ebx, rbx, NULL, gcc_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, gdb_ebx),
+ DEFINE_GPR_i386(ecx, rcx, NULL, gcc_ecx, dwarf_ecx, LLDB_INVALID_REGNUM, gdb_ecx),
+ DEFINE_GPR_i386(edx, rdx, NULL, gcc_edx, dwarf_edx, LLDB_INVALID_REGNUM, gdb_edx),
+ DEFINE_GPR_i386(edi, rdi, NULL, gcc_edi, dwarf_edi, LLDB_INVALID_REGNUM, gdb_edi),
+ DEFINE_GPR_i386(esi, rsi, NULL, gcc_esi, dwarf_esi, LLDB_INVALID_REGNUM, gdb_esi),
+ DEFINE_GPR_i386(ebp, rbp, "fp", gcc_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, gdb_ebp),
+ DEFINE_GPR_i386(esp, rsp, "sp", gcc_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, gdb_esp),
+ DEFINE_GPR_i386(eip, rip, "pc", gcc_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, gdb_eip),
+ DEFINE_GPR_i386(eflags, rflags, "flags", gcc_eflags, dwarf_eflags, LLDB_REGNUM_GENERIC_FLAGS, gdb_eflags),
+ // i387 Floating point registers.
+ DEFINE_FPR(fcw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fcw),
+ DEFINE_FPR(fsw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fsw),
+ DEFINE_FPR(ftw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ftw),
+ DEFINE_FPR(fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fop),
+ DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ip),
+ // FIXME: Extract segment from ip.
+ DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_cs_64),
+ DEFINE_FPR(dp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_dp),
+ // FIXME: Extract segment from dp.
+ DEFINE_FPR(dp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ds_64),
+ DEFINE_FPR(mxcsr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_mxcsr),
+ DEFINE_FPR(mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ // FP registers.
+ DEFINE_FP(stmm, 0),
+ DEFINE_FP(stmm, 1),
+ DEFINE_FP(stmm, 2),
+ DEFINE_FP(stmm, 3),
+ DEFINE_FP(stmm, 4),
+ DEFINE_FP(stmm, 5),
+ DEFINE_FP(stmm, 6),
+ DEFINE_FP(stmm, 7),
+
+ // XMM registers
+ DEFINE_XMM(xmm, 0),
+ DEFINE_XMM(xmm, 1),
+ DEFINE_XMM(xmm, 2),
+ DEFINE_XMM(xmm, 3),
+ DEFINE_XMM(xmm, 4),
+ DEFINE_XMM(xmm, 5),
+ DEFINE_XMM(xmm, 6),
+ DEFINE_XMM(xmm, 7),
+ DEFINE_XMM(xmm, 8),
+ DEFINE_XMM(xmm, 9),
+ DEFINE_XMM(xmm, 10),
+ DEFINE_XMM(xmm, 11),
+ DEFINE_XMM(xmm, 12),
+ DEFINE_XMM(xmm, 13),
+ DEFINE_XMM(xmm, 14),
+ DEFINE_XMM(xmm, 15),
+
+ // Copy of YMM registers assembled from xmm and ymmh
+ DEFINE_YMM(ymm, 0),
+ DEFINE_YMM(ymm, 1),
+ DEFINE_YMM(ymm, 2),
+ DEFINE_YMM(ymm, 3),
+ DEFINE_YMM(ymm, 4),
+ DEFINE_YMM(ymm, 5),
+ DEFINE_YMM(ymm, 6),
+ DEFINE_YMM(ymm, 7),
+ DEFINE_YMM(ymm, 8),
+ DEFINE_YMM(ymm, 9),
+ DEFINE_YMM(ymm, 10),
+ DEFINE_YMM(ymm, 11),
+ DEFINE_YMM(ymm, 12),
+ DEFINE_YMM(ymm, 13),
+ DEFINE_YMM(ymm, 14),
+ DEFINE_YMM(ymm, 15),
+
+ // Debug registers for lldb internal use
+ DEFINE_DR(dr, 0),
+ DEFINE_DR(dr, 1),
+ DEFINE_DR(dr, 2),
+ DEFINE_DR(dr, 3),
+ DEFINE_DR(dr, 4),
+ DEFINE_DR(dr, 5),
+ DEFINE_DR(dr, 6),
+ DEFINE_DR(dr, 7)
+};
+
+static bool IsGPR(unsigned reg)
+{
+ return reg <= k_last_gpr; // GPR's come first.
+}
+
+static bool IsAVX(unsigned reg)
+{
+ return (k_first_avx <= reg && reg <= k_last_avx);
+}
+static bool IsFPR(unsigned reg)
+{
+ return (k_first_fpr <= reg && reg <= k_last_fpr);
+}
+
+
+bool RegisterContext_x86_64::IsFPR(unsigned reg, FPRType fpr_type)
+{
+ bool generic_fpr = ::IsFPR(reg);
+ if (fpr_type == eXSAVE)
+ return generic_fpr || IsAVX(reg);
+
+ return generic_fpr;
+}
+
+RegisterContext_x86_64::RegisterContext_x86_64(Thread &thread,
+ uint32_t concrete_frame_idx)
+ : RegisterContextPOSIX(thread, concrete_frame_idx)
+{
+ // Initialize m_iovec to point to the buffer and buffer size
+ // using the conventions of Berkeley style UIO structures, as required
+ // by PTRACE extensions.
+ m_iovec.iov_base = &m_fpr.xstate.xsave;
+ m_iovec.iov_len = sizeof(m_fpr.xstate.xsave);
+
+ ::memset(&m_fpr, 0, sizeof(RegisterContext_x86_64::FPR));
+
+ // elf-core yet to support ReadFPR()
+ ProcessSP base = CalculateProcess();
+ if (base.get()->GetPluginName() == ProcessElfCore::GetPluginNameStatic())
+ return;
+
+ // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx
+ m_fpr_type = eXSAVE; // extended floating-point registers, if available
+ if (false == ReadFPR())
+ m_fpr_type = eFXSAVE; // assume generic floating-point registers
+}
+
+RegisterContext_x86_64::~RegisterContext_x86_64()
+{
+}
+
+void
+RegisterContext_x86_64::Invalidate()
+{
+}
+
+void
+RegisterContext_x86_64::InvalidateAllRegisters()
+{
+}
+
+unsigned
+RegisterContext_x86_64::GetRegisterOffset(unsigned reg)
+{
+ assert(reg < k_num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned
+RegisterContext_x86_64::GetRegisterSize(unsigned reg)
+{
+ assert(reg < k_num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t
+RegisterContext_x86_64::GetRegisterCount()
+{
+ size_t num_registers = k_num_gpr_registers + k_num_fpr_registers;
+ if (m_fpr_type == eXSAVE)
+ return num_registers + k_num_avx_registers;
+ return num_registers;
+}
+
+const RegisterInfo *
+RegisterContext_x86_64::GetRegisterInfo()
+{
+ // Commonly, this method is overridden and g_register_infos is copied and specialized.
+ // So, use GetRegisterInfo() rather than g_register_infos in this scope.
+ return g_register_infos;
+}
+
+const RegisterInfo *
+RegisterContext_x86_64::GetRegisterInfoAtIndex(size_t reg)
+{
+ if (reg < k_num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return NULL;
+}
+
+size_t
+RegisterContext_x86_64::GetRegisterSetCount()
+{
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set)
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+
+ return sets;
+}
+
+const RegisterSet *
+RegisterContext_x86_64::GetRegisterSet(size_t set)
+{
+ if (IsRegisterSetAvailable(set))
+ return &g_reg_sets[set];
+ else
+ return NULL;
+}
+
+unsigned
+RegisterContext_x86_64::GetRegisterIndexFromOffset(unsigned offset)
+{
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers; reg++)
+ {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers && "Invalid register offset.");
+ return reg;
+}
+
+const char *
+RegisterContext_x86_64::GetRegisterName(unsigned reg)
+{
+ assert(reg < k_num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder
+RegisterContext_x86_64::GetByteOrder()
+{
+ // Get the target process whose privileged thread was used for the register read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+// Parse ymm registers and into xmm.bytes and ymmh.bytes.
+bool RegisterContext_x86_64::CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order)
+{
+ if (!IsAVX(reg))
+ return false;
+
+ if (byte_order == eByteOrderLittle) {
+ ::memcpy(m_fpr.xstate.fxsave.xmm[reg - fpu_ymm0].bytes,
+ m_ymm_set.ymm[reg - fpu_ymm0].bytes,
+ sizeof(RegisterContext_x86_64::XMMReg));
+ ::memcpy(m_fpr.xstate.xsave.ymmh[reg - fpu_ymm0].bytes,
+ m_ymm_set.ymm[reg - fpu_ymm0].bytes + sizeof(RegisterContext_x86_64::XMMReg),
+ sizeof(RegisterContext_x86_64::YMMHReg));
+ return true;
+ }
+
+ if (byte_order == eByteOrderBig) {
+ ::memcpy(m_fpr.xstate.fxsave.xmm[reg - fpu_ymm0].bytes,
+ m_ymm_set.ymm[reg - fpu_ymm0].bytes + sizeof(RegisterContext_x86_64::XMMReg),
+ sizeof(RegisterContext_x86_64::XMMReg));
+ ::memcpy(m_fpr.xstate.xsave.ymmh[reg - fpu_ymm0].bytes,
+ m_ymm_set.ymm[reg - fpu_ymm0].bytes,
+ sizeof(RegisterContext_x86_64::YMMHReg));
+ return true;
+ }
+ return false; // unsupported or invalid byte order
+}
+
+// Concatenate xmm.bytes with ymmh.bytes
+bool RegisterContext_x86_64::CopyXSTATEtoYMM(uint32_t reg, lldb::ByteOrder byte_order)
+{
+ if (!IsAVX(reg))
+ return false;
+
+ if (byte_order == eByteOrderLittle) {
+ ::memcpy(m_ymm_set.ymm[reg - fpu_ymm0].bytes,
+ m_fpr.xstate.fxsave.xmm[reg - fpu_ymm0].bytes,
+ sizeof(RegisterContext_x86_64::XMMReg));
+ ::memcpy(m_ymm_set.ymm[reg - fpu_ymm0].bytes + sizeof(RegisterContext_x86_64::XMMReg),
+ m_fpr.xstate.xsave.ymmh[reg - fpu_ymm0].bytes,
+ sizeof(RegisterContext_x86_64::YMMHReg));
+ return true;
+ }
+ if (byte_order == eByteOrderBig) {
+ ::memcpy(m_ymm_set.ymm[reg - fpu_ymm0].bytes + sizeof(RegisterContext_x86_64::XMMReg),
+ m_fpr.xstate.fxsave.xmm[reg - fpu_ymm0].bytes,
+ sizeof(RegisterContext_x86_64::XMMReg));
+ ::memcpy(m_ymm_set.ymm[reg - fpu_ymm0].bytes,
+ m_fpr.xstate.xsave.ymmh[reg - fpu_ymm0].bytes,
+ sizeof(RegisterContext_x86_64::YMMHReg));
+ return true;
+ }
+ return false; // unsupported or invalid byte order
+}
+
+bool
+RegisterContext_x86_64::IsRegisterSetAvailable(size_t set_index)
+{
+ // Note: Extended register sets are assumed to be at the end of g_reg_sets...
+ size_t num_sets = k_num_register_sets - k_num_extended_register_sets;
+ if (m_fpr_type == eXSAVE) // ...and to start with AVX registers.
+ ++num_sets;
+
+ return (set_index < num_sets);
+}
+
+bool
+RegisterContext_x86_64::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value)
+{
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg, m_fpr_type)) {
+ if (!ReadFPR())
+ return false;
+ }
+ else {
+ bool success = ReadRegister(reg, value);
+
+ // If an i386 register should be parsed from an x86_64 register...
+ if (success && reg >= k_first_i386 && reg <= k_last_i386)
+ if (value.GetByteSize() > reg_info->byte_size)
+ value.SetType(reg_info); // ...use the type specified by reg_info rather than the uint64_t default
+ return success;
+ }
+
+ if (reg_info->encoding == eEncodingVector) {
+ ByteOrder byte_order = GetByteOrder();
+
+ if (byte_order != ByteOrder::eByteOrderInvalid) {
+ if (reg >= fpu_stmm0 && reg <= fpu_stmm7) {
+ value.SetBytes(m_fpr.xstate.fxsave.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, byte_order);
+ }
+ if (reg >= fpu_xmm0 && reg <= fpu_xmm15) {
+ value.SetBytes(m_fpr.xstate.fxsave.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, byte_order);
+ }
+ if (reg >= fpu_ymm0 && reg <= fpu_ymm15) {
+ // Concatenate ymm using the register halves in xmm.bytes and ymmh.bytes
+ if (m_fpr_type == eXSAVE && CopyXSTATEtoYMM(reg, byte_order))
+ value.SetBytes(m_ymm_set.ymm[reg - fpu_ymm0].bytes, reg_info->byte_size, byte_order);
+ else
+ return false;
+ }
+ return value.GetType() == RegisterValue::eTypeBytes;
+ }
+ return false;
+ }
+
+ // Note that lldb uses slightly different naming conventions from sys/user.h
+ switch (reg)
+ {
+ default:
+ return false;
+ case fpu_dp:
+ value = m_fpr.xstate.fxsave.dp;
+ break;
+ case fpu_fcw:
+ value = m_fpr.xstate.fxsave.fcw;
+ break;
+ case fpu_fsw:
+ value = m_fpr.xstate.fxsave.fsw;
+ break;
+ case fpu_ip:
+ value = m_fpr.xstate.fxsave.ip;
+ break;
+ case fpu_fop:
+ value = m_fpr.xstate.fxsave.fop;
+ break;
+ case fpu_ftw:
+ value = m_fpr.xstate.fxsave.ftw;
+ break;
+ case fpu_mxcsr:
+ value = m_fpr.xstate.fxsave.mxcsr;
+ break;
+ case fpu_mxcsrmask:
+ value = m_fpr.xstate.fxsave.mxcsrmask;
+ break;
+ }
+ return true;
+}
+
+bool
+RegisterContext_x86_64::ReadAllRegisterValues(DataBufferSP &data_sp)
+{
+ bool success = false;
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (data_sp && ReadGPR () && ReadFPR ())
+ {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy (dst, &m_gpr, GetGPRSize());
+ dst += GetGPRSize();
+ }
+ if (m_fpr_type == eFXSAVE)
+ ::memcpy (dst, &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave));
+
+ if (m_fpr_type == eXSAVE) {
+ ByteOrder byte_order = GetByteOrder();
+
+ // Assemble the YMM register content from the register halves.
+ for (uint32_t reg = fpu_ymm0; success && reg <= fpu_ymm15; ++reg)
+ success = CopyXSTATEtoYMM(reg, byte_order);
+
+ if (success) {
+ // Copy the extended register state including the assembled ymm registers.
+ ::memcpy (dst, &m_fpr, sizeof(m_fpr));
+ }
+ }
+ }
+ return success;
+}
+
+bool
+RegisterContext_x86_64::WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ if (IsGPR(reg)) {
+ return WriteRegister(reg, value);
+ }
+
+ if (IsFPR(reg, m_fpr_type)) {
+ switch (reg)
+ {
+ default:
+ if (reg_info->encoding != eEncodingVector)
+ return false;
+
+ if (reg >= fpu_stmm0 && reg <= fpu_stmm7)
+ ::memcpy (m_fpr.xstate.fxsave.stmm[reg - fpu_stmm0].bytes, value.GetBytes(), value.GetByteSize());
+
+ if (reg >= fpu_xmm0 && reg <= fpu_xmm15)
+ ::memcpy (m_fpr.xstate.fxsave.xmm[reg - fpu_xmm0].bytes, value.GetBytes(), value.GetByteSize());
+
+ if (reg >= fpu_ymm0 && reg <= fpu_ymm15) {
+ if (m_fpr_type != eXSAVE)
+ return false; // the target processor does not support AVX
+
+ // Store ymm register content, and split into the register halves in xmm.bytes and ymmh.bytes
+ ::memcpy (m_ymm_set.ymm[reg - fpu_ymm0].bytes, value.GetBytes(), value.GetByteSize());
+ if (false == CopyYMMtoXSTATE(reg, GetByteOrder()))
+ return false;
+ }
+ break;
+ case fpu_dp:
+ m_fpr.xstate.fxsave.dp = value.GetAsUInt64();
+ break;
+ case fpu_fcw:
+ m_fpr.xstate.fxsave.fcw = value.GetAsUInt16();
+ break;
+ case fpu_fsw:
+ m_fpr.xstate.fxsave.fsw = value.GetAsUInt16();
+ break;
+ case fpu_ip:
+ m_fpr.xstate.fxsave.ip = value.GetAsUInt64();
+ break;
+ case fpu_fop:
+ m_fpr.xstate.fxsave.fop = value.GetAsUInt16();
+ break;
+ case fpu_ftw:
+ m_fpr.xstate.fxsave.ftw = value.GetAsUInt16();
+ break;
+ case fpu_mxcsr:
+ m_fpr.xstate.fxsave.mxcsr = value.GetAsUInt32();
+ break;
+ case fpu_mxcsrmask:
+ m_fpr.xstate.fxsave.mxcsrmask = value.GetAsUInt32();
+ break;
+ }
+ if (WriteFPR()) {
+ if (IsAVX(reg))
+ return CopyYMMtoXSTATE(reg, GetByteOrder());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+RegisterContext_x86_64::WriteAllRegisterValues(const DataBufferSP &data_sp)
+{
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE)
+ {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy (&m_gpr, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ if (m_fpr_type == eFXSAVE)
+ ::memcpy (&m_fpr.xstate.fxsave, src, sizeof(m_fpr.xstate.fxsave));
+ if (m_fpr_type == eXSAVE)
+ ::memcpy (&m_fpr.xstate.xsave, src, sizeof(m_fpr.xstate.xsave));
+
+ success = WriteFPR();
+ if (success) {
+ success = true;
+
+ if (m_fpr_type == eXSAVE) {
+ ByteOrder byte_order = GetByteOrder();
+
+ // Parse the YMM register content from the register halves.
+ for (uint32_t reg = fpu_ymm0; success && reg <= fpu_ymm15; ++reg)
+ success = CopyYMMtoXSTATE(reg, byte_order);
+ }
+ }
+ }
+ }
+ }
+ return success;
+}
+
+bool
+RegisterContext_x86_64::UpdateAfterBreakpoint()
+{
+ // PC points one byte past the int3 responsible for the breakpoint.
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ SetPC(pc - 1);
+ return true;
+}
+
+uint32_t
+RegisterContext_x86_64::ConvertRegisterKindToRegisterNumber(uint32_t kind,
+ uint32_t num)
+{
+ const Process *process = CalculateProcess().get();
+ if (process)
+ {
+ const ArchSpec arch = process->GetTarget().GetArchitecture();;
+ switch (arch.GetCore())
+ {
+ default:
+ assert(false && "CPU type not supported!");
+ break;
+
+ case ArchSpec::eCore_x86_32_i386:
+ case ArchSpec::eCore_x86_32_i486:
+ case ArchSpec::eCore_x86_32_i486sx:
+ {
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (num)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_eip;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_esp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_ebp;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+
+ if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
+ {
+ switch (num)
+ {
+ case dwarf_eax: return gpr_eax;
+ case dwarf_edx: return gpr_edx;
+ case dwarf_ecx: return gpr_ecx;
+ case dwarf_ebx: return gpr_ebx;
+ case dwarf_esi: return gpr_esi;
+ case dwarf_edi: return gpr_edi;
+ case dwarf_ebp: return gpr_ebp;
+ case dwarf_esp: return gpr_esp;
+ case dwarf_eip: return gpr_eip;
+ case dwarf_xmm0: return fpu_xmm0;
+ case dwarf_xmm1: return fpu_xmm1;
+ case dwarf_xmm2: return fpu_xmm2;
+ case dwarf_xmm3: return fpu_xmm3;
+ case dwarf_xmm4: return fpu_xmm4;
+ case dwarf_xmm5: return fpu_xmm5;
+ case dwarf_xmm6: return fpu_xmm6;
+ case dwarf_xmm7: return fpu_xmm7;
+ case dwarf_stmm0: return fpu_stmm0;
+ case dwarf_stmm1: return fpu_stmm1;
+ case dwarf_stmm2: return fpu_stmm2;
+ case dwarf_stmm3: return fpu_stmm3;
+ case dwarf_stmm4: return fpu_stmm4;
+ case dwarf_stmm5: return fpu_stmm5;
+ case dwarf_stmm6: return fpu_stmm6;
+ case dwarf_stmm7: return fpu_stmm7;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+
+ if (kind == eRegisterKindGDB)
+ {
+ switch (num)
+ {
+ case gdb_eax : return gpr_eax;
+ case gdb_ebx : return gpr_ebx;
+ case gdb_ecx : return gpr_ecx;
+ case gdb_edx : return gpr_edx;
+ case gdb_esi : return gpr_esi;
+ case gdb_edi : return gpr_edi;
+ case gdb_ebp : return gpr_ebp;
+ case gdb_esp : return gpr_esp;
+ case gdb_eip : return gpr_eip;
+ case gdb_eflags : return gpr_eflags;
+ case gdb_cs : return gpr_cs;
+ case gdb_ss : return gpr_ss;
+ case gdb_ds : return gpr_ds;
+ case gdb_es : return gpr_es;
+ case gdb_fs : return gpr_fs;
+ case gdb_gs : return gpr_gs;
+ case gdb_stmm0 : return fpu_stmm0;
+ case gdb_stmm1 : return fpu_stmm1;
+ case gdb_stmm2 : return fpu_stmm2;
+ case gdb_stmm3 : return fpu_stmm3;
+ case gdb_stmm4 : return fpu_stmm4;
+ case gdb_stmm5 : return fpu_stmm5;
+ case gdb_stmm6 : return fpu_stmm6;
+ case gdb_stmm7 : return fpu_stmm7;
+ case gdb_fcw : return fpu_fcw;
+ case gdb_fsw : return fpu_fsw;
+ case gdb_ftw : return fpu_ftw;
+ case gdb_fpu_cs : return fpu_cs;
+ case gdb_ip : return fpu_ip;
+ case gdb_fpu_ds : return fpu_ds; //fpu_fos
+ case gdb_dp : return fpu_dp; //fpu_foo
+ case gdb_fop : return fpu_fop;
+ case gdb_xmm0 : return fpu_xmm0;
+ case gdb_xmm1 : return fpu_xmm1;
+ case gdb_xmm2 : return fpu_xmm2;
+ case gdb_xmm3 : return fpu_xmm3;
+ case gdb_xmm4 : return fpu_xmm4;
+ case gdb_xmm5 : return fpu_xmm5;
+ case gdb_xmm6 : return fpu_xmm6;
+ case gdb_xmm7 : return fpu_xmm7;
+ case gdb_mxcsr : return fpu_mxcsr;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+ else if (kind == eRegisterKindLLDB)
+ {
+ return num;
+ }
+
+ break;
+ }
+
+ case ArchSpec::eCore_x86_64_x86_64:
+ {
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (num)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_rip;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_rsp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_rbp;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+
+ if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
+ {
+ switch (num)
+ {
+ case gcc_dwarf_gpr_rax: return gpr_rax;
+ case gcc_dwarf_gpr_rdx: return gpr_rdx;
+ case gcc_dwarf_gpr_rcx: return gpr_rcx;
+ case gcc_dwarf_gpr_rbx: return gpr_rbx;
+ case gcc_dwarf_gpr_rsi: return gpr_rsi;
+ case gcc_dwarf_gpr_rdi: return gpr_rdi;
+ case gcc_dwarf_gpr_rbp: return gpr_rbp;
+ case gcc_dwarf_gpr_rsp: return gpr_rsp;
+ case gcc_dwarf_gpr_r8: return gpr_r8;
+ case gcc_dwarf_gpr_r9: return gpr_r9;
+ case gcc_dwarf_gpr_r10: return gpr_r10;
+ case gcc_dwarf_gpr_r11: return gpr_r11;
+ case gcc_dwarf_gpr_r12: return gpr_r12;
+ case gcc_dwarf_gpr_r13: return gpr_r13;
+ case gcc_dwarf_gpr_r14: return gpr_r14;
+ case gcc_dwarf_gpr_r15: return gpr_r15;
+ case gcc_dwarf_gpr_rip: return gpr_rip;
+ case gcc_dwarf_fpu_xmm0: return fpu_xmm0;
+ case gcc_dwarf_fpu_xmm1: return fpu_xmm1;
+ case gcc_dwarf_fpu_xmm2: return fpu_xmm2;
+ case gcc_dwarf_fpu_xmm3: return fpu_xmm3;
+ case gcc_dwarf_fpu_xmm4: return fpu_xmm4;
+ case gcc_dwarf_fpu_xmm5: return fpu_xmm5;
+ case gcc_dwarf_fpu_xmm6: return fpu_xmm6;
+ case gcc_dwarf_fpu_xmm7: return fpu_xmm7;
+ case gcc_dwarf_fpu_xmm8: return fpu_xmm8;
+ case gcc_dwarf_fpu_xmm9: return fpu_xmm9;
+ case gcc_dwarf_fpu_xmm10: return fpu_xmm10;
+ case gcc_dwarf_fpu_xmm11: return fpu_xmm11;
+ case gcc_dwarf_fpu_xmm12: return fpu_xmm12;
+ case gcc_dwarf_fpu_xmm13: return fpu_xmm13;
+ case gcc_dwarf_fpu_xmm14: return fpu_xmm14;
+ case gcc_dwarf_fpu_xmm15: return fpu_xmm15;
+ case gcc_dwarf_fpu_stmm0: return fpu_stmm0;
+ case gcc_dwarf_fpu_stmm1: return fpu_stmm1;
+ case gcc_dwarf_fpu_stmm2: return fpu_stmm2;
+ case gcc_dwarf_fpu_stmm3: return fpu_stmm3;
+ case gcc_dwarf_fpu_stmm4: return fpu_stmm4;
+ case gcc_dwarf_fpu_stmm5: return fpu_stmm5;
+ case gcc_dwarf_fpu_stmm6: return fpu_stmm6;
+ case gcc_dwarf_fpu_stmm7: return fpu_stmm7;
+ case gcc_dwarf_fpu_ymm0: return fpu_ymm0;
+ case gcc_dwarf_fpu_ymm1: return fpu_ymm1;
+ case gcc_dwarf_fpu_ymm2: return fpu_ymm2;
+ case gcc_dwarf_fpu_ymm3: return fpu_ymm3;
+ case gcc_dwarf_fpu_ymm4: return fpu_ymm4;
+ case gcc_dwarf_fpu_ymm5: return fpu_ymm5;
+ case gcc_dwarf_fpu_ymm6: return fpu_ymm6;
+ case gcc_dwarf_fpu_ymm7: return fpu_ymm7;
+ case gcc_dwarf_fpu_ymm8: return fpu_ymm8;
+ case gcc_dwarf_fpu_ymm9: return fpu_ymm9;
+ case gcc_dwarf_fpu_ymm10: return fpu_ymm10;
+ case gcc_dwarf_fpu_ymm11: return fpu_ymm11;
+ case gcc_dwarf_fpu_ymm12: return fpu_ymm12;
+ case gcc_dwarf_fpu_ymm13: return fpu_ymm13;
+ case gcc_dwarf_fpu_ymm14: return fpu_ymm14;
+ case gcc_dwarf_fpu_ymm15: return fpu_ymm15;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+
+ if (kind == eRegisterKindGDB)
+ {
+ switch (num)
+ {
+ case gdb_gpr_rax : return gpr_rax;
+ case gdb_gpr_rbx : return gpr_rbx;
+ case gdb_gpr_rcx : return gpr_rcx;
+ case gdb_gpr_rdx : return gpr_rdx;
+ case gdb_gpr_rsi : return gpr_rsi;
+ case gdb_gpr_rdi : return gpr_rdi;
+ case gdb_gpr_rbp : return gpr_rbp;
+ case gdb_gpr_rsp : return gpr_rsp;
+ case gdb_gpr_r8 : return gpr_r8;
+ case gdb_gpr_r9 : return gpr_r9;
+ case gdb_gpr_r10 : return gpr_r10;
+ case gdb_gpr_r11 : return gpr_r11;
+ case gdb_gpr_r12 : return gpr_r12;
+ case gdb_gpr_r13 : return gpr_r13;
+ case gdb_gpr_r14 : return gpr_r14;
+ case gdb_gpr_r15 : return gpr_r15;
+ case gdb_gpr_rip : return gpr_rip;
+ case gdb_gpr_rflags : return gpr_rflags;
+ case gdb_gpr_cs : return gpr_cs;
+ case gdb_gpr_ss : return gpr_ss;
+ case gdb_gpr_ds : return gpr_ds;
+ case gdb_gpr_es : return gpr_es;
+ case gdb_gpr_fs : return gpr_fs;
+ case gdb_gpr_gs : return gpr_gs;
+ case gdb_fpu_stmm0 : return fpu_stmm0;
+ case gdb_fpu_stmm1 : return fpu_stmm1;
+ case gdb_fpu_stmm2 : return fpu_stmm2;
+ case gdb_fpu_stmm3 : return fpu_stmm3;
+ case gdb_fpu_stmm4 : return fpu_stmm4;
+ case gdb_fpu_stmm5 : return fpu_stmm5;
+ case gdb_fpu_stmm6 : return fpu_stmm6;
+ case gdb_fpu_stmm7 : return fpu_stmm7;
+ case gdb_fpu_fcw : return fpu_fcw;
+ case gdb_fpu_fsw : return fpu_fsw;
+ case gdb_fpu_ftw : return fpu_ftw;
+ case gdb_fpu_cs_64 : return fpu_cs;
+ case gdb_fpu_ip : return fpu_ip;
+ case gdb_fpu_ds_64 : return fpu_ds;
+ case gdb_fpu_dp : return fpu_dp;
+ case gdb_fpu_fop : return fpu_fop;
+ case gdb_fpu_xmm0 : return fpu_xmm0;
+ case gdb_fpu_xmm1 : return fpu_xmm1;
+ case gdb_fpu_xmm2 : return fpu_xmm2;
+ case gdb_fpu_xmm3 : return fpu_xmm3;
+ case gdb_fpu_xmm4 : return fpu_xmm4;
+ case gdb_fpu_xmm5 : return fpu_xmm5;
+ case gdb_fpu_xmm6 : return fpu_xmm6;
+ case gdb_fpu_xmm7 : return fpu_xmm7;
+ case gdb_fpu_xmm8 : return fpu_xmm8;
+ case gdb_fpu_xmm9 : return fpu_xmm9;
+ case gdb_fpu_xmm10 : return fpu_xmm10;
+ case gdb_fpu_xmm11 : return fpu_xmm11;
+ case gdb_fpu_xmm12 : return fpu_xmm12;
+ case gdb_fpu_xmm13 : return fpu_xmm13;
+ case gdb_fpu_xmm14 : return fpu_xmm14;
+ case gdb_fpu_xmm15 : return fpu_xmm15;
+ case gdb_fpu_mxcsr : return fpu_mxcsr;
+ case gdb_fpu_ymm0 : return fpu_ymm0;
+ case gdb_fpu_ymm1 : return fpu_ymm1;
+ case gdb_fpu_ymm2 : return fpu_ymm2;
+ case gdb_fpu_ymm3 : return fpu_ymm3;
+ case gdb_fpu_ymm4 : return fpu_ymm4;
+ case gdb_fpu_ymm5 : return fpu_ymm5;
+ case gdb_fpu_ymm6 : return fpu_ymm6;
+ case gdb_fpu_ymm7 : return fpu_ymm7;
+ case gdb_fpu_ymm8 : return fpu_ymm8;
+ case gdb_fpu_ymm9 : return fpu_ymm9;
+ case gdb_fpu_ymm10 : return fpu_ymm10;
+ case gdb_fpu_ymm11 : return fpu_ymm11;
+ case gdb_fpu_ymm12 : return fpu_ymm12;
+ case gdb_fpu_ymm13 : return fpu_ymm13;
+ case gdb_fpu_ymm14 : return fpu_ymm14;
+ case gdb_fpu_ymm15 : return fpu_ymm15;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ }
+ else if (kind == eRegisterKindLLDB)
+ {
+ return num;
+ }
+ }
+ }
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
+
+uint32_t
+RegisterContext_x86_64::NumSupportedHardwareWatchpoints()
+{
+ // Available debug address registers: dr0, dr1, dr2, dr3
+ return 4;
+}
+
+bool
+RegisterContext_x86_64::IsWatchpointVacant(uint32_t hw_index)
+{
+ bool is_vacant = false;
+ RegisterValue value;
+
+ assert(hw_index < NumSupportedHardwareWatchpoints());
+
+ if (m_watchpoints_initialized == false)
+ {
+ // Reset the debug status and debug control registers
+ RegisterValue zero_bits = RegisterValue(uint64_t(0));
+ if (!WriteRegister(dr6, zero_bits) || !WriteRegister(dr7, zero_bits))
+ assert(false && "Could not initialize watchpoint registers");
+ m_watchpoints_initialized = true;
+ }
+
+ if (ReadRegister(dr7, value))
+ {
+ uint64_t val = value.GetAsUInt64();
+ is_vacant = (val & (3 << 2*hw_index)) == 0;
+ }
+
+ return is_vacant;
+}
+
+static uint32_t
+size_and_rw_bits(size_t size, bool read, bool write)
+{
+ uint32_t rw;
+ if (read) {
+ rw = 0x3; // READ or READ/WRITE
+ } else if (write) {
+ rw = 0x1; // WRITE
+ } else {
+ assert(0 && "read and write cannot both be false");
+ }
+
+ switch (size) {
+ case 1:
+ return rw;
+ case 2:
+ return (0x1 << 2) | rw;
+ case 4:
+ return (0x3 << 2) | rw;
+ case 8:
+ return (0x2 << 2) | rw;
+ default:
+ assert(0 && "invalid size, must be one of 1, 2, 4, or 8");
+ }
+}
+
+uint32_t
+RegisterContext_x86_64::SetHardwareWatchpoint(addr_t addr, size_t size,
+ bool read, bool write)
+{
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index)
+ {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size,
+ read, write,
+ hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+RegisterContext_x86_64::SetHardwareWatchpointWithIndex(addr_t addr, size_t size,
+ bool read, bool write,
+ uint32_t hw_index)
+{
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ if (num_hw_watchpoints == 0 || hw_index >= num_hw_watchpoints)
+ return false;
+
+ if (!(size == 1 || size == 2 || size == 4 || size == 8))
+ return false;
+
+ if (read == false && write == false)
+ return false;
+
+ if (!IsWatchpointVacant(hw_index))
+ return false;
+
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/gloabl enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write,
+ // 0b10: io read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ if (hw_index < num_hw_watchpoints)
+ {
+ RegisterValue current_dr7_bits;
+
+ if (ReadRegister(dr7, current_dr7_bits))
+ {
+ uint64_t new_dr7_bits = current_dr7_bits.GetAsUInt64() |
+ (1 << (2*hw_index) |
+ size_and_rw_bits(size, read, write) <<
+ (16+4*hw_index));
+
+ if (WriteRegister(dr0 + hw_index, RegisterValue(addr)) &&
+ WriteRegister(dr7, RegisterValue(new_dr7_bits)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+RegisterContext_x86_64::ClearHardwareWatchpoint(uint32_t hw_index)
+{
+ if (hw_index < NumSupportedHardwareWatchpoints())
+ {
+ RegisterValue current_dr7_bits;
+
+ if (ReadRegister(dr7, current_dr7_bits))
+ {
+ uint64_t new_dr7_bits = current_dr7_bits.GetAsUInt64() & ~(3 << (2*hw_index));
+
+ if (WriteRegister(dr7, RegisterValue(new_dr7_bits)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+RegisterContext_x86_64::IsWatchpointHit(uint32_t hw_index)
+{
+ bool is_hit = false;
+
+ if (m_watchpoints_initialized == false)
+ {
+ // Reset the debug status and debug control registers
+ RegisterValue zero_bits = RegisterValue(uint64_t(0));
+ if (!WriteRegister(dr6, zero_bits) || !WriteRegister(dr7, zero_bits))
+ assert(false && "Could not initialize watchpoint registers");
+ m_watchpoints_initialized = true;
+ }
+
+ if (hw_index < NumSupportedHardwareWatchpoints())
+ {
+ RegisterValue value;
+
+ if (ReadRegister(dr6, value))
+ {
+ uint64_t val = value.GetAsUInt64();
+ is_hit = val & (1 << hw_index);
+ }
+ }
+
+ return is_hit;
+}
+
+addr_t
+RegisterContext_x86_64::GetWatchpointAddress(uint32_t hw_index)
+{
+ addr_t wp_monitor_addr = LLDB_INVALID_ADDRESS;
+
+ if (hw_index < NumSupportedHardwareWatchpoints())
+ {
+ if (!IsWatchpointVacant(hw_index))
+ {
+ RegisterValue value;
+
+ if (ReadRegister(dr0 + hw_index, value))
+ wp_monitor_addr = value.GetAsUInt64();
+ }
+ }
+
+ return wp_monitor_addr;
+}
+
+
+bool
+RegisterContext_x86_64::ClearWatchpointHits()
+{
+ return WriteRegister(dr6, RegisterValue((uint64_t)0));
+}
+
+bool
+RegisterContext_x86_64::HardwareSingleStep(bool enable)
+{
+ enum { TRACE_BIT = 0x100 };
+ uint64_t rflags;
+
+ if ((rflags = ReadRegisterAsUnsigned(gpr_rflags, -1UL)) == -1UL)
+ return false;
+
+ if (enable)
+ {
+ if (rflags & TRACE_BIT)
+ return true;
+
+ rflags |= TRACE_BIT;
+ }
+ else
+ {
+ if (!(rflags & TRACE_BIT))
+ return false;
+
+ rflags &= ~TRACE_BIT;
+ }
+
+ return WriteRegisterFromUnsigned(gpr_rflags, rflags);
+}
+
+#if defined(__linux__) or defined(__FreeBSD__)
+
+ProcessMonitor &
+RegisterContext_x86_64::GetMonitor()
+{
+ ProcessSP base = CalculateProcess();
+ ProcessPOSIX *process = static_cast<ProcessPOSIX*>(base.get());
+ return process->GetMonitor();
+}
+
+bool
+RegisterContext_x86_64::ReadGPR()
+{
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr, GetGPRSize());
+}
+
+bool
+RegisterContext_x86_64::ReadFPR()
+{
+ ProcessMonitor &monitor = GetMonitor();
+ if (m_fpr_type == eFXSAVE)
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave));
+
+ if (m_fpr_type == eXSAVE)
+ return monitor.ReadRegisterSet(m_thread.GetID(), &m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE);
+ return false;
+}
+
+bool
+RegisterContext_x86_64::WriteGPR()
+{
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr, GetGPRSize());
+}
+
+bool
+RegisterContext_x86_64::WriteFPR()
+{
+ ProcessMonitor &monitor = GetMonitor();
+ if (m_fpr_type == eFXSAVE)
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave));
+
+ if (m_fpr_type == eXSAVE)
+ return monitor.WriteRegisterSet(m_thread.GetID(), &m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE);
+ return false;
+}
+
+bool
+RegisterContext_x86_64::ReadRegister(const unsigned reg,
+ RegisterValue &value)
+{
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(),
+ GetRegisterOffset(reg),
+ GetRegisterName(reg),
+ GetRegisterSize(reg),
+ value);
+}
+
+bool
+RegisterContext_x86_64::WriteRegister(const unsigned reg,
+ const RegisterValue &value)
+{
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(m_thread.GetID(),
+ GetRegisterOffset(reg),
+ GetRegisterName(reg),
+ value);
+}
+
+#else
+
+bool
+RegisterContext_x86_64::ReadGPR()
+{
+ llvm_unreachable("not implemented");
+ return false;
+}
+
+bool
+RegisterContext_x86_64::ReadFPR()
+{
+ llvm_unreachable("not implemented");
+ return false;
+}
+
+bool
+RegisterContext_x86_64::WriteGPR()
+{
+ llvm_unreachable("not implemented");
+ return false;
+}
+
+bool
+RegisterContext_x86_64::WriteFPR()
+{
+ llvm_unreachable("not implemented");
+ return false;
+}
+
+bool
+RegisterContext_x86_64::ReadRegister(const unsigned reg,
+ RegisterValue &value)
+{
+ llvm_unreachable("not implemented");
+ return false;
+}
+
+bool
+RegisterContext_x86_64::WriteRegister(const unsigned reg,
+ const RegisterValue &value)
+{
+ llvm_unreachable("not implemented");
+ return false;
+}
+
+#endif
diff --git a/source/Plugins/Process/POSIX/RegisterContext_x86_64.h b/source/Plugins/Process/POSIX/RegisterContext_x86_64.h
new file mode 100644
index 0000000..9d59bd7
--- /dev/null
+++ b/source/Plugins/Process/POSIX/RegisterContext_x86_64.h
@@ -0,0 +1,347 @@
+//===-- RegisterContext_x86_64.h ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContext_x86_64_H_
+#define liblldb_RegisterContext_x86_64_H_
+
+#include "lldb/Core/Log.h"
+#include "RegisterContextPOSIX.h"
+
+class ProcessMonitor;
+
+// Internal codes for all x86_64 registers.
+enum
+{
+ k_first_gpr,
+ gpr_rax = k_first_gpr,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs,
+ gpr_ss,
+ gpr_ds,
+ gpr_es,
+ k_first_i386,
+ gpr_eax = k_first_i386,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_eip,
+ gpr_eflags, // eRegisterKindLLDB == 33
+ k_last_i386 = gpr_eflags,
+ k_last_gpr = gpr_eflags,
+
+ k_first_fpr,
+ fpu_fcw = k_first_fpr,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15,
+ k_last_fpr = fpu_xmm15,
+ k_first_avx,
+ fpu_ymm0 = k_first_avx,
+ fpu_ymm1,
+ fpu_ymm2,
+ fpu_ymm3,
+ fpu_ymm4,
+ fpu_ymm5,
+ fpu_ymm6,
+ fpu_ymm7,
+ fpu_ymm8,
+ fpu_ymm9,
+ fpu_ymm10,
+ fpu_ymm11,
+ fpu_ymm12,
+ fpu_ymm13,
+ fpu_ymm14,
+ fpu_ymm15,
+ k_last_avx = fpu_ymm15,
+
+ dr0,
+ dr1,
+ dr2,
+ dr3,
+ dr4,
+ dr5,
+ dr6,
+ dr7,
+
+ k_num_registers,
+ k_num_gpr_registers = k_last_gpr - k_first_gpr + 1,
+ k_num_fpr_registers = k_last_fpr - k_first_fpr + 1,
+ k_num_avx_registers = k_last_avx - k_first_avx + 1
+};
+
+class RegisterContext_x86_64
+ : public RegisterContextPOSIX
+{
+public:
+ RegisterContext_x86_64 (lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContext_x86_64();
+
+ void
+ Invalidate();
+
+ void
+ InvalidateAllRegisters();
+
+ size_t
+ GetRegisterCount();
+
+ virtual size_t
+ GetGPRSize() = 0;
+
+ virtual unsigned
+ GetRegisterSize(unsigned reg);
+
+ virtual unsigned
+ GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex(size_t reg);
+
+ size_t
+ GetRegisterSetCount();
+
+ const lldb_private::RegisterSet *
+ GetRegisterSet(size_t set);
+
+ unsigned
+ GetRegisterIndexFromOffset(unsigned offset);
+
+ const char *
+ GetRegisterName(unsigned reg);
+
+ virtual bool
+ ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool
+ ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool
+ WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t
+ ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num);
+
+ uint32_t
+ NumSupportedHardwareWatchpoints();
+
+ uint32_t
+ SetHardwareWatchpoint(lldb::addr_t, size_t size, bool read, bool write);
+
+ bool
+ SetHardwareWatchpointWithIndex(lldb::addr_t, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ bool
+ ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool
+ HardwareSingleStep(bool enable);
+
+ bool
+ UpdateAfterBreakpoint();
+
+ bool
+ IsWatchpointVacant(uint32_t hw_index);
+
+ bool
+ IsWatchpointHit (uint32_t hw_index);
+
+ lldb::addr_t
+ GetWatchpointAddress (uint32_t hw_index);
+
+ bool
+ ClearWatchpointHits();
+
+ //---------------------------------------------------------------------------
+ // Generic floating-point registers
+ //---------------------------------------------------------------------------
+
+ struct MMSReg
+ {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg
+ {
+ uint8_t bytes[16]; // 128-bits for each XMM register
+ };
+
+ struct FXSAVE
+ {
+ uint16_t fcw;
+ uint16_t fsw;
+ uint16_t ftw;
+ uint16_t fop;
+ uint64_t ip;
+ uint64_t dp;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[16];
+ uint32_t padding[24];
+ };
+
+ //---------------------------------------------------------------------------
+ // Extended floating-point registers
+ //---------------------------------------------------------------------------
+ struct YMMHReg
+ {
+ uint8_t bytes[16]; // 16 * 8 bits for the high bytes of each YMM register
+ };
+
+ struct YMMReg
+ {
+ uint8_t bytes[32]; // 16 * 16 bits for each YMM register
+ };
+
+ struct YMM
+ {
+ YMMReg ymm[16]; // assembled from ymmh and xmm registers
+ };
+
+ struct XSAVE_HDR
+ {
+ uint64_t xstate_bv; // OS enabled xstate mask to determine the extended states supported by the processor
+ uint64_t reserved1[2];
+ uint64_t reserved2[5];
+ } __attribute__((packed));
+
+ // x86 extensions to FXSAVE (i.e. for AVX processors)
+ struct XSAVE
+ {
+ FXSAVE i387; // floating point registers typical in i387_fxsave_struct
+ XSAVE_HDR header; // The xsave_hdr_struct can be used to determine if the following extensions are usable
+ YMMHReg ymmh[16]; // High 16 bytes of each of 16 YMM registers (the low bytes are in FXSAVE.xmm for compatibility with SSE)
+ // Slot any extensions to the register file here
+ } __attribute__((packed, aligned (64)));
+
+ struct IOVEC
+ {
+ void *iov_base; // pointer to XSAVE
+ size_t iov_len; // sizeof(XSAVE)
+ };
+
+ //---------------------------------------------------------------------------
+ // Note: prefer kernel definitions over user-land
+ //---------------------------------------------------------------------------
+ enum FPRType
+ {
+ eNotValid = 0,
+ eFSAVE, // TODO
+ eFXSAVE,
+ eSOFT, // TODO
+ eXSAVE
+ };
+
+ // Floating-point registers
+ struct FPR
+ {
+ // Thread state for the floating-point unit of the processor read by ptrace.
+ union XSTATE {
+ FXSAVE fxsave; // Generic floating-point registers.
+ XSAVE xsave; // x86 extended processor state.
+ } xstate;
+ };
+
+protected:
+ // Determines if an extended register set is supported on the processor running the inferior process.
+ virtual bool
+ IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfo();
+
+ virtual bool
+ ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ virtual bool
+ WriteRegister(const unsigned reg, const lldb_private::RegisterValue &value);
+
+private:
+ uint64_t m_gpr[k_num_gpr_registers]; // general purpose registers.
+ FPRType m_fpr_type; // determines the type of data stored by union FPR, if any.
+ FPR m_fpr; // floating-point registers including extended register sets.
+ IOVEC m_iovec; // wrapper for xsave.
+ YMM m_ymm_set; // copy of ymmh and xmm register halves.
+
+ ProcessMonitor &GetMonitor();
+ lldb::ByteOrder GetByteOrder();
+
+ bool CopyXSTATEtoYMM(uint32_t reg, lldb::ByteOrder byte_order);
+ bool CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order);
+ bool IsFPR(unsigned reg, FPRType fpr_type);
+
+ bool ReadGPR();
+ bool ReadFPR();
+
+ bool WriteGPR();
+ bool WriteFPR();
+};
+
+#endif // #ifndef liblldb_RegisterContext_x86_64_H_
diff --git a/source/Plugins/Process/Utility/ARMDefines.h b/source/Plugins/Process/Utility/ARMDefines.h
new file mode 100644
index 0000000..4b1f06a
--- /dev/null
+++ b/source/Plugins/Process/Utility/ARMDefines.h
@@ -0,0 +1,110 @@
+//===-- lldb_ARMDefines.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_ARMDefines_h_
+#define lldb_ARMDefines_h_
+
+// Common defintions for the ARM/Thumb Instruction Set Architecture.
+
+namespace lldb_private {
+
+// ARM shifter types
+typedef enum
+{
+ SRType_LSL,
+ SRType_LSR,
+ SRType_ASR,
+ SRType_ROR,
+ SRType_RRX,
+ SRType_Invalid
+} ARM_ShifterType;
+
+// ARM conditions // Meaning (integer) Meaning (floating-point) Condition flags
+#define COND_EQ 0x0 // Equal Equal Z == 1
+#define COND_NE 0x1 // Not equal Not equal, or unordered Z == 0
+#define COND_CS 0x2 // Carry set >, ==, or unordered C == 1
+#define COND_HS 0x2
+#define COND_CC 0x3 // Carry clear Less than C == 0
+#define COND_LO 0x3
+#define COND_MI 0x4 // Minus, negative Less than N == 1
+#define COND_PL 0x5 // Plus, positive or zero >, ==, or unordered N == 0
+#define COND_VS 0x6 // Overflow Unordered V == 1
+#define COND_VC 0x7 // No overflow Not unordered V == 0
+#define COND_HI 0x8 // Unsigned higher Greater than, or unordered C == 1 and Z == 0
+#define COND_LS 0x9 // Unsigned lower or same Less than or equal C == 0 or Z == 1
+#define COND_GE 0xA // Greater than or equal Greater than or equal N == V
+#define COND_LT 0xB // Less than Less than, or unordered N != V
+#define COND_GT 0xC // Greater than Greater than Z == 0 and N == V
+#define COND_LE 0xD // Less than or equal <, ==, or unordered Z == 1 or N != V
+#define COND_AL 0xE // Always (unconditional) Always (unconditional) Any
+#define COND_UNCOND 0xF
+
+static inline const char *ARMCondCodeToString(uint32_t CC)
+{
+ switch (CC) {
+ default: assert(0 && "Unknown condition code");
+ case COND_EQ: return "eq";
+ case COND_NE: return "ne";
+ case COND_HS: return "hs";
+ case COND_LO: return "lo";
+ case COND_MI: return "mi";
+ case COND_PL: return "pl";
+ case COND_VS: return "vs";
+ case COND_VC: return "vc";
+ case COND_HI: return "hi";
+ case COND_LS: return "ls";
+ case COND_GE: return "ge";
+ case COND_LT: return "lt";
+ case COND_GT: return "gt";
+ case COND_LE: return "le";
+ case COND_AL: return "al";
+ }
+}
+
+// Bit positions for CPSR
+#define CPSR_T_POS 5
+#define CPSR_F_POS 6
+#define CPSR_I_POS 7
+#define CPSR_A_POS 8
+#define CPSR_E_POS 9
+#define CPSR_J_POS 24
+#define CPSR_Q_POS 27
+#define CPSR_V_POS 28
+#define CPSR_C_POS 29
+#define CPSR_Z_POS 30
+#define CPSR_N_POS 31
+
+// CPSR mode definitions
+#define CPSR_MODE_USR 0x10u
+#define CPSR_MODE_FIQ 0x11u
+#define CPSR_MODE_IRQ 0x12u
+#define CPSR_MODE_SVC 0x13u
+#define CPSR_MODE_ABT 0x17u
+#define CPSR_MODE_UND 0x1bu
+#define CPSR_MODE_SYS 0x1fu
+
+// Masks for CPSR
+#define MASK_CPSR_MODE_MASK (0x0000001fu)
+#define MASK_CPSR_IT_MASK (0x0600fc00u)
+#define MASK_CPSR_T (1u << CPSR_T_POS)
+#define MASK_CPSR_F (1u << CPSR_F_POS)
+#define MASK_CPSR_I (1u << CPSR_I_POS)
+#define MASK_CPSR_A (1u << CPSR_A_POS)
+#define MASK_CPSR_E (1u << CPSR_E_POS)
+#define MASK_CPSR_GE_MASK (0x000f0000u)
+#define MASK_CPSR_J (1u << CPSR_J_POS)
+#define MASK_CPSR_Q (1u << CPSR_Q_POS)
+#define MASK_CPSR_V (1u << CPSR_V_POS)
+#define MASK_CPSR_C (1u << CPSR_C_POS)
+#define MASK_CPSR_Z (1u << CPSR_Z_POS)
+#define MASK_CPSR_N (1u << CPSR_N_POS)
+
+} // namespace lldb_private
+
+#endif // lldb_ARMDefines_h_
diff --git a/source/Plugins/Process/Utility/ARMUtils.h b/source/Plugins/Process/Utility/ARMUtils.h
new file mode 100644
index 0000000..76d64e1
--- /dev/null
+++ b/source/Plugins/Process/Utility/ARMUtils.h
@@ -0,0 +1,394 @@
+//===-- ARMUtils.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_ARMUtils_h_
+#define lldb_ARMUtils_h_
+
+#include "ARMDefines.h"
+#include "InstructionUtils.h"
+#include "llvm/Support/MathExtras.h" // for SignExtend64 template function
+
+// Common utilities for the ARM/Thumb Instruction Set Architecture.
+
+namespace lldb_private {
+
+static inline uint32_t Align(uint32_t val, uint32_t alignment)
+{
+ return alignment * (val / alignment);
+}
+
+static inline uint32_t DecodeImmShift(const uint32_t type, const uint32_t imm5, ARM_ShifterType &shift_t)
+{
+ switch (type)
+ {
+ default:
+ //assert(0 && "Invalid shift type");
+ case 0:
+ shift_t = SRType_LSL;
+ return imm5;
+ case 1:
+ shift_t = SRType_LSR;
+ return (imm5 == 0 ? 32 : imm5);
+ case 2:
+ shift_t = SRType_ASR;
+ return (imm5 == 0 ? 32 : imm5);
+ case 3:
+ if (imm5 == 0)
+ {
+ shift_t = SRType_RRX;
+ return 1;
+ }
+ else
+ {
+ shift_t = SRType_ROR;
+ return imm5;
+ }
+ }
+ shift_t = SRType_Invalid;
+ return UINT32_MAX;
+
+}
+
+// A8.6.35 CMP (register) -- Encoding T3
+// Convenience function.
+static inline uint32_t DecodeImmShiftThumb(const uint32_t opcode, ARM_ShifterType &shift_t)
+{
+ return DecodeImmShift(Bits32(opcode, 5, 4), Bits32(opcode, 14, 12)<<2 | Bits32(opcode, 7, 6), shift_t);
+}
+
+// A8.6.35 CMP (register) -- Encoding A1
+// Convenience function.
+static inline uint32_t DecodeImmShiftARM(const uint32_t opcode, ARM_ShifterType &shift_t)
+{
+ return DecodeImmShift(Bits32(opcode, 6, 5), Bits32(opcode, 11, 7), shift_t);
+}
+
+static inline uint32_t DecodeImmShift(const ARM_ShifterType shift_t, const uint32_t imm5)
+{
+ ARM_ShifterType dont_care;
+ return DecodeImmShift(shift_t, imm5, dont_care);
+}
+
+static inline ARM_ShifterType DecodeRegShift(const uint32_t type)
+{
+ switch (type) {
+ default:
+ //assert(0 && "Invalid shift type");
+ return SRType_Invalid;
+ case 0:
+ return SRType_LSL;
+ case 1:
+ return SRType_LSR;
+ case 2:
+ return SRType_ASR;
+ case 3:
+ return SRType_ROR;
+ }
+}
+
+static inline uint32_t LSL_C(const uint32_t value, const uint32_t amount, uint32_t &carry_out, bool *success)
+{
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ carry_out = amount <= 32 ? Bit32(value, 32 - amount) : 0;
+ return value << amount;
+}
+
+static inline uint32_t LSL(const uint32_t value, const uint32_t amount, bool *success)
+{
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = LSL_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t LSR_C(const uint32_t value, const uint32_t amount, uint32_t &carry_out, bool *success)
+{
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ carry_out = amount <= 32 ? Bit32(value, amount - 1) : 0;
+ return value >> amount;
+}
+
+static inline uint32_t LSR(const uint32_t value, const uint32_t amount, bool *success)
+{
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = LSR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t ASR_C(const uint32_t value, const uint32_t amount, uint32_t &carry_out, bool *success)
+{
+ if (amount == 0 || amount > 32) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ bool negative = BitIsSet(value, 31);
+ if (amount <= 32)
+ {
+ carry_out = Bit32(value, amount - 1);
+ int64_t extended = llvm::SignExtend64<32>(value);
+ return UnsignedBits(extended, amount + 31, amount);
+ }
+ else
+ {
+ carry_out = (negative ? 1 : 0);
+ return (negative ? 0xffffffff : 0);
+ }
+}
+
+static inline uint32_t ASR(const uint32_t value, const uint32_t amount, bool *success)
+{
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = ASR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t ROR_C(const uint32_t value, const uint32_t amount, uint32_t &carry_out, bool *success)
+{
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ uint32_t amt = amount % 32;
+ uint32_t result = Rotr32(value, amt);
+ carry_out = Bit32(value, 31);
+ return result;
+}
+
+static inline uint32_t ROR(const uint32_t value, const uint32_t amount, bool *success)
+{
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = ROR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t RRX_C(const uint32_t value, const uint32_t carry_in, uint32_t &carry_out, bool *success)
+{
+ *success = true;
+ carry_out = Bit32(value, 0);
+ return Bit32(carry_in, 0) << 31 | Bits32(value, 31, 1);
+}
+
+static inline uint32_t RRX(const uint32_t value, const uint32_t carry_in, bool *success)
+{
+ *success = true;
+ uint32_t dont_care;
+ uint32_t result = RRX_C(value, carry_in, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t Shift_C(const uint32_t value, ARM_ShifterType type, const uint32_t amount,
+ const uint32_t carry_in, uint32_t &carry_out, bool *success)
+{
+ if (type == SRType_RRX && amount != 1) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+
+ if (amount == 0) {
+ carry_out = carry_in;
+ return value;
+ }
+ uint32_t result;
+ switch (type) {
+ case SRType_LSL:
+ result = LSL_C(value, amount, carry_out, success);
+ break;
+ case SRType_LSR:
+ result = LSR_C(value, amount, carry_out, success);
+ break;
+ case SRType_ASR:
+ result = ASR_C(value, amount, carry_out, success);
+ break;
+ case SRType_ROR:
+ result = ROR_C(value, amount, carry_out, success);
+ break;
+ case SRType_RRX:
+ result = RRX_C(value, carry_in, carry_out, success);
+ break;
+ default:
+ *success = false;
+ break;
+ }
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t Shift(const uint32_t value, ARM_ShifterType type, const uint32_t amount,
+ const uint32_t carry_in, bool *success)
+{
+ // Don't care about carry out in this case.
+ uint32_t dont_care;
+ uint32_t result = Shift_C(value, type, amount, carry_in, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t bits(const uint32_t val, const uint32_t msbit, const uint32_t lsbit)
+{
+ return Bits32(val, msbit, lsbit);
+}
+
+static inline uint32_t bit(const uint32_t val, const uint32_t msbit)
+{
+ return bits(val, msbit, msbit);
+}
+
+static uint32_t ror(uint32_t val, uint32_t N, uint32_t shift)
+{
+ uint32_t m = shift % N;
+ return (val >> m) | (val << (N - m));
+}
+
+// (imm32, carry_out) = ARMExpandImm_C(imm12, carry_in)
+static inline uint32_t ARMExpandImm_C(uint32_t opcode, uint32_t carry_in, uint32_t &carry_out)
+{
+ uint32_t imm32; // the expanded result
+ uint32_t imm = bits(opcode, 7, 0); // immediate value
+ uint32_t amt = 2 * bits(opcode, 11, 8); // rotate amount
+ if (amt == 0)
+ {
+ imm32 = imm;
+ carry_out = carry_in;
+ }
+ else
+ {
+ imm32 = ror(imm, 32, amt);
+ carry_out = Bit32(imm32, 31);
+ }
+ return imm32;
+}
+
+static inline uint32_t ARMExpandImm(uint32_t opcode)
+{
+ // 'carry_in' argument to following function call does not affect the imm32 result.
+ uint32_t carry_in = 0;
+ uint32_t carry_out;
+ return ARMExpandImm_C(opcode, carry_in, carry_out);
+}
+
+// (imm32, carry_out) = ThumbExpandImm_C(imm12, carry_in)
+static inline uint32_t ThumbExpandImm_C(uint32_t opcode, uint32_t carry_in, uint32_t &carry_out)
+{
+ uint32_t imm32; // the expaned result
+ const uint32_t i = bit(opcode, 26);
+ const uint32_t imm3 = bits(opcode, 14, 12);
+ const uint32_t abcdefgh = bits(opcode, 7, 0);
+ const uint32_t imm12 = i << 11 | imm3 << 8 | abcdefgh;
+
+ if (bits(imm12, 11, 10) == 0)
+ {
+ switch (bits(imm12, 9, 8)) {
+ default: // Keep static analyzer happy with a default case
+ case 0:
+ imm32 = abcdefgh;
+ break;
+
+ case 1:
+ imm32 = abcdefgh << 16 | abcdefgh;
+ break;
+
+ case 2:
+ imm32 = abcdefgh << 24 | abcdefgh << 8;
+ break;
+
+ case 3:
+ imm32 = abcdefgh << 24 | abcdefgh << 16 | abcdefgh << 8 | abcdefgh;
+ break;
+ }
+ carry_out = carry_in;
+ }
+ else
+ {
+ const uint32_t unrotated_value = 0x80 | bits(imm12, 6, 0);
+ imm32 = ror(unrotated_value, 32, bits(imm12, 11, 7));
+ carry_out = Bit32(imm32, 31);
+ }
+ return imm32;
+}
+
+static inline uint32_t ThumbExpandImm(uint32_t opcode)
+{
+ // 'carry_in' argument to following function call does not affect the imm32 result.
+ uint32_t carry_in = 0;
+ uint32_t carry_out;
+ return ThumbExpandImm_C(opcode, carry_in, carry_out);
+}
+
+// imm32 = ZeroExtend(i:imm3:imm8, 32)
+static inline uint32_t ThumbImm12(uint32_t opcode)
+{
+ const uint32_t i = bit(opcode, 26);
+ const uint32_t imm3 = bits(opcode, 14, 12);
+ const uint32_t imm8 = bits(opcode, 7, 0);
+ const uint32_t imm12 = i << 11 | imm3 << 8 | imm8;
+ return imm12;
+}
+
+// imm32 = ZeroExtend(imm7:'00', 32)
+static inline uint32_t ThumbImm7Scaled(uint32_t opcode)
+{
+ const uint32_t imm7 = bits(opcode, 6, 0);
+ return imm7 * 4;
+}
+
+// imm32 = ZeroExtend(imm8:'00', 32)
+static inline uint32_t ThumbImm8Scaled(uint32_t opcode)
+{
+ const uint32_t imm8 = bits(opcode, 7, 0);
+ return imm8 * 4;
+}
+
+// This function performs the check for the register numbers 13 and 15 that are
+// not permitted for many Thumb register specifiers.
+static inline bool BadReg(uint32_t n) { return n == 13 || n == 15; }
+
+} // namespace lldb_private
+
+#endif // lldb_ARMUtils_h_
diff --git a/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp b/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp
new file mode 100644
index 0000000..0c95d66
--- /dev/null
+++ b/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp
@@ -0,0 +1,279 @@
+//===-- DynamicRegisterInfo.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-python.h"
+
+#include "DynamicRegisterInfo.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Interpreter/Args.h"
+
+#ifndef LLDB_DISABLE_PYTHON
+#include "lldb/Interpreter/PythonDataObjects.h"
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+DynamicRegisterInfo::DynamicRegisterInfo () :
+ m_regs (),
+ m_sets (),
+ m_set_reg_nums (),
+ m_set_names (),
+ m_reg_data_byte_size (0)
+{
+}
+
+DynamicRegisterInfo::DynamicRegisterInfo (const lldb_private::PythonDictionary &dict) :
+ m_regs (),
+ m_sets (),
+ m_set_reg_nums (),
+ m_set_names (),
+ m_reg_data_byte_size (0)
+{
+ SetRegisterInfo (dict);
+}
+
+DynamicRegisterInfo::~DynamicRegisterInfo ()
+{
+}
+
+
+size_t
+DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict)
+{
+#ifndef LLDB_DISABLE_PYTHON
+ PythonList sets (dict.GetItemForKey("sets"));
+ if (sets)
+ {
+ const uint32_t num_sets = sets.GetSize();
+ for (uint32_t i=0; i<num_sets; ++i)
+ {
+ PythonString py_set_name(sets.GetItemAtIndex(i));
+ ConstString set_name;
+ if (py_set_name)
+ set_name.SetCString(py_set_name.GetString());
+ if (set_name)
+ {
+ RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL };
+ m_sets.push_back (new_set);
+ }
+ else
+ {
+ Clear();
+ return 0;
+ }
+ }
+ m_set_reg_nums.resize(m_sets.size());
+ }
+ PythonList regs (dict.GetItemForKey("registers"));
+ if (regs)
+ {
+ const uint32_t num_regs = regs.GetSize();
+ PythonString name_pystr("name");
+ PythonString altname_pystr("alt-name");
+ PythonString bitsize_pystr("bitsize");
+ PythonString offset_pystr("offset");
+ PythonString encoding_pystr("encoding");
+ PythonString format_pystr("format");
+ PythonString set_pystr("set");
+ PythonString gcc_pystr("gcc");
+ PythonString dwarf_pystr("dwarf");
+ PythonString generic_pystr("generic");
+ for (uint32_t i=0; i<num_regs; ++i)
+ {
+ PythonDictionary reg_info_dict(regs.GetItemAtIndex(i));
+ if (reg_info_dict)
+ {
+ // { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 2, 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', },
+ RegisterInfo reg_info;
+ bzero (&reg_info, sizeof(reg_info));
+
+ reg_info.name = ConstString (reg_info_dict.GetItemForKeyAsString(name_pystr)).GetCString();
+ if (reg_info.name == NULL)
+ {
+ Clear();
+ return 0;
+ }
+
+ reg_info.alt_name = ConstString (reg_info_dict.GetItemForKeyAsString(altname_pystr)).GetCString();
+
+ reg_info.byte_offset = reg_info_dict.GetItemForKeyAsInteger(offset_pystr, UINT32_MAX);
+
+ if (reg_info.byte_offset == UINT32_MAX)
+ {
+ Clear();
+ return 0;
+ }
+ reg_info.byte_size = reg_info_dict.GetItemForKeyAsInteger(bitsize_pystr, 0) / 8;
+
+ if (reg_info.byte_size == 0)
+ {
+ Clear();
+ return 0;
+ }
+
+ const char *format_cstr = reg_info_dict.GetItemForKeyAsString(format_pystr);
+ if (format_cstr)
+ {
+ if (Args::StringToFormat(format_cstr, reg_info.format, NULL).Fail())
+ {
+ Clear();
+ return 0;
+ }
+ }
+ else
+ reg_info.format = eFormatHex;
+
+ const char *encoding_cstr = reg_info_dict.GetItemForKeyAsString(encoding_pystr);
+ if (encoding_cstr)
+ reg_info.encoding = Args::StringToEncoding (encoding_cstr, eEncodingUint);
+ else
+ reg_info.encoding = eEncodingUint;
+
+ const int64_t set = reg_info_dict.GetItemForKeyAsInteger(set_pystr, -1);
+ if (set >= m_sets.size())
+ {
+ Clear();
+ return 0;
+ }
+
+ reg_info.kinds[lldb::eRegisterKindLLDB] = i;
+ reg_info.kinds[lldb::eRegisterKindGDB] = i;
+ reg_info.kinds[lldb::eRegisterKindGCC] = reg_info_dict.GetItemForKeyAsInteger(gcc_pystr, LLDB_INVALID_REGNUM);
+ reg_info.kinds[lldb::eRegisterKindDWARF] = reg_info_dict.GetItemForKeyAsInteger(dwarf_pystr, LLDB_INVALID_REGNUM);
+ reg_info.kinds[lldb::eRegisterKindGeneric] = Args::StringToGenericRegister (reg_info_dict.GetItemForKeyAsString(generic_pystr));
+ const size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
+ if (m_reg_data_byte_size < end_reg_offset)
+ m_reg_data_byte_size = end_reg_offset;
+
+ m_regs.push_back (reg_info);
+ m_set_reg_nums[set].push_back(i);
+
+ }
+ else
+ {
+ Clear();
+ return 0;
+ }
+ }
+ Finalize ();
+ }
+#endif
+ return 0;
+}
+
+
+void
+DynamicRegisterInfo::AddRegister (RegisterInfo &reg_info,
+ ConstString &reg_name,
+ ConstString &reg_alt_name,
+ ConstString &set_name)
+{
+ const uint32_t reg_num = m_regs.size();
+ reg_info.name = reg_name.AsCString();
+ assert (reg_info.name);
+ reg_info.alt_name = reg_alt_name.AsCString(NULL);
+ m_regs.push_back (reg_info);
+ uint32_t set = GetRegisterSetIndexByName (set_name, true);
+ assert (set < m_sets.size());
+ assert (set < m_set_reg_nums.size());
+ assert (set < m_set_names.size());
+ m_set_reg_nums[set].push_back(reg_num);
+ size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
+ if (m_reg_data_byte_size < end_reg_offset)
+ m_reg_data_byte_size = end_reg_offset;
+}
+
+void
+DynamicRegisterInfo::Finalize ()
+{
+ for (uint32_t set = 0; set < m_sets.size(); ++set)
+ {
+ assert (m_sets.size() == m_set_reg_nums.size());
+ m_sets[set].num_registers = m_set_reg_nums[set].size();
+ m_sets[set].registers = &m_set_reg_nums[set][0];
+ }
+}
+
+size_t
+DynamicRegisterInfo::GetNumRegisters() const
+{
+ return m_regs.size();
+}
+
+size_t
+DynamicRegisterInfo::GetNumRegisterSets() const
+{
+ return m_sets.size();
+}
+
+size_t
+DynamicRegisterInfo::GetRegisterDataByteSize() const
+{
+ return m_reg_data_byte_size;
+}
+
+const RegisterInfo *
+DynamicRegisterInfo::GetRegisterInfoAtIndex (uint32_t i) const
+{
+ if (i < m_regs.size())
+ return &m_regs[i];
+ return NULL;
+}
+
+const RegisterSet *
+DynamicRegisterInfo::GetRegisterSet (uint32_t i) const
+{
+ if (i < m_sets.size())
+ return &m_sets[i];
+ return NULL;
+}
+
+uint32_t
+DynamicRegisterInfo::GetRegisterSetIndexByName (ConstString &set_name, bool can_create)
+{
+ name_collection::iterator pos, end = m_set_names.end();
+ for (pos = m_set_names.begin(); pos != end; ++pos)
+ {
+ if (*pos == set_name)
+ return std::distance (m_set_names.begin(), pos);
+ }
+
+ m_set_names.push_back(set_name);
+ m_set_reg_nums.resize(m_set_reg_nums.size()+1);
+ RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL };
+ m_sets.push_back (new_set);
+ return m_sets.size() - 1;
+}
+
+uint32_t
+DynamicRegisterInfo::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const
+{
+ reg_collection::const_iterator pos, end = m_regs.end();
+ for (pos = m_regs.begin(); pos != end; ++pos)
+ {
+ if (pos->kinds[kind] == num)
+ return std::distance (m_regs.begin(), pos);
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
+
+void
+DynamicRegisterInfo::Clear()
+{
+ m_regs.clear();
+ m_sets.clear();
+ m_set_reg_nums.clear();
+ m_set_names.clear();
+}
diff --git a/source/Plugins/Process/Utility/DynamicRegisterInfo.h b/source/Plugins/Process/Utility/DynamicRegisterInfo.h
new file mode 100644
index 0000000..a11cd33
--- /dev/null
+++ b/source/Plugins/Process/Utility/DynamicRegisterInfo.h
@@ -0,0 +1,85 @@
+//===-- DynamicRegisterInfo.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_DynamicRegisterInfo_h_
+#define lldb_DynamicRegisterInfo_h_
+
+// C Includes
+// C++ Includes
+#include <vector>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Core/ConstString.h"
+
+class DynamicRegisterInfo
+{
+public:
+ DynamicRegisterInfo ();
+
+ DynamicRegisterInfo (const lldb_private::PythonDictionary &dict);
+
+ virtual
+ ~DynamicRegisterInfo ();
+
+ size_t
+ SetRegisterInfo (const lldb_private::PythonDictionary &dict);
+
+ void
+ AddRegister (lldb_private::RegisterInfo &reg_info,
+ lldb_private::ConstString &reg_name,
+ lldb_private::ConstString &reg_alt_name,
+ lldb_private::ConstString &set_name);
+
+ void
+ Finalize ();
+
+ size_t
+ GetNumRegisters() const;
+
+ size_t
+ GetNumRegisterSets() const;
+
+ size_t
+ GetRegisterDataByteSize() const;
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t i) const;
+
+ const lldb_private::RegisterSet *
+ GetRegisterSet (uint32_t i) const;
+
+ uint32_t
+ GetRegisterSetIndexByName (lldb_private::ConstString &set_name, bool can_create);
+
+ uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const;
+
+ void
+ Clear();
+
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from DynamicRegisterInfo can see and modify these
+ //------------------------------------------------------------------
+ typedef std::vector <lldb_private::RegisterInfo> reg_collection;
+ typedef std::vector <lldb_private::RegisterSet> set_collection;
+ typedef std::vector <uint32_t> reg_num_collection;
+ typedef std::vector <reg_num_collection> set_reg_num_collection;
+ typedef std::vector <lldb_private::ConstString> name_collection;
+
+ reg_collection m_regs;
+ set_collection m_sets;
+ set_reg_num_collection m_set_reg_nums;
+ name_collection m_set_names;
+ size_t m_reg_data_byte_size; // The number of bytes required to store all registers
+};
+
+#endif // lldb_DynamicRegisterInfo_h_
diff --git a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
new file mode 100644
index 0000000..499d6d7
--- /dev/null
+++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
@@ -0,0 +1,274 @@
+//===-- InferiorCallPOSIX.cpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InferiorCallPOSIX.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+
+#include <sys/mman.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
+ addr_t addr, addr_t length, unsigned prot,
+ unsigned flags, addr_t fd, addr_t offset) {
+ Thread *thread = process->GetThreadList().GetSelectedThread().get();
+ if (thread == NULL)
+ return false;
+
+ const bool append = true;
+ const bool include_symbols = true;
+ const bool include_inlines = false;
+ SymbolContextList sc_list;
+ const uint32_t count
+ = process->GetTarget().GetImages().FindFunctions (ConstString ("mmap"),
+ eFunctionNameTypeFull,
+ include_symbols,
+ include_inlines,
+ append,
+ sc_list);
+ if (count > 0)
+ {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(0, sc))
+ {
+ const uint32_t range_scope = eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = false;
+ const bool stop_other_threads = true;
+ const bool unwind_on_error = true;
+ const bool ignore_breakpoints = true;
+ const bool try_all_threads = true;
+ const uint32_t timeout_usec = 500000;
+
+ addr_t prot_arg, flags_arg = 0;
+ if (prot == eMmapProtNone)
+ prot_arg = PROT_NONE;
+ else {
+ prot_arg = 0;
+ if (prot & eMmapProtExec)
+ prot_arg |= PROT_EXEC;
+ if (prot & eMmapProtRead)
+ prot_arg |= PROT_READ;
+ if (prot & eMmapProtWrite)
+ prot_arg |= PROT_WRITE;
+ }
+
+ if (flags & eMmapFlagsPrivate)
+ flags_arg |= MAP_PRIVATE;
+ if (flags & eMmapFlagsAnon)
+ flags_arg |= MAP_ANON;
+
+ AddressRange mmap_range;
+ if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, mmap_range))
+ {
+ ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
+ ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ ThreadPlanCallFunction *call_function_thread_plan
+ = new ThreadPlanCallFunction (*thread,
+ mmap_range.GetBaseAddress(),
+ clang_void_ptr_type,
+ stop_other_threads,
+ unwind_on_error,
+ ignore_breakpoints,
+ &addr,
+ &length,
+ &prot_arg,
+ &flags_arg,
+ &fd,
+ &offset);
+ lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan);
+ if (call_plan_sp)
+ {
+ StreamFile error_strm;
+ // This plan is a utility plan, so set it to discard itself when done.
+ call_plan_sp->SetIsMasterPlan (true);
+ call_plan_sp->SetOkayToDiscard(true);
+
+ StackFrame *frame = thread->GetStackFrameAtIndex (0).get();
+ if (frame)
+ {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext (exe_ctx);
+ ExecutionResults result = process->RunThreadPlan (exe_ctx,
+ call_plan_sp,
+ stop_other_threads,
+ try_all_threads,
+ unwind_on_error,
+ ignore_breakpoints,
+ timeout_usec,
+ error_strm);
+ if (result == eExecutionCompleted)
+ {
+
+ allocated_addr = call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (process->GetAddressByteSize() == 4)
+ {
+ if (allocated_addr == UINT32_MAX)
+ return false;
+ }
+ else if (process->GetAddressByteSize() == 8)
+ {
+ if (allocated_addr == UINT64_MAX)
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr,
+ addr_t length) {
+ Thread *thread = process->GetThreadList().GetSelectedThread().get();
+ if (thread == NULL)
+ return false;
+
+ const bool append = true;
+ const bool include_symbols = true;
+ const bool include_inlines = false;
+ SymbolContextList sc_list;
+ const uint32_t count
+ = process->GetTarget().GetImages().FindFunctions (ConstString ("munmap"),
+ eFunctionNameTypeFull,
+ include_symbols,
+ include_inlines,
+ append,
+ sc_list);
+ if (count > 0)
+ {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(0, sc))
+ {
+ const uint32_t range_scope = eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = false;
+ const bool stop_other_threads = true;
+ const bool unwind_on_error = true;
+ const bool ignore_breakpoints = true;
+ const bool try_all_threads = true;
+ const uint32_t timeout_usec = 500000;
+
+ AddressRange munmap_range;
+ if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, munmap_range))
+ {
+ lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread,
+ munmap_range.GetBaseAddress(),
+ ClangASTType(),
+ stop_other_threads,
+ unwind_on_error,
+ ignore_breakpoints,
+ &addr,
+ &length));
+ if (call_plan_sp)
+ {
+ StreamFile error_strm;
+ // This plan is a utility plan, so set it to discard itself when done.
+ call_plan_sp->SetIsMasterPlan (true);
+ call_plan_sp->SetOkayToDiscard(true);
+
+ StackFrame *frame = thread->GetStackFrameAtIndex (0).get();
+ if (frame)
+ {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext (exe_ctx);
+ ExecutionResults result = process->RunThreadPlan (exe_ctx,
+ call_plan_sp,
+ stop_other_threads,
+ try_all_threads,
+ unwind_on_error,
+ ignore_breakpoints,
+ timeout_usec,
+ error_strm);
+ if (result == eExecutionCompleted)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::InferiorCall(Process *process, const Address *address, addr_t &returned_func) {
+ Thread *thread = process->GetThreadList().GetSelectedThread().get();
+ if (thread == NULL || address == NULL)
+ return false;
+
+ const bool stop_other_threads = true;
+ const bool unwind_on_error = true;
+ const bool ignore_breakpoints = true;
+ const bool try_all_threads = true;
+ const uint32_t timeout_usec = 500000;
+
+ ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
+ ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ ThreadPlanCallFunction *call_function_thread_plan
+ = new ThreadPlanCallFunction (*thread,
+ *address,
+ clang_void_ptr_type,
+ stop_other_threads,
+ unwind_on_error,
+ ignore_breakpoints);
+ lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan);
+ if (call_plan_sp)
+ {
+ StreamFile error_strm;
+ // This plan is a utility plan, so set it to discard itself when done.
+ call_plan_sp->SetIsMasterPlan (true);
+ call_plan_sp->SetOkayToDiscard(true);
+
+ StackFrame *frame = thread->GetStackFrameAtIndex (0).get();
+ if (frame)
+ {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext (exe_ctx);
+ ExecutionResults result = process->RunThreadPlan (exe_ctx,
+ call_plan_sp,
+ stop_other_threads,
+ try_all_threads,
+ unwind_on_error,
+ ignore_breakpoints,
+ timeout_usec,
+ error_strm);
+ if (result == eExecutionCompleted)
+ {
+ returned_func = call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+
+ if (process->GetAddressByteSize() == 4)
+ {
+ if (returned_func == UINT32_MAX)
+ return false;
+ }
+ else if (process->GetAddressByteSize() == 8)
+ {
+ if (returned_func == UINT64_MAX)
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/source/Plugins/Process/Utility/InferiorCallPOSIX.h b/source/Plugins/Process/Utility/InferiorCallPOSIX.h
new file mode 100644
index 0000000..d8b6d0e
--- /dev/null
+++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.h
@@ -0,0 +1,43 @@
+//===-- InferiorCallPOSIX.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_InferiorCallPOSIX_h_
+#define lldb_InferiorCallPOSIX_h_
+
+// Inferior execution of POSIX functions.
+
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class Process;
+
+enum MmapProt {
+ eMmapProtNone = 0,
+ eMmapProtExec = 1,
+ eMmapProtRead = 2,
+ eMmapProtWrite = 4
+};
+
+enum MmapFlags {
+ eMmapFlagsPrivate = 1,
+ eMmapFlagsAnon = 2
+};
+
+bool InferiorCallMmap(Process *proc, lldb::addr_t &allocated_addr,
+ lldb::addr_t addr, lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd, lldb::addr_t offset);
+
+bool InferiorCallMunmap(Process *proc, lldb::addr_t addr, lldb::addr_t length);
+
+bool InferiorCall(Process *proc, const Address *address, lldb::addr_t &returned_func);
+
+} // namespace lldb_private
+
+#endif // lldb_InferiorCallPOSIX_h_
diff --git a/source/Plugins/Process/Utility/InstructionUtils.h b/source/Plugins/Process/Utility/InstructionUtils.h
new file mode 100644
index 0000000..4bb644e
--- /dev/null
+++ b/source/Plugins/Process/Utility/InstructionUtils.h
@@ -0,0 +1,136 @@
+//===-- InstructionUtils.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_InstructionUtils_h_
+#define lldb_InstructionUtils_h_
+
+// Common utilities for manipulating instruction bit fields.
+
+namespace lldb_private {
+
+// Return the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 64-bit unsigned value.
+static inline uint64_t
+Bits64 (const uint64_t bits, const uint32_t msbit, const uint32_t lsbit)
+{
+ assert(msbit < 64 && lsbit <= msbit);
+ return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1);
+}
+
+// Return the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 32-bit unsigned value.
+static inline uint32_t
+Bits32 (const uint32_t bits, const uint32_t msbit, const uint32_t lsbit)
+{
+ assert(msbit < 32 && lsbit <= msbit);
+ return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1);
+}
+
+// Return the bit value from the 'bit' position of a 32-bit unsigned value.
+static inline uint32_t
+Bit32 (const uint32_t bits, const uint32_t bit)
+{
+ return (bits >> bit) & 1u;
+}
+
+static inline uint64_t
+Bit64 (const uint64_t bits, const uint32_t bit)
+{
+ return (bits >> bit) & 1ull;
+}
+
+// Set the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 32-bit unsigned value to 'val'.
+static inline void
+SetBits32(uint32_t &bits, const uint32_t msbit, const uint32_t lsbit, const uint32_t val)
+{
+ assert(msbit < 32 && lsbit < 32 && msbit >= lsbit);
+ uint32_t mask = ((1u << (msbit - lsbit + 1)) - 1);
+ bits &= ~(mask << lsbit);
+ bits |= (val & mask) << lsbit;
+}
+
+// Set the 'bit' position of a 32-bit unsigned value to 'val'.
+static inline void
+SetBit32(uint32_t &bits, const uint32_t bit, const uint32_t val)
+{
+ SetBits32(bits, bit, bit, val);
+}
+
+// Rotate a 32-bit unsigned value right by the specified amount.
+static inline uint32_t
+Rotr32 (uint32_t bits, uint32_t amt)
+{
+ assert(amt < 32 && "Invalid rotate amount");
+ return (bits >> amt) | (bits << ((32-amt)&31));
+}
+
+// Rotate a 32-bit unsigned value left by the specified amount.
+static inline uint32_t
+Rotl32 (uint32_t bits, uint32_t amt)
+{
+ assert(amt < 32 && "Invalid rotate amount");
+ return (bits << amt) | (bits >> ((32-amt)&31));
+}
+
+// Create a mask that starts at bit zero and includes "bit"
+static inline uint64_t
+MaskUpToBit (const uint64_t bit)
+{
+ return (1ull << (bit + 1ull)) - 1ull;
+}
+
+// Return an integer result equal to the number of bits of x that are ones.
+static inline uint32_t
+BitCount (uint64_t x)
+{
+ // c accumulates the total bits set in x
+ uint32_t c;
+ for (c = 0; x; ++c)
+ {
+ x &= x - 1; // clear the least significant bit set
+ }
+ return c;
+}
+
+static inline bool
+BitIsSet (const uint64_t value, const uint64_t bit)
+{
+ return (value & (1ull << bit)) != 0;
+}
+
+static inline bool
+BitIsClear (const uint64_t value, const uint64_t bit)
+{
+ return (value & (1ull << bit)) == 0;
+}
+
+static inline uint64_t
+UnsignedBits (const uint64_t value, const uint64_t msbit, const uint64_t lsbit)
+{
+ uint64_t result = value >> lsbit;
+ result &= MaskUpToBit (msbit - lsbit);
+ return result;
+}
+
+static inline int64_t
+SignedBits (const uint64_t value, const uint64_t msbit, const uint64_t lsbit)
+{
+ uint64_t result = UnsignedBits (value, msbit, lsbit);
+ if (BitIsSet(value, msbit))
+ {
+ // Sign extend
+ result |= ~MaskUpToBit (msbit - lsbit);
+ }
+ return result;
+}
+
+} // namespace lldb_private
+
+#endif // lldb_InstructionUtils_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp
new file mode 100644
index 0000000..4d77b6f
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp
@@ -0,0 +1,1226 @@
+//===-- RegisterContextDarwin_arm.cpp ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include "RegisterContextDarwin_arm.h"
+
+// C Includes
+#include <mach/mach_types.h>
+#include <mach/thread_act.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Host/Endian.h"
+#include "llvm/Support/Compiler.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+// Project includes
+#include "ARM_GCC_Registers.h"
+#include "ARM_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum
+{
+ gpr_r0 = 0,
+ gpr_r1,
+ gpr_r2,
+ gpr_r3,
+ gpr_r4,
+ gpr_r5,
+ gpr_r6,
+ gpr_r7,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13, gpr_sp = gpr_r13,
+ gpr_r14, gpr_lr = gpr_r14,
+ gpr_r15, gpr_pc = gpr_r15,
+ gpr_cpsr,
+
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+ fpu_fpscr,
+
+ exc_exception,
+ exc_fsr,
+ exc_far,
+
+ dbg_bvr0,
+ dbg_bvr1,
+ dbg_bvr2,
+ dbg_bvr3,
+ dbg_bvr4,
+ dbg_bvr5,
+ dbg_bvr6,
+ dbg_bvr7,
+ dbg_bvr8,
+ dbg_bvr9,
+ dbg_bvr10,
+ dbg_bvr11,
+ dbg_bvr12,
+ dbg_bvr13,
+ dbg_bvr14,
+ dbg_bvr15,
+
+ dbg_bcr0,
+ dbg_bcr1,
+ dbg_bcr2,
+ dbg_bcr3,
+ dbg_bcr4,
+ dbg_bcr5,
+ dbg_bcr6,
+ dbg_bcr7,
+ dbg_bcr8,
+ dbg_bcr9,
+ dbg_bcr10,
+ dbg_bcr11,
+ dbg_bcr12,
+ dbg_bcr13,
+ dbg_bcr14,
+ dbg_bcr15,
+
+ dbg_wvr0,
+ dbg_wvr1,
+ dbg_wvr2,
+ dbg_wvr3,
+ dbg_wvr4,
+ dbg_wvr5,
+ dbg_wvr6,
+ dbg_wvr7,
+ dbg_wvr8,
+ dbg_wvr9,
+ dbg_wvr10,
+ dbg_wvr11,
+ dbg_wvr12,
+ dbg_wvr13,
+ dbg_wvr14,
+ dbg_wvr15,
+
+ dbg_wcr0,
+ dbg_wcr1,
+ dbg_wcr2,
+ dbg_wcr3,
+ dbg_wcr4,
+ dbg_wcr5,
+ dbg_wcr6,
+ dbg_wcr7,
+ dbg_wcr8,
+ dbg_wcr9,
+ dbg_wcr10,
+ dbg_wcr11,
+ dbg_wcr12,
+ dbg_wcr13,
+ dbg_wcr14,
+ dbg_wcr15,
+
+ k_num_registers
+};
+
+
+RegisterContextDarwin_arm::RegisterContextDarwin_arm(Thread &thread, uint32_t concrete_frame_idx) :
+ RegisterContext(thread, concrete_frame_idx),
+ gpr(),
+ fpu(),
+ exc()
+{
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_arm::~RegisterContextDarwin_arm()
+{
+}
+
+
+#define GPR_OFFSET(idx) ((idx) * 4)
+#define FPU_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextDarwin_arm::GPR))
+#define EXC_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextDarwin_arm::GPR) + sizeof (RegisterContextDarwin_arm::FPU))
+#define DBG_OFFSET(reg) ((LLVM_EXTENSION offsetof (RegisterContextDarwin_arm::DBG, reg) + sizeof (RegisterContextDarwin_arm::GPR) + sizeof (RegisterContextDarwin_arm::FPU) + sizeof (RegisterContextDarwin_arm::EXC)))
+
+#define DEFINE_DBG(reg, i) #reg, NULL, sizeof(((RegisterContextDarwin_arm::DBG *)NULL)->reg[i]), DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, dbg_##reg##i }, NULL, NULL
+#define REG_CONTEXT_SIZE (sizeof (RegisterContextDarwin_arm::GPR) + sizeof (RegisterContextDarwin_arm::FPU) + sizeof (RegisterContextDarwin_arm::EXC))
+
+static RegisterInfo g_register_infos[] = {
+// General purpose registers
+// NAME ALT SZ OFFSET ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS
+// ====== ======= == ============= ============= ============ =============== =============== ========================= ===================== ============= ========== ===============
+{ "r0", NULL, 4, GPR_OFFSET(0), eEncodingUint, eFormatHex, { gcc_r0, dwarf_r0, LLDB_INVALID_REGNUM, gdb_arm_r0, gpr_r0 }, NULL, NULL},
+{ "r1", NULL, 4, GPR_OFFSET(1), eEncodingUint, eFormatHex, { gcc_r1, dwarf_r1, LLDB_INVALID_REGNUM, gdb_arm_r1, gpr_r1 }, NULL, NULL},
+{ "r2", NULL, 4, GPR_OFFSET(2), eEncodingUint, eFormatHex, { gcc_r2, dwarf_r2, LLDB_INVALID_REGNUM, gdb_arm_r2, gpr_r2 }, NULL, NULL},
+{ "r3", NULL, 4, GPR_OFFSET(3), eEncodingUint, eFormatHex, { gcc_r3, dwarf_r3, LLDB_INVALID_REGNUM, gdb_arm_r3, gpr_r3 }, NULL, NULL},
+{ "r4", NULL, 4, GPR_OFFSET(4), eEncodingUint, eFormatHex, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM, gdb_arm_r4, gpr_r4 }, NULL, NULL},
+{ "r5", NULL, 4, GPR_OFFSET(5), eEncodingUint, eFormatHex, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM, gdb_arm_r5, gpr_r5 }, NULL, NULL},
+{ "r6", NULL, 4, GPR_OFFSET(6), eEncodingUint, eFormatHex, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM, gdb_arm_r6, gpr_r6 }, NULL, NULL},
+{ "r7", NULL, 4, GPR_OFFSET(7), eEncodingUint, eFormatHex, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, gdb_arm_r7, gpr_r7 }, NULL, NULL},
+{ "r8", NULL, 4, GPR_OFFSET(8), eEncodingUint, eFormatHex, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM, gdb_arm_r8, gpr_r8 }, NULL, NULL},
+{ "r9", NULL, 4, GPR_OFFSET(9), eEncodingUint, eFormatHex, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM, gdb_arm_r9, gpr_r9 }, NULL, NULL},
+{ "r10", NULL, 4, GPR_OFFSET(10), eEncodingUint, eFormatHex, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM, gdb_arm_r10, gpr_r10 }, NULL, NULL},
+{ "r11", NULL, 4, GPR_OFFSET(11), eEncodingUint, eFormatHex, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM, gdb_arm_r11, gpr_r11 }, NULL, NULL},
+{ "r12", NULL, 4, GPR_OFFSET(12), eEncodingUint, eFormatHex, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM, gdb_arm_r12, gpr_r12 }, NULL, NULL},
+{ "sp", "r13", 4, GPR_OFFSET(13), eEncodingUint, eFormatHex, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, gdb_arm_sp, gpr_sp }, NULL, NULL},
+{ "lr", "r14", 4, GPR_OFFSET(14), eEncodingUint, eFormatHex, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, gdb_arm_lr, gpr_lr }, NULL, NULL},
+{ "pc", "r15", 4, GPR_OFFSET(15), eEncodingUint, eFormatHex, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, gdb_arm_pc, gpr_pc }, NULL, NULL},
+{ "cpsr", "psr", 4, GPR_OFFSET(16), eEncodingUint, eFormatHex, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, gdb_arm_cpsr, gpr_cpsr }, NULL, NULL},
+
+{ "s0", NULL, 4, FPU_OFFSET(0), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, gdb_arm_s0, fpu_s0 }, NULL, NULL},
+{ "s1", NULL, 4, FPU_OFFSET(1), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, gdb_arm_s1, fpu_s1 }, NULL, NULL},
+{ "s2", NULL, 4, FPU_OFFSET(2), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, gdb_arm_s2, fpu_s2 }, NULL, NULL},
+{ "s3", NULL, 4, FPU_OFFSET(3), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, gdb_arm_s3, fpu_s3 }, NULL, NULL},
+{ "s4", NULL, 4, FPU_OFFSET(4), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, gdb_arm_s4, fpu_s4 }, NULL, NULL},
+{ "s5", NULL, 4, FPU_OFFSET(5), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, gdb_arm_s5, fpu_s5 }, NULL, NULL},
+{ "s6", NULL, 4, FPU_OFFSET(6), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, gdb_arm_s6, fpu_s6 }, NULL, NULL},
+{ "s7", NULL, 4, FPU_OFFSET(7), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, gdb_arm_s7, fpu_s7 }, NULL, NULL},
+{ "s8", NULL, 4, FPU_OFFSET(8), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, gdb_arm_s8, fpu_s8 }, NULL, NULL},
+{ "s9", NULL, 4, FPU_OFFSET(9), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, gdb_arm_s9, fpu_s9 }, NULL, NULL},
+{ "s10", NULL, 4, FPU_OFFSET(10), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, gdb_arm_s10, fpu_s10 }, NULL, NULL},
+{ "s11", NULL, 4, FPU_OFFSET(11), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, gdb_arm_s11, fpu_s11 }, NULL, NULL},
+{ "s12", NULL, 4, FPU_OFFSET(12), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, gdb_arm_s12, fpu_s12 }, NULL, NULL},
+{ "s13", NULL, 4, FPU_OFFSET(13), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, gdb_arm_s13, fpu_s13 }, NULL, NULL},
+{ "s14", NULL, 4, FPU_OFFSET(14), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, gdb_arm_s14, fpu_s14 }, NULL, NULL},
+{ "s15", NULL, 4, FPU_OFFSET(15), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, gdb_arm_s15, fpu_s15 }, NULL, NULL},
+{ "s16", NULL, 4, FPU_OFFSET(16), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, gdb_arm_s16, fpu_s16 }, NULL, NULL},
+{ "s17", NULL, 4, FPU_OFFSET(17), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, gdb_arm_s17, fpu_s17 }, NULL, NULL},
+{ "s18", NULL, 4, FPU_OFFSET(18), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, gdb_arm_s18, fpu_s18 }, NULL, NULL},
+{ "s19", NULL, 4, FPU_OFFSET(19), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, gdb_arm_s19, fpu_s19 }, NULL, NULL},
+{ "s20", NULL, 4, FPU_OFFSET(20), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, gdb_arm_s20, fpu_s20 }, NULL, NULL},
+{ "s21", NULL, 4, FPU_OFFSET(21), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, gdb_arm_s21, fpu_s21 }, NULL, NULL},
+{ "s22", NULL, 4, FPU_OFFSET(22), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, gdb_arm_s22, fpu_s22 }, NULL, NULL},
+{ "s23", NULL, 4, FPU_OFFSET(23), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, gdb_arm_s23, fpu_s23 }, NULL, NULL},
+{ "s24", NULL, 4, FPU_OFFSET(24), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, gdb_arm_s24, fpu_s24 }, NULL, NULL},
+{ "s25", NULL, 4, FPU_OFFSET(25), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, gdb_arm_s25, fpu_s25 }, NULL, NULL},
+{ "s26", NULL, 4, FPU_OFFSET(26), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, gdb_arm_s26, fpu_s26 }, NULL, NULL},
+{ "s27", NULL, 4, FPU_OFFSET(27), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, gdb_arm_s27, fpu_s27 }, NULL, NULL},
+{ "s28", NULL, 4, FPU_OFFSET(28), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, gdb_arm_s28, fpu_s28 }, NULL, NULL},
+{ "s29", NULL, 4, FPU_OFFSET(29), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, gdb_arm_s29, fpu_s29 }, NULL, NULL},
+{ "s30", NULL, 4, FPU_OFFSET(30), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, gdb_arm_s30, fpu_s30 }, NULL, NULL},
+{ "s31", NULL, 4, FPU_OFFSET(31), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, gdb_arm_s31, fpu_s31 }, NULL, NULL},
+{ "fpscr", NULL, 4, FPU_OFFSET(32), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, gdb_arm_fpscr, fpu_fpscr }, NULL, NULL},
+
+{ "exception",NULL, 4, EXC_OFFSET(0), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_exception }, NULL, NULL},
+{ "fsr", NULL, 4, EXC_OFFSET(1), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_fsr }, NULL, NULL},
+{ "far", NULL, 4, EXC_OFFSET(2), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_far }, NULL, NULL},
+
+{ DEFINE_DBG (bvr, 0) },
+{ DEFINE_DBG (bvr, 1) },
+{ DEFINE_DBG (bvr, 2) },
+{ DEFINE_DBG (bvr, 3) },
+{ DEFINE_DBG (bvr, 4) },
+{ DEFINE_DBG (bvr, 5) },
+{ DEFINE_DBG (bvr, 6) },
+{ DEFINE_DBG (bvr, 7) },
+{ DEFINE_DBG (bvr, 8) },
+{ DEFINE_DBG (bvr, 9) },
+{ DEFINE_DBG (bvr, 10) },
+{ DEFINE_DBG (bvr, 11) },
+{ DEFINE_DBG (bvr, 12) },
+{ DEFINE_DBG (bvr, 13) },
+{ DEFINE_DBG (bvr, 14) },
+{ DEFINE_DBG (bvr, 15) },
+
+{ DEFINE_DBG (bcr, 0) },
+{ DEFINE_DBG (bcr, 1) },
+{ DEFINE_DBG (bcr, 2) },
+{ DEFINE_DBG (bcr, 3) },
+{ DEFINE_DBG (bcr, 4) },
+{ DEFINE_DBG (bcr, 5) },
+{ DEFINE_DBG (bcr, 6) },
+{ DEFINE_DBG (bcr, 7) },
+{ DEFINE_DBG (bcr, 8) },
+{ DEFINE_DBG (bcr, 9) },
+{ DEFINE_DBG (bcr, 10) },
+{ DEFINE_DBG (bcr, 11) },
+{ DEFINE_DBG (bcr, 12) },
+{ DEFINE_DBG (bcr, 13) },
+{ DEFINE_DBG (bcr, 14) },
+{ DEFINE_DBG (bcr, 15) },
+
+{ DEFINE_DBG (wvr, 0) },
+{ DEFINE_DBG (wvr, 1) },
+{ DEFINE_DBG (wvr, 2) },
+{ DEFINE_DBG (wvr, 3) },
+{ DEFINE_DBG (wvr, 4) },
+{ DEFINE_DBG (wvr, 5) },
+{ DEFINE_DBG (wvr, 6) },
+{ DEFINE_DBG (wvr, 7) },
+{ DEFINE_DBG (wvr, 8) },
+{ DEFINE_DBG (wvr, 9) },
+{ DEFINE_DBG (wvr, 10) },
+{ DEFINE_DBG (wvr, 11) },
+{ DEFINE_DBG (wvr, 12) },
+{ DEFINE_DBG (wvr, 13) },
+{ DEFINE_DBG (wvr, 14) },
+{ DEFINE_DBG (wvr, 15) },
+
+{ DEFINE_DBG (wcr, 0) },
+{ DEFINE_DBG (wcr, 1) },
+{ DEFINE_DBG (wcr, 2) },
+{ DEFINE_DBG (wcr, 3) },
+{ DEFINE_DBG (wcr, 4) },
+{ DEFINE_DBG (wcr, 5) },
+{ DEFINE_DBG (wcr, 6) },
+{ DEFINE_DBG (wcr, 7) },
+{ DEFINE_DBG (wcr, 8) },
+{ DEFINE_DBG (wcr, 9) },
+{ DEFINE_DBG (wcr, 10) },
+{ DEFINE_DBG (wcr, 11) },
+{ DEFINE_DBG (wcr, 12) },
+{ DEFINE_DBG (wcr, 13) },
+{ DEFINE_DBG (wcr, 14) },
+{ DEFINE_DBG (wcr, 15) }
+};
+
+// General purpose registers
+static uint32_t
+g_gpr_regnums[] =
+{
+ gpr_r0,
+ gpr_r1,
+ gpr_r2,
+ gpr_r3,
+ gpr_r4,
+ gpr_r5,
+ gpr_r6,
+ gpr_r7,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_sp,
+ gpr_lr,
+ gpr_pc,
+ gpr_cpsr
+};
+
+// Floating point registers
+static uint32_t
+g_fpu_regnums[] =
+{
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+ fpu_fpscr,
+};
+
+// Exception registers
+
+static uint32_t
+g_exc_regnums[] =
+{
+ exc_exception,
+ exc_fsr,
+ exc_far,
+};
+
+static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo));
+
+void
+RegisterContextDarwin_arm::InvalidateAllRegisters ()
+{
+ InvalidateAllRegisterStates();
+}
+
+
+size_t
+RegisterContextDarwin_arm::GetRegisterCount ()
+{
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_arm::GetRegisterInfoAtIndex (size_t reg)
+{
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return NULL;
+}
+
+size_t
+RegisterContextDarwin_arm::GetRegisterInfosCount ()
+{
+ return k_num_register_infos;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_arm::GetRegisterInfos ()
+{
+ return g_register_infos;
+}
+
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t);
+const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t);
+const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t);
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+static const RegisterSet g_reg_sets[] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, },
+ { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums },
+ { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums }
+};
+
+const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet);
+
+
+size_t
+RegisterContextDarwin_arm::GetRegisterSetCount ()
+{
+ return k_num_regsets;
+}
+
+const RegisterSet *
+RegisterContextDarwin_arm::GetRegisterSet (size_t reg_set)
+{
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return NULL;
+}
+
+
+//----------------------------------------------------------------------
+// Register information defintions for 32 bit i386.
+//----------------------------------------------------------------------
+int
+RegisterContextDarwin_arm::GetSetForNativeRegNum (int reg)
+{
+ if (reg < fpu_s0)
+ return GPRRegSet;
+ else if (reg < exc_exception)
+ return FPURegSet;
+ else if (reg < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+int
+RegisterContextDarwin_arm::ReadGPR (bool force)
+{
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+int
+RegisterContextDarwin_arm::ReadFPU (bool force)
+{
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+int
+RegisterContextDarwin_arm::ReadEXC (bool force)
+{
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+int
+RegisterContextDarwin_arm::ReadDBG (bool force)
+{
+ int set = DBGRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadDBG(GetThreadID(), set, dbg));
+ }
+ return GetError(DBGRegSet, Read);
+}
+
+int
+RegisterContextDarwin_arm::WriteGPR ()
+{
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError (set, Read, -1);
+ return GetError(GPRRegSet, Write);
+}
+
+int
+RegisterContextDarwin_arm::WriteFPU ()
+{
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError (set, Read, -1);
+ return GetError(FPURegSet, Write);
+}
+
+int
+RegisterContextDarwin_arm::WriteEXC ()
+{
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError (set, Read, -1);
+ return GetError(EXCRegSet, Write);
+}
+
+int
+RegisterContextDarwin_arm::WriteDBG ()
+{
+ int set = DBGRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, DoWriteDBG(GetThreadID(), set, dbg));
+ SetError (set, Read, -1);
+ return GetError(DBGRegSet, Write);
+}
+
+
+int
+RegisterContextDarwin_arm::ReadRegisterSet (uint32_t set, bool force)
+{
+ switch (set)
+ {
+ case GPRRegSet: return ReadGPR(force);
+ case FPURegSet: return ReadFPU(force);
+ case EXCRegSet: return ReadEXC(force);
+ case DBGRegSet: return ReadDBG(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+int
+RegisterContextDarwin_arm::WriteRegisterSet (uint32_t set)
+{
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set))
+ {
+ switch (set)
+ {
+ case GPRRegSet: return WriteGPR();
+ case FPURegSet: return WriteFPU();
+ case EXCRegSet: return WriteEXC();
+ case DBGRegSet: return WriteDBG();
+ default: break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+void
+RegisterContextDarwin_arm::LogDBGRegisters (Log *log, const DBG& dbg)
+{
+ if (log)
+ {
+ for (uint32_t i=0; i<16; i++)
+ log->Printf("BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }",
+ i, i, dbg.bvr[i], dbg.bcr[i],
+ i, i, dbg.wvr[i], dbg.wcr[i]);
+ }
+}
+
+
+bool
+RegisterContextDarwin_arm::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_arm::GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ value.SetUInt32 (gpr.r[reg - gpr_r0]);
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ value.SetUInt32 (fpu.floats.s[reg], RegisterValue::eTypeFloat);
+ break;
+
+ case fpu_fpscr:
+ value.SetUInt32 (fpu.fpscr);
+ break;
+
+ case exc_exception:
+ value.SetUInt32 (exc.exception);
+ break;
+ case exc_fsr:
+ value.SetUInt32 (exc.fsr);
+ break;
+ case exc_far:
+ value.SetUInt32 (exc.far);
+ break;
+
+ default:
+ value.SetValueToInvalid();
+ return false;
+
+ }
+ return true;
+}
+
+
+bool
+RegisterContextDarwin_arm::WriteRegister (const RegisterInfo *reg_info,
+ const RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ gpr.r[reg - gpr_r0] = value.GetAsUInt32();
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ fpu.floats.s[reg] = value.GetAsUInt32();
+ break;
+
+ case fpu_fpscr:
+ fpu.fpscr = value.GetAsUInt32();
+ break;
+
+ case exc_exception:
+ exc.exception = value.GetAsUInt32();
+ break;
+ case exc_fsr:
+ exc.fsr = value.GetAsUInt32();
+ break;
+ case exc_far:
+ exc.far = value.GetAsUInt32();
+ break;
+
+ default:
+ return false;
+
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool
+RegisterContextDarwin_arm::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (data_sp &&
+ ReadGPR (false) == KERN_SUCCESS &&
+ ReadFPU (false) == KERN_SUCCESS &&
+ ReadEXC (false) == KERN_SUCCESS)
+ {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy (dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool
+RegisterContextDarwin_arm::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE)
+ {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy (&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy (&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy (&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteFPU() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteEXC() == KERN_SUCCESS)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t
+RegisterContextDarwin_arm::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg)
+{
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (reg)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_pc;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_sp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_r7;
+ case LLDB_REGNUM_GENERIC_RA: return gpr_lr;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_cpsr;
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindDWARF)
+ {
+ switch (reg)
+ {
+ case dwarf_r0: return gpr_r0;
+ case dwarf_r1: return gpr_r1;
+ case dwarf_r2: return gpr_r2;
+ case dwarf_r3: return gpr_r3;
+ case dwarf_r4: return gpr_r4;
+ case dwarf_r5: return gpr_r5;
+ case dwarf_r6: return gpr_r6;
+ case dwarf_r7: return gpr_r7;
+ case dwarf_r8: return gpr_r8;
+ case dwarf_r9: return gpr_r9;
+ case dwarf_r10: return gpr_r10;
+ case dwarf_r11: return gpr_r11;
+ case dwarf_r12: return gpr_r12;
+ case dwarf_sp: return gpr_sp;
+ case dwarf_lr: return gpr_lr;
+ case dwarf_pc: return gpr_pc;
+ case dwarf_spsr: return gpr_cpsr;
+
+ case dwarf_s0: return fpu_s0;
+ case dwarf_s1: return fpu_s1;
+ case dwarf_s2: return fpu_s2;
+ case dwarf_s3: return fpu_s3;
+ case dwarf_s4: return fpu_s4;
+ case dwarf_s5: return fpu_s5;
+ case dwarf_s6: return fpu_s6;
+ case dwarf_s7: return fpu_s7;
+ case dwarf_s8: return fpu_s8;
+ case dwarf_s9: return fpu_s9;
+ case dwarf_s10: return fpu_s10;
+ case dwarf_s11: return fpu_s11;
+ case dwarf_s12: return fpu_s12;
+ case dwarf_s13: return fpu_s13;
+ case dwarf_s14: return fpu_s14;
+ case dwarf_s15: return fpu_s15;
+ case dwarf_s16: return fpu_s16;
+ case dwarf_s17: return fpu_s17;
+ case dwarf_s18: return fpu_s18;
+ case dwarf_s19: return fpu_s19;
+ case dwarf_s20: return fpu_s20;
+ case dwarf_s21: return fpu_s21;
+ case dwarf_s22: return fpu_s22;
+ case dwarf_s23: return fpu_s23;
+ case dwarf_s24: return fpu_s24;
+ case dwarf_s25: return fpu_s25;
+ case dwarf_s26: return fpu_s26;
+ case dwarf_s27: return fpu_s27;
+ case dwarf_s28: return fpu_s28;
+ case dwarf_s29: return fpu_s29;
+ case dwarf_s30: return fpu_s30;
+ case dwarf_s31: return fpu_s31;
+
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGCC)
+ {
+ switch (reg)
+ {
+ case gcc_r0: return gpr_r0;
+ case gcc_r1: return gpr_r1;
+ case gcc_r2: return gpr_r2;
+ case gcc_r3: return gpr_r3;
+ case gcc_r4: return gpr_r4;
+ case gcc_r5: return gpr_r5;
+ case gcc_r6: return gpr_r6;
+ case gcc_r7: return gpr_r7;
+ case gcc_r8: return gpr_r8;
+ case gcc_r9: return gpr_r9;
+ case gcc_r10: return gpr_r10;
+ case gcc_r11: return gpr_r11;
+ case gcc_r12: return gpr_r12;
+ case gcc_sp: return gpr_sp;
+ case gcc_lr: return gpr_lr;
+ case gcc_pc: return gpr_pc;
+ case gcc_cpsr: return gpr_cpsr;
+ }
+ }
+ else if (kind == eRegisterKindLLDB)
+ {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+
+uint32_t
+RegisterContextDarwin_arm::NumSupportedHardwareBreakpoints ()
+{
+#if defined (__arm__)
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many breakpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_breakpoints = UINT32_MAX;
+ if (g_num_supported_hw_breakpoints == UINT32_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_breakpoints = 0;
+
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ g_num_supported_hw_breakpoints = Bits32 (register_DBGDIDR, 27, 24);
+ // Zero is reserved for the BRP count, so don't increment it if it is zero
+ if (g_num_supported_hw_breakpoints > 0)
+ g_num_supported_hw_breakpoints++;
+// if (log) log->Printf ("DBGDIDR=0x%8.8x (number BRP pairs = %u)", register_DBGDIDR, g_num_supported_hw_breakpoints);
+
+ }
+ return g_num_supported_hw_breakpoints;
+#else
+ // TODO: figure out remote case here!
+ return 6;
+#endif
+}
+
+uint32_t
+RegisterContextDarwin_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size)
+{
+ // Make sure our address isn't bogus
+ if (addr & 1)
+ return LLDB_INVALID_INDEX32;
+
+ int kret = ReadDBG (false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ uint32_t i;
+ for (i=0; i<num_hw_breakpoints; ++i)
+ {
+ if ((dbg.bcr[i] & BCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_breakpoints)
+ {
+ // Make sure bits 1:0 are clear in our address
+ dbg.bvr[i] = addr & ~((lldb::addr_t)3);
+
+ if (size == 2 || addr & 2)
+ {
+ uint32_t byte_addr_select = (addr & 2) ? BAS_IMVA_2_3 : BAS_IMVA_0_1;
+
+ // We have a thumb breakpoint
+ // We have an ARM breakpoint
+ dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch
+ byte_addr_select | // Set the correct byte address select so we only trigger on the correct opcode
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (Thumb)",
+// addr,
+// size,
+// i,
+// i,
+// dbg.bvr[i],
+// dbg.bcr[i]);
+ }
+ else if (size == 4)
+ {
+ // We have an ARM breakpoint
+ dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch
+ BAS_IMVA_ALL | // Stop on any of the four bytes following the IMVA
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (ARM)",
+// addr,
+// size,
+// i,
+// i,
+// dbg.bvr[i],
+// dbg.bcr[i]);
+ }
+
+ kret = WriteDBG();
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareBreakpoint() WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+// else
+// {
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(addr = %8.8p, size = %u) => all hardware breakpoint resources are being used.", addr, size);
+// }
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+RegisterContextDarwin_arm::ClearHardwareBreakpoint (uint32_t hw_index)
+{
+ int kret = ReadDBG (false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ if (hw_index < num_hw_points)
+ {
+ dbg.bcr[hw_index] = 0;
+// if (log) log->Printf ("RegisterContextDarwin_arm::SetHardwareBreakpoint( %u ) - BVR%u = 0x%8.8x BCR%u = 0x%8.8x",
+// hw_index,
+// hw_index,
+// dbg.bvr[hw_index],
+// hw_index,
+// dbg.bcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t
+RegisterContextDarwin_arm::NumSupportedHardwareWatchpoints ()
+{
+#if defined (__arm__)
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT32_MAX;
+ if (g_num_supported_hw_watchpoints == UINT32_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+
+ uint32_t register_DBGDIDR;
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ g_num_supported_hw_watchpoints = Bits32 (register_DBGDIDR, 31, 28) + 1;
+// if (log) log->Printf ("DBGDIDR=0x%8.8x (number WRP pairs = %u)", register_DBGDIDR, g_num_supported_hw_watchpoints);
+ }
+ return g_num_supported_hw_watchpoints;
+#else
+ // TODO: figure out remote case here!
+ return 2;
+#endif
+}
+
+
+uint32_t
+RegisterContextDarwin_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write)
+{
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(addr = %8.8p, size = %u, read = %u, write = %u)", addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return LLDB_INVALID_INDEX32;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return LLDB_INVALID_INDEX32;
+
+ // Can't watch more than 4 bytes per WVR/WCR pair
+ if (size > 4)
+ return LLDB_INVALID_INDEX32;
+
+ // We can only watch up to four bytes that follow a 4 byte aligned address
+ // per watchpoint register pair. Since we have at most so we can only watch
+ // until the next 4 byte boundary and we need to make sure we can properly
+ // encode this.
+ uint32_t addr_word_offset = addr % 4;
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() - addr_word_offset = 0x%8.8x", addr_word_offset);
+
+ uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset;
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() - byte_mask = 0x%8.8x", byte_mask);
+ if (byte_mask > 0xfu)
+ return LLDB_INVALID_INDEX32;
+
+ // Read the debug state
+ int kret = ReadDBG (false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ for (i=0; i<num_hw_watchpoints; ++i)
+ {
+ if ((dbg.wcr[i] & WCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_watchpoints)
+ {
+ // Make the byte_mask into a valid Byte Address Select mask
+ uint32_t byte_address_select = byte_mask << 5;
+ // Make sure bits 1:0 are clear in our address
+ dbg.wvr[i] = addr & ~((lldb::addr_t)3);
+ dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA that we will watch
+ S_USER | // Stop only in user mode
+ (read ? WCR_LOAD : 0) | // Stop on read access?
+ (write ? WCR_STORE : 0) | // Stop on write access?
+ WCR_ENABLE; // Enable this watchpoint;
+
+ kret = WriteDBG();
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ else
+ {
+// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+RegisterContextDarwin_arm::ClearHardwareWatchpoint (uint32_t hw_index)
+{
+ int kret = ReadDBG (false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ if (hw_index < num_hw_points)
+ {
+ dbg.wcr[hw_index] = 0;
+// if (log) log->Printf ("RegisterContextDarwin_arm::ClearHardwareWatchpoint( %u ) - WVR%u = 0x%8.8x WCR%u = 0x%8.8x",
+// hw_index,
+// hw_index,
+// dbg.wvr[hw_index],
+// hw_index,
+// dbg.wcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h
new file mode 100644
index 0000000..0bf204f
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h
@@ -0,0 +1,333 @@
+//===-- RegisterContextDarwin_arm.h -----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextDarwin_arm_h_
+#define liblldb_RegisterContextDarwin_arm_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+// BCR address match type
+#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21))
+#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21))
+#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21))
+#define BCR_M_RESERVED ((uint32_t)(3u << 21))
+
+// Link a BVR/BCR or WVR/WCR pair to another
+#define E_ENABLE_LINKING ((uint32_t)(1u << 20))
+
+// Byte Address Select
+#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5))
+#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6))
+#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7))
+#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8))
+#define BAS_IMVA_0_1 ((uint32_t)(3u << 5))
+#define BAS_IMVA_2_3 ((uint32_t)(3u << 7))
+#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5))
+
+// Break only in privileged or user mode
+#define S_RSVD ((uint32_t)(0u << 1))
+#define S_PRIV ((uint32_t)(1u << 1))
+#define S_USER ((uint32_t)(2u << 1))
+#define S_PRIV_USER ((S_PRIV) | (S_USER))
+
+#define BCR_ENABLE ((uint32_t)(1u))
+#define WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+class RegisterContextDarwin_arm : public lldb_private::RegisterContext
+{
+public:
+
+ RegisterContextDarwin_arm(lldb_private::Thread &thread, uint32_t concrete_frame_idx);
+
+ virtual
+ ~RegisterContextDarwin_arm();
+
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb_private::RegisterSet *
+ GetRegisterSet (size_t set);
+
+ virtual bool
+ ReadRegister (const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ virtual bool
+ WriteRegister (const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+ virtual uint32_t
+ NumSupportedHardwareBreakpoints ();
+
+ virtual uint32_t
+ SetHardwareBreakpoint (lldb::addr_t addr, size_t size);
+
+ virtual bool
+ ClearHardwareBreakpoint (uint32_t hw_idx);
+
+ virtual uint32_t
+ NumSupportedHardwareWatchpoints ();
+
+ virtual uint32_t
+ SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write);
+
+ virtual bool
+ ClearHardwareWatchpoint (uint32_t hw_index);
+
+ struct GPR
+ {
+ uint32_t r[16]; // R0-R15
+ uint32_t cpsr; // CPSR
+ };
+
+
+ struct QReg
+ {
+ uint8_t bytes[16];
+ };
+
+ struct FPU
+ {
+ union {
+ uint32_t s[32];
+ uint64_t d[32];
+ QReg q[16]; // the 128-bit NEON registers
+ } floats;
+ uint32_t fpscr;
+ };
+
+// struct NeonReg
+// {
+// uint8_t bytes[16];
+// };
+//
+// struct VFPv3
+// {
+// union {
+// uint32_t s[32];
+// uint64_t d[32];
+// NeonReg q[16];
+// } v3;
+// uint32_t fpscr;
+// };
+
+ struct EXC
+ {
+ uint32_t exception;
+ uint32_t fsr; /* Fault status */
+ uint32_t far; /* Virtual Fault Address */
+ };
+
+ struct DBG
+ {
+ uint32_t bvr[16];
+ uint32_t bcr[16];
+ uint32_t wvr[16];
+ uint32_t wcr[16];
+ };
+
+ static void
+ LogDBGRegisters (lldb_private::Log *log, const DBG& dbg);
+
+protected:
+
+ enum
+ {
+ GPRRegSet = 1, // ARM_THREAD_STATE
+ FPURegSet = 2, // ARM_VFP_STATE
+ EXCRegSet = 3, // ARM_EXCEPTION_STATE
+ DBGRegSet = 4 // ARM_DEBUG_STATE
+ };
+
+ enum
+ {
+ GPRWordCount = sizeof(GPR)/sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU)/sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC)/sizeof(uint32_t),
+ DBGWordCount = sizeof(DBG)/sizeof(uint32_t)
+ };
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ DBG dbg;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+ int dbg_errs[2]; // Read/Write errors
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (GPRRegSet, Read, -1);
+ SetError (FPURegSet, Read, -1);
+ SetError (EXCRegSet, Read, -1);
+ }
+
+ int
+ GetError (int flavor, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet: return gpr_errs[err_idx];
+ case FPURegSet: return fpu_errs[err_idx];
+ case EXCRegSet: return exc_errs[err_idx];
+ case DBGRegSet: return dbg_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+
+ bool
+ SetError (int flavor, uint32_t err_idx, int err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case DBGRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+
+ bool
+ RegisterSetIsCached (int set) const
+ {
+ return GetError(set, Read) == 0;
+ }
+
+ int
+ ReadGPR (bool force);
+
+ int
+ ReadFPU (bool force);
+
+ int
+ ReadEXC (bool force);
+
+ int
+ ReadDBG (bool force);
+
+ int
+ WriteGPR ();
+
+ int
+ WriteFPU ();
+
+ int
+ WriteEXC ();
+
+ int
+ WriteDBG ();
+
+
+ // Subclasses override these to do the actual reading.
+ virtual int
+ DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr)
+ {
+ return -1;
+ }
+
+ virtual int
+ DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int
+ DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int
+ DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) = 0;
+
+ virtual int
+ DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int
+ DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int
+ DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ virtual int
+ DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) = 0;
+
+ int
+ ReadRegisterSet (uint32_t set, bool force);
+
+ int
+ WriteRegisterSet (uint32_t set);
+
+ static uint32_t
+ GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num);
+
+ static int
+ GetSetForNativeRegNum (int reg_num);
+
+ static size_t
+ GetRegisterInfosCount ();
+
+ static const lldb_private::RegisterInfo *
+ GetRegisterInfos ();
+};
+
+#endif // liblldb_RegisterContextDarwin_arm_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp b/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp
new file mode 100644
index 0000000..a94d1f5
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp
@@ -0,0 +1,980 @@
+//===-- RegisterContextDarwin_i386.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+// C Includes
+#include <stddef.h> // offsetof
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Host/Endian.h"
+#include "llvm/Support/Compiler.h"
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+// Project includes
+#include "RegisterContextDarwin_i386.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum
+{
+ gpr_eax = 0,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_ss,
+ gpr_eflags,
+ gpr_eip,
+ gpr_cs,
+ gpr_ds,
+ gpr_es,
+ gpr_fs,
+ gpr_gs,
+
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+
+ k_num_registers,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum
+{
+ gcc_eax = 0,
+ gcc_ecx,
+ gcc_edx,
+ gcc_ebx,
+ gcc_ebp,
+ gcc_esp,
+ gcc_esi,
+ gcc_edi,
+ gcc_eip,
+ gcc_eflags
+};
+
+enum
+{
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+ dwarf_stmm0 = 11,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_xmm0 = 21,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7
+};
+
+enum
+{
+ gdb_eax = 0,
+ gdb_ecx = 1,
+ gdb_edx = 2,
+ gdb_ebx = 3,
+ gdb_esp = 4,
+ gdb_ebp = 5,
+ gdb_esi = 6,
+ gdb_edi = 7,
+ gdb_eip = 8,
+ gdb_eflags = 9,
+ gdb_cs = 10,
+ gdb_ss = 11,
+ gdb_ds = 12,
+ gdb_es = 13,
+ gdb_fs = 14,
+ gdb_gs = 15,
+ gdb_stmm0 = 16,
+ gdb_stmm1 = 17,
+ gdb_stmm2 = 18,
+ gdb_stmm3 = 19,
+ gdb_stmm4 = 20,
+ gdb_stmm5 = 21,
+ gdb_stmm6 = 22,
+ gdb_stmm7 = 23,
+ gdb_fctrl = 24, gdb_fcw = gdb_fctrl,
+ gdb_fstat = 25, gdb_fsw = gdb_fstat,
+ gdb_ftag = 26, gdb_ftw = gdb_ftag,
+ gdb_fiseg = 27, gdb_fpu_cs = gdb_fiseg,
+ gdb_fioff = 28, gdb_ip = gdb_fioff,
+ gdb_foseg = 29, gdb_fpu_ds = gdb_foseg,
+ gdb_fooff = 30, gdb_dp = gdb_fooff,
+ gdb_fop = 31,
+ gdb_xmm0 = 32,
+ gdb_xmm1 = 33,
+ gdb_xmm2 = 34,
+ gdb_xmm3 = 35,
+ gdb_xmm4 = 36,
+ gdb_xmm5 = 37,
+ gdb_xmm6 = 38,
+ gdb_xmm7 = 39,
+ gdb_mxcsr = 40,
+ gdb_mm0 = 41,
+ gdb_mm1 = 42,
+ gdb_mm2 = 43,
+ gdb_mm3 = 44,
+ gdb_mm4 = 45,
+ gdb_mm5 = 46,
+ gdb_mm6 = 47,
+ gdb_mm7 = 48
+};
+
+RegisterContextDarwin_i386::RegisterContextDarwin_i386 (Thread &thread, uint32_t concrete_frame_idx) :
+ RegisterContext(thread, concrete_frame_idx),
+ gpr(),
+ fpu(),
+ exc()
+{
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_i386::~RegisterContextDarwin_i386()
+{
+}
+
+
+
+#define GPR_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_i386::GPR, reg))
+#define FPU_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_i386::FPU, reg) + sizeof (RegisterContextDarwin_i386::GPR))
+#define EXC_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_i386::EXC, reg) + sizeof (RegisterContextDarwin_i386::GPR) + sizeof (RegisterContextDarwin_i386::FPU))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR(reg, alt) #reg, alt, sizeof(((RegisterContextDarwin_i386::GPR *)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextDarwin_i386::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextDarwin_i386::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_##reg##i, LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i }, NULL, NULL
+
+#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextDarwin_i386::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex
+#define REG_CONTEXT_SIZE (sizeof (RegisterContextDarwin_i386::GPR) + sizeof (RegisterContextDarwin_i386::FPU) + sizeof (RegisterContextDarwin_i386::EXC))
+
+static RegisterInfo g_register_infos[] =
+{
+// Macro auto defines most stuff GCC DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS
+// =============================== ======================= =================== ========================= ================== ================= ========== ===============
+ { DEFINE_GPR(eax , NULL) , { gcc_eax , dwarf_eax , LLDB_INVALID_REGNUM , gdb_eax , gpr_eax }, NULL, NULL},
+ { DEFINE_GPR(ebx , NULL) , { gcc_ebx , dwarf_ebx , LLDB_INVALID_REGNUM , gdb_ebx , gpr_ebx }, NULL, NULL},
+ { DEFINE_GPR(ecx , NULL) , { gcc_ecx , dwarf_ecx , LLDB_INVALID_REGNUM , gdb_ecx , gpr_ecx }, NULL, NULL},
+ { DEFINE_GPR(edx , NULL) , { gcc_edx , dwarf_edx , LLDB_INVALID_REGNUM , gdb_edx , gpr_edx }, NULL, NULL},
+ { DEFINE_GPR(edi , NULL) , { gcc_edi , dwarf_edi , LLDB_INVALID_REGNUM , gdb_edi , gpr_edi }, NULL, NULL},
+ { DEFINE_GPR(esi , NULL) , { gcc_esi , dwarf_esi , LLDB_INVALID_REGNUM , gdb_esi , gpr_esi }, NULL, NULL},
+ { DEFINE_GPR(ebp , "fp") , { gcc_ebp , dwarf_ebp , LLDB_REGNUM_GENERIC_FP , gdb_ebp , gpr_ebp }, NULL, NULL},
+ { DEFINE_GPR(esp , "sp") , { gcc_esp , dwarf_esp , LLDB_REGNUM_GENERIC_SP , gdb_esp , gpr_esp }, NULL, NULL},
+ { DEFINE_GPR(ss , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ss , gpr_ss }, NULL, NULL},
+ { DEFINE_GPR(eflags , "flags") , { gcc_eflags , dwarf_eflags , LLDB_REGNUM_GENERIC_FLAGS , gdb_eflags , gpr_eflags }, NULL, NULL},
+ { DEFINE_GPR(eip , "pc") , { gcc_eip , dwarf_eip , LLDB_REGNUM_GENERIC_PC , gdb_eip , gpr_eip }, NULL, NULL},
+ { DEFINE_GPR(cs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_cs , gpr_cs }, NULL, NULL},
+ { DEFINE_GPR(ds , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ds , gpr_ds }, NULL, NULL},
+ { DEFINE_GPR(es , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_es , gpr_es }, NULL, NULL},
+ { DEFINE_GPR(fs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fs , gpr_fs }, NULL, NULL},
+ { DEFINE_GPR(gs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_gs , gpr_gs }, NULL, NULL},
+
+ { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fcw , fpu_fcw }, NULL, NULL},
+ { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fsw , fpu_fsw }, NULL, NULL},
+ { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ftw , fpu_ftw }, NULL, NULL},
+ { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fop , fpu_fop }, NULL, NULL},
+ { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ip , fpu_ip }, NULL, NULL},
+ { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_cs , fpu_cs }, NULL, NULL},
+ { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_dp , fpu_dp }, NULL, NULL},
+ { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ds , fpu_ds }, NULL, NULL},
+ { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_mxcsr , fpu_mxcsr }, NULL, NULL},
+ { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, fpu_mxcsrmask}, NULL, NULL},
+ { DEFINE_FPU_VECT(stmm,0) },
+ { DEFINE_FPU_VECT(stmm,1) },
+ { DEFINE_FPU_VECT(stmm,2) },
+ { DEFINE_FPU_VECT(stmm,3) },
+ { DEFINE_FPU_VECT(stmm,4) },
+ { DEFINE_FPU_VECT(stmm,5) },
+ { DEFINE_FPU_VECT(stmm,6) },
+ { DEFINE_FPU_VECT(stmm,7) },
+ { DEFINE_FPU_VECT(xmm,0) },
+ { DEFINE_FPU_VECT(xmm,1) },
+ { DEFINE_FPU_VECT(xmm,2) },
+ { DEFINE_FPU_VECT(xmm,3) },
+ { DEFINE_FPU_VECT(xmm,4) },
+ { DEFINE_FPU_VECT(xmm,5) },
+ { DEFINE_FPU_VECT(xmm,6) },
+ { DEFINE_FPU_VECT(xmm,7) },
+
+ { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_trapno }, NULL, NULL},
+ { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_err }, NULL, NULL},
+ { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_faultvaddr }, NULL, NULL}
+};
+
+static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo));
+
+void
+RegisterContextDarwin_i386::InvalidateAllRegisters ()
+{
+ InvalidateAllRegisterStates();
+}
+
+
+size_t
+RegisterContextDarwin_i386::GetRegisterCount ()
+{
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_i386::GetRegisterInfoAtIndex (size_t reg)
+{
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return NULL;
+}
+
+size_t
+RegisterContextDarwin_i386::GetRegisterInfosCount ()
+{
+ return k_num_register_infos;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_i386::GetRegisterInfos ()
+{
+ return g_register_infos;
+}
+
+
+// General purpose registers
+static uint32_t
+g_gpr_regnums[] =
+{
+ gpr_eax,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_ss,
+ gpr_eflags,
+ gpr_eip,
+ gpr_cs,
+ gpr_ds,
+ gpr_es,
+ gpr_fs,
+ gpr_gs
+};
+
+// Floating point registers
+static uint32_t
+g_fpu_regnums[] =
+{
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7
+};
+
+// Exception registers
+
+static uint32_t
+g_exc_regnums[] =
+{
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr
+};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t);
+const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t);
+const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t);
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+static const RegisterSet g_reg_sets[] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, },
+ { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums },
+ { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums }
+};
+
+const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet);
+
+
+size_t
+RegisterContextDarwin_i386::GetRegisterSetCount ()
+{
+ return k_num_regsets;
+}
+
+const RegisterSet *
+RegisterContextDarwin_i386::GetRegisterSet (size_t reg_set)
+{
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return NULL;
+}
+
+
+//----------------------------------------------------------------------
+// Register information definitions for 32 bit i386.
+//----------------------------------------------------------------------
+int
+RegisterContextDarwin_i386::GetSetForNativeRegNum (int reg_num)
+{
+ if (reg_num < fpu_fcw)
+ return GPRRegSet;
+ else if (reg_num < exc_trapno)
+ return FPURegSet;
+ else if (reg_num < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+
+void
+RegisterContextDarwin_i386::LogGPR(Log *log, const char *title)
+{
+ if (log)
+ {
+ if (title)
+ log->Printf ("%s", title);
+ for (uint32_t i=0; i<k_num_gpr_registers; i++)
+ {
+ uint32_t reg = gpr_eax + i;
+ log->Printf("%12s = 0x%8.8x", g_register_infos[reg].name, (&gpr.eax)[reg]);
+ }
+ }
+}
+
+
+
+int
+RegisterContextDarwin_i386::ReadGPR (bool force)
+{
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(set, Read);
+}
+
+int
+RegisterContextDarwin_i386::ReadFPU (bool force)
+{
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(set, Read);
+}
+
+int
+RegisterContextDarwin_i386::ReadEXC (bool force)
+{
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(set, Read);
+}
+
+int
+RegisterContextDarwin_i386::WriteGPR ()
+{
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return -1;
+ }
+ SetError (set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError (set, Read, -1);
+ return GetError(set, Write);
+}
+
+int
+RegisterContextDarwin_i386::WriteFPU ()
+{
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return -1;
+ }
+ SetError (set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError (set, Read, -1);
+ return GetError(set, Write);
+}
+
+int
+RegisterContextDarwin_i386::WriteEXC ()
+{
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return -1;
+ }
+ SetError (set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError (set, Read, -1);
+ return GetError(set, Write);
+}
+
+int
+RegisterContextDarwin_i386::ReadRegisterSet (uint32_t set, bool force)
+{
+ switch (set)
+ {
+ case GPRRegSet: return ReadGPR(force);
+ case FPURegSet: return ReadFPU(force);
+ case EXCRegSet: return ReadEXC(force);
+ default: break;
+ }
+ return -1;
+}
+
+int
+RegisterContextDarwin_i386::WriteRegisterSet (uint32_t set)
+{
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set))
+ {
+ switch (set)
+ {
+ case GPRRegSet: return WriteGPR();
+ case FPURegSet: return WriteFPU();
+ case EXCRegSet: return WriteEXC();
+ default: break;
+ }
+ }
+ return -1;
+}
+
+bool
+RegisterContextDarwin_i386::ReadRegister (const RegisterInfo *reg_info,
+ RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_i386::GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ value = (&gpr.eax)[reg - gpr_eax];
+ break;
+
+ case fpu_fcw:
+ value = fpu.fcw;
+ break;
+
+ case fpu_fsw:
+ value = fpu.fsw;
+ break;
+
+ case fpu_ftw:
+ value = fpu.ftw;
+ break;
+
+ case fpu_fop:
+ value = fpu.fop;
+ break;
+
+ case fpu_ip:
+ value = fpu.ip;
+ break;
+
+ case fpu_cs:
+ value = fpu.cs;
+ break;
+
+ case fpu_dp:
+ value = fpu.dp;
+ break;
+
+ case fpu_ds:
+ value = fpu.ds;
+ break;
+
+ case fpu_mxcsr:
+ value = fpu.mxcsr;
+ break;
+
+ case fpu_mxcsrmask:
+ value = fpu.mxcsrmask;
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these
+ // registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, 10);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, 16);
+ return false;
+
+ case exc_trapno:
+ value = exc.trapno;
+ break;
+
+ case exc_err:
+ value = exc.err;
+ break;
+
+ case exc_faultvaddr:
+ value = exc.faultvaddr;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+
+bool
+RegisterContextDarwin_i386::WriteRegister (const RegisterInfo *reg_info,
+ const RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.eax)[reg - gpr_eax] = value.GetAsUInt32();
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = value.GetAsUInt16();
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = value.GetAsUInt16();
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = value.GetAsUInt8();
+ break;
+
+ case fpu_fop:
+ fpu.fop = value.GetAsUInt16();
+ break;
+
+ case fpu_ip:
+ fpu.ip = value.GetAsUInt32();
+ break;
+
+ case fpu_cs:
+ fpu.cs = value.GetAsUInt16();
+ break;
+
+ case fpu_dp:
+ fpu.dp = value.GetAsUInt32();
+ break;
+
+ case fpu_ds:
+ fpu.ds = value.GetAsUInt16();
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = value.GetAsUInt32();
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = value.GetAsUInt32();
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(), value.GetByteSize());
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(), value.GetByteSize());
+ return false;
+
+ case exc_trapno:
+ exc.trapno = value.GetAsUInt32();
+ break;
+
+ case exc_err:
+ exc.err = value.GetAsUInt32();
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = value.GetAsUInt32();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == 0;
+}
+
+bool
+RegisterContextDarwin_i386::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (data_sp &&
+ ReadGPR (false) == 0 &&
+ ReadFPU (false) == 0 &&
+ ReadEXC (false) == 0)
+ {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy (dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool
+RegisterContextDarwin_i386::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE)
+ {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy (&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy (&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy (&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == 0)
+ ++success_count;
+ if (WriteFPU() == 0)
+ ++success_count;
+ if (WriteEXC() == 0)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+
+uint32_t
+RegisterContextDarwin_i386::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg)
+{
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (reg)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_eip;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_esp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_ebp;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
+ {
+ switch (reg)
+ {
+ case dwarf_eax: return gpr_eax;
+ case dwarf_ecx: return gpr_ecx;
+ case dwarf_edx: return gpr_edx;
+ case dwarf_ebx: return gpr_ebx;
+ case dwarf_esp: return gpr_esp;
+ case dwarf_ebp: return gpr_ebp;
+ case dwarf_esi: return gpr_esi;
+ case dwarf_edi: return gpr_edi;
+ case dwarf_eip: return gpr_eip;
+ case dwarf_eflags: return gpr_eflags;
+ case dwarf_stmm0: return fpu_stmm0;
+ case dwarf_stmm1: return fpu_stmm1;
+ case dwarf_stmm2: return fpu_stmm2;
+ case dwarf_stmm3: return fpu_stmm3;
+ case dwarf_stmm4: return fpu_stmm4;
+ case dwarf_stmm5: return fpu_stmm5;
+ case dwarf_stmm6: return fpu_stmm6;
+ case dwarf_stmm7: return fpu_stmm7;
+ case dwarf_xmm0: return fpu_xmm0;
+ case dwarf_xmm1: return fpu_xmm1;
+ case dwarf_xmm2: return fpu_xmm2;
+ case dwarf_xmm3: return fpu_xmm3;
+ case dwarf_xmm4: return fpu_xmm4;
+ case dwarf_xmm5: return fpu_xmm5;
+ case dwarf_xmm6: return fpu_xmm6;
+ case dwarf_xmm7: return fpu_xmm7;
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGDB)
+ {
+ switch (reg)
+ {
+ case gdb_eax : return gpr_eax;
+ case gdb_ebx : return gpr_ebx;
+ case gdb_ecx : return gpr_ecx;
+ case gdb_edx : return gpr_edx;
+ case gdb_esi : return gpr_esi;
+ case gdb_edi : return gpr_edi;
+ case gdb_ebp : return gpr_ebp;
+ case gdb_esp : return gpr_esp;
+ case gdb_eip : return gpr_eip;
+ case gdb_eflags : return gpr_eflags;
+ case gdb_cs : return gpr_cs;
+ case gdb_ss : return gpr_ss;
+ case gdb_ds : return gpr_ds;
+ case gdb_es : return gpr_es;
+ case gdb_fs : return gpr_fs;
+ case gdb_gs : return gpr_gs;
+ case gdb_stmm0 : return fpu_stmm0;
+ case gdb_stmm1 : return fpu_stmm1;
+ case gdb_stmm2 : return fpu_stmm2;
+ case gdb_stmm3 : return fpu_stmm3;
+ case gdb_stmm4 : return fpu_stmm4;
+ case gdb_stmm5 : return fpu_stmm5;
+ case gdb_stmm6 : return fpu_stmm6;
+ case gdb_stmm7 : return fpu_stmm7;
+ case gdb_fctrl : return fpu_fctrl;
+ case gdb_fstat : return fpu_fstat;
+ case gdb_ftag : return fpu_ftag;
+ case gdb_fiseg : return fpu_fiseg;
+ case gdb_fioff : return fpu_fioff;
+ case gdb_foseg : return fpu_foseg;
+ case gdb_fooff : return fpu_fooff;
+ case gdb_fop : return fpu_fop;
+ case gdb_xmm0 : return fpu_xmm0;
+ case gdb_xmm1 : return fpu_xmm1;
+ case gdb_xmm2 : return fpu_xmm2;
+ case gdb_xmm3 : return fpu_xmm3;
+ case gdb_xmm4 : return fpu_xmm4;
+ case gdb_xmm5 : return fpu_xmm5;
+ case gdb_xmm6 : return fpu_xmm6;
+ case gdb_xmm7 : return fpu_xmm7;
+ case gdb_mxcsr : return fpu_mxcsr;
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindLLDB)
+ {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+
+bool
+RegisterContextDarwin_i386::HardwareSingleStep (bool enable)
+{
+ if (ReadGPR(false) != 0)
+ return false;
+
+ const uint32_t trace_bit = 0x100u;
+ if (enable)
+ {
+ // If the trace bit is already set, there is nothing to do
+ if (gpr.eflags & trace_bit)
+ return true;
+ else
+ gpr.eflags |= trace_bit;
+ }
+ else
+ {
+ // If the trace bit is already cleared, there is nothing to do
+ if (gpr.eflags & trace_bit)
+ gpr.eflags &= ~trace_bit;
+ else
+ return true;
+ }
+
+ return WriteGPR() == 0;
+}
+
+
+
diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h b/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h
new file mode 100644
index 0000000..a588494
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h
@@ -0,0 +1,269 @@
+//===-- RegisterContextDarwin_i386.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextDarwin_i386_h_
+#define liblldb_RegisterContextDarwin_i386_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+class RegisterContextDarwin_i386 : public lldb_private::RegisterContext
+{
+public:
+
+ RegisterContextDarwin_i386(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ virtual
+ ~RegisterContextDarwin_i386();
+
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb_private::RegisterSet *
+ GetRegisterSet (size_t set);
+
+ virtual bool
+ ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value);
+
+ virtual bool
+ WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+ virtual bool
+ HardwareSingleStep (bool enable);
+
+ struct GPR
+ {
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t ss;
+ uint32_t eflags;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+ };
+
+ struct MMSReg
+ {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg
+ {
+ uint8_t bytes[16];
+ };
+
+ struct FPU
+ {
+ uint32_t pad[2];
+ uint16_t fcw;
+ uint16_t fsw;
+ uint8_t ftw;
+ uint8_t pad1;
+ uint16_t fop;
+ uint32_t ip;
+ uint16_t cs;
+ uint16_t pad2;
+ uint32_t dp;
+ uint16_t ds;
+ uint16_t pad3;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[8];
+ uint8_t pad4[14*16];
+ int pad5;
+ };
+
+ struct EXC
+ {
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t faultvaddr;
+ };
+
+protected:
+
+ enum
+ {
+ GPRRegSet = 1,
+ FPURegSet = 2,
+ EXCRegSet = 3
+ };
+
+ enum
+ {
+ GPRWordCount = sizeof(GPR)/sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU)/sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC)/sizeof(uint32_t)
+ };
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (GPRRegSet, Read, -1);
+ SetError (FPURegSet, Read, -1);
+ SetError (EXCRegSet, Read, -1);
+ }
+
+ int
+ GetError (int flavor, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet: return gpr_errs[err_idx];
+ case FPURegSet: return fpu_errs[err_idx];
+ case EXCRegSet: return exc_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+
+ bool
+ SetError (int flavor, uint32_t err_idx, int err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+
+ bool
+ RegisterSetIsCached (int set) const
+ {
+ return GetError(set, Read) == 0;
+ }
+
+ void
+ LogGPR (lldb_private::Log *log, const char *title);
+
+ int
+ ReadGPR (bool force);
+
+ int
+ ReadFPU (bool force);
+
+ int
+ ReadEXC (bool force);
+
+ int
+ WriteGPR ();
+
+ int
+ WriteFPU ();
+
+ int
+ WriteEXC ();
+
+ // Subclasses override these to do the actual reading.
+ virtual int
+ DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) = 0;
+
+ virtual int
+ DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int
+ DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int
+ DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int
+ DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int
+ DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ int
+ ReadRegisterSet (uint32_t set, bool force);
+
+ int
+ WriteRegisterSet (uint32_t set);
+
+ static uint32_t
+ GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num);
+
+ static int
+ GetSetForNativeRegNum (int reg_num);
+
+ static size_t
+ GetRegisterInfosCount ();
+
+ static const lldb_private::RegisterInfo *
+ GetRegisterInfos ();
+};
+
+#endif // liblldb_RegisterContextDarwin_i386_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp b/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp
new file mode 100644
index 0000000..433782f
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp
@@ -0,0 +1,1066 @@
+//===-- RegisterContextDarwin_x86_64.cpp ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+// C Includes
+#include <stdarg.h>
+#include <stddef.h> // offsetof
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Host/Endian.h"
+#include "llvm/Support/Compiler.h"
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+// Project includes
+#include "RegisterContextDarwin_x86_64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum
+{
+ gpr_rax = 0,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs,
+
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15,
+
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+
+ k_num_registers,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum gcc_dwarf_regnums
+{
+ gcc_dwarf_gpr_rax = 0,
+ gcc_dwarf_gpr_rdx,
+ gcc_dwarf_gpr_rcx,
+ gcc_dwarf_gpr_rbx,
+ gcc_dwarf_gpr_rsi,
+ gcc_dwarf_gpr_rdi,
+ gcc_dwarf_gpr_rbp,
+ gcc_dwarf_gpr_rsp,
+ gcc_dwarf_gpr_r8,
+ gcc_dwarf_gpr_r9,
+ gcc_dwarf_gpr_r10,
+ gcc_dwarf_gpr_r11,
+ gcc_dwarf_gpr_r12,
+ gcc_dwarf_gpr_r13,
+ gcc_dwarf_gpr_r14,
+ gcc_dwarf_gpr_r15,
+ gcc_dwarf_gpr_rip,
+ gcc_dwarf_fpu_xmm0,
+ gcc_dwarf_fpu_xmm1,
+ gcc_dwarf_fpu_xmm2,
+ gcc_dwarf_fpu_xmm3,
+ gcc_dwarf_fpu_xmm4,
+ gcc_dwarf_fpu_xmm5,
+ gcc_dwarf_fpu_xmm6,
+ gcc_dwarf_fpu_xmm7,
+ gcc_dwarf_fpu_xmm8,
+ gcc_dwarf_fpu_xmm9,
+ gcc_dwarf_fpu_xmm10,
+ gcc_dwarf_fpu_xmm11,
+ gcc_dwarf_fpu_xmm12,
+ gcc_dwarf_fpu_xmm13,
+ gcc_dwarf_fpu_xmm14,
+ gcc_dwarf_fpu_xmm15,
+ gcc_dwarf_fpu_stmm0,
+ gcc_dwarf_fpu_stmm1,
+ gcc_dwarf_fpu_stmm2,
+ gcc_dwarf_fpu_stmm3,
+ gcc_dwarf_fpu_stmm4,
+ gcc_dwarf_fpu_stmm5,
+ gcc_dwarf_fpu_stmm6,
+ gcc_dwarf_fpu_stmm7
+
+};
+
+enum gdb_regnums
+{
+ gdb_gpr_rax = 0,
+ gdb_gpr_rbx = 1,
+ gdb_gpr_rcx = 2,
+ gdb_gpr_rdx = 3,
+ gdb_gpr_rsi = 4,
+ gdb_gpr_rdi = 5,
+ gdb_gpr_rbp = 6,
+ gdb_gpr_rsp = 7,
+ gdb_gpr_r8 = 8,
+ gdb_gpr_r9 = 9,
+ gdb_gpr_r10 = 10,
+ gdb_gpr_r11 = 11,
+ gdb_gpr_r12 = 12,
+ gdb_gpr_r13 = 13,
+ gdb_gpr_r14 = 14,
+ gdb_gpr_r15 = 15,
+ gdb_gpr_rip = 16,
+ gdb_gpr_rflags = 17,
+ gdb_gpr_cs = 18,
+ gdb_gpr_ss = 19,
+ gdb_gpr_ds = 20,
+ gdb_gpr_es = 21,
+ gdb_gpr_fs = 22,
+ gdb_gpr_gs = 23,
+ gdb_fpu_stmm0 = 24,
+ gdb_fpu_stmm1 = 25,
+ gdb_fpu_stmm2 = 26,
+ gdb_fpu_stmm3 = 27,
+ gdb_fpu_stmm4 = 28,
+ gdb_fpu_stmm5 = 29,
+ gdb_fpu_stmm6 = 30,
+ gdb_fpu_stmm7 = 31,
+ gdb_fpu_fctrl = 32, gdb_fpu_fcw = gdb_fpu_fctrl,
+ gdb_fpu_fstat = 33, gdb_fpu_fsw = gdb_fpu_fstat,
+ gdb_fpu_ftag = 34, gdb_fpu_ftw = gdb_fpu_ftag,
+ gdb_fpu_fiseg = 35, gdb_fpu_cs = gdb_fpu_fiseg,
+ gdb_fpu_fioff = 36, gdb_fpu_ip = gdb_fpu_fioff,
+ gdb_fpu_foseg = 37, gdb_fpu_ds = gdb_fpu_foseg,
+ gdb_fpu_fooff = 38, gdb_fpu_dp = gdb_fpu_fooff,
+ gdb_fpu_fop = 39,
+ gdb_fpu_xmm0 = 40,
+ gdb_fpu_xmm1 = 41,
+ gdb_fpu_xmm2 = 42,
+ gdb_fpu_xmm3 = 43,
+ gdb_fpu_xmm4 = 44,
+ gdb_fpu_xmm5 = 45,
+ gdb_fpu_xmm6 = 46,
+ gdb_fpu_xmm7 = 47,
+ gdb_fpu_xmm8 = 48,
+ gdb_fpu_xmm9 = 49,
+ gdb_fpu_xmm10 = 50,
+ gdb_fpu_xmm11 = 51,
+ gdb_fpu_xmm12 = 52,
+ gdb_fpu_xmm13 = 53,
+ gdb_fpu_xmm14 = 54,
+ gdb_fpu_xmm15 = 55,
+ gdb_fpu_mxcsr = 56
+};
+
+RegisterContextDarwin_x86_64::RegisterContextDarwin_x86_64 (Thread &thread, uint32_t concrete_frame_idx) :
+ RegisterContext (thread, concrete_frame_idx),
+ gpr(),
+ fpu(),
+ exc()
+{
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_x86_64::~RegisterContextDarwin_x86_64()
+{
+}
+
+#define GPR_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_x86_64::GPR, reg))
+#define FPU_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_x86_64::FPU, reg) + sizeof (RegisterContextDarwin_x86_64::GPR))
+#define EXC_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_x86_64::EXC, reg) + sizeof (RegisterContextDarwin_x86_64::GPR) + sizeof (RegisterContextDarwin_x86_64::FPU))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR(reg, alt) #reg, alt, sizeof(((RegisterContextDarwin_x86_64::GPR *)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextDarwin_x86_64::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL
+#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex
+
+#define REG_CONTEXT_SIZE (sizeof (RegisterContextDarwin_x86_64::GPR) + sizeof (RegisterContextDarwin_x86_64::FPU) + sizeof (RegisterContextDarwin_x86_64::EXC))
+
+// General purpose registers for 64 bit
+static RegisterInfo g_register_infos[] =
+{
+// Macro auto defines most stuff GCC DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS
+// =============================== ====================== =================== ========================== ==================== =================== ========== ===============
+ { DEFINE_GPR (rax , NULL) , { gcc_dwarf_gpr_rax , gcc_dwarf_gpr_rax , LLDB_INVALID_REGNUM , gdb_gpr_rax , gpr_rax }, NULL, NULL},
+ { DEFINE_GPR (rbx , NULL) , { gcc_dwarf_gpr_rbx , gcc_dwarf_gpr_rbx , LLDB_INVALID_REGNUM , gdb_gpr_rbx , gpr_rbx }, NULL, NULL},
+ { DEFINE_GPR (rcx , NULL) , { gcc_dwarf_gpr_rcx , gcc_dwarf_gpr_rcx , LLDB_INVALID_REGNUM , gdb_gpr_rcx , gpr_rcx }, NULL, NULL},
+ { DEFINE_GPR (rdx , NULL) , { gcc_dwarf_gpr_rdx , gcc_dwarf_gpr_rdx , LLDB_INVALID_REGNUM , gdb_gpr_rdx , gpr_rdx }, NULL, NULL},
+ { DEFINE_GPR (rdi , NULL) , { gcc_dwarf_gpr_rdi , gcc_dwarf_gpr_rdi , LLDB_INVALID_REGNUM , gdb_gpr_rdi , gpr_rdi }, NULL, NULL},
+ { DEFINE_GPR (rsi , NULL) , { gcc_dwarf_gpr_rsi , gcc_dwarf_gpr_rsi , LLDB_INVALID_REGNUM , gdb_gpr_rsi , gpr_rsi }, NULL, NULL},
+ { DEFINE_GPR (rbp , "fp") , { gcc_dwarf_gpr_rbp , gcc_dwarf_gpr_rbp , LLDB_REGNUM_GENERIC_FP , gdb_gpr_rbp , gpr_rbp }, NULL, NULL},
+ { DEFINE_GPR (rsp , "sp") , { gcc_dwarf_gpr_rsp , gcc_dwarf_gpr_rsp , LLDB_REGNUM_GENERIC_SP , gdb_gpr_rsp , gpr_rsp }, NULL, NULL},
+ { DEFINE_GPR (r8 , NULL) , { gcc_dwarf_gpr_r8 , gcc_dwarf_gpr_r8 , LLDB_INVALID_REGNUM , gdb_gpr_r8 , gpr_r8 }, NULL, NULL},
+ { DEFINE_GPR (r9 , NULL) , { gcc_dwarf_gpr_r9 , gcc_dwarf_gpr_r9 , LLDB_INVALID_REGNUM , gdb_gpr_r9 , gpr_r9 }, NULL, NULL},
+ { DEFINE_GPR (r10 , NULL) , { gcc_dwarf_gpr_r10 , gcc_dwarf_gpr_r10 , LLDB_INVALID_REGNUM , gdb_gpr_r10 , gpr_r10 }, NULL, NULL},
+ { DEFINE_GPR (r11 , NULL) , { gcc_dwarf_gpr_r11 , gcc_dwarf_gpr_r11 , LLDB_INVALID_REGNUM , gdb_gpr_r11 , gpr_r11 }, NULL, NULL},
+ { DEFINE_GPR (r12 , NULL) , { gcc_dwarf_gpr_r12 , gcc_dwarf_gpr_r12 , LLDB_INVALID_REGNUM , gdb_gpr_r12 , gpr_r12 }, NULL, NULL},
+ { DEFINE_GPR (r13 , NULL) , { gcc_dwarf_gpr_r13 , gcc_dwarf_gpr_r13 , LLDB_INVALID_REGNUM , gdb_gpr_r13 , gpr_r13 }, NULL, NULL},
+ { DEFINE_GPR (r14 , NULL) , { gcc_dwarf_gpr_r14 , gcc_dwarf_gpr_r14 , LLDB_INVALID_REGNUM , gdb_gpr_r14 , gpr_r14 }, NULL, NULL},
+ { DEFINE_GPR (r15 , NULL) , { gcc_dwarf_gpr_r15 , gcc_dwarf_gpr_r15 , LLDB_INVALID_REGNUM , gdb_gpr_r15 , gpr_r15 }, NULL, NULL},
+ { DEFINE_GPR (rip , "pc") , { gcc_dwarf_gpr_rip , gcc_dwarf_gpr_rip , LLDB_REGNUM_GENERIC_PC , gdb_gpr_rip , gpr_rip }, NULL, NULL},
+ { DEFINE_GPR (rflags, "flags") , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, gdb_gpr_rflags , gpr_rflags }, NULL, NULL},
+ { DEFINE_GPR (cs , NULL) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_gpr_cs , gpr_cs }, NULL, NULL},
+ { DEFINE_GPR (fs , NULL) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_gpr_fs , gpr_fs }, NULL, NULL},
+ { DEFINE_GPR (gs , NULL) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_gpr_gs , gpr_gs }, NULL, NULL},
+
+ { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_fcw , fpu_fcw }, NULL, NULL},
+ { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_fsw , fpu_fsw }, NULL, NULL},
+ { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_ftw , fpu_ftw }, NULL, NULL},
+ { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_fop , fpu_fop }, NULL, NULL},
+ { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_ip , fpu_ip }, NULL, NULL},
+ { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_cs , fpu_cs }, NULL, NULL},
+ { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_dp , fpu_dp }, NULL, NULL},
+ { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_ds , fpu_ds }, NULL, NULL},
+ { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_mxcsr , fpu_mxcsr }, NULL, NULL},
+ { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, fpu_mxcsrmask }, NULL, NULL},
+ { DEFINE_FPU_VECT(stmm,0) },
+ { DEFINE_FPU_VECT(stmm,1) },
+ { DEFINE_FPU_VECT(stmm,2) },
+ { DEFINE_FPU_VECT(stmm,3) },
+ { DEFINE_FPU_VECT(stmm,4) },
+ { DEFINE_FPU_VECT(stmm,5) },
+ { DEFINE_FPU_VECT(stmm,6) },
+ { DEFINE_FPU_VECT(stmm,7) },
+ { DEFINE_FPU_VECT(xmm,0) },
+ { DEFINE_FPU_VECT(xmm,1) },
+ { DEFINE_FPU_VECT(xmm,2) },
+ { DEFINE_FPU_VECT(xmm,3) },
+ { DEFINE_FPU_VECT(xmm,4) },
+ { DEFINE_FPU_VECT(xmm,5) },
+ { DEFINE_FPU_VECT(xmm,6) },
+ { DEFINE_FPU_VECT(xmm,7) },
+ { DEFINE_FPU_VECT(xmm,8) },
+ { DEFINE_FPU_VECT(xmm,9) },
+ { DEFINE_FPU_VECT(xmm,10) },
+ { DEFINE_FPU_VECT(xmm,11) },
+ { DEFINE_FPU_VECT(xmm,12) },
+ { DEFINE_FPU_VECT(xmm,13) },
+ { DEFINE_FPU_VECT(xmm,14) },
+ { DEFINE_FPU_VECT(xmm,15) },
+
+ { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_trapno }, NULL, NULL},
+ { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_err }, NULL, NULL},
+ { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_faultvaddr }, NULL, NULL}
+};
+
+static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo));
+
+
+void
+RegisterContextDarwin_x86_64::InvalidateAllRegisters ()
+{
+ InvalidateAllRegisterStates();
+}
+
+
+size_t
+RegisterContextDarwin_x86_64::GetRegisterCount ()
+{
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+
+const RegisterInfo *
+RegisterContextDarwin_x86_64::GetRegisterInfoAtIndex (size_t reg)
+{
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return NULL;
+}
+
+
+size_t
+RegisterContextDarwin_x86_64::GetRegisterInfosCount ()
+{
+ return k_num_register_infos;
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextDarwin_x86_64::GetRegisterInfos ()
+{
+ return g_register_infos;
+}
+
+
+
+static uint32_t g_gpr_regnums[] =
+{
+ gpr_rax,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs
+};
+
+static uint32_t g_fpu_regnums[] =
+{
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15
+};
+
+static uint32_t
+g_exc_regnums[] =
+{
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr
+};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t);
+const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t);
+const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t);
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+static const RegisterSet g_reg_sets[] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, },
+ { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums },
+ { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums }
+};
+
+const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet);
+
+
+size_t
+RegisterContextDarwin_x86_64::GetRegisterSetCount ()
+{
+ return k_num_regsets;
+}
+
+const RegisterSet *
+RegisterContextDarwin_x86_64::GetRegisterSet (size_t reg_set)
+{
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return NULL;
+}
+
+int
+RegisterContextDarwin_x86_64::GetSetForNativeRegNum (int reg_num)
+{
+ if (reg_num < fpu_fcw)
+ return GPRRegSet;
+ else if (reg_num < exc_trapno)
+ return FPURegSet;
+ else if (reg_num < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+void
+RegisterContextDarwin_x86_64::LogGPR(Log *log, const char *format, ...)
+{
+ if (log)
+ {
+ if (format)
+ {
+ va_list args;
+ va_start (args, format);
+ log->VAPrintf (format, args);
+ va_end (args);
+ }
+ for (uint32_t i=0; i<k_num_gpr_registers; i++)
+ {
+ uint32_t reg = gpr_rax + i;
+ log->Printf("%12s = 0x%16.16" PRIx64, g_register_infos[reg].name, (&gpr.rax)[reg]);
+ }
+ }
+}
+
+int
+RegisterContextDarwin_x86_64::ReadGPR (bool force)
+{
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+int
+RegisterContextDarwin_x86_64::ReadFPU (bool force)
+{
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+int
+RegisterContextDarwin_x86_64::ReadEXC (bool force)
+{
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+int
+RegisterContextDarwin_x86_64::WriteGPR ()
+{
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return -1;
+ }
+ SetError (set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError (set, Read, -1);
+ return GetError (set, Write);
+}
+
+int
+RegisterContextDarwin_x86_64::WriteFPU ()
+{
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return -1;
+ }
+ SetError (set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError (set, Read, -1);
+ return GetError (set, Write);
+}
+
+int
+RegisterContextDarwin_x86_64::WriteEXC ()
+{
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return -1;
+ }
+ SetError (set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError (set, Read, -1);
+ return GetError (set, Write);
+}
+
+int
+RegisterContextDarwin_x86_64::ReadRegisterSet(uint32_t set, bool force)
+{
+ switch (set)
+ {
+ case GPRRegSet: return ReadGPR (force);
+ case FPURegSet: return ReadFPU (force);
+ case EXCRegSet: return ReadEXC (force);
+ default: break;
+ }
+ return -1;
+}
+
+int
+RegisterContextDarwin_x86_64::WriteRegisterSet(uint32_t set)
+{
+ // Make sure we have a valid context to set.
+ switch (set)
+ {
+ case GPRRegSet: return WriteGPR ();
+ case FPURegSet: return WriteFPU ();
+ case EXCRegSet: return WriteEXC ();
+ default: break;
+ }
+ return -1;
+}
+
+
+bool
+RegisterContextDarwin_x86_64::ReadRegister (const RegisterInfo *reg_info,
+ RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum (reg);
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ value = (&gpr.rax)[reg - gpr_rax];
+ break;
+
+ case fpu_fcw:
+ value = fpu.fcw;
+ break;
+
+ case fpu_fsw:
+ value = fpu.fsw;
+ break;
+
+ case fpu_ftw:
+ value = fpu.ftw;
+ break;
+
+ case fpu_fop:
+ value = fpu.fop;
+ break;
+
+ case fpu_ip:
+ value = fpu.ip;
+ break;
+
+ case fpu_cs:
+ value = fpu.cs;
+ break;
+
+ case fpu_dp:
+ value = fpu.dp;
+ break;
+
+ case fpu_ds:
+ value = fpu.ds;
+ break;
+
+ case fpu_mxcsr:
+ value = fpu.mxcsr;
+ break;
+
+ case fpu_mxcsrmask:
+ value = fpu.mxcsrmask;
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ value.SetBytes(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, lldb::endian::InlHostByteOrder());
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ value.SetBytes(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, lldb::endian::InlHostByteOrder());
+ break;
+
+ case exc_trapno:
+ value = exc.trapno;
+ break;
+
+ case exc_err:
+ value = exc.err;
+ break;
+
+ case exc_faultvaddr:
+ value = exc.faultvaddr;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+
+bool
+RegisterContextDarwin_x86_64::WriteRegister (const RegisterInfo *reg_info,
+ const RegisterValue &value)
+{
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.rax)[reg - gpr_rax] = value.GetAsUInt64();
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = value.GetAsUInt16();
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = value.GetAsUInt16();
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = value.GetAsUInt8();
+ break;
+
+ case fpu_fop:
+ fpu.fop = value.GetAsUInt16();
+ break;
+
+ case fpu_ip:
+ fpu.ip = value.GetAsUInt32();
+ break;
+
+ case fpu_cs:
+ fpu.cs = value.GetAsUInt16();
+ break;
+
+ case fpu_dp:
+ fpu.dp = value.GetAsUInt32();
+ break;
+
+ case fpu_ds:
+ fpu.ds = value.GetAsUInt16();
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = value.GetAsUInt32();
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = value.GetAsUInt32();
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(), value.GetByteSize());
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(), value.GetByteSize());
+ return false;
+
+ case exc_trapno:
+ exc.trapno = value.GetAsUInt32();
+ break;
+
+ case exc_err:
+ exc.err = value.GetAsUInt32();
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = value.GetAsUInt64();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == 0;
+}
+
+bool
+RegisterContextDarwin_x86_64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (data_sp &&
+ ReadGPR (false) == 0 &&
+ ReadFPU (false) == 0 &&
+ ReadEXC (false) == 0)
+ {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy (dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool
+RegisterContextDarwin_x86_64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE)
+ {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy (&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy (&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy (&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == 0)
+ ++success_count;
+ if (WriteFPU() == 0)
+ ++success_count;
+ if (WriteEXC() == 0)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+
+uint32_t
+RegisterContextDarwin_x86_64::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg)
+{
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (reg)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_rip;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_rsp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_rbp;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
+ {
+ switch (reg)
+ {
+ case gcc_dwarf_gpr_rax: return gpr_rax;
+ case gcc_dwarf_gpr_rdx: return gpr_rdx;
+ case gcc_dwarf_gpr_rcx: return gpr_rcx;
+ case gcc_dwarf_gpr_rbx: return gpr_rbx;
+ case gcc_dwarf_gpr_rsi: return gpr_rsi;
+ case gcc_dwarf_gpr_rdi: return gpr_rdi;
+ case gcc_dwarf_gpr_rbp: return gpr_rbp;
+ case gcc_dwarf_gpr_rsp: return gpr_rsp;
+ case gcc_dwarf_gpr_r8: return gpr_r8;
+ case gcc_dwarf_gpr_r9: return gpr_r9;
+ case gcc_dwarf_gpr_r10: return gpr_r10;
+ case gcc_dwarf_gpr_r11: return gpr_r11;
+ case gcc_dwarf_gpr_r12: return gpr_r12;
+ case gcc_dwarf_gpr_r13: return gpr_r13;
+ case gcc_dwarf_gpr_r14: return gpr_r14;
+ case gcc_dwarf_gpr_r15: return gpr_r15;
+ case gcc_dwarf_gpr_rip: return gpr_rip;
+ case gcc_dwarf_fpu_xmm0: return fpu_xmm0;
+ case gcc_dwarf_fpu_xmm1: return fpu_xmm1;
+ case gcc_dwarf_fpu_xmm2: return fpu_xmm2;
+ case gcc_dwarf_fpu_xmm3: return fpu_xmm3;
+ case gcc_dwarf_fpu_xmm4: return fpu_xmm4;
+ case gcc_dwarf_fpu_xmm5: return fpu_xmm5;
+ case gcc_dwarf_fpu_xmm6: return fpu_xmm6;
+ case gcc_dwarf_fpu_xmm7: return fpu_xmm7;
+ case gcc_dwarf_fpu_xmm8: return fpu_xmm8;
+ case gcc_dwarf_fpu_xmm9: return fpu_xmm9;
+ case gcc_dwarf_fpu_xmm10: return fpu_xmm10;
+ case gcc_dwarf_fpu_xmm11: return fpu_xmm11;
+ case gcc_dwarf_fpu_xmm12: return fpu_xmm12;
+ case gcc_dwarf_fpu_xmm13: return fpu_xmm13;
+ case gcc_dwarf_fpu_xmm14: return fpu_xmm14;
+ case gcc_dwarf_fpu_xmm15: return fpu_xmm15;
+ case gcc_dwarf_fpu_stmm0: return fpu_stmm0;
+ case gcc_dwarf_fpu_stmm1: return fpu_stmm1;
+ case gcc_dwarf_fpu_stmm2: return fpu_stmm2;
+ case gcc_dwarf_fpu_stmm3: return fpu_stmm3;
+ case gcc_dwarf_fpu_stmm4: return fpu_stmm4;
+ case gcc_dwarf_fpu_stmm5: return fpu_stmm5;
+ case gcc_dwarf_fpu_stmm6: return fpu_stmm6;
+ case gcc_dwarf_fpu_stmm7: return fpu_stmm7;
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGDB)
+ {
+ switch (reg)
+ {
+ case gdb_gpr_rax : return gpr_rax;
+ case gdb_gpr_rbx : return gpr_rbx;
+ case gdb_gpr_rcx : return gpr_rcx;
+ case gdb_gpr_rdx : return gpr_rdx;
+ case gdb_gpr_rsi : return gpr_rsi;
+ case gdb_gpr_rdi : return gpr_rdi;
+ case gdb_gpr_rbp : return gpr_rbp;
+ case gdb_gpr_rsp : return gpr_rsp;
+ case gdb_gpr_r8 : return gpr_r8;
+ case gdb_gpr_r9 : return gpr_r9;
+ case gdb_gpr_r10 : return gpr_r10;
+ case gdb_gpr_r11 : return gpr_r11;
+ case gdb_gpr_r12 : return gpr_r12;
+ case gdb_gpr_r13 : return gpr_r13;
+ case gdb_gpr_r14 : return gpr_r14;
+ case gdb_gpr_r15 : return gpr_r15;
+ case gdb_gpr_rip : return gpr_rip;
+ case gdb_gpr_rflags : return gpr_rflags;
+ case gdb_gpr_cs : return gpr_cs;
+ case gdb_gpr_ss : return gpr_gs; // HACK: For now for "ss", just copy what is in "gs"
+ case gdb_gpr_ds : return gpr_gs; // HACK: For now for "ds", just copy what is in "gs"
+ case gdb_gpr_es : return gpr_gs; // HACK: For now for "es", just copy what is in "gs"
+ case gdb_gpr_fs : return gpr_fs;
+ case gdb_gpr_gs : return gpr_gs;
+ case gdb_fpu_stmm0 : return fpu_stmm0;
+ case gdb_fpu_stmm1 : return fpu_stmm1;
+ case gdb_fpu_stmm2 : return fpu_stmm2;
+ case gdb_fpu_stmm3 : return fpu_stmm3;
+ case gdb_fpu_stmm4 : return fpu_stmm4;
+ case gdb_fpu_stmm5 : return fpu_stmm5;
+ case gdb_fpu_stmm6 : return fpu_stmm6;
+ case gdb_fpu_stmm7 : return fpu_stmm7;
+ case gdb_fpu_fctrl : return fpu_fctrl;
+ case gdb_fpu_fstat : return fpu_fstat;
+ case gdb_fpu_ftag : return fpu_ftag;
+ case gdb_fpu_fiseg : return fpu_fiseg;
+ case gdb_fpu_fioff : return fpu_fioff;
+ case gdb_fpu_foseg : return fpu_foseg;
+ case gdb_fpu_fooff : return fpu_fooff;
+ case gdb_fpu_fop : return fpu_fop;
+ case gdb_fpu_xmm0 : return fpu_xmm0;
+ case gdb_fpu_xmm1 : return fpu_xmm1;
+ case gdb_fpu_xmm2 : return fpu_xmm2;
+ case gdb_fpu_xmm3 : return fpu_xmm3;
+ case gdb_fpu_xmm4 : return fpu_xmm4;
+ case gdb_fpu_xmm5 : return fpu_xmm5;
+ case gdb_fpu_xmm6 : return fpu_xmm6;
+ case gdb_fpu_xmm7 : return fpu_xmm7;
+ case gdb_fpu_xmm8 : return fpu_xmm8;
+ case gdb_fpu_xmm9 : return fpu_xmm9;
+ case gdb_fpu_xmm10 : return fpu_xmm10;
+ case gdb_fpu_xmm11 : return fpu_xmm11;
+ case gdb_fpu_xmm12 : return fpu_xmm12;
+ case gdb_fpu_xmm13 : return fpu_xmm13;
+ case gdb_fpu_xmm14 : return fpu_xmm14;
+ case gdb_fpu_xmm15 : return fpu_xmm15;
+ case gdb_fpu_mxcsr : return fpu_mxcsr;
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindLLDB)
+ {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+bool
+RegisterContextDarwin_x86_64::HardwareSingleStep (bool enable)
+{
+ if (ReadGPR(true) != 0)
+ return false;
+
+ const uint64_t trace_bit = 0x100ull;
+ if (enable)
+ {
+
+ if (gpr.rflags & trace_bit)
+ return true; // trace bit is already set, there is nothing to do
+ else
+ gpr.rflags |= trace_bit;
+ }
+ else
+ {
+ if (gpr.rflags & trace_bit)
+ gpr.rflags &= ~trace_bit;
+ else
+ return true; // trace bit is clear, there is nothing to do
+ }
+
+ return WriteGPR() == 0;
+}
+
diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h b/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h
new file mode 100644
index 0000000..4b8127a
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h
@@ -0,0 +1,274 @@
+//===-- RegisterContextDarwin_x86_64.h --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextDarwin_x86_64_h_
+#define liblldb_RegisterContextDarwin_x86_64_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+class RegisterContextDarwin_x86_64 : public lldb_private::RegisterContext
+{
+public:
+ RegisterContextDarwin_x86_64 (lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ virtual
+ ~RegisterContextDarwin_x86_64();
+
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb_private::RegisterSet *
+ GetRegisterSet (size_t set);
+
+ virtual bool
+ ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value);
+
+ virtual bool
+ WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+ virtual bool
+ HardwareSingleStep (bool enable);
+
+ struct GPR
+ {
+ uint64_t rax;
+ uint64_t rbx;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rsp;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rip;
+ uint64_t rflags;
+ uint64_t cs;
+ uint64_t fs;
+ uint64_t gs;
+ };
+
+ struct MMSReg
+ {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg
+ {
+ uint8_t bytes[16];
+ };
+
+ struct FPU
+ {
+ uint32_t pad[2];
+ uint16_t fcw; // "fctrl"
+ uint16_t fsw; // "fstat"
+ uint8_t ftw; // "ftag"
+ uint8_t pad1;
+ uint16_t fop; // "fop"
+ uint32_t ip; // "fioff"
+ uint16_t cs; // "fiseg"
+ uint16_t pad2;
+ uint32_t dp; // "fooff"
+ uint16_t ds; // "foseg"
+ uint16_t pad3;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[16];
+ uint8_t pad4[6*16];
+ int pad5;
+ };
+
+ struct EXC
+ {
+ uint32_t trapno;
+ uint32_t err;
+ uint64_t faultvaddr;
+ };
+
+protected:
+
+ enum
+ {
+ GPRRegSet = 4,
+ FPURegSet = 5,
+ EXCRegSet = 6
+ };
+
+ enum
+ {
+ GPRWordCount = sizeof(GPR)/sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU)/sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC)/sizeof(uint32_t)
+ };
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (GPRRegSet, Read, -1);
+ SetError (FPURegSet, Read, -1);
+ SetError (EXCRegSet, Read, -1);
+ }
+
+ int
+ GetError (int flavor, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet: return gpr_errs[err_idx];
+ case FPURegSet: return fpu_errs[err_idx];
+ case EXCRegSet: return exc_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+
+ bool
+ SetError (int flavor, uint32_t err_idx, int err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+
+ bool
+ RegisterSetIsCached (int set) const
+ {
+ return GetError(set, Read) == 0;
+ }
+
+ void
+ LogGPR (lldb_private::Log *log, const char *format, ...);
+
+ int
+ ReadGPR (bool force);
+
+ int
+ ReadFPU (bool force);
+
+ int
+ ReadEXC (bool force);
+
+ int
+ WriteGPR ();
+
+ int
+ WriteFPU ();
+
+ int
+ WriteEXC ();
+
+ // Subclasses override these to do the actual reading.
+ virtual int
+ DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) = 0;
+
+ virtual int
+ DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int
+ DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int
+ DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int
+ DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int
+ DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ int
+ ReadRegisterSet (uint32_t set, bool force);
+
+ int
+ WriteRegisterSet (uint32_t set);
+
+ static uint32_t
+ GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num);
+
+ static int
+ GetSetForNativeRegNum (int reg_num);
+
+ static size_t
+ GetRegisterInfosCount ();
+
+ static const lldb_private::RegisterInfo *
+ GetRegisterInfos ();
+
+};
+
+#endif // liblldb_RegisterContextDarwin_x86_64_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextDummy.cpp b/source/Plugins/Process/Utility/RegisterContextDummy.cpp
new file mode 100644
index 0000000..1e282ce
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextDummy.cpp
@@ -0,0 +1,137 @@
+//===-- RegisterContextDummy.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "lldb/lldb-private.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/DynamicLoader.h"
+
+#include "RegisterContextDummy.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextDummy::RegisterContextDummy (Thread &thread, uint32_t concrete_frame_idx, uint32_t address_byte_size) :
+RegisterContext (thread, concrete_frame_idx)
+{
+ m_reg_set0.name = "General Purpose Registers";
+ m_reg_set0.short_name = "GPR";
+ m_reg_set0.num_registers = 1;
+ m_reg_set0.registers = new uint32_t(0);
+
+ m_pc_reg_info.name = "pc";
+ m_pc_reg_info.alt_name = "pc";
+ m_pc_reg_info.byte_offset = 0;
+ m_pc_reg_info.byte_size = address_byte_size;
+ m_pc_reg_info.encoding = eEncodingUint;
+ m_pc_reg_info.format = eFormatPointer;
+ m_pc_reg_info.invalidate_regs = NULL;
+ m_pc_reg_info.value_regs = NULL;
+ m_pc_reg_info.kinds[eRegisterKindGCC] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ m_pc_reg_info.kinds[eRegisterKindGDB] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindLLDB] = LLDB_INVALID_REGNUM;
+}
+
+RegisterContextDummy::~RegisterContextDummy ()
+{
+ delete m_reg_set0.registers;
+ delete m_pc_reg_info.invalidate_regs;
+ delete m_pc_reg_info.value_regs;
+}
+
+void
+RegisterContextDummy::InvalidateAllRegisters () {}
+
+size_t
+RegisterContextDummy::GetRegisterCount ()
+{
+ return 1;
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextDummy::GetRegisterInfoAtIndex (size_t reg)
+{
+ if (reg)
+ return NULL;
+ return &m_pc_reg_info;
+}
+
+size_t
+RegisterContextDummy::GetRegisterSetCount ()
+{
+ return 1;
+}
+
+const lldb_private::RegisterSet *
+RegisterContextDummy::GetRegisterSet (size_t reg_set)
+{
+ if (reg_set)
+ return NULL;
+ return &m_reg_set0;
+}
+
+bool
+RegisterContextDummy::ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value)
+{
+ if (!reg_info)
+ return false;
+ uint32_t reg_number = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_number == LLDB_REGNUM_GENERIC_PC)
+ {
+ value.SetUInt(LLDB_INVALID_ADDRESS, reg_info->byte_size);
+ return true;
+ }
+ return false;
+}
+
+bool
+RegisterContextDummy::WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value)
+{
+ return false;
+}
+
+bool
+RegisterContextDummy::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ return false;
+}
+
+bool
+RegisterContextDummy::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ return false;
+}
+
+uint32_t
+RegisterContextDummy::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ if (kind == eRegisterKindGeneric && num == LLDB_REGNUM_GENERIC_PC)
+ return 0;
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/source/Plugins/Process/Utility/RegisterContextDummy.h b/source/Plugins/Process/Utility/RegisterContextDummy.h
new file mode 100644
index 0000000..ee8d5a1
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextDummy.h
@@ -0,0 +1,77 @@
+//===-- RegisterContextDummy.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextDummy_h_
+#define lldb_RegisterContextDummy_h_
+
+#include <vector>
+
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Symbol/SymbolContext.h"
+
+namespace lldb_private {
+
+class RegisterContextDummy : public lldb_private::RegisterContext
+{
+public:
+ typedef std::shared_ptr<RegisterContextDummy> SharedPtr;
+
+ RegisterContextDummy (Thread &thread, uint32_t concrete_frame_idx, uint32_t address_byte_size);
+
+ ///
+ // pure virtual functions from the base class that we must implement
+ ///
+
+ virtual
+ ~RegisterContextDummy ();
+
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb_private::RegisterSet *
+ GetRegisterSet (size_t reg_set);
+
+ virtual bool
+ ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value);
+
+ virtual bool
+ WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+private:
+ //------------------------------------------------------------------
+ // For RegisterContextLLDB only
+ //------------------------------------------------------------------
+
+ lldb_private::RegisterSet m_reg_set0; // register set 0 (PC only)
+ lldb_private::RegisterInfo m_pc_reg_info;
+
+ DISALLOW_COPY_AND_ASSIGN (RegisterContextDummy);
+};
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextDummy_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
new file mode 100644
index 0000000..1ffc30d
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
@@ -0,0 +1,1541 @@
+//===-- RegisterContextLLDB.cpp --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "lldb/lldb-private.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/DynamicLoader.h"
+
+#include "RegisterContextLLDB.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextLLDB::RegisterContextLLDB
+(
+ Thread& thread,
+ const SharedPtr &next_frame,
+ SymbolContext& sym_ctx,
+ uint32_t frame_number,
+ UnwindLLDB& unwind_lldb
+) :
+ RegisterContext (thread, frame_number),
+ m_thread(thread),
+ m_fast_unwind_plan_sp (),
+ m_full_unwind_plan_sp (),
+ m_all_registers_available(false),
+ m_frame_type (-1),
+ m_cfa (LLDB_INVALID_ADDRESS),
+ m_start_pc (),
+ m_current_pc (),
+ m_current_offset (0),
+ m_current_offset_backed_up_one (0),
+ m_sym_ctx(sym_ctx),
+ m_sym_ctx_valid (false),
+ m_frame_number (frame_number),
+ m_registers(),
+ m_parent_unwind (unwind_lldb)
+{
+ m_sym_ctx.Clear(false);
+ m_sym_ctx_valid = false;
+
+ if (IsFrameZero ())
+ {
+ InitializeZerothFrame ();
+ }
+ else
+ {
+ InitializeNonZerothFrame ();
+ }
+
+ // This same code exists over in the GetFullUnwindPlanForFrame() but it may not have been executed yet
+ if (IsFrameZero()
+ || next_frame->m_frame_type == eSigtrampFrame
+ || next_frame->m_frame_type == eDebuggerFrame)
+ {
+ m_all_registers_available = true;
+ }
+}
+
+// Initialize a RegisterContextLLDB which is the first frame of a stack -- the zeroth frame or currently
+// executing frame.
+
+void
+RegisterContextLLDB::InitializeZerothFrame()
+{
+ Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext();
+
+ if (reg_ctx_sp.get() == NULL)
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ addr_t current_pc = reg_ctx_sp->GetPC();
+
+ if (current_pc == LLDB_INVALID_ADDRESS)
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
+ // this will strip bit zero in case we read a PC from memory or from the LR.
+ // (which would be a no-op in frame 0 where we get it from the register set,
+ // but still a good idea to make the call here for other ABIs that may exist.)
+ ABI *abi = process->GetABI().get();
+ if (abi)
+ current_pc = abi->FixCodeAddress(current_pc);
+
+ // Initialize m_current_pc, an Address object, based on current_pc, an addr_t.
+ process->GetTarget().GetSectionLoadList().ResolveLoadAddress (current_pc, m_current_pc);
+
+ // If we don't have a Module for some reason, we're not going to find symbol/function information - just
+ // stick in some reasonable defaults and hope we can unwind past this frame.
+ ModuleSP pc_module_sp (m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp)
+ {
+ UnwindLogMsg ("using architectural default unwind method");
+ }
+
+ // We require that eSymbolContextSymbol be successfully filled in or this context is of no use to us.
+ if (pc_module_sp.get()
+ && (pc_module_sp->ResolveSymbolContextForAddress (m_current_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol)
+ {
+ m_sym_ctx_valid = true;
+ }
+
+ AddressRange addr_range;
+ m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range);
+
+ static ConstString g_sigtramp_name ("_sigtramp");
+ if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == g_sigtramp_name) ||
+ (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == g_sigtramp_name))
+ {
+ m_frame_type = eSigtrampFrame;
+ }
+ else
+ {
+ // FIXME: Detect eDebuggerFrame here.
+ m_frame_type = eNormalFrame;
+ }
+
+ // If we were able to find a symbol/function, set addr_range to the bounds of that symbol/function.
+ // else treat the current pc value as the start_pc and record no offset.
+ if (addr_range.GetBaseAddress().IsValid())
+ {
+ m_start_pc = addr_range.GetBaseAddress();
+ if (m_current_pc.GetSection() == m_start_pc.GetSection())
+ {
+ m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset();
+ }
+ else if (m_current_pc.GetModule() == m_start_pc.GetModule())
+ {
+ // This means that whatever symbol we kicked up isn't really correct
+ // --- we should not cross section boundaries ... We really should NULL out
+ // the function/symbol in this case unless there is a bad assumption
+ // here due to inlined functions?
+ m_current_offset = m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress();
+ }
+ m_current_offset_backed_up_one = m_current_offset;
+ }
+ else
+ {
+ m_start_pc = m_current_pc;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ }
+
+ // We've set m_frame_type and m_sym_ctx before these calls.
+
+ m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame ();
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();
+
+ UnwindPlan::RowSP active_row;
+ int cfa_offset = 0;
+ int row_register_kind = -1;
+ if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ {
+ active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
+ row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
+ if (active_row.get() && log)
+ {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg ("%s", active_row_strm.GetString().c_str());
+ }
+ }
+
+ if (!active_row.get())
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+
+ addr_t cfa_regval = LLDB_INVALID_ADDRESS;
+ if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval))
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ cfa_offset = active_row->GetCFAOffset ();
+ m_cfa = cfa_regval + cfa_offset;
+
+ UnwindLogMsg ("cfa_regval = 0x%16.16" PRIx64 " (cfa_regval = 0x%16.16" PRIx64 ", cfa_offset = %i)", m_cfa, cfa_regval, cfa_offset);
+ UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " using %s UnwindPlan",
+ (uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()),
+ (uint64_t) m_cfa,
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+}
+
+// Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the RegisterContextLLDB "below" it
+// to provide things like its current pc value.
+
+void
+RegisterContextLLDB::InitializeNonZerothFrame()
+{
+ Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
+ if (IsFrameZero ())
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ if (!GetNextFrame().get() || !GetNextFrame()->IsValid())
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ if (!m_thread.GetRegisterContext())
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ addr_t pc;
+ if (!ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
+ {
+ UnwindLogMsg ("could not get pc value");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ if (log)
+ {
+ UnwindLogMsg ("pc = 0x%16.16" PRIx64, pc);
+ addr_t reg_val;
+ if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val))
+ UnwindLogMsg ("fp = 0x%16.16" PRIx64, reg_val);
+ if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val))
+ UnwindLogMsg ("sp = 0x%16.16" PRIx64, reg_val);
+ }
+
+ // A pc of 0x0 means it's the end of the stack crawl
+ if (pc == 0)
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
+ // this will strip bit zero in case we read a PC from memory or from the LR.
+ ABI *abi = process->GetABI().get();
+ if (abi)
+ pc = abi->FixCodeAddress(pc);
+
+ process->GetTarget().GetSectionLoadList().ResolveLoadAddress (pc, m_current_pc);
+
+ // If we don't have a Module for some reason, we're not going to find symbol/function information - just
+ // stick in some reasonable defaults and hope we can unwind past this frame.
+ ModuleSP pc_module_sp (m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp)
+ {
+ UnwindLogMsg ("using architectural default unwind method");
+
+ // Test the pc value to see if we know it's in an unmapped/non-executable region of memory.
+ uint32_t permissions;
+ if (process->GetLoadAddressPermissions(pc, permissions)
+ && (permissions & ePermissionsExecutable) == 0)
+ {
+ // If this is the second frame off the stack, we may have unwound the first frame
+ // incorrectly. But using the architecture default unwind plan may get us back on
+ // track -- albeit possibly skipping a real frame. Give this frame a clearly-invalid
+ // pc and see if we can get any further.
+ if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsFrameZero())
+ {
+ UnwindLogMsg ("had a pc of 0x%" PRIx64 " which is not in executable memory but on frame 1 -- allowing it once.",
+ (uint64_t) pc);
+ m_frame_type = eSkipFrame;
+ }
+ else
+ {
+ // anywhere other than the second frame, a non-executable pc means we're off in the weeds -- stop now.
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ }
+
+ if (abi)
+ {
+ m_fast_unwind_plan_sp.reset ();
+ m_full_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
+ abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp);
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNormalFrame;
+ }
+ m_all_registers_available = false;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ addr_t cfa_regval = LLDB_INVALID_ADDRESS;
+ int row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
+ UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0);
+ if (row.get())
+ {
+ uint32_t cfa_regnum = row->GetCFARegister();
+ int cfa_offset = row->GetCFAOffset();
+ if (!ReadGPRValue (row_register_kind, cfa_regnum, cfa_regval))
+ {
+ UnwindLogMsg ("failed to get cfa value");
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNormalFrame;
+ }
+ return;
+ }
+ m_cfa = cfa_regval + cfa_offset;
+
+ // A couple of sanity checks..
+ if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1)
+ {
+ UnwindLogMsg ("could not find a valid cfa address");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ // cfa_regval should point into the stack memory; if we can query memory region permissions,
+ // see if the memory is allocated & readable.
+ if (process->GetLoadAddressPermissions(cfa_regval, permissions)
+ && (permissions & ePermissionsReadable) == 0)
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ }
+ else
+ {
+ UnwindLogMsg ("could not find a row for function offset zero");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ UnwindLogMsg ("initialized frame cfa is 0x%" PRIx64, (uint64_t) m_cfa);
+ return;
+ }
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ // We require that eSymbolContextSymbol be successfully filled in or this context is of no use to us.
+ if ((pc_module_sp->ResolveSymbolContextForAddress (m_current_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol)
+ {
+ m_sym_ctx_valid = true;
+ }
+
+ AddressRange addr_range;
+ if (!m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range))
+ {
+ m_sym_ctx_valid = false;
+ }
+
+ bool decr_pc_and_recompute_addr_range = false;
+
+ // If the symbol lookup failed...
+ if (m_sym_ctx_valid == false)
+ decr_pc_and_recompute_addr_range = true;
+
+ // Or if we're in the middle of the stack (and not "above" an asynchronous event like sigtramp),
+ // and our "current" pc is the start of a function...
+ if (m_sym_ctx_valid
+ && GetNextFrame()->m_frame_type != eSigtrampFrame
+ && GetNextFrame()->m_frame_type != eDebuggerFrame
+ && addr_range.GetBaseAddress().IsValid()
+ && addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection()
+ && addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset())
+ {
+ decr_pc_and_recompute_addr_range = true;
+ }
+
+ // We need to back up the pc by 1 byte and re-search for the Symbol to handle the case where the "saved pc"
+ // value is pointing to the next function, e.g. if a function ends with a CALL instruction.
+ // FIXME this may need to be an architectural-dependent behavior; if so we'll need to add a member function
+ // to the ABI plugin and consult that.
+ if (decr_pc_and_recompute_addr_range)
+ {
+ Address temporary_pc(m_current_pc);
+ temporary_pc.SetOffset(m_current_pc.GetOffset() - 1);
+ m_sym_ctx.Clear(false);
+ m_sym_ctx_valid = false;
+ if ((pc_module_sp->ResolveSymbolContextForAddress (temporary_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol)
+ {
+ m_sym_ctx_valid = true;
+ }
+ if (!m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range))
+ {
+ m_sym_ctx_valid = false;
+ }
+ }
+
+ // If we were able to find a symbol/function, set addr_range_ptr to the bounds of that symbol/function.
+ // else treat the current pc value as the start_pc and record no offset.
+ if (addr_range.GetBaseAddress().IsValid())
+ {
+ m_start_pc = addr_range.GetBaseAddress();
+ m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset();
+ m_current_offset_backed_up_one = m_current_offset;
+ if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0)
+ {
+ m_current_offset_backed_up_one--;
+ if (m_sym_ctx_valid)
+ m_current_pc.SetOffset(m_current_pc.GetOffset() - 1);
+ }
+ }
+ else
+ {
+ m_start_pc = m_current_pc;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ }
+
+ static ConstString sigtramp_name ("_sigtramp");
+ if ((m_sym_ctx.function && m_sym_ctx.function->GetMangled().GetMangledName() == sigtramp_name)
+ || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetMangled().GetMangledName() == sigtramp_name))
+ {
+ m_frame_type = eSigtrampFrame;
+ }
+ else
+ {
+ // FIXME: Detect eDebuggerFrame here.
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNormalFrame;
+ }
+ }
+
+ // We've set m_frame_type and m_sym_ctx before this call.
+ m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame ();
+
+ UnwindPlan::RowSP active_row;
+ int cfa_offset = 0;
+ int row_register_kind = -1;
+
+ // Try to get by with just the fast UnwindPlan if possible - the full UnwindPlan may be expensive to get
+ // (e.g. if we have to parse the entire eh_frame section of an ObjectFile for the first time.)
+
+ if (m_fast_unwind_plan_sp && m_fast_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ {
+ active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
+ row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind ();
+ if (active_row.get() && log)
+ {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_fast_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg ("active row: %s", active_row_strm.GetString().c_str());
+ }
+ }
+ else
+ {
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();
+ if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ {
+ active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
+ row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
+ if (active_row.get() && log)
+ {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg ("active row: %s", active_row_strm.GetString().c_str());
+ }
+ }
+ }
+
+ if (!active_row.get())
+ {
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ addr_t cfa_regval = LLDB_INVALID_ADDRESS;
+ if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval))
+ {
+ UnwindLogMsg ("failed to get cfa reg %d/%d", row_register_kind, active_row->GetCFARegister());
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ cfa_offset = active_row->GetCFAOffset ();
+ m_cfa = cfa_regval + cfa_offset;
+
+ UnwindLogMsg ("cfa_regval = 0x%16.16" PRIx64 " (cfa_regval = 0x%16.16" PRIx64 ", cfa_offset = %i)", m_cfa, cfa_regval, cfa_offset);
+
+ // A couple of sanity checks..
+ if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1)
+ {
+ UnwindLogMsg ("could not find a valid cfa address");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ // If we have a bad stack setup, we can get the same CFA value multiple times -- or even
+ // more devious, we can actually oscillate between two CFA values. Detect that here and
+ // break out to avoid a possible infinite loop in lldb trying to unwind the stack.
+ addr_t next_frame_cfa;
+ addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS;
+ if (GetNextFrame().get() && GetNextFrame()->GetCFA(next_frame_cfa))
+ {
+ bool repeating_frames = false;
+ if (next_frame_cfa == m_cfa)
+ {
+ repeating_frames = true;
+ }
+ else
+ {
+ if (GetNextFrame()->GetNextFrame() && GetNextFrame()->GetNextFrame()->GetCFA(next_next_frame_cfa)
+ && next_next_frame_cfa == m_cfa)
+ {
+ repeating_frames = true;
+ }
+ }
+ if (repeating_frames && abi->FunctionCallsChangeCFA())
+ {
+ UnwindLogMsg ("same CFA address as next frame, assuming the unwind is looping - stopping");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ }
+
+ UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64,
+ (uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()), (uint64_t) m_cfa);
+}
+
+
+bool
+RegisterContextLLDB::IsFrameZero () const
+{
+ return m_frame_number == 0;
+}
+
+
+// Find a fast unwind plan for this frame, if possible.
+//
+// On entry to this method,
+//
+// 1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame if either of those are correct,
+// 2. m_sym_ctx should already be filled in, and
+// 3. m_current_pc should have the current pc value for this frame
+// 4. m_current_offset_backed_up_one should have the current byte offset into the function, maybe backed up by 1, -1 if unknown
+
+UnwindPlanSP
+RegisterContextLLDB::GetFastUnwindPlanForFrame ()
+{
+ UnwindPlanSP unwind_plan_sp;
+ ModuleSP pc_module_sp (m_current_pc.GetModule());
+
+ if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL)
+ return unwind_plan_sp;
+
+ if (IsFrameZero ())
+ return unwind_plan_sp;
+
+ FuncUnwindersSP func_unwinders_sp (pc_module_sp->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx));
+ if (!func_unwinders_sp)
+ return unwind_plan_sp;
+
+ // If we're in _sigtramp(), unwinding past this frame requires special knowledge.
+ if (m_frame_type == eSigtrampFrame || m_frame_type == eDebuggerFrame)
+ return unwind_plan_sp;
+
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind (m_thread);
+ if (unwind_plan_sp)
+ {
+ if (unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ {
+ Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
+ if (log && log->GetVerbose())
+ {
+ if (m_fast_unwind_plan_sp)
+ UnwindLogMsgVerbose ("frame, and has a fast UnwindPlan");
+ else
+ UnwindLogMsgVerbose ("frame");
+ }
+ m_frame_type = eNormalFrame;
+ return unwind_plan_sp;
+ }
+ else
+ {
+ unwind_plan_sp.reset();
+ }
+ }
+ return unwind_plan_sp;
+}
+
+// On entry to this method,
+//
+// 1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame if either of those are correct,
+// 2. m_sym_ctx should already be filled in, and
+// 3. m_current_pc should have the current pc value for this frame
+// 4. m_current_offset_backed_up_one should have the current byte offset into the function, maybe backed up by 1, -1 if unknown
+
+UnwindPlanSP
+RegisterContextLLDB::GetFullUnwindPlanForFrame ()
+{
+ UnwindPlanSP unwind_plan_sp;
+ UnwindPlanSP arch_default_unwind_plan_sp;
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ ABI *abi = process ? process->GetABI().get() : NULL;
+ if (abi)
+ {
+ arch_default_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
+ abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
+ }
+
+ bool behaves_like_zeroth_frame = false;
+ if (IsFrameZero ()
+ || GetNextFrame()->m_frame_type == eSigtrampFrame
+ || GetNextFrame()->m_frame_type == eDebuggerFrame)
+ {
+ behaves_like_zeroth_frame = true;
+ // If this frame behaves like a 0th frame (currently executing or
+ // interrupted asynchronously), all registers can be retrieved.
+ m_all_registers_available = true;
+ }
+
+ // If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer) so the pc is 0x0
+ // in the zeroth frame, we need to use the "unwind at first instruction" arch default UnwindPlan
+ // Also, if this Process can report on memory region attributes, any non-executable region means
+ // we jumped through a bad function pointer - handle the same way as 0x0.
+ // Note, if the symbol context has a function for the symbol, then we don't need to do this check.
+
+ if ((!m_sym_ctx_valid || m_sym_ctx.function == NULL) && behaves_like_zeroth_frame && m_current_pc.IsValid())
+ {
+ uint32_t permissions;
+ addr_t current_pc_addr = m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr());
+ if (current_pc_addr == 0
+ || (process->GetLoadAddressPermissions(current_pc_addr, permissions)
+ && (permissions & ePermissionsExecutable) == 0))
+ {
+ unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
+ abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp);
+ m_frame_type = eNormalFrame;
+ return unwind_plan_sp;
+ }
+ }
+
+ // No Module for the current pc, try using the architecture default unwind.
+ ModuleSP pc_module_sp (m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL)
+ {
+ m_frame_type = eNormalFrame;
+ return arch_default_unwind_plan_sp;
+ }
+
+ FuncUnwindersSP func_unwinders_sp;
+ if (m_sym_ctx_valid)
+ {
+ func_unwinders_sp = pc_module_sp->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
+ }
+
+ // No FuncUnwinders available for this pc (i.e. a stripped function symbol and -fomit-frame-pointer).
+ // Try using the eh_frame information relative to the current PC,
+ // and finally fall back on the architectural default unwind.
+ if (!func_unwinders_sp)
+ {
+ DWARFCallFrameInfo *eh_frame = pc_module_sp && pc_module_sp->GetObjectFile() ?
+ pc_module_sp->GetObjectFile()->GetUnwindTable().GetEHFrameInfo() : nullptr;
+
+ m_frame_type = eNormalFrame;
+ if (eh_frame && m_current_pc.IsValid())
+ {
+ unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
+ // Even with -fomit-frame-pointer, we can try eh_frame to get back on track.
+ if (eh_frame->GetUnwindPlan (m_current_pc, *unwind_plan_sp))
+ return unwind_plan_sp;
+ else
+ unwind_plan_sp.reset();
+ }
+ return arch_default_unwind_plan_sp;
+ }
+
+ // If we're in _sigtramp(), unwinding past this frame requires special knowledge. On Mac OS X this knowledge
+ // is properly encoded in the eh_frame section, so prefer that if available.
+ // On other platforms we may need to provide a platform-specific UnwindPlan which encodes the details of
+ // how to unwind out of sigtramp.
+ if (m_frame_type == eSigtrampFrame)
+ {
+ m_fast_unwind_plan_sp.reset();
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ return unwind_plan_sp;
+ }
+
+ // Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame even when it's frame zero
+ // This comes up if we have hand-written functions in a Module and hand-written eh_frame. The assembly
+ // instruction inspection may fail and the eh_frame CFI were probably written with some care to do the
+ // right thing. It'd be nice if there was a way to ask the eh_frame directly if it is asynchronous
+ // (can be trusted at every instruction point) or synchronous (the normal case - only at call sites).
+ // But there is not.
+ if (process && process->GetDynamicLoader() && process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo (m_sym_ctx))
+ {
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ {
+ UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan because the DynamicLoader suggested we prefer it",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+ }
+
+ // Typically the NonCallSite UnwindPlan is the unwind created by inspecting the assembly language instructions
+ if (behaves_like_zeroth_frame)
+ {
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread);
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ {
+ UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+ }
+
+ // Typically this is unwind info from an eh_frame section intended for exception handling; only valid at call sites
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ {
+ UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+
+ // We'd prefer to use an UnwindPlan intended for call sites when we're at a call site but if we've
+ // struck out on that, fall back to using the non-call-site assembly inspection UnwindPlan if possible.
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread);
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
+ {
+ UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+
+ // If nothing else, use the architectural default UnwindPlan and hope that does the job.
+ UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", arch_default_unwind_plan_sp->GetSourceName().GetCString());
+ return arch_default_unwind_plan_sp;
+}
+
+
+void
+RegisterContextLLDB::InvalidateAllRegisters ()
+{
+ m_frame_type = eNotAValidFrame;
+}
+
+size_t
+RegisterContextLLDB::GetRegisterCount ()
+{
+ return m_thread.GetRegisterContext()->GetRegisterCount();
+}
+
+const RegisterInfo *
+RegisterContextLLDB::GetRegisterInfoAtIndex (size_t reg)
+{
+ return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex (reg);
+}
+
+size_t
+RegisterContextLLDB::GetRegisterSetCount ()
+{
+ return m_thread.GetRegisterContext()->GetRegisterSetCount ();
+}
+
+const RegisterSet *
+RegisterContextLLDB::GetRegisterSet (size_t reg_set)
+{
+ return m_thread.GetRegisterContext()->GetRegisterSet (reg_set);
+}
+
+uint32_t
+RegisterContextLLDB::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num);
+}
+
+bool
+RegisterContextLLDB::ReadRegisterValueFromRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const RegisterInfo *reg_info,
+ RegisterValue &value)
+{
+ if (!IsValid())
+ return false;
+ bool success = false;
+
+ switch (regloc.type)
+ {
+ case UnwindLLDB::RegisterLocation::eRegisterInRegister:
+ {
+ const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
+
+ if (!other_reg_info)
+ return false;
+
+ if (IsFrameZero ())
+ {
+ success = m_thread.GetRegisterContext()->ReadRegister (other_reg_info, value);
+ }
+ else
+ {
+ success = GetNextFrame()->ReadRegister (other_reg_info, value);
+ }
+ }
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterValueInferred:
+ success = value.SetUInt (regloc.location.inferred_value, reg_info->byte_size);
+ break;
+
+ case UnwindLLDB::RegisterLocation::eRegisterNotSaved:
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation:
+ assert ("FIXME debugger inferior function call unwind");
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation:
+ {
+ Error error (ReadRegisterValueFromMemory(reg_info,
+ regloc.location.target_memory_location,
+ reg_info->byte_size,
+ value));
+ success = error.Success();
+ }
+ break;
+ default:
+ assert ("Unknown RegisterLocation type.");
+ break;
+ }
+ return success;
+}
+
+bool
+RegisterContextLLDB::WriteRegisterValueToRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const RegisterInfo *reg_info,
+ const RegisterValue &value)
+{
+ if (!IsValid())
+ return false;
+
+ bool success = false;
+
+ switch (regloc.type)
+ {
+ case UnwindLLDB::RegisterLocation::eRegisterInRegister:
+ {
+ const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
+ if (IsFrameZero ())
+ {
+ success = m_thread.GetRegisterContext()->WriteRegister (other_reg_info, value);
+ }
+ else
+ {
+ success = GetNextFrame()->WriteRegister (other_reg_info, value);
+ }
+ }
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterValueInferred:
+ case UnwindLLDB::RegisterLocation::eRegisterNotSaved:
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation:
+ assert ("FIXME debugger inferior function call unwind");
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation:
+ {
+ Error error (WriteRegisterValueToMemory (reg_info,
+ regloc.location.target_memory_location,
+ reg_info->byte_size,
+ value));
+ success = error.Success();
+ }
+ break;
+ default:
+ assert ("Unknown RegisterLocation type.");
+ break;
+ }
+ return success;
+}
+
+
+bool
+RegisterContextLLDB::IsValid () const
+{
+ return m_frame_type != eNotAValidFrame;
+}
+
+// A skip frame is a bogus frame on the stack -- but one where we're likely to find a real frame farther
+// up the stack if we keep looking. It's always the second frame in an unwind (i.e. the first frame after
+// frame zero) where unwinding can be the trickiest. Ideally we'll mark up this frame in some way so the
+// user knows we're displaying bad data and we may have skipped one frame of their real program in the
+// process of getting back on track.
+
+bool
+RegisterContextLLDB::IsSkipFrame () const
+{
+ return m_frame_type == eSkipFrame;
+}
+
+// Answer the question: Where did THIS frame save the CALLER frame ("previous" frame)'s register value?
+
+enum UnwindLLDB::RegisterSearchResult
+RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc)
+{
+ // Have we already found this register location?
+ if (!m_registers.empty())
+ {
+ std::map<uint32_t, lldb_private::UnwindLLDB::RegisterLocation>::const_iterator iterator;
+ iterator = m_registers.find (lldb_regnum);
+ if (iterator != m_registers.end())
+ {
+ regloc = iterator->second;
+ UnwindLogMsg ("supplying caller's saved reg %d's location, cached", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+
+ uint32_t sp_regnum = LLDB_INVALID_REGNUM;
+ uint32_t pc_regnum = LLDB_INVALID_REGNUM;
+ m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, eRegisterKindLLDB, sp_regnum);
+ m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, eRegisterKindLLDB, pc_regnum);
+
+ // Are we looking for the CALLER's stack pointer? The stack pointer is defined to be the same as THIS frame's
+ // CFA so just return the CFA value. This is true on x86-32/x86-64 at least.
+ if (sp_regnum != LLDB_INVALID_REGNUM && sp_regnum == lldb_regnum)
+ {
+ // make sure we won't lose precision copying an addr_t (m_cfa) into a uint64_t (.inferred_value)
+ assert (sizeof (addr_t) <= sizeof (uint64_t));
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = m_cfa;
+ m_registers[lldb_regnum] = regloc;
+ UnwindLogMsg ("supplying caller's stack pointer (%d) value, computed from CFA", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ // Look through the available UnwindPlans for the register location.
+
+ UnwindPlan::Row::RegisterLocation unwindplan_regloc;
+ bool have_unwindplan_regloc = false;
+ RegisterKind unwindplan_registerkind = (RegisterKind)-1;
+
+ if (m_fast_unwind_plan_sp)
+ {
+ UnwindPlan::RowSP active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
+ unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind ();
+ uint32_t row_regnum;
+ if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum))
+ {
+ UnwindLogMsg ("could not convert lldb regnum %d into %d RegisterKind reg numbering scheme",
+ lldb_regnum, (int) unwindplan_registerkind);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
+ {
+ UnwindLogMsg ("supplying caller's saved reg %d's location using FastUnwindPlan", lldb_regnum);
+ have_unwindplan_regloc = true;
+ }
+ }
+
+ if (!have_unwindplan_regloc)
+ {
+ // m_full_unwind_plan_sp being NULL means that we haven't tried to find a full UnwindPlan yet
+ if (!m_full_unwind_plan_sp)
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();
+
+ if (m_full_unwind_plan_sp)
+ {
+ UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
+ unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind ();
+ uint32_t row_regnum;
+ bool row_register_rewritten_to_return_address_reg = false;
+
+ // If we're fetching the saved pc and this UnwindPlan defines a ReturnAddress register (e.g. lr on arm),
+ // look for the return address register number in the UnwindPlan's row.
+ if (lldb_regnum == pc_regnum && m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM)
+ {
+ row_regnum = m_full_unwind_plan_sp->GetReturnAddressRegister();
+ row_register_rewritten_to_return_address_reg = true;
+ UnwindLogMsg ("requested caller's saved PC but this UnwindPlan uses a RA reg; getting reg %d instead",
+ row_regnum);
+ }
+ else
+ {
+ if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum))
+ {
+ if (unwindplan_registerkind == eRegisterKindGeneric)
+ UnwindLogMsg ("could not convert lldb regnum %d into eRegisterKindGeneric reg numbering scheme", lldb_regnum);
+ else
+ UnwindLogMsg ("could not convert lldb regnum %d into %d RegisterKind reg numbering scheme",
+ lldb_regnum, (int) unwindplan_registerkind);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ }
+
+ if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
+ {
+ have_unwindplan_regloc = true;
+ UnwindLogMsg ("supplying caller's saved reg %d's location using %s UnwindPlan", lldb_regnum,
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+ }
+
+ // This is frame 0 and we're retrieving the PC and it's saved in a Return Address register and
+ // it hasn't been saved anywhere yet -- that is, it's still live in the actual register.
+ // Handle this specially.
+
+ if (have_unwindplan_regloc == false
+ && row_register_rewritten_to_return_address_reg == true
+ && IsFrameZero()
+ && row_regnum != LLDB_INVALID_REGNUM)
+ {
+ uint32_t ra_regnum_in_lldb_reg_numbering;
+ if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (unwindplan_registerkind, row_regnum, eRegisterKindLLDB, ra_regnum_in_lldb_reg_numbering))
+ {
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
+ new_regloc.location.register_number = ra_regnum_in_lldb_reg_numbering;
+ m_registers[lldb_regnum] = new_regloc;
+ regloc = new_regloc;
+ UnwindLogMsg ("supplying caller's register %d from the live RegisterContext at frame 0, saved in %d", lldb_regnum, ra_regnum_in_lldb_reg_numbering);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+
+ // If this architecture stores the return address in a register (it defines a Return Address register)
+ // and we're on a non-zero stack frame and the Full UnwindPlan says that the pc is stored in the
+ // RA registers (e.g. lr on arm), then we know that the full unwindplan is not trustworthy -- this
+ // is an impossible situation and the instruction emulation code has likely been misled.
+ // If this stack frame meets those criteria, we need to throw away the Full UnwindPlan that the
+ // instruction emulation came up with and fall back to the architecture's Default UnwindPlan so
+ // the stack walk can get past this point.
+
+ // Special note: If the Full UnwindPlan was generated from the compiler, don't second-guess it
+ // when we're at a call site location.
+
+ // arch_default_ra_regnum is the return address register # in the Full UnwindPlan register numbering
+ uint32_t arch_default_ra_regnum = LLDB_INVALID_REGNUM;
+ if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, unwindplan_registerkind, arch_default_ra_regnum)
+ && arch_default_ra_regnum != LLDB_INVALID_REGNUM
+ && pc_regnum != LLDB_INVALID_REGNUM
+ && pc_regnum == lldb_regnum
+ && unwindplan_regloc.IsInOtherRegister()
+ && unwindplan_regloc.GetRegisterNumber() == arch_default_ra_regnum
+ && m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes
+ && !m_all_registers_available)
+ {
+ UnwindLogMsg ("%s UnwindPlan tried to restore the pc from the link register but this is a non-zero frame",
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+
+ // Throw away the full unwindplan; install the arch default unwindplan
+ InvalidateFullUnwindPlan();
+
+ // Now re-fetch the pc value we're searching for
+ uint32_t arch_default_pc_reg = LLDB_INVALID_REGNUM;
+ UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
+ if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, m_full_unwind_plan_sp->GetRegisterKind(), arch_default_pc_reg)
+ && arch_default_pc_reg != LLDB_INVALID_REGNUM
+ && active_row
+ && active_row->GetRegisterInfo (arch_default_pc_reg, unwindplan_regloc))
+ {
+ have_unwindplan_regloc = true;
+ }
+ else
+ {
+ have_unwindplan_regloc = false;
+ }
+ }
+ }
+ }
+
+
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (have_unwindplan_regloc == false)
+ {
+ // If a volatile register is being requested, we don't want to forward the next frame's register contents
+ // up the stack -- the register is not retrievable at this frame.
+ ABI *abi = process ? process->GetABI().get() : NULL;
+ if (abi)
+ {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
+ if (reg_info && abi->RegisterIsVolatile (reg_info))
+ {
+ UnwindLogMsg ("did not supply reg location for %d because it is volatile", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile;
+ }
+ }
+
+ if (IsFrameZero ())
+ {
+ // This is frame 0 - we should return the actual live register context value
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
+ new_regloc.location.register_number = lldb_regnum;
+ m_registers[lldb_regnum] = new_regloc;
+ regloc = new_regloc;
+ UnwindLogMsg ("supplying caller's register %d from the live RegisterContext at frame 0", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ else
+ UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ // unwindplan_regloc has valid contents about where to retrieve the register
+ if (unwindplan_regloc.IsUnspecified())
+ {
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterNotSaved;
+ m_registers[lldb_regnum] = new_regloc;
+ UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ if (unwindplan_regloc.IsSame())
+ {
+ if (IsFrameZero ())
+ {
+ UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ else
+ {
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ }
+
+ if (unwindplan_regloc.IsCFAPlusOffset())
+ {
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = m_cfa + offset;
+ m_registers[lldb_regnum] = regloc;
+ UnwindLogMsg ("supplying caller's register %d, value is CFA plus offset", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsAtCFAPlusOffset())
+ {
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
+ regloc.location.target_memory_location = m_cfa + offset;
+ m_registers[lldb_regnum] = regloc;
+ UnwindLogMsg ("supplying caller's register %d from the stack, saved at CFA plus offset", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsInOtherRegister())
+ {
+ uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber();
+ uint32_t row_regnum_in_lldb;
+ if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (unwindplan_registerkind, unwindplan_regnum, eRegisterKindLLDB, row_regnum_in_lldb))
+ {
+ UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
+ regloc.location.register_number = row_regnum_in_lldb;
+ m_registers[lldb_regnum] = regloc;
+ UnwindLogMsg ("supplying caller's register %d, saved in register %d", lldb_regnum, row_regnum_in_lldb);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsDWARFExpression() || unwindplan_regloc.IsAtDWARFExpression())
+ {
+ DataExtractor dwarfdata (unwindplan_regloc.GetDWARFExpressionBytes(),
+ unwindplan_regloc.GetDWARFExpressionLength(),
+ process->GetByteOrder(), process->GetAddressByteSize());
+ DWARFExpression dwarfexpr (dwarfdata, 0, unwindplan_regloc.GetDWARFExpressionLength());
+ dwarfexpr.SetRegisterKind (unwindplan_registerkind);
+ Value result;
+ Error error;
+ if (dwarfexpr.Evaluate (&exe_ctx, NULL, NULL, this, 0, NULL, result, &error))
+ {
+ addr_t val;
+ val = result.GetScalar().ULongLong();
+ if (unwindplan_regloc.IsDWARFExpression())
+ {
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = val;
+ m_registers[lldb_regnum] = regloc;
+ UnwindLogMsg ("supplying caller's register %d via DWARF expression (IsDWARFExpression)", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ else
+ {
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
+ regloc.location.target_memory_location = val;
+ m_registers[lldb_regnum] = regloc;
+ UnwindLogMsg ("supplying caller's register %d via DWARF expression (IsAtDWARFExpression)", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+ UnwindLogMsg ("tried to use IsDWARFExpression or IsAtDWARFExpression for reg %d but failed", lldb_regnum);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum);
+
+ // FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are unsupported.
+
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+}
+
+// If the Full unwindplan has been determined to be incorrect, this method will
+// replace it with the architecture's default unwindplna, if one is defined.
+// It will also find the FuncUnwinders object for this function and replace the
+// Full unwind method for the function there so we don't use the errant Full unwindplan
+// again in the future of this debug session.
+// We're most likely doing this because the Full unwindplan was generated by assembly
+// instruction profiling and the profiler got something wrong.
+
+void
+RegisterContextLLDB::InvalidateFullUnwindPlan ()
+{
+ UnwindPlan::Row::RegisterLocation unwindplan_regloc;
+ ExecutionContext exe_ctx (m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ ABI *abi = process ? process->GetABI().get() : NULL;
+ if (abi)
+ {
+ UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp;
+ UnwindPlanSP arch_default_unwind_plan_sp;
+ arch_default_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
+ abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
+ if (arch_default_unwind_plan_sp)
+ {
+ UnwindPlan::RowSP active_row = arch_default_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
+
+ if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM)
+ {
+ FuncUnwindersSP func_unwinders_sp;
+ if (m_sym_ctx_valid && m_current_pc.IsValid() && m_current_pc.GetModule())
+ {
+ func_unwinders_sp = m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
+ if (func_unwinders_sp)
+ {
+ func_unwinders_sp->InvalidateNonCallSiteUnwindPlan (m_thread);
+ }
+ }
+ m_registers.clear();
+ m_full_unwind_plan_sp = arch_default_unwind_plan_sp;
+ addr_t cfa_regval = LLDB_INVALID_ADDRESS;
+ if (ReadGPRValue (arch_default_unwind_plan_sp->GetRegisterKind(), active_row->GetCFARegister(), cfa_regval))
+ {
+ m_cfa = cfa_regval + active_row->GetCFAOffset ();
+ }
+
+ UnwindLogMsg ("full unwind plan '%s' has been replaced by architecture default unwind plan '%s' for this function from now on.",
+ original_full_unwind_plan_sp->GetSourceName().GetCString(), arch_default_unwind_plan_sp->GetSourceName().GetCString());
+ }
+ }
+ }
+}
+
+// Retrieve a general purpose register value for THIS frame, as saved by the NEXT frame, i.e. the frame that
+// this frame called. e.g.
+//
+// foo () { }
+// bar () { foo (); }
+// main () { bar (); }
+//
+// stopped in foo() so
+// frame 0 - foo
+// frame 1 - bar
+// frame 2 - main
+// and this RegisterContext is for frame 1 (bar) - if we want to get the pc value for frame 1, we need to ask
+// where frame 0 (the "next" frame) saved that and retrieve the value.
+
+bool
+RegisterContextLLDB::ReadGPRValue (int register_kind, uint32_t regnum, addr_t &value)
+{
+ if (!IsValid())
+ return false;
+
+ uint32_t lldb_regnum;
+ if (register_kind == eRegisterKindLLDB)
+ {
+ lldb_regnum = regnum;
+ }
+ else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindLLDB, lldb_regnum))
+ {
+ return false;
+ }
+
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
+ RegisterValue reg_value;
+ // if this is frame 0 (currently executing frame), get the requested reg contents from the actual thread registers
+ if (IsFrameZero ())
+ {
+ if (m_thread.GetRegisterContext()->ReadRegister (reg_info, reg_value))
+ {
+ value = reg_value.GetAsUInt64();
+ return true;
+ }
+ return false;
+ }
+
+ bool pc_register = false;
+ uint32_t generic_regnum;
+ if (register_kind == eRegisterKindGeneric && regnum == LLDB_REGNUM_GENERIC_PC)
+ {
+ pc_register = true;
+ }
+ else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindGeneric, generic_regnum)
+ && generic_regnum == LLDB_REGNUM_GENERIC_PC)
+ {
+ pc_register = true;
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, pc_register))
+ {
+ return false;
+ }
+ if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value))
+ {
+ value = reg_value.GetAsUInt64();
+ return true;
+ }
+ return false;
+}
+
+// Find the value of a register in THIS frame
+
+bool
+RegisterContextLLDB::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value)
+{
+ if (!IsValid())
+ return false;
+
+ const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
+ UnwindLogMsgVerbose ("looking for register saved location for reg %d", lldb_regnum);
+
+ // If this is the 0th frame, hand this over to the live register context
+ if (IsFrameZero ())
+ {
+ UnwindLogMsgVerbose ("passing along to the live register context for reg %d", lldb_regnum);
+ return m_thread.GetRegisterContext()->ReadRegister (reg_info, value);
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ // Find out where the NEXT frame saved THIS frame's register contents
+ if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, false))
+ return false;
+
+ return ReadRegisterValueFromRegisterLocation (regloc, reg_info, value);
+}
+
+bool
+RegisterContextLLDB::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &value)
+{
+ if (!IsValid())
+ return false;
+
+ const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
+ UnwindLogMsgVerbose ("looking for register saved location for reg %d", lldb_regnum);
+
+ // If this is the 0th frame, hand this over to the live register context
+ if (IsFrameZero ())
+ {
+ UnwindLogMsgVerbose ("passing along to the live register context for reg %d", lldb_regnum);
+ return m_thread.GetRegisterContext()->WriteRegister (reg_info, value);
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ // Find out where the NEXT frame saved THIS frame's register contents
+ if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, false))
+ return false;
+
+ return WriteRegisterValueToRegisterLocation (regloc, reg_info, value);
+}
+
+// Don't need to implement this one
+bool
+RegisterContextLLDB::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ return false;
+}
+
+// Don't need to implement this one
+bool
+RegisterContextLLDB::WriteAllRegisterValues (const lldb::DataBufferSP& data_sp)
+{
+ return false;
+}
+
+// Retrieve the pc value for THIS from
+
+bool
+RegisterContextLLDB::GetCFA (addr_t& cfa)
+{
+ if (!IsValid())
+ {
+ return false;
+ }
+ if (m_cfa == LLDB_INVALID_ADDRESS)
+ {
+ return false;
+ }
+ cfa = m_cfa;
+ return true;
+}
+
+
+RegisterContextLLDB::SharedPtr
+RegisterContextLLDB::GetNextFrame () const
+{
+ RegisterContextLLDB::SharedPtr regctx;
+ if (m_frame_number == 0)
+ return regctx;
+ return m_parent_unwind.GetRegisterContextForFrameNum (m_frame_number - 1);
+}
+
+RegisterContextLLDB::SharedPtr
+RegisterContextLLDB::GetPrevFrame () const
+{
+ RegisterContextLLDB::SharedPtr regctx;
+ return m_parent_unwind.GetRegisterContextForFrameNum (m_frame_number + 1);
+}
+
+// Retrieve the address of the start of the function of THIS frame
+
+bool
+RegisterContextLLDB::GetStartPC (addr_t& start_pc)
+{
+ if (!IsValid())
+ return false;
+
+ if (!m_start_pc.IsValid())
+ {
+ return ReadPC (start_pc);
+ }
+ start_pc = m_start_pc.GetLoadAddress (CalculateTarget().get());
+ return true;
+}
+
+// Retrieve the current pc value for THIS frame, as saved by the NEXT frame.
+
+bool
+RegisterContextLLDB::ReadPC (addr_t& pc)
+{
+ if (!IsValid())
+ return false;
+
+ if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
+ {
+ // A pc value of 0 or 1 is impossible in the middle of the stack -- it indicates the end of a stack walk.
+ // On the currently executing frame (or such a frame interrupted asynchronously by sigtramp et al) this may
+ // occur if code has jumped through a NULL pointer -- we want to be able to unwind past that frame to help
+ // find the bug.
+
+ if (m_all_registers_available == false
+ && (pc == 0 || pc == 1))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void
+RegisterContextLLDB::UnwindLogMsg (const char *fmt, ...)
+{
+ Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
+ if (log)
+ {
+ va_list args;
+ va_start (args, fmt);
+
+ char *logmsg;
+ if (vasprintf (&logmsg, fmt, args) == -1 || logmsg == NULL)
+ {
+ if (logmsg)
+ free (logmsg);
+ va_end (args);
+ return;
+ }
+ va_end (args);
+
+ log->Printf ("%*sth%d/fr%u %s",
+ m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number,
+ logmsg);
+ free (logmsg);
+ }
+}
+
+void
+RegisterContextLLDB::UnwindLogMsgVerbose (const char *fmt, ...)
+{
+ Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
+ if (log && log->GetVerbose())
+ {
+ va_list args;
+ va_start (args, fmt);
+
+ char *logmsg;
+ if (vasprintf (&logmsg, fmt, args) == -1 || logmsg == NULL)
+ {
+ if (logmsg)
+ free (logmsg);
+ va_end (args);
+ return;
+ }
+ va_end (args);
+
+ log->Printf ("%*sth%d/fr%u %s",
+ m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number,
+ logmsg);
+ free (logmsg);
+ }
+}
+
diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.h b/source/Plugins/Process/Utility/RegisterContextLLDB.h
new file mode 100644
index 0000000..dc6d8c6
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextLLDB.h
@@ -0,0 +1,212 @@
+//===-- RegisterContextLLDB.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextLLDB_h_
+#define lldb_RegisterContextLLDB_h_
+
+#include <vector>
+
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "UnwindLLDB.h"
+
+namespace lldb_private {
+
+class UnwindLLDB;
+
+class RegisterContextLLDB : public lldb_private::RegisterContext
+{
+public:
+ typedef std::shared_ptr<RegisterContextLLDB> SharedPtr;
+
+ RegisterContextLLDB (lldb_private::Thread &thread,
+ const SharedPtr& next_frame,
+ lldb_private::SymbolContext& sym_ctx,
+ uint32_t frame_number, lldb_private::UnwindLLDB& unwind_lldb);
+
+ ///
+ // pure virtual functions from the base class that we must implement
+ ///
+
+ virtual
+ ~RegisterContextLLDB () { }
+
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb_private::RegisterSet *
+ GetRegisterSet (size_t reg_set);
+
+ virtual bool
+ ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value);
+
+ virtual bool
+ WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+ bool
+ IsValid () const;
+
+ bool
+ GetCFA (lldb::addr_t& cfa);
+
+ bool
+ GetStartPC (lldb::addr_t& start_pc);
+
+ bool
+ ReadPC (lldb::addr_t& start_pc);
+
+private:
+
+ enum FrameType
+ {
+ eNormalFrame,
+ eSigtrampFrame,
+ eDebuggerFrame, // a debugger inferior function call frame; we get caller's registers from debugger
+ eSkipFrame, // The unwind resulted in a bogus frame but may get back on track so we don't want to give up yet
+ eNotAValidFrame // this frame is invalid for some reason - most likely it is past the top (end) of the stack
+ };
+
+ // UnwindLLDB needs to pass around references to RegisterLocations
+ friend class UnwindLLDB;
+
+ // Indicates whether this frame is frame zero -- the currently
+ // executing frame -- or not.
+ bool
+ IsFrameZero () const;
+
+ void
+ InitializeZerothFrame ();
+
+ void
+ InitializeNonZerothFrame();
+
+ SharedPtr
+ GetNextFrame () const;
+
+ SharedPtr
+ GetPrevFrame () const;
+
+ // A SkipFrame occurs when the unwind out of frame 0 didn't go right -- we've got one bogus frame at frame #1.
+ // There is a good chance we'll get back on track if we follow the frame pointer chain (or whatever is appropriate
+ // on this ABI) so we allow one invalid frame to be in the stack. Ideally we'll mark this frame specially at some
+ // point and indicate to the user that the unwinder had a hiccup. Often when this happens we will miss a frame of
+ // the program's actual stack in the unwind and we want to flag that for the user somehow.
+ bool
+ IsSkipFrame () const;
+
+ // Provide a location for where THIS function saved the CALLER's register value
+ // Or a frame "below" this one saved it, i.e. a function called by this one, preserved a register that this
+ // function didn't modify/use.
+ //
+ // The RegisterLocation type may be set to eRegisterNotAvailable -- this will happen for a volatile register
+ // being queried mid-stack. Instead of floating frame 0's contents of that register up the stack (which may
+ // or may not be the value of that reg when the function was executing), we won't return any value.
+ //
+ // If a non-volatile register (a "preserved" register) is requested mid-stack and no frames "below" the requested
+ // stack have saved the register anywhere, it is safe to assume that frame 0's register values are still the same
+ // as the requesting frame's.
+ lldb_private::UnwindLLDB::RegisterSearchResult
+ SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc);
+
+ bool
+ ReadRegisterValueFromRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool
+ WriteRegisterValueToRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ void
+ InvalidateFullUnwindPlan ();
+
+ // Get the contents of a general purpose (address-size) register for this frame
+ // (usually retrieved from the next frame)
+ bool
+ ReadGPRValue (int register_kind, uint32_t regnum, lldb::addr_t &value);
+
+ lldb::UnwindPlanSP
+ GetFastUnwindPlanForFrame ();
+
+ lldb::UnwindPlanSP
+ GetFullUnwindPlanForFrame ();
+
+ void
+ UnwindLogMsg (const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+
+ void
+ UnwindLogMsgVerbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+
+ lldb_private::Thread& m_thread;
+
+ ///
+ // The following tell us how to retrieve the CALLER's register values (ie the "previous" frame, aka the frame above)
+ // i.e. where THIS frame saved them
+ ///
+
+ lldb::UnwindPlanSP m_fast_unwind_plan_sp; // may be NULL
+ lldb::UnwindPlanSP m_full_unwind_plan_sp;
+ bool m_all_registers_available; // Can we retrieve all regs or just nonvolatile regs?
+ int m_frame_type; // enum FrameType
+
+ lldb::addr_t m_cfa;
+ lldb_private::Address m_start_pc;
+ lldb_private::Address m_current_pc;
+
+ int m_current_offset; // how far into the function we've executed; -1 if unknown
+ // 0 if no instructions have been executed yet.
+
+ int m_current_offset_backed_up_one; // how far into the function we've executed; -1 if unknown
+ // 0 if no instructions have been executed yet.
+ // On architectures where the return address on the stack points
+ // to the instruction after the CALL, this value will have 1
+ // subtracted from it. Else a function that ends in a CALL will
+ // have an offset pointing into the next function's address range.
+ // m_current_pc has the actual address of the "current" pc.
+
+ lldb_private::SymbolContext& m_sym_ctx;
+ bool m_sym_ctx_valid; // if ResolveSymbolContextForAddress fails, don't try to use m_sym_ctx
+
+ uint32_t m_frame_number; // What stack frame this RegisterContext is
+
+ std::map<uint32_t, lldb_private::UnwindLLDB::RegisterLocation> m_registers; // where to find reg values for this frame
+
+ lldb_private::UnwindLLDB& m_parent_unwind; // The UnwindLLDB that is creating this RegisterContextLLDB
+
+ //------------------------------------------------------------------
+ // For RegisterContextLLDB only
+ //------------------------------------------------------------------
+
+ DISALLOW_COPY_AND_ASSIGN (RegisterContextLLDB);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextLLDB_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp
new file mode 100644
index 0000000..2c3eee4
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp
@@ -0,0 +1,206 @@
+//===-- RegisterContextMacOSXFrameBackchain.cpp -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMacOSXFrameBackchain.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Target/Thread.h"
+// Project includes
+#include "Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// RegisterContextMacOSXFrameBackchain constructor
+//----------------------------------------------------------------------
+RegisterContextMacOSXFrameBackchain::RegisterContextMacOSXFrameBackchain
+(
+ Thread &thread,
+ uint32_t concrete_frame_idx,
+ const UnwindMacOSXFrameBackchain::Cursor &cursor
+) :
+ RegisterContext (thread, concrete_frame_idx),
+ m_cursor (cursor),
+ m_cursor_is_valid (true)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+RegisterContextMacOSXFrameBackchain::~RegisterContextMacOSXFrameBackchain()
+{
+}
+
+void
+RegisterContextMacOSXFrameBackchain::InvalidateAllRegisters ()
+{
+ m_cursor_is_valid = false;
+}
+
+size_t
+RegisterContextMacOSXFrameBackchain::GetRegisterCount ()
+{
+ return m_thread.GetRegisterContext()->GetRegisterCount();
+}
+
+const RegisterInfo *
+RegisterContextMacOSXFrameBackchain::GetRegisterInfoAtIndex (size_t reg)
+{
+ return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
+}
+
+size_t
+RegisterContextMacOSXFrameBackchain::GetRegisterSetCount ()
+{
+ return m_thread.GetRegisterContext()->GetRegisterSetCount();
+}
+
+
+
+const RegisterSet *
+RegisterContextMacOSXFrameBackchain::GetRegisterSet (size_t reg_set)
+{
+ return m_thread.GetRegisterContext()->GetRegisterSet (reg_set);
+}
+
+
+
+bool
+RegisterContextMacOSXFrameBackchain::ReadRegister (const RegisterInfo *reg_info,
+ RegisterValue &value)
+{
+ if (!m_cursor_is_valid)
+ return false;
+
+ uint64_t reg_value = LLDB_INVALID_ADDRESS;
+
+ switch (reg_info->kinds[eRegisterKindGeneric])
+ {
+ case LLDB_REGNUM_GENERIC_PC:
+ if (m_cursor.pc == LLDB_INVALID_ADDRESS)
+ return false;
+ reg_value = m_cursor.pc;
+ break;
+
+ case LLDB_REGNUM_GENERIC_FP:
+ if (m_cursor.fp == LLDB_INVALID_ADDRESS)
+ return false;
+ reg_value = m_cursor.fp;
+ break;
+
+ default:
+ return false;
+ }
+
+ switch (reg_info->encoding)
+ {
+ case eEncodingInvalid:
+ case eEncodingVector:
+ break;
+
+ case eEncodingUint:
+ case eEncodingSint:
+ value.SetUInt(reg_value, reg_info->byte_size);
+ return true;
+
+ case eEncodingIEEE754:
+ switch (reg_info->byte_size)
+ {
+ case sizeof (float):
+ if (sizeof (float) == sizeof(uint32_t))
+ {
+ value.SetUInt32(reg_value, RegisterValue::eTypeFloat);
+ return true;
+ }
+ else if (sizeof (float) == sizeof(uint64_t))
+ {
+ value.SetUInt64(reg_value, RegisterValue::eTypeFloat);
+ return true;
+ }
+ break;
+
+ case sizeof (double):
+ if (sizeof (double) == sizeof(uint32_t))
+ {
+ value.SetUInt32(reg_value, RegisterValue::eTypeDouble);
+ return true;
+ }
+ else if (sizeof (double) == sizeof(uint64_t))
+ {
+ value.SetUInt64(reg_value, RegisterValue::eTypeDouble);
+ return true;
+ }
+ break;
+
+ // TOOD: need a better way to detect when "long double" types are
+ // the same bytes size as "double"
+#if !defined(__arm__)
+ case sizeof (long double):
+ if (sizeof (long double) == sizeof(uint32_t))
+ {
+ value.SetUInt32(reg_value, RegisterValue::eTypeLongDouble);
+ return true;
+ }
+ else if (sizeof (long double) == sizeof(uint64_t))
+ {
+ value.SetUInt64(reg_value, RegisterValue::eTypeLongDouble);
+ return true;
+ }
+ break;
+#endif
+ }
+ break;
+ }
+ return false;
+}
+
+bool
+RegisterContextMacOSXFrameBackchain::WriteRegister (const RegisterInfo *reg_info,
+ const RegisterValue &value)
+{
+ // Not supported yet. We could easily add support for this by remembering
+ // the address of each entry (it would need to be part of the cursor)
+ return false;
+}
+
+bool
+RegisterContextMacOSXFrameBackchain::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ // libunwind frames can't handle this it doesn't always have all register
+ // values. This call should only be called on frame zero anyway so there
+ // shouldn't be any problem
+ return false;
+}
+
+bool
+RegisterContextMacOSXFrameBackchain::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ // Since this class doesn't respond to "ReadAllRegisterValues()", it must
+ // not have been the one that saved all the register values. So we just let
+ // the thread's register context (the register context for frame zero) do
+ // the writing.
+ return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp);
+}
+
+
+uint32_t
+RegisterContextMacOSXFrameBackchain::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num);
+}
+
diff --git a/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h
new file mode 100644
index 0000000..449e053
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h
@@ -0,0 +1,77 @@
+//===-- RegisterContextMacOSXFrameBackchain.h -------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextMacOSXFrameBackchain_h_
+#define lldb_RegisterContextMacOSXFrameBackchain_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+#include "UnwindMacOSXFrameBackchain.h"
+
+class RegisterContextMacOSXFrameBackchain : public lldb_private::RegisterContext
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ RegisterContextMacOSXFrameBackchain (lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx,
+ const UnwindMacOSXFrameBackchain::Cursor &cursor);
+
+ virtual
+ ~RegisterContextMacOSXFrameBackchain ();
+
+ //------------------------------------------------------------------
+ // Subclasses must override these functions
+ //------------------------------------------------------------------
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb_private::RegisterSet *
+ GetRegisterSet (size_t reg_set);
+
+ virtual bool
+ ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value);
+
+ virtual bool
+ WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+private:
+ UnwindMacOSXFrameBackchain::Cursor m_cursor;
+ bool m_cursor_is_valid;
+ //------------------------------------------------------------------
+ // For RegisterContextMacOSXFrameBackchain only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (RegisterContextMacOSXFrameBackchain);
+};
+
+#endif // lldb_RegisterContextMacOSXFrameBackchain_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp b/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp
new file mode 100644
index 0000000..7ceb536
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp
@@ -0,0 +1,87 @@
+//===-- RegisterContextMach_arm.cpp -----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include "RegisterContextMach_arm.h"
+
+// C Includes
+#include <mach/mach_types.h>
+#include <mach/thread_act.h>
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+RegisterContextMach_arm::RegisterContextMach_arm(Thread &thread, uint32_t concrete_frame_idx) :
+ RegisterContextDarwin_arm (thread, concrete_frame_idx)
+{
+}
+
+RegisterContextMach_arm::~RegisterContextMach_arm()
+{
+}
+
+int
+RegisterContextMach_arm::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr)
+{
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int
+RegisterContextMach_arm::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu)
+{
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int
+RegisterContextMach_arm::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc)
+{
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int
+RegisterContextMach_arm::DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg)
+{
+ mach_msg_type_number_t count = DBGWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&dbg, &count);
+}
+
+int
+RegisterContextMach_arm::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&gpr, GPRWordCount);
+}
+
+int
+RegisterContextMach_arm::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&fpu, FPUWordCount);
+}
+
+int
+RegisterContextMach_arm::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&exc, EXCWordCount);
+}
+
+int
+RegisterContextMach_arm::DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&dbg, DBGWordCount);
+}
+
+#endif
diff --git a/source/Plugins/Process/Utility/RegisterContextMach_arm.h b/source/Plugins/Process/Utility/RegisterContextMach_arm.h
new file mode 100644
index 0000000..e97a4bf
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMach_arm.h
@@ -0,0 +1,56 @@
+//===-- RegisterContextMach_arm.h -------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_arm_h_
+#define liblldb_RegisterContextMach_arm_h_
+
+// C Includes
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "RegisterContextDarwin_arm.h"
+
+class RegisterContextMach_arm : public RegisterContextDarwin_arm
+{
+public:
+
+ RegisterContextMach_arm(lldb_private::Thread &thread, uint32_t concrete_frame_idx);
+
+ virtual
+ ~RegisterContextMach_arm();
+
+protected:
+
+ virtual int
+ DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int
+ DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int
+ DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc);
+
+ int
+ DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg);
+
+ int
+ DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int
+ DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int
+ DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc);
+
+ int
+ DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg);
+};
+
+#endif // liblldb_RegisterContextMach_arm_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp b/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp
new file mode 100644
index 0000000..3d6c9a6
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp
@@ -0,0 +1,72 @@
+//===-- RegisterContextMach_i386.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+// C Includes
+#include <mach/thread_act.h>
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "RegisterContextMach_i386.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+RegisterContextMach_i386::RegisterContextMach_i386(Thread &thread, uint32_t concrete_frame_idx) :
+ RegisterContextDarwin_i386 (thread, concrete_frame_idx)
+{
+}
+
+RegisterContextMach_i386::~RegisterContextMach_i386()
+{
+}
+
+int
+RegisterContextMach_i386::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr)
+{
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int
+RegisterContextMach_i386::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu)
+{
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int
+RegisterContextMach_i386::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc)
+{
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int
+RegisterContextMach_i386::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&gpr, GPRWordCount);
+}
+
+int
+RegisterContextMach_i386::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&fpu, FPUWordCount);
+}
+
+int
+RegisterContextMach_i386::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&exc, EXCWordCount);
+}
+
+#endif
diff --git a/source/Plugins/Process/Utility/RegisterContextMach_i386.h b/source/Plugins/Process/Utility/RegisterContextMach_i386.h
new file mode 100644
index 0000000..ad0f69d
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMach_i386.h
@@ -0,0 +1,49 @@
+//===-- RegisterContextMach_i386.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_i386_h_
+#define liblldb_RegisterContextMach_i386_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "RegisterContextDarwin_i386.h"
+
+class RegisterContextMach_i386 : public RegisterContextDarwin_i386
+{
+public:
+
+ RegisterContextMach_i386(lldb_private::Thread &thread, uint32_t concrete_frame_idx);
+
+ virtual
+ ~RegisterContextMach_i386();
+
+protected:
+
+ virtual int
+ DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int
+ DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int
+ DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc);
+
+ int
+ DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int
+ DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int
+ DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc);
+};
+
+#endif // liblldb_RegisterContextMach_i386_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp b/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp
new file mode 100644
index 0000000..f03685e
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp
@@ -0,0 +1,72 @@
+//===-- RegisterContextMach_x86_64.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+// C Includes
+#include <mach/thread_act.h>
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "RegisterContextMach_x86_64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+RegisterContextMach_x86_64::RegisterContextMach_x86_64(Thread &thread, uint32_t concrete_frame_idx) :
+ RegisterContextDarwin_x86_64 (thread, concrete_frame_idx)
+{
+}
+
+RegisterContextMach_x86_64::~RegisterContextMach_x86_64()
+{
+}
+
+int
+RegisterContextMach_x86_64::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr)
+{
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int
+RegisterContextMach_x86_64::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu)
+{
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int
+RegisterContextMach_x86_64::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc)
+{
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int
+RegisterContextMach_x86_64::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&gpr, GPRWordCount);
+}
+
+int
+RegisterContextMach_x86_64::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&fpu, FPUWordCount);
+}
+
+int
+RegisterContextMach_x86_64::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc)
+{
+ return ::thread_set_state(tid, flavor, (thread_state_t)&exc, EXCWordCount);
+}
+
+#endif
diff --git a/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h b/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h
new file mode 100644
index 0000000..9e6dfa3
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h
@@ -0,0 +1,49 @@
+//===-- RegisterContextMach_x86_64.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_x86_64_h_
+#define liblldb_RegisterContextMach_x86_64_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "RegisterContextDarwin_x86_64.h"
+
+class RegisterContextMach_x86_64 : public RegisterContextDarwin_x86_64
+{
+public:
+
+ RegisterContextMach_x86_64(lldb_private::Thread &thread, uint32_t concrete_frame_idx);
+
+ virtual
+ ~RegisterContextMach_x86_64();
+
+protected:
+
+ virtual int
+ DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int
+ DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int
+ DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc);
+
+ int
+ DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int
+ DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int
+ DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc);
+};
+
+#endif // liblldb_RegisterContextMach_x86_64_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextMemory.cpp b/source/Plugins/Process/Utility/RegisterContextMemory.cpp
new file mode 100644
index 0000000..8c33a68
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMemory.cpp
@@ -0,0 +1,174 @@
+//===-- RegisterContextMemory.cpp -------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMemory.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "DynamicRegisterInfo.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// RegisterContextMemory constructor
+//----------------------------------------------------------------------
+RegisterContextMemory::RegisterContextMemory
+(
+ Thread &thread,
+ uint32_t concrete_frame_idx,
+ DynamicRegisterInfo &reg_infos,
+ addr_t reg_data_addr
+) :
+ RegisterContext (thread, concrete_frame_idx),
+ m_reg_infos (reg_infos),
+ m_reg_valid (),
+ m_reg_data (),
+ m_reg_data_addr (reg_data_addr)
+{
+ // Resize our vector of bools to contain one bool for every register.
+ // We will use these boolean values to know when a register value
+ // is valid in m_reg_data.
+ const size_t num_regs = reg_infos.GetNumRegisters();
+ assert (num_regs > 0);
+ m_reg_valid.resize (num_regs);
+
+ // Make a heap based buffer that is big enough to store all registers
+ DataBufferSP reg_data_sp(new DataBufferHeap (reg_infos.GetRegisterDataByteSize(), 0));
+ m_reg_data.SetData (reg_data_sp);
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+RegisterContextMemory::~RegisterContextMemory()
+{
+}
+
+void
+RegisterContextMemory::InvalidateAllRegisters ()
+{
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
+ SetAllRegisterValid (false);
+}
+
+void
+RegisterContextMemory::SetAllRegisterValid (bool b)
+{
+ std::vector<bool>::iterator pos, end = m_reg_valid.end();
+ for (pos = m_reg_valid.begin(); pos != end; ++pos)
+ *pos = b;
+}
+
+size_t
+RegisterContextMemory::GetRegisterCount ()
+{
+ return m_reg_infos.GetNumRegisters ();
+}
+
+const RegisterInfo *
+RegisterContextMemory::GetRegisterInfoAtIndex (size_t reg)
+{
+ return m_reg_infos.GetRegisterInfoAtIndex (reg);
+}
+
+size_t
+RegisterContextMemory::GetRegisterSetCount ()
+{
+ return m_reg_infos.GetNumRegisterSets ();
+}
+
+const RegisterSet *
+RegisterContextMemory::GetRegisterSet (size_t reg_set)
+{
+ return m_reg_infos.GetRegisterSet (reg_set);
+}
+
+uint32_t
+RegisterContextMemory::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ return m_reg_infos.ConvertRegisterKindToRegisterNumber (kind, num);
+}
+
+bool
+RegisterContextMemory::ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value)
+{
+ const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB];
+ if (!m_reg_valid[reg_num])
+ {
+ if (!ReadAllRegisterValues(m_reg_data.GetSharedDataBuffer ()))
+ return false;
+ }
+ const bool partial_data_ok = false;
+ return reg_value.SetValueFromData(reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok).Success();
+}
+
+bool
+RegisterContextMemory::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value)
+{
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
+ {
+ const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB];
+ addr_t reg_addr = m_reg_data_addr + reg_info->byte_offset;
+ Error error (WriteRegisterValueToMemory(reg_info, reg_addr, reg_info->byte_size, reg_value));
+ m_reg_valid[reg_num] = false;
+ return error.Success();
+ }
+ return false;
+}
+
+bool
+RegisterContextMemory::ReadAllRegisterValues (DataBufferSP &data_sp)
+{
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
+ {
+ ProcessSP process_sp (CalculateProcess());
+ if (process_sp)
+ {
+ Error error;
+ if (process_sp->ReadMemory(m_reg_data_addr, data_sp->GetBytes(), data_sp->GetByteSize(), error) == data_sp->GetByteSize())
+ {
+ SetAllRegisterValid (true);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+RegisterContextMemory::WriteAllRegisterValues (const DataBufferSP &data_sp)
+{
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
+ {
+ ProcessSP process_sp (CalculateProcess());
+ if (process_sp)
+ {
+ Error error;
+ SetAllRegisterValid (false);
+ if (process_sp->WriteMemory(m_reg_data_addr, data_sp->GetBytes(), data_sp->GetByteSize(), error) == data_sp->GetByteSize())
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+RegisterContextMemory::SetAllRegisterData (const lldb::DataBufferSP &data_sp)
+{
+ m_reg_data.SetData(data_sp);
+ SetAllRegisterValid (true);
+}
diff --git a/source/Plugins/Process/Utility/RegisterContextMemory.h b/source/Plugins/Process/Utility/RegisterContextMemory.h
new file mode 100644
index 0000000..8bba52c
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextMemory.h
@@ -0,0 +1,102 @@
+//===-- RegisterContextMemory.h ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextMemory_h_
+#define lldb_RegisterContextMemory_h_
+
+// C Includes
+// C++ Includes
+#include <vector>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Target/RegisterContext.h"
+
+class DynamicRegisterInfo;
+
+class RegisterContextMemory : public lldb_private::RegisterContext
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ RegisterContextMemory (lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx,
+ DynamicRegisterInfo &reg_info,
+ lldb::addr_t reg_data_addr);
+
+ virtual
+ ~RegisterContextMemory ();
+
+ //------------------------------------------------------------------
+ // Subclasses must override these functions
+ //------------------------------------------------------------------
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb_private::RegisterSet *
+ GetRegisterSet (size_t reg_set);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+
+ //------------------------------------------------------------------
+ // If all of the thread register are in a contiguous buffer in
+ // memory, then the default ReadRegister/WriteRegister and
+ // ReadAllRegisterValues/WriteAllRegisterValues will work. If thread
+ // registers are not contiguous, clients will want to subclass this
+ // class and modify the read/write functions as needed.
+ //------------------------------------------------------------------
+
+ virtual bool
+ ReadRegister (const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ virtual bool
+ WriteRegister (const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ void
+ SetAllRegisterData (const lldb::DataBufferSP &data_sp);
+protected:
+
+ void
+ SetAllRegisterValid (bool b);
+
+ DynamicRegisterInfo &m_reg_infos;
+ std::vector<bool> m_reg_valid;
+ lldb_private::DataExtractor m_reg_data;
+ lldb::addr_t m_reg_data_addr; // If this is valid, then we have a register context that is stored in memmory
+
+private:
+ //------------------------------------------------------------------
+ // For RegisterContextMemory only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (RegisterContextMemory);
+};
+
+#endif // lldb_RegisterContextMemory_h_
diff --git a/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp b/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp
new file mode 100644
index 0000000..d35a5d0
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp
@@ -0,0 +1,261 @@
+//===-- RegisterContextThreadMemory.cpp -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-private.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+#include "RegisterContextThreadMemory.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextThreadMemory::RegisterContextThreadMemory (Thread &thread,
+ lldb::addr_t register_data_addr) :
+ RegisterContext (thread, 0),
+ m_thread_wp (thread.shared_from_this()),
+ m_reg_ctx_sp (),
+ m_register_data_addr (register_data_addr),
+ m_stop_id(0)
+{
+}
+
+RegisterContextThreadMemory::~RegisterContextThreadMemory()
+{
+}
+
+void
+RegisterContextThreadMemory::UpdateRegisterContext ()
+{
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ ProcessSP process_sp (thread_sp->GetProcess());
+
+ if (process_sp)
+ {
+ const uint32_t stop_id = process_sp->GetModID().GetStopID();
+ if (m_stop_id != stop_id)
+ {
+ m_stop_id = stop_id;
+ m_reg_ctx_sp.reset();
+ }
+ if (!m_reg_ctx_sp)
+ {
+ ThreadSP backing_thread_sp (thread_sp->GetBackingThread());
+ if (backing_thread_sp)
+ {
+ m_reg_ctx_sp = backing_thread_sp->GetRegisterContext();
+ }
+ else
+ {
+ OperatingSystem *os = process_sp->GetOperatingSystem ();
+ if (os->IsOperatingSystemPluginThread (thread_sp))
+ m_reg_ctx_sp = os->CreateRegisterContextForThread (thread_sp.get(), LLDB_INVALID_ADDRESS);
+ }
+ }
+ }
+ else
+ {
+ m_reg_ctx_sp.reset();
+ }
+ }
+ else
+ {
+ m_reg_ctx_sp.reset();
+ }
+}
+
+//------------------------------------------------------------------
+// Subclasses must override these functions
+//------------------------------------------------------------------
+void
+RegisterContextThreadMemory::InvalidateAllRegisters ()
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ m_reg_ctx_sp->InvalidateAllRegisters();
+}
+
+size_t
+RegisterContextThreadMemory::GetRegisterCount ()
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterCount();
+ return 0;
+}
+
+const RegisterInfo *
+RegisterContextThreadMemory::GetRegisterInfoAtIndex (size_t reg)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterInfoAtIndex(reg);
+ return NULL;
+}
+
+size_t
+RegisterContextThreadMemory::GetRegisterSetCount ()
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterSetCount();
+ return 0;
+}
+
+const RegisterSet *
+RegisterContextThreadMemory::GetRegisterSet (size_t reg_set)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterSet(reg_set);
+ return NULL;
+}
+
+bool
+RegisterContextThreadMemory::ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadRegister(reg_info, reg_value);
+ return false;
+}
+
+bool
+RegisterContextThreadMemory::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteRegister (reg_info, reg_value);
+ return false;
+}
+
+bool
+RegisterContextThreadMemory::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadAllRegisterValues(data_sp);
+ return false;
+}
+
+bool
+RegisterContextThreadMemory::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteAllRegisterValues (data_sp);
+ return false;
+}
+
+bool
+RegisterContextThreadMemory::CopyFromRegisterContext (lldb::RegisterContextSP reg_ctx_sp)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->CopyFromRegisterContext(reg_ctx_sp);
+ return false;
+}
+
+uint32_t
+RegisterContextThreadMemory::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ConvertRegisterKindToRegisterNumber(kind, num);
+ return false;
+}
+
+uint32_t
+RegisterContextThreadMemory::NumSupportedHardwareBreakpoints ()
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->NumSupportedHardwareBreakpoints();
+ return false;
+}
+
+uint32_t
+RegisterContextThreadMemory::SetHardwareBreakpoint (lldb::addr_t addr, size_t size)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->SetHardwareBreakpoint(addr, size);
+ return 0;
+}
+
+bool
+RegisterContextThreadMemory::ClearHardwareBreakpoint (uint32_t hw_idx)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ClearHardwareBreakpoint (hw_idx);
+ return false;
+}
+
+uint32_t
+RegisterContextThreadMemory::NumSupportedHardwareWatchpoints ()
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t
+RegisterContextThreadMemory::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->SetHardwareWatchpoint(addr, size, read, write);
+ return 0;
+}
+
+bool
+RegisterContextThreadMemory::ClearHardwareWatchpoint (uint32_t hw_index)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ClearHardwareWatchpoint(hw_index);
+ return false;
+}
+
+bool
+RegisterContextThreadMemory::HardwareSingleStep (bool enable)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->HardwareSingleStep(enable);
+ return false;
+}
+
+Error
+RegisterContextThreadMemory::ReadRegisterValueFromMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr, uint32_t src_len, RegisterValue &reg_value)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadRegisterValueFromMemory (reg_info, src_addr, src_len, reg_value);
+ Error error;
+ error.SetErrorString("invalid register context");
+ return error;
+}
+
+Error
+RegisterContextThreadMemory::WriteRegisterValueToMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len, const RegisterValue &reg_value)
+{
+ UpdateRegisterContext ();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteRegisterValueToMemory (reg_info, dst_addr, dst_len, reg_value);
+ Error error;
+ error.SetErrorString("invalid register context");
+ return error;
+}
diff --git a/source/Plugins/Process/Utility/RegisterContextThreadMemory.h b/source/Plugins/Process/Utility/RegisterContextThreadMemory.h
new file mode 100644
index 0000000..8d7a4b6
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextThreadMemory.h
@@ -0,0 +1,114 @@
+//===-- RegisterContextThreadMemory.h ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextThreadMemory_h_
+#define lldb_RegisterContextThreadMemory_h_
+
+#include <vector>
+
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Symbol/SymbolContext.h"
+
+namespace lldb_private {
+
+class RegisterContextThreadMemory : public lldb_private::RegisterContext
+{
+public:
+ RegisterContextThreadMemory (Thread &thread,
+ lldb::addr_t register_data_addr);
+
+ virtual ~RegisterContextThreadMemory();
+ //------------------------------------------------------------------
+ // Subclasses must override these functions
+ //------------------------------------------------------------------
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const RegisterSet *
+ GetRegisterSet (size_t reg_set);
+
+ virtual bool
+ ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value);
+
+ virtual bool
+ WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value);
+
+ // These two functions are used to implement "push" and "pop" of register states. They are used primarily
+ // for expression evaluation, where we need to push a new state (storing the old one in data_sp) and then
+ // restoring the original state by passing the data_sp we got from ReadAllRegisters to WriteAllRegisterValues.
+ // ReadAllRegisters will do what is necessary to return a coherent set of register values for this thread, which
+ // may mean e.g. interrupting a thread that is sitting in a kernel trap. That is a somewhat disruptive operation,
+ // so these API's should only be used when this behavior is needed.
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ bool
+ CopyFromRegisterContext (lldb::RegisterContextSP context);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+ //------------------------------------------------------------------
+ // Subclasses can override these functions if desired
+ //------------------------------------------------------------------
+ virtual uint32_t
+ NumSupportedHardwareBreakpoints ();
+
+ virtual uint32_t
+ SetHardwareBreakpoint (lldb::addr_t addr, size_t size);
+
+ virtual bool
+ ClearHardwareBreakpoint (uint32_t hw_idx);
+
+ virtual uint32_t
+ NumSupportedHardwareWatchpoints ();
+
+ virtual uint32_t
+ SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write);
+
+ virtual bool
+ ClearHardwareWatchpoint (uint32_t hw_index);
+
+ virtual bool
+ HardwareSingleStep (bool enable);
+
+ virtual Error
+ ReadRegisterValueFromMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr, uint32_t src_len, RegisterValue &reg_value);
+
+ virtual Error
+ WriteRegisterValueToMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len, const RegisterValue &reg_value);
+
+protected:
+ void
+ UpdateRegisterContext ();
+
+ lldb::ThreadWP m_thread_wp;
+ lldb::RegisterContextSP m_reg_ctx_sp;
+ lldb::addr_t m_register_data_addr;
+ uint32_t m_stop_id;
+private:
+ DISALLOW_COPY_AND_ASSIGN (RegisterContextThreadMemory);
+};
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextThreadMemory_h_
diff --git a/source/Plugins/Process/Utility/StopInfoMachException.cpp b/source/Plugins/Process/Utility/StopInfoMachException.cpp
new file mode 100644
index 0000000..51d2052
--- /dev/null
+++ b/source/Plugins/Process/Utility/StopInfoMachException.cpp
@@ -0,0 +1,482 @@
+//===-- StopInfoMachException.cpp -------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StopInfoMachException.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/UnixSignals.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *
+StopInfoMachException::GetDescription ()
+{
+ if (m_description.empty() && m_value != 0)
+ {
+ ExecutionContext exe_ctx (m_thread_wp.lock());
+ Target *target = exe_ctx.GetTargetPtr();
+ const llvm::Triple::ArchType cpu = target ? target->GetArchitecture().GetMachine() : llvm::Triple::UnknownArch;
+
+ const char *exc_desc = NULL;
+ const char *code_label = "code";
+ const char *code_desc = NULL;
+ const char *subcode_label = "subcode";
+ const char *subcode_desc = NULL;
+ switch (m_value)
+ {
+ case 1: // EXC_BAD_ACCESS
+ exc_desc = "EXC_BAD_ACCESS";
+ subcode_label = "address";
+ switch (cpu)
+ {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code)
+ {
+ case 0xd: code_desc = "EXC_I386_GPFLT"; m_exc_data_count = 1; break;
+ }
+ break;
+ case llvm::Triple::arm:
+ switch (m_exc_code)
+ {
+ case 0x101: code_desc = "EXC_ARM_DA_ALIGN"; break;
+ case 0x102: code_desc = "EXC_ARM_DA_DEBUG"; break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code)
+ {
+ case 0x101: code_desc = "EXC_PPC_VM_PROT_READ"; break;
+ case 0x102: code_desc = "EXC_PPC_BADSPACE"; break;
+ case 0x103: code_desc = "EXC_PPC_UNALIGNED"; break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 2: // EXC_BAD_INSTRUCTION
+ exc_desc = "EXC_BAD_INSTRUCTION";
+ switch (cpu)
+ {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ if (m_exc_code == 1)
+ code_desc = "EXC_I386_INVOP";
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code)
+ {
+ case 1: code_desc = "EXC_PPC_INVALID_SYSCALL"; break;
+ case 2: code_desc = "EXC_PPC_UNIPL_INST"; break;
+ case 3: code_desc = "EXC_PPC_PRIVINST"; break;
+ case 4: code_desc = "EXC_PPC_PRIVREG"; break;
+ case 5: code_desc = "EXC_PPC_TRACE"; break;
+ case 6: code_desc = "EXC_PPC_PERFMON"; break;
+ }
+ break;
+
+ case llvm::Triple::arm:
+ if (m_exc_code == 1)
+ code_desc = "EXC_ARM_UNDEFINED";
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3: // EXC_ARITHMETIC
+ exc_desc = "EXC_ARITHMETIC";
+ switch (cpu)
+ {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code)
+ {
+ case 1: code_desc = "EXC_I386_DIV"; break;
+ case 2: code_desc = "EXC_I386_INTO"; break;
+ case 3: code_desc = "EXC_I386_NOEXT"; break;
+ case 4: code_desc = "EXC_I386_EXTOVR"; break;
+ case 5: code_desc = "EXC_I386_EXTERR"; break;
+ case 6: code_desc = "EXC_I386_EMERR"; break;
+ case 7: code_desc = "EXC_I386_BOUND"; break;
+ case 8: code_desc = "EXC_I386_SSEEXTERR"; break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code)
+ {
+ case 1: code_desc = "EXC_PPC_OVERFLOW"; break;
+ case 2: code_desc = "EXC_PPC_ZERO_DIVIDE"; break;
+ case 3: code_desc = "EXC_PPC_FLT_INEXACT"; break;
+ case 4: code_desc = "EXC_PPC_FLT_ZERO_DIVIDE"; break;
+ case 5: code_desc = "EXC_PPC_FLT_UNDERFLOW"; break;
+ case 6: code_desc = "EXC_PPC_FLT_OVERFLOW"; break;
+ case 7: code_desc = "EXC_PPC_FLT_NOT_A_NUMBER"; break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 4: // EXC_EMULATION
+ exc_desc = "EXC_EMULATION";
+ break;
+
+
+ case 5: // EXC_SOFTWARE
+ exc_desc = "EXC_SOFTWARE";
+ if (m_exc_code == 0x10003)
+ {
+ subcode_desc = "EXC_SOFT_SIGNAL";
+ subcode_label = "signo";
+ }
+ break;
+
+ case 6: // EXC_BREAKPOINT
+ {
+ exc_desc = "EXC_BREAKPOINT";
+ switch (cpu)
+ {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code)
+ {
+ case 1: code_desc = "EXC_I386_SGL"; break;
+ case 2: code_desc = "EXC_I386_BPT"; break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code)
+ {
+ case 1: code_desc = "EXC_PPC_BREAKPOINT"; break;
+ }
+ break;
+
+ case llvm::Triple::arm:
+ switch (m_exc_code)
+ {
+ case 0x101: code_desc = "EXC_ARM_DA_ALIGN"; break;
+ case 0x102: code_desc = "EXC_ARM_DA_DEBUG"; break;
+ case 1: code_desc = "EXC_ARM_BREAKPOINT"; break;
+ // FIXME temporary workaround, exc_code 0 does not really mean EXC_ARM_BREAKPOINT
+ case 0: code_desc = "EXC_ARM_BREAKPOINT"; break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ case 7:
+ exc_desc = "EXC_SYSCALL";
+ break;
+
+ case 8:
+ exc_desc = "EXC_MACH_SYSCALL";
+ break;
+
+ case 9:
+ exc_desc = "EXC_RPC_ALERT";
+ break;
+
+ case 10:
+ exc_desc = "EXC_CRASH";
+ break;
+ case 11:
+ exc_desc = "EXC_RESOURCE";
+ break;
+ case 12:
+ exc_desc = "EXC_GUARD";
+ break;
+ }
+
+ StreamString strm;
+
+ if (exc_desc)
+ strm.PutCString(exc_desc);
+ else
+ strm.Printf("EXC_??? (%" PRIu64 ")", m_value);
+
+ if (m_exc_data_count >= 1)
+ {
+ if (code_desc)
+ strm.Printf(" (%s=%s", code_label, code_desc);
+ else
+ strm.Printf(" (%s=%" PRIu64, code_label, m_exc_code);
+ }
+
+ if (m_exc_data_count >= 2)
+ {
+ if (subcode_desc)
+ strm.Printf(", %s=%s", subcode_label, subcode_desc);
+ else
+ strm.Printf(", %s=0x%" PRIx64, subcode_label, m_exc_subcode);
+ }
+
+ if (m_exc_data_count > 0)
+ strm.PutChar(')');
+
+ m_description.swap (strm.GetString());
+ }
+ return m_description.c_str();
+}
+
+
+
+
+
+StopInfoSP
+StopInfoMachException::CreateStopReasonWithMachException
+(
+ Thread &thread,
+ uint32_t exc_type,
+ uint32_t exc_data_count,
+ uint64_t exc_code,
+ uint64_t exc_sub_code,
+ uint64_t exc_sub_sub_code,
+ bool pc_already_adjusted,
+ bool adjust_pc_if_needed
+)
+{
+ if (exc_type != 0)
+ {
+ uint32_t pc_decrement = 0;
+ ExecutionContext exe_ctx (thread.shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ const llvm::Triple::ArchType cpu = target ? target->GetArchitecture().GetMachine() : llvm::Triple::UnknownArch;
+
+ switch (exc_type)
+ {
+ case 1: // EXC_BAD_ACCESS
+ break;
+
+ case 2: // EXC_BAD_INSTRUCTION
+ switch (cpu)
+ {
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (exc_code)
+ {
+ case 1: // EXC_PPC_INVALID_SYSCALL
+ case 2: // EXC_PPC_UNIPL_INST
+ case 3: // EXC_PPC_PRIVINST
+ case 4: // EXC_PPC_PRIVREG
+ break;
+ case 5: // EXC_PPC_TRACE
+ return StopInfo::CreateStopReasonToTrace (thread);
+ case 6: // EXC_PPC_PERFMON
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3: // EXC_ARITHMETIC
+ case 4: // EXC_EMULATION
+ break;
+
+ case 5: // EXC_SOFTWARE
+ if (exc_code == 0x10003) // EXC_SOFT_SIGNAL
+ {
+ if (exc_sub_code == 5)
+ {
+ // On MacOSX, a SIGTRAP can signify that a process has called
+ // exec, so we should check with our dynamic loader to verify.
+ ProcessSP process_sp (thread.GetProcess());
+ if (process_sp)
+ {
+ DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader();
+ if (dynamic_loader && dynamic_loader->ProcessDidExec())
+ {
+ // The program was re-exec'ed
+ return StopInfo::CreateStopReasonWithExec (thread);
+ }
+// if (!process_did_exec)
+// {
+// // We have a SIGTRAP, make sure we didn't exec by checking
+// // for the PC being at "_dyld_start"...
+// lldb::StackFrameSP frame_sp (thread.GetStackFrameAtIndex(0));
+// if (frame_sp)
+// {
+// const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol;
+// if (symbol)
+// {
+// if (symbol->GetName() == ConstString("_dyld_start"))
+// process_did_exec = true;
+// }
+// }
+// }
+ }
+ }
+ return StopInfo::CreateStopReasonWithSignal (thread, exc_sub_code);
+ }
+ break;
+
+ case 6: // EXC_BREAKPOINT
+ {
+ bool is_actual_breakpoint = false;
+ bool is_trace_if_actual_breakpoint_missing = false;
+ switch (cpu)
+ {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ if (exc_code == 1) // EXC_I386_SGL
+ {
+ if (!exc_sub_code)
+ return StopInfo::CreateStopReasonToTrace(thread);
+
+ // It's a watchpoint, then.
+ // The exc_sub_code indicates the data break address.
+ lldb::WatchpointSP wp_sp;
+ if (target)
+ wp_sp = target->GetWatchpointList().FindByAddress((lldb::addr_t)exc_sub_code);
+ if (wp_sp && wp_sp->IsEnabled())
+ {
+ // Debugserver may piggyback the hardware index of the fired watchpoint in the exception data.
+ // Set the hardware index if that's the case.
+ if (exc_data_count >=3)
+ wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
+ return StopInfo::CreateStopReasonWithWatchpointID(thread, wp_sp->GetID());
+ }
+ }
+ else if (exc_code == 2 || // EXC_I386_BPT
+ exc_code == 3) // EXC_I386_BPTFLT
+ {
+ // KDP returns EXC_I386_BPTFLT for trace breakpoints
+ if (exc_code == 3)
+ is_trace_if_actual_breakpoint_missing = true;
+
+ is_actual_breakpoint = true;
+ if (!pc_already_adjusted)
+ pc_decrement = 1;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ is_actual_breakpoint = exc_code == 1; // EXC_PPC_BREAKPOINT
+ break;
+
+ case llvm::Triple::arm:
+ if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
+ {
+ // It's a watchpoint, then, if the exc_sub_code indicates a known/enabled
+ // data break address from our watchpoint list.
+ lldb::WatchpointSP wp_sp;
+ if (target)
+ wp_sp = target->GetWatchpointList().FindByAddress((lldb::addr_t)exc_sub_code);
+ if (wp_sp && wp_sp->IsEnabled())
+ {
+ // Debugserver may piggyback the hardware index of the fired watchpoint in the exception data.
+ // Set the hardware index if that's the case.
+ if (exc_data_count >=3)
+ wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
+ return StopInfo::CreateStopReasonWithWatchpointID(thread, wp_sp->GetID());
+ }
+ // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as EXC_BAD_ACCESS
+ if (thread.GetTemporaryResumeState() == eStateStepping)
+ return StopInfo::CreateStopReasonToTrace(thread);
+ }
+ else if (exc_code == 1) // EXC_ARM_BREAKPOINT
+ {
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ }
+ else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel is currently returning this so accept it as indicating a breakpoint until the kernel is fixed
+ {
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (is_actual_breakpoint)
+ {
+ RegisterContextSP reg_ctx_sp (thread.GetRegisterContext());
+ addr_t pc = reg_ctx_sp->GetPC() - pc_decrement;
+
+ ProcessSP process_sp (thread.CalculateProcess());
+
+ lldb::BreakpointSiteSP bp_site_sp;
+ if (process_sp)
+ bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc);
+ if (bp_site_sp && bp_site_sp->IsEnabled())
+ {
+ // Update the PC if we were asked to do so, but only do
+ // so if we find a breakpoint that we know about cause
+ // this could be a trap instruction in the code
+ if (pc_decrement > 0 && adjust_pc_if_needed)
+ reg_ctx_sp->SetPC (pc);
+
+ // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
+ // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
+ // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
+ if (bp_site_sp->ValidForThisThread (&thread))
+ return StopInfo::CreateStopReasonWithBreakpointSiteID (thread, bp_site_sp->GetID());
+ else
+ return StopInfoSP();
+ }
+
+ // Don't call this a trace if we weren't single stepping this thread.
+ if (is_trace_if_actual_breakpoint_missing && thread.GetTemporaryResumeState() == eStateStepping)
+ {
+ return StopInfo::CreateStopReasonToTrace (thread);
+ }
+ }
+ }
+ break;
+
+ case 7: // EXC_SYSCALL
+ case 8: // EXC_MACH_SYSCALL
+ case 9: // EXC_RPC_ALERT
+ case 10: // EXC_CRASH
+ break;
+ }
+
+ return StopInfoSP(new StopInfoMachException (thread, exc_type, exc_data_count, exc_code, exc_sub_code));
+ }
+ return StopInfoSP();
+}
diff --git a/source/Plugins/Process/Utility/StopInfoMachException.h b/source/Plugins/Process/Utility/StopInfoMachException.h
new file mode 100644
index 0000000..130ee0b
--- /dev/null
+++ b/source/Plugins/Process/Utility/StopInfoMachException.h
@@ -0,0 +1,77 @@
+//===-- StopInfoMachException.h ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_StopInfoMachException_h_
+#define liblldb_StopInfoMachException_h_
+
+// C Includes
+// C++ Includes
+#include <string>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Target/StopInfo.h"
+
+namespace lldb_private {
+
+class StopInfoMachException : public StopInfo
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ StopInfoMachException (Thread &thread,
+ uint32_t exc_type,
+ uint32_t exc_data_count,
+ uint64_t exc_code,
+ uint64_t exc_subcode) :
+ StopInfo (thread, exc_type),
+ m_exc_data_count (exc_data_count),
+ m_exc_code (exc_code),
+ m_exc_subcode (exc_subcode)
+ {
+ }
+
+ virtual ~StopInfoMachException()
+ {
+ }
+
+
+ virtual lldb::StopReason
+ GetStopReason () const
+ {
+ return lldb::eStopReasonException;
+ }
+
+ virtual const char *
+ GetDescription ();
+
+ // Since some mach exceptions will be reported as breakpoints, signals,
+ // or trace, we use this static accessor which will translate the mach
+ // exception into the correct StopInfo.
+ static lldb::StopInfoSP
+ CreateStopReasonWithMachException (Thread &thread,
+ uint32_t exc_type,
+ uint32_t exc_data_count,
+ uint64_t exc_code,
+ uint64_t exc_sub_code,
+ uint64_t exc_sub_sub_code,
+ bool pc_already_adjusted = true,
+ bool adjust_pc_if_needed = false);
+
+protected:
+ uint32_t m_exc_data_count;
+ uint64_t m_exc_code;
+ uint64_t m_exc_subcode;
+};
+
+
+} // namespace lldb_private
+
+#endif // liblldb_StopInfoMachException_h_
diff --git a/source/Plugins/Process/Utility/ThreadMemory.cpp b/source/Plugins/Process/Utility/ThreadMemory.cpp
new file mode 100644
index 0000000..56e5a9a
--- /dev/null
+++ b/source/Plugins/Process/Utility/ThreadMemory.cpp
@@ -0,0 +1,140 @@
+//===-- ThreadMemory.cpp ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Process/Utility/ThreadMemory.h"
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Unwind.h"
+#include "Plugins/Process/Utility/RegisterContextThreadMemory.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadMemory::ThreadMemory (Process &process,
+ tid_t tid,
+ const ValueObjectSP &thread_info_valobj_sp) :
+ Thread (process, tid),
+ m_backing_thread_sp (),
+ m_thread_info_valobj_sp (thread_info_valobj_sp),
+ m_name(),
+ m_queue()
+{
+}
+
+
+ThreadMemory::ThreadMemory (Process &process,
+ lldb::tid_t tid,
+ const char *name,
+ const char *queue,
+ lldb::addr_t register_data_addr) :
+ Thread (process, tid),
+ m_backing_thread_sp (),
+ m_thread_info_valobj_sp (),
+ m_name(),
+ m_queue(),
+ m_register_data_addr (register_data_addr)
+{
+ if (name)
+ m_name = name;
+ if (queue)
+ m_queue = queue;
+}
+
+
+ThreadMemory::~ThreadMemory()
+{
+ DestroyThread();
+}
+
+void
+ThreadMemory::WillResume (StateType resume_state)
+{
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->WillResume(resume_state);
+}
+
+void
+ThreadMemory::ClearStackFrames ()
+{
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->ClearStackFrames();
+ Thread::ClearStackFrames();
+}
+
+RegisterContextSP
+ThreadMemory::GetRegisterContext ()
+{
+ if (!m_reg_context_sp)
+ m_reg_context_sp.reset (new RegisterContextThreadMemory (*this, m_register_data_addr));
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadMemory::CreateRegisterContextForFrame (StackFrame *frame)
+{
+ RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex ();
+
+ if (concrete_frame_idx == 0)
+ {
+ reg_ctx_sp = GetRegisterContext ();
+ }
+ else
+ {
+ Unwind *unwinder = GetUnwinder ();
+ if (unwinder)
+ reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool
+ThreadMemory::CalculateStopInfo ()
+{
+ if (m_backing_thread_sp)
+ {
+ lldb::StopInfoSP backing_stop_info_sp (m_backing_thread_sp->GetPrivateStopInfo());
+ if (backing_stop_info_sp)
+ {
+ backing_stop_info_sp->SetThread (shared_from_this());
+ SetStopInfo (backing_stop_info_sp);
+ return true;
+ }
+ }
+ else
+ {
+ ProcessSP process_sp (GetProcess());
+
+ if (process_sp)
+ {
+ OperatingSystem *os = process_sp->GetOperatingSystem ();
+ if (os)
+ {
+ SetStopInfo (os->CreateThreadStopReason (this));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void
+ThreadMemory::RefreshStateAfterStop()
+{
+ if (m_backing_thread_sp)
+ return m_backing_thread_sp->RefreshStateAfterStop();
+
+ if (m_reg_context_sp)
+ m_reg_context_sp->InvalidateAllRegisters();
+}
diff --git a/source/Plugins/Process/Utility/ThreadMemory.h b/source/Plugins/Process/Utility/ThreadMemory.h
new file mode 100644
index 0000000..07eb45d
--- /dev/null
+++ b/source/Plugins/Process/Utility/ThreadMemory.h
@@ -0,0 +1,152 @@
+//===-- ThreadMemory.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadMemory_h_
+#define liblldb_ThreadMemory_h_
+
+#include "lldb/Target/Thread.h"
+
+class ThreadMemory :
+ public lldb_private::Thread
+{
+public:
+
+ ThreadMemory (lldb_private::Process &process,
+ lldb::tid_t tid,
+ const lldb::ValueObjectSP &thread_info_valobj_sp);
+
+ ThreadMemory (lldb_private::Process &process,
+ lldb::tid_t tid,
+ const char *name,
+ const char *queue,
+ lldb::addr_t register_data_addr);
+
+ virtual
+ ~ThreadMemory();
+
+ //------------------------------------------------------------------
+ // lldb_private::Thread methods
+ //------------------------------------------------------------------
+ virtual lldb::RegisterContextSP
+ GetRegisterContext ();
+
+ virtual lldb::RegisterContextSP
+ CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ virtual bool
+ CalculateStopInfo ();
+
+ virtual const char *
+ GetInfo ()
+ {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetInfo();
+ return NULL;
+ }
+
+ virtual const char *
+ GetName ()
+ {
+ if (!m_name.empty())
+ return m_name.c_str();
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetName();
+ return NULL;
+ }
+
+ virtual const char *
+ GetQueueName ()
+ {
+ if (!m_queue.empty())
+ return m_queue.c_str();
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetQueueName();
+ return NULL;
+ }
+
+ virtual void
+ WillResume (lldb::StateType resume_state);
+
+ virtual void
+ DidResume ()
+ {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->DidResume();
+ }
+
+ virtual lldb::user_id_t
+ GetProtocolID () const
+ {
+ if (m_backing_thread_sp)
+ return m_backing_thread_sp->GetProtocolID();
+ return Thread::GetProtocolID();
+ }
+
+ virtual void
+ RefreshStateAfterStop();
+
+ lldb::ValueObjectSP &
+ GetValueObject ()
+ {
+ return m_thread_info_valobj_sp;
+ }
+
+ virtual void
+ ClearStackFrames ();
+
+ virtual void
+ ClearBackingThread ()
+ {
+ m_backing_thread_sp.reset();
+ }
+
+ virtual bool
+ SetBackingThread (const lldb::ThreadSP &thread_sp)
+ {
+ //printf ("Thread 0x%llx is being backed by thread 0x%llx\n", GetID(), thread_sp->GetID());
+ m_backing_thread_sp = thread_sp;
+ return (bool)thread_sp;
+ }
+
+ virtual lldb::ThreadSP
+ GetBackingThread () const
+ {
+ return m_backing_thread_sp;
+ }
+
+protected:
+
+ virtual bool
+ IsOperatingSystemPluginThread () const
+ {
+ return true;
+ }
+
+
+ //------------------------------------------------------------------
+ // For ThreadMemory and subclasses
+ //------------------------------------------------------------------
+ // If this memory thread is actually represented by a thread from the
+ // lldb_private::Process subclass, then fill in the thread here and
+ // all APIs will be routed through this thread object. If m_backing_thread_sp
+ // is empty, then this thread is simply in memory with no representation
+ // through the process plug-in.
+ lldb::ThreadSP m_backing_thread_sp;
+ lldb::ValueObjectSP m_thread_info_valobj_sp;
+ std::string m_name;
+ std::string m_queue;
+ lldb::addr_t m_register_data_addr;
+private:
+ //------------------------------------------------------------------
+ // For ThreadMemory only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (ThreadMemory);
+};
+
+#endif // liblldb_ThreadMemory_h_
diff --git a/source/Plugins/Process/Utility/UnwindLLDB.cpp b/source/Plugins/Process/Utility/UnwindLLDB.cpp
new file mode 100644
index 0000000..0eea003
--- /dev/null
+++ b/source/Plugins/Process/Utility/UnwindLLDB.cpp
@@ -0,0 +1,322 @@
+//===-- UnwindLLDB.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+
+#include "UnwindLLDB.h"
+#include "RegisterContextLLDB.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindLLDB::UnwindLLDB (Thread &thread) :
+ Unwind (thread),
+ m_frames(),
+ m_unwind_complete(false)
+{
+}
+
+uint32_t
+UnwindLLDB::DoGetFrameCount()
+{
+ if (!m_unwind_complete)
+ {
+//#define DEBUG_FRAME_SPEED 1
+#if DEBUG_FRAME_SPEED
+#define FRAME_COUNT 10000
+ TimeValue time_value (TimeValue::Now());
+#endif
+ if (!AddFirstFrame ())
+ return 0;
+
+ ProcessSP process_sp (m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : NULL;
+
+ while (AddOneMoreFrame (abi))
+ {
+#if DEBUG_FRAME_SPEED
+ if ((m_frames.size() % FRAME_COUNT) == 0)
+ {
+ TimeValue now(TimeValue::Now());
+ uint64_t delta_t = now - time_value;
+ printf ("%u frames in %" PRIu64 ".%09llu ms (%g frames/sec)\n",
+ FRAME_COUNT,
+ delta_t / TimeValue::NanoSecPerSec,
+ delta_t % TimeValue::NanoSecPerSec,
+ (float)FRAME_COUNT / ((float)delta_t / (float)TimeValue::NanoSecPerSec));
+ time_value = now;
+ }
+#endif
+ }
+ }
+ return m_frames.size ();
+}
+
+bool
+UnwindLLDB::AddFirstFrame ()
+{
+ if (m_frames.size() > 0)
+ return true;
+
+ // First, set up the 0th (initial) frame
+ CursorSP first_cursor_sp(new Cursor ());
+ RegisterContextLLDBSP reg_ctx_sp (new RegisterContextLLDB (m_thread,
+ RegisterContextLLDBSP(),
+ first_cursor_sp->sctx,
+ 0, *this));
+ if (reg_ctx_sp.get() == NULL)
+ goto unwind_done;
+
+ if (!reg_ctx_sp->IsValid())
+ goto unwind_done;
+
+ if (!reg_ctx_sp->GetCFA (first_cursor_sp->cfa))
+ goto unwind_done;
+
+ if (!reg_ctx_sp->ReadPC (first_cursor_sp->start_pc))
+ goto unwind_done;
+
+ // Everything checks out, so release the auto pointer value and let the
+ // cursor own it in its shared pointer
+ first_cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp;
+ m_frames.push_back (first_cursor_sp);
+ return true;
+unwind_done:
+ m_unwind_complete = true;
+ return false;
+}
+
+// For adding a non-zero stack frame to m_frames.
+bool
+UnwindLLDB::AddOneMoreFrame (ABI *abi)
+{
+ // If we've already gotten to the end of the stack, don't bother to try again...
+ if (m_unwind_complete)
+ return false;
+
+ Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
+ CursorSP cursor_sp(new Cursor ());
+
+ // Frame zero is a little different
+ if (m_frames.size() == 0)
+ return false;
+
+ uint32_t cur_idx = m_frames.size ();
+ RegisterContextLLDBSP reg_ctx_sp(new RegisterContextLLDB (m_thread,
+ m_frames[cur_idx - 1]->reg_ctx_lldb_sp,
+ cursor_sp->sctx,
+ cur_idx,
+ *this));
+
+ // We want to detect an unwind that cycles erronously and stop backtracing.
+ // Don't want this maximum unwind limit to be too low -- if you have a backtrace
+ // with an "infinitely recursing" bug, it will crash when the stack blows out
+ // and the first 35,000 frames are uninteresting - it's the top most 5 frames that
+ // you actually care about. So you can't just cap the unwind at 10,000 or something.
+ // Realistically anything over around 200,000 is going to blow out the stack space.
+ // If we're still unwinding at that point, we're probably never going to finish.
+ if (cur_idx > 300000)
+ {
+ if (log)
+ log->Printf ("%*sFrame %d unwound too many frames, assuming unwind has gone astray, stopping.",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ goto unwind_done;
+ }
+
+ if (reg_ctx_sp.get() == NULL)
+ goto unwind_done;
+
+ if (!reg_ctx_sp->IsValid())
+ {
+ if (log)
+ {
+ log->Printf("%*sFrame %d invalid RegisterContext for this frame, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ }
+ goto unwind_done;
+ }
+ if (!reg_ctx_sp->GetCFA (cursor_sp->cfa))
+ {
+ if (log)
+ {
+ log->Printf("%*sFrame %d did not get CFA for this frame, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ }
+ goto unwind_done;
+ }
+ if (abi && !abi->CallFrameAddressIsValid(cursor_sp->cfa))
+ {
+ if (log)
+ {
+ log->Printf("%*sFrame %d did not get a valid CFA for this frame, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ }
+ goto unwind_done;
+ }
+ if (!reg_ctx_sp->ReadPC (cursor_sp->start_pc))
+ {
+ if (log)
+ {
+ log->Printf("%*sFrame %d did not get PC for this frame, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ }
+ goto unwind_done;
+ }
+ if (abi && !abi->CodeAddressIsValid (cursor_sp->start_pc))
+ {
+ if (log)
+ {
+ log->Printf("%*sFrame %d did not get a valid PC, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ }
+ goto unwind_done;
+ }
+ if (!m_frames.empty())
+ {
+ if (m_frames.back()->start_pc == cursor_sp->start_pc)
+ {
+ if (m_frames.back()->cfa == cursor_sp->cfa)
+ goto unwind_done; // Infinite loop where the current cursor is the same as the previous one...
+ else if (abi && abi->StackUsesFrames())
+ {
+ // We might have a CFA that is not using the frame pointer and
+ // we want to validate that the frame pointer is valid.
+ if (reg_ctx_sp->GetFP() == 0)
+ goto unwind_done;
+ }
+ }
+ }
+ cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp;
+ m_frames.push_back (cursor_sp);
+ return true;
+
+unwind_done:
+ m_unwind_complete = true;
+ return false;
+}
+
+bool
+UnwindLLDB::DoGetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc)
+{
+ if (m_frames.size() == 0)
+ {
+ if (!AddFirstFrame())
+ return false;
+ }
+
+ ProcessSP process_sp (m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : NULL;
+
+ while (idx >= m_frames.size() && AddOneMoreFrame (abi))
+ ;
+
+ if (idx < m_frames.size ())
+ {
+ cfa = m_frames[idx]->cfa;
+ pc = m_frames[idx]->start_pc;
+ return true;
+ }
+ return false;
+}
+
+lldb::RegisterContextSP
+UnwindLLDB::DoCreateRegisterContextForFrame (StackFrame *frame)
+{
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t idx = frame->GetConcreteFrameIndex ();
+
+ if (idx == 0)
+ {
+ return m_thread.GetRegisterContext();
+ }
+
+ if (m_frames.size() == 0)
+ {
+ if (!AddFirstFrame())
+ return reg_ctx_sp;
+ }
+
+ ProcessSP process_sp (m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : NULL;
+
+ while (idx >= m_frames.size())
+ {
+ if (!AddOneMoreFrame (abi))
+ break;
+ }
+
+ const uint32_t num_frames = m_frames.size();
+ if (idx < num_frames)
+ {
+ Cursor *frame_cursor = m_frames[idx].get();
+ reg_ctx_sp = frame_cursor->reg_ctx_lldb_sp;
+ }
+ return reg_ctx_sp;
+}
+
+UnwindLLDB::RegisterContextLLDBSP
+UnwindLLDB::GetRegisterContextForFrameNum (uint32_t frame_num)
+{
+ RegisterContextLLDBSP reg_ctx_sp;
+ if (frame_num < m_frames.size())
+ reg_ctx_sp = m_frames[frame_num]->reg_ctx_lldb_sp;
+ return reg_ctx_sp;
+}
+
+bool
+UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc, uint32_t starting_frame_num, bool pc_reg)
+{
+ int64_t frame_num = starting_frame_num;
+ if (frame_num >= m_frames.size())
+ return false;
+
+ // Never interrogate more than one level while looking for the saved pc value. If the value
+ // isn't saved by frame_num, none of the frames lower on the stack will have a useful value.
+ if (pc_reg)
+ {
+ UnwindLLDB::RegisterSearchResult result;
+ result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc);
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound)
+ return true;
+ else
+ return false;
+ }
+ while (frame_num >= 0)
+ {
+ UnwindLLDB::RegisterSearchResult result;
+ result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc);
+
+ // If we have unwind instructions saying that register N is saved in register M in the middle of
+ // the stack (and N can equal M here, meaning the register was not used in this function), then
+ // change the register number we're looking for to M and keep looking for a concrete location
+ // down the stack, or an actual value from a live RegisterContext at frame 0.
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound
+ && regloc.type == UnwindLLDB::RegisterLocation::eRegisterInRegister
+ && frame_num > 0)
+ {
+ result = UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ lldb_regnum = regloc.location.register_number;
+ }
+
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound)
+ return true;
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile)
+ return false;
+ frame_num--;
+ }
+ return false;
+}
diff --git a/source/Plugins/Process/Utility/UnwindLLDB.h b/source/Plugins/Process/Utility/UnwindLLDB.h
new file mode 100644
index 0000000..5725654
--- /dev/null
+++ b/source/Plugins/Process/Utility/UnwindLLDB.h
@@ -0,0 +1,125 @@
+//===-- UnwindLLDB.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_UnwindLLDB_h_
+#define lldb_UnwindLLDB_h_
+
+#include <vector>
+
+#include "lldb/lldb-public.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Unwind.h"
+
+namespace lldb_private {
+
+class RegisterContextLLDB;
+
+class UnwindLLDB : public lldb_private::Unwind
+{
+public:
+ UnwindLLDB (lldb_private::Thread &thread);
+
+ virtual
+ ~UnwindLLDB() { }
+
+ enum RegisterSearchResult
+ {
+ eRegisterFound = 0,
+ eRegisterNotFound,
+ eRegisterIsVolatile
+ };
+
+protected:
+ friend class lldb_private::RegisterContextLLDB;
+
+ struct RegisterLocation {
+ enum RegisterLocationTypes
+ {
+ eRegisterNotSaved = 0, // register was not preserved by callee. If volatile reg, is unavailable
+ eRegisterSavedAtMemoryLocation, // register is saved at a specific word of target mem (target_memory_location)
+ eRegisterInRegister, // register is available in a (possible other) register (register_number)
+ eRegisterSavedAtHostMemoryLocation, // register is saved at a word in lldb's address space
+ eRegisterValueInferred // register val was computed (and is in inferred_value)
+ };
+ int type;
+ union
+ {
+ lldb::addr_t target_memory_location;
+ uint32_t register_number; // in eRegisterKindLLDB register numbering system
+ void* host_memory_location;
+ uint64_t inferred_value; // eRegisterValueInferred - e.g. stack pointer == cfa + offset
+ } location;
+ };
+
+ void
+ DoClear()
+ {
+ m_frames.clear();
+ m_unwind_complete = false;
+ }
+
+ virtual uint32_t
+ DoGetFrameCount();
+
+ bool
+ DoGetFrameInfoAtIndex (uint32_t frame_idx,
+ lldb::addr_t& cfa,
+ lldb::addr_t& start_pc);
+
+ lldb::RegisterContextSP
+ DoCreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ typedef std::shared_ptr<RegisterContextLLDB> RegisterContextLLDBSP;
+
+ // Needed to retrieve the "next" frame (e.g. frame 2 needs to retrieve frame 1's RegisterContextLLDB)
+ // The RegisterContext for frame_num must already exist or this returns an empty shared pointer.
+ RegisterContextLLDBSP
+ GetRegisterContextForFrameNum (uint32_t frame_num);
+
+ // Iterate over the RegisterContextLLDB's in our m_frames vector, look for the first one that
+ // has a saved location for this reg.
+ bool
+ SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc, uint32_t starting_frame_num, bool pc_register);
+
+
+private:
+
+ struct Cursor
+ {
+ lldb::addr_t start_pc; // The start address of the function/symbol for this frame - current pc if unknown
+ lldb::addr_t cfa; // The canonical frame address for this stack frame
+ lldb_private::SymbolContext sctx; // A symbol context we'll contribute to & provide to the StackFrame creation
+ RegisterContextLLDBSP reg_ctx_lldb_sp; // These are all RegisterContextLLDB's
+
+ Cursor () : start_pc (LLDB_INVALID_ADDRESS), cfa (LLDB_INVALID_ADDRESS), sctx(), reg_ctx_lldb_sp() { }
+ private:
+ DISALLOW_COPY_AND_ASSIGN (Cursor);
+ };
+
+ typedef std::shared_ptr<Cursor> CursorSP;
+ std::vector<CursorSP> m_frames;
+ bool m_unwind_complete; // If this is true, we've enumerated all the frames in the stack, and m_frames.size() is the
+ // number of frames, etc. Otherwise we've only gone as far as directly asked, and m_frames.size()
+ // is how far we've currently gone.
+
+
+ bool AddOneMoreFrame (ABI *abi);
+ bool AddFirstFrame ();
+
+ //------------------------------------------------------------------
+ // For UnwindLLDB only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (UnwindLLDB);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_UnwindLLDB_h_
diff --git a/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp b/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp
new file mode 100644
index 0000000..d011314
--- /dev/null
+++ b/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp
@@ -0,0 +1,275 @@
+//===-- UnwindMacOSXFrameBackchain.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+#include "RegisterContextMacOSXFrameBackchain.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain (Thread &thread) :
+ Unwind (thread),
+ m_cursors()
+{
+}
+
+uint32_t
+UnwindMacOSXFrameBackchain::DoGetFrameCount()
+{
+ if (m_cursors.empty())
+ {
+ ExecutionContext exe_ctx (m_thread.shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target)
+ {
+ const ArchSpec& target_arch = target->GetArchitecture ();
+ // Frame zero should always be supplied by the thread...
+ exe_ctx.SetFrameSP (m_thread.GetStackFrameAtIndex (0));
+
+ if (target_arch.GetAddressByteSize() == 8)
+ GetStackFrameData_x86_64 (exe_ctx);
+ else
+ GetStackFrameData_i386 (exe_ctx);
+ }
+ }
+ return m_cursors.size();
+}
+
+bool
+UnwindMacOSXFrameBackchain::DoGetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc)
+{
+ const uint32_t frame_count = GetFrameCount();
+ if (idx < frame_count)
+ {
+ if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS)
+ return false;
+ if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS)
+ return false;
+
+ pc = m_cursors[idx].pc;
+ cfa = m_cursors[idx].fp;
+
+ return true;
+ }
+ return false;
+}
+
+lldb::RegisterContextSP
+UnwindMacOSXFrameBackchain::DoCreateRegisterContextForFrame (StackFrame *frame)
+{
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_idx = frame->GetConcreteFrameIndex ();
+ const uint32_t frame_count = GetFrameCount();
+ if (concrete_idx < frame_count)
+ reg_ctx_sp.reset (new RegisterContextMacOSXFrameBackchain (m_thread, concrete_idx, m_cursors[concrete_idx]));
+ return reg_ctx_sp;
+}
+
+size_t
+UnwindMacOSXFrameBackchain::GetStackFrameData_i386 (const ExecutionContext &exe_ctx)
+{
+ m_cursors.clear();
+
+ StackFrame *first_frame = exe_ctx.GetFramePtr();
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == NULL)
+ return 0;
+
+ std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair;
+
+ struct Frame_i386
+ {
+ uint32_t fp;
+ uint32_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+ assert (reg_ctx);
+
+ Cursor cursor;
+ cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS);
+ cursor.fp = reg_ctx->GetFP (0);
+
+ Frame_i386 frame = { static_cast<uint32_t>(cursor.fp), static_cast<uint32_t>(cursor.pc) };
+
+ m_cursors.push_back(cursor);
+
+ const size_t k_frame_size = sizeof(frame);
+ Error error;
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0))
+ {
+ // Read both the FP and PC (8 bytes)
+ if (process->ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size)
+ break;
+ if (frame.pc >= 0x1000)
+ {
+ cursor.pc = frame.pc;
+ cursor.fp = frame.fp;
+ m_cursors.push_back (cursor);
+ }
+ }
+ if (!m_cursors.empty())
+ {
+ lldb::addr_t first_frame_pc = m_cursors.front().pc;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS)
+ {
+ const uint32_t resolve_scope = eSymbolContextModule |
+ eSymbolContextCompUnit |
+ eSymbolContextFunction |
+ eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc (first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = NULL;
+ AddressRange range;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol)
+ {
+ range.GetBaseAddress() = first_frame_sc.symbol->GetAddress();
+ range.SetByteSize (first_frame_sc.symbol->GetByteSize());
+ addr_range_ptr = &range;
+ }
+
+ if (addr_range_ptr)
+ {
+ if (first_frame->GetFrameCodeAddress() == addr_range_ptr->GetBaseAddress())
+ {
+ // We are at the first instruction, so we can recover the
+ // previous PC by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP (0);
+ // Read the real second frame return address into frame.pc
+ if (first_frame_sp && process->ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc))
+ {
+ cursor.fp = m_cursors.front().fp;
+ cursor.pc = frame.pc; // Set the new second frame PC
+
+ // Insert the second frame
+ m_cursors.insert(m_cursors.begin()+1, cursor);
+
+ m_cursors.front().fp = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+// uint32_t i=0;
+// printf(" PC FP\n");
+// printf(" ------------------ ------------------ \n");
+// for (i=0; i<m_cursors.size(); ++i)
+// {
+// printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 "\n", i, m_cursors[i].pc, m_cursors[i].fp);
+// }
+ return m_cursors.size();
+}
+
+
+size_t
+UnwindMacOSXFrameBackchain::GetStackFrameData_x86_64 (const ExecutionContext &exe_ctx)
+{
+ m_cursors.clear();
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == NULL)
+ return 0;
+
+ StackFrame *first_frame = exe_ctx.GetFramePtr();
+
+ std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair;
+
+ struct Frame_x86_64
+ {
+ uint64_t fp;
+ uint64_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+ assert (reg_ctx);
+
+ Cursor cursor;
+ cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS);
+ cursor.fp = reg_ctx->GetFP (0);
+
+ Frame_x86_64 frame = { cursor.fp, cursor.pc };
+
+ m_cursors.push_back(cursor);
+ Error error;
+ const size_t k_frame_size = sizeof(frame);
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0))
+ {
+ // Read both the FP and PC (16 bytes)
+ if (process->ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size)
+ break;
+
+ if (frame.pc >= 0x1000)
+ {
+ cursor.pc = frame.pc;
+ cursor.fp = frame.fp;
+ m_cursors.push_back (cursor);
+ }
+ }
+ if (!m_cursors.empty())
+ {
+ lldb::addr_t first_frame_pc = m_cursors.front().pc;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS)
+ {
+ const uint32_t resolve_scope = eSymbolContextModule |
+ eSymbolContextCompUnit |
+ eSymbolContextFunction |
+ eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = NULL;
+ AddressRange range;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol)
+ {
+ range.GetBaseAddress() = first_frame_sc.symbol->GetAddress();
+ range.SetByteSize (first_frame_sc.symbol->GetByteSize());
+ addr_range_ptr = &range;
+ }
+
+ if (addr_range_ptr)
+ {
+ if (first_frame->GetFrameCodeAddress() == addr_range_ptr->GetBaseAddress())
+ {
+ // We are at the first instruction, so we can recover the
+ // previous PC by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP (0);
+ // Read the real second frame return address into frame.pc
+ if (process->ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc))
+ {
+ cursor.fp = m_cursors.front().fp;
+ cursor.pc = frame.pc; // Set the new second frame PC
+
+ // Insert the second frame
+ m_cursors.insert(m_cursors.begin()+1, cursor);
+
+ m_cursors.front().fp = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+ return m_cursors.size();
+}
+
diff --git a/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h b/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h
new file mode 100644
index 0000000..2695376
--- /dev/null
+++ b/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h
@@ -0,0 +1,74 @@
+//===-- UnwindMacOSXFrameBackchain.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_UnwindMacOSXFrameBackchain_h_
+#define lldb_UnwindMacOSXFrameBackchain_h_
+
+// C Includes
+// C++ Includes
+#include <vector>
+
+// Other libraries and framework includes
+
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/Unwind.h"
+
+class UnwindMacOSXFrameBackchain : public lldb_private::Unwind
+{
+public:
+ UnwindMacOSXFrameBackchain (lldb_private::Thread &thread);
+
+ virtual
+ ~UnwindMacOSXFrameBackchain()
+ {
+ }
+
+protected:
+ virtual void
+ DoClear()
+ {
+ m_cursors.clear();
+ }
+
+ virtual uint32_t
+ DoGetFrameCount();
+
+ bool
+ DoGetFrameInfoAtIndex (uint32_t frame_idx,
+ lldb::addr_t& cfa,
+ lldb::addr_t& pc);
+
+ lldb::RegisterContextSP
+ DoCreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ friend class RegisterContextMacOSXFrameBackchain;
+
+ struct Cursor
+ {
+ lldb::addr_t pc; // Program counter
+ lldb::addr_t fp; // Frame pointer for us with backchain
+ };
+
+private:
+ std::vector<Cursor> m_cursors;
+
+ size_t
+ GetStackFrameData_i386 (const lldb_private::ExecutionContext &exe_ctx);
+
+ size_t
+ GetStackFrameData_x86_64 (const lldb_private::ExecutionContext &exe_ctx);
+
+ //------------------------------------------------------------------
+ // For UnwindMacOSXFrameBackchain only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (UnwindMacOSXFrameBackchain);
+};
+
+#endif // lldb_UnwindMacOSXFrameBackchain_h_
diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/source/Plugins/Process/elf-core/ProcessElfCore.cpp
new file mode 100644
index 0000000..dd553ce
--- /dev/null
+++ b/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -0,0 +1,619 @@
+//===-- ProcessElfCore.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <stdlib.h>
+
+// Other libraries and framework includes
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "ProcessPOSIXLog.h"
+
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"
+
+// Project includes
+#include "ProcessElfCore.h"
+#include "ThreadElfCore.h"
+
+using namespace lldb_private;
+
+ConstString
+ProcessElfCore::GetPluginNameStatic()
+{
+ static ConstString g_name("elf-core");
+ return g_name;
+}
+
+const char *
+ProcessElfCore::GetPluginDescriptionStatic()
+{
+ return "ELF core dump plug-in.";
+}
+
+void
+ProcessElfCore::Terminate()
+{
+ PluginManager::UnregisterPlugin (ProcessElfCore::CreateInstance);
+}
+
+
+lldb::ProcessSP
+ProcessElfCore::CreateInstance (Target &target, Listener &listener, const FileSpec *crash_file)
+{
+ lldb::ProcessSP process_sp;
+ if (crash_file)
+ process_sp.reset(new ProcessElfCore (target, listener, *crash_file));
+ return process_sp;
+}
+
+bool
+ProcessElfCore::CanDebug(Target &target, bool plugin_specified_by_name)
+{
+ // For now we are just making sure the file exists for a given module
+ if (!m_core_module_sp && m_core_file.Exists())
+ {
+ ModuleSpec core_module_spec(m_core_file, target.GetArchitecture());
+ Error error (ModuleList::GetSharedModule (core_module_spec, m_core_module_sp,
+ NULL, NULL, NULL));
+ if (m_core_module_sp)
+ {
+ ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
+ if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile)
+ return true;
+ }
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------
+// ProcessElfCore constructor
+//----------------------------------------------------------------------
+ProcessElfCore::ProcessElfCore(Target& target, Listener &listener,
+ const FileSpec &core_file) :
+ Process (target, listener),
+ m_core_module_sp (),
+ m_core_file (core_file),
+ m_dyld_plugin_name (),
+ m_thread_data_valid(false),
+ m_thread_data(),
+ m_core_aranges ()
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ProcessElfCore::~ProcessElfCore()
+{
+ Clear();
+ // We need to call finalize on the process before destroying ourselves
+ // to make sure all of the broadcaster cleanup goes as planned. If we
+ // destruct this class, then Process::~Process() might have problems
+ // trying to fully destroy the broadcaster.
+ Finalize();
+}
+
+//----------------------------------------------------------------------
+// PluginInterface
+//----------------------------------------------------------------------
+ConstString
+ProcessElfCore::GetPluginName()
+{
+ return GetPluginNameStatic();
+}
+
+uint32_t
+ProcessElfCore::GetPluginVersion()
+{
+ return 1;
+}
+
+lldb::addr_t
+ProcessElfCore::AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader *header)
+{
+ lldb::addr_t addr = header->p_vaddr;
+ FileRange file_range (header->p_offset, header->p_filesz);
+ VMRangeToFileOffset::Entry range_entry(addr, header->p_memsz, file_range);
+
+ VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back();
+ if (last_entry &&
+ last_entry->GetRangeEnd() == range_entry.GetRangeBase() &&
+ last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase())
+ {
+ last_entry->SetRangeEnd (range_entry.GetRangeEnd());
+ last_entry->data.SetRangeEnd (range_entry.data.GetRangeEnd());
+ }
+ else
+ {
+ m_core_aranges.Append(range_entry);
+ }
+
+ return addr;
+}
+
+//----------------------------------------------------------------------
+// Process Control
+//----------------------------------------------------------------------
+Error
+ProcessElfCore::DoLoadCore ()
+{
+ Error error;
+ if (!m_core_module_sp)
+ {
+ error.SetErrorString ("invalid core module");
+ return error;
+ }
+
+ ObjectFileELF *core = (ObjectFileELF *)(m_core_module_sp->GetObjectFile());
+ if (core == NULL)
+ {
+ error.SetErrorString ("invalid core object file");
+ return error;
+ }
+
+ const uint32_t num_segments = core->GetProgramHeaderCount();
+ if (num_segments == 0)
+ {
+ error.SetErrorString ("core file has no sections");
+ return error;
+ }
+
+ SetCanJIT(false);
+
+ m_thread_data_valid = true;
+
+ bool ranges_are_sorted = true;
+ lldb::addr_t vm_addr = 0;
+ /// Walk through segments and Thread and Address Map information.
+ /// PT_NOTE - Contains Thread and Register information
+ /// PT_LOAD - Contains a contiguous range of Process Address Space
+ for(uint32_t i = 1; i <= num_segments; i++)
+ {
+ const elf::ELFProgramHeader *header = core->GetProgramHeaderByIndex(i);
+ assert(header != NULL);
+
+ DataExtractor data = core->GetSegmentDataByIndex(i);
+
+ // Parse thread contexts and auxv structure
+ if (header->p_type == llvm::ELF::PT_NOTE)
+ ParseThreadContextsFromNoteSegment(header, data);
+
+ // PT_LOAD segments contains address map
+ if (header->p_type == llvm::ELF::PT_LOAD)
+ {
+ lldb::addr_t last_addr = AddAddressRangeFromLoadSegment(header);
+ if (vm_addr > last_addr)
+ ranges_are_sorted = false;
+ vm_addr = last_addr;
+ }
+ }
+
+ if (!ranges_are_sorted)
+ m_core_aranges.Sort();
+
+ // Even if the architecture is set in the target, we need to override
+ // it to match the core file which is always single arch.
+ ArchSpec arch (m_core_module_sp->GetArchitecture());
+ if (arch.IsValid())
+ m_target.SetArchitecture(arch);
+
+ return error;
+}
+
+lldb_private::DynamicLoader *
+ProcessElfCore::GetDynamicLoader ()
+{
+ if (m_dyld_ap.get() == NULL)
+ m_dyld_ap.reset (DynamicLoader::FindPlugin(this, DynamicLoaderPOSIXDYLD::GetPluginNameStatic().GetCString()));
+ return m_dyld_ap.get();
+}
+
+bool
+ProcessElfCore::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list)
+{
+ const uint32_t num_threads = GetNumThreadContexts ();
+ if (!m_thread_data_valid)
+ return false;
+
+ for (lldb::tid_t tid = 0; tid < num_threads; ++tid)
+ {
+ const ThreadData &td = m_thread_data[tid];
+ lldb::ThreadSP thread_sp(new ThreadElfCore (*this, tid, td));
+ new_thread_list.AddThread (thread_sp);
+ }
+ return new_thread_list.GetSize(false) > 0;
+}
+
+void
+ProcessElfCore::RefreshStateAfterStop ()
+{
+}
+
+Error
+ProcessElfCore::DoDestroy ()
+{
+ return Error();
+}
+
+//------------------------------------------------------------------
+// Process Queries
+//------------------------------------------------------------------
+
+bool
+ProcessElfCore::IsAlive ()
+{
+ return true;
+}
+
+//------------------------------------------------------------------
+// Process Memory
+//------------------------------------------------------------------
+size_t
+ProcessElfCore::ReadMemory (lldb::addr_t addr, void *buf, size_t size, Error &error)
+{
+ // Don't allow the caching that lldb_private::Process::ReadMemory does
+ // since in core files we have it all cached our our core file anyway.
+ return DoReadMemory (addr, buf, size, error);
+}
+
+size_t
+ProcessElfCore::DoReadMemory (lldb::addr_t addr, void *buf, size_t size, Error &error)
+{
+ ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
+
+ if (core_objfile == NULL)
+ return 0;
+
+ // Get the address range
+ const VMRangeToFileOffset::Entry *address_range = m_core_aranges.FindEntryThatContains (addr);
+ if (address_range == NULL || address_range->GetRangeEnd() < addr)
+ {
+ error.SetErrorStringWithFormat ("core file does not contain 0x%" PRIx64, addr);
+ return 0;
+ }
+
+ // Convert the address into core file offset
+ const lldb::addr_t offset = addr - address_range->GetRangeBase();
+ const lldb::addr_t file_start = address_range->data.GetRangeBase();
+ const lldb::addr_t file_end = address_range->data.GetRangeEnd();
+ size_t bytes_to_read = size; // Number of bytes to read from the core file
+ size_t bytes_copied = 0; // Number of bytes actually read from the core file
+ size_t zero_fill_size = 0; // Padding
+ lldb::addr_t bytes_left = 0; // Number of bytes available in the core file from the given address
+
+ if (file_end > offset)
+ bytes_left = file_end - offset;
+
+ if (bytes_to_read > bytes_left)
+ {
+ zero_fill_size = bytes_to_read - bytes_left;
+ bytes_to_read = bytes_left;
+ }
+
+ // If there is data available on the core file read it
+ if (bytes_to_read)
+ bytes_copied = core_objfile->CopyData(offset + file_start, bytes_to_read, buf);
+
+ assert(zero_fill_size <= size);
+ // Pad remaining bytes
+ if (zero_fill_size)
+ memset(((char *)buf) + bytes_copied, 0, zero_fill_size);
+
+ return bytes_copied + zero_fill_size;
+}
+
+void
+ProcessElfCore::Clear()
+{
+ m_thread_list.Clear();
+}
+
+void
+ProcessElfCore::Initialize()
+{
+ static bool g_initialized = false;
+
+ if (g_initialized == false)
+ {
+ g_initialized = true;
+ PluginManager::RegisterPlugin (GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance);
+ }
+}
+
+lldb::addr_t
+ProcessElfCore::GetImageInfoAddress()
+{
+ Target *target = &GetTarget();
+ ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress();
+
+ if (addr.IsValid())
+ return addr.GetLoadAddress(target);
+ return LLDB_INVALID_ADDRESS;
+}
+
+/// Core files PT_NOTE segment descriptor types
+enum {
+ NT_PRSTATUS = 1,
+ NT_FPREGSET,
+ NT_PRPSINFO,
+ NT_TASKSTRUCT,
+ NT_PLATFORM,
+ NT_AUXV
+};
+
+enum {
+ NT_FREEBSD_PRSTATUS = 1,
+ NT_FREEBSD_FPREGSET,
+ NT_FREEBSD_PRPSINFO,
+ NT_FREEBSD_THRMISC = 7,
+ NT_FREEBSD_PROCSTAT_AUXV = 16
+};
+
+/// Align the given value to next boundary specified by the alignment bytes
+static uint32_t
+AlignToNext(uint32_t value, int alignment_bytes)
+{
+ return (value + alignment_bytes - 1) & ~(alignment_bytes - 1);
+}
+
+/// Note Structure found in ELF core dumps.
+/// This is PT_NOTE type program/segments in the core file.
+struct ELFNote
+{
+ elf::elf_word n_namesz;
+ elf::elf_word n_descsz;
+ elf::elf_word n_type;
+
+ std::string n_name;
+
+ ELFNote() : n_namesz(0), n_descsz(0), n_type(0)
+ {
+ }
+
+ /// Parse an ELFNote entry from the given DataExtractor starting at position
+ /// \p offset.
+ ///
+ /// @param[in] data
+ /// The DataExtractor to read from.
+ ///
+ /// @param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// @return
+ /// True if the ELFRel entry was successfully read and false otherwise.
+ bool
+ Parse(const DataExtractor &data, lldb::offset_t *offset)
+ {
+ // Read all fields.
+ if (data.GetU32(offset, &n_namesz, 3) == NULL)
+ return false;
+
+ // The name field is required to be nul-terminated, and n_namesz
+ // includes the terminating nul in observed implementations (contrary
+ // to the ELF-64 spec). A special case is needed for cores generated
+ // by some older Linux versions, which write a note named "CORE"
+ // without a nul terminator and n_namesz = 4.
+ if (n_namesz == 4)
+ {
+ char buf[4];
+ if (data.ExtractBytes (*offset, 4, data.GetByteOrder(), buf) != 4)
+ return false;
+ if (strncmp (buf, "CORE", 4) == 0)
+ {
+ n_name = "CORE";
+ *offset += 4;
+ return true;
+ }
+ }
+
+ const char *cstr = data.GetCStr(offset, AlignToNext(n_namesz, 4));
+ if (cstr == NULL)
+ {
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+ if (log)
+ log->Printf("Failed to parse note name lacking nul terminator");
+
+ return false;
+ }
+ n_name = cstr;
+ return true;
+ }
+};
+
+// Parse a FreeBSD NT_PRSTATUS note - see FreeBSD sys/procfs.h for details.
+static void
+ParseFreeBSDPrStatus(ThreadData *thread_data, DataExtractor &data,
+ ArchSpec &arch)
+{
+ lldb::offset_t offset = 0;
+ bool have_padding = (arch.GetMachine() == llvm::Triple::x86_64);
+ int pr_version = data.GetU32(&offset);
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS));
+ if (log)
+ {
+ if (pr_version > 1)
+ log->Printf("FreeBSD PRSTATUS unexpected version %d", pr_version);
+ }
+
+ if (have_padding)
+ offset += 4;
+ offset += 28; // pr_statussz, pr_gregsetsz, pr_fpregsetsz, pr_osreldate
+ thread_data->signo = data.GetU32(&offset); // pr_cursig
+ offset += 4; // pr_pid
+ if (have_padding)
+ offset += 4;
+
+ size_t len = data.GetByteSize() - offset;
+ thread_data->gpregset = DataExtractor(data, offset, len);
+}
+
+static void
+ParseFreeBSDThrMisc(ThreadData *thread_data, DataExtractor &data)
+{
+ lldb::offset_t offset = 0;
+ thread_data->name = data.GetCStr(&offset, 20);
+}
+
+/// Parse Thread context from PT_NOTE segment and store it in the thread list
+/// Notes:
+/// 1) A PT_NOTE segment is composed of one or more NOTE entries.
+/// 2) NOTE Entry contains a standard header followed by variable size data.
+/// (see ELFNote structure)
+/// 3) A Thread Context in a core file usually described by 3 NOTE entries.
+/// a) NT_PRSTATUS - Register context
+/// b) NT_PRPSINFO - Process info(pid..)
+/// c) NT_FPREGSET - Floating point registers
+/// 4) The NOTE entries can be in any order
+/// 5) If a core file contains multiple thread contexts then there is two data forms
+/// a) Each thread context(2 or more NOTE entries) contained in its own segment (PT_NOTE)
+/// b) All thread context is stored in a single segment(PT_NOTE).
+/// This case is little tricker since while parsing we have to find where the
+/// new thread starts. The current implementation marks beginning of
+/// new thread when it finds NT_PRSTATUS or NT_PRPSINFO NOTE entry.
+/// For case (b) there may be either one NT_PRPSINFO per thread, or a single
+/// one that applies to all threads (depending on the platform type).
+void
+ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader *segment_header,
+ DataExtractor segment_data)
+{
+ assert(segment_header && segment_header->p_type == llvm::ELF::PT_NOTE);
+
+ lldb::offset_t offset = 0;
+ ThreadData *thread_data = new ThreadData();
+ bool have_prstatus = false;
+ bool have_prpsinfo = false;
+
+ ArchSpec arch = GetArchitecture();
+ ELFLinuxPrPsInfo prpsinfo;
+ ELFLinuxPrStatus prstatus;
+ size_t header_size;
+ size_t len;
+
+ // Loop through the NOTE entires in the segment
+ while (offset < segment_header->p_filesz)
+ {
+ ELFNote note = ELFNote();
+ note.Parse(segment_data, &offset);
+
+ // Beginning of new thread
+ if ((note.n_type == NT_PRSTATUS && have_prstatus) ||
+ (note.n_type == NT_PRPSINFO && have_prpsinfo))
+ {
+ assert(thread_data->gpregset.GetByteSize() > 0);
+ // Add the new thread to thread list
+ m_thread_data.push_back(*thread_data);
+ thread_data = new ThreadData();
+ have_prstatus = false;
+ have_prpsinfo = false;
+ }
+
+ size_t note_start, note_size;
+ note_start = offset;
+ note_size = AlignToNext(note.n_descsz, 4);
+
+ // Store the NOTE information in the current thread
+ DataExtractor note_data (segment_data, note_start, note_size);
+ if (note.n_name == "FreeBSD")
+ {
+ switch (note.n_type)
+ {
+ case NT_FREEBSD_PRSTATUS:
+ have_prstatus = true;
+ ParseFreeBSDPrStatus(thread_data, note_data, arch);
+ break;
+ case NT_FREEBSD_FPREGSET:
+ thread_data->fpregset = note_data;
+ break;
+ case NT_FREEBSD_PRPSINFO:
+ have_prpsinfo = true;
+ break;
+ case NT_FREEBSD_THRMISC:
+ ParseFreeBSDThrMisc(thread_data, note_data);
+ break;
+ case NT_FREEBSD_PROCSTAT_AUXV:
+ // FIXME: FreeBSD sticks an int at the beginning of the note
+ m_auxv = DataExtractor(segment_data, note_start + 4, note_size - 4);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (note.n_type)
+ {
+ case NT_PRSTATUS:
+ have_prstatus = true;
+ prstatus.Parse(note_data, arch);
+ thread_data->signo = prstatus.pr_cursig;
+ header_size = ELFLinuxPrStatus::GetSize(arch);
+ len = note_data.GetByteSize() - header_size;
+ thread_data->gpregset = DataExtractor(note_data, header_size, len);
+ break;
+ case NT_FPREGSET:
+ thread_data->fpregset = note_data;
+ break;
+ case NT_PRPSINFO:
+ have_prpsinfo = true;
+ prpsinfo.Parse(note_data, arch);
+ thread_data->name = prpsinfo.pr_fname;
+ break;
+ case NT_AUXV:
+ m_auxv = DataExtractor(note_data);
+ break;
+ default:
+ break;
+ }
+ }
+
+ offset += note_size;
+ }
+ // Add last entry in the note section
+ if (thread_data && thread_data->gpregset.GetByteSize() > 0)
+ {
+ m_thread_data.push_back(*thread_data);
+ }
+}
+
+uint32_t
+ProcessElfCore::GetNumThreadContexts ()
+{
+ if (!m_thread_data_valid)
+ DoLoadCore();
+ return m_thread_data.size();
+}
+
+ArchSpec
+ProcessElfCore::GetArchitecture()
+{
+ ObjectFileELF *core_file = (ObjectFileELF *)(m_core_module_sp->GetObjectFile());
+ ArchSpec arch;
+ core_file->GetArchitecture(arch);
+ return arch;
+}
+
+const lldb::DataBufferSP
+ProcessElfCore::GetAuxvData()
+{
+ const uint8_t *start = m_auxv.GetDataStart();
+ size_t len = m_auxv.GetByteSize();
+ lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(start, len));
+ return buffer;
+}
+
diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.h b/source/Plugins/Process/elf-core/ProcessElfCore.h
new file mode 100644
index 0000000..1c1ed98
--- /dev/null
+++ b/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -0,0 +1,171 @@
+//===-- ProcessElfCore.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+// Notes about Linux Process core dumps:
+// 1) Linux core dump is stored as ELF file.
+// 2) The ELF file's PT_NOTE and PT_LOAD segments describes the program's
+// address space and thread contexts.
+// 3) PT_NOTE segment contains note entries which describes a thread context.
+// 4) PT_LOAD segment describes a valid contigous range of process address
+// space.
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessElfCore_h_
+#define liblldb_ProcessElfCore_h_
+
+// C++ Includes
+#include <list>
+#include <vector>
+
+// Other libraries and framework includes
+#include "lldb/Core/ConstString.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Target/Process.h"
+
+#include "Plugins/ObjectFile/ELF/ELFHeader.h"
+
+struct ThreadData;
+
+class ProcessElfCore : public lldb_private::Process
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ static lldb::ProcessSP
+ CreateInstance (lldb_private::Target& target,
+ lldb_private::Listener &listener,
+ const lldb_private::FileSpec *crash_file_path);
+
+ static void
+ Initialize();
+
+ static void
+ Terminate();
+
+ static lldb_private::ConstString
+ GetPluginNameStatic();
+
+ static const char *
+ GetPluginDescriptionStatic();
+
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ ProcessElfCore(lldb_private::Target& target,
+ lldb_private::Listener &listener,
+ const lldb_private::FileSpec &core_file);
+
+ virtual
+ ~ProcessElfCore();
+
+ //------------------------------------------------------------------
+ // Check if a given Process
+ //------------------------------------------------------------------
+ virtual bool
+ CanDebug (lldb_private::Target &target,
+ bool plugin_specified_by_name);
+
+ //------------------------------------------------------------------
+ // Creating a new process, or attaching to an existing one
+ //------------------------------------------------------------------
+ virtual lldb_private::Error
+ DoLoadCore ();
+
+ virtual lldb_private::DynamicLoader *
+ GetDynamicLoader ();
+
+ //------------------------------------------------------------------
+ // PluginInterface protocol
+ //------------------------------------------------------------------
+ virtual lldb_private::ConstString
+ GetPluginName();
+
+ virtual uint32_t
+ GetPluginVersion();
+
+ //------------------------------------------------------------------
+ // Process Control
+ //------------------------------------------------------------------
+ virtual lldb_private::Error
+ DoDestroy ();
+
+ virtual void
+ RefreshStateAfterStop();
+
+ //------------------------------------------------------------------
+ // Process Queries
+ //------------------------------------------------------------------
+ virtual bool
+ IsAlive ();
+
+ //------------------------------------------------------------------
+ // Process Memory
+ //------------------------------------------------------------------
+ virtual size_t
+ ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error);
+
+ virtual size_t
+ DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error);
+
+ virtual lldb::addr_t
+ GetImageInfoAddress ();
+
+ lldb_private::ArchSpec
+ GetArchitecture();
+
+ // Returns AUXV structure found in the core file
+ const lldb::DataBufferSP
+ GetAuxvData();
+
+protected:
+ void
+ Clear ( );
+
+ virtual bool
+ UpdateThreadList (lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &new_thread_list);
+
+private:
+ //------------------------------------------------------------------
+ // For ProcessElfCore only
+ //------------------------------------------------------------------
+ typedef lldb_private::Range<lldb::addr_t, lldb::addr_t> FileRange;
+ typedef lldb_private::RangeDataArray<lldb::addr_t, lldb::addr_t, FileRange, 1> VMRangeToFileOffset;
+
+ lldb::ModuleSP m_core_module_sp;
+ lldb_private::FileSpec m_core_file;
+ std::string m_dyld_plugin_name;
+ DISALLOW_COPY_AND_ASSIGN (ProcessElfCore);
+
+ // True if m_thread_contexts contains valid entries
+ bool m_thread_data_valid;
+
+ // Contain thread data read from NOTE segments
+ std::vector<ThreadData> m_thread_data;
+
+ // AUXV structure found from the NOTE segment
+ lldb_private::DataExtractor m_auxv;
+
+ // Address ranges found in the core
+ VMRangeToFileOffset m_core_aranges;
+
+ // Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment
+ void
+ ParseThreadContextsFromNoteSegment (const elf::ELFProgramHeader *segment_header,
+ lldb_private::DataExtractor segment_data);
+
+ // Returns number of thread contexts stored in the core file
+ uint32_t
+ GetNumThreadContexts();
+
+ // Parse a contiguous address range of the process from LOAD segment
+ lldb::addr_t
+ AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader *header);
+};
+
+#endif // liblldb_ProcessElffCore_h_
diff --git a/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.cpp b/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.cpp
new file mode 100644
index 0000000..6210175
--- /dev/null
+++ b/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.cpp
@@ -0,0 +1,68 @@
+//===-- RegisterContextCoreFreeBSD_x86_64.cpp -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Target/Thread.h"
+#include "RegisterContextCoreFreeBSD_x86_64.h"
+
+RegisterContextCoreFreeBSD_x86_64::RegisterContextCoreFreeBSD_x86_64(Thread &thread,
+ const DataExtractor &gpregset, const DataExtractor &fpregset)
+ : RegisterContextFreeBSD_x86_64(thread, 0)
+{
+ size_t size, len;
+
+ size = GetGPRSize();
+ m_gpregset = new uint8_t[size];
+ len = gpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_gpregset);
+ assert(len == size);
+}
+
+RegisterContextCoreFreeBSD_x86_64::~RegisterContextCoreFreeBSD_x86_64()
+{
+ delete [] m_gpregset;
+}
+
+bool
+RegisterContextCoreFreeBSD_x86_64::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value)
+{
+ value = *(uint64_t *)(m_gpregset + reg_info->byte_offset);
+ return true;
+}
+
+bool
+RegisterContextCoreFreeBSD_x86_64::ReadAllRegisterValues(lldb::DataBufferSP &data_sp)
+{
+ return false;
+}
+
+bool
+RegisterContextCoreFreeBSD_x86_64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value)
+{
+ return false;
+}
+
+bool
+RegisterContextCoreFreeBSD_x86_64::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp)
+{
+ return false;
+}
+
+bool
+RegisterContextCoreFreeBSD_x86_64::UpdateAfterBreakpoint()
+{
+ return false;
+}
+
+bool
+RegisterContextCoreFreeBSD_x86_64::HardwareSingleStep(bool enable)
+{
+ return false;
+}
+
diff --git a/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.h b/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.h
new file mode 100644
index 0000000..acd594a
--- /dev/null
+++ b/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.h
@@ -0,0 +1,47 @@
+//===-- RegisterContextCoreFreeBSD_x86_64.h ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCoreFreeBSD_x86_64_H_
+#define liblldb_RegisterContextCoreFreeBSD_x86_64_H_
+
+#include "Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h"
+
+using namespace lldb_private;
+
+class RegisterContextCoreFreeBSD_x86_64: public RegisterContextFreeBSD_x86_64
+{
+public:
+ RegisterContextCoreFreeBSD_x86_64 (Thread &thread, const DataExtractor &gpregset,
+ const DataExtractor &fpregset);
+
+ ~RegisterContextCoreFreeBSD_x86_64();
+
+ virtual bool
+ ReadRegister(const RegisterInfo *reg_info, RegisterValue &value);
+
+ bool
+ ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value);
+
+ bool
+ WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ bool
+ HardwareSingleStep(bool enable);
+
+ bool
+ UpdateAfterBreakpoint();
+
+private:
+ uint8_t *m_gpregset;
+};
+
+#endif // #ifndef liblldb_RegisterContextCoreFreeBSD_x86_64_H_
diff --git a/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.cpp b/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.cpp
new file mode 100644
index 0000000..d9e3f6d
--- /dev/null
+++ b/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.cpp
@@ -0,0 +1,68 @@
+//===-- RegisterContextCoreLinux_x86_64.cpp -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Target/Thread.h"
+#include "RegisterContextCoreLinux_x86_64.h"
+
+RegisterContextCoreLinux_x86_64::RegisterContextCoreLinux_x86_64(Thread &thread,
+ const DataExtractor &gpregset,
+ const DataExtractor &fpregset)
+ : RegisterContextLinux_x86_64(thread, 0)
+{
+ size_t size, len;
+
+ size = GetGPRSize();
+ m_gpregset = new uint8_t[size];
+ len = gpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_gpregset);
+ assert(len == size);
+}
+
+RegisterContextCoreLinux_x86_64::~RegisterContextCoreLinux_x86_64()
+{
+ delete [] m_gpregset;
+}
+
+bool
+RegisterContextCoreLinux_x86_64::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value)
+{
+ value = *(uint64_t *)(m_gpregset + reg_info->byte_offset);
+ return true;
+}
+
+bool
+RegisterContextCoreLinux_x86_64::ReadAllRegisterValues(lldb::DataBufferSP &data_sp)
+{
+ return false;
+}
+
+bool
+RegisterContextCoreLinux_x86_64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value)
+{
+ return false;
+}
+
+bool
+RegisterContextCoreLinux_x86_64::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp)
+{
+ return false;
+}
+
+bool
+RegisterContextCoreLinux_x86_64::UpdateAfterBreakpoint()
+{
+ return false;
+}
+
+bool
+RegisterContextCoreLinux_x86_64::HardwareSingleStep(bool enable)
+{
+ return false;
+}
diff --git a/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.h b/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.h
new file mode 100644
index 0000000..9cf545a
--- /dev/null
+++ b/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.h
@@ -0,0 +1,54 @@
+//===-- RegisterContextCoreLinux_x86_64.h ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCoreLinux_x86_64_H_
+#define liblldb_RegisterContextCoreLinux_x86_64_H_
+
+#include "Plugins/Process/POSIX/RegisterContextLinux_x86_64.h"
+
+using namespace lldb_private;
+
+class RegisterContextCoreLinux_x86_64: public RegisterContextLinux_x86_64
+{
+public:
+ RegisterContextCoreLinux_x86_64 (Thread &thread, const DataExtractor &gpregset,
+ const DataExtractor &fpregset);
+
+ ~RegisterContextCoreLinux_x86_64();
+
+ virtual bool
+ ReadRegister(const RegisterInfo *reg_info, RegisterValue &value);
+
+ bool
+ ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value);
+
+ bool
+ WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ bool
+ HardwareSingleStep(bool enable);
+
+ bool
+ UpdateAfterBreakpoint();
+
+protected:
+ bool
+ ReadFPR()
+ {
+ assert(0);
+ }
+
+private:
+ uint8_t *m_gpregset;
+};
+
+#endif // #ifndef liblldb_RegisterContextCoreLinux_x86_64_H_
diff --git a/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/source/Plugins/Process/elf-core/ThreadElfCore.cpp
new file mode 100644
index 0000000..a722966
--- /dev/null
+++ b/source/Plugins/Process/elf-core/ThreadElfCore.cpp
@@ -0,0 +1,176 @@
+//===-- ThreadElfCore.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Unwind.h"
+#include "ProcessPOSIXLog.h"
+
+#include "ThreadElfCore.h"
+#include "ProcessElfCore.h"
+#include "RegisterContextCoreFreeBSD_x86_64.h"
+#include "RegisterContextCoreLinux_x86_64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Construct a Thread object with given data
+//----------------------------------------------------------------------
+ThreadElfCore::ThreadElfCore (Process &process, tid_t tid,
+ const ThreadData &td) :
+ Thread(process, tid),
+ m_thread_name(td.name),
+ m_thread_reg_ctx_sp (),
+ m_signo(td.signo),
+ m_gpregset_data(td.gpregset),
+ m_fpregset_data(td.fpregset)
+{
+}
+
+ThreadElfCore::~ThreadElfCore ()
+{
+ DestroyThread();
+}
+
+void
+ThreadElfCore::RefreshStateAfterStop()
+{
+ GetRegisterContext()->InvalidateIfNeeded (false);
+}
+
+void
+ThreadElfCore::ClearStackFrames ()
+{
+ Unwind *unwinder = GetUnwinder ();
+ if (unwinder)
+ unwinder->Clear();
+ Thread::ClearStackFrames();
+}
+
+RegisterContextSP
+ThreadElfCore::GetRegisterContext ()
+{
+ if (m_reg_context_sp.get() == NULL) {
+ m_reg_context_sp = CreateRegisterContextForFrame (NULL);
+ }
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame)
+{
+ RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex ();
+
+ if (concrete_frame_idx == 0)
+ {
+ if (m_thread_reg_ctx_sp)
+ return m_thread_reg_ctx_sp;
+
+ ProcessElfCore *process = static_cast<ProcessElfCore *>(GetProcess().get());
+ ArchSpec arch = process->GetArchitecture();
+ switch (arch.GetMachine())
+ {
+ case llvm::Triple::x86_64:
+ switch (arch.GetTriple().getOS())
+ {
+ case llvm::Triple::FreeBSD:
+ m_thread_reg_ctx_sp.reset(new RegisterContextCoreFreeBSD_x86_64 (*this, m_gpregset_data, m_fpregset_data));
+ break;
+ case llvm::Triple::Linux:
+ m_thread_reg_ctx_sp.reset(new RegisterContextCoreLinux_x86_64 (*this, m_gpregset_data, m_fpregset_data));
+ break;
+ default:
+ if (log)
+ log->Printf ("elf-core::%s:: OS(%d) not supported",
+ __FUNCTION__, arch.GetTriple().getOS());
+ assert (false && "OS not supported");
+ break;
+ }
+ break;
+ default:
+ if (log)
+ log->Printf ("elf-core::%s:: Architecture(%d) not supported",
+ __FUNCTION__, arch.GetMachine());
+ assert (false && "Architecture not supported");
+ }
+ reg_ctx_sp = m_thread_reg_ctx_sp;
+ }
+ else if (m_unwinder_ap.get())
+ {
+ reg_ctx_sp = m_unwinder_ap->CreateRegisterContextForFrame (frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool
+ThreadElfCore::CalculateStopInfo ()
+{
+ ProcessSP process_sp (GetProcess());
+ if (process_sp)
+ {
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, m_signo));
+ return true;
+ }
+ return false;
+}
+
+//----------------------------------------------------------------
+// Parse PRSTATUS from NOTE entry
+//----------------------------------------------------------------
+ELFLinuxPrStatus::ELFLinuxPrStatus()
+{
+ memset(this, 0, sizeof(ELFLinuxPrStatus));
+}
+
+bool
+ELFLinuxPrStatus::Parse(DataExtractor &data, ArchSpec &arch)
+{
+ ByteOrder byteorder = data.GetByteOrder();
+ size_t len;
+ switch(arch.GetCore())
+ {
+ case ArchSpec::eCore_x86_64_x86_64:
+ len = data.ExtractBytes(0, ELFLINUXPRSTATUS64_SIZE, byteorder, this);
+ return len == ELFLINUXPRSTATUS64_SIZE;
+ default:
+ return false;
+ }
+}
+
+//----------------------------------------------------------------
+// Parse PRPSINFO from NOTE entry
+//----------------------------------------------------------------
+ELFLinuxPrPsInfo::ELFLinuxPrPsInfo()
+{
+ memset(this, 0, sizeof(ELFLinuxPrPsInfo));
+}
+
+bool
+ELFLinuxPrPsInfo::Parse(DataExtractor &data, ArchSpec &arch)
+{
+ ByteOrder byteorder = data.GetByteOrder();
+ size_t len;
+ switch(arch.GetCore())
+ {
+ case ArchSpec::eCore_x86_64_x86_64:
+ len = data.ExtractBytes(0, ELFLINUXPRPSINFO64_SIZE, byteorder, this);
+ return len == ELFLINUXPRPSINFO64_SIZE;
+ default:
+ return false;
+ }
+}
+
diff --git a/source/Plugins/Process/elf-core/ThreadElfCore.h b/source/Plugins/Process/elf-core/ThreadElfCore.h
new file mode 100644
index 0000000..ca6339d
--- /dev/null
+++ b/source/Plugins/Process/elf-core/ThreadElfCore.h
@@ -0,0 +1,174 @@
+//===-- ThreadElfCore.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadElfCore_h_
+#define liblldb_ThreadElfCore_h_
+
+#include <string>
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Core/DataExtractor.h"
+
+struct compat_timeval
+{
+ int64_t tv_sec;
+ int32_t tv_usec;
+};
+
+// PRSTATUS structure's size differs based on architecture.
+// Currently parsing done only for x86-64 architecture by
+// simply reading data from the buffer.
+// The following macros are used to specify the size.
+// Calculating size using sizeof() wont work because of padding.
+#define ELFLINUXPRSTATUS64_SIZE (112)
+#define ELFLINUXPRPSINFO64_SIZE (132)
+
+struct ELFLinuxPrStatus
+{
+ int32_t si_signo;
+ int32_t si_code;
+ int32_t si_errno;
+
+ int16_t pr_cursig;
+
+ uint64_t pr_sigpend;
+ uint64_t pr_sighold;
+
+ uint32_t pr_pid;
+ uint32_t pr_ppid;
+ uint32_t pr_pgrp;
+ uint32_t pr_sid;
+
+ compat_timeval pr_utime;
+ compat_timeval pr_stime;
+ compat_timeval pr_cutime;
+ compat_timeval pr_cstime;
+
+ ELFLinuxPrStatus();
+
+ bool
+ Parse(lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch);
+
+ static size_t
+ GetSize(lldb_private::ArchSpec &arch)
+ {
+ switch(arch.GetCore())
+ {
+ case lldb_private::ArchSpec::eCore_x86_64_x86_64:
+ return ELFLINUXPRSTATUS64_SIZE;
+ default:
+ return 0;
+ }
+ }
+};
+
+struct ELFLinuxPrPsInfo
+{
+ char pr_state;
+ char pr_sname;
+ char pr_zomb;
+ char pr_nice;
+ uint64_t pr_flag;
+ uint32_t pr_uid;
+ uint32_t pr_gid;
+ int32_t pr_pid;
+ int32_t pr_ppid;
+ int32_t pr_pgrp;
+ int32_t pr_sid;
+ char pr_fname[16];
+ char pr_psargs[80];
+
+ ELFLinuxPrPsInfo();
+
+ bool
+ Parse(lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch);
+
+ static size_t
+ GetSize(lldb_private::ArchSpec &arch)
+ {
+ switch(arch.GetCore())
+ {
+ case lldb_private::ArchSpec::eCore_x86_64_x86_64:
+ return ELFLINUXPRPSINFO64_SIZE;
+ default:
+ return 0;
+ }
+ }
+
+};
+
+struct ThreadData
+{
+ lldb_private::DataExtractor gpregset;
+ lldb_private::DataExtractor fpregset;
+ int signo;
+ std::string name;
+};
+
+class ThreadElfCore : public lldb_private::Thread
+{
+public:
+ ThreadElfCore (lldb_private::Process &process, lldb::tid_t tid,
+ const ThreadData &td);
+
+ virtual
+ ~ThreadElfCore ();
+
+ virtual void
+ RefreshStateAfterStop();
+
+ virtual lldb::RegisterContextSP
+ GetRegisterContext ();
+
+ virtual lldb::RegisterContextSP
+ CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ virtual void
+ ClearStackFrames ();
+
+ static bool
+ ThreadIDIsValid (lldb::tid_t thread)
+ {
+ return thread != 0;
+ }
+
+ virtual const char *
+ GetName ()
+ {
+ if (m_thread_name.empty())
+ return NULL;
+ return m_thread_name.c_str();
+ }
+
+ void
+ SetName (const char *name)
+ {
+ if (name && name[0])
+ m_thread_name.assign (name);
+ else
+ m_thread_name.clear();
+ }
+
+protected:
+ //------------------------------------------------------------------
+ // Member variables.
+ //------------------------------------------------------------------
+ std::string m_thread_name;
+ lldb::RegisterContextSP m_thread_reg_ctx_sp;
+
+ int m_signo;
+
+ lldb_private::DataExtractor m_gpregset_data;
+ lldb_private::DataExtractor m_fpregset_data;
+
+ virtual bool CalculateStopInfo();
+
+};
+
+#endif // liblldb_ThreadElfCore_h_
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
new file mode 100644
index 0000000..d7efdf2
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -0,0 +1,643 @@
+//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "GDBRemoteCommunication.h"
+
+// C Includes
+#include <limits.h>
+#include <string.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/Log.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Target/Process.h"
+
+// Project includes
+#include "ProcessGDBRemoteLog.h"
+
+#define DEBUGSERVER_BASENAME "debugserver"
+
+using namespace lldb;
+using namespace lldb_private;
+
+GDBRemoteCommunication::History::History (uint32_t size) :
+ m_packets(),
+ m_curr_idx (0),
+ m_total_packet_count (0),
+ m_dumped_to_log (false)
+{
+ m_packets.resize(size);
+}
+
+GDBRemoteCommunication::History::~History ()
+{
+}
+
+void
+GDBRemoteCommunication::History::AddPacket (char packet_char,
+ PacketType type,
+ uint32_t bytes_transmitted)
+{
+ const size_t size = m_packets.size();
+ if (size > 0)
+ {
+ const uint32_t idx = GetNextIndex();
+ m_packets[idx].packet.assign (1, packet_char);
+ m_packets[idx].type = type;
+ m_packets[idx].bytes_transmitted = bytes_transmitted;
+ m_packets[idx].packet_idx = m_total_packet_count;
+ m_packets[idx].tid = Host::GetCurrentThreadID();
+ }
+}
+
+void
+GDBRemoteCommunication::History::AddPacket (const std::string &src,
+ uint32_t src_len,
+ PacketType type,
+ uint32_t bytes_transmitted)
+{
+ const size_t size = m_packets.size();
+ if (size > 0)
+ {
+ const uint32_t idx = GetNextIndex();
+ m_packets[idx].packet.assign (src, 0, src_len);
+ m_packets[idx].type = type;
+ m_packets[idx].bytes_transmitted = bytes_transmitted;
+ m_packets[idx].packet_idx = m_total_packet_count;
+ m_packets[idx].tid = Host::GetCurrentThreadID();
+ }
+}
+
+void
+GDBRemoteCommunication::History::Dump (lldb_private::Stream &strm) const
+{
+ const uint32_t size = GetNumPacketsInHistory ();
+ const uint32_t first_idx = GetFirstSavedPacketIndex ();
+ const uint32_t stop_idx = m_curr_idx + size;
+ for (uint32_t i = first_idx; i < stop_idx; ++i)
+ {
+ const uint32_t idx = NormalizeIndex (i);
+ const Entry &entry = m_packets[idx];
+ if (entry.type == ePacketTypeInvalid || entry.packet.empty())
+ break;
+ strm.Printf ("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n",
+ entry.packet_idx,
+ entry.tid,
+ entry.bytes_transmitted,
+ (entry.type == ePacketTypeSend) ? "send" : "read",
+ entry.packet.c_str());
+ }
+}
+
+void
+GDBRemoteCommunication::History::Dump (lldb_private::Log *log) const
+{
+ if (log && !m_dumped_to_log)
+ {
+ m_dumped_to_log = true;
+ const uint32_t size = GetNumPacketsInHistory ();
+ const uint32_t first_idx = GetFirstSavedPacketIndex ();
+ const uint32_t stop_idx = m_curr_idx + size;
+ for (uint32_t i = first_idx; i < stop_idx; ++i)
+ {
+ const uint32_t idx = NormalizeIndex (i);
+ const Entry &entry = m_packets[idx];
+ if (entry.type == ePacketTypeInvalid || entry.packet.empty())
+ break;
+ log->Printf ("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s",
+ entry.packet_idx,
+ entry.tid,
+ entry.bytes_transmitted,
+ (entry.type == ePacketTypeSend) ? "send" : "read",
+ entry.packet.c_str());
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+// GDBRemoteCommunication constructor
+//----------------------------------------------------------------------
+GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name,
+ const char *listener_name,
+ bool is_platform) :
+ Communication(comm_name),
+ m_packet_timeout (1),
+ m_sequence_mutex (Mutex::eMutexTypeRecursive),
+ m_public_is_running (false),
+ m_private_is_running (false),
+ m_history (512),
+ m_send_acks (true),
+ m_is_platform (is_platform)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+GDBRemoteCommunication::~GDBRemoteCommunication()
+{
+ if (IsConnected())
+ {
+ Disconnect();
+ }
+}
+
+char
+GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length)
+{
+ int checksum = 0;
+
+ for (size_t i = 0; i < payload_length; ++i)
+ checksum += payload[i];
+
+ return checksum & 255;
+}
+
+size_t
+GDBRemoteCommunication::SendAck ()
+{
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ char ch = '+';
+ const size_t bytes_written = Write (&ch, 1, status, NULL);
+ if (log)
+ log->Printf ("<%4zu> send packet: %c", bytes_written, ch);
+ m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written);
+ return bytes_written;
+}
+
+size_t
+GDBRemoteCommunication::SendNack ()
+{
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ char ch = '-';
+ const size_t bytes_written = Write (&ch, 1, status, NULL);
+ if (log)
+ log->Printf ("<%4zu> send packet: %c", bytes_written, ch);
+ m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written);
+ return bytes_written;
+}
+
+size_t
+GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length)
+{
+ Mutex::Locker locker(m_sequence_mutex);
+ return SendPacketNoLock (payload, payload_length);
+}
+
+size_t
+GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length)
+{
+ if (IsConnected())
+ {
+ StreamString packet(0, 4, eByteOrderBig);
+
+ packet.PutChar('$');
+ packet.Write (payload, payload_length);
+ packet.PutChar('#');
+ packet.PutHex8(CalculcateChecksum (payload, payload_length));
+
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL);
+ if (log)
+ {
+ // If logging was just enabled and we have history, then dump out what
+ // we have to the log so we get the historical context. The Dump() call that
+ // logs all of the packet will set a boolean so that we don't dump this more
+ // than once
+ if (!m_history.DidDumpToLog ())
+ m_history.Dump (log);
+
+ log->Printf ("<%4zu> send packet: %.*s", bytes_written, (int)packet.GetSize(), packet.GetData());
+ }
+
+ m_history.AddPacket (packet.GetString(), packet.GetSize(), History::ePacketTypeSend, bytes_written);
+
+
+ if (bytes_written == packet.GetSize())
+ {
+ if (GetSendAcks ())
+ {
+ if (GetAck () != '+')
+ {
+ if (log)
+ log->Printf("get ack failed...");
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData());
+ }
+ return bytes_written;
+ }
+ return 0;
+}
+
+char
+GDBRemoteCommunication::GetAck ()
+{
+ StringExtractorGDBRemote packet;
+ if (WaitForPacketWithTimeoutMicroSecondsNoLock (packet, GetPacketTimeoutInMicroSeconds ()) == 1)
+ return packet.GetChar();
+ return 0;
+}
+
+bool
+GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker, const char *failure_message)
+{
+ if (IsRunning())
+ return locker.TryLock (m_sequence_mutex, failure_message);
+
+ locker.Lock (m_sequence_mutex);
+ return true;
+}
+
+
+bool
+GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr)
+{
+ return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL);
+}
+
+size_t
+GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec)
+{
+ uint8_t buffer[8192];
+ Error error;
+
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE));
+
+ // Check for a packet from our cache first without trying any reading...
+ if (CheckForPacket (NULL, 0, packet))
+ return packet.GetStringRef().size();
+
+ bool timed_out = false;
+ while (IsConnected() && !timed_out)
+ {
+ lldb::ConnectionStatus status = eConnectionStatusNoConnection;
+ size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error);
+
+ if (log)
+ log->Printf ("%s: Read (buffer, (sizeof(buffer), timeout_usec = 0x%x, status = %s, error = %s) => bytes_read = %" PRIu64,
+ __PRETTY_FUNCTION__,
+ timeout_usec,
+ Communication::ConnectionStatusAsCString (status),
+ error.AsCString(),
+ (uint64_t)bytes_read);
+
+ if (bytes_read > 0)
+ {
+ if (CheckForPacket (buffer, bytes_read, packet))
+ return packet.GetStringRef().size();
+ }
+ else
+ {
+ switch (status)
+ {
+ case eConnectionStatusTimedOut:
+ timed_out = true;
+ break;
+ case eConnectionStatusSuccess:
+ //printf ("status = success but error = %s\n", error.AsCString("<invalid>"));
+ break;
+
+ case eConnectionStatusEndOfFile:
+ case eConnectionStatusNoConnection:
+ case eConnectionStatusLostConnection:
+ case eConnectionStatusError:
+ Disconnect();
+ break;
+ }
+ }
+ }
+ packet.Clear ();
+ return 0;
+}
+
+bool
+GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet)
+{
+ // Put the packet data into the buffer in a thread safe fashion
+ Mutex::Locker locker(m_bytes_mutex);
+
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
+
+ if (src && src_len > 0)
+ {
+ if (log && log->GetVerbose())
+ {
+ StreamString s;
+ log->Printf ("GDBRemoteCommunication::%s adding %u bytes: %.*s",
+ __FUNCTION__,
+ (uint32_t)src_len,
+ (uint32_t)src_len,
+ src);
+ }
+ m_bytes.append ((const char *)src, src_len);
+ }
+
+ // Parse up the packets into gdb remote packets
+ if (!m_bytes.empty())
+ {
+ // 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 content_start = 0;
+ size_t content_length = 0;
+ size_t total_length = 0;
+ size_t checksum_idx = std::string::npos;
+
+ switch (m_bytes[0])
+ {
+ case '+': // Look for ack
+ case '-': // Look for cancel
+ case '\x03': // ^C to halt target
+ content_length = total_length = 1; // The command is one byte long...
+ break;
+
+ case '$':
+ // Look for a standard gdb packet?
+ {
+ size_t hash_pos = m_bytes.find('#');
+ if (hash_pos != std::string::npos)
+ {
+ if (hash_pos + 2 < m_bytes.size())
+ {
+ checksum_idx = hash_pos + 1;
+ // Skip the dollar sign
+ content_start = 1;
+ // Don't include the # in the content or the $ in the content length
+ content_length = hash_pos - 1;
+
+ total_length = hash_pos + 3; // Skip the # and the two hex checksum bytes
+ }
+ else
+ {
+ // Checksum bytes aren't all here yet
+ content_length = std::string::npos;
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ // We have an unexpected byte and we need to flush all bad
+ // data that is in m_bytes, so we need to find the first
+ // byte that is a '+' (ACK), '-' (NACK), \x03 (CTRL+C interrupt),
+ // or '$' character (start of packet header) or of course,
+ // the end of the data in m_bytes...
+ const size_t bytes_len = m_bytes.size();
+ bool done = false;
+ uint32_t idx;
+ for (idx = 1; !done && idx < bytes_len; ++idx)
+ {
+ switch (m_bytes[idx])
+ {
+ case '+':
+ case '-':
+ case '\x03':
+ case '$':
+ done = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (log)
+ log->Printf ("GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'",
+ __FUNCTION__, idx, idx, m_bytes.c_str());
+ m_bytes.erase(0, idx);
+ }
+ break;
+ }
+
+ if (content_length == std::string::npos)
+ {
+ packet.Clear();
+ return false;
+ }
+ else if (total_length > 0)
+ {
+
+ // We have a valid packet...
+ assert (content_length <= m_bytes.size());
+ assert (total_length <= m_bytes.size());
+ assert (content_length <= total_length);
+
+ bool success = true;
+ std::string &packet_str = packet.GetStringRef();
+
+
+ if (log)
+ {
+ // If logging was just enabled and we have history, then dump out what
+ // we have to the log so we get the historical context. The Dump() call that
+ // logs all of the packet will set a boolean so that we don't dump this more
+ // than once
+ if (!m_history.DidDumpToLog ())
+ m_history.Dump (log);
+
+ log->Printf ("<%4zu> read packet: %.*s", total_length, (int)(total_length), m_bytes.c_str());
+ }
+
+ m_history.AddPacket (m_bytes.c_str(), total_length, History::ePacketTypeRecv, total_length);
+
+ packet_str.assign (m_bytes, content_start, content_length);
+
+ if (m_bytes[0] == '$')
+ {
+ assert (checksum_idx < m_bytes.size());
+ if (::isxdigit (m_bytes[checksum_idx+0]) ||
+ ::isxdigit (m_bytes[checksum_idx+1]))
+ {
+ if (GetSendAcks ())
+ {
+ const char *packet_checksum_cstr = &m_bytes[checksum_idx];
+ char packet_checksum = strtol (packet_checksum_cstr, NULL, 16);
+ char actual_checksum = CalculcateChecksum (packet_str.c_str(), packet_str.size());
+ success = packet_checksum == actual_checksum;
+ if (!success)
+ {
+ if (log)
+ log->Printf ("error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x",
+ (int)(total_length),
+ m_bytes.c_str(),
+ (uint8_t)packet_checksum,
+ (uint8_t)actual_checksum);
+ }
+ // Send the ack or nack if needed
+ if (!success)
+ SendNack();
+ else
+ SendAck();
+ }
+ }
+ else
+ {
+ success = false;
+ if (log)
+ log->Printf ("error: invalid checksum in packet: '%s'\n", m_bytes.c_str());
+ }
+ }
+
+ m_bytes.erase(0, total_length);
+ packet.SetFilePos(0);
+ return success;
+ }
+ }
+ packet.Clear();
+ return false;
+}
+
+Error
+GDBRemoteCommunication::StartDebugserverProcess (const char *debugserver_url,
+ const char *unix_socket_name, // For handshaking
+ lldb_private::ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ // If we locate debugserver, keep that located version around
+ static FileSpec g_debugserver_file_spec;
+
+ // This function will fill in the launch information for the debugserver
+ // instance that gets launched.
+ launch_info.Clear();
+
+ char debugserver_path[PATH_MAX];
+ FileSpec &debugserver_file_spec = launch_info.GetExecutableFile();
+
+ // Always check to see if we have an environment override for the path
+ // to the debugserver to use and use it if we do.
+ const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH");
+ if (env_debugserver_path)
+ debugserver_file_spec.SetFile (env_debugserver_path, false);
+ else
+ debugserver_file_spec = g_debugserver_file_spec;
+ bool debugserver_exists = debugserver_file_spec.Exists();
+ if (!debugserver_exists)
+ {
+ // The debugserver binary is in the LLDB.framework/Resources
+ // directory.
+ if (Host::GetLLDBPath (ePathTypeSupportExecutableDir, debugserver_file_spec))
+ {
+ debugserver_file_spec.GetFilename().SetCString(DEBUGSERVER_BASENAME);
+ debugserver_exists = debugserver_file_spec.Exists();
+ if (debugserver_exists)
+ {
+ g_debugserver_file_spec = debugserver_file_spec;
+ }
+ else
+ {
+ g_debugserver_file_spec.Clear();
+ debugserver_file_spec.Clear();
+ }
+ }
+ }
+
+ if (debugserver_exists)
+ {
+ debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path));
+
+ Args &debugserver_args = launch_info.GetArguments();
+ debugserver_args.Clear();
+ char arg_cstr[PATH_MAX];
+
+ // Start args with "debugserver /file/path -r --"
+ debugserver_args.AppendArgument(debugserver_path);
+ debugserver_args.AppendArgument(debugserver_url);
+ // use native registers, not the GDB registers
+ debugserver_args.AppendArgument("--native-regs");
+ // make debugserver run in its own session so signals generated by
+ // special terminal key sequences (^C) don't affect debugserver
+ debugserver_args.AppendArgument("--setsid");
+
+ if (unix_socket_name && unix_socket_name[0])
+ {
+ debugserver_args.AppendArgument("--unix-socket");
+ debugserver_args.AppendArgument(unix_socket_name);
+ }
+
+ const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE");
+ if (env_debugserver_log_file)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file);
+ debugserver_args.AppendArgument(arg_cstr);
+ }
+
+ const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
+ if (env_debugserver_log_flags)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags);
+ debugserver_args.AppendArgument(arg_cstr);
+ }
+ // debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt");
+ // debugserver_args.AppendArgument("--log-flags=0x802e0e");
+
+ // We currently send down all arguments, attach pids, or attach
+ // process names in dedicated GDB server packets, so we don't need
+ // to pass them as arguments. This is currently because of all the
+ // things we need to setup prior to launching: the environment,
+ // current working dir, file actions, etc.
+#if 0
+ // Now append the program arguments
+ if (inferior_argv)
+ {
+ // Terminate the debugserver args so we can now append the inferior args
+ debugserver_args.AppendArgument("--");
+
+ for (int i = 0; inferior_argv[i] != NULL; ++i)
+ debugserver_args.AppendArgument (inferior_argv[i]);
+ }
+ else if (attach_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid);
+ debugserver_args.AppendArgument (arg_cstr);
+ }
+ else if (attach_name && attach_name[0])
+ {
+ if (wait_for_launch)
+ debugserver_args.AppendArgument ("--waitfor");
+ else
+ debugserver_args.AppendArgument ("--attach");
+ debugserver_args.AppendArgument (attach_name);
+ }
+#endif
+
+ // Close STDIN, STDOUT and STDERR. We might need to redirect them
+ // to "/dev/null" if we run into any problems.
+// launch_info.AppendCloseFileAction (STDIN_FILENO);
+// launch_info.AppendCloseFileAction (STDOUT_FILENO);
+// launch_info.AppendCloseFileAction (STDERR_FILENO);
+
+ error = Host::LaunchProcess(launch_info);
+ }
+ else
+ {
+ error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME );
+ }
+ return error;
+}
+
+void
+GDBRemoteCommunication::DumpHistory(Stream &strm)
+{
+ m_history.Dump (strm);
+}
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
new file mode 100644
index 0000000..a107795
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -0,0 +1,268 @@
+//===-- GDBRemoteCommunication.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunication_h_
+#define liblldb_GDBRemoteCommunication_h_
+
+// C Includes
+// C++ Includes
+#include <list>
+#include <string>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-public.h"
+#include "lldb/Core/Communication.h"
+#include "lldb/Core/Listener.h"
+#include "lldb/Host/Mutex.h"
+#include "lldb/Host/Predicate.h"
+#include "lldb/Host/TimeValue.h"
+
+#include "Utility/StringExtractorGDBRemote.h"
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunication : public lldb_private::Communication
+{
+public:
+ enum
+ {
+ eBroadcastBitRunPacketSent = kLoUserBroadcastBit
+ };
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ GDBRemoteCommunication(const char *comm_name,
+ const char *listener_name,
+ bool is_platform);
+
+ virtual
+ ~GDBRemoteCommunication();
+
+ char
+ GetAck ();
+
+ size_t
+ SendAck ();
+
+ size_t
+ SendNack ();
+
+ char
+ CalculcateChecksum (const char *payload,
+ size_t payload_length);
+
+ bool
+ GetSequenceMutex (lldb_private::Mutex::Locker& locker, const char *failure_message = NULL);
+
+ bool
+ CheckForPacket (const uint8_t *src,
+ size_t src_len,
+ StringExtractorGDBRemote &packet);
+ bool
+ IsRunning() const
+ {
+ return m_public_is_running.GetValue();
+ }
+
+ bool
+ GetSendAcks ()
+ {
+ return m_send_acks;
+ }
+
+ //------------------------------------------------------------------
+ // Client and server must implement these pure virtual functions
+ //------------------------------------------------------------------
+ virtual bool
+ GetThreadSuffixSupported () = 0;
+
+ //------------------------------------------------------------------
+ // Set the global packet timeout.
+ //
+ // For clients, this is the timeout that gets used when sending
+ // packets and waiting for responses. For servers, this might not
+ // get used, and if it doesn't this should be moved to the
+ // GDBRemoteCommunicationClient.
+ //------------------------------------------------------------------
+ uint32_t
+ SetPacketTimeout (uint32_t packet_timeout)
+ {
+ const uint32_t old_packet_timeout = m_packet_timeout;
+ m_packet_timeout = packet_timeout;
+ return old_packet_timeout;
+ }
+
+ uint32_t
+ GetPacketTimeoutInMicroSeconds () const
+ {
+ return m_packet_timeout * lldb_private::TimeValue::MicroSecPerSec;
+ }
+ //------------------------------------------------------------------
+ // Start a debugserver instance on the current host using the
+ // supplied connection URL.
+ //------------------------------------------------------------------
+ lldb_private::Error
+ StartDebugserverProcess (const char *connect_url,
+ const char *unix_socket_name,
+ lldb_private::ProcessLaunchInfo &launch_info);
+
+ void
+ DumpHistory(lldb_private::Stream &strm);
+
+protected:
+
+ class History
+ {
+ public:
+ enum PacketType
+ {
+ ePacketTypeInvalid = 0,
+ ePacketTypeSend,
+ ePacketTypeRecv
+ };
+
+ struct Entry
+ {
+ Entry() :
+ packet(),
+ type (ePacketTypeInvalid),
+ bytes_transmitted (0),
+ packet_idx (0),
+ tid (LLDB_INVALID_THREAD_ID)
+ {
+ }
+
+ void
+ Clear ()
+ {
+ packet.clear();
+ type = ePacketTypeInvalid;
+ bytes_transmitted = 0;
+ packet_idx = 0;
+ tid = LLDB_INVALID_THREAD_ID;
+ }
+ std::string packet;
+ PacketType type;
+ uint32_t bytes_transmitted;
+ uint32_t packet_idx;
+ lldb::tid_t tid;
+ };
+
+ History (uint32_t size);
+
+ ~History ();
+
+ // For single char packets for ack, nack and /x03
+ void
+ AddPacket (char packet_char,
+ PacketType type,
+ uint32_t bytes_transmitted);
+ void
+ AddPacket (const std::string &src,
+ uint32_t src_len,
+ PacketType type,
+ uint32_t bytes_transmitted);
+
+ void
+ Dump (lldb_private::Stream &strm) const;
+
+ void
+ Dump (lldb_private::Log *log) const;
+
+ bool
+ DidDumpToLog () const
+ {
+ return m_dumped_to_log;
+ }
+
+protected:
+ uint32_t
+ GetFirstSavedPacketIndex () const
+ {
+ if (m_total_packet_count < m_packets.size())
+ return 0;
+ else
+ return m_curr_idx + 1;
+ }
+
+ uint32_t
+ GetNumPacketsInHistory () const
+ {
+ if (m_total_packet_count < m_packets.size())
+ return m_total_packet_count;
+ else
+ return (uint32_t)m_packets.size();
+ }
+
+ uint32_t
+ GetNextIndex()
+ {
+ ++m_total_packet_count;
+ const uint32_t idx = m_curr_idx;
+ m_curr_idx = NormalizeIndex(idx + 1);
+ return idx;
+ }
+
+ uint32_t
+ NormalizeIndex (uint32_t i) const
+ {
+ return i % m_packets.size();
+ }
+
+
+ std::vector<Entry> m_packets;
+ uint32_t m_curr_idx;
+ uint32_t m_total_packet_count;
+ mutable bool m_dumped_to_log;
+ };
+
+ size_t
+ SendPacket (const char *payload,
+ size_t payload_length);
+
+ size_t
+ SendPacketNoLock (const char *payload,
+ size_t payload_length);
+
+ size_t
+ WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &response,
+ uint32_t timeout_usec);
+
+ bool
+ WaitForNotRunningPrivate (const lldb_private::TimeValue *timeout_ptr);
+
+ //------------------------------------------------------------------
+ // Classes that inherit from GDBRemoteCommunication can see and modify these
+ //------------------------------------------------------------------
+ uint32_t m_packet_timeout;
+#ifdef LLDB_CONFIGURATION_DEBUG
+ lldb_private::TrackingMutex m_sequence_mutex;
+#else
+ lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time
+#endif
+ lldb_private::Predicate<bool> m_public_is_running;
+ lldb_private::Predicate<bool> m_private_is_running;
+ History m_history;
+ bool m_send_acks;
+ bool m_is_platform; // Set to true if this class represents a platform,
+ // false if this class represents a debug session for
+ // a single process
+
+
+
+
+private:
+ //------------------------------------------------------------------
+ // For GDBRemoteCommunication only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunication);
+};
+
+#endif // liblldb_GDBRemoteCommunication_h_
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
new file mode 100644
index 0000000..ca594a8
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -0,0 +1,2348 @@
+//===-- GDBRemoteCommunicationClient.cpp ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "GDBRemoteCommunicationClient.h"
+
+// C Includes
+// C++ Includes
+#include <sstream>
+
+// Other libraries and framework includes
+#include "llvm/ADT/Triple.h"
+#include "lldb/Interpreter/Args.h"
+#include "lldb/Core/ConnectionFileDescriptor.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Host/Endian.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/TimeValue.h"
+
+// Project includes
+#include "Utility/StringExtractorGDBRemote.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// GDBRemoteCommunicationClient constructor
+//----------------------------------------------------------------------
+GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) :
+ GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet", is_platform),
+ m_supports_not_sending_acks (eLazyBoolCalculate),
+ m_supports_thread_suffix (eLazyBoolCalculate),
+ m_supports_threads_in_stop_reply (eLazyBoolCalculate),
+ m_supports_vCont_all (eLazyBoolCalculate),
+ m_supports_vCont_any (eLazyBoolCalculate),
+ m_supports_vCont_c (eLazyBoolCalculate),
+ m_supports_vCont_C (eLazyBoolCalculate),
+ m_supports_vCont_s (eLazyBoolCalculate),
+ m_supports_vCont_S (eLazyBoolCalculate),
+ m_qHostInfo_is_valid (eLazyBoolCalculate),
+ m_qProcessInfo_is_valid (eLazyBoolCalculate),
+ m_supports_alloc_dealloc_memory (eLazyBoolCalculate),
+ m_supports_memory_region_info (eLazyBoolCalculate),
+ m_supports_watchpoint_support_info (eLazyBoolCalculate),
+ m_supports_detach_stay_stopped (eLazyBoolCalculate),
+ m_watchpoints_trigger_after_instruction(eLazyBoolCalculate),
+ m_attach_or_wait_reply(eLazyBoolCalculate),
+ m_prepare_for_reg_writing_reply (eLazyBoolCalculate),
+ m_supports_qProcessInfoPID (true),
+ m_supports_qfProcessInfo (true),
+ m_supports_qUserName (true),
+ m_supports_qGroupName (true),
+ m_supports_qThreadStopInfo (true),
+ m_supports_z0 (true),
+ m_supports_z1 (true),
+ m_supports_z2 (true),
+ m_supports_z3 (true),
+ m_supports_z4 (true),
+ m_curr_tid (LLDB_INVALID_THREAD_ID),
+ m_curr_tid_run (LLDB_INVALID_THREAD_ID),
+ m_num_supported_hardware_watchpoints (0),
+ m_async_mutex (Mutex::eMutexTypeRecursive),
+ m_async_packet_predicate (false),
+ m_async_packet (),
+ m_async_response (),
+ m_async_signal (-1),
+ m_thread_id_to_used_usec_map (),
+ m_host_arch(),
+ m_process_arch(),
+ m_os_version_major (UINT32_MAX),
+ m_os_version_minor (UINT32_MAX),
+ m_os_version_update (UINT32_MAX)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient()
+{
+ if (IsConnected())
+ Disconnect();
+}
+
+bool
+GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr)
+{
+ // Start the read thread after we send the handshake ack since if we
+ // fail to send the handshake ack, there is no reason to continue...
+ if (SendAck())
+ return true;
+
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to send the handshake ack");
+ return false;
+}
+
+void
+GDBRemoteCommunicationClient::QueryNoAckModeSupported ()
+{
+ if (m_supports_not_sending_acks == eLazyBoolCalculate)
+ {
+ m_send_acks = true;
+ m_supports_not_sending_acks = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false))
+ {
+ if (response.IsOKResponse())
+ {
+ m_send_acks = false;
+ m_supports_not_sending_acks = eLazyBoolYes;
+ }
+ }
+ }
+}
+
+void
+GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported ()
+{
+ if (m_supports_threads_in_stop_reply == eLazyBoolCalculate)
+ {
+ m_supports_threads_in_stop_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response, false))
+ {
+ if (response.IsOKResponse())
+ m_supports_threads_in_stop_reply = eLazyBoolYes;
+ }
+ }
+}
+
+bool
+GDBRemoteCommunicationClient::GetVAttachOrWaitSupported ()
+{
+ if (m_attach_or_wait_reply == eLazyBoolCalculate)
+ {
+ m_attach_or_wait_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response, false))
+ {
+ if (response.IsOKResponse())
+ m_attach_or_wait_reply = eLazyBoolYes;
+ }
+ }
+ if (m_attach_or_wait_reply == eLazyBoolYes)
+ return true;
+ else
+ return false;
+}
+
+bool
+GDBRemoteCommunicationClient::GetSyncThreadStateSupported ()
+{
+ if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate)
+ {
+ m_prepare_for_reg_writing_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response, false))
+ {
+ if (response.IsOKResponse())
+ m_prepare_for_reg_writing_reply = eLazyBoolYes;
+ }
+ }
+ if (m_prepare_for_reg_writing_reply == eLazyBoolYes)
+ return true;
+ else
+ return false;
+}
+
+
+void
+GDBRemoteCommunicationClient::ResetDiscoverableSettings()
+{
+ m_supports_not_sending_acks = eLazyBoolCalculate;
+ m_supports_thread_suffix = eLazyBoolCalculate;
+ m_supports_threads_in_stop_reply = eLazyBoolCalculate;
+ m_supports_vCont_c = eLazyBoolCalculate;
+ m_supports_vCont_C = eLazyBoolCalculate;
+ m_supports_vCont_s = eLazyBoolCalculate;
+ m_supports_vCont_S = eLazyBoolCalculate;
+ m_qHostInfo_is_valid = eLazyBoolCalculate;
+ m_qProcessInfo_is_valid = eLazyBoolCalculate;
+ m_supports_alloc_dealloc_memory = eLazyBoolCalculate;
+ m_supports_memory_region_info = eLazyBoolCalculate;
+ m_prepare_for_reg_writing_reply = eLazyBoolCalculate;
+ m_attach_or_wait_reply = eLazyBoolCalculate;
+
+ m_supports_qProcessInfoPID = true;
+ m_supports_qfProcessInfo = true;
+ m_supports_qUserName = true;
+ m_supports_qGroupName = true;
+ m_supports_qThreadStopInfo = true;
+ m_supports_z0 = true;
+ m_supports_z1 = true;
+ m_supports_z2 = true;
+ m_supports_z3 = true;
+ m_supports_z4 = true;
+ m_host_arch.Clear();
+ m_process_arch.Clear();
+}
+
+
+bool
+GDBRemoteCommunicationClient::GetThreadSuffixSupported ()
+{
+ if (m_supports_thread_suffix == eLazyBoolCalculate)
+ {
+ StringExtractorGDBRemote response;
+ m_supports_thread_suffix = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response, false))
+ {
+ if (response.IsOKResponse())
+ m_supports_thread_suffix = eLazyBoolYes;
+ }
+ }
+ return m_supports_thread_suffix;
+}
+bool
+GDBRemoteCommunicationClient::GetVContSupported (char flavor)
+{
+ if (m_supports_vCont_c == eLazyBoolCalculate)
+ {
+ StringExtractorGDBRemote response;
+ m_supports_vCont_any = eLazyBoolNo;
+ m_supports_vCont_all = eLazyBoolNo;
+ m_supports_vCont_c = eLazyBoolNo;
+ m_supports_vCont_C = eLazyBoolNo;
+ m_supports_vCont_s = eLazyBoolNo;
+ m_supports_vCont_S = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("vCont?", response, false))
+ {
+ const char *response_cstr = response.GetStringRef().c_str();
+ if (::strstr (response_cstr, ";c"))
+ m_supports_vCont_c = eLazyBoolYes;
+
+ if (::strstr (response_cstr, ";C"))
+ m_supports_vCont_C = eLazyBoolYes;
+
+ if (::strstr (response_cstr, ";s"))
+ m_supports_vCont_s = eLazyBoolYes;
+
+ if (::strstr (response_cstr, ";S"))
+ m_supports_vCont_S = eLazyBoolYes;
+
+ if (m_supports_vCont_c == eLazyBoolYes &&
+ m_supports_vCont_C == eLazyBoolYes &&
+ m_supports_vCont_s == eLazyBoolYes &&
+ m_supports_vCont_S == eLazyBoolYes)
+ {
+ m_supports_vCont_all = eLazyBoolYes;
+ }
+
+ if (m_supports_vCont_c == eLazyBoolYes ||
+ m_supports_vCont_C == eLazyBoolYes ||
+ m_supports_vCont_s == eLazyBoolYes ||
+ m_supports_vCont_S == eLazyBoolYes)
+ {
+ m_supports_vCont_any = eLazyBoolYes;
+ }
+ }
+ }
+
+ switch (flavor)
+ {
+ case 'a': return m_supports_vCont_any;
+ case 'A': return m_supports_vCont_all;
+ case 'c': return m_supports_vCont_c;
+ case 'C': return m_supports_vCont_C;
+ case 's': return m_supports_vCont_s;
+ case 'S': return m_supports_vCont_S;
+ default: break;
+ }
+ return false;
+}
+
+
+size_t
+GDBRemoteCommunicationClient::SendPacketAndWaitForResponse
+(
+ const char *payload,
+ StringExtractorGDBRemote &response,
+ bool send_async
+)
+{
+ return SendPacketAndWaitForResponse (payload,
+ ::strlen (payload),
+ response,
+ send_async);
+}
+
+size_t
+GDBRemoteCommunicationClient::SendPacketAndWaitForResponse
+(
+ const char *payload,
+ size_t payload_length,
+ StringExtractorGDBRemote &response,
+ bool send_async
+)
+{
+ Mutex::Locker locker;
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
+ size_t response_len = 0;
+ if (GetSequenceMutex (locker))
+ {
+ if (SendPacketNoLock (payload, payload_length))
+ response_len = WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ());
+ else
+ {
+ if (log)
+ log->Printf("error: failed to send '%*s'", (int) payload_length, payload);
+ }
+ }
+ else
+ {
+ if (send_async)
+ {
+ if (IsRunning())
+ {
+ Mutex::Locker async_locker (m_async_mutex);
+ m_async_packet.assign(payload, payload_length);
+ m_async_packet_predicate.SetValue (true, eBroadcastNever);
+
+ if (log)
+ log->Printf ("async: async packet = %s", m_async_packet.c_str());
+
+ bool timed_out = false;
+ if (SendInterrupt(locker, 2, timed_out))
+ {
+ if (m_interrupt_sent)
+ {
+ m_interrupt_sent = false;
+ TimeValue timeout_time;
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds (m_packet_timeout);
+
+ if (log)
+ log->Printf ("async: sent interrupt");
+
+ if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out))
+ {
+ if (log)
+ log->Printf ("async: got response");
+
+ // Swap the response buffer to avoid malloc and string copy
+ response.GetStringRef().swap (m_async_response.GetStringRef());
+ response_len = response.GetStringRef().size();
+ }
+ else
+ {
+ if (log)
+ log->Printf ("async: timed out waiting for response");
+ }
+
+ // Make sure we wait until the continue packet has been sent again...
+ if (m_private_is_running.WaitForValueEqualTo (true, &timeout_time, &timed_out))
+ {
+ if (log)
+ {
+ if (timed_out)
+ log->Printf ("async: timed out waiting for process to resume, but process was resumed");
+ else
+ log->Printf ("async: async packet sent");
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("async: timed out waiting for process to resume");
+ }
+ }
+ else
+ {
+ // We had a racy condition where we went to send the interrupt
+ // yet we were able to get the lock, so the process must have
+ // just stopped?
+ if (log)
+ log->Printf ("async: got lock without sending interrupt");
+ // Send the packet normally since we got the lock
+ if (SendPacketNoLock (payload, payload_length))
+ response_len = WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ());
+ else
+ {
+ if (log)
+ log->Printf("error: failed to send '%*s'", (int) payload_length, payload);
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("async: failed to interrupt");
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("async: not running, async is ignored");
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf("error: failed to get packet sequence mutex, not sending packet '%*s'", (int) payload_length, payload);
+ }
+ }
+ if (response_len == 0)
+ {
+ if (log)
+ log->Printf("error: failed to get response for '%*s'", (int) payload_length, payload);
+ }
+ return response_len;
+}
+
+static const char *end_delimiter = "--end--;";
+static const int end_delimiter_len = 8;
+
+std::string
+GDBRemoteCommunicationClient::HarmonizeThreadIdsForProfileData
+( ProcessGDBRemote *process,
+ StringExtractorGDBRemote& profileDataExtractor
+)
+{
+ std::map<uint64_t, uint32_t> new_thread_id_to_used_usec_map;
+ std::stringstream final_output;
+ std::string name, value;
+
+ // Going to assuming thread_used_usec comes first, else bail out.
+ while (profileDataExtractor.GetNameColonValue(name, value))
+ {
+ if (name.compare("thread_used_id") == 0)
+ {
+ StringExtractor threadIDHexExtractor(value.c_str());
+ uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0);
+
+ bool has_used_usec = false;
+ uint32_t curr_used_usec = 0;
+ std::string usec_name, usec_value;
+ uint32_t input_file_pos = profileDataExtractor.GetFilePos();
+ if (profileDataExtractor.GetNameColonValue(usec_name, usec_value))
+ {
+ if (usec_name.compare("thread_used_usec") == 0)
+ {
+ has_used_usec = true;
+ curr_used_usec = strtoull(usec_value.c_str(), NULL, 0);
+ }
+ else
+ {
+ // We didn't find what we want, it is probably
+ // an older version. Bail out.
+ profileDataExtractor.SetFilePos(input_file_pos);
+ }
+ }
+
+ if (has_used_usec)
+ {
+ uint32_t prev_used_usec = 0;
+ std::map<uint64_t, uint32_t>::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id);
+ if (iterator != m_thread_id_to_used_usec_map.end())
+ {
+ prev_used_usec = m_thread_id_to_used_usec_map[thread_id];
+ }
+
+ uint32_t real_used_usec = curr_used_usec - prev_used_usec;
+ // A good first time record is one that runs for at least 0.25 sec
+ bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000);
+ bool good_subsequent_time = (prev_used_usec > 0) &&
+ ((real_used_usec > 0) || (process->HasAssignedIndexIDToThread(thread_id)));
+
+ if (good_first_time || good_subsequent_time)
+ {
+ // We try to avoid doing too many index id reservation,
+ // resulting in fast increase of index ids.
+
+ final_output << name << ":";
+ int32_t index_id = process->AssignIndexIDToThread(thread_id);
+ final_output << index_id << ";";
+
+ final_output << usec_name << ":" << usec_value << ";";
+ }
+ else
+ {
+ // Skip past 'thread_used_name'.
+ std::string local_name, local_value;
+ profileDataExtractor.GetNameColonValue(local_name, local_value);
+ }
+
+ // Store current time as previous time so that they can be compared later.
+ new_thread_id_to_used_usec_map[thread_id] = curr_used_usec;
+ }
+ else
+ {
+ // Bail out and use old string.
+ final_output << name << ":" << value << ";";
+ }
+ }
+ else
+ {
+ final_output << name << ":" << value << ";";
+ }
+ }
+ final_output << end_delimiter;
+ m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map;
+
+ return final_output.str();
+}
+
+StateType
+GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse
+(
+ ProcessGDBRemote *process,
+ const char *payload,
+ size_t packet_length,
+ StringExtractorGDBRemote &response
+)
+{
+ m_curr_tid = LLDB_INVALID_THREAD_ID;
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf ("GDBRemoteCommunicationClient::%s ()", __FUNCTION__);
+
+ Mutex::Locker locker(m_sequence_mutex);
+ StateType state = eStateRunning;
+
+ BroadcastEvent(eBroadcastBitRunPacketSent, NULL);
+ m_public_is_running.SetValue (true, eBroadcastNever);
+ // Set the starting continue packet into "continue_packet". This packet
+ // may change if we are interrupted and we continue after an async packet...
+ std::string continue_packet(payload, packet_length);
+
+ bool got_async_packet = false;
+
+ while (state == eStateRunning)
+ {
+ if (!got_async_packet)
+ {
+ if (log)
+ log->Printf ("GDBRemoteCommunicationClient::%s () sending continue packet: %s", __FUNCTION__, continue_packet.c_str());
+ if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) == 0)
+ state = eStateInvalid;
+
+ m_private_is_running.SetValue (true, eBroadcastAlways);
+ }
+
+ got_async_packet = false;
+
+ if (log)
+ log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(%s)", __FUNCTION__, continue_packet.c_str());
+
+ if (WaitForPacketWithTimeoutMicroSecondsNoLock(response, UINT32_MAX))
+ {
+ if (response.Empty())
+ state = eStateInvalid;
+ else
+ {
+ const char stop_type = response.GetChar();
+ if (log)
+ log->Printf ("GDBRemoteCommunicationClient::%s () got packet: %s", __FUNCTION__, response.GetStringRef().c_str());
+ switch (stop_type)
+ {
+ case 'T':
+ case 'S':
+ {
+ if (process->GetStopID() == 0)
+ {
+ if (process->GetID() == LLDB_INVALID_PROCESS_ID)
+ {
+ lldb::pid_t pid = GetCurrentProcessID ();
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ process->SetID (pid);
+ }
+ process->BuildDynamicRegisterInfo (true);
+ }
+
+ // Privately notify any internal threads that we have stopped
+ // in case we wanted to interrupt our process, yet we might
+ // send a packet and continue without returning control to the
+ // user.
+ m_private_is_running.SetValue (false, eBroadcastAlways);
+
+ const uint8_t signo = response.GetHexU8 (UINT8_MAX);
+
+ bool continue_after_async = m_async_signal != -1 || m_async_packet_predicate.GetValue();
+ if (continue_after_async || m_interrupt_sent)
+ {
+ // We sent an interrupt packet to stop the inferior process
+ // for an async signal or to send an async packet while running
+ // but we might have been single stepping and received the
+ // stop packet for the step instead of for the interrupt packet.
+ // Typically when an interrupt is sent a SIGINT or SIGSTOP
+ // is used, so if we get anything else, we need to try and
+ // get another stop reply packet that may have been sent
+ // due to sending the interrupt when the target is stopped
+ // which will just re-send a copy of the last stop reply
+ // packet. If we don't do this, then the reply for our
+ // async packet will be the repeat stop reply packet and cause
+ // a lot of trouble for us!
+ if (signo != SIGINT && signo != SIGSTOP)
+ {
+ continue_after_async = false;
+
+ // We didn't get a a SIGINT or SIGSTOP, so try for a
+ // very brief time (1 ms) to get another stop reply
+ // packet to make sure it doesn't get in the way
+ StringExtractorGDBRemote extra_stop_reply_packet;
+ uint32_t timeout_usec = 1000;
+ if (WaitForPacketWithTimeoutMicroSecondsNoLock (extra_stop_reply_packet, timeout_usec))
+ {
+ switch (extra_stop_reply_packet.GetChar())
+ {
+ case 'T':
+ case 'S':
+ // We did get an extra stop reply, which means
+ // our interrupt didn't stop the target so we
+ // shouldn't continue after the async signal
+ // or packet is sent...
+ continue_after_async = false;
+ break;
+ }
+ }
+ }
+ }
+
+ if (m_async_signal != -1)
+ {
+ if (log)
+ log->Printf ("async: send signo = %s", Host::GetSignalAsCString (m_async_signal));
+
+ // Save off the async signal we are supposed to send
+ const int async_signal = m_async_signal;
+ // Clear the async signal member so we don't end up
+ // sending the signal multiple times...
+ m_async_signal = -1;
+ // Check which signal we stopped with
+ if (signo == async_signal)
+ {
+ if (log)
+ log->Printf ("async: stopped with signal %s, we are done running", Host::GetSignalAsCString (signo));
+
+ // We already stopped with a signal that we wanted
+ // to stop with, so we are done
+ }
+ else
+ {
+ // We stopped with a different signal that the one
+ // we wanted to stop with, so now we must resume
+ // with the signal we want
+ char signal_packet[32];
+ int signal_packet_len = 0;
+ signal_packet_len = ::snprintf (signal_packet,
+ sizeof (signal_packet),
+ "C%2.2x",
+ async_signal);
+
+ if (log)
+ log->Printf ("async: stopped with signal %s, resume with %s",
+ Host::GetSignalAsCString (signo),
+ Host::GetSignalAsCString (async_signal));
+
+ // Set the continue packet to resume even if the
+ // interrupt didn't cause our stop (ignore continue_after_async)
+ continue_packet.assign(signal_packet, signal_packet_len);
+ continue;
+ }
+ }
+ else if (m_async_packet_predicate.GetValue())
+ {
+ Log * packet_log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
+
+ // We are supposed to send an asynchronous packet while
+ // we are running.
+ m_async_response.Clear();
+ if (m_async_packet.empty())
+ {
+ if (packet_log)
+ packet_log->Printf ("async: error: empty async packet");
+
+ }
+ else
+ {
+ if (packet_log)
+ packet_log->Printf ("async: sending packet");
+
+ SendPacketAndWaitForResponse (&m_async_packet[0],
+ m_async_packet.size(),
+ m_async_response,
+ false);
+ }
+ // Let the other thread that was trying to send the async
+ // packet know that the packet has been sent and response is
+ // ready...
+ m_async_packet_predicate.SetValue(false, eBroadcastAlways);
+
+ if (packet_log)
+ packet_log->Printf ("async: sent packet, continue_after_async = %i", continue_after_async);
+
+ // Set the continue packet to resume if our interrupt
+ // for the async packet did cause the stop
+ if (continue_after_async)
+ {
+ // Reverting this for now as it is causing deadlocks
+ // in programs (<rdar://problem/11529853>). In the future
+ // we should check our thread list and "do the right thing"
+ // for new threads that show up while we stop and run async
+ // packets. Setting the packet to 'c' to continue all threads
+ // is the right thing to do 99.99% of the time because if a
+ // thread was single stepping, and we sent an interrupt, we
+ // will notice above that we didn't stop due to an interrupt
+ // but stopped due to stepping and we would _not_ continue.
+ continue_packet.assign (1, 'c');
+ continue;
+ }
+ }
+ // Stop with signal and thread info
+ state = eStateStopped;
+ }
+ break;
+
+ case 'W':
+ case 'X':
+ // process exited
+ state = eStateExited;
+ break;
+
+ case 'O':
+ // STDOUT
+ {
+ got_async_packet = true;
+ std::string inferior_stdout;
+ inferior_stdout.reserve(response.GetBytesLeft () / 2);
+ char ch;
+ while ((ch = response.GetHexU8()) != '\0')
+ inferior_stdout.append(1, ch);
+ process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size());
+ }
+ break;
+
+ case 'A':
+ // Async miscellaneous reply. Right now, only profile data is coming through this channel.
+ {
+ got_async_packet = true;
+ std::string input = response.GetStringRef().substr(1); // '1' to move beyond 'A'
+ if (m_partial_profile_data.length() > 0)
+ {
+ m_partial_profile_data.append(input);
+ input = m_partial_profile_data;
+ m_partial_profile_data.clear();
+ }
+
+ size_t found, pos = 0, len = input.length();
+ while ((found = input.find(end_delimiter, pos)) != std::string::npos)
+ {
+ StringExtractorGDBRemote profileDataExtractor(input.substr(pos, found).c_str());
+ std::string profile_data = HarmonizeThreadIdsForProfileData(process, profileDataExtractor);
+ process->BroadcastAsyncProfileData (profile_data);
+
+ pos = found + end_delimiter_len;
+ }
+
+ if (pos < len)
+ {
+ // Last incomplete chunk.
+ m_partial_profile_data = input.substr(pos);
+ }
+ }
+ break;
+
+ case 'E':
+ // ERROR
+ state = eStateInvalid;
+ break;
+
+ default:
+ if (log)
+ log->Printf ("GDBRemoteCommunicationClient::%s () unrecognized async packet", __FUNCTION__);
+ state = eStateInvalid;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(...) => false", __FUNCTION__);
+ state = eStateInvalid;
+ }
+ }
+ if (log)
+ log->Printf ("GDBRemoteCommunicationClient::%s () => %s", __FUNCTION__, StateAsCString(state));
+ response.SetFilePos(0);
+ m_private_is_running.SetValue (false, eBroadcastAlways);
+ m_public_is_running.SetValue (false, eBroadcastAlways);
+ return state;
+}
+
+bool
+GDBRemoteCommunicationClient::SendAsyncSignal (int signo)
+{
+ Mutex::Locker async_locker (m_async_mutex);
+ m_async_signal = signo;
+ bool timed_out = false;
+ Mutex::Locker locker;
+ if (SendInterrupt (locker, 1, timed_out))
+ return true;
+ m_async_signal = -1;
+ return false;
+}
+
+// This function takes a mutex locker as a parameter in case the GetSequenceMutex
+// actually succeeds. If it doesn't succeed in acquiring the sequence mutex
+// (the expected result), then it will send the halt packet. If it does succeed
+// then the caller that requested the interrupt will want to keep the sequence
+// locked down so that no one else can send packets while the caller has control.
+// This function usually gets called when we are running and need to stop the
+// target. It can also be used when we are running and and we need to do something
+// else (like read/write memory), so we need to interrupt the running process
+// (gdb remote protocol requires this), and do what we need to do, then resume.
+
+bool
+GDBRemoteCommunicationClient::SendInterrupt
+(
+ Mutex::Locker& locker,
+ uint32_t seconds_to_wait_for_stop,
+ bool &timed_out
+)
+{
+ timed_out = false;
+ Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS));
+
+ if (IsRunning())
+ {
+ // Only send an interrupt if our debugserver is running...
+ if (GetSequenceMutex (locker))
+ {
+ if (log)
+ log->Printf ("SendInterrupt () - got sequence mutex without having to interrupt");
+ }
+ else
+ {
+ // Someone has the mutex locked waiting for a response or for the
+ // inferior to stop, so send the interrupt on the down low...
+ char ctrl_c = '\x03';
+ ConnectionStatus status = eConnectionStatusSuccess;
+ size_t bytes_written = Write (&ctrl_c, 1, status, NULL);
+ if (log)
+ log->PutCString("send packet: \\x03");
+ if (bytes_written > 0)
+ {
+ m_interrupt_sent = true;
+ if (seconds_to_wait_for_stop)
+ {
+ TimeValue timeout;
+ if (seconds_to_wait_for_stop)
+ {
+ timeout = TimeValue::Now();
+ timeout.OffsetWithSeconds (seconds_to_wait_for_stop);
+ }
+ if (m_private_is_running.WaitForValueEqualTo (false, &timeout, &timed_out))
+ {
+ if (log)
+ log->PutCString ("SendInterrupt () - sent interrupt, private state stopped");
+ return true;
+ }
+ else
+ {
+ if (log)
+ log->Printf ("SendInterrupt () - sent interrupt, timed out wating for async thread resume");
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("SendInterrupt () - sent interrupt, not waiting for stop...");
+ return true;
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("SendInterrupt () - failed to write interrupt");
+ }
+ return false;
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("SendInterrupt () - not running");
+ }
+ return true;
+}
+
+lldb::pid_t
+GDBRemoteCommunicationClient::GetCurrentProcessID ()
+{
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, false))
+ {
+ if (response.GetChar() == 'Q')
+ if (response.GetChar() == 'C')
+ return response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID);
+ }
+ return LLDB_INVALID_PROCESS_ID;
+}
+
+bool
+GDBRemoteCommunicationClient::GetLaunchSuccess (std::string &error_str)
+{
+ error_str.clear();
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, false))
+ {
+ if (response.IsOKResponse())
+ return true;
+ if (response.GetChar() == 'E')
+ {
+ // A string the describes what failed when launching...
+ error_str = response.GetStringRef().substr(1);
+ }
+ else
+ {
+ error_str.assign ("unknown error occurred launching process");
+ }
+ }
+ else
+ {
+ error_str.assign ("timed out waiting for app to launch");
+ }
+ return false;
+}
+
+int
+GDBRemoteCommunicationClient::SendArgumentsPacket (char const *argv[])
+{
+ if (argv && argv[0])
+ {
+ StreamString packet;
+ packet.PutChar('A');
+ const char *arg;
+ for (uint32_t i = 0; (arg = argv[i]) != NULL; ++i)
+ {
+ const int arg_len = strlen(arg);
+ if (i > 0)
+ packet.PutChar(',');
+ packet.Printf("%i,%i,", arg_len * 2, i);
+ packet.PutBytesAsRawHex8 (arg, arg_len);
+ }
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int
+GDBRemoteCommunicationClient::SendEnvironmentPacket (char const *name_equal_value)
+{
+ if (name_equal_value && name_equal_value[0])
+ {
+ StreamString packet;
+ packet.Printf("QEnvironment:%s", name_equal_value);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int
+GDBRemoteCommunicationClient::SendLaunchArchPacket (char const *arch)
+{
+ if (arch && arch[0])
+ {
+ StreamString packet;
+ packet.Printf("QLaunchArch:%s", arch);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+bool
+GDBRemoteCommunicationClient::GetOSVersion (uint32_t &major,
+ uint32_t &minor,
+ uint32_t &update)
+{
+ if (GetHostInfo ())
+ {
+ if (m_os_version_major != UINT32_MAX)
+ {
+ major = m_os_version_major;
+ minor = m_os_version_minor;
+ update = m_os_version_update;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+GDBRemoteCommunicationClient::GetOSBuildString (std::string &s)
+{
+ if (GetHostInfo ())
+ {
+ if (!m_os_build.empty())
+ {
+ s = m_os_build;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+
+bool
+GDBRemoteCommunicationClient::GetOSKernelDescription (std::string &s)
+{
+ if (GetHostInfo ())
+ {
+ if (!m_os_kernel.empty())
+ {
+ s = m_os_kernel;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+bool
+GDBRemoteCommunicationClient::GetHostname (std::string &s)
+{
+ if (GetHostInfo ())
+ {
+ if (!m_hostname.empty())
+ {
+ s = m_hostname;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+ArchSpec
+GDBRemoteCommunicationClient::GetSystemArchitecture ()
+{
+ if (GetHostInfo ())
+ return m_host_arch;
+ return ArchSpec();
+}
+
+const lldb_private::ArchSpec &
+GDBRemoteCommunicationClient::GetProcessArchitecture ()
+{
+ if (m_qProcessInfo_is_valid == eLazyBoolCalculate)
+ GetCurrentProcessInfo ();
+ return m_process_arch;
+}
+
+
+bool
+GDBRemoteCommunicationClient::GetHostInfo (bool force)
+{
+ if (force || m_qHostInfo_is_valid == eLazyBoolCalculate)
+ {
+ m_qHostInfo_is_valid = eLazyBoolNo;
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse ("qHostInfo", response, false))
+ {
+ if (response.IsNormalResponse())
+ {
+ std::string name;
+ std::string value;
+ uint32_t cpu = LLDB_INVALID_CPUTYPE;
+ uint32_t sub = 0;
+ std::string arch_name;
+ std::string os_name;
+ std::string vendor_name;
+ std::string triple;
+ uint32_t pointer_byte_size = 0;
+ StringExtractor extractor;
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t num_keys_decoded = 0;
+ while (response.GetNameColonValue(name, value))
+ {
+ if (name.compare("cputype") == 0)
+ {
+ // exception type in big endian hex
+ cpu = Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0);
+ if (cpu != LLDB_INVALID_CPUTYPE)
+ ++num_keys_decoded;
+ }
+ else if (name.compare("cpusubtype") == 0)
+ {
+ // exception count in big endian hex
+ sub = Args::StringToUInt32 (value.c_str(), 0, 0);
+ if (sub != 0)
+ ++num_keys_decoded;
+ }
+ else if (name.compare("arch") == 0)
+ {
+ arch_name.swap (value);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("triple") == 0)
+ {
+ // The triple comes as ASCII hex bytes since it contains '-' chars
+ extractor.GetStringRef().swap(value);
+ extractor.SetFilePos(0);
+ extractor.GetHexByteString (triple);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("os_build") == 0)
+ {
+ extractor.GetStringRef().swap(value);
+ extractor.SetFilePos(0);
+ extractor.GetHexByteString (m_os_build);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("hostname") == 0)
+ {
+ extractor.GetStringRef().swap(value);
+ extractor.SetFilePos(0);
+ extractor.GetHexByteString (m_hostname);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("os_kernel") == 0)
+ {
+ extractor.GetStringRef().swap(value);
+ extractor.SetFilePos(0);
+ extractor.GetHexByteString (m_os_kernel);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("ostype") == 0)
+ {
+ os_name.swap (value);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("vendor") == 0)
+ {
+ vendor_name.swap(value);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("endian") == 0)
+ {
+ ++num_keys_decoded;
+ if (value.compare("little") == 0)
+ byte_order = eByteOrderLittle;
+ else if (value.compare("big") == 0)
+ byte_order = eByteOrderBig;
+ else if (value.compare("pdp") == 0)
+ byte_order = eByteOrderPDP;
+ else
+ --num_keys_decoded;
+ }
+ else if (name.compare("ptrsize") == 0)
+ {
+ pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 0);
+ if (pointer_byte_size != 0)
+ ++num_keys_decoded;
+ }
+ else if (name.compare("os_version") == 0)
+ {
+ Args::StringToVersion (value.c_str(),
+ m_os_version_major,
+ m_os_version_minor,
+ m_os_version_update);
+ if (m_os_version_major != UINT32_MAX)
+ ++num_keys_decoded;
+ }
+ else if (name.compare("watchpoint_exceptions_received") == 0)
+ {
+ ++num_keys_decoded;
+ if (strcmp(value.c_str(),"before") == 0)
+ m_watchpoints_trigger_after_instruction = eLazyBoolNo;
+ else if (strcmp(value.c_str(),"after") == 0)
+ m_watchpoints_trigger_after_instruction = eLazyBoolYes;
+ else
+ --num_keys_decoded;
+ }
+
+ }
+
+ if (num_keys_decoded > 0)
+ m_qHostInfo_is_valid = eLazyBoolYes;
+
+ if (triple.empty())
+ {
+ if (arch_name.empty())
+ {
+ if (cpu != LLDB_INVALID_CPUTYPE)
+ {
+ m_host_arch.SetArchitecture (eArchTypeMachO, cpu, sub);
+ if (pointer_byte_size)
+ {
+ assert (pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid)
+ {
+ assert (byte_order == m_host_arch.GetByteOrder());
+ }
+
+ if (!os_name.empty() && vendor_name.compare("apple") == 0 && os_name.find("darwin") == 0)
+ {
+ switch (m_host_arch.GetMachine())
+ {
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ os_name = "ios";
+ break;
+ default:
+ os_name = "macosx";
+ break;
+ }
+ }
+ if (!vendor_name.empty())
+ m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name));
+ if (!os_name.empty())
+ m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name));
+
+ }
+ }
+ else
+ {
+ std::string triple;
+ triple += arch_name;
+ if (!vendor_name.empty() || !os_name.empty())
+ {
+ triple += '-';
+ if (vendor_name.empty())
+ triple += "unknown";
+ else
+ triple += vendor_name;
+ triple += '-';
+ if (os_name.empty())
+ triple += "unknown";
+ else
+ triple += os_name;
+ }
+ m_host_arch.SetTriple (triple.c_str());
+
+ llvm::Triple &host_triple = m_host_arch.GetTriple();
+ if (host_triple.getVendor() == llvm::Triple::Apple && host_triple.getOS() == llvm::Triple::Darwin)
+ {
+ switch (m_host_arch.GetMachine())
+ {
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ host_triple.setOS(llvm::Triple::IOS);
+ break;
+ default:
+ host_triple.setOS(llvm::Triple::MacOSX);
+ break;
+ }
+ }
+ if (pointer_byte_size)
+ {
+ assert (pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid)
+ {
+ assert (byte_order == m_host_arch.GetByteOrder());
+ }
+
+ }
+ }
+ else
+ {
+ m_host_arch.SetTriple (triple.c_str());
+ if (pointer_byte_size)
+ {
+ assert (pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid)
+ {
+ assert (byte_order == m_host_arch.GetByteOrder());
+ }
+ }
+ }
+ }
+ }
+ return m_qHostInfo_is_valid == eLazyBoolYes;
+}
+
+int
+GDBRemoteCommunicationClient::SendAttach
+(
+ lldb::pid_t pid,
+ StringExtractorGDBRemote& response
+)
+{
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ {
+ char packet[64];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, pid);
+ assert (packet_len < (int)sizeof(packet));
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ if (response.IsErrorResponse())
+ return response.GetError();
+ return 0;
+ }
+ }
+ return -1;
+}
+
+const lldb_private::ArchSpec &
+GDBRemoteCommunicationClient::GetHostArchitecture ()
+{
+ if (m_qHostInfo_is_valid == eLazyBoolCalculate)
+ GetHostInfo ();
+ return m_host_arch;
+}
+
+addr_t
+GDBRemoteCommunicationClient::AllocateMemory (size_t size, uint32_t permissions)
+{
+ if (m_supports_alloc_dealloc_memory != eLazyBoolNo)
+ {
+ m_supports_alloc_dealloc_memory = eLazyBoolYes;
+ char packet[64];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s",
+ (uint64_t)size,
+ permissions & lldb::ePermissionsReadable ? "r" : "",
+ permissions & lldb::ePermissionsWritable ? "w" : "",
+ permissions & lldb::ePermissionsExecutable ? "x" : "");
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ if (!response.IsErrorResponse())
+ return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+ }
+ else
+ {
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool
+GDBRemoteCommunicationClient::DeallocateMemory (addr_t addr)
+{
+ if (m_supports_alloc_dealloc_memory != eLazyBoolNo)
+ {
+ m_supports_alloc_dealloc_memory = eLazyBoolYes;
+ char packet[64];
+ const int packet_len = ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr);
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ if (response.IsOKResponse())
+ return true;
+ }
+ else
+ {
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ }
+ }
+ return false;
+}
+
+Error
+GDBRemoteCommunicationClient::Detach (bool keep_stopped)
+{
+ Error error;
+
+ if (keep_stopped)
+ {
+ if (m_supports_detach_stay_stopped == eLazyBoolCalculate)
+ {
+ char packet[64];
+ const int packet_len = ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:");
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ m_supports_detach_stay_stopped = eLazyBoolYes;
+ }
+ else
+ {
+ m_supports_detach_stay_stopped = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_detach_stay_stopped == eLazyBoolNo)
+ {
+ error.SetErrorString("Stays stopped not supported by this target.");
+ return error;
+ }
+ else
+ {
+ size_t num_sent = SendPacket ("D1", 2);
+ if (num_sent == 0)
+ error.SetErrorString ("Sending extended disconnect packet failed.");
+ }
+ }
+ else
+ {
+ size_t num_sent = SendPacket ("D", 1);
+ if (num_sent == 0)
+ error.SetErrorString ("Sending disconnect packet failed.");
+ }
+ return error;
+}
+
+Error
+GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr,
+ lldb_private::MemoryRegionInfo &region_info)
+{
+ Error error;
+ region_info.Clear();
+
+ if (m_supports_memory_region_info != eLazyBoolNo)
+ {
+ m_supports_memory_region_info = eLazyBoolYes;
+ char packet[64];
+ const int packet_len = ::snprintf(packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr);
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ std::string name;
+ std::string value;
+ addr_t addr_value;
+ bool success = true;
+ bool saw_permissions = false;
+ while (success && response.GetNameColonValue(name, value))
+ {
+ if (name.compare ("start") == 0)
+ {
+ addr_value = Args::StringToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16, &success);
+ if (success)
+ region_info.GetRange().SetRangeBase(addr_value);
+ }
+ else if (name.compare ("size") == 0)
+ {
+ addr_value = Args::StringToUInt64(value.c_str(), 0, 16, &success);
+ if (success)
+ region_info.GetRange().SetByteSize (addr_value);
+ }
+ else if (name.compare ("permissions") == 0 && region_info.GetRange().IsValid())
+ {
+ saw_permissions = true;
+ if (region_info.GetRange().Contains (addr))
+ {
+ if (value.find('r') != std::string::npos)
+ region_info.SetReadable (MemoryRegionInfo::eYes);
+ else
+ region_info.SetReadable (MemoryRegionInfo::eNo);
+
+ if (value.find('w') != std::string::npos)
+ region_info.SetWritable (MemoryRegionInfo::eYes);
+ else
+ region_info.SetWritable (MemoryRegionInfo::eNo);
+
+ if (value.find('x') != std::string::npos)
+ region_info.SetExecutable (MemoryRegionInfo::eYes);
+ else
+ region_info.SetExecutable (MemoryRegionInfo::eNo);
+ }
+ else
+ {
+ // The reported region does not contain this address -- we're looking at an unmapped page
+ region_info.SetReadable (MemoryRegionInfo::eNo);
+ region_info.SetWritable (MemoryRegionInfo::eNo);
+ region_info.SetExecutable (MemoryRegionInfo::eNo);
+ }
+ }
+ else if (name.compare ("error") == 0)
+ {
+ StringExtractorGDBRemote name_extractor;
+ // Swap "value" over into "name_extractor"
+ name_extractor.GetStringRef().swap(value);
+ // Now convert the HEX bytes into a string value
+ name_extractor.GetHexByteString (value);
+ error.SetErrorString(value.c_str());
+ }
+ }
+
+ // We got a valid address range back but no permissions -- which means this is an unmapped page
+ if (region_info.GetRange().IsValid() && saw_permissions == false)
+ {
+ region_info.SetReadable (MemoryRegionInfo::eNo);
+ region_info.SetWritable (MemoryRegionInfo::eNo);
+ region_info.SetExecutable (MemoryRegionInfo::eNo);
+ }
+ }
+ else
+ {
+ m_supports_memory_region_info = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_memory_region_info == eLazyBoolNo)
+ {
+ error.SetErrorString("qMemoryRegionInfo is not supported");
+ }
+ if (error.Fail())
+ region_info.Clear();
+ return error;
+
+}
+
+Error
+GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num)
+{
+ Error error;
+
+ if (m_supports_watchpoint_support_info == eLazyBoolYes)
+ {
+ num = m_num_supported_hardware_watchpoints;
+ return error;
+ }
+
+ // Set num to 0 first.
+ num = 0;
+ if (m_supports_watchpoint_support_info != eLazyBoolNo)
+ {
+ char packet[64];
+ const int packet_len = ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:");
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ m_supports_watchpoint_support_info = eLazyBoolYes;
+ std::string name;
+ std::string value;
+ while (response.GetNameColonValue(name, value))
+ {
+ if (name.compare ("num") == 0)
+ {
+ num = Args::StringToUInt32(value.c_str(), 0, 0);
+ m_num_supported_hardware_watchpoints = num;
+ }
+ }
+ }
+ else
+ {
+ m_supports_watchpoint_support_info = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_watchpoint_support_info == eLazyBoolNo)
+ {
+ error.SetErrorString("qWatchpointSupportInfo is not supported");
+ }
+ return error;
+
+}
+
+lldb_private::Error
+GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num, bool& after)
+{
+ Error error(GetWatchpointSupportInfo(num));
+ if (error.Success())
+ error = GetWatchpointsTriggerAfterInstruction(after);
+ return error;
+}
+
+lldb_private::Error
+GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction (bool &after)
+{
+ Error error;
+
+ // we assume watchpoints will happen after running the relevant opcode
+ // and we only want to override this behavior if we have explicitly
+ // received a qHostInfo telling us otherwise
+ if (m_qHostInfo_is_valid != eLazyBoolYes)
+ after = true;
+ else
+ after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo);
+ return error;
+}
+
+int
+GDBRemoteCommunicationClient::SetSTDIN (char const *path)
+{
+ if (path && path[0])
+ {
+ StreamString packet;
+ packet.PutCString("QSetSTDIN:");
+ packet.PutBytesAsRawHex8(path, strlen(path));
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int
+GDBRemoteCommunicationClient::SetSTDOUT (char const *path)
+{
+ if (path && path[0])
+ {
+ StreamString packet;
+ packet.PutCString("QSetSTDOUT:");
+ packet.PutBytesAsRawHex8(path, strlen(path));
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int
+GDBRemoteCommunicationClient::SetSTDERR (char const *path)
+{
+ if (path && path[0])
+ {
+ StreamString packet;
+ packet.PutCString("QSetSTDERR:");
+ packet.PutBytesAsRawHex8(path, strlen(path));
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int
+GDBRemoteCommunicationClient::SetWorkingDir (char const *path)
+{
+ if (path && path[0])
+ {
+ StreamString packet;
+ packet.PutCString("QSetWorkingDir:");
+ packet.PutBytesAsRawHex8(path, strlen(path));
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int
+GDBRemoteCommunicationClient::SetDisableASLR (bool enable)
+{
+ char packet[32];
+ const int packet_len = ::snprintf (packet, sizeof (packet), "QSetDisableASLR:%i", enable ? 1 : 0);
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ return -1;
+}
+
+bool
+GDBRemoteCommunicationClient::DecodeProcessInfoResponse (StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info)
+{
+ if (response.IsNormalResponse())
+ {
+ std::string name;
+ std::string value;
+ StringExtractor extractor;
+
+ while (response.GetNameColonValue(name, value))
+ {
+ if (name.compare("pid") == 0)
+ {
+ process_info.SetProcessID (Args::StringToUInt32 (value.c_str(), LLDB_INVALID_PROCESS_ID, 0));
+ }
+ else if (name.compare("ppid") == 0)
+ {
+ process_info.SetParentProcessID (Args::StringToUInt32 (value.c_str(), LLDB_INVALID_PROCESS_ID, 0));
+ }
+ else if (name.compare("uid") == 0)
+ {
+ process_info.SetUserID (Args::StringToUInt32 (value.c_str(), UINT32_MAX, 0));
+ }
+ else if (name.compare("euid") == 0)
+ {
+ process_info.SetEffectiveUserID (Args::StringToUInt32 (value.c_str(), UINT32_MAX, 0));
+ }
+ else if (name.compare("gid") == 0)
+ {
+ process_info.SetGroupID (Args::StringToUInt32 (value.c_str(), UINT32_MAX, 0));
+ }
+ else if (name.compare("egid") == 0)
+ {
+ process_info.SetEffectiveGroupID (Args::StringToUInt32 (value.c_str(), UINT32_MAX, 0));
+ }
+ else if (name.compare("triple") == 0)
+ {
+ // The triple comes as ASCII hex bytes since it contains '-' chars
+ extractor.GetStringRef().swap(value);
+ extractor.SetFilePos(0);
+ extractor.GetHexByteString (value);
+ process_info.GetArchitecture ().SetTriple (value.c_str());
+ }
+ else if (name.compare("name") == 0)
+ {
+ StringExtractor extractor;
+ // The process name from ASCII hex bytes since we can't
+ // control the characters in a process name
+ extractor.GetStringRef().swap(value);
+ extractor.SetFilePos(0);
+ extractor.GetHexByteString (value);
+ process_info.GetExecutableFile().SetFile (value.c_str(), false);
+ }
+ }
+
+ if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
+ return true;
+ }
+ return false;
+}
+
+bool
+GDBRemoteCommunicationClient::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
+{
+ process_info.Clear();
+
+ if (m_supports_qProcessInfoPID)
+ {
+ char packet[32];
+ const int packet_len = ::snprintf (packet, sizeof (packet), "qProcessInfoPID:%" PRIu64, pid);
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ return DecodeProcessInfoResponse (response, process_info);
+ }
+ else
+ {
+ m_supports_qProcessInfoPID = false;
+ return false;
+ }
+ }
+ return false;
+}
+
+bool
+GDBRemoteCommunicationClient::GetCurrentProcessInfo ()
+{
+ if (m_qProcessInfo_is_valid == eLazyBoolYes)
+ return true;
+ if (m_qProcessInfo_is_valid == eLazyBoolNo)
+ return false;
+
+ GetHostInfo ();
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse ("qProcessInfo", response, false))
+ {
+ if (response.IsNormalResponse())
+ {
+ std::string name;
+ std::string value;
+ uint32_t cpu = LLDB_INVALID_CPUTYPE;
+ uint32_t sub = 0;
+ std::string arch_name;
+ std::string os_name;
+ std::string vendor_name;
+ std::string triple;
+ uint32_t pointer_byte_size = 0;
+ StringExtractor extractor;
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t num_keys_decoded = 0;
+ while (response.GetNameColonValue(name, value))
+ {
+ if (name.compare("cputype") == 0)
+ {
+ cpu = Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 16);
+ if (cpu != LLDB_INVALID_CPUTYPE)
+ ++num_keys_decoded;
+ }
+ else if (name.compare("cpusubtype") == 0)
+ {
+ sub = Args::StringToUInt32 (value.c_str(), 0, 16);
+ if (sub != 0)
+ ++num_keys_decoded;
+ }
+ else if (name.compare("ostype") == 0)
+ {
+ os_name.swap (value);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("vendor") == 0)
+ {
+ vendor_name.swap(value);
+ ++num_keys_decoded;
+ }
+ else if (name.compare("endian") == 0)
+ {
+ ++num_keys_decoded;
+ if (value.compare("little") == 0)
+ byte_order = eByteOrderLittle;
+ else if (value.compare("big") == 0)
+ byte_order = eByteOrderBig;
+ else if (value.compare("pdp") == 0)
+ byte_order = eByteOrderPDP;
+ else
+ --num_keys_decoded;
+ }
+ else if (name.compare("ptrsize") == 0)
+ {
+ pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 16);
+ if (pointer_byte_size != 0)
+ ++num_keys_decoded;
+ }
+ }
+ if (num_keys_decoded > 0)
+ m_qProcessInfo_is_valid = eLazyBoolYes;
+ if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && !vendor_name.empty())
+ {
+ m_process_arch.SetArchitecture (eArchTypeMachO, cpu, sub);
+ if (pointer_byte_size)
+ {
+ assert (pointer_byte_size == m_process_arch.GetAddressByteSize());
+ }
+ m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name));
+ m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name));
+ return true;
+ }
+ }
+ }
+ else
+ {
+ m_qProcessInfo_is_valid = eLazyBoolNo;
+ }
+
+ return false;
+}
+
+
+uint32_t
+GDBRemoteCommunicationClient::FindProcesses (const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos)
+{
+ process_infos.Clear();
+
+ if (m_supports_qfProcessInfo)
+ {
+ StreamString packet;
+ packet.PutCString ("qfProcessInfo");
+ if (!match_info.MatchAllProcesses())
+ {
+ packet.PutChar (':');
+ const char *name = match_info.GetProcessInfo().GetName();
+ bool has_name_match = false;
+ if (name && name[0])
+ {
+ has_name_match = true;
+ NameMatchType name_match_type = match_info.GetNameMatchType();
+ switch (name_match_type)
+ {
+ case eNameMatchIgnore:
+ has_name_match = false;
+ break;
+
+ case eNameMatchEquals:
+ packet.PutCString ("name_match:equals;");
+ break;
+
+ case eNameMatchContains:
+ packet.PutCString ("name_match:contains;");
+ break;
+
+ case eNameMatchStartsWith:
+ packet.PutCString ("name_match:starts_with;");
+ break;
+
+ case eNameMatchEndsWith:
+ packet.PutCString ("name_match:ends_with;");
+ break;
+
+ case eNameMatchRegularExpression:
+ packet.PutCString ("name_match:regex;");
+ break;
+ }
+ if (has_name_match)
+ {
+ packet.PutCString ("name:");
+ packet.PutBytesAsRawHex8(name, ::strlen(name));
+ packet.PutChar (';');
+ }
+ }
+
+ if (match_info.GetProcessInfo().ProcessIDIsValid())
+ packet.Printf("pid:%" PRIu64 ";",match_info.GetProcessInfo().GetProcessID());
+ if (match_info.GetProcessInfo().ParentProcessIDIsValid())
+ packet.Printf("parent_pid:%" PRIu64 ";",match_info.GetProcessInfo().GetParentProcessID());
+ if (match_info.GetProcessInfo().UserIDIsValid())
+ packet.Printf("uid:%u;",match_info.GetProcessInfo().GetUserID());
+ if (match_info.GetProcessInfo().GroupIDIsValid())
+ packet.Printf("gid:%u;",match_info.GetProcessInfo().GetGroupID());
+ if (match_info.GetProcessInfo().EffectiveUserIDIsValid())
+ packet.Printf("euid:%u;",match_info.GetProcessInfo().GetEffectiveUserID());
+ if (match_info.GetProcessInfo().EffectiveGroupIDIsValid())
+ packet.Printf("egid:%u;",match_info.GetProcessInfo().GetEffectiveGroupID());
+ if (match_info.GetProcessInfo().EffectiveGroupIDIsValid())
+ packet.Printf("all_users:%u;",match_info.GetMatchAllUsers() ? 1 : 0);
+ if (match_info.GetProcessInfo().GetArchitecture().IsValid())
+ {
+ const ArchSpec &match_arch = match_info.GetProcessInfo().GetArchitecture();
+ const llvm::Triple &triple = match_arch.GetTriple();
+ packet.PutCString("triple:");
+ packet.PutCStringAsRawHex8(triple.getTriple().c_str());
+ packet.PutChar (';');
+ }
+ }
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false))
+ {
+ do
+ {
+ ProcessInstanceInfo process_info;
+ if (!DecodeProcessInfoResponse (response, process_info))
+ break;
+ process_infos.Append(process_info);
+ response.GetStringRef().clear();
+ response.SetFilePos(0);
+ } while (SendPacketAndWaitForResponse ("qsProcessInfo", strlen ("qsProcessInfo"), response, false));
+ }
+ else
+ {
+ m_supports_qfProcessInfo = false;
+ return 0;
+ }
+ }
+ return process_infos.GetSize();
+
+}
+
+bool
+GDBRemoteCommunicationClient::GetUserName (uint32_t uid, std::string &name)
+{
+ if (m_supports_qUserName)
+ {
+ char packet[32];
+ const int packet_len = ::snprintf (packet, sizeof (packet), "qUserName:%i", uid);
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ if (response.IsNormalResponse())
+ {
+ // Make sure we parsed the right number of characters. The response is
+ // the hex encoded user name and should make up the entire packet.
+ // If there are any non-hex ASCII bytes, the length won't match below..
+ if (response.GetHexByteString (name) * 2 == response.GetStringRef().size())
+ return true;
+ }
+ }
+ else
+ {
+ m_supports_qUserName = false;
+ return false;
+ }
+ }
+ return false;
+
+}
+
+bool
+GDBRemoteCommunicationClient::GetGroupName (uint32_t gid, std::string &name)
+{
+ if (m_supports_qGroupName)
+ {
+ char packet[32];
+ const int packet_len = ::snprintf (packet, sizeof (packet), "qGroupName:%i", gid);
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
+ {
+ if (response.IsNormalResponse())
+ {
+ // Make sure we parsed the right number of characters. The response is
+ // the hex encoded group name and should make up the entire packet.
+ // If there are any non-hex ASCII bytes, the length won't match below..
+ if (response.GetHexByteString (name) * 2 == response.GetStringRef().size())
+ return true;
+ }
+ }
+ else
+ {
+ m_supports_qGroupName = false;
+ return false;
+ }
+ }
+ return false;
+}
+
+void
+GDBRemoteCommunicationClient::TestPacketSpeed (const uint32_t num_packets)
+{
+ uint32_t i;
+ TimeValue start_time, end_time;
+ uint64_t total_time_nsec;
+ float packets_per_second;
+ if (SendSpeedTestPacket (0, 0))
+ {
+ for (uint32_t send_size = 0; send_size <= 1024; send_size *= 2)
+ {
+ for (uint32_t recv_size = 0; recv_size <= 1024; recv_size *= 2)
+ {
+ start_time = TimeValue::Now();
+ for (i=0; i<num_packets; ++i)
+ {
+ SendSpeedTestPacket (send_size, recv_size);
+ }
+ end_time = TimeValue::Now();
+ total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970();
+ packets_per_second = (((float)num_packets)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec;
+ printf ("%u qSpeedTest(send=%-5u, recv=%-5u) in %" PRIu64 ".%9.9" PRIu64 " sec for %f packets/sec.\n",
+ num_packets,
+ send_size,
+ recv_size,
+ total_time_nsec / TimeValue::NanoSecPerSec,
+ total_time_nsec % TimeValue::NanoSecPerSec,
+ packets_per_second);
+ if (recv_size == 0)
+ recv_size = 32;
+ }
+ if (send_size == 0)
+ send_size = 32;
+ }
+ }
+ else
+ {
+ start_time = TimeValue::Now();
+ for (i=0; i<num_packets; ++i)
+ {
+ GetCurrentProcessID ();
+ }
+ end_time = TimeValue::Now();
+ total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970();
+ packets_per_second = (((float)num_packets)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec;
+ printf ("%u 'qC' packets packets in 0x%" PRIu64 "%9.9" PRIu64 " sec for %f packets/sec.\n",
+ num_packets,
+ total_time_nsec / TimeValue::NanoSecPerSec,
+ total_time_nsec % TimeValue::NanoSecPerSec,
+ packets_per_second);
+ }
+}
+
+bool
+GDBRemoteCommunicationClient::SendSpeedTestPacket (uint32_t send_size, uint32_t recv_size)
+{
+ StreamString packet;
+ packet.Printf ("qSpeedTest:response_size:%i;data:", recv_size);
+ uint32_t bytes_left = send_size;
+ while (bytes_left > 0)
+ {
+ if (bytes_left >= 26)
+ {
+ packet.PutCString("abcdefghijklmnopqrstuvwxyz");
+ bytes_left -= 26;
+ }
+ else
+ {
+ packet.Printf ("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz");
+ bytes_left = 0;
+ }
+ }
+
+ StringExtractorGDBRemote response;
+ return SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) > 0;
+ return false;
+}
+
+uint16_t
+GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort ()
+{
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qLaunchGDBServer", strlen("qLaunchGDBServer"), response, false))
+ {
+ std::string name;
+ std::string value;
+ uint16_t port = 0;
+ //lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ while (response.GetNameColonValue(name, value))
+ {
+ if (name.size() == 4 && name.compare("port") == 0)
+ port = Args::StringToUInt32(value.c_str(), 0, 0);
+// if (name.size() == 3 && name.compare("pid") == 0)
+// pid = Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0);
+ }
+ return port;
+ }
+ return 0;
+}
+
+bool
+GDBRemoteCommunicationClient::SetCurrentThread (uint64_t tid)
+{
+ if (m_curr_tid == tid)
+ return true;
+
+ char packet[32];
+ int packet_len;
+ if (tid == UINT64_MAX)
+ packet_len = ::snprintf (packet, sizeof(packet), "Hg-1");
+ else
+ packet_len = ::snprintf (packet, sizeof(packet), "Hg%" PRIx64, tid);
+ assert (packet_len + 1 < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, packet_len, response, false))
+ {
+ if (response.IsOKResponse())
+ {
+ m_curr_tid = tid;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+GDBRemoteCommunicationClient::SetCurrentThreadForRun (uint64_t tid)
+{
+ if (m_curr_tid_run == tid)
+ return true;
+
+ char packet[32];
+ int packet_len;
+ if (tid == UINT64_MAX)
+ packet_len = ::snprintf (packet, sizeof(packet), "Hc-1");
+ else
+ packet_len = ::snprintf (packet, sizeof(packet), "Hc%" PRIx64, tid);
+
+ assert (packet_len + 1 < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, packet_len, response, false))
+ {
+ if (response.IsOKResponse())
+ {
+ m_curr_tid_run = tid;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+GDBRemoteCommunicationClient::GetStopReply (StringExtractorGDBRemote &response)
+{
+ if (SendPacketAndWaitForResponse("?", 1, response, false))
+ return response.IsNormalResponse();
+ return false;
+}
+
+bool
+GDBRemoteCommunicationClient::GetThreadStopInfo (lldb::tid_t tid, StringExtractorGDBRemote &response)
+{
+ if (m_supports_qThreadStopInfo)
+ {
+ char packet[256];
+ int packet_len = ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid);
+ assert (packet_len < (int)sizeof(packet));
+ if (SendPacketAndWaitForResponse(packet, packet_len, response, false))
+ {
+ if (response.IsNormalResponse())
+ return true;
+ else
+ return false;
+ }
+ else
+ {
+ m_supports_qThreadStopInfo = false;
+ }
+ }
+// if (SetCurrentThread (tid))
+// return GetStopReply (response);
+ return false;
+}
+
+
+uint8_t
+GDBRemoteCommunicationClient::SendGDBStoppointTypePacket (GDBStoppointType type, bool insert, addr_t addr, uint32_t length)
+{
+ switch (type)
+ {
+ case eBreakpointSoftware: if (!m_supports_z0) return UINT8_MAX; break;
+ case eBreakpointHardware: if (!m_supports_z1) return UINT8_MAX; break;
+ case eWatchpointWrite: if (!m_supports_z2) return UINT8_MAX; break;
+ case eWatchpointRead: if (!m_supports_z3) return UINT8_MAX; break;
+ case eWatchpointReadWrite: if (!m_supports_z4) return UINT8_MAX; break;
+ }
+
+ char packet[64];
+ const int packet_len = ::snprintf (packet,
+ sizeof(packet),
+ "%c%i,%" PRIx64 ",%x",
+ insert ? 'Z' : 'z',
+ type,
+ addr,
+ length);
+
+ assert (packet_len + 1 < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, packet_len, response, true))
+ {
+ if (response.IsOKResponse())
+ return 0;
+ else if (response.IsErrorResponse())
+ return response.GetError();
+ }
+ else
+ {
+ switch (type)
+ {
+ case eBreakpointSoftware: m_supports_z0 = false; break;
+ case eBreakpointHardware: m_supports_z1 = false; break;
+ case eWatchpointWrite: m_supports_z2 = false; break;
+ case eWatchpointRead: m_supports_z3 = false; break;
+ case eWatchpointReadWrite: m_supports_z4 = false; break;
+ }
+ }
+
+ return UINT8_MAX;
+}
+
+size_t
+GDBRemoteCommunicationClient::GetCurrentThreadIDs (std::vector<lldb::tid_t> &thread_ids,
+ bool &sequence_mutex_unavailable)
+{
+ Mutex::Locker locker;
+ thread_ids.clear();
+
+ if (GetSequenceMutex (locker, "ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex"))
+ {
+ sequence_mutex_unavailable = false;
+ StringExtractorGDBRemote response;
+
+ for (SendPacketNoLock ("qfThreadInfo", strlen("qfThreadInfo")) && WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ());
+ response.IsNormalResponse();
+ SendPacketNoLock ("qsThreadInfo", strlen("qsThreadInfo")) && WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ()))
+ {
+ char ch = response.GetChar();
+ if (ch == 'l')
+ break;
+ if (ch == 'm')
+ {
+ do
+ {
+ tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID);
+
+ if (tid != LLDB_INVALID_THREAD_ID)
+ {
+ thread_ids.push_back (tid);
+ }
+ ch = response.GetChar(); // Skip the command separator
+ } while (ch == ','); // Make sure we got a comma separator
+ }
+ }
+ }
+ else
+ {
+#if defined (LLDB_CONFIGURATION_DEBUG)
+ // assert(!"ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex");
+#else
+ Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf("error: failed to get packet sequence mutex, not sending packet 'qfThreadInfo'");
+#endif
+ sequence_mutex_unavailable = true;
+ }
+ return thread_ids.size();
+}
+
+lldb::addr_t
+GDBRemoteCommunicationClient::GetShlibInfoAddr()
+{
+ if (!IsRunning())
+ {
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qShlibInfoAddr", ::strlen ("qShlibInfoAddr"), response, false))
+ {
+ if (response.IsNormalResponse())
+ return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
new file mode 100644
index 0000000..5bb8387
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -0,0 +1,430 @@
+//===-- GDBRemoteCommunicationClient.h --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationClient_h_
+#define liblldb_GDBRemoteCommunicationClient_h_
+
+// C Includes
+// C++ Includes
+#include <vector>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Target/Process.h"
+
+#include "GDBRemoteCommunication.h"
+
+typedef enum
+{
+ eBreakpointSoftware = 0,
+ eBreakpointHardware,
+ eWatchpointWrite,
+ eWatchpointRead,
+ eWatchpointReadWrite
+} GDBStoppointType;
+
+class GDBRemoteCommunicationClient : public GDBRemoteCommunication
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ GDBRemoteCommunicationClient(bool is_platform);
+
+ virtual
+ ~GDBRemoteCommunicationClient();
+
+ //------------------------------------------------------------------
+ // After connecting, send the handshake to the server to make sure
+ // we are communicating with it.
+ //------------------------------------------------------------------
+ bool
+ HandshakeWithServer (lldb_private::Error *error_ptr);
+
+ size_t
+ SendPacketAndWaitForResponse (const char *send_payload,
+ StringExtractorGDBRemote &response,
+ bool send_async);
+
+ size_t
+ SendPacketAndWaitForResponse (const char *send_payload,
+ size_t send_length,
+ StringExtractorGDBRemote &response,
+ bool send_async);
+
+ lldb::StateType
+ SendContinuePacketAndWaitForResponse (ProcessGDBRemote *process,
+ const char *packet_payload,
+ size_t packet_length,
+ StringExtractorGDBRemote &response);
+
+ virtual bool
+ GetThreadSuffixSupported ();
+
+ void
+ QueryNoAckModeSupported ();
+
+ void
+ GetListThreadsInStopReplySupported ();
+
+ bool
+ SendAsyncSignal (int signo);
+
+ bool
+ SendInterrupt (lldb_private::Mutex::Locker &locker,
+ uint32_t seconds_to_wait_for_stop,
+ bool &timed_out);
+
+ lldb::pid_t
+ GetCurrentProcessID ();
+
+ bool
+ GetLaunchSuccess (std::string &error_str);
+
+ uint16_t
+ LaunchGDBserverAndGetPort ();
+
+ //------------------------------------------------------------------
+ /// Sends a GDB remote protocol 'A' packet that delivers program
+ /// arguments to the remote server.
+ ///
+ /// @param[in] argv
+ /// A NULL terminated array of const C strings to use as the
+ /// arguments.
+ ///
+ /// @return
+ /// Zero if the response was "OK", a positive value if the
+ /// the response was "Exx" where xx are two hex digits, or
+ /// -1 if the call is unsupported or any other unexpected
+ /// response was received.
+ //------------------------------------------------------------------
+ int
+ SendArgumentsPacket (char const *argv[]);
+
+ //------------------------------------------------------------------
+ /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the
+ /// environment that will get used when launching an application
+ /// in conjunction with the 'A' packet. This function can be called
+ /// multiple times in a row in order to pass on the desired
+ /// environment that the inferior should be launched with.
+ ///
+ /// @param[in] name_equal_value
+ /// A NULL terminated C string that contains a single environment
+ /// in the format "NAME=VALUE".
+ ///
+ /// @return
+ /// Zero if the response was "OK", a positive value if the
+ /// the response was "Exx" where xx are two hex digits, or
+ /// -1 if the call is unsupported or any other unexpected
+ /// response was received.
+ //------------------------------------------------------------------
+ int
+ SendEnvironmentPacket (char const *name_equal_value);
+
+ int
+ SendLaunchArchPacket (const char *arch);
+ //------------------------------------------------------------------
+ /// Sends a "vAttach:PID" where PID is in hex.
+ ///
+ /// @param[in] pid
+ /// A process ID for the remote gdb server to attach to.
+ ///
+ /// @param[out] response
+ /// The response received from the gdb server. If the return
+ /// value is zero, \a response will contain a stop reply
+ /// packet.
+ ///
+ /// @return
+ /// Zero if the attach was successful, or an error indicating
+ /// an error code.
+ //------------------------------------------------------------------
+ int
+ SendAttach (lldb::pid_t pid,
+ StringExtractorGDBRemote& response);
+
+
+ //------------------------------------------------------------------
+ /// Sets the path to use for stdin/out/err for a process
+ /// that will be launched with the 'A' packet.
+ ///
+ /// @param[in] path
+ /// The path to use for stdin/out/err
+ ///
+ /// @return
+ /// Zero if the for success, or an error code for failure.
+ //------------------------------------------------------------------
+ int
+ SetSTDIN (char const *path);
+ int
+ SetSTDOUT (char const *path);
+ int
+ SetSTDERR (char const *path);
+
+ //------------------------------------------------------------------
+ /// Sets the disable ASLR flag to \a enable for a process that will
+ /// be launched with the 'A' packet.
+ ///
+ /// @param[in] enable
+ /// A boolean value indicating wether to disable ASLR or not.
+ ///
+ /// @return
+ /// Zero if the for success, or an error code for failure.
+ //------------------------------------------------------------------
+ int
+ SetDisableASLR (bool enable);
+
+ //------------------------------------------------------------------
+ /// Sets the working directory to \a path for a process that will
+ /// be launched with the 'A' packet.
+ ///
+ /// @param[in] path
+ /// The path to a directory to use when launching our processs
+ ///
+ /// @return
+ /// Zero if the for success, or an error code for failure.
+ //------------------------------------------------------------------
+ int
+ SetWorkingDir (char const *path);
+
+ lldb::addr_t
+ AllocateMemory (size_t size, uint32_t permissions);
+
+ bool
+ DeallocateMemory (lldb::addr_t addr);
+
+ lldb_private::Error
+ Detach (bool keep_stopped);
+
+ lldb_private::Error
+ GetMemoryRegionInfo (lldb::addr_t addr,
+ lldb_private::MemoryRegionInfo &range_info);
+
+ lldb_private::Error
+ GetWatchpointSupportInfo (uint32_t &num);
+
+ lldb_private::Error
+ GetWatchpointSupportInfo (uint32_t &num, bool& after);
+
+ lldb_private::Error
+ GetWatchpointsTriggerAfterInstruction (bool &after);
+
+ const lldb_private::ArchSpec &
+ GetHostArchitecture ();
+
+ const lldb_private::ArchSpec &
+ GetProcessArchitecture ();
+
+ bool
+ GetVContSupported (char flavor);
+
+ bool
+ GetVAttachOrWaitSupported ();
+
+ bool
+ GetSyncThreadStateSupported();
+
+ void
+ ResetDiscoverableSettings();
+
+ bool
+ GetHostInfo (bool force = false);
+
+ bool
+ GetOSVersion (uint32_t &major,
+ uint32_t &minor,
+ uint32_t &update);
+
+ bool
+ GetOSBuildString (std::string &s);
+
+ bool
+ GetOSKernelDescription (std::string &s);
+
+ lldb_private::ArchSpec
+ GetSystemArchitecture ();
+
+ bool
+ GetHostname (std::string &s);
+
+ lldb::addr_t
+ GetShlibInfoAddr();
+
+ bool
+ GetSupportsThreadSuffix ();
+
+ bool
+ GetProcessInfo (lldb::pid_t pid,
+ lldb_private::ProcessInstanceInfo &process_info);
+
+ uint32_t
+ FindProcesses (const lldb_private::ProcessInstanceInfoMatch &process_match_info,
+ lldb_private::ProcessInstanceInfoList &process_infos);
+
+ bool
+ GetUserName (uint32_t uid, std::string &name);
+
+ bool
+ GetGroupName (uint32_t gid, std::string &name);
+
+ bool
+ HasFullVContSupport ()
+ {
+ return GetVContSupported ('A');
+ }
+
+ bool
+ HasAnyVContSupport ()
+ {
+ return GetVContSupported ('a');
+ }
+
+ bool
+ GetStopReply (StringExtractorGDBRemote &response);
+
+ bool
+ GetThreadStopInfo (lldb::tid_t tid,
+ StringExtractorGDBRemote &response);
+
+ bool
+ SupportsGDBStoppointPacket (GDBStoppointType type)
+ {
+ switch (type)
+ {
+ case eBreakpointSoftware: return m_supports_z0;
+ case eBreakpointHardware: return m_supports_z1;
+ case eWatchpointWrite: return m_supports_z2;
+ case eWatchpointRead: return m_supports_z3;
+ case eWatchpointReadWrite: return m_supports_z4;
+ }
+ return false;
+ }
+ uint8_t
+ SendGDBStoppointTypePacket (GDBStoppointType type, // Type of breakpoint or watchpoint
+ bool insert, // Insert or remove?
+ lldb::addr_t addr, // Address of breakpoint or watchpoint
+ uint32_t length); // Byte Size of breakpoint or watchpoint
+
+ void
+ TestPacketSpeed (const uint32_t num_packets);
+
+ // This packet is for testing the speed of the interface only. Both
+ // the client and server need to support it, but this allows us to
+ // measure the packet speed without any other work being done on the
+ // other end and avoids any of that work affecting the packet send
+ // and response times.
+ bool
+ SendSpeedTestPacket (uint32_t send_size,
+ uint32_t recv_size);
+
+ bool
+ SetCurrentThread (uint64_t tid);
+
+ bool
+ SetCurrentThreadForRun (uint64_t tid);
+
+ lldb_private::LazyBool
+ SupportsAllocDeallocMemory () // const
+ {
+ // Uncomment this to have lldb pretend the debug server doesn't respond to alloc/dealloc memory packets.
+ // m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo;
+ return m_supports_alloc_dealloc_memory;
+ }
+
+ size_t
+ GetCurrentThreadIDs (std::vector<lldb::tid_t> &thread_ids,
+ bool &sequence_mutex_unavailable);
+
+ bool
+ GetInterruptWasSent () const
+ {
+ return m_interrupt_sent;
+ }
+
+ std::string
+ HarmonizeThreadIdsForProfileData (ProcessGDBRemote *process,
+ StringExtractorGDBRemote &inputStringExtractor);
+
+protected:
+
+ bool
+ GetCurrentProcessInfo ();
+
+ //------------------------------------------------------------------
+ // Classes that inherit from GDBRemoteCommunicationClient can see and modify these
+ //------------------------------------------------------------------
+ lldb_private::LazyBool m_supports_not_sending_acks;
+ lldb_private::LazyBool m_supports_thread_suffix;
+ lldb_private::LazyBool m_supports_threads_in_stop_reply;
+ lldb_private::LazyBool m_supports_vCont_all;
+ lldb_private::LazyBool m_supports_vCont_any;
+ lldb_private::LazyBool m_supports_vCont_c;
+ lldb_private::LazyBool m_supports_vCont_C;
+ lldb_private::LazyBool m_supports_vCont_s;
+ lldb_private::LazyBool m_supports_vCont_S;
+ lldb_private::LazyBool m_qHostInfo_is_valid;
+ lldb_private::LazyBool m_qProcessInfo_is_valid;
+ lldb_private::LazyBool m_supports_alloc_dealloc_memory;
+ lldb_private::LazyBool m_supports_memory_region_info;
+ lldb_private::LazyBool m_supports_watchpoint_support_info;
+ lldb_private::LazyBool m_supports_detach_stay_stopped;
+ lldb_private::LazyBool m_watchpoints_trigger_after_instruction;
+ lldb_private::LazyBool m_attach_or_wait_reply;
+ lldb_private::LazyBool m_prepare_for_reg_writing_reply;
+
+ bool
+ m_supports_qProcessInfoPID:1,
+ m_supports_qfProcessInfo:1,
+ m_supports_qUserName:1,
+ m_supports_qGroupName:1,
+ m_supports_qThreadStopInfo:1,
+ m_supports_z0:1,
+ m_supports_z1:1,
+ m_supports_z2:1,
+ m_supports_z3:1,
+ m_supports_z4:1;
+
+
+ lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations
+ lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for continue, step, etc
+
+
+ uint32_t m_num_supported_hardware_watchpoints;
+
+ // If we need to send a packet while the target is running, the m_async_XXX
+ // member variables take care of making this happen.
+ lldb_private::Mutex m_async_mutex;
+ lldb_private::Predicate<bool> m_async_packet_predicate;
+ std::string m_async_packet;
+ StringExtractorGDBRemote m_async_response;
+ int m_async_signal; // We were asked to deliver a signal to the inferior process.
+ bool m_interrupt_sent;
+ std::string m_partial_profile_data;
+ std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map;
+
+ lldb_private::ArchSpec m_host_arch;
+ lldb_private::ArchSpec m_process_arch;
+ uint32_t m_os_version_major;
+ uint32_t m_os_version_minor;
+ uint32_t m_os_version_update;
+ std::string m_os_build;
+ std::string m_os_kernel;
+ std::string m_hostname;
+
+ bool
+ DecodeProcessInfoResponse (StringExtractorGDBRemote &response,
+ lldb_private::ProcessInstanceInfo &process_info);
+private:
+ //------------------------------------------------------------------
+ // For GDBRemoteCommunicationClient only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationClient);
+};
+
+#endif // liblldb_GDBRemoteCommunicationClient_h_
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
new file mode 100644
index 0000000..3a14e9f
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
@@ -0,0 +1,839 @@
+//===-- GDBRemoteCommunicationServer.cpp ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "GDBRemoteCommunicationServer.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "llvm/ADT/Triple.h"
+#include "lldb/Interpreter/Args.h"
+#include "lldb/Core/ConnectionFileDescriptor.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Host/Endian.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Target/Process.h"
+
+// Project includes
+#include "Utility/StringExtractorGDBRemote.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// GDBRemoteCommunicationServer constructor
+//----------------------------------------------------------------------
+GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) :
+ GDBRemoteCommunication ("gdb-remote.server", "gdb-remote.server.rx_packet", is_platform),
+ m_async_thread (LLDB_INVALID_HOST_THREAD),
+ m_process_launch_info (),
+ m_process_launch_error (),
+ m_proc_infos (),
+ m_proc_infos_index (0),
+ m_lo_port_num (0),
+ m_hi_port_num (0)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer()
+{
+}
+
+
+//void *
+//GDBRemoteCommunicationServer::AsyncThread (void *arg)
+//{
+// GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer*) arg;
+//
+// Log *log;// (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
+// if (log)
+// log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID());
+//
+// StringExtractorGDBRemote packet;
+//
+// while ()
+// {
+// if (packet.
+// }
+//
+// if (log)
+// log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, arg, process->GetID());
+//
+// process->m_async_thread = LLDB_INVALID_HOST_THREAD;
+// return NULL;
+//}
+//
+bool
+GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,
+ Error &error,
+ bool &interrupt,
+ bool &quit)
+{
+ StringExtractorGDBRemote packet;
+ if (WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec))
+ {
+ const StringExtractorGDBRemote::ServerPacketType packet_type = packet.GetServerPacketType ();
+ switch (packet_type)
+ {
+ case StringExtractorGDBRemote::eServerPacketType_nack:
+ case StringExtractorGDBRemote::eServerPacketType_ack:
+ break;
+
+ case StringExtractorGDBRemote::eServerPacketType_invalid:
+ error.SetErrorString("invalid packet");
+ quit = true;
+ break;
+
+ case StringExtractorGDBRemote::eServerPacketType_interrupt:
+ error.SetErrorString("interrupt received");
+ interrupt = true;
+ break;
+
+ case StringExtractorGDBRemote::eServerPacketType_unimplemented:
+ return SendUnimplementedResponse (packet.GetStringRef().c_str()) > 0;
+
+ case StringExtractorGDBRemote::eServerPacketType_A:
+ return Handle_A (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qfProcessInfo:
+ return Handle_qfProcessInfo (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qsProcessInfo:
+ return Handle_qsProcessInfo (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qC:
+ return Handle_qC (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qHostInfo:
+ return Handle_qHostInfo (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer:
+ return Handle_qLaunchGDBServer (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess:
+ return Handle_qLaunchSuccess (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qGroupName:
+ return Handle_qGroupName (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID:
+ return Handle_qProcessInfoPID (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qSpeedTest:
+ return Handle_qSpeedTest (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_qUserName:
+ return Handle_qUserName (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_QEnvironment:
+ return Handle_QEnvironment (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR:
+ return Handle_QSetDisableASLR (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_QSetSTDIN:
+ return Handle_QSetSTDIN (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT:
+ return Handle_QSetSTDOUT (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_QSetSTDERR:
+ return Handle_QSetSTDERR (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir:
+ return Handle_QSetWorkingDir (packet);
+
+ case StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode:
+ return Handle_QStartNoAckMode (packet);
+ }
+ return true;
+ }
+ else
+ {
+ if (!IsConnected())
+ error.SetErrorString("lost connection");
+ else
+ error.SetErrorString("timeout");
+ }
+
+ return false;
+}
+
+size_t
+GDBRemoteCommunicationServer::SendUnimplementedResponse (const char *)
+{
+ // TODO: Log the packet we aren't handling...
+ return SendPacketNoLock ("", 0);
+}
+
+size_t
+GDBRemoteCommunicationServer::SendErrorResponse (uint8_t err)
+{
+ char packet[16];
+ int packet_len = ::snprintf (packet, sizeof(packet), "E%2.2x", err);
+ assert (packet_len < (int)sizeof(packet));
+ return SendPacketNoLock (packet, packet_len);
+}
+
+
+size_t
+GDBRemoteCommunicationServer::SendOKResponse ()
+{
+ return SendPacketNoLock ("OK", 2);
+}
+
+bool
+GDBRemoteCommunicationServer::HandshakeWithClient(Error *error_ptr)
+{
+ return GetAck();
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet)
+{
+ StreamString response;
+
+ // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00
+
+ ArchSpec host_arch (Host::GetArchitecture ());
+ const llvm::Triple &host_triple = host_arch.GetTriple();
+ response.PutCString("triple:");
+ response.PutCStringAsRawHex8(host_triple.getTriple().c_str());
+ response.Printf (";ptrsize:%u;",host_arch.GetAddressByteSize());
+
+ uint32_t cpu = host_arch.GetMachOCPUType();
+ uint32_t sub = host_arch.GetMachOCPUSubType();
+ if (cpu != LLDB_INVALID_CPUTYPE)
+ response.Printf ("cputype:%u;", cpu);
+ if (sub != LLDB_INVALID_CPUTYPE)
+ response.Printf ("cpusubtype:%u;", sub);
+
+ if (cpu == ArchSpec::kCore_arm_any)
+ response.Printf("watchpoint_exceptions_received:before;"); // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes.
+ else
+ response.Printf("watchpoint_exceptions_received:after;");
+
+ switch (lldb::endian::InlHostByteOrder())
+ {
+ case eByteOrderBig: response.PutCString ("endian:big;"); break;
+ case eByteOrderLittle: response.PutCString ("endian:little;"); break;
+ case eByteOrderPDP: response.PutCString ("endian:pdp;"); break;
+ default: response.PutCString ("endian:unknown;"); break;
+ }
+
+ uint32_t major = UINT32_MAX;
+ uint32_t minor = UINT32_MAX;
+ uint32_t update = UINT32_MAX;
+ if (Host::GetOSVersion (major, minor, update))
+ {
+ if (major != UINT32_MAX)
+ {
+ response.Printf("os_version:%u", major);
+ if (minor != UINT32_MAX)
+ {
+ response.Printf(".%u", minor);
+ if (update != UINT32_MAX)
+ response.Printf(".%u", update);
+ }
+ response.PutChar(';');
+ }
+ }
+
+ std::string s;
+ if (Host::GetOSBuildString (s))
+ {
+ response.PutCString ("os_build:");
+ response.PutCStringAsRawHex8(s.c_str());
+ response.PutChar(';');
+ }
+ if (Host::GetOSKernelDescription (s))
+ {
+ response.PutCString ("os_kernel:");
+ response.PutCStringAsRawHex8(s.c_str());
+ response.PutChar(';');
+ }
+ if (Host::GetHostname (s))
+ {
+ response.PutCString ("hostname:");
+ response.PutCStringAsRawHex8(s.c_str());
+ response.PutChar(';');
+ }
+
+ return SendPacketNoLock (response.GetData(), response.GetSize()) > 0;
+}
+
+static void
+CreateProcessInfoResponse (const ProcessInstanceInfo &proc_info, StreamString &response)
+{
+ response.Printf ("pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;",
+ proc_info.GetProcessID(),
+ proc_info.GetParentProcessID(),
+ proc_info.GetUserID(),
+ proc_info.GetGroupID(),
+ proc_info.GetEffectiveUserID(),
+ proc_info.GetEffectiveGroupID());
+ response.PutCString ("name:");
+ response.PutCStringAsRawHex8(proc_info.GetName());
+ response.PutChar(';');
+ const ArchSpec &proc_arch = proc_info.GetArchitecture();
+ if (proc_arch.IsValid())
+ {
+ const llvm::Triple &proc_triple = proc_arch.GetTriple();
+ response.PutCString("triple:");
+ response.PutCStringAsRawHex8(proc_triple.getTriple().c_str());
+ response.PutChar(';');
+ }
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qProcessInfoPID (StringExtractorGDBRemote &packet)
+{
+ // Packet format: "qProcessInfoPID:%i" where %i is the pid
+ packet.SetFilePos(::strlen ("qProcessInfoPID:"));
+ lldb::pid_t pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID);
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ {
+ ProcessInstanceInfo proc_info;
+ if (Host::GetProcessInfo(pid, proc_info))
+ {
+ StreamString response;
+ CreateProcessInfoResponse (proc_info, response);
+ return SendPacketNoLock (response.GetData(), response.GetSize());
+ }
+ }
+ return SendErrorResponse (1);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &packet)
+{
+ m_proc_infos_index = 0;
+ m_proc_infos.Clear();
+
+ ProcessInstanceInfoMatch match_info;
+ packet.SetFilePos(::strlen ("qfProcessInfo"));
+ if (packet.GetChar() == ':')
+ {
+
+ std::string key;
+ std::string value;
+ while (packet.GetNameColonValue(key, value))
+ {
+ bool success = true;
+ if (key.compare("name") == 0)
+ {
+ StringExtractor extractor;
+ extractor.GetStringRef().swap(value);
+ extractor.GetHexByteString (value);
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(value.c_str(), false);
+ }
+ else if (key.compare("name_match") == 0)
+ {
+ if (value.compare("equals") == 0)
+ {
+ match_info.SetNameMatchType (eNameMatchEquals);
+ }
+ else if (value.compare("starts_with") == 0)
+ {
+ match_info.SetNameMatchType (eNameMatchStartsWith);
+ }
+ else if (value.compare("ends_with") == 0)
+ {
+ match_info.SetNameMatchType (eNameMatchEndsWith);
+ }
+ else if (value.compare("contains") == 0)
+ {
+ match_info.SetNameMatchType (eNameMatchContains);
+ }
+ else if (value.compare("regex") == 0)
+ {
+ match_info.SetNameMatchType (eNameMatchRegularExpression);
+ }
+ else
+ {
+ success = false;
+ }
+ }
+ else if (key.compare("pid") == 0)
+ {
+ match_info.GetProcessInfo().SetProcessID (Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success));
+ }
+ else if (key.compare("parent_pid") == 0)
+ {
+ match_info.GetProcessInfo().SetParentProcessID (Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success));
+ }
+ else if (key.compare("uid") == 0)
+ {
+ match_info.GetProcessInfo().SetUserID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success));
+ }
+ else if (key.compare("gid") == 0)
+ {
+ match_info.GetProcessInfo().SetGroupID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success));
+ }
+ else if (key.compare("euid") == 0)
+ {
+ match_info.GetProcessInfo().SetEffectiveUserID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success));
+ }
+ else if (key.compare("egid") == 0)
+ {
+ match_info.GetProcessInfo().SetEffectiveGroupID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success));
+ }
+ else if (key.compare("all_users") == 0)
+ {
+ match_info.SetMatchAllUsers(Args::StringToBoolean(value.c_str(), false, &success));
+ }
+ else if (key.compare("triple") == 0)
+ {
+ match_info.GetProcessInfo().GetArchitecture().SetTriple (value.c_str(), NULL);
+ }
+ else
+ {
+ success = false;
+ }
+
+ if (!success)
+ return SendErrorResponse (2);
+ }
+ }
+
+ if (Host::FindProcesses (match_info, m_proc_infos))
+ {
+ // We found something, return the first item by calling the get
+ // subsequent process info packet handler...
+ return Handle_qsProcessInfo (packet);
+ }
+ return SendErrorResponse (3);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qsProcessInfo (StringExtractorGDBRemote &packet)
+{
+ if (m_proc_infos_index < m_proc_infos.GetSize())
+ {
+ StreamString response;
+ CreateProcessInfoResponse (m_proc_infos.GetProcessInfoAtIndex(m_proc_infos_index), response);
+ ++m_proc_infos_index;
+ return SendPacketNoLock (response.GetData(), response.GetSize());
+ }
+ return SendErrorResponse (4);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qUserName (StringExtractorGDBRemote &packet)
+{
+ // Packet format: "qUserName:%i" where %i is the uid
+ packet.SetFilePos(::strlen ("qUserName:"));
+ uint32_t uid = packet.GetU32 (UINT32_MAX);
+ if (uid != UINT32_MAX)
+ {
+ std::string name;
+ if (Host::GetUserName (uid, name))
+ {
+ StreamString response;
+ response.PutCStringAsRawHex8 (name.c_str());
+ return SendPacketNoLock (response.GetData(), response.GetSize());
+ }
+ }
+ return SendErrorResponse (5);
+
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qGroupName (StringExtractorGDBRemote &packet)
+{
+ // Packet format: "qGroupName:%i" where %i is the gid
+ packet.SetFilePos(::strlen ("qGroupName:"));
+ uint32_t gid = packet.GetU32 (UINT32_MAX);
+ if (gid != UINT32_MAX)
+ {
+ std::string name;
+ if (Host::GetGroupName (gid, name))
+ {
+ StreamString response;
+ response.PutCStringAsRawHex8 (name.c_str());
+ return SendPacketNoLock (response.GetData(), response.GetSize());
+ }
+ }
+ return SendErrorResponse (6);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qSpeedTest (StringExtractorGDBRemote &packet)
+{
+ packet.SetFilePos(::strlen ("qSpeedTest:"));
+
+ std::string key;
+ std::string value;
+ bool success = packet.GetNameColonValue(key, value);
+ if (success && key.compare("response_size") == 0)
+ {
+ uint32_t response_size = Args::StringToUInt32(value.c_str(), 0, 0, &success);
+ if (success)
+ {
+ if (response_size == 0)
+ return SendOKResponse();
+ StreamString response;
+ uint32_t bytes_left = response_size;
+ response.PutCString("data:");
+ while (bytes_left > 0)
+ {
+ if (bytes_left >= 26)
+ {
+ response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ bytes_left -= 26;
+ }
+ else
+ {
+ response.Printf ("%*.*s;", bytes_left, bytes_left, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ bytes_left = 0;
+ }
+ }
+ return SendPacketNoLock (response.GetData(), response.GetSize());
+ }
+ }
+ return SendErrorResponse (7);
+}
+
+
+static void *
+AcceptPortFromInferior (void *arg)
+{
+ const char *connect_url = (const char *)arg;
+ ConnectionFileDescriptor file_conn;
+ Error error;
+ if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess)
+ {
+ char pid_str[256];
+ ::memset (pid_str, 0, sizeof(pid_str));
+ ConnectionStatus status;
+ const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL);
+ if (pid_str_len > 0)
+ {
+ int pid = atoi (pid_str);
+ return (void *)(intptr_t)pid;
+ }
+ }
+ return NULL;
+}
+//
+//static bool
+//WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds)
+//{
+// const int time_delta_usecs = 100000;
+// const int num_retries = timeout_in_seconds/time_delta_usecs;
+// for (int i=0; i<num_retries; i++)
+// {
+// struct proc_bsdinfo bsd_info;
+// int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO,
+// (uint64_t) 0,
+// &bsd_info,
+// PROC_PIDTBSDINFO_SIZE);
+//
+// switch (error)
+// {
+// case EINVAL:
+// case ENOTSUP:
+// case ESRCH:
+// case EPERM:
+// return false;
+//
+// default:
+// break;
+//
+// case 0:
+// if (bsd_info.pbi_status == SSTOP)
+// return true;
+// }
+// ::usleep (time_delta_usecs);
+// }
+// return false;
+//}
+
+bool
+GDBRemoteCommunicationServer::Handle_A (StringExtractorGDBRemote &packet)
+{
+ // The 'A' packet is the most over designed packet ever here with
+ // redundant argument indexes, redundant argument lengths and needed hex
+ // encoded argument string values. Really all that is needed is a comma
+ // separated hex encoded argument value list, but we will stay true to the
+ // documented version of the 'A' packet here...
+
+ packet.SetFilePos(1); // Skip the 'A'
+ bool success = true;
+ while (success && packet.GetBytesLeft() > 0)
+ {
+ // Decode the decimal argument string length. This length is the
+ // number of hex nibbles in the argument string value.
+ const uint32_t arg_len = packet.GetU32(UINT32_MAX);
+ if (arg_len == UINT32_MAX)
+ success = false;
+ else
+ {
+ // Make sure the argument hex string length is followed by a comma
+ if (packet.GetChar() != ',')
+ success = false;
+ else
+ {
+ // Decode the argument index. We ignore this really becuase
+ // who would really send down the arguments in a random order???
+ const uint32_t arg_idx = packet.GetU32(UINT32_MAX);
+ if (arg_idx == UINT32_MAX)
+ success = false;
+ else
+ {
+ // Make sure the argument index is followed by a comma
+ if (packet.GetChar() != ',')
+ success = false;
+ else
+ {
+ // Decode the argument string value from hex bytes
+ // back into a UTF8 string and make sure the length
+ // matches the one supplied in the packet
+ std::string arg;
+ if (packet.GetHexByteString(arg) != (arg_len / 2))
+ success = false;
+ else
+ {
+ // If there are any bytes lft
+ if (packet.GetBytesLeft())
+ {
+ if (packet.GetChar() != ',')
+ success = false;
+ }
+
+ if (success)
+ {
+ if (arg_idx == 0)
+ m_process_launch_info.GetExecutableFile().SetFile(arg.c_str(), false);
+ m_process_launch_info.GetArguments().AppendArgument(arg.c_str());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ {
+ m_process_launch_info.GetFlags().Set (eLaunchFlagDebug);
+ m_process_launch_error = Host::LaunchProcess (m_process_launch_info);
+ if (m_process_launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
+ {
+ return SendOKResponse ();
+ }
+ }
+ return SendErrorResponse (8);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet)
+{
+ lldb::pid_t pid = m_process_launch_info.GetProcessID();
+ StreamString response;
+ response.Printf("QC%" PRIx64, pid);
+ if (m_is_platform)
+ {
+ // If we launch a process and this GDB server is acting as a platform,
+ // then we need to clear the process launch state so we can start
+ // launching another process. In order to launch a process a bunch or
+ // packets need to be sent: environment packets, working directory,
+ // disable ASLR, and many more settings. When we launch a process we
+ // then need to know when to clear this information. Currently we are
+ // selecting the 'qC' packet as that packet which seems to make the most
+ // sense.
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ {
+ m_process_launch_info.Clear();
+ }
+ }
+ return SendPacketNoLock (response.GetData(), response.GetSize());
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet)
+{
+ // Spawn a local debugserver as a platform so we can then attach or launch
+ // a process...
+
+ if (m_is_platform)
+ {
+ // Sleep and wait a bit for debugserver to start to listen...
+ ConnectionFileDescriptor file_conn;
+ char connect_url[PATH_MAX];
+ Error error;
+ char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX";
+ if (::mktemp (unix_socket_name) == NULL)
+ {
+ error.SetErrorString ("failed to make temporary path for a unix socket");
+ }
+ else
+ {
+ ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name);
+ // Spawn a new thread to accept the port that gets bound after
+ // binding to port 0 (zero).
+ lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name,
+ AcceptPortFromInferior,
+ connect_url,
+ &error);
+
+ if (IS_VALID_LLDB_HOST_THREAD(accept_thread))
+ {
+ // Spawn a debugserver and try to get
+ ProcessLaunchInfo debugserver_launch_info;
+ error = StartDebugserverProcess ("localhost:0",
+ unix_socket_name,
+ debugserver_launch_info);
+
+ lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID();
+ if (error.Success())
+ {
+ bool success = false;
+
+ thread_result_t accept_thread_result = NULL;
+ if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error))
+ {
+ if (accept_thread_result)
+ {
+ uint16_t port = (intptr_t)accept_thread_result;
+ char response[256];
+ const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port);
+ assert (response_len < (int)sizeof(response));
+ //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID();
+ success = SendPacketNoLock (response, response_len) > 0;
+ }
+ }
+ ::unlink (unix_socket_name);
+
+ if (!success)
+ {
+ if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ ::kill (debugserver_pid, SIGINT);
+ }
+ return success;
+ }
+ }
+ }
+ }
+ return SendErrorResponse (13);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_qLaunchSuccess (StringExtractorGDBRemote &packet)
+{
+ if (m_process_launch_error.Success())
+ return SendOKResponse();
+ StreamString response;
+ response.PutChar('E');
+ response.PutCString(m_process_launch_error.AsCString("<unknown error>"));
+ return SendPacketNoLock (response.GetData(), response.GetSize());
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_QEnvironment (StringExtractorGDBRemote &packet)
+{
+ packet.SetFilePos(::strlen ("QEnvironment:"));
+ const uint32_t bytes_left = packet.GetBytesLeft();
+ if (bytes_left > 0)
+ {
+ m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek());
+ return SendOKResponse ();
+ }
+ return SendErrorResponse (9);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_QSetDisableASLR (StringExtractorGDBRemote &packet)
+{
+ packet.SetFilePos(::strlen ("QSetDisableASLR:"));
+ if (packet.GetU32(0))
+ m_process_launch_info.GetFlags().Set (eLaunchFlagDisableASLR);
+ else
+ m_process_launch_info.GetFlags().Clear (eLaunchFlagDisableASLR);
+ return SendOKResponse ();
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet)
+{
+ packet.SetFilePos(::strlen ("QSetWorkingDir:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ m_process_launch_info.SwapWorkingDirectory (path);
+ return SendOKResponse ();
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet)
+{
+ packet.SetFilePos(::strlen ("QSetSTDIN:"));
+ ProcessLaunchInfo::FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDIN_FILENO, path.c_str(), read, write))
+ {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse ();
+ }
+ return SendErrorResponse (10);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_QSetSTDOUT (StringExtractorGDBRemote &packet)
+{
+ packet.SetFilePos(::strlen ("QSetSTDOUT:"));
+ ProcessLaunchInfo::FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = true;
+ const bool write = false;
+ if (file_action.Open(STDOUT_FILENO, path.c_str(), read, write))
+ {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse ();
+ }
+ return SendErrorResponse (11);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_QSetSTDERR (StringExtractorGDBRemote &packet)
+{
+ packet.SetFilePos(::strlen ("QSetSTDERR:"));
+ ProcessLaunchInfo::FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = true;
+ const bool write = false;
+ if (file_action.Open(STDERR_FILENO, path.c_str(), read, write))
+ {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse ();
+ }
+ return SendErrorResponse (12);
+}
+
+bool
+GDBRemoteCommunicationServer::Handle_QStartNoAckMode (StringExtractorGDBRemote &packet)
+{
+ // Send response first before changing m_send_acks to we ack this packet
+ SendOKResponse ();
+ m_send_acks = false;
+ return true;
+}
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
new file mode 100644
index 0000000..cce0e4e
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
@@ -0,0 +1,147 @@
+//===-- GDBRemoteCommunicationServer.h --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationServer_h_
+#define liblldb_GDBRemoteCommunicationServer_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Target/Process.h"
+
+#include "GDBRemoteCommunication.h"
+
+class ProcessGDBRemote;
+class StringExtractorGDBRemote;
+
+class GDBRemoteCommunicationServer : public GDBRemoteCommunication
+{
+public:
+ enum
+ {
+ eBroadcastBitRunPacketSent = kLoUserBroadcastBit
+ };
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ GDBRemoteCommunicationServer(bool is_platform);
+
+ virtual
+ ~GDBRemoteCommunicationServer();
+
+ bool
+ GetPacketAndSendResponse (uint32_t timeout_usec,
+ lldb_private::Error &error,
+ bool &interrupt,
+ bool &quit);
+
+ virtual bool
+ GetThreadSuffixSupported ()
+ {
+ return true;
+ }
+
+ // After connecting, do a little handshake with the client to make sure
+ // we are at least communicating
+ bool
+ HandshakeWithClient (lldb_private::Error *error_ptr);
+
+ // Set both ports to zero to let the platform automatically bind to
+ // a port chosen by the OS.
+ void
+ SetPortRange (uint16_t lo_port_num, uint16_t hi_port_num)
+ {
+ m_lo_port_num = lo_port_num;
+ m_hi_port_num = hi_port_num;
+ }
+
+protected:
+ //typedef std::map<uint16_t, lldb::pid_t> PortToPIDMap;
+
+ lldb::thread_t m_async_thread;
+ lldb_private::ProcessLaunchInfo m_process_launch_info;
+ lldb_private::Error m_process_launch_error;
+ lldb_private::ProcessInstanceInfoList m_proc_infos;
+ uint32_t m_proc_infos_index;
+ uint16_t m_lo_port_num;
+ uint16_t m_hi_port_num;
+ //PortToPIDMap m_port_to_pid_map;
+
+ size_t
+ SendUnimplementedResponse (const char *packet);
+
+ size_t
+ SendErrorResponse (uint8_t error);
+
+ size_t
+ SendOKResponse ();
+
+ bool
+ Handle_A (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qLaunchSuccess (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qHostInfo (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qProcessInfoPID (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qfProcessInfo (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qsProcessInfo (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qC (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qUserName (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qGroupName (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_qSpeedTest (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_QEnvironment (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_QSetDisableASLR (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_QSetWorkingDir (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_QStartNoAckMode (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_QSetSTDIN (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_QSetSTDOUT (StringExtractorGDBRemote &packet);
+
+ bool
+ Handle_QSetSTDERR (StringExtractorGDBRemote &packet);
+
+private:
+ //------------------------------------------------------------------
+ // For GDBRemoteCommunicationServer only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationServer);
+};
+
+#endif // liblldb_GDBRemoteCommunicationServer_h_
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
new file mode 100644
index 0000000..b1612a5
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
@@ -0,0 +1,971 @@
+//===-- GDBRemoteRegisterContext.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteRegisterContext.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Utils.h"
+// Project includes
+#include "Utility/StringExtractorGDBRemote.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "ThreadGDBRemote.h"
+#include "Utility/ARM_GCC_Registers.h"
+#include "Utility/ARM_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// GDBRemoteRegisterContext constructor
+//----------------------------------------------------------------------
+GDBRemoteRegisterContext::GDBRemoteRegisterContext
+(
+ ThreadGDBRemote &thread,
+ uint32_t concrete_frame_idx,
+ GDBRemoteDynamicRegisterInfo &reg_info,
+ bool read_all_at_once
+) :
+ RegisterContext (thread, concrete_frame_idx),
+ m_reg_info (reg_info),
+ m_reg_valid (),
+ m_reg_data (),
+ m_read_all_at_once (read_all_at_once)
+{
+ // Resize our vector of bools to contain one bool for every register.
+ // We will use these boolean values to know when a register value
+ // is valid in m_reg_data.
+ m_reg_valid.resize (reg_info.GetNumRegisters());
+
+ // Make a heap based buffer that is big enough to store all registers
+ DataBufferSP reg_data_sp(new DataBufferHeap (reg_info.GetRegisterDataByteSize(), 0));
+ m_reg_data.SetData (reg_data_sp);
+
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+GDBRemoteRegisterContext::~GDBRemoteRegisterContext()
+{
+}
+
+void
+GDBRemoteRegisterContext::InvalidateAllRegisters ()
+{
+ SetAllRegisterValid (false);
+}
+
+void
+GDBRemoteRegisterContext::SetAllRegisterValid (bool b)
+{
+ std::vector<bool>::iterator pos, end = m_reg_valid.end();
+ for (pos = m_reg_valid.begin(); pos != end; ++pos)
+ *pos = b;
+}
+
+size_t
+GDBRemoteRegisterContext::GetRegisterCount ()
+{
+ return m_reg_info.GetNumRegisters ();
+}
+
+const RegisterInfo *
+GDBRemoteRegisterContext::GetRegisterInfoAtIndex (size_t reg)
+{
+ return m_reg_info.GetRegisterInfoAtIndex (reg);
+}
+
+size_t
+GDBRemoteRegisterContext::GetRegisterSetCount ()
+{
+ return m_reg_info.GetNumRegisterSets ();
+}
+
+
+
+const RegisterSet *
+GDBRemoteRegisterContext::GetRegisterSet (size_t reg_set)
+{
+ return m_reg_info.GetRegisterSet (reg_set);
+}
+
+
+
+bool
+GDBRemoteRegisterContext::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value)
+{
+ // Read the register
+ if (ReadRegisterBytes (reg_info, m_reg_data))
+ {
+ const bool partial_data_ok = false;
+ Error error (value.SetValueFromData(reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok));
+ return error.Success();
+ }
+ return false;
+}
+
+bool
+GDBRemoteRegisterContext::PrivateSetRegisterValue (uint32_t reg, StringExtractor &response)
+{
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL)
+ return false;
+
+ // Invalidate if needed
+ InvalidateIfNeeded(false);
+
+ const uint32_t reg_byte_size = reg_info->byte_size;
+ const size_t bytes_copied = response.GetHexBytes (const_cast<uint8_t*>(m_reg_data.PeekData(reg_info->byte_offset, reg_byte_size)), reg_byte_size, '\xcc');
+ bool success = bytes_copied == reg_byte_size;
+ if (success)
+ {
+ SetRegisterIsValid(reg, true);
+ }
+ else if (bytes_copied > 0)
+ {
+ // Only set register is valid to false if we copied some bytes, else
+ // leave it as it was.
+ SetRegisterIsValid(reg, false);
+ }
+ return success;
+}
+
+// Helper function for GDBRemoteRegisterContext::ReadRegisterBytes().
+bool
+GDBRemoteRegisterContext::GetPrimordialRegister(const lldb_private::RegisterInfo *reg_info,
+ GDBRemoteCommunicationClient &gdb_comm)
+{
+ char packet[64];
+ StringExtractorGDBRemote response;
+ int packet_len = 0;
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ if (gdb_comm.GetThreadSuffixSupported())
+ packet_len = ::snprintf (packet, sizeof(packet), "p%x;thread:%4.4" PRIx64 ";", reg, m_thread.GetProtocolID());
+ else
+ packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg);
+ assert (packet_len < ((int)sizeof(packet) - 1));
+ if (gdb_comm.SendPacketAndWaitForResponse(packet, response, false))
+ return PrivateSetRegisterValue (reg, response);
+
+ return false;
+}
+bool
+GDBRemoteRegisterContext::ReadRegisterBytes (const RegisterInfo *reg_info, DataExtractor &data)
+{
+ ExecutionContext exe_ctx (CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == NULL || thread == NULL)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ InvalidateIfNeeded(false);
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (!GetRegisterIsValid(reg))
+ {
+ Mutex::Locker locker;
+ if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for read register."))
+ {
+ const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported();
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID()))
+ {
+ char packet[64];
+ StringExtractorGDBRemote response;
+ int packet_len = 0;
+ if (m_read_all_at_once)
+ {
+ // Get all registers in one packet
+ if (thread_suffix_supported)
+ packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
+ else
+ packet_len = ::snprintf (packet, sizeof(packet), "g");
+ assert (packet_len < ((int)sizeof(packet) - 1));
+ if (gdb_comm.SendPacketAndWaitForResponse(packet, response, false))
+ {
+ if (response.IsNormalResponse())
+ if (response.GetHexBytes ((void *)m_reg_data.GetDataStart(), m_reg_data.GetByteSize(), '\xcc') == m_reg_data.GetByteSize())
+ SetAllRegisterValid (true);
+ }
+ }
+ else if (reg_info->value_regs)
+ {
+ // Process this composite register request by delegating to the constituent
+ // primordial registers.
+
+ // Index of the primordial register.
+ bool success = true;
+ for (uint32_t idx = 0; success; ++idx)
+ {
+ const uint32_t prim_reg = reg_info->value_regs[idx];
+ if (prim_reg == LLDB_INVALID_REGNUM)
+ break;
+ // We have a valid primordial regsiter as our constituent.
+ // Grab the corresponding register info.
+ const RegisterInfo *prim_reg_info = GetRegisterInfoAtIndex(prim_reg);
+ if (prim_reg_info == NULL)
+ success = false;
+ else
+ {
+ // Read the containing register if it hasn't already been read
+ if (!GetRegisterIsValid(prim_reg))
+ success = GetPrimordialRegister(prim_reg_info, gdb_comm);
+ }
+ }
+
+ if (success)
+ {
+ // If we reach this point, all primordial register requests have succeeded.
+ // Validate this composite register.
+ SetRegisterIsValid (reg_info, true);
+ }
+ }
+ else
+ {
+ // Get each register individually
+ GetPrimordialRegister(reg_info, gdb_comm);
+ }
+ }
+ }
+ else
+ {
+#if LLDB_CONFIGURATION_DEBUG
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ Host::SetCrashDescription (strm.GetData());
+ assert (!"Didn't get sequence mutex for read register.");
+#else
+ Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS));
+ if (log)
+ {
+ if (log->GetVerbose())
+ {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending read register for \"%s\":\n%s", reg_info->name, strm.GetData());
+ }
+ else
+ {
+ log->Printf("error: failed to get packet sequence mutex, not sending read register for \"%s\"", reg_info->name);
+ }
+ }
+#endif
+ }
+
+ // Make sure we got a valid register value after reading it
+ if (!GetRegisterIsValid(reg))
+ return false;
+ }
+
+ if (&data != &m_reg_data)
+ {
+ // If we aren't extracting into our own buffer (which
+ // only happens when this function is called from
+ // ReadRegisterValue(uint32_t, Scalar&)) then
+ // we transfer bytes from our buffer into the data
+ // buffer that was passed in
+ data.SetByteOrder (m_reg_data.GetByteOrder());
+ data.SetData (m_reg_data, reg_info->byte_offset, reg_info->byte_size);
+ }
+ return true;
+}
+
+bool
+GDBRemoteRegisterContext::WriteRegister (const RegisterInfo *reg_info,
+ const RegisterValue &value)
+{
+ DataExtractor data;
+ if (value.GetData (data))
+ return WriteRegisterBytes (reg_info, data, 0);
+ return false;
+}
+
+// Helper function for GDBRemoteRegisterContext::WriteRegisterBytes().
+bool
+GDBRemoteRegisterContext::SetPrimordialRegister(const lldb_private::RegisterInfo *reg_info,
+ GDBRemoteCommunicationClient &gdb_comm)
+{
+ StreamString packet;
+ StringExtractorGDBRemote response;
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ packet.Printf ("P%x=", reg);
+ packet.PutBytesAsRawHex8 (m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size),
+ reg_info->byte_size,
+ lldb::endian::InlHostByteOrder(),
+ lldb::endian::InlHostByteOrder());
+
+ if (gdb_comm.GetThreadSuffixSupported())
+ packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
+
+ // Invalidate just this register
+ SetRegisterIsValid(reg, false);
+ if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
+ packet.GetString().size(),
+ response,
+ false))
+ {
+ if (response.IsOKResponse())
+ return true;
+ }
+ return false;
+}
+
+void
+GDBRemoteRegisterContext::SyncThreadState(Process *process)
+{
+ // NB. We assume our caller has locked the sequence mutex.
+
+ GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *) process)->GetGDBRemote());
+ if (!gdb_comm.GetSyncThreadStateSupported())
+ return;
+
+ StreamString packet;
+ StringExtractorGDBRemote response;
+ packet.Printf ("QSyncThreadState:%4.4" PRIx64 ";", m_thread.GetProtocolID());
+ if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
+ packet.GetString().size(),
+ response,
+ false))
+ {
+ if (response.IsOKResponse())
+ InvalidateAllRegisters();
+ }
+}
+
+bool
+GDBRemoteRegisterContext::WriteRegisterBytes (const lldb_private::RegisterInfo *reg_info, DataExtractor &data, uint32_t data_offset)
+{
+ ExecutionContext exe_ctx (CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == NULL || thread == NULL)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote());
+// FIXME: This check isn't right because IsRunning checks the Public state, but this
+// is work you need to do - for instance in ShouldStop & friends - before the public
+// state has been changed.
+// if (gdb_comm.IsRunning())
+// return false;
+
+ // Grab a pointer to where we are going to put this register
+ uint8_t *dst = const_cast<uint8_t*>(m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size));
+
+ if (dst == NULL)
+ return false;
+
+
+ if (data.CopyByteOrderedData (data_offset, // src offset
+ reg_info->byte_size, // src length
+ dst, // dst
+ reg_info->byte_size, // dst length
+ m_reg_data.GetByteOrder())) // dst byte order
+ {
+ Mutex::Locker locker;
+ if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for write register."))
+ {
+ const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported();
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID()))
+ {
+ StreamString packet;
+ StringExtractorGDBRemote response;
+
+ if (m_read_all_at_once)
+ {
+ // Set all registers in one packet
+ packet.PutChar ('G');
+ packet.PutBytesAsRawHex8 (m_reg_data.GetDataStart(),
+ m_reg_data.GetByteSize(),
+ lldb::endian::InlHostByteOrder(),
+ lldb::endian::InlHostByteOrder());
+
+ if (thread_suffix_supported)
+ packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
+
+ // Invalidate all register values
+ InvalidateIfNeeded (true);
+
+ if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
+ packet.GetString().size(),
+ response,
+ false))
+ {
+ SetAllRegisterValid (false);
+ if (response.IsOKResponse())
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ bool success = true;
+
+ if (reg_info->value_regs)
+ {
+ // This register is part of another register. In this case we read the actual
+ // register data for any "value_regs", and once all that data is read, we will
+ // have enough data in our register context bytes for the value of this register
+
+ // Invalidate this composite register first.
+
+ for (uint32_t idx = 0; success; ++idx)
+ {
+ const uint32_t reg = reg_info->value_regs[idx];
+ if (reg == LLDB_INVALID_REGNUM)
+ break;
+ // We have a valid primordial regsiter as our constituent.
+ // Grab the corresponding register info.
+ const RegisterInfo *value_reg_info = GetRegisterInfoAtIndex(reg);
+ if (value_reg_info == NULL)
+ success = false;
+ else
+ success = SetPrimordialRegister(value_reg_info, gdb_comm);
+ }
+ }
+ else
+ {
+ // This is an actual register, write it
+ success = SetPrimordialRegister(reg_info, gdb_comm);
+ }
+
+ // Check if writing this register will invalidate any other register values?
+ // If so, invalidate them
+ if (reg_info->invalidate_regs)
+ {
+ for (uint32_t idx = 0, reg = reg_info->invalidate_regs[0];
+ reg != LLDB_INVALID_REGNUM;
+ reg = reg_info->invalidate_regs[++idx])
+ {
+ SetRegisterIsValid(reg, false);
+ }
+ }
+
+ return success;
+ }
+ }
+ }
+ else
+ {
+ Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS));
+ if (log)
+ {
+ if (log->GetVerbose())
+ {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending write register for \"%s\":\n%s", reg_info->name, strm.GetData());
+ }
+ else
+ log->Printf("error: failed to get packet sequence mutex, not sending write register for \"%s\"", reg_info->name);
+ }
+ }
+ }
+ return false;
+}
+
+
+bool
+GDBRemoteRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ ExecutionContext exe_ctx (CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == NULL || thread == NULL)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ StringExtractorGDBRemote response;
+
+ Mutex::Locker locker;
+ if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for read all registers."))
+ {
+ SyncThreadState(process);
+
+ char packet[32];
+ const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported();
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID()))
+ {
+ int packet_len = 0;
+ if (thread_suffix_supported)
+ packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64, m_thread.GetProtocolID());
+ else
+ packet_len = ::snprintf (packet, sizeof(packet), "g");
+ assert (packet_len < ((int)sizeof(packet) - 1));
+
+ if (gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false))
+ {
+ if (response.IsErrorResponse())
+ return false;
+
+ std::string &response_str = response.GetStringRef();
+ if (isxdigit(response_str[0]))
+ {
+ response_str.insert(0, 1, 'G');
+ if (thread_suffix_supported)
+ {
+ char thread_id_cstr[64];
+ ::snprintf (thread_id_cstr, sizeof(thread_id_cstr), ";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
+ response_str.append (thread_id_cstr);
+ }
+ data_sp.reset (new DataBufferHeap (response_str.c_str(), response_str.size()));
+ return true;
+ }
+ }
+ }
+ }
+ else
+ {
+ Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS));
+ if (log)
+ {
+ if (log->GetVerbose())
+ {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending read all registers:\n%s", strm.GetData());
+ }
+ else
+ log->Printf("error: failed to get packet sequence mutex, not sending read all registers");
+ }
+ }
+
+ data_sp.reset();
+ return false;
+}
+
+bool
+GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ if (!data_sp || data_sp->GetBytes() == NULL || data_sp->GetByteSize() == 0)
+ return false;
+
+ ExecutionContext exe_ctx (CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == NULL || thread == NULL)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ StringExtractorGDBRemote response;
+ Mutex::Locker locker;
+ if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for write all registers."))
+ {
+ const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported();
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID()))
+ {
+ // The data_sp contains the entire G response packet including the
+ // G, and if the thread suffix is supported, it has the thread suffix
+ // as well.
+ const char *G_packet = (const char *)data_sp->GetBytes();
+ size_t G_packet_len = data_sp->GetByteSize();
+ if (gdb_comm.SendPacketAndWaitForResponse (G_packet,
+ G_packet_len,
+ response,
+ false))
+ {
+ if (response.IsOKResponse())
+ return true;
+ else if (response.IsErrorResponse())
+ {
+ uint32_t num_restored = 0;
+ // We need to manually go through all of the registers and
+ // restore them manually
+
+ response.GetStringRef().assign (G_packet, G_packet_len);
+ response.SetFilePos(1); // Skip the leading 'G'
+ DataBufferHeap buffer (m_reg_data.GetByteSize(), 0);
+ DataExtractor restore_data (buffer.GetBytes(),
+ buffer.GetByteSize(),
+ m_reg_data.GetByteOrder(),
+ m_reg_data.GetAddressByteSize());
+
+ const uint32_t bytes_extracted = response.GetHexBytes ((void *)restore_data.GetDataStart(),
+ restore_data.GetByteSize(),
+ '\xcc');
+
+ if (bytes_extracted < restore_data.GetByteSize())
+ restore_data.SetData(restore_data.GetDataStart(), bytes_extracted, m_reg_data.GetByteOrder());
+
+ //ReadRegisterBytes (const RegisterInfo *reg_info, RegisterValue &value, DataExtractor &data)
+ const RegisterInfo *reg_info;
+ // We have to march the offset of each register along in the
+ // buffer to make sure we get the right offset.
+ uint32_t reg_byte_offset = 0;
+ for (uint32_t reg_idx=0; (reg_info = GetRegisterInfoAtIndex (reg_idx)) != NULL; ++reg_idx, reg_byte_offset += reg_info->byte_size)
+ {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ // Skip composite registers.
+ if (reg_info->value_regs)
+ continue;
+
+ // Only write down the registers that need to be written
+ // if we are going to be doing registers individually.
+ bool write_reg = true;
+ const uint32_t reg_byte_size = reg_info->byte_size;
+
+ const char *restore_src = (const char *)restore_data.PeekData(reg_byte_offset, reg_byte_size);
+ if (restore_src)
+ {
+ if (GetRegisterIsValid(reg))
+ {
+ const char *current_src = (const char *)m_reg_data.PeekData(reg_byte_offset, reg_byte_size);
+ if (current_src)
+ write_reg = memcmp (current_src, restore_src, reg_byte_size) != 0;
+ }
+
+ if (write_reg)
+ {
+ StreamString packet;
+ packet.Printf ("P%x=", reg);
+ packet.PutBytesAsRawHex8 (restore_src,
+ reg_byte_size,
+ lldb::endian::InlHostByteOrder(),
+ lldb::endian::InlHostByteOrder());
+
+ if (thread_suffix_supported)
+ packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
+
+ SetRegisterIsValid(reg, false);
+ if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
+ packet.GetString().size(),
+ response,
+ false))
+ {
+ if (response.IsOKResponse())
+ ++num_restored;
+ }
+ }
+ }
+ }
+ return num_restored > 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS));
+ if (log)
+ {
+ if (log->GetVerbose())
+ {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending write all registers:\n%s", strm.GetData());
+ }
+ else
+ log->Printf("error: failed to get packet sequence mutex, not sending write all registers");
+ }
+ }
+ return false;
+}
+
+
+uint32_t
+GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ return m_reg_info.ConvertRegisterKindToRegisterNumber (kind, num);
+}
+
+void
+GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch)
+{
+ // For Advanced SIMD and VFP register mapping.
+ static uint32_t g_d0_regs[] = { 26, 27, LLDB_INVALID_REGNUM }; // (s0, s1)
+ static uint32_t g_d1_regs[] = { 28, 29, LLDB_INVALID_REGNUM }; // (s2, s3)
+ static uint32_t g_d2_regs[] = { 30, 31, LLDB_INVALID_REGNUM }; // (s4, s5)
+ static uint32_t g_d3_regs[] = { 32, 33, LLDB_INVALID_REGNUM }; // (s6, s7)
+ static uint32_t g_d4_regs[] = { 34, 35, LLDB_INVALID_REGNUM }; // (s8, s9)
+ static uint32_t g_d5_regs[] = { 36, 37, LLDB_INVALID_REGNUM }; // (s10, s11)
+ static uint32_t g_d6_regs[] = { 38, 39, LLDB_INVALID_REGNUM }; // (s12, s13)
+ static uint32_t g_d7_regs[] = { 40, 41, LLDB_INVALID_REGNUM }; // (s14, s15)
+ static uint32_t g_d8_regs[] = { 42, 43, LLDB_INVALID_REGNUM }; // (s16, s17)
+ static uint32_t g_d9_regs[] = { 44, 45, LLDB_INVALID_REGNUM }; // (s18, s19)
+ static uint32_t g_d10_regs[] = { 46, 47, LLDB_INVALID_REGNUM }; // (s20, s21)
+ static uint32_t g_d11_regs[] = { 48, 49, LLDB_INVALID_REGNUM }; // (s22, s23)
+ static uint32_t g_d12_regs[] = { 50, 51, LLDB_INVALID_REGNUM }; // (s24, s25)
+ static uint32_t g_d13_regs[] = { 52, 53, LLDB_INVALID_REGNUM }; // (s26, s27)
+ static uint32_t g_d14_regs[] = { 54, 55, LLDB_INVALID_REGNUM }; // (s28, s29)
+ static uint32_t g_d15_regs[] = { 56, 57, LLDB_INVALID_REGNUM }; // (s30, s31)
+ static uint32_t g_q0_regs[] = { 26, 27, 28, 29, LLDB_INVALID_REGNUM }; // (d0, d1) -> (s0, s1, s2, s3)
+ static uint32_t g_q1_regs[] = { 30, 31, 32, 33, LLDB_INVALID_REGNUM }; // (d2, d3) -> (s4, s5, s6, s7)
+ static uint32_t g_q2_regs[] = { 34, 35, 36, 37, LLDB_INVALID_REGNUM }; // (d4, d5) -> (s8, s9, s10, s11)
+ static uint32_t g_q3_regs[] = { 38, 39, 40, 41, LLDB_INVALID_REGNUM }; // (d6, d7) -> (s12, s13, s14, s15)
+ static uint32_t g_q4_regs[] = { 42, 43, 44, 45, LLDB_INVALID_REGNUM }; // (d8, d9) -> (s16, s17, s18, s19)
+ static uint32_t g_q5_regs[] = { 46, 47, 48, 49, LLDB_INVALID_REGNUM }; // (d10, d11) -> (s20, s21, s22, s23)
+ static uint32_t g_q6_regs[] = { 50, 51, 52, 53, LLDB_INVALID_REGNUM }; // (d12, d13) -> (s24, s25, s26, s27)
+ static uint32_t g_q7_regs[] = { 54, 55, 56, 57, LLDB_INVALID_REGNUM }; // (d14, d15) -> (s28, s29, s30, s31)
+ static uint32_t g_q8_regs[] = { 59, 60, LLDB_INVALID_REGNUM }; // (d16, d17)
+ static uint32_t g_q9_regs[] = { 61, 62, LLDB_INVALID_REGNUM }; // (d18, d19)
+ static uint32_t g_q10_regs[] = { 63, 64, LLDB_INVALID_REGNUM }; // (d20, d21)
+ static uint32_t g_q11_regs[] = { 65, 66, LLDB_INVALID_REGNUM }; // (d22, d23)
+ static uint32_t g_q12_regs[] = { 67, 68, LLDB_INVALID_REGNUM }; // (d24, d25)
+ static uint32_t g_q13_regs[] = { 69, 70, LLDB_INVALID_REGNUM }; // (d26, d27)
+ static uint32_t g_q14_regs[] = { 71, 72, LLDB_INVALID_REGNUM }; // (d28, d29)
+ static uint32_t g_q15_regs[] = { 73, 74, LLDB_INVALID_REGNUM }; // (d30, d31)
+
+ // This is our array of composite registers, with each element coming from the above register mappings.
+ static uint32_t *g_composites[] = {
+ g_d0_regs, g_d1_regs, g_d2_regs, g_d3_regs, g_d4_regs, g_d5_regs, g_d6_regs, g_d7_regs,
+ g_d8_regs, g_d9_regs, g_d10_regs, g_d11_regs, g_d12_regs, g_d13_regs, g_d14_regs, g_d15_regs,
+ g_q0_regs, g_q1_regs, g_q2_regs, g_q3_regs, g_q4_regs, g_q5_regs, g_q6_regs, g_q7_regs,
+ g_q8_regs, g_q9_regs, g_q10_regs, g_q11_regs, g_q12_regs, g_q13_regs, g_q14_regs, g_q15_regs
+ };
+
+ static RegisterInfo g_register_infos[] = {
+// NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS
+// ====== ====== === === ============= ============ =================== =================== ====================== === ==== ========== ===============
+ { "r0", "arg1", 4, 0, eEncodingUint, eFormatHex, { gcc_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1,0, 0 }, NULL, NULL},
+ { "r1", "arg2", 4, 0, eEncodingUint, eFormatHex, { gcc_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2,1, 1 }, NULL, NULL},
+ { "r2", "arg3", 4, 0, eEncodingUint, eFormatHex, { gcc_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3,2, 2 }, NULL, NULL},
+ { "r3", "arg4", 4, 0, eEncodingUint, eFormatHex, { gcc_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4,3, 3 }, NULL, NULL},
+ { "r4", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM, 4, 4 }, NULL, NULL},
+ { "r5", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM, 5, 5 }, NULL, NULL},
+ { "r6", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM, 6, 6 }, NULL, NULL},
+ { "r7", "fp", 4, 0, eEncodingUint, eFormatHex, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, 7, 7 }, NULL, NULL},
+ { "r8", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM, 8, 8 }, NULL, NULL},
+ { "r9", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM, 9, 9 }, NULL, NULL},
+ { "r10", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM, 10, 10 }, NULL, NULL},
+ { "r11", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM, 11, 11 }, NULL, NULL},
+ { "r12", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM, 12, 12 }, NULL, NULL},
+ { "sp", "r13", 4, 0, eEncodingUint, eFormatHex, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, 13, 13 }, NULL, NULL},
+ { "lr", "r14", 4, 0, eEncodingUint, eFormatHex, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, 14, 14 }, NULL, NULL},
+ { "pc", "r15", 4, 0, eEncodingUint, eFormatHex, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, 15, 15 }, NULL, NULL},
+ { "f0", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 16, 16 }, NULL, NULL},
+ { "f1", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 17, 17 }, NULL, NULL},
+ { "f2", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 18, 18 }, NULL, NULL},
+ { "f3", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 19, 19 }, NULL, NULL},
+ { "f4", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 20, 20 }, NULL, NULL},
+ { "f5", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 21, 21 }, NULL, NULL},
+ { "f6", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 22, 22 }, NULL, NULL},
+ { "f7", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 23, 23 }, NULL, NULL},
+ { "fps", NULL, 4, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 24, 24 }, NULL, NULL},
+ { "cpsr","flags", 4, 0, eEncodingUint, eFormatHex, { gcc_cpsr, dwarf_cpsr, LLDB_INVALID_REGNUM, 25, 25 }, NULL, NULL},
+ { "s0", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, 26, 26 }, NULL, NULL},
+ { "s1", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, 27, 27 }, NULL, NULL},
+ { "s2", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, 28, 28 }, NULL, NULL},
+ { "s3", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, 29, 29 }, NULL, NULL},
+ { "s4", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, 30, 30 }, NULL, NULL},
+ { "s5", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, 31, 31 }, NULL, NULL},
+ { "s6", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, 32, 32 }, NULL, NULL},
+ { "s7", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, 33, 33 }, NULL, NULL},
+ { "s8", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, 34, 34 }, NULL, NULL},
+ { "s9", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, 35, 35 }, NULL, NULL},
+ { "s10", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, 36, 36 }, NULL, NULL},
+ { "s11", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, 37, 37 }, NULL, NULL},
+ { "s12", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, 38, 38 }, NULL, NULL},
+ { "s13", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, 39, 39 }, NULL, NULL},
+ { "s14", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, 40, 40 }, NULL, NULL},
+ { "s15", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, 41, 41 }, NULL, NULL},
+ { "s16", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, 42, 42 }, NULL, NULL},
+ { "s17", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, 43, 43 }, NULL, NULL},
+ { "s18", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, 44, 44 }, NULL, NULL},
+ { "s19", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, 45, 45 }, NULL, NULL},
+ { "s20", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, 46, 46 }, NULL, NULL},
+ { "s21", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, 47, 47 }, NULL, NULL},
+ { "s22", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, 48, 48 }, NULL, NULL},
+ { "s23", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, 49, 49 }, NULL, NULL},
+ { "s24", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, 50, 50 }, NULL, NULL},
+ { "s25", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, 51, 51 }, NULL, NULL},
+ { "s26", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, 52, 52 }, NULL, NULL},
+ { "s27", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, 53, 53 }, NULL, NULL},
+ { "s28", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, 54, 54 }, NULL, NULL},
+ { "s29", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, 55, 55 }, NULL, NULL},
+ { "s30", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, 56, 56 }, NULL, NULL},
+ { "s31", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, 57, 57 }, NULL, NULL},
+ { "fpscr",NULL, 4, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 58, 58 }, NULL, NULL},
+ { "d16", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, 59, 59 }, NULL, NULL},
+ { "d17", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, 60, 60 }, NULL, NULL},
+ { "d18", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, 61, 61 }, NULL, NULL},
+ { "d19", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, 62, 62 }, NULL, NULL},
+ { "d20", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, 63, 63 }, NULL, NULL},
+ { "d21", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, 64, 64 }, NULL, NULL},
+ { "d22", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, 65, 65 }, NULL, NULL},
+ { "d23", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, 66, 66 }, NULL, NULL},
+ { "d24", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, 67, 67 }, NULL, NULL},
+ { "d25", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, 68, 68 }, NULL, NULL},
+ { "d26", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, 69, 69 }, NULL, NULL},
+ { "d27", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, 70, 70 }, NULL, NULL},
+ { "d28", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, 71, 71 }, NULL, NULL},
+ { "d29", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, 72, 72 }, NULL, NULL},
+ { "d30", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, 73, 73 }, NULL, NULL},
+ { "d31", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, 74, 74 }, NULL, NULL},
+ { "d0", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, 75, 75 }, g_d0_regs, NULL},
+ { "d1", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, 76, 76 }, g_d1_regs, NULL},
+ { "d2", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, 77, 77 }, g_d2_regs, NULL},
+ { "d3", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, 78, 78 }, g_d3_regs, NULL},
+ { "d4", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, 79, 79 }, g_d4_regs, NULL},
+ { "d5", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, 80, 80 }, g_d5_regs, NULL},
+ { "d6", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, 81, 81 }, g_d6_regs, NULL},
+ { "d7", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, 82, 82 }, g_d7_regs, NULL},
+ { "d8", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, 83, 83 }, g_d8_regs, NULL},
+ { "d9", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, 84, 84 }, g_d9_regs, NULL},
+ { "d10", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, 85, 85 }, g_d10_regs, NULL},
+ { "d11", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, 86, 86 }, g_d11_regs, NULL},
+ { "d12", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, 87, 87 }, g_d12_regs, NULL},
+ { "d13", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, 88, 88 }, g_d13_regs, NULL},
+ { "d14", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, 89, 89 }, g_d14_regs, NULL},
+ { "d15", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, 90, 90 }, g_d15_regs, NULL},
+ { "q0", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q0, LLDB_INVALID_REGNUM, 91, 91 }, g_q0_regs, NULL},
+ { "q1", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q1, LLDB_INVALID_REGNUM, 92, 92 }, g_q1_regs, NULL},
+ { "q2", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q2, LLDB_INVALID_REGNUM, 93, 93 }, g_q2_regs, NULL},
+ { "q3", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q3, LLDB_INVALID_REGNUM, 94, 94 }, g_q3_regs, NULL},
+ { "q4", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q4, LLDB_INVALID_REGNUM, 95, 95 }, g_q4_regs, NULL},
+ { "q5", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q5, LLDB_INVALID_REGNUM, 96, 96 }, g_q5_regs, NULL},
+ { "q6", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q6, LLDB_INVALID_REGNUM, 97, 97 }, g_q6_regs, NULL},
+ { "q7", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q7, LLDB_INVALID_REGNUM, 98, 98 }, g_q7_regs, NULL},
+ { "q8", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q8, LLDB_INVALID_REGNUM, 99, 99 }, g_q8_regs, NULL},
+ { "q9", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q9, LLDB_INVALID_REGNUM, 100, 100 }, g_q9_regs, NULL},
+ { "q10", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q10, LLDB_INVALID_REGNUM, 101, 101 }, g_q10_regs, NULL},
+ { "q11", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q11, LLDB_INVALID_REGNUM, 102, 102 }, g_q11_regs, NULL},
+ { "q12", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q12, LLDB_INVALID_REGNUM, 103, 103 }, g_q12_regs, NULL},
+ { "q13", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q13, LLDB_INVALID_REGNUM, 104, 104 }, g_q13_regs, NULL},
+ { "q14", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q14, LLDB_INVALID_REGNUM, 105, 105 }, g_q14_regs, NULL},
+ { "q15", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q15, LLDB_INVALID_REGNUM, 106, 106 }, g_q15_regs, NULL}
+ };
+
+ static const uint32_t num_registers = llvm::array_lengthof(g_register_infos);
+ static ConstString gpr_reg_set ("General Purpose Registers");
+ static ConstString sfp_reg_set ("Software Floating Point Registers");
+ static ConstString vfp_reg_set ("Floating Point Registers");
+ size_t i;
+ if (from_scratch)
+ {
+ // Calculate the offsets of the registers
+ // Note that the layout of the "composite" registers (d0-d15 and q0-q15) which comes after the
+ // "primordial" registers is important. This enables us to calculate the offset of the composite
+ // register by using the offset of its first primordial register. For example, to calculate the
+ // offset of q0, use s0's offset.
+ if (g_register_infos[2].byte_offset == 0)
+ {
+ uint32_t byte_offset = 0;
+ for (i=0; i<num_registers; ++i)
+ {
+ // For primordial registers, increment the byte_offset by the byte_size to arrive at the
+ // byte_offset for the next register. Otherwise, we have a composite register whose
+ // offset can be calculated by consulting the offset of its first primordial register.
+ if (!g_register_infos[i].value_regs)
+ {
+ g_register_infos[i].byte_offset = byte_offset;
+ byte_offset += g_register_infos[i].byte_size;
+ }
+ else
+ {
+ const uint32_t first_primordial_reg = g_register_infos[i].value_regs[0];
+ g_register_infos[i].byte_offset = g_register_infos[first_primordial_reg].byte_offset;
+ }
+ }
+ }
+ for (i=0; i<num_registers; ++i)
+ {
+ ConstString name;
+ ConstString alt_name;
+ if (g_register_infos[i].name && g_register_infos[i].name[0])
+ name.SetCString(g_register_infos[i].name);
+ if (g_register_infos[i].alt_name && g_register_infos[i].alt_name[0])
+ alt_name.SetCString(g_register_infos[i].alt_name);
+
+ if (i <= 15 || i == 25)
+ AddRegister (g_register_infos[i], name, alt_name, gpr_reg_set);
+ else if (i <= 24)
+ AddRegister (g_register_infos[i], name, alt_name, sfp_reg_set);
+ else
+ AddRegister (g_register_infos[i], name, alt_name, vfp_reg_set);
+ }
+ }
+ else
+ {
+ // Add composite registers to our primordial registers, then.
+ const size_t num_composites = llvm::array_lengthof(g_composites);
+ const size_t num_dynamic_regs = GetNumRegisters();
+ const size_t num_common_regs = num_registers - num_composites;
+ RegisterInfo *g_comp_register_infos = g_register_infos + num_common_regs;
+
+ // First we need to validate that all registers that we already have match the non composite regs.
+ // If so, then we can add the registers, else we need to bail
+ bool match = true;
+ if (num_dynamic_regs == num_common_regs)
+ {
+ for (i=0; match && i<num_dynamic_regs; ++i)
+ {
+ // Make sure all register names match
+ if (m_regs[i].name && g_register_infos[i].name)
+ {
+ if (strcmp(m_regs[i].name, g_register_infos[i].name))
+ {
+ match = false;
+ break;
+ }
+ }
+
+ // Make sure all register byte sizes match
+ if (m_regs[i].byte_size != g_register_infos[i].byte_size)
+ {
+ match = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Wrong number of registers.
+ match = false;
+ }
+ // If "match" is true, then we can add extra registers.
+ if (match)
+ {
+ for (i=0; i<num_composites; ++i)
+ {
+ ConstString name;
+ ConstString alt_name;
+ const uint32_t first_primordial_reg = g_comp_register_infos[i].value_regs[0];
+ const char *reg_name = g_register_infos[first_primordial_reg].name;
+ if (reg_name && reg_name[0])
+ {
+ for (uint32_t j = 0; j < num_dynamic_regs; ++j)
+ {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(j);
+ // Find a matching primordial register info entry.
+ if (reg_info && reg_info->name && ::strcasecmp(reg_info->name, reg_name) == 0)
+ {
+ // The name matches the existing primordial entry.
+ // Find and assign the offset, and then add this composite register entry.
+ g_comp_register_infos[i].byte_offset = reg_info->byte_offset;
+ name.SetCString(g_comp_register_infos[i].name);
+ AddRegister(g_comp_register_infos[i], name, alt_name, vfp_reg_set);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
new file mode 100644
index 0000000..3110ddf
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
@@ -0,0 +1,311 @@
+//===-- GDBRemoteRegisterContext.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_GDBRemoteRegisterContext_h_
+#define lldb_GDBRemoteRegisterContext_h_
+
+// C Includes
+// C++ Includes
+#include <vector>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/Core/ConstString.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Target/RegisterContext.h"
+#include "GDBRemoteCommunicationClient.h"
+
+class ThreadGDBRemote;
+class ProcessGDBRemote;
+class StringExtractor;
+
+class GDBRemoteDynamicRegisterInfo
+{
+public:
+ GDBRemoteDynamicRegisterInfo () :
+ m_regs (),
+ m_sets (),
+ m_set_reg_nums (),
+ m_reg_names (),
+ m_reg_alt_names (),
+ m_set_names (),
+ m_reg_data_byte_size (0)
+ {
+ }
+
+ ~GDBRemoteDynamicRegisterInfo ()
+ {
+ }
+
+ void
+ AddRegister (lldb_private::RegisterInfo reg_info,
+ lldb_private::ConstString &reg_name,
+ lldb_private::ConstString &reg_alt_name,
+ lldb_private::ConstString &set_name)
+ {
+ const uint32_t reg_num = (uint32_t)m_regs.size();
+ m_reg_names.push_back (reg_name);
+ m_reg_alt_names.push_back (reg_alt_name);
+ reg_info.name = reg_name.AsCString();
+ assert (reg_info.name);
+ reg_info.alt_name = reg_alt_name.AsCString(NULL);
+ uint32_t i;
+ if (reg_info.value_regs)
+ {
+ for (i=0; reg_info.value_regs[i] != LLDB_INVALID_REGNUM; ++i)
+ m_value_regs_map[reg_num].push_back(reg_info.value_regs[i]);
+ m_value_regs_map[reg_num].push_back(LLDB_INVALID_REGNUM);
+ reg_info.value_regs = m_value_regs_map[reg_num].data();
+ }
+ if (reg_info.invalidate_regs)
+ {
+ for (i=0; reg_info.invalidate_regs[i] != LLDB_INVALID_REGNUM; ++i)
+ m_invalidate_regs_map[reg_num].push_back(reg_info.invalidate_regs[i]);
+ m_invalidate_regs_map[reg_num].push_back(LLDB_INVALID_REGNUM);
+ reg_info.invalidate_regs = m_invalidate_regs_map[reg_num].data();
+ }
+ m_regs.push_back (reg_info);
+ uint32_t set = GetRegisterSetIndexByName (set_name);
+ assert (set < m_sets.size());
+ assert (set < m_set_reg_nums.size());
+ assert (set < m_set_names.size());
+ m_set_reg_nums[set].push_back(reg_num);
+ size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
+ if (m_reg_data_byte_size < end_reg_offset)
+ m_reg_data_byte_size = end_reg_offset;
+ }
+
+ void
+ Finalize ()
+ {
+ for (uint32_t set = 0; set < m_sets.size(); ++set)
+ {
+ assert (m_sets.size() == m_set_reg_nums.size());
+ m_sets[set].num_registers = m_set_reg_nums[set].size();
+ m_sets[set].registers = &m_set_reg_nums[set][0];
+ }
+ }
+
+ size_t
+ GetNumRegisters() const
+ {
+ return m_regs.size();
+ }
+
+ size_t
+ GetNumRegisterSets() const
+ {
+ return m_sets.size();
+ }
+
+ size_t
+ GetRegisterDataByteSize() const
+ {
+ return m_reg_data_byte_size;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t i) const
+ {
+ if (i < m_regs.size())
+ return &m_regs[i];
+ return NULL;
+ }
+
+ const lldb_private::RegisterSet *
+ GetRegisterSet (uint32_t i) const
+ {
+ if (i < m_sets.size())
+ return &m_sets[i];
+ return NULL;
+ }
+
+ uint32_t
+ GetRegisterSetIndexByName (lldb_private::ConstString &set_name)
+ {
+ name_collection::iterator pos, end = m_set_names.end();
+ for (pos = m_set_names.begin(); pos != end; ++pos)
+ {
+ if (*pos == set_name)
+ return static_cast<uint32_t>(std::distance (m_set_names.begin(), pos));
+ }
+
+ m_set_names.push_back(set_name);
+ m_set_reg_nums.resize(m_set_reg_nums.size()+1);
+ lldb_private::RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL };
+ m_sets.push_back (new_set);
+ return static_cast<uint32_t>(m_sets.size() - 1);
+ }
+
+ uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const
+ {
+ reg_collection::const_iterator pos, end = m_regs.end();
+ for (pos = m_regs.begin(); pos != end; ++pos)
+ {
+ if (pos->kinds[kind] == num)
+ return static_cast<uint32_t>(std::distance (m_regs.begin(), pos));
+ }
+
+ return LLDB_INVALID_REGNUM;
+ }
+ void
+ Clear()
+ {
+ m_regs.clear();
+ m_sets.clear();
+ m_set_reg_nums.clear();
+ m_reg_names.clear();
+ m_reg_alt_names.clear();
+ m_set_names.clear();
+ }
+
+ void
+ HardcodeARMRegisters(bool from_scratch);
+
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from GDBRemoteRegisterContext can see and modify these
+ //------------------------------------------------------------------
+ typedef std::vector <lldb_private::RegisterInfo> reg_collection;
+ typedef std::vector <lldb_private::RegisterSet> set_collection;
+ typedef std::vector <uint32_t> reg_num_collection;
+ typedef std::vector <reg_num_collection> set_reg_num_collection;
+ typedef std::vector <lldb_private::ConstString> name_collection;
+ typedef std::map<uint32_t, reg_num_collection> reg_to_regs_map;
+
+ reg_collection m_regs;
+ set_collection m_sets;
+ set_reg_num_collection m_set_reg_nums;
+ name_collection m_reg_names;
+ name_collection m_reg_alt_names;
+ name_collection m_set_names;
+ reg_to_regs_map m_value_regs_map;
+ reg_to_regs_map m_invalidate_regs_map;
+ size_t m_reg_data_byte_size; // The number of bytes required to store all registers
+};
+
+class GDBRemoteRegisterContext : public lldb_private::RegisterContext
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ GDBRemoteRegisterContext (ThreadGDBRemote &thread,
+ uint32_t concrete_frame_idx,
+ GDBRemoteDynamicRegisterInfo &reg_info,
+ bool read_all_at_once);
+
+ virtual
+ ~GDBRemoteRegisterContext ();
+
+ //------------------------------------------------------------------
+ // Subclasses must override these functions
+ //------------------------------------------------------------------
+ virtual void
+ InvalidateAllRegisters ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb_private::RegisterInfo *
+ GetRegisterInfoAtIndex (size_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb_private::RegisterSet *
+ GetRegisterSet (size_t reg_set);
+
+ virtual bool
+ ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value);
+
+ virtual bool
+ WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+protected:
+ friend class ThreadGDBRemote;
+
+ bool
+ ReadRegisterBytes (const lldb_private::RegisterInfo *reg_info,
+ lldb_private::DataExtractor &data);
+
+ bool
+ WriteRegisterBytes (const lldb_private::RegisterInfo *reg_info,
+ lldb_private::DataExtractor &data,
+ uint32_t data_offset);
+
+ bool
+ PrivateSetRegisterValue (uint32_t reg, StringExtractor &response);
+
+ void
+ SetAllRegisterValid (bool b);
+
+ bool
+ GetRegisterIsValid (uint32_t reg) const
+ {
+#if defined (LLDB_CONFIGURATION_DEBUG)
+ assert (reg < m_reg_valid.size());
+#endif
+ if (reg < m_reg_valid.size())
+ return m_reg_valid[reg];
+ return false;
+ }
+
+ void
+ SetRegisterIsValid (const lldb_private::RegisterInfo *reg_info, bool valid)
+ {
+ if (reg_info)
+ return SetRegisterIsValid (reg_info->kinds[lldb::eRegisterKindLLDB], valid);
+ }
+
+ void
+ SetRegisterIsValid (uint32_t reg, bool valid)
+ {
+#if defined (LLDB_CONFIGURATION_DEBUG)
+ assert (reg < m_reg_valid.size());
+#endif
+ if (reg < m_reg_valid.size())
+ m_reg_valid[reg] = valid;
+ }
+
+ void
+ SyncThreadState(lldb_private::Process *process); // Assumes the sequence mutex has already been acquired.
+
+ GDBRemoteDynamicRegisterInfo &m_reg_info;
+ std::vector<bool> m_reg_valid;
+ lldb_private::DataExtractor m_reg_data;
+ bool m_read_all_at_once;
+
+private:
+ // Helper function for ReadRegisterBytes().
+ bool GetPrimordialRegister(const lldb_private::RegisterInfo *reg_info,
+ GDBRemoteCommunicationClient &gdb_comm);
+ // Helper function for WriteRegisterBytes().
+ bool SetPrimordialRegister(const lldb_private::RegisterInfo *reg_info,
+ GDBRemoteCommunicationClient &gdb_comm);
+
+ //------------------------------------------------------------------
+ // For GDBRemoteRegisterContext only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (GDBRemoteRegisterContext);
+};
+
+#endif // lldb_GDBRemoteRegisterContext_h_
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
new file mode 100644
index 0000000..d27207f
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -0,0 +1,3291 @@
+//===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-python.h"
+
+// C Includes
+#include <errno.h>
+#include <spawn.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <sys/mman.h> // for mmap
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+// C++ Includes
+#include <algorithm>
+#include <map>
+
+// Other libraries and framework includes
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Interpreter/Args.h"
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Core/InputReader.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/Timer.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Host/Symbols.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Utility/PseudoTerminal.h"
+
+// Project includes
+#include "lldb/Host/Host.h"
+#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
+#include "Plugins/Process/Utility/StopInfoMachException.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h"
+#include "Utility/StringExtractorGDBRemote.h"
+#include "GDBRemoteRegisterContext.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "ThreadGDBRemote.h"
+
+
+namespace lldb
+{
+ // Provide a function that can easily dump the packet history if we know a
+ // ProcessGDBRemote * value (which we can get from logs or from debugging).
+ // We need the function in the lldb namespace so it makes it into the final
+ // executable since the LLDB shared library only exports stuff in the lldb
+ // namespace. This allows you to attach with a debugger and call this
+ // function and get the packet history dumped to a file.
+ void
+ DumpProcessGDBRemotePacketHistory (void *p, const char *path)
+ {
+ lldb_private::StreamFile strm;
+ lldb_private::Error error (strm.GetFile().Open(path, lldb_private::File::eOpenOptionWrite | lldb_private::File::eOpenOptionCanCreate));
+ if (error.Success())
+ ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory (strm);
+ }
+}
+
+#define DEBUGSERVER_BASENAME "debugserver"
+using namespace lldb;
+using namespace lldb_private;
+
+
+namespace {
+
+ static PropertyDefinition
+ g_properties[] =
+ {
+ { "packet-timeout" , OptionValue::eTypeUInt64 , true , 1, NULL, NULL, "Specify the default packet timeout in seconds." },
+ { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL }
+ };
+
+ enum
+ {
+ ePropertyPacketTimeout
+ };
+
+ class PluginProperties : public Properties
+ {
+ public:
+
+ static ConstString
+ GetSettingName ()
+ {
+ return ProcessGDBRemote::GetPluginNameStatic();
+ }
+
+ PluginProperties() :
+ Properties ()
+ {
+ m_collection_sp.reset (new OptionValueProperties(GetSettingName()));
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ virtual
+ ~PluginProperties()
+ {
+ }
+
+ uint64_t
+ GetPacketTimeout()
+ {
+ const uint32_t idx = ePropertyPacketTimeout;
+ return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value);
+ }
+ };
+
+ typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP;
+
+ static const ProcessKDPPropertiesSP &
+ GetGlobalPluginProperties()
+ {
+ static ProcessKDPPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ g_settings_sp.reset (new PluginProperties ());
+ return g_settings_sp;
+ }
+
+} // anonymous namespace end
+
+static bool rand_initialized = false;
+
+// TODO Randomly assigning a port is unsafe. We should get an unused
+// ephemeral port from the kernel and make sure we reserve it before passing
+// it to debugserver.
+
+#if defined (__APPLE__)
+#define LOW_PORT (IPPORT_RESERVED)
+#define HIGH_PORT (IPPORT_HIFIRSTAUTO)
+#else
+#define LOW_PORT (1024u)
+#define HIGH_PORT (49151u)
+#endif
+
+static inline uint16_t
+get_random_port ()
+{
+ if (!rand_initialized)
+ {
+ time_t seed = time(NULL);
+
+ rand_initialized = true;
+ srand(seed);
+ }
+ return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT;
+}
+
+
+lldb_private::ConstString
+ProcessGDBRemote::GetPluginNameStatic()
+{
+ static ConstString g_name("gdb-remote");
+ return g_name;
+}
+
+const char *
+ProcessGDBRemote::GetPluginDescriptionStatic()
+{
+ return "GDB Remote protocol based debugging plug-in.";
+}
+
+void
+ProcessGDBRemote::Terminate()
+{
+ PluginManager::UnregisterPlugin (ProcessGDBRemote::CreateInstance);
+}
+
+
+lldb::ProcessSP
+ProcessGDBRemote::CreateInstance (Target &target, Listener &listener, const FileSpec *crash_file_path)
+{
+ lldb::ProcessSP process_sp;
+ if (crash_file_path == NULL)
+ process_sp.reset (new ProcessGDBRemote (target, listener));
+ return process_sp;
+}
+
+bool
+ProcessGDBRemote::CanDebug (Target &target, bool plugin_specified_by_name)
+{
+ if (plugin_specified_by_name)
+ return true;
+
+ // For now we are just making sure the file exists for a given module
+ Module *exe_module = target.GetExecutableModulePointer();
+ if (exe_module)
+ {
+ ObjectFile *exe_objfile = exe_module->GetObjectFile();
+ // We can't debug core files...
+ switch (exe_objfile->GetType())
+ {
+ case ObjectFile::eTypeInvalid:
+ case ObjectFile::eTypeCoreFile:
+ case ObjectFile::eTypeDebugInfo:
+ case ObjectFile::eTypeObjectFile:
+ case ObjectFile::eTypeSharedLibrary:
+ case ObjectFile::eTypeStubLibrary:
+ return false;
+ case ObjectFile::eTypeExecutable:
+ case ObjectFile::eTypeDynamicLinker:
+ case ObjectFile::eTypeUnknown:
+ break;
+ }
+ return exe_module->GetFileSpec().Exists();
+ }
+ // However, if there is no executable module, we return true since we might be preparing to attach.
+ return true;
+}
+
+//----------------------------------------------------------------------
+// ProcessGDBRemote constructor
+//----------------------------------------------------------------------
+ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) :
+ Process (target, listener),
+ m_flags (0),
+ m_gdb_comm(false),
+ m_debugserver_pid (LLDB_INVALID_PROCESS_ID),
+ m_last_stop_packet (),
+ m_last_stop_packet_mutex (Mutex::eMutexTypeNormal),
+ m_register_info (),
+ m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"),
+ m_async_thread (LLDB_INVALID_HOST_THREAD),
+ m_async_thread_state(eAsyncThreadNotStarted),
+ m_async_thread_state_mutex(Mutex::eMutexTypeRecursive),
+ m_thread_ids (),
+ m_continue_c_tids (),
+ m_continue_C_tids (),
+ m_continue_s_tids (),
+ m_continue_S_tids (),
+ m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS),
+ m_max_memory_size (512),
+ m_addr_to_mmap_size (),
+ m_thread_create_bp_sp (),
+ m_waiting_for_attach (false),
+ m_destroy_tried_resuming (false),
+ m_command_sp ()
+{
+ m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit");
+ m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue");
+ m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadDidExit, "async thread did exit");
+ const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout();
+ if (timeout_seconds > 0)
+ m_gdb_comm.SetPacketTimeout(timeout_seconds);
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ProcessGDBRemote::~ProcessGDBRemote()
+{
+ // m_mach_process.UnregisterNotificationCallbacks (this);
+ Clear();
+ // We need to call finalize on the process before destroying ourselves
+ // to make sure all of the broadcaster cleanup goes as planned. If we
+ // destruct this class, then Process::~Process() might have problems
+ // trying to fully destroy the broadcaster.
+ Finalize();
+
+ // The general Finalize is going to try to destroy the process and that SHOULD
+ // shut down the async thread. However, if we don't kill it it will get stranded and
+ // its connection will go away so when it wakes up it will crash. So kill it for sure here.
+ StopAsyncThread();
+ KillDebugserverProcess();
+}
+
+//----------------------------------------------------------------------
+// PluginInterface
+//----------------------------------------------------------------------
+ConstString
+ProcessGDBRemote::GetPluginName()
+{
+ return GetPluginNameStatic();
+}
+
+uint32_t
+ProcessGDBRemote::GetPluginVersion()
+{
+ return 1;
+}
+
+void
+ProcessGDBRemote::BuildDynamicRegisterInfo (bool force)
+{
+ if (!force && m_register_info.GetNumRegisters() > 0)
+ return;
+
+ char packet[128];
+ m_register_info.Clear();
+ uint32_t reg_offset = 0;
+ uint32_t reg_num = 0;
+ for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse;
+ response_type == StringExtractorGDBRemote::eResponse;
+ ++reg_num)
+ {
+ const int packet_len = ::snprintf (packet, sizeof(packet), "qRegisterInfo%x", reg_num);
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false))
+ {
+ response_type = response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse)
+ {
+ std::string name;
+ std::string value;
+ ConstString reg_name;
+ ConstString alt_name;
+ ConstString set_name;
+ std::vector<uint32_t> value_regs;
+ std::vector<uint32_t> invalidate_regs;
+ RegisterInfo reg_info = { NULL, // Name
+ NULL, // Alt name
+ 0, // byte size
+ reg_offset, // offset
+ eEncodingUint, // encoding
+ eFormatHex, // formate
+ {
+ LLDB_INVALID_REGNUM, // GCC reg num
+ LLDB_INVALID_REGNUM, // DWARF reg num
+ LLDB_INVALID_REGNUM, // generic reg num
+ reg_num, // GDB reg num
+ reg_num // native register number
+ },
+ NULL,
+ NULL
+ };
+
+ while (response.GetNameColonValue(name, value))
+ {
+ if (name.compare("name") == 0)
+ {
+ reg_name.SetCString(value.c_str());
+ }
+ else if (name.compare("alt-name") == 0)
+ {
+ alt_name.SetCString(value.c_str());
+ }
+ else if (name.compare("bitsize") == 0)
+ {
+ reg_info.byte_size = Args::StringToUInt32(value.c_str(), 0, 0) / CHAR_BIT;
+ }
+ else if (name.compare("offset") == 0)
+ {
+ uint32_t offset = Args::StringToUInt32(value.c_str(), UINT32_MAX, 0);
+ if (reg_offset != offset)
+ {
+ reg_offset = offset;
+ }
+ }
+ else if (name.compare("encoding") == 0)
+ {
+ const Encoding encoding = Args::StringToEncoding (value.c_str());
+ if (encoding != eEncodingInvalid)
+ reg_info.encoding = encoding;
+ }
+ else if (name.compare("format") == 0)
+ {
+ Format format = eFormatInvalid;
+ if (Args::StringToFormat (value.c_str(), format, NULL).Success())
+ reg_info.format = format;
+ else if (value.compare("binary") == 0)
+ reg_info.format = eFormatBinary;
+ else if (value.compare("decimal") == 0)
+ reg_info.format = eFormatDecimal;
+ else if (value.compare("hex") == 0)
+ reg_info.format = eFormatHex;
+ else if (value.compare("float") == 0)
+ reg_info.format = eFormatFloat;
+ else if (value.compare("vector-sint8") == 0)
+ reg_info.format = eFormatVectorOfSInt8;
+ else if (value.compare("vector-uint8") == 0)
+ reg_info.format = eFormatVectorOfUInt8;
+ else if (value.compare("vector-sint16") == 0)
+ reg_info.format = eFormatVectorOfSInt16;
+ else if (value.compare("vector-uint16") == 0)
+ reg_info.format = eFormatVectorOfUInt16;
+ else if (value.compare("vector-sint32") == 0)
+ reg_info.format = eFormatVectorOfSInt32;
+ else if (value.compare("vector-uint32") == 0)
+ reg_info.format = eFormatVectorOfUInt32;
+ else if (value.compare("vector-float32") == 0)
+ reg_info.format = eFormatVectorOfFloat32;
+ else if (value.compare("vector-uint128") == 0)
+ reg_info.format = eFormatVectorOfUInt128;
+ }
+ else if (name.compare("set") == 0)
+ {
+ set_name.SetCString(value.c_str());
+ }
+ else if (name.compare("gcc") == 0)
+ {
+ reg_info.kinds[eRegisterKindGCC] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0);
+ }
+ else if (name.compare("dwarf") == 0)
+ {
+ reg_info.kinds[eRegisterKindDWARF] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0);
+ }
+ else if (name.compare("generic") == 0)
+ {
+ reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister (value.c_str());
+ }
+ else if (name.compare("container-regs") == 0)
+ {
+ std::pair<llvm::StringRef, llvm::StringRef> value_pair;
+ value_pair.second = value;
+ do
+ {
+ value_pair = value_pair.second.split(',');
+ if (!value_pair.first.empty())
+ {
+ uint32_t reg = Args::StringToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, 16);
+ if (reg != LLDB_INVALID_REGNUM)
+ value_regs.push_back (reg);
+ }
+ } while (!value_pair.second.empty());
+ }
+ else if (name.compare("invalidate-regs") == 0)
+ {
+ std::pair<llvm::StringRef, llvm::StringRef> value_pair;
+ value_pair.second = value;
+ do
+ {
+ value_pair = value_pair.second.split(',');
+ if (!value_pair.first.empty())
+ {
+ uint32_t reg = Args::StringToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, 16);
+ if (reg != LLDB_INVALID_REGNUM)
+ invalidate_regs.push_back (reg);
+ }
+ } while (!value_pair.second.empty());
+ }
+ }
+
+ reg_info.byte_offset = reg_offset;
+ assert (reg_info.byte_size != 0);
+ reg_offset += reg_info.byte_size;
+ if (!value_regs.empty())
+ {
+ value_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.value_regs = value_regs.data();
+ }
+ if (!invalidate_regs.empty())
+ {
+ invalidate_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.invalidate_regs = invalidate_regs.data();
+ }
+
+ m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name);
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // We didn't get anything if the accumulated reg_num is zero. See if we are
+ // debugging ARM and fill with a hard coded register set until we can get an
+ // updated debugserver down on the devices.
+ // On the other hand, if the accumulated reg_num is positive, see if we can
+ // add composite registers to the existing primordial ones.
+ bool from_scratch = (reg_num == 0);
+
+ const ArchSpec &target_arch = GetTarget().GetArchitecture();
+ const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture();
+ const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture();
+
+ // Use the process' architecture instead of the host arch, if available
+ ArchSpec remote_arch;
+ if (remote_process_arch.IsValid ())
+ remote_arch = remote_process_arch;
+ else
+ remote_arch = remote_host_arch;
+
+ if (!target_arch.IsValid())
+ {
+ if (remote_arch.IsValid()
+ && remote_arch.GetMachine() == llvm::Triple::arm
+ && remote_arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ m_register_info.HardcodeARMRegisters(from_scratch);
+ }
+ else if (target_arch.GetMachine() == llvm::Triple::arm)
+ {
+ m_register_info.HardcodeARMRegisters(from_scratch);
+ }
+
+ // At this point, we can finalize our register info.
+ m_register_info.Finalize ();
+}
+
+Error
+ProcessGDBRemote::WillLaunch (Module* module)
+{
+ return WillLaunchOrAttach ();
+}
+
+Error
+ProcessGDBRemote::WillAttachToProcessWithID (lldb::pid_t pid)
+{
+ return WillLaunchOrAttach ();
+}
+
+Error
+ProcessGDBRemote::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch)
+{
+ return WillLaunchOrAttach ();
+}
+
+Error
+ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url)
+{
+ Error error (WillLaunchOrAttach ());
+
+ if (error.Fail())
+ return error;
+
+ error = ConnectToDebugserver (remote_url);
+
+ if (error.Fail())
+ return error;
+ StartAsyncThread ();
+
+ lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID ();
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ // We don't have a valid process ID, so note that we are connected
+ // and could now request to launch or attach, or get remote process
+ // listings...
+ SetPrivateState (eStateConnected);
+ }
+ else
+ {
+ // We have a valid process
+ SetID (pid);
+ GetThreadList();
+ if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false))
+ {
+ const StateType state = SetThreadStopInfo (m_last_stop_packet);
+ if (state == eStateStopped)
+ {
+ SetPrivateState (state);
+ }
+ else
+ error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but state was not stopped: %s", pid, remote_url, StateAsCString (state));
+ }
+ else
+ error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but no stop reply packet was received", pid, remote_url);
+ }
+
+ if (error.Success()
+ && !GetTarget().GetArchitecture().IsValid()
+ && m_gdb_comm.GetHostArchitecture().IsValid())
+ {
+ // Prefer the *process'* architecture over that of the *host*, if available.
+ if (m_gdb_comm.GetProcessArchitecture().IsValid())
+ GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture());
+ else
+ GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture());
+ }
+
+ return error;
+}
+
+Error
+ProcessGDBRemote::WillLaunchOrAttach ()
+{
+ Error error;
+ m_stdio_communication.Clear ();
+ return error;
+}
+
+//----------------------------------------------------------------------
+// Process Control
+//----------------------------------------------------------------------
+Error
+ProcessGDBRemote::DoLaunch (Module *exe_module, const ProcessLaunchInfo &launch_info)
+{
+ Error error;
+
+ uint32_t launch_flags = launch_info.GetFlags().Get();
+ const char *stdin_path = NULL;
+ const char *stdout_path = NULL;
+ const char *stderr_path = NULL;
+ const char *working_dir = launch_info.GetWorkingDirectory();
+
+ const ProcessLaunchInfo::FileAction *file_action;
+ file_action = launch_info.GetFileActionForFD (STDIN_FILENO);
+ if (file_action)
+ {
+ if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen)
+ stdin_path = file_action->GetPath();
+ }
+ file_action = launch_info.GetFileActionForFD (STDOUT_FILENO);
+ if (file_action)
+ {
+ if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen)
+ stdout_path = file_action->GetPath();
+ }
+ file_action = launch_info.GetFileActionForFD (STDERR_FILENO);
+ if (file_action)
+ {
+ if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen)
+ stderr_path = file_action->GetPath();
+ }
+
+ // ::LogSetBitMask (GDBR_LOG_DEFAULT);
+ // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
+ // ::LogSetLogFile ("/dev/stdout");
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
+
+ ObjectFile * object_file = exe_module->GetObjectFile();
+ if (object_file)
+ {
+ char host_port[128];
+ snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ());
+ char connect_url[128];
+ snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port);
+
+ // Make sure we aren't already connected?
+ if (!m_gdb_comm.IsConnected())
+ {
+ error = StartDebugserverProcess (host_port, launch_info);
+ if (error.Fail())
+ {
+ if (log)
+ log->Printf("failed to start debugserver process: %s", error.AsCString());
+ return error;
+ }
+
+ error = ConnectToDebugserver (connect_url);
+ }
+
+ if (error.Success())
+ {
+ lldb_utility::PseudoTerminal pty;
+ const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
+
+ // If the debugserver is local and we aren't disabling STDIO, lets use
+ // a pseudo terminal to instead of relying on the 'O' packets for stdio
+ // since 'O' packets can really slow down debugging if the inferior
+ // does a lot of output.
+ PlatformSP platform_sp (m_target.GetPlatform());
+ if (platform_sp && platform_sp->IsHost() && !disable_stdio)
+ {
+ const char *slave_name = NULL;
+ if (stdin_path == NULL || stdout_path == NULL || stderr_path == NULL)
+ {
+ if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0))
+ slave_name = pty.GetSlaveName (NULL, 0);
+ }
+ if (stdin_path == NULL)
+ stdin_path = slave_name;
+
+ if (stdout_path == NULL)
+ stdout_path = slave_name;
+
+ if (stderr_path == NULL)
+ stderr_path = slave_name;
+ }
+
+ // Set STDIN to /dev/null if we want STDIO disabled or if either
+ // STDOUT or STDERR have been set to something and STDIN hasn't
+ if (disable_stdio || (stdin_path == NULL && (stdout_path || stderr_path)))
+ stdin_path = "/dev/null";
+
+ // Set STDOUT to /dev/null if we want STDIO disabled or if either
+ // STDIN or STDERR have been set to something and STDOUT hasn't
+ if (disable_stdio || (stdout_path == NULL && (stdin_path || stderr_path)))
+ stdout_path = "/dev/null";
+
+ // Set STDERR to /dev/null if we want STDIO disabled or if either
+ // STDIN or STDOUT have been set to something and STDERR hasn't
+ if (disable_stdio || (stderr_path == NULL && (stdin_path || stdout_path)))
+ stderr_path = "/dev/null";
+
+ if (stdin_path)
+ m_gdb_comm.SetSTDIN (stdin_path);
+ if (stdout_path)
+ m_gdb_comm.SetSTDOUT (stdout_path);
+ if (stderr_path)
+ m_gdb_comm.SetSTDERR (stderr_path);
+
+ m_gdb_comm.SetDisableASLR (launch_flags & eLaunchFlagDisableASLR);
+
+ m_gdb_comm.SendLaunchArchPacket (m_target.GetArchitecture().GetArchitectureName());
+
+ if (working_dir && working_dir[0])
+ {
+ m_gdb_comm.SetWorkingDir (working_dir);
+ }
+
+ // Send the environment and the program + arguments after we connect
+ const Args &environment = launch_info.GetEnvironmentEntries();
+ if (environment.GetArgumentCount())
+ {
+ size_t num_environment_entries = environment.GetArgumentCount();
+ for (size_t i=0; i<num_environment_entries; ++i)
+ {
+ const char *env_entry = environment.GetArgumentAtIndex(i);
+ if (env_entry == NULL || m_gdb_comm.SendEnvironmentPacket(env_entry) != 0)
+ break;
+ }
+ }
+
+ const uint32_t old_packet_timeout = m_gdb_comm.SetPacketTimeout (10);
+ int arg_packet_err = m_gdb_comm.SendArgumentsPacket (launch_info.GetArguments().GetConstArgumentVector());
+ if (arg_packet_err == 0)
+ {
+ std::string error_str;
+ if (m_gdb_comm.GetLaunchSuccess (error_str))
+ {
+ SetID (m_gdb_comm.GetCurrentProcessID ());
+ }
+ else
+ {
+ error.SetErrorString (error_str.c_str());
+ }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err);
+ }
+
+ m_gdb_comm.SetPacketTimeout (old_packet_timeout);
+
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ {
+ if (log)
+ log->Printf("failed to connect to debugserver: %s", error.AsCString());
+ KillDebugserverProcess ();
+ return error;
+ }
+
+ if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false))
+ {
+ SetPrivateState (SetThreadStopInfo (m_last_stop_packet));
+
+ if (!disable_stdio)
+ {
+ if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd)
+ SetSTDIOFileDescriptor (pty.ReleaseMasterFileDescriptor());
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf("failed to connect to debugserver: %s", error.AsCString());
+ }
+ }
+ else
+ {
+ // Set our user ID to an invalid process ID.
+ SetID(LLDB_INVALID_PROCESS_ID);
+ error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s",
+ exe_module->GetFileSpec().GetFilename().AsCString(),
+ exe_module->GetArchitecture().GetArchitectureName());
+ }
+ return error;
+
+}
+
+
+Error
+ProcessGDBRemote::ConnectToDebugserver (const char *connect_url)
+{
+ Error error;
+ // Sleep and wait a bit for debugserver to start to listen...
+ std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
+ if (conn_ap.get())
+ {
+ const uint32_t max_retry_count = 50;
+ uint32_t retry_count = 0;
+ while (!m_gdb_comm.IsConnected())
+ {
+ if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess)
+ {
+ m_gdb_comm.SetConnection (conn_ap.release());
+ break;
+ }
+ else if (error.WasInterrupted())
+ {
+ // If we were interrupted, don't keep retrying.
+ break;
+ }
+
+ retry_count++;
+
+ if (retry_count >= max_retry_count)
+ break;
+
+ usleep (100000);
+ }
+ }
+
+ if (!m_gdb_comm.IsConnected())
+ {
+ if (error.Success())
+ error.SetErrorString("not connected to remote gdb server");
+ return error;
+ }
+
+ // We always seem to be able to open a connection to a local port
+ // so we need to make sure we can then send data to it. If we can't
+ // then we aren't actually connected to anything, so try and do the
+ // handshake with the remote GDB server and make sure that goes
+ // alright.
+ if (!m_gdb_comm.HandshakeWithServer (NULL))
+ {
+ m_gdb_comm.Disconnect();
+ if (error.Success())
+ error.SetErrorString("not connected to remote gdb server");
+ return error;
+ }
+ m_gdb_comm.ResetDiscoverableSettings();
+ m_gdb_comm.QueryNoAckModeSupported ();
+ m_gdb_comm.GetThreadSuffixSupported ();
+ m_gdb_comm.GetListThreadsInStopReplySupported ();
+ m_gdb_comm.GetHostInfo ();
+ m_gdb_comm.GetVContSupported ('c');
+ m_gdb_comm.GetVAttachOrWaitSupported();
+
+ size_t num_cmds = GetExtraStartupCommands().GetArgumentCount();
+ for (size_t idx = 0; idx < num_cmds; idx++)
+ {
+ StringExtractorGDBRemote response;
+ m_gdb_comm.SendPacketAndWaitForResponse (GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false);
+ }
+ return error;
+}
+
+void
+ProcessGDBRemote::DidLaunchOrAttach ()
+{
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::DidLaunch()");
+ if (GetID() != LLDB_INVALID_PROCESS_ID)
+ {
+ m_dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS;
+
+ BuildDynamicRegisterInfo (false);
+
+ // See if the GDB server supports the qHostInfo information
+
+ ArchSpec gdb_remote_arch = m_gdb_comm.GetHostArchitecture();
+
+ // See if the GDB server supports the qProcessInfo packet, if so
+ // prefer that over the Host information as it will be more specific
+ // to our process.
+
+ if (m_gdb_comm.GetProcessArchitecture().IsValid())
+ gdb_remote_arch = m_gdb_comm.GetProcessArchitecture();
+
+ if (gdb_remote_arch.IsValid())
+ {
+ ArchSpec &target_arch = GetTarget().GetArchitecture();
+
+ if (target_arch.IsValid())
+ {
+ // If the remote host is ARM and we have apple as the vendor, then
+ // ARM executables and shared libraries can have mixed ARM architectures.
+ // You can have an armv6 executable, and if the host is armv7, then the
+ // system will load the best possible architecture for all shared libraries
+ // it has, so we really need to take the remote host architecture as our
+ // defacto architecture in this case.
+
+ if (gdb_remote_arch.GetMachine() == llvm::Triple::arm &&
+ gdb_remote_arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ {
+ target_arch = gdb_remote_arch;
+ }
+ else
+ {
+ // Fill in what is missing in the triple
+ const llvm::Triple &remote_triple = gdb_remote_arch.GetTriple();
+ llvm::Triple &target_triple = target_arch.GetTriple();
+ if (target_triple.getVendorName().size() == 0)
+ {
+ target_triple.setVendor (remote_triple.getVendor());
+
+ if (target_triple.getOSName().size() == 0)
+ {
+ target_triple.setOS (remote_triple.getOS());
+
+ if (target_triple.getEnvironmentName().size() == 0)
+ target_triple.setEnvironment (remote_triple.getEnvironment());
+ }
+ }
+ }
+ }
+ else
+ {
+ // The target doesn't have a valid architecture yet, set it from
+ // the architecture we got from the remote GDB server
+ target_arch = gdb_remote_arch;
+ }
+ }
+ }
+}
+
+void
+ProcessGDBRemote::DidLaunch ()
+{
+ DidLaunchOrAttach ();
+}
+
+Error
+ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid)
+{
+ ProcessAttachInfo attach_info;
+ return DoAttachToProcessWithID(attach_pid, attach_info);
+}
+
+Error
+ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info)
+{
+ Error error;
+ // Clear out and clean up from any current state
+ Clear();
+ if (attach_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ // Make sure we aren't already connected?
+ if (!m_gdb_comm.IsConnected())
+ {
+ char host_port[128];
+ snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ());
+ char connect_url[128];
+ snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port);
+
+ error = StartDebugserverProcess (host_port, attach_info);
+
+ if (error.Fail())
+ {
+ const char *error_string = error.AsCString();
+ if (error_string == NULL)
+ error_string = "unable to launch " DEBUGSERVER_BASENAME;
+
+ SetExitStatus (-1, error_string);
+ }
+ else
+ {
+ error = ConnectToDebugserver (connect_url);
+ }
+ }
+
+ if (error.Success())
+ {
+ char packet[64];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid);
+ SetID (attach_pid);
+ m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet, packet_len));
+ }
+ }
+ return error;
+}
+
+size_t
+ProcessGDBRemote::AttachInputReaderCallback
+(
+ void *baton,
+ InputReader *reader,
+ lldb::InputReaderAction notification,
+ const char *bytes,
+ size_t bytes_len
+)
+{
+ if (notification == eInputReaderGotToken)
+ {
+ ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)baton;
+ if (gdb_process->m_waiting_for_attach)
+ gdb_process->m_waiting_for_attach = false;
+ reader->SetIsDone(true);
+ return 1;
+ }
+ return 0;
+}
+
+Error
+ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const ProcessAttachInfo &attach_info)
+{
+ Error error;
+ // Clear out and clean up from any current state
+ Clear();
+
+ if (process_name && process_name[0])
+ {
+ // Make sure we aren't already connected?
+ if (!m_gdb_comm.IsConnected())
+ {
+ char host_port[128];
+ snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ());
+ char connect_url[128];
+ snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port);
+
+ error = StartDebugserverProcess (host_port, attach_info);
+ if (error.Fail())
+ {
+ const char *error_string = error.AsCString();
+ if (error_string == NULL)
+ error_string = "unable to launch " DEBUGSERVER_BASENAME;
+
+ SetExitStatus (-1, error_string);
+ }
+ else
+ {
+ error = ConnectToDebugserver (connect_url);
+ }
+ }
+
+ if (error.Success())
+ {
+ StreamString packet;
+
+ if (wait_for_launch)
+ {
+ if (!m_gdb_comm.GetVAttachOrWaitSupported())
+ {
+ packet.PutCString ("vAttachWait");
+ }
+ else
+ {
+ if (attach_info.GetIgnoreExisting())
+ packet.PutCString("vAttachWait");
+ else
+ packet.PutCString ("vAttachOrWait");
+ }
+ }
+ else
+ packet.PutCString("vAttachName");
+ packet.PutChar(';');
+ packet.PutBytesAsRawHex8(process_name, strlen(process_name), lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder());
+
+ m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet.GetData(), packet.GetSize()));
+
+ }
+ }
+ return error;
+}
+
+
+void
+ProcessGDBRemote::DidAttach ()
+{
+ DidLaunchOrAttach ();
+}
+
+
+Error
+ProcessGDBRemote::WillResume ()
+{
+ m_continue_c_tids.clear();
+ m_continue_C_tids.clear();
+ m_continue_s_tids.clear();
+ m_continue_S_tids.clear();
+ return Error();
+}
+
+Error
+ProcessGDBRemote::DoResume ()
+{
+ Error error;
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::Resume()");
+
+ Listener listener ("gdb-remote.resume-packet-sent");
+ if (listener.StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent))
+ {
+ listener.StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit);
+
+ const size_t num_threads = GetThreadList().GetSize();
+
+ StreamString continue_packet;
+ bool continue_packet_error = false;
+ if (m_gdb_comm.HasAnyVContSupport ())
+ {
+ if (m_continue_c_tids.size() == num_threads)
+ {
+ // All threads are continuing, just send a "c" packet
+ continue_packet.PutCString ("c");
+ }
+ else
+ {
+ continue_packet.PutCString ("vCont");
+
+ if (!m_continue_c_tids.empty())
+ {
+ if (m_gdb_comm.GetVContSupported ('c'))
+ {
+ for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos)
+ continue_packet.Printf(";c:%4.4" PRIx64, *t_pos);
+ }
+ else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_C_tids.empty())
+ {
+ if (m_gdb_comm.GetVContSupported ('C'))
+ {
+ for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos)
+ continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first);
+ }
+ else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_s_tids.empty())
+ {
+ if (m_gdb_comm.GetVContSupported ('s'))
+ {
+ for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos)
+ continue_packet.Printf(";s:%4.4" PRIx64, *t_pos);
+ }
+ else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_S_tids.empty())
+ {
+ if (m_gdb_comm.GetVContSupported ('S'))
+ {
+ for (tid_sig_collection::const_iterator s_pos = m_continue_S_tids.begin(), s_end = m_continue_S_tids.end(); s_pos != s_end; ++s_pos)
+ continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first);
+ }
+ else
+ continue_packet_error = true;
+ }
+
+ if (continue_packet_error)
+ continue_packet.GetString().clear();
+ }
+ }
+ else
+ continue_packet_error = true;
+
+ if (continue_packet_error)
+ {
+ // Either no vCont support, or we tried to use part of the vCont
+ // packet that wasn't supported by the remote GDB server.
+ // We need to try and make a simple packet that can do our continue
+ const size_t num_continue_c_tids = m_continue_c_tids.size();
+ const size_t num_continue_C_tids = m_continue_C_tids.size();
+ const size_t num_continue_s_tids = m_continue_s_tids.size();
+ const size_t num_continue_S_tids = m_continue_S_tids.size();
+ if (num_continue_c_tids > 0)
+ {
+ if (num_continue_c_tids == num_threads)
+ {
+ // All threads are resuming...
+ m_gdb_comm.SetCurrentThreadForRun (-1);
+ continue_packet.PutChar ('c');
+ continue_packet_error = false;
+ }
+ else if (num_continue_c_tids == 1 &&
+ num_continue_C_tids == 0 &&
+ num_continue_s_tids == 0 &&
+ num_continue_S_tids == 0 )
+ {
+ // Only one thread is continuing
+ m_gdb_comm.SetCurrentThreadForRun (m_continue_c_tids.front());
+ continue_packet.PutChar ('c');
+ continue_packet_error = false;
+ }
+ }
+
+ if (continue_packet_error && num_continue_C_tids > 0)
+ {
+ if ((num_continue_C_tids + num_continue_c_tids) == num_threads &&
+ num_continue_C_tids > 0 &&
+ num_continue_s_tids == 0 &&
+ num_continue_S_tids == 0 )
+ {
+ const int continue_signo = m_continue_C_tids.front().second;
+ // Only one thread is continuing
+ if (num_continue_C_tids > 1)
+ {
+ // More that one thread with a signal, yet we don't have
+ // vCont support and we are being asked to resume each
+ // thread with a signal, we need to make sure they are
+ // all the same signal, or we can't issue the continue
+ // accurately with the current support...
+ if (num_continue_C_tids > 1)
+ {
+ continue_packet_error = false;
+ for (size_t i=1; i<m_continue_C_tids.size(); ++i)
+ {
+ if (m_continue_C_tids[i].second != continue_signo)
+ continue_packet_error = true;
+ }
+ }
+ if (!continue_packet_error)
+ m_gdb_comm.SetCurrentThreadForRun (-1);
+ }
+ else
+ {
+ // Set the continue thread ID
+ continue_packet_error = false;
+ m_gdb_comm.SetCurrentThreadForRun (m_continue_C_tids.front().first);
+ }
+ if (!continue_packet_error)
+ {
+ // Add threads continuing with the same signo...
+ continue_packet.Printf("C%2.2x", continue_signo);
+ }
+ }
+ }
+
+ if (continue_packet_error && num_continue_s_tids > 0)
+ {
+ if (num_continue_s_tids == num_threads)
+ {
+ // All threads are resuming...
+ m_gdb_comm.SetCurrentThreadForRun (-1);
+ continue_packet.PutChar ('s');
+ continue_packet_error = false;
+ }
+ else if (num_continue_c_tids == 0 &&
+ num_continue_C_tids == 0 &&
+ num_continue_s_tids == 1 &&
+ num_continue_S_tids == 0 )
+ {
+ // Only one thread is stepping
+ m_gdb_comm.SetCurrentThreadForRun (m_continue_s_tids.front());
+ continue_packet.PutChar ('s');
+ continue_packet_error = false;
+ }
+ }
+
+ if (!continue_packet_error && num_continue_S_tids > 0)
+ {
+ if (num_continue_S_tids == num_threads)
+ {
+ const int step_signo = m_continue_S_tids.front().second;
+ // Are all threads trying to step with the same signal?
+ continue_packet_error = false;
+ if (num_continue_S_tids > 1)
+ {
+ for (size_t i=1; i<num_threads; ++i)
+ {
+ if (m_continue_S_tids[i].second != step_signo)
+ continue_packet_error = true;
+ }
+ }
+ if (!continue_packet_error)
+ {
+ // Add threads stepping with the same signo...
+ m_gdb_comm.SetCurrentThreadForRun (-1);
+ continue_packet.Printf("S%2.2x", step_signo);
+ }
+ }
+ else if (num_continue_c_tids == 0 &&
+ num_continue_C_tids == 0 &&
+ num_continue_s_tids == 0 &&
+ num_continue_S_tids == 1 )
+ {
+ // Only one thread is stepping with signal
+ m_gdb_comm.SetCurrentThreadForRun (m_continue_S_tids.front().first);
+ continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second);
+ continue_packet_error = false;
+ }
+ }
+ }
+
+ if (continue_packet_error)
+ {
+ error.SetErrorString ("can't make continue packet for this resume");
+ }
+ else
+ {
+ EventSP event_sp;
+ TimeValue timeout;
+ timeout = TimeValue::Now();
+ timeout.OffsetWithSeconds (5);
+ if (!IS_VALID_LLDB_HOST_THREAD(m_async_thread))
+ {
+ error.SetErrorString ("Trying to resume but the async thread is dead.");
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoResume: Trying to resume but the async thread is dead.");
+ return error;
+ }
+
+ m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (continue_packet.GetData(), continue_packet.GetSize()));
+
+ if (listener.WaitForEvent (&timeout, event_sp) == false)
+ {
+ error.SetErrorString("Resume timed out.");
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoResume: Resume timed out.");
+ }
+ else if (event_sp->BroadcasterIs (&m_async_broadcaster))
+ {
+ error.SetErrorString ("Broadcast continue, but the async thread was killed before we got an ack back.");
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoResume: Broadcast continue, but the async thread was killed before we got an ack back.");
+ return error;
+ }
+ }
+ }
+
+ return error;
+}
+
+void
+ProcessGDBRemote::ClearThreadIDList ()
+{
+ Mutex::Locker locker(m_thread_list_real.GetMutex());
+ m_thread_ids.clear();
+}
+
+bool
+ProcessGDBRemote::UpdateThreadIDList ()
+{
+ Mutex::Locker locker(m_thread_list_real.GetMutex());
+ bool sequence_mutex_unavailable = false;
+ m_gdb_comm.GetCurrentThreadIDs (m_thread_ids, sequence_mutex_unavailable);
+ if (sequence_mutex_unavailable)
+ {
+ return false; // We just didn't get the list
+ }
+ return true;
+}
+
+bool
+ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list)
+{
+ // locker will keep a mutex locked until it goes out of scope
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD));
+ if (log && log->GetMask().Test(GDBR_LOG_VERBOSE))
+ log->Printf ("ProcessGDBRemote::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID());
+
+ size_t num_thread_ids = m_thread_ids.size();
+ // The "m_thread_ids" thread ID list should always be updated after each stop
+ // reply packet, but in case it isn't, update it here.
+ if (num_thread_ids == 0)
+ {
+ if (!UpdateThreadIDList ())
+ return false;
+ num_thread_ids = m_thread_ids.size();
+ }
+
+ ThreadList old_thread_list_copy(old_thread_list);
+ if (num_thread_ids > 0)
+ {
+ for (size_t i=0; i<num_thread_ids; ++i)
+ {
+ tid_t tid = m_thread_ids[i];
+ ThreadSP thread_sp (old_thread_list_copy.RemoveThreadByProtocolID(tid, false));
+ if (!thread_sp)
+ {
+ thread_sp.reset (new ThreadGDBRemote (*this, tid));
+ if (log && log->GetMask().Test(GDBR_LOG_VERBOSE))
+ log->Printf(
+ "ProcessGDBRemote::%s Making new thread: %p for thread ID: 0x%" PRIx64 ".\n",
+ __FUNCTION__,
+ thread_sp.get(),
+ thread_sp->GetID());
+ }
+ else
+ {
+ if (log && log->GetMask().Test(GDBR_LOG_VERBOSE))
+ log->Printf(
+ "ProcessGDBRemote::%s Found old thread: %p for thread ID: 0x%" PRIx64 ".\n",
+ __FUNCTION__,
+ thread_sp.get(),
+ thread_sp->GetID());
+ }
+ new_thread_list.AddThread(thread_sp);
+ }
+ }
+
+ // Whatever that is left in old_thread_list_copy are not
+ // present in new_thread_list. Remove non-existent threads from internal id table.
+ size_t old_num_thread_ids = old_thread_list_copy.GetSize(false);
+ for (size_t i=0; i<old_num_thread_ids; i++)
+ {
+ ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex (i, false));
+ if (old_thread_sp)
+ {
+ lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID();
+ m_thread_id_to_index_id_map.erase(old_thread_id);
+ }
+ }
+
+ return true;
+}
+
+
+StateType
+ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
+{
+ stop_packet.SetFilePos (0);
+ const char stop_type = stop_packet.GetChar();
+ switch (stop_type)
+ {
+ case 'T':
+ case 'S':
+ {
+ // This is a bit of a hack, but is is required. If we did exec, we
+ // need to clear our thread lists and also know to rebuild our dynamic
+ // register info before we lookup and threads and populate the expedited
+ // register values so we need to know this right away so we can cleanup
+ // and update our registers.
+ const uint32_t stop_id = GetStopID();
+ if (stop_id == 0)
+ {
+ // Our first stop, make sure we have a process ID, and also make
+ // sure we know about our registers
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ {
+ lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID ();
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ SetID (pid);
+ }
+ BuildDynamicRegisterInfo (true);
+ }
+ // Stop with signal and thread info
+ const uint8_t signo = stop_packet.GetHexU8();
+ std::string name;
+ std::string value;
+ std::string thread_name;
+ std::string reason;
+ std::string description;
+ uint32_t exc_type = 0;
+ std::vector<addr_t> exc_data;
+ addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
+ ThreadSP thread_sp;
+ ThreadGDBRemote *gdb_thread = NULL;
+
+ while (stop_packet.GetNameColonValue(name, value))
+ {
+ if (name.compare("metype") == 0)
+ {
+ // exception type in big endian hex
+ exc_type = Args::StringToUInt32 (value.c_str(), 0, 16);
+ }
+ else if (name.compare("medata") == 0)
+ {
+ // exception data in big endian hex
+ exc_data.push_back(Args::StringToUInt64 (value.c_str(), 0, 16));
+ }
+ else if (name.compare("thread") == 0)
+ {
+ // thread in big endian hex
+ lldb::tid_t tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16);
+ // m_thread_list_real does have its own mutex, but we need to
+ // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...)
+ // and the m_thread_list_real.AddThread(...) so it doesn't change on us
+ Mutex::Locker locker (m_thread_list_real.GetMutex ());
+ thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false);
+
+ if (!thread_sp)
+ {
+ // Create the thread if we need to
+ thread_sp.reset (new ThreadGDBRemote (*this, tid));
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD));
+ if (log && log->GetMask().Test(GDBR_LOG_VERBOSE))
+ log->Printf ("ProcessGDBRemote::%s Adding new thread: %p for thread ID: 0x%" PRIx64 ".\n",
+ __FUNCTION__,
+ thread_sp.get(),
+ thread_sp->GetID());
+
+ m_thread_list_real.AddThread(thread_sp);
+ }
+ gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
+
+ }
+ else if (name.compare("threads") == 0)
+ {
+ Mutex::Locker locker(m_thread_list_real.GetMutex());
+ m_thread_ids.clear();
+ // A comma separated list of all threads in the current
+ // process that includes the thread for this stop reply
+ // packet
+ size_t comma_pos;
+ lldb::tid_t tid;
+ while ((comma_pos = value.find(',')) != std::string::npos)
+ {
+ value[comma_pos] = '\0';
+ // thread in big endian hex
+ tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16);
+ if (tid != LLDB_INVALID_THREAD_ID)
+ m_thread_ids.push_back (tid);
+ value.erase(0, comma_pos + 1);
+
+ }
+ tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16);
+ if (tid != LLDB_INVALID_THREAD_ID)
+ m_thread_ids.push_back (tid);
+ }
+ else if (name.compare("hexname") == 0)
+ {
+ StringExtractor name_extractor;
+ // Swap "value" over into "name_extractor"
+ name_extractor.GetStringRef().swap(value);
+ // Now convert the HEX bytes into a string value
+ name_extractor.GetHexByteString (value);
+ thread_name.swap (value);
+ }
+ else if (name.compare("name") == 0)
+ {
+ thread_name.swap (value);
+ }
+ else if (name.compare("qaddr") == 0)
+ {
+ thread_dispatch_qaddr = Args::StringToUInt64 (value.c_str(), 0, 16);
+ }
+ else if (name.compare("reason") == 0)
+ {
+ reason.swap(value);
+ }
+ else if (name.compare("description") == 0)
+ {
+ StringExtractor desc_extractor;
+ // Swap "value" over into "name_extractor"
+ desc_extractor.GetStringRef().swap(value);
+ // Now convert the HEX bytes into a string value
+ desc_extractor.GetHexByteString (thread_name);
+ }
+ else if (name.size() == 2 && ::isxdigit(name[0]) && ::isxdigit(name[1]))
+ {
+ // We have a register number that contains an expedited
+ // register value. Lets supply this register to our thread
+ // so it won't have to go and read it.
+ if (gdb_thread)
+ {
+ uint32_t reg = Args::StringToUInt32 (name.c_str(), UINT32_MAX, 16);
+
+ if (reg != UINT32_MAX)
+ {
+ StringExtractor reg_value_extractor;
+ // Swap "value" over into "reg_value_extractor"
+ reg_value_extractor.GetStringRef().swap(value);
+ if (!gdb_thread->PrivateSetRegisterValue (reg, reg_value_extractor))
+ {
+ Host::SetCrashDescriptionWithFormat("Setting thread register '%s' (decoded to %u (0x%x)) with value '%s' for stop packet: '%s'",
+ name.c_str(),
+ reg,
+ reg,
+ reg_value_extractor.GetStringRef().c_str(),
+ stop_packet.GetStringRef().c_str());
+ }
+ }
+ }
+ }
+ }
+
+ if (thread_sp)
+ {
+ // Clear the stop info just in case we don't set it to anything
+ thread_sp->SetStopInfo (StopInfoSP());
+
+ gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr);
+ gdb_thread->SetName (thread_name.empty() ? NULL : thread_name.c_str());
+ if (exc_type != 0)
+ {
+ const size_t exc_data_size = exc_data.size();
+
+ thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp,
+ exc_type,
+ exc_data_size,
+ exc_data_size >= 1 ? exc_data[0] : 0,
+ exc_data_size >= 2 ? exc_data[1] : 0,
+ exc_data_size >= 3 ? exc_data[2] : 0));
+ }
+ else
+ {
+ bool handled = false;
+ bool did_exec = false;
+ if (!reason.empty())
+ {
+ if (reason.compare("trace") == 0)
+ {
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
+ handled = true;
+ }
+ else if (reason.compare("breakpoint") == 0)
+ {
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+ lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
+ if (bp_site_sp)
+ {
+ // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
+ // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
+ // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
+ handled = true;
+ if (bp_site_sp->ValidForThisThread (thread_sp.get()))
+ {
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
+ }
+ else
+ {
+ StopInfoSP invalid_stop_info_sp;
+ thread_sp->SetStopInfo (invalid_stop_info_sp);
+ }
+ }
+
+ }
+ else if (reason.compare("trap") == 0)
+ {
+ // Let the trap just use the standard signal stop reason below...
+ }
+ else if (reason.compare("watchpoint") == 0)
+ {
+ break_id_t watch_id = LLDB_INVALID_WATCH_ID;
+ // TODO: locate the watchpoint somehow...
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id));
+ handled = true;
+ }
+ else if (reason.compare("exception") == 0)
+ {
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str()));
+ handled = true;
+ }
+ else if (reason.compare("exec") == 0)
+ {
+ did_exec = true;
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp));
+ handled = true;
+ }
+ }
+
+ if (signo && did_exec == false)
+ {
+ if (signo == SIGTRAP)
+ {
+ // Currently we are going to assume SIGTRAP means we are either
+ // hitting a breakpoint or hardware single stepping.
+ handled = true;
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+ lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
+
+ if (bp_site_sp)
+ {
+ // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
+ // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
+ // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
+ if (bp_site_sp->ValidForThisThread (thread_sp.get()))
+ {
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
+ }
+ else
+ {
+ StopInfoSP invalid_stop_info_sp;
+ thread_sp->SetStopInfo (invalid_stop_info_sp);
+ }
+ }
+ else
+ {
+ // If we were stepping then assume the stop was the result of the trace. If we were
+ // not stepping then report the SIGTRAP.
+ // FIXME: We are still missing the case where we single step over a trap instruction.
+ if (thread_sp->GetTemporaryResumeState() == eStateStepping)
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
+ else
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo));
+ }
+ }
+ if (!handled)
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo));
+ }
+
+ if (!description.empty())
+ {
+ lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ());
+ if (stop_info_sp)
+ {
+ stop_info_sp->SetDescription (description.c_str());
+ }
+ else
+ {
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str()));
+ }
+ }
+ }
+ }
+ return eStateStopped;
+ }
+ break;
+
+ case 'W':
+ // process exited
+ return eStateExited;
+
+ default:
+ break;
+ }
+ return eStateInvalid;
+}
+
+void
+ProcessGDBRemote::RefreshStateAfterStop ()
+{
+ Mutex::Locker locker(m_thread_list_real.GetMutex());
+ m_thread_ids.clear();
+ // Set the thread stop info. It might have a "threads" key whose value is
+ // a list of all thread IDs in the current process, so m_thread_ids might
+ // get set.
+ SetThreadStopInfo (m_last_stop_packet);
+ // Check to see if SetThreadStopInfo() filled in m_thread_ids?
+ if (m_thread_ids.empty())
+ {
+ // No, we need to fetch the thread list manually
+ UpdateThreadIDList();
+ }
+
+ // Let all threads recover from stopping and do any clean up based
+ // on the previous thread state (if any).
+ m_thread_list_real.RefreshStateAfterStop();
+
+}
+
+Error
+ProcessGDBRemote::DoHalt (bool &caused_stop)
+{
+ Error error;
+
+ bool timed_out = false;
+ Mutex::Locker locker;
+
+ if (m_public_state.GetValue() == eStateAttaching)
+ {
+ // We are being asked to halt during an attach. We need to just close
+ // our file handle and debugserver will go away, and we can be done...
+ m_gdb_comm.Disconnect();
+ }
+ else
+ {
+ if (!m_gdb_comm.SendInterrupt (locker, 2, timed_out))
+ {
+ if (timed_out)
+ error.SetErrorString("timed out sending interrupt packet");
+ else
+ error.SetErrorString("unknown error sending interrupt packet");
+ }
+
+ caused_stop = m_gdb_comm.GetInterruptWasSent ();
+ }
+ return error;
+}
+
+Error
+ProcessGDBRemote::DoDetach(bool keep_stopped)
+{
+ Error error;
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped);
+
+ DisableAllBreakpointSites ();
+
+ m_thread_list.DiscardThreadPlans();
+
+ error = m_gdb_comm.Detach (keep_stopped);
+ if (log)
+ {
+ if (error.Success())
+ log->PutCString ("ProcessGDBRemote::DoDetach() detach packet sent successfully");
+ else
+ log->Printf ("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : "<unknown error>");
+ }
+
+ if (!error.Success())
+ return error;
+
+ // Sleep for one second to let the process get all detached...
+ StopAsyncThread ();
+
+ SetPrivateState (eStateDetached);
+ ResumePrivateStateThread();
+
+ //KillDebugserverProcess ();
+ return error;
+}
+
+
+Error
+ProcessGDBRemote::DoDestroy ()
+{
+ Error error;
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDestroy()");
+
+ // There is a bug in older iOS debugservers where they don't shut down the process
+ // they are debugging properly. If the process is sitting at a breakpoint or an exception,
+ // this can cause problems with restarting. So we check to see if any of our threads are stopped
+ // at a breakpoint, and if so we remove all the breakpoints, resume the process, and THEN
+ // destroy it again.
+ //
+ // Note, we don't have a good way to test the version of debugserver, but I happen to know that
+ // the set of all the iOS debugservers which don't support GetThreadSuffixSupported() and that of
+ // the debugservers with this bug are equal. There really should be a better way to test this!
+ //
+ // We also use m_destroy_tried_resuming to make sure we only do this once, if we resume and then halt and
+ // get called here to destroy again and we're still at a breakpoint or exception, then we should
+ // just do the straight-forward kill.
+ //
+ // And of course, if we weren't able to stop the process by the time we get here, it isn't
+ // necessary (or helpful) to do any of this.
+
+ if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning)
+ {
+ PlatformSP platform_sp = GetTarget().GetPlatform();
+
+ // FIXME: These should be ConstStrings so we aren't doing strcmp'ing.
+ if (platform_sp
+ && platform_sp->GetName()
+ && platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic())
+ {
+ if (m_destroy_tried_resuming)
+ {
+ if (log)
+ log->PutCString ("ProcessGDBRemote::DoDestroy()Tried resuming to destroy once already, not doing it again.");
+ }
+ else
+ {
+ // At present, the plans are discarded and the breakpoints disabled Process::Destroy,
+ // but we really need it to happen here and it doesn't matter if we do it twice.
+ m_thread_list.DiscardThreadPlans();
+ DisableAllBreakpointSites();
+
+ bool stop_looks_like_crash = false;
+ ThreadList &threads = GetThreadList();
+
+ {
+ Mutex::Locker locker(threads.GetMutex());
+
+ size_t num_threads = threads.GetSize();
+ for (size_t i = 0; i < num_threads; i++)
+ {
+ ThreadSP thread_sp = threads.GetThreadAtIndex(i);
+ StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo();
+ StopReason reason = eStopReasonInvalid;
+ if (stop_info_sp)
+ reason = stop_info_sp->GetStopReason();
+ if (reason == eStopReasonBreakpoint
+ || reason == eStopReasonException)
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 " stopped with reason: %s.",
+ thread_sp->GetProtocolID(),
+ stop_info_sp->GetDescription());
+ stop_looks_like_crash = true;
+ break;
+ }
+ }
+ }
+
+ if (stop_looks_like_crash)
+ {
+ if (log)
+ log->PutCString ("ProcessGDBRemote::DoDestroy() - Stopped at a breakpoint, continue and then kill.");
+ m_destroy_tried_resuming = true;
+
+ // If we are going to run again before killing, it would be good to suspend all the threads
+ // before resuming so they won't get into more trouble. Sadly, for the threads stopped with
+ // the breakpoint or exception, the exception doesn't get cleared if it is suspended, so we do
+ // have to run the risk of letting those threads proceed a bit.
+
+ {
+ Mutex::Locker locker(threads.GetMutex());
+
+ size_t num_threads = threads.GetSize();
+ for (size_t i = 0; i < num_threads; i++)
+ {
+ ThreadSP thread_sp = threads.GetThreadAtIndex(i);
+ StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo();
+ StopReason reason = eStopReasonInvalid;
+ if (stop_info_sp)
+ reason = stop_info_sp->GetStopReason();
+ if (reason != eStopReasonBreakpoint
+ && reason != eStopReasonException)
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDestroy() - Suspending thread: 0x%4.4" PRIx64 " before running.",
+ thread_sp->GetProtocolID());
+ thread_sp->SetResumeState(eStateSuspended);
+ }
+ }
+ }
+ Resume ();
+ return Destroy();
+ }
+ }
+ }
+ }
+
+ // Interrupt if our inferior is running...
+ int exit_status = SIGABRT;
+ std::string exit_string;
+
+ if (m_gdb_comm.IsConnected())
+ {
+ if (m_public_state.GetValue() != eStateAttaching)
+ {
+
+ StringExtractorGDBRemote response;
+ bool send_async = true;
+ const uint32_t old_packet_timeout = m_gdb_comm.SetPacketTimeout (3);
+
+ if (m_gdb_comm.SendPacketAndWaitForResponse("k", 1, response, send_async))
+ {
+ char packet_cmd = response.GetChar(0);
+
+ if (packet_cmd == 'W' || packet_cmd == 'X')
+ {
+ SetLastStopPacket (response);
+ ClearThreadIDList ();
+ exit_status = response.GetHexU8();
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDestroy - got unexpected response to k packet: %s", response.GetStringRef().c_str());
+ exit_string.assign("got unexpected response to k packet: ");
+ exit_string.append(response.GetStringRef());
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet");
+ exit_string.assign("failed to send the k packet");
+ }
+
+ m_gdb_comm.SetPacketTimeout(old_packet_timeout);
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet");
+ exit_string.assign ("killed or interrupted while attaching.");
+ }
+ }
+ else
+ {
+ // If we missed setting the exit status on the way out, do it here.
+ // NB set exit status can be called multiple times, the first one sets the status.
+ exit_string.assign("destroying when not connected to debugserver");
+ }
+
+ SetExitStatus(exit_status, exit_string.c_str());
+
+ StopAsyncThread ();
+ KillDebugserverProcess ();
+ return error;
+}
+
+void
+ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response)
+{
+ lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex);
+ const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos;
+ if (did_exec)
+ {
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::SetLastStopPacket () - detected exec");
+
+ m_thread_list_real.Clear();
+ m_thread_list.Clear();
+ BuildDynamicRegisterInfo (true);
+ m_gdb_comm.ResetDiscoverableSettings();
+ }
+ m_last_stop_packet = response;
+}
+
+
+//------------------------------------------------------------------
+// Process Queries
+//------------------------------------------------------------------
+
+bool
+ProcessGDBRemote::IsAlive ()
+{
+ return m_gdb_comm.IsConnected() && m_private_state.GetValue() != eStateExited;
+}
+
+addr_t
+ProcessGDBRemote::GetImageInfoAddress()
+{
+ return m_gdb_comm.GetShlibInfoAddr();
+}
+
+//------------------------------------------------------------------
+// Process Memory
+//------------------------------------------------------------------
+size_t
+ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error)
+{
+ if (size > m_max_memory_size)
+ {
+ // Keep memory read sizes down to a sane limit. This function will be
+ // called multiple times in order to complete the task by
+ // lldb_private::Process so it is ok to do this.
+ size = m_max_memory_size;
+ }
+
+ char packet[64];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "m%" PRIx64 ",%" PRIx64, (uint64_t)addr, (uint64_t)size);
+ assert (packet_len + 1 < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, true))
+ {
+ if (response.IsNormalResponse())
+ {
+ error.Clear();
+ return response.GetHexBytes(buf, size, '\xdd');
+ }
+ else if (response.IsErrorResponse())
+ error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr);
+ else if (response.IsUnsupportedResponse())
+ error.SetErrorStringWithFormat("GDB server does not support reading memory");
+ else
+ error.SetErrorStringWithFormat("unexpected response to GDB server memory read packet '%s': '%s'", packet, response.GetStringRef().c_str());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("failed to send packet: '%s'", packet);
+ }
+ return 0;
+}
+
+size_t
+ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error)
+{
+ if (size > m_max_memory_size)
+ {
+ // Keep memory read sizes down to a sane limit. This function will be
+ // called multiple times in order to complete the task by
+ // lldb_private::Process so it is ok to do this.
+ size = m_max_memory_size;
+ }
+
+ StreamString packet;
+ packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size);
+ packet.PutBytesAsRawHex8(buf, size, lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder());
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, true))
+ {
+ if (response.IsOKResponse())
+ {
+ error.Clear();
+ return size;
+ }
+ else if (response.IsErrorResponse())
+ error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, addr);
+ else if (response.IsUnsupportedResponse())
+ error.SetErrorStringWithFormat("GDB server does not support writing memory");
+ else
+ error.SetErrorStringWithFormat("unexpected response to GDB server memory write packet '%s': '%s'", packet.GetString().c_str(), response.GetStringRef().c_str());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetString().c_str());
+ }
+ return 0;
+}
+
+lldb::addr_t
+ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error)
+{
+ addr_t allocated_addr = LLDB_INVALID_ADDRESS;
+
+ LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory();
+ switch (supported)
+ {
+ case eLazyBoolCalculate:
+ case eLazyBoolYes:
+ allocated_addr = m_gdb_comm.AllocateMemory (size, permissions);
+ if (allocated_addr != LLDB_INVALID_ADDRESS || supported == eLazyBoolYes)
+ return allocated_addr;
+
+ case eLazyBoolNo:
+ // Call mmap() to create memory in the inferior..
+ unsigned prot = 0;
+ if (permissions & lldb::ePermissionsReadable)
+ prot |= eMmapProtRead;
+ if (permissions & lldb::ePermissionsWritable)
+ prot |= eMmapProtWrite;
+ if (permissions & lldb::ePermissionsExecutable)
+ prot |= eMmapProtExec;
+
+ if (InferiorCallMmap(this, allocated_addr, 0, size, prot,
+ eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0))
+ m_addr_to_mmap_size[allocated_addr] = size;
+ else
+ allocated_addr = LLDB_INVALID_ADDRESS;
+ break;
+ }
+
+ if (allocated_addr == LLDB_INVALID_ADDRESS)
+ error.SetErrorStringWithFormat("unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString (permissions));
+ else
+ error.Clear();
+ return allocated_addr;
+}
+
+Error
+ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr,
+ MemoryRegionInfo &region_info)
+{
+
+ Error error (m_gdb_comm.GetMemoryRegionInfo (load_addr, region_info));
+ return error;
+}
+
+Error
+ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num)
+{
+
+ Error error (m_gdb_comm.GetWatchpointSupportInfo (num));
+ return error;
+}
+
+Error
+ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num, bool& after)
+{
+ Error error (m_gdb_comm.GetWatchpointSupportInfo (num, after));
+ return error;
+}
+
+Error
+ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr)
+{
+ Error error;
+ LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory();
+
+ switch (supported)
+ {
+ case eLazyBoolCalculate:
+ // We should never be deallocating memory without allocating memory
+ // first so we should never get eLazyBoolCalculate
+ error.SetErrorString ("tried to deallocate memory without ever allocating memory");
+ break;
+
+ case eLazyBoolYes:
+ if (!m_gdb_comm.DeallocateMemory (addr))
+ error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr);
+ break;
+
+ case eLazyBoolNo:
+ // Call munmap() to deallocate memory in the inferior..
+ {
+ MMapMap::iterator pos = m_addr_to_mmap_size.find(addr);
+ if (pos != m_addr_to_mmap_size.end() &&
+ InferiorCallMunmap(this, addr, pos->second))
+ m_addr_to_mmap_size.erase (pos);
+ else
+ error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr);
+ }
+ break;
+ }
+
+ return error;
+}
+
+
+//------------------------------------------------------------------
+// Process STDIO
+//------------------------------------------------------------------
+size_t
+ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error)
+{
+ if (m_stdio_communication.IsConnected())
+ {
+ ConnectionStatus status;
+ m_stdio_communication.Write(src, src_len, status, NULL);
+ }
+ return 0;
+}
+
+Error
+ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site)
+{
+ Error error;
+ assert (bp_site != NULL);
+
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS));
+ user_id_t site_id = bp_site->GetID();
+ const addr_t addr = bp_site->GetLoadAddress();
+ if (log)
+ log->Printf ("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64, site_id, (uint64_t)addr);
+
+ if (bp_site->IsEnabled())
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", site_id, (uint64_t)addr);
+ return error;
+ }
+ else
+ {
+ const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site);
+
+ if (bp_site->HardwarePreferred())
+ {
+ // Try and set hardware breakpoint, and if that fails, fall through
+ // and set a software breakpoint?
+ if (m_gdb_comm.SupportsGDBStoppointPacket (eBreakpointHardware))
+ {
+ if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, true, addr, bp_op_size) == 0)
+ {
+ bp_site->SetEnabled(true);
+ bp_site->SetType (BreakpointSite::eHardware);
+ return error;
+ }
+ }
+ }
+
+ if (m_gdb_comm.SupportsGDBStoppointPacket (eBreakpointSoftware))
+ {
+ if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, true, addr, bp_op_size) == 0)
+ {
+ bp_site->SetEnabled(true);
+ bp_site->SetType (BreakpointSite::eExternal);
+ return error;
+ }
+ }
+
+ return EnableSoftwareBreakpoint (bp_site);
+ }
+
+ if (log)
+ {
+ const char *err_string = error.AsCString();
+ log->Printf ("ProcessGDBRemote::EnableBreakpointSite () error for breakpoint at 0x%8.8" PRIx64 ": %s",
+ bp_site->GetLoadAddress(),
+ err_string ? err_string : "NULL");
+ }
+ // We shouldn't reach here on a successful breakpoint enable...
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+Error
+ProcessGDBRemote::DisableBreakpointSite (BreakpointSite *bp_site)
+{
+ Error error;
+ assert (bp_site != NULL);
+ addr_t addr = bp_site->GetLoadAddress();
+ user_id_t site_id = bp_site->GetID();
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64, site_id, (uint64_t)addr);
+
+ if (bp_site->IsEnabled())
+ {
+ const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site);
+
+ BreakpointSite::Type bp_type = bp_site->GetType();
+ switch (bp_type)
+ {
+ case BreakpointSite::eSoftware:
+ error = DisableSoftwareBreakpoint (bp_site);
+ break;
+
+ case BreakpointSite::eHardware:
+ if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, false, addr, bp_op_size))
+ error.SetErrorToGenericError();
+ break;
+
+ case BreakpointSite::eExternal:
+ if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, false, addr, bp_op_size))
+ error.SetErrorToGenericError();
+ break;
+ }
+ if (error.Success())
+ bp_site->SetEnabled(false);
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", site_id, (uint64_t)addr);
+ return error;
+ }
+
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+// Pre-requisite: wp != NULL.
+static GDBStoppointType
+GetGDBStoppointType (Watchpoint *wp)
+{
+ assert(wp);
+ bool watch_read = wp->WatchpointRead();
+ bool watch_write = wp->WatchpointWrite();
+
+ // watch_read and watch_write cannot both be false.
+ assert(watch_read || watch_write);
+ if (watch_read && watch_write)
+ return eWatchpointReadWrite;
+ else if (watch_read)
+ return eWatchpointRead;
+ else // Must be watch_write, then.
+ return eWatchpointWrite;
+}
+
+Error
+ProcessGDBRemote::EnableWatchpoint (Watchpoint *wp, bool notify)
+{
+ Error error;
+ if (wp)
+ {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", watchID);
+ if (wp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr);
+ return error;
+ }
+
+ GDBStoppointType type = GetGDBStoppointType(wp);
+ // Pass down an appropriate z/Z packet...
+ if (m_gdb_comm.SupportsGDBStoppointPacket (type))
+ {
+ if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, wp->GetByteSize()) == 0)
+ {
+ wp->SetEnabled(true, notify);
+ return error;
+ }
+ else
+ error.SetErrorString("sending gdb watchpoint packet failed");
+ }
+ else
+ error.SetErrorString("watchpoints not supported");
+ }
+ else
+ {
+ error.SetErrorString("Watchpoint argument was NULL.");
+ }
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+Error
+ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify)
+{
+ Error error;
+ if (wp)
+ {
+ user_id_t watchID = wp->GetID();
+
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS));
+
+ addr_t addr = wp->GetLoadAddress();
+
+ if (log)
+ log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64, watchID, (uint64_t)addr);
+
+ if (!wp->IsEnabled())
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr);
+ // See also 'class WatchpointSentry' within StopInfo.cpp.
+ // This disabling attempt might come from the user-supplied actions, we'll route it in order for
+ // the watchpoint object to intelligently process this action.
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+
+ if (wp->IsHardware())
+ {
+ GDBStoppointType type = GetGDBStoppointType(wp);
+ // Pass down an appropriate z/Z packet...
+ if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, wp->GetByteSize()) == 0)
+ {
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+ else
+ error.SetErrorString("sending gdb watchpoint packet failed");
+ }
+ // TODO: clear software watchpoints if we implement them
+ }
+ else
+ {
+ error.SetErrorString("Watchpoint argument was NULL.");
+ }
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+void
+ProcessGDBRemote::Clear()
+{
+ m_flags = 0;
+ m_thread_list_real.Clear();
+ m_thread_list.Clear();
+}
+
+Error
+ProcessGDBRemote::DoSignal (int signo)
+{
+ Error error;
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoSignal (signal = %d)", signo);
+
+ if (!m_gdb_comm.SendAsyncSignal (signo))
+ error.SetErrorStringWithFormat("failed to send signal %i", signo);
+ return error;
+}
+
+Error
+ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url)
+{
+ ProcessLaunchInfo launch_info;
+ return StartDebugserverProcess(debugserver_url, launch_info);
+}
+
+Error
+ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url, const ProcessInfo &process_info) // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...")
+{
+ Error error;
+ if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID)
+ {
+ // If we locate debugserver, keep that located version around
+ static FileSpec g_debugserver_file_spec;
+
+ ProcessLaunchInfo debugserver_launch_info;
+ char debugserver_path[PATH_MAX];
+ FileSpec &debugserver_file_spec = debugserver_launch_info.GetExecutableFile();
+
+ // Always check to see if we have an environment override for the path
+ // to the debugserver to use and use it if we do.
+ const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH");
+ if (env_debugserver_path)
+ debugserver_file_spec.SetFile (env_debugserver_path, false);
+ else
+ debugserver_file_spec = g_debugserver_file_spec;
+ bool debugserver_exists = debugserver_file_spec.Exists();
+ if (!debugserver_exists)
+ {
+ // The debugserver binary is in the LLDB.framework/Resources
+ // directory.
+ if (Host::GetLLDBPath (ePathTypeSupportExecutableDir, debugserver_file_spec))
+ {
+ debugserver_file_spec.GetFilename().SetCString(DEBUGSERVER_BASENAME);
+ debugserver_exists = debugserver_file_spec.Exists();
+ if (debugserver_exists)
+ {
+ g_debugserver_file_spec = debugserver_file_spec;
+ }
+ else
+ {
+ g_debugserver_file_spec.Clear();
+ debugserver_file_spec.Clear();
+ }
+ }
+ }
+
+ if (debugserver_exists)
+ {
+ debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path));
+
+ m_stdio_communication.Clear();
+
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
+
+ Args &debugserver_args = debugserver_launch_info.GetArguments();
+ char arg_cstr[PATH_MAX];
+
+ // Start args with "debugserver /file/path -r --"
+ debugserver_args.AppendArgument(debugserver_path);
+ debugserver_args.AppendArgument(debugserver_url);
+ // use native registers, not the GDB registers
+ debugserver_args.AppendArgument("--native-regs");
+ // make debugserver run in its own session so signals generated by
+ // special terminal key sequences (^C) don't affect debugserver
+ debugserver_args.AppendArgument("--setsid");
+
+ const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE");
+ if (env_debugserver_log_file)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file);
+ debugserver_args.AppendArgument(arg_cstr);
+ }
+
+ const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
+ if (env_debugserver_log_flags)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags);
+ debugserver_args.AppendArgument(arg_cstr);
+ }
+// debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt");
+// debugserver_args.AppendArgument("--log-flags=0x802e0e");
+
+ // We currently send down all arguments, attach pids, or attach
+ // process names in dedicated GDB server packets, so we don't need
+ // to pass them as arguments. This is currently because of all the
+ // things we need to setup prior to launching: the environment,
+ // current working dir, file actions, etc.
+#if 0
+ // Now append the program arguments
+ if (inferior_argv)
+ {
+ // Terminate the debugserver args so we can now append the inferior args
+ debugserver_args.AppendArgument("--");
+
+ for (int i = 0; inferior_argv[i] != NULL; ++i)
+ debugserver_args.AppendArgument (inferior_argv[i]);
+ }
+ else if (attach_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid);
+ debugserver_args.AppendArgument (arg_cstr);
+ }
+ else if (attach_name && attach_name[0])
+ {
+ if (wait_for_launch)
+ debugserver_args.AppendArgument ("--waitfor");
+ else
+ debugserver_args.AppendArgument ("--attach");
+ debugserver_args.AppendArgument (attach_name);
+ }
+#endif
+
+ ProcessLaunchInfo::FileAction file_action;
+
+ // Close STDIN, STDOUT and STDERR. We might need to redirect them
+ // to "/dev/null" if we run into any problems.
+ file_action.Close (STDIN_FILENO);
+ debugserver_launch_info.AppendFileAction (file_action);
+ file_action.Close (STDOUT_FILENO);
+ debugserver_launch_info.AppendFileAction (file_action);
+ file_action.Close (STDERR_FILENO);
+ debugserver_launch_info.AppendFileAction (file_action);
+
+ if (log)
+ {
+ StreamString strm;
+ debugserver_args.Dump (&strm);
+ log->Printf("%s arguments:\n%s", debugserver_args.GetArgumentAtIndex(0), strm.GetData());
+ }
+
+ debugserver_launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false);
+ debugserver_launch_info.SetUserID(process_info.GetUserID());
+
+ error = Host::LaunchProcess(debugserver_launch_info);
+
+ if (error.Success ())
+ m_debugserver_pid = debugserver_launch_info.GetProcessID();
+ else
+ m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+
+ if (error.Fail() || log)
+ error.PutToLog(log, "Host::LaunchProcess (launch_info) => pid=%" PRIu64 ", path='%s'", m_debugserver_pid, debugserver_path);
+ }
+ else
+ {
+ error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME);
+ }
+
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ StartAsyncThread ();
+ }
+ return error;
+}
+
+bool
+ProcessGDBRemote::MonitorDebugserverProcess
+(
+ void *callback_baton,
+ lldb::pid_t debugserver_pid,
+ bool exited, // True if the process did exit
+ int signo, // Zero for no signal
+ int exit_status // Exit value of process if signal is zero
+)
+{
+ // The baton is a "ProcessGDBRemote *". Now this class might be gone
+ // and might not exist anymore, so we need to carefully try to get the
+ // target for this process first since we have a race condition when
+ // we are done running between getting the notice that the inferior
+ // process has died and the debugserver that was debugging this process.
+ // In our test suite, we are also continually running process after
+ // process, so we must be very careful to make sure:
+ // 1 - process object hasn't been deleted already
+ // 2 - that a new process object hasn't been recreated in its place
+
+ // "debugserver_pid" argument passed in is the process ID for
+ // debugserver that we are tracking...
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ ProcessGDBRemote *process = (ProcessGDBRemote *)callback_baton;
+
+ // Get a shared pointer to the target that has a matching process pointer.
+ // This target could be gone, or the target could already have a new process
+ // object inside of it
+ TargetSP target_sp (Debugger::FindTargetWithProcess(process));
+
+ if (log)
+ log->Printf ("ProcessGDBRemote::MonitorDebugserverProcess (baton=%p, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", callback_baton, debugserver_pid, signo, signo, exit_status);
+
+ if (target_sp)
+ {
+ // We found a process in a target that matches, but another thread
+ // might be in the process of launching a new process that will
+ // soon replace it, so get a shared pointer to the process so we
+ // can keep it alive.
+ ProcessSP process_sp (target_sp->GetProcessSP());
+ // Now we have a shared pointer to the process that can't go away on us
+ // so we now make sure it was the same as the one passed in, and also make
+ // sure that our previous "process *" didn't get deleted and have a new
+ // "process *" created in its place with the same pointer. To verify this
+ // we make sure the process has our debugserver process ID. If we pass all
+ // of these tests, then we are sure that this process is the one we were
+ // looking for.
+ if (process_sp && process == process_sp.get() && process->m_debugserver_pid == debugserver_pid)
+ {
+ // Sleep for a half a second to make sure our inferior process has
+ // time to set its exit status before we set it incorrectly when
+ // both the debugserver and the inferior process shut down.
+ usleep (500000);
+ // If our process hasn't yet exited, debugserver might have died.
+ // If the process did exit, the we are reaping it.
+ const StateType state = process->GetState();
+
+ if (process->m_debugserver_pid != LLDB_INVALID_PROCESS_ID &&
+ state != eStateInvalid &&
+ state != eStateUnloaded &&
+ state != eStateExited &&
+ state != eStateDetached)
+ {
+ char error_str[1024];
+ if (signo)
+ {
+ const char *signal_cstr = process->GetUnixSignals().GetSignalAsCString (signo);
+ if (signal_cstr)
+ ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr);
+ else
+ ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo);
+ }
+ else
+ {
+ ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status);
+ }
+
+ process->SetExitStatus (-1, error_str);
+ }
+ // Debugserver has exited we need to let our ProcessGDBRemote
+ // know that it no longer has a debugserver instance
+ process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ }
+ }
+ return true;
+}
+
+void
+ProcessGDBRemote::KillDebugserverProcess ()
+{
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ ::kill (m_debugserver_pid, SIGINT);
+ m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ }
+}
+
+void
+ProcessGDBRemote::Initialize()
+{
+ static bool g_initialized = false;
+
+ if (g_initialized == false)
+ {
+ g_initialized = true;
+ PluginManager::RegisterPlugin (GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ CreateInstance,
+ DebuggerInitialize);
+
+ Log::Callbacks log_callbacks = {
+ ProcessGDBRemoteLog::DisableLog,
+ ProcessGDBRemoteLog::EnableLog,
+ ProcessGDBRemoteLog::ListLogCategories
+ };
+
+ Log::RegisterLogChannel (ProcessGDBRemote::GetPluginNameStatic(), log_callbacks);
+ }
+}
+
+void
+ProcessGDBRemote::DebuggerInitialize (lldb_private::Debugger &debugger)
+{
+ if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName()))
+ {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForProcessPlugin (debugger,
+ GetGlobalPluginProperties()->GetValueProperties(),
+ ConstString ("Properties for the gdb-remote process plug-in."),
+ is_global_setting);
+ }
+}
+
+bool
+ProcessGDBRemote::StartAsyncThread ()
+{
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__);
+
+ Mutex::Locker start_locker(m_async_thread_state_mutex);
+ if (m_async_thread_state == eAsyncThreadNotStarted)
+ {
+ // Create a thread that watches our internal state and controls which
+ // events make it to clients (into the DCProcess event queue).
+ m_async_thread = Host::ThreadCreate ("<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this, NULL);
+ if (IS_VALID_LLDB_HOST_THREAD(m_async_thread))
+ {
+ m_async_thread_state = eAsyncThreadRunning;
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ {
+ // Somebody tried to start the async thread while it was either being started or stopped. If the former, and
+ // it started up successfully, then say all's well. Otherwise it is an error, since we aren't going to restart it.
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s () - Called when Async thread was in state: %d.", __FUNCTION__, m_async_thread_state);
+ if (m_async_thread_state == eAsyncThreadRunning)
+ return true;
+ else
+ return false;
+ }
+}
+
+void
+ProcessGDBRemote::StopAsyncThread ()
+{
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__);
+
+ Mutex::Locker start_locker(m_async_thread_state_mutex);
+ if (m_async_thread_state == eAsyncThreadRunning)
+ {
+ m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit);
+
+ // This will shut down the async thread.
+ m_gdb_comm.Disconnect(); // Disconnect from the debug server.
+
+ // Stop the stdio thread
+ if (IS_VALID_LLDB_HOST_THREAD(m_async_thread))
+ {
+ Host::ThreadJoin (m_async_thread, NULL, NULL);
+ }
+ m_async_thread_state = eAsyncThreadDone;
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s () - Called when Async thread was in state: %d.", __FUNCTION__, m_async_thread_state);
+ }
+}
+
+
+void *
+ProcessGDBRemote::AsyncThread (void *arg)
+{
+ ProcessGDBRemote *process = (ProcessGDBRemote*) arg;
+
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, arg, process->GetID());
+
+ Listener listener ("ProcessGDBRemote::AsyncThread");
+ EventSP event_sp;
+ const uint32_t desired_event_mask = eBroadcastBitAsyncContinue |
+ eBroadcastBitAsyncThreadShouldExit;
+
+ if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask)
+ {
+ listener.StartListeningForEvents (&process->m_gdb_comm, Communication::eBroadcastBitReadThreadDidExit);
+
+ bool done = false;
+ while (!done)
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID());
+ if (listener.WaitForEvent (NULL, event_sp))
+ {
+ const uint32_t event_type = event_sp->GetType();
+ if (event_sp->BroadcasterIs (&process->m_async_broadcaster))
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type);
+
+ switch (event_type)
+ {
+ case eBroadcastBitAsyncContinue:
+ {
+ const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get());
+
+ if (continue_packet)
+ {
+ const char *continue_cstr = (const char *)continue_packet->GetBytes ();
+ const size_t continue_cstr_len = continue_packet->GetByteSize ();
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr);
+
+ if (::strstr (continue_cstr, "vAttach") == NULL)
+ process->SetPrivateState(eStateRunning);
+ StringExtractorGDBRemote response;
+ StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response);
+
+ // We need to immediately clear the thread ID list so we are sure to get a valid list of threads.
+ // The thread ID list might be contained within the "response", or the stop reply packet that
+ // caused the stop. So clear it now before we give the stop reply packet to the process
+ // using the process->SetLastStopPacket()...
+ process->ClearThreadIDList ();
+
+ switch (stop_state)
+ {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ process->SetLastStopPacket (response);
+ process->SetPrivateState (stop_state);
+ break;
+
+ case eStateExited:
+ process->SetLastStopPacket (response);
+ process->ClearThreadIDList();
+ response.SetFilePos(1);
+ process->SetExitStatus(response.GetHexU8(), NULL);
+ done = true;
+ break;
+
+ case eStateInvalid:
+ process->SetExitStatus(-1, "lost connection");
+ break;
+
+ default:
+ process->SetPrivateState (stop_state);
+ break;
+ }
+ }
+ }
+ break;
+
+ case eBroadcastBitAsyncThreadShouldExit:
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID());
+ done = true;
+ break;
+
+ default:
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type);
+ done = true;
+ break;
+ }
+ }
+ else if (event_sp->BroadcasterIs (&process->m_gdb_comm))
+ {
+ if (event_type & Communication::eBroadcastBitReadThreadDidExit)
+ {
+ process->SetExitStatus (-1, "lost connection");
+ done = true;
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID());
+ done = true;
+ }
+ }
+ }
+
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID());
+
+ process->m_async_thread = LLDB_INVALID_HOST_THREAD;
+ return NULL;
+}
+
+const char *
+ProcessGDBRemote::GetDispatchQueueNameForThread
+(
+ addr_t thread_dispatch_qaddr,
+ std::string &dispatch_queue_name
+)
+{
+ dispatch_queue_name.clear();
+ if (thread_dispatch_qaddr != 0 && thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
+ {
+ // Cache the dispatch_queue_offsets_addr value so we don't always have
+ // to look it up
+ if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS)
+ {
+ static ConstString g_dispatch_queue_offsets_symbol_name ("dispatch_queue_offsets");
+ const Symbol *dispatch_queue_offsets_symbol = NULL;
+ ModuleSpec libSystem_module_spec (FileSpec("libSystem.B.dylib", false));
+ ModuleSP module_sp(GetTarget().GetImages().FindFirstModule (libSystem_module_spec));
+ if (module_sp)
+ dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData);
+
+ if (dispatch_queue_offsets_symbol == NULL)
+ {
+ ModuleSpec libdispatch_module_spec (FileSpec("libdispatch.dylib", false));
+ module_sp = GetTarget().GetImages().FindFirstModule (libdispatch_module_spec);
+ if (module_sp)
+ dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData);
+ }
+ if (dispatch_queue_offsets_symbol)
+ m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetAddress().GetLoadAddress(&m_target);
+
+ if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS)
+ return NULL;
+ }
+
+ uint8_t memory_buffer[8];
+ DataExtractor data (memory_buffer,
+ sizeof(memory_buffer),
+ m_target.GetArchitecture().GetByteOrder(),
+ m_target.GetArchitecture().GetAddressByteSize());
+
+ // Excerpt from src/queue_private.h
+ struct dispatch_queue_offsets_s
+ {
+ uint16_t dqo_version;
+ uint16_t dqo_label; // in version 1-3, offset to string; in version 4+, offset to a pointer to a string
+ uint16_t dqo_label_size; // in version 1-3, length of string; in version 4+, size of a (void*) in this process
+ } dispatch_queue_offsets;
+
+
+ Error error;
+ if (ReadMemory (m_dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets))
+ {
+ lldb::offset_t data_offset = 0;
+ if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t)))
+ {
+ if (ReadMemory (thread_dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize())
+ {
+ data_offset = 0;
+ lldb::addr_t queue_addr = data.GetAddress(&data_offset);
+ if (dispatch_queue_offsets.dqo_version >= 4)
+ {
+ // libdispatch versions 4+, pointer to dispatch name is in the
+ // queue structure.
+ lldb::addr_t pointer_to_label_address = queue_addr + dispatch_queue_offsets.dqo_label;
+ if (ReadMemory (pointer_to_label_address, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize())
+ {
+ data_offset = 0;
+ lldb::addr_t label_addr = data.GetAddress(&data_offset);
+ ReadCStringFromMemory (label_addr, dispatch_queue_name, error);
+ }
+ }
+ else
+ {
+ // libdispatch versions 1-3, dispatch name is a fixed width char array
+ // in the queue structure.
+ lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label;
+ dispatch_queue_name.resize(dispatch_queue_offsets.dqo_label_size, '\0');
+ size_t bytes_read = ReadMemory (label_addr, &dispatch_queue_name[0], dispatch_queue_offsets.dqo_label_size, error);
+ if (bytes_read < dispatch_queue_offsets.dqo_label_size)
+ dispatch_queue_name.erase (bytes_read);
+ }
+ }
+ }
+ }
+ }
+ if (dispatch_queue_name.empty())
+ return NULL;
+ return dispatch_queue_name.c_str();
+}
+
+//uint32_t
+//ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList &matches, std::vector<lldb::pid_t> &pids)
+//{
+// // If we are planning to launch the debugserver remotely, then we need to fire up a debugserver
+// // process and ask it for the list of processes. But if we are local, we can let the Host do it.
+// if (m_local_debugserver)
+// {
+// return Host::ListProcessesMatchingName (name, matches, pids);
+// }
+// else
+// {
+// // FIXME: Implement talking to the remote debugserver.
+// return 0;
+// }
+//
+//}
+//
+bool
+ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton,
+ lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id)
+{
+ // I don't think I have to do anything here, just make sure I notice the new thread when it starts to
+ // run so I can stop it if that's what I want to do.
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Hit New Thread Notification breakpoint.");
+ return false;
+}
+
+
+bool
+ProcessGDBRemote::StartNoticingNewThreads()
+{
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (m_thread_create_bp_sp)
+ {
+ if (log && log->GetVerbose())
+ log->Printf("Enabled noticing new thread breakpoint.");
+ m_thread_create_bp_sp->SetEnabled(true);
+ }
+ else
+ {
+ PlatformSP platform_sp (m_target.GetPlatform());
+ if (platform_sp)
+ {
+ m_thread_create_bp_sp = platform_sp->SetThreadCreationBreakpoint(m_target);
+ if (m_thread_create_bp_sp)
+ {
+ if (log && log->GetVerbose())
+ log->Printf("Successfully created new thread notification breakpoint %i", m_thread_create_bp_sp->GetID());
+ m_thread_create_bp_sp->SetCallback (ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true);
+ }
+ else
+ {
+ if (log)
+ log->Printf("Failed to create new thread notification breakpoint.");
+ }
+ }
+ }
+ return m_thread_create_bp_sp.get() != NULL;
+}
+
+bool
+ProcessGDBRemote::StopNoticingNewThreads()
+{
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf ("Disabling new thread notification breakpoint.");
+
+ if (m_thread_create_bp_sp)
+ m_thread_create_bp_sp->SetEnabled(false);
+
+ return true;
+}
+
+lldb_private::DynamicLoader *
+ProcessGDBRemote::GetDynamicLoader ()
+{
+ if (m_dyld_ap.get() == NULL)
+ m_dyld_ap.reset (DynamicLoader::FindPlugin(this, NULL));
+ return m_dyld_ap.get();
+}
+
+
+class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed
+{
+private:
+
+public:
+ CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) :
+ CommandObjectParsed (interpreter,
+ "process plugin packet history",
+ "Dumps the packet history buffer. ",
+ NULL)
+ {
+ }
+
+ ~CommandObjectProcessGDBRemotePacketHistory ()
+ {
+ }
+
+ bool
+ DoExecute (Args& command, CommandReturnObject &result)
+ {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0)
+ {
+ ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process)
+ {
+ process->GetGDBRemote().DumpHistory(result.GetOutputStream());
+ result.SetStatus (eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ else
+ {
+ result.AppendErrorWithFormat ("'%s' takes no arguments", m_cmd_name.c_str());
+ }
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed
+{
+private:
+
+public:
+ CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) :
+ CommandObjectParsed (interpreter,
+ "process plugin packet send",
+ "Send a custom packet through the GDB remote protocol and print the answer. "
+ "The packet header and footer will automatically be added to the packet prior to sending and stripped from the result.",
+ NULL)
+ {
+ }
+
+ ~CommandObjectProcessGDBRemotePacketSend ()
+ {
+ }
+
+ bool
+ DoExecute (Args& command, CommandReturnObject &result)
+ {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0)
+ {
+ result.AppendErrorWithFormat ("'%s' takes a one or more packet content arguments", m_cmd_name.c_str());
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process)
+ {
+ for (size_t i=0; i<argc; ++ i)
+ {
+ const char *packet_cstr = command.GetArgumentAtIndex(0);
+ bool send_async = true;
+ StringExtractorGDBRemote response;
+ process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async);
+ result.SetStatus (eReturnStatusSuccessFinishResult);
+ Stream &output_strm = result.GetOutputStream();
+ output_strm.Printf (" packet: %s\n", packet_cstr);
+ std::string &response_str = response.GetStringRef();
+
+ if (strstr(packet_cstr, "qGetProfileData") != NULL)
+ {
+ response_str = process->GetGDBRemote().HarmonizeThreadIdsForProfileData(process, response);
+ }
+
+ if (response_str.empty())
+ output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n");
+ else
+ output_strm.Printf ("response: %s\n", response.GetStringRef().c_str());
+ }
+ }
+ return true;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw
+{
+private:
+
+public:
+ CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) :
+ CommandObjectRaw (interpreter,
+ "process plugin packet monitor",
+ "Send a qRcmd packet through the GDB remote protocol and print the response."
+ "The argument passed to this command will be hex encoded into a valid 'qRcmd' packet, sent and the response will be printed.",
+ NULL)
+ {
+ }
+
+ ~CommandObjectProcessGDBRemotePacketMonitor ()
+ {
+ }
+
+ bool
+ DoExecute (const char *command, CommandReturnObject &result)
+ {
+ if (command == NULL || command[0] == '\0')
+ {
+ result.AppendErrorWithFormat ("'%s' takes a command string argument", m_cmd_name.c_str());
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process)
+ {
+ StreamString packet;
+ packet.PutCString("qRcmd,");
+ packet.PutBytesAsRawHex8(command, strlen(command));
+ const char *packet_cstr = packet.GetString().c_str();
+
+ bool send_async = true;
+ StringExtractorGDBRemote response;
+ process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async);
+ result.SetStatus (eReturnStatusSuccessFinishResult);
+ Stream &output_strm = result.GetOutputStream();
+ output_strm.Printf (" packet: %s\n", packet_cstr);
+ const std::string &response_str = response.GetStringRef();
+
+ if (response_str.empty())
+ output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n");
+ else
+ output_strm.Printf ("response: %s\n", response.GetStringRef().c_str());
+ }
+ return true;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword
+{
+private:
+
+public:
+ CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) :
+ CommandObjectMultiword (interpreter,
+ "process plugin packet",
+ "Commands that deal with GDB remote packets.",
+ NULL)
+ {
+ LoadSubCommand ("history", CommandObjectSP (new CommandObjectProcessGDBRemotePacketHistory (interpreter)));
+ LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessGDBRemotePacketSend (interpreter)));
+ LoadSubCommand ("monitor", CommandObjectSP (new CommandObjectProcessGDBRemotePacketMonitor (interpreter)));
+ }
+
+ ~CommandObjectProcessGDBRemotePacket ()
+ {
+ }
+};
+
+class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword
+{
+public:
+ CommandObjectMultiwordProcessGDBRemote (CommandInterpreter &interpreter) :
+ CommandObjectMultiword (interpreter,
+ "process plugin",
+ "A set of commands for operating on a ProcessGDBRemote process.",
+ "process plugin <subcommand> [<subcommand-options>]")
+ {
+ LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessGDBRemotePacket (interpreter)));
+ }
+
+ ~CommandObjectMultiwordProcessGDBRemote ()
+ {
+ }
+};
+
+CommandObject *
+ProcessGDBRemote::GetPluginCommandObject()
+{
+ if (!m_command_sp)
+ m_command_sp.reset (new CommandObjectMultiwordProcessGDBRemote (GetTarget().GetDebugger().GetCommandInterpreter()));
+ return m_command_sp.get();
+}
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
new file mode 100644
index 0000000..e104b71
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -0,0 +1,396 @@
+//===-- ProcessGDBRemote.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessGDBRemote_h_
+#define liblldb_ProcessGDBRemote_h_
+
+// C Includes
+
+// C++ Includes
+#include <list>
+#include <vector>
+
+// Other libraries and framework includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/Broadcaster.h"
+#include "lldb/Core/ConstString.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/InputReader.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/StringList.h"
+#include "lldb/Core/ThreadSafeValue.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+#include "GDBRemoteCommunicationClient.h"
+#include "Utility/StringExtractor.h"
+#include "GDBRemoteRegisterContext.h"
+
+class ThreadGDBRemote;
+
+class ProcessGDBRemote : public lldb_private::Process
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ static lldb::ProcessSP
+ CreateInstance (lldb_private::Target& target,
+ lldb_private::Listener &listener,
+ const lldb_private::FileSpec *crash_file_path);
+
+ static void
+ Initialize();
+
+ static void
+ DebuggerInitialize (lldb_private::Debugger &debugger);
+
+ static void
+ Terminate();
+
+ static lldb_private::ConstString
+ GetPluginNameStatic();
+
+ static const char *
+ GetPluginDescriptionStatic();
+
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ ProcessGDBRemote(lldb_private::Target& target, lldb_private::Listener &listener);
+
+ virtual
+ ~ProcessGDBRemote();
+
+ //------------------------------------------------------------------
+ // Check if a given Process
+ //------------------------------------------------------------------
+ virtual bool
+ CanDebug (lldb_private::Target &target,
+ bool plugin_specified_by_name);
+
+ virtual lldb_private::CommandObject *
+ GetPluginCommandObject();
+
+ //------------------------------------------------------------------
+ // Creating a new process, or attaching to an existing one
+ //------------------------------------------------------------------
+ virtual lldb_private::Error
+ WillLaunch (lldb_private::Module* module);
+
+ virtual lldb_private::Error
+ DoLaunch (lldb_private::Module *exe_module,
+ const lldb_private::ProcessLaunchInfo &launch_info);
+
+ virtual void
+ DidLaunch ();
+
+ virtual lldb_private::Error
+ WillAttachToProcessWithID (lldb::pid_t pid);
+
+ virtual lldb_private::Error
+ WillAttachToProcessWithName (const char *process_name, bool wait_for_launch);
+
+ virtual lldb_private::Error
+ DoConnectRemote (lldb_private::Stream *strm, const char *remote_url);
+
+ lldb_private::Error
+ WillLaunchOrAttach ();
+
+ virtual lldb_private::Error
+ DoAttachToProcessWithID (lldb::pid_t pid);
+
+ virtual lldb_private::Error
+ DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info);
+
+ virtual lldb_private::Error
+ DoAttachToProcessWithName (const char *process_name,
+ bool wait_for_launch,
+ const lldb_private::ProcessAttachInfo &attach_info);
+
+ virtual void
+ DidAttach ();
+
+ //------------------------------------------------------------------
+ // PluginInterface protocol
+ //------------------------------------------------------------------
+ virtual lldb_private::ConstString
+ GetPluginName();
+
+ virtual uint32_t
+ GetPluginVersion();
+
+ //------------------------------------------------------------------
+ // Process Control
+ //------------------------------------------------------------------
+ virtual lldb_private::Error
+ WillResume ();
+
+ virtual lldb_private::Error
+ DoResume ();
+
+ virtual lldb_private::Error
+ DoHalt (bool &caused_stop);
+
+ virtual lldb_private::Error
+ DoDetach (bool keep_stopped);
+
+ virtual bool
+ DetachRequiresHalt() { return true; }
+
+ virtual lldb_private::Error
+ DoSignal (int signal);
+
+ virtual lldb_private::Error
+ DoDestroy ();
+
+ virtual void
+ RefreshStateAfterStop();
+
+ //------------------------------------------------------------------
+ // Process Queries
+ //------------------------------------------------------------------
+ virtual bool
+ IsAlive ();
+
+ virtual lldb::addr_t
+ GetImageInfoAddress();
+
+ //------------------------------------------------------------------
+ // Process Memory
+ //------------------------------------------------------------------
+ virtual size_t
+ DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error);
+
+ virtual size_t
+ DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error);
+
+ virtual lldb::addr_t
+ DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error);
+
+ virtual lldb_private::Error
+ GetMemoryRegionInfo (lldb::addr_t load_addr,
+ lldb_private::MemoryRegionInfo &region_info);
+
+ virtual lldb_private::Error
+ DoDeallocateMemory (lldb::addr_t ptr);
+
+ //------------------------------------------------------------------
+ // Process STDIO
+ //------------------------------------------------------------------
+ virtual size_t
+ PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error);
+
+ //----------------------------------------------------------------------
+ // Process Breakpoints
+ //----------------------------------------------------------------------
+ virtual lldb_private::Error
+ EnableBreakpointSite (lldb_private::BreakpointSite *bp_site);
+
+ virtual lldb_private::Error
+ DisableBreakpointSite (lldb_private::BreakpointSite *bp_site);
+
+ //----------------------------------------------------------------------
+ // Process Watchpoints
+ //----------------------------------------------------------------------
+ virtual lldb_private::Error
+ EnableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true);
+
+ virtual lldb_private::Error
+ DisableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true);
+
+ virtual lldb_private::Error
+ GetWatchpointSupportInfo (uint32_t &num);
+
+ virtual lldb_private::Error
+ GetWatchpointSupportInfo (uint32_t &num, bool& after);
+
+ virtual bool
+ StartNoticingNewThreads();
+
+ virtual bool
+ StopNoticingNewThreads();
+
+ GDBRemoteCommunicationClient &
+ GetGDBRemote()
+ {
+ return m_gdb_comm;
+ }
+
+protected:
+ friend class ThreadGDBRemote;
+ friend class GDBRemoteCommunicationClient;
+ friend class GDBRemoteRegisterContext;
+
+ //----------------------------------------------------------------------
+ // Accessors
+ //----------------------------------------------------------------------
+ bool
+ IsRunning ( lldb::StateType state )
+ {
+ return state == lldb::eStateRunning || IsStepping(state);
+ }
+
+ bool
+ IsStepping ( lldb::StateType state)
+ {
+ return state == lldb::eStateStepping;
+ }
+ bool
+ CanResume ( lldb::StateType state)
+ {
+ return state == lldb::eStateStopped;
+ }
+
+ bool
+ HasExited (lldb::StateType state)
+ {
+ return state == lldb::eStateExited;
+ }
+
+ bool
+ ProcessIDIsValid ( ) const;
+
+ void
+ Clear ( );
+
+ lldb_private::Flags &
+ GetFlags ()
+ {
+ return m_flags;
+ }
+
+ const lldb_private::Flags &
+ GetFlags () const
+ {
+ return m_flags;
+ }
+
+ virtual bool
+ UpdateThreadList (lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &new_thread_list);
+
+ lldb_private::Error
+ StartDebugserverProcess (const char *debugserver_url);
+
+ lldb_private::Error
+ StartDebugserverProcess (const char *debugserver_url, const lldb_private::ProcessInfo &process_info);
+
+ void
+ KillDebugserverProcess ();
+
+ void
+ BuildDynamicRegisterInfo (bool force);
+
+ void
+ SetLastStopPacket (const StringExtractorGDBRemote &response);
+
+ //------------------------------------------------------------------
+ /// Broadcaster event bits definitions.
+ //------------------------------------------------------------------
+ enum
+ {
+ eBroadcastBitAsyncContinue = (1 << 0),
+ eBroadcastBitAsyncThreadShouldExit = (1 << 1),
+ eBroadcastBitAsyncThreadDidExit = (1 << 2)
+ };
+
+ typedef enum AsyncThreadState
+ {
+ eAsyncThreadNotStarted,
+ eAsyncThreadRunning,
+ eAsyncThreadDone
+ } AsyncThreadState;
+
+ lldb_private::Flags m_flags; // Process specific flags (see eFlags enums)
+ GDBRemoteCommunicationClient m_gdb_comm;
+ lldb::pid_t m_debugserver_pid;
+ StringExtractorGDBRemote m_last_stop_packet;
+ lldb_private::Mutex m_last_stop_packet_mutex;
+ GDBRemoteDynamicRegisterInfo m_register_info;
+ lldb_private::Broadcaster m_async_broadcaster;
+ lldb::thread_t m_async_thread;
+ AsyncThreadState m_async_thread_state;
+ lldb_private::Mutex m_async_thread_state_mutex;
+ typedef std::vector<lldb::tid_t> tid_collection;
+ typedef std::vector< std::pair<lldb::tid_t,int> > tid_sig_collection;
+ typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
+ tid_collection m_thread_ids; // Thread IDs for all threads. This list gets updated after stopping
+ tid_collection m_continue_c_tids; // 'c' for continue
+ tid_sig_collection m_continue_C_tids; // 'C' for continue with signal
+ tid_collection m_continue_s_tids; // 's' for step
+ tid_sig_collection m_continue_S_tids; // 'S' for step with signal
+ lldb::addr_t m_dispatch_queue_offsets_addr;
+ size_t m_max_memory_size; // The maximum number of bytes to read/write when reading and writing memory
+ MMapMap m_addr_to_mmap_size;
+ lldb::BreakpointSP m_thread_create_bp_sp;
+ bool m_waiting_for_attach;
+ bool m_destroy_tried_resuming;
+ lldb::CommandObjectSP m_command_sp;
+
+ bool
+ StartAsyncThread ();
+
+ void
+ StopAsyncThread ();
+
+ static void *
+ AsyncThread (void *arg);
+
+ static bool
+ MonitorDebugserverProcess (void *callback_baton,
+ lldb::pid_t pid,
+ bool exited,
+ int signo,
+ int exit_status);
+
+ lldb::StateType
+ SetThreadStopInfo (StringExtractor& stop_packet);
+
+ void
+ ClearThreadIDList ();
+
+ bool
+ UpdateThreadIDList ();
+
+ void
+ DidLaunchOrAttach ();
+
+ lldb_private::Error
+ ConnectToDebugserver (const char *host_port);
+
+ const char *
+ GetDispatchQueueNameForThread (lldb::addr_t thread_dispatch_qaddr,
+ std::string &dispatch_queue_name);
+
+ static size_t
+ AttachInputReaderCallback (void *baton,
+ lldb_private::InputReader *reader,
+ lldb::InputReaderAction notification,
+ const char *bytes,
+ size_t bytes_len);
+
+ lldb_private::DynamicLoader *
+ GetDynamicLoader ();
+
+private:
+ //------------------------------------------------------------------
+ // For ProcessGDBRemote only
+ //------------------------------------------------------------------
+ static bool
+ NewThreadNotifyBreakpointHit (void *baton,
+ lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote);
+
+};
+
+#endif // liblldb_ProcessGDBRemote_h_
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
new file mode 100644
index 0000000..15b861f
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
@@ -0,0 +1,196 @@
+//===-- ProcessGDBRemoteLog.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessGDBRemoteLog.h"
+
+#include "lldb/Interpreter/Args.h"
+#include "lldb/Core/StreamFile.h"
+
+#include "ProcessGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+// We want to avoid global constructors where code needs to be run so here we
+// control access to our static g_log_sp by hiding it in a singleton function
+// that will construct the static g_lob_sp the first time this function is
+// called.
+static bool g_log_enabled = false;
+static Log * g_log = NULL;
+static Log *
+GetLog ()
+{
+ if (!g_log_enabled)
+ return NULL;
+ return g_log;
+}
+
+
+Log *
+ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (uint32_t mask)
+{
+ Log *log(GetLog ());
+ if (log && mask)
+ {
+ uint32_t log_mask = log->GetMask().Get();
+ if ((log_mask & mask) != mask)
+ return NULL;
+ }
+ return log;
+}
+
+Log *
+ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (uint32_t mask)
+{
+ Log *log(GetLog ());
+ if (log && log->GetMask().Get() & mask)
+ return log;
+ return NULL;
+}
+
+void
+ProcessGDBRemoteLog::DisableLog (const char **categories, Stream *feedback_strm)
+{
+ Log *log (GetLog ());
+ if (log)
+ {
+ uint32_t flag_bits = 0;
+
+ if (categories[0] != NULL)
+ {
+ flag_bits = log->GetMask().Get();
+ for (size_t i = 0; categories[i] != NULL; ++i)
+ {
+ const char *arg = categories[i];
+
+
+ if (::strcasecmp (arg, "all") == 0 ) flag_bits &= ~GDBR_LOG_ALL;
+ else if (::strcasecmp (arg, "async") == 0 ) flag_bits &= ~GDBR_LOG_ASYNC;
+ else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits &= ~GDBR_LOG_BREAKPOINTS;
+ else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits &= ~GDBR_LOG_COMM;
+ else if (::strcasecmp (arg, "default") == 0 ) flag_bits &= ~GDBR_LOG_DEFAULT;
+ else if (::strcasecmp (arg, "packets") == 0 ) flag_bits &= ~GDBR_LOG_PACKETS;
+ else if (::strcasecmp (arg, "memory") == 0 ) flag_bits &= ~GDBR_LOG_MEMORY;
+ else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits &= ~GDBR_LOG_MEMORY_DATA_SHORT;
+ else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits &= ~GDBR_LOG_MEMORY_DATA_LONG;
+ else if (::strcasecmp (arg, "process") == 0 ) flag_bits &= ~GDBR_LOG_PROCESS;
+ else if (::strcasecmp (arg, "step") == 0 ) flag_bits &= ~GDBR_LOG_STEP;
+ else if (::strcasecmp (arg, "thread") == 0 ) flag_bits &= ~GDBR_LOG_THREAD;
+ else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits &= ~GDBR_LOG_VERBOSE;
+ else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits &= ~GDBR_LOG_WATCHPOINTS;
+ else
+ {
+ feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
+ ListLogCategories (feedback_strm);
+ }
+
+ }
+ }
+
+ if (flag_bits == 0)
+ g_log_enabled = false;
+ else
+ log->GetMask().Reset (flag_bits);
+ }
+
+ return;
+}
+
+Log *
+ProcessGDBRemoteLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const char **categories, Stream *feedback_strm)
+{
+ // Try see if there already is a log - that way we can reuse its settings.
+ // We could reuse the log in toto, but we don't know that the stream is the same.
+ uint32_t flag_bits = 0;
+ if (g_log)
+ flag_bits = g_log->GetMask().Get();
+
+ // Now make a new log with this stream if one was provided
+ if (log_stream_sp)
+ {
+ if (g_log)
+ g_log->SetStream(log_stream_sp);
+ else
+ g_log = new Log(log_stream_sp);
+ }
+
+ if (g_log)
+ {
+ bool got_unknown_category = false;
+ for (size_t i=0; categories[i] != NULL; ++i)
+ {
+ const char *arg = categories[i];
+
+ if (::strcasecmp (arg, "all") == 0 ) flag_bits |= GDBR_LOG_ALL;
+ else if (::strcasecmp (arg, "async") == 0 ) flag_bits |= GDBR_LOG_ASYNC;
+ else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits |= GDBR_LOG_BREAKPOINTS;
+ else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits |= GDBR_LOG_COMM;
+ else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= GDBR_LOG_DEFAULT;
+ else if (::strcasecmp (arg, "packets") == 0 ) flag_bits |= GDBR_LOG_PACKETS;
+ else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= GDBR_LOG_MEMORY;
+ else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= GDBR_LOG_MEMORY_DATA_SHORT;
+ else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= GDBR_LOG_MEMORY_DATA_LONG;
+ else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= GDBR_LOG_PROCESS;
+ else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= GDBR_LOG_STEP;
+ else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= GDBR_LOG_THREAD;
+ else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= GDBR_LOG_VERBOSE;
+ else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits |= GDBR_LOG_WATCHPOINTS;
+ else
+ {
+ feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
+ if (got_unknown_category == false)
+ {
+ got_unknown_category = true;
+ ListLogCategories (feedback_strm);
+ }
+ }
+ }
+ if (flag_bits == 0)
+ flag_bits = GDBR_LOG_DEFAULT;
+ g_log->GetMask().Reset(flag_bits);
+ g_log->GetOptions().Reset(log_options);
+ }
+ g_log_enabled = true;
+ return g_log;
+}
+
+void
+ProcessGDBRemoteLog::ListLogCategories (Stream *strm)
+{
+ strm->Printf ("Logging categories for '%s':\n"
+ " all - turn on all available logging categories\n"
+ " async - log asynchronous activity\n"
+ " break - log breakpoints\n"
+ " communication - log communication activity\n"
+ " default - enable the default set of logging categories for liblldb\n"
+ " packets - log gdb remote packets\n"
+ " memory - log memory reads and writes\n"
+ " data-short - log memory bytes for memory reads and writes for short transactions only\n"
+ " data-long - log memory bytes for memory reads and writes for all transactions\n"
+ " process - log process events and activities\n"
+ " thread - log thread events and activities\n"
+ " step - log step related activities\n"
+ " verbose - enable verbose logging\n"
+ " watch - log watchpoint related activities\n", ProcessGDBRemote::GetPluginNameStatic().GetCString());
+}
+
+
+void
+ProcessGDBRemoteLog::LogIf (uint32_t mask, const char *format, ...)
+{
+ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (mask));
+ if (log)
+ {
+ va_list args;
+ va_start (args, format);
+ log->VAPrintf (format, args);
+ va_end (args);
+ }
+}
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
new file mode 100644
index 0000000..9373406
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
@@ -0,0 +1,57 @@
+//===-- ProcessGDBRemoteLog.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessGDBRemoteLog_h_
+#define liblldb_ProcessGDBRemoteLog_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+
+// Project includes
+#include "lldb/Core/Log.h"
+
+#define GDBR_LOG_VERBOSE (1u << 0)
+#define GDBR_LOG_PROCESS (1u << 1)
+#define GDBR_LOG_THREAD (1u << 2)
+#define GDBR_LOG_PACKETS (1u << 3)
+#define GDBR_LOG_MEMORY (1u << 4) // Log memory reads/writes calls
+#define GDBR_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes
+#define GDBR_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes
+#define GDBR_LOG_BREAKPOINTS (1u << 7)
+#define GDBR_LOG_WATCHPOINTS (1u << 8)
+#define GDBR_LOG_STEP (1u << 9)
+#define GDBR_LOG_COMM (1u << 10)
+#define GDBR_LOG_ASYNC (1u << 11)
+#define GDBR_LOG_ALL (UINT32_MAX)
+#define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS
+
+class ProcessGDBRemoteLog
+{
+public:
+ static lldb_private::Log *
+ GetLogIfAllCategoriesSet(uint32_t mask = 0);
+
+ static lldb_private::Log *
+ GetLogIfAnyCategoryIsSet (uint32_t mask);
+
+ static void
+ DisableLog (const char **categories, lldb_private::Stream *feedback_strm);
+
+ static lldb_private::Log *
+ EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, lldb_private::Stream *feedback_strm);
+
+ static void
+ ListLogCategories (lldb_private::Stream *strm);
+
+ static void
+ LogIf (uint32_t mask, const char *format, ...);
+};
+
+#endif // liblldb_ProcessGDBRemoteLog_h_
diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
new file mode 100644
index 0000000..38fb84d
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
@@ -0,0 +1,214 @@
+//===-- ThreadGDBRemote.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "ThreadGDBRemote.h"
+
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/State.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Thread Registers
+//----------------------------------------------------------------------
+
+ThreadGDBRemote::ThreadGDBRemote (Process &process, lldb::tid_t tid) :
+ Thread(process, tid),
+ m_thread_name (),
+ m_dispatch_queue_name (),
+ m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS)
+{
+ ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)",
+ this,
+ process.GetID(),
+ GetID());
+}
+
+ThreadGDBRemote::~ThreadGDBRemote ()
+{
+ ProcessSP process_sp(GetProcess());
+ ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::~ThreadGDBRemote (pid = %i, tid = 0x%4.4x)",
+ this,
+ process_sp ? process_sp->GetID() : LLDB_INVALID_PROCESS_ID,
+ GetID());
+ DestroyThread();
+}
+
+const char *
+ThreadGDBRemote::GetName ()
+{
+ if (m_thread_name.empty())
+ return NULL;
+ return m_thread_name.c_str();
+}
+
+
+const char *
+ThreadGDBRemote::GetQueueName ()
+{
+ // Always re-fetch the dispatch queue name since it can change
+
+ if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
+ {
+ ProcessSP process_sp (GetProcess());
+ if (process_sp)
+ {
+ ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
+ return gdb_process->GetDispatchQueueNameForThread (m_thread_dispatch_qaddr, m_dispatch_queue_name);
+ }
+ }
+ return NULL;
+}
+
+void
+ThreadGDBRemote::WillResume (StateType resume_state)
+{
+ int signo = GetResumeSignal();
+ const lldb::user_id_t tid = GetProtocolID();
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD));
+ if (log)
+ log->Printf ("Resuming thread: %4.4" PRIx64 " with state: %s.", tid, StateAsCString(resume_state));
+
+ ProcessSP process_sp (GetProcess());
+ if (process_sp)
+ {
+ ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
+ switch (resume_state)
+ {
+ case eStateSuspended:
+ case eStateStopped:
+ // Don't append anything for threads that should stay stopped.
+ break;
+
+ case eStateRunning:
+ if (gdb_process->GetUnixSignals().SignalIsValid (signo))
+ gdb_process->m_continue_C_tids.push_back(std::make_pair(tid, signo));
+ else
+ gdb_process->m_continue_c_tids.push_back(tid);
+ break;
+
+ case eStateStepping:
+ if (gdb_process->GetUnixSignals().SignalIsValid (signo))
+ gdb_process->m_continue_S_tids.push_back(std::make_pair(tid, signo));
+ else
+ gdb_process->m_continue_s_tids.push_back(tid);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void
+ThreadGDBRemote::RefreshStateAfterStop()
+{
+ // Invalidate all registers in our register context. We don't set "force" to
+ // true because the stop reply packet might have had some register values
+ // that were expedited and these will already be copied into the register
+ // context by the time this function gets called. The GDBRemoteRegisterContext
+ // class has been made smart enough to detect when it needs to invalidate
+ // which registers are valid by putting hooks in the register read and
+ // register supply functions where they check the process stop ID and do
+ // the right thing.
+ const bool force = false;
+ GetRegisterContext()->InvalidateIfNeeded (force);
+}
+
+bool
+ThreadGDBRemote::ThreadIDIsValid (lldb::tid_t thread)
+{
+ return thread != 0;
+}
+
+void
+ThreadGDBRemote::Dump(Log *log, uint32_t index)
+{
+}
+
+
+bool
+ThreadGDBRemote::ShouldStop (bool &step_more)
+{
+ return true;
+}
+lldb::RegisterContextSP
+ThreadGDBRemote::GetRegisterContext ()
+{
+ if (m_reg_context_sp.get() == NULL)
+ m_reg_context_sp = CreateRegisterContextForFrame (NULL);
+ return m_reg_context_sp;
+}
+
+lldb::RegisterContextSP
+ThreadGDBRemote::CreateRegisterContextForFrame (StackFrame *frame)
+{
+ lldb::RegisterContextSP reg_ctx_sp;
+ const bool read_all_registers_at_once = false;
+ uint32_t concrete_frame_idx = 0;
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex ();
+
+
+ if (concrete_frame_idx == 0)
+ {
+ ProcessSP process_sp (GetProcess());
+ if (process_sp)
+ {
+ ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
+ reg_ctx_sp.reset (new GDBRemoteRegisterContext (*this, concrete_frame_idx, gdb_process->m_register_info, read_all_registers_at_once));
+ }
+ }
+ else
+ {
+ Unwind *unwinder = GetUnwinder ();
+ if (unwinder)
+ reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool
+ThreadGDBRemote::PrivateSetRegisterValue (uint32_t reg, StringExtractor &response)
+{
+ GDBRemoteRegisterContext *gdb_reg_ctx = static_cast<GDBRemoteRegisterContext *>(GetRegisterContext ().get());
+ assert (gdb_reg_ctx);
+ return gdb_reg_ctx->PrivateSetRegisterValue (reg, response);
+}
+
+bool
+ThreadGDBRemote::CalculateStopInfo ()
+{
+ ProcessSP process_sp (GetProcess());
+ if (process_sp)
+ {
+ StringExtractorGDBRemote stop_packet;
+ ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
+ if (gdb_process->GetGDBRemote().GetThreadStopInfo(GetProtocolID(), stop_packet))
+ return gdb_process->SetThreadStopInfo (stop_packet) == eStateStopped;
+ }
+ return false;
+}
+
+
diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h
new file mode 100644
index 0000000..50a3f19
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h
@@ -0,0 +1,107 @@
+//===-- ThreadGDBRemote.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadGDBRemote_h_
+#define liblldb_ThreadGDBRemote_h_
+
+#include <string>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+class StringExtractor;
+class ProcessGDBRemote;
+
+class ThreadGDBRemote : public lldb_private::Thread
+{
+public:
+ ThreadGDBRemote (lldb_private::Process &process, lldb::tid_t tid);
+
+ virtual
+ ~ThreadGDBRemote ();
+
+ virtual void
+ WillResume (lldb::StateType resume_state);
+
+ virtual void
+ RefreshStateAfterStop();
+
+ virtual const char *
+ GetName ();
+
+ virtual const char *
+ GetQueueName ();
+
+ virtual lldb::RegisterContextSP
+ GetRegisterContext ();
+
+ virtual lldb::RegisterContextSP
+ CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ void
+ Dump (lldb_private::Log *log, uint32_t index);
+
+ static bool
+ ThreadIDIsValid (lldb::tid_t thread);
+
+ bool
+ ShouldStop (bool &step_more);
+
+ const char *
+ GetBasicInfoAsString ();
+
+ void
+ SetName (const char *name)
+ {
+ if (name && name[0])
+ m_thread_name.assign (name);
+ else
+ m_thread_name.clear();
+ }
+
+ lldb::addr_t
+ GetThreadDispatchQAddr ()
+ {
+ return m_thread_dispatch_qaddr;
+ }
+
+ void
+ SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr)
+ {
+ m_thread_dispatch_qaddr = thread_dispatch_qaddr;
+ }
+
+protected:
+
+ friend class ProcessGDBRemote;
+
+ bool
+ PrivateSetRegisterValue (uint32_t reg,
+ StringExtractor &response);
+
+ //------------------------------------------------------------------
+ // Member variables.
+ //------------------------------------------------------------------
+ std::string m_thread_name;
+ std::string m_dispatch_queue_name;
+ lldb::addr_t m_thread_dispatch_qaddr;
+ //------------------------------------------------------------------
+ // Member variables.
+ //------------------------------------------------------------------
+
+ void
+ SetStopInfoFromPacket (StringExtractor &stop_packet, uint32_t stop_id);
+
+ virtual bool
+ CalculateStopInfo ();
+
+
+};
+
+#endif // liblldb_ThreadGDBRemote_h_
OpenPOWER on IntegriCloud