aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/arch-utils.c6
-rw-r--r--gdb/arch-utils.h4
-rw-r--r--gdb/dwarf2/loc.c10
-rw-r--r--gdb/dwarf2/loc.h11
-rw-r--r--gdb/gdbarch-components.py15
-rw-r--r--gdb/gdbarch-gen.h11
-rw-r--r--gdb/gdbarch.c22
-rw-r--r--gdb/infcmd.c41
-rw-r--r--gdb/ppc-sysv-tdep.c41
-rw-r--r--gdb/ppc-tdep.h2
-rw-r--r--gdb/rs6000-tdep.c6
-rw-r--r--gdb/testsuite/gdb.cp/non-trivial-retval.exp9
-rw-r--r--gdb/testsuite/lib/gdb.exp8
13 files changed, 172 insertions, 14 deletions
diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index 5218bfc..7b84daf 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -1084,6 +1084,12 @@ default_read_core_file_mappings
{
}
+CORE_ADDR
+default_get_return_buf_addr (struct type *val_type, frame_info_ptr cur_frame)
+{
+ return 0;
+}
+
/* Non-zero if we want to trace architecture code. */
#ifndef GDBARCH_DEBUG
diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index f6229f4..46018a6 100644
--- a/gdb/arch-utils.h
+++ b/gdb/arch-utils.h
@@ -300,4 +300,8 @@ extern void default_read_core_file_mappings
struct bfd *cbfd,
read_core_file_mappings_pre_loop_ftype pre_loop_cb,
read_core_file_mappings_loop_ftype loop_cb);
+
+/* Default implementation of gdbarch default_get_return_buf_addr method. */
+extern CORE_ADDR default_get_return_buf_addr (struct type *val_typegdbarch,
+ frame_info_ptr cur_frame);
#endif /* ARCH_UTILS_H */
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index c42359a..8355aa4 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1325,14 +1325,8 @@ static const struct lval_funcs entry_data_value_funcs =
entry_data_value_free_closure
};
-/* Read parameter of TYPE at (callee) FRAME's function entry. KIND and KIND_U
- are used to match DW_AT_location at the caller's
- DW_TAG_call_site_parameter.
-
- Function always returns non-NULL value. It throws NO_ENTRY_VALUE_ERROR if it
- cannot resolve the parameter for any reason. */
-
-static struct value *
+/* See dwarf2/loc.h. */
+struct value *
value_of_dwarf_reg_entry (struct type *type, frame_info_ptr frame,
enum call_site_parameter_kind kind,
union call_site_parameter_u kind_u)
diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h
index d6709f2..9156e1e 100644
--- a/gdb/dwarf2/loc.h
+++ b/gdb/dwarf2/loc.h
@@ -296,4 +296,15 @@ extern struct value *indirect_synthetic_pointer
dwarf2_per_objfile *per_objfile, frame_info_ptr frame,
struct type *type, bool resolve_abstract_p = false);
+/* Read parameter of TYPE at (callee) FRAME's function entry. KIND and KIND_U
+ are used to match DW_AT_location at the caller's
+ DW_TAG_call_site_parameter.
+
+ Function always returns non-NULL value. It throws NO_ENTRY_VALUE_ERROR if
+ it cannot resolve the parameter for any reason. */
+
+extern struct value *value_of_dwarf_reg_entry (struct type *type,
+ struct frame_info_ptr frame,
+ enum call_site_parameter_kind kind,
+ union call_site_parameter_u kind_u);
#endif /* DWARF2LOC_H */
diff --git a/gdb/gdbarch-components.py b/gdb/gdbarch-components.py
index b2c7b78..9b68899 100644
--- a/gdb/gdbarch-components.py
+++ b/gdb/gdbarch-components.py
@@ -868,6 +868,21 @@ for instance).
invalid=True,
)
+Function(
+ comment="""
+Return the address at which the value being returned from
+the current function will be stored. This routine is only
+called if the current function uses the the "struct return
+convention".
+
+May return 0 when unable to determine that address.""",
+ type="CORE_ADDR",
+ name="get_return_buf_addr",
+ params=[("struct type *", "val_type"), ("frame_info_ptr", "cur_frame")],
+ predefault="default_get_return_buf_addr",
+ invalid=False,
+)
+
Method(
comment="""
Return true if the return value of function is stored in the first hidden
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index e0d7a08..a663316 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -441,6 +441,17 @@ typedef enum return_value_convention (gdbarch_return_value_ftype) (struct gdbarc
extern enum return_value_convention gdbarch_return_value (struct gdbarch *gdbarch, struct value *function, struct type *valtype, struct regcache *regcache, gdb_byte *readbuf, const gdb_byte *writebuf);
extern void set_gdbarch_return_value (struct gdbarch *gdbarch, gdbarch_return_value_ftype *return_value);
+/* Return the address at which the value being returned from
+ the current function will be stored. This routine is only
+ called if the current function uses the the "struct return
+ convention".
+
+ May return 0 when unable to determine that address. */
+
+typedef CORE_ADDR (gdbarch_get_return_buf_addr_ftype) (struct type *val_type, frame_info_ptr cur_frame);
+extern CORE_ADDR gdbarch_get_return_buf_addr (struct gdbarch *gdbarch, struct type *val_type, frame_info_ptr cur_frame);
+extern void set_gdbarch_get_return_buf_addr (struct gdbarch *gdbarch, gdbarch_get_return_buf_addr_ftype *get_return_buf_addr);
+
/* Return true if the return value of function is stored in the first hidden
parameter. In theory, this feature should be language-dependent, specified
by language and its ABI, such as C++. Unfortunately, compiler may
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 9d929da..3227e94 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -116,6 +116,7 @@ struct gdbarch
gdbarch_address_to_pointer_ftype *address_to_pointer = unsigned_address_to_pointer;
gdbarch_integer_to_address_ftype *integer_to_address = nullptr;
gdbarch_return_value_ftype *return_value = nullptr;
+ gdbarch_get_return_buf_addr_ftype *get_return_buf_addr = default_get_return_buf_addr;
gdbarch_return_in_first_hidden_param_p_ftype *return_in_first_hidden_param_p = default_return_in_first_hidden_param_p;
gdbarch_skip_prologue_ftype *skip_prologue = 0;
gdbarch_skip_main_prologue_ftype *skip_main_prologue = nullptr;
@@ -369,6 +370,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of address_to_pointer, invalid_p == 0 */
/* Skip verify of integer_to_address, has predicate. */
/* Skip verify of return_value, has predicate. */
+ /* Skip verify of get_return_buf_addr, invalid_p == 0 */
/* Skip verify of return_in_first_hidden_param_p, invalid_p == 0 */
if (gdbarch->skip_prologue == 0)
log.puts ("\n\tskip_prologue");
@@ -781,6 +783,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
"gdbarch_dump: return_value = <%s>\n",
host_address_to_string (gdbarch->return_value));
gdb_printf (file,
+ "gdbarch_dump: get_return_buf_addr = <%s>\n",
+ host_address_to_string (gdbarch->get_return_buf_addr));
+ gdb_printf (file,
"gdbarch_dump: return_in_first_hidden_param_p = <%s>\n",
host_address_to_string (gdbarch->return_in_first_hidden_param_p));
gdb_printf (file,
@@ -2587,6 +2592,23 @@ set_gdbarch_return_value (struct gdbarch *gdbarch,
gdbarch->return_value = return_value;
}
+CORE_ADDR
+gdbarch_get_return_buf_addr (struct gdbarch *gdbarch, struct type *val_type, frame_info_ptr cur_frame)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->get_return_buf_addr != NULL);
+ if (gdbarch_debug >= 2)
+ gdb_printf (gdb_stdlog, "gdbarch_get_return_buf_addr called\n");
+ return gdbarch->get_return_buf_addr (val_type, cur_frame);
+}
+
+void
+set_gdbarch_get_return_buf_addr (struct gdbarch *gdbarch,
+ gdbarch_get_return_buf_addr_ftype get_return_buf_addr)
+{
+ gdbarch->get_return_buf_addr = get_return_buf_addr;
+}
+
int
gdbarch_return_in_first_hidden_param_p (struct gdbarch *gdbarch, struct type *type)
{
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 6f83949..b71dc10 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -55,6 +55,7 @@
#include "gdbsupport/gdb_optional.h"
#include "source.h"
#include "cli/cli-style.h"
+#include "dwarf2/loc.h"
/* Local functions: */
@@ -1609,6 +1610,12 @@ struct finish_command_fsm : public thread_fsm
return value. */
struct return_value_info return_value_info {};
+ /* If the current function uses the "struct return convention",
+ this holds the address at which the value being returned will
+ be stored, or zero if that address could not be determined or
+ the "struct return convention" is not being used. */
+ CORE_ADDR return_buf;
+
explicit finish_command_fsm (struct interp *cmd_interp)
: thread_fsm (cmd_interp)
{
@@ -1646,7 +1653,13 @@ finish_command_fsm::should_stop (struct thread_info *tp)
struct value *func;
func = read_var_value (function, NULL, get_current_frame ());
- rv->value = get_return_value (function, func);
+
+ if (return_buf != 0)
+ /* Retrieve return value from the buffer where it was saved. */
+ rv->value = value_at (rv->type, return_buf);
+ else
+ rv->value = get_return_value (function, func);
+
if (rv->value != NULL)
rv->value_history_index = record_latest_value (rv->value);
}
@@ -1862,8 +1875,28 @@ finish_command (const char *arg, int from_tty)
}
/* Find the function we will return from. */
-
- sm->function = find_pc_function (get_frame_pc (get_selected_frame (NULL)));
+ frame_info_ptr callee_frame = get_selected_frame (NULL);
+ sm->function = find_pc_function (get_frame_pc (callee_frame));
+
+ /* Determine the return convention. If it is RETURN_VALUE_STRUCT_CONVENTION,
+ attempt to determine the address of the return buffer. */
+ enum return_value_convention return_value;
+ struct gdbarch *gdbarch = get_frame_arch (callee_frame);
+
+ struct type * val_type
+ = check_typedef (sm->function->type ()->target_type ());
+
+ return_value = gdbarch_return_value (gdbarch,
+ read_var_value (sm->function, NULL,
+ callee_frame),
+ val_type, NULL, NULL, NULL);
+
+ if (return_value == RETURN_VALUE_STRUCT_CONVENTION
+ && val_type->code () != TYPE_CODE_VOID)
+ sm->return_buf = gdbarch_get_return_buf_addr (gdbarch, val_type,
+ callee_frame);
+ else
+ sm->return_buf = 0;
/* Print info on the selected frame, including level number but not
source. */
@@ -1881,7 +1914,7 @@ finish_command (const char *arg, int from_tty)
gdb_printf (_("Run till exit from "));
}
- print_stack_frame (get_selected_frame (NULL), 1, LOCATION, 0);
+ print_stack_frame (callee_frame, 1, LOCATION, 0);
}
frame.reinflate ();
diff --git a/gdb/ppc-sysv-tdep.c b/gdb/ppc-sysv-tdep.c
index 450162d..1cbaaf2 100644
--- a/gdb/ppc-sysv-tdep.c
+++ b/gdb/ppc-sysv-tdep.c
@@ -28,6 +28,7 @@
#include "objfiles.h"
#include "infcall.h"
#include "dwarf2.h"
+#include "dwarf2/loc.h"
#include "target-float.h"
#include <algorithm>
@@ -2156,3 +2157,43 @@ ppc64_sysv_abi_return_value (struct gdbarch *gdbarch, struct value *function,
return RETURN_VALUE_STRUCT_CONVENTION;
}
+CORE_ADDR ppc64_sysv_get_return_buf_addr (struct type *val_type,
+ frame_info_ptr cur_frame)
+{
+ /* The PowerPC ABI specifies aggregates that are not returned by value
+ are returned in a storage buffer provided by the caller. The
+ address of the storage buffer is provided as a hidden first input
+ arguement in register r3. The PowerPC ABI does not guarantee that
+ register r3 will not be changed while executing the function. Hence, it
+ cannot be assumed that r3 will still contain the address of the storage
+ buffer when execution reaches the end of the function.
+
+ This function attempts to determine the value of r3 on entry to the
+ function using the DW_OP_entry_value DWARF entries. This requires
+ compiling the user program with -fvar-tracking to resolve the
+ DW_TAG_call_sites in the binary file. */
+
+ union call_site_parameter_u kind_u;
+ enum call_site_parameter_kind kind;
+ CORE_ADDR return_val = 0;
+
+ kind_u.dwarf_reg = 3; /* First passed arg/return value is in r3. */
+ kind = CALL_SITE_PARAMETER_DWARF_REG;
+
+ /* val_type is the type of the return value. Need the pointer type
+ to the return value. */
+ val_type = lookup_pointer_type (val_type);
+
+ try
+ {
+ return_val = value_as_address (value_of_dwarf_reg_entry (val_type,
+ cur_frame,
+ kind, kind_u));
+ }
+ catch (const gdb_exception_error &e)
+ {
+ warning ("Cannot determine the function return value.\n"
+ "Try compiling with -fvar-tracking.");
+ }
+ return return_val;
+}
diff --git a/gdb/ppc-tdep.h b/gdb/ppc-tdep.h
index 34b2508..cbccd87 100644
--- a/gdb/ppc-tdep.h
+++ b/gdb/ppc-tdep.h
@@ -175,6 +175,8 @@ extern void ppc_collect_vsxregset (const struct regset *regset,
const struct regcache *regcache,
int regnum, void *vsxregs, size_t len);
+extern CORE_ADDR ppc64_sysv_get_return_buf_addr (type*, frame_info_ptr);
+
/* Private data that this module attaches to struct gdbarch. */
/* ELF ABI version used by the inferior. */
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 866d43d..cbd8451 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -8243,7 +8243,11 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_ps_regnum (gdbarch, tdep->ppc_ps_regnum);
if (wordsize == 8)
- set_gdbarch_return_value (gdbarch, ppc64_sysv_abi_return_value);
+ {
+ set_gdbarch_return_value (gdbarch, ppc64_sysv_abi_return_value);
+ set_gdbarch_get_return_buf_addr (gdbarch,
+ ppc64_sysv_get_return_buf_addr);
+ }
else
set_gdbarch_return_value (gdbarch, ppc_sysv_abi_return_value);
diff --git a/gdb/testsuite/gdb.cp/non-trivial-retval.exp b/gdb/testsuite/gdb.cp/non-trivial-retval.exp
index 21c390b..93a3a68 100644
--- a/gdb/testsuite/gdb.cp/non-trivial-retval.exp
+++ b/gdb/testsuite/gdb.cp/non-trivial-retval.exp
@@ -15,11 +15,18 @@
# This file is part of the gdb testsuite
+set additional_flags ""
+
if {[skip_cplus_tests]} { continue }
standard_testfile .cc
-if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} {
+if {[have_fvar_tracking]} {
+ set additional_flags "additional_flags= -fvar-tracking"
+}
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile [list debug c++ $additional_flags]]} {
+
return -1
}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index bdb8da9..34c7a1c 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -8821,6 +8821,14 @@ gdb_caching_proc have_fuse_ld_gold {
return [gdb_simple_compile $me $src executable $flags]
}
+# Return 1 if compiler supports fvar-tracking, otherwise return 0.
+gdb_caching_proc have_fvar_tracking {
+ set me "have_fvar_tracking"
+ set flags "additional_flags=-fvar-tracking"
+ set src { int main() { return 0; } }
+ return [gdb_simple_compile $me $src executable $flags]
+}
+
# Return 1 if linker supports -Ttext-segment, otherwise return 0.
gdb_caching_proc linker_supports_Ttext_segment_flag {
set me "linker_supports_Ttext_segment_flag"