static struct value * gnuv3_method_ptr_to_value (struct value **this_p, struct value *method_ptr) { struct gdbarch *gdbarch; const gdb_byte *contents = value_contents (method_ptr); CORE_ADDR ptr_value; struct type *domain_type, *final_type, *method_type; LONGEST adjustment; struct value *adjval; int vbit; domain_type = TYPE_DOMAIN_TYPE (check_typedef (value_type (method_ptr))); final_type = lookup_pointer_type (domain_type); method_type = TYPE_TARGET_TYPE (check_typedef (value_type (method_ptr))); /* Extract the pointer to member. */ gdbarch = get_class_arch (domain_type); vbit = gnuv3_decode_method_ptr (gdbarch, contents, &ptr_value, &adjustment); /* 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 (gdbarch)->builtin_data_ptr, *this_p); adjval = value_from_longest (builtin_type (gdbarch)->builtin_long, adjustment); *this_p = value_ptradd (*this_p, adjval); *this_p = value_cast (final_type, *this_p); if (vbit) { LONGEST voffset; voffset = ptr_value / TYPE_LENGTH (vtable_ptrdiff_type (gdbarch)); return gnuv3_get_virtual_fn (gdbarch, value_ind (*this_p), method_type, voffset); } else return value_from_pointer (lookup_pointer_type (method_type), ptr_value); }
static int gnuv3_decode_method_ptr (struct gdbarch *gdbarch, const gdb_byte *contents, CORE_ADDR *value_p, LONGEST *adjustment_p) { struct type *funcptr_type = builtin_type (gdbarch)->builtin_func_ptr; struct type *offset_type = vtable_ptrdiff_type (gdbarch); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); CORE_ADDR ptr_value; LONGEST voffset, adjustment; int vbit; /* Extract the pointer to member. The first element is either a pointer or a vtable offset. For pointers, we need to use extract_typed_address to allow the back-end to convert the pointer to a GDB address -- but vtable offsets we must handle as integers. At this point, we do not yet know which case we have, so we extract the value under both interpretations and choose the right one later on. */ ptr_value = extract_typed_address (contents, funcptr_type); voffset = extract_signed_integer (contents, TYPE_LENGTH (funcptr_type), byte_order); contents += TYPE_LENGTH (funcptr_type); adjustment = extract_signed_integer (contents, TYPE_LENGTH (offset_type), byte_order); if (!gdbarch_vbit_in_delta (gdbarch)) { vbit = voffset & 1; voffset = voffset ^ vbit; } else { vbit = adjustment & 1; adjustment = adjustment >> 1; } *value_p = vbit? voffset : ptr_value; *adjustment_p = adjustment; return vbit; }
static void gnuv3_print_method_ptr (const gdb_byte *contents, struct type *type, struct ui_file *stream) { struct type *domain = TYPE_DOMAIN_TYPE (type); struct gdbarch *gdbarch = get_type_arch (domain); CORE_ADDR ptr_value; LONGEST adjustment; int vbit; /* Extract the pointer to member. */ vbit = gnuv3_decode_method_ptr (gdbarch, contents, &ptr_value, &adjustment); /* 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 (vtable_ptrdiff_type (gdbarch)); 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 = gdb_demangle (physname, DMGL_ANSI | DMGL_PARAMS); fprintf_filtered (stream, "&virtual "); if (demangled_name == NULL) fputs_filtered (physname, stream); else { fputs_filtered (demangled_name, stream); xfree (demangled_name); } return; } } else if (ptr_value != 0) { /* Found a non-virtual function: print out the type. */ fputs_filtered ("(", stream); c_print_type (type, "", stream, -1, 0, &type_print_raw_options); fputs_filtered (") ", stream); } /* 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 { struct value_print_options opts; get_user_print_options (&opts); print_address_demangle (&opts, gdbarch, ptr_value, stream, demangle); } if (adjustment) { fprintf_filtered (stream, ", this adjustment "); print_longest (stream, 'd', 1, adjustment); } }