https://github.com/mchoo7 updated 
https://github.com/llvm/llvm-project/pull/180549

>From 19d3f0817d24a365f6e37c55aa78344c0e6179c5 Mon Sep 17 00:00:00 2001
From: Minsoo Choo <[email protected]>
Date: Wed, 28 Jan 2026 19:28:59 -0500
Subject: [PATCH 1/2] [lldb][Process/FreeBSD] Add riscv64 support

Signed-off-by: Minsoo Choo <[email protected]>
---
 lldb/docs/index.rst                           |   2 +-
 .../Plugins/Process/FreeBSD/CMakeLists.txt    |   1 +
 .../NativeRegisterContextFreeBSD_riscv64.cpp  | 559 ++++++++++++++++++
 .../NativeRegisterContextFreeBSD_riscv64.h    |  81 +++
 .../Process/elf-core/ThreadElfCore.cpp        |   2 +
 llvm/docs/ReleaseNotes.md                     |   1 +
 6 files changed, 645 insertions(+), 1 deletion(-)
 create mode 100644 
lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.cpp
 create mode 100644 
lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.h

diff --git a/lldb/docs/index.rst b/lldb/docs/index.rst
index 10683c7593b01..53a37cb462a1a 100644
--- a/lldb/docs/index.rst
+++ b/lldb/docs/index.rst
@@ -73,7 +73,7 @@ are welcome:
 * iOS, tvOS, and watchOS simulator debugging on i386, x86_64 and AArch64
 * iOS, tvOS, and watchOS device debugging on ARM and AArch64
 * Linux user-space debugging for i386, x86_64, ARM, AArch64, PPC64le, s390x
-* FreeBSD user-space debugging for i386, x86_64, ARM, AArch64, PPC
+* FreeBSD user-space debugging for i386, x86_64, ARM, AArch64, PPC, RISCV64
 * FreeBSD kernel debugging for i386, x86_64, AArch64
 * NetBSD user-space debugging for i386 and x86_64
 * Windows user-space debugging for i386, x86_64, ARM and AArch64 (*)
diff --git a/lldb/source/Plugins/Process/FreeBSD/CMakeLists.txt 
b/lldb/source/Plugins/Process/FreeBSD/CMakeLists.txt
index 8574df58b4ada..b7b4b969dbfe0 100644
--- a/lldb/source/Plugins/Process/FreeBSD/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/FreeBSD/CMakeLists.txt
@@ -4,6 +4,7 @@ add_lldb_library(lldbPluginProcessFreeBSD
   NativeRegisterContextFreeBSD_arm.cpp
   NativeRegisterContextFreeBSD_arm64.cpp
   NativeRegisterContextFreeBSD_powerpc.cpp
+  NativeRegisterContextFreeBSD_riscv64.cpp
   NativeRegisterContextFreeBSD_x86_64.cpp
   NativeThreadFreeBSD.cpp
 
diff --git 
a/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.cpp 
b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.cpp
new file mode 100644
index 0000000000000..3464d0a377bd5
--- /dev/null
+++ 
b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.cpp
@@ -0,0 +1,559 @@
+//===-- NativeRegisterContextFreeBSD_riscv64.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(__riscv) && __riscv_xlen == 64
+
+#include "NativeRegisterContextFreeBSD_riscv64.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.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;
+
+// Translation between RegisterInfoPosix_riscv64 and reg.h
+// https://github.com/freebsd/freebsd-src/blob/main/sys/riscv/include/reg.h:
+//
+// struct reg {
+//     __uint64_t      ra;             /* return address */
+//     __uint64_t      sp;             /* stack pointer */
+//     __uint64_t      gp;             /* global pointer */
+//     __uint64_t      tp;             /* thread pointer */
+//     __uint64_t      t[7];           /* temporaries */
+//     __uint64_t      s[12];          /* saved registers */
+//     __uint64_t      a[8];           /* function arguments */
+//     __uint64_t      sepc;           /* exception program counter */
+//     __uint64_t      sstatus;        /* status register */
+// };
+//
+// struct fpreg {
+//     __uint64_t      fp_x[32][2];    /* Floating point registers */
+//     __uint64_t      fp_fcsr;        /* Floating point control reg */
+// };
+//
+// struct dbreg {
+//     int dummy;
+// };
+
+// ============================================================================
+// Static Conversion Functions between FreeBSD and POSIX
+// ============================================================================
+
+void NativeRegisterContextFreeBSD_riscv64::FreeBSDToPOSIXGPR(
+    const struct reg &freebsd_gpr, RegisterInfoPOSIX_riscv64::GPR &posix_gpr) {
+  posix_gpr.gpr[gpr_pc_riscv64] = freebsd_gpr.sepc; // x0/pc
+  posix_gpr.gpr[gpr_ra_riscv64] = freebsd_gpr.ra;   // x1/ra
+  posix_gpr.gpr[gpr_sp_riscv64] = freebsd_gpr.sp;   // x2/sp
+  posix_gpr.gpr[gpr_gp_riscv64] = freebsd_gpr.gp;   // x3/gp
+  posix_gpr.gpr[gpr_tp_riscv64] = freebsd_gpr.tp;   // x4/tp
+
+  // x5-x7: t0-t2
+  posix_gpr.gpr[gpr_t0_riscv64] = freebsd_gpr.t[0];
+  posix_gpr.gpr[gpr_t1_riscv64] = freebsd_gpr.t[1];
+  posix_gpr.gpr[gpr_t2_riscv64] = freebsd_gpr.t[2];
+
+  // x8-x9: s0-s1 (s0 is also fp)
+  posix_gpr.gpr[gpr_s0_riscv64] = freebsd_gpr.s[0];
+  posix_gpr.gpr[gpr_s1_riscv64] = freebsd_gpr.s[1];
+
+  // x10-x17: a0-a7
+  for (int i = 0; i < 8; i++)
+    posix_gpr.gpr[gpr_a0_riscv64 + i] = freebsd_gpr.a[i];
+
+  // x18-x27: s2-s11
+  for (int i = 0; i < 10; i++)
+    posix_gpr.gpr[gpr_s2_riscv64 + i] = freebsd_gpr.s[2 + i];
+
+  // x28-x31: t3-t6
+  posix_gpr.gpr[gpr_t3_riscv64] = freebsd_gpr.t[3];
+  posix_gpr.gpr[gpr_t4_riscv64] = freebsd_gpr.t[4];
+  posix_gpr.gpr[gpr_t5_riscv64] = freebsd_gpr.t[5];
+  posix_gpr.gpr[gpr_t6_riscv64] = freebsd_gpr.t[6];
+}
+
+void NativeRegisterContextFreeBSD_riscv64::POSIXToFreeBSDGPR(
+    const RegisterInfoPOSIX_riscv64::GPR &posix_gpr, struct reg &freebsd_gpr) {
+  freebsd_gpr.sepc = posix_gpr.gpr[gpr_pc_riscv64]; // x0/pc
+  freebsd_gpr.ra = posix_gpr.gpr[gpr_ra_riscv64];   // x1/ra
+  freebsd_gpr.sp = posix_gpr.gpr[gpr_sp_riscv64];   // x2/sp
+  freebsd_gpr.gp = posix_gpr.gpr[gpr_gp_riscv64];   // x3/gp
+  freebsd_gpr.tp = posix_gpr.gpr[gpr_tp_riscv64];   // x4/tp
+
+  // x5-x7: t0-t2
+  freebsd_gpr.t[0] = posix_gpr.gpr[gpr_t0_riscv64];
+  freebsd_gpr.t[1] = posix_gpr.gpr[gpr_t1_riscv64];
+  freebsd_gpr.t[2] = posix_gpr.gpr[gpr_t2_riscv64];
+
+  // x8-x9: s0-s1
+  freebsd_gpr.s[0] = posix_gpr.gpr[gpr_s0_riscv64];
+  freebsd_gpr.s[1] = posix_gpr.gpr[gpr_s1_riscv64];
+
+  // x10-x17: a0-a7
+  for (int i = 0; i < 8; i++)
+    freebsd_gpr.a[i] = posix_gpr.gpr[gpr_a0_riscv64 + i];
+
+  // x18-x27: s2-s11
+  for (int i = 0; i < 10; i++)
+    freebsd_gpr.s[2 + i] = posix_gpr.gpr[gpr_s2_riscv64 + i];
+
+  // x28-x31: t3-t6
+  freebsd_gpr.t[3] = posix_gpr.gpr[gpr_t3_riscv64];
+  freebsd_gpr.t[4] = posix_gpr.gpr[gpr_t4_riscv64];
+  freebsd_gpr.t[5] = posix_gpr.gpr[gpr_t5_riscv64];
+  freebsd_gpr.t[6] = posix_gpr.gpr[gpr_t6_riscv64];
+}
+
+void NativeRegisterContextFreeBSD_riscv64::FreeBSDToPOSIXFPR(
+    const struct fpreg &freebsd_fpr,
+    RegisterInfoPOSIX_riscv64::FPR &posix_fpr) {
+  // FreeBSD stores FP registers as 128-bit (fp_x[32][2])
+  // POSIX expects 64-bit (fpr[32])
+  // We only use the lower 64 bits (D extension, double precision)
+  for (int i = 0; i < 32; i++)
+    posix_fpr.fpr[i] = freebsd_fpr.fp_x[i][0];
+
+  // FCSR: FreeBSD has 64-bit, POSIX expects 32-bit
+  posix_fpr.fcsr = static_cast<uint32_t>(freebsd_fpr.fp_fcsr);
+}
+
+void NativeRegisterContextFreeBSD_riscv64::POSIXToFreeBSDFPR(
+    const RegisterInfoPOSIX_riscv64::FPR &posix_fpr,
+    struct fpreg &freebsd_fpr) {
+  // POSIX has 64-bit FP registers, FreeBSD expects 128-bit
+  for (int i = 0; i < 32; i++) {
+    freebsd_fpr.fp_x[i][0] = posix_fpr.fpr[i]; // Lower 64 bits
+    freebsd_fpr.fp_x[i][1] = 0;                // Upper 64 bits (unused for D)
+  }
+
+  // FCSR: POSIX has 32-bit, FreeBSD expects 64-bit
+  freebsd_fpr.fp_fcsr = static_cast<uint64_t>(posix_fpr.fcsr);
+}
+
+// ============================================================================
+// Constructor and Setup
+// ============================================================================
+
+NativeRegisterContextFreeBSD *
+NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD(
+    const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) {
+  return new NativeRegisterContextFreeBSD_riscv64(target_arch, native_thread);
+}
+
+NativeRegisterContextFreeBSD_riscv64::NativeRegisterContextFreeBSD_riscv64(
+    const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread)
+    : NativeRegisterContextFreeBSD(native_thread), m_gpr(), m_fpr(),
+      m_gpr_is_valid(false), m_fpr_is_valid(false) {
+  m_register_info_interface_up =
+      std::make_unique<RegisterInfoPOSIX_riscv64>(target_arch, 0);
+
+  ::memset(&m_gpr, 0, sizeof(m_gpr));
+  ::memset(&m_fpr, 0, sizeof(m_fpr));
+}
+
+RegisterInfoPOSIX_riscv64 &
+NativeRegisterContextFreeBSD_riscv64::GetRegisterInfo() const {
+  return static_cast<RegisterInfoPOSIX_riscv64 &>(
+      *m_register_info_interface_up);
+}
+
+// ============================================================================
+// Register Set Information
+// ============================================================================
+
+uint32_t NativeRegisterContextFreeBSD_riscv64::GetRegisterSetCount() const {
+  return GetRegisterInfo().GetRegisterSetCount();
+}
+
+const RegisterSet *
+NativeRegisterContextFreeBSD_riscv64::GetRegisterSet(uint32_t set_index) const 
{
+  return GetRegisterInfo().GetRegisterSet(set_index);
+}
+
+uint32_t NativeRegisterContextFreeBSD_riscv64::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;
+}
+
+void NativeRegisterContextFreeBSD_riscv64::InvalidateAllRegisters() {
+  m_gpr_is_valid = false;
+  m_fpr_is_valid = false;
+}
+
+// ============================================================================
+// Ptrace Wrappers
+// ============================================================================
+
+Status NativeRegisterContextFreeBSD_riscv64::ReadGPR() {
+  if (m_gpr_is_valid)
+    return Status();
+
+  Status error =
+      NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), 
&m_gpr);
+
+  if (error.Success())
+    m_gpr_is_valid = true;
+
+  return error;
+}
+
+Status NativeRegisterContextFreeBSD_riscv64::WriteGPR() {
+  Status error =
+      NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), 
&m_gpr);
+
+  if (error.Success())
+    m_gpr_is_valid = true;
+
+  return error;
+}
+
+Status NativeRegisterContextFreeBSD_riscv64::ReadFPR() {
+  if (m_fpr_is_valid)
+    return Status();
+
+  Status error = NativeProcessFreeBSD::PtraceWrapper(PT_GETFPREGS,
+                                                     m_thread.GetID(), &m_fpr);
+
+  if (error.Success())
+    m_fpr_is_valid = true;
+
+  return error;
+}
+
+Status NativeRegisterContextFreeBSD_riscv64::WriteFPR() {
+  Status error = NativeProcessFreeBSD::PtraceWrapper(PT_SETFPREGS,
+                                                     m_thread.GetID(), &m_fpr);
+
+  if (error.Success())
+    m_fpr_is_valid = true;
+
+  return error;
+}
+
+// ============================================================================
+// Single Register Access Helpers
+// ============================================================================
+
+Status
+NativeRegisterContextFreeBSD_riscv64::GetGPRValue(uint32_t reg_index,
+                                                  uint64_t &value) const {
+  // Map LLDB register index to FreeBSD struct reg field
+  switch (reg_index) {
+  case gpr_pc_riscv64:
+    value = m_gpr.sepc;
+    return Status();
+  case gpr_ra_riscv64:
+    value = m_gpr.ra;
+    return Status();
+  case gpr_sp_riscv64:
+    value = m_gpr.sp;
+    return Status();
+  case gpr_gp_riscv64:
+    value = m_gpr.gp;
+    return Status();
+  case gpr_tp_riscv64:
+    value = m_gpr.tp;
+    return Status();
+
+  // t0-t6
+  case gpr_t0_riscv64:
+  case gpr_t1_riscv64:
+  case gpr_t2_riscv64:
+    value = m_gpr.t[reg_index - gpr_t0_riscv64];
+    return Status();
+  case gpr_t3_riscv64:
+  case gpr_t4_riscv64:
+  case gpr_t5_riscv64:
+  case gpr_t6_riscv64:
+    value = m_gpr.t[reg_index - gpr_t3_riscv64 + 3];
+    return Status();
+
+  // s0-s11
+  case gpr_s0_riscv64:
+  case gpr_s1_riscv64:
+    value = m_gpr.s[reg_index - gpr_s0_riscv64];
+    return Status();
+  case gpr_s2_riscv64:
+  case gpr_s3_riscv64:
+  case gpr_s4_riscv64:
+  case gpr_s5_riscv64:
+  case gpr_s6_riscv64:
+  case gpr_s7_riscv64:
+  case gpr_s8_riscv64:
+  case gpr_s9_riscv64:
+  case gpr_s10_riscv64:
+  case gpr_s11_riscv64:
+    value = m_gpr.s[reg_index - gpr_s2_riscv64 + 2];
+    return Status();
+
+  // a0-a7
+  case gpr_a0_riscv64:
+  case gpr_a1_riscv64:
+  case gpr_a2_riscv64:
+  case gpr_a3_riscv64:
+  case gpr_a4_riscv64:
+  case gpr_a5_riscv64:
+  case gpr_a6_riscv64:
+  case gpr_a7_riscv64:
+    value = m_gpr.a[reg_index - gpr_a0_riscv64];
+    return Status();
+
+  default:
+    return Status::FromErrorStringWithFormat("invalid GPR register index: %u",
+                                             reg_index);
+  }
+}
+
+Status NativeRegisterContextFreeBSD_riscv64::SetGPRValue(uint32_t reg_index,
+                                                         uint64_t value) {
+  switch (reg_index) {
+  case gpr_pc_riscv64:
+    m_gpr.sepc = value;
+    return Status();
+  case gpr_ra_riscv64:
+    m_gpr.ra = value;
+    return Status();
+  case gpr_sp_riscv64:
+    m_gpr.sp = value;
+    return Status();
+  case gpr_gp_riscv64:
+    m_gpr.gp = value;
+    return Status();
+  case gpr_tp_riscv64:
+    m_gpr.tp = value;
+    return Status();
+
+  case gpr_t0_riscv64:
+  case gpr_t1_riscv64:
+  case gpr_t2_riscv64:
+    m_gpr.t[reg_index - gpr_t0_riscv64] = value;
+    return Status();
+  case gpr_t3_riscv64:
+  case gpr_t4_riscv64:
+  case gpr_t5_riscv64:
+  case gpr_t6_riscv64:
+    m_gpr.t[reg_index - gpr_t3_riscv64 + 3] = value;
+    return Status();
+
+  case gpr_s0_riscv64:
+  case gpr_s1_riscv64:
+    m_gpr.s[reg_index - gpr_s0_riscv64] = value;
+    return Status();
+  case gpr_s2_riscv64:
+  case gpr_s3_riscv64:
+  case gpr_s4_riscv64:
+  case gpr_s5_riscv64:
+  case gpr_s6_riscv64:
+  case gpr_s7_riscv64:
+  case gpr_s8_riscv64:
+  case gpr_s9_riscv64:
+  case gpr_s10_riscv64:
+  case gpr_s11_riscv64:
+    m_gpr.s[reg_index - gpr_s2_riscv64 + 2] = value;
+    return Status();
+
+  case gpr_a0_riscv64:
+  case gpr_a1_riscv64:
+  case gpr_a2_riscv64:
+  case gpr_a3_riscv64:
+  case gpr_a4_riscv64:
+  case gpr_a5_riscv64:
+  case gpr_a6_riscv64:
+  case gpr_a7_riscv64:
+    m_gpr.a[reg_index - gpr_a0_riscv64] = value;
+    return Status();
+
+  default:
+    return Status::FromErrorStringWithFormat("invalid GPR register index: %u",
+                                             reg_index);
+  }
+}
+
+// ============================================================================
+// Single Register Read/Write
+// ============================================================================
+
+Status
+NativeRegisterContextFreeBSD_riscv64::ReadRegister(const RegisterInfo 
*reg_info,
+                                                   RegisterValue &reg_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>");
+
+  if (GetRegisterInfo().IsGPR(reg)) {
+    Status error = ReadGPR();
+    if (error.Fail())
+      return error;
+
+    uint64_t value;
+    error = GetGPRValue(reg, value);
+    if (error.Fail())
+      return error;
+
+    reg_value = value;
+    return Status();
+  }
+
+  if (GetRegisterInfo().IsFPR(reg)) {
+    Status error = ReadFPR();
+    if (error.Fail())
+      return error;
+
+    uint32_t fpr_index =
+        reg - 
GetRegisterInfo().GetRegisterInfo()[reg].kinds[eRegisterKindLLDB];
+
+    if (fpr_index < 32) {
+      reg_value = m_fpr.fp_x[fpr_index][0]; // Lower 64 bits
+    } else if (fpr_index == 32) {
+      reg_value = static_cast<uint32_t>(m_fpr.fp_fcsr);
+    } else {
+      return Status::FromErrorString("invalid FPR index");
+    }
+
+    return Status();
+  }
+
+  return Status::FromErrorString("unsupported register type");
+}
+
+Status NativeRegisterContextFreeBSD_riscv64::WriteRegister(
+    const RegisterInfo *reg_info, const RegisterValue &reg_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>");
+
+  if (GetRegisterInfo().IsGPR(reg)) {
+    Status error = ReadGPR(); // Read first to preserve other registers
+    if (error.Fail())
+      return error;
+
+    error = SetGPRValue(reg, reg_value.GetAsUInt64());
+    if (error.Fail())
+      return error;
+
+    return WriteGPR();
+  }
+
+  if (GetRegisterInfo().IsFPR(reg)) {
+    Status error = ReadFPR();
+    if (error.Fail())
+      return error;
+
+    uint32_t fpr_index =
+        reg - 
GetRegisterInfo().GetRegisterInfo()[reg].kinds[eRegisterKindLLDB];
+
+    if (fpr_index < 32) {
+      m_fpr.fp_x[fpr_index][0] = reg_value.GetAsUInt64();
+      m_fpr.fp_x[fpr_index][1] = 0;
+    } else if (fpr_index == 32) {
+      m_fpr.fp_fcsr = reg_value.GetAsUInt32();
+    } else {
+      return Status::FromErrorString("invalid FPR index");
+    }
+
+    return WriteFPR();
+  }
+
+  return Status::FromErrorString("unsupported register type");
+}
+
+// ============================================================================
+// Bulk Register Read/Write (using conversion functions)
+// ============================================================================
+
+Status NativeRegisterContextFreeBSD_riscv64::ReadAllRegisterValues(
+    lldb::WritableDataBufferSP &data_sp) {
+  // Read from kernel
+  Status error = ReadGPR();
+  if (error.Fail())
+    return error;
+
+  error = ReadFPR();
+  if (error.Fail())
+    return error;
+
+  // Allocate buffer for POSIX format
+  const size_t total_size = sizeof(RegisterInfoPOSIX_riscv64::GPR) +
+                            sizeof(RegisterInfoPOSIX_riscv64::FPR);
+  data_sp = std::make_shared<DataBufferHeap>(total_size, 0);
+  if (!data_sp || !data_sp->GetBytes())
+    return Status::FromErrorString("failed to allocate data buffer");
+
+  // Get pointers to GPR and FPR sections of buffer
+  auto *gpr_dst =
+      reinterpret_cast<RegisterInfoPOSIX_riscv64::GPR *>(data_sp->GetBytes());
+  auto *fpr_dst = reinterpret_cast<RegisterInfoPOSIX_riscv64::FPR *>(
+      data_sp->GetBytes() + sizeof(RegisterInfoPOSIX_riscv64::GPR));
+
+  // Convert FreeBSD format to POSIX format
+  FreeBSDToPOSIXGPR(m_gpr, *gpr_dst);
+  FreeBSDToPOSIXFPR(m_fpr, *fpr_dst);
+
+  return Status();
+}
+
+Status NativeRegisterContextFreeBSD_riscv64::WriteAllRegisterValues(
+    const lldb::DataBufferSP &data_sp) {
+  if (!data_sp)
+    return Status::FromErrorString("invalid data_sp provided");
+
+  const size_t expected_size = sizeof(RegisterInfoPOSIX_riscv64::GPR) +
+                               sizeof(RegisterInfoPOSIX_riscv64::FPR);
+
+  if (data_sp->GetByteSize() != expected_size) {
+    return Status::FromErrorStringWithFormat(
+        "data_sp size mismatch, expected %zu, actual %" PRIu64, expected_size,
+        data_sp->GetByteSize());
+  }
+
+  const uint8_t *src = data_sp->GetBytes();
+  if (!src)
+    return Status::FromErrorString("DataBuffer::GetBytes() returned null");
+
+  // Get pointers to GPR and FPR sections of buffer
+  const auto *gpr_src =
+      reinterpret_cast<const RegisterInfoPOSIX_riscv64::GPR *>(src);
+  const auto *fpr_src =
+      reinterpret_cast<const RegisterInfoPOSIX_riscv64::FPR *>(
+          src + sizeof(RegisterInfoPOSIX_riscv64::GPR));
+
+  // Convert POSIX format to FreeBSD format
+  POSIXToFreeBSDGPR(*gpr_src, m_gpr);
+  POSIXToFreeBSDFPR(*fpr_src, m_fpr);
+
+  // Write to kernel
+  Status error = WriteGPR();
+  if (error.Fail())
+    return error;
+
+  return WriteFPR();
+}
+
+#endif // defined(__riscv) && __riscv_xlen == 64
diff --git 
a/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.h 
b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.h
new file mode 100644
index 0000000000000..cd8d72cc60773
--- /dev/null
+++ b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.h
@@ -0,0 +1,81 @@
+//===-- NativeRegisterContextFreeBSD_riscv64.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(__riscv) && __riscv_xlen == 64
+
+#ifndef lldb_NativeRegisterContextFreeBSD_riscv64_h
+#define lldb_NativeRegisterContextFreeBSD_riscv64_h
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/param.h>
+#include <machine/reg.h>
+// clang-format on
+
+#include "Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h"
+
+namespace lldb_private {
+namespace process_freebsd {
+
+class NativeRegisterContextFreeBSD_riscv64
+    : public NativeRegisterContextFreeBSD {
+public:
+  NativeRegisterContextFreeBSD_riscv64(const ArchSpec &target_arch,
+                                       NativeThreadFreeBSD &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::WritableDataBufferSP &data_sp) override;
+  Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+  void InvalidateAllRegisters() override;
+
+private:
+  // FreeBSD's native register structures
+  struct reg m_gpr;
+  struct fpreg m_fpr;
+
+  bool m_gpr_is_valid;
+  bool m_fpr_is_valid;
+
+  // Ptrace wrappers
+  Status ReadGPR();
+  Status WriteGPR();
+  Status ReadFPR();
+  Status WriteFPR();
+
+  // Conversion functions between FreeBSD and POSIX layouts
+  static void FreeBSDToPOSIXGPR(const struct reg &freebsd_gpr,
+                                RegisterInfoPOSIX_riscv64::GPR &posix_gpr);
+  static void POSIXToFreeBSDGPR(const RegisterInfoPOSIX_riscv64::GPR 
&posix_gpr,
+                                struct reg &freebsd_gpr);
+  static void FreeBSDToPOSIXFPR(const struct fpreg &freebsd_fpr,
+                                RegisterInfoPOSIX_riscv64::FPR &posix_fpr);
+  static void POSIXToFreeBSDFPR(const RegisterInfoPOSIX_riscv64::FPR 
&posix_fpr,
+                                struct fpreg &freebsd_fpr);
+
+  // Single register conversion helpers
+  Status GetGPRValue(uint32_t reg_index, uint64_t &value) const;
+  Status SetGPRValue(uint32_t reg_index, uint64_t value);
+
+  RegisterInfoPOSIX_riscv64 &GetRegisterInfo() const;
+};
+
+} // namespace process_freebsd
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextFreeBSD_riscv64_h
+#endif // defined(__riscv) && __riscv_xlen == 64
diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp 
b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
index b51d34a2614f1..fbf85012430bf 100644
--- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
@@ -97,6 +97,8 @@ ThreadElfCore::CreateRegisterContextForFrame(StackFrame 
*frame) {
       case llvm::Triple::ppc64le:
         reg_interface = new RegisterContextFreeBSD_powerpc64(arch);
         break;
+      case llvm::Triple::riscv64:
+        break;
       case llvm::Triple::x86:
         reg_interface = new RegisterContextFreeBSD_i386(arch);
         break;
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 8dabcecf3ece8..2fe27f25ef896 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -180,6 +180,7 @@ Changes to LLDB
 * Support for FreeBSD on MIPS64 has been removed.
 * The minimum assumed version of FreeBSD is now 14. The effect of which is 
that watchpoints are
   assumed to be supported.
+* Support for FreeBSD on RISCV64 has been added.
 
 Changes to BOLT
 ---------------

>From a156f42b7cea44e7c8f4a50a0b6d198d34f8070d Mon Sep 17 00:00:00 2001
From: Minsoo Choo <[email protected]>
Date: Tue, 10 Feb 2026 09:43:03 -0500
Subject: [PATCH 2/2] fixup! [lldb][Process/FreeBSD] Add riscv64 support

Signed-off-by: Minsoo Choo <[email protected]>
---
 .../NativeRegisterContextFreeBSD_riscv64.cpp  | 48 ++++---------------
 1 file changed, 10 insertions(+), 38 deletions(-)

diff --git 
a/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.cpp 
b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.cpp
index 3464d0a377bd5..06d875226c557 100644
--- 
a/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.cpp
+++ 
b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_riscv64.cpp
@@ -28,7 +28,7 @@ using namespace lldb_private;
 using namespace lldb_private::process_freebsd;
 
 // Translation between RegisterInfoPosix_riscv64 and reg.h
-// https://github.com/freebsd/freebsd-src/blob/main/sys/riscv/include/reg.h:
+// https://cgit.freebsd.org/src/tree/sys/riscv/include/reg.h:
 //
 // struct reg {
 //     __uint64_t      ra;             /* return address */
@@ -51,10 +51,6 @@ using namespace lldb_private::process_freebsd;
 //     int dummy;
 // };
 
-// ============================================================================
-// Static Conversion Functions between FreeBSD and POSIX
-// ============================================================================
-
 void NativeRegisterContextFreeBSD_riscv64::FreeBSDToPOSIXGPR(
     const struct reg &freebsd_gpr, RegisterInfoPOSIX_riscv64::GPR &posix_gpr) {
   posix_gpr.gpr[gpr_pc_riscv64] = freebsd_gpr.sepc; // x0/pc
@@ -145,10 +141,6 @@ void 
NativeRegisterContextFreeBSD_riscv64::POSIXToFreeBSDFPR(
   freebsd_fpr.fp_fcsr = static_cast<uint64_t>(posix_fpr.fcsr);
 }
 
-// ============================================================================
-// Constructor and Setup
-// ============================================================================
-
 NativeRegisterContextFreeBSD *
 NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD(
     const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) {
@@ -172,10 +164,6 @@ NativeRegisterContextFreeBSD_riscv64::GetRegisterInfo() 
const {
       *m_register_info_interface_up);
 }
 
-// ============================================================================
-// Register Set Information
-// ============================================================================
-
 uint32_t NativeRegisterContextFreeBSD_riscv64::GetRegisterSetCount() const {
   return GetRegisterInfo().GetRegisterSetCount();
 }
@@ -197,10 +185,6 @@ void 
NativeRegisterContextFreeBSD_riscv64::InvalidateAllRegisters() {
   m_fpr_is_valid = false;
 }
 
-// ============================================================================
-// Ptrace Wrappers
-// ============================================================================
-
 Status NativeRegisterContextFreeBSD_riscv64::ReadGPR() {
   if (m_gpr_is_valid)
     return Status();
@@ -247,10 +231,6 @@ Status NativeRegisterContextFreeBSD_riscv64::WriteFPR() {
   return error;
 }
 
-// ============================================================================
-// Single Register Access Helpers
-// ============================================================================
-
 Status
 NativeRegisterContextFreeBSD_riscv64::GetGPRValue(uint32_t reg_index,
                                                   uint64_t &value) const {
@@ -386,10 +366,6 @@ Status 
NativeRegisterContextFreeBSD_riscv64::SetGPRValue(uint32_t reg_index,
   }
 }
 
-// ============================================================================
-// Single Register Read/Write
-// ============================================================================
-
 Status
 NativeRegisterContextFreeBSD_riscv64::ReadRegister(const RegisterInfo 
*reg_info,
                                                    RegisterValue &reg_value) {
@@ -424,18 +400,18 @@ NativeRegisterContextFreeBSD_riscv64::ReadRegister(const 
RegisterInfo *reg_info,
     uint32_t fpr_index =
         reg - 
GetRegisterInfo().GetRegisterInfo()[reg].kinds[eRegisterKindLLDB];
 
-    if (fpr_index < 32) {
+    if (fpr_index < 32)
       reg_value = m_fpr.fp_x[fpr_index][0]; // Lower 64 bits
-    } else if (fpr_index == 32) {
+    else if (fpr_index == 32)
       reg_value = static_cast<uint32_t>(m_fpr.fp_fcsr);
-    } else {
+    else
       return Status::FromErrorString("invalid FPR index");
-    }
 
     return Status();
   }
 
-  return Status::FromErrorString("unsupported register type");
+  return Status::FromErrorStringWithFormat("unsupported register type: %u",
+                                           reg);
 }
 
 Status NativeRegisterContextFreeBSD_riscv64::WriteRegister(
@@ -481,16 +457,12 @@ Status 
NativeRegisterContextFreeBSD_riscv64::WriteRegister(
     return WriteFPR();
   }
 
-  return Status::FromErrorString("unsupported register type");
+  return Status::FromErrorStringWithFormat("unsupported register type: %u",
+                                           reg);
 }
 
-// ============================================================================
-// Bulk Register Read/Write (using conversion functions)
-// ============================================================================
-
 Status NativeRegisterContextFreeBSD_riscv64::ReadAllRegisterValues(
     lldb::WritableDataBufferSP &data_sp) {
-  // Read from kernel
   Status error = ReadGPR();
   if (error.Fail())
     return error;
@@ -504,7 +476,8 @@ Status 
NativeRegisterContextFreeBSD_riscv64::ReadAllRegisterValues(
                             sizeof(RegisterInfoPOSIX_riscv64::FPR);
   data_sp = std::make_shared<DataBufferHeap>(total_size, 0);
   if (!data_sp || !data_sp->GetBytes())
-    return Status::FromErrorString("failed to allocate data buffer");
+    return Status::FromErrorString(
+        "failed to allocate data buffer for POSIX-layout register data");
 
   // Get pointers to GPR and FPR sections of buffer
   auto *gpr_dst =
@@ -548,7 +521,6 @@ Status 
NativeRegisterContextFreeBSD_riscv64::WriteAllRegisterValues(
   POSIXToFreeBSDGPR(*gpr_src, m_gpr);
   POSIXToFreeBSDFPR(*fpr_src, m_fpr);
 
-  // Write to kernel
   Status error = WriteGPR();
   if (error.Fail())
     return error;

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to