/* Perform an inferior function call, for GDB, the GNU debugger.
- Copyright (C) 1986-2014 Free Software Foundation, Inc.
+ Copyright (C) 1986-2015 Free Software Foundation, Inc.
This file is part of GDB.
#include "regcache.h"
#include "inferior.h"
#include "infrun.h"
-#include "gdb_assert.h"
#include "block.h"
#include "gdbcore.h"
#include "language.h"
#include "objfiles.h"
#include "gdbcmd.h"
#include "command.h"
-#include <string.h>
#include "infcall.h"
#include "dummy-frame.h"
#include "ada-lang.h"
#include "gdbthread.h"
-#include "exceptions.h"
#include "event-top.h"
+#include "observer.h"
/* If we can't find a function's name from its address,
we print this instead. */
static struct gdb_exception
run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
{
- volatile struct gdb_exception e;
+ struct gdb_exception caught_error = exception_none;
int saved_in_infcall = call_thread->control.in_infcall;
ptid_t call_thread_ptid = call_thread->ptid;
+ int saved_sync_execution = sync_execution;
+
+ /* Infcalls run synchronously, in the foreground. */
+ if (target_can_async_p ())
+ sync_execution = 1;
call_thread->control.in_infcall = 1;
- clear_proceed_status ();
+ clear_proceed_status (0);
disable_watchpoints_before_interactive_call_start ();
- /* We want stop_registers, please... */
+ /* We want to print return value, please... */
call_thread->control.proceed_to_finish = 1;
- TRY_CATCH (e, RETURN_MASK_ALL)
+ TRY
{
int was_sync = sync_execution;
- proceed (real_pc, GDB_SIGNAL_0, 0);
+ proceed (real_pc, GDB_SIGNAL_0);
/* Inferior function calls are always synchronous, even if the
target supports asynchronous execution. Do here what
async_disable_stdin ();
}
}
+ CATCH (e, RETURN_MASK_ALL)
+ {
+ caught_error = e;
+ }
+ END_CATCH
/* At this point the current thread may have changed. Refresh
CALL_THREAD as it could be invalid if its thread has exited. */
If all error()s out of proceed ended up calling normal_stop
(and perhaps they should; it already does in the special case
of error out of resume()), then we wouldn't need this. */
- if (e.reason < 0)
+ if (caught_error.reason < 0)
{
if (call_thread != NULL)
breakpoint_auto_delete (call_thread->control.stop_bpstat);
if (call_thread != NULL)
call_thread->control.in_infcall = saved_in_infcall;
- return e;
+ sync_execution = saved_sync_execution;
+
+ return caught_error;
}
/* A cleanup function that calls delete_std_terminate_breakpoint. */
delete_std_terminate_breakpoint ();
}
+/* See infcall.h. */
+
+struct value *
+call_function_by_hand (struct value *function, int nargs, struct value **args)
+{
+ return call_function_by_hand_dummy (function, nargs, args, NULL, NULL);
+}
+
+/* Data for dummy_frame_context_saver. Structure can be freed only
+ after both dummy_frame_context_saver_dtor and
+ dummy_frame_context_saver_drop have been called for it. */
+
+struct dummy_frame_context_saver
+{
+ /* Inferior registers fetched before associated dummy_frame got freed
+ and before any other destructors of associated dummy_frame got called.
+ It is initialized to NULL. */
+ struct regcache *retbuf;
+
+ /* It is 1 if this dummy_frame_context_saver_drop has been already
+ called. */
+ int drop_done;
+};
+
+/* Free struct dummy_frame_context_saver. */
+
+static void
+dummy_frame_context_saver_free (struct dummy_frame_context_saver *saver)
+{
+ regcache_xfree (saver->retbuf);
+ xfree (saver);
+}
+
+/* Destructor for associated dummy_frame. */
+
+static void
+dummy_frame_context_saver_dtor (void *data_voidp, int registers_valid)
+{
+ struct dummy_frame_context_saver *data = data_voidp;
+
+ gdb_assert (data->retbuf == NULL);
+
+ if (data->drop_done)
+ dummy_frame_context_saver_free (data);
+ else if (registers_valid)
+ data->retbuf = regcache_dup (get_current_regcache ());
+}
+
+/* Caller is no longer interested in this
+ struct dummy_frame_context_saver. After its associated dummy_frame
+ gets freed struct dummy_frame_context_saver can be also freed. */
+
+void
+dummy_frame_context_saver_drop (struct dummy_frame_context_saver *saver)
+{
+ saver->drop_done = 1;
+
+ if (!find_dummy_frame_dtor (dummy_frame_context_saver_dtor, saver))
+ dummy_frame_context_saver_free (saver);
+}
+
+/* Stub dummy_frame_context_saver_drop compatible with make_cleanup. */
+
+void
+dummy_frame_context_saver_cleanup (void *data)
+{
+ struct dummy_frame_context_saver *saver = data;
+
+ dummy_frame_context_saver_drop (saver);
+}
+
+/* Fetch RETBUF field of possibly opaque DTOR_DATA.
+ RETBUF must not be NULL. */
+
+struct regcache *
+dummy_frame_context_saver_get_regs (struct dummy_frame_context_saver *saver)
+{
+ gdb_assert (saver->retbuf != NULL);
+ return saver->retbuf;
+}
+
+/* Register provider of inferior registers at the time DUMMY_ID frame of
+ PTID gets freed (before inferior registers get restored to those
+ before dummy_frame). */
+
+struct dummy_frame_context_saver *
+dummy_frame_context_saver_setup (struct frame_id dummy_id, ptid_t ptid)
+{
+ struct dummy_frame_context_saver *saver;
+
+ saver = xmalloc (sizeof (*saver));
+ saver->retbuf = NULL;
+ saver->drop_done = 0;
+ register_dummy_frame_dtor (dummy_id, inferior_ptid,
+ dummy_frame_context_saver_dtor, saver);
+ return saver;
+}
+
/* All this stuff with a dummy frame may seem unnecessarily complicated
(why not just save registers in GDB?). The purpose of pushing a dummy
frame which looks just like a real frame is so that if you call a
ARGS is modified to contain coerced values. */
struct value *
-call_function_by_hand (struct value *function, int nargs, struct value **args)
+call_function_by_hand_dummy (struct value *function,
+ int nargs, struct value **args,
+ dummy_frame_dtor_ftype *dummy_dtor,
+ void *dummy_dtor_data)
{
CORE_ADDR sp;
struct type *values_type, *target_values_type;
ptid_t call_thread_ptid;
struct gdb_exception e;
char name_buf[RAW_FUNCTION_ADDRESS_SIZE];
+ int stack_temporaries = thread_stack_temporaries_enabled_p (inferior_ptid);
+ struct dummy_frame_context_saver *context_saver;
+ struct cleanup *context_saver_cleanup;
if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
If the ABI specifies a "Red Zone" (see the doco) the code
below will quietly trash it. */
sp = old_sp;
+
+ /* Skip over the stack temporaries that might have been generated during
+ the evaluation of an expression. */
+ if (stack_temporaries)
+ {
+ struct value *lastval;
+
+ lastval = get_last_thread_stack_temporary (inferior_ptid);
+ if (lastval != NULL)
+ {
+ CORE_ADDR lastval_addr = value_address (lastval);
+
+ if (gdbarch_inner_than (gdbarch, 1, 2))
+ {
+ gdb_assert (sp >= lastval_addr);
+ sp = lastval_addr;
+ }
+ else
+ {
+ gdb_assert (sp <= lastval_addr);
+ sp = lastval_addr + TYPE_LENGTH (value_type (lastval));
+ }
+
+ if (gdbarch_frame_align_p (gdbarch))
+ sp = gdbarch_frame_align (gdbarch, sp);
+ }
+ }
}
funaddr = find_function_addr (function, &values_type);
target_values_type = values_type;
}
+ observer_notify_inferior_call_pre (inferior_ptid, funaddr);
+
/* Determine the location of the breakpoint (and possibly other
stuff) that the called function will return to. The SPARC, for a
function returning a structure or union, needs to make space for
/* Reserve space for the return structure to be written on the
stack, if necessary. Make certain that the value is correctly
- aligned. */
+ aligned.
+
+ While evaluating expressions, we reserve space on the stack for
+ return values of class type even if the language ABI and the target
+ ABI do not require that the return value be passed as a hidden first
+ argument. This is because we want to store the return value as an
+ on-stack temporary while the expression is being evaluated. This
+ enables us to have chained function calls in expressions.
+
+ Keeping the return values as on-stack temporaries while the expression
+ is being evaluated is OK because the thread is stopped until the
+ expression is completely evaluated. */
- if (struct_return || hidden_first_param_p)
+ if (struct_return || hidden_first_param_p
+ || (stack_temporaries && class_or_union_p (values_type)))
{
if (gdbarch_inner_than (gdbarch, 1, 2))
{
if (unwind_on_terminating_exception_p)
set_std_terminate_breakpoint ();
+ /* Discard both inf_status and caller_state cleanups.
+ From this point on we explicitly restore the associated state
+ or discard it. */
+ discard_cleanups (inf_status_cleanup);
+
/* Everything's ready, push all the info needed to restore the
caller (and identify the dummy-frame) onto the dummy-frame
stack. */
dummy_frame_push (caller_state, &dummy_id, inferior_ptid);
+ if (dummy_dtor != NULL)
+ register_dummy_frame_dtor (dummy_id, inferior_ptid,
+ dummy_dtor, dummy_dtor_data);
- /* Discard both inf_status and caller_state cleanups.
- From this point on we explicitly restore the associated state
- or discard it. */
- discard_cleanups (inf_status_cleanup);
+ /* dummy_frame_context_saver_setup must be called last so that its
+ saving of inferior registers gets called first (before possible
+ DUMMY_DTOR destructor). */
+ context_saver = dummy_frame_context_saver_setup (dummy_id, inferior_ptid);
+ context_saver_cleanup = make_cleanup (dummy_frame_context_saver_cleanup,
+ context_saver);
/* Register a clean-up for unwind_on_terminating_exception_breakpoint. */
terminate_bp_cleanup = make_cleanup (cleanup_delete_std_terminate_breakpoint,
e = run_inferior_call (tp, real_pc);
}
+ observer_notify_inferior_call_post (call_thread_ptid, funaddr);
+
/* Rethrow an error if we got one trying to run the inferior. */
if (e.reason < 0)
if (stopped_by_random_signal || stop_stack_dummy != STOP_STACK_DUMMY)
{
- const char *name = get_function_name (funaddr,
- name_buf, sizeof (name_buf));
+ /* Make a copy as NAME may be in an objfile freed by dummy_frame_pop. */
+ char *name = xstrdup (get_function_name (funaddr,
+ name_buf, sizeof (name_buf)));
+ make_cleanup (xfree, name);
+
if (stopped_by_random_signal)
{
and the dummy frame has already been popped. */
{
- struct address_space *aspace = get_regcache_aspace (stop_registers);
- struct regcache *retbuf = regcache_xmalloc (gdbarch, aspace);
- struct cleanup *retbuf_cleanup = make_cleanup_regcache_xfree (retbuf);
struct value *retval = NULL;
- regcache_cpy_no_passthrough (retbuf, stop_registers);
-
/* Inferior call is successful. Restore the inferior status.
At this stage, leave the RETBUF alone. */
restore_infcall_control_state (inf_status);
- /* Figure out the value returned by the function. */
- retval = allocate_value (values_type);
-
- if (hidden_first_param_p)
- read_value_memory (retval, 0, 1, struct_addr,
- value_contents_raw (retval),
- TYPE_LENGTH (values_type));
- else if (TYPE_CODE (target_values_type) != TYPE_CODE_VOID)
+ if (TYPE_CODE (values_type) == TYPE_CODE_VOID)
+ retval = allocate_value (values_type);
+ else if (struct_return || hidden_first_param_p)
{
- /* If the function returns void, don't bother fetching the
- return value. */
- switch (gdbarch_return_value (gdbarch, function, target_values_type,
- NULL, NULL, NULL))
+ if (stack_temporaries)
+ {
+ retval = value_from_contents_and_address (values_type, NULL,
+ struct_addr);
+ push_thread_stack_temporary (inferior_ptid, retval);
+ }
+ else
{
- case RETURN_VALUE_REGISTER_CONVENTION:
- case RETURN_VALUE_ABI_RETURNS_ADDRESS:
- case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
- gdbarch_return_value (gdbarch, function, values_type,
- retbuf, value_contents_raw (retval), NULL);
- break;
- case RETURN_VALUE_STRUCT_CONVENTION:
+ retval = allocate_value (values_type);
read_value_memory (retval, 0, 1, struct_addr,
value_contents_raw (retval),
TYPE_LENGTH (values_type));
- break;
+ }
+ }
+ else
+ {
+ retval = allocate_value (values_type);
+ gdbarch_return_value (gdbarch, function, values_type,
+ dummy_frame_context_saver_get_regs (context_saver),
+ value_contents_raw (retval), NULL);
+ if (stack_temporaries && class_or_union_p (values_type))
+ {
+ /* Values of class type returned in registers are copied onto
+ the stack and their lval_type set to lval_memory. This is
+ required because further evaluation of the expression
+ could potentially invoke methods on the return value
+ requiring GDB to evaluate the "this" pointer. To evaluate
+ the this pointer, GDB needs the memory address of the
+ value. */
+ value_force_lval (retval, struct_addr);
+ push_thread_stack_temporary (inferior_ptid, retval);
}
}
- do_cleanups (retbuf_cleanup);
+ do_cleanups (context_saver_cleanup);
gdb_assert (retval);
return retval;