diff mbox

Big-endian targets: Don't ignore offset into DW_OP_stack_value

Message ID m3d1elncfl.fsf@oc1027705133.ibm.com
State New
Headers show

Commit Message

Andreas Arnez Feb. 13, 2017, 7 p.m. UTC
Recently I fixed a bug that caused a DW_OP_implicit_pointer with non-zero
offset into a DW_OP_implicit_value to be handled incorrectly on big-endian
targets.  GDB ignored the offset and copied the wrong bytes:

  https://sourceware.org/ml/gdb-patches/2017-01/msg00251.html

But there is still a similar issue when a DW_OP_implicit_pointer points
into a DW_OP_stack_value instead; and again, the offset is ignored.  There
is an important difference, though: While implicit values are treated like
blocks of data and anchored at the lowest-addressed byte, stack values
traditionally contain integer numbers and are anchored at the *least
significant* byte.  Also, stack values do not come in varying sizes, but
are cut down appropriately when used.  Thus, on big-endian targets the
scenario looks like this (higher addresses shown right):

  |<- - - - - Stack value - - - - - - ->|
                  |                     |
                  |<- original object ->|
                  |
                  | offset ->|####|
			      ^^^^
                              de-referenced
			      implicit pointer

(Note how the original object's size influences the position of the
de-referenced implicit pointer within the stack value.  This is not the
case for little-endian targets, where the original object starts at offset
zero within the stack value.)

This patch implements the logic indicated in the above diagram and adds an
appropriate test case.  A new function dwarf2_fetch_die_type_sect_off is
added; it is used for retrieving the original object's type, so its size
can be determined.  That type is passed to dwarf2_evaluate_loc_desc_full
via a new parameter.

gdb/ChangeLog:

	* dwarf2loc.c (indirect_synthetic_pointer): Get data type of
	pointed-to DIE and pass it to dwarf2_evaluate_loc_desc_full.
	(dwarf2_evaluate_loc_desc_full): Add parameter 'orig_type'.  Fix
	the handling of DWARF_VALUE_STACK on big-endian targets when
	coming via an implicit pointer.
	(dwarf2_evaluate_loc_desc): Adjust call to
	dwarf2_evaluate_loc_desc_full.
	* dwarf2loc.h (dwarf2_fetch_die_type_sect_off): New declaration.
	* dwarf2read.c (dwarf2_fetch_die_type_sect_off): New function.

gdb/testsuite/ChangeLog:

  Andreas Arnez  <arnez@linux.vnet.ibm.com>

	* gdb.dwarf2/nonvar-access.exp: Add test for stack value location
	and implicit pointer into such a location.
---
 gdb/dwarf2loc.c                            | 39 +++++++++++++++---------------
 gdb/dwarf2loc.h                            |  3 +++
 gdb/dwarf2read.c                           | 25 +++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/nonvar-access.exp | 26 +++++++++++++++++++-
 4 files changed, 72 insertions(+), 21 deletions(-)
diff mbox

Patch

diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c
index c1e02eb..65479d8 100644
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -50,7 +50,8 @@  static struct value *dwarf2_evaluate_loc_desc_full (struct type *type,
 						    const gdb_byte *data,
 						    size_t size,
 						    struct dwarf2_per_cu_data *per_cu,
-						    LONGEST byte_offset);
+						    LONGEST byte_offset,
+						    struct type *orig_type);
 
 static struct call_site_parameter *dwarf_expr_reg_to_entry_parameter
     (struct frame_info *frame,
@@ -2098,13 +2099,16 @@  indirect_synthetic_pointer (sect_offset die, LONGEST byte_offset,
     = dwarf2_fetch_die_loc_sect_off (die, per_cu,
 				     get_frame_address_in_block_wrapper, frame);
 
+  /* Get type of pointed-to DIE.  */
+  struct type *orig_type = dwarf2_fetch_die_type_sect_off (die, per_cu);
+
   /* If pointed-to DIE has a DW_AT_location, evaluate it and return the
      resulting value.  Otherwise, it may have a DW_AT_const_value instead,
      or it may've been optimized out.  */
   if (baton.data != NULL)
     return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
 					  baton.data, baton.size, baton.per_cu,
-					  byte_offset);
+					  byte_offset, orig_type);
   else
     return fetch_const_value_from_synthetic_pointer (die, byte_offset, per_cu,
 						     type);
@@ -2269,7 +2273,7 @@  static struct value *
 dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
 			       const gdb_byte *data, size_t size,
 			       struct dwarf2_per_cu_data *per_cu,
-			       LONGEST byte_offset)
+			       LONGEST byte_offset, struct type *orig_type)
 {
   struct value *retval;
   struct objfile *objfile = dwarf2_per_cu_objfile (per_cu);
@@ -2404,18 +2408,15 @@  dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
 	case DWARF_VALUE_STACK:
 	  {
 	    struct value *value = ctx.fetch (0);
-	    gdb_byte *contents;
-	    const gdb_byte *val_bytes;
 	    size_t n = TYPE_LENGTH (value_type (value));
+	    size_t len = TYPE_LENGTH (type);
+	    size_t max = orig_type ? TYPE_LENGTH (orig_type) : len;
+	    struct gdbarch *objfile_gdbarch = get_objfile_arch (objfile);
 	    struct cleanup *cleanup;
 
-	    if (byte_offset + TYPE_LENGTH (type) > n)
+	    if (byte_offset + len > max)
 	      invalid_synthetic_pointer ();
 
-	    val_bytes = value_contents_all (value);
-	    val_bytes += byte_offset;
-	    n -= byte_offset;
-
 	    /* Preserve VALUE because we are going to free values back
 	       to the mark, but we still need the value contents
 	       below.  */
@@ -2424,16 +2425,13 @@  dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
 	    cleanup = make_cleanup_value_free (value);
 
 	    retval = allocate_value (type);
-	    contents = value_contents_raw (retval);
-	    if (n > TYPE_LENGTH (type))
-	      {
-		struct gdbarch *objfile_gdbarch = get_objfile_arch (objfile);
 
-		if (gdbarch_byte_order (objfile_gdbarch) == BFD_ENDIAN_BIG)
-		  val_bytes += n - TYPE_LENGTH (type);
-		n = TYPE_LENGTH (type);
-	      }
-	    memcpy (contents, val_bytes, n);
+	    /* The given offset is relative to the actual object.  */
+	    if (gdbarch_byte_order (objfile_gdbarch) == BFD_ENDIAN_BIG)
+	      byte_offset += n - max;
+
+	    memcpy (value_contents_raw (retval),
+		    value_contents_all (value) + byte_offset, len);
 
 	    do_cleanups (cleanup);
 	  }
@@ -2482,7 +2480,8 @@  dwarf2_evaluate_loc_desc (struct type *type, struct frame_info *frame,
 			  const gdb_byte *data, size_t size,
 			  struct dwarf2_per_cu_data *per_cu)
 {
-  return dwarf2_evaluate_loc_desc_full (type, frame, data, size, per_cu, 0);
+  return dwarf2_evaluate_loc_desc_full (type, frame, data, size, per_cu,
+					0, NULL);
 }
 
 /* Evaluates a dwarf expression and stores the result in VAL, expecting
diff --git a/gdb/dwarf2loc.h b/gdb/dwarf2loc.h
index d6cdbd2..d669d1e 100644
--- a/gdb/dwarf2loc.h
+++ b/gdb/dwarf2loc.h
@@ -77,6 +77,9 @@  extern const gdb_byte *dwarf2_fetch_constant_bytes (sect_offset,
 						    struct obstack *,
 						    LONGEST *);
 
+struct type *dwarf2_fetch_die_type_sect_off (sect_offset,
+					     struct dwarf2_per_cu_data *);
+
 struct type *dwarf2_get_die_type (cu_offset die_offset,
 				  struct dwarf2_per_cu_data *per_cu);
 
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 774ed73..bdaac19 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -20258,6 +20258,31 @@  dwarf2_fetch_constant_bytes (sect_offset offset,
   return result;
 }
 
+/* Return the type of the die at OFFSET in PER_CU.  Return NULL if no
+   valid type for this die is found.  */
+
+struct type *
+dwarf2_fetch_die_type_sect_off (sect_offset offset,
+				struct dwarf2_per_cu_data *per_cu)
+{
+  struct dwarf2_cu *cu;
+  struct die_info *die;
+
+  dw2_setup (per_cu->objfile);
+
+  if (per_cu->cu == NULL)
+    load_cu (per_cu);
+  cu = per_cu->cu;
+  if (!cu)
+    return NULL;
+
+  die = follow_die_offset (offset, per_cu->is_dwz, &cu);
+  if (!die)
+    return NULL;
+
+  return die_type (die, cu);
+}
+
 /* Return the type of the DIE at DIE_OFFSET in the CU named by
    PER_CU.  */
 
diff --git a/gdb/testsuite/gdb.dwarf2/nonvar-access.exp b/gdb/testsuite/gdb.dwarf2/nonvar-access.exp
index 3cb5c49..633c6b3 100644
--- a/gdb/testsuite/gdb.dwarf2/nonvar-access.exp
+++ b/gdb/testsuite/gdb.dwarf2/nonvar-access.exp
@@ -33,7 +33,7 @@  Dwarf::assemble $asm_file {
 	} {
 	    declare_labels int_type_label char_type_label \
 		struct_s_label struct_t_label array_a9_label \
-		char_ptr_label implicit_a_label
+		char_ptr_label implicit_a_label stack_b_label
 
 	    int_type_label: base_type {
 		{name "int"}
@@ -162,6 +162,23 @@  Dwarf::assemble $asm_file {
 			GNU_implicit_pointer $implicit_a_label 5
 		    } SPECIAL_expr}
 		}
+		# Stack-value location.
+		stack_b_label: DW_TAG_variable {
+		    {name def_stack_b}
+		    {type :$struct_t_label}
+		    {location {
+			const4u 0x1a2b3c4d
+			stack_value
+		    } SPECIAL_expr}
+		}
+		# Implicit pointer into stack value.
+		DW_TAG_variable {
+		    {name implicit_b_ptr}
+		    {type :$char_ptr_label}
+		    {location {
+			GNU_implicit_pointer $stack_b_label 1
+		    } SPECIAL_expr}
+		}
 	    }
 	}
     }
@@ -194,6 +211,13 @@  gdb_test "print/x def_implicit_a" \
 gdb_test "print/x def_implicit_a\[5\]" " = 0x56"
 gdb_test "print/x *(char (*)\[5\]) implicit_a_ptr" \
     " = \\{0x56, 0x67, 0x78, 0x89, 0x9a\\}"
+switch $endian {
+    big {set val "a = 52, b = 2833485"}
+    little {set val "a = 77, b = 857502"}
+}
+gdb_test "print def_stack_b" " = \\{$val\\}"
+switch $endian {big {set val 0x2b} little {set val 0x3c}}
+gdb_test "print/x *implicit_b_ptr" " = $val"
 
 # Byte-aligned fields, pieced together from DWARF stack values.
 gdb_test "print def_s" " = \\{a = 0, b = -1\\}"