diff options
-rw-r--r-- | gdb/arch-utils.c | 6 | ||||
-rw-r--r-- | gdb/arch-utils.h | 4 | ||||
-rw-r--r-- | gdb/dwarf2/loc.c | 10 | ||||
-rw-r--r-- | gdb/dwarf2/loc.h | 11 | ||||
-rw-r--r-- | gdb/gdbarch-components.py | 15 | ||||
-rw-r--r-- | gdb/gdbarch-gen.h | 11 | ||||
-rw-r--r-- | gdb/gdbarch.c | 22 | ||||
-rw-r--r-- | gdb/infcmd.c | 41 | ||||
-rw-r--r-- | gdb/ppc-sysv-tdep.c | 41 | ||||
-rw-r--r-- | gdb/ppc-tdep.h | 2 | ||||
-rw-r--r-- | gdb/rs6000-tdep.c | 6 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/non-trivial-retval.exp | 9 | ||||
-rw-r--r-- | gdb/testsuite/lib/gdb.exp | 8 |
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" |