From: Tom Tromey Date: Tue, 20 May 2025 16:45:07 +0000 (-0600) Subject: Handle dynamic DW_AT_data_bit_offset X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=692252c4b0ccd4ca98dc301ac83cc988f1ccdb57;p=thirdparty%2Fbinutils-gdb.git Handle dynamic DW_AT_data_bit_offset In Ada, a field can have a dynamic bit offset in its enclosing record. In DWARF 3, this was handled using a dynamic DW_AT_data_member_location, combined with a DW_AT_bit_offset -- this combination worked out ok because in practice GNAT only needs a dynamic byte offset with a fixed offset within the byte. However, this approach was deprecated in DWARF 4 and then removed in DWARF 5. No replacement approach was given, meaning that in strict mode there is no way to express this. This is a DWARF bug, see https://dwarfstd.org/issues/250501.1.html In a discussion on the DWARF mailing list, a couple people mentioned that compilers could use the obvious extension of a dynamic DW_AT_data_bit_offset. I've implemented this for LLVM: https://github.com/llvm/llvm-project/pull/141106 In preparation for that landing, this patch implements support for this construct in gdb. New in v2: renamed some constants and added a helper method, per Simon's review. New in v3: more renamings. Approved-By: Simon Marchi --- diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index d3d0d4c303e..22345657d36 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -9999,7 +9999,7 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu, dlbaton->per_objfile = per_objfile; dlbaton->per_cu = cu->per_cu; - field->set_loc_dwarf_block (dlbaton); + field->set_loc_dwarf_block_addr (dlbaton); } } else @@ -10010,7 +10010,28 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu, { attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu); if (attr != nullptr) - field->set_loc_bitpos (attr->unsigned_constant ().value_or (0)); + { + if (attr->form_is_constant ()) + field->set_loc_bitpos (attr->unsigned_constant ().value_or (0)); + else if (attr->form_is_block ()) + { + /* This is a DWARF extension. See + https://dwarfstd.org/issues/250501.1.html. */ + dwarf2_per_objfile *per_objfile = cu->per_objfile; + dwarf2_locexpr_baton *dlbaton + = OBSTACK_ZALLOC (&per_objfile->objfile->objfile_obstack, + dwarf2_locexpr_baton); + dlbaton->data = attr->as_block ()->data; + dlbaton->size = attr->as_block ()->size; + dlbaton->per_objfile = per_objfile; + dlbaton->per_cu = cu->per_cu; + + field->set_loc_dwarf_block_bitpos (dlbaton); + } + else + complaint (_("Unsupported form %s for DW_AT_data_bit_offset"), + dwarf_form_name (attr->form)); + } } } diff --git a/gdb/gdb-gdb.py.in b/gdb/gdb-gdb.py.in index b989348400e..7388c6f56dd 100644 --- a/gdb/gdb-gdb.py.in +++ b/gdb/gdb-gdb.py.in @@ -164,8 +164,10 @@ class StructMainTypePrettyPrinter: return "physaddr = 0x%x" % loc_val["physaddr"] elif loc_kind == "FIELD_LOC_KIND_PHYSNAME": return "physname = %s" % loc_val["physname"] - elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK": - return "dwarf_block = %s" % loc_val["dwarf_block"] + elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK_ADDR": + return "dwarf_block_addr = %s" % loc_val["dwarf_block"] + elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK_BITPOS": + return "dwarf_block_bitpos = %s" % loc_val["dwarf_block"] else: return "m_loc = ??? (unsupported m_loc_kind value)" diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index a241223a3e6..624c9b15d3a 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -2121,7 +2121,7 @@ is_dynamic_type_internal (struct type *type, bool top_level) return true; /* If the field is at a fixed offset, then it is not dynamic. */ - if (type->field (i).loc_kind () != FIELD_LOC_KIND_DWARF_BLOCK) + if (!type->field (i).loc_is_dwarf_block ()) continue; /* Do not consider C++ virtual base types to be dynamic due to the field's offset being dynamic; these are @@ -2723,7 +2723,7 @@ resolve_dynamic_field (struct field &field, { gdb_assert (!field.is_static ()); - if (field.loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK) + if (field.loc_is_dwarf_block ()) { dwarf2_locexpr_baton *field_loc = field.loc_dwarf_block (); @@ -2736,10 +2736,15 @@ resolve_dynamic_field (struct field &field, prop.set_locexpr (&baton); CORE_ADDR vals[1] = {addr_stack->addr}; - CORE_ADDR addr; - if (dwarf2_evaluate_property (&prop, frame, addr_stack, &addr, vals)) + CORE_ADDR addr_or_bitpos; + if (dwarf2_evaluate_property (&prop, frame, addr_stack, + &addr_or_bitpos, vals)) { - field.set_loc_bitpos (TARGET_CHAR_BIT * (addr - addr_stack->addr)); + if (field.loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK_ADDR) + field.set_loc_bitpos (TARGET_CHAR_BIT + * (addr_or_bitpos - addr_stack->addr)); + else + field.set_loc_bitpos (addr_or_bitpos); if (field_loc->is_field_location) { @@ -4415,7 +4420,8 @@ check_types_equal (struct type *type1, struct type *type2, field2->loc_physname ())) return false; break; - case FIELD_LOC_KIND_DWARF_BLOCK: + case FIELD_LOC_KIND_DWARF_BLOCK_ADDR: + case FIELD_LOC_KIND_DWARF_BLOCK_BITPOS: { struct dwarf2_locexpr_baton *block1, *block2; @@ -5560,8 +5566,12 @@ copy_type_recursive (struct type *type, copied_types_hash_t &copied_types) new_type->field (i).set_loc_physname (xstrdup (type->field (i).loc_physname ())); break; - case FIELD_LOC_KIND_DWARF_BLOCK: - new_type->field (i).set_loc_dwarf_block + case FIELD_LOC_KIND_DWARF_BLOCK_ADDR: + new_type->field (i).set_loc_dwarf_block_addr + (type->field (i).loc_dwarf_block ()); + break; + case FIELD_LOC_KIND_DWARF_BLOCK_BITPOS: + new_type->field (i).set_loc_dwarf_block_bitpos (type->field (i).loc_dwarf_block ()); break; default: diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index 67b4bf035ef..9e2efe99cff 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -480,7 +480,10 @@ enum field_loc_kind FIELD_LOC_KIND_ENUMVAL, /**< enumval */ FIELD_LOC_KIND_PHYSADDR, /**< physaddr */ FIELD_LOC_KIND_PHYSNAME, /**< physname */ - FIELD_LOC_KIND_DWARF_BLOCK /**< dwarf_block */ + /* A DWARF block that computes the address of the field. */ + FIELD_LOC_KIND_DWARF_BLOCK_ADDR, /**< dwarf_block */ + /* A DWARF block that computes the bit offset of the field. */ + FIELD_LOC_KIND_DWARF_BLOCK_BITPOS, }; /* * A discriminant to determine which field in the @@ -616,6 +619,13 @@ struct field return m_loc_kind; } + /* Return true if this location has either "DWARF block" kind. */ + bool loc_is_dwarf_block () const + { + return (m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK_ADDR + || m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK_BITPOS); + } + LONGEST loc_bitpos () const { gdb_assert (m_loc_kind == FIELD_LOC_KIND_BITPOS); @@ -666,13 +676,19 @@ struct field dwarf2_locexpr_baton *loc_dwarf_block () const { - gdb_assert (m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK); + gdb_assert (loc_is_dwarf_block ()); return m_loc.dwarf_block; } - void set_loc_dwarf_block (dwarf2_locexpr_baton *dwarf_block) + void set_loc_dwarf_block_addr (dwarf2_locexpr_baton *dwarf_block) + { + m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK_ADDR; + m_loc.dwarf_block = dwarf_block; + } + + void set_loc_dwarf_block_bitpos (dwarf2_locexpr_baton *dwarf_block) { - m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK; + m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK_BITPOS; m_loc.dwarf_block = dwarf_block; } diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c index 4f53e9ff154..9fbabe4ea55 100644 --- a/gdb/gnu-v3-abi.c +++ b/gdb/gnu-v3-abi.c @@ -476,7 +476,7 @@ gnuv3_baseclass_offset (struct type *type, int index, return TYPE_BASECLASS_BITPOS (type, index) / 8; /* If we have a DWARF expression for the offset, evaluate it. */ - if (type->field (index).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK) + if (type->field (index).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK_ADDR) { struct dwarf2_property_baton baton; baton.property_type diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index f37afdefe94..c546aa7536a 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -158,7 +158,7 @@ convert_field (struct type *type, int field) } else { - if (type->field (field).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK) + if (type->field (field).loc_is_dwarf_block ()) arg = gdbpy_ref<>::new_reference (Py_None); else arg = gdb_py_object_from_longest (type->field (field).loc_bitpos ()); diff --git a/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp b/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp new file mode 100644 index 00000000000..f4e02da2211 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp @@ -0,0 +1,95 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test DW_AT_data_bit_offset with an expression. This is a DWARF +# extension, but expected to be in DWARF 6. See +# https://dwarfstd.org/issues/250501.1.html + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile ada-array-bound.c -debug.S + +# Set up the DWARF for the test. + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_Ada95} + {DW_AT_name $srcfile} + } { + declare_labels byte array struct + + byte: DW_TAG_base_type { + {DW_AT_byte_size 1 DW_FORM_sdata} + {DW_AT_encoding @DW_ATE_unsigned} + {DW_AT_name byte} + } + + array: DW_TAG_array_type { + {DW_AT_name array_type} + {DW_AT_type :$byte} + } { + DW_TAG_subrange_type { + {DW_AT_type :$byte} + {DW_AT_upper_bound 3 DW_FORM_sdata} + } + } + + struct: DW_TAG_structure_type { + {DW_AT_name discriminated} + {DW_AT_byte_size 4 DW_FORM_sdata} + } { + DW_TAG_member { + {DW_AT_name disc} + {DW_AT_type :$byte} + {DW_AT_data_member_location 0 DW_FORM_sdata} + } + + # We know this is always at offset 1 but use an + # expression just to test this code path. This is a + # DWARF extension. See + # https://dwarfstd.org/issues/250501.1.html. + DW_TAG_member { + {DW_AT_name nums} + {DW_AT_type :$array} + {DW_AT_data_bit_offset {DW_OP_lit8} SPECIAL_expr} + } + } + + DW_TAG_variable { + {DW_AT_name "value"} + {DW_AT_type :$struct} + {DW_AT_external 1 DW_FORM_flag} + {DW_AT_location {DW_OP_addr [gdb_target_symbol "our_data"]} + SPECIAL_expr} + } + } + } +} + +if {[prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}]} { + return -1 +} + +gdb_test_no_output "set language ada" +gdb_test "print value" \ + [string_to_regexp " = (disc => 3, nums => (7, 11, 13))"]