From 0d5de0100fd9c669790851af4f9f50704a24b453 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Wed, 3 Jan 2007 18:05:45 +0000 Subject: * NEWS: Mention pointer to member improvements. * Makefile.in (gnu-v3-abi.o): Delete special rule. (eval.o, gnu-v3-abi.o, ia64-tdep.o): Update. * ada-valprint.c (ada_print_scalar): Update for new type codes. * c-typeprint.c (c_print_type): Update for new type codes. (c_type_print_varspec_prefix, c_type_print_varspec_suffix) (c_type_print_base): Likewise. (c_type_print_args): Rewrite. * c-valprint.c (c_val_print): Update for new type codes. Remove support for references to members. Treat methods like functions. * cp-abi.c (cplus_print_method_ptr, cplus_method_ptr_size) (cplus_make_method_ptr, cplus_method_ptr_to_value): New. * cp-abi.h (cplus_print_method_ptr, cplus_method_ptr_size) (cplus_make_method_ptr, cplus_method_ptr_to_value): New prototypes. (struct cp_abi_ops): Add corresponding members. * cp-valprint.c (cp_print_class_method): Delete. (cp_find_class_member): New function. (cp_print_class_member): Use it. Simplify support for bogus member pointers. * dwarf2read.c (quirk_gcc_member_function_pointer): Use lookup_methodptr_type. (read_tag_ptr_to_member_type): Likewise, and lookup_memberptr_type. * eval.c (evaluate_subexp_standard): Implement EVAL_SKIP for OP_SCOPE. Update call to value_aggregate_elt. Rewrite member pointer support. (evaluate_subexp_for_address): Handle OP_SCOPE explicitly. Handle references returned by user defined operators. * f-typeprint.c (f_print_type, f_type_print_varspec_prefix) (f_type_print_varspec_suffix): Remove support for member pointers. * gdbtypes.c (lookup_memberptr_type): Renamed from lookup_member_type and adjusted. (smash_to_memberptr_type): Likewise, from smash_to_member_type. (lookup_methodptr_type): New. (rank_one_type): Adjust for TYPE_CODE_MEMBERPTR. (recursive_dump_type): Update for new types. * gdbtypes.h (enum type_code): Replace TYPE_CODE_MEMBER with TYPE_CODE_MEMBERPTR and TYPE_CODE_METHODPTR. (lookup_memberptr_type, lookup_methodptr_type) (smash_to_memberptr_type): New prototypes. (smash_to_method_type): Formatting fix. (lookup_member_type, smash_to_member_type): Delete prototypes. * gnu-v3-abi.c (gnuv3_get_vtable, gnuv3_get_virtual_fn): New. Do not rely on debug information for the vptr or the method's enclosing type. Handle function descriptors for IA64. (gnuv3_virtual_fn_field): Rewrite using the new functions. (gnuv3_find_method_in, gnuv3_print_method_ptr) (gnuv3_method_ptr_size, gnuv3_make_method_ptr) (gnuv3_method_ptr_to_value): New. (init_gnuv3_ops): Set new members of gnu_v3_abi_ops. * hpread.c (hpread_type_lookup): Update for new types. * infcall.c (value_arg_coerce): Likewise. * m2-typeprint.c (m2_print_type): Remove explicit support for member pointers. * m2-valprint.c (m2_val_print): Likewise. * p-typeprint.c (pascal_type_print_varspec_prefix) (pascal_type_print_varspec_suffix, pascal_type_print_base): Likewise. * p-valprint.c (pascal_val_print): Likewise. (pascal_object_print_class_method, pascal_object_print_class_member): Delete. * p-lang.h (pascal_object_print_class_method) (pascal_object_print_class_member): Delete prototypes. * stabsread.c (read_type): Update for new types. * typeprint.c (print_type_scalar): Likewise. * valops.c (value_struct_elt_for_reference, value_namespace_elt) (value_maybe_namespace_elt, value_aggregate_elt): Add want_address argument. Construct a pointer to member if the address of a function or data member is requested. (value_cast_pointers): Don't modify the input value. (value_cast): Adjust pointer to member handling for new types. Allow null pointer to member constants. Don't modify the input value. (value_ind): Remove pointer to member check. Handle function descriptors for function pointers. (value_struct_elt, value_find_oload_method_list, check_field): Remove pointer to member checks. * value.c (unpack_long): Allow pointers to data members. (value_from_longest): Allow member pointers. * value.h (value_aggregate_elt): Add want_address. * varobj.c (c_variable_editable): Remove check for members. * gdbarch.sh: Add vtable_function_descriptors and vbit_in_delta. * ia64-tdep.c (ia64_convert_from_func_ptr_addr): Handle descriptors in virtual tables. (ia64_gdbarch_init): Call set_gdbarch_vtable_function_descriptors. * c-lang.h (cp_print_class_method): Delete prototype. * arm-tdep.c (arm_gdbarch_init): Call set_gdbarch_vbit_in_delta. * mips-tdep.c (mips_gdbarch_init): Likewise. * gdbarch.c, gdbarch.h: Regenerated. * gdb.cp/classes.exp (test_pointers_to_class_members): Update expected output. Test the types of members and member pointers. * gdb.cp/inherit.exp (test_print_mi_member_types): Remove KFAILs for gdb/2092. * gdb.cp/member-ptr.exp: Search for a comment instead of a statement. Enable for GCC. Update expected output for some tests and add new tests. Remove obsolete GCC KFAILs. Allow GCC's class layout. * gdb.cp/member-ptr.cc (Padding, Padding::vspacer, Base, Base::get_x) (Base::vget_base, Left, Left::vget, Right, Right::vget, Diamond) (Diamond::vget_base): New. (main): Add new tests. * gdb.cp/printmethod.exp: Update expected output for member functions. * gdb.cp/virtfunc.exp (test_virtual_calls): Add a KFAIL for print pEe->D::vg(). --- gdb/gnu-v3-abi.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 319 insertions(+), 58 deletions(-) (limited to 'gdb/gnu-v3-abi.c') diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c index 383b475..c3a3913 100644 --- a/gdb/gnu-v3-abi.c +++ b/gdb/gnu-v3-abi.c @@ -1,7 +1,8 @@ /* Abstraction of GNU v3 abi. Contributed by Jim Blandy - Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2005, 2006 + Free Software Foundation, Inc. This file is part of GDB. @@ -25,6 +26,8 @@ #include "cp-abi.h" #include "cp-support.h" #include "demangle.h" +#include "valprint.h" + #include "gdb_assert.h" #include "gdb_string.h" @@ -274,79 +277,94 @@ gnuv3_rtti_type (struct value *value, 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_virtual_fn_field (struct value **value_p, - struct fn_field *f, int j, - struct type *type, int offset) +gnuv3_get_vtable (struct value *container) { struct type *vtable_type = gdbarch_data (current_gdbarch, vtable_type_gdbarch_data); - struct value *value = *value_p; - struct type *values_type = check_typedef (value_type (value)); - struct type *vfn_base; - CORE_ADDR vtable_address; - struct value *vtable; - struct value *vfn; + 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 ()); +} - /* Some simple sanity checks. */ - if (TYPE_CODE (values_type) != TYPE_CODE_CLASS) - error (_("Only classes can have virtual functions.")); +/* Return a function pointer for CONTAINER's VTABLE_INDEX'th virtual + function, of type FNTYPE. */ - /* Find the base class that defines this virtual function. */ - vfn_base = TYPE_FN_FIELD_FCONTEXT (f, j); - if (! vfn_base) - /* In programs compiled with G++ version 1, the debug info doesn't - say which base class defined the virtual function. We'll guess - it's the same base class that has our vtable; this is wrong for - multiple inheritance, but it's better than nothing. */ - vfn_base = TYPE_VPTR_BASETYPE (type); - - /* This type may have been defined before its virtual function table - was. If so, fill in the virtual function table entry for the - type now. */ - if (TYPE_VPTR_FIELDNO (vfn_base) < 0) - fill_in_vptr_fieldno (vfn_base); - if (TYPE_VPTR_FIELDNO (vfn_base) < 0) - error (_("Could not find virtual table pointer for class \"%s\"."), - TYPE_TAG_NAME (vfn_base) ? TYPE_TAG_NAME (vfn_base) : ""); - - /* Now that we know which base class is defining our virtual - function, cast our value to that baseclass. This takes care of - any necessary `this' adjustments. */ - if (vfn_base != values_type) - value = value_cast (vfn_base, value); - - /* Now value is an object of the appropriate base type. Fetch its - virtual table. */ - /* It might be possible to do this cast at the same time as the above. - Does multiple inheritance affect this? - Can this even trigger, or is TYPE_VPTR_BASETYPE idempotent? - */ - if (TYPE_VPTR_BASETYPE (vfn_base) != vfn_base) - value = value_cast (TYPE_VPTR_BASETYPE (vfn_base), value); - vtable_address - = value_as_address (value_field (value, TYPE_VPTR_FIELDNO (vfn_base))); - - vtable = value_at_lazy (vtable_type, - vtable_address - vtable_address_point_offset ()); +static struct value * +gnuv3_get_virtual_fn (struct value *container, struct type *fntype, + int vtable_index) +{ + struct value *vtable = gnuv3_get_vtable (container); + struct value *vfn; /* Fetch the appropriate function pointer from the vtable. */ vfn = value_subscript (value_field (vtable, vtable_field_virtual_functions), - value_from_longest (builtin_type_int, - TYPE_FN_FIELD_VOFFSET (f, j))); + value_from_longest (builtin_type_int, vtable_index)); - /* Cast the function pointer to the appropriate type. */ - vfn = value_cast (lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j)), - vfn); + /* If this architecture uses function descriptors directly in the vtable, + then the address of the vtable entry is actually a "function pointer" + (i.e. points to the descriptor). We don't need to scale the index + by the size of a function descriptor; GCC does that before outputing + debug information. */ + if (gdbarch_vtable_function_descriptors (current_gdbarch)) + vfn = value_addr (vfn); - /* Is (type)value always numerically the same as (vfn_base)value? - If so we can spare this cast and use one of the ones above. */ - *value_p = value_addr (value_cast (type, *value_p)); + /* Cast the function pointer to the appropriate type. */ + vfn = value_cast (lookup_pointer_type (fntype), vfn); return vfn; } +/* GNU v3 implementation of value_virtual_fn_field. See cp-abi.h + for a description of the arguments. */ + +static struct value * +gnuv3_virtual_fn_field (struct value **value_p, + struct fn_field *f, int j, + struct type *vfn_base, int offset) +{ + struct type *values_type = check_typedef (value_type (*value_p)); + + /* Some simple sanity checks. */ + if (TYPE_CODE (values_type) != TYPE_CODE_CLASS) + error (_("Only classes can have virtual functions.")); + + /* Cast our value to the base class which defines this virtual + function. This takes care of any necessary `this' + adjustments. */ + if (vfn_base != values_type) + *value_p = value_cast (vfn_base, *value_p); + + return gnuv3_get_virtual_fn (*value_p, TYPE_FN_FIELD_TYPE (f, j), + TYPE_FN_FIELD_VOFFSET (f, j)); +} + /* Compute the offset of the baseclass which is the INDEXth baseclass of class TYPE, for value at VALADDR (in host) at ADDRESS (in target). @@ -416,6 +434,245 @@ gnuv3_baseclass_offset (struct type *type, int index, const bfd_byte *valaddr, return base_offset; } +/* Locate a virtual method in DOMAIN or its non-virtual base classes + which has virtual table index VOFFSET. The method has an associated + "this" adjustment of ADJUSTMENT bytes. */ + +const char * +gnuv3_find_method_in (struct type *domain, CORE_ADDR voffset, + LONGEST adjustment) +{ + int i; + const char *physname; + + /* Search this class first. */ + physname = NULL; + if (adjustment == 0) + { + int len; + + len = TYPE_NFN_FIELDS (domain); + for (i = 0; i < len; i++) + { + int len2, j; + struct fn_field *f; + + f = TYPE_FN_FIELDLIST1 (domain, i); + len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + + check_stub_method_group (domain, i); + for (j = 0; j < len2; j++) + if (TYPE_FN_FIELD_VOFFSET (f, j) == voffset) + return TYPE_FN_FIELD_PHYSNAME (f, j); + } + } + + /* Next search non-virtual bases. If it's in a virtual base, + we're out of luck. */ + for (i = 0; i < TYPE_N_BASECLASSES (domain); i++) + { + int pos; + struct type *basetype; + + if (BASETYPE_VIA_VIRTUAL (domain, i)) + continue; + + pos = TYPE_BASECLASS_BITPOS (domain, i) / 8; + basetype = TYPE_FIELD_TYPE (domain, i); + /* Recurse with a modified adjustment. We don't need to adjust + voffset. */ + if (adjustment >= pos && adjustment < pos + TYPE_LENGTH (basetype)) + return gnuv3_find_method_in (basetype, voffset, adjustment - pos); + } + + return NULL; +} + +/* GNU v3 implementation of cplus_print_method_ptr. */ + +static void +gnuv3_print_method_ptr (const gdb_byte *contents, + struct type *type, + struct ui_file *stream) +{ + CORE_ADDR ptr_value; + LONGEST adjustment; + struct type *domain; + int vbit; + + domain = TYPE_DOMAIN_TYPE (type); + + /* Extract the pointer to member. */ + ptr_value = extract_typed_address (contents, builtin_type_void_func_ptr); + contents += TYPE_LENGTH (builtin_type_void_func_ptr); + adjustment = extract_signed_integer (contents, + TYPE_LENGTH (builtin_type_long)); + + if (!gdbarch_vbit_in_delta (current_gdbarch)) + { + vbit = ptr_value & 1; + ptr_value = ptr_value ^ vbit; + } + else + { + vbit = adjustment & 1; + adjustment = adjustment >> 1; + } + + /* Check for NULL. */ + if (ptr_value == 0 && vbit == 0) + { + fprintf_filtered (stream, "NULL"); + return; + } + + /* Search for a virtual method. */ + if (vbit) + { + CORE_ADDR voffset; + const char *physname; + + /* It's a virtual table offset, maybe in this class. Search + for a field with the correct vtable offset. First convert it + to an index, as used in TYPE_FN_FIELD_VOFFSET. */ + voffset = ptr_value / TYPE_LENGTH (builtin_type_long); + + physname = gnuv3_find_method_in (domain, voffset, adjustment); + + /* If we found a method, print that. We don't bother to disambiguate + possible paths to the method based on the adjustment. */ + if (physname) + { + char *demangled_name = cplus_demangle (physname, + DMGL_ANSI | DMGL_PARAMS); + if (demangled_name != NULL) + { + fprintf_filtered (stream, "&virtual "); + fputs_filtered (demangled_name, stream); + xfree (demangled_name); + return; + } + } + } + + /* We didn't find it; print the raw data. */ + if (vbit) + { + fprintf_filtered (stream, "&virtual table offset "); + print_longest (stream, 'd', 1, ptr_value); + } + else + print_address_demangle (ptr_value, stream, demangle); + + if (adjustment) + { + fprintf_filtered (stream, ", this adjustment "); + print_longest (stream, 'd', 1, adjustment); + } +} + +/* GNU v3 implementation of cplus_method_ptr_size. */ + +static int +gnuv3_method_ptr_size (void) +{ + return 2 * TYPE_LENGTH (builtin_type_void_data_ptr); +} + +/* GNU v3 implementation of cplus_make_method_ptr. */ + +static void +gnuv3_make_method_ptr (gdb_byte *contents, CORE_ADDR value, int is_virtual) +{ + int size = TYPE_LENGTH (builtin_type_void_data_ptr); + + /* FIXME drow/2006-12-24: The adjustment of "this" is currently + always zero, since the method pointer is of the correct type. + But if the method pointer came from a base class, this is + incorrect - it should be the offset to the base. The best + fix might be to create the pointer to member pointing at the + base class and cast it to the derived class, but that requires + support for adjusting pointers to members when casting them - + not currently supported by GDB. */ + + if (!gdbarch_vbit_in_delta (current_gdbarch)) + { + store_unsigned_integer (contents, size, value | is_virtual); + store_unsigned_integer (contents + size, size, 0); + } + else + { + store_unsigned_integer (contents, size, value); + store_unsigned_integer (contents + size, size, is_virtual); + } +} + +/* GNU v3 implementation of cplus_method_ptr_to_value. */ + +static struct value * +gnuv3_method_ptr_to_value (struct value **this_p, struct value *method_ptr) +{ + const gdb_byte *contents = value_contents (method_ptr); + CORE_ADDR ptr_value; + struct type *final_type, *method_type; + LONGEST adjustment; + struct value *adjval; + int vbit; + + final_type = TYPE_DOMAIN_TYPE (check_typedef (value_type (method_ptr))); + final_type = lookup_pointer_type (final_type); + + method_type = TYPE_TARGET_TYPE (check_typedef (value_type (method_ptr))); + + ptr_value = extract_typed_address (contents, builtin_type_void_func_ptr); + contents += TYPE_LENGTH (builtin_type_void_func_ptr); + adjustment = extract_signed_integer (contents, + TYPE_LENGTH (builtin_type_long)); + + if (!gdbarch_vbit_in_delta (current_gdbarch)) + { + vbit = ptr_value & 1; + ptr_value = ptr_value ^ vbit; + } + else + { + vbit = adjustment & 1; + adjustment = adjustment >> 1; + } + + /* First convert THIS to match the containing type of the pointer to + member. This cast may adjust the value of THIS. */ + *this_p = value_cast (final_type, *this_p); + + /* Then apply whatever adjustment is necessary. This creates a somewhat + strange pointer: it claims to have type FINAL_TYPE, but in fact it + might not be a valid FINAL_TYPE. For instance, it might be a + base class of FINAL_TYPE. And if it's not the primary base class, + then printing it out as a FINAL_TYPE object would produce some pretty + garbage. + + But we don't really know the type of the first argument in + METHOD_TYPE either, which is why this happens. We can't + dereference this later as a FINAL_TYPE, but once we arrive in the + called method we'll have debugging information for the type of + "this" - and that'll match the value we produce here. + + You can provoke this case by casting a Base::* to a Derived::*, for + instance. */ + *this_p = value_cast (builtin_type_void_data_ptr, *this_p); + adjval = value_from_longest (builtin_type_long, adjustment); + *this_p = value_add (*this_p, adjval); + *this_p = value_cast (final_type, *this_p); + + if (vbit) + { + LONGEST voffset = ptr_value / TYPE_LENGTH (builtin_type_long); + return gnuv3_get_virtual_fn (value_ind (*this_p), method_type, voffset); + } + else + return value_from_pointer (lookup_pointer_type (method_type), ptr_value); +} + static void init_gnuv3_ops (void) { @@ -433,6 +690,10 @@ init_gnuv3_ops (void) gnu_v3_abi_ops.rtti_type = gnuv3_rtti_type; gnu_v3_abi_ops.virtual_fn_field = gnuv3_virtual_fn_field; gnu_v3_abi_ops.baseclass_offset = gnuv3_baseclass_offset; + gnu_v3_abi_ops.print_method_ptr = gnuv3_print_method_ptr; + gnu_v3_abi_ops.method_ptr_size = gnuv3_method_ptr_size; + gnu_v3_abi_ops.make_method_ptr = gnuv3_make_method_ptr; + gnu_v3_abi_ops.method_ptr_to_value = gnuv3_method_ptr_to_value; } extern initialize_file_ftype _initialize_gnu_v3_abi; /* -Wmissing-prototypes */ -- cgit v1.1