]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Handle base type without DW_AT_byte_size
authorTom Tromey <tromey@adacore.com>
Fri, 25 Apr 2025 14:13:36 +0000 (08:13 -0600)
committerTom Tromey <tromey@adacore.com>
Tue, 29 Apr 2025 17:34:49 +0000 (11:34 -0600)
DWARF says that a base type can have DW_AT_bit_size, without
DW_AT_byte_size.  However, gdb does not correctly handle this; in
fact, it crashes, as pointed out in this LLVM merge request:

    https://github.com/llvm/llvm-project/pull/137123

This patch reworks the base type size logic a bit to handle this
situation.

Tested-by: Kevin Buettner <kevinb@redhat.com>
Approved-by: Kevin Buettner <kevinb@redhat.com>
gdb/dwarf2/read.c
gdb/testsuite/gdb.dwarf2/intbits.c
gdb/testsuite/gdb.dwarf2/intbits.exp

index 6b7f2c784768a535109cc42af358dd416d021c43..a177a056e1359a85acf8602839360117bc19f8f0 100644 (file)
@@ -13634,7 +13634,6 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu)
   struct type *type;
   struct attribute *attr;
   ULONGEST encoding = 0;
-  int bits = 0;
   const char *name;
 
   attr = dwarf2_attr (die, DW_AT_encoding, cu);
@@ -13644,9 +13643,33 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu)
       if (value.has_value ())
        encoding = *value;
     }
+
   attr = dwarf2_attr (die, DW_AT_byte_size, cu);
+  std::optional<ULONGEST> byte_size;
+  if (attr != nullptr)
+    byte_size = attr->unsigned_constant ();
+  attr = dwarf2_attr (die, DW_AT_bit_size, cu);
+  std::optional<ULONGEST> bit_size;
   if (attr != nullptr)
-    bits = attr->unsigned_constant ().value_or (0) * TARGET_CHAR_BIT;
+    bit_size = attr->unsigned_constant ();
+
+  attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu);
+  std::optional<ULONGEST> bit_offset;
+  if (attr != nullptr)
+    bit_offset = attr->unsigned_constant ();
+
+  int bits = 0;
+  if (byte_size.has_value ())
+    bits = TARGET_CHAR_BIT * *byte_size;
+  else if (bit_size.has_value ())
+    bits = align_up (*bit_size, 8);
+  else
+    {
+      /* No size, so arrange for an error type.  */
+      complaint (_("DW_TAG_base_type has neither bit- nor byte-size"));
+      encoding = (ULONGEST) -1;
+    }
+
   name = dwarf2_full_name (nullptr, die, cu);
   if (!name)
     complaint (_("DW_AT_name missing from DW_TAG_base_type"));
@@ -13792,35 +13815,21 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu)
 
   type->set_endianity_is_not_default (not_default);
 
-  if (TYPE_SPECIFIC_FIELD (type) == TYPE_SPECIFIC_INT)
+  /* If both a byte size and bit size were provided, then that means
+     that not every bit in the object contributes to the value.  */
+  if (TYPE_SPECIFIC_FIELD (type) == TYPE_SPECIFIC_INT
+      && byte_size.has_value ()
+      && bit_size.has_value ())
     {
-      attr = dwarf2_attr (die, DW_AT_bit_size, cu);
-      if (attr != nullptr && attr->form_is_constant ())
+      /* DWARF says: If this attribute is omitted a default data bit
+        offset of zero is assumed.  */
+      ULONGEST offset = bit_offset.value_or (0);
+
+      /* Only use the attributes if they make sense together.  */
+      if (*bit_size + offset <= 8 * type->length ())
        {
-         unsigned real_bit_size = attr->unsigned_constant ().value_or (0);
-         if (real_bit_size >= 0 && real_bit_size <= 8 * type->length ())
-           {
-             attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu);
-             /* Only use the attributes if they make sense together.  */
-             std::optional<ULONGEST> bit_offset;
-             if (attr == nullptr)
-               bit_offset = 0;
-             else if (attr->form_is_constant ())
-               {
-                 bit_offset = attr->unsigned_constant ();
-                 if (bit_offset.has_value ()
-                     && *bit_offset + real_bit_size > 8 * type->length ())
-                   bit_offset.reset ();
-               }
-             if (bit_offset.has_value ())
-               {
-                 TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_size
-                   = real_bit_size;
-                 if (attr != nullptr)
-                   TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_offset
-                     = *bit_offset;
-               }
-           }
+         TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_size = *bit_size;
+         TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_offset = offset;
        }
     }
 
index 82e6ae8a0525fe930bcdf95580f18c46a0b4b5ba..909d2837b9fab849a62ae4b0bd94f115c85ddbb1 100644 (file)
@@ -41,6 +41,9 @@ unsigned char be30_1_off[4] = { 0x80, 0, 0, 2 };
    here, to catch any situation where gdb tries to use the memory.  */
 unsigned char u32_0[4] = { 0xff, 0xff, 0xff, 0xff };
 
+/* An 8 bit slot holding a 3 bit value.  */
+unsigned char just_bit_0 = 5;
+
 int
 main (void)
 {
index 7b50e15a0cd2af9e0533303fdd4372ef44704b1e..ff1d69ae2263b59b8901060925c8858a2e5067ad 100644 (file)
@@ -36,7 +36,7 @@ Dwarf::assemble ${asm_file} {
            {DW_AT_language @DW_LANG_C_plus_plus}
        } {
            declare_labels i7_type u1_type u17_type u31_type \
-               u31_1_type u32_0_type u0_0_type be30_1_type
+               u31_1_type u32_0_type u0_0_type be30_1_type just_bit_type
 
            i7_type: DW_TAG_base_type {
                {DW_AT_encoding @DW_ATE_signed}
@@ -167,6 +167,20 @@ Dwarf::assemble ${asm_file} {
                {DW_AT_location {DW_OP_addr [gdb_target_symbol "u32_0"]}
                    SPECIAL_expr}
            }
+
+           just_bit_type: DW_TAG_base_type {
+               {DW_AT_encoding @DW_ATE_unsigned}
+               {DW_AT_name "just_bit_type"}
+               {DW_AT_bit_size 3 DW_FORM_udata}
+           }
+
+           DW_TAG_variable {
+               {DW_AT_name "v_just_bit"}
+               {DW_AT_type :${just_bit_type}}
+               {DW_AT_external 1 DW_FORM_flag}
+               {DW_AT_location {DW_OP_addr [gdb_target_symbol "just_bit_0"]}
+                   SPECIAL_expr}
+           }
        }
     }
 }
@@ -197,3 +211,6 @@ gdb_test "x/4xb &v_u32_1_off" ":\t0x0e\t0x00\t0x00\t0x00"
 gdb_test "print v_be30_1_off" "= 1"
 gdb_test "print v_be30_1_off = 7" " = 7"
 gdb_test "x/4xb &v_be30_1_off" ":\t0x00\t0x00\t0x00\t0x0e"
+
+gdb_test "print/x v_just_bit" " = 0x5"
+gdb_test "print/x (just_bit_type) 5" " = 0x5"