From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jim Blandy To: David Taylor , Anthony Green Cc: gdb-patches@sources.redhat.com Subject: Re: RFA: abstract C++ ABI dependencies Date: Tue, 24 Apr 2001 17:42:00 -0000 Message-id: References: <200104241926.PAA27063@houston.candd.org> X-SW-Source: 2001-04/msg00229.html Here is the same patch, revised (hopefully) to address the problems David and Anthony pointed out. Test results on both old and new GCC's are unchanged. 2001-04-24 Jim Blandy (Changes from Daniel Berlin, with revisions by Jim Blandy.) Abstract out operations specific to particular C++ ABI's, and invoke them through a function table. This removes the C++ ABI dependencies scattered throughout the code, and allows us to cleanly add support for new C++ ABI's. * cp-abi.h, cp-abi.h, gnu-v2-abi.c, hpacc-abi.c: New files. * c-typeprint.c, c-valprint.c, dbxread.c, eval.c, gdbtypes.c, jv-typeprint.c, linespec.c, p-valprint.c, symtab.c, typeprint.c, valops.c: #include "cp-abi.h". These files all use functions now declared there. * symtab.h (OPNAME_PREFIX_P, VTBL_PREFIX_P, DESTRUCTOR_PREFIX_P): Deleted. These services are now provided by functions declared in cp-abi.h. * value.h (value_rtti_type, value_virtual_fn_field): Same. * values.c (value_virtual_fn_field): Same, for this definition. * valops.c (value_rtti_type): Same. * c-typeprint.c (c_type_print_base): Use the functions from "cp-abi.h", instead of the old macros, or hard-coded ABI-specific tests. * dbxread.c (record_minimal_symbol): Same. * gdbtypes.c (get_destructor_fn_field, virtual_base_index, virtual_base_index_skip_primaries): Same. * jv-typeprint.c (java_type_print_base): Same. * linespec.c (find_methods, decode_line_1): Same. * symtab.c (gdb_mangle_name): Same. * Makefile.in (SFILES): Add the new .c files mentioned above. (cp_abi_h): New variable. (COMMON_OBS): Add gnu-v2-abi.o, hpacc-abi.o, and cp-abi.o. (cp-abi.o, gnu-v2-abi.o, hpacc-abi.o): New targets. (c-typeprint.o, c-valprint.o, dbxread.o, eval.o, gdbtypes.o, jv-typeprint.o, p-valprint.o, symtab.o, linespec.o, typeprint.o, valops.o): Add dependency on $(cp_abi_h). Index: gdb/Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/Makefile.in,v retrieving revision 1.79 diff -c -r1.79 Makefile.in *** gdb/Makefile.in 2001/04/17 21:20:48 1.79 --- gdb/Makefile.in 2001/04/25 00:17:54 *************** *** 539,545 **** tui/tuiStack.c tui/tuiStack.h tui/tuiWin.c tui/tuiWin.h \ tui/tui-file.h tui/tui-file.c \ ui-file.h ui-file.c \ ! frame.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c --- 539,546 ---- tui/tuiStack.c tui/tuiStack.h tui/tuiWin.c tui/tuiWin.h \ tui/tui-file.h tui/tui-file.c \ ui-file.h ui-file.c \ ! frame.c \ ! gnu-v2-abi.c hpacc-abi.c cp-abi.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c *************** *** 613,618 **** --- 614,621 ---- cli_setshow_h = $(srcdir)/cli/cli-setshow.h cli_utils_h = $(srcdir)/cli/cli-utils.h + cp_abi_h = cp-abi.h + # Header files that need to have srcdir added. Note that in the cases # where we use a macro like $(gdbcmd_h), things are carefully arranged # so that each .h file is listed exactly once (M-x tags-search works *************** *** 686,692 **** c-valprint.o cp-valprint.o ch-valprint.o f-valprint.o m2-valprint.o \ nlmread.o serial.o mdebugread.o os9kread.o top.o utils.o \ ui-file.o tui-file.o \ ! frame.o OBS = $(COMMON_OBS) $(ANNOTATE_OBS) --- 689,696 ---- c-valprint.o cp-valprint.o ch-valprint.o f-valprint.o m2-valprint.o \ nlmread.o serial.o mdebugread.o os9kread.o top.o utils.o \ ui-file.o tui-file.o \ ! frame.o \ ! gnu-v2-abi.o hpacc-abi.o cp-abi.o OBS = $(COMMON_OBS) $(ANNOTATE_OBS) *************** *** 1222,1231 **** c-typeprint.o: c-typeprint.c c-lang.h $(defs_h) $(expression_h) \ $(gdbcmd_h) $(gdbcore_h) $(gdbtypes_h) language.h $(symtab_h) \ ! target.h typeprint.h $(value_h) gdb_string.h c-valprint.o: c-valprint.c $(defs_h) $(expression_h) $(gdbtypes_h) \ ! language.h $(symtab_h) valprint.h $(value_h) f-lang.o: f-lang.c f-lang.h $(defs_h) $(expression_h) $(gdbtypes_h) \ language.h parser-defs.h $(symtab_h) gdb_string.h --- 1226,1235 ---- c-typeprint.o: c-typeprint.c c-lang.h $(defs_h) $(expression_h) \ $(gdbcmd_h) $(gdbcore_h) $(gdbtypes_h) language.h $(symtab_h) \ ! target.h typeprint.h $(value_h) gdb_string.h $(cp_abi_h) c-valprint.o: c-valprint.c $(defs_h) $(expression_h) $(gdbtypes_h) \ ! language.h $(symtab_h) valprint.h $(value_h) $(cp_abi_h) f-lang.o: f-lang.c f-lang.h $(defs_h) $(expression_h) $(gdbtypes_h) \ language.h parser-defs.h $(symtab_h) gdb_string.h *************** *** 1278,1283 **** --- 1282,1289 ---- corelow.o: corelow.c $(command_h) $(defs_h) $(gdbcore_h) $(inferior_h) \ target.h gdbthread.h gdb_string.h $(regcache_h) + cp-abi.o: cp-abi.c $(defs_h) $(value_h) $(cp_abi_h) + cp-valprint.o: cp-valprint.c $(defs_h) $(expression_h) $(gdbcmd_h) \ $(gdbtypes_h) $(symtab_h) $(value_h) gdb_string.h *************** *** 1287,1293 **** dbxread.o: dbxread.c $(breakpoint_h) buildsym.h $(command_h) \ complaints.h $(defs_h) $(expression_h) gdb-stabs.h $(gdbcore_h) \ $(gdbtypes_h) language.h objfiles.h partial-stab.h stabsread.h \ ! symfile.h $(symtab_h) target.h gdb_string.h delta68-nat.o: delta68-nat.c $(defs_h) --- 1293,1299 ---- dbxread.o: dbxread.c $(breakpoint_h) buildsym.h $(command_h) \ complaints.h $(defs_h) $(expression_h) gdb-stabs.h $(gdbcore_h) \ $(gdbtypes_h) language.h objfiles.h partial-stab.h stabsread.h \ ! symfile.h $(symtab_h) target.h gdb_string.h $(cp_abi_h) delta68-nat.o: delta68-nat.c $(defs_h) *************** *** 1317,1323 **** eval.o: eval.c $(bfd_h) $(defs_h) $(expression_h) $(frame_h) \ $(gdbtypes_h) language.h $(symtab_h) target.h $(value_h) \ ! gdb_string.h event-loop.o: event-loop.c $(defs_h) $(top_h) $(event_loop_h) $(event_top_h) --- 1323,1329 ---- eval.o: eval.c $(bfd_h) $(defs_h) $(expression_h) $(frame_h) \ $(gdbtypes_h) language.h $(symtab_h) target.h $(value_h) \ ! gdb_string.h $(cp_abi_h) event-loop.o: event-loop.c $(defs_h) $(top_h) $(event_loop_h) $(event_top_h) *************** *** 1446,1452 **** gdbtypes.o: gdbtypes.c $(bfd_h) complaints.h $(defs_h) $(expression_h) \ $(gdbtypes_h) language.h objfiles.h symfile.h $(symtab_h) target.h \ ! $(value_h) gdb_string.h wrapper.h go32-nat.o: go32-nat.c $(defs_h) $(inferior_h) gdb_wait.h $(gdbcore_h) \ $(command_h) $(floatformat_h) target.h i387-nat.h $(regcache_h) --- 1452,1458 ---- gdbtypes.o: gdbtypes.c $(bfd_h) complaints.h $(defs_h) $(expression_h) \ $(gdbtypes_h) language.h objfiles.h symfile.h $(symtab_h) target.h \ ! $(value_h) gdb_string.h wrapper.h $(cp_abi_h) go32-nat.o: go32-nat.c $(defs_h) $(inferior_h) gdb_wait.h $(gdbcore_h) \ $(command_h) $(floatformat_h) target.h i387-nat.h $(regcache_h) *************** *** 1454,1459 **** --- 1460,1468 ---- gnu-nat.o: process_reply_S.h exc_request_S.h notify_S.h msg_reply_S.h \ exc_request_U.h msg_U.h gnu-nat.h + gnu-v2-abi.o: gnu-v2-abi.c $(defs_h) $(cp_abi_h) gdb_string.h $(symtab_h) \ + $(gdbtypes_h) $(value_h) + h8300-tdep.o: h8300-tdep.c $(defs_h) $(frame_h) $(symtab_h) $(regcache_h) h8500-tdep.o: h8500-tdep.c $(bfd_h) $(dis-asm_h) $(defs_h) \ *************** *** 1462,1467 **** --- 1471,1479 ---- hp300ux-nat.o: hp300ux-nat.c $(defs_h) $(gdbcore_h) $(inferior_h) $(regcache_h) + hpacc-abi.o: hpacc-abi.c $(defs_h) $(cp_abi_h) gdb_string.h $(gdbtypes_h) \ + $(value_h) $(gdbcore_h) + hppa-tdep.o: hppa-tdep.c gdb_wait.h $(defs_h) $(gdbcmd_h) $(gdbcore_h) \ $(inferior_h) objfiles.h symfile.h target.h $(regcache_h) *************** *** 1551,1557 **** jv-typeprint.o: jv-typeprint.c $(bfd_h) $(defs_h) $(symtab_h) $(gdbtypes_h) \ $(value_h) $(demangle_h) jv-lang.h gdb_string.h \ ! typeprint.h c-lang.h jv-valprint.o: jv-valprint.c $(bfd_h) $(defs_h) $(symtab_h) $(gdbtypes_h) \ $(expression_h) $(value_h) $(demangle_h) valprint.h \ --- 1563,1569 ---- jv-typeprint.o: jv-typeprint.c $(bfd_h) $(defs_h) $(symtab_h) $(gdbtypes_h) \ $(value_h) $(demangle_h) jv-lang.h gdb_string.h \ ! typeprint.h c-lang.h $(cp_abi_h) jv-valprint.o: jv-valprint.c $(bfd_h) $(defs_h) $(symtab_h) $(gdbtypes_h) \ $(expression_h) $(value_h) $(demangle_h) valprint.h \ *************** *** 1958,1968 **** symtab.o: symtab.c call-cmds.h $(defs_h) $(expression_h) $(frame_h) \ $(gdbcmd_h) $(gdbcore_h) $(gdbtypes_h) language.h objfiles.h \ gnu-regex.h symfile.h $(symtab_h) target.h $(value_h) \ ! gdb_string.h linespec.h linespec.o: linespec.c linespec.h $(defs_h) $(frame_h) $(value_h) \ objfiles.h symfile.h completer.h $(symtab_h) \ ! $(demangle_h) command.h tic80-tdep.o: tic80-tdep.c $(defs_h) $(regcache_h) --- 1970,1980 ---- symtab.o: symtab.c call-cmds.h $(defs_h) $(expression_h) $(frame_h) \ $(gdbcmd_h) $(gdbcore_h) $(gdbtypes_h) language.h objfiles.h \ gnu-regex.h symfile.h $(symtab_h) target.h $(value_h) \ ! gdb_string.h linespec.h $(cp_abi_h) linespec.o: linespec.c linespec.h $(defs_h) $(frame_h) $(value_h) \ objfiles.h symfile.h completer.h $(symtab_h) \ ! $(demangle_h) command.h $(cp_abi_h) tic80-tdep.o: tic80-tdep.c $(defs_h) $(regcache_h) *************** *** 1981,1987 **** typeprint.o: typeprint.c $(defs_h) $(expression_h) $(gdbcmd_h) \ $(gdbcore_h) $(gdbtypes_h) language.h $(symtab_h) target.h \ ! $(value_h) gdb_string.h # OBSOLETE ultra3-nat.o: ultra3-nat.c $(defs_h) $(gdbcore_h) $(inferior_h) $(regcache_h) --- 1993,1999 ---- typeprint.o: typeprint.c $(defs_h) $(expression_h) $(gdbcmd_h) \ $(gdbcore_h) $(gdbtypes_h) language.h $(symtab_h) target.h \ ! $(value_h) gdb_string.h $(cp_abi.h) # OBSOLETE ultra3-nat.o: ultra3-nat.c $(defs_h) $(gdbcore_h) $(inferior_h) $(regcache_h) *************** *** 1998,2004 **** gdb_string.h valops.o: valops.c $(defs_h) $(gdbcore_h) $(inferior_h) target.h \ ! gdb_string.h $(regcache_h) valprint.o: valprint.c $(defs_h) $(expression_h) $(gdbcmd_h) \ $(gdbcore_h) $(gdbtypes_h) language.h $(symtab_h) target.h \ --- 2010,2016 ---- gdb_string.h valops.o: valops.c $(defs_h) $(gdbcore_h) $(inferior_h) target.h \ ! gdb_string.h $(regcache_h) $(cp_abi_h) valprint.o: valprint.c $(defs_h) $(expression_h) $(gdbcmd_h) \ $(gdbcore_h) $(gdbtypes_h) language.h $(symtab_h) target.h \ Index: gdb/c-typeprint.c =================================================================== RCS file: /cvs/src/src/gdb/c-typeprint.c,v retrieving revision 1.9 diff -c -r1.9 c-typeprint.c *** gdb/c-typeprint.c 2001/03/27 20:36:23 1.9 --- gdb/c-typeprint.c 2001/04/25 00:17:56 *************** *** 33,38 **** --- 33,39 ---- #include "demangle.h" #include "c-lang.h" #include "typeprint.h" + #include "cp-abi.h" #include "gdb_string.h" #include *************** *** 900,910 **** { char *physname = TYPE_FN_FIELD_PHYSNAME (f, j); int is_full_physname_constructor = ! ((physname[0] == '_' && physname[1] == '_' ! && strchr ("0123456789Qt", physname[2])) ! || STREQN (physname, "__ct__", 6) ! || DESTRUCTOR_PREFIX_P (physname) ! || STREQN (physname, "__dt__", 6)); QUIT; if (TYPE_FN_FIELD_PROTECTED (f, j)) --- 901,910 ---- { char *physname = TYPE_FN_FIELD_PHYSNAME (f, j); int is_full_physname_constructor = ! is_constructor_name (physname) ! || is_destructor_name (physname) ! || method_name[0] == '~'; ! QUIT; if (TYPE_FN_FIELD_PROTECTED (f, j)) Index: gdb/c-valprint.c =================================================================== RCS file: /cvs/src/src/gdb/c-valprint.c,v retrieving revision 1.8 diff -c -r1.8 c-valprint.c *** gdb/c-valprint.c 2001/03/07 02:57:08 1.8 --- gdb/c-valprint.c 2001/04/25 00:17:56 *************** *** 28,33 **** --- 28,34 ---- #include "valprint.h" #include "language.h" #include "c-lang.h" + #include "cp-abi.h" /* Print function pointer with inferior address ADDRESS onto stdio *************** *** 303,308 **** --- 304,310 ---- } /* Fall through. */ case TYPE_CODE_STRUCT: + /*FIXME: Abstract this away */ if (vtblprint && cp_is_vtbl_ptr_type (type)) { /* Print the unmangled name if desired. */ Index: gdb/cp-abi.c =================================================================== RCS file: cp-abi.c diff -N cp-abi.c *** gdb/cp-abi.c Tue May 5 13:32:27 1998 --- gdb/cp-abi.c Tue Apr 24 17:17:56 2001 *************** *** 0 **** --- 1,99 ---- + /* Generic code for supporting multiple C++ ABI's + Copyright 2001 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + + #include "defs.h" + #include "value.h" + #include "cp-abi.h" + + struct cp_abi_ops current_cp_abi; + + struct cp_abi_ops *cp_abis; + + int num_cp_abis = 0; + + enum ctor_kinds + is_constructor_name (const char *name) + { + if ((current_cp_abi.is_constructor_name) == NULL) + error ("ABI doesn't define required function is_constructor_name"); + return (*current_cp_abi.is_constructor_name) (name); + } + + enum dtor_kinds + is_destructor_name (const char *name) + { + if ((current_cp_abi.is_destructor_name) == NULL) + error ("ABI doesn't define required function is_destructor_name"); + return (*current_cp_abi.is_destructor_name) (name); + } + + int + is_vtable_name (const char *name) + { + if ((current_cp_abi.is_vtable_name) == NULL) + error ("ABI doesn't define required function is_vtable_name"); + return (*current_cp_abi.is_vtable_name) (name); + } + + int + is_operator_name (const char *name) + { + if ((current_cp_abi.is_operator_name) == NULL) + error ("ABI doesn't define required function is_operator_name"); + return (*current_cp_abi.is_operator_name) (name); + } + + value_ptr + value_virtual_fn_field (value_ptr * arg1p, struct fn_field * f, int j, + struct type * type, int offset) + { + if ((current_cp_abi.virtual_fn_field) == NULL) + return NULL; + return (*current_cp_abi.virtual_fn_field) (arg1p, f, j, type, offset); + } + struct type * + value_rtti_type (value_ptr v, int *full, int *top, int *using_enc) + { + if ((current_cp_abi.rtti_type) == NULL) + return NULL; + return (*current_cp_abi.rtti_type) (v, full, top, using_enc); + } + + int + register_cp_abi (struct cp_abi_ops abi) + { + cp_abis = + xrealloc (cp_abis, (num_cp_abis + 1) * sizeof (struct cp_abi_ops)); + cp_abis[num_cp_abis++] = abi; + + return 1; + + } + + int + switch_to_cp_abi (const char *short_name) + { + int i; + for (i = 0; i < num_cp_abis; i++) + if (strcmp (cp_abis[i].shortname, short_name) == 0) + current_cp_abi = cp_abis[i]; + return 1; + } + Index: gdb/cp-abi.h =================================================================== RCS file: cp-abi.h diff -N cp-abi.h *** gdb/cp-abi.h Tue May 5 13:32:27 1998 --- gdb/cp-abi.h Tue Apr 24 17:17:57 2001 *************** *** 0 **** --- 1,131 ---- + /* Abstraction of various C++ ABI's we support, and the info we need + to get from them. + Contributed by Daniel Berlin + Copyright 2001 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or + modify + it under the terms of the GNU General Public License as published + by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + + #ifndef CP_ABI_H_ + #define CP_ABI_H_ 1 + + /* Kinds of constructors. All these values are guaranteed to be + non-zero. */ + enum ctor_kinds { + + /* Initialize a complete object, including virtual bases, using + memory provided by caller. */ + complete_object_ctor = 1, + + /* Initialize a base object of some larger object. */ + base_object_ctor, + + /* An allocating complete-object constructor. */ + complete_object_allocating_ctor + }; + + + /* Kinds of destructors. All these values are guaranteed to be + non-zero. */ + enum dtor_kinds { + + /* A destructor which finalizes the entire object, and then calls + `delete' on its storage. */ + deleting_dtor = 1, + + /* A destructor which finalizes the entire object, but does not call + `delete'. */ + complete_object_dtor, + + /* A destructor which finalizes a subobject of some larger object. */ + base_object_dtor + }; + + + struct cp_abi_ops + { + const char *shortname; + const char *longname; + const char *doc; + + /* The functions here that attempt to determine what sort of thing a + mangled name refers to may well be revised in the future. It + would certainly be cleaner to carry this information explicitly + in GDB's data structures than to derive it from the mangled name. */ + + /* Return non-zero iff NAME is the mangled name of a constructor. + Actually, return an `enum ctor_kind' value describing what *kind* + of constructor it is. */ + enum ctor_kinds (*is_constructor_name) (const char *name); + + /* Return non-zero iff NAME is the mangled name of a destructor. + Actually, return an `enum dtor_kind' value describing what *kind* + of destructor it is. */ + enum dtor_kinds (*is_destructor_name) (const char *name); + + /* Return non-zero iff NAME is the mangled name of a vtable. */ + int (*is_vtable_name) (const char *name); + + /* Return non-zero iff NAME is the un-mangled name of an operator, + perhaps scoped within some class. */ + int (*is_operator_name) (const char *name); + + value_ptr (*virtual_fn_field) (value_ptr * arg1p, struct fn_field * f, + int j, struct type * type, int offset); + + /* Find the real run-time type of a value using RTTI. + * V is a pointer to the value. + * A pointer to the struct type entry of the run-time type + * is returneed. + * FULL is a flag that is set only if the value V includes + * the entire contents of an object of the RTTI type. + * TOP is the offset to the top of the enclosing object of + * the real run-time type. This offset may be for the embedded + * object, or for the enclosing object of V. + * USING_ENC is the flag that distinguishes the two cases. + * If it is 1, then the offset is for the enclosing object, + * otherwise for the embedded object. + * + */ + + struct type *(*rtti_type) (value_ptr v, int *full, int *top, + int *using_enc); + }; + + + extern struct cp_abi_ops *cp_abis; + + extern int num_cp_abis; + + extern struct cp_abi_ops current_cp_abi; + + extern enum ctor_kinds is_constructor_name (const char *name); + extern enum dtor_kinds is_destructor_name (const char *name); + extern int is_vtable_name (const char *name); + extern int is_operator_name (const char *name); + extern value_ptr value_virtual_fn_field (value_ptr * arg1p, + struct fn_field *f, int j, + struct type *type, int offset); + extern struct type *value_rtti_type (value_ptr v, int *full, int *top, + int *using_enc); + extern int register_cp_abi (struct cp_abi_ops abi); + extern int switch_to_cp_abi (const char *short_name); + + #endif + Index: gdb/dbxread.c =================================================================== RCS file: /cvs/src/src/gdb/dbxread.c,v retrieving revision 1.17 diff -c -r1.17 dbxread.c *** gdb/dbxread.c 2001/03/27 20:36:23 1.17 --- gdb/dbxread.c 2001/04/25 00:18:00 *************** *** 57,62 **** --- 57,63 ---- #include "demangle.h" #include "language.h" /* Needed inside partial-stab.h */ #include "complaints.h" + #include "cp-abi.h" #include "aout/aout64.h" #include "aout/stab_gnu.h" /* We always use GNU stabs, not native, now */ *************** *** 514,520 **** char *tempstring = name; if (tempstring[0] == bfd_get_symbol_leading_char (objfile->obfd)) ++tempstring; ! if (VTBL_PREFIX_P ((tempstring))) ms_type = mst_data; } section = SECT_OFF_DATA (objfile); --- 515,521 ---- char *tempstring = name; if (tempstring[0] == bfd_get_symbol_leading_char (objfile->obfd)) ++tempstring; ! if (is_vtable_name (tempstring)) ms_type = mst_data; } section = SECT_OFF_DATA (objfile); Index: gdb/eval.c =================================================================== RCS file: /cvs/src/src/gdb/eval.c,v retrieving revision 1.12 diff -c -r1.12 eval.c *** gdb/eval.c 2001/03/19 23:31:41 1.12 --- gdb/eval.c 2001/04/25 00:18:02 *************** *** 30,35 **** --- 30,36 ---- #include "frame.h" #include "language.h" /* For CAST_IS_CONVERSION */ #include "f-lang.h" /* for array bound stuff */ + #include "cp-abi.h" /* Defined in symtab.c */ extern int hp_som_som_object_present; Index: gdb/gdbtypes.c =================================================================== RCS file: /cvs/src/src/gdb/gdbtypes.c,v retrieving revision 1.19 diff -c -r1.19 gdbtypes.c *** gdb/gdbtypes.c 2001/03/20 01:37:09 1.19 --- gdb/gdbtypes.c 2001/04/25 00:18:05 *************** *** 35,40 **** --- 35,41 ---- #include "complaints.h" #include "gdbcmd.h" #include "wrapper.h" + #include "cp-abi.h" /* These variables point to the objects representing the predefined C data types. */ *************** *** 1027,1033 **** for (j = 0; j < TYPE_FN_FIELDLIST_LENGTH (t, i); j++) { ! if (DESTRUCTOR_PREFIX_P (TYPE_FN_FIELD_PHYSNAME (f, j))) { *method_indexp = i; *field_indexp = j; --- 1028,1034 ---- for (j = 0; j < TYPE_FN_FIELDLIST_LENGTH (t, i); j++) { ! if (is_destructor_name (TYPE_FN_FIELD_PHYSNAME (f, j)) != 0) { *method_indexp = i; *field_indexp = j; *************** *** 1902,1913 **** return -1; i = 0; ! vbase = TYPE_VIRTUAL_BASE_LIST (dclass)[0]; while (vbase) { if (vbase == base) break; ! vbase = TYPE_VIRTUAL_BASE_LIST (dclass)[++i]; } return vbase ? i : -1; --- 1903,1914 ---- return -1; i = 0; ! vbase = virtual_base_list (dclass)[0]; while (vbase) { if (vbase == base) break; ! vbase = virtual_base_list (dclass)[++i]; } return vbase ? i : -1; *************** *** 1936,1949 **** j = -1; i = 0; ! vbase = TYPE_VIRTUAL_BASE_LIST (dclass)[0]; while (vbase) { if (!primary || (virtual_base_index_skip_primaries (vbase, primary) < 0)) j++; if (vbase == base) break; ! vbase = TYPE_VIRTUAL_BASE_LIST (dclass)[++i]; } return vbase ? j : -1; --- 1937,1950 ---- j = -1; i = 0; ! vbase = virtual_base_list (dclass)[0]; while (vbase) { if (!primary || (virtual_base_index_skip_primaries (vbase, primary) < 0)) j++; if (vbase == base) break; ! vbase = virtual_base_list (dclass)[++i]; } return vbase ? j : -1; Index: gdb/gnu-v2-abi.c =================================================================== RCS file: gnu-v2-abi.c diff -N gnu-v2-abi.c *** gdb/gnu-v2-abi.c Tue May 5 13:32:27 1998 --- gdb/gnu-v2-abi.c Tue Apr 24 17:18:07 2001 *************** *** 0 **** --- 1,332 ---- + /* Abstraction of GNU v2 abi. + Contributed by Daniel Berlin + Copyright 2001 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or + modify + it under the terms of the GNU General Public License as published + by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + + #include "defs.h" + #include "gdb_regex.h" + #include "gdb_string.h" + #include "symtab.h" + #include "gdbtypes.h" + #include "value.h" + #include "demangle.h" + #include "cp-abi.h" + + #include + + struct cp_abi_ops gnu_v2_abi_ops; + + static int vb_match (struct type *, int, struct type *); + + static enum dtor_kinds + gnuv2_is_destructor_name (const char *name) + { + if ((name[0] == '_' && is_cplus_marker (name[1]) && name[2] == '_') + || strncmp (name, "__dt__", 6) == 0) + return complete_object_dtor; + else + return 0; + } + + static enum ctor_kinds + gnuv2_is_constructor_name (const char *name) + { + if ((name[0] == '_' && name[1] == '_' + && (isdigit (name[2]) || strchr ("Qt", name[2]))) + || strncmp (name, "__ct__", 6) == 0) + return complete_object_ctor; + else + return 0; + } + + static int + gnuv2_is_vtable_name (const char *name) + { + return (((name)[0] == '_' + && (((name)[1] == 'V' && (name)[2] == 'T') + || ((name)[1] == 'v' && (name)[2] == 't')) + && is_cplus_marker ((name)[3])) || + ((name)[0] == '_' && (name)[1] == '_' + && (name)[2] == 'v' && (name)[3] == 't' && (name)[4] == '_')); + } + + static int + gnuv2_is_operator_name (const char *name) + { + return strncmp (name, "operator", 8) == 0; + } + + + /* Return a virtual function as a value. + ARG1 is the object which provides the virtual function + table pointer. *ARG1P is side-effected in calling this function. + F is the list of member functions which contains the desired virtual + function. + J is an index into F which provides the desired virtual function. + + TYPE is the type in which F is located. */ + static value_ptr + gnuv2_virtual_fn_field (value_ptr * arg1p, struct fn_field * f, int j, + struct type * type, int offset) + { + value_ptr arg1 = *arg1p; + struct type *type1 = check_typedef (VALUE_TYPE (arg1)); + + + struct type *entry_type; + /* First, get the virtual function table pointer. That comes + with a strange type, so cast it to type `pointer to long' (which + should serve just fine as a function type). Then, index into + the table, and convert final value to appropriate function type. */ + value_ptr entry, vfn, vtbl; + value_ptr vi = value_from_longest (builtin_type_int, + (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j)); + struct type *fcontext = TYPE_FN_FIELD_FCONTEXT (f, j); + struct type *context; + if (fcontext == NULL) + /* We don't have an fcontext (e.g. the program was compiled with + g++ version 1). Try to get the vtbl from the TYPE_VPTR_BASETYPE. + This won't work right for multiple inheritance, but at least we + should do as well as GDB 3.x did. */ + fcontext = TYPE_VPTR_BASETYPE (type); + context = lookup_pointer_type (fcontext); + /* Now context is a pointer to the basetype containing the vtbl. */ + if (TYPE_TARGET_TYPE (context) != type1) + { + value_ptr tmp = value_cast (context, value_addr (arg1)); + VALUE_POINTED_TO_OFFSET (tmp) = 0; + arg1 = value_ind (tmp); + type1 = check_typedef (VALUE_TYPE (arg1)); + } + + context = type1; + /* Now context is the basetype containing the vtbl. */ + + /* 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 (context) < 0) + fill_in_vptr_fieldno (context); + + /* The virtual function table is now an array of structures + which have the form { int16 offset, delta; void *pfn; }. */ + vtbl = value_primitive_field (arg1, 0, TYPE_VPTR_FIELDNO (context), + TYPE_VPTR_BASETYPE (context)); + + /* With older versions of g++, the vtbl field pointed to an array + of structures. Nowadays it points directly to the structure. */ + if (TYPE_CODE (VALUE_TYPE (vtbl)) == TYPE_CODE_PTR + && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (vtbl))) == TYPE_CODE_ARRAY) + { + /* Handle the case where the vtbl field points to an + array of structures. */ + vtbl = value_ind (vtbl); + + /* Index into the virtual function table. This is hard-coded because + looking up a field is not cheap, and it may be important to save + time, e.g. if the user has set a conditional breakpoint calling + a virtual function. */ + entry = value_subscript (vtbl, vi); + } + else + { + /* Handle the case where the vtbl field points directly to a structure. */ + vtbl = value_add (vtbl, vi); + entry = value_ind (vtbl); + } + + entry_type = check_typedef (VALUE_TYPE (entry)); + + if (TYPE_CODE (entry_type) == TYPE_CODE_STRUCT) + { + /* Move the `this' pointer according to the virtual function table. */ + VALUE_OFFSET (arg1) += value_as_long (value_field (entry, 0)); + + if (!VALUE_LAZY (arg1)) + { + VALUE_LAZY (arg1) = 1; + value_fetch_lazy (arg1); + } + + vfn = value_field (entry, 2); + } + else if (TYPE_CODE (entry_type) == TYPE_CODE_PTR) + vfn = entry; + else + error ("I'm confused: virtual function table has bad type"); + /* Reinstantiate the function pointer with the correct type. */ + VALUE_TYPE (vfn) = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j)); + + *arg1p = arg1; + return vfn; + } + + + struct type * + gnuv2_value_rtti_type (value_ptr v, int *full, int *top, int *using_enc) + { + struct type *known_type; + struct type *rtti_type; + CORE_ADDR coreptr; + value_ptr vp; + int using_enclosing = 0; + long top_offset = 0; + char rtti_type_name[256]; + CORE_ADDR vtbl; + struct minimal_symbol *minsym; + struct symbol *sym; + char *demangled_name; + struct type *btype; + + if (full) + *full = 0; + if (top) + *top = -1; + if (using_enc) + *using_enc = 0; + + /* Get declared type */ + known_type = VALUE_TYPE (v); + CHECK_TYPEDEF (known_type); + /* RTTI works only or class objects */ + if (TYPE_CODE (known_type) != TYPE_CODE_CLASS) + return NULL; + + /* Plan on this changing in the future as i get around to setting + the vtables properly for G++ compiled stuff. Also, I'll be using + the type info functions, which are always right. Deal with it + until then. */ + + /* If the type has no vptr fieldno, try to get it filled in */ + if (TYPE_VPTR_FIELDNO(known_type) < 0) + fill_in_vptr_fieldno(known_type); + + /* If we still can't find one, give up */ + if (TYPE_VPTR_FIELDNO(known_type) < 0) + return NULL; + + /* Make sure our basetype and known type match, otherwise, cast + so we can get at the vtable properly. + */ + btype = TYPE_VPTR_BASETYPE (known_type); + CHECK_TYPEDEF (btype); + if (btype != known_type ) + { + v = value_cast (btype, v); + if (using_enc) + *using_enc=1; + } + /* + We can't use value_ind here, because it would want to use RTTI, and + we'd waste a bunch of time figuring out we already know the type. + Besides, we don't care about the type, just the actual pointer + */ + if (VALUE_ADDRESS (value_field (v, TYPE_VPTR_FIELDNO (known_type))) == 0) + return NULL; + + /* + If we are enclosed by something that isn't us, adjust the + address properly and set using_enclosing. + */ + if (VALUE_ENCLOSING_TYPE(v) != VALUE_TYPE(v)) + { + value_ptr tempval; + int bitpos = TYPE_BASECLASS_BITPOS (known_type, + TYPE_VPTR_FIELDNO (known_type)); + tempval=value_field (v, TYPE_VPTR_FIELDNO(known_type)); + VALUE_ADDRESS(tempval) += bitpos / 8; + vtbl=value_as_pointer (tempval); + using_enclosing=1; + } + else + { + vtbl=value_as_pointer(value_field(v,TYPE_VPTR_FIELDNO(known_type))); + using_enclosing=0; + } + + /* Try to find a symbol that is the vtable */ + minsym=lookup_minimal_symbol_by_pc(vtbl); + if (minsym==NULL + || (demangled_name=SYMBOL_NAME(minsym))==NULL + || !is_vtable_name (demangled_name)) + return NULL; + + /* If we just skip the prefix, we get screwed by namespaces */ + demangled_name=cplus_demangle(demangled_name,DMGL_PARAMS|DMGL_ANSI); + *(strchr(demangled_name,' '))=0; + + /* Lookup the type for the name */ + rtti_type=lookup_typename(demangled_name, (struct block *)0,1); + + if (rtti_type==NULL) + return NULL; + + if (TYPE_N_BASECLASSES(rtti_type) > 1 && full && (*full) != 1) + { + if (top) + *top=TYPE_BASECLASS_BITPOS(rtti_type,TYPE_VPTR_FIELDNO(rtti_type))/8; + if (top && ((*top) >0)) + { + if (TYPE_LENGTH(rtti_type) > TYPE_LENGTH(known_type)) + { + if (full) + *full=0; + } + else + { + if (full) + *full=1; + } + } + } + else + { + if (full) + *full=1; + } + if (using_enc) + *using_enc=using_enclosing; + + return rtti_type; + } + + + static void + init_gnuv2_ops (void) + { + gnu_v2_abi_ops.shortname = "gnu-v2"; + gnu_v2_abi_ops.longname = "GNU G++ Version 2 ABI"; + gnu_v2_abi_ops.doc = "G++ Version 2 ABI"; + gnu_v2_abi_ops.is_destructor_name = gnuv2_is_destructor_name; + gnu_v2_abi_ops.is_constructor_name = gnuv2_is_constructor_name; + gnu_v2_abi_ops.is_vtable_name = gnuv2_is_vtable_name; + gnu_v2_abi_ops.is_operator_name = gnuv2_is_operator_name; + gnu_v2_abi_ops.virtual_fn_field = gnuv2_virtual_fn_field; + gnu_v2_abi_ops.rtti_type = gnuv2_value_rtti_type; + } + + void + _initialize_gnu_v2_abi (void) + { + init_gnuv2_ops (); + register_cp_abi (gnu_v2_abi_ops); + switch_to_cp_abi ("gnu-v2"); + } Index: gdb/hpacc-abi.c =================================================================== RCS file: hpacc-abi.c diff -N hpacc-abi.c *** gdb/hpacc-abi.c Tue May 5 13:32:27 1998 --- gdb/hpacc-abi.c Tue Apr 24 17:18:07 2001 *************** *** 0 **** --- 1,321 ---- + /* Abstraction of HP aCC ABI. + Contributed by Daniel Berlin + Most of the real code is from HP, i've just fiddled it to fit in + the C++ ABI abstraction framework. + + Copyright 2001 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or + modify + it under the terms of the GNU General Public License as published + by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + + #include "defs.h" + #include "value.h" + #include "gdb_regex.h" + #include "gdb_string.h" + #include "gdbtypes.h" + #include "gdbcore.h" + #include "cp-abi.h" + + struct cp_abi_ops hpacc_abi_ops; + + /* It appears the is_*_name stuff is never used when we try the hpACC + * ABI. As such, I have no clue what the real answers are. Shouldn't + * have any more effect than it does now. */ + static regex_t constructor_pattern; + static regex_t destructor_pattern; + static regex_t operator_pattern; + + static enum dtor_kinds + hpacc_is_destructor_name (const char *name) + { + if (regexec (&destructor_pattern, name, 0, 0, 0) == 0) + return complete_object_dtor; + else + return 0; + } + + static enum ctor_kinds + hpacc_is_constructor_name (const char *name) + { + if (regexec (&constructor_pattern, name, 0, 0, 0) == 0) + return complete_object_ctor; + else + return 0; + } + + static int + hpacc_is_operator_name (const char *name) + { + return regexec (&operator_pattern, name, 0, 0, 0) == 0; + } + + static int + hpacc_is_vtable_name (const char *name) + { + return strcmp (name, + "This will never match anything, please fill it in") == 0; + } + + /* Return a virtual function as a value. + ARG1 is the object which provides the virtual function + table pointer. *ARG1P is side-effected in calling this function. + F is the list of member functions which contains the desired virtual + function. + J is an index into F which provides the desired virtual function. + + TYPE is the type in which F is located. */ + static value_ptr + hpacc_virtual_fn_field (value_ptr * arg1p, struct fn_field * f, int j, + struct type * type, int offset) + { + value_ptr arg1 = *arg1p; + struct type *type1 = check_typedef (VALUE_TYPE (arg1)); + + /* Deal with HP/Taligent runtime model for virtual functions */ + value_ptr vp; + value_ptr argp; /* arg1 cast to base */ + CORE_ADDR coreptr; /* pointer to target address */ + int class_index; /* which class segment pointer to use */ + struct type *ftype = TYPE_FN_FIELD_TYPE (f, j); /* method type */ + + argp = value_cast (type, *arg1p); + + if (VALUE_ADDRESS (argp) == 0) + error ("Address of object is null; object may not have been created."); + + /* pai: FIXME -- 32x64 possible problem? */ + /* First word (4 bytes) in object layout is the vtable pointer */ + coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (argp)); /* pai: (temp) */ + /* + offset + VALUE_EMBEDDED_OFFSET (argp)); */ + + if (!coreptr) + error + ("Virtual table pointer is null for object; object may not have been created."); + + /* pai/1997-05-09 + * FIXME: The code here currently handles only + * the non-RRBC case of the Taligent/HP runtime spec; when RRBC + * is introduced, the condition for the "if" below will have to + * be changed to be a test for the RRBC case. */ + + if (1) + { + /* Non-RRBC case; the virtual function pointers are stored at fixed + * offsets in the virtual table. */ + + /* Retrieve the offset in the virtual table from the debug + * info. The offset of the vfunc's entry is in words from + * the beginning of the vtable; but first we have to adjust + * by HP_ACC_VFUNC_START to account for other entries */ + + /* pai: FIXME: 32x64 problem here, a word may be 8 bytes in + * which case the multiplier should be 8 and values should be long */ + vp = value_at (builtin_type_int, + coreptr + 4 * (TYPE_FN_FIELD_VOFFSET (f, j) + + HP_ACC_VFUNC_START), NULL); + + coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp)); + /* coreptr now contains the address of the virtual function */ + /* (Actually, it contains the pointer to the plabel for the function. */ + } + else + { + /* RRBC case; the virtual function pointers are found by double + * indirection through the class segment tables. */ + + /* Choose class segment depending on type we were passed */ + class_index = class_index_in_primary_list (type); + + /* Find class segment pointer. These are in the vtable slots after + * some other entries, so adjust by HP_ACC_VFUNC_START for that. */ + /* pai: FIXME 32x64 problem here, if words are 8 bytes long + * the multiplier below has to be 8 and value should be long. */ + vp = value_at (builtin_type_int, + coreptr + 4 * (HP_ACC_VFUNC_START + class_index), NULL); + /* Indirect once more, offset by function index */ + /* pai: FIXME 32x64 problem here, again multiplier could be 8 and value long */ + coreptr = + *(CORE_ADDR *) (VALUE_CONTENTS (vp) + + 4 * TYPE_FN_FIELD_VOFFSET (f, j)); + vp = value_at (builtin_type_int, coreptr, NULL); + coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp)); + + /* coreptr now contains the address of the virtual function */ + /* (Actually, it contains the pointer to the plabel for the function.) */ + + } + + if (!coreptr) + error ("Address of virtual function is null; error in virtual table?"); + + /* Wrap this addr in a value and return pointer */ + vp = allocate_value (ftype); + VALUE_TYPE (vp) = ftype; + VALUE_ADDRESS (vp) = coreptr; + + /* pai: (temp) do we need the value_ind stuff in value_fn_field? */ + return vp; + } + + + static struct type * + hpacc_value_rtti_type (value_ptr v, int *full, int *top, int *using_enc) + { + struct type *known_type; + struct type *rtti_type; + CORE_ADDR coreptr; + value_ptr vp; + int using_enclosing = 0; + long top_offset = 0; + char rtti_type_name[256]; + + if (full) + *full = 0; + if (top) + *top = -1; + if (using_enc) + *using_enc = 0; + + /* Get declared type */ + known_type = VALUE_TYPE (v); + CHECK_TYPEDEF (known_type); + /* RTTI works only or class objects */ + if (TYPE_CODE (known_type) != TYPE_CODE_CLASS) + return NULL; + + /* If neither the declared type nor the enclosing type of the + * value structure has a HP ANSI C++ style virtual table, + * we can't do anything. */ + if (!TYPE_HAS_VTABLE (known_type)) + { + known_type = VALUE_ENCLOSING_TYPE (v); + CHECK_TYPEDEF (known_type); + if ((TYPE_CODE (known_type) != TYPE_CODE_CLASS) || + !TYPE_HAS_VTABLE (known_type)) + return NULL; /* No RTTI, or not HP-compiled types */ + CHECK_TYPEDEF (known_type); + using_enclosing = 1; + } + + if (using_enclosing && using_enc) + *using_enc = 1; + + /* First get the virtual table address */ + coreptr = *(CORE_ADDR *) ((VALUE_CONTENTS_ALL (v)) + + VALUE_OFFSET (v) + + (using_enclosing + ? 0 + : VALUE_EMBEDDED_OFFSET (v))); + if (coreptr == 0) + /* return silently -- maybe called on gdb-generated value */ + return NULL; + + /* Fetch the top offset of the object */ + /* FIXME possible 32x64 problem with pointer size & arithmetic */ + vp = value_at (builtin_type_int, + coreptr + 4 * HP_ACC_TOP_OFFSET_OFFSET, + VALUE_BFD_SECTION (v)); + top_offset = value_as_long (vp); + if (top) + *top = top_offset; + + /* Fetch the typeinfo pointer */ + /* FIXME possible 32x64 problem with pointer size & arithmetic */ + vp = value_at (builtin_type_int, coreptr + 4 * HP_ACC_TYPEINFO_OFFSET, + VALUE_BFD_SECTION (v)); + /* Indirect through the typeinfo pointer and retrieve the pointer + * to the string name */ + coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp)); + if (!coreptr) + error ("Retrieved null typeinfo pointer in trying to determine " + "run-time type"); + /* 4 -> offset of name field */ + vp = value_at (builtin_type_int, coreptr + 4, VALUE_BFD_SECTION (v)); + /* FIXME possible 32x64 problem */ + + coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp)); + + read_memory_string (coreptr, rtti_type_name, 256); + + if (strlen (rtti_type_name) == 0) + error ("Retrieved null type name from typeinfo"); + + /* search for type */ + rtti_type = lookup_typename (rtti_type_name, (struct block *) 0, 1); + + if (!rtti_type) + error ("Could not find run-time type: invalid type name %s in typeinfo??", + rtti_type_name); + CHECK_TYPEDEF (rtti_type); + #if 0 + printf ("RTTI type name %s, tag %s, full? %d\n", TYPE_NAME (rtti_type), + TYPE_TAG_NAME (rtti_type), full ? *full : -1); + #endif + /* Check whether we have the entire object */ + if (full /* Non-null pointer passed */ + && + /* Either we checked on the whole object in hand and found the + top offset to be zero */ + (((top_offset == 0) && + using_enclosing && + TYPE_LENGTH (known_type) == TYPE_LENGTH (rtti_type)) + || + /* Or we checked on the embedded object and top offset was the + same as the embedded offset */ + ((top_offset == VALUE_EMBEDDED_OFFSET (v)) && + !using_enclosing && + TYPE_LENGTH (VALUE_ENCLOSING_TYPE (v)) == TYPE_LENGTH (rtti_type)))) + + *full = 1; + + return rtti_type; + } + + + static void + init_hpacc_ops (void) + { + hpacc_abi_ops.shortname = "hpaCC"; + hpacc_abi_ops.longname = "HP aCC ABI"; + hpacc_abi_ops.doc = "HP aCC ABI"; + hpacc_abi_ops.is_destructor_name = hpacc_is_destructor_name; + hpacc_abi_ops.is_constructor_name = hpacc_is_constructor_name; + hpacc_abi_ops.is_vtable_name = hpacc_is_vtable_name; + hpacc_abi_ops.is_operator_name = hpacc_is_operator_name; + hpacc_abi_ops.virtual_fn_field = hpacc_virtual_fn_field; + hpacc_abi_ops.rtti_type = hpacc_value_rtti_type; + } + + + void + _initialize_hpacc_abi (void) + { + init_hpacc_ops (); + + regcomp (&constructor_pattern, + "^This will never match anything, please fill it in$", REG_NOSUB); + + regcomp (&destructor_pattern, + "^This will never match anything, please fill it in$", REG_NOSUB); + + regcomp (&operator_pattern, + "^This will never match anything, please fill it in$", REG_NOSUB); + + register_cp_abi (hpacc_abi_ops); + } Index: gdb/jv-typeprint.c =================================================================== RCS file: /cvs/src/src/gdb/jv-typeprint.c,v retrieving revision 1.4 diff -c -r1.4 jv-typeprint.c *** gdb/jv-typeprint.c 2001/03/06 08:21:09 1.4 --- gdb/jv-typeprint.c 2001/04/25 00:18:07 *************** *** 28,33 **** --- 28,34 ---- #include "gdb_string.h" #include "typeprint.h" #include "c-lang.h" + #include "cp-abi.h" /* Local functions */ *************** *** 224,235 **** physname = TYPE_FN_FIELD_PHYSNAME (f, j); ! is_full_physname_constructor = ! ((physname[0] == '_' && physname[1] == '_' ! && strchr ("0123456789Qt", physname[2])) ! || STREQN (physname, "__ct__", 6) ! || DESTRUCTOR_PREFIX_P (physname) ! || STREQN (physname, "__dt__", 6)); QUIT; --- 225,233 ---- physname = TYPE_FN_FIELD_PHYSNAME (f, j); ! is_full_physname_constructor ! = (is_constructor_name (physname) ! || is_destructor_name (physname)); QUIT; Index: gdb/linespec.c =================================================================== RCS file: /cvs/src/src/gdb/linespec.c,v retrieving revision 1.10 diff -c -r1.10 linespec.c *** gdb/linespec.c 2001/03/23 00:41:01 1.10 --- gdb/linespec.c 2001/04/25 00:18:09 *************** *** 29,34 **** --- 29,35 ---- #include "demangle.h" #include "value.h" #include "completer.h" + #include "cp-abi.h" /* Prototype for one function in parser-defs.h, instead of including that entire file. */ *************** *** 166,172 **** phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter); /* Destructor is handled by caller, dont add it to the list */ ! if (DESTRUCTOR_PREFIX_P (phys_name)) continue; sym_arr[i1] = lookup_symbol (phys_name, --- 167,173 ---- phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter); /* Destructor is handled by caller, dont add it to the list */ ! if (is_destructor_name (phys_name) != 0) continue; sym_arr[i1] = lookup_symbol (phys_name, *************** *** 801,807 **** { char *tmp; ! if (OPNAME_PREFIX_P (copy)) { tmp = (char *) alloca (strlen (copy + 3) + 9); strcpy (tmp, "operator "); --- 802,808 ---- { char *tmp; ! if (is_operator_name (copy)) { tmp = (char *) alloca (strlen (copy + 3) + 9); strcpy (tmp, "operator "); Index: gdb/symtab.c =================================================================== RCS file: /cvs/src/src/gdb/symtab.c,v retrieving revision 1.34 diff -c -r1.34 symtab.c *** gdb/symtab.c 2001/04/01 19:50:50 1.34 --- gdb/symtab.c 2001/04/25 00:18:13 *************** *** 45,50 **** --- 45,51 ---- #include "gdb_string.h" #include "gdb_stat.h" #include + #include "cp-abi.h" /* Prototype for one function in parser-defs.h, instead of including that entire file. */ *************** *** 288,307 **** int is_full_physname_constructor; int is_constructor; ! int is_destructor = DESTRUCTOR_PREFIX_P (physname); /* Need a new type prefix. */ char *const_prefix = method->is_const ? "C" : ""; char *volatile_prefix = method->is_volatile ? "V" : ""; char buf[20]; int len = (newname == NULL ? 0 : strlen (newname)); ! if (OPNAME_PREFIX_P (field_name)) return xstrdup (physname); ! is_full_physname_constructor = ! ((physname[0] == '_' && physname[1] == '_' && ! (isdigit (physname[2]) || physname[2] == 'Q' || physname[2] == 't')) ! || (strncmp (physname, "__ct", 4) == 0)); is_constructor = is_full_physname_constructor || (newname && STREQ (field_name, newname)); --- 289,305 ---- int is_full_physname_constructor; int is_constructor; ! int is_destructor = is_destructor_name (physname); /* Need a new type prefix. */ char *const_prefix = method->is_const ? "C" : ""; char *volatile_prefix = method->is_volatile ? "V" : ""; char buf[20]; int len = (newname == NULL ? 0 : strlen (newname)); ! if (is_operator_name (field_name)) return xstrdup (physname); ! is_full_physname_constructor = is_constructor_name (physname); is_constructor = is_full_physname_constructor || (newname && STREQ (field_name, newname)); Index: gdb/symtab.h =================================================================== RCS file: /cvs/src/src/gdb/symtab.h,v retrieving revision 1.20 diff -c -r1.20 symtab.h *** gdb/symtab.h 2001/03/07 02:57:08 1.20 --- gdb/symtab.h 2001/04/25 00:18:15 *************** *** 1046,1075 **** #define VTBL_FNADDR_OFFSET 2 - /* Macro that yields non-zero value iff NAME is the prefix for C++ operator - names. If you leave out the parenthesis here you will lose! */ - #define OPNAME_PREFIX_P(NAME) \ - (!strncmp (NAME, "operator", 8)) - - /* Macro that yields non-zero value iff NAME is the prefix for C++ vtbl - names. Note that this macro is g++ specific (FIXME). - '_vt$' is the old cfront-style vtables; '_VT$' is the new - style, using thunks (where '$' is really CPLUS_MARKER). */ - - #define VTBL_PREFIX_P(NAME) \ - (((NAME)[0] == '_' \ - && (((NAME)[1] == 'V' && (NAME)[2] == 'T') \ - || ((NAME)[1] == 'v' && (NAME)[2] == 't')) \ - && is_cplus_marker ((NAME)[3])) || ((NAME)[0]=='_' && (NAME)[1]=='_' \ - && (NAME)[2]=='v' && (NAME)[3]=='t' && (NAME)[4]=='_')) - - /* Macro that yields non-zero value iff NAME is the prefix for C++ destructor - names. Note that this macro is g++ specific (FIXME). */ - - #define DESTRUCTOR_PREFIX_P(NAME) \ - ((NAME)[0] == '_' && is_cplus_marker ((NAME)[1]) && (NAME)[2] == '_') - - /* External variables and functions for the objects described above. */ /* This symtab variable specifies the current file for printing source lines */ --- 1046,1051 ---- Index: gdb/typeprint.c =================================================================== RCS file: /cvs/src/src/gdb/typeprint.c,v retrieving revision 1.8 diff -c -r1.8 typeprint.c *** gdb/typeprint.c 2001/03/07 02:57:08 1.8 --- gdb/typeprint.c 2001/04/25 00:18:15 *************** *** 31,36 **** --- 31,37 ---- #include "gdbcmd.h" #include "target.h" #include "language.h" + #include "cp-abi.h" #include "gdb_string.h" #include Index: gdb/valops.c =================================================================== RCS file: /cvs/src/src/gdb/valops.c,v retrieving revision 1.34 diff -c -r1.34 valops.c *** gdb/valops.c 2001/03/19 20:08:16 1.34 --- gdb/valops.c 2001/04/25 00:18:19 *************** *** 31,36 **** --- 31,37 ---- #include "language.h" #include "gdbcmd.h" #include "regcache.h" + #include "cp-abi.h" #include #include "gdb_string.h" *************** *** 3110,3335 **** return 0; } - - /* Find the real run-time type of a value using RTTI. - * V is a pointer to the value. - * A pointer to the struct type entry of the run-time type - * is returneed. - * FULL is a flag that is set only if the value V includes - * the entire contents of an object of the RTTI type. - * TOP is the offset to the top of the enclosing object of - * the real run-time type. This offset may be for the embedded - * object, or for the enclosing object of V. - * USING_ENC is the flag that distinguishes the two cases. - * If it is 1, then the offset is for the enclosing object, - * otherwise for the embedded object. - * - */ - - struct type * - value_rtti_type (value_ptr v, int *full, int *top, int *using_enc) - { - struct type *known_type; - struct type *rtti_type; - CORE_ADDR coreptr; - value_ptr vp; - int using_enclosing = 0; - long top_offset = 0; - char rtti_type_name[256]; - - if (full) - *full = 0; - if (top) - *top = -1; - if (using_enc) - *using_enc = 0; - - /* Get declared type */ - known_type = VALUE_TYPE (v); - CHECK_TYPEDEF (known_type); - /* RTTI works only or class objects */ - if (TYPE_CODE (known_type) != TYPE_CODE_CLASS) - return NULL; - if (TYPE_HAS_VTABLE(known_type)) - { - /* If neither the declared type nor the enclosing type of the - * value structure has a HP ANSI C++ style virtual table, - * we can't do anything. */ - if (!TYPE_HAS_VTABLE (known_type)) - { - known_type = VALUE_ENCLOSING_TYPE (v); - CHECK_TYPEDEF (known_type); - if ((TYPE_CODE (known_type) != TYPE_CODE_CLASS) || - !TYPE_HAS_VTABLE (known_type)) - return NULL; /* No RTTI, or not HP-compiled types */ - CHECK_TYPEDEF (known_type); - using_enclosing = 1; - } - - if (using_enclosing && using_enc) - *using_enc = 1; - - /* First get the virtual table address */ - coreptr = *(CORE_ADDR *) ((VALUE_CONTENTS_ALL (v)) - + VALUE_OFFSET (v) - + (using_enclosing ? 0 : VALUE_EMBEDDED_OFFSET (v))); - if (coreptr == 0) - return NULL; /* return silently -- maybe called on gdb-generated value */ - - /* Fetch the top offset of the object */ - /* FIXME possible 32x64 problem with pointer size & arithmetic */ - vp = value_at (builtin_type_int, - coreptr + 4 * HP_ACC_TOP_OFFSET_OFFSET, - VALUE_BFD_SECTION (v)); - top_offset = value_as_long (vp); - if (top) - *top = top_offset; - - /* Fetch the typeinfo pointer */ - /* FIXME possible 32x64 problem with pointer size & arithmetic */ - vp = value_at (builtin_type_int, coreptr + 4 * HP_ACC_TYPEINFO_OFFSET, VALUE_BFD_SECTION (v)); - /* Indirect through the typeinfo pointer and retrieve the pointer - * to the string name */ - coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp)); - if (!coreptr) - error ("Retrieved null typeinfo pointer in trying to determine run-time type"); - vp = value_at (builtin_type_int, coreptr + 4, VALUE_BFD_SECTION (v)); /* 4 -> offset of name field */ - /* FIXME possible 32x64 problem */ - - coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp)); - - read_memory_string (coreptr, rtti_type_name, 256); - - if (strlen (rtti_type_name) == 0) - error ("Retrieved null type name from typeinfo"); - - /* search for type */ - rtti_type = lookup_typename (rtti_type_name, (struct block *) 0, 1); - - if (!rtti_type) - error ("Could not find run-time type: invalid type name %s in typeinfo??", rtti_type_name); - CHECK_TYPEDEF (rtti_type); - #if 0 - printf ("RTTI type name %s, tag %s, full? %d\n", TYPE_NAME (rtti_type), TYPE_TAG_NAME (rtti_type), full ? *full : -1); - #endif - /* Check whether we have the entire object */ - if (full /* Non-null pointer passed */ - && - /* Either we checked on the whole object in hand and found the - top offset to be zero */ - (((top_offset == 0) && - using_enclosing && - TYPE_LENGTH (known_type) == TYPE_LENGTH (rtti_type)) - || - /* Or we checked on the embedded object and top offset was the - same as the embedded offset */ - ((top_offset == VALUE_EMBEDDED_OFFSET (v)) && - !using_enclosing && - TYPE_LENGTH (VALUE_ENCLOSING_TYPE (v)) == TYPE_LENGTH (rtti_type)))) - - *full = 1; - } - else - /* - Right now this is G++ RTTI. Plan on this changing in the - future as i get around to setting the vtables properly for G++ - compiled stuff. Also, i'll be using the type info functions, - which are always right. Deal with it until then. - */ - { - CORE_ADDR vtbl; - struct minimal_symbol *minsym; - struct symbol *sym; - char *demangled_name; - struct type *btype; - /* If the type has no vptr fieldno, try to get it filled in */ - if (TYPE_VPTR_FIELDNO(known_type) < 0) - fill_in_vptr_fieldno(known_type); - - /* If we still can't find one, give up */ - if (TYPE_VPTR_FIELDNO(known_type) < 0) - return NULL; - - /* Make sure our basetype and known type match, otherwise, cast - so we can get at the vtable properly. - */ - btype = TYPE_VPTR_BASETYPE (known_type); - CHECK_TYPEDEF (btype); - if (btype != known_type ) - { - v = value_cast (btype, v); - if (using_enc) - *using_enc=1; - } - /* - We can't use value_ind here, because it would want to use RTTI, and - we'd waste a bunch of time figuring out we already know the type. - Besides, we don't care about the type, just the actual pointer - */ - if (VALUE_ADDRESS (value_field (v, TYPE_VPTR_FIELDNO (known_type))) == 0) - return NULL; - - /* - If we are enclosed by something that isn't us, adjust the - address properly and set using_enclosing. - */ - if (VALUE_ENCLOSING_TYPE(v) != VALUE_TYPE(v)) - { - value_ptr tempval; - tempval=value_field(v,TYPE_VPTR_FIELDNO(known_type)); - VALUE_ADDRESS(tempval)+=(TYPE_BASECLASS_BITPOS(known_type,TYPE_VPTR_FIELDNO(known_type))/8); - vtbl=value_as_pointer(tempval); - using_enclosing=1; - } - else - { - vtbl=value_as_pointer(value_field(v,TYPE_VPTR_FIELDNO(known_type))); - using_enclosing=0; - } - - /* Try to find a symbol that is the vtable */ - minsym=lookup_minimal_symbol_by_pc(vtbl); - if (minsym==NULL || (demangled_name=SYMBOL_NAME(minsym))==NULL || !VTBL_PREFIX_P(demangled_name)) - return NULL; - - /* If we just skip the prefix, we get screwed by namespaces */ - demangled_name=cplus_demangle(demangled_name,DMGL_PARAMS|DMGL_ANSI); - *(strchr(demangled_name,' '))=0; - - /* Lookup the type for the name */ - rtti_type=lookup_typename(demangled_name, (struct block *)0,1); - - if (rtti_type==NULL) - return NULL; - - if (TYPE_N_BASECLASSES(rtti_type) > 1 && full && (*full) != 1) - { - if (top) - *top=TYPE_BASECLASS_BITPOS(rtti_type,TYPE_VPTR_FIELDNO(rtti_type))/8; - if (top && ((*top) >0)) - { - if (TYPE_LENGTH(rtti_type) > TYPE_LENGTH(known_type)) - { - if (full) - *full=0; - } - else - { - if (full) - *full=1; - } - } - } - else - { - if (full) - *full=1; - } - if (using_enc) - *using_enc=using_enclosing; - } - return rtti_type; - } /* Given a pointer value V, find the real (RTTI) type of the object it points to. --- 3111,3116 ---- Index: gdb/value.h =================================================================== RCS file: /cvs/src/src/gdb/value.h,v retrieving revision 1.17 diff -c -r1.17 value.h *** gdb/value.h 2001/03/06 08:21:18 1.17 --- gdb/value.h 2001/04/25 00:18:20 *************** *** 367,373 **** extern value_ptr value_primitive_field (value_ptr arg1, int offset, int fieldno, struct type *arg_type); - extern struct type *value_rtti_type (value_ptr, int *, int *, int *); extern struct type *value_rtti_target_type (value_ptr, int *, int *, int *); --- 367,372 ---- *************** *** 446,455 **** extern value_ptr value_fn_field (value_ptr * arg1p, struct fn_field *f, int j, struct type *type, int offset); - - extern value_ptr value_virtual_fn_field (value_ptr * arg1p, - struct fn_field *f, int j, - struct type *type, int offset); extern int binop_user_defined_p (enum exp_opcode op, value_ptr arg1, value_ptr arg2); --- 445,450 ---- Index: gdb/values.c =================================================================== RCS file: /cvs/src/src/gdb/values.c,v retrieving revision 1.14 diff -c -r1.14 values.c *** gdb/values.c 2001/03/27 20:36:24 1.14 --- gdb/values.c 2001/04/25 00:18:21 *************** *** 45,51 **** static void show_convenience (char *, int); - static int vb_match (struct type *, int, struct type *); /* The value-history records all the values printed by print commands during this session. Each chunk --- 45,50 ---- *************** *** 888,1084 **** } return v; - } - - /* Return a virtual function as a value. - ARG1 is the object which provides the virtual function - table pointer. *ARG1P is side-effected in calling this function. - F is the list of member functions which contains the desired virtual - function. - J is an index into F which provides the desired virtual function. - - TYPE is the type in which F is located. */ - value_ptr - value_virtual_fn_field (value_ptr *arg1p, struct fn_field *f, int j, - struct type *type, int offset) - { - value_ptr arg1 = *arg1p; - struct type *type1 = check_typedef (VALUE_TYPE (arg1)); - - if (TYPE_HAS_VTABLE (type)) - { - /* Deal with HP/Taligent runtime model for virtual functions */ - value_ptr vp; - value_ptr argp; /* arg1 cast to base */ - CORE_ADDR coreptr; /* pointer to target address */ - int class_index; /* which class segment pointer to use */ - struct type *ftype = TYPE_FN_FIELD_TYPE (f, j); /* method type */ - - argp = value_cast (type, *arg1p); - - if (VALUE_ADDRESS (argp) == 0) - error ("Address of object is null; object may not have been created."); - - /* pai: FIXME -- 32x64 possible problem? */ - /* First word (4 bytes) in object layout is the vtable pointer */ - coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (argp)); /* pai: (temp) */ - /* + offset + VALUE_EMBEDDED_OFFSET (argp)); */ - - if (!coreptr) - error ("Virtual table pointer is null for object; object may not have been created."); - - /* pai/1997-05-09 - * FIXME: The code here currently handles only - * the non-RRBC case of the Taligent/HP runtime spec; when RRBC - * is introduced, the condition for the "if" below will have to - * be changed to be a test for the RRBC case. */ - - if (1) - { - /* Non-RRBC case; the virtual function pointers are stored at fixed - * offsets in the virtual table. */ - - /* Retrieve the offset in the virtual table from the debug - * info. The offset of the vfunc's entry is in words from - * the beginning of the vtable; but first we have to adjust - * by HP_ACC_VFUNC_START to account for other entries */ - - /* pai: FIXME: 32x64 problem here, a word may be 8 bytes in - * which case the multiplier should be 8 and values should be long */ - vp = value_at (builtin_type_int, - coreptr + 4 * (TYPE_FN_FIELD_VOFFSET (f, j) + HP_ACC_VFUNC_START), NULL); - - coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp)); - /* coreptr now contains the address of the virtual function */ - /* (Actually, it contains the pointer to the plabel for the function. */ - } - else - { - /* RRBC case; the virtual function pointers are found by double - * indirection through the class segment tables. */ - - /* Choose class segment depending on type we were passed */ - class_index = class_index_in_primary_list (type); - - /* Find class segment pointer. These are in the vtable slots after - * some other entries, so adjust by HP_ACC_VFUNC_START for that. */ - /* pai: FIXME 32x64 problem here, if words are 8 bytes long - * the multiplier below has to be 8 and value should be long. */ - vp = value_at (builtin_type_int, - coreptr + 4 * (HP_ACC_VFUNC_START + class_index), NULL); - /* Indirect once more, offset by function index */ - /* pai: FIXME 32x64 problem here, again multiplier could be 8 and value long */ - coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp) + 4 * TYPE_FN_FIELD_VOFFSET (f, j)); - vp = value_at (builtin_type_int, coreptr, NULL); - coreptr = *(CORE_ADDR *) (VALUE_CONTENTS (vp)); - - /* coreptr now contains the address of the virtual function */ - /* (Actually, it contains the pointer to the plabel for the function.) */ - - } - - if (!coreptr) - error ("Address of virtual function is null; error in virtual table?"); - - /* Wrap this addr in a value and return pointer */ - vp = allocate_value (ftype); - VALUE_TYPE (vp) = ftype; - VALUE_ADDRESS (vp) = coreptr; - - /* pai: (temp) do we need the value_ind stuff in value_fn_field? */ - return vp; - } - else - { /* Not using HP/Taligent runtime conventions; so try to - * use g++ conventions for virtual table */ - - struct type *entry_type; - /* First, get the virtual function table pointer. That comes - with a strange type, so cast it to type `pointer to long' (which - should serve just fine as a function type). Then, index into - the table, and convert final value to appropriate function type. */ - value_ptr entry, vfn, vtbl; - value_ptr vi = value_from_longest (builtin_type_int, - (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j)); - struct type *fcontext = TYPE_FN_FIELD_FCONTEXT (f, j); - struct type *context; - if (fcontext == NULL) - /* We don't have an fcontext (e.g. the program was compiled with - g++ version 1). Try to get the vtbl from the TYPE_VPTR_BASETYPE. - This won't work right for multiple inheritance, but at least we - should do as well as GDB 3.x did. */ - fcontext = TYPE_VPTR_BASETYPE (type); - context = lookup_pointer_type (fcontext); - /* Now context is a pointer to the basetype containing the vtbl. */ - if (TYPE_TARGET_TYPE (context) != type1) - { - value_ptr tmp = value_cast (context, value_addr (arg1)); - VALUE_POINTED_TO_OFFSET (tmp) = 0; - arg1 = value_ind (tmp); - type1 = check_typedef (VALUE_TYPE (arg1)); - } - - context = type1; - /* Now context is the basetype containing the vtbl. */ - - /* 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 (context) < 0) - fill_in_vptr_fieldno (context); - - /* The virtual function table is now an array of structures - which have the form { int16 offset, delta; void *pfn; }. */ - vtbl = value_primitive_field (arg1, 0, TYPE_VPTR_FIELDNO (context), - TYPE_VPTR_BASETYPE (context)); - - /* With older versions of g++, the vtbl field pointed to an array - of structures. Nowadays it points directly to the structure. */ - if (TYPE_CODE (VALUE_TYPE (vtbl)) == TYPE_CODE_PTR - && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (vtbl))) == TYPE_CODE_ARRAY) - { - /* Handle the case where the vtbl field points to an - array of structures. */ - vtbl = value_ind (vtbl); - - /* Index into the virtual function table. This is hard-coded because - looking up a field is not cheap, and it may be important to save - time, e.g. if the user has set a conditional breakpoint calling - a virtual function. */ - entry = value_subscript (vtbl, vi); - } - else - { - /* Handle the case where the vtbl field points directly to a structure. */ - vtbl = value_add (vtbl, vi); - entry = value_ind (vtbl); - } - - entry_type = check_typedef (VALUE_TYPE (entry)); - - if (TYPE_CODE (entry_type) == TYPE_CODE_STRUCT) - { - /* Move the `this' pointer according to the virtual function table. */ - VALUE_OFFSET (arg1) += value_as_long (value_field (entry, 0)); - - if (!VALUE_LAZY (arg1)) - { - VALUE_LAZY (arg1) = 1; - value_fetch_lazy (arg1); - } - - vfn = value_field (entry, 2); - } - else if (TYPE_CODE (entry_type) == TYPE_CODE_PTR) - vfn = entry; - else - error ("I'm confused: virtual function table has bad type"); - /* Reinstantiate the function pointer with the correct type. */ - VALUE_TYPE (vfn) = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j)); - - *arg1p = arg1; - return vfn; - } } /* ARG is a pointer to an object we know to be at least --- 887,892 ----