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
