[Lldb-commits] [lldb] 1a60026 - [lldb] Initial version of FreeBSD remote process plugin

Michał Górny via lldb-commits lldb-commits at lists.llvm.org
Thu Oct 8 07:03:54 PDT 2020


Author: Michał Górny
Date: 2020-10-08T16:03:00+02:00
New Revision: 1a600266c3ad1193d41ebb08a5a87c00cf726b1b

URL: https://github.com/llvm/llvm-project/commit/1a600266c3ad1193d41ebb08a5a87c00cf726b1b
DIFF: https://github.com/llvm/llvm-project/commit/1a600266c3ad1193d41ebb08a5a87c00cf726b1b.diff

LOG: [lldb] Initial version of FreeBSD remote process plugin

Add a new FreeBSD Process plugin using client/server model.  This plugin
is based on the one used by NetBSD.  It currently supports a subset
of functionality for amd64.  It is automatically used when spawning
lldb-server.  It can also be used by lldb client by setting
FREEBSD_REMOTE_PLUGIN environment variable (to any value).

The code is capable of debugging simple single-threaded programs.  It
supports general purpose, debug and FPU registers (up to XMM) of amd64,
basic signalling, software breakpoints.

Adding the support for the plugin involves removing some dead code
from FreeBSDPlatform plugin (that was not ever used because
CanDebugProcess() returned false), and replacing it with appropriate
code from NetBSD platform support.

Differential Revision: https://reviews.llvm.org/D88796

Added: 
    lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt
    lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
    lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
    lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.cpp
    lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h
    lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp
    lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h
    lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp
    lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h

Modified: 
    lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
    lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h
    lldb/source/Plugins/Process/CMakeLists.txt
    lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
    lldb/test/Shell/lit.cfg.py
    lldb/tools/lldb-server/CMakeLists.txt
    lldb/tools/lldb-server/lldb-gdbserver.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
index 97c2f22b505f..7318264c554b 100644
--- a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
+++ b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
@@ -246,58 +246,15 @@ PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode(Target &target,
   }
 }
 
-Status PlatformFreeBSD::LaunchProcess(ProcessLaunchInfo &launch_info) {
-  Status error;
-  if (IsHost()) {
-    error = Platform::LaunchProcess(launch_info);
-  } else {
-    if (m_remote_platform_sp)
-      error = m_remote_platform_sp->LaunchProcess(launch_info);
-    else
-      error.SetErrorString("the platform is not currently connected");
-  }
-  return error;
-}
-
-lldb::ProcessSP PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info,
-                                        Debugger &debugger, Target *target,
-                                        Status &error) {
-  lldb::ProcessSP process_sp;
-  if (IsHost()) {
-    if (target == nullptr) {
-      TargetSP new_target_sp;
-      ArchSpec emptyArchSpec;
-
-      error = debugger.GetTargetList().CreateTarget(
-          debugger, "", emptyArchSpec, eLoadDependentsNo, m_remote_platform_sp,
-          new_target_sp);
-      target = new_target_sp.get();
-    } else
-      error.Clear();
-
-    if (target && error.Success()) {
-      debugger.GetTargetList().SetSelectedTarget(target);
-      // The freebsd always currently uses the GDB remote debugger plug-in so
-      // even when debugging locally we are debugging remotely! Just like the
-      // darwin plugin.
-      process_sp = target->CreateProcess(
-          attach_info.GetListenerForProcess(debugger), "gdb-remote", nullptr);
-
-      if (process_sp)
-        error = process_sp->Attach(attach_info);
+bool PlatformFreeBSD::CanDebugProcess() {
+  if (getenv("FREEBSD_REMOTE_PLUGIN")) {
+    if (IsHost()) {
+      return true;
+    } else {
+      // If we're connected, we can debug.
+      return IsConnected();
     }
-  } else {
-    if (m_remote_platform_sp)
-      process_sp =
-          m_remote_platform_sp->Attach(attach_info, debugger, target, error);
-    else
-      error.SetErrorString("the platform is not currently connected");
   }
-  return process_sp;
-}
-
-// FreeBSD processes cannot yet be launched by spawning and attaching.
-bool PlatformFreeBSD::CanDebugProcess() {
   return false;
 }
 

diff  --git a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h
index 56f2f2771d12..4f3b119c568f 100644
--- a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h
+++ b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h
@@ -49,11 +49,6 @@ class PlatformFreeBSD : public PlatformPOSIX {
   size_t GetSoftwareBreakpointTrapOpcode(Target &target,
                                          BreakpointSite *bp_site) override;
 
-  Status LaunchProcess(ProcessLaunchInfo &launch_info) override;
-
-  lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
-                         Target *target, Status &error) override;
-
   void CalculateTrapHandlerSymbolNames() override;
 
   MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,

diff  --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt
index fdeb211fe7a2..91f20ec22ac5 100644
--- a/lldb/source/Plugins/Process/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/CMakeLists.txt
@@ -2,6 +2,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android")
   add_subdirectory(Linux)
   add_subdirectory(POSIX)
 elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+  add_subdirectory(FreeBSDRemote)
   add_subdirectory(FreeBSD)
   add_subdirectory(POSIX)
 elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD")

diff  --git a/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
index 67a18bd8de13..d87b01be8fc3 100644
--- a/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
+++ b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
@@ -79,12 +79,14 @@ ProcessFreeBSD::CreateInstance(lldb::TargetSP target_sp,
 }
 
 void ProcessFreeBSD::Initialize() {
-  static llvm::once_flag g_once_flag;
+  if (!getenv("FREEBSD_REMOTE_PLUGIN")) {
+    static llvm::once_flag g_once_flag;
 
-  llvm::call_once(g_once_flag, []() {
-    PluginManager::RegisterPlugin(GetPluginNameStatic(),
-                                  GetPluginDescriptionStatic(), CreateInstance);
-  });
+    llvm::call_once(g_once_flag, []() {
+      PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                    GetPluginDescriptionStatic(), CreateInstance);
+    });
+  }
 }
 
 lldb_private::ConstString ProcessFreeBSD::GetPluginNameStatic() {

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt b/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt
new file mode 100644
index 000000000000..8c1cec52fb15
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_lldb_library(lldbPluginProcessFreeBSDRemote
+  NativeProcessFreeBSD.cpp
+  NativeRegisterContextFreeBSD.cpp
+  NativeRegisterContextFreeBSD_x86_64.cpp
+  NativeThreadFreeBSD.cpp
+
+  LINK_LIBS
+    lldbHost
+    lldbSymbol
+    lldbTarget
+    lldbUtility
+    lldbPluginProcessPOSIX
+    lldbPluginProcessUtility
+  LINK_COMPONENTS
+    Support
+  )

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
new file mode 100644
index 000000000000..c96e05f238d4
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
@@ -0,0 +1,781 @@
+//===-- NativeProcessFreeBSD.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeProcessFreeBSD.h"
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <machine/elf.h>
+// clang-format on
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/State.h"
+#include "llvm/Support/Errno.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_freebsd;
+using namespace llvm;
+
+// Simple helper function to ensure flags are enabled on the given file
+// descriptor.
+static Status EnsureFDFlags(int fd, int flags) {
+  Status 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;
+}
+
+// Public Static Methods
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessFreeBSD::Factory::Launch(ProcessLaunchInfo &launch_info,
+                                      NativeDelegate &native_delegate,
+                                      MainLoop &mainloop) const {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+  Status status;
+  ::pid_t pid = ProcessLauncherPosixFork()
+                    .LaunchProcess(launch_info, status)
+                    .GetProcessId();
+  LLDB_LOG(log, "pid = {0:x}", pid);
+  if (status.Fail()) {
+    LLDB_LOG(log, "failed to launch process: {0}", status);
+    return status.ToError();
+  }
+
+  // Wait for the child process to trap on its call to execve.
+  int wstatus;
+  ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0);
+  assert(wpid == pid);
+  (void)wpid;
+  if (!WIFSTOPPED(wstatus)) {
+    LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}",
+             WaitStatus::Decode(wstatus));
+    return llvm::make_error<StringError>("Could not sync with inferior process",
+                                         llvm::inconvertibleErrorCode());
+  }
+  LLDB_LOG(log, "inferior started, now in stopped state");
+
+  ProcessInstanceInfo Info;
+  if (!Host::GetProcessInfo(pid, Info)) {
+    return llvm::make_error<StringError>("Cannot get process architecture",
+                                         llvm::inconvertibleErrorCode());
+  }
+
+  // Set the architecture to the exe architecture.
+  LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid,
+           Info.GetArchitecture().GetArchitectureName());
+
+  std::unique_ptr<NativeProcessFreeBSD> process_up(new NativeProcessFreeBSD(
+      pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate,
+      Info.GetArchitecture(), mainloop));
+
+  status = process_up->ReinitializeThreads();
+  if (status.Fail())
+    return status.ToError();
+
+  for (const auto &thread : process_up->m_threads)
+    static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+  process_up->SetState(StateType::eStateStopped, false);
+
+  return std::move(process_up);
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessFreeBSD::Factory::Attach(
+    lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
+    MainLoop &mainloop) const {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  LLDB_LOG(log, "pid = {0:x}", pid);
+
+  // Retrieve the architecture for the running process.
+  ProcessInstanceInfo Info;
+  if (!Host::GetProcessInfo(pid, Info)) {
+    return llvm::make_error<StringError>("Cannot get process architecture",
+                                         llvm::inconvertibleErrorCode());
+  }
+
+  std::unique_ptr<NativeProcessFreeBSD> process_up(new NativeProcessFreeBSD(
+      pid, -1, native_delegate, Info.GetArchitecture(), mainloop));
+
+  Status status = process_up->Attach();
+  if (!status.Success())
+    return status.ToError();
+
+  return std::move(process_up);
+}
+
+// Public Instance Methods
+
+NativeProcessFreeBSD::NativeProcessFreeBSD(::pid_t pid, int terminal_fd,
+                                           NativeDelegate &delegate,
+                                           const ArchSpec &arch,
+                                           MainLoop &mainloop)
+    : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch) {
+  if (m_terminal_fd != -1) {
+    Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
+    assert(status.Success());
+  }
+
+  Status status;
+  m_sigchld_handle = mainloop.RegisterSignal(
+      SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status);
+  assert(m_sigchld_handle && status.Success());
+}
+
+// Handles all waitpid events from the inferior process.
+void NativeProcessFreeBSD::MonitorCallback(lldb::pid_t pid, int signal) {
+  switch (signal) {
+  case SIGTRAP:
+    return MonitorSIGTRAP(pid);
+  case SIGSTOP:
+    return MonitorSIGSTOP(pid);
+  default:
+    return MonitorSignal(pid, signal);
+  }
+}
+
+void NativeProcessFreeBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+  LLDB_LOG(log, "got exit signal({0}) , pid = {1}", status, pid);
+
+  /* Stop Tracking All Threads attached to Process */
+  m_threads.clear();
+
+  SetExitStatus(status, true);
+
+  // Notify delegate that our process has exited.
+  SetState(StateType::eStateExited, true);
+}
+
+void NativeProcessFreeBSD::MonitorSIGSTOP(lldb::pid_t pid) {
+  /* Stop all Threads attached to Process */
+  for (const auto &thread : m_threads) {
+    static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP,
+                                                                   nullptr);
+  }
+  SetState(StateType::eStateStopped, true);
+}
+
+void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  struct ptrace_lwpinfo info;
+
+  const auto siginfo_err = PtraceWrapper(PT_LWPINFO, pid, &info, sizeof(info));
+  if (siginfo_err.Fail()) {
+    LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err);
+    return;
+  }
+  assert(info.pl_event == PL_EVENT_SIGNAL);
+  // TODO: do we need to handle !PL_FLAG_SI?
+  assert(info.pl_flags & PL_FLAG_SI);
+  assert(info.pl_siginfo.si_signo == SIGTRAP);
+
+  LLDB_LOG(log, "got SIGTRAP, pid = {0}, lwpid = {1}, si_code = {2}", pid,
+           info.pl_lwpid, info.pl_siginfo.si_code);
+
+  NativeThreadFreeBSD *thread = nullptr;
+  if (info.pl_lwpid > 0) {
+    for (const auto &t : m_threads) {
+      if (t->GetID() == static_cast<lldb::tid_t>(info.pl_lwpid)) {
+        thread = static_cast<NativeThreadFreeBSD *>(t.get());
+        break;
+      }
+      static_cast<NativeThreadFreeBSD *>(t.get())->SetStoppedWithNoReason();
+    }
+    if (!thread)
+      LLDB_LOG(log, "thread not found in m_threads, pid = {0}, LWP = {1}", pid,
+               info.pl_lwpid);
+  }
+
+  switch (info.pl_siginfo.si_code) {
+  case TRAP_BRKPT:
+    if (thread) {
+      thread->SetStoppedByBreakpoint();
+      FixupBreakpointPCAsNeeded(*thread);
+    }
+    SetState(StateType::eStateStopped, true);
+    break;
+  case TRAP_TRACE:
+    if (thread)
+      thread->SetStoppedByTrace();
+    SetState(StateType::eStateStopped, true);
+    break;
+  }
+}
+
+void NativeProcessFreeBSD::MonitorSignal(lldb::pid_t pid, int signal) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  struct ptrace_lwpinfo info;
+
+  const auto siginfo_err = PtraceWrapper(PT_LWPINFO, pid, &info, sizeof(info));
+  if (siginfo_err.Fail()) {
+    LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err);
+    return;
+  }
+  assert(info.pl_event == PL_EVENT_SIGNAL);
+  // TODO: do we need to handle !PL_FLAG_SI?
+  assert(info.pl_flags & PL_FLAG_SI);
+  assert(info.pl_siginfo.si_signo == signal);
+
+  for (const auto &abs_thread : m_threads) {
+    NativeThreadFreeBSD &thread =
+        static_cast<NativeThreadFreeBSD &>(*abs_thread);
+    assert(info.pl_lwpid >= 0);
+    if (info.pl_lwpid == 0 ||
+        static_cast<lldb::tid_t>(info.pl_lwpid) == thread.GetID())
+      thread.SetStoppedBySignal(info.pl_siginfo.si_signo, &info.pl_siginfo);
+    else
+      thread.SetStoppedWithNoReason();
+  }
+  SetState(StateType::eStateStopped, true);
+}
+
+Status NativeProcessFreeBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
+                                           int data, int *result) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+  Status error;
+  int ret;
+
+  errno = 0;
+  ret =
+      ptrace(req, static_cast<::pid_t>(pid), static_cast<caddr_t>(addr), data);
+
+  if (ret == -1)
+    error.SetErrorToErrno();
+
+  if (result)
+    *result = ret;
+
+  LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3})={4:x}", req, pid, addr, data, ret);
+
+  if (error.Fail())
+    LLDB_LOG(log, "ptrace() failed: {0}", error);
+
+  return error;
+}
+
+Status NativeProcessFreeBSD::Resume(const ResumeActionList &resume_actions) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  LLDB_LOG(log, "pid {0}", GetID());
+
+  Status ret;
+
+  int signal = 0;
+  for (const auto &abs_thread : m_threads) {
+    assert(abs_thread && "thread list should not contain NULL threads");
+    NativeThreadFreeBSD &thread =
+        static_cast<NativeThreadFreeBSD &>(*abs_thread);
+
+    const ResumeAction *action =
+        resume_actions.GetActionForThread(thread.GetID(), true);
+    // we need to explicit issue suspend requests, so it is simpler to map it
+    // into proper action
+    ResumeAction suspend_action{thread.GetID(), eStateSuspended,
+                                LLDB_INVALID_SIGNAL_NUMBER};
+
+    if (action == nullptr) {
+      LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(),
+               thread.GetID());
+      action = &suspend_action;
+    }
+
+    LLDB_LOG(
+        log,
+        "processing resume action state {0} signal {1} for pid {2} tid {3}",
+        action->state, action->signal, GetID(), thread.GetID());
+
+    switch (action->state) {
+    case eStateRunning:
+      ret = thread.Resume();
+      break;
+    case eStateStepping:
+      ret = thread.SingleStep();
+      break;
+    case eStateSuspended:
+    case eStateStopped:
+      if (action->signal != LLDB_INVALID_SIGNAL_NUMBER)
+        return Status("Passing signal to suspended thread unsupported");
+
+      ret = thread.Suspend();
+      break;
+
+    default:
+      return Status(
+          "NativeProcessFreeBSD::%s (): unexpected state %s specified "
+          "for pid %" PRIu64 ", tid %" PRIu64,
+          __FUNCTION__, StateAsCString(action->state), GetID(), thread.GetID());
+    }
+
+    if (!ret.Success())
+      return ret;
+    if (action->signal != LLDB_INVALID_SIGNAL_NUMBER)
+      signal = action->signal;
+  }
+
+  ret =
+      PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), signal);
+  if (ret.Success())
+    SetState(eStateRunning, true);
+  return ret;
+}
+
+Status NativeProcessFreeBSD::Halt() {
+  Status error;
+
+  if (kill(GetID(), SIGSTOP) != 0)
+    error.SetErrorToErrno();
+  return error;
+}
+
+Status NativeProcessFreeBSD::Detach() {
+  Status error;
+
+  // Stop monitoring the inferior.
+  m_sigchld_handle.reset();
+
+  // Tell ptrace to detach from the process.
+  if (GetID() == LLDB_INVALID_PROCESS_ID)
+    return error;
+
+  return PtraceWrapper(PT_DETACH, GetID());
+}
+
+Status NativeProcessFreeBSD::Signal(int signo) {
+  Status error;
+
+  if (kill(GetID(), signo))
+    error.SetErrorToErrno();
+
+  return error;
+}
+
+Status NativeProcessFreeBSD::Interrupt() { return Halt(); }
+
+Status NativeProcessFreeBSD::Kill() {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  LLDB_LOG(log, "pid {0}", GetID());
+
+  Status 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.
+    LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", 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;
+}
+
+Status NativeProcessFreeBSD::GetMemoryRegionInfo(lldb::addr_t load_addr,
+                                                 MemoryRegionInfo &range_info) {
+
+  if (m_supports_mem_region == LazyBool::eLazyBoolNo) {
+    // We're done.
+    return Status("unsupported");
+  }
+
+  Status error = PopulateMemoryRegionCache();
+  if (error.Fail()) {
+    return error;
+  }
+
+  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->first;
+    // Sanity check assumption that memory map entries are ascending.
+    assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) &&
+           "descending memory map entries detected, unexpected");
+    prev_base_address = proc_entry_info.GetRange().GetRangeBase();
+    UNUSED_IF_ASSERT_DISABLED(prev_base_address);
+    // 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);
+      range_info.SetMapped(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);
+  range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS);
+  range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+  range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+  range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+  range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo);
+  return error;
+}
+
+Status NativeProcessFreeBSD::PopulateMemoryRegionCache() {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  // 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()) {
+    LLDB_LOG(log, "reusing {0} cached memory region entries",
+             m_mem_region_cache.size());
+    return Status();
+  }
+
+  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, static_cast<int>(m_pid)};
+  int ret;
+  size_t len;
+
+  ret = ::sysctl(mib, 4, nullptr, &len, nullptr, 0);
+  if (ret != 0) {
+    m_supports_mem_region = LazyBool::eLazyBoolNo;
+    return Status("sysctl() for KERN_PROC_VMMAP failed");
+  }
+
+  std::unique_ptr<WritableMemoryBuffer> buf =
+      llvm::WritableMemoryBuffer::getNewMemBuffer(len);
+  ret = ::sysctl(mib, 4, buf->getBufferStart(), &len, nullptr, 0);
+  if (ret != 0) {
+    m_supports_mem_region = LazyBool::eLazyBoolNo;
+    return Status("sysctl() for KERN_PROC_VMMAP failed");
+  }
+
+  char *bp = buf->getBufferStart();;
+  char *end = bp + len;
+  while (bp < end) {
+    auto *kv = reinterpret_cast<struct kinfo_vmentry *>(bp);
+    if (kv->kve_structsize == 0)
+      break;
+    bp += kv->kve_structsize;
+
+    MemoryRegionInfo info;
+    info.Clear();
+    info.GetRange().SetRangeBase(kv->kve_start);
+    info.GetRange().SetRangeEnd(kv->kve_end);
+    info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+
+    if (kv->kve_protection & VM_PROT_READ)
+      info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
+    else
+      info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+
+    if (kv->kve_protection & VM_PROT_WRITE)
+      info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
+    else
+      info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+
+    if (kv->kve_protection & VM_PROT_EXECUTE)
+      info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
+    else
+      info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+
+    if (kv->kve_path[0])
+      info.SetName(kv->kve_path);
+
+    m_mem_region_cache.emplace_back(info,
+                                    FileSpec(info.GetName().GetCString()));
+  }
+
+  if (m_mem_region_cache.empty()) {
+    // No entries after attempting to read them.  This shouldn't happen. Assume
+    // we don't support map entries.
+    LLDB_LOG(log, "failed to find any vmmap entries, assuming no support "
+                  "for memory region metadata retrieval");
+    m_supports_mem_region = LazyBool::eLazyBoolNo;
+    return Status("not supported");
+  }
+  LLDB_LOG(log, "read {0} memory region entries from process {1}",
+           m_mem_region_cache.size(), GetID());
+  // We support memory retrieval, remember that.
+  m_supports_mem_region = LazyBool::eLazyBoolYes;
+
+  return Status();
+}
+
+Status NativeProcessFreeBSD::AllocateMemory(size_t size, uint32_t permissions,
+                                            lldb::addr_t &addr) {
+  return Status("Unimplemented");
+}
+
+Status NativeProcessFreeBSD::DeallocateMemory(lldb::addr_t addr) {
+  return Status("Unimplemented");
+}
+
+lldb::addr_t NativeProcessFreeBSD::GetSharedLibraryInfoAddress() {
+  // punt on this for now
+  return LLDB_INVALID_ADDRESS;
+}
+
+size_t NativeProcessFreeBSD::UpdateThreads() { return m_threads.size(); }
+
+Status NativeProcessFreeBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+                                           bool hardware) {
+  if (hardware)
+    return Status("NativeProcessFreeBSD does not support hardware breakpoints");
+  else
+    return SetSoftwareBreakpoint(addr, size);
+}
+
+Status NativeProcessFreeBSD::GetLoadedModuleFileSpec(const char *module_path,
+                                                     FileSpec &file_spec) {
+  return Status("Unimplemented");
+}
+
+Status
+NativeProcessFreeBSD::GetFileLoadAddress(const llvm::StringRef &file_name,
+                                         lldb::addr_t &load_addr) {
+  load_addr = LLDB_INVALID_ADDRESS;
+  return Status();
+}
+
+void NativeProcessFreeBSD::SigchldHandler() {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  // Process all pending waitpid notifications.
+  int status;
+  ::pid_t wait_pid =
+      llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WNOHANG);
+
+  if (wait_pid == 0)
+    return; // We are done.
+
+  if (wait_pid == -1) {
+    Status error(errno, eErrorTypePOSIX);
+    LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error);
+  }
+
+  WaitStatus wait_status = WaitStatus::Decode(status);
+  bool exited = wait_status.type == WaitStatus::Exit ||
+                (wait_status.type == WaitStatus::Signal &&
+                 wait_pid == static_cast<::pid_t>(GetID()));
+
+  LLDB_LOG(log,
+           "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}",
+           GetID(), wait_pid, status, exited);
+
+  if (exited)
+    MonitorExited(wait_pid, wait_status);
+  else {
+    assert(wait_status.type == WaitStatus::Stop);
+    MonitorCallback(wait_pid, wait_status.status);
+  }
+}
+
+bool NativeProcessFreeBSD::HasThreadNoLock(lldb::tid_t thread_id) {
+  for (const auto &thread : m_threads) {
+    assert(thread && "thread list should not contain NULL threads");
+    if (thread->GetID() == thread_id) {
+      // We have this thread.
+      return true;
+    }
+  }
+
+  // We don't have this thread.
+  return false;
+}
+
+NativeThreadFreeBSD &NativeProcessFreeBSD::AddThread(lldb::tid_t thread_id) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+  LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id);
+
+  assert(thread_id > 0);
+  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);
+
+  m_threads.push_back(std::make_unique<NativeThreadFreeBSD>(*this, thread_id));
+  return static_cast<NativeThreadFreeBSD &>(*m_threads.back());
+}
+
+void NativeProcessFreeBSD::RemoveThread(lldb::tid_t thread_id) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+  LLDB_LOG(log, "pid {0} removing thread with tid {1}", GetID(), thread_id);
+
+  assert(thread_id > 0);
+  assert(HasThreadNoLock(thread_id) &&
+         "attempted to remove a thread that does not exist");
+
+  for (auto it = m_threads.begin(); it != m_threads.end(); ++it) {
+    if ((*it)->GetID() == thread_id) {
+      m_threads.erase(it);
+      break;
+    }
+  }
+}
+
+Status NativeProcessFreeBSD::Attach() {
+  // Attach to the requested process.
+  // An attach will cause the thread to stop with a SIGSTOP.
+  Status status = PtraceWrapper(PT_ATTACH, m_pid);
+  if (status.Fail())
+    return status;
+
+  int wstatus;
+  // Need to use WALLSIG otherwise we receive an error with errno=ECHLD At this
+  // point we should have a thread stopped if waitpid succeeds.
+  if ((wstatus = llvm::sys::RetryAfterSignal(-1, waitpid, m_pid, nullptr, 0)) <
+      0)
+    return Status(errno, eErrorTypePOSIX);
+
+  /* Initialize threads */
+  status = ReinitializeThreads();
+  if (status.Fail())
+    return status;
+
+  for (const auto &thread : m_threads)
+    static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+
+  // Let our process instance know the thread has stopped.
+  SetState(StateType::eStateStopped);
+  return Status();
+}
+
+Status NativeProcessFreeBSD::ReadMemory(lldb::addr_t addr, void *buf,
+                                        size_t size, size_t &bytes_read) {
+  unsigned char *dst = static_cast<unsigned char *>(buf);
+  struct ptrace_io_desc io;
+
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY));
+  LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+  bytes_read = 0;
+  io.piod_op = PIOD_READ_D;
+  io.piod_len = size;
+
+  do {
+    io.piod_offs = (void *)(addr + bytes_read);
+    io.piod_addr = dst + bytes_read;
+
+    Status error = NativeProcessFreeBSD::PtraceWrapper(PT_IO, GetID(), &io);
+    if (error.Fail() || io.piod_len == 0)
+      return error;
+
+    bytes_read += io.piod_len;
+    io.piod_len = size - bytes_read;
+  } while (bytes_read < size);
+
+  return Status();
+}
+
+Status NativeProcessFreeBSD::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);
+  Status error;
+  struct ptrace_io_desc io;
+
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY));
+  LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+  bytes_written = 0;
+  io.piod_op = PIOD_WRITE_D;
+  io.piod_len = size;
+
+  do {
+    io.piod_addr =
+        const_cast<void *>(static_cast<const void *>(src + bytes_written));
+    io.piod_offs = (void *)(addr + bytes_written);
+
+    Status error = NativeProcessFreeBSD::PtraceWrapper(PT_IO, GetID(), &io);
+    if (error.Fail() || io.piod_len == 0)
+      return error;
+
+    bytes_written += io.piod_len;
+    io.piod_len = size - bytes_written;
+  } while (bytes_written < size);
+
+  return error;
+}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+NativeProcessFreeBSD::GetAuxvData() const {
+  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_AUXV, static_cast<int>(GetID())};
+  size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo);
+  std::unique_ptr<WritableMemoryBuffer> buf =
+      llvm::WritableMemoryBuffer::getNewMemBuffer(auxv_size);
+
+  if (::sysctl(mib, 4, buf->getBufferStart(), &auxv_size, nullptr, 0) != 0)
+    return std::error_code(errno, std::generic_category());
+
+  return buf;
+}
+
+Status NativeProcessFreeBSD::ReinitializeThreads() {
+  // Clear old threads
+  m_threads.clear();
+
+  int num_lwps;
+  Status error = PtraceWrapper(PT_GETNUMLWPS, GetID(), nullptr, 0, &num_lwps);
+  if (error.Fail())
+    return error;
+
+  std::vector<lwpid_t> lwp_ids;
+  lwp_ids.resize(num_lwps);
+  error = PtraceWrapper(PT_GETLWPLIST, GetID(), lwp_ids.data(),
+                        lwp_ids.size() * sizeof(lwpid_t), &num_lwps);
+  if (error.Fail())
+    return error;
+
+  // Reinitialize from scratch threads and register them in process
+  for (lwpid_t lwp : lwp_ids)
+    AddThread(lwp);
+
+  return error;
+}

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
new file mode 100644
index 000000000000..e1334c3bcf5d
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
@@ -0,0 +1,121 @@
+//===-- NativeProcessFreeBSD.h -------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NativeProcessFreeBSD_H_
+#define liblldb_NativeProcessFreeBSD_H_
+
+#include "Plugins/Process/POSIX/NativeProcessELF.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include "NativeThreadFreeBSD.h"
+
+namespace lldb_private {
+namespace process_freebsd {
+/// \class NativeProcessFreeBSD
+/// 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 NativeProcessFreeBSD : public NativeProcessELF {
+public:
+  class Factory : public NativeProcessProtocol::Factory {
+  public:
+    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+    Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate,
+           MainLoop &mainloop) const override;
+
+    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+    Attach(lldb::pid_t pid, NativeDelegate &native_delegate,
+           MainLoop &mainloop) const override;
+  };
+
+  // NativeProcessProtocol Interface
+  Status Resume(const ResumeActionList &resume_actions) override;
+
+  Status Halt() override;
+
+  Status Detach() override;
+
+  Status Signal(int signo) override;
+
+  Status Interrupt() override;
+
+  Status Kill() override;
+
+  Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+                             MemoryRegionInfo &range_info) override;
+
+  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                    size_t &bytes_read) override;
+
+  Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+                     size_t &bytes_written) override;
+
+  Status AllocateMemory(size_t size, uint32_t permissions,
+                        lldb::addr_t &addr) override;
+
+  Status DeallocateMemory(lldb::addr_t addr) override;
+
+  lldb::addr_t GetSharedLibraryInfoAddress() override;
+
+  size_t UpdateThreads() override;
+
+  const ArchSpec &GetArchitecture() const override { return m_arch; }
+
+  Status SetBreakpoint(lldb::addr_t addr, uint32_t size,
+                       bool hardware) override;
+
+  Status GetLoadedModuleFileSpec(const char *module_path,
+                                 FileSpec &file_spec) override;
+
+  Status GetFileLoadAddress(const llvm::StringRef &file_name,
+                            lldb::addr_t &load_addr) override;
+
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+  GetAuxvData() const override;
+
+  // Interface used by NativeRegisterContext-derived classes.
+  static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
+                              int data = 0, int *result = nullptr);
+
+private:
+  MainLoop::SignalHandleUP m_sigchld_handle;
+  ArchSpec m_arch;
+  LazyBool m_supports_mem_region = eLazyBoolCalculate;
+  std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
+
+  // Private Instance Methods
+  NativeProcessFreeBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate,
+                       const ArchSpec &arch, MainLoop &mainloop);
+
+  bool HasThreadNoLock(lldb::tid_t thread_id);
+
+  NativeThreadFreeBSD &AddThread(lldb::tid_t thread_id);
+  void RemoveThread(lldb::tid_t thread_id);
+
+  void MonitorCallback(lldb::pid_t pid, int signal);
+  void MonitorExited(lldb::pid_t pid, WaitStatus status);
+  void MonitorSIGSTOP(lldb::pid_t pid);
+  void MonitorSIGTRAP(lldb::pid_t pid);
+  void MonitorSignal(lldb::pid_t pid, int signal);
+
+  Status PopulateMemoryRegionCache();
+  void SigchldHandler();
+
+  Status Attach();
+  Status ReinitializeThreads();
+};
+
+} // namespace process_freebsd
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeProcessFreeBSD_H_

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.cpp
new file mode 100644
index 000000000000..2a7dc3dbc441
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.cpp
@@ -0,0 +1,39 @@
+//===-- NativeRegisterContextFreeBSD.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeRegisterContextFreeBSD.h"
+
+#include "Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h"
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_freebsd;
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+// clang-format on
+
+NativeRegisterContextFreeBSD::NativeRegisterContextFreeBSD(
+    NativeThreadProtocol &native_thread,
+    RegisterInfoInterface *reg_info_interface_p)
+    : NativeRegisterContextRegisterInfo(native_thread, reg_info_interface_p) {}
+
+Status NativeRegisterContextFreeBSD::DoRegisterSet(int ptrace_req, void *buf) {
+  return NativeProcessFreeBSD::PtraceWrapper(ptrace_req, GetProcessPid(), buf,
+                                             m_thread.GetID());
+}
+
+NativeProcessFreeBSD &NativeRegisterContextFreeBSD::GetProcess() {
+  return static_cast<NativeProcessFreeBSD &>(m_thread.GetProcess());
+}
+
+::pid_t NativeRegisterContextFreeBSD::GetProcessPid() {
+  return GetProcess().GetID();
+}

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h
new file mode 100644
index 000000000000..db32e216c925
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h
@@ -0,0 +1,48 @@
+//===-- NativeRegisterContextFreeBSD.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_NativeRegisterContextFreeBSD_h
+#define lldb_NativeRegisterContextFreeBSD_h
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
+
+namespace lldb_private {
+namespace process_freebsd {
+
+class NativeProcessFreeBSD;
+
+class NativeRegisterContextFreeBSD : public NativeRegisterContextRegisterInfo {
+public:
+  NativeRegisterContextFreeBSD(NativeThreadProtocol &native_thread,
+                               RegisterInfoInterface *reg_info_interface_p);
+
+  // This function is implemented in the NativeRegisterContextFreeBSD_*
+  // subclasses to create a new instance of the host specific
+  // NativeRegisterContextFreeBSD. The implementations can't collide as only one
+  // NativeRegisterContextFreeBSD_* variant should be compiled into the final
+  // executable.
+  static NativeRegisterContextFreeBSD *
+  CreateHostNativeRegisterContextFreeBSD(const ArchSpec &target_arch,
+                                         NativeThreadProtocol &native_thread);
+  virtual Status
+  CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) = 0;
+
+  virtual Status ClearWatchpointHit(uint32_t wp_index) = 0;
+
+protected:
+  Status DoRegisterSet(int req, void *buf);
+  virtual NativeProcessFreeBSD &GetProcess();
+  virtual ::pid_t GetProcessPid();
+};
+
+} // namespace process_freebsd
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextFreeBSD_h

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp
new file mode 100644
index 000000000000..157b2167f2ff
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp
@@ -0,0 +1,1315 @@
+//===-- NativeRegisterContextFreeBSD_x86_64.cpp ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "NativeRegisterContextFreeBSD_x86_64.h"
+
+#include <machine/fpu.h>
+
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_freebsd;
+
+// Private namespace.
+
+namespace {
+// 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 registers available via XState.
+static const uint32_t g_xstate_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,
+    // Note: we currently do not provide them but this is needed to avoid
+    // unnamed groups in SBFrame::GetRegisterContext().
+    lldb_bnd0_x86_64, lldb_bnd1_x86_64, lldb_bnd2_x86_64, lldb_bnd3_x86_64,
+    lldb_bndcfgu_x86_64, lldb_bndstatus_x86_64,
+    LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_xstate_regnums_x86_64) /
+               sizeof(g_xstate_regnums_x86_64[0])) -
+                      1 ==
+                  k_num_avx_registers_x86_64 + k_num_mpx_registers_x86_64,
+              "g_xstate_regnums_x86_64 has wrong number of register infos");
+
+// x86 debug registers.
+static const uint32_t g_dbr_regnums_x86_64[] = {
+    lldb_dr0_x86_64,    lldb_dr1_x86_64, lldb_dr2_x86_64, lldb_dr3_x86_64,
+    lldb_dr4_x86_64,    lldb_dr5_x86_64, lldb_dr6_x86_64, lldb_dr7_x86_64,
+    LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_dbr_regnums_x86_64) / sizeof(g_dbr_regnums_x86_64[0])) -
+                      1 ==
+                  k_num_dbr_registers_x86_64,
+              "g_dbr_regnums_x86_64 has wrong number of register infos");
+
+// 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 64-bit registers available via XState.
+static const uint32_t g_xstate_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,
+    // Note: we currently do not provide them but this is needed to avoid
+    // unnamed groups in SBFrame::GetRegisterContext().
+    lldb_bnd0_i386, lldb_bnd1_i386, lldb_bnd2_i386, lldb_bnd3_i386,
+    lldb_bndcfgu_i386, lldb_bndstatus_i386,
+    LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(
+    (sizeof(g_xstate_regnums_i386) / sizeof(g_xstate_regnums_i386[0])) - 1 ==
+        k_num_avx_registers_i386 + k_num_mpx_registers_i386,
+    "g_xstate_regnums_i386 has wrong number of register infos");
+
+// x86 debug registers.
+static const uint32_t g_dbr_regnums_i386[] = {
+    lldb_dr0_i386,      lldb_dr1_i386, lldb_dr2_i386, lldb_dr3_i386,
+    lldb_dr4_i386,      lldb_dr5_i386, lldb_dr6_i386, lldb_dr7_i386,
+    LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_dbr_regnums_i386) / sizeof(g_dbr_regnums_i386[0])) -
+                      1 ==
+                  k_num_dbr_registers_i386,
+              "g_dbr_regnums_i386 has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 4 };
+
+// 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},
+    {"Extended State Registers", "xstate",
+     k_num_avx_registers_i386 + k_num_mpx_registers_i386,
+     g_xstate_regnums_i386},
+    {"Debug Registers", "dbr", k_num_dbr_registers_i386, g_dbr_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},
+    {"Extended State Registers", "xstate",
+     k_num_avx_registers_x86_64 + k_num_mpx_registers_x86_64,
+     g_xstate_regnums_x86_64},
+    {"Debug Registers", "dbr", k_num_dbr_registers_x86_64,
+     g_dbr_regnums_x86_64},
+};
+
+#define REG_CONTEXT_SIZE (GetRegisterInfoInterface().GetGPRSize())
+} // namespace
+
+NativeRegisterContextFreeBSD *
+NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD(
+    const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+  return new NativeRegisterContextFreeBSD_x86_64(target_arch, native_thread);
+}
+
+// NativeRegisterContextFreeBSD_x86_64 members.
+
+static RegisterInfoInterface *
+CreateRegisterInfoInterface(const ArchSpec &target_arch) {
+  if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) {
+    // 32-bit hosts run with a RegisterContextFreeBSD_i386 context.
+    return new RegisterContextFreeBSD_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 RegisterContextFreeBSD_x86_64(target_arch);
+  }
+}
+
+NativeRegisterContextFreeBSD_x86_64::NativeRegisterContextFreeBSD_x86_64(
+    const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+    : NativeRegisterContextFreeBSD(native_thread,
+                                   CreateRegisterInfoInterface(target_arch)),
+      m_gpr(), m_fpr(), m_dbr() {}
+
+// 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 NativeRegisterContextFreeBSD_x86_64::GetRegisterSetCount() const {
+  uint32_t sets = 0;
+  for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) {
+    if (GetSetForNativeRegNum(set_index) != -1)
+      ++sets;
+  }
+
+  return sets;
+}
+
+const RegisterSet *
+NativeRegisterContextFreeBSD_x86_64::GetRegisterSet(uint32_t set_index) const {
+  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:
+    llvm_unreachable("Unhandled target architecture.");
+  }
+}
+
+static constexpr int RegNumX86ToX86_64(int regnum) {
+  switch (regnum) {
+  case lldb_eax_i386:
+    return lldb_rax_x86_64;
+  case lldb_ebx_i386:
+    return lldb_rbx_x86_64;
+  case lldb_ecx_i386:
+    return lldb_rcx_x86_64;
+  case lldb_edx_i386:
+    return lldb_rdx_x86_64;
+  case lldb_edi_i386:
+    return lldb_rdi_x86_64;
+  case lldb_esi_i386:
+    return lldb_rsi_x86_64;
+  case lldb_ebp_i386:
+    return lldb_rbp_x86_64;
+  case lldb_esp_i386:
+    return lldb_rsp_x86_64;
+  case lldb_eip_i386:
+    return lldb_rip_x86_64;
+  case lldb_eflags_i386:
+    return lldb_rflags_x86_64;
+  case lldb_cs_i386:
+    return lldb_cs_x86_64;
+  case lldb_fs_i386:
+    return lldb_fs_x86_64;
+  case lldb_gs_i386:
+    return lldb_gs_x86_64;
+  case lldb_ss_i386:
+    return lldb_ss_x86_64;
+  case lldb_ds_i386:
+    return lldb_ds_x86_64;
+  case lldb_es_i386:
+    return lldb_es_x86_64;
+  case lldb_fctrl_i386:
+    return lldb_fctrl_x86_64;
+  case lldb_fstat_i386:
+    return lldb_fstat_x86_64;
+  case lldb_ftag_i386:
+    return lldb_ftag_x86_64;
+  case lldb_fop_i386:
+    return lldb_fop_x86_64;
+  case lldb_fiseg_i386:
+    return lldb_fiseg_x86_64;
+  case lldb_fioff_i386:
+    return lldb_fioff_x86_64;
+  case lldb_foseg_i386:
+    return lldb_foseg_x86_64;
+  case lldb_fooff_i386:
+    return lldb_fooff_x86_64;
+  case lldb_mxcsr_i386:
+    return lldb_mxcsr_x86_64;
+  case lldb_mxcsrmask_i386:
+    return lldb_mxcsrmask_x86_64;
+  case lldb_st0_i386:
+  case lldb_st1_i386:
+  case lldb_st2_i386:
+  case lldb_st3_i386:
+  case lldb_st4_i386:
+  case lldb_st5_i386:
+  case lldb_st6_i386:
+  case lldb_st7_i386:
+    return lldb_st0_x86_64 + regnum - lldb_st0_i386;
+  case lldb_mm0_i386:
+  case lldb_mm1_i386:
+  case lldb_mm2_i386:
+  case lldb_mm3_i386:
+  case lldb_mm4_i386:
+  case lldb_mm5_i386:
+  case lldb_mm6_i386:
+  case lldb_mm7_i386:
+    return lldb_mm0_x86_64 + regnum - lldb_mm0_i386;
+  case lldb_xmm0_i386:
+  case lldb_xmm1_i386:
+  case lldb_xmm2_i386:
+  case lldb_xmm3_i386:
+  case lldb_xmm4_i386:
+  case lldb_xmm5_i386:
+  case lldb_xmm6_i386:
+  case lldb_xmm7_i386:
+    return lldb_xmm0_x86_64 + regnum - lldb_xmm0_i386;
+  case lldb_ymm0_i386:
+  case lldb_ymm1_i386:
+  case lldb_ymm2_i386:
+  case lldb_ymm3_i386:
+  case lldb_ymm4_i386:
+  case lldb_ymm5_i386:
+  case lldb_ymm6_i386:
+  case lldb_ymm7_i386:
+    return lldb_ymm0_x86_64 + regnum - lldb_ymm0_i386;
+  case lldb_bnd0_i386:
+  case lldb_bnd1_i386:
+  case lldb_bnd2_i386:
+  case lldb_bnd3_i386:
+    return lldb_bnd0_x86_64 + regnum - lldb_bnd0_i386;
+  case lldb_bndcfgu_i386:
+    return lldb_bndcfgu_x86_64;
+  case lldb_bndstatus_i386:
+    return lldb_bndstatus_x86_64;
+  case lldb_dr0_i386:
+  case lldb_dr1_i386:
+  case lldb_dr2_i386:
+  case lldb_dr3_i386:
+  case lldb_dr4_i386:
+  case lldb_dr5_i386:
+  case lldb_dr6_i386:
+  case lldb_dr7_i386:
+    return lldb_dr0_x86_64 + regnum - lldb_dr0_i386;
+  default:
+    llvm_unreachable("Unhandled i386 register.");
+  }
+}
+
+int NativeRegisterContextFreeBSD_x86_64::GetSetForNativeRegNum(
+    int reg_num) const {
+
+  switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
+  case llvm::Triple::x86:
+    if (reg_num >= k_first_gpr_i386 && reg_num <= k_last_gpr_i386)
+      return GPRegSet;
+    if (reg_num >= k_first_fpr_i386 && reg_num <= k_last_fpr_i386)
+      return FPRegSet;
+    if (reg_num >= k_first_avx_i386 && reg_num <= k_last_avx_i386)
+      return -1; // AVX
+    if (reg_num >= k_first_mpxr_i386 && reg_num <= k_last_mpxr_i386)
+      return -1; // MPXR
+    if (reg_num >= k_first_mpxc_i386 && reg_num <= k_last_mpxc_i386)
+      return -1; // MPXC
+    if (reg_num >= k_first_dbr_i386 && reg_num <= k_last_dbr_i386)
+      return DBRegSet; // DBR
+    break;
+  case llvm::Triple::x86_64:
+    if (reg_num >= k_first_gpr_x86_64 && reg_num <= k_last_gpr_x86_64)
+      return GPRegSet;
+    if (reg_num >= k_first_fpr_x86_64 && reg_num <= k_last_fpr_x86_64)
+      return FPRegSet;
+    if (reg_num >= k_first_avx_x86_64 && reg_num <= k_last_avx_x86_64)
+      return -1; // AVX
+    if (reg_num >= k_first_mpxr_x86_64 && reg_num <= k_last_mpxr_x86_64)
+      return -1; // MPXR
+    if (reg_num >= k_first_mpxc_x86_64 && reg_num <= k_last_mpxc_x86_64)
+      return -1; // MPXC
+    if (reg_num >= k_first_dbr_x86_64 && reg_num <= k_last_dbr_x86_64)
+      return DBRegSet; // DBR
+    break;
+  default:
+    llvm_unreachable("Unhandled target architecture.");
+  }
+
+  llvm_unreachable("Register does not belong to any register set");
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::ReadRegisterSet(uint32_t set) {
+  switch (set) {
+  case GPRegSet:
+    return DoRegisterSet(PT_GETREGS, &m_gpr);
+  case FPRegSet:
+#if defined(__x86_64__)
+    return DoRegisterSet(PT_GETFPREGS, &m_fpr);
+#else
+    return DoRegisterSet(PT_GETXMMREGS, &m_fpr);
+#endif
+  case DBRegSet:
+    return DoRegisterSet(PT_GETDBREGS, &m_dbr);
+  }
+  llvm_unreachable("NativeRegisterContextFreeBSD_x86_64::ReadRegisterSet");
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::WriteRegisterSet(uint32_t set) {
+  switch (set) {
+  case GPRegSet:
+    return DoRegisterSet(PT_SETREGS, &m_gpr);
+  case FPRegSet:
+#if defined(__x86_64__)
+    return DoRegisterSet(PT_SETFPREGS, &m_fpr);
+#else
+    return DoRegisterSet(PT_SETXMMREGS, &m_fpr);
+#endif
+  case DBRegSet:
+    return DoRegisterSet(PT_SETDBREGS, &m_dbr);
+  }
+  llvm_unreachable("NativeRegisterContextFreeBSD_x86_64::WriteRegisterSet");
+}
+
+Status
+NativeRegisterContextFreeBSD_x86_64::ReadRegister(const RegisterInfo *reg_info,
+                                                  RegisterValue &reg_value) {
+  Status error;
+
+  if (!reg_info) {
+    error.SetErrorString("reg_info NULL");
+    return error;
+  }
+
+  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;
+  }
+
+  int set = GetSetForNativeRegNum(reg);
+  if (set == -1) {
+    // This is likely an internal register for lldb use only and should not be
+    // directly queried.
+    error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
+                                   reg_info->name);
+    return error;
+  }
+
+  switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
+  case llvm::Triple::x86_64:
+    break;
+  case llvm::Triple::x86:
+    reg = RegNumX86ToX86_64(reg);
+    break;
+  default:
+    llvm_unreachable("Unhandled target architecture.");
+  }
+
+  error = ReadRegisterSet(set);
+  if (error.Fail())
+    return error;
+
+  switch (reg) {
+#if defined(__x86_64__)
+  case lldb_rax_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rax;
+    break;
+  case lldb_rbx_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rbx;
+    break;
+  case lldb_rcx_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rcx;
+    break;
+  case lldb_rdx_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rdx;
+    break;
+  case lldb_rdi_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rdi;
+    break;
+  case lldb_rsi_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rsi;
+    break;
+  case lldb_rbp_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rbp;
+    break;
+  case lldb_rsp_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rsp;
+    break;
+  case lldb_r8_x86_64:
+    reg_value = (uint64_t)m_gpr.r_r8;
+    break;
+  case lldb_r9_x86_64:
+    reg_value = (uint64_t)m_gpr.r_r9;
+    break;
+  case lldb_r10_x86_64:
+    reg_value = (uint64_t)m_gpr.r_r10;
+    break;
+  case lldb_r11_x86_64:
+    reg_value = (uint64_t)m_gpr.r_r11;
+    break;
+  case lldb_r12_x86_64:
+    reg_value = (uint64_t)m_gpr.r_r12;
+    break;
+  case lldb_r13_x86_64:
+    reg_value = (uint64_t)m_gpr.r_r13;
+    break;
+  case lldb_r14_x86_64:
+    reg_value = (uint64_t)m_gpr.r_r14;
+    break;
+  case lldb_r15_x86_64:
+    reg_value = (uint64_t)m_gpr.r_r15;
+    break;
+  case lldb_rip_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rip;
+    break;
+  case lldb_rflags_x86_64:
+    reg_value = (uint64_t)m_gpr.r_rflags;
+    break;
+  case lldb_cs_x86_64:
+    reg_value = (uint64_t)m_gpr.r_cs;
+    break;
+  case lldb_fs_x86_64:
+    reg_value = (uint64_t)m_gpr.r_fs;
+    break;
+  case lldb_gs_x86_64:
+    reg_value = (uint64_t)m_gpr.r_gs;
+    break;
+  case lldb_ss_x86_64:
+    reg_value = (uint64_t)m_gpr.r_ss;
+    break;
+  case lldb_ds_x86_64:
+    reg_value = (uint64_t)m_gpr.r_ds;
+    break;
+  case lldb_es_x86_64:
+    reg_value = (uint64_t)m_gpr.r_es;
+    break;
+#else
+  case lldb_rax_x86_64:
+    reg_value = (uint32_t)m_gpr.r_eax;
+    break;
+  case lldb_rbx_x86_64:
+    reg_value = (uint32_t)m_gpr.r_ebx;
+    break;
+  case lldb_rcx_x86_64:
+    reg_value = (uint32_t)m_gpr.r_ecx;
+    break;
+  case lldb_rdx_x86_64:
+    reg_value = (uint32_t)m_gpr.r_edx;
+    break;
+  case lldb_rdi_x86_64:
+    reg_value = (uint32_t)m_gpr.r_edi;
+    break;
+  case lldb_rsi_x86_64:
+    reg_value = (uint32_t)m_gpr.r_esi;
+    break;
+  case lldb_rsp_x86_64:
+    reg_value = (uint32_t)m_gpr.r_esp;
+    break;
+  case lldb_rbp_x86_64:
+    reg_value = (uint32_t)m_gpr.r_ebp;
+    break;
+  case lldb_rip_x86_64:
+    reg_value = (uint32_t)m_gpr.r_eip;
+    break;
+  case lldb_rflags_x86_64:
+    reg_value = (uint32_t)m_gpr.r_eflags;
+    break;
+  case lldb_cs_x86_64:
+    reg_value = (uint32_t)m_gpr.r_cs;
+    break;
+  case lldb_fs_x86_64:
+    reg_value = (uint32_t)m_gpr.r_fs;
+    break;
+  case lldb_gs_x86_64:
+    reg_value = (uint32_t)m_gpr.r_gs;
+    break;
+  case lldb_ss_x86_64:
+    reg_value = (uint32_t)m_gpr.r_ss;
+    break;
+  case lldb_ds_x86_64:
+    reg_value = (uint32_t)m_gpr.r_ds;
+    break;
+  case lldb_es_x86_64:
+    reg_value = (uint32_t)m_gpr.r_es;
+    break;
+#endif
+#if defined(__x86_64__)
+// the 32-bit field carries more detail, so we don't have to reinvent
+// the wheel
+#define FPR_ENV(x) ((struct envxmm32 *)m_fpr.fpr_env)->x
+#else
+#define FPR_ENV(x) ((struct envxmm *)m_fpr.xmm_env)->x
+#endif
+  case lldb_fctrl_x86_64:
+    reg_value = (uint16_t)FPR_ENV(en_cw);
+    break;
+  case lldb_fstat_x86_64:
+    reg_value = (uint16_t)FPR_ENV(en_sw);
+    break;
+  case lldb_ftag_x86_64:
+    reg_value = (uint16_t)FPR_ENV(en_tw);
+    break;
+  case lldb_fop_x86_64:
+    reg_value = (uint16_t)FPR_ENV(en_opcode);
+    break;
+  case lldb_fiseg_x86_64:
+    reg_value = (uint32_t)FPR_ENV(en_fcs);
+    break;
+  case lldb_fioff_x86_64:
+    reg_value = (uint32_t)FPR_ENV(en_fip);
+    break;
+  case lldb_foseg_x86_64:
+    reg_value = (uint32_t)FPR_ENV(en_fos);
+    break;
+  case lldb_fooff_x86_64:
+    reg_value = (uint32_t)FPR_ENV(en_foo);
+    break;
+  case lldb_mxcsr_x86_64:
+    reg_value = (uint32_t)FPR_ENV(en_mxcsr);
+    break;
+  case lldb_mxcsrmask_x86_64:
+    reg_value = (uint32_t)FPR_ENV(en_mxcsr_mask);
+    break;
+  case lldb_st0_x86_64:
+  case lldb_st1_x86_64:
+  case lldb_st2_x86_64:
+  case lldb_st3_x86_64:
+  case lldb_st4_x86_64:
+  case lldb_st5_x86_64:
+  case lldb_st6_x86_64:
+  case lldb_st7_x86_64:
+#if defined(__x86_64__)
+    reg_value.SetBytes(&m_fpr.fpr_acc[reg - lldb_st0_x86_64],
+                       reg_info->byte_size, endian::InlHostByteOrder());
+#else
+    reg_value.SetBytes(&m_fpr.xmm_acc[reg - lldb_st0_x86_64],
+                       reg_info->byte_size, endian::InlHostByteOrder());
+#endif
+    break;
+  case lldb_mm0_x86_64:
+  case lldb_mm1_x86_64:
+  case lldb_mm2_x86_64:
+  case lldb_mm3_x86_64:
+  case lldb_mm4_x86_64:
+  case lldb_mm5_x86_64:
+  case lldb_mm6_x86_64:
+  case lldb_mm7_x86_64:
+#if defined(__x86_64__)
+    reg_value.SetBytes(&m_fpr.fpr_acc[reg - lldb_mm0_x86_64],
+                       reg_info->byte_size, endian::InlHostByteOrder());
+#else
+    reg_value.SetBytes(&m_fpr.xmm_acc[reg - lldb_mm0_x86_64],
+                       reg_info->byte_size, endian::InlHostByteOrder());
+#endif
+    break;
+  case lldb_xmm0_x86_64:
+  case lldb_xmm1_x86_64:
+  case lldb_xmm2_x86_64:
+  case lldb_xmm3_x86_64:
+  case lldb_xmm4_x86_64:
+  case lldb_xmm5_x86_64:
+  case lldb_xmm6_x86_64:
+  case lldb_xmm7_x86_64:
+  case lldb_xmm8_x86_64:
+  case lldb_xmm9_x86_64:
+  case lldb_xmm10_x86_64:
+  case lldb_xmm11_x86_64:
+  case lldb_xmm12_x86_64:
+  case lldb_xmm13_x86_64:
+  case lldb_xmm14_x86_64:
+  case lldb_xmm15_x86_64:
+#if defined(__x86_64__)
+    reg_value.SetBytes(&m_fpr.fpr_xacc[reg - lldb_xmm0_x86_64],
+                       reg_info->byte_size, endian::InlHostByteOrder());
+#else
+    reg_value.SetBytes(&m_fpr.xmm_reg[reg - lldb_xmm0_x86_64],
+                       reg_info->byte_size, endian::InlHostByteOrder());
+#endif
+    break;
+  case lldb_dr0_x86_64:
+  case lldb_dr1_x86_64:
+  case lldb_dr2_x86_64:
+  case lldb_dr3_x86_64:
+  case lldb_dr4_x86_64:
+  case lldb_dr5_x86_64:
+  case lldb_dr6_x86_64:
+  case lldb_dr7_x86_64:
+    reg_value = (uint64_t)m_dbr.dr[reg - lldb_dr0_x86_64];
+    break;
+  default:
+    llvm_unreachable("Reading unknown/unsupported register");
+  }
+
+  return error;
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::WriteRegister(
+    const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+
+  Status error;
+
+  if (!reg_info) {
+    error.SetErrorString("reg_info NULL");
+    return error;
+  }
+
+  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;
+  }
+
+  int set = GetSetForNativeRegNum(reg);
+  if (set == -1) {
+    // This is likely an internal register for lldb use only and should not be
+    // directly queried.
+    error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
+                                   reg_info->name);
+    return error;
+  }
+
+  switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
+  case llvm::Triple::x86_64:
+    break;
+  case llvm::Triple::x86:
+    reg = RegNumX86ToX86_64(reg);
+    break;
+  default:
+    llvm_unreachable("Unhandled target architecture.");
+  }
+
+  error = ReadRegisterSet(set);
+  if (error.Fail())
+    return error;
+
+  switch (reg) {
+#if defined(__x86_64__)
+  case lldb_rax_x86_64:
+    m_gpr.r_rax = reg_value.GetAsUInt64();
+    break;
+  case lldb_rbx_x86_64:
+    m_gpr.r_rbx = reg_value.GetAsUInt64();
+    break;
+  case lldb_rcx_x86_64:
+    m_gpr.r_rcx = reg_value.GetAsUInt64();
+    break;
+  case lldb_rdx_x86_64:
+    m_gpr.r_rdx = reg_value.GetAsUInt64();
+    break;
+  case lldb_rdi_x86_64:
+    m_gpr.r_rdi = reg_value.GetAsUInt64();
+    break;
+  case lldb_rsi_x86_64:
+    m_gpr.r_rsi = reg_value.GetAsUInt64();
+    break;
+  case lldb_rbp_x86_64:
+    m_gpr.r_rbp = reg_value.GetAsUInt64();
+    break;
+  case lldb_rsp_x86_64:
+    m_gpr.r_rsp = reg_value.GetAsUInt64();
+    break;
+  case lldb_r8_x86_64:
+    m_gpr.r_r8 = reg_value.GetAsUInt64();
+    break;
+  case lldb_r9_x86_64:
+    m_gpr.r_r9 = reg_value.GetAsUInt64();
+    break;
+  case lldb_r10_x86_64:
+    m_gpr.r_r10 = reg_value.GetAsUInt64();
+    break;
+  case lldb_r11_x86_64:
+    m_gpr.r_r11 = reg_value.GetAsUInt64();
+    break;
+  case lldb_r12_x86_64:
+    m_gpr.r_r12 = reg_value.GetAsUInt64();
+    break;
+  case lldb_r13_x86_64:
+    m_gpr.r_r13 = reg_value.GetAsUInt64();
+    break;
+  case lldb_r14_x86_64:
+    m_gpr.r_r14 = reg_value.GetAsUInt64();
+    break;
+  case lldb_r15_x86_64:
+    m_gpr.r_r15 = reg_value.GetAsUInt64();
+    break;
+  case lldb_rip_x86_64:
+    m_gpr.r_rip = reg_value.GetAsUInt64();
+    break;
+  case lldb_rflags_x86_64:
+    m_gpr.r_rflags = reg_value.GetAsUInt64();
+    break;
+  case lldb_cs_x86_64:
+    m_gpr.r_cs = reg_value.GetAsUInt64();
+    break;
+  case lldb_fs_x86_64:
+    m_gpr.r_fs = reg_value.GetAsUInt64();
+    break;
+  case lldb_gs_x86_64:
+    m_gpr.r_gs = reg_value.GetAsUInt64();
+    break;
+  case lldb_ss_x86_64:
+    m_gpr.r_ss = reg_value.GetAsUInt64();
+    break;
+  case lldb_ds_x86_64:
+    m_gpr.r_ds = reg_value.GetAsUInt64();
+    break;
+  case lldb_es_x86_64:
+    m_gpr.r_es = reg_value.GetAsUInt64();
+    break;
+#else
+  case lldb_rax_x86_64:
+    m_gpr.r_eax = reg_value.GetAsUInt32();
+    break;
+  case lldb_rbx_x86_64:
+    m_gpr.r_ebx = reg_value.GetAsUInt32();
+    break;
+  case lldb_rcx_x86_64:
+    m_gpr.r_ecx = reg_value.GetAsUInt32();
+    break;
+  case lldb_rdx_x86_64:
+    m_gpr.r_edx = reg_value.GetAsUInt32();
+    break;
+  case lldb_rdi_x86_64:
+    m_gpr.r_edi = reg_value.GetAsUInt32();
+    break;
+  case lldb_rsi_x86_64:
+    m_gpr.r_esi = reg_value.GetAsUInt32();
+    break;
+  case lldb_rsp_x86_64:
+    m_gpr.r_esp = reg_value.GetAsUInt32();
+    break;
+  case lldb_rbp_x86_64:
+    m_gpr.r_ebp = reg_value.GetAsUInt32();
+    break;
+  case lldb_rip_x86_64:
+    m_gpr.r_eip = reg_value.GetAsUInt32();
+    break;
+  case lldb_rflags_x86_64:
+    m_gpr.r_eflags = reg_value.GetAsUInt32();
+    break;
+  case lldb_cs_x86_64:
+    m_gpr.r_cs = reg_value.GetAsUInt32();
+    break;
+  case lldb_fs_x86_64:
+    m_gpr.r_fs = reg_value.GetAsUInt32();
+    break;
+  case lldb_gs_x86_64:
+    m_gpr.r_gs = reg_value.GetAsUInt32();
+    break;
+  case lldb_ss_x86_64:
+    m_gpr.r_ss = reg_value.GetAsUInt32();
+    break;
+  case lldb_ds_x86_64:
+    m_gpr.r_ds = reg_value.GetAsUInt32();
+    break;
+  case lldb_es_x86_64:
+    m_gpr.r_es = reg_value.GetAsUInt32();
+    break;
+#endif
+  case lldb_fctrl_x86_64:
+    FPR_ENV(en_cw) = reg_value.GetAsUInt16();
+    break;
+  case lldb_fstat_x86_64:
+    FPR_ENV(en_sw) = reg_value.GetAsUInt16();
+    break;
+  case lldb_ftag_x86_64:
+    FPR_ENV(en_tw) = reg_value.GetAsUInt16();
+    break;
+  case lldb_fop_x86_64:
+    FPR_ENV(en_opcode) = reg_value.GetAsUInt16();
+    break;
+  case lldb_fiseg_x86_64:
+    FPR_ENV(en_fcs) = reg_value.GetAsUInt32();
+    break;
+  case lldb_fioff_x86_64:
+    FPR_ENV(en_fip) = reg_value.GetAsUInt32();
+    break;
+  case lldb_foseg_x86_64:
+    FPR_ENV(en_fos) = reg_value.GetAsUInt32();
+    break;
+  case lldb_fooff_x86_64:
+    FPR_ENV(en_foo) = reg_value.GetAsUInt32();
+    break;
+  case lldb_mxcsr_x86_64:
+    FPR_ENV(en_mxcsr) = reg_value.GetAsUInt32();
+    break;
+  case lldb_mxcsrmask_x86_64:
+    FPR_ENV(en_mxcsr_mask) = reg_value.GetAsUInt32();
+    break;
+  case lldb_st0_x86_64:
+  case lldb_st1_x86_64:
+  case lldb_st2_x86_64:
+  case lldb_st3_x86_64:
+  case lldb_st4_x86_64:
+  case lldb_st5_x86_64:
+  case lldb_st6_x86_64:
+  case lldb_st7_x86_64:
+#if defined(__x86_64__)
+    ::memcpy(&m_fpr.fpr_acc[reg - lldb_st0_x86_64], reg_value.GetBytes(),
+             reg_value.GetByteSize());
+#else
+    ::memcpy(&m_fpr.xmm_acc[reg - lldb_st0_x86_64], reg_value.GetBytes(),
+             reg_value.GetByteSize());
+#endif
+    break;
+  case lldb_mm0_x86_64:
+  case lldb_mm1_x86_64:
+  case lldb_mm2_x86_64:
+  case lldb_mm3_x86_64:
+  case lldb_mm4_x86_64:
+  case lldb_mm5_x86_64:
+  case lldb_mm6_x86_64:
+  case lldb_mm7_x86_64:
+#if defined(__x86_64__)
+    ::memcpy(&m_fpr.fpr_acc[reg - lldb_mm0_x86_64], reg_value.GetBytes(),
+             reg_value.GetByteSize());
+#else
+    ::memcpy(&m_fpr.xmm_acc[reg - lldb_mm0_x86_64], reg_value.GetBytes(),
+             reg_value.GetByteSize());
+#endif
+    break;
+  case lldb_xmm0_x86_64:
+  case lldb_xmm1_x86_64:
+  case lldb_xmm2_x86_64:
+  case lldb_xmm3_x86_64:
+  case lldb_xmm4_x86_64:
+  case lldb_xmm5_x86_64:
+  case lldb_xmm6_x86_64:
+  case lldb_xmm7_x86_64:
+  case lldb_xmm8_x86_64:
+  case lldb_xmm9_x86_64:
+  case lldb_xmm10_x86_64:
+  case lldb_xmm11_x86_64:
+  case lldb_xmm12_x86_64:
+  case lldb_xmm13_x86_64:
+  case lldb_xmm14_x86_64:
+  case lldb_xmm15_x86_64:
+#if defined(__x86_64__)
+    ::memcpy(&m_fpr.fpr_xacc[reg - lldb_xmm0_x86_64], reg_value.GetBytes(),
+             reg_value.GetByteSize());
+#else
+    ::memcpy(&m_fpr.xmm_reg[reg - lldb_xmm0_x86_64], reg_value.GetBytes(),
+             reg_value.GetByteSize());
+#endif
+    break;
+  case lldb_dr0_x86_64:
+  case lldb_dr1_x86_64:
+  case lldb_dr2_x86_64:
+  case lldb_dr3_x86_64:
+  case lldb_dr4_x86_64:
+  case lldb_dr5_x86_64:
+  case lldb_dr6_x86_64:
+  case lldb_dr7_x86_64:
+    m_dbr.dr[reg - lldb_dr0_x86_64] = reg_value.GetAsUInt64();
+    break;
+  default:
+    llvm_unreachable("Reading unknown/unsupported register");
+  }
+
+  return WriteRegisterSet(set);
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::ReadAllRegisterValues(
+    lldb::DataBufferSP &data_sp) {
+  Status error;
+
+  data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+  error = ReadRegisterSet(GPRegSet);
+  if (error.Fail())
+    return error;
+
+  uint8_t *dst = data_sp->GetBytes();
+  ::memcpy(dst, &m_gpr, GetRegisterInfoInterface().GetGPRSize());
+  dst += GetRegisterInfoInterface().GetGPRSize();
+
+  return error;
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::WriteAllRegisterValues(
+    const lldb::DataBufferSP &data_sp) {
+  Status error;
+
+  if (!data_sp) {
+    error.SetErrorStringWithFormat(
+        "NativeRegisterContextFreeBSD_x86_64::%s invalid data_sp provided",
+        __FUNCTION__);
+    return error;
+  }
+
+  if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
+    error.SetErrorStringWithFormat(
+        "NativeRegisterContextFreeBSD_x86_64::%s data_sp contained mismatched "
+        "data size, expected %zu, actual %" PRIu64,
+        __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
+    return error;
+  }
+
+  uint8_t *src = data_sp->GetBytes();
+  if (src == nullptr) {
+    error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_x86_64::%s "
+                                   "DataBuffer::GetBytes() returned a null "
+                                   "pointer",
+                                   __FUNCTION__);
+    return error;
+  }
+  ::memcpy(&m_gpr, src, GetRegisterInfoInterface().GetGPRSize());
+
+  error = WriteRegisterSet(GPRegSet);
+  if (error.Fail())
+    return error;
+  src += GetRegisterInfoInterface().GetGPRSize();
+
+  return error;
+}
+
+int NativeRegisterContextFreeBSD_x86_64::GetDR(int num) const {
+  assert(num >= 0 && num <= 7);
+  switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
+  case llvm::Triple::x86:
+    return lldb_dr0_i386 + num;
+  case llvm::Triple::x86_64:
+    return lldb_dr0_x86_64 + num;
+  default:
+    llvm_unreachable("Unhandled target architecture.");
+  }
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::IsWatchpointHit(uint32_t wp_index,
+                                                            bool &is_hit) {
+  if (wp_index >= NumSupportedHardwareWatchpoints())
+    return Status("Watchpoint index out of range");
+
+  RegisterValue reg_value;
+  const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(GetDR(6));
+  Status error = ReadRegister(reg_info, 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;
+}
+
+Status NativeRegisterContextFreeBSD_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;
+    Status 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 Status();
+}
+
+Status
+NativeRegisterContextFreeBSD_x86_64::IsWatchpointVacant(uint32_t wp_index,
+                                                        bool &is_vacant) {
+  if (wp_index >= NumSupportedHardwareWatchpoints())
+    return Status("Watchpoint index out of range");
+
+  RegisterValue reg_value;
+  const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(GetDR(7));
+  Status error = ReadRegister(reg_info, 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 + 1)));
+
+  return error;
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::SetHardwareWatchpointWithIndex(
+    lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
+
+  if (wp_index >= NumSupportedHardwareWatchpoints())
+    return Status("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 Status("Invalid read/write bits for watchpoint");
+
+  if (size != 1 && size != 2 && size != 4 && size != 8)
+    return Status("Invalid size for watchpoint");
+
+  bool is_vacant;
+  Status error = IsWatchpointVacant(wp_index, is_vacant);
+  if (error.Fail())
+    return error;
+  if (!is_vacant)
+    return Status("Watchpoint index not vacant");
+
+  const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7));
+  RegisterValue dr7_value;
+  error = ReadRegister(reg_info_dr7, dr7_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 + 1);
+
+  // 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 = dr7_value.GetAsUInt64() & ~bit_mask;
+
+  control_bits |= enable_bit | rw_bits | size_bits;
+
+  const RegisterInfo *const reg_info_drN =
+      GetRegisterInfoAtIndex(GetDR(wp_index));
+  RegisterValue drN_value;
+  error = ReadRegister(reg_info_drN, drN_value);
+  if (error.Fail())
+    return error;
+
+  // clear dr6 if address or bits changed (i.e. we're not reenabling the same
+  // watchpoint)
+  if (drN_value.GetAsUInt64() != addr ||
+      (dr7_value.GetAsUInt64() & bit_mask) != (rw_bits | size_bits)) {
+    ClearWatchpointHit(wp_index);
+
+    error = WriteRegister(reg_info_drN, RegisterValue(addr));
+    if (error.Fail())
+      return error;
+  }
+
+  error = WriteRegister(reg_info_dr7, RegisterValue(control_bits));
+  if (error.Fail())
+    return error;
+
+  error.Clear();
+  return error;
+}
+
+bool NativeRegisterContextFreeBSD_x86_64::ClearHardwareWatchpoint(
+    uint32_t wp_index) {
+  if (wp_index >= NumSupportedHardwareWatchpoints())
+    return false;
+
+  // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0-1, 2-3, 4-5
+  // or 6-7 of the debug control register (DR7)
+  const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7));
+  RegisterValue reg_value;
+  Status error = ReadRegister(reg_info_dr7, reg_value);
+  if (error.Fail())
+    return false;
+  uint64_t bit_mask = 0x3 << (2 * wp_index);
+  uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+
+  return WriteRegister(reg_info_dr7, RegisterValue(control_bits)).Success();
+}
+
+Status
+NativeRegisterContextFreeBSD_x86_64::ClearWatchpointHit(uint32_t wp_index) {
+  if (wp_index >= NumSupportedHardwareWatchpoints())
+    return Status("Watchpoint index out of range");
+
+  // for watchpoints 0, 1, 2, or 3, respectively, check bits 0, 1, 2, or 3 of
+  // the debug status register (DR6)
+  const RegisterInfo *const reg_info_dr6 = GetRegisterInfoAtIndex(GetDR(6));
+  RegisterValue reg_value;
+  Status error = ReadRegister(reg_info_dr6, reg_value);
+  if (error.Fail())
+    return error;
+
+  uint64_t bit_mask = 1 << wp_index;
+  uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+  return WriteRegister(reg_info_dr6, RegisterValue(status_bits));
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::ClearAllHardwareWatchpoints() {
+  RegisterValue reg_value;
+
+  // clear bits {0-4} of the debug status register (DR6)
+  const RegisterInfo *const reg_info_dr6 = GetRegisterInfoAtIndex(GetDR(6));
+  Status error = ReadRegister(reg_info_dr6, reg_value);
+  if (error.Fail())
+    return error;
+  uint64_t bit_mask = 0xF;
+  uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+  error = WriteRegister(reg_info_dr6, RegisterValue(status_bits));
+  if (error.Fail())
+    return error;
+
+  // clear bits {0-7,16-31} of the debug control register (DR7)
+  const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7));
+  error = ReadRegister(reg_info_dr7, reg_value);
+  if (error.Fail())
+    return error;
+  bit_mask = 0xFF | (0xFFFF << 16);
+  uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+  return WriteRegister(reg_info_dr7, RegisterValue(control_bits));
+}
+
+uint32_t NativeRegisterContextFreeBSD_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;
+    Status 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) {
+      LLDB_LOGF(log, "NativeRegisterContextFreeBSD_x86_64::%s Error: %s",
+                __FUNCTION__, error.AsCString());
+    }
+  }
+  return LLDB_INVALID_INDEX32;
+}
+
+lldb::addr_t
+NativeRegisterContextFreeBSD_x86_64::GetWatchpointAddress(uint32_t wp_index) {
+  if (wp_index >= NumSupportedHardwareWatchpoints())
+    return LLDB_INVALID_ADDRESS;
+  RegisterValue reg_value;
+  const RegisterInfo *const reg_info_drN =
+      GetRegisterInfoAtIndex(GetDR(wp_index));
+  if (ReadRegister(reg_info_drN, reg_value).Fail())
+    return LLDB_INVALID_ADDRESS;
+  return reg_value.GetAsUInt64();
+}
+
+uint32_t
+NativeRegisterContextFreeBSD_x86_64::NumSupportedHardwareWatchpoints() {
+  // Available debug address registers: dr0, dr1, dr2, dr3
+  return 4;
+}
+
+Status NativeRegisterContextFreeBSD_x86_64::CopyHardwareWatchpointsFrom(
+    NativeRegisterContextFreeBSD &source) {
+  auto &r_source = static_cast<NativeRegisterContextFreeBSD_x86_64 &>(source);
+  Status res = r_source.ReadRegisterSet(DBRegSet);
+  if (!res.Fail()) {
+    // copy dbregs only if any watchpoints were set
+    if ((r_source.m_dbr.dr[7] & 0xFF) == 0)
+      return res;
+
+    m_dbr = r_source.m_dbr;
+    res = WriteRegisterSet(DBRegSet);
+  }
+  return res;
+}
+
+#endif // defined(__x86_64__)

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h
new file mode 100644
index 000000000000..261d75f2290f
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h
@@ -0,0 +1,101 @@
+//===-- NativeRegisterContextFreeBSD_x86_64.h -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#ifndef lldb_NativeRegisterContextFreeBSD_x86_64_h
+#define lldb_NativeRegisterContextFreeBSD_x86_64_h
+
+// clang-format off
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <machine/reg.h>
+// clang-format on
+
+#include "Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h"
+#include "Plugins/Process/Utility/RegisterContext_x86.h"
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+namespace lldb_private {
+namespace process_freebsd {
+
+class NativeProcessFreeBSD;
+
+class NativeRegisterContextFreeBSD_x86_64
+    : public NativeRegisterContextFreeBSD {
+public:
+  NativeRegisterContextFreeBSD_x86_64(const ArchSpec &target_arch,
+                                      NativeThreadProtocol &native_thread);
+  uint32_t GetRegisterSetCount() const override;
+
+  const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
+
+  Status ReadRegister(const RegisterInfo *reg_info,
+                      RegisterValue &reg_value) override;
+
+  Status WriteRegister(const RegisterInfo *reg_info,
+                       const RegisterValue &reg_value) override;
+
+  Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+  Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+  Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
+
+  Status GetWatchpointHitIndex(uint32_t &wp_index,
+                               lldb::addr_t trap_addr) override;
+
+  Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
+
+  bool ClearHardwareWatchpoint(uint32_t wp_index) override;
+
+  Status ClearWatchpointHit(uint32_t wp_index) override;
+
+  Status ClearAllHardwareWatchpoints() override;
+
+  Status 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;
+
+  Status
+  CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override;
+
+private:
+  // Private member types.
+  enum { GPRegSet, FPRegSet, DBRegSet };
+
+  // Private member variables.
+  struct reg m_gpr;
+#if defined(__x86_64__)
+  struct fpreg m_fpr;
+#else
+  struct xmmreg m_fpr;
+#endif
+  struct dbreg m_dbr;
+
+  int GetSetForNativeRegNum(int reg_num) const;
+  int GetDR(int num) const;
+
+  Status ReadRegisterSet(uint32_t set);
+  Status WriteRegisterSet(uint32_t set);
+};
+
+} // namespace process_freebsd
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextFreeBSD_x86_64_h
+
+#endif // defined(__x86_64__)

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp
new file mode 100644
index 000000000000..1517e7ff8ab5
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp
@@ -0,0 +1,216 @@
+//===-- NativeThreadFreeBSD.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeThreadFreeBSD.h"
+#include "NativeRegisterContextFreeBSD.h"
+
+#include "NativeProcessFreeBSD.h"
+
+#include "Plugins/Process/POSIX/CrashReason.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/State.h"
+#include "llvm/Support/Errno.h"
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+// clang-format on
+
+#include <sstream>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_freebsd;
+
+NativeThreadFreeBSD::NativeThreadFreeBSD(NativeProcessFreeBSD &process,
+                                         lldb::tid_t tid)
+    : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid),
+      m_stop_info(),
+      m_reg_context_up(
+          NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD(
+              process.GetArchitecture(), *this)),
+      m_stop_description() {}
+
+Status NativeThreadFreeBSD::Resume() {
+  Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, m_process.GetID(),
+                                                   nullptr, GetID());
+  if (!ret.Success())
+    return ret;
+  ret = NativeProcessFreeBSD::PtraceWrapper(PT_CLEARSTEP, m_process.GetID(),
+                                            nullptr, GetID());
+  if (ret.Success())
+    SetRunning();
+  return ret;
+}
+
+Status NativeThreadFreeBSD::SingleStep() {
+  Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, m_process.GetID(),
+                                                   nullptr, GetID());
+  if (!ret.Success())
+    return ret;
+  ret = NativeProcessFreeBSD::PtraceWrapper(PT_SETSTEP, m_process.GetID(),
+                                            nullptr, GetID());
+  if (ret.Success())
+    SetStepping();
+  return ret;
+}
+
+Status NativeThreadFreeBSD::Suspend() {
+  Status ret = NativeProcessFreeBSD::PtraceWrapper(
+      PT_SUSPEND, m_process.GetID(), nullptr, GetID());
+  if (ret.Success())
+    SetStopped();
+  return ret;
+}
+
+void NativeThreadFreeBSD::SetStoppedBySignal(uint32_t signo,
+                                             const siginfo_t *info) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+  LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo);
+
+  SetStopped();
+
+  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:
+      const auto reason = GetCrashReason(*info);
+      m_stop_description = GetCrashReasonString(reason, *info);
+      break;
+    }
+  }
+}
+
+void NativeThreadFreeBSD::SetStoppedByBreakpoint() {
+  SetStopped();
+  m_stop_info.reason = StopReason::eStopReasonBreakpoint;
+  m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadFreeBSD::SetStoppedByTrace() {
+  SetStopped();
+  m_stop_info.reason = StopReason::eStopReasonTrace;
+  m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadFreeBSD::SetStoppedByExec() {
+  SetStopped();
+  m_stop_info.reason = StopReason::eStopReasonExec;
+  m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadFreeBSD::SetStoppedByWatchpoint(uint32_t wp_index) {
+  SetStopped();
+}
+
+void NativeThreadFreeBSD::SetStoppedWithNoReason() {
+  SetStopped();
+
+  m_stop_info.reason = StopReason::eStopReasonNone;
+  m_stop_info.details.signal.signo = 0;
+}
+
+void NativeThreadFreeBSD::SetStopped() {
+  const StateType new_state = StateType::eStateStopped;
+  m_state = new_state;
+  m_stop_description.clear();
+}
+
+void NativeThreadFreeBSD::SetRunning() {
+  m_state = StateType::eStateRunning;
+  m_stop_info.reason = StopReason::eStopReasonNone;
+}
+
+void NativeThreadFreeBSD::SetStepping() {
+  m_state = StateType::eStateStepping;
+  m_stop_info.reason = StopReason::eStopReasonNone;
+}
+
+std::string NativeThreadFreeBSD::GetName() { return ""; }
+
+lldb::StateType NativeThreadFreeBSD::GetState() { return m_state; }
+
+bool NativeThreadFreeBSD::GetStopReason(ThreadStopInfo &stop_info,
+                                        std::string &description) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+  description.clear();
+
+  switch (m_state) {
+  case eStateStopped:
+  case eStateCrashed:
+  case eStateExited:
+  case eStateSuspended:
+  case eStateUnloaded:
+    stop_info = m_stop_info;
+    description = m_stop_description;
+
+    return true;
+
+  case eStateInvalid:
+  case eStateConnected:
+  case eStateAttaching:
+  case eStateLaunching:
+  case eStateRunning:
+  case eStateStepping:
+  case eStateDetached:
+    LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason", GetID(),
+             StateAsCString(m_state));
+    return false;
+  }
+  llvm_unreachable("unhandled StateType!");
+}
+
+NativeRegisterContextFreeBSD &NativeThreadFreeBSD::GetRegisterContext() {
+  assert(m_reg_context_up);
+  return *m_reg_context_up;
+}
+
+Status NativeThreadFreeBSD::SetWatchpoint(lldb::addr_t addr, size_t size,
+                                          uint32_t watch_flags, bool hardware) {
+  return Status("not implemented");
+}
+
+Status NativeThreadFreeBSD::RemoveWatchpoint(lldb::addr_t addr) {
+  auto wp = m_watchpoint_index_map.find(addr);
+  if (wp == m_watchpoint_index_map.end())
+    return Status();
+  return Status("not implemented");
+}
+
+Status NativeThreadFreeBSD::SetHardwareBreakpoint(lldb::addr_t addr,
+                                                  size_t size) {
+  if (m_state == eStateLaunching)
+    return Status();
+
+  Status error = RemoveHardwareBreakpoint(addr);
+  if (error.Fail())
+    return error;
+
+  return Status("not implemented");
+}
+
+Status NativeThreadFreeBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+  auto bp = m_hw_break_index_map.find(addr);
+  if (bp == m_hw_break_index_map.end())
+    return Status();
+
+  return Status("not implemented");
+}
+
+Status NativeThreadFreeBSD::CopyWatchpointsFrom(NativeThreadFreeBSD &source) {
+  return Status("not implemented");
+}

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h
new file mode 100644
index 000000000000..e4d494174736
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h
@@ -0,0 +1,83 @@
+//===-- NativeThreadFreeBSD.h --------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NativeThreadFreeBSD_H_
+#define liblldb_NativeThreadFreeBSD_H_
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h"
+
+#include <csignal>
+#include <map>
+#include <string>
+
+namespace lldb_private {
+namespace process_freebsd {
+
+class NativeProcessFreeBSD;
+
+class NativeThreadFreeBSD : public NativeThreadProtocol {
+  friend class NativeProcessFreeBSD;
+
+public:
+  NativeThreadFreeBSD(NativeProcessFreeBSD &process, lldb::tid_t tid);
+
+  // NativeThreadProtocol Interface
+  std::string GetName() override;
+
+  lldb::StateType GetState() override;
+
+  bool GetStopReason(ThreadStopInfo &stop_info,
+                     std::string &description) override;
+
+  NativeRegisterContextFreeBSD &GetRegisterContext() override;
+
+  Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags,
+                       bool hardware) override;
+
+  Status RemoveWatchpoint(lldb::addr_t addr) override;
+
+  Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+  Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
+
+private:
+  // Interface for friend classes
+
+  Status Resume();
+  Status SingleStep();
+  Status Suspend();
+
+  void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
+  void SetStoppedByBreakpoint();
+  void SetStoppedByTrace();
+  void SetStoppedByExec();
+  void SetStoppedByWatchpoint(uint32_t wp_index);
+  void SetStoppedWithNoReason();
+  void SetStopped();
+  void SetRunning();
+  void SetStepping();
+
+  Status CopyWatchpointsFrom(NativeThreadFreeBSD &source);
+
+  // Member Variables
+  lldb::StateType m_state;
+  ThreadStopInfo m_stop_info;
+  std::unique_ptr<NativeRegisterContextFreeBSD> m_reg_context_up;
+  std::string m_stop_description;
+  using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
+  WatchpointIndexMap m_watchpoint_index_map;
+  WatchpointIndexMap m_hw_break_index_map;
+};
+
+typedef std::shared_ptr<NativeThreadFreeBSD> NativeThreadFreeBSDSP;
+} // namespace process_freebsd
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeThreadFreeBSD_H_

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
index 08d489851799..1ca0290eda13 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -843,7 +843,7 @@ GDBRemoteCommunicationServerCommon::Handle_qSupported(
   response.PutCString(";QListThreadsInStopReply+");
   response.PutCString(";qEcho+");
   response.PutCString(";qXfer:features:read+");
-#if defined(__linux__) || defined(__NetBSD__)
+#if defined(__linux__) || defined(__NetBSD__) || defined(__FreeBSD__)
   response.PutCString(";QPassSignals+");
   response.PutCString(";qXfer:auxv:read+");
   response.PutCString(";qXfer:libraries-svr4:read+");

diff  --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py
index 2ee646e3fc7d..56357c603ae3 100644
--- a/lldb/test/Shell/lit.cfg.py
+++ b/lldb/test/Shell/lit.cfg.py
@@ -133,3 +133,6 @@ def calculate_arch_features(arch_string):
         can_set_dbregs = False
 if can_set_dbregs:
     config.available_features.add('dbregs-set')
+
+# pass control variable through
+llvm_config.with_system_environment('FREEBSD_REMOTE_PLUGIN')

diff  --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index 02453b205deb..6e7b30df5c58 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -4,6 +4,12 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android")
   list(APPEND LLDB_PLUGINS lldbPluginProcessLinux)
 endif()
 
+if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+  list(APPEND LLDB_PLUGINS
+    lldbPluginProcessFreeBSDRemote
+    lldbPluginProcessFreeBSD)
+endif()
+
 if(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
   list(APPEND LLDB_PLUGINS lldbPluginProcessNetBSD)
 endif()

diff  --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index 7f53756424c6..633e37c3a043 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -38,6 +38,8 @@
 
 #if defined(__linux__)
 #include "Plugins/Process/Linux/NativeProcessLinux.h"
+#elif defined(__FreeBSD__)
+#include "Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h"
 #elif defined(__NetBSD__)
 #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"
 #elif defined(_WIN32)
@@ -61,6 +63,8 @@ using namespace lldb_private::process_gdb_remote;
 namespace {
 #if defined(__linux__)
 typedef process_linux::NativeProcessLinux::Factory NativeProcessFactory;
+#elif defined(__FreeBSD__)
+typedef process_freebsd::NativeProcessFreeBSD::Factory NativeProcessFactory;
 #elif defined(__NetBSD__)
 typedef process_netbsd::NativeProcessNetBSD::Factory NativeProcessFactory;
 #elif defined(_WIN32)


        


More information about the lldb-commits mailing list