[Lldb-commits] [lldb] [lldb] Evaluate exprloc array bounds (PR #204119)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Jul 9 23:01:09 PDT 2026
https://github.com/firmiana402 updated https://github.com/llvm/llvm-project/pull/204119
>From 5d3376e5ea09768fc39451d17ada7424fe988885 Mon Sep 17 00:00:00 2001
From: firmiana402 <firmiana402 at gmail.com>
Date: Tue, 16 Jun 2026 18:45:25 +0800
Subject: [PATCH 1/3] [lldb] Evaluate exprloc array bounds
---
.../SymbolFile/DWARF/DWARFASTParser.cpp | 78 ++++++--
.../DWARF/x86/array-upper-bound-exprloc.s | 180 ++++++++++++++++++
2 files changed, 247 insertions(+), 11 deletions(-)
create mode 100644 lldb/test/Shell/SymbolFile/DWARF/x86/array-upper-bound-exprloc.s
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, at 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,"", at 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,"", at 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,"", at progbits
>From 0455be6889445db0d4427f92cfec99ebafbfa8e6 Mon Sep 17 00:00:00 2001
From: firmiana402 <firmiana402 at gmail.com>
Date: Fri, 10 Jul 2026 00:53:45 +0800
Subject: [PATCH 2/3] [lldb] Refine array property evaluation
---
.../SymbolFile/DWARF/DWARFASTParser.cpp | 91 ++++++++++---------
.../DWARF/DWARFASTParserClangTests.cpp | 80 ++++++++++++++++
2 files changed, 128 insertions(+), 43 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
index fe67dadf1b072..466f7b9241593 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
@@ -11,6 +11,7 @@
#include "DWARFDIE.h"
#include "DWARFFormValue.h"
#include "DWARFUnit.h"
+#include "LogChannelDWARF.h"
#include "SymbolFileDWARF.h"
#include "lldb/Core/Value.h"
@@ -27,47 +28,51 @@ using namespace lldb_private;
using namespace lldb_private::plugin::dwarf;
using namespace llvm::dwarf;
+// Array properties can be encoded directly as constants, or as DWARF
+// expression blocks whose values need to be evaluated in an execution context.
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.
+EvaluateArrayPropertyAsUnsigned(const DWARFFormValue &form_value,
+ const DWARFDIE &parent_die,
+ const ExecutionContext *exe_ctx) {
+ if (DWARFFormValue::IsBlockForm(form_value.Form())) {
+ // Block-form values carry their byte length as the stored unsigned value.
+ // Evaluate the block instead of treating that length as the property value.
+ if (!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) {
+ LLDB_LOG_ERROR(GetLog(DWARFLog::DebugInfo), result.takeError(),
+ "failed to evaluate array property expression: {0}");
+ return std::nullopt;
+ }
+
+ return result->GetScalar().ULongLong();
+ }
+
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();
+ return std::nullopt;
}
std::optional<SymbolFile::ArrayInfo>
@@ -113,32 +118,32 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die,
}
}
} else
- num_elements =
- EvaluateUnsignedArrayProperty(form_value, parent_die, exe_ctx);
+ num_elements = EvaluateArrayPropertyAsUnsigned(form_value,
+ parent_die, exe_ctx);
break;
case DW_AT_bit_stride:
if (std::optional<uint64_t> bit_stride =
- EvaluateUnsignedArrayProperty(form_value, parent_die,
- exe_ctx))
+ EvaluateArrayPropertyAsUnsigned(form_value, parent_die,
+ exe_ctx))
array_info.bit_stride = *bit_stride;
break;
case DW_AT_byte_stride:
if (std::optional<uint64_t> byte_stride =
- EvaluateUnsignedArrayProperty(form_value, parent_die,
- exe_ctx))
+ EvaluateArrayPropertyAsUnsigned(form_value, parent_die,
+ exe_ctx))
array_info.byte_stride = *byte_stride;
break;
case DW_AT_lower_bound:
lower_bound =
- EvaluateUnsignedArrayProperty(form_value, parent_die, exe_ctx);
+ EvaluateArrayPropertyAsUnsigned(form_value, parent_die, exe_ctx);
break;
case DW_AT_upper_bound:
upper_bound =
- EvaluateUnsignedArrayProperty(form_value, parent_die, exe_ctx);
+ EvaluateArrayPropertyAsUnsigned(form_value, parent_die, exe_ctx);
break;
default:
diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
index a95d6ecfab790..951d30cf3b7e1 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
@@ -15,6 +15,7 @@
#include "lldb/Core/Debugger.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include <optional>
using namespace lldb;
using namespace lldb_private;
@@ -174,6 +175,85 @@ TEST_F(DWARFASTParserClangTests,
testing::UnorderedElementsAre(decl_ctxs[0], decl_ctxs[3]));
}
+TEST_F(DWARFASTParserClangTests, ParseChildArrayInfoStaticProperties) {
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_386
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Code: 0x00000001
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x00000002
+ Tag: DW_TAG_array_type
+ Children: DW_CHILDREN_yes
+ - Code: 0x00000003
+ Tag: DW_TAG_subrange_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_lower_bound
+ Form: DW_FORM_sdata
+ - Attribute: DW_AT_upper_bound
+ Form: DW_FORM_sdata
+ - Attribute: DW_AT_byte_stride
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_bit_stride
+ Form: DW_FORM_data1
+ - Code: 0x00000004
+ Tag: DW_TAG_subrange_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_count
+ Form: DW_FORM_data1
+ debug_info:
+ - Version: 4
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x00000001
+ Values:
+ - Value: 0x000000000000000C
+ - AbbrCode: 0x00000002
+ - AbbrCode: 0x00000003
+ Values:
+ - Value: 0x0000000000000002
+ - Value: 0x0000000000000005
+ - Value: 0x0000000000000004
+ - Value: 0x0000000000000020
+ - AbbrCode: 0x00000004
+ Values:
+ - Value: 0x0000000000000003
+ - AbbrCode: 0x00000000
+ - AbbrCode: 0x00000000
+)";
+
+ YAMLModuleTester t(yamldata);
+ DWARFUnit *unit = t.GetDwarfUnit();
+ ASSERT_NE(unit, nullptr);
+
+ DWARFDIE array_die = unit->DIE().GetFirstChild();
+ ASSERT_TRUE(array_die.IsValid());
+
+ std::optional<SymbolFile::ArrayInfo> array_info =
+ DWARFASTParser::ParseChildArrayInfo(array_die);
+ ASSERT_TRUE(array_info);
+
+ ASSERT_EQ(array_info->element_orders.size(), 2u);
+ ASSERT_TRUE(array_info->element_orders[0]);
+ EXPECT_EQ(*array_info->element_orders[0], 4u);
+ ASSERT_TRUE(array_info->element_orders[1]);
+ EXPECT_EQ(*array_info->element_orders[1], 3u);
+ EXPECT_EQ(array_info->byte_stride, 4u);
+ EXPECT_EQ(array_info->bit_stride, 32u);
+}
+
TEST_F(DWARFASTParserClangTests, TestCallingConventionParsing) {
// Tests parsing DW_AT_calling_convention values.
>From 14cb19f2454519226062679a557ada3ca797b10e Mon Sep 17 00:00:00 2001
From: firmiana402 <firmiana402 at gmail.com>
Date: Fri, 10 Jul 2026 14:00:20 +0800
Subject: [PATCH 3/3] [lldb] Evaluate array properties with language lower
bounds
Use llvm::dwarf::LanguageLowerBound when DW_AT_lower_bound is omitted instead of assuming all languages default array lower bounds to zero. This preserves C-like behavior while handling languages such as Fortran whose default lower bound is one.
Also return targeted errors from array property evaluation and keep coverage for static array properties.
---
.../SymbolFile/DWARF/DWARFASTParser.cpp | 77 +++++++++++++------
.../DWARF/DWARFASTParserClangTests.cpp | 15 +++-
2 files changed, 66 insertions(+), 26 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
index 466f7b9241593..70b0a880f49db 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp
@@ -21,6 +21,10 @@
#include "lldb/Target/StackFrame.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/ValueObject/ValueObject.h"
+
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/Support/Error.h"
+
#include <optional>
using namespace lldb;
@@ -28,9 +32,9 @@ using namespace lldb_private;
using namespace lldb_private::plugin::dwarf;
using namespace llvm::dwarf;
-// Array properties can be encoded directly as constants, or as DWARF
-// expression blocks whose values need to be evaluated in an execution context.
-static std::optional<uint64_t>
+/// Array properties can be encoded directly as constants, or as DWARF
+/// expression blocks whose values need to be evaluated in an execution context.
+static llvm::Expected<uint64_t>
EvaluateArrayPropertyAsUnsigned(const DWARFFormValue &form_value,
const DWARFDIE &parent_die,
const ExecutionContext *exe_ctx) {
@@ -38,12 +42,15 @@ EvaluateArrayPropertyAsUnsigned(const DWARFFormValue &form_value,
// Block-form values carry their byte length as the stored unsigned value.
// Evaluate the block instead of treating that length as the property value.
if (!exe_ctx)
- return std::nullopt;
+ return llvm::createStringError(
+ "cannot evaluate array property expression without an execution "
+ "context");
DWARFUnit *unit = parent_die.GetCU();
SymbolFileDWARF *dwarf = parent_die.GetDWARF();
if (!unit || !dwarf || !dwarf->GetObjectFile())
- return std::nullopt;
+ return llvm::createStringError(
+ "cannot evaluate array property expression without DWARF context");
lldb::RegisterContextSP reg_ctx_sp;
if (lldb::StackFrameSP frame_sp = exe_ctx->GetFrameSP())
@@ -58,21 +65,35 @@ EvaluateArrayPropertyAsUnsigned(const DWARFFormValue &form_value,
&exe_ctx_copy, reg_ctx_sp.get(), dwarf->GetObjectFile()->GetModule(),
data, unit, eRegisterKindDWARF,
/*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr);
- if (!result) {
- LLDB_LOG_ERROR(GetLog(DWARFLog::DebugInfo), result.takeError(),
- "failed to evaluate array property expression: {0}");
- return std::nullopt;
- }
+ if (!result)
+ return result.takeError();
return result->GetScalar().ULongLong();
}
if (std::optional<uint64_t> value = form_value.getAsUnsignedConstant())
- return value;
- if (std::optional<int64_t> value = form_value.getAsSignedConstant())
+ return *value;
+ if (std::optional<int64_t> value = form_value.getAsSignedConstant()) {
if (*value >= 0)
return *value;
- return std::nullopt;
+ return llvm::createStringError("array property value is negative: %lld",
+ static_cast<long long>(*value));
+ }
+ return llvm::createStringError("unsupported array property form");
+}
+
+static std::optional<uint64_t>
+GetDefaultArrayLowerBound(const DWARFDIE &parent_die) {
+ DWARFUnit *unit = parent_die.GetCU();
+ if (!unit)
+ return std::nullopt;
+
+ std::optional<unsigned> lower_bound = LanguageLowerBound(
+ static_cast<llvm::dwarf::SourceLanguage>(unit->GetDWARFLanguageType()));
+ if (!lower_bound)
+ return std::nullopt;
+
+ return *lower_bound;
}
std::optional<SymbolFile::ArrayInfo>
@@ -92,8 +113,21 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die,
continue;
std::optional<uint64_t> num_elements;
- std::optional<uint64_t> lower_bound = 0;
+ std::optional<uint64_t> lower_bound = GetDefaultArrayLowerBound(parent_die);
std::optional<uint64_t> upper_bound;
+
+ auto evaluate_array_property =
+ [&](const DWARFFormValue &form_value) -> std::optional<uint64_t> {
+ llvm::Expected<uint64_t> value =
+ EvaluateArrayPropertyAsUnsigned(form_value, parent_die, exe_ctx);
+ if (!value) {
+ LLDB_LOG_ERROR(GetLog(DWARFLog::DebugInfo), value.takeError(),
+ "failed to evaluate array property: {0}");
+ return std::nullopt;
+ }
+ return value.get();
+ };
+
for (size_t i = 0; i < attributes.Size(); ++i) {
const dw_attr_t attr = attributes.AttributeAtIndex(i);
DWARFFormValue form_value;
@@ -118,32 +152,27 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die,
}
}
} else
- num_elements = EvaluateArrayPropertyAsUnsigned(form_value,
- parent_die, exe_ctx);
+ num_elements = evaluate_array_property(form_value);
break;
case DW_AT_bit_stride:
if (std::optional<uint64_t> bit_stride =
- EvaluateArrayPropertyAsUnsigned(form_value, parent_die,
- exe_ctx))
+ evaluate_array_property(form_value))
array_info.bit_stride = *bit_stride;
break;
case DW_AT_byte_stride:
if (std::optional<uint64_t> byte_stride =
- EvaluateArrayPropertyAsUnsigned(form_value, parent_die,
- exe_ctx))
+ evaluate_array_property(form_value))
array_info.byte_stride = *byte_stride;
break;
case DW_AT_lower_bound:
- lower_bound =
- EvaluateArrayPropertyAsUnsigned(form_value, parent_die, exe_ctx);
+ lower_bound = evaluate_array_property(form_value);
break;
case DW_AT_upper_bound:
- upper_bound =
- EvaluateArrayPropertyAsUnsigned(form_value, parent_die, exe_ctx);
+ upper_bound = evaluate_array_property(form_value);
break;
default:
diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
index 951d30cf3b7e1..51112930ebe21 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
@@ -213,13 +213,19 @@ TEST_F(DWARFASTParserClangTests, ParseChildArrayInfoStaticProperties) {
Attributes:
- Attribute: DW_AT_count
Form: DW_FORM_data1
+ - Code: 0x00000005
+ Tag: DW_TAG_subrange_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_upper_bound
+ Form: DW_FORM_data1
debug_info:
- Version: 4
AddrSize: 8
Entries:
- AbbrCode: 0x00000001
Values:
- - Value: 0x000000000000000C
+ - Value: 0x0000000000000008
- AbbrCode: 0x00000002
- AbbrCode: 0x00000003
Values:
@@ -230,6 +236,9 @@ TEST_F(DWARFASTParserClangTests, ParseChildArrayInfoStaticProperties) {
- AbbrCode: 0x00000004
Values:
- Value: 0x0000000000000003
+ - AbbrCode: 0x00000005
+ Values:
+ - Value: 0x0000000000000005
- AbbrCode: 0x00000000
- AbbrCode: 0x00000000
)";
@@ -245,11 +254,13 @@ TEST_F(DWARFASTParserClangTests, ParseChildArrayInfoStaticProperties) {
DWARFASTParser::ParseChildArrayInfo(array_die);
ASSERT_TRUE(array_info);
- ASSERT_EQ(array_info->element_orders.size(), 2u);
+ ASSERT_EQ(array_info->element_orders.size(), 3u);
ASSERT_TRUE(array_info->element_orders[0]);
EXPECT_EQ(*array_info->element_orders[0], 4u);
ASSERT_TRUE(array_info->element_orders[1]);
EXPECT_EQ(*array_info->element_orders[1], 3u);
+ ASSERT_TRUE(array_info->element_orders[2]);
+ EXPECT_EQ(*array_info->element_orders[2], 5u);
EXPECT_EQ(array_info->byte_stride, 4u);
EXPECT_EQ(array_info->bit_stride, 32u);
}
More information about the lldb-commits
mailing list