summaryrefslogtreecommitdiff
path: root/source/Plugins/Process/Linux
diff options
context:
space:
mode:
Diffstat (limited to 'source/Plugins/Process/Linux')
-rw-r--r--source/Plugins/Process/Linux/CMakeLists.txt14
-rw-r--r--source/Plugins/Process/Linux/Makefile17
-rw-r--r--source/Plugins/Process/Linux/NativeProcessLinux.cpp3189
-rw-r--r--source/Plugins/Process/Linux/NativeProcessLinux.h329
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp230
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux.h107
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp1017
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h185
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp1042
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h196
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp1431
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h167
-rwxr-xr-xsource/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp1239
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h171
-rw-r--r--source/Plugins/Process/Linux/NativeThreadLinux.cpp440
-rw-r--r--source/Plugins/Process/Linux/NativeThreadLinux.h120
-rw-r--r--source/Plugins/Process/Linux/ProcFileReader.cpp108
-rw-r--r--source/Plugins/Process/Linux/ProcFileReader.h37
-rw-r--r--source/Plugins/Process/Linux/Procfs.h31
19 files changed, 10070 insertions, 0 deletions
diff --git a/source/Plugins/Process/Linux/CMakeLists.txt b/source/Plugins/Process/Linux/CMakeLists.txt
new file mode 100644
index 0000000000000..80de8413d2093
--- /dev/null
+++ b/source/Plugins/Process/Linux/CMakeLists.txt
@@ -0,0 +1,14 @@
+include_directories(.)
+include_directories(../POSIX)
+include_directories(../Utility)
+
+add_lldb_library(lldbPluginProcessLinux
+ NativeProcessLinux.cpp
+ NativeRegisterContextLinux.cpp
+ NativeRegisterContextLinux_arm.cpp
+ NativeRegisterContextLinux_arm64.cpp
+ NativeRegisterContextLinux_x86_64.cpp
+ NativeRegisterContextLinux_mips64.cpp
+ NativeThreadLinux.cpp
+ ProcFileReader.cpp
+ )
diff --git a/source/Plugins/Process/Linux/Makefile b/source/Plugins/Process/Linux/Makefile
new file mode 100644
index 0000000000000..239e94d608e9b
--- /dev/null
+++ b/source/Plugins/Process/Linux/Makefile
@@ -0,0 +1,17 @@
+##===- source/Plugins/Process/Linux/Makefile ---------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LLDB_LEVEL := ../../../..
+LIBRARYNAME := lldbPluginProcessLinux
+BUILD_ARCHIVE = 1
+
+# Extend the include path so we may locate UnwindLLDB.h
+CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility
+
+include $(LLDB_LEVEL)/Makefile
diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/source/Plugins/Process/Linux/NativeProcessLinux.cpp
new file mode 100644
index 0000000000000..87c76f57830c5
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -0,0 +1,3189 @@
+//===-- NativeProcessLinux.cpp -------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeProcessLinux.h"
+
+// C Includes
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+
+// C++ Includes
+#include <fstream>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+
+// Other libraries and framework includes
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/State.h"
+#include "lldb/Host/common/NativeBreakpoint.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/PseudoTerminal.h"
+#include "lldb/Utility/StringExtractor.h"
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "NativeThreadLinux.h"
+#include "ProcFileReader.h"
+#include "Procfs.h"
+
+// System includes - They have to be included after framework includes because they define some
+// macros which collide with variable names in other modules
+#include <linux/unistd.h>
+#include <sys/socket.h>
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include "lldb/Host/linux/Personality.h"
+#include "lldb/Host/linux/Ptrace.h"
+#include "lldb/Host/linux/Signalfd.h"
+#include "lldb/Host/linux/Uio.h"
+#include "lldb/Host/android/Android.h"
+
+#define LLDB_PERSONALITY_GET_CURRENT_SETTINGS 0xffffffff
+
+// Support hardware breakpoints in case it has not been defined
+#ifndef TRAP_HWBKPT
+ #define TRAP_HWBKPT 4
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+using namespace llvm;
+
+// Private bits we only need internally.
+
+static bool ProcessVmReadvSupported()
+{
+ static bool is_supported;
+ static std::once_flag flag;
+
+ std::call_once(flag, [] {
+ Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ uint32_t source = 0x47424742;
+ uint32_t dest = 0;
+
+ struct iovec local, remote;
+ remote.iov_base = &source;
+ local.iov_base = &dest;
+ remote.iov_len = local.iov_len = sizeof source;
+
+ // We shall try if cross-process-memory reads work by attempting to read a value from our own process.
+ ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0);
+ is_supported = (res == sizeof(source) && source == dest);
+ if (log)
+ {
+ if (is_supported)
+ log->Printf("%s: Detected kernel support for process_vm_readv syscall. Fast memory reads enabled.",
+ __FUNCTION__);
+ else
+ log->Printf("%s: syscall process_vm_readv failed (error: %s). Fast memory reads disabled.",
+ __FUNCTION__, strerror(errno));
+ }
+ });
+
+ return is_supported;
+}
+
+namespace
+{
+ Error
+ ResolveProcessArchitecture (lldb::pid_t pid, Platform &platform, ArchSpec &arch)
+ {
+ // Grab process info for the running process.
+ ProcessInstanceInfo process_info;
+ if (!platform.GetProcessInfo (pid, process_info))
+ return Error("failed to get process info");
+
+ // Resolve the executable module.
+ ModuleSP exe_module_sp;
+ ModuleSpec exe_module_spec(process_info.GetExecutableFile(), process_info.GetArchitecture());
+ FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths ());
+ Error error = platform.ResolveExecutable(
+ exe_module_spec,
+ exe_module_sp,
+ executable_search_paths.GetSize () ? &executable_search_paths : NULL);
+
+ if (!error.Success ())
+ return error;
+
+ // Check if we've got our architecture from the exe_module.
+ arch = exe_module_sp->GetArchitecture ();
+ if (arch.IsValid ())
+ return Error();
+ else
+ return Error("failed to retrieve a valid architecture from the exe module");
+ }
+
+ void
+ DisplayBytes (StreamString &s, void *bytes, uint32_t count)
+ {
+ uint8_t *ptr = (uint8_t *)bytes;
+ const uint32_t loop_count = std::min<uint32_t>(DEBUG_PTRACE_MAXBYTES, count);
+ for(uint32_t i=0; i<loop_count; i++)
+ {
+ s.Printf ("[%x]", *ptr);
+ ptr++;
+ }
+ }
+
+ void
+ PtraceDisplayBytes(int &req, void *data, size_t data_size)
+ {
+ StreamString buf;
+ Log *verbose_log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (
+ POSIX_LOG_PTRACE | POSIX_LOG_VERBOSE));
+
+ if (verbose_log)
+ {
+ switch(req)
+ {
+ case PTRACE_POKETEXT:
+ {
+ DisplayBytes(buf, &data, 8);
+ verbose_log->Printf("PTRACE_POKETEXT %s", buf.GetData());
+ break;
+ }
+ case PTRACE_POKEDATA:
+ {
+ DisplayBytes(buf, &data, 8);
+ verbose_log->Printf("PTRACE_POKEDATA %s", buf.GetData());
+ break;
+ }
+ case PTRACE_POKEUSER:
+ {
+ DisplayBytes(buf, &data, 8);
+ verbose_log->Printf("PTRACE_POKEUSER %s", buf.GetData());
+ break;
+ }
+ case PTRACE_SETREGS:
+ {
+ DisplayBytes(buf, data, data_size);
+ verbose_log->Printf("PTRACE_SETREGS %s", buf.GetData());
+ break;
+ }
+ case PTRACE_SETFPREGS:
+ {
+ DisplayBytes(buf, data, data_size);
+ verbose_log->Printf("PTRACE_SETFPREGS %s", buf.GetData());
+ break;
+ }
+ case PTRACE_SETSIGINFO:
+ {
+ DisplayBytes(buf, data, sizeof(siginfo_t));
+ verbose_log->Printf("PTRACE_SETSIGINFO %s", buf.GetData());
+ break;
+ }
+ case PTRACE_SETREGSET:
+ {
+ // Extract iov_base from data, which is a pointer to the struct IOVEC
+ DisplayBytes(buf, *(void **)data, data_size);
+ verbose_log->Printf("PTRACE_SETREGSET %s", buf.GetData());
+ break;
+ }
+ default:
+ {
+ }
+ }
+ }
+ }
+
+ static constexpr unsigned k_ptrace_word_size = sizeof(void*);
+ static_assert(sizeof(long) >= k_ptrace_word_size, "Size of long must be larger than ptrace word size");
+} // end of anonymous namespace
+
+// Simple helper function to ensure flags are enabled on the given file
+// descriptor.
+static Error
+EnsureFDFlags(int fd, int flags)
+{
+ Error error;
+
+ int status = fcntl(fd, F_GETFL);
+ if (status == -1)
+ {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ if (fcntl(fd, F_SETFL, status | flags) == -1)
+ {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ return error;
+}
+
+NativeProcessLinux::LaunchArgs::LaunchArgs(Module *module,
+ char const **argv,
+ char const **envp,
+ const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec,
+ const FileSpec &stderr_file_spec,
+ const FileSpec &working_dir,
+ const ProcessLaunchInfo &launch_info)
+ : m_module(module),
+ m_argv(argv),
+ m_envp(envp),
+ m_stdin_file_spec(stdin_file_spec),
+ m_stdout_file_spec(stdout_file_spec),
+ m_stderr_file_spec(stderr_file_spec),
+ m_working_dir(working_dir),
+ m_launch_info(launch_info)
+{
+}
+
+NativeProcessLinux::LaunchArgs::~LaunchArgs()
+{ }
+
+// -----------------------------------------------------------------------------
+// Public Static Methods
+// -----------------------------------------------------------------------------
+
+Error
+NativeProcessProtocol::Launch (
+ ProcessLaunchInfo &launch_info,
+ NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop,
+ NativeProcessProtocolSP &native_process_sp)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ lldb::ModuleSP exe_module_sp;
+ PlatformSP platform_sp (Platform::GetHostPlatform ());
+ Error error = platform_sp->ResolveExecutable(
+ ModuleSpec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()),
+ exe_module_sp,
+ nullptr);
+
+ if (! error.Success())
+ return error;
+
+ // Verify the working directory is valid if one was specified.
+ FileSpec working_dir{launch_info.GetWorkingDirectory()};
+ if (working_dir &&
+ (!working_dir.ResolvePath() ||
+ working_dir.GetFileType() != FileSpec::eFileTypeDirectory))
+ {
+ error.SetErrorStringWithFormat ("No such file or directory: %s",
+ working_dir.GetCString());
+ return error;
+ }
+
+ const FileAction *file_action;
+
+ // Default of empty will mean to use existing open file descriptors.
+ FileSpec stdin_file_spec{};
+ FileSpec stdout_file_spec{};
+ FileSpec stderr_file_spec{};
+
+ file_action = launch_info.GetFileActionForFD (STDIN_FILENO);
+ if (file_action)
+ stdin_file_spec = file_action->GetFileSpec();
+
+ file_action = launch_info.GetFileActionForFD (STDOUT_FILENO);
+ if (file_action)
+ stdout_file_spec = file_action->GetFileSpec();
+
+ file_action = launch_info.GetFileActionForFD (STDERR_FILENO);
+ if (file_action)
+ stderr_file_spec = file_action->GetFileSpec();
+
+ if (log)
+ {
+ if (stdin_file_spec)
+ log->Printf ("NativeProcessLinux::%s setting STDIN to '%s'",
+ __FUNCTION__, stdin_file_spec.GetCString());
+ else
+ log->Printf ("NativeProcessLinux::%s leaving STDIN as is", __FUNCTION__);
+
+ if (stdout_file_spec)
+ log->Printf ("NativeProcessLinux::%s setting STDOUT to '%s'",
+ __FUNCTION__, stdout_file_spec.GetCString());
+ else
+ log->Printf ("NativeProcessLinux::%s leaving STDOUT as is", __FUNCTION__);
+
+ if (stderr_file_spec)
+ log->Printf ("NativeProcessLinux::%s setting STDERR to '%s'",
+ __FUNCTION__, stderr_file_spec.GetCString());
+ else
+ log->Printf ("NativeProcessLinux::%s leaving STDERR as is", __FUNCTION__);
+ }
+
+ // Create the NativeProcessLinux in launch mode.
+ native_process_sp.reset (new NativeProcessLinux ());
+
+ if (log)
+ {
+ int i = 0;
+ for (const char **args = launch_info.GetArguments ().GetConstArgumentVector (); *args; ++args, ++i)
+ {
+ log->Printf ("NativeProcessLinux::%s arg %d: \"%s\"", __FUNCTION__, i, *args ? *args : "nullptr");
+ ++i;
+ }
+ }
+
+ if (!native_process_sp->RegisterNativeDelegate (native_delegate))
+ {
+ native_process_sp.reset ();
+ error.SetErrorStringWithFormat ("failed to register the native delegate");
+ return error;
+ }
+
+ std::static_pointer_cast<NativeProcessLinux> (native_process_sp)->LaunchInferior (
+ mainloop,
+ exe_module_sp.get(),
+ launch_info.GetArguments ().GetConstArgumentVector (),
+ launch_info.GetEnvironmentEntries ().GetConstArgumentVector (),
+ stdin_file_spec,
+ stdout_file_spec,
+ stderr_file_spec,
+ working_dir,
+ launch_info,
+ error);
+
+ if (error.Fail ())
+ {
+ native_process_sp.reset ();
+ if (log)
+ log->Printf ("NativeProcessLinux::%s failed to launch process: %s", __FUNCTION__, error.AsCString ());
+ return error;
+ }
+
+ launch_info.SetProcessID (native_process_sp->GetID ());
+
+ return error;
+}
+
+Error
+NativeProcessProtocol::Attach (
+ lldb::pid_t pid,
+ NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop,
+ NativeProcessProtocolSP &native_process_sp)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log && log->GetMask ().Test (POSIX_LOG_VERBOSE))
+ log->Printf ("NativeProcessLinux::%s(pid = %" PRIi64 ")", __FUNCTION__, pid);
+
+ // Grab the current platform architecture. This should be Linux,
+ // since this code is only intended to run on a Linux host.
+ PlatformSP platform_sp (Platform::GetHostPlatform ());
+ if (!platform_sp)
+ return Error("failed to get a valid default platform");
+
+ // Retrieve the architecture for the running process.
+ ArchSpec process_arch;
+ Error error = ResolveProcessArchitecture (pid, *platform_sp.get (), process_arch);
+ if (!error.Success ())
+ return error;
+
+ std::shared_ptr<NativeProcessLinux> native_process_linux_sp (new NativeProcessLinux ());
+
+ if (!native_process_linux_sp->RegisterNativeDelegate (native_delegate))
+ {
+ error.SetErrorStringWithFormat ("failed to register the native delegate");
+ return error;
+ }
+
+ native_process_linux_sp->AttachToInferior (mainloop, pid, error);
+ if (!error.Success ())
+ return error;
+
+ native_process_sp = native_process_linux_sp;
+ return error;
+}
+
+// -----------------------------------------------------------------------------
+// Public Instance Methods
+// -----------------------------------------------------------------------------
+
+NativeProcessLinux::NativeProcessLinux () :
+ NativeProcessProtocol (LLDB_INVALID_PROCESS_ID),
+ m_arch (),
+ m_supports_mem_region (eLazyBoolCalculate),
+ m_mem_region_cache (),
+ m_mem_region_cache_mutex(),
+ m_pending_notification_tid(LLDB_INVALID_THREAD_ID)
+{
+}
+
+void
+NativeProcessLinux::LaunchInferior (
+ MainLoop &mainloop,
+ Module *module,
+ const char *argv[],
+ const char *envp[],
+ const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec,
+ const FileSpec &stderr_file_spec,
+ const FileSpec &working_dir,
+ const ProcessLaunchInfo &launch_info,
+ Error &error)
+{
+ m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD,
+ [this] (MainLoopBase &) { SigchldHandler(); }, error);
+ if (! m_sigchld_handle)
+ return;
+
+ if (module)
+ m_arch = module->GetArchitecture ();
+
+ SetState (eStateLaunching);
+
+ std::unique_ptr<LaunchArgs> args(
+ new LaunchArgs(module, argv, envp,
+ stdin_file_spec,
+ stdout_file_spec,
+ stderr_file_spec,
+ working_dir,
+ launch_info));
+
+ Launch(args.get(), error);
+}
+
+void
+NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ")", __FUNCTION__, pid);
+
+ m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD,
+ [this] (MainLoopBase &) { SigchldHandler(); }, error);
+ if (! m_sigchld_handle)
+ return;
+
+ // We can use the Host for everything except the ResolveExecutable portion.
+ PlatformSP platform_sp = Platform::GetHostPlatform ();
+ if (!platform_sp)
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 "): no default platform set", __FUNCTION__, pid);
+ error.SetErrorString ("no default platform available");
+ return;
+ }
+
+ // Gather info about the process.
+ ProcessInstanceInfo process_info;
+ if (!platform_sp->GetProcessInfo (pid, process_info))
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 "): failed to get process info", __FUNCTION__, pid);
+ error.SetErrorString ("failed to get process info");
+ return;
+ }
+
+ // Resolve the executable module
+ ModuleSP exe_module_sp;
+ FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths());
+ ModuleSpec exe_module_spec(process_info.GetExecutableFile(), process_info.GetArchitecture());
+ error = platform_sp->ResolveExecutable(exe_module_spec, exe_module_sp,
+ executable_search_paths.GetSize() ? &executable_search_paths : NULL);
+ if (!error.Success())
+ return;
+
+ // Set the architecture to the exe architecture.
+ m_arch = exe_module_sp->GetArchitecture();
+ if (log)
+ log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ") detected architecture %s", __FUNCTION__, pid, m_arch.GetArchitectureName ());
+
+ m_pid = pid;
+ SetState(eStateAttaching);
+
+ Attach(pid, error);
+}
+
+::pid_t
+NativeProcessLinux::Launch(LaunchArgs *args, Error &error)
+{
+ assert (args && "null args");
+
+ const char **argv = args->m_argv;
+ const char **envp = args->m_envp;
+ const FileSpec working_dir = args->m_working_dir;
+
+ lldb_utility::PseudoTerminal terminal;
+ const size_t err_len = 1024;
+ char err_str[err_len];
+ lldb::pid_t pid;
+
+ // Propagate the environment if one is not supplied.
+ if (envp == NULL || envp[0] == NULL)
+ envp = const_cast<const char **>(environ);
+
+ if ((pid = terminal.Fork(err_str, err_len)) == static_cast<lldb::pid_t> (-1))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("Process fork failed: %s", err_str);
+ return -1;
+ }
+
+ // Recognized child exit status codes.
+ enum {
+ ePtraceFailed = 1,
+ eDupStdinFailed,
+ eDupStdoutFailed,
+ eDupStderrFailed,
+ eChdirFailed,
+ eExecFailed,
+ eSetGidFailed,
+ eSetSigMaskFailed
+ };
+
+ // Child process.
+ if (pid == 0)
+ {
+ // First, make sure we disable all logging. If we are logging to stdout, our logs can be
+ // mistaken for inferior output.
+ Log::DisableAllLogChannels(nullptr);
+ // FIXME consider opening a pipe between parent/child and have this forked child
+ // send log info to parent re: launch status.
+
+ // Start tracing this child that is about to exec.
+ error = PtraceWrapper(PTRACE_TRACEME, 0);
+ if (error.Fail())
+ exit(ePtraceFailed);
+
+ // terminal has already dupped the tty descriptors to stdin/out/err.
+ // This closes original fd from which they were copied (and avoids
+ // leaking descriptors to the debugged process.
+ terminal.CloseSlaveFileDescriptor();
+
+ // Do not inherit setgid powers.
+ if (setgid(getgid()) != 0)
+ exit(eSetGidFailed);
+
+ // Attempt to have our own process group.
+ if (setpgid(0, 0) != 0)
+ {
+ // FIXME log that this failed. This is common.
+ // Don't allow this to prevent an inferior exec.
+ }
+
+ // Dup file descriptors if needed.
+ if (args->m_stdin_file_spec)
+ if (!DupDescriptor(args->m_stdin_file_spec, STDIN_FILENO, O_RDONLY))
+ exit(eDupStdinFailed);
+
+ if (args->m_stdout_file_spec)
+ if (!DupDescriptor(args->m_stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
+ exit(eDupStdoutFailed);
+
+ if (args->m_stderr_file_spec)
+ if (!DupDescriptor(args->m_stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
+ exit(eDupStderrFailed);
+
+ // Close everything besides stdin, stdout, and stderr that has no file
+ // action to avoid leaking
+ for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd)
+ if (!args->m_launch_info.GetFileActionForFD(fd))
+ close(fd);
+
+ // Change working directory
+ if (working_dir && 0 != ::chdir(working_dir.GetCString()))
+ exit(eChdirFailed);
+
+ // Disable ASLR if requested.
+ if (args->m_launch_info.GetFlags ().Test (lldb::eLaunchFlagDisableASLR))
+ {
+ const int old_personality = personality (LLDB_PERSONALITY_GET_CURRENT_SETTINGS);
+ if (old_personality == -1)
+ {
+ // Can't retrieve Linux personality. Cannot disable ASLR.
+ }
+ else
+ {
+ const int new_personality = personality (ADDR_NO_RANDOMIZE | old_personality);
+ if (new_personality == -1)
+ {
+ // Disabling ASLR failed.
+ }
+ else
+ {
+ // Disabling ASLR succeeded.
+ }
+ }
+ }
+
+ // Clear the signal mask to prevent the child from being affected by
+ // any masking done by the parent.
+ sigset_t set;
+ if (sigemptyset(&set) != 0 || pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)
+ exit(eSetSigMaskFailed);
+
+ // Execute. We should never return...
+ execve(argv[0],
+ const_cast<char *const *>(argv),
+ const_cast<char *const *>(envp));
+
+ // ...unless exec fails. In which case we definitely need to end the child here.
+ exit(eExecFailed);
+ }
+
+ //
+ // This is the parent code here.
+ //
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ // Wait for the child process to trap on its call to execve.
+ ::pid_t wpid;
+ int status;
+ if ((wpid = waitpid(pid, &status, 0)) < 0)
+ {
+ error.SetErrorToErrno();
+ if (log)
+ log->Printf ("NativeProcessLinux::%s waitpid for inferior failed with %s",
+ __FUNCTION__, error.AsCString ());
+
+ // Mark the inferior as invalid.
+ // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
+ SetState (StateType::eStateInvalid);
+
+ return -1;
+ }
+ else if (WIFEXITED(status))
+ {
+ // open, dup or execve likely failed for some reason.
+ error.SetErrorToGenericError();
+ switch (WEXITSTATUS(status))
+ {
+ case ePtraceFailed:
+ error.SetErrorString("Child ptrace failed.");
+ break;
+ case eDupStdinFailed:
+ error.SetErrorString("Child open stdin failed.");
+ break;
+ case eDupStdoutFailed:
+ error.SetErrorString("Child open stdout failed.");
+ break;
+ case eDupStderrFailed:
+ error.SetErrorString("Child open stderr failed.");
+ break;
+ case eChdirFailed:
+ error.SetErrorString("Child failed to set working directory.");
+ break;
+ case eExecFailed:
+ error.SetErrorString("Child exec failed.");
+ break;
+ case eSetGidFailed:
+ error.SetErrorString("Child setgid failed.");
+ break;
+ case eSetSigMaskFailed:
+ error.SetErrorString("Child failed to set signal mask.");
+ break;
+ default:
+ error.SetErrorString("Child returned unknown exit status.");
+ break;
+ }
+
+ if (log)
+ {
+ log->Printf ("NativeProcessLinux::%s inferior exited with status %d before issuing a STOP",
+ __FUNCTION__,
+ WEXITSTATUS(status));
+ }
+
+ // Mark the inferior as invalid.
+ // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
+ SetState (StateType::eStateInvalid);
+
+ return -1;
+ }
+ assert(WIFSTOPPED(status) && (wpid == static_cast< ::pid_t> (pid)) &&
+ "Could not sync with inferior process.");
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s inferior started, now in stopped state", __FUNCTION__);
+
+ error = SetDefaultPtraceOpts(pid);
+ if (error.Fail())
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s inferior failed to set default ptrace options: %s",
+ __FUNCTION__, error.AsCString ());
+
+ // Mark the inferior as invalid.
+ // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
+ SetState (StateType::eStateInvalid);
+
+ return -1;
+ }
+
+ // Release the master terminal descriptor and pass it off to the
+ // NativeProcessLinux instance. Similarly stash the inferior pid.
+ m_terminal_fd = terminal.ReleaseMasterFileDescriptor();
+ m_pid = pid;
+
+ // Set the terminal fd to be in non blocking mode (it simplifies the
+ // implementation of ProcessLinux::GetSTDOUT to have a non-blocking
+ // descriptor to read from).
+ error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
+ if (error.Fail())
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s inferior EnsureFDFlags failed for ensuring terminal O_NONBLOCK setting: %s",
+ __FUNCTION__, error.AsCString ());
+
+ // Mark the inferior as invalid.
+ // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
+ SetState (StateType::eStateInvalid);
+
+ return -1;
+ }
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, pid);
+
+ NativeThreadLinuxSP thread_sp = AddThread(pid);
+ assert (thread_sp && "AddThread() returned a nullptr thread");
+ thread_sp->SetStoppedBySignal(SIGSTOP);
+ ThreadWasCreated(*thread_sp);
+
+ // Let our process instance know the thread has stopped.
+ SetCurrentThreadID (thread_sp->GetID ());
+ SetState (StateType::eStateStopped);
+
+ if (log)
+ {
+ if (error.Success ())
+ {
+ log->Printf ("NativeProcessLinux::%s inferior launching succeeded", __FUNCTION__);
+ }
+ else
+ {
+ log->Printf ("NativeProcessLinux::%s inferior launching failed: %s",
+ __FUNCTION__, error.AsCString ());
+ return -1;
+ }
+ }
+ return pid;
+}
+
+::pid_t
+NativeProcessLinux::Attach(lldb::pid_t pid, Error &error)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ // Use a map to keep track of the threads which we have attached/need to attach.
+ Host::TidMap tids_to_attach;
+ if (pid <= 1)
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Attaching to process 1 is not allowed.");
+ return -1;
+ }
+
+ while (Host::FindProcessThreads(pid, tids_to_attach))
+ {
+ for (Host::TidMap::iterator it = tids_to_attach.begin();
+ it != tids_to_attach.end();)
+ {
+ if (it->second == false)
+ {
+ lldb::tid_t tid = it->first;
+
+ // Attach to the requested process.
+ // An attach will cause the thread to stop with a SIGSTOP.
+ error = PtraceWrapper(PTRACE_ATTACH, tid);
+ if (error.Fail())
+ {
+ // No such thread. The thread may have exited.
+ // More error handling may be needed.
+ if (error.GetError() == ESRCH)
+ {
+ it = tids_to_attach.erase(it);
+ continue;
+ }
+ else
+ return -1;
+ }
+
+ int status;
+ // Need to use __WALL otherwise we receive an error with errno=ECHLD
+ // At this point we should have a thread stopped if waitpid succeeds.
+ if ((status = waitpid(tid, NULL, __WALL)) < 0)
+ {
+ // No such thread. The thread may have exited.
+ // More error handling may be needed.
+ if (errno == ESRCH)
+ {
+ it = tids_to_attach.erase(it);
+ continue;
+ }
+ else
+ {
+ error.SetErrorToErrno();
+ return -1;
+ }
+ }
+
+ error = SetDefaultPtraceOpts(tid);
+ if (error.Fail())
+ return -1;
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() adding tid = %" PRIu64, __FUNCTION__, tid);
+
+ it->second = true;
+
+ // Create the thread, mark it as stopped.
+ NativeThreadLinuxSP thread_sp (AddThread(static_cast<lldb::tid_t>(tid)));
+ assert (thread_sp && "AddThread() returned a nullptr");
+
+ // This will notify this is a new thread and tell the system it is stopped.
+ thread_sp->SetStoppedBySignal(SIGSTOP);
+ ThreadWasCreated(*thread_sp);
+ SetCurrentThreadID (thread_sp->GetID ());
+ }
+
+ // move the loop forward
+ ++it;
+ }
+ }
+
+ if (tids_to_attach.size() > 0)
+ {
+ m_pid = pid;
+ // Let our process instance know the thread has stopped.
+ SetState (StateType::eStateStopped);
+ }
+ else
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("No such process.");
+ return -1;
+ }
+
+ return pid;
+}
+
+Error
+NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid)
+{
+ long ptrace_opts = 0;
+
+ // Have the child raise an event on exit. This is used to keep the child in
+ // limbo until it is destroyed.
+ ptrace_opts |= PTRACE_O_TRACEEXIT;
+
+ // Have the tracer trace threads which spawn in the inferior process.
+ // TODO: if we want to support tracing the inferiors' child, add the
+ // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK)
+ ptrace_opts |= PTRACE_O_TRACECLONE;
+
+ // Have the tracer notify us before execve returns
+ // (needed to disable legacy SIGTRAP generation)
+ ptrace_opts |= PTRACE_O_TRACEEXEC;
+
+ return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void*)ptrace_opts);
+}
+
+static ExitType convert_pid_status_to_exit_type (int status)
+{
+ if (WIFEXITED (status))
+ return ExitType::eExitTypeExit;
+ else if (WIFSIGNALED (status))
+ return ExitType::eExitTypeSignal;
+ else if (WIFSTOPPED (status))
+ return ExitType::eExitTypeStop;
+ else
+ {
+ // We don't know what this is.
+ return ExitType::eExitTypeInvalid;
+ }
+}
+
+static int convert_pid_status_to_return_code (int status)
+{
+ if (WIFEXITED (status))
+ return WEXITSTATUS (status);
+ else if (WIFSIGNALED (status))
+ return WTERMSIG (status);
+ else if (WIFSTOPPED (status))
+ return WSTOPSIG (status);
+ else
+ {
+ // We don't know what this is.
+ return ExitType::eExitTypeInvalid;
+ }
+}
+
+// Handles all waitpid events from the inferior process.
+void
+NativeProcessLinux::MonitorCallback(lldb::pid_t pid,
+ bool exited,
+ int signal,
+ int status)
+{
+ Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ // Certain activities differ based on whether the pid is the tid of the main thread.
+ const bool is_main_thread = (pid == GetID ());
+
+ // Handle when the thread exits.
+ if (exited)
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() got exit signal(%d) , tid = %" PRIu64 " (%s main thread)", __FUNCTION__, signal, pid, is_main_thread ? "is" : "is not");
+
+ // This is a thread that exited. Ensure we're not tracking it anymore.
+ const bool thread_found = StopTrackingThread (pid);
+
+ if (is_main_thread)
+ {
+ // We only set the exit status and notify the delegate if we haven't already set the process
+ // state to an exited state. We normally should have received a SIGTRAP | (PTRACE_EVENT_EXIT << 8)
+ // for the main thread.
+ const bool already_notified = (GetState() == StateType::eStateExited) || (GetState () == StateType::eStateCrashed);
+ if (!already_notified)
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " handling main thread exit (%s), expected exit state already set but state was %s instead, setting exit state now", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found", StateAsCString (GetState ()));
+ // The main thread exited. We're done monitoring. Report to delegate.
+ SetExitStatus (convert_pid_status_to_exit_type (status), convert_pid_status_to_return_code (status), nullptr, true);
+
+ // Notify delegate that our process has exited.
+ SetState (StateType::eStateExited, true);
+ }
+ else
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " main thread now exited (%s)", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found");
+ }
+ }
+ else
+ {
+ // Do we want to report to the delegate in this case? I think not. If this was an orderly
+ // thread exit, we would already have received the SIGTRAP | (PTRACE_EVENT_EXIT << 8) signal,
+ // and we would have done an all-stop then.
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " handling non-main thread exit (%s)", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found");
+ }
+ return;
+ }
+
+ siginfo_t info;
+ const auto info_err = GetSignalInfo(pid, &info);
+ auto thread_sp = GetThreadByID(pid);
+
+ if (! thread_sp)
+ {
+ // Normally, the only situation when we cannot find the thread is if we have just
+ // received a new thread notification. This is indicated by GetSignalInfo() returning
+ // si_code == SI_USER and si_pid == 0
+ if (log)
+ log->Printf("NativeProcessLinux::%s received notification about an unknown tid %" PRIu64 ".", __FUNCTION__, pid);
+
+ if (info_err.Fail())
+ {
+ if (log)
+ log->Printf("NativeProcessLinux::%s (tid %" PRIu64 ") GetSignalInfo failed (%s). Ingoring this notification.", __FUNCTION__, pid, info_err.AsCString());
+ return;
+ }
+
+ if (log && (info.si_code != SI_USER || info.si_pid != 0))
+ log->Printf("NativeProcessLinux::%s (tid %" PRIu64 ") unexpected signal info (si_code: %d, si_pid: %d). Treating as a new thread notification anyway.", __FUNCTION__, pid, info.si_code, info.si_pid);
+
+ auto thread_sp = AddThread(pid);
+ // Resume the newly created thread.
+ ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
+ ThreadWasCreated(*thread_sp);
+ return;
+ }
+
+ // Get details on the signal raised.
+ if (info_err.Success())
+ {
+ // We have retrieved the signal info. Dispatch appropriately.
+ if (info.si_signo == SIGTRAP)
+ MonitorSIGTRAP(info, *thread_sp);
+ else
+ MonitorSignal(info, *thread_sp, exited);
+ }
+ else
+ {
+ if (info_err.GetError() == EINVAL)
+ {
+ // This is a group stop reception for this tid.
+ // We can reach here if we reinject SIGSTOP, SIGSTP, SIGTTIN or SIGTTOU into the
+ // tracee, triggering the group-stop mechanism. Normally receiving these would stop
+ // the process, pending a SIGCONT. Simulating this state in a debugger is hard and is
+ // generally not needed (one use case is debugging background task being managed by a
+ // shell). For general use, it is sufficient to stop the process in a signal-delivery
+ // stop which happens before the group stop. This done by MonitorSignal and works
+ // correctly for all signals.
+ if (log)
+ log->Printf("NativeProcessLinux::%s received a group stop for pid %" PRIu64 " tid %" PRIu64 ". Transparent handling of group stops not supported, resuming the thread.", __FUNCTION__, GetID (), pid);
+ ResumeThread(*thread_sp, thread_sp->GetState(), LLDB_INVALID_SIGNAL_NUMBER);
+ }
+ else
+ {
+ // ptrace(GETSIGINFO) failed (but not due to group-stop).
+
+ // A return value of ESRCH means the thread/process is no longer on the system,
+ // so it was killed somehow outside of our control. Either way, we can't do anything
+ // with it anymore.
+
+ // Stop tracking the metadata for the thread since it's entirely off the system now.
+ const bool thread_found = StopTrackingThread (pid);
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s GetSignalInfo failed: %s, tid = %" PRIu64 ", signal = %d, status = %d (%s, %s, %s)",
+ __FUNCTION__, info_err.AsCString(), pid, signal, status, info_err.GetError() == ESRCH ? "thread/process killed" : "unknown reason", is_main_thread ? "is main thread" : "is not main thread", thread_found ? "thread metadata removed" : "thread metadata not found");
+
+ if (is_main_thread)
+ {
+ // Notify the delegate - our process is not available but appears to have been killed outside
+ // our control. Is eStateExited the right exit state in this case?
+ SetExitStatus (convert_pid_status_to_exit_type (status), convert_pid_status_to_return_code (status), nullptr, true);
+ SetState (StateType::eStateExited, true);
+ }
+ else
+ {
+ // This thread was pulled out from underneath us. Anything to do here? Do we want to do an all stop?
+ if (log)
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 " non-main thread exit occurred, didn't tell delegate anything since thread disappeared out from underneath us", __FUNCTION__, GetID (), pid);
+ }
+ }
+ }
+}
+
+void
+NativeProcessLinux::WaitForNewThread(::pid_t tid)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ NativeThreadLinuxSP new_thread_sp = GetThreadByID(tid);
+
+ if (new_thread_sp)
+ {
+ // We are already tracking the thread - we got the event on the new thread (see
+ // MonitorSignal) before this one. We are done.
+ return;
+ }
+
+ // The thread is not tracked yet, let's wait for it to appear.
+ int status = -1;
+ ::pid_t wait_pid;
+ do
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() received thread creation event for tid %" PRIu32 ". tid not tracked yet, waiting for thread to appear...", __FUNCTION__, tid);
+ wait_pid = waitpid(tid, &status, __WALL);
+ }
+ while (wait_pid == -1 && errno == EINTR);
+ // Since we are waiting on a specific tid, this must be the creation event. But let's do
+ // some checks just in case.
+ if (wait_pid != tid) {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() waiting for tid %" PRIu32 " failed. Assuming the thread has disappeared in the meantime", __FUNCTION__, tid);
+ // The only way I know of this could happen is if the whole process was
+ // SIGKILLed in the mean time. In any case, we can't do anything about that now.
+ return;
+ }
+ if (WIFEXITED(status))
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() waiting for tid %" PRIu32 " returned an 'exited' event. Not tracking the thread.", __FUNCTION__, tid);
+ // Also a very improbable event.
+ return;
+ }
+
+ siginfo_t info;
+ Error error = GetSignalInfo(tid, &info);
+ if (error.Fail())
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32 " failed. Assuming the thread has disappeared in the meantime.", __FUNCTION__, tid);
+ return;
+ }
+
+ if (((info.si_pid != 0) || (info.si_code != SI_USER)) && log)
+ {
+ // We should be getting a thread creation signal here, but we received something
+ // else. There isn't much we can do about it now, so we will just log that. Since the
+ // thread is alive and we are receiving events from it, we shall pretend that it was
+ // created properly.
+ log->Printf ("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32 " received unexpected signal with code %d from pid %d.", __FUNCTION__, tid, info.si_code, info.si_pid);
+ }
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 ": tracking new thread tid %" PRIu32,
+ __FUNCTION__, GetID (), tid);
+
+ new_thread_sp = AddThread(tid);
+ ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
+ ThreadWasCreated(*new_thread_sp);
+}
+
+void
+NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ const bool is_main_thread = (thread.GetID() == GetID ());
+
+ assert(info.si_signo == SIGTRAP && "Unexpected child signal!");
+
+ Mutex::Locker locker (m_threads_mutex);
+
+ switch (info.si_code)
+ {
+ // TODO: these two cases are required if we want to support tracing of the inferiors' children. We'd need this to debug a monitor.
+ // case (SIGTRAP | (PTRACE_EVENT_FORK << 8)):
+ // case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)):
+
+ case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)):
+ {
+ // This is the notification on the parent thread which informs us of new thread
+ // creation.
+ // We don't want to do anything with the parent thread so we just resume it. In case we
+ // want to implement "break on thread creation" functionality, we would need to stop
+ // here.
+
+ unsigned long event_message = 0;
+ if (GetEventMessage(thread.GetID(), &event_message).Fail())
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " received thread creation event but GetEventMessage failed so we don't know the new tid", __FUNCTION__, thread.GetID());
+ } else
+ WaitForNewThread(event_message);
+
+ ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
+ break;
+ }
+
+ case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)):
+ {
+ NativeThreadLinuxSP main_thread_sp;
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() received exec event, code = %d", __FUNCTION__, info.si_code ^ SIGTRAP);
+
+ // Exec clears any pending notifications.
+ m_pending_notification_tid = LLDB_INVALID_THREAD_ID;
+
+ // Remove all but the main thread here. Linux fork creates a new process which only copies the main thread. Mutexes are in undefined state.
+ if (log)
+ log->Printf ("NativeProcessLinux::%s exec received, stop tracking all but main thread", __FUNCTION__);
+
+ for (auto thread_sp : m_threads)
+ {
+ const bool is_main_thread = thread_sp && thread_sp->GetID () == GetID ();
+ if (is_main_thread)
+ {
+ main_thread_sp = std::static_pointer_cast<NativeThreadLinux>(thread_sp);
+ if (log)
+ log->Printf ("NativeProcessLinux::%s found main thread with tid %" PRIu64 ", keeping", __FUNCTION__, main_thread_sp->GetID ());
+ }
+ else
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s discarding non-main-thread tid %" PRIu64 " due to exec", __FUNCTION__, thread_sp->GetID ());
+ }
+ }
+
+ m_threads.clear ();
+
+ if (main_thread_sp)
+ {
+ m_threads.push_back (main_thread_sp);
+ SetCurrentThreadID (main_thread_sp->GetID ());
+ main_thread_sp->SetStoppedByExec();
+ }
+ else
+ {
+ SetCurrentThreadID (LLDB_INVALID_THREAD_ID);
+ if (log)
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 "no main thread found, discarded all threads, we're in a no-thread state!", __FUNCTION__, GetID ());
+ }
+
+ // Tell coordinator about about the "new" (since exec) stopped main thread.
+ ThreadWasCreated(*main_thread_sp);
+
+ // Let our delegate know we have just exec'd.
+ NotifyDidExec ();
+
+ // If we have a main thread, indicate we are stopped.
+ assert (main_thread_sp && "exec called during ptraced process but no main thread metadata tracked");
+
+ // Let the process know we're stopped.
+ StopRunningThreads(main_thread_sp->GetID());
+
+ break;
+ }
+
+ case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)):
+ {
+ // The inferior process or one of its threads is about to exit.
+ // We don't want to do anything with the thread so we just resume it. In case we
+ // want to implement "break on thread exit" functionality, we would need to stop
+ // here.
+
+ unsigned long data = 0;
+ if (GetEventMessage(thread.GetID(), &data).Fail())
+ data = -1;
+
+ if (log)
+ {
+ log->Printf ("NativeProcessLinux::%s() received PTRACE_EVENT_EXIT, data = %lx (WIFEXITED=%s,WIFSIGNALED=%s), pid = %" PRIu64 " (%s)",
+ __FUNCTION__,
+ data, WIFEXITED (data) ? "true" : "false", WIFSIGNALED (data) ? "true" : "false",
+ thread.GetID(),
+ is_main_thread ? "is main thread" : "not main thread");
+ }
+
+ if (is_main_thread)
+ {
+ SetExitStatus (convert_pid_status_to_exit_type (data), convert_pid_status_to_return_code (data), nullptr, true);
+ }
+
+ StateType state = thread.GetState();
+ if (! StateIsRunningState(state))
+ {
+ // Due to a kernel bug, we may sometimes get this stop after the inferior gets a
+ // SIGKILL. This confuses our state tracking logic in ResumeThread(), since normally,
+ // we should not be receiving any ptrace events while the inferior is stopped. This
+ // makes sure that the inferior is resumed and exits normally.
+ state = eStateRunning;
+ }
+ ResumeThread(thread, state, LLDB_INVALID_SIGNAL_NUMBER);
+
+ break;
+ }
+
+ case 0:
+ case TRAP_TRACE: // We receive this on single stepping.
+ case TRAP_HWBKPT: // We receive this on watchpoint hit
+ {
+ // If a watchpoint was hit, report it
+ uint32_t wp_index;
+ Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(wp_index, (uintptr_t)info.si_addr);
+ if (error.Fail() && log)
+ log->Printf("NativeProcessLinux::%s() "
+ "received error while checking for watchpoint hits, "
+ "pid = %" PRIu64 " error = %s",
+ __FUNCTION__, thread.GetID(), error.AsCString());
+ if (wp_index != LLDB_INVALID_INDEX32)
+ {
+ MonitorWatchpoint(thread, wp_index);
+ break;
+ }
+
+ // Otherwise, report step over
+ MonitorTrace(thread);
+ break;
+ }
+
+ case SI_KERNEL:
+#if defined __mips__
+ // For mips there is no special signal for watchpoint
+ // So we check for watchpoint in kernel trap
+ {
+ // If a watchpoint was hit, report it
+ uint32_t wp_index;
+ Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(wp_index, LLDB_INVALID_ADDRESS);
+ if (error.Fail() && log)
+ log->Printf("NativeProcessLinux::%s() "
+ "received error while checking for watchpoint hits, "
+ "pid = %" PRIu64 " error = %s",
+ __FUNCTION__, thread.GetID(), error.AsCString());
+ if (wp_index != LLDB_INVALID_INDEX32)
+ {
+ MonitorWatchpoint(thread, wp_index);
+ break;
+ }
+ }
+ // NO BREAK
+#endif
+ case TRAP_BRKPT:
+ MonitorBreakpoint(thread);
+ break;
+
+ case SIGTRAP:
+ case (SIGTRAP | 0x80):
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() received unknown SIGTRAP system call stop event, pid %" PRIu64 "tid %" PRIu64 ", resuming", __FUNCTION__, GetID (), thread.GetID());
+
+ // Ignore these signals until we know more about them.
+ ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
+ break;
+
+ default:
+ assert(false && "Unexpected SIGTRAP code!");
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 "tid %" PRIu64 " received unhandled SIGTRAP code: 0x%d",
+ __FUNCTION__, GetID(), thread.GetID(), info.si_code);
+ break;
+
+ }
+}
+
+void
+NativeProcessLinux::MonitorTrace(NativeThreadLinux &thread)
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received trace event, pid = %" PRIu64 " (single stepping)",
+ __FUNCTION__, thread.GetID());
+
+ // This thread is currently stopped.
+ thread.SetStoppedByTrace();
+
+ StopRunningThreads(thread.GetID());
+}
+
+void
+NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread)
+{
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received breakpoint event, pid = %" PRIu64,
+ __FUNCTION__, thread.GetID());
+
+ // Mark the thread as stopped at breakpoint.
+ thread.SetStoppedByBreakpoint();
+ Error error = FixupBreakpointPCAsNeeded(thread);
+ if (error.Fail())
+ if (log)
+ log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " fixup: %s",
+ __FUNCTION__, thread.GetID(), error.AsCString());
+
+ if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != m_threads_stepping_with_breakpoint.end())
+ thread.SetStoppedByTrace();
+
+ StopRunningThreads(thread.GetID());
+}
+
+void
+NativeProcessLinux::MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index)
+{
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received watchpoint event, "
+ "pid = %" PRIu64 ", wp_index = %" PRIu32,
+ __FUNCTION__, thread.GetID(), wp_index);
+
+ // Mark the thread as stopped at watchpoint.
+ // The address is at (lldb::addr_t)info->si_addr if we need it.
+ thread.SetStoppedByWatchpoint(wp_index);
+
+ // We need to tell all other running threads before we notify the delegate about this stop.
+ StopRunningThreads(thread.GetID());
+}
+
+void
+NativeProcessLinux::MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited)
+{
+ const int signo = info.si_signo;
+ const bool is_from_llgs = info.si_pid == getpid ();
+
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ // POSIX says that process behaviour is undefined after it ignores a SIGFPE,
+ // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a
+ // kill(2) or raise(3). Similarly for tgkill(2) on Linux.
+ //
+ // IOW, user generated signals never generate what we consider to be a
+ // "crash".
+ //
+ // Similarly, ACK signals generated by this monitor.
+
+ Mutex::Locker locker (m_threads_mutex);
+
+ // Handle the signal.
+ if (info.si_code == SI_TKILL || info.si_code == SI_USER)
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() received signal %s (%d) with code %s, (siginfo pid = %d (%s), waitpid pid = %" PRIu64 ")",
+ __FUNCTION__,
+ Host::GetSignalAsCString(signo),
+ signo,
+ (info.si_code == SI_TKILL ? "SI_TKILL" : "SI_USER"),
+ info.si_pid,
+ is_from_llgs ? "from llgs" : "not from llgs",
+ thread.GetID());
+ }
+
+ // Check for thread stop notification.
+ if (is_from_llgs && (info.si_code == SI_TKILL) && (signo == SIGSTOP))
+ {
+ // This is a tgkill()-based stop.
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread stopped",
+ __FUNCTION__,
+ GetID (),
+ thread.GetID());
+
+ // Check that we're not already marked with a stop reason.
+ // Note this thread really shouldn't already be marked as stopped - if we were, that would imply that
+ // the kernel signaled us with the thread stopping which we handled and marked as stopped,
+ // and that, without an intervening resume, we received another stop. It is more likely
+ // that we are missing the marking of a run state somewhere if we find that the thread was
+ // marked as stopped.
+ const StateType thread_state = thread.GetState();
+ if (!StateIsStoppedState (thread_state, false))
+ {
+ // An inferior thread has stopped because of a SIGSTOP we have sent it.
+ // Generally, these are not important stops and we don't want to report them as
+ // they are just used to stop other threads when one thread (the one with the
+ // *real* stop reason) hits a breakpoint (watchpoint, etc...). However, in the
+ // case of an asynchronous Interrupt(), this *is* the real stop reason, so we
+ // leave the signal intact if this is the thread that was chosen as the
+ // triggering thread.
+ if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID)
+ {
+ if (m_pending_notification_tid == thread.GetID())
+ thread.SetStoppedBySignal(SIGSTOP, &info);
+ else
+ thread.SetStoppedWithNoReason();
+
+ SetCurrentThreadID (thread.GetID ());
+ SignalIfAllThreadsStopped();
+ }
+ else
+ {
+ // We can end up here if stop was initiated by LLGS but by this time a
+ // thread stop has occurred - maybe initiated by another event.
+ Error error = ResumeThread(thread, thread.GetState(), 0);
+ if (error.Fail() && log)
+ {
+ log->Printf("NativeProcessLinux::%s failed to resume thread tid %" PRIu64 ": %s",
+ __FUNCTION__, thread.GetID(), error.AsCString());
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ {
+ // Retrieve the signal name if the thread was stopped by a signal.
+ int stop_signo = 0;
+ const bool stopped_by_signal = thread.IsStopped(&stop_signo);
+ const char *signal_name = stopped_by_signal ? Host::GetSignalAsCString(stop_signo) : "<not stopped by signal>";
+ if (!signal_name)
+ signal_name = "<no-signal-name>";
+
+ log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread was already marked as a stopped state (state=%s, signal=%d (%s)), leaving stop signal as is",
+ __FUNCTION__,
+ GetID (),
+ thread.GetID(),
+ StateAsCString (thread_state),
+ stop_signo,
+ signal_name);
+ }
+ SignalIfAllThreadsStopped();
+ }
+
+ // Done handling.
+ return;
+ }
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() received signal %s", __FUNCTION__, Host::GetSignalAsCString(signo));
+
+ // This thread is stopped.
+ thread.SetStoppedBySignal(signo, &info);
+
+ // Send a stop to the debugger after we get all other threads to stop.
+ StopRunningThreads(thread.GetID());
+}
+
+namespace {
+
+struct EmulatorBaton
+{
+ NativeProcessLinux* m_process;
+ NativeRegisterContext* m_reg_context;
+
+ // eRegisterKindDWARF -> RegsiterValue
+ std::unordered_map<uint32_t, RegisterValue> m_register_values;
+
+ EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) :
+ m_process(process), m_reg_context(reg_context) {}
+};
+
+} // anonymous namespace
+
+static size_t
+ReadMemoryCallback (EmulateInstruction *instruction,
+ void *baton,
+ const EmulateInstruction::Context &context,
+ lldb::addr_t addr,
+ void *dst,
+ size_t length)
+{
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+
+ size_t bytes_read;
+ emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read);
+ return bytes_read;
+}
+
+static bool
+ReadRegisterCallback (EmulateInstruction *instruction,
+ void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value)
+{
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+
+ auto it = emulator_baton->m_register_values.find(reg_info->kinds[eRegisterKindDWARF]);
+ if (it != emulator_baton->m_register_values.end())
+ {
+ reg_value = it->second;
+ return true;
+ }
+
+ // The emulator only fill in the dwarf regsiter numbers (and in some case
+ // the generic register numbers). Get the full register info from the
+ // register context based on the dwarf register numbers.
+ const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo(
+ eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
+
+ Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
+ if (error.Success())
+ return true;
+
+ return false;
+}
+
+static bool
+WriteRegisterCallback (EmulateInstruction *instruction,
+ void *baton,
+ const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value)
+{
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+ emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = reg_value;
+ return true;
+}
+
+static size_t
+WriteMemoryCallback (EmulateInstruction *instruction,
+ void *baton,
+ const EmulateInstruction::Context &context,
+ lldb::addr_t addr,
+ const void *dst,
+ size_t length)
+{
+ return length;
+}
+
+static lldb::addr_t
+ReadFlags (NativeRegisterContext* regsiter_context)
+{
+ const RegisterInfo* flags_info = regsiter_context->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+ return regsiter_context->ReadRegisterAsUnsigned(flags_info, LLDB_INVALID_ADDRESS);
+}
+
+Error
+NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadLinux &thread)
+{
+ Error error;
+ NativeRegisterContextSP register_context_sp = thread.GetRegisterContext();
+
+ std::unique_ptr<EmulateInstruction> emulator_ap(
+ EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, nullptr));
+
+ if (emulator_ap == nullptr)
+ return Error("Instruction emulator not found!");
+
+ EmulatorBaton baton(this, register_context_sp.get());
+ emulator_ap->SetBaton(&baton);
+ emulator_ap->SetReadMemCallback(&ReadMemoryCallback);
+ emulator_ap->SetReadRegCallback(&ReadRegisterCallback);
+ emulator_ap->SetWriteMemCallback(&WriteMemoryCallback);
+ emulator_ap->SetWriteRegCallback(&WriteRegisterCallback);
+
+ if (!emulator_ap->ReadInstruction())
+ return Error("Read instruction failed!");
+
+ bool emulation_result = emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
+
+ const RegisterInfo* reg_info_pc = register_context_sp->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo* reg_info_flags = register_context_sp->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+
+ auto pc_it = baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
+ auto flags_it = baton.m_register_values.find(reg_info_flags->kinds[eRegisterKindDWARF]);
+
+ lldb::addr_t next_pc;
+ lldb::addr_t next_flags;
+ if (emulation_result)
+ {
+ assert(pc_it != baton.m_register_values.end() && "Emulation was successfull but PC wasn't updated");
+ next_pc = pc_it->second.GetAsUInt64();
+
+ if (flags_it != baton.m_register_values.end())
+ next_flags = flags_it->second.GetAsUInt64();
+ else
+ next_flags = ReadFlags (register_context_sp.get());
+ }
+ else if (pc_it == baton.m_register_values.end())
+ {
+ // Emulate instruction failed and it haven't changed PC. Advance PC
+ // with the size of the current opcode because the emulation of all
+ // PC modifying instruction should be successful. The failure most
+ // likely caused by a not supported instruction which don't modify PC.
+ next_pc = register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize();
+ next_flags = ReadFlags (register_context_sp.get());
+ }
+ else
+ {
+ // The instruction emulation failed after it modified the PC. It is an
+ // unknown error where we can't continue because the next instruction is
+ // modifying the PC but we don't know how.
+ return Error ("Instruction emulation failed unexpectedly.");
+ }
+
+ if (m_arch.GetMachine() == llvm::Triple::arm)
+ {
+ if (next_flags & 0x20)
+ {
+ // Thumb mode
+ error = SetSoftwareBreakpoint(next_pc, 2);
+ }
+ else
+ {
+ // Arm mode
+ error = SetSoftwareBreakpoint(next_pc, 4);
+ }
+ }
+ else if (m_arch.GetMachine() == llvm::Triple::mips64
+ || m_arch.GetMachine() == llvm::Triple::mips64el
+ || m_arch.GetMachine() == llvm::Triple::mips
+ || m_arch.GetMachine() == llvm::Triple::mipsel)
+ error = SetSoftwareBreakpoint(next_pc, 4);
+ else
+ {
+ // No size hint is given for the next breakpoint
+ error = SetSoftwareBreakpoint(next_pc, 0);
+ }
+
+ if (error.Fail())
+ return error;
+
+ m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
+
+ return Error();
+}
+
+bool
+NativeProcessLinux::SupportHardwareSingleStepping() const
+{
+ if (m_arch.GetMachine() == llvm::Triple::arm
+ || m_arch.GetMachine() == llvm::Triple::mips64 || m_arch.GetMachine() == llvm::Triple::mips64el
+ || m_arch.GetMachine() == llvm::Triple::mips || m_arch.GetMachine() == llvm::Triple::mipsel)
+ return false;
+ return true;
+}
+
+Error
+NativeProcessLinux::Resume (const ResumeActionList &resume_actions)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf ("NativeProcessLinux::%s called: pid %" PRIu64, __FUNCTION__, GetID ());
+
+ bool software_single_step = !SupportHardwareSingleStepping();
+
+ Mutex::Locker locker (m_threads_mutex);
+
+ if (software_single_step)
+ {
+ for (auto thread_sp : m_threads)
+ {
+ assert (thread_sp && "thread list should not contain NULL threads");
+
+ const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true);
+ if (action == nullptr)
+ continue;
+
+ if (action->state == eStateStepping)
+ {
+ Error error = SetupSoftwareSingleStepping(static_cast<NativeThreadLinux &>(*thread_sp));
+ if (error.Fail())
+ return error;
+ }
+ }
+ }
+
+ for (auto thread_sp : m_threads)
+ {
+ assert (thread_sp && "thread list should not contain NULL threads");
+
+ const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true);
+
+ if (action == nullptr)
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s no action specified for pid %" PRIu64 " tid %" PRIu64,
+ __FUNCTION__, GetID (), thread_sp->GetID ());
+ continue;
+ }
+
+ if (log)
+ {
+ log->Printf ("NativeProcessLinux::%s processing resume action state %s for pid %" PRIu64 " tid %" PRIu64,
+ __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ());
+ }
+
+ switch (action->state)
+ {
+ case eStateRunning:
+ case eStateStepping:
+ {
+ // Run the thread, possibly feeding it the signal.
+ const int signo = action->signal;
+ ResumeThread(static_cast<NativeThreadLinux &>(*thread_sp), action->state, signo);
+ break;
+ }
+
+ case eStateSuspended:
+ case eStateStopped:
+ lldbassert(0 && "Unexpected state");
+
+ default:
+ return Error ("NativeProcessLinux::%s (): unexpected state %s specified for pid %" PRIu64 ", tid %" PRIu64,
+ __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ());
+ }
+ }
+
+ return Error();
+}
+
+Error
+NativeProcessLinux::Halt ()
+{
+ Error error;
+
+ if (kill (GetID (), SIGSTOP) != 0)
+ error.SetErrorToErrno ();
+
+ return error;
+}
+
+Error
+NativeProcessLinux::Detach ()
+{
+ Error error;
+
+ // Stop monitoring the inferior.
+ m_sigchld_handle.reset();
+
+ // Tell ptrace to detach from the process.
+ if (GetID () == LLDB_INVALID_PROCESS_ID)
+ return error;
+
+ for (auto thread_sp : m_threads)
+ {
+ Error e = Detach(thread_sp->GetID());
+ if (e.Fail())
+ error = e; // Save the error, but still attempt to detach from other threads.
+ }
+
+ return error;
+}
+
+Error
+NativeProcessLinux::Signal (int signo)
+{
+ Error error;
+
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("NativeProcessLinux::%s: sending signal %d (%s) to pid %" PRIu64,
+ __FUNCTION__, signo, Host::GetSignalAsCString(signo), GetID());
+
+ if (kill(GetID(), signo))
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Error
+NativeProcessLinux::Interrupt ()
+{
+ // Pick a running thread (or if none, a not-dead stopped thread) as
+ // the chosen thread that will be the stop-reason thread.
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ NativeThreadProtocolSP running_thread_sp;
+ NativeThreadProtocolSP stopped_thread_sp;
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s selecting running thread for interrupt target", __FUNCTION__);
+
+ Mutex::Locker locker (m_threads_mutex);
+
+ for (auto thread_sp : m_threads)
+ {
+ // The thread shouldn't be null but lets just cover that here.
+ if (!thread_sp)
+ continue;
+
+ // If we have a running or stepping thread, we'll call that the
+ // target of the interrupt.
+ const auto thread_state = thread_sp->GetState ();
+ if (thread_state == eStateRunning ||
+ thread_state == eStateStepping)
+ {
+ running_thread_sp = thread_sp;
+ break;
+ }
+ else if (!stopped_thread_sp && StateIsStoppedState (thread_state, true))
+ {
+ // Remember the first non-dead stopped thread. We'll use that as a backup if there are no running threads.
+ stopped_thread_sp = thread_sp;
+ }
+ }
+
+ if (!running_thread_sp && !stopped_thread_sp)
+ {
+ Error error("found no running/stepping or live stopped threads as target for interrupt");
+ if (log)
+ log->Printf ("NativeProcessLinux::%s skipping due to error: %s", __FUNCTION__, error.AsCString ());
+
+ return error;
+ }
+
+ NativeThreadProtocolSP deferred_signal_thread_sp = running_thread_sp ? running_thread_sp : stopped_thread_sp;
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " %s tid %" PRIu64 " chosen for interrupt target",
+ __FUNCTION__,
+ GetID (),
+ running_thread_sp ? "running" : "stopped",
+ deferred_signal_thread_sp->GetID ());
+
+ StopRunningThreads(deferred_signal_thread_sp->GetID());
+
+ return Error();
+}
+
+Error
+NativeProcessLinux::Kill ()
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("NativeProcessLinux::%s called for PID %" PRIu64, __FUNCTION__, GetID ());
+
+ Error error;
+
+ switch (m_state)
+ {
+ case StateType::eStateInvalid:
+ case StateType::eStateExited:
+ case StateType::eStateCrashed:
+ case StateType::eStateDetached:
+ case StateType::eStateUnloaded:
+ // Nothing to do - the process is already dead.
+ if (log)
+ log->Printf ("NativeProcessLinux::%s ignored for PID %" PRIu64 " due to current state: %s", __FUNCTION__, GetID (), StateAsCString (m_state));
+ return error;
+
+ case StateType::eStateConnected:
+ case StateType::eStateAttaching:
+ case StateType::eStateLaunching:
+ case StateType::eStateStopped:
+ case StateType::eStateRunning:
+ case StateType::eStateStepping:
+ case StateType::eStateSuspended:
+ // We can try to kill a process in these states.
+ break;
+ }
+
+ if (kill (GetID (), SIGKILL) != 0)
+ {
+ error.SetErrorToErrno ();
+ return error;
+ }
+
+ return error;
+}
+
+static Error
+ParseMemoryRegionInfoFromProcMapsLine (const std::string &maps_line, MemoryRegionInfo &memory_region_info)
+{
+ memory_region_info.Clear();
+
+ StringExtractor line_extractor (maps_line.c_str ());
+
+ // Format: {address_start_hex}-{address_end_hex} perms offset dev inode pathname
+ // perms: rwxp (letter is present if set, '-' if not, final character is p=private, s=shared).
+
+ // Parse out the starting address
+ lldb::addr_t start_address = line_extractor.GetHexMaxU64 (false, 0);
+
+ // Parse out hyphen separating start and end address from range.
+ if (!line_extractor.GetBytesLeft () || (line_extractor.GetChar () != '-'))
+ return Error ("malformed /proc/{pid}/maps entry, missing dash between address range");
+
+ // Parse out the ending address
+ lldb::addr_t end_address = line_extractor.GetHexMaxU64 (false, start_address);
+
+ // Parse out the space after the address.
+ if (!line_extractor.GetBytesLeft () || (line_extractor.GetChar () != ' '))
+ return Error ("malformed /proc/{pid}/maps entry, missing space after range");
+
+ // Save the range.
+ memory_region_info.GetRange ().SetRangeBase (start_address);
+ memory_region_info.GetRange ().SetRangeEnd (end_address);
+
+ // Parse out each permission entry.
+ if (line_extractor.GetBytesLeft () < 4)
+ return Error ("malformed /proc/{pid}/maps entry, missing some portion of permissions");
+
+ // Handle read permission.
+ const char read_perm_char = line_extractor.GetChar ();
+ if (read_perm_char == 'r')
+ memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eYes);
+ else
+ {
+ assert ( (read_perm_char == '-') && "unexpected /proc/{pid}/maps read permission char" );
+ memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo);
+ }
+
+ // Handle write permission.
+ const char write_perm_char = line_extractor.GetChar ();
+ if (write_perm_char == 'w')
+ memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eYes);
+ else
+ {
+ assert ( (write_perm_char == '-') && "unexpected /proc/{pid}/maps write permission char" );
+ memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo);
+ }
+
+ // Handle execute permission.
+ const char exec_perm_char = line_extractor.GetChar ();
+ if (exec_perm_char == 'x')
+ memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eYes);
+ else
+ {
+ assert ( (exec_perm_char == '-') && "unexpected /proc/{pid}/maps exec permission char" );
+ memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo);
+ }
+
+ return Error ();
+}
+
+Error
+NativeProcessLinux::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info)
+{
+ // FIXME review that the final memory region returned extends to the end of the virtual address space,
+ // with no perms if it is not mapped.
+
+ // Use an approach that reads memory regions from /proc/{pid}/maps.
+ // Assume proc maps entries are in ascending order.
+ // FIXME assert if we find differently.
+ Mutex::Locker locker (m_mem_region_cache_mutex);
+
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ Error error;
+
+ if (m_supports_mem_region == LazyBool::eLazyBoolNo)
+ {
+ // We're done.
+ error.SetErrorString ("unsupported");
+ return error;
+ }
+
+ // If our cache is empty, pull the latest. There should always be at least one memory region
+ // if memory region handling is supported.
+ if (m_mem_region_cache.empty ())
+ {
+ error = ProcFileReader::ProcessLineByLine (GetID (), "maps",
+ [&] (const std::string &line) -> bool
+ {
+ MemoryRegionInfo info;
+ const Error parse_error = ParseMemoryRegionInfoFromProcMapsLine (line, info);
+ if (parse_error.Success ())
+ {
+ m_mem_region_cache.push_back (info);
+ return true;
+ }
+ else
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s failed to parse proc maps line '%s': %s", __FUNCTION__, line.c_str (), error.AsCString ());
+ return false;
+ }
+ });
+
+ // If we had an error, we'll mark unsupported.
+ if (error.Fail ())
+ {
+ m_supports_mem_region = LazyBool::eLazyBoolNo;
+ return error;
+ }
+ else if (m_mem_region_cache.empty ())
+ {
+ // No entries after attempting to read them. This shouldn't happen if /proc/{pid}/maps
+ // is supported. Assume we don't support map entries via procfs.
+ if (log)
+ log->Printf ("NativeProcessLinux::%s failed to find any procfs maps entries, assuming no support for memory region metadata retrieval", __FUNCTION__);
+ m_supports_mem_region = LazyBool::eLazyBoolNo;
+ error.SetErrorString ("not supported");
+ return error;
+ }
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s read %" PRIu64 " memory region entries from /proc/%" PRIu64 "/maps", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ()), GetID ());
+
+ // We support memory retrieval, remember that.
+ m_supports_mem_region = LazyBool::eLazyBoolYes;
+ }
+ else
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s reusing %" PRIu64 " cached memory region entries", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ()));
+ }
+
+ lldb::addr_t prev_base_address = 0;
+
+ // FIXME start by finding the last region that is <= target address using binary search. Data is sorted.
+ // There can be a ton of regions on pthreads apps with lots of threads.
+ for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end (); ++it)
+ {
+ MemoryRegionInfo &proc_entry_info = *it;
+
+ // Sanity check assumption that /proc/{pid}/maps entries are ascending.
+ assert ((proc_entry_info.GetRange ().GetRangeBase () >= prev_base_address) && "descending /proc/pid/maps entries detected, unexpected");
+ prev_base_address = proc_entry_info.GetRange ().GetRangeBase ();
+
+ // If the target address comes before this entry, indicate distance to next region.
+ if (load_addr < proc_entry_info.GetRange ().GetRangeBase ())
+ {
+ range_info.GetRange ().SetRangeBase (load_addr);
+ range_info.GetRange ().SetByteSize (proc_entry_info.GetRange ().GetRangeBase () - load_addr);
+ range_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo);
+
+ return error;
+ }
+ else if (proc_entry_info.GetRange ().Contains (load_addr))
+ {
+ // The target address is within the memory region we're processing here.
+ range_info = proc_entry_info;
+ return error;
+ }
+
+ // The target memory address comes somewhere after the region we just parsed.
+ }
+
+ // If we made it here, we didn't find an entry that contained the given address. Return the
+ // load_addr as start and the amount of bytes betwwen load address and the end of the memory as
+ // size.
+ range_info.GetRange ().SetRangeBase (load_addr);
+ switch (m_arch.GetAddressByteSize())
+ {
+ case 4:
+ range_info.GetRange ().SetByteSize (0x100000000ull - load_addr);
+ break;
+ case 8:
+ range_info.GetRange ().SetByteSize (0ull - load_addr);
+ break;
+ default:
+ assert(false && "Unrecognized data byte size");
+ break;
+ }
+ range_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo);
+ return error;
+}
+
+void
+NativeProcessLinux::DoStopIDBumped (uint32_t newBumpId)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("NativeProcessLinux::%s(newBumpId=%" PRIu32 ") called", __FUNCTION__, newBumpId);
+
+ {
+ Mutex::Locker locker (m_mem_region_cache_mutex);
+ if (log)
+ log->Printf ("NativeProcessLinux::%s clearing %" PRIu64 " entries from the cache", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ()));
+ m_mem_region_cache.clear ();
+ }
+}
+
+Error
+NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr)
+{
+ // FIXME implementing this requires the equivalent of
+ // InferiorCallPOSIX::InferiorCallMmap, which depends on
+ // functional ThreadPlans working with Native*Protocol.
+#if 1
+ return Error ("not implemented yet");
+#else
+ addr = LLDB_INVALID_ADDRESS;
+
+ unsigned prot = 0;
+ if (permissions & lldb::ePermissionsReadable)
+ prot |= eMmapProtRead;
+ if (permissions & lldb::ePermissionsWritable)
+ prot |= eMmapProtWrite;
+ if (permissions & lldb::ePermissionsExecutable)
+ prot |= eMmapProtExec;
+
+ // TODO implement this directly in NativeProcessLinux
+ // (and lift to NativeProcessPOSIX if/when that class is
+ // refactored out).
+ if (InferiorCallMmap(this, addr, 0, size, prot,
+ eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) {
+ m_addr_to_mmap_size[addr] = size;
+ return Error ();
+ } else {
+ addr = LLDB_INVALID_ADDRESS;
+ return Error("unable to allocate %" PRIu64 " bytes of memory with permissions %s", size, GetPermissionsAsCString (permissions));
+ }
+#endif
+}
+
+Error
+NativeProcessLinux::DeallocateMemory (lldb::addr_t addr)
+{
+ // FIXME see comments in AllocateMemory - required lower-level
+ // bits not in place yet (ThreadPlans)
+ return Error ("not implemented");
+}
+
+lldb::addr_t
+NativeProcessLinux::GetSharedLibraryInfoAddress ()
+{
+#if 1
+ // punt on this for now
+ return LLDB_INVALID_ADDRESS;
+#else
+ // Return the image info address for the exe module
+#if 1
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ ModuleSP module_sp;
+ Error error = GetExeModuleSP (module_sp);
+ if (error.Fail ())
+ {
+ if (log)
+ log->Warning ("NativeProcessLinux::%s failed to retrieve exe module: %s", __FUNCTION__, error.AsCString ());
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ if (module_sp == nullptr)
+ {
+ if (log)
+ log->Warning ("NativeProcessLinux::%s exe module returned was NULL", __FUNCTION__);
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ ObjectFileSP object_file_sp = module_sp->GetObjectFile ();
+ if (object_file_sp == nullptr)
+ {
+ if (log)
+ log->Warning ("NativeProcessLinux::%s exe module returned a NULL object file", __FUNCTION__);
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ return obj_file_sp->GetImageInfoAddress();
+#else
+ Target *target = &GetTarget();
+ ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress(target);
+
+ if (addr.IsValid())
+ return addr.GetLoadAddress(target);
+ return LLDB_INVALID_ADDRESS;
+#endif
+#endif // punt on this for now
+}
+
+size_t
+NativeProcessLinux::UpdateThreads ()
+{
+ // The NativeProcessLinux monitoring threads are always up to date
+ // with respect to thread state and they keep the thread list
+ // populated properly. All this method needs to do is return the
+ // thread count.
+ Mutex::Locker locker (m_threads_mutex);
+ return m_threads.size ();
+}
+
+bool
+NativeProcessLinux::GetArchitecture (ArchSpec &arch) const
+{
+ arch = m_arch;
+ return true;
+}
+
+Error
+NativeProcessLinux::GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size)
+{
+ // FIXME put this behind a breakpoint protocol class that can be
+ // set per architecture. Need ARM, MIPS support here.
+ static const uint8_t g_i386_opcode [] = { 0xCC };
+
+ switch (m_arch.GetMachine ())
+ {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ actual_opcode_size = static_cast<uint32_t> (sizeof(g_i386_opcode));
+ return Error ();
+
+ case llvm::Triple::arm:
+ case llvm::Triple::aarch64:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ // On these architectures the PC don't get updated for breakpoint hits
+ actual_opcode_size = 0;
+ return Error ();
+
+ default:
+ assert(false && "CPU type not supported!");
+ return Error ("CPU type not supported");
+ }
+}
+
+Error
+NativeProcessLinux::SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware)
+{
+ if (hardware)
+ return Error ("NativeProcessLinux does not support hardware breakpoints");
+ else
+ return SetSoftwareBreakpoint (addr, size);
+}
+
+Error
+NativeProcessLinux::GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hint,
+ size_t &actual_opcode_size,
+ const uint8_t *&trap_opcode_bytes)
+{
+ // FIXME put this behind a breakpoint protocol class that can be set per
+ // architecture. Need MIPS support here.
+ static const uint8_t g_aarch64_opcode[] = { 0x00, 0x00, 0x20, 0xd4 };
+ // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the
+ // linux kernel does otherwise.
+ static const uint8_t g_arm_breakpoint_opcode[] = { 0xf0, 0x01, 0xf0, 0xe7 };
+ static const uint8_t g_i386_opcode [] = { 0xCC };
+ static const uint8_t g_mips64_opcode[] = { 0x00, 0x00, 0x00, 0x0d };
+ static const uint8_t g_mips64el_opcode[] = { 0x0d, 0x00, 0x00, 0x00 };
+ static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde };
+
+ switch (m_arch.GetMachine ())
+ {
+ case llvm::Triple::aarch64:
+ trap_opcode_bytes = g_aarch64_opcode;
+ actual_opcode_size = sizeof(g_aarch64_opcode);
+ return Error ();
+
+ case llvm::Triple::arm:
+ switch (trap_opcode_size_hint)
+ {
+ case 2:
+ trap_opcode_bytes = g_thumb_breakpoint_opcode;
+ actual_opcode_size = sizeof(g_thumb_breakpoint_opcode);
+ return Error ();
+ case 4:
+ trap_opcode_bytes = g_arm_breakpoint_opcode;
+ actual_opcode_size = sizeof(g_arm_breakpoint_opcode);
+ return Error ();
+ default:
+ assert(false && "Unrecognised trap opcode size hint!");
+ return Error ("Unrecognised trap opcode size hint!");
+ }
+
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ trap_opcode_bytes = g_i386_opcode;
+ actual_opcode_size = sizeof(g_i386_opcode);
+ return Error ();
+
+ case llvm::Triple::mips:
+ case llvm::Triple::mips64:
+ trap_opcode_bytes = g_mips64_opcode;
+ actual_opcode_size = sizeof(g_mips64_opcode);
+ return Error ();
+
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64el:
+ trap_opcode_bytes = g_mips64el_opcode;
+ actual_opcode_size = sizeof(g_mips64el_opcode);
+ return Error ();
+
+ default:
+ assert(false && "CPU type not supported!");
+ return Error ("CPU type not supported");
+ }
+}
+
+#if 0
+ProcessMessage::CrashReason
+NativeProcessLinux::GetCrashReasonForSIGSEGV(const siginfo_t *info)
+{
+ ProcessMessage::CrashReason reason;
+ assert(info->si_signo == SIGSEGV);
+
+ reason = ProcessMessage::eInvalidCrashReason;
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "unexpected si_code for SIGSEGV");
+ break;
+ case SI_KERNEL:
+ // Linux will occasionally send spurious SI_KERNEL codes.
+ // (this is poorly documented in sigaction)
+ // One way to get this is via unaligned SIMD loads.
+ reason = ProcessMessage::eInvalidAddress; // for lack of anything better
+ break;
+ case SEGV_MAPERR:
+ reason = ProcessMessage::eInvalidAddress;
+ break;
+ case SEGV_ACCERR:
+ reason = ProcessMessage::ePrivilegedAddress;
+ break;
+ }
+
+ return reason;
+}
+#endif
+
+
+#if 0
+ProcessMessage::CrashReason
+NativeProcessLinux::GetCrashReasonForSIGILL(const siginfo_t *info)
+{
+ ProcessMessage::CrashReason reason;
+ assert(info->si_signo == SIGILL);
+
+ reason = ProcessMessage::eInvalidCrashReason;
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "unexpected si_code for SIGILL");
+ break;
+ case ILL_ILLOPC:
+ reason = ProcessMessage::eIllegalOpcode;
+ break;
+ case ILL_ILLOPN:
+ reason = ProcessMessage::eIllegalOperand;
+ break;
+ case ILL_ILLADR:
+ reason = ProcessMessage::eIllegalAddressingMode;
+ break;
+ case ILL_ILLTRP:
+ reason = ProcessMessage::eIllegalTrap;
+ break;
+ case ILL_PRVOPC:
+ reason = ProcessMessage::ePrivilegedOpcode;
+ break;
+ case ILL_PRVREG:
+ reason = ProcessMessage::ePrivilegedRegister;
+ break;
+ case ILL_COPROC:
+ reason = ProcessMessage::eCoprocessorError;
+ break;
+ case ILL_BADSTK:
+ reason = ProcessMessage::eInternalStackError;
+ break;
+ }
+
+ return reason;
+}
+#endif
+
+#if 0
+ProcessMessage::CrashReason
+NativeProcessLinux::GetCrashReasonForSIGFPE(const siginfo_t *info)
+{
+ ProcessMessage::CrashReason reason;
+ assert(info->si_signo == SIGFPE);
+
+ reason = ProcessMessage::eInvalidCrashReason;
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "unexpected si_code for SIGFPE");
+ break;
+ case FPE_INTDIV:
+ reason = ProcessMessage::eIntegerDivideByZero;
+ break;
+ case FPE_INTOVF:
+ reason = ProcessMessage::eIntegerOverflow;
+ break;
+ case FPE_FLTDIV:
+ reason = ProcessMessage::eFloatDivideByZero;
+ break;
+ case FPE_FLTOVF:
+ reason = ProcessMessage::eFloatOverflow;
+ break;
+ case FPE_FLTUND:
+ reason = ProcessMessage::eFloatUnderflow;
+ break;
+ case FPE_FLTRES:
+ reason = ProcessMessage::eFloatInexactResult;
+ break;
+ case FPE_FLTINV:
+ reason = ProcessMessage::eFloatInvalidOperation;
+ break;
+ case FPE_FLTSUB:
+ reason = ProcessMessage::eFloatSubscriptRange;
+ break;
+ }
+
+ return reason;
+}
+#endif
+
+#if 0
+ProcessMessage::CrashReason
+NativeProcessLinux::GetCrashReasonForSIGBUS(const siginfo_t *info)
+{
+ ProcessMessage::CrashReason reason;
+ assert(info->si_signo == SIGBUS);
+
+ reason = ProcessMessage::eInvalidCrashReason;
+
+ switch (info->si_code)
+ {
+ default:
+ assert(false && "unexpected si_code for SIGBUS");
+ break;
+ case BUS_ADRALN:
+ reason = ProcessMessage::eIllegalAlignment;
+ break;
+ case BUS_ADRERR:
+ reason = ProcessMessage::eIllegalAddress;
+ break;
+ case BUS_OBJERR:
+ reason = ProcessMessage::eHardwareError;
+ break;
+ }
+
+ return reason;
+}
+#endif
+
+Error
+NativeProcessLinux::ReadMemory (lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read)
+{
+ if (ProcessVmReadvSupported()) {
+ // The process_vm_readv path is about 50 times faster than ptrace api. We want to use
+ // this syscall if it is supported.
+
+ const ::pid_t pid = GetID();
+
+ struct iovec local_iov, remote_iov;
+ local_iov.iov_base = buf;
+ local_iov.iov_len = size;
+ remote_iov.iov_base = reinterpret_cast<void *>(addr);
+ remote_iov.iov_len = size;
+
+ bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);
+ const bool success = bytes_read == size;
+
+ Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("NativeProcessLinux::%s using process_vm_readv to read %zd bytes from inferior address 0x%" PRIx64": %s",
+ __FUNCTION__, size, addr, success ? "Success" : strerror(errno));
+
+ if (success)
+ return Error();
+ // else
+ // the call failed for some reason, let's retry the read using ptrace api.
+ }
+
+ unsigned char *dst = static_cast<unsigned char*>(buf);
+ size_t remainder;
+ long data;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL));
+ if (log)
+ ProcessPOSIXLog::IncNestLevel();
+ if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY))
+ log->Printf ("NativeProcessLinux::%s(%p, %p, %zd, _)", __FUNCTION__, (void*)addr, buf, size);
+
+ for (bytes_read = 0; bytes_read < size; bytes_read += remainder)
+ {
+ Error error = NativeProcessLinux::PtraceWrapper(PTRACE_PEEKDATA, GetID(), (void*)addr, nullptr, 0, &data);
+ if (error.Fail())
+ {
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
+ }
+
+ remainder = size - bytes_read;
+ remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
+
+ // Copy the data into our buffer
+ memcpy(dst, &data, remainder);
+
+ if (log && ProcessPOSIXLog::AtTopNestLevel() &&
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
+ size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
+ {
+ uintptr_t print_dst = 0;
+ // Format bytes from data by moving into print_dst for log output
+ for (unsigned i = 0; i < remainder; ++i)
+ print_dst |= (((data >> i*8) & 0xFF) << i*8);
+ log->Printf ("NativeProcessLinux::%s() [0x%" PRIx64 "]:0x%" PRIx64 " (0x%" PRIx64 ")",
+ __FUNCTION__, addr, uint64_t(print_dst), uint64_t(data));
+ }
+ addr += k_ptrace_word_size;
+ dst += k_ptrace_word_size;
+ }
+
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return Error();
+}
+
+Error
+NativeProcessLinux::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read)
+{
+ Error error = ReadMemory(addr, buf, size, bytes_read);
+ if (error.Fail()) return error;
+ return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size);
+}
+
+Error
+NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written)
+{
+ const unsigned char *src = static_cast<const unsigned char*>(buf);
+ size_t remainder;
+ Error error;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL));
+ if (log)
+ ProcessPOSIXLog::IncNestLevel();
+ if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY))
+ log->Printf ("NativeProcessLinux::%s(0x%" PRIx64 ", %p, %zu)", __FUNCTION__, addr, buf, size);
+
+ for (bytes_written = 0; bytes_written < size; bytes_written += remainder)
+ {
+ remainder = size - bytes_written;
+ remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
+
+ if (remainder == k_ptrace_word_size)
+ {
+ unsigned long data = 0;
+ memcpy(&data, src, k_ptrace_word_size);
+
+ if (log && ProcessPOSIXLog::AtTopNestLevel() &&
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
+ size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
+ log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
+ (void*)addr, *(const unsigned long*)src, data);
+
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(), (void*)addr, (void*)data);
+ if (error.Fail())
+ {
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
+ }
+ }
+ else
+ {
+ unsigned char buff[8];
+ size_t bytes_read;
+ error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read);
+ if (error.Fail())
+ {
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
+ }
+
+ memcpy(buff, src, remainder);
+
+ size_t bytes_written_rec;
+ error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec);
+ if (error.Fail())
+ {
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
+ }
+
+ if (log && ProcessPOSIXLog::AtTopNestLevel() &&
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
+ size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
+ log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
+ (void*)addr, *(const unsigned long*)src, *(unsigned long*)buff);
+ }
+
+ addr += k_ptrace_word_size;
+ src += k_ptrace_word_size;
+ }
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
+}
+
+Error
+NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid,
+ Host::GetSignalAsCString(signo));
+
+
+
+ intptr_t data = 0;
+
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ data = signo;
+
+ Error error = PtraceWrapper(PTRACE_CONT, tid, nullptr, (void*)data);
+
+ if (log)
+ log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " result = %s", __FUNCTION__, tid, error.Success() ? "true" : "false");
+ return error;
+}
+
+Error
+NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo)
+{
+ intptr_t data = 0;
+
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ data = signo;
+
+ // If hardware single-stepping is not supported, we just do a continue. The breakpoint on the
+ // next instruction has been setup in NativeProcessLinux::Resume.
+ return PtraceWrapper(SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT,
+ tid, nullptr, (void*)data);
+}
+
+Error
+NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo)
+{
+ return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo);
+}
+
+Error
+NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message)
+{
+ return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message);
+}
+
+Error
+NativeProcessLinux::Detach(lldb::tid_t tid)
+{
+ if (tid == LLDB_INVALID_THREAD_ID)
+ return Error();
+
+ return PtraceWrapper(PTRACE_DETACH, tid);
+}
+
+bool
+NativeProcessLinux::DupDescriptor(const FileSpec &file_spec, int fd, int flags)
+{
+ int target_fd = open(file_spec.GetCString(), flags, 0666);
+
+ if (target_fd == -1)
+ return false;
+
+ if (dup2(target_fd, fd) == -1)
+ return false;
+
+ return (close(target_fd) == -1) ? false : true;
+}
+
+bool
+NativeProcessLinux::HasThreadNoLock (lldb::tid_t thread_id)
+{
+ for (auto thread_sp : m_threads)
+ {
+ assert (thread_sp && "thread list should not contain NULL threads");
+ if (thread_sp->GetID () == thread_id)
+ {
+ // We have this thread.
+ return true;
+ }
+ }
+
+ // We don't have this thread.
+ return false;
+}
+
+bool
+NativeProcessLinux::StopTrackingThread (lldb::tid_t thread_id)
+{
+ Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
+
+ if (log)
+ log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__, thread_id);
+
+ bool found = false;
+
+ Mutex::Locker locker (m_threads_mutex);
+ for (auto it = m_threads.begin (); it != m_threads.end (); ++it)
+ {
+ if (*it && ((*it)->GetID () == thread_id))
+ {
+ m_threads.erase (it);
+ found = true;
+ break;
+ }
+ }
+
+ SignalIfAllThreadsStopped();
+
+ return found;
+}
+
+NativeThreadLinuxSP
+NativeProcessLinux::AddThread (lldb::tid_t thread_id)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+
+ Mutex::Locker locker (m_threads_mutex);
+
+ if (log)
+ {
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " adding thread with tid %" PRIu64,
+ __FUNCTION__,
+ GetID (),
+ thread_id);
+ }
+
+ assert (!HasThreadNoLock (thread_id) && "attempted to add a thread by id that already exists");
+
+ // If this is the first thread, save it as the current thread
+ if (m_threads.empty ())
+ SetCurrentThreadID (thread_id);
+
+ auto thread_sp = std::make_shared<NativeThreadLinux>(this, thread_id);
+ m_threads.push_back (thread_sp);
+ return thread_sp;
+}
+
+Error
+NativeProcessLinux::FixupBreakpointPCAsNeeded(NativeThreadLinux &thread)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+
+ Error error;
+
+ // Find out the size of a breakpoint (might depend on where we are in the code).
+ NativeRegisterContextSP context_sp = thread.GetRegisterContext();
+ if (!context_sp)
+ {
+ error.SetErrorString ("cannot get a NativeRegisterContext for the thread");
+ if (log)
+ log->Printf ("NativeProcessLinux::%s failed: %s", __FUNCTION__, error.AsCString ());
+ return error;
+ }
+
+ uint32_t breakpoint_size = 0;
+ error = GetSoftwareBreakpointPCOffset(breakpoint_size);
+ if (error.Fail ())
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s GetBreakpointSize() failed: %s", __FUNCTION__, error.AsCString ());
+ return error;
+ }
+ else
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s breakpoint size: %" PRIu32, __FUNCTION__, breakpoint_size);
+ }
+
+ // First try probing for a breakpoint at a software breakpoint location: PC - breakpoint size.
+ const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation ();
+ lldb::addr_t breakpoint_addr = initial_pc_addr;
+ if (breakpoint_size > 0)
+ {
+ // Do not allow breakpoint probe to wrap around.
+ if (breakpoint_addr >= breakpoint_size)
+ breakpoint_addr -= breakpoint_size;
+ }
+
+ // Check if we stopped because of a breakpoint.
+ NativeBreakpointSP breakpoint_sp;
+ error = m_breakpoint_list.GetBreakpoint (breakpoint_addr, breakpoint_sp);
+ if (!error.Success () || !breakpoint_sp)
+ {
+ // We didn't find one at a software probe location. Nothing to do.
+ if (log)
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " no lldb breakpoint found at current pc with adjustment: 0x%" PRIx64, __FUNCTION__, GetID (), breakpoint_addr);
+ return Error ();
+ }
+
+ // If the breakpoint is not a software breakpoint, nothing to do.
+ if (!breakpoint_sp->IsSoftwareBreakpoint ())
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " breakpoint found at 0x%" PRIx64 ", not software, nothing to adjust", __FUNCTION__, GetID (), breakpoint_addr);
+ return Error ();
+ }
+
+ //
+ // We have a software breakpoint and need to adjust the PC.
+ //
+
+ // Sanity check.
+ if (breakpoint_size == 0)
+ {
+ // Nothing to do! How did we get here?
+ if (log)
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " breakpoint found at 0x%" PRIx64 ", it is software, but the size is zero, nothing to do (unexpected)", __FUNCTION__, GetID (), breakpoint_addr);
+ return Error ();
+ }
+
+ // Change the program counter.
+ if (log)
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 ": changing PC from 0x%" PRIx64 " to 0x%" PRIx64, __FUNCTION__, GetID(), thread.GetID(), initial_pc_addr, breakpoint_addr);
+
+ error = context_sp->SetPC (breakpoint_addr);
+ if (error.Fail ())
+ {
+ if (log)
+ log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 ": failed to set PC: %s", __FUNCTION__, GetID(), thread.GetID(), error.AsCString ());
+ return error;
+ }
+
+ return error;
+}
+
+Error
+NativeProcessLinux::GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec)
+{
+ FileSpec module_file_spec(module_path, true);
+
+ bool found = false;
+ file_spec.Clear();
+ ProcFileReader::ProcessLineByLine(GetID(), "maps",
+ [&] (const std::string &line)
+ {
+ SmallVector<StringRef, 16> columns;
+ StringRef(line).split(columns, " ", -1, false);
+ if (columns.size() < 6)
+ return true; // continue searching
+
+ FileSpec this_file_spec(columns[5].str().c_str(), false);
+ if (this_file_spec.GetFilename() != module_file_spec.GetFilename())
+ return true; // continue searching
+
+ file_spec = this_file_spec;
+ found = true;
+ return false; // we are done
+ });
+
+ if (! found)
+ return Error("Module file (%s) not found in /proc/%" PRIu64 "/maps file!",
+ module_file_spec.GetFilename().AsCString(), GetID());
+
+ return Error();
+}
+
+Error
+NativeProcessLinux::GetFileLoadAddress(const llvm::StringRef& file_name, lldb::addr_t& load_addr)
+{
+ load_addr = LLDB_INVALID_ADDRESS;
+ Error error = ProcFileReader::ProcessLineByLine (GetID (), "maps",
+ [&] (const std::string &line) -> bool
+ {
+ StringRef maps_row(line);
+
+ SmallVector<StringRef, 16> maps_columns;
+ maps_row.split(maps_columns, StringRef(" "), -1, false);
+
+ if (maps_columns.size() < 6)
+ {
+ // Return true to continue reading the proc file
+ return true;
+ }
+
+ if (maps_columns[5] == file_name)
+ {
+ StringExtractor addr_extractor(maps_columns[0].str().c_str());
+ load_addr = addr_extractor.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+
+ // Return false to stop reading the proc file further
+ return false;
+ }
+
+ // Return true to continue reading the proc file
+ return true;
+ });
+ return error;
+}
+
+NativeThreadLinuxSP
+NativeProcessLinux::GetThreadByID(lldb::tid_t tid)
+{
+ return std::static_pointer_cast<NativeThreadLinux>(NativeProcessProtocol::GetThreadByID(tid));
+}
+
+Error
+NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo)
+{
+ Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
+
+ if (log)
+ log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")",
+ __FUNCTION__, thread.GetID());
+
+ // Before we do the resume below, first check if we have a pending
+ // stop notification that is currently waiting for
+ // all threads to stop. This is potentially a buggy situation since
+ // we're ostensibly waiting for threads to stop before we send out the
+ // pending notification, and here we are resuming one before we send
+ // out the pending stop notification.
+ if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && log)
+ {
+ log->Printf("NativeProcessLinux::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that is actively waiting for this thread to stop. Valid sequence of events?", __FUNCTION__, thread.GetID(), m_pending_notification_tid);
+ }
+
+ // Request a resume. We expect this to be synchronous and the system
+ // to reflect it is running after this completes.
+ switch (state)
+ {
+ case eStateRunning:
+ {
+ thread.SetRunning();
+ const auto resume_result = Resume(thread.GetID(), signo);
+ if (resume_result.Success())
+ SetState(eStateRunning, true);
+ return resume_result;
+ }
+ case eStateStepping:
+ {
+ thread.SetStepping();
+ const auto step_result = SingleStep(thread.GetID(), signo);
+ if (step_result.Success())
+ SetState(eStateRunning, true);
+ return step_result;
+ }
+ default:
+ if (log)
+ log->Printf("NativeProcessLinux::%s Unhandled state %s.",
+ __FUNCTION__, StateAsCString(state));
+ llvm_unreachable("Unhandled state for resume");
+ }
+}
+
+//===----------------------------------------------------------------------===//
+
+void
+NativeProcessLinux::StopRunningThreads(const lldb::tid_t triggering_tid)
+{
+ Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
+
+ if (log)
+ {
+ log->Printf("NativeProcessLinux::%s about to process event: (triggering_tid: %" PRIu64 ")",
+ __FUNCTION__, triggering_tid);
+ }
+
+ m_pending_notification_tid = triggering_tid;
+
+ // Request a stop for all the thread stops that need to be stopped
+ // and are not already known to be stopped.
+ for (const auto &thread_sp: m_threads)
+ {
+ if (StateIsRunningState(thread_sp->GetState()))
+ static_pointer_cast<NativeThreadLinux>(thread_sp)->RequestStop();
+ }
+
+ SignalIfAllThreadsStopped();
+
+ if (log)
+ {
+ log->Printf("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::SignalIfAllThreadsStopped()
+{
+ if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID)
+ return; // No pending notification. Nothing to do.
+
+ for (const auto &thread_sp: m_threads)
+ {
+ if (StateIsRunningState(thread_sp->GetState()))
+ return; // Some threads are still running. Don't signal yet.
+ }
+
+ // We have a pending notification and all threads have stopped.
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS));
+
+ // Clear any temporary breakpoints we used to implement software single stepping.
+ for (const auto &thread_info: m_threads_stepping_with_breakpoint)
+ {
+ Error error = RemoveBreakpoint (thread_info.second);
+ if (error.Fail())
+ if (log)
+ log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " remove stepping breakpoint: %s",
+ __FUNCTION__, thread_info.first, error.AsCString());
+ }
+ m_threads_stepping_with_breakpoint.clear();
+
+ // Notify the delegate about the stop
+ SetCurrentThreadID(m_pending_notification_tid);
+ SetState(StateType::eStateStopped, true);
+ m_pending_notification_tid = LLDB_INVALID_THREAD_ID;
+}
+
+void
+NativeProcessLinux::ThreadWasCreated(NativeThreadLinux &thread)
+{
+ Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
+
+ if (log)
+ log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__, thread.GetID());
+
+ if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && StateIsRunningState(thread.GetState()))
+ {
+ // We will need to wait for this new thread to stop as well before firing the
+ // notification.
+ thread.RequestStop();
+ }
+}
+
+void
+NativeProcessLinux::SigchldHandler()
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ // Process all pending waitpid notifications.
+ while (true)
+ {
+ int status = -1;
+ ::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG);
+
+ if (wait_pid == 0)
+ break; // We are done.
+
+ if (wait_pid == -1)
+ {
+ if (errno == EINTR)
+ continue;
+
+ Error error(errno, eErrorTypePOSIX);
+ if (log)
+ log->Printf("NativeProcessLinux::%s waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG) failed: %s",
+ __FUNCTION__, error.AsCString());
+ break;
+ }
+
+ bool exited = false;
+ int signal = 0;
+ int exit_status = 0;
+ const char *status_cstr = nullptr;
+ if (WIFSTOPPED(status))
+ {
+ signal = WSTOPSIG(status);
+ status_cstr = "STOPPED";
+ }
+ else if (WIFEXITED(status))
+ {
+ exit_status = WEXITSTATUS(status);
+ status_cstr = "EXITED";
+ exited = true;
+ }
+ else if (WIFSIGNALED(status))
+ {
+ signal = WTERMSIG(status);
+ status_cstr = "SIGNALED";
+ if (wait_pid == static_cast< ::pid_t>(GetID())) {
+ exited = true;
+ exit_status = -1;
+ }
+ }
+ else
+ status_cstr = "(\?\?\?)";
+
+ if (log)
+ log->Printf("NativeProcessLinux::%s: waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG)"
+ "=> pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i",
+ __FUNCTION__, wait_pid, status, status_cstr, signal, exit_status);
+
+ MonitorCallback (wait_pid, exited, signal, exit_status);
+ }
+}
+
+// Wrapper for ptrace to catch errors and log calls.
+// Note that ptrace sets errno on error because -1 can be a valid result (i.e. for PTRACE_PEEK*)
+Error
+NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, long *result)
+{
+ Error error;
+ long int ret;
+
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PTRACE));
+
+ PtraceDisplayBytes(req, data, data_size);
+
+ errno = 0;
+ if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET)
+ ret = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), *(unsigned int *)addr, data);
+ else
+ ret = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), addr, data);
+
+ if (ret == -1)
+ error.SetErrorToErrno();
+
+ if (result)
+ *result = ret;
+
+ if (log)
+ log->Printf("ptrace(%d, %" PRIu64 ", %p, %p, %zu)=%lX", req, pid, addr, data, data_size, ret);
+
+ PtraceDisplayBytes(req, data, data_size);
+
+ if (log && error.GetError() != 0)
+ {
+ const char* str;
+ switch (error.GetError())
+ {
+ case ESRCH: str = "ESRCH"; break;
+ case EINVAL: str = "EINVAL"; break;
+ case EBUSY: str = "EBUSY"; break;
+ case EPERM: str = "EPERM"; break;
+ default: str = error.AsCString();
+ }
+ log->Printf("ptrace() failed; errno=%d (%s)", error.GetError(), str);
+ }
+
+ return error;
+}
diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.h b/source/Plugins/Process/Linux/NativeProcessLinux.h
new file mode 100644
index 0000000000000..7bac1dc8dbb7e
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -0,0 +1,329 @@
+//===-- NativeProcessLinux.h ---------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NativeProcessLinux_H_
+#define liblldb_NativeProcessLinux_H_
+
+// C++ Includes
+#include <unordered_set>
+
+// Other libraries and framework includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/lldb-types.h"
+#include "lldb/Host/Debug.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/Mutex.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "NativeThreadLinux.h"
+
+namespace lldb_private {
+ class Error;
+ class Module;
+ class Scalar;
+
+namespace process_linux {
+ /// @class NativeProcessLinux
+ /// @brief Manages communication with the inferior (debugee) process.
+ ///
+ /// Upon construction, this class prepares and launches an inferior process for
+ /// debugging.
+ ///
+ /// Changes in the inferior process state are broadcasted.
+ class NativeProcessLinux: public NativeProcessProtocol
+ {
+ friend Error
+ NativeProcessProtocol::Launch (ProcessLaunchInfo &launch_info,
+ NativeDelegate &native_delegate,
+ MainLoop &mainloop,
+ NativeProcessProtocolSP &process_sp);
+
+ friend Error
+ NativeProcessProtocol::Attach (lldb::pid_t pid,
+ NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop,
+ NativeProcessProtocolSP &process_sp);
+
+ public:
+ // ---------------------------------------------------------------------
+ // NativeProcessProtocol Interface
+ // ---------------------------------------------------------------------
+ Error
+ Resume (const ResumeActionList &resume_actions) override;
+
+ Error
+ Halt () override;
+
+ Error
+ Detach () override;
+
+ Error
+ Signal (int signo) override;
+
+ Error
+ Interrupt () override;
+
+ Error
+ Kill () override;
+
+ Error
+ GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info) override;
+
+ Error
+ ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override;
+
+ Error
+ ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override;
+
+ Error
+ WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override;
+
+ Error
+ AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) override;
+
+ Error
+ DeallocateMemory (lldb::addr_t addr) override;
+
+ lldb::addr_t
+ GetSharedLibraryInfoAddress () override;
+
+ size_t
+ UpdateThreads () override;
+
+ bool
+ GetArchitecture (ArchSpec &arch) const override;
+
+ Error
+ SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware) override;
+
+ void
+ DoStopIDBumped (uint32_t newBumpId) override;
+
+ Error
+ GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec) override;
+
+ Error
+ GetFileLoadAddress(const llvm::StringRef& file_name, lldb::addr_t& load_addr) override;
+
+ NativeThreadLinuxSP
+ GetThreadByID(lldb::tid_t id);
+
+ // ---------------------------------------------------------------------
+ // Interface used by NativeRegisterContext-derived classes.
+ // ---------------------------------------------------------------------
+ static Error
+ PtraceWrapper(int req,
+ lldb::pid_t pid,
+ void *addr = nullptr,
+ void *data = nullptr,
+ size_t data_size = 0,
+ long *result = nullptr);
+
+ protected:
+ // ---------------------------------------------------------------------
+ // NativeProcessProtocol protected interface
+ // ---------------------------------------------------------------------
+ Error
+ GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) override;
+
+ private:
+
+ MainLoop::SignalHandleUP m_sigchld_handle;
+ ArchSpec m_arch;
+
+ LazyBool m_supports_mem_region;
+ std::vector<MemoryRegionInfo> m_mem_region_cache;
+ Mutex m_mem_region_cache_mutex;
+
+ lldb::tid_t m_pending_notification_tid;
+
+ // List of thread ids stepping with a breakpoint with the address of
+ // the relevan breakpoint
+ std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint;
+
+ /// @class LauchArgs
+ ///
+ /// @brief Simple structure to pass data to the thread responsible for
+ /// launching a child process.
+ struct LaunchArgs
+ {
+ LaunchArgs(Module *module,
+ char const **argv,
+ char const **envp,
+ const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec,
+ const FileSpec &stderr_file_spec,
+ const FileSpec &working_dir,
+ const ProcessLaunchInfo &launch_info);
+
+ ~LaunchArgs();
+
+ Module *m_module; // The executable image to launch.
+ char const **m_argv; // Process arguments.
+ char const **m_envp; // Process environment.
+ const FileSpec m_stdin_file_spec; // Redirect stdin if not empty.
+ const FileSpec m_stdout_file_spec; // Redirect stdout if not empty.
+ const FileSpec m_stderr_file_spec; // Redirect stderr if not empty.
+ const FileSpec m_working_dir; // Working directory or empty.
+ const ProcessLaunchInfo &m_launch_info;
+ };
+
+ typedef std::function< ::pid_t(Error &)> InitialOperation;
+
+ // ---------------------------------------------------------------------
+ // Private Instance Methods
+ // ---------------------------------------------------------------------
+ NativeProcessLinux ();
+
+ /// Launches an inferior process ready for debugging. Forms the
+ /// implementation of Process::DoLaunch.
+ void
+ LaunchInferior (
+ MainLoop &mainloop,
+ Module *module,
+ char const *argv[],
+ char const *envp[],
+ const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec,
+ const FileSpec &stderr_file_spec,
+ const FileSpec &working_dir,
+ const ProcessLaunchInfo &launch_info,
+ Error &error);
+
+ /// Attaches to an existing process. Forms the
+ /// implementation of Process::DoAttach
+ void
+ AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error);
+
+ ::pid_t
+ Launch(LaunchArgs *args, Error &error);
+
+ ::pid_t
+ Attach(lldb::pid_t pid, Error &error);
+
+ static Error
+ SetDefaultPtraceOpts(const lldb::pid_t);
+
+ static bool
+ DupDescriptor(const FileSpec &file_spec, int fd, int flags);
+
+ static void *
+ MonitorThread(void *baton);
+
+ void
+ MonitorCallback(lldb::pid_t pid, bool exited, int signal, int status);
+
+ void
+ WaitForNewThread(::pid_t tid);
+
+ void
+ MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread);
+
+ void
+ MonitorTrace(NativeThreadLinux &thread);
+
+ void
+ MonitorBreakpoint(NativeThreadLinux &thread);
+
+ void
+ MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index);
+
+ void
+ MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited);
+
+ bool
+ SupportHardwareSingleStepping() const;
+
+ Error
+ SetupSoftwareSingleStepping(NativeThreadLinux &thread);
+
+#if 0
+ static ::ProcessMessage::CrashReason
+ GetCrashReasonForSIGSEGV(const siginfo_t *info);
+
+ static ::ProcessMessage::CrashReason
+ GetCrashReasonForSIGILL(const siginfo_t *info);
+
+ static ::ProcessMessage::CrashReason
+ GetCrashReasonForSIGFPE(const siginfo_t *info);
+
+ static ::ProcessMessage::CrashReason
+ GetCrashReasonForSIGBUS(const siginfo_t *info);
+#endif
+
+ bool
+ HasThreadNoLock (lldb::tid_t thread_id);
+
+ bool
+ StopTrackingThread (lldb::tid_t thread_id);
+
+ NativeThreadLinuxSP
+ AddThread (lldb::tid_t thread_id);
+
+ Error
+ GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size);
+
+ Error
+ FixupBreakpointPCAsNeeded(NativeThreadLinux &thread);
+
+ /// Writes a siginfo_t structure corresponding to the given thread ID to the
+ /// memory region pointed to by @p siginfo.
+ Error
+ GetSignalInfo(lldb::tid_t tid, void *siginfo);
+
+ /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
+ /// corresponding to the given thread ID to the memory pointed to by @p
+ /// message.
+ Error
+ GetEventMessage(lldb::tid_t tid, unsigned long *message);
+
+ /// Resumes the given thread. If @p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+ Error
+ Resume(lldb::tid_t tid, uint32_t signo);
+
+ /// Single steps the given thread. If @p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+ Error
+ SingleStep(lldb::tid_t tid, uint32_t signo);
+
+ void
+ NotifyThreadDeath (lldb::tid_t tid);
+
+ Error
+ Detach(lldb::tid_t tid);
+
+
+ // This method is requests a stop on all threads which are still running. It sets up a
+ // deferred delegate notification, which will fire once threads report as stopped. The
+ // triggerring_tid will be set as the current thread (main stop reason).
+ void
+ StopRunningThreads(lldb::tid_t triggering_tid);
+
+ // Notify the delegate if all threads have stopped.
+ void SignalIfAllThreadsStopped();
+
+ // Resume the given thread, optionally passing it the given signal. The type of resume
+ // operation (continue, single-step) depends on the state parameter.
+ Error
+ ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo);
+
+ void
+ ThreadWasCreated(NativeThreadLinux &thread);
+
+ void
+ SigchldHandler();
+ };
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeProcessLinux_H_
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp
new file mode 100644
index 0000000000000..df0a008ff5f7e
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp
@@ -0,0 +1,230 @@
+//===-- NativeRegisterContextLinux.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeRegisterContextLinux.h"
+
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/Host/linux/Ptrace.h"
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+
+NativeRegisterContextLinux::NativeRegisterContextLinux(NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx,
+ RegisterInfoInterface *reg_info_interface_p) :
+ NativeRegisterContextRegisterInfo(native_thread, concrete_frame_idx, reg_info_interface_p)
+{}
+
+lldb::ByteOrder
+NativeRegisterContextLinux::GetByteOrder() const
+{
+ // Get the target process whose privileged thread was used for the register read.
+ lldb::ByteOrder byte_order = lldb::eByteOrderInvalid;
+
+ NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
+ if (!process_sp)
+ return byte_order;
+
+ if (!process_sp->GetByteOrder (byte_order))
+ {
+ // FIXME log here
+ }
+
+ return byte_order;
+}
+
+Error
+NativeRegisterContextLinux::ReadRegisterRaw(uint32_t reg_index, RegisterValue &reg_value)
+{
+ const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info)
+ return Error("register %" PRIu32 " not found", reg_index);
+
+ return DoReadRegisterValue(reg_info->byte_offset, reg_info->name, reg_info->byte_size, reg_value);
+}
+
+Error
+NativeRegisterContextLinux::WriteRegisterRaw(uint32_t reg_index, const RegisterValue &reg_value)
+{
+ uint32_t reg_to_write = reg_index;
+ RegisterValue value_to_write = reg_value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_index);
+ if (reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM))
+ {
+ Error error;
+
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ error = ReadRegister(full_reg_info, full_value);
+ if (error.Fail ())
+ return error;
+
+ lldb::ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData (full_reg_info,
+ dst,
+ sizeof(dst),
+ byte_order,
+ error);
+ if (error.Success() && dest_size)
+ {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = reg_value.GetAsMemoryData (reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size))
+ {
+ // Copy the src bytes to the destination.
+ memcpy (dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+
+ const RegisterInfo *const register_to_write_info_p = GetRegisterInfoAtIndex (reg_to_write);
+ assert (register_to_write_info_p && "register to write does not have valid RegisterInfo");
+ if (!register_to_write_info_p)
+ return Error("NativeRegisterContextLinux::%s failed to get RegisterInfo for write register index %" PRIu32, __FUNCTION__, reg_to_write);
+
+ return DoWriteRegisterValue(reg_info->byte_offset, reg_info->name, reg_value);
+}
+
+Error
+NativeRegisterContextLinux::ReadGPR()
+{
+ void* buf = GetGPRBuffer();
+ if (!buf)
+ return Error("GPR buffer is NULL");
+ size_t buf_size = GetGPRSize();
+
+ return DoReadGPR(buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::WriteGPR()
+{
+ void* buf = GetGPRBuffer();
+ if (!buf)
+ return Error("GPR buffer is NULL");
+ size_t buf_size = GetGPRSize();
+
+ return DoWriteGPR(buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::ReadFPR()
+{
+ void* buf = GetFPRBuffer();
+ if (!buf)
+ return Error("FPR buffer is NULL");
+ size_t buf_size = GetFPRSize();
+
+ return DoReadFPR(buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::WriteFPR()
+{
+ void* buf = GetFPRBuffer();
+ if (!buf)
+ return Error("FPR buffer is NULL");
+ size_t buf_size = GetFPRSize();
+
+ return DoWriteFPR(buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::ReadRegisterSet(void *buf, size_t buf_size, unsigned int regset)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(),
+ static_cast<void *>(&regset), buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::WriteRegisterSet(void *buf, size_t buf_size, unsigned int regset)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
+ static_cast<void *>(&regset), buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::DoReadRegisterValue(uint32_t offset,
+ const char* reg_name,
+ uint32_t size,
+ RegisterValue &value)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS));
+
+ long data;
+ Error error = NativeProcessLinux::PtraceWrapper(
+ PTRACE_PEEKUSER, m_thread.GetID(), reinterpret_cast<void *>(offset), nullptr, 0, &data);
+
+ if (error.Success())
+ // First cast to an unsigned of the same size to avoid sign extension.
+ value.SetUInt64(static_cast<unsigned long>(data));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux::%s() reg %s: 0x%lx", __FUNCTION__, reg_name, data);
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux::DoWriteRegisterValue(uint32_t offset,
+ const char* reg_name,
+ const RegisterValue &value)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS));
+
+ void* buf = reinterpret_cast<void *>(value.GetAsUInt64());
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux::%s() reg %s: %p", __FUNCTION__, reg_name, buf);
+
+ return NativeProcessLinux::PtraceWrapper(
+ PTRACE_POKEUSER, m_thread.GetID(), reinterpret_cast<void *>(offset), buf);
+}
+
+Error
+NativeRegisterContextLinux::DoReadGPR(void *buf, size_t buf_size)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), nullptr, buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::DoWriteGPR(void *buf, size_t buf_size)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), nullptr, buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::DoReadFPR(void *buf, size_t buf_size)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_GETFPREGS, m_thread.GetID(), nullptr, buf, buf_size);
+}
+
+Error
+NativeRegisterContextLinux::DoWriteFPR(void *buf, size_t buf_size)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETFPREGS, m_thread.GetID(), nullptr, buf, buf_size);
+}
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
new file mode 100644
index 0000000000000..0b0b747984b4d
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
@@ -0,0 +1,107 @@
+//===-- NativeRegisterContextLinux.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_NativeRegisterContextLinux_h
+#define lldb_NativeRegisterContextLinux_h
+
+#include "lldb/Host/common/NativeRegisterContextRegisterInfo.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "Plugins/Process/Linux/NativeProcessLinux.h"
+
+namespace lldb_private {
+namespace process_linux {
+
+class NativeRegisterContextLinux : public NativeRegisterContextRegisterInfo
+{
+public:
+ NativeRegisterContextLinux(NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx,
+ RegisterInfoInterface *reg_info_interface_p);
+
+ // This function is implemented in the NativeRegisterContextLinux_* subclasses to create a new
+ // instance of the host specific NativeRegisterContextLinux. The implementations can't collide
+ // as only one NativeRegisterContextLinux_* variant should be compiled into the final
+ // executable.
+ static NativeRegisterContextLinux*
+ CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx);
+
+protected:
+ lldb::ByteOrder
+ GetByteOrder() const;
+
+ virtual Error
+ ReadRegisterRaw(uint32_t reg_index, RegisterValue& reg_value);
+
+ virtual Error
+ WriteRegisterRaw(uint32_t reg_index, const RegisterValue& reg_value);
+
+ virtual Error
+ ReadRegisterSet(void *buf, size_t buf_size, unsigned int regset);
+
+ virtual Error
+ WriteRegisterSet(void *buf, size_t buf_size, unsigned int regset);
+
+ virtual Error
+ ReadGPR();
+
+ virtual Error
+ WriteGPR();
+
+ virtual Error
+ ReadFPR();
+
+ virtual Error
+ WriteFPR();
+
+ virtual void*
+ GetGPRBuffer() { return nullptr; }
+
+ virtual size_t
+ GetGPRSize() { return GetRegisterInfoInterface().GetGPRSize(); }
+
+ virtual void*
+ GetFPRBuffer() { return nullptr; }
+
+ virtual size_t
+ GetFPRSize() { return 0; }
+
+
+ // The Do*** functions are executed on the privileged thread and can perform ptrace
+ // operations directly.
+ virtual Error
+ DoReadRegisterValue(uint32_t offset,
+ const char* reg_name,
+ uint32_t size,
+ RegisterValue &value);
+
+ virtual Error
+ DoWriteRegisterValue(uint32_t offset,
+ const char* reg_name,
+ const RegisterValue &value);
+
+ virtual Error
+ DoReadGPR(void *buf, size_t buf_size);
+
+ virtual Error
+ DoWriteGPR(void *buf, size_t buf_size);
+
+ virtual Error
+ DoReadFPR(void *buf, size_t buf_size);
+
+ virtual Error
+ DoWriteFPR(void *buf, size_t buf_size);
+};
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextLinux_h
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
new file mode 100644
index 0000000000000..31752fed5b42a
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
@@ -0,0 +1,1017 @@
+//===-- NativeRegisterContextLinux_arm.cpp --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__arm__)
+
+#include "NativeRegisterContextLinux_arm.h"
+
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/RegisterValue.h"
+
+#include "Plugins/Process/Utility/RegisterContextLinux_arm.h"
+
+#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof (m_fpr))
+
+#ifndef PTRACE_GETVFPREGS
+ #define PTRACE_GETVFPREGS 27
+ #define PTRACE_SETVFPREGS 28
+#endif
+#ifndef PTRACE_GETHBPREGS
+ #define PTRACE_GETHBPREGS 29
+ #define PTRACE_SETHBPREGS 30
+#endif
+#if !defined(PTRACE_TYPE_ARG3)
+ #define PTRACE_TYPE_ARG3 void *
+#endif
+#if !defined(PTRACE_TYPE_ARG4)
+ #define PTRACE_TYPE_ARG4 void *
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+
+// arm general purpose registers.
+static const uint32_t g_gpr_regnums_arm[] =
+{
+ gpr_r0_arm,
+ gpr_r1_arm,
+ gpr_r2_arm,
+ gpr_r3_arm,
+ gpr_r4_arm,
+ gpr_r5_arm,
+ gpr_r6_arm,
+ gpr_r7_arm,
+ gpr_r8_arm,
+ gpr_r9_arm,
+ gpr_r10_arm,
+ gpr_r11_arm,
+ gpr_r12_arm,
+ gpr_sp_arm,
+ gpr_lr_arm,
+ gpr_pc_arm,
+ gpr_cpsr_arm,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) == k_num_gpr_registers_arm, \
+ "g_gpr_regnums_arm has wrong number of register infos");
+
+// arm floating point registers.
+static const uint32_t g_fpu_regnums_arm[] =
+{
+ fpu_s0_arm,
+ fpu_s1_arm,
+ fpu_s2_arm,
+ fpu_s3_arm,
+ fpu_s4_arm,
+ fpu_s5_arm,
+ fpu_s6_arm,
+ fpu_s7_arm,
+ fpu_s8_arm,
+ fpu_s9_arm,
+ fpu_s10_arm,
+ fpu_s11_arm,
+ fpu_s12_arm,
+ fpu_s13_arm,
+ fpu_s14_arm,
+ fpu_s15_arm,
+ fpu_s16_arm,
+ fpu_s17_arm,
+ fpu_s18_arm,
+ fpu_s19_arm,
+ fpu_s20_arm,
+ fpu_s21_arm,
+ fpu_s22_arm,
+ fpu_s23_arm,
+ fpu_s24_arm,
+ fpu_s25_arm,
+ fpu_s26_arm,
+ fpu_s27_arm,
+ fpu_s28_arm,
+ fpu_s29_arm,
+ fpu_s30_arm,
+ fpu_s31_arm,
+ fpu_fpscr_arm,
+ fpu_d0_arm,
+ fpu_d1_arm,
+ fpu_d2_arm,
+ fpu_d3_arm,
+ fpu_d4_arm,
+ fpu_d5_arm,
+ fpu_d6_arm,
+ fpu_d7_arm,
+ fpu_d8_arm,
+ fpu_d9_arm,
+ fpu_d10_arm,
+ fpu_d11_arm,
+ fpu_d12_arm,
+ fpu_d13_arm,
+ fpu_d14_arm,
+ fpu_d15_arm,
+ fpu_d16_arm,
+ fpu_d17_arm,
+ fpu_d18_arm,
+ fpu_d19_arm,
+ fpu_d20_arm,
+ fpu_d21_arm,
+ fpu_d22_arm,
+ fpu_d23_arm,
+ fpu_d24_arm,
+ fpu_d25_arm,
+ fpu_d26_arm,
+ fpu_d27_arm,
+ fpu_d28_arm,
+ fpu_d29_arm,
+ fpu_d30_arm,
+ fpu_d31_arm,
+ fpu_q0_arm,
+ fpu_q1_arm,
+ fpu_q2_arm,
+ fpu_q3_arm,
+ fpu_q4_arm,
+ fpu_q5_arm,
+ fpu_q6_arm,
+ fpu_q7_arm,
+ fpu_q8_arm,
+ fpu_q9_arm,
+ fpu_q10_arm,
+ fpu_q11_arm,
+ fpu_q12_arm,
+ fpu_q13_arm,
+ fpu_q14_arm,
+ fpu_q15_arm,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_fpu_regnums_arm / sizeof g_fpu_regnums_arm[0]) - 1) == k_num_fpr_registers_arm, \
+ "g_fpu_regnums_arm has wrong number of register infos");
+
+namespace {
+ // Number of register sets provided by this context.
+ enum
+ {
+ k_num_register_sets = 2
+ };
+}
+
+// Register sets for arm.
+static const RegisterSet
+g_reg_sets_arm[k_num_register_sets] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers_arm, g_gpr_regnums_arm },
+ { "Floating Point Registers", "fpu", k_num_fpr_registers_arm, g_fpu_regnums_arm }
+};
+
+NativeRegisterContextLinux*
+NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx)
+{
+ return new NativeRegisterContextLinux_arm(target_arch, native_thread, concrete_frame_idx);
+}
+
+NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm (const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx) :
+ NativeRegisterContextLinux (native_thread, concrete_frame_idx, new RegisterContextLinux_arm(target_arch))
+{
+ switch (target_arch.GetMachine())
+ {
+ case llvm::Triple::arm:
+ m_reg_info.num_registers = k_num_registers_arm;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_arm;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_arm;
+ m_reg_info.last_gpr = k_last_gpr_arm;
+ m_reg_info.first_fpr = k_first_fpr_arm;
+ m_reg_info.last_fpr = k_last_fpr_arm;
+ m_reg_info.first_fpr_v = fpu_s0_arm;
+ m_reg_info.last_fpr_v = fpu_s31_arm;
+ m_reg_info.gpr_flags = gpr_cpsr_arm;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ ::memset(&m_fpr, 0, sizeof (m_fpr));
+ ::memset(&m_gpr_arm, 0, sizeof (m_gpr_arm));
+ ::memset(&m_hwp_regs, 0, sizeof (m_hwp_regs));
+
+ // 16 is just a maximum value, query hardware for actual watchpoint count
+ m_max_hwp_supported = 16;
+ m_max_hbp_supported = 16;
+ m_refresh_hwdebug_info = true;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::GetRegisterSetCount () const
+{
+ return k_num_register_sets;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::GetUserRegisterCount() const
+{
+ uint32_t count = 0;
+ for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index)
+ count += g_reg_sets_arm[set_index].num_registers;
+ return count;
+}
+
+const RegisterSet *
+NativeRegisterContextLinux_arm::GetRegisterSet (uint32_t set_index) const
+{
+ if (set_index < k_num_register_sets)
+ return &g_reg_sets_arm[set_index];
+
+ return nullptr;
+}
+
+Error
+NativeRegisterContextLinux_arm::ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value)
+{
+ Error error;
+
+ if (!reg_info)
+ {
+ error.SetErrorString ("reg_info NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (IsFPR(reg))
+ {
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+ }
+ else
+ {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg)
+ {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+
+ error = ReadRegisterRaw(full_reg, reg_value);
+
+ if (error.Success ())
+ {
+ // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right.
+ if (is_subreg && (reg_info->byte_offset & 0x1))
+ reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8);
+
+ // If our return byte size was greater than the return value reg size, then
+ // use the type specified by reg_info rather than the uint64_t default
+ if (reg_value.GetByteSize() > reg_info->byte_size)
+ reg_value.SetType(reg_info);
+ }
+ return error;
+ }
+
+ // Get pointer to m_fpr variable and set the data from it.
+ uint32_t fpr_offset = CalculateFprOffset(reg_info);
+ assert (fpr_offset < sizeof m_fpr);
+ uint8_t *src = (uint8_t *)&m_fpr + fpr_offset;
+ switch (reg_info->byte_size)
+ {
+ case 2:
+ reg_value.SetUInt16(*(uint16_t *)src);
+ break;
+ case 4:
+ reg_value.SetUInt32(*(uint32_t *)src);
+ break;
+ case 8:
+ reg_value.SetUInt64(*(uint64_t *)src);
+ break;
+ case 16:
+ reg_value.SetBytes(src, 16, GetByteOrder());
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size);
+ break;
+ }
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_arm::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value)
+{
+ if (!reg_info)
+ return Error ("reg_info NULL");
+
+ const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg_index == LLDB_INVALID_REGNUM)
+ return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>");
+
+ if (IsGPR(reg_index))
+ return WriteRegisterRaw(reg_index, reg_value);
+
+ if (IsFPR(reg_index))
+ {
+ // Get pointer to m_fpr variable and set the data to it.
+ uint32_t fpr_offset = CalculateFprOffset(reg_info);
+ assert (fpr_offset < sizeof m_fpr);
+ uint8_t *dst = (uint8_t *)&m_fpr + fpr_offset;
+ switch (reg_info->byte_size)
+ {
+ case 2:
+ *(uint16_t *)dst = reg_value.GetAsUInt16();
+ break;
+ case 4:
+ *(uint32_t *)dst = reg_value.GetAsUInt32();
+ break;
+ case 8:
+ *(uint64_t *)dst = reg_value.GetAsUInt64();
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ return Error ("unhandled register data size %" PRIu32, reg_info->byte_size);
+ }
+
+ Error error = WriteFPR();
+ if (error.Fail())
+ return error;
+
+ return Error ();
+ }
+
+ return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown");
+}
+
+Error
+NativeRegisterContextLinux_arm::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ Error error;
+
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (!data_sp)
+ return Error ("failed to allocate DataBufferHeap instance of size %" PRIu64, (uint64_t)REG_CONTEXT_SIZE);
+
+ error = ReadGPR();
+ if (error.Fail())
+ return error;
+
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+
+ uint8_t *dst = data_sp->GetBytes ();
+ if (dst == nullptr)
+ {
+ error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", (uint64_t)REG_CONTEXT_SIZE);
+ return error;
+ }
+
+ ::memcpy (dst, &m_gpr_arm, GetGPRSize());
+ dst += GetGPRSize();
+ ::memcpy (dst, &m_fpr, sizeof(m_fpr));
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_arm::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ Error error;
+
+ if (!data_sp)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__);
+ return error;
+ }
+
+ if (data_sp->GetByteSize () != REG_CONTEXT_SIZE)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, (uint64_t)REG_CONTEXT_SIZE, data_sp->GetByteSize ());
+ return error;
+ }
+
+
+ uint8_t *src = data_sp->GetBytes ();
+ if (src == nullptr)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__);
+ return error;
+ }
+ ::memcpy (&m_gpr_arm, src, GetRegisterInfoInterface ().GetGPRSize ());
+
+ error = WriteGPR();
+ if (error.Fail())
+ return error;
+
+ src += GetRegisterInfoInterface ().GetGPRSize ();
+ ::memcpy (&m_fpr, src, sizeof(m_fpr));
+
+ error = WriteFPR();
+ if (error.Fail())
+ return error;
+
+ return error;
+}
+
+bool
+NativeRegisterContextLinux_arm::IsGPR(unsigned reg) const
+{
+ return reg <= m_reg_info.last_gpr; // GPR's come first.
+}
+
+bool
+NativeRegisterContextLinux_arm::IsFPR(unsigned reg) const
+{
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return LLDB_INVALID_INDEX32;
+
+ uint32_t control_value = 0, bp_index = 0;
+
+ // Check if size has a valid hardware breakpoint length.
+ // Thumb instructions are 2-bytes but we have no way here to determine
+ // if target address is a thumb or arm instruction.
+ // TODO: Add support for setting thumb mode hardware breakpoints
+ if (size != 4 && size != 2)
+ return LLDB_INVALID_INDEX32;
+
+ // Setup control value
+ // Make the byte_mask into a valid Byte Address Select mask
+ control_value = 0xfu << 5;
+
+ // Enable this breakpoint and make it stop in privileged or user mode;
+ control_value |= 7;
+
+ // Make sure bits 1:0 are clear in our address
+ // This should be different once we support thumb here.
+ addr &= ~((lldb::addr_t)3);
+
+ // Iterate over stored hardware breakpoints
+ // Find a free bp_index or update reference count if duplicate.
+ bp_index = LLDB_INVALID_INDEX32;
+
+ for (uint32_t i = 0; i < m_max_hbp_supported; i++)
+ {
+ if ((m_hbr_regs[i].control & 1) == 0)
+ {
+ bp_index = i; // Mark last free slot
+ }
+ else if (m_hbr_regs[i].address == addr && m_hbr_regs[i].control == control_value)
+ {
+ bp_index = i; // Mark duplicate index
+ break; // Stop searching here
+ }
+ }
+
+ if (bp_index == LLDB_INVALID_INDEX32)
+ return LLDB_INVALID_INDEX32;
+
+ // Add new or update existing breakpoint
+ if ((m_hbr_regs[bp_index].control & 1) == 0)
+ {
+ m_hbr_regs[bp_index].address = addr;
+ m_hbr_regs[bp_index].control = control_value;
+ m_hbr_regs[bp_index].refcount = 1;
+
+ // PTRACE call to set corresponding hardware breakpoint register.
+ error = WriteHardwareDebugRegs(eDREGTypeBREAK, bp_index);
+
+ if (error.Fail())
+ {
+ m_hbr_regs[bp_index].address = 0;
+ m_hbr_regs[bp_index].control &= ~1;
+ m_hbr_regs[bp_index].refcount = 0;
+
+ return LLDB_INVALID_INDEX32;
+ }
+ }
+ else
+ m_hbr_regs[bp_index].refcount++;
+
+ return bp_index;
+}
+
+bool
+NativeRegisterContextLinux_arm::ClearHardwareBreakpoint (uint32_t hw_idx)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return false;
+
+ if (hw_idx >= m_max_hbp_supported)
+ return false;
+
+ // Update reference count if multiple references.
+ if (m_hbr_regs[hw_idx].refcount > 1)
+ {
+ m_hbr_regs[hw_idx].refcount--;
+ return true;
+ }
+ else if (m_hbr_regs[hw_idx].refcount == 1)
+ {
+ // Create a backup we can revert to in case of failure.
+ lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address;
+ uint32_t tempControl = m_hbr_regs[hw_idx].control;
+ uint32_t tempRefCount = m_hbr_regs[hw_idx].refcount;
+
+ m_hbr_regs[hw_idx].control &= ~1;
+ m_hbr_regs[hw_idx].address = 0;
+ m_hbr_regs[hw_idx].refcount = 0;
+
+ // PTRACE call to clear corresponding hardware breakpoint register.
+ WriteHardwareDebugRegs(eDREGTypeBREAK, hw_idx);
+
+ if (error.Fail())
+ {
+ m_hbr_regs[hw_idx].control = tempControl;
+ m_hbr_regs[hw_idx].address = tempAddr;
+ m_hbr_regs[hw_idx].refcount = tempRefCount;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return LLDB_INVALID_INDEX32;
+
+ return m_max_hwp_supported;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return LLDB_INVALID_INDEX32;
+
+ uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0;
+
+ // Check if we are setting watchpoint other than read/write/access
+ // Also update watchpoint flag to match Arm write-read bit configuration.
+ switch (watch_flags)
+ {
+ case 1:
+ watch_flags = 2;
+ break;
+ case 2:
+ watch_flags = 1;
+ break;
+ case 3:
+ break;
+ default:
+ return LLDB_INVALID_INDEX32;
+ }
+
+ // Can't watch zero bytes
+ // Can't watch more than 4 bytes per WVR/WCR pair
+
+ if (size == 0 || size > 4)
+ return LLDB_INVALID_INDEX32;
+
+ // We can only watch up to four bytes that follow a 4 byte aligned address
+ // per watchpoint register pair, so make sure we can properly encode this.
+ addr_word_offset = addr % 4;
+ byte_mask = ((1u << size) - 1u) << addr_word_offset;
+
+ // Check if we need multiple watchpoint register
+ if (byte_mask > 0xfu)
+ return LLDB_INVALID_INDEX32;
+
+ // Setup control value
+ // Make the byte_mask into a valid Byte Address Select mask
+ control_value = byte_mask << 5;
+
+ //Turn on appropriate watchpoint flags read or write
+ control_value |= (watch_flags << 3);
+
+ // Enable this watchpoint and make it stop in privileged or user mode;
+ control_value |= 7;
+
+ // Make sure bits 1:0 are clear in our address
+ addr &= ~((lldb::addr_t)3);
+
+ // Iterate over stored watchpoints
+ // Find a free wp_index or update reference count if duplicate.
+ wp_index = LLDB_INVALID_INDEX32;
+ for (uint32_t i = 0; i < m_max_hwp_supported; i++)
+ {
+ if ((m_hwp_regs[i].control & 1) == 0)
+ {
+ wp_index = i; // Mark last free slot
+ }
+ else if (m_hwp_regs[i].address == addr && m_hwp_regs[i].control == control_value)
+ {
+ wp_index = i; // Mark duplicate index
+ break; // Stop searching here
+ }
+ }
+
+ if (wp_index == LLDB_INVALID_INDEX32)
+ return LLDB_INVALID_INDEX32;
+
+ // Add new or update existing watchpoint
+ if ((m_hwp_regs[wp_index].control & 1) == 0)
+ {
+ // Update watchpoint in local cache
+ m_hwp_regs[wp_index].address = addr;
+ m_hwp_regs[wp_index].control = control_value;
+ m_hwp_regs[wp_index].refcount = 1;
+
+ // PTRACE call to set corresponding watchpoint register.
+ error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index);
+
+ if (error.Fail())
+ {
+ m_hwp_regs[wp_index].address = 0;
+ m_hwp_regs[wp_index].control &= ~1;
+ m_hwp_regs[wp_index].refcount = 0;
+
+ return LLDB_INVALID_INDEX32;
+ }
+ }
+ else
+ m_hwp_regs[wp_index].refcount++;
+
+ return wp_index;
+}
+
+bool
+NativeRegisterContextLinux_arm::ClearHardwareWatchpoint (uint32_t wp_index)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return false;
+
+ if (wp_index >= m_max_hwp_supported)
+ return false;
+
+ // Update reference count if multiple references.
+ if (m_hwp_regs[wp_index].refcount > 1)
+ {
+ m_hwp_regs[wp_index].refcount--;
+ return true;
+ }
+ else if (m_hwp_regs[wp_index].refcount == 1)
+ {
+ // Create a backup we can revert to in case of failure.
+ lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
+ uint32_t tempControl = m_hwp_regs[wp_index].control;
+ uint32_t tempRefCount = m_hwp_regs[wp_index].refcount;
+
+ // Update watchpoint in local cache
+ m_hwp_regs[wp_index].control &= ~1;
+ m_hwp_regs[wp_index].address = 0;
+ m_hwp_regs[wp_index].refcount = 0;
+
+ // Ptrace call to update hardware debug registers
+ error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index);
+
+ if (error.Fail())
+ {
+ m_hwp_regs[wp_index].control = tempControl;
+ m_hwp_regs[wp_index].address = tempAddr;
+ m_hwp_regs[wp_index].refcount = tempRefCount;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+Error
+NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return error;
+
+ lldb::addr_t tempAddr = 0;
+ uint32_t tempControl = 0, tempRefCount = 0;
+
+ for (uint32_t i = 0; i < m_max_hwp_supported; i++)
+ {
+ if (m_hwp_regs[i].control & 0x01)
+ {
+ // Create a backup we can revert to in case of failure.
+ tempAddr = m_hwp_regs[i].address;
+ tempControl = m_hwp_regs[i].control;
+ tempRefCount = m_hwp_regs[i].refcount;
+
+ // Clear watchpoints in local cache
+ m_hwp_regs[i].control &= ~1;
+ m_hwp_regs[i].address = 0;
+ m_hwp_regs[i].refcount = 0;
+
+ // Ptrace call to update hardware debug registers
+ error = WriteHardwareDebugRegs(eDREGTypeWATCH, i);
+
+ if (error.Fail())
+ {
+ m_hwp_regs[i].control = tempControl;
+ m_hwp_regs[i].address = tempAddr;
+ m_hwp_regs[i].refcount = tempRefCount;
+
+ return error;
+ }
+ }
+ }
+
+ return Error();
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f)
+ {
+ case 0x01:
+ return 1;
+ case 0x03:
+ return 2;
+ case 0x07:
+ return 3;
+ case 0x0f:
+ return 4;
+ default:
+ return 0;
+ }
+}
+bool
+NativeRegisterContextLinux_arm::WatchpointIsEnabled(uint32_t wp_index)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ if ((m_hwp_regs[wp_index].control & 0x1) == 0x1)
+ return true;
+ else
+ return false;
+}
+
+Error
+NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ uint32_t watch_size;
+ lldb::addr_t watch_addr;
+
+ for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index)
+ {
+ watch_size = GetWatchpointSize (wp_index);
+ watch_addr = m_hwp_regs[wp_index].address;
+
+ if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index)
+ && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size)
+ {
+ return Error();
+ }
+ }
+
+ wp_index = LLDB_INVALID_INDEX32;
+ return Error();
+}
+
+lldb::addr_t
+NativeRegisterContextLinux_arm::GetWatchpointAddress (uint32_t wp_index)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+ if (wp_index >= m_max_hwp_supported)
+ return LLDB_INVALID_ADDRESS;
+
+ if (WatchpointIsEnabled(wp_index))
+ return m_hwp_regs[wp_index].address;
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+Error
+NativeRegisterContextLinux_arm::ReadHardwareDebugInfo()
+{
+ Error error;
+
+ if (!m_refresh_hwdebug_info)
+ {
+ return Error();
+ }
+
+ unsigned int cap_val;
+
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(), nullptr, &cap_val, sizeof(unsigned int));
+
+ if (error.Fail())
+ return error;
+
+ m_max_hwp_supported = (cap_val >> 8) & 0xff;
+ m_max_hbp_supported = cap_val & 0xff;
+ m_refresh_hwdebug_info = false;
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType, int hwb_index)
+{
+ Error error;
+
+ lldb::addr_t *addr_buf;
+ uint32_t *ctrl_buf;
+
+ if (hwbType == eDREGTypeWATCH)
+ {
+ addr_buf = &m_hwp_regs[hwb_index].address;
+ ctrl_buf = &m_hwp_regs[hwb_index].control;
+
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
+ m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 1),
+ addr_buf, sizeof(unsigned int));
+
+ if (error.Fail())
+ return error;
+
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
+ m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 2),
+ ctrl_buf, sizeof(unsigned int));
+ }
+ else
+ {
+ addr_buf = &m_hwp_regs[hwb_index].address;
+ ctrl_buf = &m_hwp_regs[hwb_index].control;
+
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
+ m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 1),
+ addr_buf, sizeof(unsigned int));
+
+ if (error.Fail())
+ return error;
+
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
+ m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 2),
+ ctrl_buf, sizeof(unsigned int));
+
+ }
+
+ return error;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::CalculateFprOffset(const RegisterInfo* reg_info) const
+{
+ return reg_info->byte_offset - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset;
+}
+
+Error
+NativeRegisterContextLinux_arm::DoWriteRegisterValue(uint32_t offset,
+ const char* reg_name,
+ const RegisterValue &value)
+{
+ // PTRACE_POKEUSER don't work in the aarch64 liux kernel used on android devices (always return
+ // "Bad address"). To avoid using PTRACE_POKEUSER we read out the full GPR register set, modify
+ // the requested register and write it back. This approach is about 4 times slower but the
+ // performance overhead is negligible in comparision to processing time in lldb-server.
+ assert(offset % 4 == 0 && "Try to write a register with unaligned offset");
+ if (offset + sizeof(uint32_t) > sizeof(m_gpr_arm))
+ return Error("Register isn't fit into the size of the GPR area");
+
+ Error error = DoReadGPR(m_gpr_arm, sizeof(m_gpr_arm));
+ if (error.Fail())
+ return error;
+
+ uint32_t reg_value = value.GetAsUInt32();
+ // As precaution for an undefined behavior encountered while setting PC we
+ // will clear thumb bit of new PC if we are already in thumb mode; that is
+ // CPSR thumb mode bit is set.
+ if (offset / sizeof(uint32_t) == gpr_pc_arm)
+ {
+ // Check if we are already in thumb mode and
+ // thumb bit of current PC is read out to be zero and
+ // thumb bit of next PC is read out to be one.
+ if ((m_gpr_arm[gpr_cpsr_arm] & 0x20) &&
+ !(m_gpr_arm[gpr_pc_arm] & 0x01) &&
+ (value.GetAsUInt32() & 0x01))
+ {
+ reg_value &= (~1ull);
+ }
+ }
+
+ m_gpr_arm[offset / sizeof(uint32_t)] = reg_value;
+ return DoWriteGPR(m_gpr_arm, sizeof(m_gpr_arm));
+}
+
+Error
+NativeRegisterContextLinux_arm::DoReadFPR(void *buf, size_t buf_size)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_GETVFPREGS,
+ m_thread.GetID(),
+ nullptr,
+ buf,
+ buf_size);
+}
+
+Error
+NativeRegisterContextLinux_arm::DoWriteFPR(void *buf, size_t buf_size)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETVFPREGS,
+ m_thread.GetID(),
+ nullptr,
+ buf,
+ buf_size);
+}
+
+#endif // defined(__arm__)
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
new file mode 100644
index 0000000000000..611b36ad4db14
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
@@ -0,0 +1,185 @@
+//===-- NativeRegisterContextLinux_arm.h ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__arm__) // arm register context only needed on arm devices
+
+#ifndef lldb_NativeRegisterContextLinux_arm_h
+#define lldb_NativeRegisterContextLinux_arm_h
+
+#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
+#include "Plugins/Process/Utility/lldb-arm-register-enums.h"
+
+namespace lldb_private {
+namespace process_linux {
+
+ class NativeProcessLinux;
+
+ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux
+ {
+ public:
+ NativeRegisterContextLinux_arm (const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx);
+
+ uint32_t
+ GetRegisterSetCount () const override;
+
+ const RegisterSet *
+ GetRegisterSet (uint32_t set_index) const override;
+
+ uint32_t
+ GetUserRegisterCount() const override;
+
+ Error
+ ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value) override;
+
+ Error
+ WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value) override;
+
+ Error
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override;
+
+ Error
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override;
+
+ //------------------------------------------------------------------
+ // Hardware breakpoints/watchpoint mangement functions
+ //------------------------------------------------------------------
+
+ uint32_t
+ SetHardwareBreakpoint (lldb::addr_t addr, size_t size) override;
+
+ bool
+ ClearHardwareBreakpoint (uint32_t hw_idx) override;
+
+ uint32_t
+ NumSupportedHardwareWatchpoints () override;
+
+ uint32_t
+ SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) override;
+
+ bool
+ ClearHardwareWatchpoint (uint32_t hw_index) override;
+
+ Error
+ ClearAllHardwareWatchpoints () override;
+
+ Error
+ GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override;
+
+ lldb::addr_t
+ GetWatchpointAddress (uint32_t wp_index) override;
+
+ uint32_t
+ GetWatchpointSize(uint32_t wp_index);
+
+ bool
+ WatchpointIsEnabled(uint32_t wp_index);
+
+ // Debug register type select
+ enum DREGType
+ {
+ eDREGTypeWATCH = 0,
+ eDREGTypeBREAK
+ };
+
+ protected:
+ Error
+ DoWriteRegisterValue(uint32_t offset,
+ const char* reg_name,
+ const RegisterValue &value) override;
+
+ Error
+ DoReadFPR(void *buf, size_t buf_size) override;
+
+ Error
+ DoWriteFPR(void *buf, size_t buf_size) override;
+
+ void*
+ GetGPRBuffer() override { return &m_gpr_arm; }
+
+ void*
+ GetFPRBuffer() override { return &m_fpr; }
+
+ size_t
+ GetFPRSize() override { return sizeof(m_fpr); }
+
+ private:
+ struct RegInfo
+ {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_fpr_v;
+ uint32_t last_fpr_v;
+
+ uint32_t gpr_flags;
+ };
+
+ struct QReg
+ {
+ uint8_t bytes[16];
+ };
+
+ struct FPU
+ {
+ union {
+ uint32_t s[32];
+ uint64_t d[32];
+ QReg q[16]; // the 128-bit NEON registers
+ } floats;
+ uint32_t fpscr;
+ };
+
+ uint32_t m_gpr_arm[k_num_gpr_registers_arm];
+ RegInfo m_reg_info;
+ FPU m_fpr;
+
+ // Debug register info for hardware breakpoints and watchpoints management.
+ struct DREG
+ {
+ lldb::addr_t address; // Breakpoint/watchpoint address value.
+ uint32_t control; // Breakpoint/watchpoint control value.
+ uint32_t refcount; // Serves as enable/disable and refernce counter.
+ };
+
+ struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints
+ struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints
+
+ uint32_t m_max_hwp_supported;
+ uint32_t m_max_hbp_supported;
+ bool m_refresh_hwdebug_info;
+
+ bool
+ IsGPR(unsigned reg) const;
+
+ bool
+ IsFPR(unsigned reg) const;
+
+ Error
+ ReadHardwareDebugInfo();
+
+ Error
+ WriteHardwareDebugRegs(int hwbType, int hwb_index);
+
+ uint32_t
+ CalculateFprOffset(const RegisterInfo* reg_info) const;
+ };
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextLinux_arm_h
+
+#endif // defined(__arm__)
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
new file mode 100644
index 0000000000000..e4d26fc640f30
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -0,0 +1,1042 @@
+//===-- NativeRegisterContextLinux_arm64.cpp --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__arm64__) || defined (__aarch64__)
+
+#include "NativeRegisterContextLinux_arm64.h"
+
+// C Includes
+// C++ Includes
+
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+
+#include "Plugins/Process/Linux/NativeProcessLinux.h"
+#include "Plugins/Process/Linux/Procfs.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_arm64.h"
+
+// System includes - They have to be included after framework includes because they define some
+// macros which collide with variable names in other modules
+#include <sys/socket.h>
+// NT_PRSTATUS and NT_FPREGSET definition
+#include <elf.h>
+
+#define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize())
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+
+// ARM64 general purpose registers.
+static const uint32_t g_gpr_regnums_arm64[] =
+{
+ gpr_x0_arm64,
+ gpr_x1_arm64,
+ gpr_x2_arm64,
+ gpr_x3_arm64,
+ gpr_x4_arm64,
+ gpr_x5_arm64,
+ gpr_x6_arm64,
+ gpr_x7_arm64,
+ gpr_x8_arm64,
+ gpr_x9_arm64,
+ gpr_x10_arm64,
+ gpr_x11_arm64,
+ gpr_x12_arm64,
+ gpr_x13_arm64,
+ gpr_x14_arm64,
+ gpr_x15_arm64,
+ gpr_x16_arm64,
+ gpr_x17_arm64,
+ gpr_x18_arm64,
+ gpr_x19_arm64,
+ gpr_x20_arm64,
+ gpr_x21_arm64,
+ gpr_x22_arm64,
+ gpr_x23_arm64,
+ gpr_x24_arm64,
+ gpr_x25_arm64,
+ gpr_x26_arm64,
+ gpr_x27_arm64,
+ gpr_x28_arm64,
+ gpr_fp_arm64,
+ gpr_lr_arm64,
+ gpr_sp_arm64,
+ gpr_pc_arm64,
+ gpr_cpsr_arm64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) - 1) == k_num_gpr_registers_arm64, \
+ "g_gpr_regnums_arm64 has wrong number of register infos");
+
+// ARM64 floating point registers.
+static const uint32_t g_fpu_regnums_arm64[] =
+{
+ fpu_v0_arm64,
+ fpu_v1_arm64,
+ fpu_v2_arm64,
+ fpu_v3_arm64,
+ fpu_v4_arm64,
+ fpu_v5_arm64,
+ fpu_v6_arm64,
+ fpu_v7_arm64,
+ fpu_v8_arm64,
+ fpu_v9_arm64,
+ fpu_v10_arm64,
+ fpu_v11_arm64,
+ fpu_v12_arm64,
+ fpu_v13_arm64,
+ fpu_v14_arm64,
+ fpu_v15_arm64,
+ fpu_v16_arm64,
+ fpu_v17_arm64,
+ fpu_v18_arm64,
+ fpu_v19_arm64,
+ fpu_v20_arm64,
+ fpu_v21_arm64,
+ fpu_v22_arm64,
+ fpu_v23_arm64,
+ fpu_v24_arm64,
+ fpu_v25_arm64,
+ fpu_v26_arm64,
+ fpu_v27_arm64,
+ fpu_v28_arm64,
+ fpu_v29_arm64,
+ fpu_v30_arm64,
+ fpu_v31_arm64,
+ fpu_fpsr_arm64,
+ fpu_fpcr_arm64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_fpu_regnums_arm64 / sizeof g_fpu_regnums_arm64[0]) - 1) == k_num_fpr_registers_arm64, \
+ "g_fpu_regnums_arm64 has wrong number of register infos");
+
+namespace {
+ // Number of register sets provided by this context.
+ enum
+ {
+ k_num_register_sets = 2
+ };
+}
+
+// Register sets for ARM64.
+static const RegisterSet
+g_reg_sets_arm64[k_num_register_sets] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers_arm64, g_gpr_regnums_arm64 },
+ { "Floating Point Registers", "fpu", k_num_fpr_registers_arm64, g_fpu_regnums_arm64 }
+};
+
+NativeRegisterContextLinux*
+NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx)
+{
+ return new NativeRegisterContextLinux_arm64(target_arch, native_thread, concrete_frame_idx);
+}
+
+NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64 (const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx) :
+ NativeRegisterContextLinux (native_thread, concrete_frame_idx, new RegisterContextLinux_arm64(target_arch))
+{
+ switch (target_arch.GetMachine())
+ {
+ case llvm::Triple::aarch64:
+ m_reg_info.num_registers = k_num_registers_arm64;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64;
+ m_reg_info.last_gpr = k_last_gpr_arm64;
+ m_reg_info.first_fpr = k_first_fpr_arm64;
+ m_reg_info.last_fpr = k_last_fpr_arm64;
+ m_reg_info.first_fpr_v = fpu_v0_arm64;
+ m_reg_info.last_fpr_v = fpu_v31_arm64;
+ m_reg_info.gpr_flags = gpr_cpsr_arm64;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ ::memset(&m_fpr, 0, sizeof (m_fpr));
+ ::memset(&m_gpr_arm64, 0, sizeof (m_gpr_arm64));
+ ::memset(&m_hwp_regs, 0, sizeof (m_hwp_regs));
+
+ // 16 is just a maximum value, query hardware for actual watchpoint count
+ m_max_hwp_supported = 16;
+ m_max_hbp_supported = 16;
+ m_refresh_hwdebug_info = true;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm64::GetRegisterSetCount () const
+{
+ return k_num_register_sets;
+}
+
+const RegisterSet *
+NativeRegisterContextLinux_arm64::GetRegisterSet (uint32_t set_index) const
+{
+ if (set_index < k_num_register_sets)
+ return &g_reg_sets_arm64[set_index];
+
+ return nullptr;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm64::GetUserRegisterCount() const
+{
+ uint32_t count = 0;
+ for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index)
+ count += g_reg_sets_arm64[set_index].num_registers;
+ return count;
+}
+
+Error
+NativeRegisterContextLinux_arm64::ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value)
+{
+ Error error;
+
+ if (!reg_info)
+ {
+ error.SetErrorString ("reg_info NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (IsFPR(reg))
+ {
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+ }
+ else
+ {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg)
+ {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+
+ error = ReadRegisterRaw(full_reg, reg_value);
+
+ if (error.Success ())
+ {
+ // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right.
+ if (is_subreg && (reg_info->byte_offset & 0x1))
+ reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8);
+
+ // If our return byte size was greater than the return value reg size, then
+ // use the type specified by reg_info rather than the uint64_t default
+ if (reg_value.GetByteSize() > reg_info->byte_size)
+ reg_value.SetType(reg_info);
+ }
+ return error;
+ }
+
+ // Get pointer to m_fpr variable and set the data from it.
+ uint32_t fpr_offset = CalculateFprOffset(reg_info);
+ assert (fpr_offset < sizeof m_fpr);
+ uint8_t *src = (uint8_t *)&m_fpr + fpr_offset;
+ reg_value.SetFromMemoryData(reg_info, src, reg_info->byte_size, eByteOrderLittle, error);
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_arm64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value)
+{
+ if (!reg_info)
+ return Error ("reg_info NULL");
+
+ const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg_index == LLDB_INVALID_REGNUM)
+ return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>");
+
+ if (IsGPR(reg_index))
+ return WriteRegisterRaw(reg_index, reg_value);
+
+ if (IsFPR(reg_index))
+ {
+ // Get pointer to m_fpr variable and set the data to it.
+ uint32_t fpr_offset = CalculateFprOffset(reg_info);
+ assert (fpr_offset < sizeof m_fpr);
+ uint8_t *dst = (uint8_t *)&m_fpr + fpr_offset;
+ switch (reg_info->byte_size)
+ {
+ case 2:
+ *(uint16_t *)dst = reg_value.GetAsUInt16();
+ break;
+ case 4:
+ *(uint32_t *)dst = reg_value.GetAsUInt32();
+ break;
+ case 8:
+ *(uint64_t *)dst = reg_value.GetAsUInt64();
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ return Error ("unhandled register data size %" PRIu32, reg_info->byte_size);
+ }
+
+ Error error = WriteFPR();
+ if (error.Fail())
+ return error;
+
+ return Error ();
+ }
+
+ return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown");
+}
+
+Error
+NativeRegisterContextLinux_arm64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ Error error;
+
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (!data_sp)
+ return Error ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE);
+
+ error = ReadGPR();
+ if (error.Fail())
+ return error;
+
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+
+ uint8_t *dst = data_sp->GetBytes ();
+ if (dst == nullptr)
+ {
+ error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE);
+ return error;
+ }
+
+ ::memcpy (dst, &m_gpr_arm64, GetGPRSize());
+ dst += GetGPRSize();
+ ::memcpy (dst, &m_fpr, sizeof(m_fpr));
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_arm64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ Error error;
+
+ if (!data_sp)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__);
+ return error;
+ }
+
+ if (data_sp->GetByteSize () != REG_CONTEXT_SIZE)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ());
+ return error;
+ }
+
+
+ uint8_t *src = data_sp->GetBytes ();
+ if (src == nullptr)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__);
+ return error;
+ }
+ ::memcpy (&m_gpr_arm64, src, GetRegisterInfoInterface ().GetGPRSize ());
+
+ error = WriteGPR();
+ if (error.Fail())
+ return error;
+
+ src += GetRegisterInfoInterface ().GetGPRSize ();
+ ::memcpy (&m_fpr, src, sizeof(m_fpr));
+
+ error = WriteFPR();
+ if (error.Fail())
+ return error;
+
+ return error;
+}
+
+bool
+NativeRegisterContextLinux_arm64::IsGPR(unsigned reg) const
+{
+ return reg <= m_reg_info.last_gpr; // GPR's come first.
+}
+
+bool
+NativeRegisterContextLinux_arm64::IsFPR(unsigned reg) const
+{
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+uint32_t
+NativeRegisterContextLinux_arm64::SetHardwareBreakpoint (lldb::addr_t addr, size_t size)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return LLDB_INVALID_INDEX32;
+
+ uint32_t control_value = 0, bp_index = 0;
+
+ // Check if size has a valid hardware breakpoint length.
+ if (size != 4)
+ return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware breakpoint
+
+ // Check 4-byte alignment for hardware breakpoint target address.
+ if (addr & 0x03)
+ return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.
+
+ // Setup control value
+ control_value = 0;
+ control_value |= ((1 << size) - 1) << 5;
+ control_value |= (2 << 1) | 1;
+
+ // Iterate over stored hardware breakpoints
+ // Find a free bp_index or update reference count if duplicate.
+ bp_index = LLDB_INVALID_INDEX32;
+ for (uint32_t i = 0; i < m_max_hbp_supported; i++)
+ {
+ if ((m_hbr_regs[i].control & 1) == 0)
+ {
+ bp_index = i; // Mark last free slot
+ }
+ else if (m_hbr_regs[i].address == addr && m_hbr_regs[i].control == control_value)
+ {
+ bp_index = i; // Mark duplicate index
+ break; // Stop searching here
+ }
+ }
+
+ if (bp_index == LLDB_INVALID_INDEX32)
+ return LLDB_INVALID_INDEX32;
+
+ // Add new or update existing breakpoint
+ if ((m_hbr_regs[bp_index].control & 1) == 0)
+ {
+ m_hbr_regs[bp_index].address = addr;
+ m_hbr_regs[bp_index].control = control_value;
+ m_hbr_regs[bp_index].refcount = 1;
+
+ // PTRACE call to set corresponding hardware breakpoint register.
+ error = WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+ if (error.Fail())
+ {
+ m_hbr_regs[bp_index].address = 0;
+ m_hbr_regs[bp_index].control &= ~1;
+ m_hbr_regs[bp_index].refcount = 0;
+
+ return LLDB_INVALID_INDEX32;
+ }
+ }
+ else
+ m_hbr_regs[bp_index].refcount++;
+
+ return bp_index;
+}
+
+bool
+NativeRegisterContextLinux_arm64::ClearHardwareBreakpoint (uint32_t hw_idx)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return false;
+
+ if (hw_idx >= m_max_hbp_supported)
+ return false;
+
+ // Update reference count if multiple references.
+ if (m_hbr_regs[hw_idx].refcount > 1)
+ {
+ m_hbr_regs[hw_idx].refcount--;
+ return true;
+ }
+ else if (m_hbr_regs[hw_idx].refcount == 1)
+ {
+ // Create a backup we can revert to in case of failure.
+ lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address;
+ uint32_t tempControl = m_hbr_regs[hw_idx].control;
+ uint32_t tempRefCount = m_hbr_regs[hw_idx].refcount;
+
+ m_hbr_regs[hw_idx].control &= ~1;
+ m_hbr_regs[hw_idx].address = 0;
+ m_hbr_regs[hw_idx].refcount = 0;
+
+ // PTRACE call to clear corresponding hardware breakpoint register.
+ WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+ if (error.Fail())
+ {
+ m_hbr_regs[hw_idx].control = tempControl;
+ m_hbr_regs[hw_idx].address = tempAddr;
+ m_hbr_regs[hw_idx].refcount = tempRefCount;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return LLDB_INVALID_INDEX32;
+
+ return m_max_hwp_supported;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return LLDB_INVALID_INDEX32;
+
+ uint32_t control_value = 0, wp_index = 0;
+
+ // Check if we are setting watchpoint other than read/write/access
+ // Also update watchpoint flag to match AArch64 write-read bit configuration.
+ switch (watch_flags)
+ {
+ case 1:
+ watch_flags = 2;
+ break;
+ case 2:
+ watch_flags = 1;
+ break;
+ case 3:
+ break;
+ default:
+ return LLDB_INVALID_INDEX32;
+ }
+
+ // Check if size has a valid hardware watchpoint length.
+ if (size != 1 && size != 2 && size != 4 && size != 8)
+ return LLDB_INVALID_INDEX32;
+
+ // Check 8-byte alignment for hardware watchpoint target address.
+ // TODO: Add support for watching un-aligned addresses
+ if (addr & 0x07)
+ return LLDB_INVALID_INDEX32;
+
+ // Setup control value
+ control_value = watch_flags << 3;
+ control_value |= ((1 << size) - 1) << 5;
+ control_value |= (2 << 1) | 1;
+
+ // Iterate over stored watchpoints
+ // Find a free wp_index or update reference count if duplicate.
+ wp_index = LLDB_INVALID_INDEX32;
+ for (uint32_t i = 0; i < m_max_hwp_supported; i++)
+ {
+ if ((m_hwp_regs[i].control & 1) == 0)
+ {
+ wp_index = i; // Mark last free slot
+ }
+ else if (m_hwp_regs[i].address == addr && m_hwp_regs[i].control == control_value)
+ {
+ wp_index = i; // Mark duplicate index
+ break; // Stop searching here
+ }
+ }
+
+ if (wp_index == LLDB_INVALID_INDEX32)
+ return LLDB_INVALID_INDEX32;
+
+ // Add new or update existing watchpoint
+ if ((m_hwp_regs[wp_index].control & 1) == 0)
+ {
+ // Update watchpoint in local cache
+ m_hwp_regs[wp_index].address = addr;
+ m_hwp_regs[wp_index].control = control_value;
+ m_hwp_regs[wp_index].refcount = 1;
+
+ // PTRACE call to set corresponding watchpoint register.
+ error = WriteHardwareDebugRegs(eDREGTypeWATCH);
+
+ if (error.Fail())
+ {
+ m_hwp_regs[wp_index].address = 0;
+ m_hwp_regs[wp_index].control &= ~1;
+ m_hwp_regs[wp_index].refcount = 0;
+
+ return LLDB_INVALID_INDEX32;
+ }
+ }
+ else
+ m_hwp_regs[wp_index].refcount++;
+
+ return wp_index;
+}
+
+bool
+NativeRegisterContextLinux_arm64::ClearHardwareWatchpoint (uint32_t wp_index)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return false;
+
+ if (wp_index >= m_max_hwp_supported)
+ return false;
+
+ // Update reference count if multiple references.
+ if (m_hwp_regs[wp_index].refcount > 1)
+ {
+ m_hwp_regs[wp_index].refcount--;
+ return true;
+ }
+ else if (m_hwp_regs[wp_index].refcount == 1)
+ {
+ // Create a backup we can revert to in case of failure.
+ lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
+ uint32_t tempControl = m_hwp_regs[wp_index].control;
+ uint32_t tempRefCount = m_hwp_regs[wp_index].refcount;
+
+ // Update watchpoint in local cache
+ m_hwp_regs[wp_index].control &= ~1;
+ m_hwp_regs[wp_index].address = 0;
+ m_hwp_regs[wp_index].refcount = 0;
+
+ // Ptrace call to update hardware debug registers
+ error = WriteHardwareDebugRegs(eDREGTypeWATCH);
+
+ if (error.Fail())
+ {
+ m_hwp_regs[wp_index].control = tempControl;
+ m_hwp_regs[wp_index].address = tempAddr;
+ m_hwp_regs[wp_index].refcount = tempRefCount;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+Error
+NativeRegisterContextLinux_arm64::ClearAllHardwareWatchpoints ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ Error error;
+
+ // Read hardware breakpoint and watchpoint information.
+ error = ReadHardwareDebugInfo ();
+
+ if (error.Fail())
+ return error;
+
+ lldb::addr_t tempAddr = 0;
+ uint32_t tempControl = 0, tempRefCount = 0;
+
+ for (uint32_t i = 0; i < m_max_hwp_supported; i++)
+ {
+ if (m_hwp_regs[i].control & 0x01)
+ {
+ // Create a backup we can revert to in case of failure.
+ tempAddr = m_hwp_regs[i].address;
+ tempControl = m_hwp_regs[i].control;
+ tempRefCount = m_hwp_regs[i].refcount;
+
+ // Clear watchpoints in local cache
+ m_hwp_regs[i].control &= ~1;
+ m_hwp_regs[i].address = 0;
+ m_hwp_regs[i].refcount = 0;
+
+ // Ptrace call to update hardware debug registers
+ error = WriteHardwareDebugRegs(eDREGTypeWATCH);
+
+ if (error.Fail())
+ {
+ m_hwp_regs[i].control = tempControl;
+ m_hwp_regs[i].address = tempAddr;
+ m_hwp_regs[i].refcount = tempRefCount;
+
+ return error;
+ }
+ }
+ }
+
+ return Error();
+}
+
+uint32_t
+NativeRegisterContextLinux_arm64::GetWatchpointSize(uint32_t wp_index)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+ switch ((m_hwp_regs[wp_index].control >> 5) & 0xff)
+ {
+ case 0x01:
+ return 1;
+ case 0x03:
+ return 2;
+ case 0x0f:
+ return 4;
+ case 0xff:
+ return 8;
+ default:
+ return 0;
+ }
+}
+bool
+NativeRegisterContextLinux_arm64::WatchpointIsEnabled(uint32_t wp_index)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ if ((m_hwp_regs[wp_index].control & 0x1) == 0x1)
+ return true;
+ else
+ return false;
+}
+
+Error
+NativeRegisterContextLinux_arm64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ uint32_t watch_size;
+ lldb::addr_t watch_addr;
+
+ for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index)
+ {
+ watch_size = GetWatchpointSize (wp_index);
+ watch_addr = m_hwp_regs[wp_index].address;
+
+ if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index)
+ && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size)
+ {
+ return Error();
+ }
+ }
+
+ wp_index = LLDB_INVALID_INDEX32;
+ return Error();
+}
+
+lldb::addr_t
+NativeRegisterContextLinux_arm64::GetWatchpointAddress (uint32_t wp_index)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+ if (wp_index >= m_max_hwp_supported)
+ return LLDB_INVALID_ADDRESS;
+
+ if (WatchpointIsEnabled(wp_index))
+ return m_hwp_regs[wp_index].address;
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+Error
+NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo()
+{
+ if (!m_refresh_hwdebug_info)
+ {
+ return Error();
+ }
+
+ ::pid_t tid = m_thread.GetID();
+
+ int regset = NT_ARM_HW_WATCH;
+ struct iovec ioVec;
+ struct user_hwdebug_state dreg_state;
+ Error error;
+
+ ioVec.iov_base = &dreg_state;
+ ioVec.iov_len = sizeof (dreg_state);
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset, &ioVec, ioVec.iov_len);
+
+ if (error.Fail())
+ return error;
+
+ m_max_hwp_supported = dreg_state.dbg_info & 0xff;
+
+ regset = NT_ARM_HW_BREAK;
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset, &ioVec, ioVec.iov_len);
+
+ if (error.Fail())
+ return error;
+
+ m_max_hbp_supported = dreg_state.dbg_info & 0xff;
+ m_refresh_hwdebug_info = false;
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType)
+{
+ struct iovec ioVec;
+ struct user_hwdebug_state dreg_state;
+ Error error;
+
+ memset (&dreg_state, 0, sizeof (dreg_state));
+ ioVec.iov_base = &dreg_state;
+
+ if (hwbType == eDREGTypeWATCH)
+ {
+ hwbType = NT_ARM_HW_WATCH;
+ ioVec.iov_len = sizeof (dreg_state.dbg_info) + sizeof (dreg_state.pad)
+ + (sizeof (dreg_state.dbg_regs [0]) * m_max_hwp_supported);
+
+ for (uint32_t i = 0; i < m_max_hwp_supported; i++)
+ {
+ dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
+ dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
+ }
+ }
+ else
+ {
+ hwbType = NT_ARM_HW_BREAK;
+ ioVec.iov_len = sizeof (dreg_state.dbg_info) + sizeof (dreg_state.pad)
+ + (sizeof (dreg_state.dbg_regs [0]) * m_max_hbp_supported);
+
+ for (uint32_t i = 0; i < m_max_hbp_supported; i++)
+ {
+ dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address;
+ dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control;
+ }
+ }
+
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), &hwbType, &ioVec, ioVec.iov_len);
+}
+
+Error
+NativeRegisterContextLinux_arm64::DoReadRegisterValue(uint32_t offset,
+ const char* reg_name,
+ uint32_t size,
+ RegisterValue &value)
+{
+ Error error;
+ if (offset > sizeof(struct user_pt_regs))
+ {
+ uintptr_t offset = offset - sizeof(struct user_pt_regs);
+ if (offset > sizeof(struct user_fpsimd_state))
+ {
+ error.SetErrorString("invalid offset value");
+ return error;
+ }
+ elf_fpregset_t regs;
+ int regset = NT_FPREGSET;
+ struct iovec ioVec;
+
+ ioVec.iov_base = &regs;
+ ioVec.iov_len = sizeof regs;
+ error = NativeProcessLinux::PtraceWrapper(
+ PTRACE_GETREGSET, m_thread.GetID(), &regset, &ioVec, sizeof regs);
+ if (error.Success())
+ {
+ ArchSpec arch;
+ if (m_thread.GetProcess()->GetArchitecture(arch))
+ value.SetBytes((void *)(((unsigned char *)(&regs)) + offset), 16, arch.GetByteOrder());
+ else
+ error.SetErrorString("failed to get architecture");
+ }
+ }
+ else
+ {
+ elf_gregset_t regs;
+ int regset = NT_PRSTATUS;
+ struct iovec ioVec;
+
+ ioVec.iov_base = &regs;
+ ioVec.iov_len = sizeof regs;
+ error = NativeProcessLinux::PtraceWrapper(
+ PTRACE_GETREGSET, m_thread.GetID(), &regset, &ioVec, sizeof regs);
+ if (error.Success())
+ {
+ ArchSpec arch;
+ if (m_thread.GetProcess()->GetArchitecture(arch))
+ value.SetBytes((void *)(((unsigned char *)(regs)) + offset), 8, arch.GetByteOrder());
+ else
+ error.SetErrorString("failed to get architecture");
+ }
+ }
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_arm64::DoWriteRegisterValue(uint32_t offset,
+ const char* reg_name,
+ const RegisterValue &value)
+{
+ Error error;
+ ::pid_t tid = m_thread.GetID();
+ if (offset > sizeof(struct user_pt_regs))
+ {
+ uintptr_t offset = offset - sizeof(struct user_pt_regs);
+ if (offset > sizeof(struct user_fpsimd_state))
+ {
+ error.SetErrorString("invalid offset value");
+ return error;
+ }
+ elf_fpregset_t regs;
+ int regset = NT_FPREGSET;
+ struct iovec ioVec;
+
+ ioVec.iov_base = &regs;
+ ioVec.iov_len = sizeof regs;
+ error = NativeProcessLinux::PtraceWrapper( PTRACE_GETREGSET, tid, &regset, &ioVec, sizeof regs);
+
+ if (error.Success())
+ {
+ ::memcpy((void *)(((unsigned char *)(&regs)) + offset), value.GetBytes(), 16);
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, &regset, &ioVec, sizeof regs);
+ }
+ }
+ else
+ {
+ elf_gregset_t regs;
+ int regset = NT_PRSTATUS;
+ struct iovec ioVec;
+
+ ioVec.iov_base = &regs;
+ ioVec.iov_len = sizeof regs;
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset, &ioVec, sizeof regs);
+ if (error.Success())
+ {
+ ::memcpy((void *)(((unsigned char *)(&regs)) + offset), value.GetBytes(), 8);
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, &regset, &ioVec, sizeof regs);
+ }
+ }
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_arm64::DoReadGPR(void *buf, size_t buf_size)
+{
+ int regset = NT_PRSTATUS;
+ struct iovec ioVec;
+ Error error;
+
+ ioVec.iov_base = buf;
+ ioVec.iov_len = buf_size;
+ return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), &regset, &ioVec, buf_size);
+}
+
+Error
+NativeRegisterContextLinux_arm64::DoWriteGPR(void *buf, size_t buf_size)
+{
+ int regset = NT_PRSTATUS;
+ struct iovec ioVec;
+ Error error;
+
+ ioVec.iov_base = buf;
+ ioVec.iov_len = buf_size;
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), &regset, &ioVec, buf_size);
+}
+
+Error
+NativeRegisterContextLinux_arm64::DoReadFPR(void *buf, size_t buf_size)
+{
+ int regset = NT_FPREGSET;
+ struct iovec ioVec;
+ Error error;
+
+ ioVec.iov_base = buf;
+ ioVec.iov_len = buf_size;
+ return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), &regset, &ioVec, buf_size);
+}
+
+Error
+NativeRegisterContextLinux_arm64::DoWriteFPR(void *buf, size_t buf_size)
+{
+ int regset = NT_FPREGSET;
+ struct iovec ioVec;
+ Error error;
+
+ ioVec.iov_base = buf;
+ ioVec.iov_len = buf_size;
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), &regset, &ioVec, buf_size);
+}
+
+uint32_t
+NativeRegisterContextLinux_arm64::CalculateFprOffset(const RegisterInfo* reg_info) const
+{
+ return reg_info->byte_offset - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset;
+}
+
+#endif // defined (__arm64__) || defined (__aarch64__)
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
new file mode 100644
index 0000000000000..c60baa637b8ae
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -0,0 +1,196 @@
+//===-- NativeRegisterContextLinux_arm64.h ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__arm64__) || defined (__aarch64__)
+
+#ifndef lldb_NativeRegisterContextLinux_arm64_h
+#define lldb_NativeRegisterContextLinux_arm64_h
+
+#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
+#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
+
+namespace lldb_private {
+namespace process_linux {
+
+ class NativeProcessLinux;
+
+ class NativeRegisterContextLinux_arm64 : public NativeRegisterContextLinux
+ {
+ public:
+ NativeRegisterContextLinux_arm64 (const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx);
+
+ uint32_t
+ GetRegisterSetCount () const override;
+
+ uint32_t
+ GetUserRegisterCount() const override;
+
+ const RegisterSet *
+ GetRegisterSet (uint32_t set_index) const override;
+
+ Error
+ ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value) override;
+
+ Error
+ WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value) override;
+
+ Error
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override;
+
+ Error
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override;
+
+ //------------------------------------------------------------------
+ // Hardware breakpoints/watchpoint mangement functions
+ //------------------------------------------------------------------
+
+ uint32_t
+ SetHardwareBreakpoint (lldb::addr_t addr, size_t size) override;
+
+ bool
+ ClearHardwareBreakpoint (uint32_t hw_idx) override;
+
+ uint32_t
+ NumSupportedHardwareWatchpoints () override;
+
+ uint32_t
+ SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) override;
+
+ bool
+ ClearHardwareWatchpoint (uint32_t hw_index) override;
+
+ Error
+ ClearAllHardwareWatchpoints () override;
+
+ Error
+ GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override;
+
+ lldb::addr_t
+ GetWatchpointAddress (uint32_t wp_index) override;
+
+ uint32_t
+ GetWatchpointSize(uint32_t wp_index);
+
+ bool
+ WatchpointIsEnabled(uint32_t wp_index);
+
+ // Debug register type select
+ enum DREGType
+ {
+ eDREGTypeWATCH = 0,
+ eDREGTypeBREAK
+ };
+
+ protected:
+ Error
+ DoReadRegisterValue(uint32_t offset,
+ const char* reg_name,
+ uint32_t size,
+ RegisterValue &value) override;
+
+ Error
+ DoWriteRegisterValue(uint32_t offset,
+ const char* reg_name,
+ const RegisterValue &value) override;
+
+ Error
+ DoReadGPR(void *buf, size_t buf_size) override;
+
+ Error
+ DoWriteGPR(void *buf, size_t buf_size) override;
+
+ Error
+ DoReadFPR(void *buf, size_t buf_size) override;
+
+ Error
+ DoWriteFPR(void *buf, size_t buf_size) override;
+
+ void*
+ GetGPRBuffer() override { return &m_gpr_arm64; }
+
+ void*
+ GetFPRBuffer() override { return &m_fpr; }
+
+ size_t
+ GetFPRSize() override { return sizeof(m_fpr); }
+
+ private:
+ struct RegInfo
+ {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_fpr_v;
+ uint32_t last_fpr_v;
+
+ uint32_t gpr_flags;
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct VReg
+ {
+ uint8_t bytes[16];
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct FPU
+ {
+ VReg v[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+ };
+
+ uint64_t m_gpr_arm64[k_num_gpr_registers_arm64]; // 64-bit general purpose registers.
+ RegInfo m_reg_info;
+ FPU m_fpr; // floating-point registers including extended register sets.
+
+ // Debug register info for hardware breakpoints and watchpoints management.
+ struct DREG
+ {
+ lldb::addr_t address; // Breakpoint/watchpoint address value.
+ uint32_t control; // Breakpoint/watchpoint control value.
+ uint32_t refcount; // Serves as enable/disable and refernce counter.
+ };
+
+ struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints
+ struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints
+
+ uint32_t m_max_hwp_supported;
+ uint32_t m_max_hbp_supported;
+ bool m_refresh_hwdebug_info;
+
+ bool
+ IsGPR(unsigned reg) const;
+
+ bool
+ IsFPR(unsigned reg) const;
+
+ Error
+ ReadHardwareDebugInfo();
+
+ Error
+ WriteHardwareDebugRegs(int hwbType);
+
+ uint32_t
+ CalculateFprOffset(const RegisterInfo* reg_info) const;
+ };
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextLinux_arm64_h
+
+#endif // defined (__arm64__) || defined (__aarch64__)
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp
new file mode 100644
index 0000000000000..3cfeaf5546bc7
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp
@@ -0,0 +1,1431 @@
+//===-- NativeRegisterContextLinux_mips64.cpp ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__mips__)
+
+#include "NativeRegisterContextLinux_mips64.h"
+
+// C Includes
+// C++ Includes
+
+// Other libraries and framework includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private-enumerations.h"
+#include "Plugins/Process/Linux/NativeProcessLinux.h"
+#include "Plugins/Process/Linux/Procfs.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_mips64.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_mips.h"
+#define NT_MIPS_MSA 0x600
+#define CONFIG5_FRE (1 << 8)
+#define SR_FR (1 << 26)
+#define NUM_REGISTERS 32
+
+#include <sys/ptrace.h>
+#include <asm/ptrace.h>
+
+#ifndef PTRACE_GET_WATCH_REGS
+enum pt_watch_style
+{
+ pt_watch_style_mips32,
+ pt_watch_style_mips64
+};
+struct mips32_watch_regs
+{
+ uint32_t watchlo[8];
+ uint16_t watchhi[8];
+ uint16_t watch_masks[8];
+ uint32_t num_valid;
+} __attribute__((aligned(8)));
+
+struct mips64_watch_regs
+{
+ uint64_t watchlo[8];
+ uint16_t watchhi[8];
+ uint16_t watch_masks[8];
+ uint32_t num_valid;
+} __attribute__((aligned(8)));
+
+struct pt_watch_regs
+{
+ enum pt_watch_style style;
+ union
+ {
+ struct mips32_watch_regs mips32;
+ struct mips64_watch_regs mips64;
+ };
+};
+
+#define PTRACE_GET_WATCH_REGS 0xd0
+#define PTRACE_SET_WATCH_REGS 0xd1
+#endif
+
+#define W (1 << 0)
+#define R (1 << 1)
+#define I (1 << 2)
+
+#define IRW (I | R | W)
+
+struct pt_watch_regs default_watch_regs;
+
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+
+// ----------------------------------------------------------------------------
+// Private namespace.
+// ----------------------------------------------------------------------------
+
+namespace
+{
+ // mips general purpose registers.
+ const uint32_t
+ g_gp_regnums_mips[] =
+ {
+ gpr_zero_mips,
+ gpr_r1_mips,
+ gpr_r2_mips,
+ gpr_r3_mips,
+ gpr_r4_mips,
+ gpr_r5_mips,
+ gpr_r6_mips,
+ gpr_r7_mips,
+ gpr_r8_mips,
+ gpr_r9_mips,
+ gpr_r10_mips,
+ gpr_r11_mips,
+ gpr_r12_mips,
+ gpr_r13_mips,
+ gpr_r14_mips,
+ gpr_r15_mips,
+ gpr_r16_mips,
+ gpr_r17_mips,
+ gpr_r18_mips,
+ gpr_r19_mips,
+ gpr_r20_mips,
+ gpr_r21_mips,
+ gpr_r22_mips,
+ gpr_r23_mips,
+ gpr_r24_mips,
+ gpr_r25_mips,
+ gpr_r26_mips,
+ gpr_r27_mips,
+ gpr_gp_mips,
+ gpr_sp_mips,
+ gpr_r30_mips,
+ gpr_ra_mips,
+ gpr_sr_mips,
+ gpr_mullo_mips,
+ gpr_mulhi_mips,
+ gpr_badvaddr_mips,
+ gpr_cause_mips,
+ gpr_pc_mips,
+ gpr_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+
+ static_assert((sizeof(g_gp_regnums_mips) / sizeof(g_gp_regnums_mips[0])) - 1 == k_num_gpr_registers_mips,
+ "g_gp_regnums_mips has wrong number of register infos");
+
+ // mips floating point registers.
+ const uint32_t
+ g_fp_regnums_mips[] =
+ {
+ fpr_f0_mips,
+ fpr_f1_mips,
+ fpr_f2_mips,
+ fpr_f3_mips,
+ fpr_f4_mips,
+ fpr_f5_mips,
+ fpr_f6_mips,
+ fpr_f7_mips,
+ fpr_f8_mips,
+ fpr_f9_mips,
+ fpr_f10_mips,
+ fpr_f11_mips,
+ fpr_f12_mips,
+ fpr_f13_mips,
+ fpr_f14_mips,
+ fpr_f15_mips,
+ fpr_f16_mips,
+ fpr_f17_mips,
+ fpr_f18_mips,
+ fpr_f19_mips,
+ fpr_f20_mips,
+ fpr_f21_mips,
+ fpr_f22_mips,
+ fpr_f23_mips,
+ fpr_f24_mips,
+ fpr_f25_mips,
+ fpr_f26_mips,
+ fpr_f27_mips,
+ fpr_f28_mips,
+ fpr_f29_mips,
+ fpr_f30_mips,
+ fpr_f31_mips,
+ fpr_fcsr_mips,
+ fpr_fir_mips,
+ fpr_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+
+ static_assert((sizeof(g_fp_regnums_mips) / sizeof(g_fp_regnums_mips[0])) - 1 == k_num_fpr_registers_mips,
+ "g_fp_regnums_mips has wrong number of register infos");
+
+ // mips MSA registers.
+ const uint32_t
+ g_msa_regnums_mips[] =
+ {
+ msa_w0_mips,
+ msa_w1_mips,
+ msa_w2_mips,
+ msa_w3_mips,
+ msa_w4_mips,
+ msa_w5_mips,
+ msa_w6_mips,
+ msa_w7_mips,
+ msa_w8_mips,
+ msa_w9_mips,
+ msa_w10_mips,
+ msa_w11_mips,
+ msa_w12_mips,
+ msa_w13_mips,
+ msa_w14_mips,
+ msa_w15_mips,
+ msa_w16_mips,
+ msa_w17_mips,
+ msa_w18_mips,
+ msa_w19_mips,
+ msa_w20_mips,
+ msa_w21_mips,
+ msa_w22_mips,
+ msa_w23_mips,
+ msa_w24_mips,
+ msa_w25_mips,
+ msa_w26_mips,
+ msa_w27_mips,
+ msa_w28_mips,
+ msa_w29_mips,
+ msa_w30_mips,
+ msa_w31_mips,
+ msa_fcsr_mips,
+ msa_fir_mips,
+ msa_mcsr_mips,
+ msa_mir_mips,
+ msa_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+
+ static_assert((sizeof(g_msa_regnums_mips) / sizeof(g_msa_regnums_mips[0])) - 1 == k_num_msa_registers_mips,
+ "g_msa_regnums_mips has wrong number of register infos");
+
+ // mips64 general purpose registers.
+ const uint32_t
+ g_gp_regnums_mips64[] =
+ {
+ gpr_zero_mips64,
+ gpr_r1_mips64,
+ gpr_r2_mips64,
+ gpr_r3_mips64,
+ gpr_r4_mips64,
+ gpr_r5_mips64,
+ gpr_r6_mips64,
+ gpr_r7_mips64,
+ gpr_r8_mips64,
+ gpr_r9_mips64,
+ gpr_r10_mips64,
+ gpr_r11_mips64,
+ gpr_r12_mips64,
+ gpr_r13_mips64,
+ gpr_r14_mips64,
+ gpr_r15_mips64,
+ gpr_r16_mips64,
+ gpr_r17_mips64,
+ gpr_r18_mips64,
+ gpr_r19_mips64,
+ gpr_r20_mips64,
+ gpr_r21_mips64,
+ gpr_r22_mips64,
+ gpr_r23_mips64,
+ gpr_r24_mips64,
+ gpr_r25_mips64,
+ gpr_r26_mips64,
+ gpr_r27_mips64,
+ gpr_gp_mips64,
+ gpr_sp_mips64,
+ gpr_r30_mips64,
+ gpr_ra_mips64,
+ gpr_sr_mips64,
+ gpr_mullo_mips64,
+ gpr_mulhi_mips64,
+ gpr_badvaddr_mips64,
+ gpr_cause_mips64,
+ gpr_pc_mips64,
+ gpr_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+
+ static_assert((sizeof(g_gp_regnums_mips64) / sizeof(g_gp_regnums_mips64[0])) - 1 == k_num_gpr_registers_mips64,
+ "g_gp_regnums_mips64 has wrong number of register infos");
+
+ // mips64 floating point registers.
+ const uint32_t
+ g_fp_regnums_mips64[] =
+ {
+ fpr_f0_mips64,
+ fpr_f1_mips64,
+ fpr_f2_mips64,
+ fpr_f3_mips64,
+ fpr_f4_mips64,
+ fpr_f5_mips64,
+ fpr_f6_mips64,
+ fpr_f7_mips64,
+ fpr_f8_mips64,
+ fpr_f9_mips64,
+ fpr_f10_mips64,
+ fpr_f11_mips64,
+ fpr_f12_mips64,
+ fpr_f13_mips64,
+ fpr_f14_mips64,
+ fpr_f15_mips64,
+ fpr_f16_mips64,
+ fpr_f17_mips64,
+ fpr_f18_mips64,
+ fpr_f19_mips64,
+ fpr_f20_mips64,
+ fpr_f21_mips64,
+ fpr_f22_mips64,
+ fpr_f23_mips64,
+ fpr_f24_mips64,
+ fpr_f25_mips64,
+ fpr_f26_mips64,
+ fpr_f27_mips64,
+ fpr_f28_mips64,
+ fpr_f29_mips64,
+ fpr_f30_mips64,
+ fpr_f31_mips64,
+ fpr_fcsr_mips64,
+ fpr_fir_mips64,
+ fpr_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+
+ static_assert((sizeof(g_fp_regnums_mips64) / sizeof(g_fp_regnums_mips64[0])) - 1 == k_num_fpr_registers_mips64,
+ "g_fp_regnums_mips64 has wrong number of register infos");
+
+ // mips64 MSA registers.
+ const uint32_t
+ g_msa_regnums_mips64[] =
+ {
+ msa_w0_mips64,
+ msa_w1_mips64,
+ msa_w2_mips64,
+ msa_w3_mips64,
+ msa_w4_mips64,
+ msa_w5_mips64,
+ msa_w6_mips64,
+ msa_w7_mips64,
+ msa_w8_mips64,
+ msa_w9_mips64,
+ msa_w10_mips64,
+ msa_w11_mips64,
+ msa_w12_mips64,
+ msa_w13_mips64,
+ msa_w14_mips64,
+ msa_w15_mips64,
+ msa_w16_mips64,
+ msa_w17_mips64,
+ msa_w18_mips64,
+ msa_w19_mips64,
+ msa_w20_mips64,
+ msa_w21_mips64,
+ msa_w22_mips64,
+ msa_w23_mips64,
+ msa_w24_mips64,
+ msa_w25_mips64,
+ msa_w26_mips64,
+ msa_w27_mips64,
+ msa_w28_mips64,
+ msa_w29_mips64,
+ msa_w30_mips64,
+ msa_w31_mips64,
+ msa_fcsr_mips64,
+ msa_fir_mips64,
+ msa_mcsr_mips64,
+ msa_mir_mips64,
+ msa_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+
+ static_assert((sizeof(g_msa_regnums_mips64) / sizeof(g_msa_regnums_mips64[0])) - 1 == k_num_msa_registers_mips64,
+ "g_msa_regnums_mips64 has wrong number of register infos");
+
+ // Number of register sets provided by this context.
+ enum
+ {
+ k_num_register_sets = 3
+ };
+
+ // Register sets for mips.
+ static const RegisterSet
+ g_reg_sets_mips[k_num_register_sets] =
+ {
+ { "General Purpose Registers", "gpr", k_num_gpr_registers_mips, g_gp_regnums_mips },
+ { "Floating Point Registers", "fpu", k_num_fpr_registers_mips, g_fp_regnums_mips },
+ { "MSA Registers", "msa", k_num_msa_registers_mips, g_msa_regnums_mips }
+ };
+
+ // Register sets for mips64.
+ static const RegisterSet
+ g_reg_sets_mips64[k_num_register_sets] =
+ {
+ { "General Purpose Registers", "gpr", k_num_gpr_registers_mips64, g_gp_regnums_mips64 },
+ { "Floating Point Registers", "fpu", k_num_fpr_registers_mips64, g_fp_regnums_mips64 },
+ { "MSA Registers", "msa", k_num_msa_registers_mips64, g_msa_regnums_mips64 },
+ };
+
+} // end of anonymous namespace
+
+NativeRegisterContextLinux*
+NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx)
+{
+ return new NativeRegisterContextLinux_mips64(target_arch, native_thread, concrete_frame_idx);
+}
+
+#define REG_CONTEXT_SIZE (GetRegisterInfoInterface ().GetGPRSize () + sizeof(FPR_linux_mips) + sizeof(MSA_linux_mips))
+
+// ----------------------------------------------------------------------------
+// NativeRegisterContextLinux_mips64 members.
+// ----------------------------------------------------------------------------
+
+static RegisterInfoInterface*
+CreateRegisterInfoInterface(const ArchSpec& target_arch)
+{
+ if (HostInfo::GetArchitecture().GetAddressByteSize() == 4)
+ {
+ // 32-bit hosts run with a RegisterContextLinux_mips context.
+ return new RegisterContextLinux_mips(target_arch, NativeRegisterContextLinux_mips64::IsMSAAvailable());
+ }
+ else
+ {
+ assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) &&
+ "Register setting path assumes this is a 64-bit host");
+ // mips64 hosts know how to work with 64-bit and 32-bit EXEs using the mips64 register context.
+ return new RegisterContextLinux_mips64 (target_arch, NativeRegisterContextLinux_mips64::IsMSAAvailable());
+ }
+}
+
+NativeRegisterContextLinux_mips64::NativeRegisterContextLinux_mips64 (const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx) :
+ NativeRegisterContextLinux (native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch))
+{
+ switch (target_arch.GetMachine ())
+ {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ m_reg_info.num_registers = k_num_registers_mips;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_mips;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_mips;
+ m_reg_info.last_gpr = k_last_gpr_mips;
+ m_reg_info.first_fpr = k_first_fpr_mips;
+ m_reg_info.last_fpr = k_last_fpr_mips;
+ m_reg_info.first_msa = k_first_msa_mips;
+ m_reg_info.last_msa = k_last_msa_mips;
+ break;
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ m_reg_info.num_registers = k_num_registers_mips64;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_mips64;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_mips64;
+ m_reg_info.last_gpr = k_last_gpr_mips64;
+ m_reg_info.first_fpr = k_first_fpr_mips64;
+ m_reg_info.last_fpr = k_last_fpr_mips64;
+ m_reg_info.first_msa = k_first_msa_mips64;
+ m_reg_info.last_msa = k_last_msa_mips64;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ // Initialize m_iovec to point to the buffer and buffer size
+ // using the conventions of Berkeley style UIO structures, as required
+ // by PTRACE extensions.
+ m_iovec.iov_base = &m_msa;
+ m_iovec.iov_len = sizeof(MSA_linux_mips);
+
+ // init h/w watchpoint addr map
+ for (int index = 0;index <= MAX_NUM_WP; index++)
+ hw_addr_map[index] = LLDB_INVALID_ADDRESS;
+
+ ::memset(&m_gpr, 0, sizeof(GPR_linux_mips));
+ ::memset(&m_fpr, 0, sizeof(FPR_linux_mips));
+ ::memset(&m_msa, 0, sizeof(MSA_linux_mips));
+}
+
+uint32_t
+NativeRegisterContextLinux_mips64::GetRegisterSetCount () const
+{
+ return k_num_register_sets;
+}
+
+lldb::addr_t
+NativeRegisterContextLinux_mips64::GetPCfromBreakpointLocation (lldb::addr_t fail_value)
+{
+ Error error;
+ RegisterValue pc_value;
+ lldb::addr_t pc = fail_value;
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_mips64::%s Reading PC from breakpoint location", __FUNCTION__);
+
+ // PC register is at index 34 of the register array
+ const RegisterInfo *const pc_info_p = GetRegisterInfoAtIndex (gpr_pc_mips64);
+
+ error = ReadRegister (pc_info_p, pc_value);
+ if (error.Success ())
+ {
+ pc = pc_value.GetAsUInt64 ();
+
+ // CAUSE register is at index 37 of the register array
+ const RegisterInfo *const cause_info_p = GetRegisterInfoAtIndex (gpr_cause_mips64);
+ RegisterValue cause_value;
+
+ ReadRegister (cause_info_p, cause_value);
+
+ uint64_t cause = cause_value.GetAsUInt64 ();
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_mips64::%s PC 0x%" PRIx64 " Cause 0x%" PRIx64, __FUNCTION__, pc, cause);
+
+ /*
+ * The breakpoint might be in a delay slot. In this case PC points
+ * to the delayed branch instruction rather then the instruction
+ * in the delay slot. If the CAUSE.BD flag is set then adjust the
+ * PC based on the size of the branch instruction.
+ */
+ if ((cause & (1 << 31)) != 0)
+ {
+ lldb::addr_t branch_delay = 0;
+ branch_delay = 4; // FIXME - Adjust according to size of branch instruction at PC
+ pc = pc + branch_delay;
+ pc_value.SetUInt64 (pc);
+ WriteRegister (pc_info_p, pc_value);
+
+ if (log)
+ log->Printf ("NativeRegisterContextLinux_mips64::%s New PC 0x%" PRIx64, __FUNCTION__, pc);
+ }
+ }
+
+ return pc;
+}
+
+const RegisterSet *
+NativeRegisterContextLinux_mips64::GetRegisterSet (uint32_t set_index) const
+{
+ if (set_index >= k_num_register_sets)
+ return nullptr;
+
+ switch (GetRegisterInfoInterface ().GetTargetArchitecture ().GetMachine ())
+ {
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return &g_reg_sets_mips64[set_index];
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return &g_reg_sets_mips[set_index];
+ default:
+ assert (false && "Unhandled target architecture.");
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+lldb_private::Error
+NativeRegisterContextLinux_mips64::ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value)
+{
+ Error error;
+
+ if (!reg_info)
+ {
+ error.SetErrorString ("reg_info NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM)
+ {
+ // This is likely an internal register for lldb use only and should not be directly queried.
+ error.SetErrorStringWithFormat ("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name);
+ return error;
+ }
+
+ if (IsMSA(reg) && !IsMSAAvailable())
+ {
+ error.SetErrorString ("MSA not available on this processor");
+ return error;
+ }
+
+ if (IsMSA(reg) || IsFPR(reg))
+ {
+ uint8_t *src;
+
+ error = ReadCP1();
+
+ if (!error.Success())
+ {
+ error.SetErrorString ("failed to read co-processor 1 register");
+ return error;
+ }
+
+ if (IsFPR(reg))
+ {
+ assert (reg_info->byte_offset < sizeof(UserArea));
+ src = (uint8_t *)&m_fpr + reg_info->byte_offset - (sizeof(m_gpr));
+ }
+ else
+ {
+ assert (reg_info->byte_offset < sizeof(UserArea));
+ src = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr));
+ }
+ switch (reg_info->byte_size)
+ {
+ case 4:
+ reg_value.SetUInt32(*(uint32_t *)src);
+ break;
+ case 8:
+ reg_value.SetUInt64(*(uint64_t *)src);
+ break;
+ case 16:
+ reg_value.SetBytes((const void *)src, 16, GetByteOrder());
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size);
+ break;
+ }
+ }
+ else
+ {
+ error = ReadRegisterRaw(reg, reg_value);
+ }
+
+ return error;
+}
+
+lldb_private::Error
+NativeRegisterContextLinux_mips64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value)
+{
+ Error error;
+
+ assert (reg_info && "reg_info is null");
+
+ const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (reg_index == LLDB_INVALID_REGNUM)
+ return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>");
+
+ if (IsMSA(reg_index) && !IsMSAAvailable())
+ {
+ error.SetErrorString ("MSA not available on this processor");
+ return error;
+ }
+
+ if (IsFPR(reg_index) || IsMSA(reg_index))
+ {
+ uint8_t *dst;
+ uint64_t *src;
+
+ // Initialise the FP and MSA buffers by reading all co-processor 1 registers
+ ReadCP1();
+
+ if (IsFPR(reg_index))
+ {
+ assert (reg_info->byte_offset < sizeof(UserArea));
+ dst = (uint8_t *)&m_fpr + reg_info->byte_offset - (sizeof(m_gpr));
+ }
+ else
+ {
+ assert (reg_info->byte_offset < sizeof(UserArea));
+ dst = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr));
+ }
+ switch (reg_info->byte_size)
+ {
+ case 4:
+ *(uint32_t *)dst = reg_value.GetAsUInt32();
+ break;
+ case 8:
+ *(uint64_t *)dst = reg_value.GetAsUInt64();
+ break;
+ case 16:
+ src = (uint64_t *)reg_value.GetBytes();
+ *(uint64_t *)dst = *src;
+ *(uint64_t *)(dst + 8) = *(src + 1);
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size);
+ break;
+ }
+ error = WriteCP1();
+ if (!error.Success())
+ {
+ error.SetErrorString ("failed to write co-processor 1 register");
+ return error;
+ }
+ }
+ else
+ {
+ error = WriteRegisterRaw(reg_index, reg_value);
+ }
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_mips64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ Error error;
+
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (!data_sp)
+ {
+ error.SetErrorStringWithFormat ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE);
+ return error;
+ }
+
+ error = ReadGPR();
+ if (!error.Success())
+ {
+ error.SetErrorString ("ReadGPR() failed");
+ return error;
+ }
+
+ error = ReadCP1();
+ if (!error.Success())
+ {
+ error.SetErrorString ("ReadCP1() failed");
+ return error;
+ }
+
+ uint8_t *dst = data_sp->GetBytes ();
+ if (dst == nullptr)
+ {
+ error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE);
+ return error;
+ }
+
+ ::memcpy (dst, &m_gpr, GetRegisterInfoInterface ().GetGPRSize ());
+ dst += GetRegisterInfoInterface ().GetGPRSize ();
+
+ ::memcpy (dst, &m_fpr, GetFPRSize ());
+ dst += GetFPRSize ();
+
+ ::memcpy (dst, &m_msa, sizeof(MSA_linux_mips));
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_mips64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ Error error;
+
+ if (!data_sp)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s invalid data_sp provided", __FUNCTION__);
+ return error;
+ }
+
+ if (data_sp->GetByteSize () != REG_CONTEXT_SIZE)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ());
+ return error;
+ }
+
+
+ uint8_t *src = data_sp->GetBytes ();
+ if (src == nullptr)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__);
+ return error;
+ }
+
+ ::memcpy (&m_gpr, src, GetRegisterInfoInterface ().GetGPRSize ());
+ src += GetRegisterInfoInterface ().GetGPRSize ();
+
+ ::memcpy (&m_fpr, src, GetFPRSize ());
+ src += GetFPRSize ();
+
+ ::memcpy (&m_msa, src, sizeof(MSA_linux_mips));
+
+ error = WriteGPR();
+ if (!error.Success())
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s WriteGPR() failed", __FUNCTION__);
+ return error;
+ }
+
+ error = WriteCP1();
+ if (!error.Success())
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s WriteCP1() failed", __FUNCTION__);
+ return error;
+ }
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_mips64::ReadCP1()
+{
+ Error error;
+
+ uint8_t *src, *dst;
+
+ lldb::ByteOrder byte_order = GetByteOrder();
+
+ uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig);
+
+ if (IsMSAAvailable())
+ {
+ error = NativeRegisterContextLinux::ReadRegisterSet(&m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA);
+ src = (uint8_t *)&m_msa + (IsBigEndian * 8);
+ dst = (uint8_t *)&m_fpr;
+ for ( int i = 0; i < NUM_REGISTERS; i++)
+ {
+ // Copy fp values from msa buffer fetched via ptrace
+ *(uint64_t *) dst = *(uint64_t *) src;
+ src = src + 16;
+ dst = dst + 8;
+ }
+ m_fpr.fir = m_msa.fir;
+ m_fpr.fcsr = m_msa.fcsr;
+ m_fpr.config5 = m_msa.config5;
+ }
+ else
+ {
+ error = NativeRegisterContextLinux::ReadFPR();
+ }
+
+ if (IsFR0() || IsFRE())
+ {
+ src = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4);
+ dst = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4);
+ for (int i = 0; i < (NUM_REGISTERS / 2); i++)
+ {
+ // copy odd single from top of neighbouring even double
+ *(uint32_t *) dst = *(uint32_t *) src;
+ src = src + 16;
+ dst = dst + 16;
+ }
+ }
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_mips64::WriteCP1()
+{
+ Error error;
+
+ uint8_t *src, *dst;
+
+ lldb::ByteOrder byte_order = GetByteOrder();
+
+ uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig);
+
+ if (IsFR0() || IsFRE())
+ {
+ src = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4);
+ dst = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4);
+ for (int i = 0; i < (NUM_REGISTERS / 2); i++)
+ {
+ // copy odd single to top of neighbouring even double
+ *(uint32_t *) dst = *(uint32_t *) src;
+ src = src + 16;
+ dst = dst + 16;
+ }
+ }
+
+ if (IsMSAAvailable())
+ {
+ dst = (uint8_t *)&m_msa + (IsBigEndian * 8);
+ src = (uint8_t *)&m_fpr;
+ for (int i = 0; i < NUM_REGISTERS; i++)
+ {
+ // Copy fp values to msa buffer for ptrace
+ *(uint64_t *) dst = *(uint64_t *) src;
+ dst = dst + 16;
+ src = src + 8;
+ }
+ m_msa.fir = m_fpr.fir;
+ m_msa.fcsr = m_fpr.fcsr;
+ m_msa.config5 = m_fpr.config5;
+ error = NativeRegisterContextLinux::WriteRegisterSet(&m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA);
+ }
+ else
+ {
+ error = NativeRegisterContextLinux::WriteFPR();
+ }
+
+ return error;
+}
+
+bool
+NativeRegisterContextLinux_mips64::IsFR0()
+{
+ const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex (gpr_sr_mips64);
+
+ RegisterValue reg_value;
+ ReadRegister (reg_info_p, reg_value);
+
+ uint64_t value = reg_value.GetAsUInt64();
+
+ return (!(value & SR_FR));
+}
+
+bool
+NativeRegisterContextLinux_mips64::IsFRE()
+{
+ const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex (gpr_config5_mips64);
+
+ RegisterValue reg_value;
+ ReadRegister (reg_info_p, reg_value);
+
+ uint64_t config5 = reg_value.GetAsUInt64();
+
+ return (config5 & CONFIG5_FRE);
+}
+
+bool
+NativeRegisterContextLinux_mips64::IsFPR(uint32_t reg_index) const
+{
+ return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr);
+}
+
+static uint32_t
+GetWatchHi (struct pt_watch_regs *regs, uint32_t index)
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (regs->style == pt_watch_style_mips32)
+ return regs->mips32.watchhi[index];
+ else if (regs->style == pt_watch_style_mips64)
+ return regs->mips64.watchhi[index];
+ if(log)
+ log->Printf("Invalid watch register style");
+ return 0;
+}
+
+static void
+SetWatchHi (struct pt_watch_regs *regs, uint32_t index, uint16_t value)
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (regs->style == pt_watch_style_mips32)
+ regs->mips32.watchhi[index] = value;
+ else if (regs->style == pt_watch_style_mips64)
+ regs->mips64.watchhi[index] = value;
+ if(log)
+ log->Printf("Invalid watch register style");
+ return;
+}
+
+static lldb::addr_t
+GetWatchLo (struct pt_watch_regs *regs, uint32_t index)
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (regs->style == pt_watch_style_mips32)
+ return regs->mips32.watchlo[index];
+ else if (regs->style == pt_watch_style_mips64)
+ return regs->mips64.watchlo[index];
+ if(log)
+ log->Printf("Invalid watch register style");
+ return LLDB_INVALID_ADDRESS;
+}
+
+static void
+SetWatchLo (struct pt_watch_regs *regs, uint32_t index, uint64_t value)
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (regs->style == pt_watch_style_mips32)
+ regs->mips32.watchlo[index] = (uint32_t) value;
+ else if (regs->style == pt_watch_style_mips64)
+ regs->mips64.watchlo[index] = value;
+ if(log)
+ log->Printf("Invalid watch register style");
+ return;
+}
+
+static uint32_t
+GetIRWMask (struct pt_watch_regs *regs, uint32_t index)
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (regs->style == pt_watch_style_mips32)
+ return regs->mips32.watch_masks[index] & IRW;
+ else if (regs->style == pt_watch_style_mips64)
+ return regs->mips64.watch_masks[index] & IRW;
+ if(log)
+ log->Printf("Invalid watch register style");
+ return 0;
+}
+
+static uint32_t
+GetRegMask (struct pt_watch_regs *regs, uint32_t index)
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (regs->style == pt_watch_style_mips32)
+ return regs->mips32.watch_masks[index] & ~IRW;
+ else if (regs->style == pt_watch_style_mips64)
+ return regs->mips64.watch_masks[index] & ~IRW;
+ if(log)
+ log->Printf("Invalid watch register style");
+ return 0;
+}
+
+static lldb::addr_t
+GetRangeMask (lldb::addr_t mask)
+{
+ lldb::addr_t mask_bit = 1;
+ while (mask_bit < mask)
+ {
+ mask = mask | mask_bit;
+ mask_bit <<= 1;
+ }
+ return mask;
+}
+
+static int
+GetVacantWatchIndex (struct pt_watch_regs *regs, lldb::addr_t addr, uint32_t size, uint32_t irw, uint32_t num_valid)
+{
+ lldb::addr_t last_byte = addr + size - 1;
+ lldb::addr_t mask = GetRangeMask (addr ^ last_byte) | IRW;
+ lldb::addr_t base_addr = addr & ~mask;
+
+ // Check if this address is already watched by previous watch points.
+ lldb::addr_t lo;
+ uint16_t hi;
+ uint32_t vacant_watches = 0;
+ for (uint32_t index = 0; index < num_valid; index++)
+ {
+ lo = GetWatchLo (regs, index);
+ if (lo != 0 && irw == ((uint32_t) lo & irw))
+ {
+ hi = GetWatchHi (regs, index) | IRW;
+ lo &= ~(lldb::addr_t) hi;
+ if (addr >= lo && last_byte <= (lo + hi))
+ return index;
+ }
+ else
+ vacant_watches++;
+ }
+
+ // Now try to find a vacant index
+ if(vacant_watches > 0)
+ {
+ vacant_watches = 0;
+ for (uint32_t index = 0; index < num_valid; index++)
+ {
+ lo = GetWatchLo (regs, index);
+ if (lo == 0
+ && irw == (GetIRWMask (regs, index) & irw))
+ {
+ if (mask <= (GetRegMask (regs, index) | IRW))
+ {
+ // It fits, we can use it.
+ SetWatchLo (regs, index, base_addr | irw);
+ SetWatchHi (regs, index, mask & ~IRW);
+ return index;
+ }
+ else
+ {
+ // It doesn't fit, but has the proper IRW capabilities
+ vacant_watches++;
+ }
+ }
+ }
+
+ if (vacant_watches > 1)
+ {
+ // Split this watchpoint accross several registers
+ struct pt_watch_regs regs_copy;
+ regs_copy = *regs;
+ lldb::addr_t break_addr;
+ uint32_t segment_size;
+ for (uint32_t index = 0; index < num_valid; index++)
+ {
+ lo = GetWatchLo (&regs_copy, index);
+ hi = GetRegMask (&regs_copy, index) | IRW;
+ if (lo == 0 && irw == (hi & irw))
+ {
+ lo = addr & ~(lldb::addr_t) hi;
+ break_addr = lo + hi + 1;
+ if (break_addr >= addr + size)
+ segment_size = size;
+ else
+ segment_size = break_addr - addr;
+ mask = GetRangeMask (addr ^ (addr + segment_size - 1));
+ SetWatchLo (&regs_copy, index, (addr & ~mask) | irw);
+ SetWatchHi (&regs_copy, index, mask & ~IRW);
+ if (break_addr >= addr + size)
+ {
+ *regs = regs_copy;
+ return index;
+ }
+ size = addr + size - break_addr;
+ addr = break_addr;
+ }
+ }
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+NativeRegisterContextLinux_mips64::IsMSA(uint32_t reg_index) const
+{
+ return (m_reg_info.first_msa <= reg_index && reg_index <= m_reg_info.last_msa);
+}
+
+bool
+NativeRegisterContextLinux_mips64::IsMSAAvailable()
+{
+ MSA_linux_mips msa_buf;
+ unsigned int regset = NT_MIPS_MSA;
+
+ Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, Host::GetCurrentProcessID(), static_cast<void *>(&regset), &msa_buf, sizeof(MSA_linux_mips));
+
+ if (error.Success() && msa_buf.mir)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+Error
+NativeRegisterContextLinux_mips64::IsWatchpointHit (uint32_t wp_index, bool &is_hit)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Error("Watchpoint index out of range");
+
+ // reading the current state of watch regs
+ struct pt_watch_regs watch_readback;
+ Error error = DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback));
+
+ if (GetWatchHi (&watch_readback, wp_index) & (IRW))
+ {
+ // clear hit flag in watchhi
+ SetWatchHi (&watch_readback, wp_index, (GetWatchHi (&watch_readback, wp_index) & ~(IRW)));
+ DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback));
+
+ is_hit = true;
+ return error;
+ }
+ is_hit = false;
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_mips64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) {
+ uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
+ for (wp_index = 0; wp_index < num_hw_wps; ++wp_index)
+ {
+ bool is_hit;
+ Error error = IsWatchpointHit(wp_index, is_hit);
+ if (error.Fail()) {
+ wp_index = LLDB_INVALID_INDEX32;
+ } else if (is_hit) {
+ return error;
+ }
+ }
+ wp_index = LLDB_INVALID_INDEX32;
+ return Error();
+}
+
+Error
+NativeRegisterContextLinux_mips64::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant)
+{
+ is_vacant = false;
+ return Error("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointVacant not implemented");
+}
+
+bool
+NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint(uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return false;
+
+ struct pt_watch_regs regs;
+ // First reading the current state of watch regs
+ DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void*>(&regs));
+
+ if (regs.style == pt_watch_style_mips32)
+ {
+ regs.mips32.watchlo[wp_index] = default_watch_regs.mips32.watchlo[wp_index];
+ regs.mips32.watchhi[wp_index] = default_watch_regs.mips32.watchhi[wp_index];
+ regs.mips32.watch_masks[wp_index] = default_watch_regs.mips32.watch_masks[wp_index];
+ }
+ else // pt_watch_style_mips64
+ {
+ regs.mips64.watchlo[wp_index] = default_watch_regs.mips64.watchlo[wp_index];
+ regs.mips64.watchhi[wp_index] = default_watch_regs.mips64.watchhi[wp_index];
+ regs.mips64.watch_masks[wp_index] = default_watch_regs.mips64.watch_masks[wp_index];
+ }
+
+ Error error = DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&regs));
+ if(!error.Fail())
+ {
+ hw_addr_map[wp_index] = LLDB_INVALID_ADDRESS;
+ return true;
+ }
+ return false;
+}
+
+Error
+NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints()
+{
+ return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&default_watch_regs));
+}
+
+Error
+NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex (
+ lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index)
+{
+ Error error;
+ error.SetErrorString ("MIPS TODO: NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex not implemented");
+ return error;
+}
+
+uint32_t
+NativeRegisterContextLinux_mips64::SetHardwareWatchpoint (
+ lldb::addr_t addr, size_t size, uint32_t watch_flags)
+{
+ struct pt_watch_regs regs;
+
+ // First reading the current state of watch regs
+ DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&regs));
+
+ // Try if a new watch point fits in this state
+ int index = GetVacantWatchIndex (&regs, addr, size, watch_flags, NumSupportedHardwareWatchpoints());
+
+ // New watchpoint doesn't fit
+ if (index == LLDB_INVALID_INDEX32)
+ return LLDB_INVALID_INDEX32;
+
+
+ // It fits, so we go ahead with updating the state of watch regs
+ DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&regs));
+
+ // Storing exact address
+ hw_addr_map[index] = addr;
+ return index;
+}
+
+lldb::addr_t
+NativeRegisterContextLinux_mips64::GetWatchpointAddress (uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return LLDB_INVALID_ADDRESS;
+
+ return hw_addr_map[wp_index];
+}
+
+struct EmulatorBaton
+{
+ lldb::addr_t m_watch_hit_addr;
+ NativeProcessLinux* m_process;
+ NativeRegisterContext* m_reg_context;
+
+ EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) :
+ m_watch_hit_addr(LLDB_INVALID_ADDRESS),
+ m_process(process),
+ m_reg_context(reg_context)
+ {}
+};
+
+static size_t
+ReadMemoryCallback (EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr,
+ void *dst, size_t length)
+{
+ size_t bytes_read;
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+ emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read);
+ return bytes_read;
+}
+
+static size_t
+WriteMemoryCallback (EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t length)
+{
+ return length;
+}
+
+static bool
+ReadRegisterCallback (EmulateInstruction *instruction, void *baton,
+ const RegisterInfo *reg_info, RegisterValue &reg_value)
+{
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+
+ const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo(
+ lldb::eRegisterKindDWARF, reg_info->kinds[lldb::eRegisterKindDWARF]);
+
+ Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
+ if (error.Success())
+ return true;
+
+ return false;
+}
+
+static bool
+WriteRegisterCallback (EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info, const RegisterValue &reg_value)
+{
+ if (reg_info->kinds[lldb::eRegisterKindDWARF] == dwarf_bad_mips64)
+ {
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+ emulator_baton->m_watch_hit_addr = reg_value.GetAsUInt64 ();
+ }
+
+ return true;
+}
+
+/*
+ * MIPS Linux kernel returns a masked address (last 3bits are masked)
+ * when a HW watchpoint is hit. However user may not have set a watchpoint
+ * on this address. Emulate instruction at PC and find the base address of
+ * the load/store instruction. This will give the exact address used to
+ * read/write the variable. Send this exact address to client so that
+ * it can decide to stop or continue the thread.
+*/
+lldb::addr_t
+NativeRegisterContextLinux_mips64::GetWatchpointHitAddress (uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return LLDB_INVALID_ADDRESS;
+
+ lldb_private::ArchSpec arch;
+ arch = GetRegisterInfoInterface().GetTargetArchitecture();
+ std::unique_ptr<EmulateInstruction> emulator_ap(
+ EmulateInstruction::FindPlugin(arch, lldb_private::eInstructionTypeAny, nullptr));
+
+ if (emulator_ap == nullptr)
+ return LLDB_INVALID_ADDRESS;
+
+ EmulatorBaton baton(static_cast<NativeProcessLinux*>(m_thread.GetProcess().get()), this);
+ emulator_ap->SetBaton (&baton);
+ emulator_ap->SetReadMemCallback (&ReadMemoryCallback);
+ emulator_ap->SetReadRegCallback (&ReadRegisterCallback);
+ emulator_ap->SetWriteMemCallback (&WriteMemoryCallback);
+ emulator_ap->SetWriteRegCallback (&WriteRegisterCallback);
+
+ if (!emulator_ap->ReadInstruction())
+ return LLDB_INVALID_ADDRESS;
+
+ if (emulator_ap->EvaluateInstruction(lldb::eEmulateInstructionOptionNone))
+ return baton.m_watch_hit_addr;
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+uint32_t
+NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints ()
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ struct pt_watch_regs regs;
+ static int num_valid = 0;
+ if (!num_valid)
+ {
+ DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&regs));
+ default_watch_regs = regs; // Keeping default watch regs values for future use
+ switch (regs.style)
+ {
+ case pt_watch_style_mips32:
+ num_valid = regs.mips32.num_valid; // Using num_valid as cache
+ return num_valid;
+ case pt_watch_style_mips64:
+ num_valid = regs.mips64.num_valid;
+ return num_valid;
+ default:
+ if(log)
+ log->Printf("NativeRegisterContextLinux_mips64::%s Error: Unrecognized watch register style", __FUNCTION__);
+ }
+ return 0;
+ }
+ return num_valid;
+}
+Error
+NativeRegisterContextLinux_mips64::DoReadRegisterValue(uint32_t offset,
+ const char* reg_name,
+ uint32_t size,
+ RegisterValue &value)
+{
+ GPR_linux_mips regs;
+ ::memset(&regs, 0, sizeof(GPR_linux_mips));
+
+ // Clear all bits in RegisterValue before writing actual value read from ptrace to avoid garbage value in 32-bit MSB
+ value.SetBytes((void *)(((unsigned char *)&regs) + offset), 8, GetByteOrder());
+ Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), NULL, &regs, sizeof regs);
+ if (error.Success())
+ {
+ lldb_private::ArchSpec arch;
+ if (m_thread.GetProcess()->GetArchitecture(arch))
+ value.SetBytes((void *)(((unsigned char *)&regs) + offset + 4 * (arch.GetMachine() == llvm::Triple::mips)), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8, arch.GetByteOrder());
+ else
+ error.SetErrorString("failed to get architecture");
+ }
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_mips64::DoWriteRegisterValue(uint32_t offset,
+ const char* reg_name,
+ const RegisterValue &value)
+{
+ GPR_linux_mips regs;
+ Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), NULL, &regs, sizeof regs);
+ if (error.Success())
+ {
+ lldb_private::ArchSpec arch;
+ if (m_thread.GetProcess()->GetArchitecture(arch))
+ {
+ ::memcpy((void *)(((unsigned char *)(&regs)) + offset), value.GetBytes(), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8);
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), NULL, &regs, sizeof regs);
+ }
+ else
+ error.SetErrorString("failed to get architecture");
+ }
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_mips64::DoReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback)
+{
+ return NativeProcessLinux::PtraceWrapper( PTRACE_GET_WATCH_REGS, m_thread.GetID(), watch_readback);
+}
+
+Error
+NativeRegisterContextLinux_mips64::DoWriteWatchPointRegisterValue(lldb::tid_t tid, void* watch_reg_value)
+{
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SET_WATCH_REGS, m_thread.GetID(), watch_reg_value);
+}
+
+#endif // defined (__mips__)
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h
new file mode 100644
index 0000000000000..9368645116e94
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h
@@ -0,0 +1,167 @@
+//===-- NativeRegisterContextLinux_mips64.h ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__mips__)
+
+#ifndef lldb_NativeRegisterContextLinux_mips64_h
+#define lldb_NativeRegisterContextLinux_mips64_h
+
+#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
+#include "Plugins/Process/Utility/RegisterContext_mips.h"
+#include "Plugins/Process/Utility/lldb-mips-linux-register-enums.h"
+
+#define MAX_NUM_WP 8
+
+namespace lldb_private {
+namespace process_linux {
+
+ class NativeProcessLinux;
+
+ class NativeRegisterContextLinux_mips64 : public NativeRegisterContextLinux
+ {
+ public:
+ NativeRegisterContextLinux_mips64 (const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx);
+
+ uint32_t
+ GetRegisterSetCount () const override;
+
+ lldb::addr_t
+ GetPCfromBreakpointLocation (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS) override;
+
+ lldb::addr_t
+ GetWatchpointHitAddress (uint32_t wp_index) override;
+
+ const RegisterSet *
+ GetRegisterSet (uint32_t set_index) const override;
+
+ Error
+ ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value) override;
+
+ Error
+ WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value) override;
+
+ Error
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override;
+
+ Error
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override;
+
+ Error
+ ReadCP1();
+
+ Error
+ WriteCP1();
+
+ Error
+ IsWatchpointHit (uint32_t wp_index, bool &is_hit) override;
+
+ Error
+ GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override;
+
+ Error
+ IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) override;
+
+ bool
+ ClearHardwareWatchpoint (uint32_t wp_index) override;
+
+ Error
+ ClearAllHardwareWatchpoints () override;
+
+ Error
+ SetHardwareWatchpointWithIndex (lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, uint32_t wp_index);
+
+ uint32_t
+ SetHardwareWatchpoint (lldb::addr_t addr, size_t size,
+ uint32_t watch_flags) override;
+
+ lldb::addr_t
+ GetWatchpointAddress (uint32_t wp_index) override;
+
+ uint32_t
+ NumSupportedHardwareWatchpoints () override;
+
+ static bool
+ IsMSAAvailable();
+
+ protected:
+ Error
+ DoReadRegisterValue(uint32_t offset,
+ const char* reg_name,
+ uint32_t size,
+ RegisterValue &value) override;
+
+ Error
+ DoWriteRegisterValue(uint32_t offset,
+ const char* reg_name,
+ const RegisterValue &value) override;
+
+ Error
+ DoReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback);
+
+ Error
+ DoWriteWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback);
+
+ bool
+ IsFR0();
+
+ bool
+ IsFRE();
+
+ bool
+ IsFPR(uint32_t reg_index) const;
+
+ bool
+ IsMSA(uint32_t reg_index) const;
+
+ void*
+ GetGPRBuffer() override { return &m_gpr; }
+
+ void*
+ GetFPRBuffer() override { return &m_fpr; }
+
+ size_t
+ GetFPRSize() override { return sizeof(FPR_linux_mips); }
+
+ private:
+ // Info about register ranges.
+ struct RegInfo
+ {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+ uint32_t first_msa;
+ uint32_t last_msa;
+ };
+
+ RegInfo m_reg_info;
+
+ GPR_linux_mips m_gpr;
+
+ FPR_linux_mips m_fpr;
+
+ MSA_linux_mips m_msa;
+
+ lldb::addr_t hw_addr_map[MAX_NUM_WP];
+
+ IOVEC_mips m_iovec;
+ };
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextLinux_mips64_h
+
+#endif // defined (__mips__)
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
new file mode 100755
index 0000000000000..2080d2ede3724
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
@@ -0,0 +1,1239 @@
+//===-- NativeRegisterContextLinux_x86_64.cpp ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "NativeRegisterContextLinux_x86_64.h"
+
+#include "lldb/Core/Log.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Host/HostInfo.h"
+
+#include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+
+// ----------------------------------------------------------------------------
+// Private namespace.
+// ----------------------------------------------------------------------------
+
+namespace
+{
+ // x86 32-bit general purpose registers.
+ const uint32_t
+ g_gpr_regnums_i386[] =
+ {
+ lldb_eax_i386,
+ lldb_ebx_i386,
+ lldb_ecx_i386,
+ lldb_edx_i386,
+ lldb_edi_i386,
+ lldb_esi_i386,
+ lldb_ebp_i386,
+ lldb_esp_i386,
+ lldb_eip_i386,
+ lldb_eflags_i386,
+ lldb_cs_i386,
+ lldb_fs_i386,
+ lldb_gs_i386,
+ lldb_ss_i386,
+ lldb_ds_i386,
+ lldb_es_i386,
+ lldb_ax_i386,
+ lldb_bx_i386,
+ lldb_cx_i386,
+ lldb_dx_i386,
+ lldb_di_i386,
+ lldb_si_i386,
+ lldb_bp_i386,
+ lldb_sp_i386,
+ lldb_ah_i386,
+ lldb_bh_i386,
+ lldb_ch_i386,
+ lldb_dh_i386,
+ lldb_al_i386,
+ lldb_bl_i386,
+ lldb_cl_i386,
+ lldb_dl_i386,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+ static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) - 1 == k_num_gpr_registers_i386,
+ "g_gpr_regnums_i386 has wrong number of register infos");
+
+ // x86 32-bit floating point registers.
+ const uint32_t
+ g_fpu_regnums_i386[] =
+ {
+ lldb_fctrl_i386,
+ lldb_fstat_i386,
+ lldb_ftag_i386,
+ lldb_fop_i386,
+ lldb_fiseg_i386,
+ lldb_fioff_i386,
+ lldb_foseg_i386,
+ lldb_fooff_i386,
+ lldb_mxcsr_i386,
+ lldb_mxcsrmask_i386,
+ lldb_st0_i386,
+ lldb_st1_i386,
+ lldb_st2_i386,
+ lldb_st3_i386,
+ lldb_st4_i386,
+ lldb_st5_i386,
+ lldb_st6_i386,
+ lldb_st7_i386,
+ lldb_mm0_i386,
+ lldb_mm1_i386,
+ lldb_mm2_i386,
+ lldb_mm3_i386,
+ lldb_mm4_i386,
+ lldb_mm5_i386,
+ lldb_mm6_i386,
+ lldb_mm7_i386,
+ lldb_xmm0_i386,
+ lldb_xmm1_i386,
+ lldb_xmm2_i386,
+ lldb_xmm3_i386,
+ lldb_xmm4_i386,
+ lldb_xmm5_i386,
+ lldb_xmm6_i386,
+ lldb_xmm7_i386,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+ static_assert((sizeof(g_fpu_regnums_i386) / sizeof(g_fpu_regnums_i386[0])) - 1 == k_num_fpr_registers_i386,
+ "g_fpu_regnums_i386 has wrong number of register infos");
+
+ // x86 32-bit AVX registers.
+ const uint32_t
+ g_avx_regnums_i386[] =
+ {
+ lldb_ymm0_i386,
+ lldb_ymm1_i386,
+ lldb_ymm2_i386,
+ lldb_ymm3_i386,
+ lldb_ymm4_i386,
+ lldb_ymm5_i386,
+ lldb_ymm6_i386,
+ lldb_ymm7_i386,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+ static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) - 1 == k_num_avx_registers_i386,
+ " g_avx_regnums_i386 has wrong number of register infos");
+
+ // x86 64-bit general purpose registers.
+ static const
+ uint32_t g_gpr_regnums_x86_64[] =
+ {
+ lldb_rax_x86_64,
+ lldb_rbx_x86_64,
+ lldb_rcx_x86_64,
+ lldb_rdx_x86_64,
+ lldb_rdi_x86_64,
+ lldb_rsi_x86_64,
+ lldb_rbp_x86_64,
+ lldb_rsp_x86_64,
+ lldb_r8_x86_64,
+ lldb_r9_x86_64,
+ lldb_r10_x86_64,
+ lldb_r11_x86_64,
+ lldb_r12_x86_64,
+ lldb_r13_x86_64,
+ lldb_r14_x86_64,
+ lldb_r15_x86_64,
+ lldb_rip_x86_64,
+ lldb_rflags_x86_64,
+ lldb_cs_x86_64,
+ lldb_fs_x86_64,
+ lldb_gs_x86_64,
+ lldb_ss_x86_64,
+ lldb_ds_x86_64,
+ lldb_es_x86_64,
+ lldb_eax_x86_64,
+ lldb_ebx_x86_64,
+ lldb_ecx_x86_64,
+ lldb_edx_x86_64,
+ lldb_edi_x86_64,
+ lldb_esi_x86_64,
+ lldb_ebp_x86_64,
+ lldb_esp_x86_64,
+ lldb_r8d_x86_64, // Low 32 bits or r8
+ lldb_r9d_x86_64, // Low 32 bits or r9
+ lldb_r10d_x86_64, // Low 32 bits or r10
+ lldb_r11d_x86_64, // Low 32 bits or r11
+ lldb_r12d_x86_64, // Low 32 bits or r12
+ lldb_r13d_x86_64, // Low 32 bits or r13
+ lldb_r14d_x86_64, // Low 32 bits or r14
+ lldb_r15d_x86_64, // Low 32 bits or r15
+ lldb_ax_x86_64,
+ lldb_bx_x86_64,
+ lldb_cx_x86_64,
+ lldb_dx_x86_64,
+ lldb_di_x86_64,
+ lldb_si_x86_64,
+ lldb_bp_x86_64,
+ lldb_sp_x86_64,
+ lldb_r8w_x86_64, // Low 16 bits or r8
+ lldb_r9w_x86_64, // Low 16 bits or r9
+ lldb_r10w_x86_64, // Low 16 bits or r10
+ lldb_r11w_x86_64, // Low 16 bits or r11
+ lldb_r12w_x86_64, // Low 16 bits or r12
+ lldb_r13w_x86_64, // Low 16 bits or r13
+ lldb_r14w_x86_64, // Low 16 bits or r14
+ lldb_r15w_x86_64, // Low 16 bits or r15
+ lldb_ah_x86_64,
+ lldb_bh_x86_64,
+ lldb_ch_x86_64,
+ lldb_dh_x86_64,
+ lldb_al_x86_64,
+ lldb_bl_x86_64,
+ lldb_cl_x86_64,
+ lldb_dl_x86_64,
+ lldb_dil_x86_64,
+ lldb_sil_x86_64,
+ lldb_bpl_x86_64,
+ lldb_spl_x86_64,
+ lldb_r8l_x86_64, // Low 8 bits or r8
+ lldb_r9l_x86_64, // Low 8 bits or r9
+ lldb_r10l_x86_64, // Low 8 bits or r10
+ lldb_r11l_x86_64, // Low 8 bits or r11
+ lldb_r12l_x86_64, // Low 8 bits or r12
+ lldb_r13l_x86_64, // Low 8 bits or r13
+ lldb_r14l_x86_64, // Low 8 bits or r14
+ lldb_r15l_x86_64, // Low 8 bits or r15
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+ static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) - 1 == k_num_gpr_registers_x86_64,
+ "g_gpr_regnums_x86_64 has wrong number of register infos");
+
+ // x86 64-bit floating point registers.
+ static const uint32_t
+ g_fpu_regnums_x86_64[] =
+ {
+ lldb_fctrl_x86_64,
+ lldb_fstat_x86_64,
+ lldb_ftag_x86_64,
+ lldb_fop_x86_64,
+ lldb_fiseg_x86_64,
+ lldb_fioff_x86_64,
+ lldb_foseg_x86_64,
+ lldb_fooff_x86_64,
+ lldb_mxcsr_x86_64,
+ lldb_mxcsrmask_x86_64,
+ lldb_st0_x86_64,
+ lldb_st1_x86_64,
+ lldb_st2_x86_64,
+ lldb_st3_x86_64,
+ lldb_st4_x86_64,
+ lldb_st5_x86_64,
+ lldb_st6_x86_64,
+ lldb_st7_x86_64,
+ lldb_mm0_x86_64,
+ lldb_mm1_x86_64,
+ lldb_mm2_x86_64,
+ lldb_mm3_x86_64,
+ lldb_mm4_x86_64,
+ lldb_mm5_x86_64,
+ lldb_mm6_x86_64,
+ lldb_mm7_x86_64,
+ lldb_xmm0_x86_64,
+ lldb_xmm1_x86_64,
+ lldb_xmm2_x86_64,
+ lldb_xmm3_x86_64,
+ lldb_xmm4_x86_64,
+ lldb_xmm5_x86_64,
+ lldb_xmm6_x86_64,
+ lldb_xmm7_x86_64,
+ lldb_xmm8_x86_64,
+ lldb_xmm9_x86_64,
+ lldb_xmm10_x86_64,
+ lldb_xmm11_x86_64,
+ lldb_xmm12_x86_64,
+ lldb_xmm13_x86_64,
+ lldb_xmm14_x86_64,
+ lldb_xmm15_x86_64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+ static_assert((sizeof(g_fpu_regnums_x86_64) / sizeof(g_fpu_regnums_x86_64[0])) - 1 == k_num_fpr_registers_x86_64,
+ "g_fpu_regnums_x86_64 has wrong number of register infos");
+
+ // x86 64-bit AVX registers.
+ static const uint32_t
+ g_avx_regnums_x86_64[] =
+ {
+ lldb_ymm0_x86_64,
+ lldb_ymm1_x86_64,
+ lldb_ymm2_x86_64,
+ lldb_ymm3_x86_64,
+ lldb_ymm4_x86_64,
+ lldb_ymm5_x86_64,
+ lldb_ymm6_x86_64,
+ lldb_ymm7_x86_64,
+ lldb_ymm8_x86_64,
+ lldb_ymm9_x86_64,
+ lldb_ymm10_x86_64,
+ lldb_ymm11_x86_64,
+ lldb_ymm12_x86_64,
+ lldb_ymm13_x86_64,
+ lldb_ymm14_x86_64,
+ lldb_ymm15_x86_64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+ };
+ static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) - 1 == k_num_avx_registers_x86_64,
+ "g_avx_regnums_x86_64 has wrong number of register infos");
+
+ // Number of register sets provided by this context.
+ enum
+ {
+ k_num_extended_register_sets = 1,
+ k_num_register_sets = 3
+ };
+
+ // Register sets for x86 32-bit.
+ static const RegisterSet
+ g_reg_sets_i386[k_num_register_sets] =
+ {
+ { "General Purpose Registers", "gpr", k_num_gpr_registers_i386, g_gpr_regnums_i386 },
+ { "Floating Point Registers", "fpu", k_num_fpr_registers_i386, g_fpu_regnums_i386 },
+ { "Advanced Vector Extensions", "avx", k_num_avx_registers_i386, g_avx_regnums_i386 }
+ };
+
+ // Register sets for x86 64-bit.
+ static const RegisterSet
+ g_reg_sets_x86_64[k_num_register_sets] =
+ {
+ { "General Purpose Registers", "gpr", k_num_gpr_registers_x86_64, g_gpr_regnums_x86_64 },
+ { "Floating Point Registers", "fpu", k_num_fpr_registers_x86_64, g_fpu_regnums_x86_64 },
+ { "Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64, g_avx_regnums_x86_64 }
+ };
+}
+
+#define REG_CONTEXT_SIZE (GetRegisterInfoInterface ().GetGPRSize () + sizeof(FPR))
+
+// ----------------------------------------------------------------------------
+// Required ptrace defines.
+// ----------------------------------------------------------------------------
+
+// Support ptrace extensions even when compiled without required kernel support
+#ifndef NT_X86_XSTATE
+#define NT_X86_XSTATE 0x202
+#endif
+#ifndef NT_PRXFPREG
+#define NT_PRXFPREG 0x46e62b7f
+#endif
+
+NativeRegisterContextLinux*
+NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx)
+{
+ return new NativeRegisterContextLinux_x86_64(target_arch, native_thread, concrete_frame_idx);
+}
+
+// ----------------------------------------------------------------------------
+// NativeRegisterContextLinux_x86_64 members.
+// ----------------------------------------------------------------------------
+
+static RegisterInfoInterface*
+CreateRegisterInfoInterface(const ArchSpec& target_arch)
+{
+ if (HostInfo::GetArchitecture().GetAddressByteSize() == 4)
+ {
+ // 32-bit hosts run with a RegisterContextLinux_i386 context.
+ return new RegisterContextLinux_i386(target_arch);
+ }
+ else
+ {
+ assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) &&
+ "Register setting path assumes this is a 64-bit host");
+ // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the x86_64 register context.
+ return new RegisterContextLinux_x86_64 (target_arch);
+ }
+}
+
+NativeRegisterContextLinux_x86_64::NativeRegisterContextLinux_x86_64 (const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx) :
+ NativeRegisterContextLinux (native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)),
+ m_fpr_type (eFPRTypeNotValid),
+ m_fpr (),
+ m_iovec (),
+ m_ymm_set (),
+ m_reg_info (),
+ m_gpr_x86_64 ()
+{
+ // Set up data about ranges of valid registers.
+ switch (target_arch.GetMachine ())
+ {
+ case llvm::Triple::x86:
+ m_reg_info.num_registers = k_num_registers_i386;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_i386;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_i386;
+ m_reg_info.num_avx_registers = k_num_avx_registers_i386;
+ m_reg_info.last_gpr = k_last_gpr_i386;
+ m_reg_info.first_fpr = k_first_fpr_i386;
+ m_reg_info.last_fpr = k_last_fpr_i386;
+ m_reg_info.first_st = lldb_st0_i386;
+ m_reg_info.last_st = lldb_st7_i386;
+ m_reg_info.first_mm = lldb_mm0_i386;
+ m_reg_info.last_mm = lldb_mm7_i386;
+ m_reg_info.first_xmm = lldb_xmm0_i386;
+ m_reg_info.last_xmm = lldb_xmm7_i386;
+ m_reg_info.first_ymm = lldb_ymm0_i386;
+ m_reg_info.last_ymm = lldb_ymm7_i386;
+ m_reg_info.first_dr = lldb_dr0_i386;
+ m_reg_info.gpr_flags = lldb_eflags_i386;
+ break;
+ case llvm::Triple::x86_64:
+ m_reg_info.num_registers = k_num_registers_x86_64;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_x86_64;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_x86_64;
+ m_reg_info.num_avx_registers = k_num_avx_registers_x86_64;
+ m_reg_info.last_gpr = k_last_gpr_x86_64;
+ m_reg_info.first_fpr = k_first_fpr_x86_64;
+ m_reg_info.last_fpr = k_last_fpr_x86_64;
+ m_reg_info.first_st = lldb_st0_x86_64;
+ m_reg_info.last_st = lldb_st7_x86_64;
+ m_reg_info.first_mm = lldb_mm0_x86_64;
+ m_reg_info.last_mm = lldb_mm7_x86_64;
+ m_reg_info.first_xmm = lldb_xmm0_x86_64;
+ m_reg_info.last_xmm = lldb_xmm15_x86_64;
+ m_reg_info.first_ymm = lldb_ymm0_x86_64;
+ m_reg_info.last_ymm = lldb_ymm15_x86_64;
+ m_reg_info.first_dr = lldb_dr0_x86_64;
+ m_reg_info.gpr_flags = lldb_rflags_x86_64;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ // Initialize m_iovec to point to the buffer and buffer size
+ // using the conventions of Berkeley style UIO structures, as required
+ // by PTRACE extensions.
+ m_iovec.iov_base = &m_fpr.xstate.xsave;
+ m_iovec.iov_len = sizeof(m_fpr.xstate.xsave);
+
+ // Clear out the FPR state.
+ ::memset(&m_fpr, 0, sizeof(FPR));
+
+ // Store byte offset of fctrl (i.e. first register of FPR)
+ const RegisterInfo *reg_info_fctrl = GetRegisterInfoByName("fctrl");
+ m_fctrl_offset_in_userarea = reg_info_fctrl->byte_offset;
+}
+
+// CONSIDER after local and llgs debugging are merged, register set support can
+// be moved into a base x86-64 class with IsRegisterSetAvailable made virtual.
+uint32_t
+NativeRegisterContextLinux_x86_64::GetRegisterSetCount () const
+{
+ uint32_t sets = 0;
+ for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index)
+ {
+ if (IsRegisterSetAvailable (set_index))
+ ++sets;
+ }
+
+ return sets;
+}
+
+uint32_t
+NativeRegisterContextLinux_x86_64::GetUserRegisterCount() const
+{
+ uint32_t count = 0;
+ for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index)
+ {
+ const RegisterSet* set = GetRegisterSet(set_index);
+ if (set)
+ count += set->num_registers;
+ }
+ return count;
+}
+
+const RegisterSet *
+NativeRegisterContextLinux_x86_64::GetRegisterSet (uint32_t set_index) const
+{
+ if (!IsRegisterSetAvailable (set_index))
+ return nullptr;
+
+ switch (GetRegisterInfoInterface ().GetTargetArchitecture ().GetMachine ())
+ {
+ case llvm::Triple::x86:
+ return &g_reg_sets_i386[set_index];
+ case llvm::Triple::x86_64:
+ return &g_reg_sets_x86_64[set_index];
+ default:
+ assert (false && "Unhandled target architecture.");
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+Error
+NativeRegisterContextLinux_x86_64::ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value)
+{
+ Error error;
+
+ if (!reg_info)
+ {
+ error.SetErrorString ("reg_info NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM)
+ {
+ // This is likely an internal register for lldb use only and should not be directly queried.
+ error.SetErrorStringWithFormat ("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name);
+ return error;
+ }
+
+ if (IsFPR(reg, GetFPRType()))
+ {
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+ }
+ else
+ {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg)
+ {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+
+ error = ReadRegisterRaw(full_reg, reg_value);
+
+ if (error.Success ())
+ {
+ // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right.
+ if (is_subreg && (reg_info->byte_offset & 0x1))
+ reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8);
+
+ // If our return byte size was greater than the return value reg size, then
+ // use the type specified by reg_info rather than the uint64_t default
+ if (reg_value.GetByteSize() > reg_info->byte_size)
+ reg_value.SetType(reg_info);
+ }
+ return error;
+ }
+
+ if (reg_info->encoding == lldb::eEncodingVector)
+ {
+ lldb::ByteOrder byte_order = GetByteOrder();
+
+ if (byte_order != lldb::eByteOrderInvalid)
+ {
+ if (reg >= m_reg_info.first_st && reg <= m_reg_info.last_st)
+ reg_value.SetBytes(m_fpr.xstate.fxsave.stmm[reg - m_reg_info.first_st].bytes, reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_mm && reg <= m_reg_info.last_mm)
+ reg_value.SetBytes(m_fpr.xstate.fxsave.stmm[reg - m_reg_info.first_mm].bytes, reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_xmm && reg <= m_reg_info.last_xmm)
+ reg_value.SetBytes(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_xmm].bytes, reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm)
+ {
+ // Concatenate ymm using the register halves in xmm.bytes and ymmh.bytes
+ if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoYMM(reg, byte_order))
+ reg_value.SetBytes(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes, reg_info->byte_size, byte_order);
+ else
+ {
+ error.SetErrorString ("failed to copy ymm register value");
+ return error;
+ }
+ }
+
+ if (reg_value.GetType() != RegisterValue::eTypeBytes)
+ error.SetErrorString ("write failed - type was expected to be RegisterValue::eTypeBytes");
+
+ return error;
+ }
+
+ error.SetErrorString ("byte order is invalid");
+ return error;
+ }
+
+ // Get pointer to m_fpr.xstate.fxsave variable and set the data from it.
+
+ // Byte offsets of all registers are calculated wrt 'UserArea' structure.
+ // However, ReadFPR() reads fpu registers {using ptrace(PTRACE_GETFPREGS,..)}
+ // and stores them in 'm_fpr' (of type FPR structure). To extract values of fpu
+ // registers, m_fpr should be read at byte offsets calculated wrt to FPR structure.
+
+ // Since, FPR structure is also one of the member of UserArea structure.
+ // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) - byte_offset(fctrl wrt UserArea)
+ assert ( (reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr));
+ uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea;
+ switch (reg_info->byte_size)
+ {
+ case 1:
+ reg_value.SetUInt8(*(uint8_t *)src);
+ break;
+ case 2:
+ reg_value.SetUInt16(*(uint16_t *)src);
+ break;
+ case 4:
+ reg_value.SetUInt32(*(uint32_t *)src);
+ break;
+ case 8:
+ reg_value.SetUInt64(*(uint64_t *)src);
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size);
+ break;
+ }
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_x86_64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value)
+{
+ assert (reg_info && "reg_info is null");
+
+ const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg_index == LLDB_INVALID_REGNUM)
+ return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>");
+
+ if (IsGPR(reg_index))
+ return WriteRegisterRaw(reg_index, reg_value);
+
+ if (IsFPR(reg_index, GetFPRType()))
+ {
+ if (reg_info->encoding == lldb::eEncodingVector)
+ {
+ if (reg_index >= m_reg_info.first_st && reg_index <= m_reg_info.last_st)
+ ::memcpy (m_fpr.xstate.fxsave.stmm[reg_index - m_reg_info.first_st].bytes, reg_value.GetBytes(), reg_value.GetByteSize());
+
+ if (reg_index >= m_reg_info.first_mm && reg_index <= m_reg_info.last_mm)
+ ::memcpy (m_fpr.xstate.fxsave.stmm[reg_index - m_reg_info.first_mm].bytes, reg_value.GetBytes(), reg_value.GetByteSize());
+
+ if (reg_index >= m_reg_info.first_xmm && reg_index <= m_reg_info.last_xmm)
+ ::memcpy (m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_xmm].bytes, reg_value.GetBytes(), reg_value.GetByteSize());
+
+ if (reg_index >= m_reg_info.first_ymm && reg_index <= m_reg_info.last_ymm)
+ {
+ if (GetFPRType() != eFPRTypeXSAVE)
+ return Error ("target processor does not support AVX");
+
+ // Store ymm register content, and split into the register halves in xmm.bytes and ymmh.bytes
+ ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes, reg_value.GetBytes(), reg_value.GetByteSize());
+ if (!CopyYMMtoXSTATE(reg_index, GetByteOrder()))
+ return Error ("CopyYMMtoXSTATE() failed");
+ }
+ }
+ else
+ {
+ // Get pointer to m_fpr.xstate.fxsave variable and set the data to it.
+
+ // Byte offsets of all registers are calculated wrt 'UserArea' structure.
+ // However, WriteFPR() takes m_fpr (of type FPR structure) and writes only fpu
+ // registers using ptrace(PTRACE_SETFPREGS,..) API. Hence fpu registers should
+ // be written in m_fpr at byte offsets calculated wrt FPR structure.
+
+ // Since, FPR structure is also one of the member of UserArea structure.
+ // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) - byte_offset(fctrl wrt UserArea)
+ assert ( (reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr));
+ uint8_t *dst = (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea;
+ switch (reg_info->byte_size)
+ {
+ case 1:
+ *(uint8_t *)dst = reg_value.GetAsUInt8();
+ break;
+ case 2:
+ *(uint16_t *)dst = reg_value.GetAsUInt16();
+ break;
+ case 4:
+ *(uint32_t *)dst = reg_value.GetAsUInt32();
+ break;
+ case 8:
+ *(uint64_t *)dst = reg_value.GetAsUInt64();
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ return Error ("unhandled register data size %" PRIu32, reg_info->byte_size);
+ }
+ }
+
+ Error error = WriteFPR();
+ if (error.Fail())
+ return error;
+
+ if (IsAVX(reg_index))
+ {
+ if (!CopyYMMtoXSTATE(reg_index, GetByteOrder()))
+ return Error ("CopyYMMtoXSTATE() failed");
+ }
+ return Error ();
+ }
+ return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown");
+}
+
+Error
+NativeRegisterContextLinux_x86_64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ Error error;
+
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (!data_sp)
+ {
+ error.SetErrorStringWithFormat ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE);
+ return error;
+ }
+
+ error = ReadGPR();
+ if (error.Fail())
+ return error;
+
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+
+ uint8_t *dst = data_sp->GetBytes ();
+ if (dst == nullptr)
+ {
+ error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE);
+ return error;
+ }
+
+ ::memcpy (dst, &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ());
+ dst += GetRegisterInfoInterface ().GetGPRSize ();
+ if (GetFPRType () == eFPRTypeFXSAVE)
+ ::memcpy (dst, &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave));
+ else if (GetFPRType () == eFPRTypeXSAVE)
+ {
+ lldb::ByteOrder byte_order = GetByteOrder ();
+
+ // Assemble the YMM register content from the register halves.
+ for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; ++reg)
+ {
+ if (!CopyXSTATEtoYMM (reg, byte_order))
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s CopyXSTATEtoYMM() failed for reg num %" PRIu32, __FUNCTION__, reg);
+ return error;
+ }
+ }
+
+ // Copy the extended register state including the assembled ymm registers.
+ ::memcpy (dst, &m_fpr, sizeof (m_fpr));
+ }
+ else
+ {
+ assert (false && "how do we save the floating point registers?");
+ error.SetErrorString ("unsure how to save the floating point registers");
+ }
+ /** The following code is specific to Linux x86 based architectures,
+ * where the register orig_eax (32 bit)/orig_rax (64 bit) is set to
+ * -1 to solve the bug 23659, such a setting prevents the automatic
+ * decrement of the instruction pointer which was causing the SIGILL
+ * exception.
+ * **/
+
+ RegisterValue value((uint64_t) -1);
+ const RegisterInfo *reg_info = GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_eax");
+ if (reg_info == nullptr)
+ reg_info = GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_rax");
+
+ if (reg_info != nullptr)
+ return DoWriteRegisterValue(reg_info->byte_offset,reg_info->name,value);
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_x86_64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ Error error;
+
+ if (!data_sp)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__);
+ return error;
+ }
+
+ if (data_sp->GetByteSize () != REG_CONTEXT_SIZE)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ());
+ return error;
+ }
+
+
+ uint8_t *src = data_sp->GetBytes ();
+ if (src == nullptr)
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__);
+ return error;
+ }
+ ::memcpy (&m_gpr_x86_64, src, GetRegisterInfoInterface ().GetGPRSize ());
+
+ error = WriteGPR();
+ if (error.Fail())
+ return error;
+
+ src += GetRegisterInfoInterface ().GetGPRSize ();
+ if (GetFPRType () == eFPRTypeFXSAVE)
+ ::memcpy (&m_fpr.xstate.fxsave, src, sizeof(m_fpr.xstate.fxsave));
+ else if (GetFPRType () == eFPRTypeXSAVE)
+ ::memcpy (&m_fpr.xstate.xsave, src, sizeof(m_fpr.xstate.xsave));
+
+ error = WriteFPR();
+ if (error.Fail())
+ return error;
+
+ if (GetFPRType() == eFPRTypeXSAVE)
+ {
+ lldb::ByteOrder byte_order = GetByteOrder();
+
+ // Parse the YMM register content from the register halves.
+ for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; ++reg)
+ {
+ if (!CopyYMMtoXSTATE (reg, byte_order))
+ {
+ error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s CopyYMMtoXSTATE() failed for reg num %" PRIu32, __FUNCTION__, reg);
+ return error;
+ }
+ }
+ }
+
+ return error;
+}
+
+bool
+NativeRegisterContextLinux_x86_64::IsRegisterSetAvailable (uint32_t set_index) const
+{
+ // Note: Extended register sets are assumed to be at the end of g_reg_sets.
+ uint32_t num_sets = k_num_register_sets - k_num_extended_register_sets;
+
+ if (GetFPRType () == eFPRTypeXSAVE)
+ {
+ // AVX is the first extended register set.
+ ++num_sets;
+ }
+ return (set_index < num_sets);
+}
+
+bool
+NativeRegisterContextLinux_x86_64::IsGPR(uint32_t reg_index) const
+{
+ // GPRs come first.
+ return reg_index <= m_reg_info.last_gpr;
+}
+
+NativeRegisterContextLinux_x86_64::FPRType
+NativeRegisterContextLinux_x86_64::GetFPRType () const
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (m_fpr_type == eFPRTypeNotValid)
+ {
+ // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx.
+
+ // Try and see if AVX register retrieval works.
+ m_fpr_type = eFPRTypeXSAVE;
+ if (const_cast<NativeRegisterContextLinux_x86_64*>(this)->ReadFPR().Fail())
+ {
+ // Fall back to general floating point with no AVX support.
+ m_fpr_type = eFPRTypeFXSAVE;
+
+ // Check if FXSAVE area can be read.
+ if (const_cast<NativeRegisterContextLinux_x86_64*>(this)->ReadFPR().Fail())
+ {
+ if (log)
+ log->Printf("NativeRegisterContextLinux_x86_64::%s ptrace APIs failed to read XSAVE/FXSAVE area", __FUNCTION__);
+ }
+ }
+ }
+ return m_fpr_type;
+}
+
+bool
+NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index) const
+{
+ return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr);
+}
+
+bool
+NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index, FPRType fpr_type) const
+{
+ bool generic_fpr = IsFPR(reg_index);
+
+ if (fpr_type == eFPRTypeXSAVE)
+ return generic_fpr || IsAVX(reg_index);
+ return generic_fpr;
+}
+
+Error
+NativeRegisterContextLinux_x86_64::WriteFPR()
+{
+ const FPRType fpr_type = GetFPRType ();
+ const lldb_private::ArchSpec& target_arch = GetRegisterInfoInterface().GetTargetArchitecture();
+ switch (fpr_type)
+ {
+ case FPRType::eFPRTypeFXSAVE:
+ // For 32-bit inferiors on x86_32/x86_64 architectures,
+ // FXSAVE area can be written using PTRACE_SETREGSET ptrace api
+ // For 64-bit inferiors on x86_64 architectures,
+ // FXSAVE area can be written using PTRACE_SETFPREGS ptrace api
+ switch (target_arch.GetMachine ())
+ {
+ case llvm::Triple::x86:
+ return WriteRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_PRXFPREG);
+ case llvm::Triple::x86_64:
+ return NativeRegisterContextLinux::WriteFPR();
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+ case FPRType::eFPRTypeXSAVE:
+ return WriteRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE);
+ default:
+ return Error("Unrecognized FPR type");
+ }
+}
+
+bool
+NativeRegisterContextLinux_x86_64::IsAVX(uint32_t reg_index) const
+{
+ return (m_reg_info.first_ymm <= reg_index && reg_index <= m_reg_info.last_ymm);
+}
+
+bool
+NativeRegisterContextLinux_x86_64::CopyXSTATEtoYMM (uint32_t reg_index, lldb::ByteOrder byte_order)
+{
+ if (!IsAVX (reg_index))
+ return false;
+
+ if (byte_order == lldb::eByteOrderLittle)
+ {
+ ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes,
+ m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_ymm].bytes,
+ sizeof (XMMReg));
+ ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes + sizeof (XMMReg),
+ m_fpr.xstate.xsave.ymmh[reg_index - m_reg_info.first_ymm].bytes,
+ sizeof (YMMHReg));
+ return true;
+ }
+
+ if (byte_order == lldb::eByteOrderBig)
+ {
+ ::memcpy(m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes + sizeof (XMMReg),
+ m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_ymm].bytes,
+ sizeof (XMMReg));
+ ::memcpy(m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes,
+ m_fpr.xstate.xsave.ymmh[reg_index - m_reg_info.first_ymm].bytes,
+ sizeof (YMMHReg));
+ return true;
+ }
+ return false; // unsupported or invalid byte order
+
+}
+
+bool
+NativeRegisterContextLinux_x86_64::CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order)
+{
+ if (!IsAVX(reg))
+ return false;
+
+ if (byte_order == lldb::eByteOrderLittle)
+ {
+ ::memcpy(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_ymm].bytes,
+ m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes,
+ sizeof(XMMReg));
+ ::memcpy(m_fpr.xstate.xsave.ymmh[reg - m_reg_info.first_ymm].bytes,
+ m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes + sizeof(XMMReg),
+ sizeof(YMMHReg));
+ return true;
+ }
+
+ if (byte_order == lldb::eByteOrderBig)
+ {
+ ::memcpy(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_ymm].bytes,
+ m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes + sizeof(XMMReg),
+ sizeof(XMMReg));
+ ::memcpy(m_fpr.xstate.xsave.ymmh[reg - m_reg_info.first_ymm].bytes,
+ m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes,
+ sizeof(YMMHReg));
+ return true;
+ }
+ return false; // unsupported or invalid byte order
+}
+
+void*
+NativeRegisterContextLinux_x86_64::GetFPRBuffer()
+{
+ const FPRType fpr_type = GetFPRType ();
+ switch (fpr_type)
+ {
+ case FPRType::eFPRTypeFXSAVE:
+ return &m_fpr.xstate.fxsave;
+ case FPRType::eFPRTypeXSAVE:
+ return &m_iovec;
+ default:
+ return nullptr;
+ }
+}
+
+size_t
+NativeRegisterContextLinux_x86_64::GetFPRSize()
+{
+ const FPRType fpr_type = GetFPRType ();
+ switch (fpr_type)
+ {
+ case FPRType::eFPRTypeFXSAVE:
+ return sizeof(m_fpr.xstate.fxsave);
+ case FPRType::eFPRTypeXSAVE:
+ return sizeof(m_iovec);
+ default:
+ return 0;
+ }
+}
+
+Error
+NativeRegisterContextLinux_x86_64::ReadFPR ()
+{
+ const FPRType fpr_type = GetFPRType ();
+ const lldb_private::ArchSpec& target_arch = GetRegisterInfoInterface().GetTargetArchitecture();
+ switch (fpr_type)
+ {
+ case FPRType::eFPRTypeFXSAVE:
+ // For 32-bit inferiors on x86_32/x86_64 architectures,
+ // FXSAVE area can be read using PTRACE_GETREGSET ptrace api
+ // For 64-bit inferiors on x86_64 architectures,
+ // FXSAVE area can be read using PTRACE_GETFPREGS ptrace api
+ switch (target_arch.GetMachine ())
+ {
+ case llvm::Triple::x86:
+ return ReadRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_PRXFPREG);
+ case llvm::Triple::x86_64:
+ return NativeRegisterContextLinux::ReadFPR();
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+ case FPRType::eFPRTypeXSAVE:
+ return ReadRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE);
+ default:
+ return Error("Unrecognized FPR type");
+ }
+}
+
+Error
+NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint32_t wp_index, bool &is_hit)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Error("Watchpoint index out of range");
+
+ RegisterValue reg_value;
+ Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value);
+ if (error.Fail())
+ {
+ is_hit = false;
+ return error;
+ }
+
+ uint64_t status_bits = reg_value.GetAsUInt64();
+
+ is_hit = status_bits & (1 << wp_index);
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_x86_64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) {
+ uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
+ for (wp_index = 0; wp_index < num_hw_wps; ++wp_index)
+ {
+ bool is_hit;
+ Error error = IsWatchpointHit(wp_index, is_hit);
+ if (error.Fail()) {
+ wp_index = LLDB_INVALID_INDEX32;
+ return error;
+ } else if (is_hit) {
+ return error;
+ }
+ }
+ wp_index = LLDB_INVALID_INDEX32;
+ return Error();
+}
+
+Error
+NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Error ("Watchpoint index out of range");
+
+ RegisterValue reg_value;
+ Error error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value);
+ if (error.Fail())
+ {
+ is_vacant = false;
+ return error;
+ }
+
+ uint64_t control_bits = reg_value.GetAsUInt64();
+
+ is_vacant = !(control_bits & (1 << (2 * wp_index)));
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_x86_64::SetHardwareWatchpointWithIndex(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
+
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Error ("Watchpoint index out of range");
+
+ // Read only watchpoints aren't supported on x86_64. Fall back to read/write waitchpoints instead.
+ // TODO: Add logic to detect when a write happens and ignore that watchpoint hit.
+ if (watch_flags == 0x2)
+ watch_flags = 0x3;
+
+ if (watch_flags != 0x1 && watch_flags != 0x3)
+ return Error ("Invalid read/write bits for watchpoint");
+
+ if (size != 1 && size != 2 && size != 4 && size != 8)
+ return Error ("Invalid size for watchpoint");
+
+ bool is_vacant;
+ Error error = IsWatchpointVacant (wp_index, is_vacant);
+ if (error.Fail()) return error;
+ if (!is_vacant) return Error("Watchpoint index not vacant");
+
+ RegisterValue reg_value;
+ error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value);
+ if (error.Fail()) return error;
+
+ // for watchpoints 0, 1, 2, or 3, respectively,
+ // set bits 1, 3, 5, or 7
+ uint64_t enable_bit = 1 << (2 * wp_index);
+
+ // set bits 16-17, 20-21, 24-25, or 28-29
+ // with 0b01 for write, and 0b11 for read/write
+ uint64_t rw_bits = watch_flags << (16 + 4 * wp_index);
+
+ // set bits 18-19, 22-23, 26-27, or 30-31
+ // with 0b00, 0b01, 0b10, or 0b11
+ // for 1, 2, 8 (if supported), or 4 bytes, respectively
+ uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index);
+
+ uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
+
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+
+ control_bits |= enable_bit | rw_bits | size_bits;
+
+ error = WriteRegisterRaw(m_reg_info.first_dr + wp_index, RegisterValue(addr));
+ if (error.Fail()) return error;
+
+ error = WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits));
+ if (error.Fail()) return error;
+
+ error.Clear();
+ return error;
+}
+
+bool
+NativeRegisterContextLinux_x86_64::ClearHardwareWatchpoint(uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return false;
+
+ RegisterValue reg_value;
+
+ // for watchpoints 0, 1, 2, or 3, respectively,
+ // clear bits 0, 1, 2, or 3 of the debug status register (DR6)
+ Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value);
+ if (error.Fail()) return false;
+ uint64_t bit_mask = 1 << wp_index;
+ uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ error = WriteRegisterRaw(m_reg_info.first_dr + 6, RegisterValue(status_bits));
+ if (error.Fail()) return false;
+
+ // for watchpoints 0, 1, 2, or 3, respectively,
+ // clear bits {0-1,16-19}, {2-3,20-23}, {4-5,24-27}, or {6-7,28-31}
+ // of the debug control register (DR7)
+ error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value);
+ if (error.Fail()) return false;
+ bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ return WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)).Success();
+}
+
+Error
+NativeRegisterContextLinux_x86_64::ClearAllHardwareWatchpoints()
+{
+ RegisterValue reg_value;
+
+ // clear bits {0-4} of the debug status register (DR6)
+ Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value);
+ if (error.Fail()) return error;
+ uint64_t bit_mask = 0xF;
+ uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ error = WriteRegisterRaw(m_reg_info.first_dr + 6, RegisterValue(status_bits));
+ if (error.Fail()) return error;
+
+ // clear bits {0-7,16-31} of the debug control register (DR7)
+ error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value);
+ if (error.Fail()) return error;
+ bit_mask = 0xFF | (0xFFFF << 16);
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ return WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits));
+}
+
+uint32_t
+NativeRegisterContextLinux_x86_64::SetHardwareWatchpoint(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags)
+{
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index)
+ {
+ bool is_vacant;
+ Error error = IsWatchpointVacant(wp_index, is_vacant);
+ if (is_vacant)
+ {
+ error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index);
+ if (error.Success())
+ return wp_index;
+ }
+ if (error.Fail() && log)
+ {
+ log->Printf("NativeRegisterContextLinux_x86_64::%s Error: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+lldb::addr_t
+NativeRegisterContextLinux_x86_64::GetWatchpointAddress(uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return LLDB_INVALID_ADDRESS;
+ RegisterValue reg_value;
+ if (ReadRegisterRaw(m_reg_info.first_dr + wp_index, reg_value).Fail())
+ return LLDB_INVALID_ADDRESS;
+ return reg_value.GetAsUInt64();
+}
+
+uint32_t
+NativeRegisterContextLinux_x86_64::NumSupportedHardwareWatchpoints ()
+{
+ // Available debug address registers: dr0, dr1, dr2, dr3
+ return 4;
+}
+
+#endif // defined(__i386__) || defined(__x86_64__)
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
new file mode 100644
index 0000000000000..b04be4bb76880
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
@@ -0,0 +1,171 @@
+//===-- NativeRegisterContextLinux_x86_64.h ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#ifndef lldb_NativeRegisterContextLinux_x86_64_h
+#define lldb_NativeRegisterContextLinux_x86_64_h
+
+#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
+#include "Plugins/Process/Utility/RegisterContext_x86.h"
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+namespace lldb_private {
+namespace process_linux {
+
+ class NativeProcessLinux;
+
+ class NativeRegisterContextLinux_x86_64 : public NativeRegisterContextLinux
+ {
+ public:
+ NativeRegisterContextLinux_x86_64 (const ArchSpec& target_arch,
+ NativeThreadProtocol &native_thread,
+ uint32_t concrete_frame_idx);
+
+ uint32_t
+ GetRegisterSetCount () const override;
+
+ const RegisterSet *
+ GetRegisterSet (uint32_t set_index) const override;
+
+ uint32_t
+ GetUserRegisterCount() const override;
+
+ Error
+ ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value) override;
+
+ Error
+ WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value) override;
+
+ Error
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override;
+
+ Error
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override;
+
+ Error
+ IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
+
+ Error
+ GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override;
+
+ Error
+ IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
+
+ bool
+ ClearHardwareWatchpoint(uint32_t wp_index) override;
+
+ Error
+ ClearAllHardwareWatchpoints () override;
+
+ Error
+ SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, uint32_t wp_index);
+
+ uint32_t
+ SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags) override;
+
+ lldb::addr_t
+ GetWatchpointAddress(uint32_t wp_index) override;
+
+ uint32_t
+ NumSupportedHardwareWatchpoints() override;
+
+ protected:
+ void*
+ GetGPRBuffer() override { return &m_gpr_x86_64; }
+
+ void*
+ GetFPRBuffer() override;
+
+ size_t
+ GetFPRSize() override;
+
+ Error
+ ReadFPR() override;
+
+ Error
+ WriteFPR() override;
+
+ private:
+
+ // Private member types.
+ enum FPRType
+ {
+ eFPRTypeNotValid = 0,
+ eFPRTypeFXSAVE,
+ eFPRTypeXSAVE
+ };
+
+ // Info about register ranges.
+ struct RegInfo
+ {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+ uint32_t num_avx_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_st;
+ uint32_t last_st;
+ uint32_t first_mm;
+ uint32_t last_mm;
+ uint32_t first_xmm;
+ uint32_t last_xmm;
+ uint32_t first_ymm;
+ uint32_t last_ymm;
+
+ uint32_t first_dr;
+ uint32_t gpr_flags;
+ };
+
+ // Private member variables.
+ mutable FPRType m_fpr_type;
+ FPR m_fpr;
+ IOVEC m_iovec;
+ YMM m_ymm_set;
+ RegInfo m_reg_info;
+ uint64_t m_gpr_x86_64[k_num_gpr_registers_x86_64];
+ uint32_t m_fctrl_offset_in_userarea;
+
+ // Private member methods.
+ bool IsRegisterSetAvailable (uint32_t set_index) const;
+
+ bool
+ IsGPR(uint32_t reg_index) const;
+
+ FPRType
+ GetFPRType () const;
+
+ bool
+ IsFPR(uint32_t reg_index) const;
+
+ bool
+ IsFPR(uint32_t reg_index, FPRType fpr_type) const;
+
+ bool
+ CopyXSTATEtoYMM (uint32_t reg_index, lldb::ByteOrder byte_order);
+
+ bool
+ CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order);
+
+ bool
+ IsAVX (uint32_t reg_index) const;
+ };
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextLinux_x86_64_h
+
+#endif // defined(__i386__) || defined(__x86_64__)
diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/source/Plugins/Process/Linux/NativeThreadLinux.cpp
new file mode 100644
index 0000000000000..cbf82885e23a6
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -0,0 +1,440 @@
+//===-- NativeThreadLinux.cpp --------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeThreadLinux.h"
+
+#include <signal.h>
+#include <sstream>
+
+#include "NativeProcessLinux.h"
+#include "NativeRegisterContextLinux.h"
+
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Host/HostNativeThread.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "llvm/ADT/SmallString.h"
+
+#include "Plugins/Process/POSIX/CrashReason.h"
+
+#include <sys/syscall.h>
+// Try to define a macro to encapsulate the tgkill syscall
+#define tgkill(pid, tid, sig) \
+ syscall(SYS_tgkill, static_cast< ::pid_t>(pid), static_cast< ::pid_t>(tid), sig)
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+
+namespace
+{
+ void LogThreadStopInfo (Log &log, const ThreadStopInfo &stop_info, const char *const header)
+ {
+ switch (stop_info.reason)
+ {
+ case eStopReasonNone:
+ log.Printf ("%s: %s no stop reason", __FUNCTION__, header);
+ return;
+ case eStopReasonTrace:
+ log.Printf ("%s: %s trace, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo);
+ return;
+ case eStopReasonBreakpoint:
+ log.Printf ("%s: %s breakpoint, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo);
+ return;
+ case eStopReasonWatchpoint:
+ log.Printf ("%s: %s watchpoint, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo);
+ return;
+ case eStopReasonSignal:
+ log.Printf ("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo);
+ return;
+ case eStopReasonException:
+ log.Printf ("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, stop_info.details.exception.type);
+ return;
+ case eStopReasonExec:
+ log.Printf ("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo);
+ return;
+ case eStopReasonPlanComplete:
+ log.Printf ("%s: %s plan complete", __FUNCTION__, header);
+ return;
+ case eStopReasonThreadExiting:
+ log.Printf ("%s: %s thread exiting", __FUNCTION__, header);
+ return;
+ case eStopReasonInstrumentation:
+ log.Printf ("%s: %s instrumentation", __FUNCTION__, header);
+ return;
+ default:
+ log.Printf ("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header, static_cast<uint32_t> (stop_info.reason));
+ }
+ }
+}
+
+NativeThreadLinux::NativeThreadLinux (NativeProcessLinux *process, lldb::tid_t tid) :
+ NativeThreadProtocol (process, tid),
+ m_state (StateType::eStateInvalid),
+ m_stop_info (),
+ m_reg_context_sp (),
+ m_stop_description ()
+{
+}
+
+std::string
+NativeThreadLinux::GetName()
+{
+ NativeProcessProtocolSP process_sp = m_process_wp.lock ();
+ if (!process_sp)
+ return "<unknown: no process>";
+
+ // const NativeProcessLinux *const process = reinterpret_cast<NativeProcessLinux*> (process_sp->get ());
+ llvm::SmallString<32> thread_name;
+ HostNativeThread::GetName(GetID(), thread_name);
+ return thread_name.c_str();
+}
+
+lldb::StateType
+NativeThreadLinux::GetState ()
+{
+ return m_state;
+}
+
+
+bool
+NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info, std::string& description)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+
+ description.clear();
+
+ switch (m_state)
+ {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateExited:
+ case eStateSuspended:
+ case eStateUnloaded:
+ if (log)
+ LogThreadStopInfo (*log, m_stop_info, "m_stop_info in thread:");
+ stop_info = m_stop_info;
+ description = m_stop_description;
+ if (log)
+ LogThreadStopInfo (*log, stop_info, "returned stop_info:");
+
+ return true;
+
+ case eStateInvalid:
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateDetached:
+ if (log)
+ {
+ log->Printf ("NativeThreadLinux::%s tid %" PRIu64 " in state %s cannot answer stop reason",
+ __FUNCTION__, GetID (), StateAsCString (m_state));
+ }
+ return false;
+ }
+ llvm_unreachable("unhandled StateType!");
+}
+
+NativeRegisterContextSP
+NativeThreadLinux::GetRegisterContext ()
+{
+ // Return the register context if we already created it.
+ if (m_reg_context_sp)
+ return m_reg_context_sp;
+
+ NativeProcessProtocolSP m_process_sp = m_process_wp.lock ();
+ if (!m_process_sp)
+ return NativeRegisterContextSP ();
+
+ ArchSpec target_arch;
+ if (!m_process_sp->GetArchitecture (target_arch))
+ return NativeRegisterContextSP ();
+
+ const uint32_t concrete_frame_idx = 0;
+ m_reg_context_sp.reset (NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(target_arch,
+ *this,
+ concrete_frame_idx));
+
+ return m_reg_context_sp;
+}
+
+Error
+NativeThreadLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware)
+{
+ if (!hardware)
+ return Error ("not implemented");
+ if (m_state == eStateLaunching)
+ return Error ();
+ Error error = RemoveWatchpoint(addr);
+ if (error.Fail()) return error;
+ NativeRegisterContextSP reg_ctx = GetRegisterContext ();
+ uint32_t wp_index =
+ reg_ctx->SetHardwareWatchpoint (addr, size, watch_flags);
+ if (wp_index == LLDB_INVALID_INDEX32)
+ return Error ("Setting hardware watchpoint failed.");
+ m_watchpoint_index_map.insert({addr, wp_index});
+ return Error ();
+}
+
+Error
+NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr)
+{
+ auto wp = m_watchpoint_index_map.find(addr);
+ if (wp == m_watchpoint_index_map.end())
+ return Error ();
+ uint32_t wp_index = wp->second;
+ m_watchpoint_index_map.erase(wp);
+ if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index))
+ return Error ();
+ return Error ("Clearing hardware watchpoint failed.");
+}
+
+void
+NativeThreadLinux::SetRunning ()
+{
+ const StateType new_state = StateType::eStateRunning;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonNone;
+ m_stop_description.clear();
+
+ // If watchpoints have been set, but none on this thread,
+ // then this is a new thread. So set all existing watchpoints.
+ if (m_watchpoint_index_map.empty())
+ {
+ const auto process_sp = GetProcess();
+ if (process_sp)
+ {
+ const auto &watchpoint_map = process_sp->GetWatchpointMap();
+ if (watchpoint_map.empty()) return;
+ GetRegisterContext()->ClearAllHardwareWatchpoints();
+ for (const auto &pair : watchpoint_map)
+ {
+ const auto& wp = pair.second;
+ SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware);
+ }
+ }
+ }
+}
+
+void
+NativeThreadLinux::SetStepping ()
+{
+ const StateType new_state = StateType::eStateStepping;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonNone;
+}
+
+void
+NativeThreadLinux::SetStoppedBySignal(uint32_t signo, const siginfo_t *info)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf ("NativeThreadLinux::%s called with signal 0x%02" PRIx32, __FUNCTION__, signo);
+
+ const StateType new_state = StateType::eStateStopped;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonSignal;
+ m_stop_info.details.signal.signo = signo;
+
+ m_stop_description.clear();
+ if (info)
+ {
+ switch (signo)
+ {
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ //In case of MIPS64 target, SI_KERNEL is generated for invalid 64bit address.
+ const auto reason = (info->si_signo == SIGBUS && info->si_code == SI_KERNEL) ?
+ CrashReason::eInvalidAddress : GetCrashReason(*info);
+ m_stop_description = GetCrashReasonString(reason, reinterpret_cast<uintptr_t>(info->si_addr));
+ break;
+ }
+ }
+}
+
+bool
+NativeThreadLinux::IsStopped (int *signo)
+{
+ if (!StateIsStoppedState (m_state, false))
+ return false;
+
+ // If we are stopped by a signal, return the signo.
+ if (signo &&
+ m_state == StateType::eStateStopped &&
+ m_stop_info.reason == StopReason::eStopReasonSignal)
+ {
+ *signo = m_stop_info.details.signal.signo;
+ }
+
+ // Regardless, we are stopped.
+ return true;
+}
+
+
+void
+NativeThreadLinux::SetStoppedByExec ()
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf ("NativeThreadLinux::%s()", __FUNCTION__);
+
+ const StateType new_state = StateType::eStateStopped;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonExec;
+ m_stop_info.details.signal.signo = SIGSTOP;
+}
+
+void
+NativeThreadLinux::SetStoppedByBreakpoint ()
+{
+ const StateType new_state = StateType::eStateStopped;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonBreakpoint;
+ m_stop_info.details.signal.signo = SIGTRAP;
+ m_stop_description.clear();
+}
+
+void
+NativeThreadLinux::SetStoppedByWatchpoint (uint32_t wp_index)
+{
+ const StateType new_state = StateType::eStateStopped;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+ m_stop_description.clear ();
+
+ lldbassert(wp_index != LLDB_INVALID_INDEX32 &&
+ "wp_index cannot be invalid");
+
+ std::ostringstream ostr;
+ ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " ";
+ ostr << wp_index;
+
+ /*
+ * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For example:
+ * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at 'm', then
+ * watch exception is generated even when 'n' is read/written. To handle this case,
+ * find the base address of the load/store instruction and append it in the stop-info
+ * packet.
+ */
+ ostr << " " << GetRegisterContext()->GetWatchpointHitAddress(wp_index);
+
+ m_stop_description = ostr.str();
+
+ m_stop_info.reason = StopReason::eStopReasonWatchpoint;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+bool
+NativeThreadLinux::IsStoppedAtBreakpoint ()
+{
+ return GetState () == StateType::eStateStopped &&
+ m_stop_info.reason == StopReason::eStopReasonBreakpoint;
+}
+
+bool
+NativeThreadLinux::IsStoppedAtWatchpoint ()
+{
+ return GetState () == StateType::eStateStopped &&
+ m_stop_info.reason == StopReason::eStopReasonWatchpoint;
+}
+
+void
+NativeThreadLinux::SetStoppedByTrace ()
+{
+ const StateType new_state = StateType::eStateStopped;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonTrace;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void
+NativeThreadLinux::SetStoppedWithNoReason ()
+{
+ const StateType new_state = StateType::eStateStopped;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonNone;
+ m_stop_info.details.signal.signo = 0;
+}
+
+void
+NativeThreadLinux::SetExited ()
+{
+ const StateType new_state = StateType::eStateExited;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonThreadExiting;
+}
+
+Error
+NativeThreadLinux::RequestStop ()
+{
+ Log* log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+
+ const auto process_sp = GetProcess();
+ if (! process_sp)
+ return Error("Process is null.");
+
+ lldb::pid_t pid = process_sp->GetID();
+ lldb::tid_t tid = GetID();
+
+ if (log)
+ log->Printf ("NativeThreadLinux::%s requesting thread stop(pid: %" PRIu64 ", tid: %" PRIu64 ")", __FUNCTION__, pid, tid);
+
+ Error err;
+ errno = 0;
+ if (::tgkill (pid, tid, SIGSTOP) != 0)
+ {
+ err.SetErrorToErrno ();
+ if (log)
+ log->Printf ("NativeThreadLinux::%s tgkill(%" PRIu64 ", %" PRIu64 ", SIGSTOP) failed: %s", __FUNCTION__, pid, tid, err.AsCString ());
+ }
+
+ return err;
+}
+
+void
+NativeThreadLinux::MaybeLogStateChange (lldb::StateType new_state)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ // If we're not logging, we're done.
+ if (!log)
+ return;
+
+ // If this is a state change to the same state, we're done.
+ lldb::StateType old_state = m_state;
+ if (new_state == old_state)
+ return;
+
+ NativeProcessProtocolSP m_process_sp = m_process_wp.lock ();
+ lldb::pid_t pid = m_process_sp ? m_process_sp->GetID () : LLDB_INVALID_PROCESS_ID;
+
+ // Log it.
+ log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") changing from state %s to %s", pid, GetID (), StateAsCString (old_state), StateAsCString (new_state));
+}
diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.h b/source/Plugins/Process/Linux/NativeThreadLinux.h
new file mode 100644
index 0000000000000..bf6b00a78cfda
--- /dev/null
+++ b/source/Plugins/Process/Linux/NativeThreadLinux.h
@@ -0,0 +1,120 @@
+//===-- NativeThreadLinux.h ----------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NativeThreadLinux_H_
+#define liblldb_NativeThreadLinux_H_
+
+#include "lldb/lldb-private-forward.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include <map>
+#include <memory>
+#include <string>
+
+namespace lldb_private {
+namespace process_linux {
+
+ class NativeProcessLinux;
+
+ class NativeThreadLinux : public NativeThreadProtocol
+ {
+ friend class NativeProcessLinux;
+
+ public:
+ NativeThreadLinux (NativeProcessLinux *process, lldb::tid_t tid);
+
+ // ---------------------------------------------------------------------
+ // NativeThreadProtocol Interface
+ // ---------------------------------------------------------------------
+ std::string
+ GetName() override;
+
+ lldb::StateType
+ GetState () override;
+
+ bool
+ GetStopReason (ThreadStopInfo &stop_info, std::string& description) override;
+
+ NativeRegisterContextSP
+ GetRegisterContext () override;
+
+ Error
+ SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override;
+
+ Error
+ RemoveWatchpoint (lldb::addr_t addr) override;
+
+ private:
+ // ---------------------------------------------------------------------
+ // Interface for friend classes
+ // ---------------------------------------------------------------------
+ void
+ SetRunning ();
+
+ void
+ SetStepping ();
+
+ void
+ SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
+
+ /// Return true if the thread is stopped.
+ /// If stopped by a signal, indicate the signo in the signo argument.
+ /// Otherwise, return LLDB_INVALID_SIGNAL_NUMBER.
+ bool
+ IsStopped (int *signo);
+
+ void
+ SetStoppedByExec ();
+
+ void
+ SetStoppedByBreakpoint ();
+
+ void
+ SetStoppedByWatchpoint (uint32_t wp_index);
+
+ bool
+ IsStoppedAtBreakpoint ();
+
+ bool
+ IsStoppedAtWatchpoint ();
+
+ void
+ SetStoppedByTrace ();
+
+ void
+ SetStoppedWithNoReason ();
+
+ void
+ SetExited ();
+
+ Error
+ RequestStop ();
+
+ // ---------------------------------------------------------------------
+ // Private interface
+ // ---------------------------------------------------------------------
+ void
+ MaybeLogStateChange (lldb::StateType new_state);
+
+ // ---------------------------------------------------------------------
+ // Member Variables
+ // ---------------------------------------------------------------------
+ lldb::StateType m_state;
+ ThreadStopInfo m_stop_info;
+ NativeRegisterContextSP m_reg_context_sp;
+ std::string m_stop_description;
+ using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
+ WatchpointIndexMap m_watchpoint_index_map;
+ };
+
+ typedef std::shared_ptr<NativeThreadLinux> NativeThreadLinuxSP;
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeThreadLinux_H_
diff --git a/source/Plugins/Process/Linux/ProcFileReader.cpp b/source/Plugins/Process/Linux/ProcFileReader.cpp
new file mode 100644
index 0000000000000..4d1f231f4f9be
--- /dev/null
+++ b/source/Plugins/Process/Linux/ProcFileReader.cpp
@@ -0,0 +1,108 @@
+//===-- ProcFileReader.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Process/Linux/ProcFileReader.h"
+
+// C Headers
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+// C++ Headers
+#include <fstream>
+
+// LLDB Headers
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Error.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+
+lldb::DataBufferSP
+ProcFileReader::ReadIntoDataBuffer (lldb::pid_t pid, const char *name)
+{
+ int fd;
+ char path[PATH_MAX];
+
+ // Make sure we've got a nil terminated buffer for all the folks calling
+ // GetBytes() directly off our returned DataBufferSP if we hit an error.
+ lldb::DataBufferSP buf_sp (new DataBufferHeap(1, 0));
+
+ // Ideally, we would simply create a FileSpec and call ReadFileContents.
+ // However, files in procfs have zero size (since they are, in general,
+ // dynamically generated by the kernel) which is incompatible with the
+ // current ReadFileContents implementation. Therefore we simply stream the
+ // data into a DataBuffer ourselves.
+ if (snprintf (path, PATH_MAX, "/proc/%" PRIu64 "/%s", pid, name) > 0)
+ {
+ if ((fd = open (path, O_RDONLY, 0)) >= 0)
+ {
+ size_t bytes_read = 0;
+ std::unique_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(1024, 0));
+
+ for (;;)
+ {
+ size_t avail = buf_ap->GetByteSize() - bytes_read;
+ ssize_t status = read (fd, buf_ap->GetBytes() + bytes_read, avail);
+
+ if (status < 0)
+ break;
+
+ if (status == 0)
+ {
+ buf_ap->SetByteSize (bytes_read);
+ buf_sp.reset (buf_ap.release());
+ break;
+ }
+
+ bytes_read += status;
+
+ if (avail - status == 0)
+ buf_ap->SetByteSize (2 * buf_ap->GetByteSize());
+ }
+
+ close (fd);
+ }
+ }
+
+ return buf_sp;
+}
+
+Error
+ProcFileReader::ProcessLineByLine (lldb::pid_t pid, const char *name, std::function<bool (const std::string &line)> line_parser)
+{
+ Error error;
+
+ // Try to open the /proc/{pid}/maps entry.
+ char filename [PATH_MAX];
+ snprintf (filename, sizeof(filename), "/proc/%" PRIu64 "/%s", pid, name);
+ filename[sizeof (filename) - 1] = '\0';
+
+ std::ifstream proc_file (filename);
+ if (proc_file.fail ())
+ {
+ error.SetErrorStringWithFormat ("failed to open file '%s'", filename);
+ return error;
+ }
+
+ // Read the file line by line, processing until either end of file or when the line_parser returns false.
+ std::string line;
+ bool should_continue = true;
+
+ while (should_continue && std::getline (proc_file, line))
+ {
+ // Pass the line over to the line_parser for processing. If the line_parser returns false, we
+ // stop processing.
+ should_continue = line_parser (line);
+ }
+
+ return error;
+}
diff --git a/source/Plugins/Process/Linux/ProcFileReader.h b/source/Plugins/Process/Linux/ProcFileReader.h
new file mode 100644
index 0000000000000..7b38124330684
--- /dev/null
+++ b/source/Plugins/Process/Linux/ProcFileReader.h
@@ -0,0 +1,37 @@
+//===-- ProcFileReader.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcFileReader_h_
+#define liblldb_ProcFileReader_h_
+
+#include <functional>
+
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+namespace process_linux {
+
+ class ProcFileReader
+ {
+ public:
+
+ static lldb::DataBufferSP
+ ReadIntoDataBuffer (lldb::pid_t pid, const char *name);
+
+ /// Parse the /proc/{@a pid}/{@a name} file line by line, passing each line to line_parser, until
+ /// either end of file or until line_parser returns false.
+ static Error
+ ProcessLineByLine (lldb::pid_t pid, const char *name, std::function<bool (const std::string &line)> line_parser);
+ };
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_ProcFileReader_h_
diff --git a/source/Plugins/Process/Linux/Procfs.h b/source/Plugins/Process/Linux/Procfs.h
new file mode 100644
index 0000000000000..cad433fb095db
--- /dev/null
+++ b/source/Plugins/Process/Linux/Procfs.h
@@ -0,0 +1,31 @@
+//===-- Procfs.h ---------------------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// source/Plugins/Process/Linux/Procfs.h defines the symbols we need from
+// sys/procfs.h on Android/Linux for all supported architectures.
+
+#include <sys/ptrace.h>
+
+#ifdef __ANDROID__
+#if defined (__arm64__) || defined (__aarch64__)
+typedef unsigned long elf_greg_t;
+typedef elf_greg_t elf_gregset_t[(sizeof (struct user_pt_regs) / sizeof(elf_greg_t))];
+typedef struct user_fpsimd_state elf_fpregset_t;
+#ifndef NT_FPREGSET
+ #define NT_FPREGSET NT_PRFPREG
+#endif // NT_FPREGSET
+#elif defined (__mips__)
+#ifndef NT_FPREGSET
+ #define NT_FPREGSET NT_PRFPREG
+#endif // NT_FPREGSET
+#endif
+#else // __ANDROID__
+#include <sys/procfs.h>
+#endif // __ANDROID__
+