summaryrefslogtreecommitdiffstats
path: root/lldb/tools/debugserver/source/MacOSX
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/tools/debugserver/source/MacOSX')
-rw-r--r--lldb/tools/debugserver/source/MacOSX/CFBundle.cpp87
-rw-r--r--lldb/tools/debugserver/source/MacOSX/CFBundle.h37
-rw-r--r--lldb/tools/debugserver/source/MacOSX/CFData.cpp85
-rw-r--r--lldb/tools/debugserver/source/MacOSX/CFData.h39
-rw-r--r--lldb/tools/debugserver/source/MacOSX/CFString.cpp201
-rw-r--r--lldb/tools/debugserver/source/MacOSX/CFString.h43
-rw-r--r--lldb/tools/debugserver/source/MacOSX/CFUtils.h81
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp679
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachDYLD.h145
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachException.cpp533
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachException.h147
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachProcess.cpp2008
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachProcess.h263
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachTask.cpp660
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachTask.h91
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachThread.cpp745
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachThread.h124
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp432
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachThreadList.h71
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp186
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMMemory.h40
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp179
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMRegion.h67
-rw-r--r--lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp2610
-rw-r--r--lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h217
-rw-r--r--lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs16
-rw-r--r--lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp879
-rw-r--r--lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h196
-rw-r--r--lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp569
-rw-r--r--lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h180
-rw-r--r--lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp1035
-rw-r--r--lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h199
32 files changed, 12844 insertions, 0 deletions
diff --git a/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp b/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp
new file mode 100644
index 00000000000..a15755044c3
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp
@@ -0,0 +1,87 @@
+//===-- CFBundle.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+//----------------------------------------------------------------------
+// CFBundle constructor
+//----------------------------------------------------------------------
+CFBundle::CFBundle(const char *path) :
+ CFReleaser<CFBundleRef>(),
+ m_bundle_url()
+{
+ if (path && path[0])
+ SetPath(path);
+}
+
+//----------------------------------------------------------------------
+// CFBundle copy constructor
+//----------------------------------------------------------------------
+CFBundle::CFBundle(const CFBundle& rhs) :
+ CFReleaser<CFBundleRef>(rhs),
+ m_bundle_url(rhs.m_bundle_url)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFBundle copy constructor
+//----------------------------------------------------------------------
+CFBundle&
+CFBundle::operator=(const CFBundle& rhs)
+{
+ *this = rhs;
+ return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFBundle::~CFBundle()
+{
+}
+
+//----------------------------------------------------------------------
+// Set the path for a bundle by supplying a
+//----------------------------------------------------------------------
+bool
+CFBundle::SetPath (const char *path)
+{
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ // Release our old bundle and ULR
+ reset(); // This class is a CFReleaser<CFBundleRef>
+ m_bundle_url.reset();
+ // Make a CFStringRef from the supplied path
+ CFString cf_path;
+ cf_path.SetFileSystemRepresentation(path);
+ if (cf_path.get())
+ {
+ // Make our Bundle URL
+ m_bundle_url.reset (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true));
+ if (m_bundle_url.get())
+ {
+ reset (::CFBundleCreate (alloc, m_bundle_url.get()));
+ }
+ }
+ return get() != NULL;
+}
+
+CFStringRef
+CFBundle::GetIdentifier () const
+{
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return ::CFBundleGetIdentifier (bundle);
+ return NULL;
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/CFBundle.h b/lldb/tools/debugserver/source/MacOSX/CFBundle.h
new file mode 100644
index 00000000000..d980c0ba16f
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/CFBundle.h
@@ -0,0 +1,37 @@
+//===-- CFBundle.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFBundle_h__
+#define __CFBundle_h__
+
+#include "CFUtils.h"
+
+class CFBundle : public CFReleaser<CFBundleRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFBundle(const char *path = NULL);
+ CFBundle(const CFBundle& rhs);
+ CFBundle& operator=(const CFBundle& rhs);
+ virtual ~CFBundle();
+
+ bool SetPath (const char *path);
+ CFStringRef GetIdentifier () const;
+
+protected:
+ CFReleaser<CFURLRef> m_bundle_url;
+};
+
+#endif // #ifndef __CFBundle_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/CFData.cpp b/lldb/tools/debugserver/source/MacOSX/CFData.cpp
new file mode 100644
index 00000000000..94c93da544a
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/CFData.cpp
@@ -0,0 +1,85 @@
+//===-- CFData.cpp ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFData.h"
+
+//----------------------------------------------------------------------
+// CFData constructor
+//----------------------------------------------------------------------
+CFData::CFData(CFDataRef data) :
+ CFReleaser<CFDataRef>(data)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFData copy constructor
+//----------------------------------------------------------------------
+CFData::CFData(const CFData& rhs) :
+ CFReleaser<CFDataRef>(rhs)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFData copy constructor
+//----------------------------------------------------------------------
+CFData&
+CFData::operator=(const CFData& rhs)
+
+{
+ *this = rhs;
+ return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFData::~CFData()
+{
+}
+
+
+CFIndex
+CFData::GetLength() const
+{
+ CFDataRef data = get();
+ if (data)
+ return CFDataGetLength (data);
+ return 0;
+}
+
+
+const uint8_t*
+CFData::GetBytePtr() const
+{
+ CFDataRef data = get();
+ if (data)
+ return CFDataGetBytePtr (data);
+ return NULL;
+}
+
+CFDataRef
+CFData::Serialize(CFPropertyListRef plist, CFPropertyListFormat format)
+{
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ reset();
+ CFReleaser<CFWriteStreamRef> stream (::CFWriteStreamCreateWithAllocatedBuffers (alloc, alloc));
+ ::CFWriteStreamOpen (stream.get());
+ CFIndex len = ::CFPropertyListWriteToStream (plist, stream.get(), format, NULL);
+ if (len > 0)
+ reset((CFDataRef)::CFWriteStreamCopyProperty (stream.get(), kCFStreamPropertyDataWritten));
+ ::CFWriteStreamClose (stream.get());
+ return get();
+}
+
diff --git a/lldb/tools/debugserver/source/MacOSX/CFData.h b/lldb/tools/debugserver/source/MacOSX/CFData.h
new file mode 100644
index 00000000000..2c9d65d3af7
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/CFData.h
@@ -0,0 +1,39 @@
+//===-- CFData.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFData_h__
+#define __CFData_h__
+
+#include "CFUtils.h"
+
+class CFData : public CFReleaser<CFDataRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFData(CFDataRef data = NULL);
+ CFData(const CFData& rhs);
+ CFData& operator=(const CFData& rhs);
+ virtual ~CFData();
+
+ CFDataRef Serialize(CFPropertyListRef plist, CFPropertyListFormat format);
+ const uint8_t* GetBytePtr () const;
+ CFIndex GetLength () const;
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from CFData can see and modify these
+ //------------------------------------------------------------------
+};
+
+#endif // #ifndef __CFData_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/CFString.cpp b/lldb/tools/debugserver/source/MacOSX/CFString.cpp
new file mode 100644
index 00000000000..819024ca3bc
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/CFString.cpp
@@ -0,0 +1,201 @@
+//===-- CFString.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFString.h"
+#include <string>
+#include <glob.h>
+
+//----------------------------------------------------------------------
+// CFString constructor
+//----------------------------------------------------------------------
+CFString::CFString(CFStringRef s) :
+ CFReleaser<CFStringRef> (s)
+{
+}
+
+//----------------------------------------------------------------------
+// CFString copy constructor
+//----------------------------------------------------------------------
+CFString::CFString(const CFString& rhs) :
+ CFReleaser<CFStringRef> (rhs)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFString copy constructor
+//----------------------------------------------------------------------
+CFString&
+CFString::operator=(const CFString& rhs)
+{
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+CFString::CFString (const char *cstr, CFStringEncoding cstr_encoding) :
+ CFReleaser<CFStringRef> ()
+{
+ if (cstr && cstr[0])
+ {
+ reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding));
+ }
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFString::~CFString()
+{
+}
+
+const char *
+CFString::GetFileSystemRepresentation(std::string& s)
+{
+ return CFString::FileSystemRepresentation(get(), s);
+}
+
+CFStringRef
+CFString::SetFileSystemRepresentation (const char *path)
+{
+ CFStringRef new_value = NULL;
+ if (path && path[0])
+ new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path);
+ reset(new_value);
+ return get();
+}
+
+
+CFStringRef
+CFString::SetFileSystemRepresentationFromCFType (CFTypeRef cf_type)
+{
+ CFStringRef new_value = NULL;
+ if (cf_type != NULL)
+ {
+ CFTypeID cf_type_id = ::CFGetTypeID(cf_type);
+
+ if (cf_type_id == ::CFStringGetTypeID())
+ {
+ // Retain since we are using the existing object
+ new_value = (CFStringRef)::CFRetain(cf_type);
+ }
+ else if (cf_type_id == ::CFURLGetTypeID())
+ {
+ new_value = ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle);
+ }
+ }
+ reset(new_value);
+ return get();
+}
+
+CFStringRef
+CFString::SetFileSystemRepresentationAndExpandTilde (const char *path)
+{
+ std::string expanded_path;
+ if (CFString::GlobPath(path, expanded_path))
+ SetFileSystemRepresentation(expanded_path.c_str());
+ else
+ reset();
+ return get();
+}
+
+const char *
+CFString::UTF8(std::string& str)
+{
+ return CFString::UTF8(get(), str);
+}
+
+// Static function that puts a copy of the UTF8 contents of CF_STR into STR
+// and returns the C string pointer that is contained in STR when successful, else
+// NULL is returned. This allows the std::string parameter to own the extracted string,
+// and also allows that string to be returned as a C string pointer that can be used.
+
+const char *
+CFString::UTF8 (CFStringRef cf_str, std::string& str)
+{
+ if (cf_str)
+ {
+ const CFStringEncoding encoding = kCFStringEncodingUTF8;
+ CFIndex max_utf8_str_len = CFStringGetLength (cf_str);
+ max_utf8_str_len = CFStringGetMaximumSizeForEncoding (max_utf8_str_len, encoding);
+ if (max_utf8_str_len > 0)
+ {
+ str.resize(max_utf8_str_len);
+ if (!str.empty())
+ {
+ if (CFStringGetCString (cf_str, &str[0], str.size(), encoding))
+ {
+ str.resize(strlen(str.c_str()));
+ return str.c_str();
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+// Static function that puts a copy of the file system representation of CF_STR
+// into STR and returns the C string pointer that is contained in STR when
+// successful, else NULL is returned. This allows the std::string parameter
+// to own the extracted string, and also allows that string to be returned as
+// a C string pointer that can be used.
+
+const char *
+CFString::FileSystemRepresentation (CFStringRef cf_str, std::string& str)
+{
+ if (cf_str)
+ {
+ CFIndex max_length = ::CFStringGetMaximumSizeOfFileSystemRepresentation (cf_str);
+ if (max_length > 0)
+ {
+ str.resize(max_length);
+ if (!str.empty())
+ {
+ if (::CFStringGetFileSystemRepresentation (cf_str, &str[0], str.size()))
+ {
+ str.erase(::strlen(str.c_str()));
+ return str.c_str();
+ }
+ }
+ }
+ }
+ str.erase();
+ return NULL;
+}
+
+
+CFIndex
+CFString::GetLength() const
+{
+ CFStringRef str = get();
+ if (str)
+ return CFStringGetLength (str);
+ return 0;
+}
+
+
+const char*
+CFString::GlobPath(const char* path, std::string &expanded_path)
+{
+ glob_t globbuf;
+ if (::glob (path, GLOB_TILDE, NULL, &globbuf) == 0)
+ {
+ expanded_path = globbuf.gl_pathv[0];
+ ::globfree (&globbuf);
+ }
+ else
+ expanded_path.clear();
+
+ return expanded_path.c_str();
+}
+
diff --git a/lldb/tools/debugserver/source/MacOSX/CFString.h b/lldb/tools/debugserver/source/MacOSX/CFString.h
new file mode 100644
index 00000000000..2b96b8237fc
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/CFString.h
@@ -0,0 +1,43 @@
+//===-- CFString.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFString_h__
+#define __CFString_h__
+
+#include "CFUtils.h"
+#include <iosfwd>
+
+class CFString : public CFReleaser<CFStringRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFString (CFStringRef cf_str = NULL);
+ CFString (const char *s, CFStringEncoding encoding);
+ CFString (const CFString& rhs);
+ CFString& operator= (const CFString& rhs);
+ virtual ~CFString ();
+
+ const char * GetFileSystemRepresentation (std::string& str);
+ CFStringRef SetFileSystemRepresentation (const char *path);
+ CFStringRef SetFileSystemRepresentationFromCFType (CFTypeRef cf_type);
+ CFStringRef SetFileSystemRepresentationAndExpandTilde (const char *path);
+ const char * UTF8 (std::string& str);
+ CFIndex GetLength() const;
+ static const char *UTF8 (CFStringRef cf_str, std::string& str);
+ static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str);
+ static const char* GlobPath(const char* path, std::string &expanded_path);
+};
+
+#endif // #ifndef __CFString_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/CFUtils.h b/lldb/tools/debugserver/source/MacOSX/CFUtils.h
new file mode 100644
index 00000000000..afa984fa11c
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/CFUtils.h
@@ -0,0 +1,81 @@
+//===-- CFUtils.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/5/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFUtils_h__
+#define __CFUtils_h__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#ifdef __cplusplus
+
+//----------------------------------------------------------------------
+// Templatized CF helper class that can own any CF pointer and will
+// call CFRelease() on any valid pointer it owns unless that pointer is
+// explicitly released using the release() member function.
+//----------------------------------------------------------------------
+template <class T>
+class CFReleaser
+{
+public:
+ // Type names for the avlue
+ typedef T element_type;
+
+ // Constructors and destructors
+ CFReleaser(T ptr = NULL) : _ptr(ptr) { }
+ CFReleaser(const CFReleaser& copy) : _ptr(copy.get())
+ {
+ if (get())
+ ::CFRetain(get());
+ }
+ virtual ~CFReleaser() { reset(); }
+
+ // Assignments
+ CFReleaser& operator= (const CFReleaser<T>& copy)
+ {
+ if (copy != *this)
+ {
+ // Replace our owned pointer with the new one
+ reset(copy.get());
+ // Retain the current pointer that we own
+ if (get())
+ ::CFRetain(get());
+ }
+ }
+ // Get the address of the contained type
+ T * ptr_address() { return &_ptr; }
+
+ // Access the pointer itself
+ const T get() const { return _ptr; }
+ T get() { return _ptr; }
+
+ // Set a new value for the pointer and CFRelease our old
+ // value if we had a valid one.
+ void reset(T ptr = NULL)
+ {
+ if (ptr != _ptr)
+ {
+ if (_ptr != NULL)
+ ::CFRelease(_ptr);
+ _ptr = ptr;
+ }
+ }
+
+ // Release ownership without calling CFRelease
+ T release() { T tmp = _ptr; _ptr = NULL; return tmp; }
+private:
+ element_type _ptr;
+};
+
+#endif // #ifdef __cplusplus
+#endif // #ifndef __CFUtils_h__
+
diff --git a/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp b/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp
new file mode 100644
index 00000000000..76288c55bf5
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp
@@ -0,0 +1,679 @@
+//===-- MachDYLD.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/29/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachDYLD.h"
+#include "DNB.h"
+#include "DNBDataRef.h"
+#include <mach-o/loader.h>
+#include "DNBLog.h"
+
+MachDYLD::MachDYLD() :
+ m_pid(INVALID_NUB_PROCESS),
+ m_addr_size(4),
+ m_dyld_addr(INVALID_NUB_ADDRESS),
+ m_dyld_all_image_infos_addr(INVALID_NUB_ADDRESS),
+ m_dylib_info_header(),
+ m_current_dylibs(),
+ m_changed_dylibs(),
+ m_notify_break_id(INVALID_NUB_BREAK_ID),
+ m_dyld_info_mutex(PTHREAD_MUTEX_RECURSIVE)
+{
+}
+
+MachDYLD::~MachDYLD()
+{
+ Clear();
+}
+
+
+void
+MachDYLD::Clear()
+{
+ PThreadMutex::Locker locker(m_dyld_info_mutex);
+
+ nub_process_t pid = m_pid;
+ if (pid != INVALID_NUB_PROCESS)
+ {
+ DNBProcessSetSharedLibraryInfoCallback ( pid, NULL, NULL);
+ DNBBreakpointClear(pid, m_notify_break_id);
+ }
+
+ m_addr_size = 4;
+ m_dyld_addr = INVALID_NUB_ADDRESS;
+ m_dyld_all_image_infos_addr = INVALID_NUB_ADDRESS;
+ m_dylib_info_header.Clear();
+ m_current_dylibs.clear();
+ m_changed_dylibs.clear();
+ m_notify_break_id = INVALID_NUB_BREAK_ID;
+}
+
+
+void
+MachDYLD::Initialize(nub_process_t pid)
+{
+ //printf("MachDYLD::%s(0x%4.4x)\n", __FUNCTION__, pid);
+ Clear();
+ m_pid = pid;
+}
+
+
+void
+MachDYLD::ProcessStateChanged(nub_state_t state)
+{
+ //printf("MachDYLD::%s(%s)\n", __FUNCTION__, DNBStateAsString(state));
+
+ switch (state)
+ {
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateExited:
+ case eStateDetached:
+ case eStateAttaching:
+ case eStateLaunching:
+ Clear();
+ break;
+
+ case eStateStopped:
+ // Keep trying find dyld each time we stop until we do
+ if (!FoundDYLD())
+ {
+ assert(m_pid != INVALID_NUB_PROCESS);
+ DNBProcessSetSharedLibraryInfoCallback ( m_pid, CopySharedInfoCallback, this);
+ CheckForDYLDInMemory();
+ }
+ break;
+
+ case eStateRunning:
+ case eStateStepping:
+ case eStateCrashed:
+ case eStateSuspended:
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+MachDYLD::SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos)
+{
+ //printf("MachDYLD::%s(%p, %u)\n", __FUNCTION__, image_infos, image_infos);
+
+}
+
+bool
+MachDYLD::FoundDYLD() const
+{
+ return m_dyld_addr != INVALID_NUB_ADDRESS;
+}
+
+bool
+MachDYLD::CheckForDYLDInMemory()
+{
+#if defined (__arm__)
+ return CheckForDYLDInMemory(0x2fe00000);
+#else
+ return CheckForDYLDInMemory(0x8fe00000);
+#endif
+}
+
+bool
+MachDYLD::CheckForDYLDInMemory(nub_addr_t addr)
+{
+ std::vector<uint8_t> dyld_header;
+ nub_size_t page_size = 0x1000;
+ dyld_header.resize(page_size);
+ nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, addr, dyld_header.size(), &dyld_header[0]);
+ if (bytes_read > 0)
+ {
+ DNBDataRef::offset_t offset = 0;
+ DNBDataRef data(&dyld_header[0], bytes_read, false);
+ struct mach_header *header = (struct mach_header*)data.GetData(&offset, sizeof(struct mach_header));
+ if (header)
+ {
+ switch (header->magic)
+ {
+ case MH_MAGIC:
+ case MH_CIGAM:
+ data.SetPointerSize(4);
+ m_addr_size = 4;
+ break;
+
+ case MH_MAGIC_64:
+ case MH_CIGAM_64:
+ data.SetPointerSize(8);
+ m_addr_size = 8;
+ break;
+
+ default:
+ return false;
+ }
+
+ if (header->filetype == MH_DYLINKER)
+ {
+ // printf( "Found DYLD mach image at %8.8p", addr);
+
+ m_dyld_all_image_infos_addr = DNBProcessLookupAddress(m_pid, "dyld_all_image_infos", "/usr/lib/dyld");
+
+#if defined (__arm__)
+ m_dyld_all_image_infos_addr = 0x2fe3a004;
+#endif
+
+ if (m_dyld_all_image_infos_addr != INVALID_NUB_ADDRESS)
+ {
+ // printf( "Found DYLD data symbol 'dyld_all_image_infos' is %8.8p", m_dyld_all_image_infos_addr);
+
+ if (ReadDYLIBInfo())
+ {
+ if (m_dylib_info_header.notification != INVALID_NUB_ADDRESS)
+ {
+ m_notify_break_id = DNBBreakpointSet(m_pid, m_dylib_info_header.notification, 4, true);
+ if (NUB_BREAK_ID_IS_VALID(m_notify_break_id))
+ {
+ DNBBreakpointSetCallback(m_pid, m_notify_break_id, MachDYLD::BreakpointHit, this);
+ m_dyld_addr = addr;
+ }
+ }
+ }
+ // if (DNBLogCheckLogBit(LOG_SHLIB))
+ // Dump(DNBLogGetLogFile());
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+nub_bool_t
+MachDYLD::BreakpointHit(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton)
+{
+ MachDYLD *dyld = (MachDYLD*) baton;
+ //printf("MachDYLD::BreakpointHit called");
+ dyld->ReadDYLIBInfo();
+ DNBProcessSharedLibrariesUpdated(pid);
+ return false; // Don't stop the process, let it continue
+}
+
+bool
+MachDYLD::ReadDYLIBInfo()
+{
+ nub_addr_t addr = m_dyld_all_image_infos_addr;
+ if (addr != INVALID_NUB_ADDRESS)
+ {
+ PThreadMutex::Locker locker(m_dyld_info_mutex);
+ //printf("MachDYLD::ReadDYLIBInfo(addr =%8.8p)", addr);
+ bool swap = false;
+ uint32_t i = 0;
+ DYLIBInfo::collection previous_dylibs;
+ previous_dylibs.swap(m_current_dylibs);
+ uint8_t all_dylib_info_data[32];
+ nub_size_t count = 8 + m_addr_size * 2;
+ nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, addr, count, &all_dylib_info_data[0]);
+ if (bytes_read != count)
+ {
+ m_dylib_info_header.Clear();
+ return false;
+ }
+
+ DNBDataRef data(all_dylib_info_data, sizeof(all_dylib_info_data), swap);
+ data.SetPointerSize(m_addr_size);
+ DNBDataRef::offset_t offset = 0;
+ m_dylib_info_header.version = data.Get32(&offset);
+ m_dylib_info_header.dylib_info_count = data.Get32(&offset);
+ m_dylib_info_header.dylib_info_addr = data.GetPointer(&offset);
+ m_dylib_info_header.notification = data.GetPointer(&offset);
+ //printf( "%s: version=%d, count=%d, addr=%8.8p, notify=%8.8p",
+ // __PRETTY_FUNCTION__,
+ // m_dylib_info_header.version,
+ // m_dylib_info_header.dylib_info_count,
+ // m_dylib_info_header.dylib_info_addr,
+ // m_dylib_info_header.notification);
+
+ switch (m_dylib_info_header.version)
+ {
+ case 1: // 10.4.x and prior
+ {
+ }
+ break;
+
+ case 2: // 10.5 and later
+ {
+ }
+ break;
+
+ default:
+ //printf( "Invalid dyld all_dylib_infos version number: %d", m_dylib_info_header.version);
+ return false;
+ break;
+ }
+
+ // If we made it here, we are assuming that the all dylib info data should
+ // be valid, lets read the info array.
+ if (m_dylib_info_header.dylib_info_count > 0)
+ {
+ if (m_dylib_info_header.dylib_info_addr == 0)
+ {
+ //printf( "dyld is currently updating all_dylib_infos.");
+ }
+ else
+ {
+ m_current_dylibs.resize(m_dylib_info_header.dylib_info_count);
+ count = m_current_dylibs.size() * 3 * m_addr_size;
+ std::vector<uint8_t> info_data(count, 0);
+ bytes_read = DNBProcessMemoryRead(m_pid, m_dylib_info_header.dylib_info_addr, count, &info_data[0]);
+ if (bytes_read == count)
+ {
+ DNBDataRef::offset_t info_data_offset = 0;
+ DNBDataRef info_data_ref(&info_data[0], info_data.size(), swap);
+ info_data_ref.SetPointerSize(m_addr_size);
+ for (i = 0; info_data_ref.ValidOffset(info_data_offset); i++)
+ {
+ assert (i < m_current_dylibs.size());
+ m_current_dylibs[i].address = info_data_ref.GetPointer(&info_data_offset);
+ nub_addr_t path_addr = info_data_ref.GetPointer(&info_data_offset);
+ m_current_dylibs[i].mod_date = info_data_ref.GetPointer(&info_data_offset);
+
+ char raw_path[PATH_MAX];
+ char resolved_path[PATH_MAX];
+ bytes_read = DNBProcessMemoryRead(m_pid, path_addr, sizeof(raw_path), (char*)&raw_path[0]);
+ if (::realpath(raw_path, resolved_path))
+ m_current_dylibs[i].path = resolved_path;
+ else
+ m_current_dylibs[i].path = raw_path;
+ }
+ assert(i == m_dylib_info_header.dylib_info_count);
+
+ UpdateUUIDs();
+ }
+ else
+ {
+ //printf( "unable to read all data for all_dylib_infos.");
+ m_current_dylibs.clear();
+ return false;
+ }
+ }
+ }
+ // Read any UUID values that we can get
+ if (m_current_dylibs.empty())
+ {
+ m_changed_dylibs = previous_dylibs;
+ const size_t num_changed_dylibs = m_changed_dylibs.size();
+ for (i = 0; i < num_changed_dylibs; i++)
+ {
+ // Indicate the shared library was unloaded by giving it an invalid
+ // address...
+ m_changed_dylibs[i].address = INVALID_NUB_ADDRESS;
+ }
+ }
+ else
+ {
+ m_changed_dylibs.clear();
+
+ // Most of the time when we get shared library changes, they just
+ // get appended to the end of the list, so find out the min number
+ // of entries in the current and previous list that match and see
+ // how many are equal.
+ uint32_t curr_dylib_count = m_current_dylibs.size();
+ uint32_t prev_dylib_count = previous_dylibs.size();
+ uint32_t common_count = std::min<uint32_t>(prev_dylib_count, curr_dylib_count);
+ MachDYLD::DYLIBInfo::const_iterator curr_pos = m_current_dylibs.begin();
+ MachDYLD::DYLIBInfo::const_iterator curr_end = m_current_dylibs.end();
+ MachDYLD::DYLIBInfo::iterator prev_pos = previous_dylibs.begin();
+ uint32_t idx;
+ for (idx = 0; idx < common_count; idx++)
+ {
+ if (*curr_pos == *prev_pos)
+ {
+ ++curr_pos;
+ ++prev_pos;
+ }
+ else
+ break;
+ }
+
+ // Remove all the entries that were at the exact same index and that
+ // matched between the previous_dylibs and m_current_dylibs arrays. This will cover
+ // most of the cases as when shared libraries get loaded they get
+ // appended to the end of the list.
+ if (prev_pos != previous_dylibs.begin())
+ {
+ previous_dylibs.erase(previous_dylibs.begin(), prev_pos);
+ }
+
+ if (previous_dylibs.empty())
+ {
+ // We only have new libraries to add, they are the only ones that
+ // have changed.
+ if (curr_pos != curr_end)
+ {
+ m_changed_dylibs.assign(curr_pos, curr_end);
+ }
+ }
+ else
+ {
+ // We still have items in our previous dylib list which means either
+ // one or more shared libraries got unloaded somewhere in the middle
+ // of the list, so we will manually search for each remaining item
+ // in our current list in the previous list
+ for (; curr_pos != curr_end; ++curr_pos)
+ {
+ MachDYLD::DYLIBInfo::iterator pos = std::find(previous_dylibs.begin(), previous_dylibs.end(), *curr_pos);
+ if (pos == previous_dylibs.end())
+ {
+ // This dylib wasn't here before, add it to our change list
+ m_changed_dylibs.push_back(*curr_pos);
+ }
+ else
+ {
+ // This dylib was in our previous dylib list, it didn't
+ // change, so lets remove it from the previous list so we
+ // don't see it again.
+ previous_dylibs.erase(pos);
+ }
+ }
+
+ // The only items left if our previous_dylibs array will be shared
+ // libraries that got unloaded (still in previous list, and not
+ // mentioned in the current list).
+ if (!previous_dylibs.empty())
+ {
+ const size_t num_previous_dylibs = previous_dylibs.size();
+ for (i = 0; i < num_previous_dylibs; i++)
+ {
+ // Indicate the shared library was unloaded by giving it
+ // an invalid address...
+ previous_dylibs[i].address = INVALID_NUB_ADDRESS;
+ }
+ // Add all remaining previous_dylibs to the changed list with
+ // invalidated addresses so we know they got unloaded.
+ m_changed_dylibs.insert(m_changed_dylibs.end(), previous_dylibs.begin(), previous_dylibs.end());
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+void
+MachDYLD::UpdateUUIDs()
+{
+ bool swap = false;
+ nub_size_t page_size = 0x1000;
+ uint32_t i;
+ // Read any UUID values that we can get
+ for (i = 0; i < m_dylib_info_header.dylib_info_count; i++)
+ {
+ if (!m_current_dylibs[i].UUIDValid())
+ {
+ std::vector<uint8_t> bytes(page_size, 0);
+ nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, m_current_dylibs[i].address, page_size, &bytes[0]);
+ if (bytes_read > 0)
+ {
+ DNBDataRef::offset_t offset = 0;
+ DNBDataRef data(&bytes[0], bytes_read, swap);
+ struct mach_header *header = (struct mach_header*)data.GetData(&offset, sizeof(struct mach_header));
+ if (header)
+ {
+ switch (header->magic)
+ {
+ case MH_MAGIC:
+ case MH_CIGAM:
+ data.SetPointerSize(4);
+ m_addr_size = 4;
+ break;
+
+ case MH_MAGIC_64:
+ case MH_CIGAM_64:
+ data.SetPointerSize(8);
+ m_addr_size = 8;
+ offset += 4; // Skip the extra reserved field in the 64 bit mach header
+ break;
+
+ default:
+ continue;
+ }
+
+ if (header->sizeofcmds > bytes_read)
+ {
+ bytes.resize(header->sizeofcmds);
+ nub_addr_t addr = m_current_dylibs[i].address + bytes_read;
+ bytes_read += DNBProcessMemoryRead(m_pid, addr , header->sizeofcmds - bytes_read, &bytes[bytes_read]);
+ }
+ assert(bytes_read >= header->sizeofcmds);
+ uint32_t cmd_idx;
+ DNBSegment segment;
+
+ for (cmd_idx = 0; cmd_idx < header->ncmds; cmd_idx++)
+ {
+ if (data.ValidOffsetForDataOfSize(offset, sizeof(struct load_command)))
+ {
+ struct load_command load_cmd;
+ DNBDataRef::offset_t load_cmd_offset = offset;
+ load_cmd.cmd = data.Get32(&offset);
+ load_cmd.cmdsize = data.Get32(&offset);
+ switch (load_cmd.cmd)
+ {
+ case LC_SEGMENT:
+ {
+ strncpy(segment.name, data.GetCStr(&offset, 16), 16);
+ memset(&segment.name[16], 0, DNB_MAX_SEGMENT_NAME_LENGTH - 16);
+ segment.addr = data.Get32(&offset);
+ segment.size = data.Get32(&offset);
+ m_current_dylibs[i].segments.push_back(segment);
+ }
+ break;
+
+ case LC_SEGMENT_64:
+ {
+ strncpy(segment.name, data.GetCStr(&offset, 16), 16);
+ memset(&segment.name[16], 0, DNB_MAX_SEGMENT_NAME_LENGTH - 16);
+ segment.addr = data.Get64(&offset);
+ segment.size = data.Get64(&offset);
+ m_current_dylibs[i].segments.push_back(segment);
+ }
+ break;
+
+ case LC_UUID:
+ // We found our UUID, we can stop now...
+ memcpy(m_current_dylibs[i].uuid, data.GetData(&offset, 16), 16);
+ // if (DNBLogCheckLogBit(LOG_SHLIB))
+ // {
+ // DNBLogThreaded("UUID found for aii[%d]:", i);
+ // m_current_dylibs[i].Dump(DNBLogGetLogFile());
+ // }
+ break;
+
+ default:
+ break;
+ }
+ // Set offset to be the beginning of the next load command.
+ offset = load_cmd_offset + load_cmd.cmdsize;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+nub_addr_t
+MachDYLD::GetSharedLibraryHeaderAddress(const char *shlib_path) const
+{
+ if (!m_current_dylibs.empty() && shlib_path && shlib_path[0])
+ {
+ uint32_t i;
+ for (i = 0; i<m_current_dylibs.size(); i++)
+ {
+ if (m_current_dylibs[i].path == shlib_path)
+ return m_current_dylibs[i].address;
+ }
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+
+nub_size_t
+MachDYLD::CopySharedLibraryInfo(DYLIBInfo::collection& dylib_coll, DNBExecutableImageInfo **image_infos)
+{
+ if (!dylib_coll.empty())
+ {
+ size_t i;
+ size_t total_num_segments = 0;
+ size_t segment_index = 0;
+ for (i = 0; i<dylib_coll.size(); i++)
+ {
+ total_num_segments += dylib_coll[i].segments.size();
+ }
+ size_t image_infos_byte_size = sizeof(DNBExecutableImageInfo) * dylib_coll.size();
+ size_t all_segments_byte_size = sizeof(DNBSegment) * total_num_segments;
+ size_t total_byte_size = image_infos_byte_size + all_segments_byte_size;
+
+ // Allocate enough space to fit all of the shared library information in
+ // a single buffer so consumers can free a single chunk of data when done
+ uint8_t *buf = (uint8_t*)malloc (total_byte_size);
+
+ DNBExecutableImageInfo *info = (DNBExecutableImageInfo*)buf;
+ DNBSegment *all_segments = (DNBSegment*)(buf + image_infos_byte_size);
+ if (info)
+ {
+ for (i = 0; i<dylib_coll.size(); i++)
+ {
+ strncpy(info[i].name, dylib_coll[i].path.c_str(), PATH_MAX);
+ // NULL terminate paths that are too long (redundant for path
+ // that fit, but harmless
+ info[i].name[PATH_MAX-1] = '\0';
+ info[i].header_addr = dylib_coll[i].address;
+ info[i].state = (dylib_coll[i].address == INVALID_NUB_ADDRESS ? eShlibStateUnloaded : eShlibStateLoaded);
+ memcpy(info[i].uuid, dylib_coll[i].uuid, sizeof(uuid_t));
+ info[i].num_segments = dylib_coll[i].segments.size();
+ if (info[i].num_segments == 0)
+ {
+ info[i].segments = NULL;
+ }
+ else
+ {
+ info[i].segments = &all_segments[segment_index];
+ memcpy(info[i].segments, &(dylib_coll[i].segments[0]), sizeof(DNBSegment) * info[i].num_segments);
+ segment_index += info[i].num_segments;
+ }
+
+ }
+ // Release ownership of the shared library array to the caller
+ *image_infos = info;
+ return dylib_coll.size();
+ }
+ }
+ *image_infos = NULL;
+ return 0;
+}
+
+
+
+nub_size_t
+MachDYLD::CopySharedInfoCallback(nub_process_t pid, struct DNBExecutableImageInfo **image_infos, nub_bool_t only_changed, void *baton)
+{
+ MachDYLD *dyld = (MachDYLD*) baton;
+
+ if (only_changed)
+ return dyld->CopyChangedShlibInfo(image_infos);
+ else
+ return dyld->CopyCurrentShlibInfo(image_infos);
+
+ *image_infos = NULL;
+ return 0;
+}
+
+nub_size_t
+MachDYLD::CopyCurrentShlibInfo(DNBExecutableImageInfo **image_infos)
+{
+ PThreadMutex::Locker locker(m_dyld_info_mutex);
+ return CopySharedLibraryInfo(m_current_dylibs, image_infos);
+}
+
+
+nub_size_t
+MachDYLD::CopyChangedShlibInfo(DNBExecutableImageInfo **image_infos)
+{
+ PThreadMutex::Locker locker(m_dyld_info_mutex);
+ return CopySharedLibraryInfo(m_changed_dylibs, image_infos);
+}
+
+
+
+void
+MachDYLD::DYLIBInfo::Dump(FILE *f) const
+{
+ if (f == NULL)
+ return;
+ if (address == INVALID_NUB_ADDRESS)
+ {
+ if (UUIDValid())
+ {
+ fprintf(f, "UNLOADED %8.8llx %2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X %s",
+ (uint64_t)mod_date,
+ uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3],
+ uuid[ 4], uuid[ 5], uuid[ 6], uuid[ 7],
+ uuid[ 8], uuid[ 9], uuid[10], uuid[11],
+ uuid[12], uuid[13], uuid[14], uuid[15],
+ path.c_str());
+ }
+ else
+ {
+ fprintf(f, "UNLOADED %8.8llx %s", (uint64_t)mod_date, path.c_str());
+ }
+ }
+ else
+ {
+ if (UUIDValid())
+ {
+ fprintf(f, "%8.8llx %8.8llx %2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X %s",
+ (uint64_t)address,
+ (uint64_t)mod_date,
+ uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3],
+ uuid[ 4], uuid[ 5], uuid[ 6], uuid[ 7],
+ uuid[ 8], uuid[ 9], uuid[10], uuid[11],
+ uuid[12], uuid[13], uuid[14], uuid[15],
+ path.c_str());
+ }
+ else
+ {
+ fprintf(f, "%8.8llx %8.8llx %s", (uint64_t)address, (uint64_t)mod_date, path.c_str());
+ }
+ }
+}
+
+void
+MachDYLD::Dump(FILE *f) const
+{
+ if (f == NULL)
+ return;
+
+ PThreadMutex::Locker locker(m_dyld_info_mutex);
+ fprintf(f, "\n\tMachDYLD.m_dylib_info_header: version=%d, count=%d, addr=0x%llx, notify=0x%llx",
+ m_dylib_info_header.version,
+ m_dylib_info_header.dylib_info_count,
+ (uint64_t)m_dylib_info_header.dylib_info_addr,
+ (uint64_t)m_dylib_info_header.notification);
+ uint32_t i;
+ fprintf(f, "\n\tMachDYLD.m_current_dylibs");
+ for (i = 0; i<m_current_dylibs.size(); i++)
+ m_current_dylibs[i].Dump(f);
+ fprintf(f, "\n\tMachDYLD.m_changed_dylibs");
+ for (i = 0; i<m_changed_dylibs.size(); i++)
+ m_changed_dylibs[i].Dump(f);
+}
+
diff --git a/lldb/tools/debugserver/source/MacOSX/MachDYLD.h b/lldb/tools/debugserver/source/MacOSX/MachDYLD.h
new file mode 100644
index 00000000000..1c7747cd137
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachDYLD.h
@@ -0,0 +1,145 @@
+//===-- MachDYLD.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/29/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachDYLD_h__
+#define __MachDYLD_h__
+
+#include "DNBDefs.h"
+#include "DNBRuntimeAction.h"
+#include "PThreadMutex.h"
+#include <map>
+#include <vector>
+#include <string>
+
+class DNBBreakpoint;
+class MachProcess;
+
+class MachDYLD : public DNBRuntimeAction
+{
+public:
+ MachDYLD ();
+ virtual ~MachDYLD ();
+
+ //------------------------------------------------------------------
+ // DNBRuntimeAction required functions
+ //------------------------------------------------------------------
+ virtual void Initialize(nub_process_t pid);
+ virtual void ProcessStateChanged(nub_state_t state);
+ virtual void SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos);
+
+protected:
+ bool CheckForDYLDInMemory();
+ bool FoundDYLD() const;
+ void Clear();
+ void Dump(FILE *f) const;
+ nub_process_t ProcessID() const { return m_pid; }
+ uint32_t AddrByteSize() const { return m_addr_size; }
+ nub_size_t CopyCurrentShlibInfo(DNBExecutableImageInfo **image_infos);
+ nub_size_t CopyChangedShlibInfo(DNBExecutableImageInfo **image_infos);
+ nub_addr_t GetSharedLibraryHeaderAddress(const char *shlib_path) const;
+ bool CheckForDYLDInMemory(nub_addr_t addr);
+ bool ReadDYLIBInfo ();
+ static nub_bool_t BreakpointHit (nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton);
+ void UpdateUUIDs();
+
+ struct DYLIBInfo
+ {
+ nub_addr_t address; // Address of mach header for this dylib
+ nub_addr_t mod_date; // Modification date for this dylib
+ std::string path; // Resolved path for this dylib
+ uint8_t uuid[16]; // UUID for this dylib if it has one, else all zeros
+ std::vector<DNBSegment> segments; // All segment vmaddr and vmsize pairs for this executable (from memory of inferior)
+
+ DYLIBInfo() :
+ address(INVALID_NUB_ADDRESS),
+ mod_date(0),
+ path(),
+ segments()
+ {
+ memset(uuid, 0, 16);
+ }
+
+ void Clear()
+ {
+ address = INVALID_NUB_ADDRESS;
+ mod_date = 0;
+ path.clear();
+ segments.clear();
+ memset(uuid, 0, 16);
+ }
+
+ bool operator == (const DYLIBInfo& rhs) const
+ {
+ return address == rhs.address
+ && mod_date == rhs.mod_date
+ && path == rhs.path
+ && memcmp(uuid, rhs.uuid, 16) == 0;
+ }
+ bool UUIDValid() const
+ {
+ return uuid[ 0] || uuid[ 1] || uuid[ 2] || uuid[ 3] ||
+ uuid[ 4] || uuid[ 5] || uuid[ 6] || uuid[ 7] ||
+ uuid[ 8] || uuid[ 9] || uuid[10] || uuid[11] ||
+ uuid[12] || uuid[13] || uuid[14] || uuid[15];
+ }
+
+ void Dump(FILE *f) const;
+ typedef std::vector<DYLIBInfo> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ };
+ struct InfoHeader
+ {
+ uint32_t version; /* == 1 in Mac OS X 10.4, == 2 in Mac OS 10.5 */
+ uint32_t dylib_info_count;
+ nub_addr_t dylib_info_addr;
+ nub_addr_t notification;
+ bool processDetachedFromSharedRegion;
+
+ InfoHeader() :
+ version(0),
+ dylib_info_count(0),
+ dylib_info_addr(INVALID_NUB_ADDRESS),
+ notification(INVALID_NUB_ADDRESS),
+ processDetachedFromSharedRegion(false)
+ {
+ }
+
+ void Clear()
+ {
+ version = 0;
+ dylib_info_count = 0;
+ dylib_info_addr = INVALID_NUB_ADDRESS;
+ notification = INVALID_NUB_ADDRESS;
+ processDetachedFromSharedRegion = false;
+ }
+
+ bool IsValid() const
+ {
+ return version == 1 || version == 2;
+ }
+ };
+ static nub_size_t CopySharedLibraryInfo(DYLIBInfo::collection& dylib_coll, DNBExecutableImageInfo **image_infos);
+ static nub_size_t CopySharedInfoCallback(nub_process_t pid, struct DNBExecutableImageInfo **image_infos, nub_bool_t only_changed, void *baton);
+ nub_process_t m_pid;
+ uint32_t m_addr_size;
+ nub_addr_t m_dyld_addr;
+ nub_addr_t m_dyld_all_image_infos_addr;
+ InfoHeader m_dylib_info_header;
+ DYLIBInfo::collection m_current_dylibs; // Current shared libraries information
+ DYLIBInfo::collection m_changed_dylibs; // Shared libraries that changed since last shared library update
+ nub_break_t m_notify_break_id;
+ mutable PThreadMutex m_dyld_info_mutex;
+};
+
+#endif // #ifndef __MachDYLD_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/MachException.cpp b/lldb/tools/debugserver/source/MacOSX/MachException.cpp
new file mode 100644
index 00000000000..356160bb621
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachException.cpp
@@ -0,0 +1,533 @@
+//===-- MachException.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachException.h"
+#include "MachProcess.h"
+#include "DNB.h"
+#include "DNBError.h"
+#include <sys/types.h>
+#include "DNBLog.h"
+#include "PThreadMutex.h"
+#include "SysSignal.h"
+#include <errno.h>
+#include <sys/ptrace.h>
+
+// Routine mach_exception_raise
+extern "C"
+kern_return_t catch_mach_exception_raise
+(
+ mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt
+);
+
+extern "C"
+kern_return_t catch_mach_exception_raise_state
+(
+ mach_port_t exception_port,
+ exception_type_t exception,
+ const mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int *flavor,
+ const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+);
+
+// Routine mach_exception_raise_state_identity
+extern "C"
+kern_return_t catch_mach_exception_raise_state_identity
+(
+ mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int *flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+);
+
+extern "C" boolean_t mach_exc_server(
+ mach_msg_header_t *InHeadP,
+ mach_msg_header_t *OutHeadP);
+
+// Any access to the g_message variable should be done by locking the
+// g_message_mutex first, using the g_message variable, then unlocking
+// the g_message_mutex. See MachException::Message::CatchExceptionRaise()
+// for sample code.
+
+static MachException::Data *g_message = NULL;
+//static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise_state
+(
+ mach_port_t exc_port,
+ exception_type_t exc_type,
+ const mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count,
+ int * flavor,
+ const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t * new_stateCnt
+)
+{
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ {
+ DNBLogThreaded("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data = " MACH_EXCEPTION_DATA_FMT_HEX ", exc_data_count = %d)",
+ __FUNCTION__,
+ exc_port,
+ exc_type, MachException::Name(exc_type),
+ exc_data,
+ exc_data_count);
+ }
+ return KERN_FAILURE;
+}
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise_state_identity
+(
+ mach_port_t exc_port,
+ mach_port_t thread_port,
+ mach_port_t task_port,
+ exception_type_t exc_type,
+ mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count,
+ int * flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+)
+{
+ kern_return_t kret;
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ {
+ DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })",
+ __FUNCTION__,
+ exc_port,
+ thread_port,
+ task_port,
+ exc_type, MachException::Name(exc_type),
+ exc_data_count,
+ exc_data_count > 0 ? exc_data[0] : 0xBADDBADD,
+ exc_data_count > 1 ? exc_data[1] : 0xBADDBADD);
+ }
+ kret = mach_port_deallocate (mach_task_self (), task_port);
+ kret = mach_port_deallocate (mach_task_self (), thread_port);
+
+ return KERN_FAILURE;
+}
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise
+(
+ mach_port_t exc_port,
+ mach_port_t thread_port,
+ mach_port_t task_port,
+ exception_type_t exc_type,
+ mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count)
+{
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ {
+ DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })",
+ __FUNCTION__,
+ exc_port,
+ thread_port,
+ task_port,
+ exc_type, MachException::Name(exc_type),
+ exc_data_count,
+ exc_data_count > 0 ? exc_data[0] : 0xBADDBADD,
+ exc_data_count > 1 ? exc_data[1] : 0xBADDBADD);
+ }
+
+ g_message->task_port = task_port;
+ g_message->thread_port = thread_port;
+ g_message->exc_type = exc_type;
+ g_message->exc_data.resize(exc_data_count);
+ ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t));
+ return KERN_SUCCESS;
+}
+
+
+void
+MachException::Message::Dump() const
+{
+ DNBLogThreadedIf(LOG_EXCEPTIONS,
+ " exc_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx } ",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id);
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS,
+ "reply_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx }",
+ reply_msg.hdr.msgh_bits,
+ reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port,
+ reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved,
+ reply_msg.hdr.msgh_id);
+
+ state.Dump();
+}
+
+bool
+MachException::Data::GetStopInfo(struct DNBThreadStopInfo *stop_info) const
+{
+ // Zero out the structure.
+ memset(stop_info, 0, sizeof(struct DNBThreadStopInfo));
+ // We always stop with a mach exceptions
+ stop_info->reason = eStopTypeException;
+ // Save the EXC_XXXX exception type
+ stop_info->details.exception.type = exc_type;
+
+ // Fill in a text description
+ const char * exc_name = MachException::Name(exc_type);
+ char *desc = stop_info->description;
+ const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH;
+ if (exc_name)
+ desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s", exc_name);
+ else
+ desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i", exc_type);
+
+ stop_info->details.exception.data_count = exc_data.size();
+
+ int soft_signal = SoftSignal();
+ if (soft_signal)
+ {
+ if (desc < end_desc)
+ {
+ const char *sig_str = SysSignal::Name(soft_signal);
+ desc += snprintf(desc, end_desc - desc, " EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal, sig_str ? sig_str : "unknown signal");
+ }
+ }
+ else
+ {
+ // No special disassembly for exception data, just
+ size_t idx;
+ if (desc < end_desc)
+ {
+ desc += snprintf(desc, end_desc - desc, " data[%zu] = {", stop_info->details.exception.data_count);
+
+ for (idx = 0; desc < end_desc && idx < stop_info->details.exception.data_count; ++idx)
+ desc += snprintf(desc, end_desc - desc, MACH_EXCEPTION_DATA_FMT_MINHEX "%c", exc_data[idx], ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ','));
+ }
+ }
+
+ // Copy the exception data
+ size_t i;
+ for (i=0; i<stop_info->details.exception.data_count; i++)
+ stop_info->details.exception.data[i] = exc_data[i];
+
+ return true;
+}
+
+
+void
+MachException::Data::DumpStopReason() const
+{
+ int soft_signal = SoftSignal();
+ if (soft_signal)
+ {
+ const char *signal_str = SysSignal::Name(soft_signal);
+ if (signal_str)
+ DNBLog("signal(%s)", signal_str);
+ else
+ DNBLog("signal(%i)", soft_signal);
+ return;
+ }
+ DNBLog("%s", Name(exc_type));
+}
+
+kern_return_t
+MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port)
+{
+ DNBError err;
+ const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS);
+ mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0;
+ if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0))
+ {
+ // Dump this log message if we have no timeout in case it never returns
+ DNBLogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id,
+ options,
+ 0,
+ sizeof (exc_msg.data),
+ port,
+ mach_msg_timeout,
+ notify_port);
+ }
+
+ err = ::mach_msg (&exc_msg.hdr,
+ options, // options
+ 0, // Send size
+ sizeof (exc_msg.data), // Receive size
+ port, // exception port to watch for exception on
+ mach_msg_timeout, // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter)
+ notify_port);
+
+ // Dump any errors we get
+ if (log_exceptions)
+ {
+ err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id,
+ options,
+ 0,
+ sizeof (exc_msg.data),
+ port,
+ mach_msg_timeout,
+ notify_port);
+ }
+ return err.Error();
+}
+
+bool
+MachException::Message::CatchExceptionRaise()
+{
+ bool success = false;
+ // locker will keep a mutex locked until it goes out of scope
+// PThreadMutex::Locker locker(&g_message_mutex);
+ // DNBLogThreaded("calling mach_exc_server");
+ g_message = &state;
+ // The exc_server function is the MIG generated server handling function
+ // to handle messages from the kernel relating to the occurrence of an
+ // exception in a thread. Such messages are delivered to the exception port
+ // set via thread_set_exception_ports or task_set_exception_ports. When an
+ // exception occurs in a thread, the thread sends an exception message to
+ // its exception port, blocking in the kernel waiting for the receipt of a
+ // reply. The exc_server function performs all necessary argument handling
+ // for this kernel message and calls catch_exception_raise,
+ // catch_exception_raise_state or catch_exception_raise_state_identity,
+ // which should handle the exception. If the called routine returns
+ // KERN_SUCCESS, a reply message will be sent, allowing the thread to
+ // continue from the point of the exception; otherwise, no reply message
+ // is sent and the called routine must have dealt with the exception
+ // thread directly.
+ if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr))
+ {
+ success = true;
+ }
+ else if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ {
+ DNBLogThreaded("mach_exc_server returned zero...");
+ }
+ g_message = NULL;
+ return success;
+}
+
+
+
+kern_return_t
+MachException::Message::Reply(MachProcess *process, int signal)
+{
+ // Reply to the exception...
+ DNBError err;
+
+ // If we had a soft signal, we need to update the thread first so it can
+ // continue without signaling
+ int soft_signal = state.SoftSignal();
+ if (soft_signal)
+ {
+ int state_pid = -1;
+ if (process->Task().TaskPort() == state.task_port)
+ {
+ // This is our task, so we can update the signal to send to it
+ state_pid = process->ProcessID();
+ soft_signal = signal;
+ }
+ else
+ {
+ err = ::pid_for_task(state.task_port, &state_pid);
+ }
+
+ assert (state_pid != -1);
+ if (state_pid != -1)
+ {
+ errno = 0;
+ if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)state.thread_port, soft_signal) != 0)
+ err.SetError(errno, DNBError::POSIX);
+ else
+ err.Clear();
+
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, soft_signal);
+ }
+ }
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
+ reply_msg.hdr.msgh_bits,
+ reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port,
+ reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved,
+ reply_msg.hdr.msgh_id,
+ MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ reply_msg.hdr.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+
+ err = ::mach_msg ( &reply_msg.hdr,
+ MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ reply_msg.hdr.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+
+ if (err.Fail())
+ {
+ if (err.Error() == MACH_SEND_INTERRUPTED)
+ {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreaded("::mach_msg() - send interrupted");
+ // TODO: keep retrying to reply???
+ }
+ else
+ {
+ if (state.task_port == process->Task().TaskPort())
+ {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreaded("::mach_msg() - failed (task)");
+ abort ();
+ }
+ else
+ {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreaded("::mach_msg() - failed (child of task)");
+ }
+ }
+ }
+
+ return err.Error();
+}
+
+
+void
+MachException::Data::Dump() const
+{
+ const char *exc_type_name = MachException::Name(exc_type);
+ DNBLogThreadedIf(LOG_EXCEPTIONS, " state { task_port = 0x%4.4x, thread_port = 0x%4.4x, exc_type = %i (%s) ...", task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???");
+
+ const size_t exc_data_count = exc_data.size();
+ // Dump any special exception data contents
+ int soft_signal = SoftSignal();
+ if (soft_signal != 0)
+ {
+ const char *sig_str = SysSignal::Name(soft_signal);
+ DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data: EXC_SOFT_SIGNAL (%i (%s))", soft_signal, sig_str ? sig_str : "unknown signal");
+ }
+ else
+ {
+ // No special disassembly for this data, just dump the data
+ size_t idx;
+ for (idx = 0; idx < exc_data_count; ++idx)
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data[%u]: " MACH_EXCEPTION_DATA_FMT_HEX, idx, exc_data[idx]);
+ }
+ }
+}
+
+
+kern_return_t
+MachException::PortInfo::Save (task_t task)
+{
+ count = (sizeof (ports) / sizeof (ports[0]));
+ DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Save ( task = 0x%4.4x )", task);
+ DNBError err;
+ err = ::task_get_exception_ports (task, EXC_MASK_ALL, masks, &count, ports, behaviors, flavors);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, EXC_MASK_ALL, count);
+ if (err.Fail())
+ count = 0;
+ return err.Error();
+}
+
+kern_return_t
+MachException::PortInfo::Restore (task_t task)
+{
+ DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Restore( task = 0x%4.4x )", task);
+ uint32_t i = 0;
+ DNBError err;
+ if (count > 0)
+ {
+ for (i = 0; i < count; i++)
+ {
+ err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ {
+ err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, masks[i], ports[i], behaviors[i], flavors[i]);
+ // Bail if we encounter any errors
+ }
+
+ if (err.Fail())
+ break;
+ }
+ }
+ count = 0;
+ return err.Error();
+}
+
+const char *
+MachException::Name(exception_type_t exc_type)
+{
+ switch (exc_type)
+ {
+ case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS";
+ case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION";
+ case EXC_ARITHMETIC: return "EXC_ARITHMETIC";
+ case EXC_EMULATION: return "EXC_EMULATION";
+ case EXC_SOFTWARE: return "EXC_SOFTWARE";
+ case EXC_BREAKPOINT: return "EXC_BREAKPOINT";
+ case EXC_SYSCALL: return "EXC_SYSCALL";
+ case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL";
+ case EXC_RPC_ALERT: return "EXC_RPC_ALERT";
+#ifdef EXC_CRASH
+ case EXC_CRASH: return "EXC_CRASH";
+#endif
+ default:
+ break;
+ }
+ return NULL;
+}
+
+
+
diff --git a/lldb/tools/debugserver/source/MacOSX/MachException.h b/lldb/tools/debugserver/source/MacOSX/MachException.h
new file mode 100644
index 00000000000..5dc394bd55a
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachException.h
@@ -0,0 +1,147 @@
+//===-- MachException.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef __MachException_h__
+#define __MachException_h__
+
+#include <mach/mach.h>
+#include <vector>
+#include "DNBConfig.h"
+
+#ifdef HAVE_64_BIT_MACH_EXCEPTIONS
+
+#define MACH_EXCEPTION_DATA_FMT_DEC "%lld"
+#define MACH_EXCEPTION_DATA_FMT_HEX "0x%16.16llx"
+#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%llx"
+
+#else
+
+#define MACH_EXCEPTION_DATA_FMT_DEC "%d"
+#define MACH_EXCEPTION_DATA_FMT_HEX "0x%8.8x"
+#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%x"
+
+#endif
+
+class MachProcess;
+class PThreadMutex;
+
+typedef union MachMessageTag
+{
+ mach_msg_header_t hdr;
+ char data[1024];
+} MachMessage;
+
+
+class MachException
+{
+public:
+
+ struct PortInfo
+ {
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ mach_port_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+ mach_msg_type_number_t count;
+
+ kern_return_t Save(task_t task);
+ kern_return_t Restore(task_t task);
+ };
+
+ struct Data
+ {
+ task_t task_port;
+ thread_t thread_port;
+ exception_type_t exc_type;
+ std::vector<mach_exception_data_type_t> exc_data;
+ Data() :
+ task_port(TASK_NULL),
+ thread_port(THREAD_NULL),
+ exc_type(0),
+ exc_data()
+ {
+ }
+
+ void Clear()
+ {
+ task_port = TASK_NULL;
+ thread_port = THREAD_NULL;
+ exc_type = 0;
+ exc_data.clear();
+ }
+ bool IsValid() const
+ {
+ return task_port != TASK_NULL &&
+ thread_port != THREAD_NULL &&
+ exc_type != 0;
+ }
+ // Return the SoftSignal for this MachException data, or zero if there is none
+ int SoftSignal() const
+ {
+ if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL)
+ return exc_data[1];
+ return 0;
+ }
+ bool IsBreakpoint() const
+ {
+ return (exc_type == EXC_BREAKPOINT) || ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1);
+ }
+ void Dump() const;
+ void DumpStopReason() const;
+ bool GetStopInfo(struct DNBThreadStopInfo *stop_info) const;
+ };
+
+ struct Message
+ {
+ MachMessage exc_msg;
+ MachMessage reply_msg;
+ Data state;
+
+ Message() :
+ state()
+ {
+ memset(&exc_msg, 0, sizeof(exc_msg));
+ memset(&reply_msg, 0, sizeof(reply_msg));
+ }
+ bool CatchExceptionRaise();
+ void Dump() const;
+ kern_return_t Reply (MachProcess *process, int signal);
+ kern_return_t Receive( mach_port_t receive_port,
+ mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port = MACH_PORT_NULL);
+
+ typedef std::vector<Message> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ };
+
+ enum
+ {
+ e_actionForward, // Forward signal to inferior process
+ e_actionStop, // Stop when this signal is received
+ };
+ struct Action
+ {
+ task_t task_port; // Set to TASK_NULL for any TASK
+ thread_t thread_port; // Set to THREAD_NULL for any thread
+ exception_type_t exc_mask; // Mach exception mask to watch for
+ std::vector<mach_exception_data_type_t> exc_data_mask; // Mask to apply to exception data, or empty to ignore exc_data value for exception
+ std::vector<mach_exception_data_type_t> exc_data_value; // Value to compare to exception data after masking, or empty to ignore exc_data value for exception
+ uint8_t flags; // Action flags describing what to do with the exception
+ };
+ static const char *Name(exception_type_t exc_type);
+};
+
+#endif
diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp b/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp
new file mode 100644
index 00000000000..2bfc7603e2a
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp
@@ -0,0 +1,2008 @@
+//===-- MachProcess.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/15/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNB.h"
+#include <mach/mach.h>
+#include <spawn.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "MacOSX/CFUtils.h"
+#include "SysSignal.h"
+
+#include <algorithm>
+#include <map>
+
+#include "DNBDataRef.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "DNBTimer.h"
+#include "MachProcess.h"
+#include "PseudoTerminal.h"
+
+#include "CFBundle.h"
+#include "CFData.h"
+#include "CFString.h"
+
+static CFStringRef CopyBundleIDForPath (const char *app_buncle_path, DNBError &err_str);
+
+#if defined (__arm__)
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SpringBoardServices/SpringBoardServer.h>
+#include <SpringBoardServices/SBSWatchdogAssertion.h>
+
+
+static bool
+IsSBProcess (nub_process_t pid)
+{
+ bool opt_runningApps = true;
+ bool opt_debuggable = false;
+
+ CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable));
+ if (sbsAppIDs.get() != NULL)
+ {
+ CFIndex count = ::CFArrayGetCount (sbsAppIDs.get());
+ CFIndex i = 0;
+ for (i = 0; i < count; i++)
+ {
+ CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i);
+
+ // Get the process id for the app (if there is one)
+ pid_t sbs_pid = INVALID_NUB_PROCESS;
+ if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE)
+ {
+ if (sbs_pid == pid)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+#endif
+
+#if 0
+#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DEBUG_LOG(fmt, ...)
+#endif
+
+#ifndef MACH_PROCESS_USE_POSIX_SPAWN
+#define MACH_PROCESS_USE_POSIX_SPAWN 1
+#endif
+
+
+MachProcess::MachProcess() :
+ m_pid (0),
+ m_child_stdin (-1),
+ m_child_stdout (-1),
+ m_child_stderr (-1),
+ m_path (),
+ m_args (),
+ m_task (this),
+ m_flags (eMachProcessFlagsNone),
+ m_stdio_thread (0),
+ m_stdio_mutex (PTHREAD_MUTEX_RECURSIVE),
+ m_stdout_data (),
+ m_threadList (),
+ m_exception_messages (),
+ m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE),
+ m_err (KERN_SUCCESS),
+ m_state (eStateUnloaded),
+ m_state_mutex (PTHREAD_MUTEX_RECURSIVE),
+ m_events (0, kAllEventsMask),
+ m_breakpoints (),
+ m_watchpoints (),
+ m_name_to_addr_callback(NULL),
+ m_name_to_addr_baton(NULL),
+ m_image_infos_callback(NULL),
+ m_image_infos_baton(NULL)
+{
+ DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
+}
+
+MachProcess::~MachProcess()
+{
+ DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
+ Clear();
+}
+
+pid_t
+MachProcess::SetProcessID(pid_t pid)
+{
+ // Free any previous process specific data or resources
+ Clear();
+ // Set the current PID appropriately
+ if (pid == 0)
+ m_pid == ::getpid ();
+ else
+ m_pid = pid;
+ return m_pid; // Return actualy PID in case a zero pid was passed in
+}
+
+nub_state_t
+MachProcess::GetState()
+{
+ // If any other threads access this we will need a mutex for it
+ PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
+ return m_state;
+}
+
+const char *
+MachProcess::ThreadGetName(nub_thread_t tid)
+{
+ return m_threadList.GetName(tid);
+}
+
+nub_state_t
+MachProcess::ThreadGetState(nub_thread_t tid)
+{
+ return m_threadList.GetState(tid);
+}
+
+
+nub_size_t
+MachProcess::GetNumThreads () const
+{
+ return m_threadList.NumThreads();
+}
+
+nub_thread_t
+MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const
+{
+ return m_threadList.ThreadIDAtIndex(thread_idx);
+}
+
+uint32_t
+MachProcess::GetThreadIndexFromThreadID (nub_thread_t tid)
+{
+ return m_threadList.GetThreadIndexByID(tid);
+}
+
+nub_thread_t
+MachProcess::GetCurrentThread ()
+{
+ return m_threadList.CurrentThreadID();
+}
+
+nub_thread_t
+MachProcess::SetCurrentThread(nub_thread_t tid)
+{
+ return m_threadList.SetCurrentThread(tid);
+}
+
+bool
+MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const
+{
+ return m_threadList.GetThreadStoppedReason(tid, stop_info);
+}
+
+void
+MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const
+{
+ return m_threadList.DumpThreadStoppedReason(tid);
+}
+
+const char *
+MachProcess::GetThreadInfo(nub_thread_t tid) const
+{
+ return m_threadList.GetThreadInfo(tid);
+}
+
+const DNBRegisterSetInfo *
+MachProcess::GetRegisterSetInfo(nub_thread_t tid, nub_size_t *num_reg_sets ) const
+{
+ return DNBArch::GetRegisterSetInfo (num_reg_sets);
+}
+
+bool
+MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const
+{
+ return m_threadList.GetRegisterValue(tid, set, reg, value);
+}
+
+bool
+MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const
+{
+ return m_threadList.SetRegisterValue(tid, set, reg, value);
+}
+
+void
+MachProcess::SetState(nub_state_t new_state)
+{
+ // If any other threads access this we will need a mutex for it
+ uint32_t event_mask = 0;
+
+ // Scope for mutex locker
+ {
+ PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( %s )", DNBStateAsString(new_state));
+
+ const nub_state_t old_state = m_state;
+
+ if (old_state != new_state)
+ {
+ if (NUB_STATE_IS_STOPPED(new_state))
+ event_mask = eEventProcessStoppedStateChanged;
+ else
+ event_mask = eEventProcessRunningStateChanged;
+
+ m_state = new_state;
+ if (new_state == eStateStopped)
+ m_stop_count++;
+ }
+ }
+
+ if (event_mask != 0)
+ {
+ m_events.SetEvents (event_mask);
+
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(event_mask);
+ }
+
+}
+
+void
+MachProcess::Clear()
+{
+ // Clear any cached thread list while the pid and task are still valid
+
+ m_task.Clear();
+ // Now clear out all member variables
+ m_pid = INVALID_NUB_PROCESS;
+ CloseChildFileDescriptors();
+ m_path.clear();
+ m_args.clear();
+ SetState(eStateUnloaded);
+ m_flags = eMachProcessFlagsNone;
+ m_stop_count = 0;
+ m_threadList.Clear();
+ {
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+ m_exception_messages.clear();
+ }
+ m_err.Clear();
+
+}
+
+
+bool
+MachProcess::StartSTDIOThread()
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
+ // Create the thread that watches for the child STDIO
+ m_err = ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this);
+ return m_err.Success();
+}
+
+
+nub_addr_t
+MachProcess::LookupSymbol(const char *name, const char *shlib)
+{
+ if (m_name_to_addr_callback != NULL && name && name[0])
+ return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton);
+ return INVALID_NUB_ADDRESS;
+}
+
+bool
+MachProcess::Resume (const DNBThreadResumeActions& thread_actions)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()");
+ nub_state_t state = GetState();
+
+ if (CanResume(state))
+ {
+ PrivateResume(thread_actions);
+ }
+ else if (state == eStateRunning)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort());
+ m_err.Clear();
+
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort());
+ m_err.SetError(UINT_MAX, DNBError::Generic);
+ }
+ return m_err.Success();
+}
+
+bool
+MachProcess::Kill (const struct timespec *timeout_abstime)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()");
+ nub_state_t state = DoSIGSTOP(true);
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state));
+ errno = 0;
+ ::ptrace (PT_KILL, m_pid, 0, 0);
+ m_err.SetErrorToErrno();
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", m_err.Error(), m_err.AsString());
+ PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
+ return true;
+}
+
+bool
+MachProcess::Signal (int signal, const struct timespec *timeout_abstime)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime);
+ nub_state_t state = GetState();
+ if (::kill (ProcessID(), signal) == 0)
+ {
+ m_err.Clear();
+ // If we were running and we have a timeout, wait for the signal to stop
+ if (IsRunning(state) && timeout_abstime)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime);
+ m_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime);
+ state = GetState();
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state));
+ return !IsRunning (state);
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime);
+ }
+ else
+ {
+ m_err.SetError(errno, DNBError::POSIX);
+ m_err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal);
+ }
+ return m_err.Success();
+
+}
+
+nub_state_t
+MachProcess::DoSIGSTOP (bool clear_bps_and_wps)
+{
+ nub_state_t state = GetState();
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state));
+
+ if (!IsRunning(state))
+ {
+ if (clear_bps_and_wps)
+ {
+ DisableAllBreakpoints (true);
+ DisableAllWatchpoints (true);
+ clear_bps_and_wps = false;
+ }
+
+ // Resume our process
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state));
+ PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
+
+ // Reset the event that says we were indeed running
+ m_events.ResetEvents(eEventProcessRunningStateChanged);
+ state = GetState();
+ }
+
+ // We need to be stopped in order to be able to detach, so we need
+ // to send ourselves a SIGSTOP
+
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state));
+ struct timespec sigstop_timeout;
+ DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0);
+ Signal (SIGSTOP, &sigstop_timeout);
+ if (clear_bps_and_wps)
+ {
+ DisableAllBreakpoints (true);
+ DisableAllWatchpoints (true);
+ clear_bps_and_wps = false;
+ }
+ return GetState();
+}
+
+bool
+MachProcess::Detach()
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()");
+
+ nub_state_t state = DoSIGSTOP(true);
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state));
+
+
+ // Don't reply to our SIGSTOP exception, just make sure no threads
+ // are still suspended.
+ PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
+
+
+ // Detach from our process
+ errno = 0;
+ nub_process_t pid = m_pid;
+ ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
+ m_err.SetError (errno, DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_PROCESS) || m_err.Fail())
+ m_err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
+
+ // Resume our task
+ m_task.Resume();
+
+ SetState(eStateDetached);
+
+ // NULL our task out as we have already retored all exception ports
+ m_task.Clear();
+
+ // Clear out any notion of the process we once were
+ Clear();
+ return true;
+}
+
+
+nub_size_t
+MachProcess::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, uint8_t *buf) const
+{
+ nub_size_t bytes_removed = 0;
+ const DNBBreakpoint *bp;
+ nub_addr_t intersect_addr;
+ nub_size_t intersect_size;
+ nub_size_t opcode_offset;
+ nub_size_t idx;
+ for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx)
+ {
+ if (bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset))
+ {
+ assert(addr <= intersect_addr && intersect_addr < addr + size);
+ assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
+ assert(opcode_offset + intersect_size <= bp->ByteSize());
+ nub_size_t buf_offset = intersect_addr - addr;
+ ::memcpy(buf + buf_offset, bp->SavedOpcodeBytes() + opcode_offset, intersect_size);
+ }
+ }
+ return bytes_removed;
+}
+
+//----------------------------------------------------------------------
+// ReadMemory from the MachProcess level will always remove any software
+// breakpoints from the memory buffer before returning. If you wish to
+// read memory and see those traps, read from the MachTask
+// (m_task.ReadMemory()) as that version will give you what is actually
+// in inferior memory.
+//----------------------------------------------------------------------
+nub_size_t
+MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
+{
+ // We need to remove any current software traps (enabled software
+ // breakpoints) that we may have placed in our tasks memory.
+
+ // First just read the memory as is
+ nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf);
+
+ // Then place any opcodes that fall into this range back into the buffer
+ // before we return this to callers.
+ if (bytes_read > 0)
+ RemoveTrapsFromBuffer (addr, size, (uint8_t *)buf);
+ return bytes_read;
+}
+
+//----------------------------------------------------------------------
+// WriteMemory from the MachProcess level will always write memory around
+// any software breakpoints. Any software breakpoints will have their
+// opcodes modified if they are enabled. Any memory that doesn't overlap
+// with software breakpoints will be written to. If you wish to write to
+// inferior memory without this interference, then write to the MachTask
+// (m_task.WriteMemory()) as that version will always modify inferior
+// memory.
+//----------------------------------------------------------------------
+nub_size_t
+MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
+{
+ // We need to write any data that would go where any current software traps
+ // (enabled software breakpoints) any software traps (breakpoints) that we
+ // may have placed in our tasks memory.
+
+ std::map<nub_addr_t, DNBBreakpoint *> addr_to_bp_map;
+ DNBBreakpoint *bp;
+ nub_size_t idx;
+ for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx)
+ {
+ if (bp->IntersectsRange(addr, size, NULL, NULL, NULL))
+ addr_to_bp_map[bp->Address()] = bp;
+ }
+
+ // If we don't have any software breakpoints that are in this buffer, then
+ // we can just write memory and be done with it.
+ if (addr_to_bp_map.empty())
+ return m_task.WriteMemory(addr, size, buf);
+
+ // If we make it here, we have some breakpoints that overlap and we need
+ // to work around them.
+
+ nub_size_t bytes_written = 0;
+ nub_addr_t intersect_addr;
+ nub_size_t intersect_size;
+ nub_size_t opcode_offset;
+ const uint8_t *ubuf = (const uint8_t *)buf;
+ std::map<nub_addr_t, DNBBreakpoint *>::iterator pos, end = addr_to_bp_map.end();
+ for (pos = addr_to_bp_map.begin(); pos != end; ++pos)
+ {
+ bp = pos->second;
+
+ assert(bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset));
+ assert(addr <= intersect_addr && intersect_addr < addr + size);
+ assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
+ assert(opcode_offset + intersect_size <= bp->ByteSize());
+
+ // Check for bytes before this breakpoint
+ const nub_addr_t curr_addr = addr + bytes_written;
+ if (intersect_addr > curr_addr)
+ {
+ // There are some bytes before this breakpoint that we need to
+ // just write to memory
+ nub_size_t curr_size = intersect_addr - curr_addr;
+ nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written);
+ bytes_written += curr_bytes_written;
+ if (curr_bytes_written != curr_size)
+ {
+ // We weren't able to write all of the requested bytes, we
+ // are done looping and will return the number of bytes that
+ // we have written so far.
+ break;
+ }
+ }
+
+ // Now write any bytes that would cover up any software breakpoints
+ // directly into the breakpoint opcode buffer
+ ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size);
+ bytes_written += intersect_size;
+ }
+
+ // Write any remaining bytes after the last breakpoint if we have any left
+ if (bytes_written < size)
+ bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written);
+
+ return bytes_written;
+}
+
+
+void
+MachProcess::ReplyToAllExceptions (const DNBThreadResumeActions& thread_actions)
+{
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+ if (m_exception_messages.empty() == false)
+ {
+ MachException::Message::iterator pos;
+ MachException::Message::iterator begin = m_exception_messages.begin();
+ MachException::Message::iterator end = m_exception_messages.end();
+ for (pos = begin; pos != end; ++pos)
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %d...", std::distance(begin, pos));
+ int thread_reply_signal = 0;
+
+ const DNBThreadResumeAction *action = thread_actions.GetActionForThread (pos->state.thread_port, false);
+
+ if (action)
+ {
+ thread_reply_signal = action->signal;
+ if (thread_reply_signal)
+ thread_actions.SetSignalHandledForThread (pos->state.thread_port);
+ }
+
+ m_err = pos->Reply(this, thread_reply_signal);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ m_err.LogThreadedIfError("Error replying to exception");
+ }
+
+ // Erase all exception message as we should have used and replied
+ // to them all already.
+ m_exception_messages.clear();
+ }
+}
+void
+MachProcess::PrivateResume (const DNBThreadResumeActions& thread_actions)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
+
+ ReplyToAllExceptions (thread_actions);
+// bool stepOverBreakInstruction = step;
+
+ // Let the thread prepare to resume and see if any threads want us to
+ // step over a breakpoint instruction (ProcessWillResume will modify
+ // the value of stepOverBreakInstruction).
+ m_threadList.ProcessWillResume (this, thread_actions);
+
+ // Set our state accordingly
+ if (thread_actions.NumActionsWithState(eStateStepping))
+ SetState (eStateStepping);
+ else
+ SetState (eStateRunning);
+
+ // Now resume our task.
+ m_err = m_task.Resume();
+
+}
+
+nub_break_t
+MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware, thread_t tid)
+{
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, hardware, tid);
+ if (hardware && tid == INVALID_NUB_THREAD)
+ tid = GetCurrentThread();
+
+ DNBBreakpoint bp(addr, length, tid, hardware);
+ nub_break_t breakID = m_breakpoints.Add(bp);
+ if (EnableBreakpoint(breakID))
+ {
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%4.4x ) => %u", (uint64_t)addr, length, tid, breakID);
+ return breakID;
+ }
+ else
+ {
+ m_breakpoints.Remove(breakID);
+ }
+ // We failed to enable the breakpoint
+ return INVALID_NUB_BREAK_ID;
+}
+
+nub_watch_t
+MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware, thread_t tid)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, flags = 0x%8.8x, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, watch_flags, hardware, tid);
+ if (hardware && tid == INVALID_NUB_THREAD)
+ tid = GetCurrentThread();
+
+ DNBBreakpoint watch(addr, length, tid, hardware);
+ watch.SetIsWatchpoint(watch_flags);
+
+ nub_watch_t watchID = m_watchpoints.Add(watch);
+ if (EnableWatchpoint(watchID))
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => %u", (uint64_t)addr, length, tid, watchID);
+ return watchID;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => FAILED (%u)", (uint64_t)addr, length, tid, watchID);
+ m_watchpoints.Remove(watchID);
+ }
+ // We failed to enable the watchpoint
+ return INVALID_NUB_BREAK_ID;
+}
+
+nub_size_t
+MachProcess::DisableAllBreakpoints(bool remove)
+{
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
+ DNBBreakpoint *bp;
+ nub_size_t disabled_count = 0;
+ nub_size_t idx = 0;
+ while ((bp = m_breakpoints.GetByIndex(idx)) != NULL)
+ {
+ bool success = DisableBreakpoint(bp->GetID(), remove);
+
+ if (success)
+ disabled_count++;
+ // If we failed to disable the breakpoint or we aren't removing the breakpoint
+ // increment the breakpoint index. Otherwise DisableBreakpoint will have removed
+ // the breakpoint at this index and we don't need to change it.
+ if ((success == false) || (remove == false))
+ idx++;
+ }
+ return disabled_count;
+}
+
+nub_size_t
+MachProcess::DisableAllWatchpoints(bool remove)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
+ DNBBreakpoint *wp;
+ nub_size_t disabled_count = 0;
+ nub_size_t idx = 0;
+ while ((wp = m_watchpoints.GetByIndex(idx)) != NULL)
+ {
+ bool success = DisableWatchpoint(wp->GetID(), remove);
+
+ if (success)
+ disabled_count++;
+ // If we failed to disable the watchpoint or we aren't removing the watchpoint
+ // increment the watchpoint index. Otherwise DisableWatchpoint will have removed
+ // the watchpoint at this index and we don't need to change it.
+ if ((success == false) || (remove == false))
+ idx++;
+ }
+ return disabled_count;
+}
+
+bool
+MachProcess::DisableBreakpoint(nub_break_t breakID, bool remove)
+{
+ DNBBreakpoint *bp = m_breakpoints.FindByID (breakID);
+ if (bp)
+ {
+ nub_addr_t addr = bp->Address();
+ DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx", breakID, remove, (uint64_t)addr);
+
+ if (bp->IsHardware())
+ {
+ bool hw_disable_result = m_threadList.DisableHardwareBreakpoint (bp);
+
+ if (hw_disable_result == true)
+ {
+ bp->SetEnabled(false);
+ // Let the thread list know that a breakpoint has been modified
+ if (remove)
+ {
+ m_threadList.NotifyBreakpointChanged(bp);
+ m_breakpoints.Remove(breakID);
+ }
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", breakID, remove, (uint64_t)addr);
+ return true;
+ }
+
+ return false;
+ }
+
+ const nub_size_t break_op_size = bp->ByteSize();
+ assert (break_op_size > 0);
+ const uint8_t * const break_op = DNBArch::SoftwareBreakpointOpcode(bp->ByteSize());
+ if (break_op_size > 0)
+ {
+ // Clear a software breakoint instruction
+ uint8_t curr_break_op[break_op_size];
+ bool break_op_found = false;
+
+ // Read the breakpoint opcode
+ if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size)
+ {
+ bool verify = false;
+ if (bp->IsEnabled())
+ {
+ // Make sure we have the a breakpoint opcode exists at this address
+ if (memcmp(curr_break_op, break_op, break_op_size) == 0)
+ {
+ break_op_found = true;
+ // We found a valid breakpoint opcode at this address, now restore
+ // the saved opcode.
+ if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
+ {
+ verify = true;
+ }
+ else
+ {
+ DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx memory write failed when restoring original opcode", breakID, remove, (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogWarning("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx expected a breakpoint opcode but didn't find one.", breakID, remove, (uint64_t)addr);
+ // Set verify to true and so we can check if the original opcode has already been restored
+ verify = true;
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x$8.8llx is not enabled", breakID, remove, (uint64_t)addr);
+ // Set verify to true and so we can check if the original opcode is there
+ verify = true;
+ }
+
+ if (verify)
+ {
+ uint8_t verify_opcode[break_op_size];
+ // Verify that our original opcode made it back to the inferior
+ if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size)
+ {
+ // compare the memory we just read with the original opcode
+ if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0)
+ {
+ // SUCCESS
+ bp->SetEnabled(false);
+ // Let the thread list know that a breakpoint has been modified
+ if (remove)
+ {
+ m_threadList.NotifyBreakpointChanged(bp);
+ m_breakpoints.Remove(breakID);
+ }
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx => success", breakID, remove, (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ if (break_op_found)
+ DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: failed to restore original opcode", breakID, remove, (uint64_t)addr);
+ else
+ DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: opcode changed", breakID, remove, (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr);
+ }
+ }
+ }
+ else
+ {
+ DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr);
+ }
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) invalid breakpoint ID", breakID, remove);
+ }
+ return false;
+}
+
+bool
+MachProcess::DisableWatchpoint(nub_watch_t watchID, bool remove)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(watchID = %d, remove = %d)", __FUNCTION__, watchID, remove);
+ DNBBreakpoint *wp = m_watchpoints.FindByID (watchID);
+ if (wp)
+ {
+ nub_addr_t addr = wp->Address();
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx", watchID, remove, (uint64_t)addr);
+
+ if (wp->IsHardware())
+ {
+ bool hw_disable_result = m_threadList.DisableHardwareWatchpoint (wp);
+
+ if (hw_disable_result == true)
+ {
+ wp->SetEnabled(false);
+ if (remove)
+ m_watchpoints.Remove(watchID);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", watchID, remove, (uint64_t)addr);
+ return true;
+ }
+ }
+
+ // TODO: clear software watchpoints if we implement them
+ }
+ else
+ {
+ DNBLogError("MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) invalid watchpoint ID", watchID, remove);
+ }
+ return false;
+}
+
+
+void
+MachProcess::DumpBreakpoint(nub_break_t breakID) const
+{
+ DNBLogThreaded("MachProcess::DumpBreakpoint(breakID = %d)", breakID);
+
+ if (NUB_BREAK_ID_IS_VALID(breakID))
+ {
+ const DNBBreakpoint *bp = m_breakpoints.FindByID(breakID);
+ if (bp)
+ bp->Dump();
+ else
+ DNBLog("MachProcess::DumpBreakpoint(breakID = %d): invalid breakID", breakID);
+ }
+ else
+ {
+ m_breakpoints.Dump();
+ }
+}
+
+void
+MachProcess::DumpWatchpoint(nub_watch_t watchID) const
+{
+ DNBLogThreaded("MachProcess::DumpWatchpoint(watchID = %d)", watchID);
+
+ if (NUB_BREAK_ID_IS_VALID(watchID))
+ {
+ const DNBBreakpoint *wp = m_watchpoints.FindByID(watchID);
+ if (wp)
+ wp->Dump();
+ else
+ DNBLog("MachProcess::DumpWatchpoint(watchID = %d): invalid watchID", watchID);
+ }
+ else
+ {
+ m_watchpoints.Dump();
+ }
+}
+
+bool
+MachProcess::EnableBreakpoint(nub_break_t breakID)
+{
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d )", breakID);
+ DNBBreakpoint *bp = m_breakpoints.FindByID (breakID);
+ if (bp)
+ {
+ nub_addr_t addr = bp->Address();
+ if (bp->IsEnabled())
+ {
+ DNBLogWarning("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint already enabled.", breakID, (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ if (bp->HardwarePreferred())
+ {
+ bp->SetHardwareIndex(m_threadList.EnableHardwareBreakpoint(bp));
+ if (bp->IsHardware())
+ {
+ bp->SetEnabled(true);
+ return true;
+ }
+ }
+
+ const nub_size_t break_op_size = bp->ByteSize();
+ assert (break_op_size != 0);
+ const uint8_t * const break_op = DNBArch::SoftwareBreakpointOpcode(break_op_size);
+ if (break_op_size > 0)
+ {
+ // Save the original opcode by reading it
+ if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
+ {
+ // Write a software breakpoint in place of the original opcode
+ if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size)
+ {
+ uint8_t verify_break_op[4];
+ if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size)
+ {
+ if (memcmp(break_op, verify_break_op, break_op_size) == 0)
+ {
+ bp->SetEnabled(true);
+ // Let the thread list know that a breakpoint has been modified
+ m_threadList.NotifyBreakpointChanged(bp);
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: SUCCESS.", breakID, (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint opcode verification failed.", breakID, (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory to verify breakpoint opcode.", breakID, (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to write breakpoint opcode to memory.", breakID, (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory at breakpoint address.", breakID, (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) no software breakpoint opcode for current architecture.", breakID);
+ }
+ }
+ }
+ return false;
+}
+
+bool
+MachProcess::EnableWatchpoint(nub_watch_t watchID)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(watchID = %d)", watchID);
+ DNBBreakpoint *wp = m_watchpoints.FindByID (watchID);
+ if (wp)
+ {
+ nub_addr_t addr = wp->Address();
+ if (wp->IsEnabled())
+ {
+ DNBLogWarning("MachProcess::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ // Currently only try and set hardware watchpoints.
+ wp->SetHardwareIndex(m_threadList.EnableHardwareWatchpoint(wp));
+ if (wp->IsHardware())
+ {
+ wp->SetEnabled(true);
+ return true;
+ }
+ // TODO: Add software watchpoints by doing page protection tricks.
+ }
+ }
+ return false;
+}
+
+// Called by the exception thread when an exception has been received from
+// our process. The exception message is completely filled and the exception
+// data has already been copied.
+void
+MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
+
+ if (m_exception_messages.empty())
+ m_task.Suspend();
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )");
+
+ // Use a locker to automatically unlock our mutex in case of exceptions
+ // Add the exception to our internal exception stack
+ m_exception_messages.push_back(exceptionMessage);
+}
+
+void
+MachProcess::ExceptionMessageBundleComplete()
+{
+ // We have a complete bundle of exceptions for our child process.
+ PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size());
+ if (!m_exception_messages.empty())
+ {
+ // Let all threads recover from stopping and do any clean up based
+ // on the previous thread state (if any).
+ m_threadList.ProcessDidStop(this);
+
+ // Let each thread know of any exceptions
+ task_t task = m_task.TaskPort();
+ size_t i;
+ for (i=0; i<m_exception_messages.size(); ++i)
+ {
+ // Let the thread list figure use the MachProcess to forward all exceptions
+ // on down to each thread.
+ if (m_exception_messages[i].state.task_port == task)
+ m_threadList.NotifyException(m_exception_messages[i].state);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ m_exception_messages[i].Dump();
+ }
+
+ if (DNBLogCheckLogBit(LOG_THREAD))
+ m_threadList.Dump();
+
+ bool step_more = false;
+ if (m_threadList.ShouldStop(step_more))
+ {
+ // Wait for the eEventProcessRunningStateChanged event to be reset
+ // before changing state to stopped to avoid race condition with
+ // very fast start/stops
+ struct timespec timeout;
+ //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 ms
+ DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms
+ m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout);
+ SetState(eStateStopped);
+ }
+ else
+ {
+ // Resume without checking our current state.
+ PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size());
+ }
+}
+
+nub_size_t
+MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed)
+{
+ if (m_image_infos_callback != NULL)
+ return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton);
+ return 0;
+}
+
+void
+MachProcess::SharedLibrariesUpdated ( )
+{
+ uint32_t event_bits = eEventSharedLibsStateChange;
+ // Set the shared library event bit to let clients know of shared library
+ // changes
+ m_events.SetEvents(event_bits);
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(event_bits);
+}
+
+void
+MachProcess::AppendSTDOUT (char* s, size_t len)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%d> %s) ...", __FUNCTION__, len, s);
+ PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
+ m_stdout_data.append(s, len);
+ m_events.SetEvents(eEventStdioAvailable);
+
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(eEventStdioAvailable);
+}
+
+size_t
+MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size);
+ PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
+ size_t bytes_available = m_stdout_data.size();
+ if (bytes_available > 0)
+ {
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, m_stdout_data.data(), buf_size);
+ m_stdout_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, m_stdout_data.data(), bytes_available);
+ m_stdout_data.clear();
+ }
+ }
+ return bytes_available;
+}
+
+nub_addr_t
+MachProcess::GetDYLDAllImageInfosAddress ()
+{
+ return m_task.GetDYLDAllImageInfosAddress(m_err);
+}
+
+size_t
+MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size)
+{
+ return 0;
+}
+
+void *
+MachProcess::STDIOThread(void *arg)
+{
+ MachProcess *proc = (MachProcess*) arg;
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg);
+
+ // We start use a base and more options so we can control if we
+ // are currently using a timeout on the mach_msg. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main thread loop
+ // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle avaiable. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ DNBError err;
+ int stdout_fd = proc->GetStdoutFileDescriptor();
+ int stderr_fd = proc->GetStderrFileDescriptor();
+ if (stdout_fd == stderr_fd)
+ stderr_fd = -1;
+
+ while (stdout_fd >= 0 || stderr_fd >= 0)
+ {
+ ::pthread_testcancel ();
+
+ fd_set read_fds;
+ FD_ZERO (&read_fds);
+ if (stdout_fd >= 0)
+ FD_SET (stdout_fd, &read_fds);
+ if (stderr_fd >= 0)
+ FD_SET (stderr_fd, &read_fds);
+ int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
+
+ int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL);
+ DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+
+ if (num_set_fds < 0)
+ {
+ int select_errno = errno;
+ if (DNBLogCheckLogBit(LOG_PROCESS))
+ {
+ err.SetError (select_errno, DNBError::POSIX);
+ err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+ }
+
+ switch (select_errno)
+ {
+ case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO
+ break;
+ case EBADF: // One of the descriptor sets specified an invalid descriptor.
+ return NULL;
+ break;
+ case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred.
+ case EINVAL: // The specified time limit is invalid. One of its components is negative or too large.
+ default: // Other unknown error
+ break;
+ }
+ }
+ else if (num_set_fds == 0)
+ {
+ }
+ else
+ {
+ char s[1024];
+ s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination
+ int bytes_read = 0;
+ if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds))
+ {
+ do
+ {
+ bytes_read = ::read (stdout_fd, s, sizeof(s)-1);
+ if (bytes_read < 0)
+ {
+ int read_errno = errno;
+ DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
+ }
+ else if (bytes_read == 0)
+ {
+ // EOF...
+ DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read);
+ stdout_fd = -1;
+ }
+ else if (bytes_read > 0)
+ {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+
+ if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds))
+ {
+ do
+ {
+ bytes_read = ::read (stderr_fd, s, sizeof(s)-1);
+ if (bytes_read < 0)
+ {
+ int read_errno = errno;
+ DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
+ }
+ else if (bytes_read == 0)
+ {
+ // EOF...
+ DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read);
+ stderr_fd = -1;
+ }
+ else if (bytes_read > 0)
+ {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+ }
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg);
+ return NULL;
+}
+
+pid_t
+MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len)
+{
+ // Clear out and clean up from any current state
+ Clear();
+ if (pid != 0)
+ {
+ // Make sure the process exists...
+ if (::getpgid (pid) < 0)
+ {
+ m_err.SetErrorToErrno();
+ const char *err_cstr = m_err.AsString();
+ ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process");
+ return INVALID_NUB_PROCESS;
+ }
+
+ SetState(eStateAttaching);
+ m_pid = pid;
+ // Let ourselves know we are going to be using SBS if the correct flag bit is set...
+#if defined (__arm__)
+ if (IsSBProcess(pid))
+ m_flags |= eMachProcessFlagsUsingSBS;
+#endif
+ if (!m_task.StartExceptionThread(m_err))
+ {
+ const char *err_cstr = m_err.AsString();
+ ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread");
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ errno = 0;
+ int err = ptrace (PT_ATTACHEXC, pid, 0, 0);
+
+ if (err < 0)
+ m_err.SetError(errno);
+ else
+ m_err.Clear();
+
+ if (m_err.Success())
+ {
+ m_flags |= eMachProcessFlagsAttached;
+ // Sleep a bit to let the exception get received and set our process status
+ // to stopped.
+ ::usleep(250000);
+ DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid);
+ return m_pid;
+ }
+ else
+ {
+ ::snprintf (err_str, err_len, "%s", m_err.AsString());
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
+ }
+ }
+ return INVALID_NUB_PROCESS;
+}
+
+// Do the process specific setup for attach. If this returns NULL, then there's no
+// platform specific stuff to be done to wait for the attach. If you get non-null,
+// pass that token to the CheckForProcess method, and then to CleanupAfterAttach.
+
+// Call PrepareForAttach before attaching to a process that has not yet launched
+// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach.
+// You should call CleanupAfterAttach to free the token, and do whatever other
+// cleanup seems good.
+
+const void *
+MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str)
+{
+#if defined (__arm__)
+ // Tell SpringBoard to halt the next launch of this application on startup.
+
+ if (!waitfor)
+ return NULL;
+
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext == NULL)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "%s: path '%s' doesn't contain .app, we can't tell springboard to wait for launch...", path);
+ return NULL;
+ }
+
+ if (launch_flavor != eLaunchFlavorSpringBoard
+ && launch_flavor != eLaunchFlavorDefault)
+ return NULL;
+
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+
+ CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), err_str);
+ std::string bundleIDStr;
+ CFString::UTF8(bundleIDCFStr, bundleIDStr);
+ DNBLogThreadedIf(LOG_PROCESS, "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", app_bundle_path.c_str (), bundleIDStr.c_str());
+
+ if (bundleIDCFStr == NULL)
+ {
+ return NULL;
+ }
+
+ SBSApplicationLaunchError sbs_error = 0;
+
+ const char *stdout_err = "/dev/null";
+ CFString stdio_path;
+ stdio_path.SetFileSystemRepresentation (stdout_err);
+
+ DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )", bundleIDStr.c_str(), stdout_err, stdout_err);
+ sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
+ (CFURLRef)NULL, // openURL
+ NULL, // launch_argv.get(),
+ NULL, // launch_envp.get(), // CFDictionaryRef environment
+ stdio_path.get(),
+ stdio_path.get(),
+ SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger);
+
+ if (sbs_error != SBSApplicationLaunchErrorSuccess)
+ {
+ err_str.SetError(sbs_error, DNBError::SpringBoard);
+ return NULL;
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch.");
+ return bundleIDCFStr;
+# else
+ return NULL;
+#endif
+}
+
+// Pass in the token you got from PrepareForAttach. If there is a process
+// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS
+// will be returned.
+
+nub_process_t
+MachProcess::CheckForProcess (const void *attach_token)
+{
+ if (attach_token == NULL)
+ return INVALID_NUB_PROCESS;
+
+#if defined (__arm__)
+ CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
+ Boolean got_it;
+ nub_process_t attach_pid;
+ got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid);
+ if (got_it)
+ return attach_pid;
+ else
+ return INVALID_NUB_PROCESS;
+#endif
+ return INVALID_NUB_PROCESS;
+}
+
+// Call this to clean up after you have either attached or given up on the attach.
+// Pass true for success if you have attached, false if you have not.
+// The token will also be freed at this point, so you can't use it after calling
+// this method.
+
+void
+MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str)
+{
+#if defined (__arm__)
+ if (attach_token == NULL)
+ return;
+
+ // Tell SpringBoard to cancel the debug on next launch of this application
+ // if we failed to attach
+ if (!success)
+ {
+ SBSApplicationLaunchError sbs_error = 0;
+ CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
+
+ sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
+ (CFURLRef)NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ SBSApplicationCancelDebugOnNextLaunch);
+
+ if (sbs_error != SBSApplicationLaunchErrorSuccess)
+ {
+ err_str.SetError(sbs_error, DNBError::SpringBoard);
+ return;
+ }
+ }
+
+ CFRelease((CFStringRef) attach_token);
+#endif
+}
+
+pid_t
+MachProcess::LaunchForDebug
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ const char *stdio_path,
+ nub_launch_flavor_t launch_flavor,
+ DNBError &launch_err
+)
+{
+ // Clear out and clean up from any current state
+ Clear();
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u )", __FUNCTION__, path, argv, envp, launch_flavor);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+
+ switch (launch_flavor)
+ {
+ case eLaunchFlavorForkExec:
+ m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err);
+ break;
+
+ case eLaunchFlavorPosixSpawn:
+ m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, argv, envp, stdio_path, this, launch_err);
+ break;
+
+#if defined (__arm__)
+
+ case eLaunchFlavorSpringBoard:
+ {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext != NULL)
+ {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, launch_err);
+ }
+ }
+ break;
+
+#endif
+
+ default:
+ // Invalid launch
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ return INVALID_NUB_PROCESS;
+ }
+
+ if (m_pid == INVALID_NUB_PROCESS)
+ {
+ // If we don't have a valid process ID and no one has set the error,
+ // then return a generic error
+ if (launch_err.Success())
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ }
+ else
+ {
+ m_path = path;
+ size_t i;
+ char const *arg;
+ for (i=0; (arg = argv[i]) != NULL; i++)
+ m_args.push_back(arg);
+
+ m_task.StartExceptionThread(m_err);
+ if (m_err.Fail())
+ {
+ if (m_err.AsString() == NULL)
+ m_err.SetErrorString("unable to start the exception thread");
+ ::ptrace (PT_KILL, m_pid, 0, 0);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ StartSTDIOThread();
+
+ if (launch_flavor == eLaunchFlavorPosixSpawn)
+ {
+
+ SetState (eStateAttaching);
+ errno = 0;
+ int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0)
+ {
+ m_flags |= eMachProcessFlagsAttached;
+ DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid);
+ launch_err.Clear();
+ }
+ else
+ {
+ SetState (eStateExited);
+ DNBError ptrace_err(errno, DNBError::POSIX);
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid %d (err = %i, errno = %i (%s))", m_pid, err, ptrace_err.Error(), ptrace_err.AsString());
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ }
+ }
+ else
+ {
+ launch_err.Clear();
+ }
+ }
+ return m_pid;
+}
+
+pid_t
+MachProcess::PosixSpawnChildForPTraceDebugging
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ const char *stdio_path,
+ MachProcess* process,
+ DNBError& err
+)
+{
+ posix_spawnattr_t attr;
+ DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp);
+
+ err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnattr_init ( &attr )");
+ if (err.Fail())
+ return INVALID_NUB_PROCESS;
+
+ err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )");
+ if (err.Fail())
+ return INVALID_NUB_PROCESS;
+
+ // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail
+ // and we will fail to continue with our process...
+
+ // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment....
+
+//#ifndef _POSIX_SPAWN_DISABLE_ASLR
+//#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+//#endif
+// err.SetError( ::posix_spawnattr_setflags (&attr, _POSIX_SPAWN_DISABLE_ASLR), DNBError::POSIX);
+// if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+// err.LogThreaded("::posix_spawnattr_setflags ( &attr, _POSIX_SPAWN_DISABLE_ASLR )");
+
+#if !defined(__arm__)
+
+ // We don't need to do this for ARM, and we really shouldn't now that we
+ // have multiple CPU subtypes and no posix_spawnattr call that allows us
+ // to set which CPU subtype to launch...
+ cpu_type_t cpu_type = DNBArch::GetCPUType();
+ size_t ocount = 0;
+ err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu_type, ocount);
+
+ if (err.Fail() != 0 || ocount != 1)
+ return INVALID_NUB_PROCESS;
+
+#endif
+
+ PseudoTerminal pty;
+
+ posix_spawn_file_actions_t file_actions;
+ err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX);
+ int file_actions_valid = err.Success();
+ if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )");
+ int pty_error = -1;
+ pid_t pid = INVALID_NUB_PROCESS;
+ if (file_actions_valid)
+ {
+ if (stdio_path == NULL)
+ {
+ pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
+ if (pty_error == PseudoTerminal::success)
+ stdio_path = pty.SlaveName();
+ // Make sure we were able to get the slave name
+ if (stdio_path == NULL)
+ stdio_path = "/dev/null";
+ }
+
+ if (stdio_path != NULL)
+ {
+ err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stdio_path, O_RDWR, 0), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stdio_path);
+
+ err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdio_path, O_RDONLY, 0), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdio_path);
+
+ err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdio_path, O_WRONLY, 0), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdio_path);
+ }
+ err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp);
+ }
+ else
+ {
+ err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp);
+ }
+
+ // We have seen some cases where posix_spawnp was returning a valid
+ // looking pid even when an error was returned, so clear it out
+ if (err.Fail())
+ pid = INVALID_NUB_PROCESS;
+
+ if (pty_error == 0)
+ {
+ if (process != NULL)
+ {
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ }
+
+ if (file_actions_valid)
+ {
+ DNBError err2;
+ err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX);
+ if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )");
+ }
+
+ return pid;
+}
+
+pid_t
+MachProcess::ForkChildForPTraceDebugging
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ MachProcess* process,
+ DNBError& launch_err
+)
+{
+ PseudoTerminal::Error pty_error = PseudoTerminal::success;
+
+ // Use a fork that ties the child process's stdin/out/err to a pseudo
+ // terminal so we can read it in our MachProcess::STDIOThread
+ // as unbuffered io.
+ PseudoTerminal pty;
+ pid_t pid = pty.Fork(pty_error);
+
+ if (pid < 0)
+ {
+ //--------------------------------------------------------------
+ // Error during fork.
+ //--------------------------------------------------------------
+ return pid;
+ }
+ else if (pid == 0)
+ {
+ //--------------------------------------------------------------
+ // Child process
+ //--------------------------------------------------------------
+ ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process
+ ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions
+
+ // If our parent is setgid, lets make sure we don't inherit those
+ // extra powers due to nepotism.
+ ::setgid (getgid ());
+
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid (0, 0); // Set the child process group to match its pid
+
+ // Sleep a bit to before the exec call
+ ::sleep (1);
+
+ // Turn this process into
+ ::execv (path, (char * const *)argv);
+ // Exit with error code. Child process should have taken
+ // over in above exec call and if the exec fails it will
+ // exit the child process below.
+ ::exit (127);
+ }
+ else
+ {
+ //--------------------------------------------------------------
+ // Parent process
+ //--------------------------------------------------------------
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid (pid, pid); // Set the child process group to match its pid
+
+ if (process != NULL)
+ {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ }
+ return pid;
+}
+
+#if defined (__arm__)
+
+pid_t
+MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], DNBError &launch_err)
+{
+ // Clear out and clean up from any current state
+ Clear();
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+ m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, this, launch_err);
+ if (m_pid != 0)
+ {
+ m_flags |= eMachProcessFlagsUsingSBS;
+ m_path = path;
+ size_t i;
+ char const *arg;
+ for (i=0; (arg = argv[i]) != NULL; i++)
+ m_args.push_back(arg);
+ m_task.StartExceptionThread();
+ StartSTDIOThread();
+ SetState (eStateAttaching);
+ int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0)
+ {
+ m_flags |= eMachProcessFlagsAttached;
+ DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
+ }
+ else
+ {
+ SetState (eStateExited);
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
+ }
+ }
+ return m_pid;
+}
+
+#include <servers/bootstrap.h>
+
+// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
+// or NULL if there was some problem getting the bundle id.
+static CFStringRef
+CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str)
+{
+ CFBundle bundle(app_bundle_path);
+ CFStringRef bundleIDCFStr = bundle.GetIdentifier();
+ std::string bundleID;
+ if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
+ {
+ struct stat app_bundle_stat;
+ char err_msg[PATH_MAX];
+
+ if (::stat (app_bundle_path, &app_bundle_stat) < 0)
+ {
+ err_str.SetError(errno, DNBError::POSIX);
+ snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path);
+ err_str.SetErrorString(err_msg);
+ DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
+ }
+ else
+ {
+ err_str.SetError(-1, DNBError::Generic);
+ snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path);
+ err_str.SetErrorString(err_msg);
+ DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path);
+ }
+ return NULL;
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
+ CFRetain (bundleIDCFStr);
+
+ return bundleIDCFStr;
+}
+
+pid_t
+MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], MachProcess* process, DNBError &launch_err)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process);
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+
+ if (argv[0] == NULL)
+ return INVALID_NUB_PROCESS;
+
+ size_t argc = 0;
+ // Count the number of arguments
+ while (argv[argc] != NULL)
+ argc++;
+
+ // Enumerate the arguments
+ size_t first_launch_arg_idx = 1;
+ CFReleaser<CFMutableArrayRef> launch_argv;
+
+ if (argv[first_launch_arg_idx])
+ {
+ size_t launch_argc = argc > 0 ? argc - 1 : 0;
+ launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks));
+ size_t i;
+ char const *arg;
+ CFString launch_arg;
+ for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
+ {
+ launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8));
+ if (launch_arg.get() != NULL)
+ CFArrayAppendValue(launch_argv.get(), launch_arg.get());
+ else
+ break;
+ }
+ }
+
+ // Next fill in the arguments dictionary. Note, the envp array is of the form
+ // Variable=value but SpringBoard wants a CF dictionary. So we have to convert
+ // this here.
+
+ CFReleaser<CFMutableDictionaryRef> launch_envp;
+
+ if (envp[0])
+ {
+ launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ const char *value;
+ int name_len;
+ CFString name_string, value_string;
+
+ for (int i = 0; envp[i] != NULL; i++)
+ {
+ value = strstr (envp[i], "=");
+
+ // If the name field is empty or there's no =, skip it. Somebody's messing with us.
+ if (value == NULL || value == envp[i])
+ continue;
+
+ name_len = value - envp[i];
+
+ // Now move value over the "="
+ value++;
+
+ name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false));
+ value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
+ CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get());
+ }
+ }
+
+ CFString stdio_path;
+
+ PseudoTerminal pty;
+ PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
+ if (pty_err == PseudoTerminal::success)
+ {
+ const char* slave_name = pty.SlaveName();
+ DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
+ if (slave_name && slave_name[0])
+ {
+ ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
+ stdio_path.SetFileSystemRepresentation (slave_name);
+ }
+ }
+
+ if (stdio_path.get() == NULL)
+ {
+ stdio_path.SetFileSystemRepresentation ("/dev/null");
+ }
+
+ CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err);
+ if (bundleIDCFStr == NULL)
+ return INVALID_NUB_PROCESS;
+
+ std::string bundleID;
+ CFString::UTF8(bundleIDCFStr, bundleID);
+
+ CFData argv_data(NULL);
+
+ if (launch_argv.get())
+ {
+ if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__);
+ return INVALID_NUB_PROCESS;
+ }
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__);
+
+ // Find SpringBoard
+ SBSApplicationLaunchError sbs_error = 0;
+ sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
+ (CFURLRef)NULL, // openURL
+ launch_argv.get(),
+ launch_envp.get(), // CFDictionaryRef environment
+ stdio_path.get(),
+ stdio_path.get(),
+ SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
+
+
+ launch_err.SetError(sbs_error, DNBError::SpringBoard);
+
+ if (sbs_error == SBSApplicationLaunchErrorSuccess)
+ {
+ static const useconds_t pid_poll_interval = 200000;
+ static const useconds_t pid_poll_timeout = 30000000;
+
+ useconds_t pid_poll_total = 0;
+
+ nub_process_t pid = INVALID_NUB_PROCESS;
+ Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired
+ // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started
+ // yet, or that it died very quickly (if you weren't using waitForDebugger).
+ while (!pid_found && pid_poll_total < pid_poll_timeout)
+ {
+ usleep (pid_poll_interval);
+ pid_poll_total += pid_poll_interval;
+ DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str());
+ pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ }
+
+ CFRelease (bundleIDCFStr);
+ if (pid_found)
+ {
+ if (process != NULL)
+ {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
+ }
+ else
+ {
+ DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str());
+ }
+ return pid;
+ }
+
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error);
+ return INVALID_NUB_PROCESS;
+}
+
+#endif // #if defined (__arm__)
+
+
diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h
new file mode 100644
index 00000000000..4d5d0d4af92
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h
@@ -0,0 +1,263 @@
+//===-- MachProcess.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/15/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachProcess_h__
+#define __MachProcess_h__
+
+#include "DNBDefs.h"
+#include "DNBBreakpoint.h"
+#include "DNBError.h"
+//#include "MachDYLD.h"
+#include "MachException.h"
+#include "MachVMMemory.h"
+#include "MachTask.h"
+#include "MachThreadList.h"
+#include "PThreadCondition.h"
+#include "PThreadEvent.h"
+#include "PThreadMutex.h"
+
+#include <mach/mach.h>
+#include <sys/signal.h>
+#include <pthread.h>
+#include <vector>
+
+class DNBThreadResumeActions;
+
+class MachProcess
+{
+public:
+ //----------------------------------------------------------------------
+ // Constructors and Destructors
+ //----------------------------------------------------------------------
+ MachProcess ();
+ ~MachProcess ();
+
+ //----------------------------------------------------------------------
+ // Child process control
+ //----------------------------------------------------------------------
+ pid_t AttachForDebug (pid_t pid, char *err_str, size_t err_len);
+ pid_t LaunchForDebug (const char *path, char const *argv[], char const *envp[], const char *stdio_path, nub_launch_flavor_t launch_flavor, DNBError &err);
+ static pid_t ForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], MachProcess* process, DNBError &err);
+ static pid_t PosixSpawnChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], const char *stdio_path, MachProcess* process, DNBError& err);
+ nub_addr_t GetDYLDAllImageInfosAddress ();
+ static const void * PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str);
+ static void CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str);
+ static nub_process_t CheckForProcess (const void *attach_token);
+#if defined (__arm__)
+ pid_t SBLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], DNBError &launch_err);
+ static pid_t SBForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], MachProcess* process, DNBError &launch_err);
+#endif
+ nub_addr_t LookupSymbol (const char *name, const char *shlib);
+ void SetNameToAddressCallback (DNBCallbackNameToAddress callback, void *baton)
+ {
+ m_name_to_addr_callback = callback;
+ m_name_to_addr_baton = baton;
+ }
+ void SetSharedLibraryInfoCallback (DNBCallbackCopyExecutableImageInfos callback, void *baton)
+ {
+ m_image_infos_callback = callback;
+ m_image_infos_baton = baton;
+ }
+
+ bool Resume (const DNBThreadResumeActions& thread_actions);
+ bool Signal (int signal, const struct timespec *timeout_abstime = NULL);
+ bool Kill (const struct timespec *timeout_abstime = NULL);
+ bool Detach ();
+ nub_size_t ReadMemory (nub_addr_t addr, nub_size_t size, void *buf);
+ nub_size_t WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf);
+
+ //----------------------------------------------------------------------
+ // Path and arg accessors
+ //----------------------------------------------------------------------
+ const char * Path () const { return m_path.c_str(); }
+ size_t ArgumentCount () const { return m_args.size(); }
+ const char * ArgumentAtIndex (size_t arg_idx) const
+ {
+ if (arg_idx < m_args.size())
+ return m_args[arg_idx].c_str();
+ return NULL;
+ }
+
+ //----------------------------------------------------------------------
+ // Breakpoint functions
+ //----------------------------------------------------------------------
+ nub_break_t CreateBreakpoint (nub_addr_t addr, nub_size_t length, bool hardware, thread_t thread);
+ bool DisableBreakpoint (nub_break_t breakID, bool remove);
+ nub_size_t DisableAllBreakpoints (bool remove);
+ bool EnableBreakpoint (nub_break_t breakID);
+ void DumpBreakpoint(nub_break_t breakID) const;
+ DNBBreakpointList& Breakpoints() { return m_breakpoints; }
+ const DNBBreakpointList& Breakpoints() const { return m_breakpoints; }
+
+ //----------------------------------------------------------------------
+ // Watchpoint functions
+ //----------------------------------------------------------------------
+ nub_watch_t CreateWatchpoint (nub_addr_t addr, nub_size_t length, uint32_t watch_type, bool hardware, thread_t thread);
+ bool DisableWatchpoint (nub_watch_t watchID, bool remove);
+ nub_size_t DisableAllWatchpoints (bool remove);
+ bool EnableWatchpoint (nub_watch_t watchID);
+ void DumpWatchpoint(nub_watch_t watchID) const;
+ DNBBreakpointList& Watchpoints() { return m_watchpoints; }
+ const DNBBreakpointList& Watchpoints() const { return m_watchpoints; }
+
+ //----------------------------------------------------------------------
+ // Exception thread functions
+ //----------------------------------------------------------------------
+ bool StartSTDIOThread ();
+ static void * STDIOThread (void *arg);
+ void ExceptionMessageReceived (const MachException::Message& exceptionMessage);
+ void ExceptionMessageBundleComplete ();
+ void SharedLibrariesUpdated ();
+ nub_size_t CopyImageInfos (struct DNBExecutableImageInfo **image_infos, bool only_changed);
+
+ //----------------------------------------------------------------------
+ // Accessors
+ //----------------------------------------------------------------------
+ pid_t ProcessID () const { return m_pid; }
+ bool ProcessIDIsValid () const { return m_pid > 0; }
+ pid_t SetProcessID (pid_t pid);
+ MachTask& Task() { return m_task; }
+ const MachTask& Task() const { return m_task; }
+
+ PThreadEvent& Events() { return m_events; }
+ const DNBRegisterSetInfo *
+ GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const;
+ bool GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value) const;
+ bool SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value) const;
+ const char * ThreadGetName (nub_thread_t tid);
+ nub_state_t ThreadGetState (nub_thread_t tid);
+ nub_size_t GetNumThreads () const;
+ nub_thread_t GetThreadAtIndex (nub_size_t thread_idx) const;
+ uint32_t GetThreadIndexFromThreadID (nub_thread_t tid);
+ nub_thread_t GetCurrentThread ();
+ nub_thread_t SetCurrentThread (nub_thread_t tid);
+ MachThreadList & GetThreadList() { return m_threadList; }
+ bool GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const;
+ void DumpThreadStoppedReason(nub_thread_t tid) const;
+ const char * GetThreadInfo (nub_thread_t tid) const;
+
+ nub_state_t GetState ();
+ void SetState (nub_state_t state);
+ bool IsRunning (nub_state_t state)
+ {
+ return state == eStateRunning || IsStepping(state);
+ }
+ bool IsStepping (nub_state_t state)
+ {
+ return state == eStateStepping;
+ }
+ bool CanResume (nub_state_t state)
+ {
+ return state == eStateStopped;
+ }
+
+ const DNBError& GetLastError () const { return m_err; }
+
+ bool GetExitStatus(int* status)
+ {
+ if (GetState() == eStateExited)
+ {
+ if (status)
+ *status = m_exit_status;
+ return true;
+ }
+ return false;
+ }
+ void SetExitStatus(int status)
+ {
+ m_exit_status = status;
+ SetState(eStateExited);
+ }
+
+ uint32_t StopCount() const { return m_stop_count; }
+ void SetChildFileDescriptors (int stdin_fileno, int stdout_fileno, int stderr_fileno)
+ {
+ m_child_stdin = stdin_fileno;
+ m_child_stdout = stdout_fileno;
+ m_child_stderr = stderr_fileno;
+ }
+
+ int GetStdinFileDescriptor () const { return m_child_stdin; }
+ int GetStdoutFileDescriptor () const { return m_child_stdout; }
+ int GetStderrFileDescriptor () const { return m_child_stderr; }
+ void AppendSTDOUT (char* s, size_t len);
+ size_t GetAvailableSTDOUT (char *buf, size_t buf_size);
+ size_t GetAvailableSTDERR (char *buf, size_t buf_size);
+ void CloseChildFileDescriptors ()
+ {
+ if (m_child_stdin >= 0)
+ {
+ ::close (m_child_stdin);
+ m_child_stdin = -1;
+ }
+ if (m_child_stdout >= 0)
+ {
+ ::close (m_child_stdout);
+ m_child_stdout = -1;
+ }
+ if (m_child_stderr >= 0)
+ {
+ ::close (m_child_stderr);
+ m_child_stderr = -1;
+ }
+ }
+
+ bool ProcessUsingSpringBoard() const { return (m_flags & eMachProcessFlagsUsingSBS) != 0; }
+private:
+ enum
+ {
+ eMachProcessFlagsNone = 0,
+ eMachProcessFlagsAttached = (1 << 0),
+ eMachProcessFlagsUsingSBS = (1 << 1)
+ };
+ void Clear ();
+ void ReplyToAllExceptions (const DNBThreadResumeActions& thread_actions);
+ void PrivateResume (const DNBThreadResumeActions& thread_actions);
+ nub_size_t RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, uint8_t *buf) const;
+
+ uint32_t Flags () const { return m_flags; }
+ nub_state_t DoSIGSTOP (bool clear_bps_and_wps);
+
+ pid_t m_pid; // Process ID of child process
+ int m_child_stdin;
+ int m_child_stdout;
+ int m_child_stderr;
+ std::string m_path; // A path to the executable if we have one
+ std::vector<std::string> m_args; // The arguments with which the process was lauched
+ int m_exit_status; // The exit status for the process
+ MachTask m_task; // The mach task for this process
+ uint32_t m_flags; // Process specific flags (see eMachProcessFlags enums)
+ uint32_t m_stop_count; // A count of many times have we stopped
+ pthread_t m_stdio_thread; // Thread ID for the thread that watches for child process stdio
+ PThreadMutex m_stdio_mutex; // Multithreaded protection for stdio
+ std::string m_stdout_data;
+ MachException::Message::collection
+ m_exception_messages; // A collection of exception messages caught when listening to the exception port
+ PThreadMutex m_exception_messages_mutex; // Multithreaded protection for m_exception_messages
+
+ MachThreadList m_threadList; // A list of threads that is maintained/updated after each stop
+ DNBError m_err; // The last error for any transaction
+ nub_state_t m_state; // The state of our process
+ PThreadMutex m_state_mutex; // Multithreaded protection for m_state
+ PThreadEvent m_events; // Process related events in the child processes lifetime can be waited upon
+ DNBBreakpointList m_breakpoints; // Breakpoint list for this process
+ DNBBreakpointList m_watchpoints; // Watchpoint list for this process
+ DNBCallbackNameToAddress m_name_to_addr_callback;
+ void * m_name_to_addr_baton;
+ DNBCallbackCopyExecutableImageInfos
+ m_image_infos_callback;
+ void * m_image_infos_baton;
+};
+
+
+#endif // __MachProcess_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.cpp b/lldb/tools/debugserver/source/MacOSX/MachTask.cpp
new file mode 100644
index 00000000000..68d858c014c
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachTask.cpp
@@ -0,0 +1,660 @@
+//===-- MachTask.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// MachTask.cpp
+// debugserver
+//
+// Created by Greg Clayton on 12/5/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachTask.h"
+
+// C Includes
+
+#include <mach-o/dyld_images.h>
+#include <mach/mach_vm.h>
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "CFUtils.h"
+#include "DNB.h"
+#include "DNBError.h"
+#include "DNBLog.h"
+#include "MachProcess.h"
+#include "DNBDataRef.h"
+
+#if defined (__arm__)
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SpringBoardServices/SpringBoardServer.h>
+#include <SpringBoardServices/SBSWatchdogAssertion.h>
+
+#endif
+
+//----------------------------------------------------------------------
+// MachTask constructor
+//----------------------------------------------------------------------
+MachTask::MachTask(MachProcess *process) :
+ m_process (process),
+ m_task (TASK_NULL),
+ m_vm_memory (),
+ m_exception_thread (0),
+ m_exception_port (MACH_PORT_NULL)
+{
+ memset(&m_exc_port_info, 0, sizeof(m_exc_port_info));
+
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+MachTask::~MachTask()
+{
+ Clear();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Suspend
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::Suspend()
+{
+ DNBError err;
+ task_t task = TaskPort();
+ err = ::task_suspend (task);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task);
+ return err.Error();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Resume
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::Resume()
+{
+ struct task_basic_info task_info;
+ task_t task = TaskPort();
+
+ DNBError err;
+ err = BasicInfo(task, &task_info);
+
+ if (err.Success())
+ {
+ integer_t i;
+ for (i=0; i<task_info.suspend_count; i++)
+ {
+ err = ::task_resume (task);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task);
+ }
+ }
+ return err.Error();
+}
+
+//----------------------------------------------------------------------
+// MachTask::ExceptionPort
+//----------------------------------------------------------------------
+mach_port_t
+MachTask::ExceptionPort() const
+{
+ return m_exception_port;
+}
+
+//----------------------------------------------------------------------
+// MachTask::ExceptionPortIsValid
+//----------------------------------------------------------------------
+bool
+MachTask::ExceptionPortIsValid() const
+{
+ return MACH_PORT_VALID(m_exception_port);
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Clear
+//----------------------------------------------------------------------
+void
+MachTask::Clear()
+{
+ // Do any cleanup needed for this task
+ m_task = TASK_NULL;
+ m_exception_thread = 0;
+ m_exception_port = MACH_PORT_NULL;
+
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::SaveExceptionPortInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::SaveExceptionPortInfo()
+{
+ return m_exc_port_info.Save(TaskPort());
+}
+
+//----------------------------------------------------------------------
+// MachTask::RestoreExceptionPortInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::RestoreExceptionPortInfo()
+{
+ return m_exc_port_info.Restore(TaskPort());
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::ReadMemory
+//----------------------------------------------------------------------
+nub_size_t
+MachTask::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
+{
+ nub_size_t n = 0;
+ task_t task = TaskPort();
+ if (task != TASK_NULL)
+ {
+ n = m_vm_memory.Read(task, addr, buf, size);
+
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::ReadMemory ( addr = 0x%8.8llx, size = %zu, buf = %8.8p) => %u bytes read", (uint64_t)addr, size, buf, n);
+ if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8))
+ {
+ DNBDataRef data((uint8_t*)buf, n, false);
+ data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16);
+ }
+ }
+ return n;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::WriteMemory
+//----------------------------------------------------------------------
+nub_size_t
+MachTask::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
+{
+ nub_size_t n = 0;
+ task_t task = TaskPort();
+ if (task != TASK_NULL)
+ {
+ n = m_vm_memory.Write(task, addr, buf, size);
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::WriteMemory ( addr = 0x%8.8llx, size = %zu, buf = %8.8p) => %u bytes written", (uint64_t)addr, size, buf, n);
+ if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8))
+ {
+ DNBDataRef data((uint8_t*)buf, n, false);
+ data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16);
+ }
+ }
+ return n;
+}
+
+//----------------------------------------------------------------------
+// MachTask::TaskPortForProcessID
+//----------------------------------------------------------------------
+task_t
+MachTask::TaskPortForProcessID (DNBError &err)
+{
+ if (m_task == TASK_NULL && m_process != NULL)
+ m_task = MachTask::TaskPortForProcessID(m_process->ProcessID(), err);
+ return m_task;
+}
+
+//----------------------------------------------------------------------
+// MachTask::TaskPortForProcessID
+//----------------------------------------------------------------------
+task_t
+MachTask::TaskPortForProcessID (pid_t pid, DNBError &err)
+{
+ task_t task = TASK_NULL;
+ if (pid != INVALID_NUB_PROCESS)
+ {
+ mach_port_t task_self = mach_task_self ();
+ err = ::task_for_pid ( task_self, pid, &task);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ {
+ char str[1024];
+ ::snprintf (str,
+ sizeof(str),
+ "::task_for_pid ( task_self, pid = %d, task => TASK_NULL (0x%4.4x) ) uid=%u, euid=%u gid=%u egid=%u (%s)",
+ pid,
+ task,
+ getuid(),
+ geteuid(),
+ getgid(),
+ getegid(),
+ err.AsString() ? err.AsString() : "success");
+ if (err.Fail())
+ err.SetErrorString(str);
+ err.LogThreaded(str);
+ }
+ }
+ return task;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::BasicInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::BasicInfo(struct task_basic_info *info)
+{
+ return BasicInfo (TaskPort(), info);
+}
+
+//----------------------------------------------------------------------
+// MachTask::BasicInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::BasicInfo(task_t task, struct task_basic_info *info)
+{
+ if (info == NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ DNBError err;
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count);
+ const bool log_process = DNBLogCheckLogBit(LOG_TASK);
+ if (log_process || err.Fail())
+ err.LogThreaded("::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count);
+ if (DNBLogCheckLogBit(LOG_TASK) && DNBLogCheckLogBit(LOG_VERBOSE) && err.Success())
+ {
+ float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
+ float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
+ DNBLogThreaded("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8x, resident_size = 0x%8.8x, user_time = %f, system_time = %f }",
+ info->suspend_count, info->virtual_size, info->resident_size, user, system);
+ }
+ return err.Error();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::IsValid
+//
+// Returns true if a task is a valid task port for a current process.
+//----------------------------------------------------------------------
+bool
+MachTask::IsValid () const
+{
+ return MachTask::IsValid(TaskPort());
+}
+
+//----------------------------------------------------------------------
+// MachTask::IsValid
+//
+// Returns true if a task is a valid task port for a current process.
+//----------------------------------------------------------------------
+bool
+MachTask::IsValid (task_t task)
+{
+ if (task != TASK_NULL)
+ {
+ struct task_basic_info task_info;
+ return BasicInfo(task, &task_info) == KERN_SUCCESS;
+ }
+ return false;
+}
+
+
+bool
+MachTask::StartExceptionThread(DNBError &err)
+{
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__);
+ task_t task = TaskPortForProcessID(err);
+ if (MachTask::IsValid(task))
+ {
+ // Got the mach port for the current process
+ mach_port_t task_self = mach_task_self ();
+
+ // Allocate an exception port that we will use to track our child process
+ err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port);
+ if (err.Fail())
+ return false;
+
+ // Add the ability to send messages on the new exception port
+ err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
+ if (err.Fail())
+ return false;
+
+ // Save the original state of the exception ports for our child process
+ SaveExceptionPortInfo();
+
+ // Set the ability to get all exceptions on this port
+ err = ::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
+ if (err.Fail())
+ return false;
+
+ // Create the exception thread
+ err = ::pthread_create (&m_exception_thread, NULL, MachTask::ExceptionThread, this);
+ return err.Success();
+ }
+ else
+ {
+ DNBLogError("MachTask::%s (): task invalid, exception thread start failed.", __FUNCTION__);
+ }
+ return false;
+}
+
+kern_return_t
+MachTask::ShutDownExcecptionThread()
+{
+ DNBError err;
+
+ err = RestoreExceptionPortInfo();
+
+ // NULL our our exception port and let our exception thread exit
+ mach_port_t exception_port = m_exception_port;
+ m_exception_port = NULL;
+
+ err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread);
+
+ err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)", m_exception_thread);
+
+ // Deallocate our exception port that we used to track our child process
+ mach_port_t task_self = mach_task_self ();
+ err = ::mach_port_deallocate (task_self, exception_port);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port);
+ exception_port = NULL;
+
+ return err.Error();
+}
+
+
+void *
+MachTask::ExceptionThread (void *arg)
+{
+ if (arg == NULL)
+ return NULL;
+
+ MachTask *mach_task = (MachTask*) arg;
+ MachProcess *mach_proc = mach_task->Process();
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__, arg);
+
+ // We keep a count of the number of consecutive exceptions received so
+ // we know to grab all exceptions without a timeout. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main loop in this
+ // thread can stop periodically if needed to service things related to this
+ // process.
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle avaiable. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ uint32_t num_exceptions_received = 0;
+ DNBError err;
+ task_t task = mach_task->TaskPort();
+ mach_msg_timeout_t periodic_timeout = 0;
+
+#if defined (__arm__)
+ mach_msg_timeout_t watchdog_elapsed = 0;
+ mach_msg_timeout_t watchdog_timeout = 60 * 1000;
+ pid_t pid = mach_proc->ProcessID();
+ CFReleaser<SBSWatchdogAssertionRef> watchdog;
+
+ if (mach_proc->ProcessUsingSpringBoard())
+ {
+ // Request a renewal for every 60 seconds if we attached using SpringBoard
+ watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60));
+ DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get());
+
+ if (watchdog.get())
+ {
+ ::SBSWatchdogAssertionRenew (watchdog.get());
+
+ CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get());
+ DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval);
+ if (watchdogRenewalInterval > 0.0)
+ {
+ watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
+ if (watchdog_timeout > 3000)
+ watchdog_timeout -= 1000; // Give us a second to renew our timeout
+ else if (watchdog_timeout > 1000)
+ watchdog_timeout -= 250; // Give us a quarter of a second to renew our timeout
+ }
+ }
+ if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout)
+ periodic_timeout = watchdog_timeout;
+ }
+#endif // #if defined (__arm__)
+
+ while (mach_task->ExceptionPortIsValid())
+ {
+ ::pthread_testcancel ();
+
+ MachException::Message exception_message;
+
+
+ if (num_exceptions_received > 0)
+ {
+ // No timeout, just receive as many exceptions as we can since we already have one and we want
+ // to get all currently available exceptions for this task
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0);
+ }
+ else if (periodic_timeout > 0)
+ {
+ // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms)
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout);
+ }
+ else
+ {
+ // We don't need to parse all current exceptions or stop periodically,
+ // just wait for an exception forever.
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0);
+ }
+
+ if (err.Error() == MACH_RCV_INTERRUPTED)
+ {
+ // If we have no task port we should exit this thread
+ if (!mach_task->ExceptionPortIsValid())
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "thread cancelled...");
+ break;
+ }
+
+ // Make sure our task is still valid
+ if (MachTask::IsValid(task))
+ {
+ // Task is still ok
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "interrupted, but task still valid, continuing...");
+ continue;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
+ mach_proc->SetState(eStateExited);
+ // Our task has died, exit the thread.
+ break;
+ }
+ }
+ else if (err.Error() == MACH_RCV_TIMED_OUT)
+ {
+ if (num_exceptions_received > 0)
+ {
+ // We were receiving all current exceptions with a timeout of zero
+ // it is time to go back to our normal looping mode
+ num_exceptions_received = 0;
+
+ // Notify our main thread we have a complete exception message
+ // bundle available.
+ mach_proc->ExceptionMessageBundleComplete();
+
+ // in case we use a timeout value when getting exceptions...
+ // Make sure our task is still valid
+ if (MachTask::IsValid(task))
+ {
+ // Task is still ok
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "got a timeout, continuing...");
+ continue;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
+ mach_proc->SetState(eStateExited);
+ // Our task has died, exit the thread.
+ break;
+ }
+ continue;
+ }
+
+#if defined (__arm__)
+ if (watchdog.get())
+ {
+ watchdog_elapsed += periodic_timeout;
+ if (watchdog_elapsed >= watchdog_timeout)
+ {
+ DNBLogThreadedIf(LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get());
+ ::SBSWatchdogAssertionRenew (watchdog.get());
+ watchdog_elapsed = 0;
+ }
+ }
+#endif
+ }
+ else if (err.Error() != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "got some other error, do something about it??? nah, continuing for now...");
+ // TODO: notify of error?
+ }
+ else
+ {
+ if (exception_message.CatchExceptionRaise())
+ {
+ ++num_exceptions_received;
+ mach_proc->ExceptionMessageReceived(exception_message);
+ }
+ }
+ }
+
+#if defined (__arm__)
+ if (watchdog.get())
+ {
+ // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we
+ // all are up and running on systems that support it. The SBS framework has a #define
+ // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now
+ // so it should still build either way.
+ DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get());
+ ::SBSWatchdogAssertionRelease (watchdog.get());
+ }
+#endif // #if defined (__arm__)
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s (%p): thread exiting...", __FUNCTION__, arg);
+ return NULL;
+}
+
+
+// So the TASK_DYLD_INFO used to just return the address of the all image infos
+// as a single member called "all_image_info". Then someone decided it would be
+// a good idea to rename this first member to "all_image_info_addr" and add a
+// size member called "all_image_info_size". This of course can not be detected
+// using code or #defines. So to hack around this problem, we define our own
+// version of the TASK_DYLD_INFO structure so we can guarantee what is inside it.
+
+struct hack_task_dyld_info {
+ mach_vm_address_t all_image_info_addr;
+ mach_vm_size_t all_image_info_size;
+};
+
+nub_addr_t
+MachTask::GetDYLDAllImageInfosAddress (DNBError& err)
+{
+ struct hack_task_dyld_info dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ // Make sure that COUNT isn't bigger than our hacked up struct hack_task_dyld_info.
+ // If it is, then make COUNT smaller to match.
+ if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t)))
+ count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t));
+
+ task_t task = TaskPortForProcessID (err);
+ if (err.Success())
+ {
+ err = ::task_info (task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
+ if (err.Success())
+ {
+ // We now have the address of the all image infos structure
+ return dyld_info.all_image_info_addr;
+ }
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::AllocateMemory
+//----------------------------------------------------------------------
+nub_addr_t
+MachTask::AllocateMemory (size_t size, uint32_t permissions)
+{
+ mach_vm_address_t addr;
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return INVALID_NUB_ADDRESS;
+
+ DNBError err;
+ err = ::mach_vm_allocate (task, &addr, size, TRUE);
+ if (err.Error() == KERN_SUCCESS)
+ {
+ // Set the protections:
+ vm_prot_t mach_prot = 0;
+ if (permissions & eMemoryPermissionsReadable)
+ mach_prot |= VM_PROT_READ;
+ if (permissions & eMemoryPermissionsWritable)
+ mach_prot |= VM_PROT_WRITE;
+ if (permissions & eMemoryPermissionsExecutable)
+ mach_prot |= VM_PROT_EXECUTE;
+
+
+ err = ::mach_vm_protect (task, addr, size, 0, mach_prot);
+ if (err.Error() == KERN_SUCCESS)
+ {
+ m_allocations.insert (std::make_pair(addr, size));
+ return addr;
+ }
+ ::mach_vm_deallocate (task, addr, size);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// MachTask::DeallocateMemory
+//----------------------------------------------------------------------
+nub_bool_t
+MachTask::DeallocateMemory (nub_addr_t addr)
+{
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return false;
+
+ // We have to stash away sizes for the allocations...
+ allocation_collection::iterator pos, end = m_allocations.end();
+ for (pos = m_allocations.begin(); pos != end; pos++)
+ {
+ if ((*pos).first == addr)
+ {
+ m_allocations.erase(pos);
+ return ::mach_vm_deallocate (task, (*pos).first, (*pos).second) == KERN_SUCCESS;
+ }
+
+ }
+ return false;
+}
+
diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.h b/lldb/tools/debugserver/source/MacOSX/MachTask.h
new file mode 100644
index 00000000000..3bf40e13457
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachTask.h
@@ -0,0 +1,91 @@
+//===-- MachTask.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// MachTask.h
+// debugserver
+//
+// Created by Greg Clayton on 12/5/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachTask_h__
+#define __MachTask_h__
+
+// C Includes
+#include <mach/mach.h>
+#include <sys/socket.h>
+// C++ Includes
+#include <map>
+// Other libraries and framework includes
+// Project includes
+#include "MachException.h"
+#include "MachVMMemory.h"
+#include "PThreadMutex.h"
+
+class MachProcess;
+
+class MachTask
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ MachTask (MachProcess *process);
+ virtual ~MachTask ();
+
+ void Clear ();
+
+ kern_return_t Suspend ();
+ kern_return_t Resume ();
+
+ nub_size_t ReadMemory (nub_addr_t addr, nub_size_t size, void *buf);
+ nub_size_t WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf);
+
+ nub_addr_t AllocateMemory (nub_size_t size, uint32_t permissions);
+ nub_bool_t DeallocateMemory (nub_addr_t addr);
+
+ mach_port_t ExceptionPort () const;
+ bool ExceptionPortIsValid () const;
+ kern_return_t SaveExceptionPortInfo ();
+ kern_return_t RestoreExceptionPortInfo ();
+ kern_return_t ShutDownExcecptionThread ();
+
+ bool StartExceptionThread (DNBError &err);
+ nub_addr_t GetDYLDAllImageInfosAddress (DNBError& err);
+ kern_return_t BasicInfo (struct task_basic_info *info);
+ static kern_return_t BasicInfo (task_t task, struct task_basic_info *info);
+ bool IsValid () const;
+ static bool IsValid (task_t task);
+ static void * ExceptionThread (void *arg);
+ task_t TaskPort () const { return m_task; }
+ task_t TaskPortForProcessID (DNBError &err);
+ static task_t TaskPortForProcessID (pid_t pid, DNBError &err);
+
+ MachProcess * Process () { return m_process; }
+ const MachProcess * Process () const { return m_process; }
+
+protected:
+ MachProcess * m_process; // The mach process that owns this MachTask
+ task_t m_task;
+ MachVMMemory m_vm_memory; // Special mach memory reading class that will take care of watching for page and region boundaries
+ MachException::PortInfo
+ m_exc_port_info; // Saved settings for all exception ports
+ pthread_t m_exception_thread; // Thread ID for the exception thread in case we need it
+ mach_port_t m_exception_port; // Exception port on which we will receive child exceptions
+
+ typedef std::map <mach_vm_address_t, size_t> allocation_collection;
+ allocation_collection m_allocations;
+
+private:
+ MachTask(const MachTask&); // Outlaw
+ MachTask& operator=(const MachTask& rhs);// Outlaw
+};
+
+#endif // __MachTask_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/MachThread.cpp b/lldb/tools/debugserver/source/MacOSX/MachThread.cpp
new file mode 100644
index 00000000000..9d018f66ea6
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachThread.cpp
@@ -0,0 +1,745 @@
+//===-- MachThread.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachThread.h"
+#include "MachProcess.h"
+#include "DNBLog.h"
+#include "DNB.h"
+
+static uint32_t
+GetSequenceID()
+{
+ static uint32_t g_nextID = 0;
+ return ++g_nextID;
+}
+
+MachThread::MachThread (MachProcess *process, thread_t thread) :
+ m_process(process),
+ m_tid(thread),
+ m_seq_id(GetSequenceID()),
+ m_state(eStateUnloaded),
+ m_state_mutex(PTHREAD_MUTEX_RECURSIVE),
+ m_breakID(INVALID_NUB_BREAK_ID),
+ m_suspendCount(0),
+ m_arch(this),
+ m_regSets()
+{
+ nub_size_t num_reg_sets = 0;
+ const DNBRegisterSetInfo *regSetInfo = m_arch.GetRegisterSetInfo(&num_reg_sets);
+ if (num_reg_sets > 0)
+ m_regSets.assign(regSetInfo, regSetInfo + num_reg_sets);
+
+ ::memset (&m_basicInfo, 0, sizeof (m_basicInfo));
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::MachThread ( process = %p, tid = 0x%4.4x, seq_id = %u )", &m_process, m_tid, m_seq_id);
+}
+
+MachThread::~MachThread()
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::~MachThread() for tid = 0x%4.4x (%u)", m_tid, m_seq_id);
+}
+
+
+
+uint32_t
+MachThread::Suspend()
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
+ if (ThreadIDIsValid(m_tid))
+ {
+ DNBError err(::thread_suspend (m_tid), DNBError::MachKernel);
+ if (err.Success())
+ m_suspendCount++;
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_suspend (%4.4x)", m_tid);
+ }
+ return SuspendCount();
+}
+
+uint32_t
+MachThread::Resume()
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
+ if (ThreadIDIsValid(m_tid))
+ {
+ while (m_suspendCount > 0)
+ {
+ DNBError err(::thread_resume (m_tid), DNBError::MachKernel);
+ if (err.Success())
+ m_suspendCount--;
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_resume (%4.4x)", m_tid);
+ }
+ }
+ return SuspendCount();
+}
+
+bool
+MachThread::RestoreSuspendCount()
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
+ DNBError err;
+ if (ThreadIDIsValid(m_tid) == false)
+ return false;
+ else if (m_suspendCount > m_basicInfo.suspend_count)
+ {
+ while (m_suspendCount > m_basicInfo.suspend_count)
+ {
+ err = ::thread_resume (m_tid);
+ if (err.Success())
+ --m_suspendCount;
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_resume (%4.4x)", m_tid);
+ }
+ }
+ else if (m_suspendCount < m_basicInfo.suspend_count)
+ {
+ while (m_suspendCount < m_basicInfo.suspend_count)
+ {
+ err = ::thread_suspend (m_tid);
+ if (err.Success())
+ --m_suspendCount;
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_suspend (%4.4x)", m_tid);
+ }
+ }
+ return m_suspendCount == m_basicInfo.suspend_count;
+}
+
+
+const char *
+MachThread::GetBasicInfoAsString () const
+{
+ static char g_basic_info_string[1024];
+ struct thread_basic_info basicInfo;
+
+ if (GetBasicInfo(m_tid, &basicInfo))
+ {
+
+// char run_state_str[32];
+// size_t run_state_str_size = sizeof(run_state_str);
+// switch (basicInfo.run_state)
+// {
+// case TH_STATE_RUNNING: strncpy(run_state_str, "running", run_state_str_size); break;
+// case TH_STATE_STOPPED: strncpy(run_state_str, "stopped", run_state_str_size); break;
+// case TH_STATE_WAITING: strncpy(run_state_str, "waiting", run_state_str_size); break;
+// case TH_STATE_UNINTERRUPTIBLE: strncpy(run_state_str, "uninterruptible", run_state_str_size); break;
+// case TH_STATE_HALTED: strncpy(run_state_str, "halted", run_state_str_size); break;
+// default: snprintf(run_state_str, run_state_str_size, "%d", basicInfo.run_state); break; // ???
+// }
+ float user = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f;
+ float system = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f;
+ snprintf(g_basic_info_string, sizeof(g_basic_info_string), "Thread 0x%4.4x: user=%f system=%f cpu=%d sleep_time=%d",
+ InferiorThreadID(),
+ user,
+ system,
+ basicInfo.cpu_usage,
+ basicInfo.sleep_time);
+
+ return g_basic_info_string;
+ }
+ return NULL;
+}
+
+thread_t
+MachThread::InferiorThreadID() const
+{
+ mach_msg_type_number_t i;
+ mach_port_name_array_t names;
+ mach_port_type_array_t types;
+ mach_msg_type_number_t ncount, tcount;
+ thread_t inferior_tid = INVALID_NUB_THREAD;
+ task_t my_task = ::mach_task_self();
+ task_t task = m_process->Task().TaskPort();
+
+ kern_return_t kret = ::mach_port_names (task, &names, &ncount, &types, &tcount);
+ if (kret == KERN_SUCCESS)
+ {
+
+ for (i = 0; i < ncount; i++)
+ {
+ mach_port_t my_name;
+ mach_msg_type_name_t my_type;
+
+ kret = ::mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &my_name, &my_type);
+ if (kret == KERN_SUCCESS)
+ {
+ ::mach_port_deallocate (my_task, my_name);
+ if (my_name == m_tid)
+ {
+ inferior_tid = names[i];
+ break;
+ }
+ }
+ }
+ // Free up the names and types
+ ::vm_deallocate (my_task, (vm_address_t) names, ncount * sizeof (mach_port_name_t));
+ ::vm_deallocate (my_task, (vm_address_t) types, tcount * sizeof (mach_port_type_t));
+ }
+ return inferior_tid;
+}
+
+bool
+MachThread::GetBasicInfo(thread_t thread, struct thread_basic_info *basicInfoPtr)
+{
+ if (ThreadIDIsValid(thread))
+ {
+ unsigned int info_count = THREAD_BASIC_INFO_COUNT;
+ kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count);
+ if (err == KERN_SUCCESS)
+ return true;
+ }
+ ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info));
+ return false;
+}
+
+
+bool
+MachThread::ThreadIDIsValid(thread_t thread)
+{
+ return thread != THREAD_NULL;
+}
+
+bool
+MachThread::GetRegisterState(int flavor, bool force)
+{
+ return m_arch.GetRegisterState(flavor, force) == KERN_SUCCESS;
+}
+
+bool
+MachThread::SetRegisterState(int flavor)
+{
+ return m_arch.SetRegisterState(flavor) == KERN_SUCCESS;
+}
+
+uint64_t
+MachThread::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ return m_arch.GetPC(failValue);
+}
+
+bool
+MachThread::SetPC(uint64_t value)
+{
+ // Set program counter
+ return m_arch.SetPC(value);
+}
+
+uint64_t
+MachThread::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ return m_arch.GetSP(failValue);
+}
+
+nub_process_t
+MachThread::ProcessID() const
+{
+ if (m_process)
+ return m_process->ProcessID();
+ return INVALID_NUB_PROCESS;
+}
+
+void
+MachThread::Dump(uint32_t index)
+{
+ const char * thread_run_state = NULL;
+
+ switch (m_basicInfo.run_state)
+ {
+ case TH_STATE_RUNNING: thread_run_state = "running"; break; // 1 thread is running normally
+ case TH_STATE_STOPPED: thread_run_state = "stopped"; break; // 2 thread is stopped
+ case TH_STATE_WAITING: thread_run_state = "waiting"; break; // 3 thread is waiting normally
+ case TH_STATE_UNINTERRUPTIBLE: thread_run_state = "uninter"; break; // 4 thread is in an uninterruptible wait
+ case TH_STATE_HALTED: thread_run_state = "halted "; break; // 5 thread is halted at a
+ default: thread_run_state = "???"; break;
+ }
+
+ DNBLogThreaded("thread[%u] %4.4x (%u): pc: 0x%8.8llx sp: 0x%8.8llx breakID: %d user: %d.%06.6d system: %d.%06.6d cpu: %d policy: %d run_state: %d (%s) flags: %d suspend_count: %d (current %d) sleep_time: %d",
+ index,
+ m_tid,
+ m_seq_id,
+ GetPC(INVALID_NUB_ADDRESS),
+ GetSP(INVALID_NUB_ADDRESS),
+ m_breakID,
+ m_basicInfo.user_time.seconds, m_basicInfo.user_time.microseconds,
+ m_basicInfo.system_time.seconds, m_basicInfo.system_time.microseconds,
+ m_basicInfo.cpu_usage,
+ m_basicInfo.policy,
+ m_basicInfo.run_state,
+ thread_run_state,
+ m_basicInfo.flags,
+ m_basicInfo.suspend_count, m_suspendCount,
+ m_basicInfo.sleep_time);
+ //DumpRegisterState(0);
+}
+
+void
+MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action)
+{
+ if (thread_action->addr != INVALID_NUB_ADDRESS)
+ SetPC (thread_action->addr);
+
+ SetState (thread_action->state);
+ switch (thread_action->state)
+ {
+ case eStateStopped:
+ case eStateSuspended:
+ Suspend();
+ break;
+
+ case eStateRunning:
+ case eStateStepping:
+ Resume();
+ break;
+ }
+ m_arch.ThreadWillResume();
+ m_stop_exception.Clear();
+}
+
+bool
+MachThread::ShouldStop(bool &step_more)
+{
+ // See if this thread is at a breakpoint?
+ nub_break_t breakID = CurrentBreakpoint();
+
+ if (NUB_BREAK_ID_IS_VALID(breakID))
+ {
+ // This thread is sitting at a breakpoint, ask the breakpoint
+ // if we should be stopping here.
+ if (Process()->Breakpoints().ShouldStop(ProcessID(), ThreadID(), breakID))
+ return true;
+ else
+ {
+ // The breakpoint said we shouldn't stop, but we may have gotten
+ // a signal or the user may have requested to stop in some other
+ // way. Stop if we have a valid exception (this thread won't if
+ // another thread was the reason this process stopped) and that
+ // exception, is NOT a breakpoint exception (a common case would
+ // be a SIGINT signal).
+ if (GetStopException().IsValid() && !GetStopException().IsBreakpoint())
+ return true;
+ }
+ }
+ else
+ {
+ if (m_arch.StepNotComplete())
+ {
+ step_more = true;
+ return false;
+ }
+ // The thread state is used to let us know what the thread was
+ // trying to do. MachThread::ThreadWillResume() will set the
+ // thread state to various values depending if the thread was
+ // the current thread and if it was to be single stepped, or
+ // resumed.
+ if (GetState() == eStateRunning)
+ {
+ // If our state is running, then we should continue as we are in
+ // the process of stepping over a breakpoint.
+ return false;
+ }
+ else
+ {
+ // Stop if we have any kind of valid exception for this
+ // thread.
+ if (GetStopException().IsValid())
+ return true;
+ }
+ }
+ return false;
+}
+bool
+MachThread::IsStepping()
+{
+ // Return true if this thread is currently being stepped.
+ // MachThread::ThreadWillResume currently determines this by looking if we
+ // have been asked to single step, or if we are at a breakpoint instruction
+ // and have been asked to resume. In the latter case we need to disable the
+ // breakpoint we are at, single step, re-enable and continue.
+ nub_state_t state = GetState();
+ return (state == eStateStepping) ||
+ (state == eStateRunning && NUB_BREAK_ID_IS_VALID(CurrentBreakpoint()));
+}
+
+
+bool
+MachThread::ThreadDidStop()
+{
+ // This thread has existed prior to resuming under debug nub control,
+ // and has just been stopped. Do any cleanup that needs to be done
+ // after running.
+
+ // The thread state and breakpoint will still have the same values
+ // as they had prior to resuming the thread, so it makes it easy to check
+ // if we were trying to step a thread, or we tried to resume while being
+ // at a breakpoint.
+
+ // When this method gets called, the process state is still in the
+ // state it was in while running so we can act accordingly.
+ m_arch.ThreadDidStop();
+
+
+ // We may have suspended this thread so the primary thread could step
+ // without worrying about race conditions, so lets restore our suspend
+ // count.
+ RestoreSuspendCount();
+
+ // Update the basic information for a thread
+ MachThread::GetBasicInfo(m_tid, &m_basicInfo);
+ m_suspendCount = m_basicInfo.suspend_count;
+
+ // See if we were at a breakpoint when we last resumed that we disabled,
+ // re-enable it.
+ nub_break_t breakID = CurrentBreakpoint();
+
+ if (NUB_BREAK_ID_IS_VALID(breakID))
+ {
+ m_process->EnableBreakpoint(breakID);
+ if (m_suspendCount > 0)
+ {
+ SetState(eStateSuspended);
+ }
+ else
+ {
+ // If we last were at a breakpoint and we single stepped, our state
+ // will be "running" to indicate we need to continue after stepping
+ // over the breakpoint instruction. If we step over a breakpoint
+ // instruction, we need to stop.
+ if (GetState() == eStateRunning)
+ {
+ // Leave state set to running so we will continue automatically
+ // from this breakpoint
+ }
+ else
+ {
+ SetState(eStateStopped);
+ }
+ }
+ }
+ else
+ {
+ if (m_suspendCount > 0)
+ {
+ SetState(eStateSuspended);
+ }
+ else
+ {
+ SetState(eStateStopped);
+ }
+ }
+
+
+ SetCurrentBreakpoint(INVALID_NUB_BREAK_ID);
+
+ return true;
+}
+
+bool
+MachThread::NotifyException(MachException::Data& exc)
+{
+ if (m_stop_exception.IsValid())
+ {
+ // We may have more than one exception for a thread, but we need to
+ // only remember the one that we will say is the reason we stopped.
+ // We may have been single stepping and also gotten a signal exception,
+ // so just remember the most pertinent one.
+ if (m_stop_exception.IsBreakpoint())
+ m_stop_exception = exc;
+ }
+ else
+ {
+ m_stop_exception = exc;
+ }
+ bool handled = m_arch.NotifyException(exc);
+ if (!handled)
+ {
+ handled = true;
+ nub_addr_t pc = GetPC();
+ nub_break_t breakID = m_process->Breakpoints().FindIDByAddress(pc);
+ SetCurrentBreakpoint(breakID);
+ switch (exc.exc_type)
+ {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ }
+ return handled;
+}
+
+
+nub_state_t
+MachThread::GetState()
+{
+ // If any other threads access this we will need a mutex for it
+ PTHREAD_MUTEX_LOCKER (locker, m_state_mutex);
+ return m_state;
+}
+
+void
+MachThread::SetState(nub_state_t state)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_state_mutex);
+ m_state = state;
+ DNBLogThreadedIf(LOG_THREAD, "MachThread::SetState ( %s ) for tid = 0x%4.4x", DNBStateAsString(state), m_tid);
+}
+
+uint32_t
+MachThread::GetNumRegistersInSet(int regSet) const
+{
+ if (regSet < m_regSets.size())
+ return m_regSets[regSet].num_registers;
+ return 0;
+}
+
+const char *
+MachThread::GetRegisterSetName(int regSet) const
+{
+ if (regSet < m_regSets.size())
+ return m_regSets[regSet].name;
+ return NULL;
+}
+
+const DNBRegisterInfo *
+MachThread::GetRegisterInfo(int regSet, int regIndex) const
+{
+ if (regSet < m_regSets.size())
+ if (regIndex < m_regSets[regSet].num_registers)
+ return &m_regSets[regSet].registers[regIndex];
+ return NULL;
+}
+void
+MachThread::DumpRegisterState(int regSet)
+{
+ if (regSet == REGISTER_SET_ALL)
+ {
+ for (regSet = 1; regSet < m_regSets.size(); regSet++)
+ DumpRegisterState(regSet);
+ }
+ else
+ {
+ if (m_arch.RegisterSetStateIsValid(regSet))
+ {
+ const size_t numRegisters = GetNumRegistersInSet(regSet);
+ size_t regIndex = 0;
+ DNBRegisterValueClass reg;
+ for (regIndex = 0; regIndex < numRegisters; ++regIndex)
+ {
+ if (m_arch.GetRegisterValue(regSet, regIndex, &reg))
+ {
+ reg.Dump(NULL, NULL);
+ }
+ }
+ }
+ else
+ {
+ DNBLog("%s: registers are not currently valid.", GetRegisterSetName(regSet));
+ }
+ }
+}
+
+const DNBRegisterSetInfo *
+MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets ) const
+{
+ *num_reg_sets = m_regSets.size();
+ return &m_regSets[0];
+}
+
+bool
+MachThread::GetRegisterValue ( uint32_t set, uint32_t reg, DNBRegisterValue *value )
+{
+ return m_arch.GetRegisterValue(set, reg, value);
+}
+
+bool
+MachThread::SetRegisterValue ( uint32_t set, uint32_t reg, const DNBRegisterValue *value )
+{
+ return m_arch.SetRegisterValue(set, reg, value);
+}
+
+nub_size_t
+MachThread::GetRegisterContext (void *buf, nub_size_t buf_len)
+{
+ return m_arch.GetRegisterContext(buf, buf_len);
+}
+
+nub_size_t
+MachThread::SetRegisterContext (const void *buf, nub_size_t buf_len)
+{
+ return m_arch.SetRegisterContext(buf, buf_len);
+}
+
+uint32_t
+MachThread::EnableHardwareBreakpoint (const DNBBreakpoint *bp)
+{
+ if (bp != NULL && bp->IsBreakpoint())
+ return m_arch.EnableHardwareBreakpoint(bp->Address(), bp->ByteSize());
+ return INVALID_NUB_HW_INDEX;
+}
+
+uint32_t
+MachThread::EnableHardwareWatchpoint (const DNBBreakpoint *wp)
+{
+ if (wp != NULL && wp->IsWatchpoint())
+ return m_arch.EnableHardwareWatchpoint(wp->Address(), wp->ByteSize(), wp->WatchpointRead(), wp->WatchpointWrite());
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+MachThread::DisableHardwareBreakpoint (const DNBBreakpoint *bp)
+{
+ if (bp != NULL && bp->IsHardware())
+ return m_arch.DisableHardwareBreakpoint(bp->GetHardwareIndex());
+ return false;
+}
+
+bool
+MachThread::DisableHardwareWatchpoint (const DNBBreakpoint *wp)
+{
+ if (wp != NULL && wp->IsHardware())
+ return m_arch.DisableHardwareWatchpoint(wp->GetHardwareIndex());
+ return false;
+}
+
+
+void
+MachThread::NotifyBreakpointChanged (const DNBBreakpoint *bp)
+{
+ nub_break_t breakID = bp->GetID();
+ if (bp->IsEnabled())
+ {
+ if (bp->Address() == GetPC())
+ {
+ SetCurrentBreakpoint(breakID);
+ }
+ }
+ else
+ {
+ if (CurrentBreakpoint() == breakID)
+ {
+ SetCurrentBreakpoint(INVALID_NUB_BREAK_ID);
+ }
+ }
+}
+
+bool
+MachThread::GetIdentifierInfo ()
+{
+#ifdef THREAD_IDENTIFIER_INFO_COUNT
+ if (m_ident_info.thread_id == 0)
+ {
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ return ::thread_info (ThreadID(), THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count) == KERN_SUCCESS;
+ }
+#endif
+
+ return false;
+}
+
+
+const char *
+MachThread::GetName ()
+{
+ if (GetIdentifierInfo ())
+ {
+ int len = ::proc_pidinfo (m_process->ProcessID(), PROC_PIDTHREADINFO, m_ident_info.thread_handle, &m_proc_threadinfo, sizeof (m_proc_threadinfo));
+
+ if (len && m_proc_threadinfo.pth_name[0])
+ return m_proc_threadinfo.pth_name;
+ }
+ return NULL;
+}
+
+
+//
+//const char *
+//MachThread::GetDispatchQueueName()
+//{
+// if (GetIdentifierInfo ())
+// {
+// if (m_ident_info.dispatch_qaddr == 0)
+// return NULL;
+//
+// uint8_t memory_buffer[8];
+// DNBDataRef data(memory_buffer, sizeof(memory_buffer), false);
+// ModuleSP module_sp(GetProcess()->GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib")));
+// if (module_sp.get() == NULL)
+// return NULL;
+//
+// lldb::addr_t dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS;
+// const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData);
+// if (dispatch_queue_offsets_symbol)
+// dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(GetProcess());
+//
+// if (dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS)
+// return NULL;
+//
+// // Excerpt from src/queue_private.h
+// struct dispatch_queue_offsets_s
+// {
+// uint16_t dqo_version;
+// uint16_t dqo_label;
+// uint16_t dqo_label_size;
+// } dispatch_queue_offsets;
+//
+//
+// if (GetProcess()->ReadMemory (dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets)) == sizeof(dispatch_queue_offsets))
+// {
+// uint32_t data_offset = 0;
+// if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t)))
+// {
+// if (GetProcess()->ReadMemory (m_ident_info.dispatch_qaddr, &memory_buffer, data.GetAddressByteSize()) == data.GetAddressByteSize())
+// {
+// data_offset = 0;
+// lldb::addr_t queue_addr = data.GetAddress(&data_offset);
+// lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label;
+// const size_t chunk_size = 32;
+// uint32_t label_pos = 0;
+// m_dispatch_queue_name.resize(chunk_size, '\0');
+// while (1)
+// {
+// size_t bytes_read = GetProcess()->ReadMemory (label_addr + label_pos, &m_dispatch_queue_name[label_pos], chunk_size);
+//
+// if (bytes_read <= 0)
+// break;
+//
+// if (m_dispatch_queue_name.find('\0', label_pos) != std::string::npos)
+// break;
+// label_pos += bytes_read;
+// }
+// m_dispatch_queue_name.erase(m_dispatch_queue_name.find('\0'));
+// }
+// }
+// }
+// }
+//
+// if (m_dispatch_queue_name.empty())
+// return NULL;
+// return m_dispatch_queue_name.c_str();
+//}
diff --git a/lldb/tools/debugserver/source/MacOSX/MachThread.h b/lldb/tools/debugserver/source/MacOSX/MachThread.h
new file mode 100644
index 00000000000..2bf5ff2c3bb
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachThread.h
@@ -0,0 +1,124 @@
+//===-- MachThread.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachThread_h__
+#define __MachThread_h__
+
+#include <string>
+#include <vector>
+#include <tr1/memory> // for std::tr1::shared_ptr
+
+#include <libproc.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#include <sys/signal.h>
+
+#include "PThreadCondition.h"
+#include "PThreadMutex.h"
+#include "MachException.h"
+#include "DNBArch.h"
+#include "DNBRegisterInfo.h"
+
+class DNBBreakpoint;
+class MachProcess;
+
+class MachThread
+{
+public:
+
+ MachThread (MachProcess *process, thread_t thread = 0);
+ ~MachThread ();
+
+ MachProcess * Process() { return m_process; }
+ const MachProcess *
+ Process() const { return m_process; }
+ nub_process_t ProcessID() const;
+ void Dump(uint32_t index);
+ thread_t ThreadID() const { return m_tid; }
+ thread_t InferiorThreadID() const;
+
+ uint32_t SequenceID() const { return m_seq_id; }
+ static bool ThreadIDIsValid(thread_t thread);
+ uint32_t Resume();
+ uint32_t Suspend();
+ uint32_t SuspendCount() const { return m_suspendCount; }
+ bool RestoreSuspendCount();
+
+ bool GetRegisterState(int flavor, bool force);
+ bool SetRegisterState(int flavor);
+ uint64_t GetPC(uint64_t failValue = INVALID_NUB_ADDRESS); // Get program counter
+ bool SetPC(uint64_t value); // Set program counter
+ uint64_t GetSP(uint64_t failValue = INVALID_NUB_ADDRESS); // Get stack pointer
+
+ nub_break_t CurrentBreakpoint() const { return m_breakID; }
+ void SetCurrentBreakpoint(nub_break_t breakID) { m_breakID = breakID; }
+ uint32_t EnableHardwareBreakpoint (const DNBBreakpoint *breakpoint);
+ uint32_t EnableHardwareWatchpoint (const DNBBreakpoint *watchpoint);
+ bool DisableHardwareBreakpoint (const DNBBreakpoint *breakpoint);
+ bool DisableHardwareWatchpoint (const DNBBreakpoint *watchpoint);
+
+ nub_state_t GetState();
+ void SetState(nub_state_t state);
+
+ void ThreadWillResume (const DNBThreadResumeAction *thread_action);
+ bool ShouldStop(bool &step_more);
+ bool IsStepping();
+ bool ThreadDidStop();
+ bool NotifyException(MachException::Data& exc);
+ const MachException::Data& GetStopException() { return m_stop_exception; }
+
+ uint32_t GetNumRegistersInSet(int regSet) const;
+ const char * GetRegisterSetName(int regSet) const;
+ const DNBRegisterInfo *
+ GetRegisterInfo(int regSet, int regIndex) const;
+ void DumpRegisterState(int regSet);
+ const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets ) const;
+ bool GetRegisterValue ( uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value );
+ bool SetRegisterValue ( uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value );
+ nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len);
+ nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len);
+ void NotifyBreakpointChanged (const DNBBreakpoint *bp);
+ const char * GetBasicInfoAsString () const;
+ const char * GetName ();
+protected:
+ static bool GetBasicInfo(thread_t threadID, struct thread_basic_info *basic_info);
+
+ bool
+ GetIdentifierInfo ();
+
+// const char *
+// GetDispatchQueueName();
+//
+ MachProcess * m_process; // The process that owns this thread
+ thread_t m_tid; // The thread port for this thread
+ uint32_t m_seq_id; // A Sequential ID that increments with each new thread
+ nub_state_t m_state; // The state of our process
+ PThreadMutex m_state_mutex; // Multithreaded protection for m_state
+ nub_break_t m_breakID; // Breakpoint that this thread is (stopped)/was(running) at (NULL for none)
+ struct thread_basic_info m_basicInfo; // Basic information for a thread used to see if a thread is valid
+ uint32_t m_suspendCount; // The current suspend count
+ MachException::Data m_stop_exception; // The best exception that describes why this thread is stopped
+ DNBArch m_arch; // Arch specific information for register state and more
+ std::vector<DNBRegisterSetInfo> m_regSets; // Register set information for this thread
+#ifdef THREAD_IDENTIFIER_INFO_COUNT
+ thread_identifier_info_data_t m_ident_info;
+ struct proc_threadinfo m_proc_threadinfo;
+ std::string m_dispatch_queue_name;
+#endif
+
+};
+
+typedef std::tr1::shared_ptr<MachThread> MachThreadSP;
+
+#endif
diff --git a/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp
new file mode 100644
index 00000000000..b1ccc74f8e6
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp
@@ -0,0 +1,432 @@
+//===-- MachThreadList.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachThreadList.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "MachProcess.h"
+
+MachThreadList::MachThreadList() :
+ m_threads(),
+ m_threads_mutex(PTHREAD_MUTEX_RECURSIVE)
+{
+}
+
+MachThreadList::~MachThreadList()
+{
+}
+
+// Not thread safe, must lock m_threads_mutex prior to using this function.
+uint32_t
+MachThreadList::GetThreadIndexByID(thread_t tid) const
+{
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->ThreadID() == tid)
+ return idx;
+ }
+ return ~((uint32_t)0);
+}
+
+nub_state_t
+MachThreadList::GetState(thread_t tid)
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ return m_threads[idx]->GetState();
+ return eStateInvalid;
+}
+
+const char *
+MachThreadList::GetName (thread_t tid)
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ return m_threads[idx]->GetName();
+ return NULL;
+}
+
+nub_thread_t
+MachThreadList::SetCurrentThread(thread_t tid)
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ m_current_thread = m_threads[idx];
+
+ if (m_current_thread.get())
+ return m_current_thread->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+
+bool
+MachThreadList::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ return m_threads[idx]->GetStopException().GetStopInfo(stop_info);
+ return false;
+}
+
+bool
+MachThreadList::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info)
+{
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ return ::thread_info (tid, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS;
+}
+
+void
+MachThreadList::DumpThreadStoppedReason(nub_thread_t tid) const
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ m_threads[idx]->GetStopException().DumpStopReason();
+}
+
+const char *
+MachThreadList::GetThreadInfo(nub_thread_t tid) const
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ return m_threads[idx]->GetBasicInfoAsString();
+ return NULL;
+}
+
+bool
+MachThreadList::GetRegisterValue ( nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value ) const
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ return m_threads[idx]->GetRegisterValue(reg_set_idx, reg_idx, reg_value);
+
+ return false;
+}
+
+bool
+MachThreadList::SetRegisterValue ( nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value ) const
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ return m_threads[idx]->SetRegisterValue(reg_set_idx, reg_idx, reg_value);
+
+ return false;
+}
+
+nub_size_t
+MachThreadList::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len)
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ return m_threads[idx]->GetRegisterContext (buf, buf_len);
+ return 0;
+}
+
+nub_size_t
+MachThreadList::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len)
+{
+ uint32_t idx = GetThreadIndexByID(tid);
+ if (idx < m_threads.size())
+ return m_threads[idx]->SetRegisterContext (buf, buf_len);
+ return 0;
+}
+
+nub_size_t
+MachThreadList::NumThreads() const
+{
+ return m_threads.size();
+}
+
+nub_thread_t
+MachThreadList::ThreadIDAtIndex(nub_size_t idx) const
+{
+ if (idx < m_threads.size())
+ return m_threads[idx]->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+nub_thread_t
+MachThreadList::CurrentThreadID ( )
+{
+ MachThreadSP threadSP;
+ CurrentThread(threadSP);
+ if (threadSP.get())
+ return threadSP->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+bool
+MachThreadList::NotifyException(MachException::Data& exc)
+{
+ uint32_t idx = GetThreadIndexByID(exc.thread_port);
+ if (idx < m_threads.size())
+ {
+ m_threads[idx]->NotifyException(exc);
+ return true;
+ }
+ return false;
+}
+
+/*
+MachThreadList::const_iterator
+MachThreadList::FindThreadByID(thread_t tid) const
+{
+ const_iterator pos;
+ const_iterator end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ if (pos->ThreadID() == tid)
+ return pos;
+ }
+ return NULL;
+}
+*/
+void
+MachThreadList::Clear()
+{
+ m_threads.clear();
+}
+
+uint32_t
+MachThreadList::UpdateThreadList(MachProcess *process, bool update)
+{
+ // locker will keep a mutex locked until it goes out of scope
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThreadList::UpdateThreadList (pid = %4.4x, update = %u )", process->ProcessID(), update);
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+
+ if (m_threads.empty() || update)
+ {
+ thread_array_t thread_list = NULL;
+ mach_msg_type_number_t thread_list_count = 0;
+ task_t task = process->Task().TaskPort();
+ DNBError err(::task_threads (task, &thread_list, &thread_list_count), DNBError::MachKernel);
+
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count);
+
+ if (err.Error() == KERN_SUCCESS && thread_list_count > 0)
+ {
+ MachThreadList::collection currThreads;
+ const size_t numOldThreads = m_threads.size();
+ size_t idx;
+ // Iterator through the current thread list and see which threads
+ // we already have in our list (keep them), which ones we don't
+ // (add them), and which ones are not around anymore (remove them).
+ for (idx = 0; idx < thread_list_count; ++idx)
+ {
+ uint32_t existing_idx = 0;
+ if (numOldThreads > 0)
+ existing_idx = GetThreadIndexByID(thread_list[idx]);
+ if (existing_idx < numOldThreads)
+ {
+ // Keep the existing thread class
+ currThreads.push_back(m_threads[existing_idx]);
+ }
+ else
+ {
+ // We don't have this thread, lets add it.
+ MachThreadSP threadSP(new MachThread(process, thread_list[idx]));
+ currThreads.push_back(threadSP);
+ }
+ }
+
+ m_threads.swap(currThreads);
+ m_current_thread.reset();
+
+ // Free the vm memory given to us by ::task_threads()
+ vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t));
+ ::vm_deallocate (::mach_task_self(),
+ (vm_address_t)thread_list,
+ thread_list_size);
+ }
+ }
+ return m_threads.size();
+}
+
+
+void
+MachThreadList::CurrentThread(MachThreadSP& threadSP)
+{
+ // locker will keep a mutex locked until it goes out of scope
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ if (m_current_thread.get() == NULL)
+ {
+ // Figure out which thread is going to be our current thread.
+ // This is currently done by finding the first thread in the list
+ // that has a valid exception.
+ const size_t num_threads = m_threads.size();
+ size_t idx;
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ MachThread *thread = m_threads[idx].get();
+ if (thread->GetStopException().IsValid())
+ {
+ m_current_thread = m_threads[idx];
+ break;
+ }
+ }
+ }
+ threadSP = m_current_thread;
+}
+
+void
+MachThreadList::GetRegisterState(int flavor, bool force)
+{
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->GetRegisterState(flavor, force);
+ }
+}
+
+void
+MachThreadList::SetRegisterState(int flavor)
+{
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->SetRegisterState(flavor);
+ }
+}
+
+void
+MachThreadList::Dump() const
+{
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->Dump(idx);
+ }
+}
+
+
+void
+MachThreadList::ProcessWillResume(MachProcess *process, const DNBThreadResumeActions &thread_actions)
+{
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ MachThread *thread = m_threads[idx].get();
+
+ const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true);
+ // There must always be a thread action for every thread.
+ assert (thread_action);
+ thread->ThreadWillResume (thread_action);
+ }
+}
+
+uint32_t
+MachThreadList::ProcessDidStop(MachProcess *process)
+{
+ // Update our thread list
+ const uint32_t num_threads = UpdateThreadList(process, true);
+ uint32_t idx = 0;
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->ThreadDidStop();
+ }
+ return num_threads;
+}
+
+//----------------------------------------------------------------------
+// Check each thread in our thread list to see if we should notify our
+// client of the current halt in execution.
+//
+// Breakpoints can have callback functions associated with them than
+// can return true to stop, or false to continue executing the inferior.
+//
+// RETURNS
+// true if we should stop and notify our clients
+// false if we should resume our child process and skip notification
+//----------------------------------------------------------------------
+bool
+MachThreadList::ShouldStop(bool &step_more)
+{
+ uint32_t should_stop = false;
+ const uint32_t num_threads = m_threads.size();
+ uint32_t idx = 0;
+ for (idx = 0; !should_stop && idx < num_threads; ++idx)
+ {
+ should_stop = m_threads[idx]->ShouldStop(step_more);
+ }
+ return should_stop;
+}
+
+
+void
+MachThreadList::NotifyBreakpointChanged (const DNBBreakpoint *bp)
+{
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->NotifyBreakpointChanged(bp);
+ }
+}
+
+
+uint32_t
+MachThreadList::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const
+{
+ if (bp != NULL)
+ {
+ uint32_t idx = GetThreadIndexByID(bp->ThreadID());
+ if (idx < m_threads.size())
+ return m_threads[idx]->EnableHardwareBreakpoint(bp);
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+MachThreadList::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const
+{
+ if (bp != NULL)
+ {
+ uint32_t idx = GetThreadIndexByID(bp->ThreadID());
+ if (idx < m_threads.size())
+ return m_threads[idx]->DisableHardwareBreakpoint(bp);
+ }
+ return false;
+}
+
+uint32_t
+MachThreadList::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const
+{
+ if (wp != NULL)
+ {
+ uint32_t idx = GetThreadIndexByID(wp->ThreadID());
+ if (idx < m_threads.size())
+ return m_threads[idx]->EnableHardwareWatchpoint(wp);
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+MachThreadList::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const
+{
+ if (wp != NULL)
+ {
+ uint32_t idx = GetThreadIndexByID(wp->ThreadID());
+ if (idx < m_threads.size())
+ return m_threads[idx]->DisableHardwareWatchpoint(wp);
+ }
+ return false;
+}
+
+
diff --git a/lldb/tools/debugserver/source/MacOSX/MachThreadList.h b/lldb/tools/debugserver/source/MacOSX/MachThreadList.h
new file mode 100644
index 00000000000..b52a97b547b
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachThreadList.h
@@ -0,0 +1,71 @@
+//===-- MachThreadList.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachThreadList_h__
+#define __MachThreadList_h__
+
+#include "MachThread.h"
+
+class DNBThreadResumeActions;
+
+class MachThreadList
+{
+public:
+ MachThreadList ();
+ ~MachThreadList ();
+
+ void Clear ();
+ void Dump () const;
+ void GetRegisterState (int flavor, bool force);
+ void SetRegisterState (int flavor);
+ bool GetRegisterValue (nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value) const;
+ bool SetRegisterValue (nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value) const;
+ nub_size_t GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len);
+ nub_size_t SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len);
+ const char * GetThreadInfo (nub_thread_t tid) const;
+ void ProcessWillResume (MachProcess *process, const DNBThreadResumeActions &thread_actions);
+ uint32_t ProcessDidStop (MachProcess *process);
+ bool NotifyException (MachException::Data& exc);
+ bool ShouldStop (bool &step_more);
+ const char * GetName (thread_t tid);
+ nub_state_t GetState (thread_t tid);
+ nub_thread_t SetCurrentThread (thread_t tid);
+ bool GetThreadStoppedReason (nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const;
+ void DumpThreadStoppedReason (nub_thread_t tid) const;
+ bool GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info);
+ nub_size_t NumThreads () const;
+ nub_thread_t ThreadIDAtIndex (nub_size_t idx) const;
+ nub_thread_t CurrentThreadID ();
+ uint32_t GetThreadIndexByID (thread_t tid) const;
+ void CurrentThread (MachThreadSP& threadSP);
+ void NotifyBreakpointChanged (const DNBBreakpoint *bp);
+ uint32_t EnableHardwareBreakpoint (const DNBBreakpoint *bp) const;
+ bool DisableHardwareBreakpoint (const DNBBreakpoint *bp) const;
+ uint32_t EnableHardwareWatchpoint (const DNBBreakpoint *wp) const;
+ bool DisableHardwareWatchpoint (const DNBBreakpoint *wp) const;
+
+protected:
+ typedef std::vector<MachThreadSP> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ uint32_t UpdateThreadList (MachProcess *process, bool update);
+// const_iterator FindThreadByID (thread_t tid) const;
+
+ collection m_threads;
+ PThreadMutex m_threads_mutex;
+ MachThreadSP m_current_thread;
+};
+
+#endif // #ifndef __MachThreadList_h__
+
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
new file mode 100644
index 00000000000..eb7e10746e2
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
@@ -0,0 +1,186 @@
+//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachVMMemory.h"
+#include "MachVMRegion.h"
+#include "DNBLog.h"
+#include <mach/mach_vm.h>
+
+MachVMMemory::MachVMMemory() :
+ m_page_size (kInvalidPageSize),
+ m_err (0)
+{
+}
+
+MachVMMemory::~MachVMMemory()
+{
+}
+
+nub_size_t
+MachVMMemory::PageSize()
+{
+ if (m_page_size == kInvalidPageSize)
+ {
+ m_err = ::host_page_size( ::mach_host_self(), &m_page_size);
+ if (m_err.Fail())
+ m_page_size = 0;
+ }
+ return m_page_size;
+}
+
+nub_size_t
+MachVMMemory::MaxBytesLeftInPage(nub_addr_t addr, nub_size_t count)
+{
+ const nub_size_t page_size = PageSize();
+ if (page_size > 0)
+ {
+ nub_size_t page_offset = (addr % page_size);
+ nub_size_t bytes_left_in_page = page_size - page_offset;
+ if (count > bytes_left_in_page)
+ count = bytes_left_in_page;
+ }
+ return count;
+}
+
+nub_size_t
+MachVMMemory::Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count)
+{
+ if (data == NULL || data_count == 0)
+ return 0;
+
+ nub_size_t total_bytes_read = 0;
+ nub_addr_t curr_addr = address;
+ uint8_t *curr_data = (uint8_t*)data;
+ while (total_bytes_read < data_count)
+ {
+ mach_vm_size_t curr_size = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_read);
+ mach_msg_type_number_t curr_bytes_read = 0;
+ vm_offset_t vm_memory = NULL;
+ m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read);
+ if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i )", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read);
+
+ if (m_err.Success())
+ {
+ if (curr_bytes_read != curr_size)
+ {
+ if (DNBLogCheckLogBit(LOG_MEMORY))
+ m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i ) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size);
+ }
+ ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read);
+ ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read);
+ total_bytes_read += curr_bytes_read;
+ curr_addr += curr_bytes_read;
+ curr_data += curr_bytes_read;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return total_bytes_read;
+}
+
+
+nub_size_t
+MachVMMemory::Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count)
+{
+ MachVMRegion vmRegion(task);
+
+ nub_size_t total_bytes_written = 0;
+ nub_addr_t curr_addr = address;
+ const uint8_t *curr_data = (const uint8_t*)data;
+
+
+ while (total_bytes_written < data_count)
+ {
+ if (vmRegion.GetRegionForAddress(curr_addr))
+ {
+ mach_vm_size_t curr_data_count = data_count - total_bytes_written;
+ mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr);
+ if (region_bytes_left == 0)
+ {
+ break;
+ }
+ if (curr_data_count > region_bytes_left)
+ curr_data_count = region_bytes_left;
+
+ if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE))
+ {
+ nub_size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count);
+ if (bytes_written <= 0)
+ {
+ // Error should have already be posted by WriteRegion...
+ break;
+ }
+ else
+ {
+ total_bytes_written += bytes_written;
+ curr_addr += bytes_written;
+ curr_data += bytes_written;
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on region for address: [0x%8.8llx-0x%8.8llx)", (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count));
+ break;
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address);
+ break;
+ }
+ }
+
+ return total_bytes_written;
+}
+
+
+nub_size_t
+MachVMMemory::WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count)
+{
+ if (data == NULL || data_count == 0)
+ return 0;
+
+ nub_size_t total_bytes_written = 0;
+ nub_addr_t curr_addr = address;
+ const uint8_t *curr_data = (const uint8_t*)data;
+ while (total_bytes_written < data_count)
+ {
+ mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_written);
+ m_err = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count);
+ if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count);
+
+#if !defined (__i386__) && !defined (__x86_64__)
+ vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH;
+
+ m_err = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value);
+ if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
+ m_err.LogThreaded("::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count);
+#endif
+
+ if (m_err.Success())
+ {
+ total_bytes_written += curr_data_count;
+ curr_addr += curr_data_count;
+ curr_data += curr_data_count;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return total_bytes_written;
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
new file mode 100644
index 00000000000..5635186854a
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
@@ -0,0 +1,40 @@
+//===-- MachVMMemory.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachVMMemory_h__
+#define __MachVMMemory_h__
+
+#include "DNBDefs.h"
+#include "DNBError.h"
+#include <mach/mach.h>
+
+class MachVMMemory
+{
+public:
+ enum { kInvalidPageSize = ~0 };
+ MachVMMemory();
+ ~MachVMMemory();
+ nub_size_t Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count);
+ nub_size_t Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count);
+ nub_size_t PageSize();
+
+protected:
+ nub_size_t MaxBytesLeftInPage(nub_addr_t addr, nub_size_t count);
+
+ nub_size_t WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count);
+ vm_size_t m_page_size;
+ DNBError m_err;
+};
+
+
+#endif // #ifndef __MachVMMemory_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
new file mode 100644
index 00000000000..6299cf4179f
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
@@ -0,0 +1,179 @@
+//===-- MachVMRegion.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachVMRegion.h"
+#include <mach/mach_vm.h>
+#include "DNBLog.h"
+#include <assert.h>
+
+MachVMRegion::MachVMRegion(task_t task) :
+ m_task(task),
+ m_addr(INVALID_NUB_ADDRESS),
+ m_err(),
+ m_start(INVALID_NUB_ADDRESS),
+ m_size(0),
+ m_depth(-1),
+ m_curr_protection(0),
+ m_protection_addr(INVALID_NUB_ADDRESS),
+ m_protection_size(0)
+{
+ memset(&m_data, 0, sizeof(m_data));
+}
+
+MachVMRegion::~MachVMRegion()
+{
+ // Restore any original protections and clear our vars
+ Clear();
+}
+
+void
+MachVMRegion::Clear()
+{
+ RestoreProtections();
+ m_addr = INVALID_NUB_ADDRESS;
+ m_err.Clear();
+ m_start = INVALID_NUB_ADDRESS;
+ m_size = 0;
+ m_depth = -1;
+ memset(&m_data, 0, sizeof(m_data));
+ m_curr_protection = 0;
+ m_protection_addr = INVALID_NUB_ADDRESS;
+ m_protection_size = 0;
+}
+
+bool
+MachVMRegion::SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot)
+{
+ if (ContainsAddress(addr))
+ {
+ mach_vm_size_t prot_size = size;
+ mach_vm_address_t end_addr = EndAddress();
+ if (prot_size > (end_addr - addr))
+ prot_size = end_addr - addr;
+
+ if (prot_size > 0)
+ {
+ if (prot == (m_curr_protection & VM_PROT_ALL))
+ {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE, "MachVMRegion::%s: protections (%u) already sufficient for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, prot, m_task, (uint64_t)addr);
+ // Protections are already set as requested...
+ return true;
+ }
+ else
+ {
+ m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS))
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot);
+ if (m_err.Fail())
+ {
+ // Try again with the ability to create a copy on write region
+ m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot | VM_PROT_COPY);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot | VM_PROT_COPY);
+ }
+ if (m_err.Success())
+ {
+ m_curr_protection = prot;
+ m_protection_addr = addr;
+ m_protection_size = prot_size;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE, "%s: Zero size for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, m_task, (uint64_t)addr);
+ }
+ }
+ return false;
+}
+
+bool
+MachVMRegion::RestoreProtections()
+{
+ if (m_curr_protection != m_data.protection && m_protection_size > 0)
+ {
+ m_err = ::mach_vm_protect (m_task, m_protection_addr, m_protection_size, 0, m_data.protection);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)m_protection_addr, (uint64_t)m_protection_size, 0, m_data.protection);
+ if (m_err.Success())
+ {
+ m_protection_size = 0;
+ m_protection_addr = INVALID_NUB_ADDRESS;
+ m_curr_protection = m_data.protection;
+ return true;
+ }
+ }
+ else
+ {
+ m_err.Clear();
+ return true;
+ }
+
+ return false;
+}
+
+bool
+MachVMRegion::GetRegionForAddress(nub_addr_t addr)
+{
+ // Restore any original protections and clear our vars
+ Clear();
+ m_addr = addr;
+ m_start = addr;
+ m_depth = 1024;
+ mach_msg_type_number_t info_size = kRegionInfoSize;
+ assert(sizeof(info_size) == 4);
+ m_err = ::mach_vm_region_recurse (m_task, &m_start, &m_size, &m_depth, (vm_region_recurse_info_t)&m_data, &info_size);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_region_recurse ( task = 0x%4.4x, address => 0x%8.8llx, size => %llu, nesting_depth => %d, info => %p, infoCnt => %d) addr = 0x%8.8llx ", m_task, (uint64_t)m_start, (uint64_t)m_size, m_depth, &m_data, info_size, (uint64_t)addr);
+ if (m_err.Fail())
+ {
+ return false;
+ }
+ else
+ {
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS))
+ {
+ DNBLogThreaded("info = { prot = %u, "
+ "max_prot = %u, "
+ "inheritance = 0x%8.8x, "
+ "offset = 0x%8.8llx, "
+ "user_tag = 0x%8.8x, "
+ "ref_count = %u, "
+ "shadow_depth = %u, "
+ "ext_pager = %u, "
+ "share_mode = %u, "
+ "is_submap = %d, "
+ "behavior = %d, "
+ "object_id = 0x%8.8x, "
+ "user_wired_count = 0x%4.4x }",
+ m_data.protection,
+ m_data.max_protection,
+ m_data.inheritance,
+ (uint64_t)m_data.offset,
+ m_data.user_tag,
+ m_data.ref_count,
+ m_data.shadow_depth,
+ m_data.external_pager,
+ m_data.share_mode,
+ m_data.is_submap,
+ m_data.behavior,
+ m_data.object_id,
+ m_data.user_wired_count);
+ }
+ }
+
+ m_curr_protection = m_data.protection;
+
+ return true;
+}
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
new file mode 100644
index 00000000000..617e221a57e
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
@@ -0,0 +1,67 @@
+//===-- MachVMRegion.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachVMRegion_h__
+#define __MachVMRegion_h__
+
+#include "DNBDefs.h"
+#include "DNBError.h"
+#include <mach/mach.h>
+
+class MachVMRegion
+{
+public:
+ MachVMRegion(task_t task);
+ ~MachVMRegion();
+
+ void Clear();
+ mach_vm_address_t StartAddress() const { return m_start; }
+ mach_vm_address_t EndAddress() const { return m_start + m_size; }
+ mach_vm_address_t BytesRemaining(mach_vm_address_t addr) const
+ {
+ if (ContainsAddress(addr))
+ return m_size - (addr - m_start);
+ else
+ return 0;
+ }
+ bool ContainsAddress(mach_vm_address_t addr) const
+ {
+ return addr >= StartAddress() && addr < EndAddress();
+ }
+
+ bool SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot);
+ bool RestoreProtections();
+ bool GetRegionForAddress(nub_addr_t addr);
+protected:
+#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64)
+ typedef vm_region_submap_short_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
+#else
+ typedef vm_region_submap_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 };
+#endif
+
+ task_t m_task;
+ mach_vm_address_t m_addr;
+ DNBError m_err;
+ mach_vm_address_t m_start;
+ mach_vm_size_t m_size;
+ natural_t m_depth;
+ RegionInfo m_data;
+ vm_prot_t m_curr_protection; // The current, possibly modified protections. Original value is saved in m_data.protections.
+ mach_vm_address_t m_protection_addr; // The start address at which protections were changed
+ mach_vm_size_t m_protection_size; // The size of memory that had its protections changed
+
+};
+
+#endif // #ifndef __MachVMRegion_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp
new file mode 100644
index 00000000000..6eec80cfd7a
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp
@@ -0,0 +1,2610 @@
+//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__arm__)
+
+#include "MacOSX/arm/DNBArchImpl.h"
+#include "MacOSX/MachProcess.h"
+#include "MacOSX/MachThread.h"
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "DNBRegisterInfo.h"
+#include "DNB.h"
+
+#include <sys/sysctl.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 priveleged 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))
+
+//#define DNB_ARCH_MACH_ARM_DEBUG_SW_STEP 1
+
+static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 };
+static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE };
+
+// ARM constants used during decoding
+#define REG_RD 0
+#define LDM_REGLIST 1
+#define PC_REG 15
+#define PC_REGLIST_BIT 0x8000
+
+// ARM conditions
+#define COND_EQ 0x0
+#define COND_NE 0x1
+#define COND_CS 0x2
+#define COND_HS 0x2
+#define COND_CC 0x3
+#define COND_LO 0x3
+#define COND_MI 0x4
+#define COND_PL 0x5
+#define COND_VS 0x6
+#define COND_VC 0x7
+#define COND_HI 0x8
+#define COND_LS 0x9
+#define COND_GE 0xA
+#define COND_LT 0xB
+#define COND_GT 0xC
+#define COND_LE 0xD
+#define COND_AL 0xE
+#define COND_UNCOND 0xF
+
+#define MASK_CPSR_T (1u << 5)
+#define MASK_CPSR_J (1u << 24)
+
+#define MNEMONIC_STRING_SIZE 32
+#define OPERAND_STRING_SIZE 128
+
+const uint8_t * const
+DNBArchMachARM::SoftwareBreakpointOpcode (nub_size_t byte_size)
+{
+ switch (byte_size)
+ {
+ case 2: return g_thumb_breakpooint_opcode;
+ case 4: return g_arm_breakpoint_opcode;
+ }
+ return NULL;
+}
+
+uint32_t
+DNBArchMachARM::GetCPUType()
+{
+ return CPU_TYPE_ARM;
+}
+
+uint64_t
+DNBArchMachARM::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.gpr.__pc;
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachARM::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.gpr.__pc = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchMachARM::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.gpr.__sp;
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachARM::GetGPRState(bool force)
+{
+ int set = e_regSetGPR;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_THREAD_STATE_COUNT;
+ kern_return_t kret = ::thread_get_state(m_thread->ThreadID(), ARM_THREAD_STATE, (thread_state_t)&m_state.gpr, &count);
+ uint32_t *r = &m_state.gpr.__r[0];
+ DNBLogThreadedIf(LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x regs r0=%8.8x r1=%8.8x r2=%8.8x r3=%8.8x r4=%8.8x r5=%8.8x r6=%8.8x r7=%8.8x r8=%8.8x r9=%8.8x r10=%8.8x r11=%8.8x s12=%8.8x sp=%8.8x lr=%8.8x pc=%8.8x cpsr=%8.8x", m_thread->ThreadID(), ARM_THREAD_STATE, ARM_THREAD_STATE_COUNT, kret,
+ r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15], r[16]);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM::GetVFPState(bool force)
+{
+ int set = e_regSetVFP;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_VFP_STATE_COUNT;
+ kern_return_t kret = ::thread_get_state(m_thread->ThreadID(), ARM_VFP_STATE, (thread_state_t)&m_state.vfp, &count);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM::GetEXCState(bool force)
+{
+ int set = e_regSetEXC;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_EXCEPTION_STATE_COUNT;
+ kern_return_t kret = ::thread_get_state(m_thread->ThreadID(), ARM_EXCEPTION_STATE, (thread_state_t)&m_state.exc, &count);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+static void
+DumpDBGState(const arm_debug_state_t& dbg)
+{
+ uint32_t i = 0;
+ for (i=0; i<16; i++)
+ DNBLogThreadedIf(LOG_STEP, "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]);
+}
+
+kern_return_t
+DNBArchMachARM::GetDBGState(bool force)
+{
+ int set = e_regSetDBG;
+
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_DEBUG_STATE_COUNT;
+ kern_return_t kret = ::thread_get_state(m_thread->ThreadID(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, &count);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM::SetGPRState()
+{
+ int set = e_regSetGPR;
+ kern_return_t kret = ::thread_set_state(m_thread->ThreadID(), ARM_THREAD_STATE, (thread_state_t)&m_state.gpr, ARM_THREAD_STATE_COUNT);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM::SetVFPState()
+{
+ int set = e_regSetVFP;
+ kern_return_t kret = ::thread_set_state (m_thread->ThreadID(), ARM_VFP_STATE, (thread_state_t)&m_state.vfp, ARM_VFP_STATE_COUNT);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM::SetEXCState()
+{
+ int set = e_regSetEXC;
+ kern_return_t kret = ::thread_set_state (m_thread->ThreadID(), ARM_EXCEPTION_STATE, (thread_state_t)&m_state.exc, ARM_EXCEPTION_STATE_COUNT);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM::SetDBGState()
+{
+ int set = e_regSetDBG;
+ kern_return_t kret = ::thread_set_state (m_thread->ThreadID(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE_COUNT);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+void
+DNBArchMachARM::ThreadWillResume()
+{
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ bool step_handled = false;
+ // This is the primary thread, let the arch do anything it needs
+ if (NumSupportedHardwareBreakpoints() > 0)
+ {
+#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP)
+ bool half_step = m_hw_single_chained_step_addr != INVALID_NUB_ADDRESS;
+#endif
+ step_handled = EnableHardwareSingleStep(true) == KERN_SUCCESS;
+#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP)
+ if (!half_step)
+ step_handled = false;
+#endif
+ }
+
+ if (!step_handled)
+ {
+ SetSingleStepSoftwareBreakpoints();
+ }
+ }
+}
+
+bool
+DNBArchMachARM::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateRegisterSetState (e_regSetALL);
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP)
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ // Hardware single step must work if we are going to test software
+ // single step functionality
+ assert(success);
+ if (m_hw_single_chained_step_addr == INVALID_NUB_ADDRESS && m_sw_single_step_next_pc != INVALID_NUB_ADDRESS)
+ {
+ uint32_t sw_step_next_pc = m_sw_single_step_next_pc & 0xFFFFFFFEu;
+ bool sw_step_next_pc_is_thumb = (m_sw_single_step_next_pc & 1) != 0;
+ bool actual_next_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0;
+ if (m_state.gpr.__pc != sw_step_next_pc)
+ {
+ DNBLogError("curr pc = 0x%8.8x - calculated single step target PC was incorrect: 0x%8.8x != 0x%8.8x", m_state.gpr.__pc, sw_step_next_pc, m_state.gpr.__pc);
+ exit(1);
+ }
+ if (actual_next_pc_is_thumb != sw_step_next_pc_is_thumb)
+ {
+ DNBLogError("curr pc = 0x%8.8x - calculated single step calculated mode mismatch: sw single mode = %s != %s",
+ m_state.gpr.__pc,
+ actual_next_pc_is_thumb ? "Thumb" : "ARM",
+ sw_step_next_pc_is_thumb ? "Thumb" : "ARM");
+ exit(1);
+ }
+ m_sw_single_step_next_pc = INVALID_NUB_ADDRESS;
+ }
+#else
+ // Are we software single stepping?
+ if (NUB_BREAK_ID_IS_VALID(m_sw_single_step_break_id) || m_sw_single_step_itblock_break_count)
+ {
+ // Remove any software single stepping breakpoints that we have set
+
+ // Do we have a normal software single step breakpoint?
+ if (NUB_BREAK_ID_IS_VALID(m_sw_single_step_break_id))
+ {
+ DNBLogThreadedIf(LOG_STEP, "%s: removing software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_break_id);
+ success = m_thread->Process()->DisableBreakpoint(m_sw_single_step_break_id, true);
+ m_sw_single_step_break_id = INVALID_NUB_BREAK_ID;
+ }
+
+ // Do we have any Thumb IT breakpoints?
+ if (m_sw_single_step_itblock_break_count > 0)
+ {
+ // See if we hit one of our Thumb IT breakpoints?
+ DNBBreakpoint *step_bp = m_thread->Process()->Breakpoints().FindByAddress(m_state.gpr.__pc);
+
+ if (step_bp)
+ {
+ // We did hit our breakpoint, tell the breakpoint it was
+ // hit so that it can run its callback routine and fixup
+ // the PC.
+ DNBLogThreadedIf(LOG_STEP, "%s: IT software single step breakpoint hit (breakID=%u)", __FUNCTION__, step_bp->GetID());
+ step_bp->BreakpointHit(m_thread->Process()->ProcessID(), m_thread->ThreadID());
+ }
+
+ // Remove all Thumb IT breakpoints
+ for (int i = 0; i < m_sw_single_step_itblock_break_count; i++)
+ {
+ if (NUB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i]))
+ {
+ DNBLogThreadedIf(LOG_STEP, "%s: removing IT software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_itblock_break_id[i]);
+ success = m_thread->Process()->DisableBreakpoint(m_sw_single_step_itblock_break_id[i], true);
+ m_sw_single_step_itblock_break_id[i] = INVALID_NUB_BREAK_ID;
+ }
+ }
+ m_sw_single_step_itblock_break_count = 0;
+
+ // Decode instructions up to the current PC to ensure the internal decoder state is valid for the IT block
+ // The decoder has to decode each instruction in the IT block even if it is not executed so that
+ // the fields are correctly updated
+ DecodeITBlockInstructions(m_state.gpr.__pc);
+ }
+
+ }
+ else
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+#endif
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool
+DNBArchMachARM::StepNotComplete ()
+{
+ if (m_hw_single_chained_step_addr != INVALID_NUB_ADDRESS)
+ {
+ kern_return_t kret = KERN_INVALID_ARGUMENT;
+ kret = GetGPRState(false);
+ if (kret == KERN_SUCCESS)
+ {
+ if (m_state.gpr.__pc == m_hw_single_chained_step_addr)
+ {
+ DNBLogThreadedIf(LOG_STEP, "Need to step some more at 0x%8.8x", m_hw_single_chained_step_addr);
+ return true;
+ }
+ }
+ }
+
+ m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS;
+ return false;
+}
+
+
+void
+DNBArchMachARM::DecodeITBlockInstructions(nub_addr_t curr_pc)
+
+{
+ uint16_t opcode16;
+ uint32_t opcode32;
+ nub_addr_t next_pc_in_itblock;
+ nub_addr_t pc_in_itblock = m_last_decode_pc;
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc);
+
+ // Decode IT block instruction from the instruction following the m_last_decoded_instruction at
+ // PC m_last_decode_pc upto and including the instruction at curr_pc
+ if (m_thread->Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2)
+ {
+ opcode32 = opcode16;
+ pc_in_itblock += 2;
+ // Check for 32 bit thumb opcode and read the upper 16 bits if needed
+ if (((opcode32 & 0xE000) == 0xE000) && opcode32 & 0x1800)
+ {
+ // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for
+ // a 32 bit Thumb opcode
+ // Read bits 31:16 of a 32 bit Thumb opcode
+ if (m_thread->Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2)
+ {
+ pc_in_itblock += 2;
+ // 32 bit thumb opcode
+ opcode32 = (opcode32 << 16) | opcode16;
+ }
+ else
+ {
+ DNBLogError("%s: Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", __FUNCTION__, pc_in_itblock);
+ }
+ }
+ }
+ else
+ {
+ DNBLogError("%s: Error reading 16-bit Thumb instruction at pc=0x%8.8x", __FUNCTION__, pc_in_itblock);
+ }
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: pc_in_itblock=0x%8.8x, curr_pc=0x%8.8x", __FUNCTION__, pc_in_itblock, curr_pc);
+
+ next_pc_in_itblock = pc_in_itblock;
+ while (next_pc_in_itblock <= curr_pc)
+ {
+ arm_error_t decodeError;
+
+ m_last_decode_pc = pc_in_itblock;
+ decodeError = DecodeInstructionUsingDisassembler(pc_in_itblock, m_state.gpr.__cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc_in_itblock);
+
+ pc_in_itblock = next_pc_in_itblock;
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: next_pc_in_itblock=0x%8.8x", __FUNCTION__, next_pc_in_itblock);
+ }
+}
+
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchMachARM::EnableHardwareSingleStep (bool enable)
+{
+ DNBError err;
+ DNBLogThreadedIf(LOG_STEP, "%s( enable = %d )", __FUNCTION__, enable);
+
+ err = GetGPRState(false);
+
+ if (err.Fail())
+ {
+ err.LogThreaded("%s: failed to read the GPR registers", __FUNCTION__);
+ return err.Error();
+ }
+
+ err = GetDBGState(false);
+
+ if (err.Fail())
+ {
+ err.LogThreaded("%s: failed to read the DBG registers", __FUNCTION__);
+ return err.Error();
+ }
+
+ const uint32_t i = 0;
+ if (enable)
+ {
+ m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS;
+
+ // Save our previous state
+ m_dbg_save = m_state.dbg;
+ // Set a breakpoint that will stop when the PC doesn't match the current one!
+ m_state.dbg.__bvr[i] = m_state.gpr.__pc & 0xFFFFFFFCu; // Set the current PC as the breakpoint address
+ m_state.dbg.__bcr[i] = BCR_M_IMVA_MISMATCH | // Stop on address mismatch
+ S_USER | // Stop only in user mode
+ BCR_ENABLE; // Enable this breakpoint
+ if (m_state.gpr.__cpsr & 0x20)
+ {
+ // Thumb breakpoint
+ if (m_state.gpr.__pc & 2)
+ m_state.dbg.__bcr[i] |= BAS_IMVA_2_3;
+ else
+ m_state.dbg.__bcr[i] |= BAS_IMVA_0_1;
+
+ uint16_t opcode;
+ if (sizeof(opcode) == m_thread->Process()->Task().ReadMemory(m_state.gpr.__pc, sizeof(opcode), &opcode))
+ {
+ if (((opcode & 0xE000) == 0xE000) && opcode & 0x1800)
+ {
+ // 32 bit thumb opcode...
+ if (m_state.gpr.__pc & 2)
+ {
+ // We can't take care of a 32 bit thumb instruction single step
+ // with just IVA mismatching. We will need to chain an extra
+ // hardware single step in order to complete this single step...
+ m_hw_single_chained_step_addr = m_state.gpr.__pc + 2;
+ }
+ else
+ {
+ // Extend the number of bits to ignore for the mismatch
+ m_state.dbg.__bcr[i] |= BAS_IMVA_ALL;
+ }
+ }
+ }
+ }
+ else
+ {
+ // ARM breakpoint
+ m_state.dbg.__bcr[i] |= BAS_IMVA_ALL; // Stop when any address bits change
+ }
+
+ DNBLogThreadedIf(LOG_STEP, "%s: BVR%u=0x%8.8x BCR%u=0x%8.8x", __FUNCTION__, i, m_state.dbg.__bvr[i], i, m_state.dbg.__bcr[i]);
+
+ for (uint32_t j=i+1; j<16; ++j)
+ {
+ // Disable all others
+ m_state.dbg.__bvr[j] = 0;
+ m_state.dbg.__bcr[j] = 0;
+ }
+ }
+ else
+ {
+ // Just restore the state we had before we did single stepping
+ m_state.dbg = m_dbg_save;
+ }
+
+ return SetDBGState();
+}
+
+// return 1 if bit "BIT" is set in "value"
+static inline uint32_t bit(uint32_t value, uint32_t bit)
+{
+ return (value >> bit) & 1u;
+}
+
+// return the bitfield "value[msbit:lsbit]".
+static inline uint32_t bits(uint32_t value, uint32_t msbit, uint32_t lsbit)
+{
+ assert(msbit >= lsbit);
+ uint32_t shift_left = sizeof(value) * 8 - 1 - msbit;
+ value <<= shift_left; // shift anything above the msbit off of the unsigned edge
+ value >>= shift_left + lsbit; // shift it back again down to the lsbit (including undoing any shift from above)
+ return value; // return our result
+}
+
+bool
+DNBArchMachARM::ConditionPassed(uint8_t condition, uint32_t cpsr)
+{
+ uint32_t cpsr_n = bit(cpsr, 31); // Negative condition code flag
+ uint32_t cpsr_z = bit(cpsr, 30); // Zero condition code flag
+ uint32_t cpsr_c = bit(cpsr, 29); // Carry condition code flag
+ uint32_t cpsr_v = bit(cpsr, 28); // Overflow condition code flag
+
+ switch (condition) {
+ case COND_EQ: // (0x0)
+ if (cpsr_z == 1) return true;
+ break;
+ case COND_NE: // (0x1)
+ if (cpsr_z == 0) return true;
+ break;
+ case COND_CS: // (0x2)
+ if (cpsr_c == 1) return true;
+ break;
+ case COND_CC: // (0x3)
+ if (cpsr_c == 0) return true;
+ break;
+ case COND_MI: // (0x4)
+ if (cpsr_n == 1) return true;
+ break;
+ case COND_PL: // (0x5)
+ if (cpsr_n == 0) return true;
+ break;
+ case COND_VS: // (0x6)
+ if (cpsr_v == 1) return true;
+ break;
+ case COND_VC: // (0x7)
+ if (cpsr_v == 0) return true;
+ break;
+ case COND_HI: // (0x8)
+ if ((cpsr_c == 1) && (cpsr_z == 0)) return true;
+ break;
+ case COND_LS: // (0x9)
+ if ((cpsr_c == 0) || (cpsr_z == 1)) return true;
+ break;
+ case COND_GE: // (0xA)
+ if (cpsr_n == cpsr_v) return true;
+ break;
+ case COND_LT: // (0xB)
+ if (cpsr_n != cpsr_v) return true;
+ break;
+ case COND_GT: // (0xC)
+ if ((cpsr_z == 0) && (cpsr_n == cpsr_v)) return true;
+ break;
+ case COND_LE: // (0xD)
+ if ((cpsr_z == 1) || (cpsr_n != cpsr_v)) return true;
+ break;
+ default:
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+bool
+DNBArchMachARM::ComputeNextPC(nub_addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, nub_addr_t *targetPC)
+{
+ nub_addr_t myTargetPC, addressWherePCLives;
+ pid_t mypid;
+
+ uint32_t cpsr_c = bit(m_state.gpr.__cpsr, 29); // Carry condition code flag
+
+ uint32_t firstOperand=0, secondOperand=0, shiftAmount=0, secondOperandAfterShift=0, immediateValue=0;
+ uint32_t halfwords=0, baseAddress=0, immediateOffset=0, addressOffsetFromRegister=0, addressOffsetFromRegisterAfterShift;
+ uint32_t baseAddressIndex=INVALID_NUB_HW_INDEX;
+ uint32_t firstOperandIndex=INVALID_NUB_HW_INDEX;
+ uint32_t secondOperandIndex=INVALID_NUB_HW_INDEX;
+ uint32_t addressOffsetFromRegisterIndex=INVALID_NUB_HW_INDEX;
+ uint32_t shiftRegisterIndex=INVALID_NUB_HW_INDEX;
+ uint16_t registerList16, registerList16NoPC;
+ uint8_t registerList8;
+ uint32_t numRegistersToLoad=0;
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: instruction->code=%d", __FUNCTION__, decodedInstruction.instruction->code);
+
+ // Get the following in this switch statement:
+ // - firstOperand, secondOperand, immediateValue, shiftAmount: For arithmetic, logical and move instructions
+ // - baseAddress, immediateOffset, shiftAmount: For LDR
+ // - numRegistersToLoad: For LDM and POP instructions
+ switch (decodedInstruction.instruction->code)
+ {
+ // Arithmetic operations that can change the PC
+ case ARM_INST_ADC:
+ case ARM_INST_ADCS:
+ case ARM_INST_ADD:
+ case ARM_INST_ADDS:
+ case ARM_INST_AND:
+ case ARM_INST_ANDS:
+ case ARM_INST_ASR:
+ case ARM_INST_ASRS:
+ case ARM_INST_BIC:
+ case ARM_INST_BICS:
+ case ARM_INST_EOR:
+ case ARM_INST_EORS:
+ case ARM_INST_ORR:
+ case ARM_INST_ORRS:
+ case ARM_INST_RSB:
+ case ARM_INST_RSBS:
+ case ARM_INST_RSC:
+ case ARM_INST_RSCS:
+ case ARM_INST_SBC:
+ case ARM_INST_SBCS:
+ case ARM_INST_SUB:
+ case ARM_INST_SUBS:
+ switch (decodedInstruction.addressMode)
+ {
+ case ARM_ADDR_DATA_IMM:
+ if (decodedInstruction.numOperands != 3)
+ {
+ DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=1)
+ firstOperandIndex = decodedInstruction.op[1].value; // first operand register index
+ firstOperand = m_state.gpr.__r[firstOperandIndex];
+
+ // Get immediateValue (at index=2)
+ immediateValue = decodedInstruction.op[2].value;
+
+ break;
+
+ case ARM_ADDR_DATA_REG:
+ if (decodedInstruction.numOperands != 3)
+ {
+ DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=1)
+ firstOperandIndex = decodedInstruction.op[1].value; // first operand register index
+ firstOperand = m_state.gpr.__r[firstOperandIndex];
+
+ // Get secondOperand register value (at index=2)
+ secondOperandIndex = decodedInstruction.op[2].value; // second operand register index
+ secondOperand = m_state.gpr.__r[secondOperandIndex];
+
+ break;
+
+ case ARM_ADDR_DATA_SCALED_IMM:
+ if (decodedInstruction.numOperands != 4)
+ {
+ DNBLogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=1)
+ firstOperandIndex = decodedInstruction.op[1].value; // first operand register index
+ firstOperand = m_state.gpr.__r[firstOperandIndex];
+
+ // Get secondOperand register value (at index=2)
+ secondOperandIndex = decodedInstruction.op[2].value; // second operand register index
+ secondOperand = m_state.gpr.__r[secondOperandIndex];
+
+ // Get shiftAmount as immediate value (at index=3)
+ shiftAmount = decodedInstruction.op[3].value;
+
+ break;
+
+
+ case ARM_ADDR_DATA_SCALED_REG:
+ if (decodedInstruction.numOperands != 4)
+ {
+ DNBLogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=1)
+ firstOperandIndex = decodedInstruction.op[1].value; // first operand register index
+ firstOperand = m_state.gpr.__r[firstOperandIndex];
+
+ // Get secondOperand register value (at index=2)
+ secondOperandIndex = decodedInstruction.op[2].value; // second operand register index
+ secondOperand = m_state.gpr.__r[secondOperandIndex];
+
+ // Get shiftAmount from register (at index=3)
+ shiftRegisterIndex = decodedInstruction.op[3].value; // second operand register index
+ shiftAmount = m_state.gpr.__r[shiftRegisterIndex];
+
+ break;
+
+ case THUMB_ADDR_HR_HR:
+ if (decodedInstruction.numOperands != 2)
+ {
+ DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=0)
+ firstOperandIndex = decodedInstruction.op[0].value; // first operand register index
+ firstOperand = m_state.gpr.__r[firstOperandIndex];
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[1].value; // second operand register index
+ secondOperand = m_state.gpr.__r[secondOperandIndex];
+
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ // Logical shifts and move operations that can change the PC
+ case ARM_INST_LSL:
+ case ARM_INST_LSLS:
+ case ARM_INST_LSR:
+ case ARM_INST_LSRS:
+ case ARM_INST_MOV:
+ case ARM_INST_MOVS:
+ case ARM_INST_MVN:
+ case ARM_INST_MVNS:
+ case ARM_INST_ROR:
+ case ARM_INST_RORS:
+ case ARM_INST_RRX:
+ case ARM_INST_RRXS:
+ // In these cases, the firstOperand is always 0, as if it does not exist
+ switch (decodedInstruction.addressMode)
+ {
+ case ARM_ADDR_DATA_IMM:
+ if (decodedInstruction.numOperands != 2)
+ {
+ DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get immediateValue (at index=1)
+ immediateValue = decodedInstruction.op[1].value;
+
+ break;
+
+ case ARM_ADDR_DATA_REG:
+ if (decodedInstruction.numOperands != 2)
+ {
+ DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[1].value; // second operand register index
+ secondOperand = m_state.gpr.__r[secondOperandIndex];
+
+ break;
+
+ case ARM_ADDR_DATA_SCALED_IMM:
+ if (decodedInstruction.numOperands != 3)
+ {
+ DNBLogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[2].value; // second operand register index
+ secondOperand = m_state.gpr.__r[secondOperandIndex];
+
+ // Get shiftAmount as immediate value (at index=2)
+ shiftAmount = decodedInstruction.op[2].value;
+
+ break;
+
+
+ case ARM_ADDR_DATA_SCALED_REG:
+ if (decodedInstruction.numOperands != 3)
+ {
+ DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[1].value; // second operand register index
+ secondOperand = m_state.gpr.__r[secondOperandIndex];
+
+ // Get shiftAmount from register (at index=2)
+ shiftRegisterIndex = decodedInstruction.op[2].value; // second operand register index
+ shiftAmount = m_state.gpr.__r[shiftRegisterIndex];
+
+ break;
+
+ case THUMB_ADDR_HR_HR:
+ if (decodedInstruction.numOperands != 2)
+ {
+ DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[1].value; // second operand register index
+ secondOperand = m_state.gpr.__r[secondOperandIndex];
+
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ // Simple branches, used to hop around within a routine
+ case ARM_INST_B:
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ break;
+
+ // Branch-and-link, used to call ARM subroutines
+ case ARM_INST_BL:
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ break;
+
+ // Branch-and-link with exchange, used to call opposite-mode subroutines
+ case ARM_INST_BLX:
+ if ((decodedInstruction.addressMode == ARM_ADDR_BRANCH_IMM) ||
+ (decodedInstruction.addressMode == THUMB_ADDR_UNCOND))
+ {
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ }
+ else // addressMode == ARM_ADDR_BRANCH_REG
+ {
+ // Unknown target unless we're branching to the PC itself,
+ // although this may not work properly with BLX
+ if (decodedInstruction.op[REG_RD].value == PC_REG)
+ {
+ // this should (almost) never happen
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ }
+
+ // Get the branch address and return
+ if (decodedInstruction.numOperands != 1)
+ {
+ DNBLogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get branch address in register (at index=0)
+ *targetPC = m_state.gpr.__r[decodedInstruction.op[0].value];
+ return true;
+ }
+ break;
+
+ // Branch with exchange, used to hop to opposite-mode code
+ // Branch to Jazelle code, used to execute Java; included here since it
+ // acts just like BX unless the Jazelle unit is active and JPC is
+ // already loaded into it.
+ case ARM_INST_BX:
+ case ARM_INST_BXJ:
+ // Unknown target unless we're branching to the PC itself,
+ // although this can never switch to Thumb mode and is
+ // therefore pretty much useless
+ if (decodedInstruction.op[REG_RD].value == PC_REG)
+ {
+ // this should (almost) never happen
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ }
+
+ // Get the branch address and return
+ if (decodedInstruction.numOperands != 1)
+ {
+ DNBLogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get branch address in register (at index=0)
+ *targetPC = m_state.gpr.__r[decodedInstruction.op[0].value];
+ return true;
+ break;
+
+ // Compare and branch on zero/non-zero (Thumb-16 only)
+ // Unusual condition check built into the instruction
+ case ARM_INST_CBZ:
+ case ARM_INST_CBNZ:
+ // Branch address is known at compile time
+ // Get the branch address and return
+ if (decodedInstruction.numOperands != 2)
+ {
+ DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get branch address as an immediate value (at index=1)
+ *targetPC = decodedInstruction.op[1].value;
+ return true;
+ break;
+
+ // Load register can be used to load PC, usually with a function pointer
+ case ARM_INST_LDR:
+ if (decodedInstruction.op[REG_RD].value != PC_REG)
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+ switch (decodedInstruction.addressMode)
+ {
+ case ARM_ADDR_LSWUB_IMM:
+ case ARM_ADDR_LSWUB_IMM_PRE:
+ case ARM_ADDR_LSWUB_IMM_POST:
+ if (decodedInstruction.numOperands != 3)
+ {
+ DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get baseAddress from register (at index=1)
+ baseAddressIndex = decodedInstruction.op[1].value;
+ baseAddress = m_state.gpr.__r[baseAddressIndex];
+
+ // Get immediateOffset (at index=2)
+ immediateOffset = decodedInstruction.op[2].value;
+ break;
+
+ case ARM_ADDR_LSWUB_REG:
+ case ARM_ADDR_LSWUB_REG_PRE:
+ case ARM_ADDR_LSWUB_REG_POST:
+ if (decodedInstruction.numOperands != 3)
+ {
+ DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get baseAddress from register (at index=1)
+ baseAddressIndex = decodedInstruction.op[1].value;
+ baseAddress = m_state.gpr.__r[baseAddressIndex];
+
+ // Get immediateOffset from register (at index=2)
+ addressOffsetFromRegisterIndex = decodedInstruction.op[2].value;
+ addressOffsetFromRegister = m_state.gpr.__r[addressOffsetFromRegisterIndex];
+
+ break;
+
+ case ARM_ADDR_LSWUB_SCALED:
+ case ARM_ADDR_LSWUB_SCALED_PRE:
+ case ARM_ADDR_LSWUB_SCALED_POST:
+ if (decodedInstruction.numOperands != 4)
+ {
+ DNBLogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get baseAddress from register (at index=1)
+ baseAddressIndex = decodedInstruction.op[1].value;
+ baseAddress = m_state.gpr.__r[baseAddressIndex];
+
+ // Get immediateOffset from register (at index=2)
+ addressOffsetFromRegisterIndex = decodedInstruction.op[2].value;
+ addressOffsetFromRegister = m_state.gpr.__r[addressOffsetFromRegisterIndex];
+
+ // Get shiftAmount (at index=3)
+ shiftAmount = decodedInstruction.op[3].value;
+
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ // 32b load multiple operations can load the PC along with everything else,
+ // usually to return from a function call
+ case ARM_INST_LDMDA:
+ case ARM_INST_LDMDB:
+ case ARM_INST_LDMIA:
+ case ARM_INST_LDMIB:
+ if (decodedInstruction.op[LDM_REGLIST].value & PC_REGLIST_BIT)
+ {
+ if (decodedInstruction.numOperands != 2)
+ {
+ DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get baseAddress from register (at index=0)
+ baseAddressIndex = decodedInstruction.op[0].value;
+ baseAddress = m_state.gpr.__r[baseAddressIndex];
+
+ // Get registerList from register (at index=1)
+ registerList16 = (uint16_t)decodedInstruction.op[1].value;
+
+ // Count number of registers to load in the multiple register list excluding the PC
+ registerList16NoPC = registerList16&0x3FFF; // exclude the PC
+ numRegistersToLoad=0;
+ for (int i = 0; i < 16; i++)
+ {
+ if (registerList16NoPC & 0x1) numRegistersToLoad++;
+ registerList16NoPC = registerList16NoPC >> 1;
+ }
+ }
+ else
+ {
+ DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+ break;
+
+ // Normal 16-bit LD multiple can't touch R15, but POP can
+ case ARM_INST_POP: // Can also get the PC & updates SP
+ // Get baseAddress from SP (at index=0)
+ baseAddress = m_state.gpr.__sp;
+
+ if (decodedInstruction.thumb16b)
+ {
+ // Get registerList from register (at index=0)
+ registerList8 = (uint8_t)decodedInstruction.op[0].value;
+
+ // Count number of registers to load in the multiple register list
+ numRegistersToLoad=0;
+ for (int i = 0; i < 8; i++)
+ {
+ if (registerList8 & 0x1) numRegistersToLoad++;
+ registerList8 = registerList8 >> 1;
+ }
+ }
+ else
+ {
+ // Get registerList from register (at index=0)
+ registerList16 = (uint16_t)decodedInstruction.op[0].value;
+
+ // Count number of registers to load in the multiple register list excluding the PC
+ registerList16NoPC = registerList16&0x3FFF; // exclude the PC
+ numRegistersToLoad=0;
+ for (int i = 0; i < 16; i++)
+ {
+ if (registerList16NoPC & 0x1) numRegistersToLoad++;
+ registerList16NoPC = registerList16NoPC >> 1;
+ }
+ }
+ break;
+
+ // 16b TBB and TBH instructions load a jump address from a table
+ case ARM_INST_TBB:
+ case ARM_INST_TBH:
+ // Get baseAddress from register (at index=0)
+ baseAddressIndex = decodedInstruction.op[0].value;
+ baseAddress = m_state.gpr.__r[baseAddressIndex];
+
+ // Get immediateOffset from register (at index=1)
+ addressOffsetFromRegisterIndex = decodedInstruction.op[1].value;
+ addressOffsetFromRegister = m_state.gpr.__r[addressOffsetFromRegisterIndex];
+ break;
+
+ // ThumbEE branch-to-handler instructions: Jump to handlers at some offset
+ // from a special base pointer register (which is unknown at disassembly time)
+ case ARM_INST_HB:
+ case ARM_INST_HBP:
+// TODO: ARM_INST_HB, ARM_INST_HBP
+ break;
+
+ case ARM_INST_HBL:
+ case ARM_INST_HBLP:
+// TODO: ARM_INST_HBL, ARM_INST_HBLP
+ break;
+
+ // Breakpoint and software interrupt jump to interrupt handler (always ARM)
+ case ARM_INST_BKPT:
+ case ARM_INST_SMC:
+ case ARM_INST_SVC:
+
+ // Return from exception, obviously modifies PC [interrupt only!]
+ case ARM_INST_RFEDA:
+ case ARM_INST_RFEDB:
+ case ARM_INST_RFEIA:
+ case ARM_INST_RFEIB:
+
+ // Other instructions either can't change R15 or are "undefined" if you do,
+ // so no sane compiler should ever generate them & we don't care here.
+ // Also, R15 can only legally be used in a read-only manner for the
+ // various ARM addressing mode (to get PC-relative addressing of constants),
+ // but can NOT be used with any of the update modes.
+ default:
+ DNBLogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code);
+ return false;
+ break;
+ }
+
+ // Adjust PC if PC is one of the input operands
+ if (baseAddressIndex == PC_REG)
+ {
+ if (currentPCIsThumb)
+ baseAddress += 4;
+ else
+ baseAddress += 8;
+ }
+
+ if (firstOperandIndex == PC_REG)
+ {
+ if (currentPCIsThumb)
+ firstOperand += 4;
+ else
+ firstOperand += 8;
+ }
+
+ if (secondOperandIndex == PC_REG)
+ {
+ if (currentPCIsThumb)
+ secondOperand += 4;
+ else
+ secondOperand += 8;
+ }
+
+ if (addressOffsetFromRegisterIndex == PC_REG)
+ {
+ if (currentPCIsThumb)
+ addressOffsetFromRegister += 4;
+ else
+ addressOffsetFromRegister += 8;
+ }
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE,
+ "%s: firstOperand=%8.8x, secondOperand=%8.8x, immediateValue = %d, shiftAmount = %d, baseAddress = %8.8x, addressOffsetFromRegister = %8.8x, immediateOffset = %d, numRegistersToLoad = %d",
+ __FUNCTION__,
+ firstOperand,
+ secondOperand,
+ immediateValue,
+ shiftAmount,
+ baseAddress,
+ addressOffsetFromRegister,
+ immediateOffset,
+ numRegistersToLoad);
+
+
+ // Calculate following values after applying shiftAmount:
+ // - immediateOffsetAfterShift, secondOperandAfterShift
+
+ switch (decodedInstruction.scaleMode)
+ {
+ case ARM_SCALE_NONE:
+ addressOffsetFromRegisterAfterShift = addressOffsetFromRegister;
+ secondOperandAfterShift = secondOperand;
+ break;
+
+ case ARM_SCALE_LSL: // Logical shift left
+ addressOffsetFromRegisterAfterShift = addressOffsetFromRegister << shiftAmount;
+ secondOperandAfterShift = secondOperand << shiftAmount;
+ break;
+
+ case ARM_SCALE_LSR: // Logical shift right
+ addressOffsetFromRegisterAfterShift = addressOffsetFromRegister >> shiftAmount;
+ secondOperandAfterShift = secondOperand >> shiftAmount;
+ break;
+
+ case ARM_SCALE_ASR: // Arithmetic shift right
+ asm("mov %0, %1, asr %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount));
+ asm("mov %0, %1, asr %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount));
+ break;
+
+ case ARM_SCALE_ROR: // Rotate right
+ asm("mov %0, %1, ror %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount));
+ asm("mov %0, %1, ror %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount));
+ break;
+
+ case ARM_SCALE_RRX: // Rotate right, pulling in carry (1-bit shift only)
+ asm("mov %0, %1, rrx" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister));
+ asm("mov %0, %1, rrx" : "=r" (secondOperandAfterShift) : "r" (secondOperand));
+ break;
+ }
+
+ // Emulate instruction to calculate targetPC
+ // All branches are already handled in the first switch statement. A branch should not reach this switch
+ switch (decodedInstruction.instruction->code)
+ {
+ // Arithmetic operations that can change the PC
+ case ARM_INST_ADC:
+ case ARM_INST_ADCS:
+ // Add with Carry
+ *targetPC = firstOperand + (secondOperandAfterShift + immediateValue) + cpsr_c;
+ break;
+
+ case ARM_INST_ADD:
+ case ARM_INST_ADDS:
+ *targetPC = firstOperand + (secondOperandAfterShift + immediateValue);
+ break;
+
+ case ARM_INST_AND:
+ case ARM_INST_ANDS:
+ *targetPC = firstOperand & (secondOperandAfterShift + immediateValue);
+ break;
+
+ case ARM_INST_ASR:
+ case ARM_INST_ASRS:
+ asm("mov %0, %1, asr %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_BIC:
+ case ARM_INST_BICS:
+ asm("bic %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_EOR:
+ case ARM_INST_EORS:
+ asm("eor %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_ORR:
+ case ARM_INST_ORRS:
+ asm("orr %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_RSB:
+ case ARM_INST_RSBS:
+ asm("rsb %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_RSC:
+ case ARM_INST_RSCS:
+ myTargetPC = secondOperandAfterShift - (firstOperand + !cpsr_c);
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_SBC:
+ case ARM_INST_SBCS:
+ asm("sbc %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue + !cpsr_c));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_SUB:
+ case ARM_INST_SUBS:
+ asm("sub %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ // Logical shifts and move operations that can change the PC
+ case ARM_INST_LSL:
+ case ARM_INST_LSLS:
+ case ARM_INST_LSR:
+ case ARM_INST_LSRS:
+ case ARM_INST_MOV:
+ case ARM_INST_MOVS:
+ case ARM_INST_ROR:
+ case ARM_INST_RORS:
+ case ARM_INST_RRX:
+ case ARM_INST_RRXS:
+ myTargetPC = secondOperandAfterShift + immediateValue;
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_MVN:
+ case ARM_INST_MVNS:
+ myTargetPC = !(secondOperandAfterShift + immediateValue);
+ *targetPC = myTargetPC;
+ break;
+
+ // Load register can be used to load PC, usually with a function pointer
+ case ARM_INST_LDR:
+ switch (decodedInstruction.addressMode) {
+ case ARM_ADDR_LSWUB_IMM_POST:
+ case ARM_ADDR_LSWUB_REG_POST:
+ case ARM_ADDR_LSWUB_SCALED_POST:
+ addressWherePCLives = baseAddress;
+ break;
+
+ case ARM_ADDR_LSWUB_IMM:
+ case ARM_ADDR_LSWUB_REG:
+ case ARM_ADDR_LSWUB_SCALED:
+ case ARM_ADDR_LSWUB_IMM_PRE:
+ case ARM_ADDR_LSWUB_REG_PRE:
+ case ARM_ADDR_LSWUB_SCALED_PRE:
+ addressWherePCLives = baseAddress + (addressOffsetFromRegisterAfterShift + immediateOffset);
+ break;
+
+ default:
+ break;
+ }
+
+ mypid = m_thread->ProcessID();
+ if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t))
+ {
+ DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ // 32b load multiple operations can load the PC along with everything else,
+ // usually to return from a function call
+ case ARM_INST_LDMDA:
+ mypid = m_thread->ProcessID();
+ addressWherePCLives = baseAddress;
+ if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t))
+ {
+ DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_LDMDB:
+ mypid = m_thread->ProcessID();
+ addressWherePCLives = baseAddress - 4;
+ if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t))
+ {
+ DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_LDMIB:
+ mypid = m_thread->ProcessID();
+ addressWherePCLives = baseAddress + numRegistersToLoad*4 + 4;
+ if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t))
+ {
+ DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_LDMIA: // same as pop
+ // Normal 16-bit LD multiple can't touch R15, but POP can
+ case ARM_INST_POP: // Can also get the PC & updates SP
+ mypid = m_thread->ProcessID();
+ addressWherePCLives = baseAddress + numRegistersToLoad*4;
+ if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t))
+ {
+ DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ // 16b TBB and TBH instructions load a jump address from a table
+ case ARM_INST_TBB:
+ mypid = m_thread->ProcessID();
+ addressWherePCLives = baseAddress + addressOffsetFromRegisterAfterShift;
+ if (DNBProcessMemoryRead(mypid, addressWherePCLives, 1, &halfwords) != 1)
+ {
+ DNBLogError("Could not read memory at %8.8x to get targetPC when processing the TBB instruction!", addressWherePCLives);
+ return false;
+ }
+ // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords
+ *targetPC = (currentPC + 4) + 2*halfwords;
+ break;
+
+ case ARM_INST_TBH:
+ mypid = m_thread->ProcessID();
+ addressWherePCLives = ((baseAddress + (addressOffsetFromRegisterAfterShift << 1)) & ~0x1);
+ if (DNBProcessMemoryRead(mypid, addressWherePCLives, 2, &halfwords) != 2)
+ {
+ DNBLogError("Could not read memory at %8.8x to get targetPC when processing the TBH instruction!", addressWherePCLives);
+ return false;
+ }
+ // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords
+ *targetPC = (currentPC + 4) + 2*halfwords;
+ break;
+
+ // ThumbEE branch-to-handler instructions: Jump to handlers at some offset
+ // from a special base pointer register (which is unknown at disassembly time)
+ case ARM_INST_HB:
+ case ARM_INST_HBP:
+ // TODO: ARM_INST_HB, ARM_INST_HBP
+ break;
+
+ case ARM_INST_HBL:
+ case ARM_INST_HBLP:
+ // TODO: ARM_INST_HBL, ARM_INST_HBLP
+ break;
+
+ // Breakpoint and software interrupt jump to interrupt handler (always ARM)
+ case ARM_INST_BKPT:
+ case ARM_INST_SMC:
+ case ARM_INST_SVC:
+ // TODO: ARM_INST_BKPT, ARM_INST_SMC, ARM_INST_SVC
+ break;
+
+ // Return from exception, obviously modifies PC [interrupt only!]
+ case ARM_INST_RFEDA:
+ case ARM_INST_RFEDB:
+ case ARM_INST_RFEIA:
+ case ARM_INST_RFEIB:
+ // TODO: ARM_INST_RFEDA, ARM_INST_RFEDB, ARM_INST_RFEIA, ARM_INST_RFEIB
+ break;
+
+ // Other instructions either can't change R15 or are "undefined" if you do,
+ // so no sane compiler should ever generate them & we don't care here.
+ // Also, R15 can only legally be used in a read-only manner for the
+ // various ARM addressing mode (to get PC-relative addressing of constants),
+ // but can NOT be used with any of the update modes.
+ default:
+ DNBLogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code);
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+void
+DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup(nub_addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, nub_addr_t *nextPC, bool *nextPCIsThumb)
+{
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup() called");
+
+ nub_addr_t targetPC = INVALID_NUB_ADDRESS;
+ uint32_t registerValue;
+ arm_error_t decodeError;
+ nub_addr_t currentPCInITBlock, nextPCInITBlock;
+ int i;
+ bool last_decoded_instruction_executes = true;
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: default nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM");
+
+ // Update *nextPC and *nextPCIsThumb for special cases
+ if (m_last_decode_thumb.itBlockRemaining) // we are in an IT block
+ {
+ // Set the nextPC to the PC of the instruction which will execute in the IT block
+ // If none of the instruction execute in the IT block based on the condition flags,
+ // then point to the instruction immediately following the IT block
+ const int itBlockRemaining = m_last_decode_thumb.itBlockRemaining;
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: itBlockRemaining=%8.8x", __FUNCTION__, itBlockRemaining);
+
+ // Determine the PC at which the next instruction resides
+ if (m_last_decode_arm.thumb16b)
+ currentPCInITBlock = currentPC + 2;
+ else
+ currentPCInITBlock = currentPC + 4;
+
+ for (i = 0; i < itBlockRemaining; i++)
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: currentPCInITBlock=%8.8x", __FUNCTION__, currentPCInITBlock);
+ decodeError = DecodeInstructionUsingDisassembler(currentPCInITBlock, cpsr, &m_last_decode_arm, &m_last_decode_thumb, &nextPCInITBlock);
+
+ if (decodeError != ARM_SUCCESS)
+ DNBLogError("unable to disassemble instruction at 0x%8.8lx", currentPCInITBlock);
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: condition=%d", __FUNCTION__, m_last_decode_arm.condition);
+ if (ConditionPassed(m_last_decode_arm.condition, cpsr))
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Condition codes matched for instruction %d", __FUNCTION__, i);
+ break; // break from the for loop
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Condition codes DID NOT matched for instruction %d", __FUNCTION__, i);
+ }
+
+ // update currentPC and nextPCInITBlock
+ currentPCInITBlock = nextPCInITBlock;
+ }
+
+ if (i == itBlockRemaining) // We came out of the IT block without executing any instructions
+ last_decoded_instruction_executes = false;
+
+ *nextPC = currentPCInITBlock;
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: After IT block step-through: *nextPC=%8.8x", __FUNCTION__, *nextPC);
+ }
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE,
+ "%s: cpsr = %8.8x, thumb16b = %d, thumb = %d, branch = %d, conditional = %d, knownTarget = %d, links = %d, canSwitchMode = %d, doesSwitchMode = %d",
+ __FUNCTION__,
+ cpsr,
+ m_last_decode_arm.thumb16b,
+ m_last_decode_arm.thumb,
+ m_last_decode_arm.branch,
+ m_last_decode_arm.conditional,
+ m_last_decode_arm.knownTarget,
+ m_last_decode_arm.links,
+ m_last_decode_arm.canSwitchMode,
+ m_last_decode_arm.doesSwitchMode);
+
+
+ if (last_decoded_instruction_executes && // Was this a conditional instruction that did execute?
+ m_last_decode_arm.branch && // Can this instruction change the PC?
+ (m_last_decode_arm.instruction->code != ARM_INST_SVC)) // If this instruction is not an SVC instruction
+ {
+ // Set targetPC. Compute if needed.
+ if (m_last_decode_arm.knownTarget)
+ {
+ // Fixed, known PC-relative
+ targetPC = m_last_decode_arm.targetPC;
+ }
+ else
+ {
+ // if targetPC is not known at compile time (PC-relative target), compute targetPC
+ if (!ComputeNextPC(currentPC, m_last_decode_arm, currentPCIsThumb, &targetPC))
+ {
+ DNBLogError("%s: Unable to compute targetPC for instruction at 0x%8.8lx", __FUNCTION__, currentPC);
+ targetPC = INVALID_NUB_ADDRESS;
+ }
+ }
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: targetPC=0x%8.8x, cpsr=0x%8.8x, condition=0x%hhx", __FUNCTION__, targetPC, cpsr, m_last_decode_arm.condition);
+
+ // Refine nextPC computation
+ if ((m_last_decode_arm.instruction->code == ARM_INST_CBZ) ||
+ (m_last_decode_arm.instruction->code == ARM_INST_CBNZ))
+ {
+ // Compare and branch on zero/non-zero (Thumb-16 only)
+ // Unusual condition check built into the instruction
+ registerValue = m_state.gpr.__r[m_last_decode_arm.op[REG_RD].value];
+
+ if (m_last_decode_arm.instruction->code == ARM_INST_CBZ)
+ {
+ if (registerValue == 0)
+ *nextPC = targetPC;
+ }
+ else
+ {
+ if (registerValue != 0)
+ *nextPC = targetPC;
+ }
+ }
+ else if (m_last_decode_arm.conditional) // Is the change conditional on flag results?
+ {
+ if (ConditionPassed(m_last_decode_arm.condition, cpsr)) // conditions match
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Condition matched!", __FUNCTION__);
+ *nextPC = targetPC;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Condition did not match!", __FUNCTION__);
+ }
+ }
+ else
+ {
+ *nextPC = targetPC;
+ }
+
+ // Refine nextPCIsThumb computation
+ if (m_last_decode_arm.doesSwitchMode)
+ {
+ *nextPCIsThumb = !currentPCIsThumb;
+ }
+ else if (m_last_decode_arm.canSwitchMode)
+ {
+ // Legal to switch ARM <--> Thumb mode with this branch
+ // dependent on bit[0] of targetPC
+ *nextPCIsThumb = (*nextPC & 1u) != 0;
+ }
+ else
+ {
+ *nextPCIsThumb = currentPCIsThumb;
+ }
+ }
+
+ DNBLogThreadedIf(LOG_STEP, "%s: calculated nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM");
+}
+
+
+arm_error_t
+DNBArchMachARM::DecodeInstructionUsingDisassembler(nub_addr_t curr_pc, uint32_t curr_cpsr, arm_decoded_instruction_t *decodedInstruction, thumb_static_data_t *thumbStaticData, nub_addr_t *next_pc)
+{
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: pc=0x%8.8x, cpsr=0x%8.8x", __FUNCTION__, curr_pc, curr_cpsr);
+
+ const uint32_t isetstate_mask = MASK_CPSR_T | MASK_CPSR_J;
+ const uint32_t curr_isetstate = curr_cpsr & isetstate_mask;
+ uint32_t opcode32;
+ nub_addr_t nextPC = curr_pc;
+ arm_error_t decodeReturnCode = ARM_SUCCESS;
+
+ m_last_decode_pc = curr_pc;
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc);
+
+ switch (curr_isetstate) {
+ case 0x0: // ARM Instruction
+ // Read the ARM opcode
+ if (m_thread->Process()->Task().ReadMemory(curr_pc, 4, &opcode32) != 4)
+ {
+ DNBLogError("unable to read opcode bits 31:0 for an ARM opcode at 0x%8.8lx", curr_pc);
+ decodeReturnCode = ARM_ERROR;
+ }
+ else
+ {
+ nextPC += 4;
+ decodeReturnCode = ArmDisassembler((uint64_t)curr_pc, opcode32, false, decodedInstruction, NULL, 0, NULL, 0);
+
+ if (decodeReturnCode != ARM_SUCCESS)
+ DNBLogError("Unable to decode ARM instruction 0x%8.8x at 0x%8.8lx", opcode32, curr_pc);
+ }
+ break;
+
+ case 0x20: // Thumb Instruction
+ uint16_t opcode16;
+ // Read the a 16 bit Thumb opcode
+ if (m_thread->Process()->Task().ReadMemory(curr_pc, 2, &opcode16) != 2)
+ {
+ DNBLogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc);
+ decodeReturnCode = ARM_ERROR;
+ }
+ else
+ {
+ nextPC += 2;
+ opcode32 = opcode16;
+
+ decodeReturnCode = ThumbDisassembler((uint64_t)curr_pc, opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0);
+
+ switch (decodeReturnCode) {
+ case ARM_SKIP:
+ // 32 bit thumb opcode
+ nextPC += 2;
+ if (m_thread->Process()->Task().ReadMemory(curr_pc+2, 2, &opcode16) != 2)
+ {
+ DNBLogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc+2);
+ }
+ else
+ {
+ opcode32 = (opcode32 << 16) | opcode16;
+
+ decodeReturnCode = ThumbDisassembler((uint64_t)(curr_pc+2), opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0);
+
+ if (decodeReturnCode != ARM_SUCCESS)
+ DNBLogError("Unable to decode 2nd half of Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc+2);
+ break;
+ }
+ break;
+
+ case ARM_SUCCESS:
+ // 16 bit thumb opcode; at this point we are done decoding the opcode
+ break;
+
+ default:
+ DNBLogError("Unable to decode Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc);
+ decodeReturnCode = ARM_ERROR;
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (next_pc)
+ *next_pc = nextPC;
+
+ return decodeReturnCode;
+}
+
+nub_bool_t
+DNBArchMachARM::BreakpointHit(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton)
+{
+ nub_addr_t bkpt_pc = (nub_addr_t)baton;
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s(pid = %i, tid = %4.4x, breakID = %u, baton = %p): Setting PC to 0x%8.8x", __FUNCTION__, pid, tid, breakID, baton, bkpt_pc);
+ return DNBThreadSetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_PC, bkpt_pc);
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchMachARM::SetSingleStepSoftwareBreakpoints()
+{
+ DNBError err;
+ err = GetGPRState(false);
+
+ if (err.Fail())
+ {
+ err.LogThreaded("%s: failed to read the GPR registers", __FUNCTION__);
+ return err.Error();
+ }
+
+ nub_addr_t curr_pc = m_state.gpr.__pc;
+ uint32_t curr_cpsr = m_state.gpr.__cpsr;
+ nub_addr_t next_pc = curr_pc;
+
+ bool curr_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0;
+ bool next_pc_is_thumb = curr_pc_is_thumb;
+
+ uint32_t curr_itstate = ((curr_cpsr & 0x6000000) >> 25) | ((curr_cpsr & 0xFC00) >> 8);
+ bool inITBlock = (curr_itstate & 0xF) ? 1 : 0;
+ bool lastInITBlock = ((curr_itstate & 0xF) == 0x8) ? 1 : 0;
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: curr_pc=0x%8.8x (%s), curr_itstate=0x%x, inITBlock=%d, lastInITBlock=%d", __FUNCTION__, curr_pc, curr_pc_is_thumb ? "Thumb" : "ARM", curr_itstate, inITBlock, lastInITBlock);
+
+ // If the instruction is not in the IT block, then decode using the Disassembler and compute next_pc
+ if (!inITBlock)
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Decoding an instruction NOT in the IT block", __FUNCTION__);
+
+ arm_error_t decodeReturnCode = DecodeInstructionUsingDisassembler(curr_pc, curr_cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc);
+
+ if (decodeReturnCode != ARM_SUCCESS)
+ {
+ err = KERN_INVALID_ARGUMENT;
+ DNBLogError("DNBArchMachARM::SetSingleStepSoftwareBreakpoints: Unable to disassemble instruction at 0x%8.8lx", curr_pc);
+ }
+ }
+ else
+ {
+ next_pc = curr_pc + ((m_last_decode_arm.thumb16b) ? 2 : 4);
+ }
+
+ // Instruction is NOT in the IT block OR
+ if (!inITBlock)
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: normal instruction", __FUNCTION__);
+ EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb);
+ }
+ else if (inITBlock && !m_last_decode_arm.setsFlags)
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: IT instruction that doesn't set flags", __FUNCTION__);
+ EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb);
+ }
+ else if (lastInITBlock && m_last_decode_arm.branch)
+ {
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: IT instruction which last in the IT block and is a branch", __FUNCTION__);
+ EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb);
+ }
+ else
+ {
+ // Instruction is in IT block and can modify the CPSR flags
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: IT instruction that sets flags", __FUNCTION__);
+
+ // NOTE: When this point of code is reached, the instruction at curr_pc has already been decoded
+ // inside the function ThreadDidStop(). Therefore m_last_decode_arm, m_last_decode_thumb
+ // reflect the decoded instruction at curr_pc
+
+ // If we find an instruction inside the IT block which will set/modify the condition flags (NZCV bits in CPSR),
+ // we set breakpoints at all remaining instructions inside the IT block starting from the instruction immediately
+ // following this one AND a breakpoint at the instruction immediately following the IT block. We do this because
+ // we cannot determine the next_pc until the instruction at which we are currently stopped executes. Hence we
+ // insert (m_last_decode_thumb.itBlockRemaining+1) 16-bit Thumb breakpoints at consecutive memory locations
+ // starting at addrOfNextInstructionInITBlock. We record these breakpoints in class variable m_sw_single_step_itblock_break_id[],
+ // and also record the total number of IT breakpoints set in the variable 'm_sw_single_step_itblock_break_count'.
+
+ // The instructions inside the IT block, which are replaced by the 16-bit Thumb breakpoints (opcode=0xDEFE)
+ // instructions, can be either Thumb-16 or Thumb-32. When a Thumb-32 instruction (say, inst#1) is replaced Thumb
+ // by a 16-bit breakpoint (OS only supports 16-bit breakpoints in Thumb mode and 32-bit breakpoints in ARM mode), the
+ // breakpoint for the next instruction (say instr#2) is saved in the upper half of this Thumb-32 (instr#1)
+ // instruction. Hence if the execution stops at Breakpoint2 corresponding to instr#2, the PC is offset by 16-bits.
+ // We therefore have to keep track of PC of each instruction in the IT block that is being replaced with the 16-bit
+ // Thumb breakpoint, to ensure that when the breakpoint is hit, the PC is adjusted to the correct value. We save
+ // the actual PC corresponding to each instruction in the IT block by associating a call back with each breakpoint
+ // we set and passing it as a baton. When the breakpoint hits and the callback routine is called, the routine
+ // adjusts the PC based on the baton that is passed to it.
+
+ nub_addr_t addrOfNextInstructionInITBlock, pcInITBlock, nextPCInITBlock, bpAddressInITBlock;
+ uint16_t opcode16;
+ uint32_t opcode32;
+
+ addrOfNextInstructionInITBlock = (m_last_decode_arm.thumb16b) ? curr_pc + 2 : curr_pc + 4;
+
+ pcInITBlock = addrOfNextInstructionInITBlock;
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: itBlockRemaining=%d", __FUNCTION__, m_last_decode_thumb.itBlockRemaining);
+
+ m_sw_single_step_itblock_break_count = 0;
+ for (int i = 0; i <= m_last_decode_thumb.itBlockRemaining; i++)
+ {
+ if (NUB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i]))
+ {
+ DNBLogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Array m_sw_single_step_itblock_break_id should not contain any valid breakpoint IDs at this point. But found a valid breakID=%d at index=%d", m_sw_single_step_itblock_break_id[i], i);
+ }
+ else
+ {
+ nextPCInITBlock = pcInITBlock;
+ // Compute nextPCInITBlock based on opcode present at pcInITBlock
+ if (m_thread->Process()->Task().ReadMemory(pcInITBlock, 2, &opcode16) == 2)
+ {
+ opcode32 = opcode16;
+ nextPCInITBlock += 2;
+
+ // Check for 32 bit thumb opcode and read the upper 16 bits if needed
+ if (((opcode32 & 0xE000) == 0xE000) && (opcode32 & 0x1800))
+ {
+ // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for
+ // a 32 bit Thumb opcode
+ // Read bits 31:16 of a 32 bit Thumb opcode
+ if (m_thread->Process()->Task().ReadMemory(pcInITBlock+2, 2, &opcode16) == 2)
+ {
+ // 32 bit thumb opcode
+ opcode32 = (opcode32 << 16) | opcode16;
+ nextPCInITBlock += 2;
+ }
+ else
+ {
+ DNBLogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", nextPCInITBlock);
+ }
+ }
+ }
+ else
+ {
+ DNBLogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Error reading 16-bit Thumb instruction at pc=0x%8.8x", nextPCInITBlock);
+ }
+
+
+ // Set breakpoint and associate a callback function with it
+ bpAddressInITBlock = addrOfNextInstructionInITBlock + 2*i;
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Setting IT breakpoint[%d] at address: 0x%8.8x", __FUNCTION__, i, bpAddressInITBlock);
+
+ m_sw_single_step_itblock_break_id[i] = m_thread->Process()->CreateBreakpoint(bpAddressInITBlock, 2, false, m_thread->ThreadID());
+ if (!NUB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i]))
+ err = KERN_INVALID_ARGUMENT;
+ else
+ {
+ DNBLogThreadedIf(LOG_STEP, "%s: Set IT breakpoint[%i]=%d set at 0x%8.8x for instruction at 0x%8.8x", __FUNCTION__, i, m_sw_single_step_itblock_break_id[i], bpAddressInITBlock, pcInITBlock);
+
+ // Set the breakpoint callback for these special IT breakpoints
+ // so that if one of these breakpoints gets hit, it knows to
+ // update the PC to the original address of the conditional
+ // IT instruction.
+ DNBBreakpointSetCallback(m_thread->ProcessID(), m_sw_single_step_itblock_break_id[i], DNBArchMachARM::BreakpointHit, (void*)pcInITBlock);
+ m_sw_single_step_itblock_break_count++;
+ }
+ }
+
+ pcInITBlock = nextPCInITBlock;
+ }
+
+ DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Set %u IT software single breakpoints.", __FUNCTION__, m_sw_single_step_itblock_break_count);
+
+ }
+
+ DNBLogThreadedIf(LOG_STEP, "%s: next_pc=0x%8.8x (%s)", __FUNCTION__, next_pc, next_pc_is_thumb ? "Thumb" : "ARM");
+
+ if (next_pc & 0x1)
+ {
+ assert(next_pc_is_thumb);
+ }
+
+ if (next_pc_is_thumb)
+ {
+ next_pc &= ~0x1;
+ }
+ else
+ {
+ assert((next_pc & 0x3) == 0);
+ }
+
+ if (!inITBlock || (inITBlock && !m_last_decode_arm.setsFlags) || (lastInITBlock && m_last_decode_arm.branch))
+ {
+ err = KERN_SUCCESS;
+
+#if defined DNB_ARCH_MACH_ARM_DEBUG_SW_STEP
+ m_sw_single_step_next_pc = next_pc;
+ if (next_pc_is_thumb)
+ m_sw_single_step_next_pc |= 1; // Set bit zero if the next PC is expected to be Thumb
+#else
+ const DNBBreakpoint *bp = m_thread->Process()->Breakpoints().FindByAddress(next_pc);
+
+ if (bp == NULL)
+ {
+ m_sw_single_step_break_id = m_thread->Process()->CreateBreakpoint(next_pc, next_pc_is_thumb ? 2 : 4, false, m_thread->ThreadID());
+ if (!NUB_BREAK_ID_IS_VALID(m_sw_single_step_break_id))
+ err = KERN_INVALID_ARGUMENT;
+ DNBLogThreadedIf(LOG_STEP, "%s: software single step breakpoint with breakID=%d set at 0x%8.8x", __FUNCTION__, m_sw_single_step_break_id, next_pc);
+ }
+#endif
+ }
+
+ return err.Error();
+}
+
+uint32_t
+DNBArchMachARM::NumSupportedHardwareBreakpoints()
+{
+ // 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 = UINT_MAX;
+ if (g_num_supported_hw_breakpoints == UINT_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_breakpoints = 0;
+
+ // Read the DBGDIDR to get the number of available hardware breakpoints
+ // However, in some of our current armv7 processors, hardware
+ // breakpoints/watchpoints were not properly connected. So detect those
+ // cases using a field in a sysctl. For now we are using "hw.cpusubtype"
+ // field to distinguish CPU architectures. This is a hack until we can
+ // get <rdar://problem/6372672> fixed, at which point we will switch to
+ // using a different sysctl string that will tell us how many BRPs
+ // are available to us directly without having to read DBGDIDR.
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ uint32_t numBRPs = bits(register_DBGDIDR, 27, 24);
+ // Zero is reserved for the BRP count, so don't increment it if it is zero
+ if (numBRPs > 0)
+ numBRPs++;
+ DNBLogThreadedIf(LOG_THREAD, "DBGDIDR=0x%8.8x (number BRP pairs = %u)", register_DBGDIDR, numBRPs);
+
+ if (numBRPs > 0)
+ {
+ uint32_t cpusubtype;
+ size_t len;
+ len = sizeof(cpusubtype);
+ // TODO: remove this hack and change to using hw.optional.xx when implmented
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
+ {
+ DNBLogThreadedIf(LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype);
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7)
+ DNBLogThreadedIf(LOG_THREAD, "Hardware breakpoints disabled for armv7 (rdar://problem/6372672)");
+ else
+ g_num_supported_hw_breakpoints = numBRPs;
+ }
+ }
+
+ }
+ return g_num_supported_hw_breakpoints;
+}
+
+
+uint32_t
+DNBArchMachARM::NumSupportedHardwareWatchpoints()
+{
+ // 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 = UINT_MAX;
+ if (g_num_supported_hw_watchpoints == UINT_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+ // Read the DBGDIDR to get the number of available hardware breakpoints
+ // However, in some of our current armv7 processors, hardware
+ // breakpoints/watchpoints were not properly connected. So detect those
+ // cases using a field in a sysctl. For now we are using "hw.cpusubtype"
+ // field to distinguish CPU architectures. This is a hack until we can
+ // get <rdar://problem/6372672> fixed, at which point we will switch to
+ // using a different sysctl string that will tell us how many WRPs
+ // are available to us directly without having to read DBGDIDR.
+
+ uint32_t register_DBGDIDR;
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ uint32_t numWRPs = bits(register_DBGDIDR, 31, 28) + 1;
+ DNBLogThreadedIf(LOG_THREAD, "DBGDIDR=0x%8.8x (number WRP pairs = %u)", register_DBGDIDR, numWRPs);
+
+ if (numWRPs > 0)
+ {
+ uint32_t cpusubtype;
+ size_t len;
+ len = sizeof(cpusubtype);
+ // TODO: remove this hack and change to using hw.optional.xx when implmented
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
+ {
+ DNBLogThreadedIf(LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype);
+
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7)
+ DNBLogThreadedIf(LOG_THREAD, "Hardware watchpoints disabled for armv7 (rdar://problem/6372672)");
+ else
+ g_num_supported_hw_watchpoints = numWRPs;
+ }
+ }
+
+ }
+ return g_num_supported_hw_watchpoints;
+}
+
+
+uint32_t
+DNBArchMachARM::EnableHardwareBreakpoint (nub_addr_t addr, nub_size_t size)
+{
+ // Make sure our address isn't bogus
+ if (addr & 1)
+ return INVALID_NUB_HW_INDEX;
+
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ uint32_t i;
+ for (i=0; i<num_hw_breakpoints; ++i)
+ {
+ if ((m_state.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
+ m_state.dbg.__bvr[i] = addr & ~((nub_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
+ m_state.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
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (Thumb)",
+ addr,
+ size,
+ i,
+ i,
+ m_state.dbg.__bvr[i],
+ m_state.dbg.__bcr[i]);
+ }
+ else if (size == 4)
+ {
+ // We have an ARM breakpoint
+ m_state.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
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (ARM)",
+ addr,
+ size,
+ i,
+ i,
+ m_state.dbg.__bvr[i],
+ m_state.dbg.__bcr[i]);
+ }
+
+ kret = SetDBGState();
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint(addr = %8.8p, size = %u) => all hardware breakpoint resources are being used.", addr, size);
+ }
+ }
+
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+DNBArchMachARM::DisableHardwareBreakpoint (uint32_t hw_index)
+{
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ if (hw_index < num_hw_points)
+ {
+ m_state.dbg.__bcr[hw_index] = 0;
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::SetHardwareBreakpoint( %u ) - BVR%u = 0x%8.8x BCR%u = 0x%8.8x",
+ hw_index,
+ hw_index,
+ m_state.dbg.__bvr[hw_index],
+ hw_index,
+ m_state.dbg.__bcr[hw_index]);
+
+ kret = SetDBGState();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t
+DNBArchMachARM::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::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 INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return INVALID_NUB_HW_INDEX;
+
+ // Can't watch more than 4 bytes per WVR/WCR pair
+ if (size > 4)
+ return INVALID_NUB_HW_INDEX;
+
+ // 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;
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint() - addr_word_offset = 0x%8.8x", addr_word_offset);
+
+ uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset;
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint() - byte_mask = 0x%8.8x", byte_mask);
+ if (byte_mask > 0xfu)
+ return INVALID_NUB_HW_INDEX;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(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 ((m_state.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
+ m_state.dbg.__wvr[i] = addr & ~((nub_addr_t)3);
+ m_state.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 = SetDBGState();
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+DNBArchMachARM::DisableHardwareWatchpoint (uint32_t hw_index)
+{
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ if (hw_index < num_hw_points)
+ {
+ m_state.dbg.__wcr[hw_index] = 0;
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ClearHardwareWatchpoint( %u ) - WVR%u = 0x%8.8x WCR%u = 0x%8.8x",
+ hw_index,
+ hw_index,
+ m_state.dbg.__wvr[hw_index],
+ hw_index,
+ m_state.dbg.__wcr[hw_index]);
+
+ kret = SetDBGState();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------
+// Register information defintions for 32 bit ARMV6.
+//----------------------------------------------------------------------
+enum gpr_regnums
+{
+ e_regNumGPR_r0 = 0,
+ e_regNumGPR_r1,
+ e_regNumGPR_r2,
+ e_regNumGPR_r3,
+ e_regNumGPR_r4,
+ e_regNumGPR_r5,
+ e_regNumGPR_r6,
+ e_regNumGPR_r7,
+ e_regNumGPR_r8,
+ e_regNumGPR_r9,
+ e_regNumGPR_r10,
+ e_regNumGPR_r11,
+ e_regNumGPR_r12,
+ e_regNumGPR_sp,
+ e_regNumGPR_lr,
+ e_regNumGPR_pc,
+ e_regNumGPR_cpsr
+};
+
+// General purpose registers
+static DNBRegisterInfo g_gpr_registers[] =
+{
+ { "r0" , Uint, 4, Hex },
+ { "r1" , Uint, 4, Hex },
+ { "r2" , Uint, 4, Hex },
+ { "r3" , Uint, 4, Hex },
+ { "r4" , Uint, 4, Hex },
+ { "r5" , Uint, 4, Hex },
+ { "r6" , Uint, 4, Hex },
+ { "r7" , Uint, 4, Hex },
+ { "r8" , Uint, 4, Hex },
+ { "r9" , Uint, 4, Hex },
+ { "r10" , Uint, 4, Hex },
+ { "r11" , Uint, 4, Hex },
+ { "r12" , Uint, 4, Hex },
+ { "sp" , Uint, 4, Hex },
+ { "lr" , Uint, 4, Hex },
+ { "pc" , Uint, 4, Hex },
+ { "cpsr" , Uint, 4, Hex },
+};
+
+// Floating point registers
+static DNBRegisterInfo g_vfp_registers[] =
+{
+ { "s0" , IEEE754, 4, Float },
+ { "s1" , IEEE754, 4, Float },
+ { "s2" , IEEE754, 4, Float },
+ { "s3" , IEEE754, 4, Float },
+ { "s4" , IEEE754, 4, Float },
+ { "s5" , IEEE754, 4, Float },
+ { "s6" , IEEE754, 4, Float },
+ { "s7" , IEEE754, 4, Float },
+ { "s8" , IEEE754, 4, Float },
+ { "s9" , IEEE754, 4, Float },
+ { "s10" , IEEE754, 4, Float },
+ { "s11" , IEEE754, 4, Float },
+ { "s12" , IEEE754, 4, Float },
+ { "s13" , IEEE754, 4, Float },
+ { "s14" , IEEE754, 4, Float },
+ { "s15" , IEEE754, 4, Float },
+ { "s16" , IEEE754, 4, Float },
+ { "s17" , IEEE754, 4, Float },
+ { "s18" , IEEE754, 4, Float },
+ { "s19" , IEEE754, 4, Float },
+ { "s20" , IEEE754, 4, Float },
+ { "s21" , IEEE754, 4, Float },
+ { "s22" , IEEE754, 4, Float },
+ { "s23" , IEEE754, 4, Float },
+ { "s24" , IEEE754, 4, Float },
+ { "s25" , IEEE754, 4, Float },
+ { "s26" , IEEE754, 4, Float },
+ { "s27" , IEEE754, 4, Float },
+ { "s28" , IEEE754, 4, Float },
+ { "s29" , IEEE754, 4, Float },
+ { "s30" , IEEE754, 4, Float },
+ { "s31" , IEEE754, 4, Float },
+ { "fpscr" , Uint, 4, Hex }
+};
+
+// Exception registers
+
+static DNBRegisterInfo g_exc_registers[] =
+{
+ { "dar" , Uint, 4, Hex },
+ { "dsisr" , Uint, 4, Hex },
+ { "exception" , Uint, 4, Hex }
+};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t k_num_vfp_registers = sizeof(g_vfp_registers)/sizeof(DNBRegisterInfo);
+const size_t k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+// Total number of registers for this architecture
+const size_t k_num_armv6_registers = k_num_gpr_registers + k_num_vfp_registers + k_num_exc_registers;
+
+//----------------------------------------------------------------------
+// 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 DNBRegisterSetInfo g_reg_sets[] =
+{
+ { "ARMV6 Registers", NULL, k_num_armv6_registers },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_vfp_registers, k_num_vfp_registers },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+// Total number of register sets for this architecture
+const size_t k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo);
+
+
+const DNBRegisterSetInfo *
+DNBArchMachARM::GetRegisterSetInfo(nub_size_t *num_reg_sets) const
+{
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool
+DNBArchMachARM::GetRegisterValue(int set, int reg, DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = e_regNumGPR_pc;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = e_regNumGPR_sp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = e_regNumGPR_r7; // is this the right reg?
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = e_regNumGPR_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = e_regNumGPR_cpsr;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ value->value.uint32 = m_state.gpr.__r[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetVFP:
+ if (reg < 32)
+ {
+ value->value.uint32 = m_state.vfp.__r[reg];
+ return true;
+ }
+ else if (reg == 32)
+ {
+ value->value.uint32 = m_state.vfp.__fpscr;
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ value->value.uint32 = (&m_state.exc.__exception)[reg];
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool
+DNBArchMachARM::SetRegisterValue(int set, int reg, const DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = e_regNumGPR_pc;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = e_regNumGPR_sp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = e_regNumGPR_r7;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = e_regNumGPR_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = e_regNumGPR_cpsr;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ m_state.gpr.__r[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetVFP:
+ if (reg < 32)
+ {
+ m_state.vfp.__r[reg] = value->value.float64;
+ success = true;
+ }
+ else if (reg == 32)
+ {
+ m_state.vfp.__fpscr = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ (&m_state.exc.__exception)[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+ }
+
+ }
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+kern_return_t
+DNBArchMachARM::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL: return GetGPRState(force) |
+ GetVFPState(force) |
+ GetEXCState(force) |
+ GetDBGState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetVFP: return GetVFPState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ case e_regSetDBG: return GetDBGState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchMachARM::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ kern_return_t err = GetRegisterState(set, false);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() |
+ SetVFPState() |
+ SetEXCState() |
+ SetDBGState();
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetVFP: return SetVFPState();
+ case e_regSetEXC: return SetEXCState();
+ case e_regSetDBG: return SetDBGState();
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchMachARM::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+
+#endif // #if defined (__arm__)
+
diff --git a/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h
new file mode 100644
index 00000000000..f40c891ce98
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h
@@ -0,0 +1,217 @@
+//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DebugNubArchMachARM_h__
+#define __DebugNubArchMachARM_h__
+
+#if defined (__arm__)
+
+#include "DNBArch.h"
+#include <ARMDisassembler/ARMDisassembler.h>
+
+class MachThread;
+
+class DNBArchMachARM : public DNBArchProtocol
+{
+public:
+ enum { kMaxNumThumbITBreakpoints = 4 };
+
+ DNBArchMachARM(MachThread *thread) :
+ m_thread(thread),
+ m_state(),
+ m_hw_single_chained_step_addr(INVALID_NUB_ADDRESS),
+ m_sw_single_step_next_pc(INVALID_NUB_ADDRESS),
+ m_sw_single_step_break_id(INVALID_NUB_BREAK_ID),
+ m_sw_single_step_itblock_break_count(0),
+ m_last_decode_pc(INVALID_NUB_ADDRESS)
+ {
+ memset(&m_dbg_save, 0, sizeof(m_dbg_save));
+ ThumbStaticsInit(&m_last_decode_thumb);
+ for (int i = 0; i < kMaxNumThumbITBreakpoints; i++)
+ m_sw_single_step_itblock_break_id[i] = INVALID_NUB_BREAK_ID;
+ }
+
+ virtual ~DNBArchMachARM()
+ {
+ }
+
+ virtual const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets) const;
+ virtual bool GetRegisterValue(int set, int reg, DNBRegisterValue *value);
+ virtual bool SetRegisterValue(int set, int reg, const DNBRegisterValue *value);
+
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+
+ static const uint8_t * const SoftwareBreakpointOpcode (nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+ virtual uint32_t NumSupportedHardwareBreakpoints();
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareBreakpoint (nub_addr_t addr, nub_size_t size);
+ virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write);
+ virtual bool DisableHardwareBreakpoint (uint32_t hw_break_index);
+ virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index);
+ virtual bool StepNotComplete ();
+
+protected:
+
+
+ kern_return_t EnableHardwareSingleStep (bool enable);
+ kern_return_t SetSingleStepSoftwareBreakpoints ();
+
+ bool ConditionPassed(uint8_t condition, uint32_t cpsr);
+ bool ComputeNextPC(nub_addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, nub_addr_t *targetPC);
+ void EvaluateNextInstructionForSoftwareBreakpointSetup(nub_addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, nub_addr_t *nextPC, bool *nextPCIsThumb);
+ void DecodeITBlockInstructions(nub_addr_t curr_pc);
+ arm_error_t DecodeInstructionUsingDisassembler(nub_addr_t curr_pc, uint32_t curr_cpsr, arm_decoded_instruction_t *decodedInstruction, thumb_static_data_t *thumbStaticData, nub_addr_t *next_pc);
+ static nub_bool_t BreakpointHit (nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton);
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR = ARM_THREAD_STATE,
+ e_regSetVFP = ARM_VFP_STATE,
+ e_regSetEXC = ARM_EXCEPTION_STATE,
+ e_regSetDBG = ARM_DEBUG_STATE,
+ kNumRegisterSets
+ } RegisterSet;
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ struct State
+ {
+ arm_thread_state_t gpr;
+ arm_vfp_state_t vfp;
+ arm_exception_state_t exc;
+ arm_debug_state_t dbg;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t vfp_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ vfp_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+ void InvalidateRegisterSetState(int set)
+ {
+ SetError (set, Read, -1);
+ }
+ kern_return_t GetError (int set, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL: return gpr_errs[err_idx] |
+ vfp_errs[err_idx] |
+ exc_errs[err_idx] |
+ dbg_errs[err_idx] ;
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetVFP: return vfp_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ case e_regSetDBG: return dbg_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+ bool SetError (int set, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] = err;
+ vfp_errs[err_idx] = err;
+ dbg_errs[err_idx] = err;
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetVFP:
+ vfp_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetDBG:
+ dbg_errs[err_idx] = err;
+ return true;
+ default: break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid (int set) const
+ {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetVFPState (bool force);
+ kern_return_t GetEXCState (bool force);
+ kern_return_t GetDBGState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetVFPState ();
+ kern_return_t SetEXCState ();
+ kern_return_t SetDBGState ();
+protected:
+ MachThread * m_thread;
+ State m_state;
+ arm_debug_state_t m_dbg_save;
+ nub_addr_t m_hw_single_chained_step_addr;
+ // Software single stepping support
+ nub_addr_t m_sw_single_step_next_pc;
+ nub_break_t m_sw_single_step_break_id;
+ nub_break_t m_sw_single_step_itblock_break_id[kMaxNumThumbITBreakpoints];
+ nub_addr_t m_sw_single_step_itblock_break_count;
+ // Disassembler state
+ thumb_static_data_t m_last_decode_thumb;
+ arm_decoded_instruction_t m_last_decode_arm;
+ nub_addr_t m_last_decode_pc;
+
+};
+
+typedef DNBArchMachARM DNBArch;
+#endif // #if defined (__arm__)
+#endif // #ifndef __DebugNubArchMachARM_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs b/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs
new file mode 100644
index 00000000000..9b1554f8708
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs
@@ -0,0 +1,16 @@
+/*
+ * nub.defs
+ */
+
+/*
+ * DNBConfig.h is autogenerated by a perl script that is run as a build
+ * script in XCode. XCode is responsible for calling the script and setting
+ * the include paths correctly to locate it. The file will exist in the
+ * derived sources directory in the build folder.
+ *
+ */
+
+#include "DNBConfig.h"
+
+
+#import <mach/mach_exc.defs>
diff --git a/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp
new file mode 100644
index 00000000000..0a0601f6378
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp
@@ -0,0 +1,879 @@
+//===-- DNBArchImplI386.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__i386__)
+
+#include <sys/cdefs.h>
+
+#include "MacOSX/i386/DNBArchImplI386.h"
+#include "DNBLog.h"
+#include "MachThread.h"
+#include "MachProcess.h"
+
+static const uint8_t g_breakpoint_opcode[] = { 0xCC };
+
+enum
+{
+ gpr_eax = 0,
+ gpr_ebx = 1,
+ gpr_ecx = 2,
+ gpr_edx = 3,
+ gpr_edi = 4,
+ gpr_esi = 5,
+ gpr_ebp = 6,
+ gpr_esp = 7,
+ gpr_ss = 8,
+ gpr_eflags = 9,
+ gpr_eip = 10,
+ gpr_cs = 11,
+ gpr_ds = 12,
+ gpr_es = 13,
+ gpr_fs = 14,
+ gpr_gs = 15,
+ k_num_gpr_regs
+};
+
+enum {
+ 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,
+ k_num_fpu_regs,
+
+ // 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 {
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+ k_num_exc_regs,
+};
+
+
+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
+};
+
+
+const uint8_t * const
+DNBArchImplI386::SoftwareBreakpointOpcode (nub_size_t byte_size)
+{
+ if (byte_size == 1)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+uint32_t
+DNBArchImplI386::GetCPUType()
+{
+ return CPU_TYPE_I386;
+}
+
+uint64_t
+DNBArchImplI386::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__eip;
+ return failValue;
+}
+
+kern_return_t
+DNBArchImplI386::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.context.gpr.__eip = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchImplI386::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__esp;
+ return failValue;
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_GPR_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+//#define SET_GPR(reg) m_state.context.gpr.__##reg = gpr_##reg
+
+kern_return_t
+DNBArchImplI386::GetGPRState(bool force)
+{
+ if (force || m_state.GetError(e_regSetGPR, Read))
+ {
+#if DEBUG_GPR_VALUES
+ SET_GPR(eax);
+ SET_GPR(ebx);
+ SET_GPR(ecx);
+ SET_GPR(edx);
+ SET_GPR(edi);
+ SET_GPR(esi);
+ SET_GPR(ebp);
+ SET_GPR(esp);
+ SET_GPR(ss);
+ SET_GPR(eflags);
+ SET_GPR(eip);
+ SET_GPR(cs);
+ SET_GPR(ds);
+ SET_GPR(es);
+ SET_GPR(fs);
+ SET_GPR(gs);
+ m_state.SetError(e_regSetGPR, Read, 0);
+#else
+ mach_msg_type_number_t count = e_regSetWordSizeGPR;
+ m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->ThreadID(), x86_THREAD_STATE32, (thread_state_t)&m_state.context.gpr, &count));
+#endif
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_FPU_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t
+DNBArchImplI386::GetFPUState(bool force)
+{
+ if (force || m_state.GetError(e_regSetFPU, Read))
+ {
+#if DEBUG_FPU_VALUES
+ m_state.context.fpu.__fpu_reserved[0] = -1;
+ m_state.context.fpu.__fpu_reserved[1] = -1;
+ *(uint16_t *)&(m_state.context.fpu.__fpu_fcw) = 0x1234;
+ *(uint16_t *)&(m_state.context.fpu.__fpu_fsw) = 0x5678;
+ m_state.context.fpu.__fpu_ftw = 1;
+ m_state.context.fpu.__fpu_rsrv1 = UINT8_MAX;
+ m_state.context.fpu.__fpu_fop = 2;
+ m_state.context.fpu.__fpu_ip = 3;
+ m_state.context.fpu.__fpu_cs = 4;
+ m_state.context.fpu.__fpu_rsrv2 = 5;
+ m_state.context.fpu.__fpu_dp = 6;
+ m_state.context.fpu.__fpu_ds = 7;
+ m_state.context.fpu.__fpu_rsrv3 = UINT16_MAX;
+ m_state.context.fpu.__fpu_mxcsr = 8;
+ m_state.context.fpu.__fpu_mxcsrmask = 9;
+ int i;
+ for (i=0; i<16; ++i)
+ {
+ if (i<10)
+ {
+ m_state.context.fpu.__fpu_stmm0.__mmst_reg[i] = 'a';
+ m_state.context.fpu.__fpu_stmm1.__mmst_reg[i] = 'b';
+ m_state.context.fpu.__fpu_stmm2.__mmst_reg[i] = 'c';
+ m_state.context.fpu.__fpu_stmm3.__mmst_reg[i] = 'd';
+ m_state.context.fpu.__fpu_stmm4.__mmst_reg[i] = 'e';
+ m_state.context.fpu.__fpu_stmm5.__mmst_reg[i] = 'f';
+ m_state.context.fpu.__fpu_stmm6.__mmst_reg[i] = 'g';
+ m_state.context.fpu.__fpu_stmm7.__mmst_reg[i] = 'h';
+ }
+ else
+ {
+ m_state.context.fpu.__fpu_stmm0.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm1.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm2.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm3.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm4.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm5.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm6.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm7.__mmst_reg[i] = INT8_MIN;
+ }
+
+ m_state.context.fpu.__fpu_xmm0.__xmm_reg[i] = '0';
+ m_state.context.fpu.__fpu_xmm1.__xmm_reg[i] = '1';
+ m_state.context.fpu.__fpu_xmm2.__xmm_reg[i] = '2';
+ m_state.context.fpu.__fpu_xmm3.__xmm_reg[i] = '3';
+ m_state.context.fpu.__fpu_xmm4.__xmm_reg[i] = '4';
+ m_state.context.fpu.__fpu_xmm5.__xmm_reg[i] = '5';
+ m_state.context.fpu.__fpu_xmm6.__xmm_reg[i] = '6';
+ m_state.context.fpu.__fpu_xmm7.__xmm_reg[i] = '7';
+ }
+ for (i=0; i<sizeof(m_state.context.fpu.__fpu_rsrv4); ++i)
+ m_state.context.fpu.__fpu_rsrv4[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_reserved1 = -1;
+ m_state.SetError(e_regSetFPU, Read, 0);
+#else
+ mach_msg_type_number_t count = e_regSetWordSizeFPR;
+ m_state.SetError(e_regSetFPU, Read, ::thread_get_state(m_thread->ThreadID(), x86_FLOAT_STATE32, (thread_state_t)&m_state.context.fpu, &count));
+#endif
+ }
+ return m_state.GetError(e_regSetFPU, Read);
+}
+
+kern_return_t
+DNBArchImplI386::GetEXCState(bool force)
+{
+ if (force || m_state.GetError(e_regSetEXC, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeEXC;
+ m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->ThreadID(), x86_EXCEPTION_STATE32, (thread_state_t)&m_state.context.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t
+DNBArchImplI386::SetGPRState()
+{
+ m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->ThreadID(), x86_THREAD_STATE32, (thread_state_t)&m_state.context.gpr, e_regSetWordSizeGPR));
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t
+DNBArchImplI386::SetFPUState()
+{
+ m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->ThreadID(), x86_FLOAT_STATE32, (thread_state_t)&m_state.context.fpu, e_regSetWordSizeFPR));
+ return m_state.GetError(e_regSetFPU, Write);
+}
+
+kern_return_t
+DNBArchImplI386::SetEXCState()
+{
+ m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->ThreadID(), x86_EXCEPTION_STATE32, (thread_state_t)&m_state.context.exc, e_regSetWordSizeEXC));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+void
+DNBArchImplI386::ThreadWillResume()
+{
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ // This is the primary thread, let the arch do anything it needs
+ EnableHardwareSingleStep(true) == KERN_SUCCESS;
+ }
+}
+
+bool
+DNBArchImplI386::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool
+DNBArchImplI386::NotifyException(MachException::Data& exc)
+{
+ switch (exc.exc_type)
+ {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2)
+ {
+ nub_addr_t pc = GetPC(INVALID_NUB_ADDRESS);
+ if (pc != INVALID_NUB_ADDRESS && pc > 0)
+ {
+ pc -= 1;
+ // Check for a breakpoint at one byte prior to the current PC value
+ // since the PC will be just past the trap.
+
+ nub_break_t breakID = m_thread->Process()->Breakpoints().FindIDByAddress(pc);
+ if (NUB_BREAK_ID_IS_VALID(breakID))
+ {
+ // Backup the PC for i386 since the trap was taken and the PC
+ // is at the address following the single byte trap instruction.
+ if (m_state.context.gpr.__eip > 0)
+ {
+ m_state.context.gpr.__eip = pc;
+ // Write the new PC back out
+ SetGPRState ();
+ }
+
+ m_thread->SetCurrentBreakpoint(breakID);
+ }
+ return true;
+ }
+ }
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ return false;
+}
+
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchImplI386::EnableHardwareSingleStep (bool enable)
+{
+ if (GetGPRState(false) == KERN_SUCCESS)
+ {
+ const uint32_t trace_bit = 0x100u;
+ if (enable)
+ m_state.context.gpr.__eflags |= trace_bit;
+ else
+ m_state.context.gpr.__eflags &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+
+//----------------------------------------------------------------------
+// Register information defintions
+//----------------------------------------------------------------------
+
+
+#define GPR_OFFSET(reg) (offsetof (DNBArchImplI386::GPR, __##reg))
+#define FPU_OFFSET(reg) (offsetof (DNBArchImplI386::FPU, __fpu_##reg) + offsetof (DNBArchImplI386::Context, fpu))
+#define EXC_OFFSET(reg) (offsetof (DNBArchImplI386::EXC, __##reg) + offsetof (DNBArchImplI386::Context, exc))
+
+#define GPR_SIZE(reg) (sizeof(((DNBArchImplI386::GPR *)NULL)->__##reg))
+#define FPU_SIZE_UINT(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg))
+#define FPU_SIZE_MMST(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg.__mmst_reg))
+#define FPU_SIZE_XMM(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg.__xmm_reg))
+#define EXC_SIZE(reg) (sizeof(((DNBArchImplI386::EXC *)NULL)->__##reg))
+
+// 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.
+
+// General purpose registers for 64 bit
+const DNBRegisterInfo
+DNBArchImplI386::g_gpr_registers[] =
+{
+{ e_regSetGPR, gpr_eax, "eax" , NULL , Uint, Hex, GPR_SIZE(eax), GPR_OFFSET(eax) , gcc_eax , dwarf_eax , -1 , gdb_eax },
+{ e_regSetGPR, gpr_ebx, "ebx" , NULL , Uint, Hex, GPR_SIZE(ebx), GPR_OFFSET(ebx) , gcc_ebx , dwarf_ebx , -1 , gdb_ebx },
+{ e_regSetGPR, gpr_ecx, "ecx" , NULL , Uint, Hex, GPR_SIZE(ecx), GPR_OFFSET(ecx) , gcc_ecx , dwarf_ecx , -1 , gdb_ecx },
+{ e_regSetGPR, gpr_edx, "edx" , NULL , Uint, Hex, GPR_SIZE(edx), GPR_OFFSET(edx) , gcc_edx , dwarf_edx , -1 , gdb_edx },
+{ e_regSetGPR, gpr_edi, "edi" , NULL , Uint, Hex, GPR_SIZE(edi), GPR_OFFSET(edi) , gcc_edi , dwarf_edi , -1 , gdb_edi },
+{ e_regSetGPR, gpr_esi, "esi" , NULL , Uint, Hex, GPR_SIZE(esi), GPR_OFFSET(esi) , gcc_esi , dwarf_esi , -1 , gdb_esi },
+{ e_regSetGPR, gpr_ebp, "ebp" , "fp" , Uint, Hex, GPR_SIZE(ebp), GPR_OFFSET(ebp) , gcc_ebp , dwarf_ebp , GENERIC_REGNUM_FP , gdb_ebp },
+{ e_regSetGPR, gpr_esp, "esp" , "sp" , Uint, Hex, GPR_SIZE(esp), GPR_OFFSET(esp) , gcc_esp , dwarf_esp , GENERIC_REGNUM_SP , gdb_esp },
+{ e_regSetGPR, gpr_ss, "ss" , NULL , Uint, Hex, GPR_SIZE(ss), GPR_OFFSET(ss) , -1 , -1 , -1 , gdb_ss },
+{ e_regSetGPR, gpr_eflags, "eflags", "flags" , Uint, Hex, GPR_SIZE(eflags), GPR_OFFSET(eflags) , gcc_eflags, dwarf_eflags , GENERIC_REGNUM_FLAGS , gdb_eflags},
+{ e_regSetGPR, gpr_eip, "eip" , "pc" , Uint, Hex, GPR_SIZE(eip), GPR_OFFSET(eip) , gcc_eip , dwarf_eip , GENERIC_REGNUM_PC , gdb_eip },
+{ e_regSetGPR, gpr_cs, "cs" , NULL , Uint, Hex, GPR_SIZE(cs), GPR_OFFSET(cs) , -1 , -1 , -1 , gdb_cs },
+{ e_regSetGPR, gpr_ds, "ds" , NULL , Uint, Hex, GPR_SIZE(ds), GPR_OFFSET(ds) , -1 , -1 , -1 , gdb_ds },
+{ e_regSetGPR, gpr_es, "es" , NULL , Uint, Hex, GPR_SIZE(es), GPR_OFFSET(es) , -1 , -1 , -1 , gdb_es },
+{ e_regSetGPR, gpr_fs, "fs" , NULL , Uint, Hex, GPR_SIZE(fs), GPR_OFFSET(fs) , -1 , -1 , -1 , gdb_fs },
+{ e_regSetGPR, gpr_gs, "gs" , NULL , Uint, Hex, GPR_SIZE(gs), GPR_OFFSET(gs) , -1 , -1 , -1 , gdb_gs }
+};
+
+
+const DNBRegisterInfo
+DNBArchImplI386::g_fpu_registers[] =
+{
+{ e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , FPU_OFFSET(fcw) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , FPU_OFFSET(fsw) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , FPU_OFFSET(ftw) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , FPU_OFFSET(fop) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , FPU_OFFSET(ip) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , FPU_OFFSET(cs) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , FPU_OFFSET(dp) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , FPU_OFFSET(ds) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , FPU_OFFSET(mxcsr) , -1, -1, -1, -1 },
+{ e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , FPU_OFFSET(mxcsrmask) , -1, -1, -1, -1 },
+
+{ e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), -1, dwarf_stmm0, -1, gdb_stmm0 },
+{ e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), -1, dwarf_stmm1, -1, gdb_stmm1 },
+{ e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), -1, dwarf_stmm2, -1, gdb_stmm2 },
+{ e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), -1, dwarf_stmm3, -1, gdb_stmm3 },
+{ e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), -1, dwarf_stmm4, -1, gdb_stmm4 },
+{ e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), -1, dwarf_stmm5, -1, gdb_stmm5 },
+{ e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), -1, dwarf_stmm6, -1, gdb_stmm6 },
+{ e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), -1, dwarf_stmm7, -1, gdb_stmm7 },
+
+{ e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0), FPU_OFFSET(xmm0), -1, dwarf_xmm0, -1, gdb_xmm0 },
+{ e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1), FPU_OFFSET(xmm1), -1, dwarf_xmm1, -1, gdb_xmm1 },
+{ e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2), FPU_OFFSET(xmm2), -1, dwarf_xmm2, -1, gdb_xmm2 },
+{ e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3), FPU_OFFSET(xmm3), -1, dwarf_xmm3, -1, gdb_xmm3 },
+{ e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4), FPU_OFFSET(xmm4), -1, dwarf_xmm4, -1, gdb_xmm4 },
+{ e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5), FPU_OFFSET(xmm5), -1, dwarf_xmm5, -1, gdb_xmm5 },
+{ e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6), FPU_OFFSET(xmm6), -1, dwarf_xmm6, -1, gdb_xmm6 },
+{ e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7), FPU_OFFSET(xmm7), -1, dwarf_xmm7, -1, gdb_xmm7 }
+};
+
+
+
+const DNBRegisterInfo
+DNBArchImplI386::g_exc_registers[] =
+{
+{ e_regSetEXC, exc_trapno, "trapno" , NULL, Uint, Hex, EXC_SIZE (trapno) , EXC_OFFSET (trapno) , -1, -1, -1, -1 },
+{ e_regSetEXC, exc_err, "err" , NULL, Uint, Hex, EXC_SIZE (err) , EXC_OFFSET (err) , -1, -1, -1, -1 },
+{ e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex, EXC_SIZE (faultvaddr), EXC_OFFSET (faultvaddr) , -1, -1, -1, -1 }
+};
+
+// Number of registers in each register set
+const size_t DNBArchImplI386::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_fpu_registers = sizeof(g_fpu_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_all_registers = k_num_gpr_registers + k_num_fpu_registers + k_num_exc_registers;
+
+//----------------------------------------------------------------------
+// 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.
+//----------------------------------------------------------------------
+const DNBRegisterSetInfo
+DNBArchImplI386::g_reg_sets[] =
+{
+ { "i386 Registers", NULL, k_num_all_registers },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_fpu_registers, k_num_fpu_registers },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+// Total number of register sets for this architecture
+const size_t DNBArchImplI386::k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo);
+
+
+const DNBRegisterSetInfo *
+DNBArchImplI386::GetRegisterSetInfo(nub_size_t *num_reg_sets)
+{
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool
+DNBArchImplI386::GetRegisterValue(int set, int reg, DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_eip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_esp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_ebp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_eflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ value->value.uint32 = ((uint32_t*)(&m_state.context.gpr))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPU:
+ switch (reg)
+ {
+ case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.__fpu_fcw)); return true;
+ case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.__fpu_fsw)); return true;
+ case fpu_ftw: value->value.uint8 = m_state.context.fpu.__fpu_ftw; return true;
+ case fpu_fop: value->value.uint16 = m_state.context.fpu.__fpu_fop; return true;
+ case fpu_ip: value->value.uint32 = m_state.context.fpu.__fpu_ip; return true;
+ case fpu_cs: value->value.uint16 = m_state.context.fpu.__fpu_cs; return true;
+ case fpu_dp: value->value.uint32 = m_state.context.fpu.__fpu_dp; return true;
+ case fpu_ds: value->value.uint16 = m_state.context.fpu.__fpu_ds; return true;
+ case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.__fpu_mxcsr; return true;
+ case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.__fpu_mxcsrmask; return true;
+
+ case fpu_stmm0: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm0.__mmst_reg, 10); return true;
+ case fpu_stmm1: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm1.__mmst_reg, 10); return true;
+ case fpu_stmm2: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm2.__mmst_reg, 10); return true;
+ case fpu_stmm3: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm3.__mmst_reg, 10); return true;
+ case fpu_stmm4: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm4.__mmst_reg, 10); return true;
+ case fpu_stmm5: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm5.__mmst_reg, 10); return true;
+ case fpu_stmm6: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm6.__mmst_reg, 10); return true;
+ case fpu_stmm7: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm7.__mmst_reg, 10); return true;
+
+ case fpu_xmm0: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm0.__xmm_reg, 16); return true;
+ case fpu_xmm1: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm1.__xmm_reg, 16); return true;
+ case fpu_xmm2: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm2.__xmm_reg, 16); return true;
+ case fpu_xmm3: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm3.__xmm_reg, 16); return true;
+ case fpu_xmm4: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm4.__xmm_reg, 16); return true;
+ case fpu_xmm5: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm5.__xmm_reg, 16); return true;
+ case fpu_xmm6: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm6.__xmm_reg, 16); return true;
+ case fpu_xmm7: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm7.__xmm_reg, 16); return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ value->value.uint32 = (&m_state.context.exc.__trapno)[reg];
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+
+bool
+DNBArchImplI386::SetRegisterValue(int set, int reg, const DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_eip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_esp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_ebp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_eflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ ((uint32_t*)(&m_state.context.gpr))[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetFPU:
+ switch (reg)
+ {
+ case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.__fpu_fcw)) = value->value.uint16; success = true; break;
+ case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.__fpu_fsw)) = value->value.uint16; success = true; break;
+ case fpu_ftw: m_state.context.fpu.__fpu_ftw = value->value.uint8; success = true; break;
+ case fpu_fop: m_state.context.fpu.__fpu_fop = value->value.uint16; success = true; break;
+ case fpu_ip: m_state.context.fpu.__fpu_ip = value->value.uint32; success = true; break;
+ case fpu_cs: m_state.context.fpu.__fpu_cs = value->value.uint16; success = true; break;
+ case fpu_dp: m_state.context.fpu.__fpu_dp = value->value.uint32; success = true; break;
+ case fpu_ds: m_state.context.fpu.__fpu_ds = value->value.uint16; success = true; break;
+ case fpu_mxcsr: m_state.context.fpu.__fpu_mxcsr = value->value.uint32; success = true; break;
+ case fpu_mxcsrmask: m_state.context.fpu.__fpu_mxcsrmask = value->value.uint32; success = true; break;
+
+ case fpu_stmm0: memcpy (m_state.context.fpu.__fpu_stmm0.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm1: memcpy (m_state.context.fpu.__fpu_stmm1.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm2: memcpy (m_state.context.fpu.__fpu_stmm2.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm3: memcpy (m_state.context.fpu.__fpu_stmm3.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm4: memcpy (m_state.context.fpu.__fpu_stmm4.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm5: memcpy (m_state.context.fpu.__fpu_stmm5.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm6: memcpy (m_state.context.fpu.__fpu_stmm6.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm7: memcpy (m_state.context.fpu.__fpu_stmm7.__mmst_reg, &value->value.uint8, 10); success = true; break;
+
+ case fpu_xmm0: memcpy(m_state.context.fpu.__fpu_xmm0.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm1: memcpy(m_state.context.fpu.__fpu_xmm1.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm2: memcpy(m_state.context.fpu.__fpu_xmm2.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm3: memcpy(m_state.context.fpu.__fpu_xmm3.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm4: memcpy(m_state.context.fpu.__fpu_xmm4.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm5: memcpy(m_state.context.fpu.__fpu_xmm5.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm6: memcpy(m_state.context.fpu.__fpu_xmm6.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm7: memcpy(m_state.context.fpu.__fpu_xmm7.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ (&m_state.context.exc.__trapno)[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+ }
+ }
+
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+
+nub_size_t
+DNBArchImplI386::GetRegisterContext (void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context);
+
+ if (buf && buf_len)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ bool force = false;
+ if (GetGPRState(force) | GetFPUState(force) | GetEXCState(force))
+ return 0;
+ ::memcpy (buf, &m_state.context, size);
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t
+DNBArchImplI386::SetRegisterContext (const void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context);
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ ::memcpy (&m_state.context, buf, size);
+ SetGPRState();
+ SetFPUState();
+ SetEXCState();
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size);
+ return size;
+}
+
+
+
+kern_return_t
+DNBArchImplI386::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL: return GetGPRState(force) | GetFPUState(force) | GetEXCState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetFPU: return GetFPUState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchImplI386::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ if (RegisterSetStateIsValid(set))
+ {
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() | SetFPUState() | SetEXCState();
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetFPU: return SetFPUState();
+ case e_regSetEXC: return SetEXCState();
+ default: break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchImplI386::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+
+
+#endif // #if defined (__i386__)
diff --git a/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h
new file mode 100644
index 00000000000..10972c24ca5
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h
@@ -0,0 +1,196 @@
+//===-- DNBArchImplI386.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBArchImplI386_h__
+#define __DNBArchImplI386_h__
+
+#if defined (__i386__)
+
+#include "DNBArch.h"
+#include <mach/mach_types.h>
+#include <mach/thread_status.h>
+
+
+class MachThread;
+
+class DNBArchImplI386 : public DNBArchProtocol
+{
+public:
+ DNBArchImplI386(MachThread *thread) :
+ m_thread(thread),
+ m_state()
+ {
+ }
+ virtual ~DNBArchImplI386()
+ {
+ }
+
+ static const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ virtual bool GetRegisterValue(int set, int reg, DNBRegisterValue *reg);
+ virtual bool SetRegisterValue(int set, int reg, const DNBRegisterValue *reg);
+ virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len);
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data& exc);
+
+ static const uint8_t * const SoftwareBreakpointOpcode (nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+protected:
+ kern_return_t EnableHardwareSingleStep (bool enable);
+
+ typedef i386_thread_state_t GPR;
+ typedef i386_float_state_t FPU;
+ typedef i386_exception_state_t EXC;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_fpu_registers[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets[];
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_fpu_registers;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers;
+ static const size_t k_num_register_sets;
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPU,
+ e_regSetEXC,
+ kNumRegisterSets
+ } RegisterSet;
+
+ typedef enum RegisterSetWordSizeTag
+ {
+ e_regSetWordSizeGPR = i386_THREAD_STATE_COUNT,
+ e_regSetWordSizeFPR = i386_FLOAT_STATE_COUNT,
+ e_regSetWordSizeEXC = i386_EXCEPTION_STATE_COUNT
+ } RegisterSetWordSize;
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ struct Context
+ {
+ i386_thread_state_t gpr;
+ i386_float_state_t fpu;
+ i386_exception_state_t exc;
+ };
+
+ struct State
+ {
+ Context context;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+ }
+ void InvalidateAllRegisterStates()
+ {
+ SetError (e_regSetALL, Read, -1);
+ }
+ kern_return_t 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 e_regSetALL: return gpr_errs[err_idx] |
+ fpu_errs[err_idx] |
+ exc_errs[err_idx];
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetFPU: return fpu_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+ bool SetError (int flavor, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] =
+ fpu_errs[err_idx] =
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPU:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid (int flavor) const
+ {
+ return GetError(flavor, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetFPUState (bool force);
+ kern_return_t GetEXCState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetFPUState ();
+ kern_return_t SetEXCState ();
+
+ MachThread *m_thread;
+ State m_state;
+};
+
+typedef DNBArchImplI386 DNBArch;
+
+#endif // #if defined (__i386__)
+#endif // #ifndef __DNBArchImplI386_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp b/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp
new file mode 100644
index 00000000000..abd1a353158
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp
@@ -0,0 +1,569 @@
+//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+
+#if __DARWIN_UNIX03
+#define PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(reg) __##reg
+#else
+#define PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(reg) reg
+#endif
+
+#include "MacOSX/ppc/DNBArchImpl.h"
+#include "MacOSX/MachThread.h"
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "DNBRegisterInfo.h"
+
+static const uint8_t g_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 };
+
+const uint8_t * const
+DNBArchMachPPC::SoftwareBreakpointOpcode (nub_size_t size)
+{
+ if (size == 4)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+uint32_t
+DNBArchMachPPC::GetCPUType()
+{
+ return CPU_TYPE_POWERPC;
+}
+
+uint64_t
+DNBArchMachPPC::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0);
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachPPC::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0) = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchMachPPC::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(r1);
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachPPC::GetGPRState(bool force)
+{
+ if (force || m_state.GetError(e_regSetGPR, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeGPR;
+ m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->ThreadID(), e_regSetGPR, (thread_state_t)&m_state.gpr, &count));
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+kern_return_t
+DNBArchMachPPC::GetFPRState(bool force)
+{
+ if (force || m_state.GetError(e_regSetFPR, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeFPR;
+ m_state.SetError(e_regSetFPR, Read, ::thread_get_state(m_thread->ThreadID(), e_regSetFPR, (thread_state_t)&m_state.fpr, &count));
+ }
+ return m_state.GetError(e_regSetFPR, Read);
+}
+
+kern_return_t
+DNBArchMachPPC::GetEXCState(bool force)
+{
+ if (force || m_state.GetError(e_regSetEXC, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeEXC;
+ m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->ThreadID(), e_regSetEXC, (thread_state_t)&m_state.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t
+DNBArchMachPPC::GetVECState(bool force)
+{
+ if (force || m_state.GetError(e_regSetVEC, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeVEC;
+ m_state.SetError(e_regSetVEC, Read, ::thread_get_state(m_thread->ThreadID(), e_regSetVEC, (thread_state_t)&m_state.vec, &count));
+ }
+ return m_state.GetError(e_regSetVEC, Read);
+}
+
+kern_return_t
+DNBArchMachPPC::SetGPRState()
+{
+ m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->ThreadID(), e_regSetGPR, (thread_state_t)&m_state.gpr, e_regSetWordSizeGPR));
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t
+DNBArchMachPPC::SetFPRState()
+{
+ m_state.SetError(e_regSetFPR, Write, ::thread_set_state(m_thread->ThreadID(), e_regSetFPR, (thread_state_t)&m_state.fpr, e_regSetWordSizeFPR));
+ return m_state.GetError(e_regSetFPR, Write);
+}
+
+kern_return_t
+DNBArchMachPPC::SetEXCState()
+{
+ m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->ThreadID(), e_regSetEXC, (thread_state_t)&m_state.exc, e_regSetWordSizeEXC));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+kern_return_t
+DNBArchMachPPC::SetVECState()
+{
+ m_state.SetError(e_regSetVEC, Write, ::thread_set_state(m_thread->ThreadID(), e_regSetVEC, (thread_state_t)&m_state.vec, e_regSetWordSizeVEC));
+ return m_state.GetError(e_regSetVEC, Write);
+}
+
+bool
+DNBArchMachPPC::ThreadWillResume()
+{
+ bool success = true;
+
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ // This is the primary thread, let the arch do anything it needs
+ success = EnableHardwareSingleStep(true) == KERN_SUCCESS;
+ }
+ return success;
+}
+
+bool
+DNBArchMachPPC::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchMachPPC::EnableHardwareSingleStep (bool enable)
+{
+ DNBLogThreadedIf(LOG_STEP, "DNBArchMachPPC::EnableHardwareSingleStep( enable = %d )", enable);
+ if (GetGPRState(false) == KERN_SUCCESS)
+ {
+ const uint32_t trace_bit = 0x400;
+ if (enable)
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr1) |= trace_bit;
+ else
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr1) &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+//----------------------------------------------------------------------
+// Register information defintions for 32 bit PowerPC.
+//----------------------------------------------------------------------
+
+enum gpr_regnums
+{
+ e_regNumGPR_srr0,
+ e_regNumGPR_srr1,
+ e_regNumGPR_r0,
+ e_regNumGPR_r1,
+ e_regNumGPR_r2,
+ e_regNumGPR_r3,
+ e_regNumGPR_r4,
+ e_regNumGPR_r5,
+ e_regNumGPR_r6,
+ e_regNumGPR_r7,
+ e_regNumGPR_r8,
+ e_regNumGPR_r9,
+ e_regNumGPR_r10,
+ e_regNumGPR_r11,
+ e_regNumGPR_r12,
+ e_regNumGPR_r13,
+ e_regNumGPR_r14,
+ e_regNumGPR_r15,
+ e_regNumGPR_r16,
+ e_regNumGPR_r17,
+ e_regNumGPR_r18,
+ e_regNumGPR_r19,
+ e_regNumGPR_r20,
+ e_regNumGPR_r21,
+ e_regNumGPR_r22,
+ e_regNumGPR_r23,
+ e_regNumGPR_r24,
+ e_regNumGPR_r25,
+ e_regNumGPR_r26,
+ e_regNumGPR_r27,
+ e_regNumGPR_r28,
+ e_regNumGPR_r29,
+ e_regNumGPR_r30,
+ e_regNumGPR_r31,
+ e_regNumGPR_cr,
+ e_regNumGPR_xer,
+ e_regNumGPR_lr,
+ e_regNumGPR_ctr,
+ e_regNumGPR_mq,
+ e_regNumGPR_vrsave
+};
+
+
+
+
+// General purpose registers
+static DNBRegisterInfo g_gpr_registers[] =
+{
+ { "srr0" , Uint, 4, Hex },
+ { "srr1" , Uint, 4, Hex },
+ { "r0" , Uint, 4, Hex },
+ { "r1" , Uint, 4, Hex },
+ { "r2" , Uint, 4, Hex },
+ { "r3" , Uint, 4, Hex },
+ { "r4" , Uint, 4, Hex },
+ { "r5" , Uint, 4, Hex },
+ { "r6" , Uint, 4, Hex },
+ { "r7" , Uint, 4, Hex },
+ { "r8" , Uint, 4, Hex },
+ { "r9" , Uint, 4, Hex },
+ { "r10" , Uint, 4, Hex },
+ { "r11" , Uint, 4, Hex },
+ { "r12" , Uint, 4, Hex },
+ { "r13" , Uint, 4, Hex },
+ { "r14" , Uint, 4, Hex },
+ { "r15" , Uint, 4, Hex },
+ { "r16" , Uint, 4, Hex },
+ { "r17" , Uint, 4, Hex },
+ { "r18" , Uint, 4, Hex },
+ { "r19" , Uint, 4, Hex },
+ { "r20" , Uint, 4, Hex },
+ { "r21" , Uint, 4, Hex },
+ { "r22" , Uint, 4, Hex },
+ { "r23" , Uint, 4, Hex },
+ { "r24" , Uint, 4, Hex },
+ { "r25" , Uint, 4, Hex },
+ { "r26" , Uint, 4, Hex },
+ { "r27" , Uint, 4, Hex },
+ { "r28" , Uint, 4, Hex },
+ { "r29" , Uint, 4, Hex },
+ { "r30" , Uint, 4, Hex },
+ { "r31" , Uint, 4, Hex },
+ { "cr" , Uint, 4, Hex },
+ { "xer" , Uint, 4, Hex },
+ { "lr" , Uint, 4, Hex },
+ { "ctr" , Uint, 4, Hex },
+ { "mq" , Uint, 4, Hex },
+ { "vrsave", Uint, 4, Hex },
+};
+
+// Floating point registers
+static DNBRegisterInfo g_fpr_registers[] =
+{
+ { "fp0" , IEEE754, 8, Float },
+ { "fp1" , IEEE754, 8, Float },
+ { "fp2" , IEEE754, 8, Float },
+ { "fp3" , IEEE754, 8, Float },
+ { "fp4" , IEEE754, 8, Float },
+ { "fp5" , IEEE754, 8, Float },
+ { "fp6" , IEEE754, 8, Float },
+ { "fp7" , IEEE754, 8, Float },
+ { "fp8" , IEEE754, 8, Float },
+ { "fp9" , IEEE754, 8, Float },
+ { "fp10" , IEEE754, 8, Float },
+ { "fp11" , IEEE754, 8, Float },
+ { "fp12" , IEEE754, 8, Float },
+ { "fp13" , IEEE754, 8, Float },
+ { "fp14" , IEEE754, 8, Float },
+ { "fp15" , IEEE754, 8, Float },
+ { "fp16" , IEEE754, 8, Float },
+ { "fp17" , IEEE754, 8, Float },
+ { "fp18" , IEEE754, 8, Float },
+ { "fp19" , IEEE754, 8, Float },
+ { "fp20" , IEEE754, 8, Float },
+ { "fp21" , IEEE754, 8, Float },
+ { "fp22" , IEEE754, 8, Float },
+ { "fp23" , IEEE754, 8, Float },
+ { "fp24" , IEEE754, 8, Float },
+ { "fp25" , IEEE754, 8, Float },
+ { "fp26" , IEEE754, 8, Float },
+ { "fp27" , IEEE754, 8, Float },
+ { "fp28" , IEEE754, 8, Float },
+ { "fp29" , IEEE754, 8, Float },
+ { "fp30" , IEEE754, 8, Float },
+ { "fp31" , IEEE754, 8, Float },
+ { "fpscr" , Uint, 4, Hex }
+};
+
+// Exception registers
+
+static DNBRegisterInfo g_exc_registers[] =
+{
+ { "dar" , Uint, 4, Hex },
+ { "dsisr" , Uint, 4, Hex },
+ { "exception" , Uint, 4, Hex }
+};
+
+// Altivec registers
+static DNBRegisterInfo g_vec_registers[] =
+{
+ { "vr0" , Vector, 16, VectorOfFloat32 },
+ { "vr1" , Vector, 16, VectorOfFloat32 },
+ { "vr2" , Vector, 16, VectorOfFloat32 },
+ { "vr3" , Vector, 16, VectorOfFloat32 },
+ { "vr4" , Vector, 16, VectorOfFloat32 },
+ { "vr5" , Vector, 16, VectorOfFloat32 },
+ { "vr6" , Vector, 16, VectorOfFloat32 },
+ { "vr7" , Vector, 16, VectorOfFloat32 },
+ { "vr8" , Vector, 16, VectorOfFloat32 },
+ { "vr9" , Vector, 16, VectorOfFloat32 },
+ { "vr10" , Vector, 16, VectorOfFloat32 },
+ { "vr11" , Vector, 16, VectorOfFloat32 },
+ { "vr12" , Vector, 16, VectorOfFloat32 },
+ { "vr13" , Vector, 16, VectorOfFloat32 },
+ { "vr14" , Vector, 16, VectorOfFloat32 },
+ { "vr15" , Vector, 16, VectorOfFloat32 },
+ { "vr16" , Vector, 16, VectorOfFloat32 },
+ { "vr17" , Vector, 16, VectorOfFloat32 },
+ { "vr18" , Vector, 16, VectorOfFloat32 },
+ { "vr19" , Vector, 16, VectorOfFloat32 },
+ { "vr20" , Vector, 16, VectorOfFloat32 },
+ { "vr21" , Vector, 16, VectorOfFloat32 },
+ { "vr22" , Vector, 16, VectorOfFloat32 },
+ { "vr23" , Vector, 16, VectorOfFloat32 },
+ { "vr24" , Vector, 16, VectorOfFloat32 },
+ { "vr25" , Vector, 16, VectorOfFloat32 },
+ { "vr26" , Vector, 16, VectorOfFloat32 },
+ { "vr27" , Vector, 16, VectorOfFloat32 },
+ { "vr28" , Vector, 16, VectorOfFloat32 },
+ { "vr29" , Vector, 16, VectorOfFloat32 },
+ { "vr30" , Vector, 16, VectorOfFloat32 },
+ { "vr31" , Vector, 16, VectorOfFloat32 },
+ { "vscr" , Uint, 16, Hex },
+ { "vrvalid" , Uint, 4, Hex }
+};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t k_num_fpr_registers = sizeof(g_fpr_registers)/sizeof(DNBRegisterInfo);
+const size_t k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+const size_t k_num_vec_registers = sizeof(g_vec_registers)/sizeof(DNBRegisterInfo);
+// Total number of registers for this architecture
+const size_t k_num_ppc_registers = k_num_gpr_registers + k_num_fpr_registers + k_num_exc_registers + k_num_vec_registers;
+
+//----------------------------------------------------------------------
+// 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 DNBRegisterSetInfo g_reg_sets[] =
+{
+ { "PowerPC Registers", NULL, k_num_ppc_registers },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_fpr_registers, k_num_fpr_registers },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers },
+ { "Altivec Registers", g_vec_registers, k_num_vec_registers }
+};
+// Total number of register sets for this architecture
+const size_t k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo);
+
+
+const DNBRegisterSetInfo *
+DNBArchMachPPC::GetRegisterSetInfo(nub_size_t *num_reg_sets) const
+{
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool
+DNBArchMachPPC::GetRegisterValue(int set, int reg, DNBRegisterValue *value) const
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = e_regNumGPR_srr0;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = e_regNumGPR_r1;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ // Return false for now instead of returning r30 as gcc 3.x would
+ // use a variety of registers for the FP and it takes inspecting
+ // the stack to make sure there is a frame pointer before we can
+ // determine the FP.
+ return false;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = e_regNumGPR_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = e_regNumGPR_srr1;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (!m_state.RegsAreValid(set))
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ value->value.uint32 = (&m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPR:
+ if (reg < 32)
+ {
+ value->value.float64 = m_state.fpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(fpregs)[reg];
+ return true;
+ }
+ else if (reg == 32)
+ {
+ value->value.uint32 = m_state.fpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(fpscr);
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ value->value.uint32 = (&m_state.exc.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(dar))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetVEC:
+ if (reg < k_num_vec_registers)
+ {
+ if (reg < 33) // FP0 - FP31 and VSCR
+ {
+ // Copy all 4 uint32 values for this vector register
+ value->value.v_uint32[0] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][0];
+ value->value.v_uint32[1] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][1];
+ value->value.v_uint32[2] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][2];
+ value->value.v_uint32[3] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][3];
+ return true;
+ }
+ else if (reg == 34) // VRVALID
+ {
+ value->value.uint32 = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vrvalid);
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+
+kern_return_t
+DNBArchMachPPC::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL:
+ return GetGPRState(force) |
+ GetFPRState(force) |
+ GetEXCState(force) |
+ GetVECState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetFPR: return GetFPRState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ case e_regSetVEC: return GetVECState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchMachPPC::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ kern_return_t err = GetRegisterState(set, false);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() | SetFPRState() | SetEXCState() | SetVECState();
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetFPR: return SetFPRState();
+ case e_regSetEXC: return SetEXCState();
+ case e_regSetVEC: return SetVECState();
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchMachPPC::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+
+#endif // #if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+
diff --git a/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h b/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h
new file mode 100644
index 00000000000..6263407460a
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h
@@ -0,0 +1,180 @@
+//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DebugNubArchMachPPC_h__
+#define __DebugNubArchMachPPC_h__
+
+#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+
+#include "DNBArch.h"
+
+class MachThread;
+
+class DNBArchMachPPC : public DNBArchProtocol
+{
+public:
+ DNBArchMachPPC(MachThread *thread) :
+ m_thread(thread),
+ m_state()
+ {
+ }
+
+ virtual ~DNBArchMachPPC()
+ {
+ }
+
+ virtual const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets) const;
+ virtual bool GetRegisterValue(int set, int reg, DNBRegisterValue *value) const;
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual bool ThreadWillResume();
+ virtual bool ThreadDidStop();
+
+ static const uint8_t * const SoftwareBreakpointOpcode (nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+protected:
+
+
+ kern_return_t EnableHardwareSingleStep (bool enable);
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPR,
+ e_regSetEXC,
+ e_regSetVEC,
+ kNumRegisterSets
+ } RegisterSet;
+
+ typedef enum RegisterSetWordSizeTag
+ {
+ e_regSetWordSizeGPR = PPC_THREAD_STATE_COUNT,
+ e_regSetWordSizeFPR = PPC_FLOAT_STATE_COUNT,
+ e_regSetWordSizeEXC = PPC_EXCEPTION_STATE_COUNT,
+ e_regSetWordSizeVEC = PPC_VECTOR_STATE_COUNT
+ } RegisterSetWordSize;
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ struct State
+ {
+ ppc_thread_state_t gpr;
+ ppc_float_state_t fpr;
+ ppc_exception_state_t exc;
+ ppc_vector_state_t vec;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpr_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t vec_errs[2]; // Read/Write errors
+
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpr_errs[i] = -1;
+ exc_errs[i] = -1;
+ vec_errs[i] = -1;
+ }
+ }
+ void InvalidateAllRegisterStates()
+ {
+ SetError (e_regSetALL, Read, -1);
+ }
+ kern_return_t GetError (int set, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL: return gpr_errs[err_idx] | fpr_errs[err_idx] | exc_errs[err_idx] | vec_errs[err_idx];
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetFPR: return fpr_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ case e_regSetVEC: return vec_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+ bool SetError (int set, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] = fpr_errs[err_idx] = exc_errs[err_idx] = vec_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPR:
+ fpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetVEC:
+ vec_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid (int set) const
+ {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetFPRState (bool force);
+ kern_return_t GetEXCState (bool force);
+ kern_return_t GetVECState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetFPRState ();
+ kern_return_t SetEXCState ();
+ kern_return_t SetVECState ();
+
+protected:
+ MachThread * m_thread;
+ State m_state;
+};
+
+typedef DNBArchMachPPC DNBArch;
+#endif // #if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+#endif // #ifndef __DebugNubArchMachPPC_h__
diff --git a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp
new file mode 100644
index 00000000000..1f18156650c
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp
@@ -0,0 +1,1035 @@
+//===-- DNBArchImplX86_64.cpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__x86_64__)
+
+#include <sys/cdefs.h>
+
+#include "MacOSX/x86_64/DNBArchImplX86_64.h"
+#include "DNBLog.h"
+#include "MachThread.h"
+#include "MachProcess.h"
+
+static const uint8_t g_breakpoint_opcode[] = { 0xCC };
+
+const uint8_t * const
+DNBArchImplX86_64::SoftwareBreakpointOpcode (nub_size_t byte_size)
+{
+ if (byte_size == 1)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+uint32_t
+DNBArchImplX86_64::GetCPUType()
+{
+ return CPU_TYPE_X86_64;
+}
+
+uint64_t
+DNBArchImplX86_64::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__rip;
+ return failValue;
+}
+
+kern_return_t
+DNBArchImplX86_64::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.context.gpr.__rip = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchImplX86_64::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__rsp;
+ return failValue;
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_GPR_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t
+DNBArchImplX86_64::GetGPRState(bool force)
+{
+ if (force || m_state.GetError(e_regSetGPR, Read))
+ {
+#if DEBUG_GPR_VALUES
+ m_state.context.gpr.__rax = ('a' << 8) + 'x';
+ m_state.context.gpr.__rbx = ('b' << 8) + 'x';
+ m_state.context.gpr.__rcx = ('c' << 8) + 'x';
+ m_state.context.gpr.__rdx = ('d' << 8) + 'x';
+ m_state.context.gpr.__rdi = ('d' << 8) + 'i';
+ m_state.context.gpr.__rsi = ('s' << 8) + 'i';
+ m_state.context.gpr.__rbp = ('b' << 8) + 'p';
+ m_state.context.gpr.__rsp = ('s' << 8) + 'p';
+ m_state.context.gpr.__r8 = ('r' << 8) + '8';
+ m_state.context.gpr.__r9 = ('r' << 8) + '9';
+ m_state.context.gpr.__r10 = ('r' << 8) + 'a';
+ m_state.context.gpr.__r11 = ('r' << 8) + 'b';
+ m_state.context.gpr.__r12 = ('r' << 8) + 'c';
+ m_state.context.gpr.__r13 = ('r' << 8) + 'd';
+ m_state.context.gpr.__r14 = ('r' << 8) + 'e';
+ m_state.context.gpr.__r15 = ('r' << 8) + 'f';
+ m_state.context.gpr.__rip = ('i' << 8) + 'p';
+ m_state.context.gpr.__rflags = ('f' << 8) + 'l';
+ m_state.context.gpr.__cs = ('c' << 8) + 's';
+ m_state.context.gpr.__fs = ('f' << 8) + 's';
+ m_state.context.gpr.__gs = ('g' << 8) + 's';
+ m_state.SetError(e_regSetGPR, Read, 0);
+#else
+ mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
+ m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->ThreadID(), x86_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, &count));
+ DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
+ "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx"
+ "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx"
+ "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx"
+ "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx"
+ "\n\trip = %16.16llx"
+ "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx",
+ m_thread->ThreadID(), x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT,
+ m_state.GetError(e_regSetGPR, Read),
+ m_state.context.gpr.__rax,m_state.context.gpr.__rbx,m_state.context.gpr.__rcx,
+ m_state.context.gpr.__rdx,m_state.context.gpr.__rdi,m_state.context.gpr.__rsi,
+ m_state.context.gpr.__rbp,m_state.context.gpr.__rsp,m_state.context.gpr.__r8,
+ m_state.context.gpr.__r9, m_state.context.gpr.__r10,m_state.context.gpr.__r11,
+ m_state.context.gpr.__r12,m_state.context.gpr.__r13,m_state.context.gpr.__r14,
+ m_state.context.gpr.__r15,m_state.context.gpr.__rip,m_state.context.gpr.__rflags,
+ m_state.context.gpr.__cs,m_state.context.gpr.__fs, m_state.context.gpr.__gs);
+
+ // DNBLogThreadedIf (LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
+ // "\n\trax = %16.16llx"
+ // "\n\trbx = %16.16llx"
+ // "\n\trcx = %16.16llx"
+ // "\n\trdx = %16.16llx"
+ // "\n\trdi = %16.16llx"
+ // "\n\trsi = %16.16llx"
+ // "\n\trbp = %16.16llx"
+ // "\n\trsp = %16.16llx"
+ // "\n\t r8 = %16.16llx"
+ // "\n\t r9 = %16.16llx"
+ // "\n\tr10 = %16.16llx"
+ // "\n\tr11 = %16.16llx"
+ // "\n\tr12 = %16.16llx"
+ // "\n\tr13 = %16.16llx"
+ // "\n\tr14 = %16.16llx"
+ // "\n\tr15 = %16.16llx"
+ // "\n\trip = %16.16llx"
+ // "\n\tflg = %16.16llx"
+ // "\n\t cs = %16.16llx"
+ // "\n\t fs = %16.16llx"
+ // "\n\t gs = %16.16llx",
+ // m_thread->ThreadID(),
+ // x86_THREAD_STATE64,
+ // x86_THREAD_STATE64_COUNT,
+ // m_state.GetError(e_regSetGPR, Read),
+ // m_state.context.gpr.__rax,
+ // m_state.context.gpr.__rbx,
+ // m_state.context.gpr.__rcx,
+ // m_state.context.gpr.__rdx,
+ // m_state.context.gpr.__rdi,
+ // m_state.context.gpr.__rsi,
+ // m_state.context.gpr.__rbp,
+ // m_state.context.gpr.__rsp,
+ // m_state.context.gpr.__r8,
+ // m_state.context.gpr.__r9,
+ // m_state.context.gpr.__r10,
+ // m_state.context.gpr.__r11,
+ // m_state.context.gpr.__r12,
+ // m_state.context.gpr.__r13,
+ // m_state.context.gpr.__r14,
+ // m_state.context.gpr.__r15,
+ // m_state.context.gpr.__rip,
+ // m_state.context.gpr.__rflags,
+ // m_state.context.gpr.__cs,
+ // m_state.context.gpr.__fs,
+ // m_state.context.gpr.__gs);
+#endif
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_FPU_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t
+DNBArchImplX86_64::GetFPUState(bool force)
+{
+ if (force || m_state.GetError(e_regSetFPU, Read))
+ {
+#if DEBUG_FPU_VALUES
+ m_state.context.fpu.__fpu_reserved[0] = -1;
+ m_state.context.fpu.__fpu_reserved[1] = -1;
+ *(uint16_t *)&(m_state.context.fpu.__fpu_fcw) = 0x1234;
+ *(uint16_t *)&(m_state.context.fpu.__fpu_fsw) = 0x5678;
+ m_state.context.fpu.__fpu_ftw = 1;
+ m_state.context.fpu.__fpu_rsrv1 = UINT8_MAX;
+ m_state.context.fpu.__fpu_fop = 2;
+ m_state.context.fpu.__fpu_ip = 3;
+ m_state.context.fpu.__fpu_cs = 4;
+ m_state.context.fpu.__fpu_rsrv2 = 5;
+ m_state.context.fpu.__fpu_dp = 6;
+ m_state.context.fpu.__fpu_ds = 7;
+ m_state.context.fpu.__fpu_rsrv3 = UINT16_MAX;
+ m_state.context.fpu.__fpu_mxcsr = 8;
+ m_state.context.fpu.__fpu_mxcsrmask = 9;
+ int i;
+ for (i=0; i<16; ++i)
+ {
+ if (i<10)
+ {
+ m_state.context.fpu.__fpu_stmm0.__mmst_reg[i] = 'a';
+ m_state.context.fpu.__fpu_stmm1.__mmst_reg[i] = 'b';
+ m_state.context.fpu.__fpu_stmm2.__mmst_reg[i] = 'c';
+ m_state.context.fpu.__fpu_stmm3.__mmst_reg[i] = 'd';
+ m_state.context.fpu.__fpu_stmm4.__mmst_reg[i] = 'e';
+ m_state.context.fpu.__fpu_stmm5.__mmst_reg[i] = 'f';
+ m_state.context.fpu.__fpu_stmm6.__mmst_reg[i] = 'g';
+ m_state.context.fpu.__fpu_stmm7.__mmst_reg[i] = 'h';
+ }
+ else
+ {
+ m_state.context.fpu.__fpu_stmm0.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm1.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm2.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm3.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm4.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm5.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm6.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_stmm7.__mmst_reg[i] = INT8_MIN;
+ }
+
+ m_state.context.fpu.__fpu_xmm0.__xmm_reg[i] = '0';
+ m_state.context.fpu.__fpu_xmm1.__xmm_reg[i] = '1';
+ m_state.context.fpu.__fpu_xmm2.__xmm_reg[i] = '2';
+ m_state.context.fpu.__fpu_xmm3.__xmm_reg[i] = '3';
+ m_state.context.fpu.__fpu_xmm4.__xmm_reg[i] = '4';
+ m_state.context.fpu.__fpu_xmm5.__xmm_reg[i] = '5';
+ m_state.context.fpu.__fpu_xmm6.__xmm_reg[i] = '6';
+ m_state.context.fpu.__fpu_xmm7.__xmm_reg[i] = '7';
+ m_state.context.fpu.__fpu_xmm8.__xmm_reg[i] = '8';
+ m_state.context.fpu.__fpu_xmm9.__xmm_reg[i] = '9';
+ m_state.context.fpu.__fpu_xmm10.__xmm_reg[i] = 'A';
+ m_state.context.fpu.__fpu_xmm11.__xmm_reg[i] = 'B';
+ m_state.context.fpu.__fpu_xmm12.__xmm_reg[i] = 'C';
+ m_state.context.fpu.__fpu_xmm13.__xmm_reg[i] = 'D';
+ m_state.context.fpu.__fpu_xmm14.__xmm_reg[i] = 'E';
+ m_state.context.fpu.__fpu_xmm15.__xmm_reg[i] = 'F';
+ }
+ for (i=0; i<sizeof(m_state.context.fpu.__fpu_rsrv4); ++i)
+ m_state.context.fpu.__fpu_rsrv4[i] = INT8_MIN;
+ m_state.context.fpu.__fpu_reserved1 = -1;
+ m_state.SetError(e_regSetFPU, Read, 0);
+#else
+ mach_msg_type_number_t count = x86_FLOAT_STATE64_COUNT;
+ m_state.SetError(e_regSetFPU, Read, ::thread_get_state(m_thread->ThreadID(), x86_FLOAT_STATE64, (thread_state_t)&m_state.context.fpu, &count));
+#endif
+ }
+ return m_state.GetError(e_regSetFPU, Read);
+}
+
+kern_return_t
+DNBArchImplX86_64::GetEXCState(bool force)
+{
+ if (force || m_state.GetError(e_regSetEXC, Read))
+ {
+ mach_msg_type_number_t count = X86_EXCEPTION_STATE64_COUNT;
+ m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->ThreadID(), x86_EXCEPTION_STATE64, (thread_state_t)&m_state.context.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t
+DNBArchImplX86_64::SetGPRState()
+{
+ m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->ThreadID(), x86_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, x86_THREAD_STATE64_COUNT));
+ DNBLogThreadedIf (LOG_THREAD, "::thread_set_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
+ "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx"
+ "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx"
+ "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx"
+ "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx"
+ "\n\trip = %16.16llx"
+ "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx",
+ m_thread->ThreadID(), x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT,
+ m_state.GetError(e_regSetGPR, Write),
+ m_state.context.gpr.__rax,m_state.context.gpr.__rbx,m_state.context.gpr.__rcx,
+ m_state.context.gpr.__rdx,m_state.context.gpr.__rdi,m_state.context.gpr.__rsi,
+ m_state.context.gpr.__rbp,m_state.context.gpr.__rsp,m_state.context.gpr.__r8,
+ m_state.context.gpr.__r9, m_state.context.gpr.__r10,m_state.context.gpr.__r11,
+ m_state.context.gpr.__r12,m_state.context.gpr.__r13,m_state.context.gpr.__r14,
+ m_state.context.gpr.__r15,m_state.context.gpr.__rip,m_state.context.gpr.__rflags,
+ m_state.context.gpr.__cs, m_state.context.gpr.__fs, m_state.context.gpr.__gs);
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t
+DNBArchImplX86_64::SetFPUState()
+{
+ m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->ThreadID(), x86_FLOAT_STATE64, (thread_state_t)&m_state.context.fpu, x86_FLOAT_STATE64_COUNT));
+ return m_state.GetError(e_regSetFPU, Write);
+}
+
+kern_return_t
+DNBArchImplX86_64::SetEXCState()
+{
+ m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->ThreadID(), x86_EXCEPTION_STATE64, (thread_state_t)&m_state.context.exc, X86_EXCEPTION_STATE64_COUNT));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+void
+DNBArchImplX86_64::ThreadWillResume()
+{
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ // This is the primary thread, let the arch do anything it needs
+ EnableHardwareSingleStep(true) == KERN_SUCCESS;
+ }
+}
+
+bool
+DNBArchImplX86_64::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool
+DNBArchImplX86_64::NotifyException(MachException::Data& exc)
+{
+ switch (exc.exc_type)
+ {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2)
+ {
+ nub_addr_t pc = GetPC(INVALID_NUB_ADDRESS);
+ if (pc != INVALID_NUB_ADDRESS && pc > 0)
+ {
+ pc -= 1;
+ // Check for a breakpoint at one byte prior to the current PC value
+ // since the PC will be just past the trap.
+
+ nub_break_t breakID = m_thread->Process()->Breakpoints().FindIDByAddress(pc);
+ if (NUB_BREAK_ID_IS_VALID(breakID))
+ {
+ // Backup the PC for i386 since the trap was taken and the PC
+ // is at the address following the single byte trap instruction.
+ if (m_state.context.gpr.__rip > 0)
+ {
+ m_state.context.gpr.__rip = pc;
+ // Write the new PC back out
+ SetGPRState ();
+ }
+
+ m_thread->SetCurrentBreakpoint(breakID);
+ }
+ return true;
+ }
+ }
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ return false;
+}
+
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchImplX86_64::EnableHardwareSingleStep (bool enable)
+{
+ if (GetGPRState(false) == KERN_SUCCESS)
+ {
+ const uint32_t trace_bit = 0x100u;
+ if (enable)
+ m_state.context.gpr.__rflags |= trace_bit;
+ else
+ m_state.context.gpr.__rflags &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+
+//----------------------------------------------------------------------
+// Register information defintions
+//----------------------------------------------------------------------
+
+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,
+ k_num_gpr_regs
+};
+
+enum {
+ 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,
+ k_num_fpu_regs,
+
+ // 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 {
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+ k_num_exc_regs,
+};
+
+
+enum gcc_dwarf_regnums
+{
+ gcc_dwarf_rax = 0,
+ gcc_dwarf_rdx,
+ gcc_dwarf_rcx,
+ gcc_dwarf_rbx,
+ gcc_dwarf_rsi,
+ gcc_dwarf_rdi,
+ gcc_dwarf_rbp,
+ gcc_dwarf_rsp,
+ gcc_dwarf_r8,
+ gcc_dwarf_r9,
+ gcc_dwarf_r10,
+ gcc_dwarf_r11,
+ gcc_dwarf_r12,
+ gcc_dwarf_r13,
+ gcc_dwarf_r14,
+ gcc_dwarf_r15,
+ gcc_dwarf_rip,
+ gcc_dwarf_xmm0,
+ gcc_dwarf_xmm1,
+ gcc_dwarf_xmm2,
+ gcc_dwarf_xmm3,
+ gcc_dwarf_xmm4,
+ gcc_dwarf_xmm5,
+ gcc_dwarf_xmm6,
+ gcc_dwarf_xmm7,
+ gcc_dwarf_xmm8,
+ gcc_dwarf_xmm9,
+ gcc_dwarf_xmm10,
+ gcc_dwarf_xmm11,
+ gcc_dwarf_xmm12,
+ gcc_dwarf_xmm13,
+ gcc_dwarf_xmm14,
+ gcc_dwarf_xmm15,
+ gcc_dwarf_stmm0,
+ gcc_dwarf_stmm1,
+ gcc_dwarf_stmm2,
+ gcc_dwarf_stmm3,
+ gcc_dwarf_stmm4,
+ gcc_dwarf_stmm5,
+ gcc_dwarf_stmm6,
+ gcc_dwarf_stmm7,
+
+};
+
+enum gdb_regnums
+{
+ gdb_rax = 0,
+ gdb_rbx = 1,
+ gdb_rcx = 2,
+ gdb_rdx = 3,
+ gdb_rsi = 4,
+ gdb_rdi = 5,
+ gdb_rbp = 6,
+ gdb_rsp = 7,
+ gdb_r8 = 8,
+ gdb_r9 = 9,
+ gdb_r10 = 10,
+ gdb_r11 = 11,
+ gdb_r12 = 12,
+ gdb_r13 = 13,
+ gdb_r14 = 14,
+ gdb_r15 = 15,
+ gdb_rip = 16,
+ gdb_rflags = 17,
+ gdb_cs = 18,
+ gdb_ss = 19,
+ gdb_ds = 20,
+ gdb_es = 21,
+ gdb_fs = 22,
+ gdb_gs = 23,
+ gdb_stmm0 = 24,
+ gdb_stmm1 = 25,
+ gdb_stmm2 = 26,
+ gdb_stmm3 = 27,
+ gdb_stmm4 = 28,
+ gdb_stmm5 = 29,
+ gdb_stmm6 = 30,
+ gdb_stmm7 = 31,
+ gdb_fctrl = 32, gdb_fcw = gdb_fctrl,
+ gdb_fstat = 33, gdb_fsw = gdb_fstat,
+ gdb_ftag = 34, gdb_ftw = gdb_ftag,
+ gdb_fiseg = 35, gdb_fpu_cs = gdb_fiseg,
+ gdb_fioff = 36, gdb_ip = gdb_fioff,
+ gdb_foseg = 37, gdb_fpu_ds = gdb_foseg,
+ gdb_fooff = 38, gdb_dp = gdb_fooff,
+ gdb_fop = 39,
+ gdb_xmm0 = 40,
+ gdb_xmm1 = 41,
+ gdb_xmm2 = 42,
+ gdb_xmm3 = 43,
+ gdb_xmm4 = 44,
+ gdb_xmm5 = 45,
+ gdb_xmm6 = 46,
+ gdb_xmm7 = 47,
+ gdb_xmm8 = 48,
+ gdb_xmm9 = 49,
+ gdb_xmm10 = 50,
+ gdb_xmm11 = 51,
+ gdb_xmm12 = 52,
+ gdb_xmm13 = 53,
+ gdb_xmm14 = 54,
+ gdb_xmm15 = 55,
+ gdb_mxcsr = 56,
+};
+
+#define GPR_OFFSET(reg) (offsetof (DNBArchImplX86_64::GPR, __##reg))
+#define FPU_OFFSET(reg) (offsetof (DNBArchImplX86_64::FPU, __fpu_##reg) + offsetof (DNBArchImplX86_64::Context, fpu))
+#define EXC_OFFSET(reg) (offsetof (DNBArchImplX86_64::EXC, __##reg) + offsetof (DNBArchImplX86_64::Context, exc))
+
+#define GPR_SIZE(reg) (sizeof(((DNBArchImplX86_64::GPR *)NULL)->__##reg))
+#define FPU_SIZE_UINT(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg))
+#define FPU_SIZE_MMST(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg.__mmst_reg))
+#define FPU_SIZE_XMM(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg.__xmm_reg))
+#define EXC_SIZE(reg) (sizeof(((DNBArchImplX86_64::EXC *)NULL)->__##reg))
+
+// 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) { e_regSetGPR, gpr_##reg, #reg, NULL, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), gcc_dwarf_##reg, gcc_dwarf_##reg, INVALID_NUB_REGNUM, gdb_##reg }
+#define DEFINE_GPR_ALT(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), gcc_dwarf_##reg, gcc_dwarf_##reg, gen, gdb_##reg }
+#define DEFINE_GPR_ALT2(reg, alt) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, gdb_##reg }
+
+// General purpose registers for 64 bit
+const DNBRegisterInfo
+DNBArchImplX86_64::g_gpr_registers[] =
+{
+ DEFINE_GPR (rax),
+ DEFINE_GPR (rbx),
+ DEFINE_GPR (rcx),
+ DEFINE_GPR (rdx),
+ DEFINE_GPR (rdi),
+ DEFINE_GPR (rsi),
+ DEFINE_GPR_ALT (rbp, "fp", GENERIC_REGNUM_FP),
+ DEFINE_GPR_ALT (rsp, "sp", GENERIC_REGNUM_SP),
+ DEFINE_GPR (r8),
+ DEFINE_GPR (r9),
+ DEFINE_GPR (r10),
+ DEFINE_GPR (r11),
+ DEFINE_GPR (r12),
+ DEFINE_GPR (r13),
+ DEFINE_GPR (r14),
+ DEFINE_GPR (r15),
+ DEFINE_GPR_ALT (rip, "pc", GENERIC_REGNUM_PC),
+ DEFINE_GPR_ALT2 (rflags, "flags"),
+ DEFINE_GPR_ALT2 (cs, NULL),
+ DEFINE_GPR_ALT2 (fs, NULL),
+ DEFINE_GPR_ALT2 (gs, NULL),
+};
+
+// Floating point registers 64 bit
+const DNBRegisterInfo
+DNBArchImplX86_64::g_fpu_registers[] =
+{
+ { e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , FPU_OFFSET(fcw) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , FPU_OFFSET(fsw) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , FPU_OFFSET(ftw) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , FPU_OFFSET(fop) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , FPU_OFFSET(ip) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , FPU_OFFSET(cs) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , FPU_OFFSET(dp) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , FPU_OFFSET(ds) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , FPU_OFFSET(mxcsr) , -1, -1, -1, -1 },
+ { e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , FPU_OFFSET(mxcsrmask) , -1, -1, -1, -1 },
+
+ { e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), gcc_dwarf_stmm0, gcc_dwarf_stmm0, -1, gdb_stmm0 },
+ { e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), gcc_dwarf_stmm1, gcc_dwarf_stmm1, -1, gdb_stmm1 },
+ { e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), gcc_dwarf_stmm2, gcc_dwarf_stmm2, -1, gdb_stmm2 },
+ { e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), gcc_dwarf_stmm3, gcc_dwarf_stmm3, -1, gdb_stmm3 },
+ { e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), gcc_dwarf_stmm4, gcc_dwarf_stmm4, -1, gdb_stmm4 },
+ { e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), gcc_dwarf_stmm5, gcc_dwarf_stmm5, -1, gdb_stmm5 },
+ { e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), gcc_dwarf_stmm6, gcc_dwarf_stmm6, -1, gdb_stmm6 },
+ { e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), gcc_dwarf_stmm7, gcc_dwarf_stmm7, -1, gdb_stmm7 },
+
+ { e_regSetFPU, fpu_xmm0 , "xmm0" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0) , FPU_OFFSET(xmm0) , gcc_dwarf_xmm0 , gcc_dwarf_xmm0 , -1, gdb_xmm0 },
+ { e_regSetFPU, fpu_xmm1 , "xmm1" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1) , FPU_OFFSET(xmm1) , gcc_dwarf_xmm1 , gcc_dwarf_xmm1 , -1, gdb_xmm1 },
+ { e_regSetFPU, fpu_xmm2 , "xmm2" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2) , FPU_OFFSET(xmm2) , gcc_dwarf_xmm2 , gcc_dwarf_xmm2 , -1, gdb_xmm2 },
+ { e_regSetFPU, fpu_xmm3 , "xmm3" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3) , FPU_OFFSET(xmm3) , gcc_dwarf_xmm3 , gcc_dwarf_xmm3 , -1, gdb_xmm3 },
+ { e_regSetFPU, fpu_xmm4 , "xmm4" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4) , FPU_OFFSET(xmm4) , gcc_dwarf_xmm4 , gcc_dwarf_xmm4 , -1, gdb_xmm4 },
+ { e_regSetFPU, fpu_xmm5 , "xmm5" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5) , FPU_OFFSET(xmm5) , gcc_dwarf_xmm5 , gcc_dwarf_xmm5 , -1, gdb_xmm5 },
+ { e_regSetFPU, fpu_xmm6 , "xmm6" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6) , FPU_OFFSET(xmm6) , gcc_dwarf_xmm6 , gcc_dwarf_xmm6 , -1, gdb_xmm6 },
+ { e_regSetFPU, fpu_xmm7 , "xmm7" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7) , FPU_OFFSET(xmm7) , gcc_dwarf_xmm7 , gcc_dwarf_xmm7 , -1, gdb_xmm7 },
+ { e_regSetFPU, fpu_xmm8 , "xmm8" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm8) , FPU_OFFSET(xmm8) , gcc_dwarf_xmm8 , gcc_dwarf_xmm8 , -1, gdb_xmm8 },
+ { e_regSetFPU, fpu_xmm9 , "xmm9" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm9) , FPU_OFFSET(xmm9) , gcc_dwarf_xmm9 , gcc_dwarf_xmm9 , -1, gdb_xmm9 },
+ { e_regSetFPU, fpu_xmm10, "xmm10" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm10) , FPU_OFFSET(xmm10), gcc_dwarf_xmm10, gcc_dwarf_xmm10, -1, gdb_xmm10 },
+ { e_regSetFPU, fpu_xmm11, "xmm11" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm11) , FPU_OFFSET(xmm11), gcc_dwarf_xmm11, gcc_dwarf_xmm11, -1, gdb_xmm11 },
+ { e_regSetFPU, fpu_xmm12, "xmm12" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm12) , FPU_OFFSET(xmm12), gcc_dwarf_xmm12, gcc_dwarf_xmm12, -1, gdb_xmm12 },
+ { e_regSetFPU, fpu_xmm13, "xmm13" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm13) , FPU_OFFSET(xmm13), gcc_dwarf_xmm13, gcc_dwarf_xmm13, -1, gdb_xmm13 },
+ { e_regSetFPU, fpu_xmm14, "xmm14" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm14) , FPU_OFFSET(xmm14), gcc_dwarf_xmm14, gcc_dwarf_xmm14, -1, gdb_xmm14 },
+ { e_regSetFPU, fpu_xmm15, "xmm15" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm15) , FPU_OFFSET(xmm15), gcc_dwarf_xmm15, gcc_dwarf_xmm15, -1, gdb_xmm15 },
+};
+
+// Exception registers
+
+const DNBRegisterInfo
+DNBArchImplX86_64::g_exc_registers[] =
+{
+ { e_regSetEXC, exc_trapno, "trapno" , NULL, Uint, Hex, EXC_SIZE (trapno) , EXC_OFFSET (trapno) , -1, -1, -1, -1 },
+ { e_regSetEXC, exc_err, "err" , NULL, Uint, Hex, EXC_SIZE (err) , EXC_OFFSET (err) , -1, -1, -1, -1 },
+ { e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex, EXC_SIZE (faultvaddr), EXC_OFFSET (faultvaddr) , -1, -1, -1, -1 }
+};
+
+// Number of registers in each register set
+const size_t DNBArchImplX86_64::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_fpu_registers = sizeof(g_fpu_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_all_registers = k_num_gpr_registers + k_num_fpu_registers + k_num_exc_registers;
+
+//----------------------------------------------------------------------
+// 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.
+//----------------------------------------------------------------------
+const DNBRegisterSetInfo
+DNBArchImplX86_64::g_reg_sets[] =
+{
+ { "x86_64 Registers", NULL, k_num_all_registers },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_fpu_registers, k_num_fpu_registers },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+// Total number of register sets for this architecture
+const size_t DNBArchImplX86_64::k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo);
+
+
+const DNBRegisterSetInfo *
+DNBArchImplX86_64::GetRegisterSetInfo (nub_size_t *num_reg_sets)
+{
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool
+DNBArchImplX86_64::GetRegisterValue(int set, int reg, DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_rip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_rsp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_rbp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_rflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ value->value.uint64 = ((uint64_t*)(&m_state.context.gpr))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPU:
+ switch (reg)
+ {
+ case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.__fpu_fcw)); return true;
+ case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.__fpu_fsw)); return true;
+ case fpu_ftw: value->value.uint8 = m_state.context.fpu.__fpu_ftw; return true;
+ case fpu_fop: value->value.uint16 = m_state.context.fpu.__fpu_fop; return true;
+ case fpu_ip: value->value.uint32 = m_state.context.fpu.__fpu_ip; return true;
+ case fpu_cs: value->value.uint16 = m_state.context.fpu.__fpu_cs; return true;
+ case fpu_dp: value->value.uint32 = m_state.context.fpu.__fpu_dp; return true;
+ case fpu_ds: value->value.uint16 = m_state.context.fpu.__fpu_ds; return true;
+ case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.__fpu_mxcsr; return true;
+ case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.__fpu_mxcsrmask; return true;
+
+ 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(&value->value.uint8, &m_state.context.fpu.__fpu_stmm0 + (reg - fpu_stmm0), 10);
+ return true;
+
+ 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(&value->value.uint8, &m_state.context.fpu.__fpu_xmm0 + (reg - fpu_xmm0), 16);
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ switch (reg)
+ {
+ case exc_trapno: value->value.uint32 = m_state.context.exc.__trapno; return true;
+ case exc_err: value->value.uint32 = m_state.context.exc.__err; return true;
+ case exc_faultvaddr:value->value.uint64 = m_state.context.exc.__faultvaddr; return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+
+bool
+DNBArchImplX86_64::SetRegisterValue(int set, int reg, const DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_rip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_rsp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_rbp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_rflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ ((uint64_t*)(&m_state.context.gpr))[reg] = value->value.uint64;
+ success = true;
+ }
+ break;
+
+ case e_regSetFPU:
+ switch (reg)
+ {
+ case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.__fpu_fcw)) = value->value.uint16; success = true; break;
+ case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.__fpu_fsw)) = value->value.uint16; success = true; break;
+ case fpu_ftw: m_state.context.fpu.__fpu_ftw = value->value.uint8; success = true; break;
+ case fpu_fop: m_state.context.fpu.__fpu_fop = value->value.uint16; success = true; break;
+ case fpu_ip: m_state.context.fpu.__fpu_ip = value->value.uint32; success = true; break;
+ case fpu_cs: m_state.context.fpu.__fpu_cs = value->value.uint16; success = true; break;
+ case fpu_dp: m_state.context.fpu.__fpu_dp = value->value.uint32; success = true; break;
+ case fpu_ds: m_state.context.fpu.__fpu_ds = value->value.uint16; success = true; break;
+ case fpu_mxcsr: m_state.context.fpu.__fpu_mxcsr = value->value.uint32; success = true; break;
+ case fpu_mxcsrmask: m_state.context.fpu.__fpu_mxcsrmask = value->value.uint32; success = true; 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 (&m_state.context.fpu.__fpu_stmm0 + (reg - fpu_stmm0), &value->value.uint8, 10);
+ success = true;
+ 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 (&m_state.context.fpu.__fpu_xmm0 + (reg - fpu_xmm0), &value->value.uint8, 16);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_regSetEXC:
+ switch (reg)
+ {
+ case exc_trapno: m_state.context.exc.__trapno = value->value.uint32; success = true; break;
+ case exc_err: m_state.context.exc.__err = value->value.uint32; success = true; break;
+ case exc_faultvaddr:m_state.context.exc.__faultvaddr = value->value.uint64; success = true; break;
+ }
+ break;
+ }
+ }
+
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+
+nub_size_t
+DNBArchImplX86_64::GetRegisterContext (void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context);
+
+ if (buf && buf_len)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ bool force = false;
+ if (GetGPRState(force) | GetFPUState(force) | GetEXCState(force))
+ return 0;
+ ::memcpy (buf, &m_state.context, size);
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t
+DNBArchImplX86_64::SetRegisterContext (const void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context);
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ ::memcpy (&m_state.context, buf, size);
+ SetGPRState();
+ SetFPUState();
+ SetEXCState();
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size);
+ return size;
+}
+
+
+kern_return_t
+DNBArchImplX86_64::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL: return GetGPRState(force) | GetFPUState(force) | GetEXCState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetFPU: return GetFPUState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchImplX86_64::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ if (RegisterSetStateIsValid(set))
+ {
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() | SetFPUState() | SetEXCState();
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetFPU: return SetFPUState();
+ case e_regSetEXC: return SetEXCState();
+ default: break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchImplX86_64::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+
+
+#endif // #if defined (__i386__)
diff --git a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h
new file mode 100644
index 00000000000..f445d473892
--- /dev/null
+++ b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h
@@ -0,0 +1,199 @@
+//===-- DNBArchImplX86_64.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBArchImplX86_64_h__
+#define __DNBArchImplX86_64_h__
+
+//#if defined (__i386__)
+#if defined(__x86_64__)
+#include "DNBArch.h"
+#include <mach/mach_types.h>
+#include <mach/thread_status.h>
+
+
+class MachThread;
+
+class DNBArchImplX86_64 : public DNBArchProtocol
+{
+public:
+ DNBArchImplX86_64(MachThread *thread) :
+ m_thread(thread),
+ m_state()
+ {
+ }
+ virtual ~DNBArchImplX86_64()
+ {
+ }
+
+ static const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ virtual bool GetRegisterValue(int set, int reg, DNBRegisterValue *reg);
+ virtual bool SetRegisterValue(int set, int reg, const DNBRegisterValue *reg);
+ virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len);
+
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data& exc);
+
+ static const uint8_t * const SoftwareBreakpointOpcode (nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+protected:
+ kern_return_t EnableHardwareSingleStep (bool enable);
+
+ typedef x86_thread_state64_t GPR;
+ typedef x86_float_state64_t FPU;
+ typedef x86_exception_state64_t EXC;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_fpu_registers[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets[];
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_fpu_registers;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers;
+ static const size_t k_num_register_sets;
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPU,
+ e_regSetEXC,
+ kNumRegisterSets
+ } RegisterSet;
+
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ struct Context
+ {
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ };
+
+ struct State
+ {
+ Context context;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+ }
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (e_regSetALL, Read, -1);
+ }
+
+ kern_return_t
+ 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 e_regSetALL: return gpr_errs[err_idx] |
+ fpu_errs[err_idx] |
+ exc_errs[err_idx];
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetFPU: return fpu_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+
+ bool
+ SetError (int flavor, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] =
+ fpu_errs[err_idx] =
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPU:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+
+ bool
+ RegsAreValid (int flavor) const
+ {
+ return GetError(flavor, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetFPUState (bool force);
+ kern_return_t GetEXCState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetFPUState ();
+ kern_return_t SetEXCState ();
+
+ MachThread *m_thread;
+ State m_state;
+};
+
+typedef DNBArchImplX86_64 DNBArch;
+
+#endif // #if defined (__x86_64__)
+#endif // #ifndef __DNBArchImplX86_64_h__
OpenPOWER on IntegriCloud