[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:24:13 PDT 2026


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

>From e13f1a215e94b336576ea72b36941046ef88d2f0 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..9ed603542e7f6
--- /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>
+#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.
+// 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..2f26cdea1ac7b
--- /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>
+#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.
+//
+// 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..9ac0d5a15f28b
--- /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>
+#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
+
+// 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..be32c38cc8baf
--- /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>
+#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;
+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