forked from Imagelibrary/binutils-gdb
* gnu-v3-abi.c (struct value_and_voffset): New.
(hash_value_and_voffset, eq_value_and_voffset) (compare_value_and_voffset, compute_vtable_size) (print_one_vtable, gnuv3_print_vtable): New functions. (init_gnuv3_ops): Initialize 'print_vtable' field. * cp-support.c (info_vtbl_command): New function. (_initialize_cp_support): Add "info vtbl". * cp-abi.h (cplus_print_vtable): Declare. (struct cp_abi_ops) <print_vtable>: New field. * cp-abi.c (cplus_print_vtable): New function. * NEWS: Update. gdb/testsuite * gdb.cp/virtfunc.exp (make_one_vtable_result): New proc. (test_info_vtbl): Likewise. (do_tests): Call test_info_vtbl. * gdb.cp/virtfunc.cc (va): New global. gdb/doc * gdb.texinfo (Debugging C Plus Plus): Document "info vtbl".
This commit is contained in:
241
gdb/gnu-v3-abi.c
241
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"
|
||||
@@ -725,6 +726,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)
|
||||
{
|
||||
struct value *vfn;
|
||||
CORE_ADDR addr;
|
||||
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 (gdbarch, addr, gdb_stdout,
|
||||
opts->addressprint);
|
||||
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;
|
||||
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. */
|
||||
|
||||
@@ -873,6 +1113,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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user