Skip to content

Commit

Permalink
PowerPC, fix support for printing the function return value for non-t…
Browse files Browse the repository at this point in the history
…rivial values.

Currently, a non-trivial return value from a function cannot currently be
reliably determined on PowerPC.  This is due to the fact that the PowerPC
ABI uses register r3 to store the address of the buffer containing the
non-trivial return value when the function is called.  The PowerPC ABI
does not guarantee the value in register r3 is not modified in the
function.  Thus the value in r3 cannot be reliably used to obtain the
return addreses on exit from the function.

This patch adds a new gdbarch method to allow PowerPC to access the value
of r3 on entry to a function. On PowerPC, the new gdbarch method attempts
to use the DW_OP_entry_value for the DWARF entries, when exiting the
function, to determine the value of r3 on entry to the function.  This
requires the use of the -fvar-tracking compiler option to compile the
user application thus generating the DW_OP_entry_value in the binary.  The
DW_OP_entry_value entries in the binary file allows GDB to resolve the
DW_TAG_call_site entries.  This new gdbarch method is used to get the
return buffer address, in the case of a function returning a nontrivial
data type, on exit from the function.  The GDB function should_stop checks
to see if RETURN_BUF is non-zero.  By default, RETURN_BUF will be set to
zero by the new gdbarch method call for all architectures except PowerPC.
The get_return_value function will be used to obtain the return value on
all other architectures as is currently being done if RETURN_BUF is zero.
On PowerPC, the new gdbarch method will return a nonzero address in
RETURN_BUF if the value can be determined.  The value_at function uses the
return buffer address to get the return value.

This patch fixes five testcase failures in gdb.cp/non-trivial-retval.exp.
The correct function return values are now reported.

Note this patch is dependent on patch: "PowerPC, function
ppc64_sysv_abi_return_value add missing return value convention".

This patch has been tested on Power 10 and x86-64 with no regressions.
  • Loading branch information
carlelove committed Nov 14, 2022
1 parent 24b27e5 commit a0eda3d
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 14 deletions.
6 changes: 6 additions & 0 deletions gdb/arch-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions gdb/arch-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
10 changes: 2 additions & 8 deletions gdb/dwarf2/loc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 11 additions & 0 deletions gdb/dwarf2/loc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
15 changes: 15 additions & 0 deletions gdb/gdbarch-components.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,21 @@
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
Expand Down
11 changes: 11 additions & 0 deletions gdb/gdbarch-gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions gdb/gdbarch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -780,6 +782,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
gdb_printf (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));
Expand Down Expand Up @@ -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)
{
Expand Down
41 changes: 37 additions & 4 deletions gdb/infcmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "gdbsupport/gdb_optional.h"
#include "source.h"
#include "cli/cli-style.h"
#include "dwarf2/loc.h"

/* Local functions: */

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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. */
Expand All @@ -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 ();

Expand Down
41 changes: 41 additions & 0 deletions gdb/ppc-sysv-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "objfiles.h"
#include "infcall.h"
#include "dwarf2.h"
#include "dwarf2/loc.h"
#include "target-float.h"
#include <algorithm>

Expand Down Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions gdb/ppc-tdep.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
6 changes: 5 additions & 1 deletion gdb/rs6000-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
9 changes: 8 additions & 1 deletion gdb/testsuite/gdb.cp/non-trivial-retval.exp
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
8 changes: 8 additions & 0 deletions gdb/testsuite/lib/gdb.exp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit a0eda3d

Please sign in to comment.