llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: firmiana402

<details>
<summary>Changes</summary>


LLDB currently parses array subrange properties such as `DW_AT_upper_bound` as 
unsigned form values in `DWARFASTParser::ParseChildArrayInfo`.

This works for constant forms, but not for expression/block forms. For example, 
a valid dynamic bound such as:

```text
DW_AT_upper_bound: DW_OP_fbreg -8; DW_OP_deref
```

was treated as the raw block/form value instead of being evaluated as a DWARF 
expression. 

As a result, LLDB currently uses the byte size of the expression block as the 
array bound instead of evaluating the expression, which leads to an incorrect 
number of array elements.

# Fix
This patch adds a small helper function for array properties that: keeps the 
existing handling for constants, evaluates block-form array properties with 
`DWARFExpression::Evaluate` when an execution context is available, and 
conservatively treats unavailable or failing evaluations as unavailable.

`DW_AT_count`, `DW_AT_lower_bound`, `DW_AT_upper_bound`, `DW_AT_bit_stride`, 
and `DW_AT_byte_stride` now use this helper in `ParseChildArrayInfo`.

# Test

Added a shell test with a frame-relative dynamic upper bound:

```text
DW_AT_upper_bound: DW_OP_fbreg -8; DW_OP_deref
```

The test verifies that LLDB evaluates the bound expression and prints all 
expected array children, rather than using the expression block size as the 
bound.


---
Full diff: https://github.com/llvm/llvm-project/pull/204119.diff


2 Files Affected:

- (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp (+67-11) 
- (added) lldb/test/Shell/SymbolFile/DWARF/x86/array-upper-bound-exprloc.s 
(+180) 


``````````diff
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
index a8eafc94215dc..fe67dadf1b072 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
@@ -9,10 +9,16 @@
 #include "DWARFASTParser.h"
 #include "DWARFAttribute.h"
 #include "DWARFDIE.h"
+#include "DWARFFormValue.h"
+#include "DWARFUnit.h"
 #include "SymbolFileDWARF.h"
 
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Symbol/SymbolFile.h"
 #include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/DataExtractor.h"
 #include "lldb/ValueObject/ValueObject.h"
 #include <optional>
 
@@ -21,6 +27,49 @@ using namespace lldb_private;
 using namespace lldb_private::plugin::dwarf;
 using namespace llvm::dwarf;
 
+static std::optional<uint64_t>
+EvaluateUnsignedArrayProperty(const DWARFFormValue &form_value,
+                              const DWARFDIE &parent_die,
+                              const ExecutionContext *exe_ctx) {
+  // Array properties may be encoded directly as constants.
+  if (std::optional<uint64_t> value = form_value.getAsUnsignedConstant())
+    return value;
+  if (std::optional<int64_t> value = form_value.getAsSignedConstant())
+    if (*value >= 0)
+      return *value;
+
+  // Otherwise, evaluate expression blocks in the current execution context.
+  // Without a context there is no frame/register state to use for operations
+  // such as DW_OP_fbreg.
+  if (!DWARFFormValue::IsBlockForm(form_value.Form()) || !exe_ctx)
+    return std::nullopt;
+
+  DWARFUnit *unit = parent_die.GetCU();
+  SymbolFileDWARF *dwarf = parent_die.GetDWARF();
+  if (!unit || !dwarf || !dwarf->GetObjectFile())
+    return std::nullopt;
+
+  lldb::RegisterContextSP reg_ctx_sp;
+  if (lldb::StackFrameSP frame_sp = exe_ctx->GetFrameSP())
+    reg_ctx_sp = frame_sp->GetRegisterContext();
+
+  DataExtractor data(form_value.BlockData(), form_value.Unsigned(),
+                     unit->GetByteOrder(), unit->GetAddressByteSize());
+  ExecutionContext exe_ctx_copy(*exe_ctx);
+
+  // Evaluate the DWARF expression to obtain the dynamic property value.
+  llvm::Expected<Value> result = DWARFExpression::Evaluate(
+      &exe_ctx_copy, reg_ctx_sp.get(), dwarf->GetObjectFile()->GetModule(),
+      data, unit, eRegisterKindDWARF,
+      /*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr);
+  if (!result) {
+    llvm::consumeError(result.takeError());
+    return std::nullopt;
+  }
+
+  return result->GetScalar().ULongLong();
+}
+
 std::optional<SymbolFile::ArrayInfo>
 DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die,
                                     const ExecutionContext *exe_ctx) {
@@ -38,9 +87,8 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE 
&parent_die,
       continue;
 
     std::optional<uint64_t> num_elements;
-    uint64_t lower_bound = 0;
-    uint64_t upper_bound = 0;
-    bool upper_bound_valid = false;
+    std::optional<uint64_t> lower_bound = 0;
+    std::optional<uint64_t> upper_bound;
     for (size_t i = 0; i < attributes.Size(); ++i) {
       const dw_attr_t attr = attributes.AttributeAtIndex(i);
       DWARFFormValue form_value;
@@ -65,24 +113,32 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE 
&parent_die,
                 }
               }
           } else
-            num_elements = form_value.Unsigned();
+            num_elements =
+                EvaluateUnsignedArrayProperty(form_value, parent_die, exe_ctx);
           break;
 
         case DW_AT_bit_stride:
-          array_info.bit_stride = form_value.Unsigned();
+          if (std::optional<uint64_t> bit_stride =
+                  EvaluateUnsignedArrayProperty(form_value, parent_die,
+                                                exe_ctx))
+            array_info.bit_stride = *bit_stride;
           break;
 
         case DW_AT_byte_stride:
-          array_info.byte_stride = form_value.Unsigned();
+          if (std::optional<uint64_t> byte_stride =
+                  EvaluateUnsignedArrayProperty(form_value, parent_die,
+                                                exe_ctx))
+            array_info.byte_stride = *byte_stride;
           break;
 
         case DW_AT_lower_bound:
-          lower_bound = form_value.Unsigned();
+          lower_bound =
+              EvaluateUnsignedArrayProperty(form_value, parent_die, exe_ctx);
           break;
 
         case DW_AT_upper_bound:
-          upper_bound_valid = true;
-          upper_bound = form_value.Unsigned();
+          upper_bound =
+              EvaluateUnsignedArrayProperty(form_value, parent_die, exe_ctx);
           break;
 
         default:
@@ -92,8 +148,8 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE 
&parent_die,
     }
 
     if (!num_elements || *num_elements == 0) {
-      if (upper_bound_valid && upper_bound >= lower_bound)
-        num_elements = upper_bound - lower_bound + 1;
+      if (lower_bound && upper_bound && *upper_bound >= *lower_bound)
+        num_elements = *upper_bound - *lower_bound + 1;
     }
 
     array_info.element_orders.push_back(num_elements);
diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/array-upper-bound-exprloc.s 
b/lldb/test/Shell/SymbolFile/DWARF/x86/array-upper-bound-exprloc.s
new file mode 100644
index 0000000000000..2582b3c89d72d
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/x86/array-upper-bound-exprloc.s
@@ -0,0 +1,180 @@
+# Test that exprloc-valued subrange bounds are evaluated instead of being
+# treated as static constants.
+
+# REQUIRES: lld, native, target-x86_64, system-linux
+
+# RUN: llvm-mc -triple x86_64-unknown-linux-gnu %s -filetype=obj -o %t.o
+# RUN: ld.lld %t.o -o %t -e main
+# RUN: %lldb %t -b -o "breakpoint set -n after_init" -o run \
+# RUN:   -o "frame variable --show-all-children array" -o exit | FileCheck %s
+
+# CHECK-LABEL: frame variable --show-all-children array
+# CHECK: (unsigned int[]) array = ([0] = 1, [1] = 3, [2] = 170, [3] = 187, [4] 
= 204, [5] = 221)
+
+        .text
+        .globl          main
+        .type           main,@function
+main:
+.Lfunc_begin:
+        .cfi_startproc
+        pushq           %rbp
+        .cfi_def_cfa_offset 16
+        .cfi_offset     %rbp, -16
+        movq            %rsp, %rbp
+        .cfi_def_cfa_register %rbp
+        subq            $64, %rsp
+        movq            $5, -8(%rbp)
+        movl            $1, -48(%rbp)
+        movl            $3, -44(%rbp)
+        movl            $170, -40(%rbp)
+        movl            $187, -36(%rbp)
+        movl            $204, -32(%rbp)
+        movl            $221, -28(%rbp)
+        .globl          after_init
+after_init:
+        nop
+        xorl            %eax, %eax
+        addq            $64, %rsp
+        popq            %rbp
+        .cfi_def_cfa    %rsp, 8
+        retq
+.Lfunc_end:
+        .cfi_endproc
+        .size           main, .Lfunc_end-main
+
+        .section        .debug_info,"",@progbits
+.Lcu_begin:
+        .4byte          .Lcu_end-.Lcu_start
+.Lcu_start:
+        .2byte          4                       # DWARF version
+        .4byte          .Labbrev_begin
+        .byte           8                       # Address size
+
+        .uleb128        1                       # DW_TAG_compile_unit
+        .ascii          "exprloc-array.c\0"     # DW_AT_name
+        .ascii          "hand-written DWARF\0"  # DW_AT_producer
+        .byte           0x0c                    # DW_AT_language
+        .quad           .Lfunc_begin            # DW_AT_low_pc
+        .4byte          .Lfunc_end-.Lfunc_begin # DW_AT_high_pc
+
+        .uleb128        2                       # DW_TAG_subprogram
+        .ascii          "main\0"                # DW_AT_name
+        .quad           .Lfunc_begin            # DW_AT_low_pc
+        .4byte          .Lfunc_end-.Lfunc_begin # DW_AT_high_pc
+        .byte           .Lframe_base_end-.Lframe_base_begin # DW_AT_frame_base 
exprloc size
+.Lframe_base_begin:
+        .byte           0x56                    # DW_OP_reg6
+.Lframe_base_end:
+
+        .uleb128        3                       # DW_TAG_variable
+        .ascii          "array\0"               # DW_AT_name
+        .4byte          .Larray_type-.Lcu_begin # DW_AT_type
+        .uleb128        .Larray_loc_end-.Larray_loc_begin # DW_AT_location 
exprloc size
+.Larray_loc_begin:
+        .byte           0x91                    # DW_OP_fbreg
+        .sleb128        -48                     # offset
+.Larray_loc_end:
+
+.Lu32_type:
+        .uleb128        4                       # DW_TAG_base_type
+        .ascii          "unsigned int\0"        # DW_AT_name
+        .byte           4                       # DW_AT_byte_size
+        .byte           7                       # DW_ATE_unsigned
+
+.Larray_type:
+        .uleb128        5                       # DW_TAG_array_type
+        .4byte          .Lu32_type-.Lcu_begin   # DW_AT_type
+        .uleb128        6                       # DW_TAG_subrange_type
+        .4byte          .Lu32_type-.Lcu_begin   # DW_AT_type
+        .byte           0                       # DW_AT_lower_bound
+        .uleb128        .Lupper_bound_end-.Lupper_bound_begin # 
DW_AT_upper_bound exprloc size
+.Lupper_bound_begin:
+        .byte           0x91                    # DW_OP_fbreg
+        .sleb128        -8                      # offset
+        .byte           0x06                    # DW_OP_deref
+.Lupper_bound_end:
+        .byte           0                       # End of array type children
+
+        .byte           0                       # End of subprogram children
+        .byte           0                       # End of compile unit children
+.Lcu_end:
+
+        .section        .debug_abbrev,"",@progbits
+.Labbrev_begin:
+        .uleb128        1                       # Abbreviation code
+        .uleb128        0x11                    # DW_TAG_compile_unit
+        .byte           1                       # DW_CHILDREN_yes
+        .uleb128        0x03                    # DW_AT_name
+        .uleb128        0x08                    # DW_FORM_string
+        .uleb128        0x25                    # DW_AT_producer
+        .uleb128        0x08                    # DW_FORM_string
+        .uleb128        0x13                    # DW_AT_language
+        .uleb128        0x0b                    # DW_FORM_data1
+        .uleb128        0x11                    # DW_AT_low_pc
+        .uleb128        0x01                    # DW_FORM_addr
+        .uleb128        0x12                    # DW_AT_high_pc
+        .uleb128        0x06                    # DW_FORM_data4
+        .byte           0
+        .byte           0
+
+        .uleb128        2                       # Abbreviation code
+        .uleb128        0x2e                    # DW_TAG_subprogram
+        .byte           1                       # DW_CHILDREN_yes
+        .uleb128        0x03                    # DW_AT_name
+        .uleb128        0x08                    # DW_FORM_string
+        .uleb128        0x11                    # DW_AT_low_pc
+        .uleb128        0x01                    # DW_FORM_addr
+        .uleb128        0x12                    # DW_AT_high_pc
+        .uleb128        0x06                    # DW_FORM_data4
+        .uleb128        0x40                    # DW_AT_frame_base
+        .uleb128        0x18                    # DW_FORM_exprloc
+        .byte           0
+        .byte           0
+
+        .uleb128        3                       # Abbreviation code
+        .uleb128        0x34                    # DW_TAG_variable
+        .byte           0                       # DW_CHILDREN_no
+        .uleb128        0x03                    # DW_AT_name
+        .uleb128        0x08                    # DW_FORM_string
+        .uleb128        0x49                    # DW_AT_type
+        .uleb128        0x13                    # DW_FORM_ref4
+        .uleb128        0x02                    # DW_AT_location
+        .uleb128        0x18                    # DW_FORM_exprloc
+        .byte           0
+        .byte           0
+
+        .uleb128        4                       # Abbreviation code
+        .uleb128        0x24                    # DW_TAG_base_type
+        .byte           0                       # DW_CHILDREN_no
+        .uleb128        0x03                    # DW_AT_name
+        .uleb128        0x08                    # DW_FORM_string
+        .uleb128        0x0b                    # DW_AT_byte_size
+        .uleb128        0x0b                    # DW_FORM_data1
+        .uleb128        0x3e                    # DW_AT_encoding
+        .uleb128        0x0b                    # DW_FORM_data1
+        .byte           0
+        .byte           0
+
+        .uleb128        5                       # Abbreviation code
+        .uleb128        0x01                    # DW_TAG_array_type
+        .byte           1                       # DW_CHILDREN_yes
+        .uleb128        0x49                    # DW_AT_type
+        .uleb128        0x13                    # DW_FORM_ref4
+        .byte           0
+        .byte           0
+
+        .uleb128        6                       # Abbreviation code
+        .uleb128        0x21                    # DW_TAG_subrange_type
+        .byte           0                       # DW_CHILDREN_no
+        .uleb128        0x49                    # DW_AT_type
+        .uleb128        0x13                    # DW_FORM_ref4
+        .uleb128        0x22                    # DW_AT_lower_bound
+        .uleb128        0x0b                    # DW_FORM_data1
+        .uleb128        0x2f                    # DW_AT_upper_bound
+        .uleb128        0x18                    # DW_FORM_exprloc
+        .byte           0
+        .byte           0
+
+        .byte           0
+
+        .section        .note.GNU-stack,"",@progbits

``````````

</details>


https://github.com/llvm/llvm-project/pull/204119
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to