Fix crash in value_print_array_elements
authorTom Tromey <tromey@adacore.com>
Tue, 27 Sep 2022 18:53:25 +0000 (12:53 -0600)
committerTom Tromey <tromey@adacore.com>
Fri, 21 Oct 2022 15:40:59 +0000 (09:40 -0600)
A user noticed that gdb would crash when printing a packed array after
doing "set lang c".  Packed arrays don't exist in C, but it's
occasionally useful to print things in C mode when working in a non-C
language -- this lets you see under the hood a little bit.

The bug here is that generic value printing does not handle packed
arrays at all.  This patch fixes the bug by introducing a new function
to extract a value from a bit offset and width.

The new function includes a hack to avoid problems with some existing
test cases when using -fgnat-encodings=all.  Cleaning up this code
looked difficult, and since "all" is effectively deprecated, I thought
it made sense to simply work around the problems.

gdb/testsuite/gdb.ada/packed_array.exp
gdb/valprint.c
gdb/value.c
gdb/value.h

index df34f31348a625b103ace6ac364ac5c5237c53cb..e73298ec84c96373ac21811b888b2df82da5ef00 100644 (file)
@@ -63,4 +63,17 @@ foreach_with_prefix scenario {all minimal} {
        [string_to_regexp " = ($line, $line, $line, $line)"]
     gdb_test "print o2_var" \
        [string_to_regexp " = ($line, $line, $line, $line)"]
+
+    # This is a regression test for a crash with
+    # -fgnat-encodings=minimal, and with 'all' the output is unusual,
+    # so restrict the test.
+    if {$scenario == "minimal"} {
+       set line "{true, false, true, false, true}"
+
+       gdb_test "set lang c" \
+           "Warning: the current language does not match this frame."
+       gdb_test "print o2_var" \
+           [string_to_regexp " = {$line, $line, $line, $line}"] \
+           "print o2_var in c mode"
+    }
 }
index 685f89006280c95f91d77d10babdf7e859de1d01..25db57ea7946c9c53065d34f6de457265dd09afa 100644 (file)
@@ -1927,7 +1927,6 @@ value_print_array_elements (struct value *val, struct ui_file *stream,
   unsigned int things_printed = 0;
   unsigned len;
   struct type *elttype, *index_type;
-  unsigned eltlen;
   /* Position of the array element we are examining to see
      whether it is repeated.  */
   unsigned int rep1;
@@ -1938,7 +1937,9 @@ value_print_array_elements (struct value *val, struct ui_file *stream,
   struct type *type = check_typedef (value_type (val));
 
   elttype = type->target_type ();
-  eltlen = type_length_units (check_typedef (elttype));
+  unsigned bit_stride = type->bit_stride ();
+  if (bit_stride == 0)
+    bit_stride = 8 * check_typedef (elttype)->length ();
   index_type = type->index_type ();
   if (index_type->code () == TYPE_CODE_RANGE)
     index_type = index_type->target_type ();
@@ -1987,23 +1988,28 @@ value_print_array_elements (struct value *val, struct ui_file *stream,
       maybe_print_array_index (index_type, i + low_bound,
                               stream, options);
 
+      struct value *element = value_from_component_bitsize (val, elttype,
+                                                           bit_stride * i,
+                                                           bit_stride);
       rep1 = i + 1;
       reps = 1;
       /* Only check for reps if repeat_count_threshold is not set to
         UINT_MAX (unlimited).  */
       if (options->repeat_count_threshold < UINT_MAX)
        {
-         while (rep1 < len
-                && value_contents_eq (val, i * eltlen,
-                                      val, rep1 * eltlen,
-                                      eltlen))
+         while (rep1 < len)
            {
+             struct value *rep_elt
+               = value_from_component_bitsize (val, elttype,
+                                               rep1 * bit_stride,
+                                               bit_stride);
+             if (!value_contents_eq (element, rep_elt))
+               break;
              ++reps;
              ++rep1;
            }
        }
 
-      struct value *element = value_from_component (val, elttype, eltlen * i);
       common_val_print (element, stream, recurse + 1, options,
                        current_language);
 
index 470286302597fd3413d372b7160804c5fe0cc063..74af654c4510c11acbba9a22e90679b60e26f4ce 100644 (file)
@@ -916,6 +916,17 @@ value_contents_eq (const struct value *val1, LONGEST offset1,
                                 length * TARGET_CHAR_BIT);
 }
 
+/* See value.h.  */
+
+bool
+value_contents_eq (const struct value *val1, const struct value *val2)
+{
+  ULONGEST len1 = check_typedef (value_enclosing_type (val1))->length ();
+  ULONGEST len2 = check_typedef (value_enclosing_type (val2))->length ();
+  if (len1 != len2)
+    return false;
+  return value_contents_eq (val1, 0, val2, 0, len1);
+}
 
 /* The value-history records all the values printed by print commands
    during this session.  */
@@ -1368,6 +1379,43 @@ value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
                              bit_length);
 }
 
+/* A helper for value_from_component_bitsize that copies bits from SRC
+   to DEST.  */
+
+static void
+value_contents_copy_raw_bitwise (struct value *dst, LONGEST dst_bit_offset,
+                                struct value *src, LONGEST src_bit_offset,
+                                LONGEST bit_length)
+{
+  /* A lazy DST would make that this copy operation useless, since as
+     soon as DST's contents were un-lazied (by a later value_contents
+     call, say), the contents would be overwritten.  A lazy SRC would
+     mean we'd be copying garbage.  */
+  gdb_assert (!dst->lazy && !src->lazy);
+
+  /* The overwritten DST range gets unavailability ORed in, not
+     replaced.  Make sure to remember to implement replacing if it
+     turns out actually necessary.  */
+  LONGEST dst_offset = dst_bit_offset / TARGET_CHAR_BIT;
+  LONGEST length = bit_length / TARGET_CHAR_BIT;
+  gdb_assert (value_bytes_available (dst, dst_offset, length));
+  gdb_assert (!value_bits_any_optimized_out (dst, dst_bit_offset,
+                                            bit_length));
+
+  /* Copy the data.  */
+  gdb::array_view<gdb_byte> dst_contents = value_contents_all_raw (dst);
+  gdb::array_view<const gdb_byte> src_contents = value_contents_all_raw (src);
+  copy_bitwise (dst_contents.data (), dst_bit_offset,
+               src_contents.data (), src_bit_offset,
+               bit_length,
+               type_byte_order (value_type (src)) == BFD_ENDIAN_BIG);
+
+  /* Copy the meta-data.  */
+  value_ranges_copy_adjusted (dst, dst_bit_offset,
+                             src, src_bit_offset,
+                             bit_length);
+}
+
 /* Copy LENGTH bytes of SRC value's (all) contents
    (value_contents_all) starting at SRC_OFFSET byte, into DST value's
    (all) contents, starting at DST_OFFSET.  If unavailable contents
@@ -3774,6 +3822,37 @@ value_from_component (struct value *whole, struct type *type, LONGEST offset)
   return v;
 }
 
+/* See value.h.  */
+
+struct value *
+value_from_component_bitsize (struct value *whole, struct type *type,
+                             LONGEST bit_offset, LONGEST bit_length)
+{
+  gdb_assert (!value_lazy (whole));
+
+  /* Preserve lvalue-ness if possible.  This is needed to avoid
+     array-printing failures (including crashes) when printing Ada
+     arrays in programs compiled with -fgnat-encodings=all.  */
+  if ((bit_offset % TARGET_CHAR_BIT) == 0
+      && (bit_length % TARGET_CHAR_BIT) == 0
+      && bit_length == TARGET_CHAR_BIT * type->length ())
+    return value_from_component (whole, type, bit_offset / TARGET_CHAR_BIT);
+
+  struct value *v = allocate_value (type);
+
+  LONGEST dst_offset = TARGET_CHAR_BIT * value_embedded_offset (v);
+  if (is_scalar_type (type) && type_byte_order (type) == BFD_ENDIAN_BIG)
+    dst_offset += TARGET_CHAR_BIT * type->length () - bit_length;
+
+  value_contents_copy_raw_bitwise (v, dst_offset,
+                                  whole,
+                                  TARGET_CHAR_BIT
+                                  * value_embedded_offset (whole)
+                                  + bit_offset,
+                                  bit_length);
+  return v;
+}
+
 struct value *
 coerce_ref_if_computed (const struct value *arg)
 {
index d4b4f95a9c5813a6f3fe261703579d1795773a3d..2d148ce13d3d4998fc9bb152ae92bc5c229ba847 100644 (file)
@@ -599,6 +599,12 @@ extern bool value_contents_eq (const struct value *val1, LONGEST offset1,
                               const struct value *val2, LONGEST offset2,
                               LONGEST length);
 
+/* An overload of value_contents_eq that compares the entirety of both
+   values.  */
+
+extern bool value_contents_eq (const struct value *val1,
+                              const struct value *val2);
+
 /* Read LENGTH addressable memory units starting at MEMADDR into BUFFER,
    which is (or will be copied to) VAL's contents buffer offset by
    BIT_OFFSET bits.  Marks value contents ranges as unavailable if
@@ -687,6 +693,21 @@ extern struct value *value_from_history_ref (const char *, const char **);
 extern struct value *value_from_component (struct value *, struct type *,
                                           LONGEST);
 
+
+/* Create a new value by extracting it from WHOLE.  TYPE is the type
+   of the new value.  BIT_OFFSET and BIT_LENGTH describe the offset
+   and field width of the value to extract from WHOLE -- BIT_LENGTH
+   may differ from TYPE's length in the case where WHOLE's type is
+   packed.
+
+   When the value does come from a non-byte-aligned offset or field
+   width, it will be marked non_lval.  */
+
+extern struct value *value_from_component_bitsize (struct value *whole,
+                                                  struct type *type,
+                                                  LONGEST bit_offset,
+                                                  LONGEST bit_length);
+
 extern struct value *value_at (struct type *type, CORE_ADDR addr);
 extern struct value *value_at_lazy (struct type *type, CORE_ADDR addr);