This patch needs to be submitted for the FSF.  Also, there are testcases in
the GDB testsuite (currently disabled) that it would probably fix.

Index: gdb-6.1/gdb/infcall.c
===================================================================
--- gdb-6.1.orig/gdb/infcall.c	2004-02-23 11:27:40.000000000 -0500
+++ gdb-6.1/gdb/infcall.c	2004-05-27 13:37:27.000000000 -0400
@@ -35,6 +35,7 @@
 #include "command.h"
 #include "gdb_string.h"
 #include "infcall.h"
+#include "cp-abi.h"
 
 /* NOTE: cagney/2003-04-16: What's the future of this code?
 
@@ -383,8 +384,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;
@@ -395,6 +396,7 @@ call_function_by_hand (struct value *fun
   CORE_ADDR real_pc;
   struct type *ftype = check_typedef (SYMBOL_TYPE (function));
   CORE_ADDR bp_addr;
+  struct cleanup *args_cleanup;
 
   if (!target_has_execution)
     noprocess ();
@@ -515,10 +517,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
@@ -537,7 +560,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;
 	}
@@ -545,7 +568,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;
@@ -560,7 +583,7 @@ call_function_by_hand (struct value *fun
 	  /* If the target is manipulating DUMMY1, it looses big time.  */
 	  void *dummy1 = NULL;
 	  DEPRECATED_FIX_CALL_DUMMY (dummy1, sp, funaddr, nargs, args,
-				     value_type, using_gcc);
+				     target_value_type, using_gcc);
 	}
       real_pc = funaddr;
       dummy_addr = entry_point_address ();
@@ -629,9 +652,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
@@ -736,7 +765,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 (DEPRECATED_STACK_ALIGN_P ())
@@ -765,6 +794,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.  */
@@ -782,6 +827,8 @@ You must use a pointer to function type 
   else
     sp = legacy_push_arguments (nargs, args, sp, struct_return, struct_addr);
 
+  do_cleanups (args_cleanup);
+
   if (DEPRECATED_PUSH_RETURN_ADDRESS_P ())
     /* for targets that use no CALL_DUMMY */
     /* There are a number of targets now which actually don't write
@@ -1038,7 +1085,7 @@ the function call).", name);
   do_cleanups (inf_status_cleanup);
 
   /* Figure out the value returned by the function.  */
-  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
Index: gdb-6.1/gdb/cp-abi.h
===================================================================
--- gdb-6.1.orig/gdb/cp-abi.h	2003-04-12 13:41:25.000000000 -0400
+++ gdb-6.1/gdb/cp-abi.h	2004-05-27 12:05:57.000000000 -0400
@@ -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.1/gdb/cp-abi.c
===================================================================
--- gdb-6.1.orig/gdb/cp-abi.c	2003-11-26 17:04:00.000000000 -0500
+++ gdb-6.1/gdb/cp-abi.c	2004-05-27 12:05:57.000000000 -0400
@@ -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.1/gdb/gnu-v3-abi.c
===================================================================
--- gdb-6.1.orig/gdb/gnu-v3-abi.c	2003-12-04 23:25:09.000000000 -0500
+++ gdb-6.1/gdb/gnu-v3-abi.c	2004-05-27 12:05:57.000000000 -0400
@@ -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.1/gdb/hpacc-abi.c
===================================================================
--- gdb-6.1.orig/gdb/hpacc-abi.c	2003-06-08 14:27:13.000000000 -0400
+++ gdb-6.1/gdb/hpacc-abi.c	2004-05-27 12:05:57.000000000 -0400
@@ -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.1/gdb/Makefile.in
===================================================================
--- gdb-6.1.orig/gdb/Makefile.in	2004-05-27 12:05:57.000000000 -0400
+++ gdb-6.1/gdb/Makefile.in	2004-05-27 12:05:58.000000000 -0400
@@ -1905,7 +1905,8 @@ ia64-tdep.o: ia64-tdep.c $(defs_h) $(inf
 	$(libunwind_frame_h) $(libunwind_ia64_h)
 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)
+	$(objfiles_h) $(gdbcmd_h) $(command_h) $(gdb_string_h) $(infcall_h) \
+	$(cp_abi_h)
 infcmd.o: infcmd.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \
 	$(frame_h) $(inferior_h) $(environ_h) $(value_h) $(gdbcmd_h) \
 	$(symfile_h) $(gdbcore_h) $(target_h) $(language_h) $(symfile_h) \
