]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Handle dynamic DW_AT_bit_size
authorTom Tromey <tromey@adacore.com>
Tue, 14 Oct 2025 19:26:30 +0000 (13:26 -0600)
committerTom Tromey <tromey@adacore.com>
Fri, 24 Oct 2025 17:52:18 +0000 (11:52 -0600)
gnat-llvm will sometimes emit a structure that that uses
DW_AT_bit_size with an expression to compute the bit size of a record.
I believe this is a DWARF extension.  This patch implements support
for this in gdb.

Reviewed-By: Keith Seitz <keiths@redhat.com>
gdb/dwarf2/read.c
gdb/gdbtypes.c
gdb/gdbtypes.h
gdb/testsuite/gdb.dwarf2/dynamic-bit-size.exp [new file with mode: 0644]

index 49c5fd2323a0373b2b3af118467a95ff930e58f9..802a4550322d6334e6b0e59b3601c8bc3eefbcb1 100644 (file)
@@ -11193,7 +11193,27 @@ read_structure_type (struct die_info *die, struct dwarf2_cu *cu)
        }
     }
   else
-    type->set_length (0);
+    {
+      attr = dwarf2_attr (die, DW_AT_bit_size, cu);
+      if (attr != nullptr)
+       {
+         if (attr->form_is_constant ())
+           {
+             ULONGEST len = attr->unsigned_constant ().value_or (0);
+             type->set_length (align_up (len, 8) / 8);
+           }
+         else
+           {
+             struct dynamic_prop prop;
+             if (attr_to_dynamic_prop (attr, die, cu, &prop, cu->addr_type ()))
+               type->add_dyn_prop (DYN_PROP_BIT_SIZE, prop);
+
+             type->set_length (0);
+           }
+       }
+      else
+       type->set_length (0);
+    }
 
   maybe_set_alignment (cu, die, type);
 
index ac622d54201c10cef406ef59b4afb4f23b699851..63d6f4c0ceb68436211970365bd116bf0329afb9 100644 (file)
@@ -2890,10 +2890,21 @@ resolve_dynamic_type_internal (struct type *type,
     return type;
 
   std::optional<CORE_ADDR> type_length;
+  dynamic_prop_node_kind type_kind = DYN_PROP_BYTE_SIZE;
   prop = type->dyn_prop (DYN_PROP_BYTE_SIZE);
-  if (prop != NULL
+  if (prop != nullptr
       && dwarf2_evaluate_property (prop, frame, addr_stack, &value))
     type_length = value;
+  else
+    {
+      prop = type->dyn_prop (DYN_PROP_BIT_SIZE);
+      if (prop != nullptr
+         && dwarf2_evaluate_property (prop, frame, addr_stack, &value))
+       {
+         type_kind = DYN_PROP_BIT_SIZE;
+         type_length = align_up (value, 8) / 8;
+       }
+    }
 
   if (type->code () == TYPE_CODE_TYPEDEF)
     {
@@ -2969,7 +2980,7 @@ resolve_dynamic_type_internal (struct type *type,
   if (type_length.has_value ())
     {
       resolved_type->set_length (*type_length);
-      resolved_type->remove_dyn_prop (DYN_PROP_BYTE_SIZE);
+      resolved_type->remove_dyn_prop (type_kind);
     }
 
   /* Resolve data_location attribute.  */
index e66701c70298a177b556fc1d95ea9ea04dcf451e..eee0445b5eb0bdf7b440a10a9a216af736251a66 100644 (file)
@@ -133,8 +133,9 @@ DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags);
   ((t)->dyn_prop (DYN_PROP_VARIANT_PARTS) != nullptr)
 
 /* * True if this type has a dynamic length.  */
-#define TYPE_HAS_DYNAMIC_LENGTH(t) \
-  ((t)->dyn_prop (DYN_PROP_BYTE_SIZE) != nullptr)
+#define TYPE_HAS_DYNAMIC_LENGTH(t)                     \
+  (((t)->dyn_prop (DYN_PROP_BYTE_SIZE) != nullptr)     \
+   || ((t)->dyn_prop (DYN_PROP_BIT_SIZE) != nullptr))
 
 /* * Instruction-space delimited type.  This is for Harvard architectures
    which have separate instruction and data address spaces (and perhaps
@@ -456,6 +457,9 @@ enum dynamic_prop_node_kind
 
   /* A property holding the size of the type.  */
   DYN_PROP_BYTE_SIZE,
+
+  /* A property holding the size of the type, in bits.  */
+  DYN_PROP_BIT_SIZE,
 };
 
 /* * List for dynamic type attributes.  */
diff --git a/gdb/testsuite/gdb.dwarf2/dynamic-bit-size.exp b/gdb/testsuite/gdb.dwarf2/dynamic-bit-size.exp
new file mode 100644 (file)
index 0000000..cd57a93
--- /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_bit_size with an expression.  This is a DWARF extension.
+
+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
+               # This "expression" is constant, but the point of this
+               # is to test the code path for evaluating an
+               # expression here.
+               DW_AT_bit_size {
+                   DW_OP_const1u 32
+               } SPECIAL_expr
+           } {
+               DW_TAG_member {
+                   DW_AT_name disc
+                   DW_AT_type :$byte
+                   DW_AT_data_member_location 0 DW_FORM_sdata
+               }
+
+               DW_TAG_member {
+                   DW_AT_name nums
+                   DW_AT_type :$array
+                   DW_AT_data_bit_offset 8 DW_FORM_udata
+               }
+           }
+
+           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
+}
+
+gdb_test_no_output "set language ada"
+gdb_test "print value" \
+    [string_to_regexp " = (disc => 3, nums => (7, 11, 13))"]