[Lldb-commits] [lldb] [lldb][FreeBSDKernel] Implement trapframe unwinding (PR #192184)
Minsoo Choo via lldb-commits
lldb-commits at lists.llvm.org
Tue Apr 14 23:32:22 PDT 2026
https://github.com/mchoo7 updated https://github.com/llvm/llvm-project/pull/192184
>From 39ff82fdc3555c6f7775eeea900ccac4d834fd4c Mon Sep 17 00:00:00 2001
From: Minsoo Choo <minsoochoo0122 at proton.me>
Date: Mon, 2 Mar 2026 10:58:30 -0500
Subject: [PATCH] [lldb][Platform] Add FreeBSD kernel plugin
Signed-off-by: Minsoo Choo <minsoochoo0122 at proton.me>
---
lldb/source/Plugins/Platform/CMakeLists.txt | 1 +
.../Platform/FreeBSD-Kernel/CMakeLists.txt | 15 +
.../FreeBSD-Kernel/PlatformFreeBSDKernel.cpp | 260 ++++++++++++++++++
.../FreeBSD-Kernel/PlatformFreeBSDKernel.h | 102 +++++++
.../TrapframeUnwindPlan_arm64.cpp | 60 ++++
.../TrapframeUnwindPlan_ppc64le.cpp | 90 ++++++
.../TrapframeUnwindPlan_riscv64.cpp | 90 ++++++
.../TrapframeUnwindPlan_x86_64.cpp | 120 ++++++++
.../ProcessFreeBSDKernelCore.cpp | 9 +
9 files changed, 747 insertions(+)
create mode 100644 lldb/source/Plugins/Platform/FreeBSD-Kernel/CMakeLists.txt
create mode 100644 lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.cpp
create mode 100644 lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.h
create mode 100644 lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_arm64.cpp
create mode 100644 lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_ppc64le.cpp
create mode 100644 lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_riscv64.cpp
create mode 100644 lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_x86_64.cpp
diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt
index cc1432aa4754b..a01666ce48305 100644
--- a/lldb/source/Plugins/Platform/CMakeLists.txt
+++ b/lldb/source/Plugins/Platform/CMakeLists.txt
@@ -8,6 +8,7 @@ set_property(DIRECTORY PROPERTY LLDB_TOLERATED_PLUGIN_DEPENDENCIES
add_subdirectory(AIX)
add_subdirectory(Android)
add_subdirectory(FreeBSD)
+add_subdirectory(FreeBSD-Kernel)
add_subdirectory(gdb-server)
add_subdirectory(Linux)
add_subdirectory(MacOSX)
diff --git a/lldb/source/Plugins/Platform/FreeBSD-Kernel/CMakeLists.txt b/lldb/source/Plugins/Platform/FreeBSD-Kernel/CMakeLists.txt
new file mode 100644
index 0000000000000..9d22f0acbe7f6
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_lldb_library(lldbPluginPlatformFreeBSDKernel PLUGIN
+ PlatformFreeBSDKernel.cpp
+ TrapframeUnwindPlan_arm64.cpp
+ TrapframeUnwindPlan_ppc64le.cpp
+ TrapframeUnwindPlan_riscv64.cpp
+ TrapframeUnwindPlan_x86_64.cpp
+
+ LINK_COMPONENTS
+ TargetParser
+ LINK_LIBS
+ lldbBreakpoint
+ lldbCore
+ lldbHost
+ lldbTarget
+ )
diff --git a/lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.cpp b/lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.cpp
new file mode 100644
index 0000000000000..42f7eed6a09ec
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.cpp
@@ -0,0 +1,260 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "PlatformFreeBSDKernel.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/TargetParser/Triple.h"
+
+#include <set>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_freebsdkernel;
+
+LLDB_PLUGIN_DEFINE(PlatformFreeBSDKernel)
+
+static uint32_t g_initialize_count = 0;
+
+PlatformFreeBSDKernel::PlatformFreeBSDKernel() : Platform(/*is_host=*/false) {
+ const llvm::Triple::ArchType arches[] = {
+ llvm::Triple::arm, // arm32 (legacy)
+ llvm::Triple::aarch64, // arm64
+ llvm::Triple::ppc64le, // powerpc64le
+ llvm::Triple::riscv64, // riscv64
+ llvm::Triple::x86, // i386 (legacy)
+ llvm::Triple::x86_64, // amd64
+ };
+
+ for (auto arch : arches) {
+ ArchSpec spec;
+ spec.SetTriple(llvm::Triple(llvm::Triple::getArchTypeName(arch), "unknown",
+ "freebsd"));
+ m_supported_architectures.push_back(spec);
+ }
+}
+
+void PlatformFreeBSDKernel::Initialize() {
+ Platform::Initialize();
+ if (g_initialize_count++ == 0) {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ nullptr);
+ }
+}
+
+void PlatformFreeBSDKernel::Terminate() {
+ if (g_initialize_count > 0) {
+ if (--g_initialize_count == 0)
+ PluginManager::UnregisterPlugin(CreateInstance);
+ }
+ Platform::Terminate();
+}
+
+lldb::PlatformSP PlatformFreeBSDKernel::CreateInstance(bool force,
+ const ArchSpec *arch) {
+ // PlatformFreeBSDKernel is never auto-selected. ProcessFreeBSDKernelCore sets
+ // this platform explicitly (force=true).
+ if (!force)
+ return nullptr;
+ return std::make_shared<PlatformFreeBSDKernel>();
+}
+
+void PlatformFreeBSDKernel::GetStatus(Stream &strm) {
+ Platform::GetStatus(strm);
+ strm.Printf(" Kernel Mode: yes\n");
+}
+
+std::vector<ArchSpec> PlatformFreeBSDKernel::GetSupportedArchitectures(
+ const ArchSpec &process_host_arch) {
+ return m_supported_architectures;
+}
+
+bool PlatformFreeBSDKernel::IsCompatibleArchitecture(
+ const ArchSpec &arch, const ArchSpec &process_host_arch,
+ ArchSpec::MatchType match, ArchSpec *compatible_arch_ptr) {
+ for (const auto &supported : m_supported_architectures) {
+ if (arch.IsCompatibleMatch(supported)) {
+ if (compatible_arch_ptr)
+ *compatible_arch_ptr = supported;
+ return true;
+ }
+ }
+ return false;
+}
+
+lldb::UnwindPlanSP
+PlatformFreeBSDKernel::GetTrapHandlerUnwindPlan(const ArchSpec &arch,
+ ConstString name) {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ return GetTrapframeUnwindPlan_arm64(name);
+ case llvm::Triple::ppc64le:
+ return GetTrapframeUnwindPlan_ppc64le(name);
+ case llvm::Triple::riscv64:
+ return GetTrapframeUnwindPlan_riscv64(name);
+ case llvm::Triple::x86_64:
+ return GetTrapframeUnwindPlan_x86_64(name);
+
+ // UnwindPlan is not implemented for the archs below as they are not
+ // expressible as a static UnwindPlan.
+ case llvm::Triple::arm:
+ // SP/LR offsets depend on saved PSR mode bits (runtime memory read
+ // required.
+ case llvm::Triple::x86:
+ // Trapframe base depends on stub identity, kernel version opcode probe, and
+ // CPL of the interrupted context.
+ default:
+ return {};
+ }
+}
+
+void PlatformFreeBSDKernel::CalculateTrapHandlerSymbolNames() {
+ // Intentionally empty. All trap handler names are populated in
+ // PopulateTrapHandlerNames() once the target architecture is known.
+ // This override exists only to suppress the default implementation.
+}
+
+void PlatformFreeBSDKernel::PopulateTrapHandlerNames(Target &target) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (m_trap_handlers_calculated)
+ return;
+ m_trap_handlers_calculated = true;
+
+ ModuleSP kernel_module = target.GetExecutableModule();
+ if (!kernel_module)
+ return;
+
+ const llvm::Triple::ArchType arch =
+ target.GetArchitecture().GetTriple().getArch();
+
+ // List trapframe sniffers from
+ // https://cgit.freebsd.org/ports/tree/devel/gdb/files/kgdb/<arch>-kern.c
+ switch (arch) {
+ case llvm::Triple::aarch64:
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ m_trap_handlers.push_back(ConstString("fork_trampoline"));
+ break;
+ default:
+ break;
+ }
+
+ switch (arch) {
+ case llvm::Triple::aarch64:
+ // From aarch64_fbsd_trapframe_sniffer in kgdb.
+ m_trap_handlers.push_back(ConstString("handle_el1h_sync"));
+ m_trap_handlers.push_back(ConstString("handle_el1h_irq"));
+ m_trap_handlers.push_back(ConstString("handle_el0_sync"));
+ m_trap_handlers.push_back(ConstString("handle_el0_irq"));
+ m_trap_handlers.push_back(ConstString("handle_el0_error"));
+ m_trap_handlers.push_back(ConstString("fork_trampoline"));
+ break;
+
+ case llvm::Triple::arm:
+ // From arm_fbsd_trapframe_sniffer in kgdb.
+ m_trap_handlers.push_back(ConstString("data_abort_entry"));
+ m_trap_handlers.push_back(ConstString("prefetch_abort_entry"));
+ m_trap_handlers.push_back(ConstString("undefined_entry"));
+ m_trap_handlers.push_back(ConstString("exception_exit"));
+ m_trap_handlers.push_back(ConstString("irq_entry"));
+ m_trap_handlers.push_back(ConstString("swi_entry"));
+ m_trap_handlers.push_back(ConstString("swi_exit"));
+ break;
+
+ case llvm::Triple::ppc64le:
+ // From ppcfbsd_trapframe_sniffer in kgdb.
+ m_trap_handlers.push_back(ConstString("trapagain"));
+ m_trap_handlers.push_back(ConstString("trapexit"));
+ m_trap_handlers.push_back(ConstString("dbtrap"));
+ break;
+
+ case llvm::Triple::riscv64:
+ // From riscv_fbsd_trapframe_sniffer in kgdb.
+ m_trap_handlers.push_back(ConstString("cpu_exception_handler_user"));
+ m_trap_handlers.push_back(ConstString("cpu_exception_handler_supervisor"));
+ break;
+
+ case llvm::Triple::x86:
+ // Fixed names from i386fbsd_trapframe_sniffer in kgdb.
+ m_trap_handlers.push_back(ConstString("calltrap"));
+ m_trap_handlers.push_back(ConstString("fork_trampoline"));
+ break;
+
+ case llvm::Triple::x86_64:
+ // Fixed names from amd64fbsd_trapframe_sniffer in kgdb.
+ m_trap_handlers.push_back(ConstString("calltrap"));
+ m_trap_handlers.push_back(ConstString("fast_syscall_common"));
+ m_trap_handlers.push_back(ConstString("fork_trampoline"));
+ m_trap_handlers.push_back(ConstString("mchk_calltrap"));
+ m_trap_handlers.push_back(ConstString("nmi_calltrap"));
+ break;
+
+ default:
+ break;
+ }
+
+ // x86 / x86_64: scan symtab for IDTVEC stubs matching kgdb heuristic:
+ // name[0] == 'X' && name[1] != '_'
+ if (arch != llvm::Triple::x86_64 && arch != llvm::Triple::x86)
+ return;
+
+ Symtab *symtab = kernel_module->GetSymtab();
+ if (!symtab)
+ return;
+
+ std::set<llvm::StringRef> existing;
+ for (const auto &cs : m_trap_handlers)
+ existing.insert(cs.GetStringRef());
+
+ const uint32_t num_syms = symtab->GetNumSymbols();
+ for (uint32_t i = 0; i < num_syms; i++) {
+ const Symbol *sym = symtab->SymbolAtIndex(i);
+ if (!sym || sym->GetType() != eSymbolTypeCode)
+ continue;
+ const char *name = sym->GetName().GetCString();
+ if (!name || name[0] != 'X' || name[1] == '\0' || name[1] == '_')
+ continue;
+ if (existing.insert(llvm::StringRef(name)).second)
+ m_trap_handlers.push_back(ConstString(name));
+ }
+}
+
+lldb::UnwindPlanSP PlatformFreeBSDKernel::BuildTrapframeUnwindPlan(
+ llvm::StringRef source_name, uint32_t cfa_dwarf_reg, int32_t cfa_offset,
+ llvm::ArrayRef<std::pair<uint32_t, int32_t>> regs) {
+ auto plan = std::make_shared<UnwindPlan>(eRegisterKindDWARF);
+ UnwindPlan::Row row;
+
+ // CFA = <cfa_reg> + cfa_offset. For most trapframes this is SP+0,
+ // meaning RSP/SP at stub entry already points at the struct base.
+ row.GetCFAValue().SetIsRegisterPlusOffset(cfa_dwarf_reg, cfa_offset);
+
+ for (auto [dwarf_reg, offset] : regs)
+ row.SetRegisterLocationToAtCFAPlusOffset(dwarf_reg, offset,
+ /*can_replace=*/true);
+
+ plan->AppendRow(row);
+ plan->SetSourceName(source_name.data());
+ plan->SetSourcedFromCompiler(eLazyBoolNo);
+ // Valid at all PCs within the stub: the trapframe is fully populated
+ // before any C function is called, so the plan applies everywhere.
+ plan->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ // Signals to RegisterContextUnwind that the frame *above* this one (i.e. the
+ // interrupted context) has its registers available from this saved trapframe.
+ plan->SetUnwindPlanForSignalTrap(eLazyBoolYes);
+ return plan;
+}
diff --git a/lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.h b/lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.h
new file mode 100644
index 0000000000000..442347410ff08
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.h
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_PLATFORM_FREEBSD_PLATFORMFREEBSDKERNEL_H
+#define LLDB_SOURCE_PLUGINS_PLATFORM_FREEBSD_PLATFORMFREEBSDKERNEL_H
+
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Platform.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <cstdint>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace lldb_private {
+namespace platform_freebsdkernel {
+
+class PlatformFreeBSDKernel : public Platform {
+public:
+ PlatformFreeBSDKernel();
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static llvm::StringRef GetPluginNameStatic() { return "freebsd-kernel"; }
+
+ static llvm::StringRef GetPluginDescriptionStatic() {
+ return "FreeBSD Kernel platform plug-in.";
+ }
+
+ // lldb_private::PluginInterface functions
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+ // lldb_private::Platform functions
+ llvm::StringRef GetDescription() override {
+ return GetPluginDescriptionStatic();
+ }
+
+ void GetStatus(Stream &strm) override;
+
+ std::vector<ArchSpec>
+ GetSupportedArchitectures(const ArchSpec &process_host_arch) override;
+
+ Status LaunchProcess(ProcessLaunchInfo &launch_info) override {
+ return Status::FromErrorString("kernel platform cannot launch processes");
+ }
+
+ bool IsCompatibleArchitecture(const ArchSpec &arch,
+ const ArchSpec &process_host_arch,
+ ArchSpec::MatchType match,
+ ArchSpec *compatible_arch_ptr) override;
+
+ bool CanDebugProcess() override { return false; }
+
+ lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
+ Target *target, Status &error) override {
+ error =
+ Status::FromErrorString("kernel platform cannot attach to processes");
+ return {};
+ }
+
+ lldb::UnwindPlanSP GetTrapHandlerUnwindPlan(const ArchSpec &arch,
+ ConstString name) override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ // Called by ProcessFreeBSDKernelCore::DoLoadCore() to populate
+ // m_trap_handlers.
+ void PopulateTrapHandlerNames(Target &target);
+
+private:
+ lldb::UnwindPlanSP
+ BuildTrapframeUnwindPlan(llvm::StringRef source_name, uint32_t cfa_dwarf_reg,
+ int32_t cfa_offset,
+ llvm::ArrayRef<std::pair<uint32_t, int32_t>> regs);
+
+ // Per-architecture unwind plan builders.
+ // Implemented in TrapframeUnwindPlan_<arch>.cpp.
+ lldb::UnwindPlanSP GetTrapframeUnwindPlan_arm64(ConstString name);
+ lldb::UnwindPlanSP GetTrapframeUnwindPlan_ppc64le(ConstString name);
+ lldb::UnwindPlanSP GetTrapframeUnwindPlan_riscv64(ConstString name);
+ lldb::UnwindPlanSP GetTrapframeUnwindPlan_x86_64(ConstString name);
+
+ std::mutex m_mutex;
+ bool m_trap_handlers_calculated = false;
+
+ std::vector<ArchSpec> m_supported_architectures;
+};
+
+} // namespace platform_freebsdkernel
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PLATFORM_FREEBSD_PLATFORMFREEBSDKERNEL_H
diff --git a/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_arm64.cpp b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_arm64.cpp
new file mode 100644
index 0000000000000..ae10a2ed897d4
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_arm64.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "PlatformFreeBSDKernel.h"
+
+#include <cstddef>
+#if defined(__FreeBSD__) && defined(__aarch64__)
+#include <machine/frame.h>
+#endif
+
+using namespace lldb_private::platform_freebsdkernel;
+
+// DWARF register numbers for AArch64.
+static constexpr uint32_t kDwarfSP = 31;
+static constexpr uint32_t kDwarfPC = 32;
+static constexpr uint32_t kDwarfLR = 30; // x30
+static constexpr uint32_t kDwarfCPSR = 33; // PSTATE / CPSR
+
+// Trapframe byte offsets.
+// struct trapframe layout (sys/arm64/include/frame.h, FreeBSD 14+):
+static constexpr int32_t kTfSP = 0;
+static constexpr int32_t kTfLR = 8;
+static constexpr int32_t kTfELR = 16; // saved PC
+static constexpr int32_t kTfSPSR = 24; // cpsr
+static constexpr int32_t kTfX0Base = 48; // tf_x[0]; tf_x[n] = kTfX0Base + n*8
+
+#if defined(__FreeBSD__) && defined(__aarch64__)
+static_assert(offsetof(struct trapframe, tf_sp) == (size_t)kTfSP,
+ "tf_sp offset mismatch");
+static_assert(offsetof(struct trapframe, tf_lr) == (size_t)kTfLR,
+ "tf_lr offset mismatch");
+static_assert(offsetof(struct trapframe, tf_elr) == (size_t)kTfELR,
+ "tf_elr offset mismatch");
+static_assert(offsetof(struct trapframe, tf_spsr) == (size_t)kTfSPSR,
+ "tf_spsr offset mismatch");
+static_assert(offsetof(struct trapframe, tf_x[0]) == (size_t)kTfX0Base,
+ "tf_x[0] offset mismatch");
+#endif
+
+lldb::UnwindPlanSP
+PlatformFreeBSDKernel::GetTrapframeUnwindPlan_arm64(ConstString name) {
+ std::vector<std::pair<uint32_t, int32_t>> regs;
+ regs.reserve(33);
+
+ regs.push_back({kDwarfSP, kTfSP});
+ regs.push_back({kDwarfLR, kTfLR});
+ regs.push_back({kDwarfPC, kTfELR});
+ regs.push_back({kDwarfCPSR, kTfSPSR});
+
+ for (int i = 0; i <= 29; i++)
+ regs.push_back({(uint32_t)i, kTfX0Base + i * 8});
+
+ return BuildTrapframeUnwindPlan("FreeBSD aarch64 trapframe", kDwarfSP, 0,
+ regs);
+}
diff --git a/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_ppc64le.cpp b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_ppc64le.cpp
new file mode 100644
index 0000000000000..0da6254c66377
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_ppc64le.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "PlatformFreeBSDKernel.h"
+
+#include <cstddef>
+#if defined(__FreeBSD__) && defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
+#include <machine/frame.h>
+#endif
+
+using namespace lldb_private::platform_freebsdkernel;
+
+// DWARF register numbers for PowerPC64 ELF ABI v2.
+static constexpr uint32_t kDwarfR1 = 1; // sp
+static constexpr uint32_t kDwarfLR = 65;
+static constexpr uint32_t kDwarfCTR = 66;
+static constexpr uint32_t kDwarfCR = 68;
+static constexpr uint32_t kDwarfXER = 76;
+
+// Trapframe slot offsets.
+//
+// struct trapframe layout (from sys/powerpc/include/frame.h):
+static constexpr int32_t kOffFixreg = 0 * 8;
+static constexpr int32_t kOffLR = 32 * 8;
+static constexpr int32_t kOffCR = 33 * 8;
+static constexpr int32_t kOffXER = 34 * 8;
+static constexpr int32_t kOffCTR = 35 * 8;
+// kOffSRR0 = 36 * 8: saved PC
+// SRR0 (the saved PC / return address from trap) has no standard DWARF
+// register number in the PowerPC ABI. LLDB maps it via the architecture
+// plugin's gdbarch_pc_regnum equivalent, but there is no portable DWARF
+// number to use here. We therefore omit PC recovery from the UnwindPlan;
+// LLDB will use LR as a fallback for the return address when PC is unknown,
+// which is the correct behaviour for kernel trap frames where LR holds the
+// pre-trap link register (not the PC).
+//
+// In practice: the frame above the trap handler will have an unknown PC,
+// but all GP registers including r1 (sp) will be correctly recovered,
+// which is sufficient for a useful backtrace.
+
+// On PowerPC, SP does NOT point at the base of struct trapframe.
+// The trapframe is above the standard linkage area on the stack:
+//
+// base = SP + 48 (48-byte ABI linkage area)
+static constexpr int32_t kPPC64LinkageArea = 48;
+
+#if defined(__FreeBSD__) && defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
+static_assert(offsetof(struct trapframe, fixreg[0]) ==
+ (size_t)(kOffFixreg + 0 * 8),
+ "fixreg[0] offset mismatch");
+static_assert(offsetof(struct trapframe, fixreg[31]) ==
+ (size_t)(kOffFixreg + 31 * 8),
+ "fixreg[31] offset mismatch");
+static_assert(offsetof(struct trapframe, lr) == (size_t)kOffLR,
+ "lr offset mismatch");
+static_assert(offsetof(struct trapframe, cr) == (size_t)kOffCR,
+ "cr offset mismatch");
+static_assert(offsetof(struct trapframe, xer) == (size_t)kOffXER,
+ "xer offset mismatch");
+static_assert(offsetof(struct trapframe, ctr) == (size_t)kOffCTR,
+ "ctr offset mismatch");
+#endif
+
+lldb::UnwindPlanSP
+PlatformFreeBSDKernel::GetTrapframeUnwindPlan_ppc64le(ConstString name) {
+ // CFA = r1 (SP) + 48. All register offsets are relative to CFA
+ // (= base of struct trapframe), matching kgdb's `base = SP + 48`.
+
+ std::vector<std::pair<uint32_t, int32_t>> regs;
+ regs.reserve(37);
+
+ // r0–r31 from tf_fixreg[].
+ for (int i = 0; i <= 31; i++)
+ regs.push_back({(uint32_t)i, kOffFixreg + i * 8});
+
+ // Special registers.
+ regs.push_back({kDwarfLR, kOffLR});
+ regs.push_back({kDwarfCR, kOffCR});
+ regs.push_back({kDwarfXER, kOffXER});
+ regs.push_back({kDwarfCTR, kOffCTR});
+ // SRR0 (PC) deliberately omitted — no standard DWARF register number.
+
+ return BuildTrapframeUnwindPlan("FreeBSD ppc64le trapframe", kDwarfR1,
+ kPPC64LinkageArea, regs);
+}
diff --git a/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_riscv64.cpp b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_riscv64.cpp
new file mode 100644
index 0000000000000..35cc515a3ef05
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_riscv64.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "PlatformFreeBSDKernel.h"
+
+#include <cstddef>
+#if defined(__FreeBSD__) && defined(__riscv)
+#include <machine/frame.h>
+#endif
+
+using namespace lldb_private::platform_freebsdkernel;
+
+// DWARF register numbers for RISC-V.
+static constexpr uint32_t kDwarfSP = 2; // x2
+static constexpr uint32_t kDwarfPC = 32; // pc
+
+// struct trapframe layout (sys/riscv/include/frame.h):
+#if defined(__FreeBSD__) && defined(__riscv)
+static_assert(offsetof(struct trapframe, tf_ra) == (size_t)(0 * 8),
+ "tf_ra offset mismatch");
+static_assert(offsetof(struct trapframe, tf_sp) == (size_t)(1 * 8),
+ "tf_sp offset mismatch");
+static_assert(offsetof(struct trapframe, tf_gp) == (size_t)(2 * 8),
+ "tf_gp offset mismatch");
+static_assert(offsetof(struct trapframe, tf_tp) == (size_t)(3 * 8),
+ "tf_tp offset mismatch");
+static_assert(offsetof(struct trapframe, tf_t[0]) == (size_t)(4 * 8),
+ "tf_t[0] offset mismatch");
+static_assert(offsetof(struct trapframe, tf_t[3]) == (size_t)(7 * 8),
+ "tf_t[3] offset mismatch");
+static_assert(offsetof(struct trapframe, tf_s[0]) == (size_t)(11 * 8),
+ "tf_s[0] offset mismatch");
+static_assert(offsetof(struct trapframe, tf_a[0]) == (size_t)(23 * 8),
+ "tf_a[0] offset mismatch");
+static_assert(offsetof(struct trapframe, tf_sepc) == (size_t)(31 * 8),
+ "tf_sepc offset mismatch");
+#endif
+
+lldb::UnwindPlanSP
+PlatformFreeBSDKernel::GetTrapframeUnwindPlan_riscv64(ConstString name) {
+ return BuildTrapframeUnwindPlan(
+ "FreeBSD riscv64 trapframe", kDwarfSP, 0,
+ {
+ // slot 0–3: ra, sp, gp, tp
+ {1, 0 * 8}, // ra (x1)
+ {2, 1 * 8}, // sp (x2) — caller's sp saved here
+ {3, 2 * 8}, // gp (x3)
+ {4, 3 * 8}, // tp (x4)
+ // slot 4–6: t0–t2
+ {5, 4 * 8}, // t0 (x5)
+ {6, 5 * 8}, // t1 (x6)
+ {7,
+ 6 * 8}, // t2 (x7)
+ // slot 7–10: t3–t6 ← BEFORE s0/s1; matches FreeBSD frame.h
+ {28, 7 * 8}, // t3 (x28)
+ {29, 8 * 8}, // t4 (x29)
+ {30, 9 * 8}, // t5 (x30)
+ {31, 10 * 8}, // t6 (x31)
+ // slot 11–12: s0 (fp), s1
+ {8, 11 * 8}, // s0/fp (x8)
+ {9, 12 * 8}, // s1 (x9)
+ // slot 13–22: s2–s11
+ {18, 13 * 8}, // s2 (x18)
+ {19, 14 * 8}, // s3 (x19)
+ {20, 15 * 8}, // s4 (x20)
+ {21, 16 * 8}, // s5 (x21)
+ {22, 17 * 8}, // s6 (x22)
+ {23, 18 * 8}, // s7 (x23)
+ {24, 19 * 8}, // s8 (x24)
+ {25, 20 * 8}, // s9 (x25)
+ {26, 21 * 8}, // s10 (x26)
+ {27, 22 * 8}, // s11 (x27)
+ // slot 23–30: a0–a7
+ {10, 23 * 8}, // a0 (x10)
+ {11, 24 * 8}, // a1 (x11)
+ {12, 25 * 8}, // a2 (x12)
+ {13, 26 * 8}, // a3 (x13)
+ {14, 27 * 8}, // a4 (x14)
+ {15, 28 * 8}, // a5 (x15)
+ {16, 29 * 8}, // a6 (x16)
+ {17, 30 * 8}, // a7 (x17)
+ // slot 31: sepc = saved PC
+ {kDwarfPC, 31 * 8},
+ });
+}
diff --git a/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_x86_64.cpp b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_x86_64.cpp
new file mode 100644
index 0000000000000..5dfe65e308a19
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_x86_64.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "PlatformFreeBSDKernel.h"
+
+#include <cstddef>
+#if defined(__FreeBSD__) && defined(__amd64__)
+#include <machine/frame.h>
+#endif
+
+using namespace lldb_private::platform_freebsdkernel;
+
+// DWARF register numbers for x86-64 (SYSV AMD64 ABI).
+static constexpr uint32_t kDwarfRAX = 0;
+static constexpr uint32_t kDwarfRDX = 1;
+static constexpr uint32_t kDwarfRCX = 2;
+static constexpr uint32_t kDwarfRBX = 3;
+static constexpr uint32_t kDwarfRSI = 4;
+static constexpr uint32_t kDwarfRDI = 5;
+static constexpr uint32_t kDwarfRBP = 6;
+static constexpr uint32_t kDwarfRSP = 7;
+static constexpr uint32_t kDwarfR8 = 8;
+static constexpr uint32_t kDwarfR9 = 9;
+static constexpr uint32_t kDwarfR10 = 10;
+static constexpr uint32_t kDwarfR11 = 11;
+static constexpr uint32_t kDwarfR12 = 12;
+static constexpr uint32_t kDwarfR13 = 13;
+static constexpr uint32_t kDwarfR14 = 14;
+static constexpr uint32_t kDwarfR15 = 15;
+static constexpr uint32_t kDwarfRIP = 16;
+static constexpr uint32_t kDwarfRFLAGS = 49;
+static constexpr uint32_t kDwarfCS = 51;
+static constexpr uint32_t kDwarfSS = 52;
+
+// Trapframe byte offsets
+//
+// struct trapframe layout (sys/x86/include/frame.h):
+static constexpr int32_t kTfRDI = 0 * 8;
+static constexpr int32_t kTfRSI = 1 * 8;
+static constexpr int32_t kTfRDX = 2 * 8;
+static constexpr int32_t kTfRCX = 3 * 8;
+static constexpr int32_t kTfR8 = 4 * 8;
+static constexpr int32_t kTfR9 = 5 * 8;
+static constexpr int32_t kTfRAX = 6 * 8;
+static constexpr int32_t kTfRBX = 7 * 8;
+static constexpr int32_t kTfRBP = 8 * 8;
+static constexpr int32_t kTfR10 = 9 * 8;
+static constexpr int32_t kTfR11 = 10 * 8;
+static constexpr int32_t kTfR12 = 11 * 8;
+static constexpr int32_t kTfR13 = 12 * 8;
+static constexpr int32_t kTfR14 = 13 * 8;
+static constexpr int32_t kTfR15 = 14 * 8;
+static constexpr int32_t kTfRIP = 19 * 8;
+static constexpr int32_t kTfCS = 20 * 8;
+static constexpr int32_t kTfRFLAGS = 21 * 8;
+static constexpr int32_t kTfRSP = 22 * 8;
+static constexpr int32_t kTfSS = 23 * 8;
+
+#if defined(__FreeBSD__) && defined(__amd64__)
+static_assert(offsetof(struct trapframe, tf_rdi) == (size_t)kTfRDI,
+ "tf_rdi offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rsi) == (size_t)kTfRSI,
+ "tf_rsi offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rdx) == (size_t)kTfRDX,
+ "tf_rdx offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rcx) == (size_t)kTfRCX,
+ "tf_rcx offset mismatch");
+static_assert(offsetof(struct trapframe, tf_r8) == (size_t)kTfR8,
+ "tf_r8 offset mismatch");
+static_assert(offsetof(struct trapframe, tf_r9) == (size_t)kTfR9,
+ "tf_r9 offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rax) == (size_t)kTfRAX,
+ "tf_rax offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rbx) == (size_t)kTfRBX,
+ "tf_rbx offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rbp) == (size_t)kTfRBP,
+ "tf_rbp offset mismatch");
+static_assert(offsetof(struct trapframe, tf_r10) == (size_t)kTfR10,
+ "tf_r10 offset mismatch");
+static_assert(offsetof(struct trapframe, tf_r11) == (size_t)kTfR11,
+ "tf_r11 offset mismatch");
+static_assert(offsetof(struct trapframe, tf_r12) == (size_t)kTfR12,
+ "tf_r12 offset mismatch");
+static_assert(offsetof(struct trapframe, tf_r13) == (size_t)kTfR13,
+ "tf_r13 offset mismatch");
+static_assert(offsetof(struct trapframe, tf_r14) == (size_t)kTfR14,
+ "tf_r14 offset mismatch");
+static_assert(offsetof(struct trapframe, tf_r15) == (size_t)kTfR15,
+ "tf_r15 offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rip) == (size_t)kTfRIP,
+ "tf_rip offset mismatch");
+static_assert(offsetof(struct trapframe, tf_cs) == (size_t)kTfCS,
+ "tf_cs offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rflags) == (size_t)kTfRFLAGS,
+ "tf_rflags offset mismatch");
+static_assert(offsetof(struct trapframe, tf_rsp) == (size_t)kTfRSP,
+ "tf_rsp offset mismatch");
+static_assert(offsetof(struct trapframe, tf_ss) == (size_t)kTfSS,
+ "tf_ss offset mismatch");
+#endif
+
+lldb::UnwindPlanSP
+PlatformFreeBSDKernel::GetTrapframeUnwindPlan_x86_64(ConstString name) {
+ return BuildTrapframeUnwindPlan(
+ "FreeBSD amd64 trapframe", kDwarfRSP, 0,
+ {
+ {kDwarfRDI, kTfRDI}, {kDwarfRSI, kTfRSI}, {kDwarfRDX, kTfRDX},
+ {kDwarfRCX, kTfRCX}, {kDwarfR8, kTfR8}, {kDwarfR9, kTfR9},
+ {kDwarfRAX, kTfRAX}, {kDwarfRBX, kTfRBX}, {kDwarfRBP, kTfRBP},
+ {kDwarfR10, kTfR10}, {kDwarfR11, kTfR11}, {kDwarfR12, kTfR12},
+ {kDwarfR13, kTfR13}, {kDwarfR14, kTfR14}, {kDwarfR15, kTfR15},
+ {kDwarfRIP, kTfRIP}, {kDwarfCS, kTfCS}, {kDwarfRFLAGS, kTfRFLAGS},
+ {kDwarfRSP, kTfRSP}, {kDwarfSS, kTfSS},
+ });
+}
diff --git a/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp b/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp
index 3ec46ea7fdf25..fbda2971ec011 100644
--- a/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp
+++ b/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp
@@ -18,11 +18,13 @@
#include "lldb/Utility/StreamString.h"
#include "Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h"
+#include "Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.h"
#include "ProcessFreeBSDKernelCore.h"
#include "ThreadFreeBSDKernelCore.h"
using namespace lldb;
using namespace lldb_private;
+using namespace lldb_private::platform_freebsdkernel;
LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernelCore)
@@ -208,6 +210,13 @@ Status ProcessFreeBSDKernelCore::DoLoadCore() {
SetKernelDisplacement();
+ PlatformSP platform_sp =
+ PlatformFreeBSDKernel::CreateInstance(/*force=*/true, nullptr);
+ GetTarget().SetPlatform(platform_sp);
+
+ static_cast<PlatformFreeBSDKernel *>(platform_sp.get())
+ ->PopulateTrapHandlerNames(GetTarget());
+
return Status();
}
More information about the lldb-commits
mailing list