This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[commit/c++] Do not require debug info to find the vtable
- From: Daniel Jacobowitz <drow at false dot org>
- To: gdb-patches at sourceware dot org
- Date: Thu, 12 Nov 2009 14:49:17 -0500
- Subject: [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