[Lldb-commits] [lldb] [lldb][FreeBSDKernel] Implement trapframe unwinding (PR #192184)
Minsoo Choo via lldb-commits
lldb-commits at lists.llvm.org
Tue Apr 14 22:20:41 PDT 2026
https://github.com/mchoo7 created https://github.com/llvm/llvm-project/pull/192184
This commit implements trapframe unwinding through the new `Platform::FreeBSDKernel` plugin. Supported architectures are arm64, amd64, ppc64le, and riscv64. Support for arm32 and i386 doesn't exist for now since current static UnwindPlan mechanism isn't enough.
As different architectures expect different symbol names for trapframe unwinding, a custom function (`PopulateTrapHandlerNames`) is called instead of `CalculateTrapHandlerSymbolNames()` at the end of `DoLoadCore()` in `ProcessFreeBSDKernelCore` plugin.
Tested on arm64 and amd64.
TODO: Add mechanism to get symbol names through filters (e.g. `X_*` for x86)
TODO: Add mechanism to unwind trapframes dynamically (i.e. at frame) for arm32 and i386
Assisted-by: Claude
>From a22cded7d9ac2ac09401895b0c3d41d8d009a9f9 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 | 71 +++++
.../TrapframeUnwindPlan_ppc64le.cpp | 99 +++++++
.../TrapframeUnwindPlan_riscv64.cpp | 101 +++++++
.../TrapframeUnwindPlan_x86_64.cpp | 148 ++++++++++
.../ProcessFreeBSDKernelCore.cpp | 9 +
9 files changed, 806 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..c23b47a518239
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_arm64.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FreeBSD aarch64 struct trapframe unwinding.
+//
+// struct trapframe layout (sys/arm64/include/frame.h, FreeBSD 14+):
+// offset 0: tf_sp
+// offset 8: tf_lr — x30
+// offset 16: tf_elr — saved PC (ELR_EL1)
+// offset 24: tf_spsr — CPSR / PSTATE
+// ...
+// offset 48: tf_x[0]
+// ...
+// offset 280: tf_x[29]
+
+#include "PlatformFreeBSDKernel.h"
+
+#include <cstddef>
+#ifdef __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.
+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
+
+#ifdef __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..a0407b1d99fbc
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_ppc64le.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FreeBSD ppc64le struct trapframe unwinding.
+//
+// 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)
+//
+// struct trapframe layout (from sys/powerpc/include/frame.h):
+// offset 0: fixreg[32]; — r0..r31 (32 registers)
+// offset 256: lr — link register
+// offset 264: cr — condition register
+// offset 272: xer — XER
+// offset 280: ctr — count register
+// offset 288: srr0 — saved PC (SRR0 = machine status save/restore 0)
+
+#include "PlatformFreeBSDKernel.h"
+
+#include <cstddef>
+#ifdef __powerpc64__
+#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 (slot × 8 bytes for ppc64le).
+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.
+
+static constexpr int32_t kPPC64LinkageArea = 48;
+
+#ifdef __powerpc64__
+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..73ac7e5ce8e7f
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_riscv64.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FreeBSD riscv64 struct trapframe unwinding.
+//
+// struct trapframe layout (sys/riscv/include/frame.h):
+// offset 0: tf_ra;
+// offset 8: tf_sp;
+// offset 16: tf_gp;
+// offset 24: tf_tp;
+// offset 32: tf_t[7];
+// offset 88: tf_s[12];
+// offset 184: tf_a[8];
+// offset 248: tf_sepc; — sepc (saved PC): read sp+31*xlen for kthread
+
+#include "PlatformFreeBSDKernel.h"
+
+#include <cstddef>
+#ifdef __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
+
+#ifdef __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..0d19837cb272a
--- /dev/null
+++ b/lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_x86_64.cpp
@@ -0,0 +1,148 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FreeBSD amd64 struct trapframe unwinding.
+//
+// struct trapframe layout (sys/x86/include/frame.h):
+// offset 0 = tf_rdi
+// offset 8 = tf_rsi
+// offset 16 = tf_rdx
+// offset 24 = tf_rcx
+// offset 32 = tf_r8
+// offset 40 = tf_r9
+// offset 48 = tf_rax
+// offset 56 = tf_rbx
+// offset 64 = tf_rbp
+// offset 72 = tf_r10
+// offset 80 = tf_r11
+// offset 88 = tf_r12
+// offset 96 = tf_r13
+// offset 104 = tf_r14
+// offset 112 = tf_r15
+// ...
+// offset 152 = tf_rip
+// offset 160 = tf_cs
+// offset 168 = tf_rflags
+// offset 176 = tf_rsp — caller's stack pointer
+// offset 184 = tf_ss
+//
+// CFA = RSP + 0: at entry to every IDTVEC stub, the TRAP/TRAP_ERR macro has
+// already done "subq $TF_RIP, %rsp" and filled in all fields above tf_rip,
+// so RSP points at slot 0 = tf_rdi = base of struct trapframe.
+// For TRAP_ERR stubs the CPU also pushed the error code, but the struct layout
+// accounts for tf_err (slot 18), so offsets are uniform across all stubs.
+
+#include "PlatformFreeBSDKernel.h"
+
+#include <cstddef>
+#ifdef __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;
+
+// Trapframe byte offsets
+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;
+// slots 15-18: tf_trapno/tf_addr/tf_flags/tf_err — not GP registers, skip.
+static constexpr int32_t kTfRIP = 19 * 8; // 152 — NOT 144
+static constexpr int32_t kTfRSP = 22 * 8; // 176 — caller's RSP
+
+#ifdef __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_rsp) == (size_t)kTfRSP,
+ "tf_rsp 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},
+ {kDwarfRSP, kTfRSP}, // caller's stack pointer
+ });
+}
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