Index: gdb/gnu-v3-abi.c |
diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c |
index a461bb0e674a0dfe2c599c693d33cc390751f805..3a83e2d48b5424588cfe55fdae81aeaf63bb6440 100644 |
--- a/gdb/gnu-v3-abi.c |
+++ b/gdb/gnu-v3-abi.c |
@@ -26,6 +26,7 @@ |
#include "objfiles.h" |
#include "valprint.h" |
#include "c-lang.h" |
+#include "exceptions.h" |
#include "gdb_assert.h" |
#include "gdb_string.h" |
@@ -129,28 +130,28 @@ build_gdb_vtable_type (struct gdbarch *arch) |
/* ptrdiff_t vcall_and_vbase_offsets[0]; */ |
FIELD_NAME (*field) = "vcall_and_vbase_offsets"; |
FIELD_TYPE (*field) = lookup_array_range_type (ptrdiff_type, 0, -1); |
- FIELD_BITPOS (*field) = offset * TARGET_CHAR_BIT; |
+ SET_FIELD_BITPOS (*field, offset * TARGET_CHAR_BIT); |
offset += TYPE_LENGTH (FIELD_TYPE (*field)); |
field++; |
/* ptrdiff_t offset_to_top; */ |
FIELD_NAME (*field) = "offset_to_top"; |
FIELD_TYPE (*field) = ptrdiff_type; |
- FIELD_BITPOS (*field) = offset * TARGET_CHAR_BIT; |
+ SET_FIELD_BITPOS (*field, offset * TARGET_CHAR_BIT); |
offset += TYPE_LENGTH (FIELD_TYPE (*field)); |
field++; |
/* void *type_info; */ |
FIELD_NAME (*field) = "type_info"; |
FIELD_TYPE (*field) = void_ptr_type; |
- FIELD_BITPOS (*field) = offset * TARGET_CHAR_BIT; |
+ SET_FIELD_BITPOS (*field, offset * TARGET_CHAR_BIT); |
offset += TYPE_LENGTH (FIELD_TYPE (*field)); |
field++; |
/* void (*virtual_functions[0]) (); */ |
FIELD_NAME (*field) = "virtual_functions"; |
FIELD_TYPE (*field) = lookup_array_range_type (ptr_to_void_fn_type, 0, -1); |
- FIELD_BITPOS (*field) = offset * TARGET_CHAR_BIT; |
+ SET_FIELD_BITPOS (*field, offset * TARGET_CHAR_BIT); |
offset += TYPE_LENGTH (FIELD_TYPE (*field)); |
field++; |
@@ -429,8 +430,9 @@ gnuv3_baseclass_offset (struct type *type, int index, |
ptr_type = builtin_type (gdbarch)->builtin_data_ptr; |
/* If it isn't a virtual base, this is easy. The offset is in the |
- type definition. */ |
- if (!BASETYPE_VIA_VIRTUAL (type, index)) |
+ type definition. Likewise for Java, which doesn't really have |
+ virtual inheritance in the C++ sense. */ |
+ if (!BASETYPE_VIA_VIRTUAL (type, index) || TYPE_CPLUS_REALLY_JAVA (type)) |
return TYPE_BASECLASS_BITPOS (type, index) / 8; |
/* To access a virtual base, we need to use the vbase offset stored in |
@@ -619,7 +621,12 @@ gnuv3_print_method_ptr (const gdb_byte *contents, |
print_longest (stream, 'd', 1, ptr_value); |
} |
else |
- print_address_demangle (gdbarch, ptr_value, stream, demangle); |
+ { |
+ struct value_print_options opts; |
+ |
+ get_user_print_options (&opts); |
+ print_address_demangle (&opts, gdbarch, ptr_value, stream, demangle); |
+ } |
if (adjustment) |
{ |
@@ -725,6 +732,245 @@ gnuv3_method_ptr_to_value (struct value **this_p, struct value *method_ptr) |
return value_from_pointer (lookup_pointer_type (method_type), ptr_value); |
} |
+/* Objects of this type are stored in a hash table and a vector when |
+ printing the vtables for a class. */ |
+ |
+struct value_and_voffset |
+{ |
+ /* The value representing the object. */ |
+ struct value *value; |
+ |
+ /* The maximum vtable offset we've found for any object at this |
+ offset in the outermost object. */ |
+ int max_voffset; |
+}; |
+ |
+typedef struct value_and_voffset *value_and_voffset_p; |
+DEF_VEC_P (value_and_voffset_p); |
+ |
+/* Hash function for value_and_voffset. */ |
+ |
+static hashval_t |
+hash_value_and_voffset (const void *p) |
+{ |
+ const struct value_and_voffset *o = p; |
+ |
+ return value_address (o->value) + value_embedded_offset (o->value); |
+} |
+ |
+/* Equality function for value_and_voffset. */ |
+ |
+static int |
+eq_value_and_voffset (const void *a, const void *b) |
+{ |
+ const struct value_and_voffset *ova = a; |
+ const struct value_and_voffset *ovb = b; |
+ |
+ return (value_address (ova->value) + value_embedded_offset (ova->value) |
+ == value_address (ovb->value) + value_embedded_offset (ovb->value)); |
+} |
+ |
+/* qsort comparison function for value_and_voffset. */ |
+ |
+static int |
+compare_value_and_voffset (const void *a, const void *b) |
+{ |
+ const struct value_and_voffset * const *ova = a; |
+ CORE_ADDR addra = (value_address ((*ova)->value) |
+ + value_embedded_offset ((*ova)->value)); |
+ const struct value_and_voffset * const *ovb = b; |
+ CORE_ADDR addrb = (value_address ((*ovb)->value) |
+ + value_embedded_offset ((*ovb)->value)); |
+ |
+ if (addra < addrb) |
+ return -1; |
+ if (addra > addrb) |
+ return 1; |
+ return 0; |
+} |
+ |
+/* A helper function used when printing vtables. This determines the |
+ key (most derived) sub-object at each address and also computes the |
+ maximum vtable offset seen for the corresponding vtable. Updates |
+ OFFSET_HASH and OFFSET_VEC with a new value_and_voffset object, if |
+ needed. VALUE is the object to examine. */ |
+ |
+static void |
+compute_vtable_size (htab_t offset_hash, |
+ VEC (value_and_voffset_p) **offset_vec, |
+ struct value *value) |
+{ |
+ int i; |
+ struct type *type = check_typedef (value_type (value)); |
+ void **slot; |
+ struct value_and_voffset search_vo, *current_vo; |
+ CORE_ADDR addr = value_address (value) + value_embedded_offset (value); |
+ |
+ /* If the object is not dynamic, then we are done; as it cannot have |
+ dynamic base types either. */ |
+ if (!gnuv3_dynamic_class (type)) |
+ return; |
+ |
+ /* Update the hash and the vec, if needed. */ |
+ search_vo.value = value; |
+ slot = htab_find_slot (offset_hash, &search_vo, INSERT); |
+ if (*slot) |
+ current_vo = *slot; |
+ else |
+ { |
+ current_vo = XNEW (struct value_and_voffset); |
+ current_vo->value = value; |
+ current_vo->max_voffset = -1; |
+ *slot = current_vo; |
+ VEC_safe_push (value_and_voffset_p, *offset_vec, current_vo); |
+ } |
+ |
+ /* Update the value_and_voffset object with the highest vtable |
+ offset from this class. */ |
+ for (i = 0; i < TYPE_NFN_FIELDS (type); ++i) |
+ { |
+ int j; |
+ struct fn_field *fn = TYPE_FN_FIELDLIST1 (type, i); |
+ |
+ for (j = 0; j < TYPE_FN_FIELDLIST_LENGTH (type, i); ++j) |
+ { |
+ if (TYPE_FN_FIELD_VIRTUAL_P (fn, j)) |
+ { |
+ int voffset = TYPE_FN_FIELD_VOFFSET (fn, j); |
+ |
+ if (voffset > current_vo->max_voffset) |
+ current_vo->max_voffset = voffset; |
+ } |
+ } |
+ } |
+ |
+ /* Recurse into base classes. */ |
+ for (i = 0; i < TYPE_N_BASECLASSES (type); ++i) |
+ compute_vtable_size (offset_hash, offset_vec, value_field (value, i)); |
+} |
+ |
+/* Helper for gnuv3_print_vtable that prints a single vtable. */ |
+ |
+static void |
+print_one_vtable (struct gdbarch *gdbarch, struct value *value, |
+ int max_voffset, |
+ struct value_print_options *opts) |
+{ |
+ int i; |
+ struct type *type = check_typedef (value_type (value)); |
+ struct value *vtable; |
+ CORE_ADDR vt_addr; |
+ |
+ vtable = gnuv3_get_vtable (gdbarch, type, |
+ value_address (value) |
+ + value_embedded_offset (value)); |
+ vt_addr = value_address (value_field (vtable, |
+ vtable_field_virtual_functions)); |
+ |
+ printf_filtered (_("vtable for '%s' @ %s (subobject @ %s):\n"), |
+ TYPE_SAFE_NAME (type), |
+ paddress (gdbarch, vt_addr), |
+ paddress (gdbarch, (value_address (value) |
+ + value_embedded_offset (value)))); |
+ |
+ for (i = 0; i <= max_voffset; ++i) |
+ { |
+ /* Initialize it just to avoid a GCC false warning. */ |
+ CORE_ADDR addr = 0; |
+ struct value *vfn; |
+ volatile struct gdb_exception ex; |
+ |
+ printf_filtered ("[%d]: ", i); |
+ |
+ vfn = value_subscript (value_field (vtable, |
+ vtable_field_virtual_functions), |
+ i); |
+ |
+ if (gdbarch_vtable_function_descriptors (gdbarch)) |
+ vfn = value_addr (vfn); |
+ |
+ TRY_CATCH (ex, RETURN_MASK_ERROR) |
+ { |
+ addr = value_as_address (vfn); |
+ } |
+ if (ex.reason < 0) |
+ printf_filtered (_("<error: %s>"), ex.message); |
+ else |
+ print_function_pointer_address (opts, gdbarch, addr, gdb_stdout); |
+ printf_filtered ("\n"); |
+ } |
+} |
+ |
+/* Implementation of the print_vtable method. */ |
+ |
+static void |
+gnuv3_print_vtable (struct value *value) |
+{ |
+ struct gdbarch *gdbarch; |
+ struct type *type; |
+ struct value *vtable; |
+ struct value_print_options opts; |
+ htab_t offset_hash; |
+ struct cleanup *cleanup; |
+ VEC (value_and_voffset_p) *result_vec = NULL; |
+ struct value_and_voffset *iter; |
+ int i, count; |
+ |
+ value = coerce_ref (value); |
+ type = check_typedef (value_type (value)); |
+ if (TYPE_CODE (type) == TYPE_CODE_PTR) |
+ { |
+ value = value_ind (value); |
+ type = check_typedef (value_type (value)); |
+ } |
+ |
+ get_user_print_options (&opts); |
+ |
+ /* Respect 'set print object'. */ |
+ if (opts.objectprint) |
+ { |
+ value = value_full_object (value, NULL, 0, 0, 0); |
+ type = check_typedef (value_type (value)); |
+ } |
+ |
+ gdbarch = get_type_arch (type); |
+ vtable = gnuv3_get_vtable (gdbarch, type, |
+ value_as_address (value_addr (value))); |
+ |
+ if (!vtable) |
+ { |
+ printf_filtered (_("This object does not have a virtual function table\n")); |
+ return; |
+ } |
+ |
+ offset_hash = htab_create_alloc (1, hash_value_and_voffset, |
+ eq_value_and_voffset, |
+ xfree, xcalloc, xfree); |
+ cleanup = make_cleanup_htab_delete (offset_hash); |
+ make_cleanup (VEC_cleanup (value_and_voffset_p), &result_vec); |
+ |
+ compute_vtable_size (offset_hash, &result_vec, value); |
+ |
+ qsort (VEC_address (value_and_voffset_p, result_vec), |
+ VEC_length (value_and_voffset_p, result_vec), |
+ sizeof (value_and_voffset_p), |
+ compare_value_and_voffset); |
+ |
+ count = 0; |
+ for (i = 0; VEC_iterate (value_and_voffset_p, result_vec, i, iter); ++i) |
+ { |
+ if (iter->max_voffset >= 0) |
+ { |
+ if (count > 0) |
+ printf_filtered ("\n"); |
+ print_one_vtable (gdbarch, iter->value, iter->max_voffset, &opts); |
+ ++count; |
+ } |
+ } |
+ |
+ do_cleanups (cleanup); |
+} |
+ |
/* Determine if we are currently in a C++ thunk. If so, get the address |
of the routine we are thunking to and continue to there instead. */ |
@@ -735,7 +981,7 @@ gnuv3_skip_trampoline (struct frame_info *frame, CORE_ADDR stop_pc) |
struct gdbarch *gdbarch = get_frame_arch (frame); |
struct minimal_symbol *thunk_sym, *fn_sym; |
struct obj_section *section; |
- char *thunk_name, *fn_name; |
+ const char *thunk_name, *fn_name; |
real_stop_pc = gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc); |
if (real_stop_pc == 0) |
@@ -804,7 +1050,7 @@ gnuv3_pass_by_reference (struct type *type) |
fieldelem++) |
{ |
struct fn_field *fn = TYPE_FN_FIELDLIST1 (type, fieldnum); |
- char *name = TYPE_FN_FIELDLIST_NAME (type, fieldnum); |
+ const char *name = TYPE_FN_FIELDLIST_NAME (type, fieldnum); |
struct type *fieldtype = TYPE_FN_FIELD_TYPE (fn, fieldelem); |
/* If this function is marked as artificial, it is compiler-generated, |
@@ -873,6 +1119,7 @@ init_gnuv3_ops (void) |
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; |
+ gnu_v3_abi_ops.print_vtable = gnuv3_print_vtable; |
gnu_v3_abi_ops.skip_trampoline = gnuv3_skip_trampoline; |
gnu_v3_abi_ops.pass_by_reference = gnuv3_pass_by_reference; |
} |