]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Handle dynamic DW_AT_data_bit_offset
authorTom Tromey <tromey@adacore.com>
Tue, 20 May 2025 16:45:07 +0000 (10:45 -0600)
committerTom Tromey <tromey@adacore.com>
Tue, 3 Jun 2025 13:17:53 +0000 (07:17 -0600)
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 <simon.marchi@efficios.com>
gdb/dwarf2/read.c
gdb/gdb-gdb.py.in
gdb/gdbtypes.c
gdb/gdbtypes.h
gdb/gnu-v3-abi.c
gdb/python/py-type.c
gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp [new file with mode: 0644]

index d3d0d4c303e8f328d5ce2430c3ce5f495a2f467f..22345657d361b164e6d9a2251146ca287b49aa3b 100644 (file)
@@ -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));
+       }
     }
 }
 
index b989348400ec49eded01bb13f0cbbcd974f0dfff..7388c6f56dd283a316f46d4fbdf45f4671801696 100644 (file)
@@ -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)"
 
index a241223a3e6194db847e6736eab3d6dd84c709d0..624c9b15d3a86cb0c26b331bee1138a4f1a65b68 100644 (file)
@@ -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:
index 67b4bf035ef6635f7ceaacc783c81b4331afd9f5..9e2efe99cff4a58f10217ff4e19a82c923b8120b 100644 (file)
@@ -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;
   }
 
index 4f53e9ff1549817e4ffea1cd78792b6f73360a50..9fbabe4ea55b5b51acc1dc7aabb44026726fed44 100644 (file)
@@ -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
index f37afdefe94392543f2ae5f3c8327c7e981b4ddc..c546aa7536ab5622be41dec1936fd163df716158 100644 (file)
@@ -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 (file)
index 0000000..f4e02da
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+
+# 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))"]