[Lldb-commits] [lldb] [lldb] Interpret the DW_OP_plus_uconst addend in the popped operand's… (PR #217239)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 19 02:05:56 PDT 2026
https://github.com/MrEven132 created https://github.com/llvm/llvm-project/pull/217239
… typeFor typed DWARF values, `DW_OP_plus_uconst` must add the ULEB128 addend
in the same explicit base type as the popped operand (DWARF v5, 2.5.1.4).
LLDB forwarded the operation to `uint64_t` `Scalar` arithmetic: the
addend was implicitly converted to a 64-bit unsigned `Scalar`, and
`Scalar::operator+=` then promoted the narrow operand to 64 bits before
adding. This destroys the wraparound semantics of the original base
type, e.g. `(unsigned char)0xff + 1` evaluated to `0x100` instead of 0.
Construct the addend with the popped operand's exact integer type (bit
width and signedness) so the addition is performed in that type and
wraps accordingly. Non-integer operands keep the existing promotion
behavior.
Adds a `DW_OP_plus_uconst_typed` unit test covering unsigned and signed
narrow wraparound as well as the reproducer from the issue.
Fixes #204520
>From bf1f44601f1ae5787284e523215f191080c0ec0e Mon Sep 17 00:00:00 2001
From: MrEven132 <13554966229 at 163.com>
Date: Wed, 19 Aug 2026 16:43:42 +0800
Subject: [PATCH] [lldb] Interpret the DW_OP_plus_uconst addend in the popped
operand's type
---
lldb/source/Expression/DWARFExpression.cpp | 14 ++++--
.../Expression/DWARFExpressionTest.cpp | 49 +++++++++++++++++++
2 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index e927708f12d14..8a99d0a356ccd 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -1630,9 +1630,17 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
case DW_OP_plus_uconst: {
const uint64_t uconst_value = op->getRawOperand(0);
- // Implicit conversion from a UINT to a Scalar...
- stack.back().GetScalar() += uconst_value;
- if (!stack.back().GetScalar().IsValid())
+ Scalar &operand = stack.back().GetScalar();
+ Scalar addend(uconst_value);
+ // The addend is interpreted as the same type as the popped operand
+ // (DWARF v5, 2.5.1.4). Give it the operand's exact integer type so
+ // the addition keeps the operand's width and wraparound semantics
+ // instead of promoting to a 64-bit unsigned value.
+ if (operand.GetType() == Scalar::e_int)
+ addend.TruncOrExtendTo(operand.GetAPSInt().getBitWidth(),
+ operand.GetAPSInt().isSigned());
+ operand += addend;
+ if (!operand.IsValid())
return llvm::createStringError("DW_OP_plus_uconst failed");
} break;
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index 6985dd0dd4939..05e23165455b1 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -959,6 +959,55 @@ TEST(DWARFExpression, DW_OP_plus_uconst) {
ExpectScalar(static_cast<int32_t>(-5)));
}
+TEST(DWARFExpression, DW_OP_plus_uconst_typed) {
+ class TypedDwarfDelegate : public MockDwarfDelegate {
+ public:
+ enum : uint8_t {
+ UnsignedChar = 1,
+ SignedChar = 2,
+ };
+
+ llvm::Expected<std::pair<uint64_t, bool>>
+ GetDIEBitSizeAndSign(uint64_t relative_die_offset) const override {
+ switch (relative_die_offset) {
+ case UnsignedChar:
+ return std::pair<uint64_t, bool>{8, false};
+ case SignedChar:
+ return std::pair<uint64_t, bool>{8, true};
+ default:
+ return llvm::createStringError("unknown base type offset");
+ }
+ }
+ };
+ TypedDwarfDelegate unit;
+
+ // The addend is interpreted as the same type as the popped operand
+ // (DWARF v5, 2.5.1.4), so the addition wraps within that type.
+ // (unsigned char)0xff + 1 == 0
+ EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0xff, DW_OP_convert,
+ TypedDwarfDelegate::UnsignedChar,
+ DW_OP_plus_uconst, 1, DW_OP_stack_value},
+ {}, &unit),
+ ExpectScalar(8, 0, /*sign=*/false));
+
+ // (signed char)0x7f + 1 == -128
+ EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x7f, DW_OP_convert,
+ TypedDwarfDelegate::SignedChar,
+ DW_OP_plus_uconst, 1, DW_OP_stack_value},
+ {}, &unit),
+ ExpectScalar(8, 0x80, /*sign=*/true));
+
+ // The reproducer from the issue: if 0xff + 1 fails to wrap to zero, the
+ // subsequent right shift produces 1 instead of 0.
+ EXPECT_THAT_EXPECTED(
+ Evaluate({DW_OP_const8u, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ DW_OP_convert, TypedDwarfDelegate::UnsignedChar,
+ DW_OP_plus_uconst, 1, DW_OP_const1u, 7, DW_OP_convert,
+ TypedDwarfDelegate::UnsignedChar, DW_OP_shr, DW_OP_stack_value},
+ {}, &unit),
+ ExpectScalar(8, 0, /*sign=*/false));
+}
+
TEST(DWARFExpression, DW_OP_and) {
EXPECT_THAT_EXPECTED(
Evaluate({DW_OP_const1u, 0x0F, DW_OP_const1u, 0x33, DW_OP_and}),
More information about the lldb-commits
mailing list