[Lldb-commits] [lldb] [lldb] Use the .pdata function start for a frame 0 that has no symbol (PR #227298)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 16 07:53:49 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/227298
>From 00c9f4cf5fc97e129f88149fc69cad14898d4551 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 29 Sep 2026 13:44:12 +0100
Subject: [PATCH 1/2] [lldb] Use the .pdata function start for a frame 0 that
has no symbol
---
lldb/source/Target/RegisterContextUnwind.cpp | 25 +++++++++-
.../Inputs/symbolless-entry-unwind.dmp.yaml | 27 +++++++++++
.../Inputs/symbolless-entry-unwind.exe.yaml | 47 +++++++++++++++++++
.../Windows/symbolless-entry-unwind.test | 12 +++++
4 files changed, 109 insertions(+), 2 deletions(-)
create mode 100644 lldb/test/Shell/Minidump/Windows/Inputs/symbolless-entry-unwind.dmp.yaml
create mode 100644 lldb/test/Shell/Minidump/Windows/Inputs/symbolless-entry-unwind.exe.yaml
create mode 100644 lldb/test/Shell/Minidump/Windows/symbolless-entry-unwind.test
diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp
index 8d31c291f21686..b9d10f585bd528 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -75,6 +75,20 @@ static bool IsClangOutlinedFunction(const SymbolContext &sym_ctx) {
return false;
}
+/// Return the start of the function containing \p pc according to the object
+/// file's own unwind info (the .pdata RUNTIME_FUNCTION on PE/COFF x64), or an
+/// invalid address if there is none.
+static Address GetFunctionStartFromObjectFileUnwindInfo(const Address &pc) {
+ ModuleSP module_sp = pc.GetModule();
+ if (!module_sp)
+ return Address();
+ CallFrameInfo *cfi = module_sp->GetUnwindTable().GetObjectFileUnwindInfo();
+ AddressRange range;
+ if (!cfi || !cfi->GetAddressRange(pc, range))
+ return Address();
+ return range.GetBaseAddress();
+}
+
#define UNWIND_LOG_IMPL(LOG_FN, log, ...) \
LOG_FN(log, "{0}th{1}/fr{2} {3}", \
llvm::indent(std::min(m_frame_number, 100U)), m_thread.GetIndexID(), \
@@ -223,6 +237,13 @@ void RegisterContextUnwind::InitializeZerothFrame() {
m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress();
}
m_current_offset_backed_up_one = m_current_offset;
+ } else if (Address start =
+ GetFunctionStartFromObjectFileUnwindInfo(m_current_pc);
+ start.IsValid()) {
+ m_start_pc = start;
+ m_current_offset =
+ m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress();
+ m_current_offset_backed_up_one = m_current_offset;
} else {
m_start_pc = m_current_pc;
m_current_offset = std::nullopt;
@@ -928,8 +949,8 @@ RegisterContextUnwind::GetFullUnwindPlanForFrame() {
// track.
if (DWARFCallFrameInfo *eh_frame =
pc_module_sp->GetUnwindTable().GetEHFrameInfo()) {
- if (std::unique_ptr<UnwindPlan> plan_up =
- eh_frame->GetUnwindPlan(m_current_pc))
+ if (std::unique_ptr<UnwindPlan> plan_up = eh_frame->GetUnwindPlan(
+ {AddressRange(m_current_pc, 1)}, m_start_pc))
return plan_up;
}
diff --git a/lldb/test/Shell/Minidump/Windows/Inputs/symbolless-entry-unwind.dmp.yaml b/lldb/test/Shell/Minidump/Windows/Inputs/symbolless-entry-unwind.dmp.yaml
new file mode 100644
index 00000000000000..591998c483c9b5
--- /dev/null
+++ b/lldb/test/Shell/Minidump/Windows/Inputs/symbolless-entry-unwind.dmp.yaml
@@ -0,0 +1,27 @@
+--- !minidump
+Streams:
+ - Type: ThreadList
+ Threads:
+ - Thread Id: 0x00001000
+ Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101000400100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ Stack:
+ Start of Memory Range: 0x20000
+ Content: 0910004001000000DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD0000000000000000DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+ - Type: MemoryList
+ Memory Ranges:
+ - Start of Memory Range: 0x20000
+ Content: 0910004001000000DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD0000000000000000DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+ - Type: ModuleList
+ Modules:
+ - Base of Image: 0x140000000
+ Size of Image: 0x00004000
+ Module Name: 'symbolless-entry-unwind.exe'
+ CodeView Record: ''
+ - Type: SystemInfo
+ Processor Arch: AMD64
+ Platform ID: Win32NT
+ CPU:
+ Vendor ID: GenuineIntel
+ Version Info: 0x00000000
+ Feature Info: 0x00000000
+...
diff --git a/lldb/test/Shell/Minidump/Windows/Inputs/symbolless-entry-unwind.exe.yaml b/lldb/test/Shell/Minidump/Windows/Inputs/symbolless-entry-unwind.exe.yaml
new file mode 100644
index 00000000000000..27dc947d47bad0
--- /dev/null
+++ b/lldb/test/Shell/Minidump/Windows/Inputs/symbolless-entry-unwind.exe.yaml
@@ -0,0 +1,47 @@
+# An x86_64 PE image with no symbol table and no exports. Its two functions
+# are described only by .pdata/.xdata:
+# 0x140001000 caller: sub rsp, 0x28; call callee; add rsp, 0x28; ret
+# 0x140001010 callee: push rbp; push rbx; sub rsp, 0x48;
+# add rsp, 0x48; pop rbx; pop rbp; ret
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 5368709120
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 24
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 29
+ SectionData: 4883EC28E8070000004883C428C3CCCC55534883EC484883C4485B5DC3
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: 0104010004420000010603000682023001500000
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 24
+ SectionData: 001000000E10000000200000101000001D10000008200000
+symbols: []
+...
diff --git a/lldb/test/Shell/Minidump/Windows/symbolless-entry-unwind.test b/lldb/test/Shell/Minidump/Windows/symbolless-entry-unwind.test
new file mode 100644
index 00000000000000..33809de47b68b7
--- /dev/null
+++ b/lldb/test/Shell/Minidump/Windows/symbolless-entry-unwind.test
@@ -0,0 +1,12 @@
+Test unwinding from the first instruction of a function that no symbol covers
+and that is described only by the PE .pdata/.xdata unwind info.
+
+RUN: mkdir -p %t.dir
+RUN: yaml2obj %S/Inputs/symbolless-entry-unwind.exe.yaml -o %t.dir/symbolless-entry-unwind.exe
+RUN: yaml2obj %S/Inputs/symbolless-entry-unwind.dmp.yaml -o %t.dir/symbolless-entry-unwind.dmp
+RUN: %lldb -O "settings set target.exec-search-paths %t.dir" \
+RUN: -c %t.dir/symbolless-entry-unwind.dmp -o "thread backtrace" -o exit \
+RUN: | FileCheck %s
+
+CHECK: frame #0: 0x0000000140001010 symbolless-entry-unwind.exe`
+CHECK-NEXT: frame #1: 0x0000000140001009 symbolless-entry-unwind.exe`
>From 4714e76dbafb1b2fa8b8aef024f4f169c9a33e44 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 16 Oct 2026 13:57:28 +0100
Subject: [PATCH 2/2] Address review comments
---
lldb/include/lldb/Symbol/UnwindTable.h | 5 ++++
lldb/source/Symbol/UnwindTable.cpp | 11 ++++++++
lldb/source/Target/RegisterContextUnwind.cpp | 26 ++++++-------------
.../lang/cpp/signed_types/TestSignedTypes.py | 2 +-
4 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/lldb/include/lldb/Symbol/UnwindTable.h b/lldb/include/lldb/Symbol/UnwindTable.h
index f2741cb46dbf9a..0ba86fa89dc897 100644
--- a/lldb/include/lldb/Symbol/UnwindTable.h
+++ b/lldb/include/lldb/Symbol/UnwindTable.h
@@ -43,6 +43,11 @@ class UnwindTable {
GetFuncUnwindersContainingAddress(const Address &addr,
const SymbolContext &sc);
+ /// Return the start of the function containing \p addr according to the
+ /// object file's own unwind info (the .pdata RUNTIME_FUNCTION on PE/COFF
+ /// x64), or an invalid address if there is none.
+ Address GetFunctionStartFromObjectFileUnwindInfo(const Address &addr);
+
bool GetAllowAssemblyEmulationUnwindPlans();
// Normally when we create a new FuncUnwinders object we track it in this
diff --git a/lldb/source/Symbol/UnwindTable.cpp b/lldb/source/Symbol/UnwindTable.cpp
index 3aca495696c844..b89409e8eb0d4f 100644
--- a/lldb/source/Symbol/UnwindTable.cpp
+++ b/lldb/source/Symbol/UnwindTable.cpp
@@ -174,6 +174,17 @@ FuncUnwindersSP UnwindTable::GetUncachedFuncUnwindersContainingAddress(
return std::make_shared<FuncUnwinders>(*this, start_addr, std::move(ranges));
}
+Address
+UnwindTable::GetFunctionStartFromObjectFileUnwindInfo(const Address &addr) {
+ Initialize();
+
+ AddressRange range;
+ if (!m_object_file_unwind_up ||
+ !m_object_file_unwind_up->GetAddressRange(addr, range))
+ return Address();
+ return range.GetBaseAddress();
+}
+
void UnwindTable::Dump(Stream &s) {
std::lock_guard<std::mutex> guard(m_mutex);
s.Format("UnwindTable for '{0}':\n", m_module.GetFileSpec());
diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp
index b9d10f585bd528..68c0730ec94c81 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -75,20 +75,6 @@ static bool IsClangOutlinedFunction(const SymbolContext &sym_ctx) {
return false;
}
-/// Return the start of the function containing \p pc according to the object
-/// file's own unwind info (the .pdata RUNTIME_FUNCTION on PE/COFF x64), or an
-/// invalid address if there is none.
-static Address GetFunctionStartFromObjectFileUnwindInfo(const Address &pc) {
- ModuleSP module_sp = pc.GetModule();
- if (!module_sp)
- return Address();
- CallFrameInfo *cfi = module_sp->GetUnwindTable().GetObjectFileUnwindInfo();
- AddressRange range;
- if (!cfi || !cfi->GetAddressRange(pc, range))
- return Address();
- return range.GetBaseAddress();
-}
-
#define UNWIND_LOG_IMPL(LOG_FN, log, ...) \
LOG_FN(log, "{0}th{1}/fr{2} {3}", \
llvm::indent(std::min(m_frame_number, 100U)), m_thread.GetIndexID(), \
@@ -238,7 +224,10 @@ void RegisterContextUnwind::InitializeZerothFrame() {
}
m_current_offset_backed_up_one = m_current_offset;
} else if (Address start =
- GetFunctionStartFromObjectFileUnwindInfo(m_current_pc);
+ pc_module_sp ? pc_module_sp->GetUnwindTable()
+ .GetFunctionStartFromObjectFileUnwindInfo(
+ m_current_pc)
+ : Address();
start.IsValid()) {
m_start_pc = start;
m_current_offset =
@@ -935,9 +924,10 @@ RegisterContextUnwind::GetFullUnwindPlanForFrame() {
// No FuncUnwinders available for this pc (stripped function symbols, lldb
// could not augment its function table with another source, like
- // LC_FUNCTION_STARTS or eh_frame in ObjectFileMachO). See if eh_frame or the
- // .ARM.exidx tables have unwind information for this address, else fall back
- // to the architectural default unwind.
+ // LC_FUNCTION_STARTS or eh_frame in ObjectFileMachO). See if eh_frame, the
+ // .ARM.exidx tables or the object file's own unwind info (e.g. the PE EH info
+ // in .pdata) have unwind information for this address, else fall back to the
+ // architectural default unwind.
if (!func_unwinders_sp) {
m_frame_type = eNormalFrame;
diff --git a/lldb/test/API/lang/cpp/signed_types/TestSignedTypes.py b/lldb/test/API/lang/cpp/signed_types/TestSignedTypes.py
index de9bbeaa9f33fd..12c3412929c84b 100644
--- a/lldb/test/API/lang/cpp/signed_types/TestSignedTypes.py
+++ b/lldb/test/API/lang/cpp/signed_types/TestSignedTypes.py
@@ -20,7 +20,7 @@ def setUp(self):
@expectedFailureAll(
oslist=["windows"],
debug_info=no_match(["pdb"]),
- archs=["i[3-6]86", "x86_64", "aarch64"],
+ archs=["i[3-6]86", "aarch64"],
bugnumber="llvm.org/pr24489",
)
def test(self):
More information about the lldb-commits
mailing list