[lldb] [Process/FreeBSDRemote] Introduce arm (32-bit) support

Introduce a NativeRegisterContextFreeBSD for 32-bit ARM platform.
This includes support for GPR + VFP registers as exposed by FreeBSD's
ptrace(2) API.  Hardware breakpoints or watchpoints are not supported
due to missing kernel support.  The code is roughly based on the arm64
context.

It also includes an override for GetSoftwareBreakpointTrapOpcode() based
on the matching code in the PlatformFreeBSD plugin.

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

GitOrigin-RevId: 9d029362d1ed48c38565aeab0ca04bf4143d3e5b
diff --git a/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
index 0c9068e..40fef59 100644
--- a/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
+++ b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
@@ -254,6 +254,7 @@
 
     switch (host_triple.getArch()) {
       case llvm::Triple::aarch64:
+      case llvm::Triple::arm:
       case llvm::Triple::x86:
       case llvm::Triple::x86_64:
         use_legacy_plugin = !!getenv("FREEBSD_LEGACY_PLUGIN");
diff --git a/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt b/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt
index 2b19255..9d102be 100644
--- a/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt
+++ b/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_library(lldbPluginProcessFreeBSDRemote
   NativeProcessFreeBSD.cpp
   NativeRegisterContextFreeBSD.cpp
+  NativeRegisterContextFreeBSD_arm.cpp
   NativeRegisterContextFreeBSD_arm64.cpp
   NativeRegisterContextFreeBSD_x86_64.cpp
   NativeThreadFreeBSD.cpp
diff --git a/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp b/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
index 163093c..139313b 100644
--- a/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
+++ b/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
@@ -354,6 +354,27 @@
   return error;
 }
 
+llvm::Expected<llvm::ArrayRef<uint8_t>>
+NativeProcessFreeBSD::GetSoftwareBreakpointTrapOpcode(size_t size_hint) {
+  static const uint8_t g_arm_opcode[] = {0xfe, 0xde, 0xff, 0xe7};
+  static const uint8_t g_thumb_opcode[] = {0x01, 0xde};
+
+  switch (GetArchitecture().GetMachine()) {
+  case llvm::Triple::arm:
+    switch (size_hint) {
+    case 2:
+      return llvm::makeArrayRef(g_thumb_opcode);
+    case 4:
+      return llvm::makeArrayRef(g_arm_opcode);
+    default:
+      return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                     "Unrecognised trap opcode size hint!");
+    }
+  default:
+    return NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_hint);
+  }
+}
+
 Status NativeProcessFreeBSD::Resume(const ResumeActionList &resume_actions) {
   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
   LLDB_LOG(log, "pid {0}", GetID());
diff --git a/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h b/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
index 3c7a940..99123d5 100644
--- a/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
+++ b/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
@@ -84,6 +84,10 @@
   static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
                               int data = 0, int *result = nullptr);
 
+protected:
+  llvm::Expected<llvm::ArrayRef<uint8_t>>
+  GetSoftwareBreakpointTrapOpcode(size_t size_hint) override;
+
 private:
   MainLoop::SignalHandleUP m_sigchld_handle;
   ArchSpec m_arch;
diff --git a/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_arm.cpp b/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_arm.cpp
new file mode 100644
index 0000000..18926d7
--- /dev/null
+++ b/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_arm.cpp
@@ -0,0 +1,202 @@
+//===-- NativeRegisterContextFreeBSD_arm.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(__arm__)
+
+#include "NativeRegisterContextFreeBSD_arm.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
+
+// clang-format off
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+// clang-format on
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_freebsd;
+
+NativeRegisterContextFreeBSD *
+NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD(
+    const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+  return new NativeRegisterContextFreeBSD_arm(target_arch, native_thread);
+}
+
+NativeRegisterContextFreeBSD_arm::NativeRegisterContextFreeBSD_arm(
+    const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+    : NativeRegisterContextRegisterInfo(
+          native_thread, new RegisterInfoPOSIX_arm(target_arch)) {}
+
+RegisterInfoPOSIX_arm &
+NativeRegisterContextFreeBSD_arm::GetRegisterInfo() const {
+  return static_cast<RegisterInfoPOSIX_arm &>(*m_register_info_interface_up);
+}
+
+uint32_t NativeRegisterContextFreeBSD_arm::GetRegisterSetCount() const {
+  return GetRegisterInfo().GetRegisterSetCount();
+}
+
+const RegisterSet *
+NativeRegisterContextFreeBSD_arm::GetRegisterSet(uint32_t set_index) const {
+  return GetRegisterInfo().GetRegisterSet(set_index);
+}
+
+uint32_t NativeRegisterContextFreeBSD_arm::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;
+}
+
+Status NativeRegisterContextFreeBSD_arm::ReadRegisterSet(uint32_t set) {
+  switch (set) {
+  case RegisterInfoPOSIX_arm::GPRegSet:
+    return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(),
+                                               m_reg_data.data());
+  case RegisterInfoPOSIX_arm::FPRegSet:
+    return NativeProcessFreeBSD::PtraceWrapper(
+        PT_GETVFPREGS, m_thread.GetID(),
+        m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm::GPR));
+  }
+  llvm_unreachable("NativeRegisterContextFreeBSD_arm::ReadRegisterSet");
+}
+
+Status NativeRegisterContextFreeBSD_arm::WriteRegisterSet(uint32_t set) {
+  switch (set) {
+  case RegisterInfoPOSIX_arm::GPRegSet:
+    return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(),
+                                               m_reg_data.data());
+  case RegisterInfoPOSIX_arm::FPRegSet:
+    return NativeProcessFreeBSD::PtraceWrapper(
+        PT_SETVFPREGS, m_thread.GetID(),
+        m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm::GPR));
+  }
+  llvm_unreachable("NativeRegisterContextFreeBSD_arm::WriteRegisterSet");
+}
+
+Status
+NativeRegisterContextFreeBSD_arm::ReadRegister(const RegisterInfo *reg_info,
+                                               RegisterValue &reg_value) {
+  Status error;
+
+  if (!reg_info) {
+    error.SetErrorString("reg_info NULL");
+    return error;
+  }
+
+  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+  if (reg == LLDB_INVALID_REGNUM)
+    return Status("no lldb regnum for %s", reg_info && reg_info->name
+                                               ? reg_info->name
+                                               : "<unknown register>");
+
+  uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg);
+  error = ReadRegisterSet(set);
+  if (error.Fail())
+    return error;
+
+  assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size());
+  reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset,
+                     reg_info->byte_size, endian::InlHostByteOrder());
+  return error;
+}
+
+Status NativeRegisterContextFreeBSD_arm::WriteRegister(
+    const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+  Status error;
+
+  if (!reg_info)
+    return Status("reg_info NULL");
+
+  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+  if (reg == LLDB_INVALID_REGNUM)
+    return Status("no lldb regnum for %s", reg_info && reg_info->name
+                                               ? reg_info->name
+                                               : "<unknown register>");
+
+  uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg);
+  error = ReadRegisterSet(set);
+  if (error.Fail())
+    return error;
+
+  assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size());
+  ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(),
+           reg_info->byte_size);
+
+  return WriteRegisterSet(set);
+}
+
+Status NativeRegisterContextFreeBSD_arm::ReadAllRegisterValues(
+    lldb::DataBufferSP &data_sp) {
+  Status error;
+
+  error = ReadRegisterSet(RegisterInfoPOSIX_arm::GPRegSet);
+  if (error.Fail())
+    return error;
+
+  error = ReadRegisterSet(RegisterInfoPOSIX_arm::FPRegSet);
+  if (error.Fail())
+    return error;
+
+  data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0));
+  uint8_t *dst = data_sp->GetBytes();
+  ::memcpy(dst, m_reg_data.data(), m_reg_data.size());
+
+  return error;
+}
+
+Status NativeRegisterContextFreeBSD_arm::WriteAllRegisterValues(
+    const lldb::DataBufferSP &data_sp) {
+  Status error;
+
+  if (!data_sp) {
+    error.SetErrorStringWithFormat(
+        "NativeRegisterContextFreeBSD_arm::%s invalid data_sp provided",
+        __FUNCTION__);
+    return error;
+  }
+
+  if (data_sp->GetByteSize() != m_reg_data.size()) {
+    error.SetErrorStringWithFormat(
+        "NativeRegisterContextFreeBSD_arm::%s data_sp contained mismatched "
+        "data size, expected %" PRIu64 ", actual %" PRIu64,
+        __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize());
+    return error;
+  }
+
+  uint8_t *src = data_sp->GetBytes();
+  if (src == nullptr) {
+    error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_arm::%s "
+                                   "DataBuffer::GetBytes() returned a null "
+                                   "pointer",
+                                   __FUNCTION__);
+    return error;
+  }
+  ::memcpy(m_reg_data.data(), src, m_reg_data.size());
+
+  error = WriteRegisterSet(RegisterInfoPOSIX_arm::GPRegSet);
+  if (error.Fail())
+    return error;
+
+  return WriteRegisterSet(RegisterInfoPOSIX_arm::FPRegSet);
+}
+
+llvm::Error NativeRegisterContextFreeBSD_arm::CopyHardwareWatchpointsFrom(
+    NativeRegisterContextFreeBSD &source) {
+  return llvm::Error::success();
+}
+
+#endif // defined (__arm__)
diff --git a/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_arm.h b/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_arm.h
new file mode 100644
index 0000000..106aa07
--- /dev/null
+++ b/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_arm.h
@@ -0,0 +1,68 @@
+//===-- NativeRegisterContextFreeBSD_arm.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(__arm__)
+
+#ifndef lldb_NativeRegisterContextFreeBSD_arm_h
+#define lldb_NativeRegisterContextFreeBSD_arm_h
+
+// clang-format off
+#include <sys/types.h>
+#include <machine/reg.h>
+#include <machine/vfp.h>
+// clang-format on
+
+#include "Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
+
+#include <array>
+
+namespace lldb_private {
+namespace process_freebsd {
+
+class NativeProcessFreeBSD;
+
+class NativeRegisterContextFreeBSD_arm : public NativeRegisterContextFreeBSD {
+public:
+  NativeRegisterContextFreeBSD_arm(const ArchSpec &target_arch,
+                                   NativeThreadProtocol &native_thread);
+
+  uint32_t GetRegisterSetCount() const override;
+
+  uint32_t GetUserRegisterCount() const override;
+
+  const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
+
+  Status ReadRegister(const RegisterInfo *reg_info,
+                      RegisterValue &reg_value) override;
+
+  Status WriteRegister(const RegisterInfo *reg_info,
+                       const RegisterValue &reg_value) override;
+
+  Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+  Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+  llvm::Error
+  CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override;
+
+private:
+  std::array<uint8_t, sizeof(reg) + sizeof(vfp_state)> m_reg_data;
+
+  Status ReadRegisterSet(uint32_t set);
+  Status WriteRegisterSet(uint32_t set);
+
+  RegisterInfoPOSIX_arm &GetRegisterInfo() const;
+};
+
+} // namespace process_freebsd
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextFreeBSD_arm_h
+
+#endif // defined (__arm__)
diff --git a/unittests/Process/Utility/RegisterContextFreeBSDTest.cpp b/unittests/Process/Utility/RegisterContextFreeBSDTest.cpp
index 083e29d..145b8b2 100644
--- a/unittests/Process/Utility/RegisterContextFreeBSDTest.cpp
+++ b/unittests/Process/Utility/RegisterContextFreeBSDTest.cpp
@@ -9,6 +9,9 @@
 // clang-format off
 #include <sys/types.h>
 #include <machine/reg.h>
+#if defined(__arm__)
+#include <machine/vfp.h>
+#endif
 // clang-format on
 
 #include "gmock/gmock.h"
@@ -17,7 +20,9 @@
 #include "Plugins/Process/Utility/lldb-x86-register-enums.h"
 #include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h"
 #include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
 #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+#include "Plugins/Process/Utility/lldb-arm-register-enums.h"
 #include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
 
 using namespace lldb;
@@ -234,6 +239,77 @@
 
 #endif // defined(__i386__) || defined(__x86_64__)
 
+#if defined(__arm__)
+
+#define EXPECT_GPR_ARM(lldb_reg, fbsd_reg)                                     \
+  EXPECT_THAT(GetRegParams(reg_ctx, gpr_##lldb_reg##_arm),                     \
+              ::testing::Pair(offsetof(reg, fbsd_reg), sizeof(reg::fbsd_reg)))
+#define EXPECT_FPU_ARM(lldb_reg, fbsd_reg)                                     \
+  EXPECT_THAT(GetRegParams(reg_ctx, fpu_##lldb_reg##_arm),                     \
+              ::testing::Pair(offsetof(vfp_state, fbsd_reg) + base_offset,     \
+                              sizeof(vfp_state::fbsd_reg)))
+
+TEST(RegisterContextFreeBSDTest, arm) {
+  ArchSpec arch{"arm-unknown-freebsd"};
+  RegisterInfoPOSIX_arm reg_ctx{arch};
+
+  EXPECT_GPR_ARM(r0, r[0]);
+  EXPECT_GPR_ARM(r1, r[1]);
+  EXPECT_GPR_ARM(r2, r[2]);
+  EXPECT_GPR_ARM(r3, r[3]);
+  EXPECT_GPR_ARM(r4, r[4]);
+  EXPECT_GPR_ARM(r5, r[5]);
+  EXPECT_GPR_ARM(r6, r[6]);
+  EXPECT_GPR_ARM(r7, r[7]);
+  EXPECT_GPR_ARM(r8, r[8]);
+  EXPECT_GPR_ARM(r9, r[9]);
+  EXPECT_GPR_ARM(r10, r[10]);
+  EXPECT_GPR_ARM(r11, r[11]);
+  EXPECT_GPR_ARM(r12, r[12]);
+  EXPECT_GPR_ARM(sp, r_sp);
+  EXPECT_GPR_ARM(lr, r_lr);
+  EXPECT_GPR_ARM(pc, r_pc);
+  EXPECT_GPR_ARM(cpsr, r_cpsr);
+
+  size_t base_offset = reg_ctx.GetRegisterInfo()[fpu_d0_arm].byte_offset;
+
+  EXPECT_FPU_ARM(d0, reg[0]);
+  EXPECT_FPU_ARM(d1, reg[1]);
+  EXPECT_FPU_ARM(d2, reg[2]);
+  EXPECT_FPU_ARM(d3, reg[3]);
+  EXPECT_FPU_ARM(d4, reg[4]);
+  EXPECT_FPU_ARM(d5, reg[5]);
+  EXPECT_FPU_ARM(d6, reg[6]);
+  EXPECT_FPU_ARM(d7, reg[7]);
+  EXPECT_FPU_ARM(d8, reg[8]);
+  EXPECT_FPU_ARM(d9, reg[9]);
+  EXPECT_FPU_ARM(d10, reg[10]);
+  EXPECT_FPU_ARM(d11, reg[11]);
+  EXPECT_FPU_ARM(d12, reg[12]);
+  EXPECT_FPU_ARM(d13, reg[13]);
+  EXPECT_FPU_ARM(d14, reg[14]);
+  EXPECT_FPU_ARM(d15, reg[15]);
+  EXPECT_FPU_ARM(d16, reg[16]);
+  EXPECT_FPU_ARM(d17, reg[17]);
+  EXPECT_FPU_ARM(d18, reg[18]);
+  EXPECT_FPU_ARM(d19, reg[19]);
+  EXPECT_FPU_ARM(d20, reg[20]);
+  EXPECT_FPU_ARM(d21, reg[21]);
+  EXPECT_FPU_ARM(d22, reg[22]);
+  EXPECT_FPU_ARM(d23, reg[23]);
+  EXPECT_FPU_ARM(d24, reg[24]);
+  EXPECT_FPU_ARM(d25, reg[25]);
+  EXPECT_FPU_ARM(d26, reg[26]);
+  EXPECT_FPU_ARM(d27, reg[27]);
+  EXPECT_FPU_ARM(d28, reg[28]);
+  EXPECT_FPU_ARM(d29, reg[29]);
+  EXPECT_FPU_ARM(d30, reg[30]);
+  EXPECT_FPU_ARM(d31, reg[31]);
+  EXPECT_FPU_ARM(fpscr, fpscr);
+}
+
+#endif // defined(__arm__)
+
 #if defined(__aarch64__)
 
 #define EXPECT_GPR_ARM64(lldb_reg, fbsd_reg)                                   \