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 <[email protected]> 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 <[email protected]> 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}), _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
