[Lldb-commits] [lldb] [lldb][FreeBSDKernel] Implement trapframe unwinding (PR #192184)

via lldb-commits lldb-commits at lists.llvm.org
Tue Apr 14 23:39:39 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb

@llvm/pr-subscribers-backend-risc-v

Author: Minsoo Choo (mchoo7)

<details>
<summary>Changes</summary>

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

---

Patch is 31.78 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/192184.diff


9 Files Affected:

- (modified) lldb/source/Plugins/Platform/CMakeLists.txt (+1) 
- (added) lldb/source/Plugins/Platform/FreeBSD-Kernel/CMakeLists.txt (+15) 
- (added) lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.cpp (+260) 
- (added) lldb/source/Plugins/Platform/FreeBSD-Kernel/PlatformFreeBSDKernel.h (+102) 
- (added) lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_arm64.cpp (+60) 
- (added) lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_ppc64le.cpp (+90) 
- (added) lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_riscv64.cpp (+90) 
- (added) lldb/source/Plugins/Platform/FreeBSD-Kernel/TrapframeUnwindPlan_x86_64.cpp (+120) 
- (modified) lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp (+9) 


``````````diff
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 m...
[truncated]

``````````

</details>


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


More information about the lldb-commits mailing list