[Lldb-commits] [lldb] [lldb] Add native process support for OpenBSD (PR #235641)
Brad Smith via lldb-commits
lldb-commits at lists.llvm.org
Mon Oct 26 00:57:45 PDT 2026
https://github.com/brad0 created https://github.com/llvm/llvm-project/pull/235641
Add a NativeProcessOpenBSD plugin so that lldb-server can launch,
attach to and debug processes on OpenBSD, and allow process debugging
in PlatformOpenBSD.
Supported architectures are aarch64, i386 and x86_64. The plugin
provides:
- Launch, attach and detach. Attaching to a process that is not a
child of the debugger requires root or kern.global_ptrace=1.
- Software breakpoints, instruction stepping and step-out.
- Memory reads and writes via PT_IO, and auxv via PIOD_READ_AUXV.
- Registers: GPRs, FP/SIMD, the PAC masks and tpidr on aarch64; GPRs,
x87/SSE (FXSAVE) and AVX (XSAVE) on x86_64; GPRs and x87/SSE on
i386. Registers are accessed by their offset in the register info,
and the aarch64 cpsr/fpsr/fpcr fields are detected from AT_HWCAP.
- Multi-threaded processes. OpenBSD does not report thread creation
or exit, so the thread list is refreshed on every stop. The thread
that stopped is taken from PT_GET_PROCESS_STATE, and register
accesses and single-steps are addressed by thread ID.
- The shared library list, provided by deriving from
NativeProcessELF (qXfer:libraries-svr4), which locates r_debug
through DT_DEBUG. Module file and load address lookups use the
same list, since OpenBSD's memory map has no file names.
- Memory region information from KERN_PROC_VMMAP, which is
root-only.
- Following fork() (fork-events and multiprocess), using PTRACE_FORK
when the client enables it.
- Expression evaluation.
Also teach ABISysV_arm64 to read the PAC code/data masks on OpenBSD,
so that return addresses signed with pointer authentication are
stripped when unwinding.
Add RegisterInfoOpenBSDTest, which checks the register infos against
the layout of OpenBSD's ptrace(2) register structures.
Limitations, due to what OpenBSD's ptrace(2) provides:
- There is no siginfo, so crash stops report only the signal.
- There are no exec, vfork or thread events.
- Individual threads cannot be suspended. lldb can resume either all
threads, or only the thread that reported the last stop.
- There are no hardware breakpoints or watchpoints.
Tested on OpenBSD/aarch64, OpenBSD/amd64 and OpenBSD/i386: launch,
attach and detach, breakpoints, stepping, finish, register reads and
writes, expression evaluation, shared library loading, memory
regions, following fork in parent and child mode, and a
multi-threaded program. With this change, check-lldb-unit passes on
all three.
>From 5c679971af26be637fb387f142cffca284b370d8 Mon Sep 17 00:00:00 2001
From: Brad Smith <brad at comstyle.com>
Date: Sat, 24 Oct 2026 05:05:28 -0400
Subject: [PATCH] [lldb] Add native process support for OpenBSD
Add a NativeProcessOpenBSD plugin so that lldb-server can launch,
attach to and debug processes on OpenBSD, and allow process debugging
in PlatformOpenBSD.
Supported architectures are aarch64, i386 and x86_64. The plugin
provides:
- Launch, attach and detach. Attaching to a process that is not a
child of the debugger requires root or kern.global_ptrace=1.
- Software breakpoints, instruction stepping and step-out.
- Memory reads and writes via PT_IO, and auxv via PIOD_READ_AUXV.
- Registers: GPRs, FP/SIMD, the PAC masks and tpidr on aarch64; GPRs,
x87/SSE (FXSAVE) and AVX (XSAVE) on x86_64; GPRs and x87/SSE on
i386. Registers are accessed by their offset in the register info,
and the aarch64 cpsr/fpsr/fpcr fields are detected from AT_HWCAP.
- Multi-threaded processes. OpenBSD does not report thread creation
or exit, so the thread list is refreshed on every stop. The thread
that stopped is taken from PT_GET_PROCESS_STATE, and register
accesses and single-steps are addressed by thread ID.
- The shared library list, provided by deriving from
NativeProcessELF (qXfer:libraries-svr4), which locates r_debug
through DT_DEBUG. Module file and load address lookups use the
same list, since OpenBSD's memory map has no file names.
- Memory region information from KERN_PROC_VMMAP, which is
root-only.
- Following fork() (fork-events and multiprocess), using PTRACE_FORK
when the client enables it.
- Expression evaluation.
Also teach ABISysV_arm64 to read the PAC code/data masks on OpenBSD,
so that return addresses signed with pointer authentication are
stripped when unwinding.
Add RegisterInfoOpenBSDTest, which checks the register infos against
the layout of OpenBSD's ptrace(2) register structures.
Limitations, due to what OpenBSD's ptrace(2) provides:
- There is no siginfo, so crash stops report only the signal.
- There are no exec, vfork or thread events.
- Individual threads cannot be suspended. lldb can resume either all
threads, or only the thread that reported the last stop.
- There are no hardware breakpoints or watchpoints.
Tested on OpenBSD/aarch64, OpenBSD/amd64 and OpenBSD/i386: launch,
attach and detach, breakpoints, stepping, finish, register reads and
writes, expression evaluation, shared library loading, memory
regions, following fork in parent and child mode, and a
multi-threaded program. With this change, check-lldb-unit passes on
all three.
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
---
.../Plugins/ABI/AArch64/ABISysV_arm64.cpp | 29 +
.../Platform/OpenBSD/PlatformOpenBSD.cpp | 10 +-
lldb/source/Plugins/Process/CMakeLists.txt | 1 +
.../Plugins/Process/OpenBSD/CMakeLists.txt | 18 +
.../Process/OpenBSD/NativeProcessOpenBSD.cpp | 1001 +++++++++++++++++
.../Process/OpenBSD/NativeProcessOpenBSD.h | 131 +++
.../OpenBSD/NativeRegisterContextOpenBSD.cpp | 98 ++
.../OpenBSD/NativeRegisterContextOpenBSD.h | 62 +
.../NativeRegisterContextOpenBSD_arch.cpp | 29 +
.../NativeRegisterContextOpenBSD_arm64.cpp | 315 ++++++
.../NativeRegisterContextOpenBSD_arm64.h | 91 ++
.../NativeRegisterContextOpenBSD_x86.cpp | 505 +++++++++
.../NativeRegisterContextOpenBSD_x86.h | 96 ++
.../Process/OpenBSD/NativeThreadOpenBSD.cpp | 151 +++
.../Process/OpenBSD/NativeThreadOpenBSD.h | 72 ++
lldb/tools/lldb-server/CMakeLists.txt | 4 +
lldb/tools/lldb-server/lldb-gdbserver.cpp | 4 +
lldb/unittests/Process/Utility/CMakeLists.txt | 7 +-
.../Utility/RegisterInfoOpenBSDTest.cpp | 244 ++++
19 files changed, 2864 insertions(+), 4 deletions(-)
create mode 100644 lldb/source/Plugins/Process/OpenBSD/CMakeLists.txt
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeProcessOpenBSD.cpp
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeProcessOpenBSD.h
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.cpp
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.h
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arch.cpp
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arm64.cpp
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arm64.h
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86.cpp
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86.h
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeThreadOpenBSD.cpp
create mode 100644 lldb/source/Plugins/Process/OpenBSD/NativeThreadOpenBSD.h
create mode 100644 lldb/unittests/Process/Utility/RegisterInfoOpenBSDTest.cpp
diff --git a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
index b298ec1564bd86..0287b599c11445 100644
--- a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
+++ b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
@@ -834,6 +834,25 @@ static lldb::addr_t ReadLinuxProcessAddressMask(lldb::ProcessSP process_sp,
return address_mask;
}
+// Reads code or data address mask for the current OpenBSD process.
+static lldb::addr_t ReadOpenBSDProcessAddressMask(lldb::ProcessSP process_sp,
+ llvm::StringRef reg_name) {
+ // LLDB_INVALID_ADDRESS_MASK means the mask has not been read yet; return it
+ // until there is a thread to read the PAC mask register from, so the read is
+ // retried rather than locking in an empty mask.
+ lldb::ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread();
+ if (!thread_sp)
+ return LLDB_INVALID_ADDRESS_MASK;
+ lldb::RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext();
+ if (!reg_ctx_sp)
+ return LLDB_INVALID_ADDRESS_MASK;
+ const RegisterInfo *reg_info = reg_ctx_sp->GetRegisterInfoByName(reg_name, 0);
+ if (!reg_info)
+ return LLDB_INVALID_ADDRESS_MASK;
+ return reg_ctx_sp->ReadRegisterAsUnsigned(reg_info->kinds[eRegisterKindLLDB],
+ LLDB_INVALID_ADDRESS_MASK);
+}
+
lldb::addr_t ABISysV_arm64::FixCodeAddress(lldb::addr_t pc) {
if (lldb::ProcessSP process_sp = GetProcessSP()) {
if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSLinux() &&
@@ -841,6 +860,11 @@ lldb::addr_t ABISysV_arm64::FixCodeAddress(lldb::addr_t pc) {
process_sp->SetCodeAddressMask(
ReadLinuxProcessAddressMask(process_sp, "code_mask"));
+ if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSOpenBSD() &&
+ process_sp->GetCodeAddressMask() == LLDB_INVALID_ADDRESS_MASK)
+ process_sp->SetCodeAddressMask(
+ ReadOpenBSDProcessAddressMask(process_sp, "code_mask"));
+
// b55 is the highest bit outside TBI (if it's enabled), use
// it to determine if the high bits are set to 0 or 1.
const addr_t pac_sign_extension = 0x0080000000000000ULL;
@@ -862,6 +886,11 @@ lldb::addr_t ABISysV_arm64::FixDataAddress(lldb::addr_t pc) {
process_sp->SetDataAddressMask(
ReadLinuxProcessAddressMask(process_sp, "data_mask"));
+ if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSOpenBSD() &&
+ process_sp->GetDataAddressMask() == LLDB_INVALID_ADDRESS_MASK)
+ process_sp->SetDataAddressMask(
+ ReadOpenBSDProcessAddressMask(process_sp, "data_mask"));
+
// b55 is the highest bit outside TBI (if it's enabled), use
// it to determine if the high bits are set to 0 or 1.
const addr_t pac_sign_extension = 0x0080000000000000ULL;
diff --git a/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp b/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp
index e5c30adc056158..7f4c38c0a918a5 100644
--- a/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp
+++ b/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp
@@ -79,7 +79,7 @@ llvm::StringRef PlatformOpenBSD::GetPluginDescriptionStatic(bool is_host) {
}
void PlatformOpenBSD::Initialize() {
- Platform::Initialize();
+ PlatformPOSIX::Initialize();
if (g_initialize_count++ == 0) {
#if defined(__OpenBSD__)
@@ -145,9 +145,13 @@ void PlatformOpenBSD::GetStatus(Stream &strm) {
#endif
}
-// OpenBSD processes cannot yet be launched by spawning and attaching.
bool PlatformOpenBSD::CanDebugProcess() {
- return false;
+ if (IsHost()) {
+ return true;
+ } else {
+ // If we're connected, we can debug.
+ return IsConnected();
+ }
}
void PlatformOpenBSD::CalculateTrapHandlerSymbolNames() {
diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt
index 84cc65e45157e4..8a8f813b4870d3 100644
--- a/lldb/source/Plugins/Process/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/CMakeLists.txt
@@ -16,6 +16,7 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "AIX")
add_subdirectory(AIX)
add_subdirectory(POSIX)
elseif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
+ add_subdirectory(OpenBSD)
add_subdirectory(POSIX)
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
add_subdirectory(Windows/Common)
diff --git a/lldb/source/Plugins/Process/OpenBSD/CMakeLists.txt b/lldb/source/Plugins/Process/OpenBSD/CMakeLists.txt
new file mode 100644
index 00000000000000..bf9fe08a3d93c7
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_lldb_library(lldbPluginProcessOpenBSD
+ NativeProcessOpenBSD.cpp
+ NativeRegisterContextOpenBSD.cpp
+ NativeRegisterContextOpenBSD_arch.cpp
+ NativeRegisterContextOpenBSD_arm64.cpp
+ NativeRegisterContextOpenBSD_x86.cpp
+ NativeThreadOpenBSD.cpp
+
+ LINK_COMPONENTS
+ Support
+ LINK_LIBS
+ lldbHost
+ lldbSymbol
+ lldbTarget
+ lldbUtility
+ lldbPluginProcessPOSIX
+ lldbPluginProcessUtility
+ )
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeProcessOpenBSD.cpp b/lldb/source/Plugins/Process/OpenBSD/NativeProcessOpenBSD.cpp
new file mode 100644
index 00000000000000..e594397de663ec
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeProcessOpenBSD.cpp
@@ -0,0 +1,1001 @@
+//===-- NativeProcessOpenBSD.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 "NativeProcessOpenBSD.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#define _DYN_LOADER
+#include <elf.h>
+#include <util.h>
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/State.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Errno.h"
+
+#include <cstring>
+
+// System includes - They have to be included after framework includes because
+// they define some macros which collide with variable names in other modules
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+// clang-format on
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_openbsd;
+using namespace llvm;
+
+// Simple helper function to ensure flags are enabled on the given file
+// descriptor.
+static Status EnsureFDFlags(int fd, int flags) {
+ int status = fcntl(fd, F_GETFL);
+ if (status == -1)
+ return Status::FromErrno();
+
+ if (fcntl(fd, F_SETFL, status | flags) == -1)
+ return Status::FromErrno();
+
+ return Status();
+}
+
+// Register contexts are only implemented for these architectures.
+static llvm::Error CheckArchitectureSupported() {
+#if defined(__aarch64__) || defined(__i386__) || defined(__x86_64__)
+ return llvm::Error::success();
+#else
+ return llvm::createStringError(
+ "debugging is not supported on this architecture");
+#endif
+}
+
+// Whether kern.global_ptrace allows attaching to processes that are not
+// children of the debugger.
+static bool IsGlobalPtraceEnabled() {
+#ifdef KERN_GLOBAL_PTRACE
+ int mib[2] = {CTL_KERN, KERN_GLOBAL_PTRACE};
+ int value = 0;
+ size_t len = sizeof(value);
+ return ::sysctl(mib, 2, &value, &len, nullptr, 0) == 0 && value != 0;
+#else
+ return false;
+#endif
+}
+
+// Public Static Methods
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessOpenBSD::Manager::Launch(ProcessLaunchInfo &launch_info,
+ NativeDelegate &native_delegate) {
+ Log *log = GetLog(POSIXLog::Process);
+
+ if (llvm::Error error = CheckArchitectureSupported())
+ return std::move(error);
+
+ 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);
+ UNUSED_IF_ASSERT_DISABLED(wpid);
+ if (!WIFSTOPPED(wstatus)) {
+ LLDB_LOG(log, "Could not sync with inferior process: wstatus={0}",
+ WaitStatus::Decode(wstatus));
+ return llvm::createStringError("could not sync with inferior process");
+ }
+ LLDB_LOG(log, "inferior started, now in stopped state");
+
+ ProcessInstanceInfo Info;
+ if (!Host::GetProcessInfo(pid, Info)) {
+ return llvm::createStringError("cannot get process architecture");
+ }
+
+ // Set the architecture to the exe architecture.
+ LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid,
+ Info.GetArchitecture().GetArchitectureName());
+
+ std::unique_ptr<NativeProcessOpenBSD> process_up(new NativeProcessOpenBSD(
+ pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate,
+ Info.GetArchitecture(), m_mainloop));
+
+ status = process_up->RefreshThreads();
+ if (status.Fail())
+ return status.ToError();
+
+ process_up->m_trapped_tid = process_up->GetTrappedThreadID();
+ for (const auto &thread : process_up->m_threads)
+ static_cast<NativeThreadOpenBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+ process_up->SetState(StateType::eStateStopped, false);
+
+ return std::move(process_up);
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessOpenBSD::Manager::Attach(
+ lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) {
+
+ Log *log = GetLog(POSIXLog::Process);
+ LLDB_LOG(log, "pid = {0:x}", pid);
+
+ if (llvm::Error error = CheckArchitectureSupported())
+ return std::move(error);
+
+ // Retrieve the architecture for the running process.
+ ProcessInstanceInfo Info;
+ if (!Host::GetProcessInfo(pid, Info)) {
+ return llvm::createStringError("cannot get process architecture");
+ }
+
+ std::unique_ptr<NativeProcessOpenBSD> process_up(new NativeProcessOpenBSD(
+ pid, -1, native_delegate, Info.GetArchitecture(), m_mainloop));
+
+ Status status = process_up->Attach();
+ if (!status.Success())
+ return status.ToError();
+
+ return std::move(process_up);
+}
+
+NativeProcessOpenBSD::Extension
+NativeProcessOpenBSD::Manager::GetSupportedExtensions() const {
+ Extension supported =
+ Extension::pass_signals | Extension::auxv | Extension::libraries_svr4;
+#ifdef PTRACE_FORK
+ // OpenBSD reports fork() but not vfork().
+ supported |= Extension::multiprocess | Extension::fork;
+#endif
+ return supported;
+}
+
+// Public Instance Methods
+
+NativeProcessOpenBSD::NativeProcessOpenBSD(::pid_t pid, int terminal_fd,
+ NativeDelegate &delegate,
+ const ArchSpec &arch,
+ MainLoop &mainloop)
+ : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch),
+ m_main_loop(mainloop) {
+ 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 NativeProcessOpenBSD::MonitorCallback(lldb::pid_t pid, int signal) {
+ // OpenBSD does not report thread creation or exit, so resynchronize the
+ // thread list every time the process stops.
+ Status error = RefreshThreads();
+ if (error.Fail()) {
+ Log *log = GetLog(POSIXLog::Thread);
+ LLDB_LOG(log, "failed to refresh threads for pid {0}: {1}", pid, error);
+ }
+
+ switch (signal) {
+ case SIGTRAP:
+ return MonitorSIGTRAP(pid);
+ default:
+ return MonitorSignal(pid, signal);
+ }
+}
+
+void NativeProcessOpenBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) {
+ Log *log = GetLog(POSIXLog::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 NativeProcessOpenBSD::MonitorSIGTRAP(lldb::pid_t pid) {
+ // OpenBSD reports a plain SIGTRAP for both a completed PT_STEP and a
+ // software breakpoint, so use what the last resume requested to tell them
+ // apart: it is a step only if the thread that trapped is the one that was
+ // being stepped.
+ m_trapped_tid = GetTrappedThreadID();
+
+ if (::pid_t child_pid = GetForkedChildPID()) {
+ m_step_tid = LLDB_INVALID_THREAD_ID;
+ auto *parent_thread =
+ static_cast<NativeThreadOpenBSD *>(GetThreadByID(m_trapped_tid));
+ if (parent_thread)
+ return MonitorFork(child_pid, *parent_thread);
+ LLDB_LOG(GetLog(POSIXLog::Process),
+ "fork reported by pid {0} without a trapped thread", GetID());
+ }
+
+ const bool stepped =
+ m_step_tid != LLDB_INVALID_THREAD_ID && m_step_tid == m_trapped_tid;
+ m_step_tid = LLDB_INVALID_THREAD_ID;
+
+ for (const auto &thread : m_threads) {
+ auto &t = static_cast<NativeThreadOpenBSD &>(*thread);
+ if (t.GetID() != m_trapped_tid) {
+ t.SetStoppedWithNoReason();
+ } else if (stepped) {
+ t.SetStoppedByTrace();
+ } else {
+ t.SetStoppedByBreakpoint();
+ FixupBreakpointPCAsNeeded(t);
+ }
+ }
+ if (m_trapped_tid != LLDB_INVALID_THREAD_ID)
+ SetCurrentThreadID(m_trapped_tid);
+ SetState(StateType::eStateStopped, true);
+}
+
+void NativeProcessOpenBSD::MonitorSignal(lldb::pid_t pid, int signal) {
+ m_trapped_tid = GetTrappedThreadID();
+ m_step_tid = LLDB_INVALID_THREAD_ID;
+
+ for (const auto &thread : m_threads) {
+ auto &t = static_cast<NativeThreadOpenBSD &>(*thread);
+ if (t.GetID() == m_trapped_tid)
+ t.SetStoppedBySignal(signal);
+ else
+ t.SetStoppedWithNoReason();
+ }
+ if (m_trapped_tid != LLDB_INVALID_THREAD_ID)
+ SetCurrentThreadID(m_trapped_tid);
+ SetState(StateType::eStateStopped, true);
+}
+
+void NativeProcessOpenBSD::MonitorFork(::pid_t child_pid,
+ NativeThreadOpenBSD &parent_thread) {
+ Log *log = GetLog(POSIXLog::Process);
+ LLDB_LOG(log, "fork, child_pid={0}", child_pid);
+
+ auto resume_parent = [&]() {
+ Status error =
+ PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), 0);
+ if (error.Fail()) {
+ LLDB_LOG(log, "unable to resume parent process {0}: {1}", GetID(), error);
+ SetState(StateType::eStateInvalid);
+ }
+ };
+
+ // The child is traced by us from the start, and stops with a SIGTRAP as
+ // soon as it runs.
+ int status;
+ ::pid_t wait_pid =
+ llvm::sys::RetryAfterSignal(-1, ::waitpid, child_pid, &status, 0);
+ if (wait_pid != child_pid || !WIFSTOPPED(status)) {
+ LLDB_LOG(log, "fork child {0} did not stop, not tracking it", child_pid);
+ return resume_parent();
+ }
+
+ std::unique_ptr<NativeProcessOpenBSD> child_process{new NativeProcessOpenBSD(
+ child_pid, m_terminal_fd, m_delegate, m_arch, m_main_loop)};
+ // The child's ptrace state names this process until the child forks.
+ child_process->m_last_fork_pid = GetID();
+ // The child has a copy of our memory, breakpoints included.
+ child_process->m_software_breakpoints = m_software_breakpoints;
+
+ Status error = child_process->RefreshThreads();
+ if (error.Fail() || child_process->m_threads.empty() ||
+ !bool(m_enabled_extensions & Extension::fork)) {
+ LLDB_LOG(log, "not following fork child {0}: {1}", child_pid, error);
+ for (const auto &[addr, bp] : m_software_breakpoints) {
+ size_t bytes_written;
+ child_process->DoWriteMemory(addr, bp.saved_opcodes.data(),
+ bp.saved_opcodes.size(), bytes_written);
+ }
+ child_process->Detach();
+ return resume_parent();
+ }
+
+ child_process->SetEnabledExtensions(m_enabled_extensions);
+ child_process->m_trapped_tid = child_process->GetTrappedThreadID();
+ const lldb::tid_t child_tid = child_process->m_trapped_tid;
+ for (const auto &thread : child_process->m_threads)
+ static_cast<NativeThreadOpenBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+ child_process->SetState(StateType::eStateStopped, false);
+
+ m_delegate.NewSubprocess(this, std::move(child_process));
+
+ for (const auto &thread : m_threads) {
+ auto &t = static_cast<NativeThreadOpenBSD &>(*thread);
+ if (&t == &parent_thread)
+ t.SetStoppedByFork(child_pid, child_tid);
+ else
+ t.SetStoppedWithNoReason();
+ }
+ SetCurrentThreadID(parent_thread.GetID());
+ SetState(StateType::eStateStopped, true);
+}
+
+Status NativeProcessOpenBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
+ int data, int *result) {
+ Log *log = GetLog(POSIXLog::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 = Status::FromErrno();
+
+ 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 NativeProcessOpenBSD::Resume(const ResumeActionList &resume_actions) {
+ Log *log = GetLog(POSIXLog::Process);
+ LLDB_LOG(log, "pid {0}", GetID());
+
+ // OpenBSD's ptrace(2) can resume either the whole process or, when given a
+ // thread ID, only the thread that reported the last stop (the others stay
+ // suspended). It cannot suspend individual threads, so map lldb's
+ // per-thread actions onto those two modes.
+ NativeThreadOpenBSD *step_thread = nullptr;
+ std::vector<NativeThreadOpenBSD *> run_threads;
+ bool any_suspended = false;
+ int signal = 0;
+
+ for (const auto &abs_thread : m_threads) {
+ auto &thread = static_cast<NativeThreadOpenBSD &>(*abs_thread);
+ const ResumeAction *action =
+ resume_actions.GetActionForThread(thread.GetID(), true);
+ if (action == nullptr) {
+ any_suspended = true;
+ continue;
+ }
+
+ switch (action->state) {
+ case eStateRunning:
+ run_threads.push_back(&thread);
+ break;
+ case eStateStepping:
+ if (step_thread)
+ return Status::FromErrorString(
+ "OpenBSD can only single-step one thread at a time");
+ step_thread = &thread;
+ break;
+ case eStateSuspended:
+ case eStateStopped:
+ any_suspended = true;
+ break;
+ default:
+ return Status::FromErrorStringWithFormat(
+ "NativeProcessOpenBSD::%s (): unexpected state %s specified "
+ "for pid %" PRIu64 ", tid %" PRIu64,
+ __FUNCTION__, StateAsCString(action->state), GetID(), thread.GetID());
+ }
+
+ if (action->signal != LLDB_INVALID_SIGNAL_NUMBER)
+ signal = action->signal;
+ }
+
+ lldb::pid_t target = GetID();
+ int req;
+ if (step_thread) {
+#ifdef PT_STEP
+ if (step_thread->GetID() != m_trapped_tid)
+ return Status::FromErrorStringWithFormat(
+ "OpenBSD can only single-step the thread that reported the last "
+ "stop (tid %" PRIu64 ")",
+ m_trapped_tid);
+ req = PT_STEP;
+ // With the thread ID only the stepping thread runs, which is what lldb
+ // wants when the other threads are suspended (e.g. stepping over a
+ // breakpoint). With the process ID the other threads run as well.
+ if (run_threads.empty())
+ target = step_thread->GetID();
+#else
+ return Status::FromErrorString(
+ "NativeProcessOpenBSD stepping not supported on this platform");
+#endif
+ } else if (run_threads.empty()) {
+ LLDB_LOG(log, "no threads to resume for pid {0}", GetID());
+ return Status();
+ } else {
+ req = PT_CONTINUE;
+ if (any_suspended) {
+ if (run_threads.size() == 1 &&
+ run_threads.front()->GetID() == m_trapped_tid)
+ target = m_trapped_tid;
+ else
+ LLDB_LOG(log,
+ "cannot suspend individual threads, resuming all threads "
+ "of pid {0}",
+ GetID());
+ }
+ }
+
+ Status error = PtraceWrapper(req, target, (void *)1, signal);
+ if (error.Fail())
+ return error;
+
+ const bool whole_process = target == GetID();
+ for (const auto &abs_thread : m_threads) {
+ auto &thread = static_cast<NativeThreadOpenBSD &>(*abs_thread);
+ if (&thread == step_thread)
+ thread.SetStepping();
+ else if (whole_process || thread.GetID() == target)
+ thread.SetRunning();
+ }
+ m_step_tid = step_thread ? step_thread->GetID() : LLDB_INVALID_THREAD_ID;
+ SetState(step_thread ? eStateStepping : eStateRunning, true);
+
+ return Status();
+}
+
+Status NativeProcessOpenBSD::Halt() {
+ // Do not try to stop a process that's already stopped, this may cause
+ // the SIGSTOP to get queued and stop the process again once resumed.
+ if (StateIsStoppedState(m_state, false))
+ return Status();
+ if (kill(GetID(), SIGSTOP) != 0)
+ return Status::FromErrno();
+ return Status();
+}
+
+Status NativeProcessOpenBSD::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 NativeProcessOpenBSD::Signal(int signo) {
+ if (kill(GetID(), signo) != 0)
+ return Status::FromErrno();
+ return Status();
+}
+
+Status NativeProcessOpenBSD::Kill() {
+ Log *log = GetLog(POSIXLog::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;
+ }
+
+ // A process stopped by ptrace(2) does not act on SIGKILL until it is
+ // resumed, so use PT_KILL, which resumes it with SIGKILL. PT_KILL fails on
+ // a running process, so signal that one directly.
+ if (StateIsStoppedState(m_state, false))
+ return PtraceWrapper(PT_KILL, GetID());
+
+ if (kill(GetID(), SIGKILL) != 0)
+ return Status::FromErrno();
+
+ return error;
+}
+
+Status NativeProcessOpenBSD::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+ if (m_supports_mem_region == eLazyBoolNo)
+ return Status::FromErrorString("unsupported");
+
+ Status error = PopulateMemoryRegionCache();
+ if (error.Fail())
+ return error;
+
+ // The cache is sorted by address.
+ for (const auto &entry : m_mem_region_cache) {
+ const MemoryRegionInfo &proc_entry_info = entry.first;
+ // 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(eLazyBoolNo);
+ range_info.SetWritable(eLazyBoolNo);
+ range_info.SetExecutable(eLazyBoolNo);
+ range_info.SetMapped(eLazyBoolNo);
+ return Status();
+ }
+ if (proc_entry_info.GetRange().Contains(load_addr)) {
+ range_info = proc_entry_info;
+ return Status();
+ }
+ }
+
+ // The address is past the last mapping.
+ range_info.GetRange().SetRangeBase(load_addr);
+ range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS);
+ range_info.SetReadable(eLazyBoolNo);
+ range_info.SetWritable(eLazyBoolNo);
+ range_info.SetExecutable(eLazyBoolNo);
+ range_info.SetMapped(eLazyBoolNo);
+ return Status();
+}
+
+Status NativeProcessOpenBSD::PopulateMemoryRegionCache() {
+ Log *log = GetLog(POSIXLog::Process);
+
+ // The cache is cleared every time the process stops.
+ if (!m_mem_region_cache.empty())
+ return Status();
+
+ // KERN_PROC_VMMAP returns the entries starting at the address passed in the
+ // first entry's kve_start, and ENOMEM if they did not all fit; continue from
+ // the end of the last entry returned. Only root may read the map of
+ // another process.
+ int mib[3] = {CTL_KERN, KERN_PROC_VMMAP, static_cast<int>(GetID())};
+ std::vector<struct kinfo_vmentry> entries(256);
+ u_long next_start = 0;
+ while (true) {
+ entries[0].kve_start = next_start;
+ size_t len = entries.size() * sizeof(struct kinfo_vmentry);
+ int ret = ::sysctl(mib, 3, entries.data(), &len, nullptr, 0);
+ int saved_errno = errno;
+ if (ret != 0 && saved_errno != ENOMEM) {
+ m_mem_region_cache.clear();
+ m_supports_mem_region = eLazyBoolNo;
+ LLDB_LOG(log, "sysctl(KERN_PROC_VMMAP) failed for pid {0}: {1}", GetID(),
+ llvm::sys::StrError(saved_errno));
+ return Status(saved_errno, eErrorTypePOSIX);
+ }
+
+ size_t count = len / sizeof(struct kinfo_vmentry);
+ for (size_t i = 0; i < count; ++i) {
+ const struct kinfo_vmentry &kve = entries[i];
+#ifdef KVE_ET_HOLE
+ if (kve.kve_etype & KVE_ET_HOLE)
+ continue;
+#endif
+ MemoryRegionInfo info;
+ info.Clear();
+ info.GetRange().SetRangeBase(kve.kve_start);
+ info.GetRange().SetRangeEnd(kve.kve_end);
+ info.SetMapped(eLazyBoolYes);
+ info.SetReadable(kve.kve_protection & KVE_PROT_READ ? eLazyBoolYes
+ : eLazyBoolNo);
+ info.SetWritable(kve.kve_protection & KVE_PROT_WRITE ? eLazyBoolYes
+ : eLazyBoolNo);
+ info.SetExecutable(kve.kve_protection & KVE_PROT_EXEC ? eLazyBoolYes
+ : eLazyBoolNo);
+ m_mem_region_cache.emplace_back(info, FileSpec());
+ }
+
+ if (ret == 0 || count == 0)
+ break;
+ next_start = entries[count - 1].kve_end;
+ }
+
+ if (m_mem_region_cache.empty()) {
+ m_supports_mem_region = eLazyBoolNo;
+ return Status::FromErrorString("no memory map entries found");
+ }
+
+ LLDB_LOG(log, "read {0} memory region entries from process {1}",
+ m_mem_region_cache.size(), GetID());
+ m_supports_mem_region = eLazyBoolYes;
+ return Status();
+}
+
+llvm::Expected<lldb::addr_t>
+NativeProcessOpenBSD::AllocateMemory(size_t size, uint32_t permissions) {
+ return llvm::make_error<UnimplementedError>();
+}
+
+llvm::Error NativeProcessOpenBSD::DeallocateMemory(lldb::addr_t addr) {
+ return llvm::make_error<UnimplementedError>();
+}
+
+size_t NativeProcessOpenBSD::UpdateThreads() { return m_threads.size(); }
+
+Status NativeProcessOpenBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) {
+ if (hardware)
+ return Status::FromErrorString(
+ "NativeProcessOpenBSD does not support hardware breakpoints");
+ else
+ return SetSoftwareBreakpoint(addr, size);
+}
+
+// OpenBSD's memory map (KERN_PROC_VMMAP) carries no file names, so look
+// modules up in the dynamic loader's list of loaded objects instead.
+Status NativeProcessOpenBSD::GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) {
+ file_spec.Clear();
+
+ llvm::Expected<std::vector<SVR4LibraryInfo>> libraries =
+ GetLoadedSVR4Libraries();
+ if (!libraries)
+ return Status::FromError(libraries.takeError());
+
+ FileSpec module_file_spec(module_path);
+ FileSystem::Instance().Resolve(module_file_spec);
+
+ for (const SVR4LibraryInfo &library : *libraries) {
+ FileSpec library_file_spec(library.name);
+ if (library_file_spec.GetFilename() == module_file_spec.GetFilename()) {
+ file_spec = library_file_spec;
+ return Status();
+ }
+ }
+ return Status::FromErrorStringWithFormatv(
+ "Module file ({0}) not found in the loaded object list",
+ module_file_spec.GetFilename());
+}
+
+Status
+NativeProcessOpenBSD::GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) {
+ load_addr = LLDB_INVALID_ADDRESS;
+
+ llvm::Expected<std::vector<SVR4LibraryInfo>> libraries =
+ GetLoadedSVR4Libraries();
+ if (!libraries)
+ return Status::FromError(libraries.takeError());
+
+ FileSpec file(file_name);
+ for (const SVR4LibraryInfo &library : *libraries) {
+ if (FileSpec(library.name) == file) {
+ load_addr = library.base_addr;
+ return Status();
+ }
+ }
+ return Status::FromErrorStringWithFormatv(
+ "No load address found for file {0}", file_name);
+}
+
+void NativeProcessOpenBSD::SigchldHandler() {
+ Log *log = GetLog(POSIXLog::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);
+ return;
+ }
+
+ 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 NativeProcessOpenBSD::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;
+}
+
+NativeThreadOpenBSD &NativeProcessOpenBSD::AddThread(lldb::tid_t thread_id) {
+
+ Log *log = GetLog(POSIXLog::Thread);
+ LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id);
+
+ assert(!HasThreadNoLock(thread_id) &&
+ "attempted to add a thread by id that already exists");
+
+ // If this is the first thread, save it as the current thread
+ if (m_threads.empty())
+ SetCurrentThreadID(thread_id);
+
+ m_threads.push_back(std::make_unique<NativeThreadOpenBSD>(*this, thread_id));
+ return static_cast<NativeThreadOpenBSD &>(*m_threads.back());
+}
+
+Status NativeProcessOpenBSD::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()) {
+ if (status.GetError() == EPERM && ::geteuid() != 0 &&
+ !IsGlobalPtraceEnabled())
+ return Status::FromErrorString(
+ "operation not permitted: attaching to a process that is not a "
+ "child of the debugger requires root or kern.global_ptrace=1");
+ return status;
+ }
+
+ int wstatus;
+ // 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 = RefreshThreads();
+ if (status.Fail())
+ return status;
+
+ m_trapped_tid = GetTrappedThreadID();
+
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadOpenBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+
+ // Let our process instance know the thread has stopped.
+ SetCurrentThreadID(m_threads.front()->GetID());
+ SetState(StateType::eStateStopped, false);
+ return Status();
+}
+
+Status NativeProcessOpenBSD::ReadMemory(const ProcessAddress &process_addr,
+ void *buf, size_t size,
+ size_t &bytes_read) {
+ if (!process_addr.IsInDefaultAddressSpace())
+ return Status::FromErrorString("address spaces are not supported");
+ lldb::addr_t addr = process_addr.GetValue();
+ unsigned char *dst = static_cast<unsigned char *>(buf);
+ struct ptrace_io_desc io;
+
+ Log *log = GetLog(POSIXLog::Memory);
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_read = 0;
+ io.piod_op = PIOD_READ_D;
+
+ do {
+ io.piod_offs = (void *)(addr + bytes_read);
+ io.piod_addr = dst + bytes_read;
+ io.piod_len = size - bytes_read;
+
+ Status error = NativeProcessOpenBSD::PtraceWrapper(PT_IO, GetID(), &io);
+ if (error.Fail() || io.piod_len == 0)
+ return error;
+
+ bytes_read += io.piod_len;
+ } while (bytes_read < size);
+
+ return Status();
+}
+
+Status NativeProcessOpenBSD::DoWriteMemory(lldb::addr_t addr, const void *buf,
+ size_t size, size_t &bytes_written) {
+ const unsigned char *src = static_cast<const unsigned char *>(buf);
+ struct ptrace_io_desc io;
+
+ Log *log = GetLog(POSIXLog::Memory);
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_written = 0;
+ io.piod_op = PIOD_WRITE_D;
+
+ do {
+ io.piod_addr =
+ const_cast<void *>(static_cast<const void *>(src + bytes_written));
+ io.piod_offs = (void *)(addr + bytes_written);
+ io.piod_len = size - bytes_written;
+
+ Status error = NativeProcessOpenBSD::PtraceWrapper(PT_IO, GetID(), &io);
+ if (error.Fail() || io.piod_len == 0)
+ return error;
+
+ bytes_written += io.piod_len;
+ } while (bytes_written < size);
+
+ return Status();
+}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+NativeProcessOpenBSD::GetAuxvData() const {
+ size_t auxv_size = 100 * sizeof(AuxInfo);
+
+ std::unique_ptr<WritableMemoryBuffer> buf =
+ llvm::WritableMemoryBuffer::getNewMemBuffer(auxv_size);
+ if (!buf)
+ return std::make_error_code(std::errc::not_enough_memory);
+
+ struct ptrace_io_desc io;
+ io.piod_op = PIOD_READ_AUXV;
+ io.piod_offs = 0;
+ io.piod_addr = buf->getBufferStart();
+ io.piod_len = auxv_size;
+
+ Status error = NativeProcessOpenBSD::PtraceWrapper(PT_IO, GetID(), &io);
+ if (error.Fail())
+ return std::error_code(error.GetError(), std::generic_category());
+
+ if (io.piod_len < 1)
+ return std::error_code(ECANCELED, std::generic_category());
+
+ // Return only what the kernel actually filled in.
+ return llvm::MemoryBuffer::getMemBufferCopy(
+ llvm::StringRef(buf->getBufferStart(), io.piod_len));
+}
+
+Status NativeProcessOpenBSD::RefreshThreads() {
+ Log *log = GetLog(POSIXLog::Thread);
+
+ // Read the kernel's current list of threads.
+ std::vector<std::pair<lldb::tid_t, std::string>> kernel_threads;
+ struct ptrace_thread_state info = {};
+ Status error =
+ PtraceWrapper(PT_GET_THREAD_FIRST, GetID(), &info, sizeof(info));
+ if (error.Fail())
+ return error;
+ while (info.pts_tid > 0) {
+ kernel_threads.emplace_back(
+ info.pts_tid,
+ std::string(info.pts_name,
+ strnlen(info.pts_name, sizeof(info.pts_name))));
+ error = PtraceWrapper(PT_GET_THREAD_NEXT, GetID(), &info, sizeof(info));
+ if (error.Fail()) {
+ LLDB_LOG(log, "PT_GET_THREAD_NEXT failed for pid {0}: {1}", GetID(),
+ error);
+ break;
+ }
+ }
+
+ // Drop threads that have exited since the last stop.
+ llvm::erase_if(
+ m_threads, [&](const std::unique_ptr<NativeThreadProtocol> &t) {
+ bool alive = llvm::any_of(kernel_threads, [&](const auto &kt) {
+ return kt.first == t->GetID();
+ });
+ if (!alive)
+ LLDB_LOG(log, "pid {0} removing exited thread with tid {1}", GetID(),
+ t->GetID());
+ return !alive;
+ });
+
+ // Add threads created since the last stop, and refresh thread names.
+ for (const auto &[tid, name] : kernel_threads) {
+ auto *thread = static_cast<NativeThreadOpenBSD *>(GetThreadByID(tid));
+ if (!thread)
+ thread = &AddThread(tid);
+ thread->SetName(name);
+ }
+
+ if (!m_threads.empty() && !HasThreadNoLock(GetCurrentThreadID()))
+ SetCurrentThreadID(m_threads.front()->GetID());
+
+ return Status();
+}
+
+// Returns the child of the fork() that this stop reports, or 0 if it does not
+// report one.
+::pid_t NativeProcessOpenBSD::GetForkedChildPID() {
+#ifdef PTRACE_FORK
+ struct ptrace_state state = {};
+ if (PtraceWrapper(PT_GET_PROCESS_STATE, GetID(), &state, sizeof(state))
+ .Fail())
+ return 0;
+ // The kernel does not clear the report once it has been read, so a fork is
+ // only new if it names another process than the last one.
+ if (state.pe_report_event != PTRACE_FORK ||
+ state.pe_other_pid == m_last_fork_pid)
+ return 0;
+ m_last_fork_pid = state.pe_other_pid;
+ return state.pe_other_pid;
+#else
+ return 0;
+#endif
+}
+
+Status NativeProcessOpenBSD::SetForkReporting(bool enable) {
+#ifdef PTRACE_FORK
+ ptrace_event_t events = {};
+ Status error =
+ PtraceWrapper(PT_GET_EVENT_MASK, GetID(), &events, sizeof(events));
+ if (error.Fail())
+ return error;
+ if (enable)
+ events.pe_set_event |= PTRACE_FORK;
+ else
+ events.pe_set_event &= ~PTRACE_FORK;
+ return PtraceWrapper(PT_SET_EVENT_MASK, GetID(), &events, sizeof(events));
+#else
+ if (enable)
+ return Status::FromErrorString("fork reporting is not supported");
+ return Status();
+#endif
+}
+
+void NativeProcessOpenBSD::SetEnabledExtensions(Extension flags) {
+ NativeProcessELF::SetEnabledExtensions(flags);
+
+ // Only have the kernel trace fork children when the client follows them.
+ Status error = SetForkReporting(bool(flags & Extension::fork));
+ if (error.Fail())
+ LLDB_LOG(GetLog(POSIXLog::Process),
+ "could not change fork reporting for pid {0}: {1}", GetID(),
+ error);
+}
+
+lldb::tid_t NativeProcessOpenBSD::GetTrappedThreadID() {
+ Log *log = GetLog(POSIXLog::Thread);
+
+ struct ptrace_state state = {};
+ Status error =
+ PtraceWrapper(PT_GET_PROCESS_STATE, GetID(), &state, sizeof(state));
+ if (error.Success() && state.pe_tid > 0 &&
+ HasThreadNoLock(static_cast<lldb::tid_t>(state.pe_tid)))
+ return state.pe_tid;
+
+ LLDB_LOG(log, "could not determine the stopped thread of pid {0}: {1}",
+ GetID(), error);
+ return m_threads.empty() ? LLDB_INVALID_THREAD_ID
+ : m_threads.front()->GetID();
+}
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeProcessOpenBSD.h b/lldb/source/Plugins/Process/OpenBSD/NativeProcessOpenBSD.h
new file mode 100644
index 00000000000000..c69a3d0ec27439
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeProcessOpenBSD.h
@@ -0,0 +1,131 @@
+//===-- NativeProcessOpenBSD.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_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEPROCESSOPENBSD_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEPROCESSOPENBSD_H
+
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include "NativeThreadOpenBSD.h"
+#include "Plugins/Process/POSIX/NativeProcessELF.h"
+
+namespace lldb_private {
+namespace process_openbsd {
+/// @class NativeProcessOpenBSD
+/// 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 NativeProcessOpenBSD : public NativeProcessELF {
+public:
+ class Manager : public NativeProcessProtocol::Manager {
+ public:
+ using NativeProcessProtocol::Manager::Manager;
+
+ llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ Launch(ProcessLaunchInfo &launch_info,
+ NativeDelegate &native_delegate) override;
+
+ llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override;
+
+ Extension GetSupportedExtensions() const override;
+ };
+
+ // NativeProcessProtocol Interface
+ Status Resume(const ResumeActionList &resume_actions) override;
+
+ Status Halt() override;
+
+ Status Detach() override;
+
+ Status Signal(int signo) override;
+
+ Status Kill() override;
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) override;
+
+ Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
+ size_t &bytes_read) override;
+
+ Status DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+ size_t &bytes_written) override;
+
+ llvm::Expected<lldb::addr_t> AllocateMemory(size_t size,
+ uint32_t permissions) override;
+
+ llvm::Error DeallocateMemory(lldb::addr_t addr) 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;
+
+ void SetEnabledExtensions(Extension flags) 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;
+ MainLoop &m_main_loop;
+ LazyBool m_supports_mem_region = eLazyBoolCalculate;
+ // Thread that was single-stepped by the last Resume(), if any.
+ lldb::tid_t m_step_tid = LLDB_INVALID_THREAD_ID;
+ // Thread that reported the last stop.
+ lldb::tid_t m_trapped_tid = LLDB_INVALID_THREAD_ID;
+ // The other process of the last fork seen. The kernel keeps reporting the
+ // last fork in the ptrace state until the next one.
+ ::pid_t m_last_fork_pid = 0;
+
+ // Private Instance Methods
+ NativeProcessOpenBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate,
+ const ArchSpec &arch, MainLoop &mainloop);
+
+ bool HasThreadNoLock(lldb::tid_t thread_id);
+
+ NativeThreadOpenBSD &AddThread(lldb::tid_t thread_id);
+
+ void MonitorCallback(lldb::pid_t pid, int signal);
+ void MonitorExited(lldb::pid_t pid, WaitStatus status);
+ void MonitorSIGTRAP(lldb::pid_t pid);
+ void MonitorSignal(lldb::pid_t pid, int signal);
+ void MonitorFork(::pid_t child_pid, NativeThreadOpenBSD &parent_thread);
+
+ Status PopulateMemoryRegionCache();
+ void SigchldHandler();
+
+ Status Attach();
+ Status RefreshThreads();
+ lldb::tid_t GetTrappedThreadID();
+ ::pid_t GetForkedChildPID();
+ Status SetForkReporting(bool enable);
+};
+
+} // namespace process_openbsd
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEPROCESSOPENBSD_H
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.cpp b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.cpp
new file mode 100644
index 00000000000000..ff8820edcf5def
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.cpp
@@ -0,0 +1,98 @@
+//===-- NativeRegisterContextOpenBSD.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 "NativeRegisterContextOpenBSD.h"
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_openbsd;
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+// clang-format on
+
+NativeRegisterContextOpenBSD::NativeRegisterContextOpenBSD(
+ NativeThreadProtocol &native_thread,
+ RegisterInfoInterface *reg_info_interface_p)
+ : NativeRegisterContextRegisterInfo(native_thread, reg_info_interface_p) {}
+
+Status NativeRegisterContextOpenBSD::ReadGPR() {
+ void *buf = GetGPRBuffer();
+ if (!buf)
+ return Status::FromErrorString("GPR buffer is NULL");
+
+ return DoReadGPR(buf);
+}
+
+Status NativeRegisterContextOpenBSD::WriteGPR() {
+ void *buf = GetGPRBuffer();
+ if (!buf)
+ return Status::FromErrorString("GPR buffer is NULL");
+
+ return DoWriteGPR(buf);
+}
+
+Status NativeRegisterContextOpenBSD::ReadFPR() {
+ void *buf = GetFPRBuffer();
+ if (!buf)
+ return Status::FromErrorString("FPR buffer is NULL");
+
+ return DoReadFPR(buf);
+}
+
+Status NativeRegisterContextOpenBSD::WriteFPR() {
+ void *buf = GetFPRBuffer();
+ if (!buf)
+ return Status::FromErrorString("FPR buffer is NULL");
+
+ return DoWriteFPR(buf);
+}
+
+Status NativeRegisterContextOpenBSD::DoReadGPR(void *buf) {
+#ifdef PT_GETREGS
+ return NativeProcessOpenBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), buf);
+#else
+ return Status::FromErrorString("PT_GETREGS not supported on this platform");
+#endif
+}
+
+Status NativeRegisterContextOpenBSD::DoWriteGPR(void *buf) {
+#ifdef PT_SETREGS
+ return NativeProcessOpenBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), buf);
+#else
+ return Status::FromErrorString("PT_SETREGS not supported on this platform");
+#endif
+}
+
+Status NativeRegisterContextOpenBSD::DoReadFPR(void *buf) {
+#ifdef PT_GETFPREGS
+ return NativeProcessOpenBSD::PtraceWrapper(PT_GETFPREGS, m_thread.GetID(),
+ buf);
+#else
+ return Status::FromErrorString("PT_GETFPREGS not supported on this platform");
+#endif
+}
+
+Status NativeRegisterContextOpenBSD::DoWriteFPR(void *buf) {
+#ifdef PT_SETFPREGS
+ return NativeProcessOpenBSD::PtraceWrapper(PT_SETFPREGS, m_thread.GetID(),
+ buf);
+#else
+ return Status::FromErrorString("PT_SETFPREGS not supported on this platform");
+#endif
+}
+
+NativeProcessOpenBSD &NativeRegisterContextOpenBSD::GetProcess() {
+ return static_cast<NativeProcessOpenBSD &>(m_thread.GetProcess());
+}
+
+::pid_t NativeRegisterContextOpenBSD::GetProcessPid() {
+ return GetProcess().GetID();
+}
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.h b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.h
new file mode 100644
index 00000000000000..09c33d0cc85c6a
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.h
@@ -0,0 +1,62 @@
+//===-- NativeRegisterContextOpenBSD.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_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_H
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "Plugins/Process/OpenBSD/NativeProcessOpenBSD.h"
+#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
+
+namespace lldb_private {
+namespace process_openbsd {
+
+class NativeRegisterContextOpenBSD : public NativeRegisterContextRegisterInfo {
+public:
+ NativeRegisterContextOpenBSD(NativeThreadProtocol &native_thread,
+ RegisterInfoInterface *reg_info_interface_p);
+
+ // This function is implemented in the NativeRegisterContextOpenBSD_*
+ // subclasses to create a new instance of the host specific
+ // NativeRegisterContextOpenBSD. The implementations can't collide as only one
+ // NativeRegisterContextOpenBSD_* variant should be compiled into the final
+ // executable.
+ static std::unique_ptr<NativeRegisterContextOpenBSD>
+ CreateHostNativeRegisterContextOpenBSD(const ArchSpec &target_arch,
+ NativeThreadProtocol &native_thread);
+
+protected:
+ virtual Status ReadGPR();
+ virtual Status WriteGPR();
+
+ virtual Status ReadFPR();
+ virtual Status WriteFPR();
+
+ virtual void *GetGPRBuffer() { return nullptr; }
+ virtual size_t GetGPRSize() {
+ return GetRegisterInfoInterface().GetGPRSize();
+ }
+
+ virtual void *GetFPRBuffer() { return nullptr; }
+ virtual size_t GetFPRSize() { return 0; }
+
+ virtual Status DoReadGPR(void *buf);
+ virtual Status DoWriteGPR(void *buf);
+
+ virtual Status DoReadFPR(void *buf);
+ virtual Status DoWriteFPR(void *buf);
+
+ virtual NativeProcessOpenBSD &GetProcess();
+ virtual ::pid_t GetProcessPid();
+};
+
+} // namespace process_openbsd
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_H
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arch.cpp b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arch.cpp
new file mode 100644
index 00000000000000..7d589d34cc2bd9
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arch.cpp
@@ -0,0 +1,29 @@
+//===-- NativeRegisterContextOpenBSD_arch.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
+//
+//===----------------------------------------------------------------------===//
+
+// NativeRegisterContextOpenBSD_* contains the implementations for each
+// supported architecture, and includes the static initalizer
+// CreateHostNativeRegisterContextOpenBSD() implementation which returns a arch
+// specific register context. This implementation contains a stub
+// which just reports an error and exits on architectures which do
+// not have a backend.
+
+#if !defined(__aarch64__) && !defined(__i386__) && !defined(__x86_64__)
+
+#include "Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_openbsd;
+
+std::unique_ptr<NativeRegisterContextOpenBSD>
+NativeRegisterContextOpenBSD::CreateHostNativeRegisterContextOpenBSD(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+ return std::unique_ptr<NativeRegisterContextOpenBSD>{};
+}
+
+#endif
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arm64.cpp b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arm64.cpp
new file mode 100644
index 00000000000000..e00ef80b3aca29
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arm64.cpp
@@ -0,0 +1,315 @@
+//===-- NativeRegisterContextOpenBSD_arm64.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(__aarch64__)
+
+#include "NativeRegisterContextOpenBSD_arm64.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include "Plugins/Process/OpenBSD/NativeProcessOpenBSD.h"
+#include "Register/Common/RegisterInfo_arm64.h"
+#include "Register/Common/RegisterTypeDetector_arm64.h"
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+// clang-format on
+
+#include <cinttypes>
+#include <cstddef>
+#include <cstring>
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_openbsd;
+
+// The registers are accessed by their RegisterInfo byte_offset, which is only
+// correct if OpenBSD's structures match LLDB's at the start.
+static_assert(offsetof(struct reg, r_reg) ==
+ offsetof(RegisterInfo_arm64::GPR, x));
+static_assert(offsetof(struct reg, r_reg[29]) ==
+ offsetof(RegisterInfo_arm64::GPR, fp));
+static_assert(offsetof(struct reg, r_lr) ==
+ offsetof(RegisterInfo_arm64::GPR, lr));
+static_assert(offsetof(struct reg, r_sp) ==
+ offsetof(RegisterInfo_arm64::GPR, sp));
+static_assert(offsetof(struct reg, r_pc) ==
+ offsetof(RegisterInfo_arm64::GPR, pc));
+// cpsr is the low 32 bits of r_spsr.
+static_assert(offsetof(struct reg, r_spsr) ==
+ offsetof(RegisterInfo_arm64::GPR, cpsr));
+static_assert(sizeof(struct reg) >= sizeof(RegisterInfo_arm64::GPR));
+static_assert(offsetof(struct fpreg, fp_reg) ==
+ offsetof(RegisterInfo_arm64::FPU, v));
+static_assert(offsetof(struct fpreg, fp_sr) ==
+ offsetof(RegisterInfo_arm64::FPU, fpsr));
+static_assert(offsetof(struct fpreg, fp_cr) ==
+ offsetof(RegisterInfo_arm64::FPU, fpcr));
+
+// A register context is created for each thread, but every thread has the
+// same register fields. The mutex keeps the instances from racing each other
+// and lets later ones reuse the fields the first one detected.
+static std::mutex g_register_type_detector_mutex;
+static Arm64RegisterTypeDetector g_register_type_detector;
+
+std::unique_ptr<NativeRegisterContextOpenBSD>
+NativeRegisterContextOpenBSD::CreateHostNativeRegisterContextOpenBSD(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+ std::lock_guard<std::mutex> lock(g_register_type_detector_mutex);
+ if (!g_register_type_detector.HasDetected()) {
+ auto &process =
+ static_cast<NativeProcessOpenBSD &>(native_thread.GetProcess());
+ // OpenBSD uses the same AT_HWCAP number as FreeBSD, and the same hwcap
+ // bits as Linux.
+ g_register_type_detector.DetectTypes(
+ process.GetAuxValue(AuxVector::AUXV_FREEBSD_AT_HWCAP).value_or(0),
+ process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2).value_or(0),
+ /*hwcap3=*/0);
+ }
+
+ return std::make_unique<NativeRegisterContextOpenBSD_arm64>(target_arch,
+ native_thread);
+}
+
+static RegisterInfoInterface *
+CreateRegisterInfoInterface(const ArchSpec &target_arch) {
+ Flags opt_regsets =
+ RegisterInfo_arm64::eRegsetMaskPAuth | RegisterInfo_arm64::eRegsetMaskTLS;
+ return new RegisterInfo_arm64(target_arch, opt_regsets);
+}
+
+NativeRegisterContextOpenBSD_arm64::NativeRegisterContextOpenBSD_arm64(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+ : NativeRegisterContextOpenBSD(native_thread,
+ CreateRegisterInfoInterface(target_arch)),
+ m_gpr(), m_fpr(), m_pacmask() {
+ g_register_type_detector.UpdateRegisterInfo(
+ GetRegisterInfoInterface().GetRegisterInfo(),
+ GetRegisterInfoInterface().GetRegisterCount());
+}
+
+RegisterInfo_arm64 &
+NativeRegisterContextOpenBSD_arm64::GetRegisterInfo() const {
+ return static_cast<RegisterInfo_arm64 &>(*m_register_info_interface_up);
+}
+
+uint32_t NativeRegisterContextOpenBSD_arm64::GetRegisterSetCount() const {
+ return GetRegisterInfo().GetRegisterSetCount();
+}
+
+const RegisterSet *
+NativeRegisterContextOpenBSD_arm64::GetRegisterSet(uint32_t set_index) const {
+ return GetRegisterInfo().GetRegisterSet(set_index);
+}
+
+uint32_t NativeRegisterContextOpenBSD_arm64::GetUserRegisterCount() const {
+ uint32_t count = 0;
+ for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
+ count += GetRegisterSet(set_index)->num_registers;
+ return count;
+}
+
+std::optional<NativeRegisterContextOpenBSD_arm64::RegSetKind>
+NativeRegisterContextOpenBSD_arm64::GetSetForNativeRegNum(
+ uint32_t reg_num) const {
+ if (GetRegisterInfo().IsPAuthReg(reg_num))
+ return PACMaskRegSet;
+ if (GetRegisterInfo().IsTLSReg(reg_num))
+ return TLSRegSet;
+
+ switch (GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg_num)) {
+ case RegisterInfo_arm64::GPRegSet:
+ return GPRegSet;
+ case RegisterInfo_arm64::FPRegSet:
+ return FPRegSet;
+ }
+ return std::nullopt;
+}
+
+Status NativeRegisterContextOpenBSD_arm64::ReadRegisterSet(RegSetKind set) {
+ switch (set) {
+ case GPRegSet:
+ case TLSRegSet:
+ return ReadGPR();
+ case FPRegSet:
+ return ReadFPR();
+ case PACMaskRegSet:
+#ifdef PT_PACMASK
+ return NativeProcessOpenBSD::PtraceWrapper(PT_PACMASK, m_thread.GetID(),
+ &m_pacmask, sizeof(m_pacmask));
+#else
+ ::memset(&m_pacmask, 0, sizeof(m_pacmask));
+ return Status();
+#endif
+ }
+ llvm_unreachable("NativeRegisterContextOpenBSD_arm64::ReadRegisterSet");
+}
+
+Status NativeRegisterContextOpenBSD_arm64::WriteRegisterSet(RegSetKind set) {
+ switch (set) {
+ case GPRegSet:
+ case TLSRegSet:
+ return WriteGPR();
+ case FPRegSet:
+ return WriteFPR();
+ case PACMaskRegSet:
+ return Status::FromErrorString("the PAC mask registers are read-only");
+ }
+ llvm_unreachable("NativeRegisterContextOpenBSD_arm64::WriteRegisterSet");
+}
+
+uint8_t *
+NativeRegisterContextOpenBSD_arm64::GetRegisterData(RegSetKind set,
+ const RegisterInfo &info) {
+ uint8_t *base = nullptr;
+ size_t base_size = 0;
+ uint32_t offset = info.byte_offset;
+
+ switch (set) {
+ case GPRegSet:
+ base = reinterpret_cast<uint8_t *>(&m_gpr);
+ base_size = sizeof(m_gpr);
+ break;
+ case FPRegSet:
+ base = reinterpret_cast<uint8_t *>(&m_fpr);
+ base_size = sizeof(m_fpr);
+ offset -= sizeof(RegisterInfo_arm64::GPR);
+ break;
+ case PACMaskRegSet:
+ base = reinterpret_cast<uint8_t *>(&m_pacmask);
+ base_size = sizeof(m_pacmask);
+ offset -= GetRegisterInfo().GetPAuthOffset();
+ break;
+ case TLSRegSet:
+ // tpidr is r_tpidr in struct reg.
+ base = reinterpret_cast<uint8_t *>(&m_gpr.r_tpidr);
+ base_size = sizeof(m_gpr.r_tpidr);
+ offset -= GetRegisterInfo().GetTLSOffset();
+ break;
+ }
+
+ assert(offset + info.byte_size <= base_size);
+ UNUSED_IF_ASSERT_DISABLED(base_size);
+ return base + offset;
+}
+
+Status
+NativeRegisterContextOpenBSD_arm64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue ®_value) {
+ if (!reg_info)
+ return Status::FromErrorString("reg_info NULL");
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM)
+ return Status::FromErrorStringWithFormat(
+ "no lldb regnum for %s",
+ reg_info->name ? reg_info->name : "<unknown register>");
+
+ std::optional<RegSetKind> set = GetSetForNativeRegNum(reg);
+ if (!set)
+ return Status::FromErrorStringWithFormat(
+ "register \"%s\" is in unrecognized set", reg_info->name);
+
+ Status error = ReadRegisterSet(*set);
+ if (error.Fail())
+ return error;
+
+ reg_value.SetBytes(GetRegisterData(*set, *reg_info), reg_info->byte_size,
+ endian::InlHostByteOrder());
+ return error;
+}
+
+Status NativeRegisterContextOpenBSD_arm64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue ®_value) {
+ if (!reg_info)
+ return Status::FromErrorString("reg_info NULL");
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM)
+ return Status::FromErrorStringWithFormat(
+ "no lldb regnum for %s",
+ reg_info->name ? reg_info->name : "<unknown register>");
+
+ std::optional<RegSetKind> set = GetSetForNativeRegNum(reg);
+ if (!set)
+ return Status::FromErrorStringWithFormat(
+ "register \"%s\" is in unrecognized set", reg_info->name);
+
+ if (*set == PACMaskRegSet)
+ return Status::FromErrorStringWithFormat("register \"%s\" is read-only",
+ reg_info->name);
+
+ Status error = ReadRegisterSet(*set);
+ if (error.Fail())
+ return error;
+
+ if (reg_value.GetByteSize() < reg_info->byte_size)
+ return Status::FromErrorStringWithFormat(
+ "value for register \"%s\" is too short", reg_info->name);
+
+ ::memcpy(GetRegisterData(*set, *reg_info), reg_value.GetBytes(),
+ reg_info->byte_size);
+
+ return WriteRegisterSet(*set);
+}
+
+Status NativeRegisterContextOpenBSD_arm64::ReadAllRegisterValues(
+ lldb::WritableDataBufferSP &data_sp) {
+ Status error = ReadRegisterSet(GPRegSet);
+ if (error.Fail())
+ return error;
+
+ error = ReadRegisterSet(FPRegSet);
+ if (error.Fail())
+ return error;
+
+ data_sp.reset(new DataBufferHeap(sizeof(m_gpr) + sizeof(m_fpr), 0));
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &m_gpr, sizeof(m_gpr));
+ ::memcpy(dst + sizeof(m_gpr), &m_fpr, sizeof(m_fpr));
+
+ return error;
+}
+
+Status NativeRegisterContextOpenBSD_arm64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (!data_sp)
+ return Status::FromErrorStringWithFormat(
+ "NativeRegisterContextOpenBSD_arm64::%s invalid data_sp provided",
+ __FUNCTION__);
+
+ const size_t size = sizeof(m_gpr) + sizeof(m_fpr);
+ if (data_sp->GetByteSize() != size)
+ return Status::FromErrorStringWithFormat(
+ "NativeRegisterContextOpenBSD_arm64::%s data_sp contained mismatched "
+ "data size, expected %zu, actual %" PRIu64,
+ __FUNCTION__, size, data_sp->GetByteSize());
+
+ const uint8_t *src = data_sp->GetBytes();
+ if (src == nullptr)
+ return Status::FromErrorStringWithFormat(
+ "NativeRegisterContextOpenBSD_arm64::%s "
+ "DataBuffer::GetBytes() returned a null pointer",
+ __FUNCTION__);
+
+ ::memcpy(&m_gpr, src, sizeof(m_gpr));
+ ::memcpy(&m_fpr, src + sizeof(m_gpr), sizeof(m_fpr));
+
+ Status error = WriteRegisterSet(GPRegSet);
+ if (error.Fail())
+ return error;
+
+ return WriteRegisterSet(FPRegSet);
+}
+
+#endif // defined(__aarch64__)
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arm64.h b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arm64.h
new file mode 100644
index 00000000000000..e2648737898c87
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_arm64.h
@@ -0,0 +1,91 @@
+//===-- NativeRegisterContextOpenBSD_arm64.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(__aarch64__)
+
+#ifndef LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_ARM64_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_ARM64_H
+
+// clang-format off
+#include <sys/types.h>
+#include <machine/reg.h>
+// clang-format on
+
+#include <optional>
+
+#include "Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.h"
+#include "Register/Common/RegisterInfo_arm64.h"
+
+namespace lldb_private {
+namespace process_openbsd {
+
+class NativeProcessOpenBSD;
+
+class NativeRegisterContextOpenBSD_arm64 : public NativeRegisterContextOpenBSD {
+public:
+ NativeRegisterContextOpenBSD_arm64(const ArchSpec &target_arch,
+ NativeThreadProtocol &native_thread);
+
+ uint32_t GetUserRegisterCount() const override;
+ uint32_t GetRegisterSetCount() const override;
+
+ const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
+
+ Status ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue ®_value) override;
+
+ Status WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue ®_value) override;
+
+ Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override;
+
+ Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+protected:
+ void *GetGPRBuffer() override { return &m_gpr; }
+ size_t GetGPRSize() override { return sizeof(m_gpr); }
+ void *GetFPRBuffer() override { return &m_fpr; }
+ size_t GetFPRSize() override { return sizeof(m_fpr); }
+
+private:
+ // Private member types.
+ enum RegSetKind {
+ GPRegSet,
+ FPRegSet,
+ PACMaskRegSet,
+ TLSRegSet,
+ };
+
+ // Private member variables.
+ //
+ // OpenBSD's struct reg and struct fpreg begin with the same layout as
+ // RegisterInfo_arm64::GPR and RegisterInfo_arm64::FPU, so registers are
+ // accessed through their byte_offset. struct reg is longer than GPR (it
+ // also holds r_tpidr, which is the tpidr register of the TLS set), so the
+ // two sets are kept in separate buffers rather than one buffer laid out
+ // like the register infos.
+ struct reg m_gpr;
+ struct fpreg m_fpr;
+ register_t m_pacmask[2];
+
+ std::optional<RegSetKind> GetSetForNativeRegNum(uint32_t reg_num) const;
+
+ Status ReadRegisterSet(RegSetKind set);
+ Status WriteRegisterSet(RegSetKind set);
+
+ uint8_t *GetRegisterData(RegSetKind set, const RegisterInfo ®_info);
+
+ RegisterInfo_arm64 &GetRegisterInfo() const;
+};
+
+} // namespace process_openbsd
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_ARM64_H
+
+#endif // defined(__aarch64__)
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86.cpp b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86.cpp
new file mode 100644
index 00000000000000..7bc9a4d7c38b6a
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86.cpp
@@ -0,0 +1,505 @@
+//===-- NativeRegisterContextOpenBSD_x86.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 "NativeRegisterContextOpenBSD_x86.h"
+
+#include <cinttypes>
+#include <cpuid.h>
+#include <cstring>
+#include <iterator>
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "Register/OpenBSD/RegisterInfoOpenBSD_i386.h"
+#include "Register/OpenBSD/RegisterInfoOpenBSD_x86_64.h"
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#if defined(__x86_64__)
+#include <machine/specialreg.h>
+#endif
+// clang-format on
+
+using namespace lldb_private;
+using namespace lldb_private::process_openbsd;
+
+namespace {
+
+enum {
+ k_xsave_offset_legacy_region = 160,
+ k_xsave_offset_invalid = UINT32_MAX,
+};
+
+#if defined(__x86_64__)
+
+// x86 64-bit general purpose registers.
+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, lldb_r9d_x86_64, lldb_r10d_x86_64, lldb_r11d_x86_64,
+ lldb_r12d_x86_64, lldb_r13d_x86_64, lldb_r14d_x86_64, lldb_r15d_x86_64,
+ 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, lldb_r9w_x86_64, lldb_r10w_x86_64, lldb_r11w_x86_64,
+ lldb_r12w_x86_64, lldb_r13w_x86_64, lldb_r14w_x86_64, lldb_r15w_x86_64,
+ 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, lldb_r9l_x86_64, lldb_r10l_x86_64, lldb_r11l_x86_64,
+ lldb_r12l_x86_64, lldb_r13l_x86_64, lldb_r14l_x86_64, lldb_r15l_x86_64,
+ 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.
+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_fip_x86_64, lldb_foseg_x86_64, lldb_fooff_x86_64,
+ lldb_fdp_x86_64, lldb_mxcsr_x86_64, lldb_mxcsrmask_x86_64,
+ lldb_st0_x86_64, lldb_st1_x86_64, lldb_st2_x86_64,
+ lldb_st3_x86_64, lldb_st4_x86_64, lldb_st5_x86_64,
+ lldb_st6_x86_64, lldb_st7_x86_64, lldb_mm0_x86_64,
+ lldb_mm1_x86_64, lldb_mm2_x86_64, lldb_mm3_x86_64,
+ lldb_mm4_x86_64, lldb_mm5_x86_64, lldb_mm6_x86_64,
+ lldb_mm7_x86_64, lldb_xmm0_x86_64, lldb_xmm1_x86_64,
+ lldb_xmm2_x86_64, lldb_xmm3_x86_64, lldb_xmm4_x86_64,
+ lldb_xmm5_x86_64, lldb_xmm6_x86_64, lldb_xmm7_x86_64,
+ lldb_xmm8_x86_64, lldb_xmm9_x86_64, lldb_xmm10_x86_64,
+ lldb_xmm11_x86_64, lldb_xmm12_x86_64, lldb_xmm13_x86_64,
+ lldb_xmm14_x86_64, lldb_xmm15_x86_64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_fpu_regnums_x86_64) / sizeof(g_fpu_regnums_x86_64[0])) -
+ 1 ==
+ k_num_fpr_registers_x86_64,
+ "g_fpu_regnums_x86_64 has wrong number of register infos");
+
+// x86 64-bit AVX registers.
+const uint32_t g_avx_regnums_x86_64[] = {
+ lldb_ymm0_x86_64, lldb_ymm1_x86_64, lldb_ymm2_x86_64, lldb_ymm3_x86_64,
+ lldb_ymm4_x86_64, lldb_ymm5_x86_64, lldb_ymm6_x86_64, lldb_ymm7_x86_64,
+ lldb_ymm8_x86_64, lldb_ymm9_x86_64, lldb_ymm10_x86_64, lldb_ymm11_x86_64,
+ lldb_ymm12_x86_64, lldb_ymm13_x86_64, lldb_ymm14_x86_64, lldb_ymm15_x86_64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) -
+ 1 ==
+ k_num_avx_registers_x86_64,
+ "g_avx_regnums_x86_64 has wrong number of register infos");
+
+// Register sets for x86 64-bit.
+const RegisterSet g_reg_sets[] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_x86_64,
+ g_gpr_regnums_x86_64},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_x86_64,
+ g_fpu_regnums_x86_64},
+ {"Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64,
+ g_avx_regnums_x86_64},
+};
+
+#else // defined(__i386__)
+
+// 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");
+
+// Register sets for x86 32-bit. OpenBSD/i386 has no PT_GETXSTATE, so the AVX
+// registers are not available.
+const RegisterSet g_reg_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},
+};
+
+#endif
+
+constexpr uint32_t k_num_register_sets = std::size(g_reg_sets);
+
+} // namespace
+
+std::unique_ptr<NativeRegisterContextOpenBSD>
+NativeRegisterContextOpenBSD::CreateHostNativeRegisterContextOpenBSD(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+ return std::make_unique<NativeRegisterContextOpenBSD_x86>(target_arch,
+ native_thread);
+}
+
+// NativeRegisterContextOpenBSD_x86 members.
+
+static RegisterInfoInterface *
+CreateRegisterInfoInterface(const ArchSpec &target_arch) {
+ // OpenBSD/amd64 cannot run i386 executables, so the register layout always
+ // matches the host.
+#if defined(__x86_64__)
+ return new RegisterInfoOpenBSD_x86_64(target_arch);
+#else
+ return new RegisterInfoOpenBSD_i386(target_arch);
+#endif
+}
+
+NativeRegisterContextOpenBSD_x86::NativeRegisterContextOpenBSD_x86(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+ : NativeRegisterContextOpenBSD(native_thread,
+ CreateRegisterInfoInterface(target_arch)),
+ m_gpr(), m_fpr(), m_regset_offsets() {
+ assert(m_gpr.size() == GetRegisterInfoInterface().GetGPRSize());
+#if defined(__x86_64__)
+ static_assert(sizeof(struct fpreg) == sizeof(FXSAVE),
+ "struct fpreg is not an FXSAVE area");
+ const uint32_t first_fpr = lldb_fctrl_x86_64;
+#else
+ static_assert(sizeof(struct xmmregs) == sizeof(FXSAVE),
+ "struct xmmregs is not an FXSAVE area");
+ const uint32_t first_fpr = lldb_fctrl_i386;
+#endif
+
+ // The RegisterInfo byte offsets describe a UserArea with the FPR area
+ // following the GPRs; remember where it starts so that the offsets can be
+ // mapped into m_fpr.
+ m_regset_offsets[FPRegSet] =
+ GetRegisterInfoInterface().GetRegisterInfo()[first_fpr].byte_offset;
+
+ m_xsave_offsets.fill(k_xsave_offset_invalid);
+
+#if defined(PT_GETXSTATE_INFO)
+ struct ptrace_xstate_info info;
+ const Status error = NativeProcessOpenBSD::PtraceWrapper(
+ PT_GETXSTATE_INFO, m_thread.GetID(), &info, sizeof(info));
+ if (error.Fail())
+ return;
+
+ assert(info.xsave_mask & XFEATURE_X87);
+ assert(info.xsave_mask & XFEATURE_SSE);
+
+ if (info.xsave_mask & XFEATURE_AVX) {
+ uint32_t eax, ecx, edx;
+ __get_cpuid_count(0x0D, 2, &eax, &m_xsave_offsets[YMMRegSet], &ecx, &edx);
+ }
+
+ m_xsave.resize(info.xsave_len);
+#endif
+}
+
+uint32_t NativeRegisterContextOpenBSD_x86::GetUserRegisterCount() const {
+ uint32_t count = 0;
+ for (const RegisterSet &set : g_reg_sets)
+ count += set.num_registers;
+ return count;
+}
+
+uint32_t NativeRegisterContextOpenBSD_x86::GetRegisterSetCount() const {
+ return k_num_register_sets;
+}
+
+const RegisterSet *
+NativeRegisterContextOpenBSD_x86::GetRegisterSet(uint32_t set_index) const {
+ if (set_index >= k_num_register_sets)
+ return nullptr;
+ return &g_reg_sets[set_index];
+}
+
+std::optional<NativeRegisterContextOpenBSD_x86::RegSetKind>
+NativeRegisterContextOpenBSD_x86::GetSetForNativeRegNum(
+ uint32_t reg_num) const {
+#if defined(__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 YMMRegSet;
+#else
+ 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;
+#endif
+ return std::nullopt;
+}
+
+Status NativeRegisterContextOpenBSD_x86::DoReadFPR(void *buf) {
+#if defined(__x86_64__)
+ return NativeRegisterContextOpenBSD::DoReadFPR(buf);
+#else
+ // On i386 struct fpreg is not an FXSAVE area; PT_GETXMMREGS returns one.
+ return NativeProcessOpenBSD::PtraceWrapper(PT_GETXMMREGS, m_thread.GetID(),
+ buf);
+#endif
+}
+
+Status NativeRegisterContextOpenBSD_x86::DoWriteFPR(void *buf) {
+#if defined(__x86_64__)
+ return NativeRegisterContextOpenBSD::DoWriteFPR(buf);
+#else
+ return NativeProcessOpenBSD::PtraceWrapper(PT_SETXMMREGS, m_thread.GetID(),
+ buf);
+#endif
+}
+
+Status NativeRegisterContextOpenBSD_x86::ReadRegisterSet(RegSetKind set) {
+ switch (set) {
+ case GPRegSet:
+ return ReadGPR();
+ case FPRegSet:
+ return ReadFPR();
+ case YMMRegSet:
+#if defined(PT_GETXSTATE)
+ if (m_xsave.empty())
+ return Status::FromErrorString("XSAVE area is not available");
+ return NativeProcessOpenBSD::PtraceWrapper(PT_GETXSTATE, m_thread.GetID(),
+ m_xsave.data(), m_xsave.size());
+#else
+ return Status::FromErrorString("XSAVE area is not available");
+#endif
+ }
+ llvm_unreachable("NativeRegisterContextOpenBSD_x86::ReadRegisterSet");
+}
+
+Status NativeRegisterContextOpenBSD_x86::WriteRegisterSet(RegSetKind set) {
+ switch (set) {
+ case GPRegSet:
+ return WriteGPR();
+ case FPRegSet:
+ return WriteFPR();
+ case YMMRegSet:
+#if defined(PT_SETXSTATE)
+ // ReadRegisterSet() must always be called before WriteRegisterSet().
+ if (m_xsave.empty())
+ return Status::FromErrorString("XSAVE area is not available");
+ return NativeProcessOpenBSD::PtraceWrapper(PT_SETXSTATE, m_thread.GetID(),
+ m_xsave.data(), m_xsave.size());
+#else
+ return Status::FromErrorString("XSAVE area is not available");
+#endif
+ }
+ llvm_unreachable("NativeRegisterContextOpenBSD_x86::WriteRegisterSet");
+}
+
+Status
+NativeRegisterContextOpenBSD_x86::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue ®_value) {
+ if (!reg_info)
+ return Status::FromErrorString("reg_info NULL");
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ return Status::FromErrorStringWithFormat(
+ "register \"%s\" is an internal-only lldb register, cannot read "
+ "directly",
+ reg_info->name);
+ }
+
+ std::optional<RegSetKind> set = GetSetForNativeRegNum(reg);
+ if (!set)
+ return Status::FromErrorStringWithFormat(
+ "register \"%s\" is in unrecognized set", reg_info->name);
+
+ Status error = ReadRegisterSet(*set);
+ if (error.Fail())
+ return error;
+
+ if (*set == YMMRegSet) {
+ std::optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg);
+ if (!ymm_reg)
+ return Status::FromErrorStringWithFormat(
+ "register \"%s\" not supported by CPU/kernel", reg_info->name);
+ YMMReg ymm = XStateToYMM(ymm_reg->xmm, ymm_reg->ymm_hi);
+ reg_value.SetBytes(ymm.bytes, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ return error;
+ }
+
+ void *data = GetOffsetRegSetData(*set, reg_info->byte_offset);
+ FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_fpr.data());
+ if (data == &fpr->ftag) // FXSAVE stores the abridged tag word.
+ reg_value.SetUInt16(
+ AbridgedToFullTagWord(fpr->ftag, fpr->fstat, fpr->stmm));
+ else
+ reg_value.SetBytes(data, reg_info->byte_size, endian::InlHostByteOrder());
+ return error;
+}
+
+Status NativeRegisterContextOpenBSD_x86::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue ®_value) {
+ if (!reg_info)
+ return Status::FromErrorString("reg_info NULL");
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ return Status::FromErrorStringWithFormat(
+ "register \"%s\" is an internal-only lldb register, cannot write "
+ "directly",
+ reg_info->name);
+ }
+
+ std::optional<RegSetKind> set = GetSetForNativeRegNum(reg);
+ if (!set)
+ return Status::FromErrorStringWithFormat(
+ "register \"%s\" is in unrecognized set", reg_info->name);
+
+ Status error = ReadRegisterSet(*set);
+ if (error.Fail())
+ return error;
+
+ if (*set == YMMRegSet) {
+ std::optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg);
+ if (!ymm_reg)
+ return Status::FromErrorStringWithFormat(
+ "register \"%s\" not supported by CPU/kernel", reg_info->name);
+ YMMReg ymm;
+ ::memcpy(ymm.bytes, reg_value.GetBytes(), reg_value.GetByteSize());
+ YMMToXState(ymm, ymm_reg->xmm, ymm_reg->ymm_hi);
+ } else {
+ void *data = GetOffsetRegSetData(*set, reg_info->byte_offset);
+ FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_fpr.data());
+ if (data == &fpr->ftag) // FXSAVE stores the abridged tag word.
+ fpr->ftag = FullToAbridgedTagWord(reg_value.GetAsUInt16());
+ else
+ ::memcpy(data, reg_value.GetBytes(), reg_value.GetByteSize());
+ }
+
+ return WriteRegisterSet(*set);
+}
+
+Status NativeRegisterContextOpenBSD_x86::ReadAllRegisterValues(
+ lldb::WritableDataBufferSP &data_sp) {
+ Status error = ReadGPR();
+ if (error.Fail())
+ return error;
+
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+
+ data_sp.reset(new DataBufferHeap(m_gpr.size() + m_fpr.size(), 0));
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, m_gpr.data(), m_gpr.size());
+ dst += m_gpr.size();
+ ::memcpy(dst, m_fpr.data(), m_fpr.size());
+
+ return error;
+}
+
+Status NativeRegisterContextOpenBSD_x86::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (!data_sp)
+ return Status::FromErrorStringWithFormat(
+ "NativeRegisterContextOpenBSD_x86::%s invalid data_sp provided",
+ __FUNCTION__);
+
+ const size_t expected = m_gpr.size() + m_fpr.size();
+ if (data_sp->GetByteSize() != expected)
+ return Status::FromErrorStringWithFormat(
+ "NativeRegisterContextOpenBSD_x86::%s data_sp contained mismatched "
+ "data size, expected %zu, actual %" PRIu64,
+ __FUNCTION__, expected, data_sp->GetByteSize());
+
+ const uint8_t *src = data_sp->GetBytes();
+ if (src == nullptr)
+ return Status::FromErrorStringWithFormat(
+ "NativeRegisterContextOpenBSD_x86::%s DataBuffer::GetBytes() returned "
+ "a null pointer",
+ __FUNCTION__);
+
+ ::memcpy(m_gpr.data(), src, m_gpr.size());
+ Status error = WriteGPR();
+ if (error.Fail())
+ return error;
+ src += m_gpr.size();
+
+ ::memcpy(m_fpr.data(), src, m_fpr.size());
+ return WriteFPR();
+}
+
+uint8_t *
+NativeRegisterContextOpenBSD_x86::GetOffsetRegSetData(RegSetKind set,
+ size_t reg_offset) {
+ uint8_t *base;
+ switch (set) {
+ case GPRegSet:
+ base = m_gpr.data();
+ break;
+ case FPRegSet:
+ base = m_fpr.data();
+ break;
+ case YMMRegSet:
+ llvm_unreachable("GetOffsetRegSetData() is unsuitable for this regset.");
+ }
+ assert(reg_offset >= m_regset_offsets[set]);
+ return base + (reg_offset - m_regset_offsets[set]);
+}
+
+std::optional<NativeRegisterContextOpenBSD_x86::YMMSplitPtr>
+NativeRegisterContextOpenBSD_x86::GetYMMSplitReg(uint32_t reg) {
+#if defined(__x86_64__)
+ if (m_xsave_offsets[YMMRegSet] == k_xsave_offset_invalid)
+ return std::nullopt;
+
+ uint32_t reg_index = reg - lldb_ymm0_x86_64;
+ auto *xmm =
+ reinterpret_cast<XMMReg *>(m_xsave.data() + k_xsave_offset_legacy_region);
+ auto *ymm =
+ reinterpret_cast<XMMReg *>(m_xsave.data() + m_xsave_offsets[YMMRegSet]);
+ return YMMSplitPtr{&xmm[reg_index], &ymm[reg_index]};
+#else
+ return std::nullopt;
+#endif
+}
+
+#endif // defined(__i386__) || defined(__x86_64__)
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86.h b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86.h
new file mode 100644
index 00000000000000..c20c35044455f0
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86.h
@@ -0,0 +1,96 @@
+//===-- NativeRegisterContextOpenBSD_x86.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_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_X86_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_X86_H
+
+// clang-format off
+#include <sys/types.h>
+#include <machine/reg.h>
+// clang-format on
+
+#include <array>
+#include <optional>
+#include <vector>
+
+#include "Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.h"
+#include "Register/Arch/RegisterContext_x86.h"
+#include "Register/Arch/lldb-x86-register-enums.h"
+
+namespace lldb_private {
+namespace process_openbsd {
+
+class NativeProcessOpenBSD;
+
+class NativeRegisterContextOpenBSD_x86 : public NativeRegisterContextOpenBSD {
+public:
+ NativeRegisterContextOpenBSD_x86(const ArchSpec &target_arch,
+ NativeThreadProtocol &native_thread);
+
+ uint32_t GetUserRegisterCount() const override;
+ uint32_t GetRegisterSetCount() const override;
+
+ const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
+
+ Status ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue ®_value) override;
+
+ Status WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue ®_value) override;
+
+ Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override;
+
+ Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+protected:
+ void *GetGPRBuffer() override { return m_gpr.data(); }
+ size_t GetGPRSize() override { return m_gpr.size(); }
+ void *GetFPRBuffer() override { return m_fpr.data(); }
+ size_t GetFPRSize() override { return m_fpr.size(); }
+
+ Status DoReadFPR(void *buf) override;
+ Status DoWriteFPR(void *buf) override;
+
+private:
+ // Private member types.
+ enum RegSetKind {
+ GPRegSet,
+ FPRegSet,
+ YMMRegSet,
+ MaxRegSet = YMMRegSet,
+ };
+
+ // Private member variables.
+ std::array<uint8_t, sizeof(struct reg)> m_gpr;
+ std::array<uint8_t, sizeof(FXSAVE)> m_fpr;
+ std::vector<uint8_t> m_xsave;
+ std::array<uint32_t, MaxRegSet + 1> m_xsave_offsets;
+ std::array<size_t, MaxRegSet + 1> m_regset_offsets;
+
+ std::optional<RegSetKind> GetSetForNativeRegNum(uint32_t reg_num) const;
+
+ Status ReadRegisterSet(RegSetKind set);
+ Status WriteRegisterSet(RegSetKind set);
+
+ uint8_t *GetOffsetRegSetData(RegSetKind set, size_t reg_offset);
+
+ struct YMMSplitPtr {
+ void *xmm;
+ void *ymm_hi;
+ };
+ std::optional<YMMSplitPtr> GetYMMSplitReg(uint32_t reg);
+};
+
+} // namespace process_openbsd
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVEREGISTERCONTEXTOPENBSD_X86_H
+
+#endif // defined(__i386__) || defined(__x86_64__)
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeThreadOpenBSD.cpp b/lldb/source/Plugins/Process/OpenBSD/NativeThreadOpenBSD.cpp
new file mode 100644
index 00000000000000..5ab33e3860e7ec
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeThreadOpenBSD.cpp
@@ -0,0 +1,151 @@
+//===-- NativeThreadOpenBSD.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 "NativeThreadOpenBSD.h"
+#include "NativeRegisterContextOpenBSD.h"
+
+#include "NativeProcessOpenBSD.h"
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_openbsd;
+
+NativeThreadOpenBSD::NativeThreadOpenBSD(NativeProcessOpenBSD &process,
+ lldb::tid_t tid)
+ : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid) {
+ m_reg_context_up =
+ NativeRegisterContextOpenBSD::CreateHostNativeRegisterContextOpenBSD(
+ process.GetArchitecture(), *this);
+ // NativeProcessOpenBSD::Manager refuses to launch or attach on
+ // architectures without a register context.
+ assert(m_reg_context_up && "no register context for this architecture");
+}
+
+void NativeThreadOpenBSD::SetStoppedBySignal(uint32_t signo) {
+ Log *log = GetLog(POSIXLog::Thread);
+ LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo);
+
+ SetStopped();
+
+ m_stop_info.reason = StopReason::eStopReasonSignal;
+ m_stop_info.signo = signo;
+}
+
+void NativeThreadOpenBSD::SetStoppedByBreakpoint() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonBreakpoint;
+ m_stop_info.signo = SIGTRAP;
+}
+
+void NativeThreadOpenBSD::SetStoppedByTrace() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonTrace;
+ m_stop_info.signo = SIGTRAP;
+}
+
+void NativeThreadOpenBSD::SetStoppedByExec() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonExec;
+ m_stop_info.signo = SIGTRAP;
+}
+
+void NativeThreadOpenBSD::SetStoppedByFork(lldb::pid_t child_pid,
+ lldb::tid_t child_tid) {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonFork;
+ m_stop_info.signo = SIGTRAP;
+ m_stop_info.details.fork.child_pid = child_pid;
+ m_stop_info.details.fork.child_tid = child_tid;
+}
+
+void NativeThreadOpenBSD::SetStoppedWithNoReason() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonNone;
+ m_stop_info.signo = 0;
+}
+
+void NativeThreadOpenBSD::SetStopped() {
+ const StateType new_state = StateType::eStateStopped;
+ m_state = new_state;
+ m_stop_description.clear();
+}
+
+void NativeThreadOpenBSD::SetRunning() {
+ m_state = StateType::eStateRunning;
+ ClearStopInfo();
+}
+
+void NativeThreadOpenBSD::SetStepping() {
+ m_state = StateType::eStateStepping;
+ ClearStopInfo();
+}
+
+std::string NativeThreadOpenBSD::GetName() { return m_name; }
+
+lldb::StateType NativeThreadOpenBSD::GetState() { return m_state; }
+
+bool NativeThreadOpenBSD::GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) {
+ Log *log = GetLog(POSIXLog::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!");
+}
+
+NativeRegisterContext &NativeThreadOpenBSD::GetRegisterContext() {
+ assert(m_reg_context_up);
+ return *m_reg_context_up;
+}
+
+Status NativeThreadOpenBSD::SetWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, bool hardware) {
+ if (hardware)
+ return Status::FromErrorString("Not Available");
+ return Status::FromErrorString("Software watchpoints not implemented");
+}
+
+Status NativeThreadOpenBSD::RemoveWatchpoint(lldb::addr_t addr) {
+ return Status::FromErrorString("Software watchpoints not implemented");
+}
+
+Status NativeThreadOpenBSD::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ return Status::FromErrorString("Not Available");
+}
+
+Status NativeThreadOpenBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+ return Status::FromErrorString("Not Available");
+}
diff --git a/lldb/source/Plugins/Process/OpenBSD/NativeThreadOpenBSD.h b/lldb/source/Plugins/Process/OpenBSD/NativeThreadOpenBSD.h
new file mode 100644
index 00000000000000..0a072dda16da45
--- /dev/null
+++ b/lldb/source/Plugins/Process/OpenBSD/NativeThreadOpenBSD.h
@@ -0,0 +1,72 @@
+//===-- NativeThreadOpenBSD.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_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVETHREADOPENBSD_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVETHREADOPENBSD_H
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include <csignal>
+#include <string>
+
+namespace lldb_private {
+namespace process_openbsd {
+
+class NativeProcessOpenBSD;
+
+class NativeThreadOpenBSD : public NativeThreadProtocol {
+ friend class NativeProcessOpenBSD;
+
+public:
+ NativeThreadOpenBSD(NativeProcessOpenBSD &process, lldb::tid_t tid);
+
+ // NativeThreadProtocol Interface
+ std::string GetName() override;
+
+ lldb::StateType GetState() override;
+
+ bool GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) override;
+
+ NativeRegisterContext &GetRegisterContext() override;
+
+ // OpenBSD does not expose hardware debug registers to userland
+ // so these functions will just return a Status error.
+ Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+ Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
+
+ // Similarly, until software watchpoints are implemented in lldb,
+ // these functions will just return a Status error.
+ Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags,
+ bool hardware) override;
+ Status RemoveWatchpoint(lldb::addr_t addr) override;
+
+private:
+ // Interface for friend classes
+
+ void SetStoppedBySignal(uint32_t signo);
+ void SetStoppedByBreakpoint();
+ void SetStoppedByTrace();
+ void SetStoppedByExec();
+ void SetStoppedByFork(lldb::pid_t child_pid, lldb::tid_t child_tid);
+ void SetStoppedWithNoReason();
+ void SetStopped();
+ void SetRunning();
+ void SetStepping();
+ void SetName(std::string name) { m_name = std::move(name); }
+
+ // Member Variables
+ lldb::StateType m_state;
+ std::unique_ptr<NativeRegisterContext> m_reg_context_up;
+ std::string m_name;
+};
+
+} // namespace process_openbsd
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_OPENBSD_NATIVETHREADOPENBSD_H
diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index fd31d81a35c202..e0e50ba1df7ef7 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -24,6 +24,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
list(APPEND LLDB_PLUGINS lldbPluginProcessNetBSD)
endif()
+if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
+ list(APPEND LLDB_PLUGINS lldbPluginProcessOpenBSD)
+endif()
+
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
list(APPEND LLDB_PLUGINS lldbPluginObjectFileMachO)
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index 98a79dedf65c02..8bb79e9696daba 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -50,6 +50,8 @@
#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h"
#elif defined(__NetBSD__)
#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"
+#elif defined(__OpenBSD__)
+#include "Plugins/Process/OpenBSD/NativeProcessOpenBSD.h"
#elif defined(_WIN32)
#include "Plugins/Process/Windows/Common/NativeProcessWindows.h"
#elif defined(_AIX)
@@ -77,6 +79,8 @@ typedef process_linux::NativeProcessLinux::Manager NativeProcessManager;
typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager;
#elif defined(__NetBSD__)
typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager;
+#elif defined(__OpenBSD__)
+typedef process_openbsd::NativeProcessOpenBSD::Manager NativeProcessManager;
#elif defined(_WIN32)
typedef NativeProcessWindows::Manager NativeProcessManager;
#elif defined(_AIX)
diff --git a/lldb/unittests/Process/Utility/CMakeLists.txt b/lldb/unittests/Process/Utility/CMakeLists.txt
index c7dc4e969bd6f4..c494acc956e52c 100644
--- a/lldb/unittests/Process/Utility/CMakeLists.txt
+++ b/lldb/unittests/Process/Utility/CMakeLists.txt
@@ -3,16 +3,21 @@ set(FREEBSD_SOURCES
set(NETBSD_SOURCES
RegisterInfoNetBSDTest_i386.cpp
RegisterInfoNetBSDTest_x86_64.cpp)
+set(OPENBSD_SOURCES
+ RegisterInfoOpenBSDTest.cpp)
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
list(APPEND PLATFORM_SOURCES ${FREEBSD_SOURCES})
elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
list(APPEND PLATFORM_SOURCES ${NETBSD_SOURCES})
+elseif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
+ list(APPEND PLATFORM_SOURCES ${OPENBSD_SOURCES})
endif()
set(LLVM_OPTIONAL_SOURCES
${FREEBSD_SOURCES}
- ${NETBSD_SOURCES})
+ ${NETBSD_SOURCES}
+ ${OPENBSD_SOURCES})
add_lldb_unittest(ProcessUtilityTests
PARTIAL_SOURCES_INTENDED
diff --git a/lldb/unittests/Process/Utility/RegisterInfoOpenBSDTest.cpp b/lldb/unittests/Process/Utility/RegisterInfoOpenBSDTest.cpp
new file mode 100644
index 00000000000000..4a8d363123c861
--- /dev/null
+++ b/lldb/unittests/Process/Utility/RegisterInfoOpenBSDTest.cpp
@@ -0,0 +1,244 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Check that the register infos used by NativeProcessOpenBSD match the layout
+// of the structures that OpenBSD's ptrace(2) returns, since the register
+// contexts access registers by their byte_offset into those structures.
+
+// clang-format off
+#include <sys/types.h>
+#include <machine/reg.h>
+// clang-format on
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "Register/Arch/lldb-arm64-register-enums.h"
+#include "Register/Arch/lldb-x86-register-enums.h"
+#include "Register/Common/RegisterInfo_arm64.h"
+#include "Register/OpenBSD/RegisterInfoOpenBSD_i386.h"
+#include "Register/OpenBSD/RegisterInfoOpenBSD_x86_64.h"
+
+#include <cstddef>
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static std::pair<size_t, size_t> GetRegParams(RegisterInfoInterface &ctx,
+ uint32_t reg) {
+ const RegisterInfo &info = ctx.GetRegisterInfo()[reg];
+ return {info.byte_offset, info.byte_size};
+}
+
+[[maybe_unused]] static std::optional<uint32_t>
+FindRegister(RegisterInfoInterface &ctx, llvm::StringRef name) {
+ for (uint32_t i = 0; i < ctx.GetRegisterCount(); ++i)
+ if (name == ctx.GetRegisterInfo()[i].name)
+ return i;
+ return std::nullopt;
+}
+
+#define EXPECT_REG(reg, offset, size) \
+ EXPECT_THAT(GetRegParams(reg_ctx, reg), ::testing::Pair(offset, size))
+
+#if defined(__x86_64__)
+
+#define EXPECT_GPR_X86_64(regname) \
+ EXPECT_REG(lldb_##regname##_x86_64, offsetof(reg, r_##regname), \
+ sizeof(reg::r_##regname))
+#define EXPECT_FPR_X86_64(regname, offset, size) \
+ EXPECT_REG(lldb_##regname##_x86_64, base_offset + (offset), size)
+
+TEST(RegisterInfoOpenBSDTest, x86_64) {
+ ArchSpec arch{"x86_64-unknown-openbsd"};
+ RegisterInfoOpenBSD_x86_64 reg_ctx{arch};
+
+ EXPECT_EQ(reg_ctx.GetGPRSize(), sizeof(reg));
+
+ EXPECT_GPR_X86_64(rdi);
+ EXPECT_GPR_X86_64(rsi);
+ EXPECT_GPR_X86_64(rdx);
+ EXPECT_GPR_X86_64(rcx);
+ EXPECT_GPR_X86_64(r8);
+ EXPECT_GPR_X86_64(r9);
+ EXPECT_GPR_X86_64(r10);
+ EXPECT_GPR_X86_64(r11);
+ EXPECT_GPR_X86_64(r12);
+ EXPECT_GPR_X86_64(r13);
+ EXPECT_GPR_X86_64(r14);
+ EXPECT_GPR_X86_64(r15);
+ EXPECT_GPR_X86_64(rbp);
+ EXPECT_GPR_X86_64(rbx);
+ EXPECT_GPR_X86_64(rax);
+ EXPECT_GPR_X86_64(rsp);
+ EXPECT_GPR_X86_64(rip);
+ EXPECT_GPR_X86_64(rflags);
+ EXPECT_GPR_X86_64(cs);
+ EXPECT_GPR_X86_64(ss);
+ EXPECT_GPR_X86_64(ds);
+ EXPECT_GPR_X86_64(es);
+ EXPECT_GPR_X86_64(fs);
+ EXPECT_GPR_X86_64(gs);
+
+ // struct fpreg is the FXSAVE area. fctrl is its first field and gives the
+ // offset of the whole area.
+ EXPECT_EQ(sizeof(fpreg), 512u);
+ size_t base_offset = reg_ctx.GetRegisterInfo()[lldb_fctrl_x86_64].byte_offset;
+
+ EXPECT_FPR_X86_64(fctrl, offsetof(fxsave64, fx_fcw), 2);
+ EXPECT_FPR_X86_64(fstat, offsetof(fxsave64, fx_fsw), 2);
+ // TODO: This is a known bug, the abridged ftag is 8 bits in length.
+ EXPECT_FPR_X86_64(ftag, offsetof(fxsave64, fx_ftw), 2);
+ EXPECT_FPR_X86_64(fop, offsetof(fxsave64, fx_fop), 2);
+ // fioff/fiseg and fooff/foseg are the two halves of FIP and FDP.
+ EXPECT_FPR_X86_64(fioff, offsetof(fxsave64, fx_rip), 4);
+ EXPECT_FPR_X86_64(fiseg, offsetof(fxsave64, fx_rip) + 4, 4);
+ EXPECT_FPR_X86_64(fip, offsetof(fxsave64, fx_rip), 8);
+ EXPECT_FPR_X86_64(fooff, offsetof(fxsave64, fx_rdp), 4);
+ EXPECT_FPR_X86_64(foseg, offsetof(fxsave64, fx_rdp) + 4, 4);
+ EXPECT_FPR_X86_64(fdp, offsetof(fxsave64, fx_rdp), 8);
+ EXPECT_FPR_X86_64(mxcsr, offsetof(fxsave64, fx_mxcsr), 4);
+ EXPECT_FPR_X86_64(mxcsrmask, offsetof(fxsave64, fx_mxcsr_mask), 4);
+
+ for (uint32_t i = 0; i < 8; ++i) {
+ const size_t st =
+ offsetof(fxsave64, fx_st) + i * sizeof(fxsave64::fx_st[0]);
+ EXPECT_REG(lldb_st0_x86_64 + i, base_offset + st, 10u) << "st" << i;
+ EXPECT_REG(lldb_mm0_x86_64 + i, base_offset + st, 8u) << "mm" << i;
+ }
+ for (uint32_t i = 0; i < 16; ++i)
+ EXPECT_REG(lldb_xmm0_x86_64 + i,
+ base_offset + offsetof(fxsave64, fx_xmm) +
+ i * sizeof(fxsave64::fx_xmm[0]),
+ sizeof(fxsave64::fx_xmm[0]))
+ << "xmm" << i;
+}
+
+#endif // defined(__x86_64__)
+
+#if defined(__i386__)
+
+#define EXPECT_GPR_I386(regname) \
+ EXPECT_REG(lldb_##regname##_i386, offsetof(reg, r_##regname), \
+ sizeof(reg::r_##regname))
+#define EXPECT_FPR_I386(regname, offset, size) \
+ EXPECT_REG(lldb_##regname##_i386, base_offset + (offset), size)
+
+TEST(RegisterInfoOpenBSDTest, i386) {
+ ArchSpec arch{"i386-unknown-openbsd"};
+ RegisterInfoOpenBSD_i386 reg_ctx{arch};
+
+ EXPECT_EQ(reg_ctx.GetGPRSize(), sizeof(reg));
+
+ EXPECT_GPR_I386(eax);
+ EXPECT_GPR_I386(ecx);
+ EXPECT_GPR_I386(edx);
+ EXPECT_GPR_I386(ebx);
+ EXPECT_GPR_I386(esp);
+ EXPECT_GPR_I386(ebp);
+ EXPECT_GPR_I386(esi);
+ EXPECT_GPR_I386(edi);
+ EXPECT_GPR_I386(eip);
+ EXPECT_GPR_I386(eflags);
+ EXPECT_GPR_I386(cs);
+ EXPECT_GPR_I386(ss);
+ EXPECT_GPR_I386(ds);
+ EXPECT_GPR_I386(es);
+ EXPECT_GPR_I386(fs);
+ EXPECT_GPR_I386(gs);
+
+ // The FP registers are read with PT_GETXMMREGS, whose struct xmmregs is an
+ // opaque FXSAVE area, so check against the FXSAVE layout. fctrl is its first
+ // field and gives the offset of the whole area.
+ EXPECT_EQ(sizeof(xmmregs), 512u);
+ size_t base_offset = reg_ctx.GetRegisterInfo()[lldb_fctrl_i386].byte_offset;
+
+ EXPECT_FPR_I386(fctrl, 0x00, 2);
+ EXPECT_FPR_I386(fstat, 0x02, 2);
+ // TODO: This is a known bug, the abridged ftag is 8 bits in length.
+ EXPECT_FPR_I386(ftag, 0x04, 2);
+ EXPECT_FPR_I386(fop, 0x06, 2);
+ EXPECT_FPR_I386(fioff, 0x08, 4);
+ EXPECT_FPR_I386(fiseg, 0x0C, 4);
+ EXPECT_FPR_I386(fooff, 0x10, 4);
+ EXPECT_FPR_I386(foseg, 0x14, 4);
+ EXPECT_FPR_I386(mxcsr, 0x18, 4);
+ EXPECT_FPR_I386(mxcsrmask, 0x1C, 4);
+
+ for (uint32_t i = 0; i < 8; ++i) {
+ EXPECT_REG(lldb_st0_i386 + i, base_offset + 0x20 + i * 0x10, 10u)
+ << "st" << i;
+ EXPECT_REG(lldb_mm0_i386 + i, base_offset + 0x20 + i * 0x10, 8u)
+ << "mm" << i;
+ EXPECT_REG(lldb_xmm0_i386 + i, base_offset + 0xA0 + i * 0x10, 16u)
+ << "xmm" << i;
+ }
+}
+
+#endif // defined(__i386__)
+
+#if defined(__aarch64__)
+
+TEST(RegisterInfoOpenBSDTest, arm64) {
+ ArchSpec arch{"aarch64-unknown-openbsd"};
+ // The register sets that NativeRegisterContextOpenBSD_arm64 provides.
+ Flags opt_regsets =
+ RegisterInfo_arm64::eRegsetMaskPAuth | RegisterInfo_arm64::eRegsetMaskTLS;
+ RegisterInfo_arm64 reg_ctx{arch, opt_regsets};
+
+ // struct reg starts with the same layout as RegisterInfo_arm64::GPR, but is
+ // longer because it also holds r_tpidr.
+ for (uint32_t i = 0; i <= 28; ++i)
+ EXPECT_REG(gpr_x0_arm64 + i,
+ offsetof(reg, r_reg) + i * sizeof(reg::r_reg[0]),
+ sizeof(reg::r_reg[0]))
+ << "x" << i;
+ EXPECT_REG(gpr_fp_arm64, offsetof(reg, r_reg) + 29 * sizeof(reg::r_reg[0]),
+ sizeof(reg::r_reg[0]));
+ EXPECT_REG(gpr_lr_arm64, offsetof(reg, r_lr), sizeof(reg::r_lr));
+ EXPECT_REG(gpr_sp_arm64, offsetof(reg, r_sp), sizeof(reg::r_sp));
+ EXPECT_REG(gpr_pc_arm64, offsetof(reg, r_pc), sizeof(reg::r_pc));
+ // cpsr is the low 32 bits of r_spsr.
+ EXPECT_REG(gpr_cpsr_arm64, offsetof(reg, r_spsr), 4u);
+ EXPECT_GE(sizeof(reg), sizeof(RegisterInfo_arm64::GPR));
+
+ // The FP registers follow the GPR struct, and struct fpreg starts with the
+ // same layout as RegisterInfo_arm64::FPU.
+ size_t base_offset = reg_ctx.GetRegisterInfo()[fpu_v0_arm64].byte_offset;
+ EXPECT_EQ(base_offset, sizeof(RegisterInfo_arm64::GPR));
+
+ for (uint32_t i = 0; i < 32; ++i)
+ EXPECT_REG(fpu_v0_arm64 + i,
+ base_offset + offsetof(fpreg, fp_reg) +
+ i * sizeof(fpreg::fp_reg[0]),
+ sizeof(fpreg::fp_reg[0]))
+ << "v" << i;
+ EXPECT_REG(fpu_fpsr_arm64, base_offset + offsetof(fpreg, fp_sr),
+ sizeof(fpreg::fp_sr));
+ EXPECT_REG(fpu_fpcr_arm64, base_offset + offsetof(fpreg, fp_cr),
+ sizeof(fpreg::fp_cr));
+
+ // PT_PACMASK returns the data mask followed by the code mask.
+ std::optional<uint32_t> data_mask = FindRegister(reg_ctx, "data_mask");
+ std::optional<uint32_t> code_mask = FindRegister(reg_ctx, "code_mask");
+ ASSERT_TRUE(data_mask);
+ ASSERT_TRUE(code_mask);
+ EXPECT_REG(*data_mask, reg_ctx.GetPAuthOffset(), sizeof(register_t));
+ EXPECT_REG(*code_mask, reg_ctx.GetPAuthOffset() + sizeof(register_t),
+ sizeof(register_t));
+
+ // The TLS set has only tpidr, which is r_tpidr in struct reg.
+ std::optional<uint32_t> tpidr = FindRegister(reg_ctx, "tpidr");
+ ASSERT_TRUE(tpidr);
+ EXPECT_TRUE(reg_ctx.IsTLSReg(*tpidr));
+ EXPECT_REG(*tpidr, reg_ctx.GetTLSOffset(), sizeof(reg::r_tpidr));
+ EXPECT_FALSE(FindRegister(reg_ctx, "tpidr2"));
+}
+
+#endif // defined(__aarch64__)
More information about the lldb-commits
mailing list