This patch needs to be submitted for the FSF. Also, there may be testcases already in the GDB testsuite (currently disabled) that it would probably fix. 2005-11-20 Patch updated, but still not submitted. --- gdb/Makefile.in | 2 gdb/cp-abi.c | 8 +++ gdb/cp-abi.h | 5 ++ gdb/gnu-v3-abi.c | 79 +++++++++++++++++++++++++++++++++++ gdb/infcall.c | 71 ++++++++++++++++++++++++++----- gdb/testsuite/gdb.cp/pass-by-ref.cc | 57 +++++++++++++++++++++++++ gdb/testsuite/gdb.cp/pass-by-ref.exp | 38 ++++++++++++++++ 7 files changed, 248 insertions(+), 12 deletions(-) Index: gdb-6.6.dfsg/gdb/infcall.c =================================================================== --- gdb-6.6.dfsg.orig/gdb/infcall.c 2007-01-27 20:35:19.000000000 -0500 +++ gdb-6.6.dfsg/gdb/infcall.c 2007-01-27 20:35:30.000000000 -0500 @@ -36,6 +36,7 @@ #include "gdb_string.h" #include "infcall.h" #include "dummy-frame.h" +#include "cp-abi.h" /* NOTE: cagney/2003-04-16: What's the future of this code? @@ -320,8 +321,8 @@ call_function_by_hand (struct value *fun { CORE_ADDR sp; CORE_ADDR dummy_addr; - struct type *values_type; - unsigned char struct_return; + struct type *values_type, *target_values_type; + unsigned char struct_return = 0, cp_struct_return = 0; CORE_ADDR struct_addr = 0; struct regcache *retbuf; struct cleanup *retbuf_cleanup; @@ -335,6 +336,7 @@ call_function_by_hand (struct value *fun struct regcache *caller_regcache; struct cleanup *caller_regcache_cleanup; struct frame_id dummy_id; + struct cleanup *args_cleanup; if (TYPE_CODE (ftype) == TYPE_CODE_PTR) ftype = check_typedef (TYPE_TARGET_TYPE (ftype)); @@ -439,10 +441,31 @@ call_function_by_hand (struct value *fun using_gcc = (b == NULL ? 2 : BLOCK_GCC_COMPILED (b)); } - /* Are we returning a value using a structure return or a normal - value return? */ + /* Are we returning a value using a structure return (passing a + hidden argument pointing to storage) or a normal value return? + There are two cases: C++ ABI mandated structure return and + target ABI structure return. The variable STRUCT_RETURN only + describes the latter. The C++ version is handled by passing + the return location as the first parameter to the function, + even preceding "this". This is different from the target + ABI version, which is target-specific; for instance, on ia64 + the first argument is passed in out0 but the hidden structure + return pointer would normally be passed in r8. */ - struct_return = using_struct_return (values_type, using_gcc); + if (current_language->la_language == language_cplus + && cp_pass_by_reference (values_type)) + { + cp_struct_return = 1; + + /* Tell the target specific argument pushing routine not to + expect a value. */ + target_values_type = builtin_type_void; + } + else + { + struct_return = using_struct_return (values_type, using_gcc); + target_values_type = values_type; + } /* Determine the location of the breakpoint (and possibly other stuff) that the called function will return to. The SPARC, for a @@ -461,7 +484,7 @@ call_function_by_hand (struct value *fun if (INNER_THAN (1, 2)) { sp = push_dummy_code (current_gdbarch, sp, funaddr, - using_gcc, args, nargs, values_type, + using_gcc, args, nargs, target_values_type, &real_pc, &bp_addr); dummy_addr = sp; } @@ -469,7 +492,7 @@ call_function_by_hand (struct value *fun { dummy_addr = sp; sp = push_dummy_code (current_gdbarch, sp, funaddr, - using_gcc, args, nargs, values_type, + using_gcc, args, nargs, target_values_type, &real_pc, &bp_addr); } break; @@ -536,9 +559,15 @@ call_function_by_hand (struct value *fun param_type = TYPE_FIELD_TYPE (ftype, i); else param_type = NULL; - + args[i] = value_arg_coerce (args[i], param_type, prototyped); + /* FIXME: Is current_language the right language? */ + if (current_language->la_language == language_cplus + && param_type != NULL + && cp_pass_by_reference (param_type)) + args[i] = value_addr (args[i]); + /* elz: this code is to handle the case in which the function to be called has a pointer to function as parameter and the corresponding actual argument is the address of a function @@ -637,7 +666,7 @@ You must use a pointer to function type stack, if necessary. Make certain that the value is correctly aligned. */ - if (struct_return) + if (struct_return || cp_struct_return) { int len = TYPE_LENGTH (values_type); if (INNER_THAN (1, 2)) @@ -662,6 +691,22 @@ You must use a pointer to function type } } + if (cp_struct_return) + { + struct value **new_args; + + /* Add the new argument to the front of the argument list. */ + new_args = xmalloc (sizeof (struct value *) * (nargs + 1)); + new_args[0] = value_from_pointer (lookup_pointer_type (values_type), + struct_addr); + memcpy (&new_args[1], &args[0], sizeof (struct value *) * nargs); + args = new_args; + nargs++; + args_cleanup = make_cleanup (xfree, args); + } + else + args_cleanup = make_cleanup (null_cleanup, NULL); + /* Create the dummy stack frame. Pass in the call dummy address as, presumably, the ABI code knows where, in the call dummy, the return address should be pointed. */ @@ -669,6 +714,8 @@ You must use a pointer to function type bp_addr, nargs, args, sp, struct_return, struct_addr); + do_cleanups (args_cleanup); + /* Set up a frame ID for the dummy frame so we can pass it to set_momentary_breakpoint. We need to give the breakpoint a frame ID so that the breakpoint code can correctly re-identify the @@ -860,7 +907,9 @@ the function call)."), name); { struct value *retval = NULL; - if (TYPE_CODE (values_type) == TYPE_CODE_VOID) + if (cp_struct_return) + retval = value_at (values_type, struct_addr); + else if (TYPE_CODE (target_values_type) == TYPE_CODE_VOID) { /* If the function returns void, don't bother fetching the return value. */ @@ -870,7 +919,7 @@ the function call)."), name); { struct gdbarch *arch = current_gdbarch; - switch (gdbarch_return_value (arch, values_type, NULL, NULL, NULL)) + switch (gdbarch_return_value (arch, target_values_type, NULL, NULL, NULL)) { case RETURN_VALUE_REGISTER_CONVENTION: case RETURN_VALUE_ABI_RETURNS_ADDRESS: Index: gdb-6.6.dfsg/gdb/cp-abi.h =================================================================== --- gdb-6.6.dfsg.orig/gdb/cp-abi.h 2007-01-27 20:35:19.000000000 -0500 +++ gdb-6.6.dfsg/gdb/cp-abi.h 2007-01-27 20:35:30.000000000 -0500 @@ -147,6 +147,10 @@ extern struct type *value_rtti_type (str extern int baseclass_offset (struct type *type, int index, const bfd_byte *valaddr, CORE_ADDR address); +/* Return non-zero if an argument of type TYPE should be passed by reference + instead of value. */ +extern int cp_pass_by_reference (struct type *type); + struct cp_abi_ops { const char *shortname; @@ -164,6 +168,7 @@ struct cp_abi_ops int *using_enc); int (*baseclass_offset) (struct type *type, int index, const bfd_byte *valaddr, CORE_ADDR address); + int (*pass_by_reference) (struct type *type); }; Index: gdb-6.6.dfsg/gdb/cp-abi.c =================================================================== --- gdb-6.6.dfsg.orig/gdb/cp-abi.c 2007-01-27 20:35:19.000000000 -0500 +++ gdb-6.6.dfsg/gdb/cp-abi.c 2007-01-27 20:35:30.000000000 -0500 @@ -95,6 +95,14 @@ value_rtti_type (struct value *v, int *f return (*current_cp_abi.rtti_type) (v, full, top, using_enc); } +int +cp_pass_by_reference (struct type *type) +{ + if ((current_cp_abi.pass_by_reference) == NULL) + return 0; + return (*current_cp_abi.pass_by_reference) (type); +} + /* Set the current C++ ABI to SHORT_NAME. */ static int Index: gdb-6.6.dfsg/gdb/gnu-v3-abi.c =================================================================== --- gdb-6.6.dfsg.orig/gdb/gnu-v3-abi.c 2007-01-27 20:35:19.000000000 -0500 +++ gdb-6.6.dfsg/gdb/gnu-v3-abi.c 2007-01-27 20:35:30.000000000 -0500 @@ -416,6 +416,84 @@ gnuv3_baseclass_offset (struct type *typ return base_offset; } +/* Return nonzero if a type should be passed by reference. + + The rule in the v3 ABI document comes from section 3.1.1. If the + type has a non-trivial copy constructor or destructor, then the + caller must make a copy (by calling the copy constructor if there + is one or perform the copy itself otherwise), pass the address of + the copy, and then destroy the temporary (if necessary). + + For return values with non-trivial copy constructors or + destructors, space will be allocated in the caller, and a pointer + will be passed as the first argument (preceding "this"). + + We don't have a bulletproof mechanism for determining whether a + constructor or destructor is trivial. For GCC and DWARF2 debug + information, we can check the artificial flag. + + We don't do anything with the constructors or destructors yet, + but we have to get the argument passing right anyway. */ +static int +gnuv3_pass_by_reference (struct type *type) +{ + int fieldnum, fieldelem, basenum; + + CHECK_TYPEDEF (type); + + /* We're only interested in things that can have methods. */ + if (TYPE_CODE (type) != TYPE_CODE_STRUCT + && TYPE_CODE (type) != TYPE_CODE_CLASS + && TYPE_CODE (type) != TYPE_CODE_UNION) + return 0; + + for (fieldnum = 0; fieldnum < TYPE_NFN_FIELDS (type); fieldnum++) + for (fieldelem = 0; fieldelem < TYPE_FN_FIELDLIST_LENGTH (type, fieldnum); + fieldelem++) + { + struct fn_field *fn = TYPE_FN_FIELDLIST1 (type, fieldnum); + 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, + and we assume it is trivial. */ + if (TYPE_FN_FIELD_ARTIFICIAL (fn, fieldelem)) + continue; + + /* If we've found a destructor, we must pass this by reference. */ + if (name[0] == '~') + return 1; + + /* If the mangled name of this method doesn't indicate that it + is a constructor, we're not interested. + + FIXME drow/2004-05-27: We could do this using the name of + the method and the name of the class instead of dealing + with the mangled name. We don't have a convenient function + to strip off both leading scope qualifiers and trailing + template arguments yet. */ + if (!is_constructor_name (TYPE_FN_FIELD_PHYSNAME (fn, fieldelem))) + continue; + + /* If this method takes two arguments, and the second argument is + a reference to this class, then it is a copy constructor. */ + if (TYPE_NFIELDS (fieldtype) == 2 + && TYPE_CODE (TYPE_FIELD_TYPE (fieldtype, 1)) == TYPE_CODE_REF + && check_typedef (TYPE_TARGET_TYPE (TYPE_FIELD_TYPE (fieldtype, 1))) == type) + return 1; + } + + /* Even if all the constructors and destructors were artificial, one + of them may have invoked a non-artificial constructor or + destructor in a base class. If any base class needs to be passed + by reference, so does this class. */ + for (basenum = 0; basenum < TYPE_N_BASECLASSES (type); basenum++) + if (gnuv3_pass_by_reference (TYPE_BASECLASS (type, basenum))) + return 1; + + return 0; +} + static void init_gnuv3_ops (void) { @@ -433,6 +511,7 @@ 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.pass_by_reference = gnuv3_pass_by_reference; } extern initialize_file_ftype _initialize_gnu_v3_abi; /* -Wmissing-prototypes */ Index: gdb-6.6.dfsg/gdb/Makefile.in =================================================================== --- gdb-6.6.dfsg.orig/gdb/Makefile.in 2007-01-27 20:35:29.000000000 -0500 +++ gdb-6.6.dfsg/gdb/Makefile.in 2007-01-27 20:35:30.000000000 -0500 @@ -2174,7 +2174,7 @@ ia64-tdep.o: ia64-tdep.c $(defs_h) $(inf infcall.o: infcall.c $(defs_h) $(breakpoint_h) $(target_h) $(regcache_h) \ $(inferior_h) $(gdb_assert_h) $(block_h) $(gdbcore_h) $(language_h) \ $(objfiles_h) $(gdbcmd_h) $(command_h) $(gdb_string_h) $(infcall_h) \ - $(dummy_frame_h) + $(dummy_frame_h) $(cp_abi_h) inf-child.o: inf-child.c $(defs_h) $(regcache_h) $(memattr_h) $(symtab_h) \ $(target_h) $(inferior_h) $(gdb_string_h) infcmd.o: infcmd.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \ Index: gdb-6.6.dfsg/gdb/testsuite/gdb.cp/pass-by-ref.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-6.6.dfsg/gdb/testsuite/gdb.cp/pass-by-ref.exp 2007-01-27 20:35:30.000000000 -0500 @@ -0,0 +1,38 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2004 Free Software Foundation, Inc. + +# 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. + +# Check that GDB can call C++ functions whose parameters have +# object type, but are passed by reference. + +set testfile "pass-by-ref" +set srcfile ${testfile}.cc +set binfile ${objdir}/${subdir}/${testfile} +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } { + return -1 +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if ![runto_main] then { + return -1 +} + +gdb_test "print foo (global_obj)" " = 3" "call function" Index: gdb-6.6.dfsg/gdb/testsuite/gdb.cp/pass-by-ref.cc =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-6.6.dfsg/gdb/testsuite/gdb.cp/pass-by-ref.cc 2007-01-27 20:35:30.000000000 -0500 @@ -0,0 +1,57 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2004 Free Software Foundation, Inc. + + 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. */ + +class Obj { +public: + Obj (); + Obj (const Obj &); + ~Obj (); + int var[2]; +}; + +int foo (Obj arg) +{ + return arg.var[0] + arg.var[1]; +} + +Obj::Obj () +{ + var[0] = 1; + var[1] = 2; +} + +Obj::Obj (const Obj &obj) +{ + var[0] = obj.var[0]; + var[1] = obj.var[1]; +} + +Obj::~Obj () +{ + +} + +Obj global_obj; + +int +main () +{ + int bar = foo (global_obj); + return bar; +}