This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[commit/c++] Do not require debug info to find the vtable


Here's a change I've been meaning to make for ages.

Currently GDB relies on TYPE_VPTR_BASETYPE and TYPE_VPTR_FIELDNO to
(A) determine whether a class should have a vtable, and (B) locate the
field corresponding to the vtable pointer in GDB's internal type
representation.

In the Itanium C++ ABI (gnu-v3-abi.c), this is silly.  The location of
the field is very simple: it's at offset 0 if it exists.  So cast
appropriately and load it directly.

This does mean figuring out whether the class has a vtable pointer.
The ABI gives simple rules for a "dynamic class".  Anything with a
virtual base class, a dynamic base class, or a virtual method is
itself dynamic.  Nothing else is dynamic.  To stop things from being
to simple, this did turn up a GCC PR (debug/37237) which I've added a
complaint and a workaround for.

This lets us remove the dependence on TYPE_VPTR_FIELDNO, which is
currently set by the nasty hack of looking for the names various
compilers give this artificial field.  And by detecting that the field
is artificial, we can even hide it in ptype output without recognizing
a magic name.  I have patches to recognize the names used by various
versions of RealView, but they predate this patch; I hope I can revert
them instead of adding to the existing (GCC and xlC) list.

Tested on arm-none-eabi and x86_64-linux.  Checked in.

2009-11-12  Daniel Jacobowitz  <dan@codesourcery.com>
	    Paul Brook  <paul@codesourcery.com>

	gdb/
	* c-typeprint.c (c_type_print_base): Skip artificial fields.
	Use get_vptr_fieldno to skip the vtable pointer.
	* dwarf2read.c (dwarf2_add_field): Set FIELD_ARTIFICIAL on artificial
	fields.
	(dwarf2_add_member_fn): Complain about virtual member functions
	without DW_AT_vtable_elem_location and force TYPE_CPLUS_DYNAMIC.
	* gdbtypes.c (get_vptr_fieldno): Update comment.
	* gdbtypes.h (struct cplus_struct_type): Add is_dynamic.
	(TYPE_CPLUS_DYNAMIC): New macro.
	* gnu-v3-abi.c (gnuv3_dynamic_class): New.
	(gnuv3_get_vtable): Rewrite to use gnuv3_dynamic_class.  Move higher.
	(gnuv3_rtti_type, gnuv3_get_virtual_fn, gnuv3_baseclass_offset): Use
	gnuv3_get_vtable.
	* varobj.c (cplus_class_num_children, cplus_describe_child): Skip
	artificial fields.  Use get_vptr_fieldno to skip the vtable pointer.

---
 gdb/c-typeprint.c |   13 ++-
 gdb/dwarf2read.c  |   13 +++
 gdb/gdbtypes.c    |    3 
 gdb/gdbtypes.h    |    8 ++
 gdb/gnu-v3-abi.c  |  185 ++++++++++++++++++++++++++----------------------------
 gdb/varobj.c      |   19 ++++-
 6 files changed, 138 insertions(+), 103 deletions(-)

Index: gdb/c-typeprint.c
===================================================================
--- gdb/c-typeprint.c.orig	2009-11-12 06:58:09.000000000 -0800
+++ gdb/c-typeprint.c	2009-11-12 07:08:50.000000000 -0800
@@ -761,6 +761,9 @@ c_type_print_base (struct type *type, st
 	}
       else if (show > 0 || TYPE_TAG_NAME (type) == NULL)
 	{
+	  struct type *basetype;
+	  int vptr_fieldno;
+
 	  cp_type_print_derivation_info (stream, type);
 
 	  fprintf_filtered (stream, "{\n");
@@ -848,12 +851,16 @@ c_type_print_base (struct type *type, st
 	     do not print the field that it occupies.  */
 
 	  len = TYPE_NFIELDS (type);
+	  vptr_fieldno = get_vptr_fieldno (type, &basetype);
 	  for (i = TYPE_N_BASECLASSES (type); i < len; i++)
 	    {
 	      QUIT;
-	      /* Don't print out virtual function table.  */
-	      if (strncmp (TYPE_FIELD_NAME (type, i), "_vptr", 5) == 0
-		  && is_cplus_marker ((TYPE_FIELD_NAME (type, i))[5]))
+
+	      /* If we have a virtual table pointer, omit it.  Even if
+		 virtual table pointers are not specifically marked in
+		 the debug info, they should be artificial.  */
+	      if ((type == basetype && i == vptr_fieldno)
+		  || TYPE_FIELD_ARTIFICIAL (type, i))
 		continue;
 
 	      /* If this is a C++ class we can print the various C++ section
Index: gdb/dwarf2read.c
===================================================================
--- gdb/dwarf2read.c.orig	2009-11-12 06:58:38.000000000 -0800
+++ gdb/dwarf2read.c	2009-11-12 07:08:50.000000000 -0800
@@ -4482,6 +4482,7 @@ dwarf2_add_field (struct field_info *fip
          pointer or virtual base class pointer) to private.  */
       if (dwarf2_attr (die, DW_AT_artificial, cu))
 	{
+	  FIELD_ARTIFICIAL (*fp) = 1;
 	  new_field->accessibility = DW_ACCESS_private;
 	  fip->non_public_fields = 1;
 	}
@@ -4801,6 +4802,18 @@ dwarf2_add_member_fn (struct field_info 
       else
 	dwarf2_complex_location_expr_complaint ();
     }
+  else
+    {
+      attr = dwarf2_attr (die, DW_AT_virtuality, cu);
+      if (attr && DW_UNSND (attr))
+	{
+	  /* GCC does this, as of 2008-08-25; PR debug/37237.  */
+	  complaint (&symfile_complaints,
+		     _("Member function \"%s\" (offset %d) is virtual but the vtable offset is not specified"),
+		     fieldname, die->offset);
+	  TYPE_CPLUS_DYNAMIC (type) = 1;
+	}
+    }
 }
 
 /* Create the vector of member function fields, and attach it to the type.  */
Index: gdb/gdbtypes.c
===================================================================
--- gdb/gdbtypes.c.orig	2009-11-12 06:58:09.000000000 -0800
+++ gdb/gdbtypes.c	2009-11-12 07:08:50.000000000 -0800
@@ -1277,7 +1277,8 @@ lookup_struct_elt_type (struct type *typ
    If not found, return -1 and ignore BASETYPEP.
    Callers should be aware that in some cases (for example,
    the type or one of its baseclasses is a stub type and we are
-   debugging a .o file), this function will not be able to find the
+   debugging a .o file, or the compiler uses DWARF-2 and is not GCC),
+   this function will not be able to find the
    virtual function table pointer, and vptr_fieldno will remain -1 and
    vptr_basetype will remain NULL or incomplete.  */
 
Index: gdb/gdbtypes.h
===================================================================
--- gdb/gdbtypes.h.orig	2009-11-12 06:58:09.000000000 -0800
+++ gdb/gdbtypes.h	2009-11-12 07:08:50.000000000 -0800
@@ -775,6 +775,13 @@ struct cplus_struct_type
 	int line;
       }
      *localtype_ptr;
+
+    /* One if this struct is a dynamic class, as defined by the
+       Itanium C++ ABI: if it requires a virtual table pointer,
+       because it or any of its base classes have one or more virtual
+       member functions or virtual base classes.  Minus one if not
+       dynamic.  Zero if not yet computed.  */
+    int is_dynamic : 2;
   };
 
 /* Struct used in computing virtual base list */
@@ -861,6 +868,7 @@ extern void allocate_cplus_struct_type (
 #define TYPE_BASECLASS_BITPOS(thistype,index) TYPE_FIELD_BITPOS(thistype,index)
 #define BASETYPE_VIA_PUBLIC(thistype, index) \
   ((!TYPE_FIELD_PRIVATE(thistype, index)) && (!TYPE_FIELD_PROTECTED(thistype, index)))
+#define TYPE_CPLUS_DYNAMIC(thistype) TYPE_CPLUS_SPECIFIC (thistype)->is_dynamic
 
 #define BASETYPE_VIA_VIRTUAL(thistype, index) \
   (TYPE_CPLUS_SPECIFIC(thistype)->virtual_field_bits == NULL ? 0 \
Index: gdb/gnu-v3-abi.c
===================================================================
--- gdb/gnu-v3-abi.c.orig	2009-11-12 06:58:09.000000000 -0800
+++ gdb/gnu-v3-abi.c	2009-11-12 07:08:50.000000000 -0800
@@ -190,23 +190,96 @@ vtable_address_point_offset (struct gdba
 }
 
 
+/* Determine whether structure TYPE is a dynamic class.  Cache the
+   result.  */
+
+static int
+gnuv3_dynamic_class (struct type *type)
+{
+  int fieldnum, fieldelem;
+
+  if (TYPE_CPLUS_DYNAMIC (type))
+    return TYPE_CPLUS_DYNAMIC (type) == 1;
+
+  ALLOCATE_CPLUS_STRUCT_TYPE (type);
+
+  for (fieldnum = 0; fieldnum < TYPE_N_BASECLASSES (type); fieldnum++)
+    if (BASETYPE_VIA_VIRTUAL (type, fieldnum)
+	|| gnuv3_dynamic_class (TYPE_FIELD_TYPE (type, fieldnum)))
+      {
+	TYPE_CPLUS_DYNAMIC (type) = 1;
+	return 1;
+      }
+
+  for (fieldnum = 0; fieldnum < TYPE_NFN_FIELDS (type); fieldnum++)
+    for (fieldelem = 0; fieldelem < TYPE_FN_FIELDLIST_LENGTH (type, fieldnum);
+	 fieldelem++)
+      {
+	struct fn_field *f = TYPE_FN_FIELDLIST1 (type, fieldnum);
+
+	if (TYPE_FN_FIELD_VIRTUAL_P (f, fieldelem))
+	  {
+	    TYPE_CPLUS_DYNAMIC (type) = 1;
+	    return 1;
+	  }
+      }
+
+  TYPE_CPLUS_DYNAMIC (type) = -1;
+  return 0;
+}
+
+/* Find the vtable for a value of CONTAINER_TYPE located at
+   CONTAINER_ADDR.  Return a value of the correct vtable type for this
+   architecture, or NULL if CONTAINER does not have a vtable.  */
+
+static struct value *
+gnuv3_get_vtable (struct gdbarch *gdbarch,
+		  struct type *container_type, CORE_ADDR container_addr)
+{
+  struct type *vtable_type = gdbarch_data (gdbarch,
+					   vtable_type_gdbarch_data);
+  struct type *vtable_pointer_type;
+  struct value *vtable_pointer;
+  CORE_ADDR vtable_address;
+
+  /* If this type does not have a virtual table, don't read the first
+     field.  */
+  if (!gnuv3_dynamic_class (check_typedef (container_type)))
+    return NULL;
+
+  /* We do not consult the debug information to find the virtual table.
+     The ABI specifies that it is always at offset zero in any class,
+     and debug information may not represent it.
+
+     We avoid using value_contents on principle, because the object might
+     be large.  */
+
+  /* Find the type "pointer to virtual table".  */
+  vtable_pointer_type = lookup_pointer_type (vtable_type);
+
+  /* Load it from the start of the class.  */
+  vtable_pointer = value_at (vtable_pointer_type, container_addr);
+  vtable_address = value_as_address (vtable_pointer);
+
+  /* Correct it to point at the start of the virtual table, rather
+     than the address point.  */
+  return value_at_lazy (vtable_type,
+			vtable_address - vtable_address_point_offset (gdbarch));
+}
+
+
 static struct type *
 gnuv3_rtti_type (struct value *value,
                  int *full_p, int *top_p, int *using_enc_p)
 {
   struct gdbarch *gdbarch;
-  struct type *vtable_type;
   struct type *values_type = check_typedef (value_type (value));
-  CORE_ADDR vtable_address;
   struct value *vtable;
   struct minimal_symbol *vtable_symbol;
   const char *vtable_symbol_name;
   const char *class_name;
   struct type *run_time_type;
-  struct type *base_type;
   LONGEST offset_to_top;
-  struct type *values_type_vptr_basetype;
-  int values_type_vptr_fieldno;
 
   /* We only have RTTI for class objects.  */
   if (TYPE_CODE (values_type) != TYPE_CODE_CLASS)
@@ -214,33 +287,15 @@ gnuv3_rtti_type (struct value *value,
 
   /* Determine architecture.  */
   gdbarch = get_type_arch (values_type);
-  vtable_type = gdbarch_data (gdbarch, vtable_type_gdbarch_data);
-
-  /* If we can't find the virtual table pointer for values_type, we
-     can't find the RTTI.  */
-  values_type_vptr_fieldno = get_vptr_fieldno (values_type,
-					       &values_type_vptr_basetype);
-  if (values_type_vptr_fieldno == -1)
-    return NULL;
 
   if (using_enc_p)
     *using_enc_p = 0;
 
-  /* Fetch VALUE's virtual table pointer, and tweak it to point at
-     an instance of our imaginary gdb_gnu_v3_abi_vtable structure.  */
-  base_type = check_typedef (values_type_vptr_basetype);
-  if (values_type != base_type)
-    {
-      value = value_cast (base_type, value);
-      if (using_enc_p)
-	*using_enc_p = 1;
-    }
-  vtable_address
-    = value_as_address (value_field (value, values_type_vptr_fieldno));
-  vtable
-    = value_at_lazy (vtable_type,
-		     vtable_address - vtable_address_point_offset (gdbarch));
-  
+  vtable = gnuv3_get_vtable (gdbarch, value_type (value),
+			     value_as_address (value_addr (value)));
+  if (vtable == NULL)
+    return NULL;
+
   /* Find the linker symbol for this vtable.  */
   vtable_symbol
     = lookup_minimal_symbol_by_pc (value_address (vtable)
@@ -282,45 +337,9 @@ gnuv3_rtti_type (struct value *value,
                    >= TYPE_LENGTH (run_time_type)));
   if (top_p)
     *top_p = - offset_to_top;
-
   return run_time_type;
 }
 
-/* Find the vtable for CONTAINER and return a value of the correct
-   vtable type for this architecture.  */
-
-static struct value *
-gnuv3_get_vtable (struct gdbarch *gdbarch, struct value *container)
-{
-  struct type *vtable_type = gdbarch_data (gdbarch, vtable_type_gdbarch_data);
-  struct type *vtable_pointer_type;
-  struct value *vtable_pointer;
-  CORE_ADDR vtable_pointer_address, vtable_address;
-
-  /* We do not consult the debug information to find the virtual table.
-     The ABI specifies that it is always at offset zero in any class,
-     and debug information may not represent it.  We won't issue an
-     error if there's a class with virtual functions but no virtual table
-     pointer, but something's already gone seriously wrong if that
-     happens.
-
-     We avoid using value_contents on principle, because the object might
-     be large.  */
-
-  /* Find the type "pointer to virtual table".  */
-  vtable_pointer_type = lookup_pointer_type (vtable_type);
-
-  /* Load it from the start of the class.  */
-  vtable_pointer_address = value_as_address (value_addr (container));
-  vtable_pointer = value_at (vtable_pointer_type, vtable_pointer_address);
-  vtable_address = value_as_address (vtable_pointer);
-
-  /* Correct it to point at the start of the virtual table, rather
-     than the address point.  */
-  return value_at_lazy (vtable_type,
-			vtable_address - vtable_address_point_offset (gdbarch));
-}
-
 /* Return a function pointer for CONTAINER's VTABLE_INDEX'th virtual
    function, of type FNTYPE.  */
 
@@ -328,8 +347,12 @@ static struct value *
 gnuv3_get_virtual_fn (struct gdbarch *gdbarch, struct value *container,
 		      struct type *fntype, int vtable_index)
 {
-  struct value *vtable = gnuv3_get_vtable (gdbarch, container);
-  struct value *vfn;
+  struct value *vtable, *vfn;
+
+  /* Every class with virtual functions must have a vtable.  */
+  vtable = gnuv3_get_vtable (gdbarch, value_type (container),
+			     value_as_address (value_addr (container)));
+  gdb_assert (vtable != NULL);
 
   /* Fetch the appropriate function pointer from the vtable.  */
   vfn = value_subscript (value_field (vtable, vtable_field_virtual_functions),
@@ -389,18 +412,13 @@ gnuv3_baseclass_offset (struct type *typ
 			CORE_ADDR address)
 {
   struct gdbarch *gdbarch;
-  struct type *vtable_type;
   struct type *ptr_type;
   struct value *vtable;
-  struct type *vbasetype;
   struct value *vbase_array;
-  CORE_ADDR vtable_address;
   long int cur_base_offset, base_offset;
-  int vbasetype_vptr_fieldno;
 
   /* Determine architecture.  */
   gdbarch = get_type_arch (type);
-  vtable_type = gdbarch_data (gdbarch, vtable_type_gdbarch_data);
   ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
 
   /* If it isn't a virtual base, this is easy.  The offset is in the
@@ -422,29 +440,8 @@ gnuv3_baseclass_offset (struct type *typ
     error (_("Misaligned vbase offset."));
   cur_base_offset = cur_base_offset / ((int) TYPE_LENGTH (ptr_type));
 
-  /* We're now looking for the cur_base_offset'th entry (negative index)
-     in the vcall_and_vbase_offsets array.  We used to cast the object to
-     its TYPE_VPTR_BASETYPE, and reference the vtable as TYPE_VPTR_FIELDNO;
-     however, that cast can not be done without calling baseclass_offset again
-     if the TYPE_VPTR_BASETYPE is a virtual base class, as described in the
-     v3 C++ ABI Section 2.4.I.2.b.  Fortunately the ABI guarantees that the
-     vtable pointer will be located at the beginning of the object, so we can
-     bypass the casting.  Verify that the TYPE_VPTR_FIELDNO is in fact at the
-     start of whichever baseclass it resides in, as a sanity measure - iff
-     we have debugging information for that baseclass.  */
-
-  vbasetype = check_typedef (TYPE_VPTR_BASETYPE (type));
-  vbasetype_vptr_fieldno = get_vptr_fieldno (vbasetype, NULL);
-
-  if (vbasetype_vptr_fieldno >= 0
-      && TYPE_FIELD_BITPOS (vbasetype, vbasetype_vptr_fieldno) != 0)
-    error (_("Illegal vptr offset in class %s"),
-	   TYPE_NAME (vbasetype) ? TYPE_NAME (vbasetype) : "<unknown>");
-
-  vtable_address = value_as_address (value_at_lazy (ptr_type, address));
-  vtable
-    = value_at_lazy (vtable_type,
-		     vtable_address - vtable_address_point_offset (gdbarch));
+  vtable = gnuv3_get_vtable (gdbarch, type, address);
+  gdb_assert (vtable != NULL);
   vbase_array = value_field (vtable, vtable_field_vcall_and_vbase_offsets);
   base_offset = value_as_long (value_subscript (vbase_array, cur_base_offset));
   return base_offset;
Index: gdb/varobj.c
===================================================================
--- gdb/varobj.c.orig	2009-11-12 06:58:09.000000000 -0800
+++ gdb/varobj.c	2009-11-12 07:08:50.000000000 -0800
@@ -3103,16 +3103,21 @@ cplus_number_of_children (struct varobj 
 static void
 cplus_class_num_children (struct type *type, int children[3])
 {
-  int i;
+  int i, vptr_fieldno;
+  struct type *basetype = NULL;
 
   children[v_public] = 0;
   children[v_private] = 0;
   children[v_protected] = 0;
 
+  vptr_fieldno = get_vptr_fieldno (type, &basetype);
   for (i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++)
     {
-      /* If we have a virtual table pointer, omit it. */
-      if (TYPE_VPTR_BASETYPE (type) == type && TYPE_VPTR_FIELDNO (type) == i)
+      /* If we have a virtual table pointer, omit it.  Even if virtual
+	 table pointers are not specifically marked in the debug info,
+	 they should be artificial.  */
+      if ((type == basetype && i == vptr_fieldno)
+	  || TYPE_FIELD_ARTIFICIAL (type, i))
 	continue;
 
       if (TYPE_FIELD_PROTECTED (type, i))
@@ -3199,6 +3204,10 @@ cplus_describe_child (struct varobj *par
 	     find the indexed field. */
 	  int type_index = TYPE_N_BASECLASSES (type);
 	  enum accessibility acc = public_field;
+	  int vptr_fieldno;
+	  struct type *basetype = NULL;
+
+	  vptr_fieldno = get_vptr_fieldno (type, &basetype);
 	  if (strcmp (parent->name, "private") == 0)
 	    acc = private_field;
 	  else if (strcmp (parent->name, "protected") == 0)
@@ -3206,8 +3215,8 @@ cplus_describe_child (struct varobj *par
 
 	  while (index >= 0)
 	    {
-	      if (TYPE_VPTR_BASETYPE (type) == type
-		  && type_index == TYPE_VPTR_FIELDNO (type))
+	      if ((type == basetype && type_index == vptr_fieldno)
+		  || TYPE_FIELD_ARTIFICIAL (type, type_index))
 		; /* ignore vptr */
 	      else if (match_accessibility (type, type_index, acc))
 		    --index;

-- 
Daniel Jacobowitz
CodeSourcery


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]