[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