https://github.com/firmiana402 updated https://github.com/llvm/llvm-project/pull/204119
>From 5d3376e5ea09768fc39451d17ada7424fe988885 Mon Sep 17 00:00:00 2001 From: firmiana402 <[email protected]> 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,@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 >From 0455be6889445db0d4427f92cfec99ebafbfa8e6 Mon Sep 17 00:00:00 2001 From: firmiana402 <[email protected]> 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 <[email protected]> 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); } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
