Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
- 2008 Free Software Foundation, Inc.
+ 2008, 2009, 2010 Free Software Foundation, Inc.
This file is part of GDB.
#include "dummy-frame.h"
#include "ada-lang.h"
#include "gdbthread.h"
+#include "exceptions.h"
+
+/* If we can't find a function's name from its address,
+ we print this instead. */
+#define RAW_FUNCTION_ADDRESS_FORMAT "at 0x%s"
+#define RAW_FUNCTION_ADDRESS_SIZE (sizeof (RAW_FUNCTION_ADDRESS_FORMAT) \
+ + 2 * sizeof (CORE_ADDR))
/* NOTE: cagney/2003-04-16: What's the future of this code?
value);
}
+/* This boolean tells what gdb should do if a std::terminate call is
+ made while in a function called from gdb (call dummy).
+ As the confines of a single dummy stack prohibit out-of-frame
+ handlers from handling a raised exception, and as out-of-frame
+ handlers are common in C++, this can lead to no handler being found
+ by the unwinder, and a std::terminate call. This is a false positive.
+ If set, gdb unwinds the stack and restores the context to what it
+ was before the call.
+
+ The default is to unwind the frame if a std::terminate call is
+ made. */
+
+static int unwind_on_terminating_exception_p = 1;
+
+static void
+show_unwind_on_terminating_exception_p (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c,
+ const char *value)
+
+{
+ fprintf_filtered (file, _("\
+Unwind stack if a C++ exception is unhandled while in a call dummy is %s.\n"),
+ value);
+}
/* Perform the standard coercions that are specified
for arguments to be passed to C or Ada functions.
its value as needed). */
static struct value *
-value_arg_coerce (struct value *arg, struct type *param_type,
- int is_prototyped, CORE_ADDR *sp)
+value_arg_coerce (struct gdbarch *gdbarch, struct value *arg,
+ struct type *param_type, int is_prototyped, CORE_ADDR *sp)
{
+ const struct builtin_type *builtin = builtin_type (gdbarch);
struct type *arg_type = check_typedef (value_type (arg));
struct type *type
= param_type ? check_typedef (param_type) : arg_type;
/* Perform any Ada-specific coercion first. */
if (current_language->la_language == language_ada)
- arg = ada_convert_actual (arg, type, sp);
+ arg = ada_convert_actual (arg, type, gdbarch, sp);
/* Force the value to the target if we will need its address. At
this point, we could allocate arguments on the stack instead of
/* If we don't have a prototype, coerce to integer type if necessary. */
if (!is_prototyped)
{
- if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_int))
- type = builtin_type_int;
+ if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin->builtin_int))
+ type = builtin->builtin_int;
}
/* Currently all target ABIs require at least the width of an integer
type for an argument. We may have to conditionalize the following
type coercion for future targets. */
- if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_int))
- type = builtin_type_int;
+ if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin->builtin_int))
+ type = builtin->builtin_int;
break;
case TYPE_CODE_FLT:
if (!is_prototyped && coerce_float_to_double_p)
{
- if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_double))
- type = builtin_type_double;
- else if (TYPE_LENGTH (type) > TYPE_LENGTH (builtin_type_double))
- type = builtin_type_long_double;
+ if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin->builtin_double))
+ type = builtin->builtin_double;
+ else if (TYPE_LENGTH (type) > TYPE_LENGTH (builtin->builtin_double))
+ type = builtin->builtin_long_double;
}
break;
case TYPE_CODE_FUNC:
find_function_addr (struct value *function, struct type **retval_type)
{
struct type *ftype = check_typedef (value_type (function));
+ struct gdbarch *gdbarch = get_type_arch (ftype);
enum type_code code = TYPE_CODE (ftype);
- struct type *value_type;
+ struct type *value_type = NULL;
CORE_ADDR funaddr;
/* If it's a member function, just look at the function
/* Determine address to call. */
if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD)
{
- funaddr = VALUE_ADDRESS (function);
+ funaddr = value_address (function);
value_type = TYPE_TARGET_TYPE (ftype);
}
else if (code == TYPE_CODE_PTR)
if (TYPE_CODE (ftype) == TYPE_CODE_FUNC
|| TYPE_CODE (ftype) == TYPE_CODE_METHOD)
{
- funaddr = gdbarch_convert_from_func_ptr_addr (current_gdbarch,
- funaddr,
+ funaddr = gdbarch_convert_from_func_ptr_addr (gdbarch, funaddr,
¤t_target);
value_type = TYPE_TARGET_TYPE (ftype);
}
- else
- value_type = builtin_type_int;
}
else if (code == TYPE_CODE_INT)
{
{
/* Handle function descriptors lacking debug info. */
int found_descriptor = 0;
+ funaddr = 0; /* pacify "gcc -Werror" */
if (VALUE_LVAL (function) == lval_memory)
{
CORE_ADDR nfunaddr;
funaddr = value_as_address (value_addr (function));
nfunaddr = funaddr;
- funaddr = gdbarch_convert_from_func_ptr_addr (current_gdbarch,
- funaddr,
+ funaddr = gdbarch_convert_from_func_ptr_addr (gdbarch, funaddr,
¤t_target);
if (funaddr != nfunaddr)
found_descriptor = 1;
/* Handle integer used as address of a function. */
funaddr = (CORE_ADDR) value_as_long (function);
}
-
- value_type = builtin_type_int;
}
else
error (_("Invalid data type for function to be called."));
if (retval_type != NULL)
*retval_type = value_type;
- return funaddr + gdbarch_deprecated_function_start_offset (current_gdbarch);
-}
-
-/* Call breakpoint_auto_delete on the current contents of the bpstat
- of the current thread. */
-
-static void
-breakpoint_auto_delete_contents (void *arg)
-{
- if (!ptid_equal (inferior_ptid, null_ptid))
- breakpoint_auto_delete (inferior_thread ()->stop_bpstat);
-}
-
-static CORE_ADDR
-generic_push_dummy_code (struct gdbarch *gdbarch,
- CORE_ADDR sp, CORE_ADDR funaddr,
- struct value **args, int nargs,
- struct type *value_type,
- CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
- struct regcache *regcache)
-{
- /* Something here to findout the size of a breakpoint and then
- allocate space for it on the stack. */
- int bplen;
- /* This code assumes frame align. */
- gdb_assert (gdbarch_frame_align_p (gdbarch));
- /* Force the stack's alignment. The intent is to ensure that the SP
- is aligned to at least a breakpoint instruction's boundary. */
- sp = gdbarch_frame_align (gdbarch, sp);
- /* Allocate space for, and then position the breakpoint on the
- stack. */
- if (gdbarch_inner_than (gdbarch, 1, 2))
- {
- CORE_ADDR bppc = sp;
- gdbarch_breakpoint_from_pc (gdbarch, &bppc, &bplen);
- sp = gdbarch_frame_align (gdbarch, sp - bplen);
- (*bp_addr) = sp;
- /* Should the breakpoint size/location be re-computed here? */
- }
- else
- {
- (*bp_addr) = sp;
- gdbarch_breakpoint_from_pc (gdbarch, bp_addr, &bplen);
- sp = gdbarch_frame_align (gdbarch, sp + bplen);
- }
- /* Inferior resumes at the function entry point. */
- (*real_pc) = funaddr;
- return sp;
+ return funaddr + gdbarch_deprecated_function_start_offset (gdbarch);
}
/* For CALL_DUMMY_ON_STACK, push a breakpoint sequence that the called
CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
struct regcache *regcache)
{
- if (gdbarch_push_dummy_code_p (gdbarch))
- return gdbarch_push_dummy_code (gdbarch, sp, funaddr,
- args, nargs, value_type, real_pc, bp_addr,
- regcache);
- else
- return generic_push_dummy_code (gdbarch, sp, funaddr,
- args, nargs, value_type, real_pc, bp_addr,
- regcache);
+ gdb_assert (gdbarch_push_dummy_code_p (gdbarch));
+
+ return gdbarch_push_dummy_code (gdbarch, sp, funaddr,
+ args, nargs, value_type, real_pc, bp_addr,
+ regcache);
+}
+
+/* Fetch the name of the function at FUNADDR.
+ This is used in printing an error message for call_function_by_hand.
+ BUF is used to print FUNADDR in hex if the function name cannot be
+ determined. It must be large enough to hold formatted result of
+ RAW_FUNCTION_ADDRESS_FORMAT. */
+
+static const char *
+get_function_name (CORE_ADDR funaddr, char *buf, int buf_size)
+{
+ {
+ struct symbol *symbol = find_pc_function (funaddr);
+ if (symbol)
+ return SYMBOL_PRINT_NAME (symbol);
+ }
+
+ {
+ /* Try the minimal symbols. */
+ struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (funaddr);
+ if (msymbol)
+ return SYMBOL_PRINT_NAME (msymbol);
+ }
+
+ {
+ char *tmp = xstrprintf (_(RAW_FUNCTION_ADDRESS_FORMAT),
+ hex_string (funaddr));
+ gdb_assert (strlen (tmp) + 1 <= buf_size);
+ strcpy (buf, tmp);
+ xfree (tmp);
+ return buf;
+ }
+}
+
+/* Subroutine of call_function_by_hand to simplify it.
+ Start up the inferior and wait for it to stop.
+ Return the exception if there's an error, or an exception with
+ reason >= 0 if there's no error.
+
+ This is done inside a TRY_CATCH so the caller needn't worry about
+ thrown errors. The caller should rethrow if there's an error. */
+
+static struct gdb_exception
+run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
+{
+ volatile struct gdb_exception e;
+ int saved_async = 0;
+ int saved_in_infcall = call_thread->in_infcall;
+ ptid_t call_thread_ptid = call_thread->ptid;
+ char *saved_target_shortname = xstrdup (target_shortname);
+
+ call_thread->in_infcall = 1;
+
+ clear_proceed_status ();
+
+ disable_watchpoints_before_interactive_call_start ();
+ call_thread->proceed_to_finish = 1; /* We want stop_registers, please... */
+
+ if (target_can_async_p ())
+ saved_async = target_async_mask (0);
+
+ TRY_CATCH (e, RETURN_MASK_ALL)
+ proceed (real_pc, TARGET_SIGNAL_0, 0);
+
+ /* At this point the current thread may have changed. Refresh
+ CALL_THREAD as it could be invalid if its thread has exited. */
+ call_thread = find_thread_ptid (call_thread_ptid);
+
+ /* Don't restore the async mask if the target has changed,
+ saved_async is for the original target. */
+ if (saved_async
+ && strcmp (saved_target_shortname, target_shortname) == 0)
+ target_async_mask (saved_async);
+
+ enable_watchpoints_after_interactive_call_stop ();
+
+ /* Call breakpoint_auto_delete on the current contents of the bpstat
+ of inferior call thread.
+ 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 (call_thread != NULL)
+ breakpoint_auto_delete (call_thread->stop_bpstat);
+ }
+
+ if (call_thread != NULL)
+ call_thread->in_infcall = saved_in_infcall;
+
+ xfree (saved_target_shortname);
+
+ return e;
}
/* All this stuff with a dummy frame may seem unnecessarily complicated
call_function_by_hand (struct value *function, int nargs, struct value **args)
{
CORE_ADDR sp;
- CORE_ADDR dummy_addr;
struct type *values_type, *target_values_type;
unsigned char struct_return = 0, lang_struct_return = 0;
CORE_ADDR struct_addr = 0;
- struct regcache *retbuf;
- struct cleanup *retbuf_cleanup;
struct inferior_status *inf_status;
struct cleanup *inf_status_cleanup;
+ struct inferior_thread_state *caller_state;
+ struct cleanup *caller_state_cleanup;
CORE_ADDR funaddr;
CORE_ADDR real_pc;
struct type *ftype = check_typedef (value_type (function));
CORE_ADDR bp_addr;
- struct regcache *caller_regcache;
- struct cleanup *caller_regcache_cleanup;
struct frame_id dummy_id;
struct cleanup *args_cleanup;
struct frame_info *frame;
struct gdbarch *gdbarch;
+ struct breakpoint *terminate_bp = NULL;
+ struct minimal_symbol *tm;
+ struct cleanup *terminate_bp_cleanup = NULL;
+ ptid_t call_thread_ptid;
+ struct gdb_exception e;
+ const char *name;
+ char name_buf[RAW_FUNCTION_ADDRESS_SIZE];
if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
gdbarch = get_frame_arch (frame);
if (!gdbarch_push_dummy_call_p (gdbarch))
- error (_("This target does not support function calls"));
-
- /* Create a cleanup chain that contains the retbuf (buffer
- containing the register values). This chain is create BEFORE the
- inf_status chain so that the inferior status can cleaned up
- (restored or discarded) without having the retbuf freed. */
- retbuf = regcache_xmalloc (gdbarch);
- retbuf_cleanup = make_cleanup_regcache_xfree (retbuf);
-
- /* A cleanup for the inferior status. Create this AFTER the retbuf
- so that this can be discarded or applied without interfering with
- the regbuf. */
- inf_status = save_inferior_status (1);
+ error (_("This target does not support function calls."));
+
+ /* A cleanup for the inferior status.
+ This is only needed while we're preparing the inferior function call. */
+ inf_status = save_inferior_status ();
inf_status_cleanup = make_cleanup_restore_inferior_status (inf_status);
- /* Save the caller's registers so that they can be restored once the
+ /* Save the caller's registers and other state associated with the
+ inferior itself so that they can be restored once the
callee returns. To allow nested calls the registers are (further
down) pushed onto a dummy frame stack. Include a cleanup (which
is tossed once the regcache has been pushed). */
- caller_regcache = frame_save_as_regcache (frame);
- caller_regcache_cleanup = make_cleanup_regcache_xfree (caller_regcache);
+ caller_state = save_inferior_thread_state ();
+ caller_state_cleanup = make_cleanup_restore_inferior_thread_state (caller_state);
/* Ensure that the initial SP is correctly aligned. */
{
}
funaddr = find_function_addr (function, &values_type);
+ if (!values_type)
+ values_type = builtin_type (gdbarch)->builtin_int;
+
CHECK_TYPEDEF (values_type);
/* Are we returning a value using a structure return (passing a
/* Tell the target specific argument pushing routine not to
expect a value. */
- target_values_type = builtin_type_void;
+ target_values_type = builtin_type (gdbarch)->builtin_void;
}
else
{
- struct_return = using_struct_return (value_type (function), values_type);
+ struct_return = using_struct_return (gdbarch,
+ value_type (function), values_type);
target_values_type = values_type;
}
switch (gdbarch_call_dummy_location (gdbarch))
{
case ON_STACK:
- /* "dummy_addr" is here just to keep old targets happy. New
- targets return that same information via "sp" and "bp_addr". */
- if (gdbarch_inner_than (gdbarch, 1, 2))
- {
- sp = push_dummy_code (gdbarch, sp, funaddr,
- args, nargs, target_values_type,
- &real_pc, &bp_addr, get_current_regcache ());
- dummy_addr = sp;
- }
- else
- {
- dummy_addr = sp;
- sp = push_dummy_code (gdbarch, sp, funaddr,
+ sp = push_dummy_code (gdbarch, sp, funaddr,
args, nargs, target_values_type,
&real_pc, &bp_addr, get_current_regcache ());
- }
break;
case AT_ENTRY_POINT:
- real_pc = funaddr;
- dummy_addr = entry_point_address ();
- /* Make certain that the address points at real code, and not a
- function descriptor. */
- dummy_addr = gdbarch_convert_from_func_ptr_addr (gdbarch,
- dummy_addr,
- ¤t_target);
- /* A call dummy always consists of just a single breakpoint, so
- it's address is the same as the address of the dummy. */
- bp_addr = dummy_addr;
- break;
+ {
+ CORE_ADDR dummy_addr;
+
+ real_pc = funaddr;
+ dummy_addr = entry_point_address ();
+ /* A call dummy always consists of just a single breakpoint, so
+ its address is the same as the address of the dummy. */
+ bp_addr = dummy_addr;
+ break;
+ }
case AT_SYMBOL:
/* Some executables define a symbol __CALL_DUMMY_ADDRESS whose
address is the location where the breakpoint should be
this can be deleted - ON_STACK is a better option. */
{
struct minimal_symbol *sym;
+ CORE_ADDR dummy_addr;
sym = lookup_minimal_symbol ("__CALL_DUMMY_ADDRESS", NULL, NULL);
real_pc = funaddr;
if (sym)
- dummy_addr = SYMBOL_VALUE_ADDRESS (sym);
+ {
+ dummy_addr = SYMBOL_VALUE_ADDRESS (sym);
+ /* Make certain that the address points at real code, and not
+ a function descriptor. */
+ dummy_addr = gdbarch_convert_from_func_ptr_addr (gdbarch,
+ dummy_addr,
+ ¤t_target);
+ }
else
dummy_addr = entry_point_address ();
- /* Make certain that the address points at real code, and not
- a function descriptor. */
- dummy_addr = gdbarch_convert_from_func_ptr_addr (gdbarch,
- dummy_addr,
- ¤t_target);
/* A call dummy always consists of just a single breakpoint,
so it's address is the same as the address of the dummy. */
bp_addr = dummy_addr;
}
if (nargs < TYPE_NFIELDS (ftype))
- error (_("too few arguments in function call"));
+ error (_("Too few arguments in function call."));
{
int i;
else
param_type = NULL;
- args[i] = value_arg_coerce (args[i], param_type, prototyped, &sp);
+ args[i] = value_arg_coerce (gdbarch, args[i],
+ param_type, prototyped, &sp);
if (param_type != NULL && language_pass_by_reference (param_type))
args[i] = value_addr (args[i]);
struct breakpoint *bpt;
struct symtab_and_line sal;
init_sal (&sal); /* initialize to zeroes */
+ sal.pspace = current_program_space;
sal.pc = bp_addr;
sal.section = find_pc_overlay (sal.pc);
/* Sanity. The exact same SP value is returned by
PUSH_DUMMY_CALL, saved as the dummy-frame TOS, and used by
dummy_id to form the frame ID's stack address. */
- bpt = set_momentary_breakpoint (sal, dummy_id, bp_call_dummy);
+ bpt = set_momentary_breakpoint (gdbarch, sal, dummy_id, bp_call_dummy);
bpt->disposition = disp_del;
}
+ /* Create a breakpoint in std::terminate.
+ If a C++ exception is raised in the dummy-frame, and the
+ exception handler is (normally, and expected to be) out-of-frame,
+ the default C++ handler will (wrongly) be called in an inferior
+ function call. This is wrong, as an exception can be normally
+ and legally handled out-of-frame. The confines of the dummy frame
+ prevent the unwinder from finding the correct handler (or any
+ handler, unless it is in-frame). The default handler calls
+ std::terminate. This will kill the inferior. Assert that
+ terminate should never be called in an inferior function
+ call. Place a momentary breakpoint in the std::terminate function
+ and if triggered in the call, rewind. */
+ if (unwind_on_terminating_exception_p)
+ {
+ struct minimal_symbol *tm = lookup_minimal_symbol ("std::terminate()",
+ NULL, NULL);
+ if (tm != NULL)
+ terminate_bp = set_momentary_breakpoint_at_pc
+ (gdbarch, SYMBOL_VALUE_ADDRESS (tm), bp_breakpoint);
+ }
+
/* 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_regcache, &dummy_id);
- discard_cleanups (caller_regcache_cleanup);
+ dummy_frame_push (caller_state, &dummy_id);
+
+ /* 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);
+
+ /* Register a clean-up for unwind_on_terminating_exception_breakpoint. */
+ if (terminate_bp)
+ terminate_bp_cleanup = make_cleanup_delete_breakpoint (terminate_bp);
/* - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP -
If you're looking to implement asynchronous dummy-frames, then
just below is the place to chop this function in two.. */
- /* Now proceed, having reached the desired place. */
- clear_proceed_status ();
-
- /* Execute a "stack dummy", a piece of code stored in the stack by
- the debugger to be executed in the inferior.
-
- The dummy's frame is automatically popped whenever that break is
- hit. If that is the first time the program stops,
- call_function_by_hand returns to its caller with that frame
- already gone and sets RC to 0.
-
- Otherwise, set RC to a non-zero value. If the called function
- receives a random signal, we do not allow the user to continue
- executing it as this may not work. The dummy frame is poped and
- we return 1. If we hit a breakpoint, we leave the frame in place
- and return 2 (the frame will eventually be popped when we do hit
- the dummy end breakpoint). */
-
+ /* TP is invalid after run_inferior_call returns, so enclose this
+ in a block so that it's only in scope during the time it's valid. */
{
- struct cleanup *old_cleanups = make_cleanup (null_cleanup, 0);
- struct cleanup *old_cleanups2;
- int saved_async = 0;
-
- /* 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. */
- make_cleanup (breakpoint_auto_delete_contents, NULL);
+ struct thread_info *tp = inferior_thread ();
- disable_watchpoints_before_interactive_call_start ();
- proceed_to_finish = 1; /* We want stop_registers, please... */
+ /* Save this thread's ptid, we need it later but the thread
+ may have exited. */
+ call_thread_ptid = tp->ptid;
- if (target_can_async_p ())
- saved_async = target_async_mask (0);
+ /* Run the inferior until it stops. */
- old_cleanups2 = make_cleanup_restore_integer (&suppress_resume_observer);
- suppress_resume_observer = 1;
- make_cleanup_restore_integer (&suppress_stop_observer);
- suppress_stop_observer = 1;
- proceed (real_pc, TARGET_SIGNAL_0, 0);
- do_cleanups (old_cleanups2);
-
- if (saved_async)
- target_async_mask (saved_async);
-
- enable_watchpoints_after_interactive_call_stop ();
-
- discard_cleanups (old_cleanups);
+ e = run_inferior_call (tp, real_pc);
}
+ /* Rethrow an error if we got one trying to run the inferior. */
+
+ if (e.reason < 0)
+ {
+ const char *name = get_function_name (funaddr,
+ name_buf, sizeof (name_buf));
+
+ discard_inferior_status (inf_status);
+
+ /* We could discard the dummy frame here if the program exited,
+ but it will get garbage collected the next time the program is
+ run anyway. */
+
+ switch (e.reason)
+ {
+ case RETURN_ERROR:
+ throw_error (e.error, _("\
+%s\n\
+An error occurred while in a function called from GDB.\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
+ e.message, name);
+ case RETURN_QUIT:
+ default:
+ throw_exception (e);
+ }
+ }
+
+ /* If the program has exited, or we stopped at a different thread,
+ exit and inform the user. */
+
+ if (! target_has_execution)
+ {
+ const char *name = get_function_name (funaddr,
+ name_buf, sizeof (name_buf));
+
+ /* If we try to restore the inferior status,
+ we'll crash as the inferior is no longer running. */
+ discard_inferior_status (inf_status);
+
+ /* We could discard the dummy frame here given that the program exited,
+ but it will get garbage collected the next time the program is
+ run anyway. */
+
+ error (_("\
+The program being debugged exited while in a function called from GDB.\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned."),
+ name);
+ }
+
+ if (! ptid_equal (call_thread_ptid, inferior_ptid))
+ {
+ const char *name = get_function_name (funaddr,
+ name_buf, sizeof (name_buf));
+
+ /* We've switched threads. This can happen if another thread gets a
+ signal or breakpoint while our thread was running.
+ There's no point in restoring the inferior status,
+ we're in a different thread. */
+ discard_inferior_status (inf_status);
+ /* Keep the dummy frame record, if the user switches back to the
+ thread with the hand-call, we'll need it. */
+ if (stopped_by_random_signal)
+ error (_("\
+The program received a signal in another thread while\n\
+making a function call from GDB.\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
+ name);
+ else
+ error (_("\
+The program stopped in another thread while making a function call from GDB.\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
+ name);
+ }
+
if (stopped_by_random_signal || !stop_stack_dummy)
{
- /* Find the name of the function we're about to complain about. */
- const char *name = NULL;
- {
- struct symbol *symbol = find_pc_function (funaddr);
- if (symbol)
- name = SYMBOL_PRINT_NAME (symbol);
- else
- {
- /* Try the minimal symbols. */
- struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (funaddr);
- if (msymbol)
- name = SYMBOL_PRINT_NAME (msymbol);
- }
- if (name == NULL)
- {
- /* Can't use a cleanup here. It is discarded, instead use
- an alloca. */
- char *tmp = xstrprintf ("at %s", hex_string (funaddr));
- char *a = alloca (strlen (tmp) + 1);
- strcpy (a, tmp);
- xfree (tmp);
- name = a;
- }
- }
+ const char *name = get_function_name (funaddr,
+ name_buf, sizeof (name_buf));
+
if (stopped_by_random_signal)
{
/* We stopped inside the FUNCTION because of a random
/* The user wants the context restored. */
/* We must get back to the frame we were before the
- dummy call. */
- frame_pop (get_current_frame ());
+ dummy call. */
+ dummy_frame_pop (dummy_id);
+
+ /* We also need to restore inferior status to that before the
+ dummy call. */
+ restore_inferior_status (inf_status);
/* FIXME: Insert a bunch of wrap_here; name can be very
long if it's a C++ name with arguments and stuff. */
error (_("\
The program being debugged was signaled while in a function called from GDB.\n\
GDB has restored the context to what it was before the call.\n\
-To change this behavior use \"set unwindonsignal off\"\n\
-Evaluation of the expression containing the function (%s) will be abandoned."),
+To change this behavior use \"set unwindonsignal off\".\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned."),
name);
}
else
{
/* The user wants to stay in the frame where we stopped
- (default).*/
- /* If we restored the inferior status (via the cleanup),
- we would print a spurious error message (Unable to
- restore previously selected frame), would write the
- registers from the inf_status (which is wrong), and
- would do other wrong things. */
- discard_cleanups (inf_status_cleanup);
+ (default).
+ Discard inferior status, we're not at the same point
+ we started at. */
discard_inferior_status (inf_status);
+
/* FIXME: Insert a bunch of wrap_here; name can be very
long if it's a C++ name with arguments and stuff. */
error (_("\
The program being debugged was signaled while in a function called from GDB.\n\
GDB remains in the frame where the signal was received.\n\
-To change this behavior use \"set unwindonsignal on\"\n\
-Evaluation of the expression containing the function (%s) will be abandoned."),
+To change this behavior use \"set unwindonsignal on\".\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
name);
}
}
if (!stop_stack_dummy)
{
- /* We hit a breakpoint inside the FUNCTION. */
- /* If we restored the inferior status (via the cleanup), we
- would print a spurious error message (Unable to restore
- previously selected frame), would write the registers
- from the inf_status (which is wrong), and would do other
- wrong things. */
- discard_cleanups (inf_status_cleanup);
+
+ /* Check if unwind on terminating exception behaviour is on. */
+ if (unwind_on_terminating_exception_p)
+ {
+ /* Check that the breakpoint is our special std::terminate
+ breakpoint. If it is, we do not want to kill the inferior
+ in an inferior function call. Rewind, and warn the
+ user. */
+
+ if (terminate_bp != NULL
+ && (inferior_thread ()->stop_bpstat->breakpoint_at->address
+ == terminate_bp->loc->address))
+ {
+ /* We must get back to the frame we were before the
+ dummy call. */
+ dummy_frame_pop (dummy_id);
+
+ /* We also need to restore inferior status to that before the
+ dummy call. */
+ restore_inferior_status (inf_status);
+
+ error (_("\
+The program being debugged entered a std::terminate call, most likely\n\
+caused by an unhandled C++ exception. GDB blocked this call in order\n\
+to prevent the program from being terminated, and has restored the\n\
+context to its original state before the call.\n\
+To change this behaviour use \"set unwind-on-terminating-exception off\".\n\
+Evaluation of the expression containing the function (%s)\n\
+will be abandoned."),
+ name);
+ }
+ }
+ /* We hit a breakpoint inside the FUNCTION.
+ Keep the dummy frame, the user may want to examine its state.
+ Discard inferior status, we're not at the same point
+ we started at. */
discard_inferior_status (inf_status);
+
/* The following error message used to say "The expression
which contained the function call has been discarded."
It is a hard concept to explain in a few words. Ideally,
a C++ name with arguments and stuff. */
error (_("\
The program being debugged stopped while in a function called from GDB.\n\
-When the function (%s) is done executing, GDB will silently\n\
-stop (instead of continuing to evaluate the expression containing\n\
-the function call)."), name);
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
+ name);
}
/* The above code errors out, so ... */
internal_error (__FILE__, __LINE__, _("... should not be here"));
}
- /* If we get here the called FUNCTION run to completion. */
-
- /* On normal return, the stack dummy has been popped already. */
- regcache_cpy_no_passthrough (retbuf, stop_registers);
+ /* If we get here and the std::terminate() breakpoint has been set,
+ it has to be cleaned manually. */
+ if (terminate_bp)
+ do_cleanups (terminate_bp_cleanup);
- /* Restore the inferior status, via its cleanup. At this stage,
- leave the RETBUF alone. */
- do_cleanups (inf_status_cleanup);
+ /* If we get here the called FUNCTION ran to completion,
+ and the dummy frame has already been popped. */
- /* Figure out the value returned by the function. */
{
+ struct regcache *retbuf = regcache_xmalloc (gdbarch);
+ 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_inferior_status (inf_status);
+
+ /* Figure out the value returned by the function. */
+
if (lang_struct_return)
retval = value_at (values_type, struct_addr);
else if (TYPE_CODE (target_values_type) == TYPE_CODE_VOID)
do_cleanups (retbuf_cleanup);
- gdb_assert(retval);
+ gdb_assert (retval);
return retval;
}
}
NULL,
show_unwind_on_signal_p,
&setlist, &showlist);
+
+ add_setshow_boolean_cmd ("unwind-on-terminating-exception", no_class,
+ &unwind_on_terminating_exception_p, _("\
+Set unwinding of stack if std::terminate is called while in call dummy."), _("\
+Show unwinding of stack if std::terminate() is called while in a call dummy."), _("\
+The unwind on terminating exception flag lets the user determine\n\
+what gdb should do if a std::terminate() call is made from the\n\
+default exception handler. If set, gdb unwinds the stack and restores\n\
+the context to what it was before the call. If unset, gdb allows the\n\
+std::terminate call to proceed.\n\
+The default is to unwind the frame."),
+ NULL,
+ show_unwind_on_terminating_exception_p,
+ &setlist, &showlist);
+
}