| 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. |
| |
| Index: gdb-6.3/gdb/infcall.c |
| =================================================================== |
| --- gdb-6.3.orig/gdb/infcall.c 2004-10-08 04:15:56.000000000 -0400 |
| +++ gdb-6.3/gdb/infcall.c 2004-11-10 12:30:07.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? |
| |
| @@ -297,8 +298,8 @@ call_function_by_hand (struct value *fun |
| { |
| CORE_ADDR sp; |
| CORE_ADDR dummy_addr; |
| - struct type *value_type; |
| - unsigned char struct_return; |
| + struct type *value_type, *target_value_type; |
| + unsigned char struct_return = 0, cp_struct_return = 0; |
| CORE_ADDR struct_addr = 0; |
| struct regcache *retbuf; |
| struct cleanup *retbuf_cleanup; |
| @@ -312,6 +313,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 (!target_has_execution) |
| noprocess (); |
| @@ -410,10 +412,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 (value_type, using_gcc); |
| + if (current_language->la_language == language_cplus |
| + && cp_pass_by_reference (value_type)) |
| + { |
| + cp_struct_return = 1; |
| + |
| + /* Tell the target specific argument pushing routine not to |
| + expect a value. */ |
| + target_value_type = builtin_type_void; |
| + } |
| + else |
| + { |
| + struct_return = using_struct_return (value_type, using_gcc); |
| + target_value_type = value_type; |
| + } |
| |
| /* Determine the location of the breakpoint (and possibly other |
| stuff) that the called function will return to. The SPARC, for a |
| @@ -432,7 +455,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, value_type, |
| + using_gcc, args, nargs, target_value_type, |
| &real_pc, &bp_addr); |
| dummy_addr = sp; |
| } |
| @@ -440,7 +463,7 @@ call_function_by_hand (struct value *fun |
| { |
| dummy_addr = sp; |
| sp = push_dummy_code (current_gdbarch, sp, funaddr, |
| - using_gcc, args, nargs, value_type, |
| + using_gcc, args, nargs, target_value_type, |
| &real_pc, &bp_addr); |
| } |
| break; |
| @@ -507,9 +530,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 |
| @@ -607,7 +636,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 (value_type); |
| if (INNER_THAN (1, 2)) |
| @@ -632,6 +661,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 (value_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. */ |
| @@ -649,6 +694,8 @@ You must use a pointer to function type |
| else |
| error ("This target does not support function calls"); |
| |
| + 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 |
| @@ -839,11 +886,7 @@ the function call).", name); |
| /* Figure out the value returned by the function, return that. */ |
| { |
| struct value *retval; |
| - if (TYPE_CODE (value_type) == TYPE_CODE_VOID) |
| - /* If the function returns void, don't bother fetching the |
| - return value. */ |
| - retval = allocate_value (value_type); |
| - else if (struct_return) |
| + if (struct_return || cp_struct_return) |
| /* NOTE: cagney/2003-09-27: This assumes that PUSH_DUMMY_CALL |
| has correctly stored STRUCT_ADDR in the target. In the past |
| that hasn't been the case, the old MIPS PUSH_ARGUMENTS |
| @@ -853,6 +896,10 @@ the function call).", name); |
| "struct return convention", check that PUSH_DUMMY_CALL isn't |
| playing tricks. */ |
| retval = value_at (value_type, struct_addr, NULL); |
| + else if (TYPE_CODE (value_type) == TYPE_CODE_VOID) |
| + /* If the function returns void, don't bother fetching the |
| + return value. */ |
| + retval = allocate_value (value_type); |
| else |
| { |
| /* This code only handles "register convention". */ |
| Index: gdb-6.3/gdb/cp-abi.h |
| =================================================================== |
| --- gdb-6.3.orig/gdb/cp-abi.h 2003-04-12 13:41:25.000000000 -0400 |
| +++ gdb-6.3/gdb/cp-abi.h 2004-11-10 12:30:07.000000000 -0500 |
| @@ -1,7 +1,7 @@ |
| /* Abstraction of various C++ ABI's we support, and the info we need |
| to get from them. |
| Contributed by Daniel Berlin <dberlin@redhat.com> |
| - Copyright 2001 Free Software Foundation, Inc. |
| + Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| @@ -145,6 +145,10 @@ extern struct type *value_rtti_type (str |
| extern int baseclass_offset (struct type *type, int index, char *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; |
| @@ -162,6 +166,7 @@ struct cp_abi_ops |
| int *using_enc); |
| int (*baseclass_offset) (struct type *type, int index, char *valaddr, |
| CORE_ADDR address); |
| + int (*pass_by_reference) (struct type *type); |
| }; |
| |
| |
| Index: gdb-6.3/gdb/cp-abi.c |
| =================================================================== |
| --- gdb-6.3.orig/gdb/cp-abi.c 2003-11-26 17:04:00.000000000 -0500 |
| +++ gdb-6.3/gdb/cp-abi.c 2004-11-10 12:30:07.000000000 -0500 |
| @@ -1,5 +1,5 @@ |
| /* Generic code for supporting multiple C++ ABI's |
| - Copyright 2001, 2002, 2003 Free Software Foundation, Inc. |
| + Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| @@ -94,6 +94,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.3/gdb/gnu-v3-abi.c |
| =================================================================== |
| --- gdb-6.3.orig/gdb/gnu-v3-abi.c 2004-03-15 15:38:08.000000000 -0500 |
| +++ gdb-6.3/gdb/gnu-v3-abi.c 2004-11-10 12:30:07.000000000 -0500 |
| @@ -1,7 +1,7 @@ |
| /* Abstraction of GNU v3 abi. |
| Contributed by Jim Blandy <jimb@redhat.com> |
| |
| - Copyright 2001, 2002, 2003 Free Software Foundation, Inc. |
| + Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| @@ -419,6 +419,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) |
| { |
| @@ -434,6 +512,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.3/gdb/hpacc-abi.c |
| =================================================================== |
| --- gdb-6.3.orig/gdb/hpacc-abi.c 2003-06-08 14:27:13.000000000 -0400 |
| +++ gdb-6.3/gdb/hpacc-abi.c 2004-11-10 12:30:07.000000000 -0500 |
| @@ -3,7 +3,7 @@ |
| 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. |
| + Copyright 2001, 2002, 2003 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| Index: gdb-6.3/gdb/Makefile.in |
| =================================================================== |
| --- gdb-6.3.orig/gdb/Makefile.in 2004-11-10 12:30:06.000000000 -0500 |
| +++ gdb-6.3/gdb/Makefile.in 2004-11-10 12:30:07.000000000 -0500 |
| @@ -2073,7 +2073,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.3/gdb/testsuite/gdb.cp/pass-by-ref.exp |
| =================================================================== |
| --- /dev/null 1970-01-01 00:00:00.000000000 +0000 |
| +++ gdb-6.3/gdb/testsuite/gdb.cp/pass-by-ref.exp 2004-11-11 09:48:00.498518899 -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}] != "" } { |
| + 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.3/gdb/testsuite/gdb.cp/pass-by-ref.cc |
| =================================================================== |
| --- /dev/null 1970-01-01 00:00:00.000000000 +0000 |
| +++ gdb-6.3/gdb/testsuite/gdb.cp/pass-by-ref.cc 2004-11-11 09:44:17.815014667 -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; |
| +} |