[Lldb-commits] [lldb] [lldb] Correct reference operand scanning in DWARF expressions (PR #214519)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Aug 6 09:06:12 PDT 2026
https://github.com/firmiana402 created https://github.com/llvm/llvm-project/pull/214519
`GetOpcodeDataSize` incorrectly scanned reference operands in two DWARF operations. It treated the `DW_OP_call_ref` reference as target-address-sized, although its width is determined by the containing unit's DWARF32/DWARF64 format. It also treated the `DW_OP_implicit_pointer` reference as address-sized and attempted to decode its following SLEB128 offset from the beginning of the reference.
Expose the unit's DWARF offset byte size through `DWARFExpression::Delegate` and use it for both reference operands. `DW_OP_implicit_pointer` now skips the complete reference before decoding its SLEB128 offset. A scan without a DWARF unit fails conservatively instead of guessing the width.
## Testing
Unit tests cover `DW_OP_call_ref` with mismatched DWARF/address widths and a DWARF64 `DW_OP_implicit_pointer` with a multi-byte SLEB128 offset.
Fixes #43966
Fixes #214459
>From 75dd97829bb608a8aa4d94167b3d0e09f7d0d34c Mon Sep 17 00:00:00 2001
From: firmiana402 <firmiana402 at gmail.com>
Date: Thu, 6 Aug 2026 17:51:45 +0800
Subject: [PATCH 1/2] [lldb] Use the DWARF format for DW_OP_call_ref operand
size
---
.../include/lldb/Expression/DWARFExpression.h | 1 +
lldb/source/Expression/DWARFExpression.cpp | 3 +-
.../Plugins/SymbolFile/DWARF/DWARFUnit.h | 3 ++
.../Expression/DWARFExpressionTest.cpp | 42 ++++++++++++++++++-
4 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index feecd50fc9c22..d5e12eda4ac0c 100644
--- a/lldb/include/lldb/Expression/DWARFExpression.h
+++ b/lldb/include/lldb/Expression/DWARFExpression.h
@@ -45,6 +45,7 @@ class DWARFExpression {
virtual uint16_t GetVersion() const = 0;
virtual dw_addr_t GetBaseAddress() const = 0;
virtual uint8_t GetAddressByteSize() const = 0;
+ virtual uint8_t GetDwarfOffsetByteSize() const = 0;
virtual llvm::Expected<std::pair<uint64_t, bool>>
GetDIEBitSizeAndSign(uint64_t relative_die_offset) const = 0;
virtual dw_addr_t ReadAddressFromDebugAddrSection(uint32_t index) const = 0;
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index 2dbf61a14eac4..81eae980286c6 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -223,8 +223,9 @@ GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset,
break;
case DW_OP_addr:
- case DW_OP_call_ref: // 0x9a 1 address sized offset of DIE (DWARF3)
return data.GetAddressByteSize();
+ case DW_OP_call_ref:
+ return dwarf_cu ? dwarf_cu->GetDwarfOffsetByteSize() : LLDB_INVALID_OFFSET;
// Opcodes with no arguments
case DW_OP_deref: // 0x06
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
index 6fde9af57fa8b..acea9f49b1c6a 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
@@ -127,6 +127,9 @@ class DWARFUnit : public DWARFExpression::Delegate, public UserID {
uint8_t GetAddressByteSize() const override {
return m_header.getAddressByteSize();
}
+ uint8_t GetDwarfOffsetByteSize() const override {
+ return GetFormParams().getDwarfOffsetByteSize();
+ }
dw_addr_t GetAddrBase() const { return m_addr_base.value_or(0); }
dw_addr_t GetBaseAddress() const override { return m_base_addr; }
dw_offset_t GetLineTableOffset();
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index e8bca7208c8d5..cd4b90c414e6c 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -42,15 +42,20 @@ using namespace llvm::dwarf;
namespace {
/// A mock implementation of DWARFExpression::Delegate for testing.
/// This class provides default implementations of all delegate methods,
-/// with the DWARF version being configurable via the constructor.
+/// with the DWARF version and offset byte size configurable via the
+/// constructor.
class MockDwarfDelegate : public DWARFExpression::Delegate {
public:
static constexpr uint16_t DEFAULT_DWARF_VERSION = 5;
static MockDwarfDelegate Dwarf5() { return MockDwarfDelegate(5); }
static MockDwarfDelegate Dwarf2() { return MockDwarfDelegate(2); }
+ static MockDwarfDelegate Dwarf64() {
+ return MockDwarfDelegate(DEFAULT_DWARF_VERSION, /*offset_byte_size=*/8);
+ }
MockDwarfDelegate() : MockDwarfDelegate(DEFAULT_DWARF_VERSION) {}
- explicit MockDwarfDelegate(uint16_t version) : m_dwarf_version(version) {}
+ explicit MockDwarfDelegate(uint16_t version, uint8_t offset_byte_size = 4)
+ : m_dwarf_version(version), m_offset_byte_size(offset_byte_size) {}
uint16_t GetVersion() const override { return m_dwarf_version; }
@@ -58,6 +63,8 @@ class MockDwarfDelegate : public DWARFExpression::Delegate {
uint8_t GetAddressByteSize() const override { return 4; }
+ uint8_t GetDwarfOffsetByteSize() const override { return m_offset_byte_size; }
+
llvm::Expected<std::pair<uint64_t, bool>>
GetDIEBitSizeAndSign(uint64_t relative_die_offset) const override {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -83,6 +90,7 @@ class MockDwarfDelegate : public DWARFExpression::Delegate {
private:
uint16_t m_dwarf_version;
+ uint8_t m_offset_byte_size;
};
/// Mock memory implementation for testing.
@@ -820,6 +828,36 @@ TEST(DWARFExpression, DW_OP_implicit_value) {
llvm::HasValue(0x40302010u));
}
+TEST(DWARFExpression, GetLocationSkipsDW_OP_call_refOperand) {
+ {
+ // In DWARF64 the reference is eight bytes even when addresses are four
+ // bytes. Embed a false DW_OP_addr after the first four operand bytes to
+ // detect an address-sized skip.
+ uint8_t expr[] = {DW_OP_call_ref, 0x00, 0x00, 0x00, 0x00,
+ DW_OP_addr, 0x11, 0x22, 0x33, DW_OP_addr,
+ 0x10, 0x20, 0x30, 0x40};
+ DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
+ /*addr_size=*/4);
+ DWARFExpression dwarf_expr(extractor);
+ MockDwarfDelegate dwarf64 = MockDwarfDelegate::Dwarf64();
+ EXPECT_THAT_EXPECTED(dwarf_expr.GetLocation_DW_OP_addr(&dwarf64),
+ llvm::HasValue(0x40302010u));
+ }
+
+ {
+ // In DWARF32 the reference is four bytes even when addresses are eight
+ // bytes. An address-sized skip would consume the following DW_OP_addr.
+ uint8_t expr[] = {DW_OP_call_ref, 0x00, 0x00, 0x00, 0x00, DW_OP_addr, 0x30,
+ 0x31, 0x32, 0x30, 0x31, 0x32, 0x33, 0x34};
+ DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
+ /*addr_size=*/8);
+ DWARFExpression dwarf_expr(extractor);
+ MockDwarfDelegate dwarf32;
+ EXPECT_THAT_EXPECTED(dwarf_expr.GetLocation_DW_OP_addr(&dwarf32),
+ llvm::HasValue(lldb::addr_t{0x3433323130323130}));
+ }
+}
+
TEST(DWARFExpression, DW_OP_unknown) {
EXPECT_THAT_EXPECTED(
Evaluate({0xff}),
>From c60846a1982b3d66fac504b27863b860755ed963 Mon Sep 17 00:00:00 2001
From: firmiana402 <firmiana402 at gmail.com>
Date: Thu, 6 Aug 2026 17:59:16 +0800
Subject: [PATCH 2/2] [lldb] Correct DW_OP_implicit_pointer operand scanning
---
lldb/source/Expression/DWARFExpression.cpp | 10 +++++----
.../Expression/DWARFExpressionTest.cpp | 21 +++++++++++++++++++
2 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index 81eae980286c6..da4094cd8c6cc 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -420,12 +420,14 @@ GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset,
return offset - data_offset;
}
- case DW_OP_implicit_pointer: // 0xa0 4-byte (or 8-byte for DWARF 64) constant
- // + LEB128
+ case DW_OP_implicit_pointer: // 0xa0 4-byte (or 8-byte for DWARF 64)
+ // reference + SLEB128 offset
{
+ if (!dwarf_cu)
+ return LLDB_INVALID_OFFSET;
+ offset += dwarf_cu->GetDwarfOffsetByteSize();
data.Skip_LEB128(&offset);
- return (dwarf_cu ? dwarf_cu->GetAddressByteSize() : 4) + offset -
- data_offset;
+ return offset - data_offset;
}
case DW_OP_GNU_entry_value:
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index cd4b90c414e6c..624ae081d2dca 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -858,6 +858,27 @@ TEST(DWARFExpression, GetLocationSkipsDW_OP_call_refOperand) {
}
}
+TEST(DWARFExpression, GetLocationSkipsDW_OP_implicit_pointerOperands) {
+ // DW_OP_implicit_pointer contains an eight-byte reference in DWARF64,
+ // followed by an SLEB128 offset. The operand bytes contain false DW_OP_addr
+ // operations that detect either an address-sized reference or decoding the
+ // SLEB128 before skipping the reference.
+ uint8_t expr[] = {
+ DW_OP_implicit_pointer,
+ // Eight-byte reference with a false DW_OP_addr at byte six.
+ 0x00, 0x11, 0x22, 0x33, 0x44, DW_OP_addr, 0x55, 0x66,
+ // Two-byte SLEB128 whose final byte is another false DW_OP_addr.
+ 0x80, DW_OP_addr,
+ // The real address operation.
+ DW_OP_addr, 0x10, 0x20, 0x30, 0x40};
+ DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
+ /*addr_size=*/4);
+ DWARFExpression dwarf_expr(extractor);
+ MockDwarfDelegate dwarf64 = MockDwarfDelegate::Dwarf64();
+ EXPECT_THAT_EXPECTED(dwarf_expr.GetLocation_DW_OP_addr(&dwarf64),
+ llvm::HasValue(0x40302010u));
+}
+
TEST(DWARFExpression, DW_OP_unknown) {
EXPECT_THAT_EXPECTED(
Evaluate({0xff}),
More information about the lldb-commits
mailing list