[Lldb-commits] [lldb] [lldb] Handle non-zero offsets from CFA when computing initial SP value (PR #192752)
Sergei Barannikov via lldb-commits
lldb-commits at lists.llvm.org
Fri Apr 17 16:08:03 PDT 2026
https://github.com/s-barannikov created https://github.com/llvm/llvm-project/pull/192752
Some targets (for example, X86 and MSP430) push the return address on the stack when calling a function, and have CFA=SP+offset initial rule. This offset must be taken into account when building an unwind plan by analyzing instructions.
None of the in-tree targets that currently implement EmulateInstruction plugin interface experience this bug because all of them define the initial CFA value as zero offset from SP.
>From 5e2298301bc2c79de5f7109f1be486f77e984295 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Sat, 18 Apr 2026 02:05:24 +0300
Subject: [PATCH] [lldb] Handle non-zero offsets from CFA when computing
initial SP value
Some targets (for example, X86 and MSP430) push the return address on
the stack when calling a function, and have CFA=SP+offset initial rule.
This offset must be taken into account when building an unwind plan by
analyzing instructions.
None of the in-tree targets that currently implement EmulateInstruction
plugin interface experience this bug because all of them define the
initial CFA value as zero offset from SP.
---
lldb/include/lldb/Symbol/UnwindPlan.h | 4 +++-
.../InstEmulation/UnwindAssemblyInstEmulation.cpp | 12 ++++++++++--
.../InstEmulation/UnwindAssemblyInstEmulation.h | 2 +-
lldb/source/Symbol/UnwindPlan.cpp | 6 ++++++
4 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h
index fe8081f83c590..31483f97fe39d 100644
--- a/lldb/include/lldb/Symbol/UnwindPlan.h
+++ b/lldb/include/lldb/Symbol/UnwindPlan.h
@@ -486,7 +486,9 @@ class UnwindPlan {
const UnwindPlan::Row *GetRowAtIndex(uint32_t idx) const;
- const UnwindPlan::Row *GetLastRow() const;
+ const Row *GetFirstRow() const;
+
+ const Row *GetLastRow() const;
lldb_private::ConstString GetSourceName() const;
diff --git a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
index 19ae1cf392efa..8b50b2c1042fa 100644
--- a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
+++ b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
@@ -95,7 +95,13 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
// CreateFunctionEntryUnwind should have created the first row. If it doesn't,
// then we are done.
- if (unwind_plan.GetRowCount() == 0)
+ const UnwindPlan::Row *initial_row = unwind_plan.GetFirstRow();
+ if (!initial_row)
+ return false;
+
+ // Only isRegisterPlusOffset rule is currently supported.
+ const UnwindPlan::Row::FAValue &initial_row_cfa = initial_row->GetCFAValue();
+ if (!initial_row_cfa.IsRegisterPlusOffset())
return false;
const bool prefer_file_cache = true;
@@ -119,7 +125,9 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
m_pushed_regs.clear();
RegisterValue cfa_reg_value;
- cfa_reg_value.SetUInt(m_initial_cfa, m_state.cfa_reg_info.byte_size);
+ assert(initial_row_cfa.IsRegisterPlusOffset());
+ cfa_reg_value.SetUInt(m_initial_cfa - initial_row_cfa.GetOffset(),
+ m_state.cfa_reg_info.byte_size);
SetRegisterValue(m_state.cfa_reg_info, cfa_reg_value);
InstructionList inst_list = disasm_sp->GetInstructionList();
diff --git a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
index 43daf1c9f9fd6..ddce8c61ec413 100644
--- a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
+++ b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
@@ -138,7 +138,7 @@ class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly {
lldb_private::AddressRange *m_range_ptr;
lldb_private::UnwindPlan *m_unwind_plan_ptr;
UnwindState m_state;
- uint64_t m_initial_cfa;
+ lldb::addr_t m_initial_cfa;
typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap;
PushedRegisterToAddrMap m_pushed_regs;
diff --git a/lldb/source/Symbol/UnwindPlan.cpp b/lldb/source/Symbol/UnwindPlan.cpp
index 9245e52732061..008568d4aacb2 100644
--- a/lldb/source/Symbol/UnwindPlan.cpp
+++ b/lldb/source/Symbol/UnwindPlan.cpp
@@ -444,6 +444,12 @@ const UnwindPlan::Row *UnwindPlan::GetRowAtIndex(uint32_t idx) const {
return nullptr;
}
+const UnwindPlan::Row *UnwindPlan::GetFirstRow() const {
+ if (m_row_list.empty())
+ return nullptr;
+ return &m_row_list.front();
+}
+
const UnwindPlan::Row *UnwindPlan::GetLastRow() const {
if (m_row_list.empty()) {
LLDB_LOG(GetLog(LLDBLog::Unwind),
More information about the lldb-commits
mailing list