[Lldb-commits] [lldb] [llvm] [lldb][Process/FreeBSD] Add riscv64 support (PR #180549)
Minsoo Choo via lldb-commits
lldb-commits at lists.llvm.org
Tue Feb 10 06:56:51 PST 2026
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 <minsoochoo0122 at proton.me>
Date: Wed, 28 Jan 2026 19:28:59 -0500
Subject: [PATCH 1/2] [lldb][Process/FreeBSD] Add riscv64 support
Signed-off-by: Minsoo Choo <minsoochoo0122 at proton.me>
---
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 ®_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 ®_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 ®_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;
+
+ 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 <minsoochoo0122 at proton.me>
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 <minsoochoo0122 at proton.me>
---
.../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 ®_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;
More information about the lldb-commits
mailing list