#include "defs.h"
#include "breakpoint.h"
#include "target.h"
#include "regcache.h"
#include "inferior.h"
#include "gdb_assert.h"
#include "block.h"
#include "gdbcore.h"
#include "language.h"
#include "objfiles.h"
#include "gdbcmd.h"
#include "command.h"
#include "gdb_string.h"
#include "infcall.h"
#include "dummy-frame.h"
#include <sys/time.h>
#include "exceptions.h"
extern void begin_inferior_call_checkpoints (void);
extern void end_inferior_call_checkpoints (void);
#if defined (NM_NEXTSTEP)
#include "macosx-nat-infthread.h"
int inferior_function_calls_disabled_p = 0;
#endif
ptid_t hand_call_ptid;
ptid_t get_hand_call_ptid ()
{
return hand_call_ptid;
}
static void
do_reset_hand_call_ptid ()
{
hand_call_ptid = minus_one_ptid;
}
static int coerce_float_to_double_p = 1;
static void
show_coerce_float_to_double_p (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
fprintf_filtered (file, _("\
Coercion of floats to doubles when calling functions is %s.\n"),
value);
}
int unwind_on_signal_p = 0;
int
set_unwind_on_signal (int new_val)
{
int old_val = unwind_on_signal_p;
unwind_on_signal_p = new_val;
return old_val;
}
void
set_unwind_on_signal_cleanup (void *new_val)
{
unwind_on_signal_p = (int) new_val;
}
static void
show_unwind_on_signal_p (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
fprintf_filtered (file, _("\
Unwinding of stack if a signal is received while in a call dummy is %s.\n"),
value);
}
static struct value *
value_arg_coerce (struct value *arg, struct type *param_type,
int is_prototyped)
{
struct type *arg_type = check_typedef (value_type (arg));
struct type *type
= param_type ? check_typedef (param_type) : arg_type;
switch (TYPE_CODE (type))
{
case TYPE_CODE_REF:
if (TYPE_CODE (arg_type) != TYPE_CODE_REF
&& TYPE_CODE (arg_type) != TYPE_CODE_PTR)
{
arg = value_addr (arg);
deprecated_set_value_type (arg, param_type);
return arg;
}
break;
case TYPE_CODE_INT:
case TYPE_CODE_CHAR:
case TYPE_CODE_BOOL:
case TYPE_CODE_ENUM:
if (!is_prototyped)
{
if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_int))
type = builtin_type_int;
}
if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_int))
type = builtin_type_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;
}
break;
case TYPE_CODE_FUNC:
type = lookup_pointer_type (type);
break;
case TYPE_CODE_ARRAY:
if (current_language->c_style_arrays)
if (!TYPE_VECTOR (type))
type = lookup_pointer_type (TYPE_TARGET_TYPE (type));
break;
case TYPE_CODE_UNDEF:
case TYPE_CODE_PTR:
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
case TYPE_CODE_VOID:
case TYPE_CODE_SET:
case TYPE_CODE_RANGE:
case TYPE_CODE_STRING:
case TYPE_CODE_BITSTRING:
case TYPE_CODE_ERROR:
case TYPE_CODE_MEMBER:
case TYPE_CODE_METHOD:
case TYPE_CODE_COMPLEX:
default:
break;
}
return value_cast (type, arg);
}
CORE_ADDR
find_function_addr (struct value *function, struct type **retval_type)
{
struct type *ftype = check_typedef (value_type (function));
enum type_code code = TYPE_CODE (ftype);
struct type *value_type;
CORE_ADDR funaddr;
if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD)
{
funaddr = VALUE_ADDRESS (function);
value_type = TYPE_TARGET_TYPE (ftype);
}
else if (code == TYPE_CODE_PTR)
{
funaddr = value_as_address (function);
ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
if (TYPE_CODE (ftype) == TYPE_CODE_FUNC
|| TYPE_CODE (ftype) == TYPE_CODE_METHOD)
{
funaddr = gdbarch_convert_from_func_ptr_addr (current_gdbarch,
funaddr,
¤t_target);
value_type = TYPE_TARGET_TYPE (ftype);
}
else
value_type = builtin_type_int;
}
else if (code == TYPE_CODE_INT)
{
if (TYPE_LENGTH (ftype) == 1)
funaddr = value_as_address (value_addr (function));
else
funaddr = (CORE_ADDR) value_as_long (function);
value_type = builtin_type_int;
}
else if (code == TYPE_CODE_ERROR)
{
value_type = builtin_type_error;
}
else
error (_("Invalid data type for function to be called."));
if (retval_type != NULL)
*retval_type = value_type;
return funaddr + DEPRECATED_FUNCTION_START_OFFSET;
}
static void
breakpoint_auto_delete_contents (void *arg)
{
breakpoint_auto_delete (*(bpstat *) arg);
}
static CORE_ADDR
generic_push_dummy_code (struct gdbarch *gdbarch,
CORE_ADDR sp, CORE_ADDR funaddr, int using_gcc,
struct value **args, int nargs,
struct type *value_type,
CORE_ADDR *real_pc, CORE_ADDR *bp_addr)
{
int bplen;
gdb_assert (gdbarch_frame_align_p (gdbarch));
sp = gdbarch_frame_align (gdbarch, sp);
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;
}
else
{
(*bp_addr) = sp;
gdbarch_breakpoint_from_pc (gdbarch, bp_addr, &bplen);
sp = gdbarch_frame_align (gdbarch, sp + bplen);
}
(*real_pc) = funaddr;
return sp;
}
static CORE_ADDR
push_dummy_code (struct gdbarch *gdbarch,
CORE_ADDR sp, CORE_ADDR funaddr, int using_gcc,
struct value **args, int nargs,
struct type *value_type,
CORE_ADDR *real_pc, CORE_ADDR *bp_addr)
{
if (gdbarch_push_dummy_code_p (gdbarch))
return gdbarch_push_dummy_code (gdbarch, sp, funaddr, using_gcc,
args, nargs, value_type, real_pc, bp_addr);
else
return generic_push_dummy_code (gdbarch, sp, funaddr, using_gcc,
args, nargs, value_type, real_pc, bp_addr);
}
static int timer_fired;
static int hand_call_function_timeout;
int
set_hand_function_call_timeout (int newval)
{
int oldvalue = hand_call_function_timeout;
hand_call_function_timeout = newval;
return oldvalue;
}
int
hand_function_call_timeout_p ()
{
return timer_fired;
}
static void
handle_alarm_while_calling (int signo)
{
timer_fired = 1;
target_stop ();
}
struct value *
hand_function_call (struct value *function, struct type *expect_type,
int nargs, struct value **args, int restore_frame)
{
CORE_ADDR sp;
CORE_ADDR dummy_addr;
struct type *values_type;
struct type *orig_return_type = NULL;
unsigned char struct_return;
CORE_ADDR struct_addr = 0;
struct regcache *retbuf;
struct cleanup *retbuf_cleanup;
struct inferior_status *inf_status;
struct cleanup *inf_status_cleanup;
CORE_ADDR funaddr;
int using_gcc;
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;
if (!target_has_execution)
noprocess ();
funaddr = find_function_addr (function, &orig_return_type);
values_type = check_typedef (orig_return_type);
if ((values_type == NULL) || (TYPE_CODE (values_type) == TYPE_CODE_ERROR))
if (expect_type != NULL)
{
orig_return_type = expect_type;
values_type = check_typedef (expect_type);
}
if ((values_type == NULL) || (TYPE_CODE (values_type) == TYPE_CODE_ERROR))
{
char *sym_name;
find_pc_partial_function (funaddr, &sym_name, NULL, NULL);
error ("Unable to call function \"%s\" at 0x%s: "
"no return type information available.\n"
"To call this function anyway, you can cast the "
"return type explicitly (e.g. 'print (float) fabs (3.0)')",
sym_name ? sym_name : "<unknown>",
paddr_nz (funaddr));
}
retbuf = regcache_xmalloc (current_gdbarch);
retbuf_cleanup = make_cleanup_regcache_xfree (retbuf);
#if defined (NM_NEXTSTEP)
macosx_setup_registers_before_hand_call ();
if (inferior_function_calls_disabled_p)
error ("Function calls from gdb not possible when debugging translated processes.");
#endif
inf_status = save_inferior_status (1);
inf_status_cleanup = make_cleanup_restore_inferior_status (inf_status);
caller_regcache = frame_save_as_regcache (get_current_frame ());
caller_regcache_cleanup = make_cleanup_regcache_xfree (caller_regcache);
{
CORE_ADDR old_sp = read_sp ();
if (gdbarch_frame_align_p (current_gdbarch))
{
sp = gdbarch_frame_align (current_gdbarch, old_sp);
if (INNER_THAN (1, 2))
sp -= gdbarch_frame_red_zone_size (current_gdbarch);
else
sp += gdbarch_frame_red_zone_size (current_gdbarch);
gdb_assert (sp == gdbarch_frame_align (current_gdbarch, sp));
if (sp == old_sp)
{
if (INNER_THAN (1, 2))
sp = gdbarch_frame_align (current_gdbarch, old_sp - 1);
else
sp = gdbarch_frame_align (current_gdbarch, old_sp + 1);
}
gdb_assert ((INNER_THAN (1, 2) && sp <= old_sp)
|| (INNER_THAN (2, 1) && sp >= old_sp));
}
else
sp = old_sp;
}
{
struct block *b = block_for_pc (funaddr);
using_gcc = (b == NULL ? 2 : BLOCK_GCC_COMPILED (b));
}
struct_return = using_struct_return (values_type, using_gcc);
switch (CALL_DUMMY_LOCATION)
{
case ON_STACK:
if (INNER_THAN (1, 2))
{
sp = push_dummy_code (current_gdbarch, sp, funaddr,
using_gcc, args, nargs, values_type,
&real_pc, &bp_addr);
dummy_addr = sp;
}
else
{
dummy_addr = sp;
sp = push_dummy_code (current_gdbarch, sp, funaddr,
using_gcc, args, nargs, values_type,
&real_pc, &bp_addr);
}
break;
case AT_ENTRY_POINT:
real_pc = funaddr;
dummy_addr = entry_point_address ();
dummy_addr = gdbarch_convert_from_func_ptr_addr (current_gdbarch,
dummy_addr,
¤t_target);
bp_addr = dummy_addr;
break;
case AT_SYMBOL:
{
struct minimal_symbol *sym;
sym = lookup_minimal_symbol ("__CALL_DUMMY_ADDRESS", NULL, NULL);
real_pc = funaddr;
if (sym)
dummy_addr = SYMBOL_VALUE_ADDRESS (sym);
else
dummy_addr = entry_point_address ();
dummy_addr = gdbarch_convert_from_func_ptr_addr (current_gdbarch,
dummy_addr,
¤t_target);
bp_addr = dummy_addr;
break;
}
default:
internal_error (__FILE__, __LINE__, _("bad switch"));
}
if (nargs < TYPE_NFIELDS (ftype))
error (_("too few arguments in function call"));
{
int i;
for (i = nargs - 1; i >= 0; i--)
{
int prototyped;
struct type *param_type;
if (TYPE_CODE (ftype) == TYPE_CODE_METHOD)
prototyped = 1;
else if (i < TYPE_NFIELDS (ftype))
prototyped = TYPE_PROTOTYPED (ftype);
else
prototyped = 0;
if (i < TYPE_NFIELDS (ftype))
param_type = TYPE_FIELD_TYPE (ftype, i);
else
param_type = NULL;
args[i] = value_arg_coerce (args[i], param_type, prototyped);
if (using_gcc == 0)
{
if (param_type != NULL && TYPE_CODE (ftype) != TYPE_CODE_METHOD)
{
if (TYPE_CODE (param_type) == TYPE_CODE_PTR)
if (TYPE_CODE (TYPE_TARGET_TYPE (param_type)) == TYPE_CODE_FUNC)
if (VALUE_LVAL (args[i]) == not_lval)
{
char *arg_name;
if (find_pc_partial_function ((CORE_ADDR) value_contents (args[i])[0], &arg_name, NULL, NULL))
error (_("\
You cannot use function <%s> as argument. \n\
You must use a pointer to function type variable. Command ignored."), arg_name);
}
}
}
}
}
if (DEPRECATED_REG_STRUCT_HAS_ADDR_P ())
{
int i;
for (i = nargs - 1; i >= 0; i--)
{
struct type *arg_type = check_typedef (value_type (args[i]));
if ((TYPE_CODE (arg_type) == TYPE_CODE_STRUCT
|| TYPE_CODE (arg_type) == TYPE_CODE_UNION
|| TYPE_CODE (arg_type) == TYPE_CODE_ARRAY
|| TYPE_CODE (arg_type) == TYPE_CODE_STRING
|| TYPE_CODE (arg_type) == TYPE_CODE_BITSTRING
|| TYPE_CODE (arg_type) == TYPE_CODE_SET
|| (TYPE_CODE (arg_type) == TYPE_CODE_FLT
&& TYPE_LENGTH (arg_type) > 8)
)
&& DEPRECATED_REG_STRUCT_HAS_ADDR (using_gcc, arg_type))
{
CORE_ADDR addr;
int len;
int aligned_len;
arg_type = check_typedef (value_enclosing_type (args[i]));
len = TYPE_LENGTH (arg_type);
aligned_len = len;
if (INNER_THAN (1, 2))
{
sp -= aligned_len;
addr = sp;
}
else
{
addr = sp;
sp += aligned_len;
}
write_memory (addr, value_contents_all (args[i]), len);
args[i] = value_from_pointer (lookup_pointer_type (arg_type),
addr);
}
}
}
if (struct_return)
{
int len = TYPE_LENGTH (values_type);
if (INNER_THAN (1, 2))
{
sp -= len;
if (gdbarch_frame_align_p (current_gdbarch))
sp = gdbarch_frame_align (current_gdbarch, sp);
struct_addr = sp;
}
else
{
if (gdbarch_frame_align_p (current_gdbarch))
sp = gdbarch_frame_align (current_gdbarch, sp);
struct_addr = sp;
sp += len;
if (gdbarch_frame_align_p (current_gdbarch))
sp = gdbarch_frame_align (current_gdbarch, sp);
}
}
if (gdbarch_push_dummy_call_p (current_gdbarch))
sp = gdbarch_push_dummy_call (current_gdbarch, function, current_regcache,
bp_addr, nargs, args, sp, struct_return,
struct_addr);
else if (DEPRECATED_PUSH_ARGUMENTS_P ())
sp = DEPRECATED_PUSH_ARGUMENTS (nargs, args, sp, struct_return,
struct_addr);
else
error (_("This target does not support function calls"));
dummy_id = frame_id_build (sp, bp_addr);
{
struct breakpoint *bpt;
struct symtab_and_line sal;
init_sal (&sal);
sal.pc = bp_addr;
sal.section = find_pc_overlay (sal.pc);
bpt = set_momentary_breakpoint (sal, dummy_id, bp_call_dummy);
bpt->disposition = disp_del;
}
dummy_frame_push (caller_regcache, &dummy_id);
discard_cleanups (caller_regcache_cleanup);
clear_proceed_status ();
{
struct cleanup *old_cleanups = make_cleanup (null_cleanup, 0);
int saved_async = 0;
make_cleanup (breakpoint_auto_delete_contents, &stop_bpstat);
disable_watchpoints_before_interactive_call_start ();
begin_inferior_call_checkpoints ();
proceed_to_finish = 1;
if (hand_call_function_hook != NULL)
hand_call_function_hook ();
if (target_can_async_p ())
saved_async = target_async_mask (0);
hand_call_ptid = inferior_ptid;
make_cleanup (do_reset_hand_call_ptid, NULL);
if (hand_call_function_timeout != 0)
{
struct itimerval itval;
struct gdb_exception e;
itval.it_interval.tv_sec = 0;
itval.it_interval.tv_usec = 0;
itval.it_value.tv_sec = hand_call_function_timeout/1000000;
itval.it_value.tv_usec = hand_call_function_timeout% 1000000;
timer_fired = 0;
signal (SIGALRM, handle_alarm_while_calling);
setitimer (ITIMER_REAL, &itval, NULL);
TRY_CATCH (e, RETURN_MASK_ERROR)
{
proceed (real_pc, TARGET_SIGNAL_0, 0);
}
itval.it_value.tv_sec = 0;
itval.it_value.tv_usec = 0;
setitimer (ITIMER_REAL, &itval, NULL);
signal (SIGALRM, SIG_DFL);
if (e.reason != NO_ERROR)
throw_exception (e);
}
else
{
timer_fired = 0;
proceed (real_pc, TARGET_SIGNAL_0, 0);
}
hand_call_ptid = minus_one_ptid;
if (saved_async)
target_async_mask (saved_async);
end_inferior_call_checkpoints ();
enable_watchpoints_after_interactive_call_stop ();
discard_cleanups (old_cleanups);
}
if (timer_fired)
{
frame_pop (get_current_frame ());
error ("User called function timer expired. Aborting call.");
}
if (stopped_by_random_signal || !stop_stack_dummy)
{
const char *name = NULL;
{
struct symbol *symbol = find_pc_function (funaddr);
if (symbol)
name = SYMBOL_PRINT_NAME (symbol);
else
{
struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (funaddr);
if (msymbol)
name = SYMBOL_PRINT_NAME (msymbol);
}
if (name == NULL)
{
char *tmp = xstrprintf ("at %s", hex_string (funaddr));
char *a = alloca (strlen (tmp) + 1);
strcpy (a, tmp);
xfree (tmp);
name = a;
}
}
if (stopped_by_random_signal)
{
if (unwind_on_signal_p)
{
frame_pop (get_current_frame ());
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."),
name);
}
else
{
discard_cleanups (inf_status_cleanup);
discard_inferior_status (inf_status);
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."),
name);
}
}
if (!stop_stack_dummy)
{
discard_cleanups (inf_status_cleanup);
discard_inferior_status (inf_status);
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);
}
internal_error (__FILE__, __LINE__, _("... should not be here"));
}
regcache_cpy_no_passthrough (retbuf, stop_registers);
do_cleanups (inf_status_cleanup);
inlined_subroutine_restore_after_dummy_call ();
{
struct value *retval;
if (TYPE_CODE (values_type) == TYPE_CODE_VOID)
retval = allocate_value (values_type);
else if (struct_return)
retval = value_at (values_type, struct_addr);
else
{
retval = allocate_value (values_type);
gdb_assert (gdbarch_return_value (current_gdbarch, values_type,
NULL, NULL, NULL)
== RETURN_VALUE_REGISTER_CONVENTION);
gdbarch_return_value (current_gdbarch, values_type, retbuf,
value_contents_raw (retval) ,
NULL );
}
do_cleanups (retbuf_cleanup);
if (retval != NULL)
deprecated_set_value_type (retval, orig_return_type);
return retval;
}
}
struct value *
call_function_by_hand (struct value *function, int nargs, struct value **args)
{
return hand_function_call (function, NULL, nargs, args, 1);
}
struct value *
call_function_by_hand_expecting_type (struct value *function, struct type *expect_type,
int nargs, struct value **args, int restore_frame)
{
return hand_function_call (function, expect_type, nargs, args, restore_frame);
}
void _initialize_infcall (void);
void
_initialize_infcall (void)
{
add_setshow_boolean_cmd ("coerce-float-to-double", class_obscure,
&coerce_float_to_double_p, _("\
Set coercion of floats to doubles when calling functions."), _("\
Show coercion of floats to doubles when calling functions"), _("\
Variables of type float should generally be converted to doubles before\n\
calling an unprototyped function, and left alone when calling a prototyped\n\
function. However, some older debug info formats do not provide enough\n\
information to determine that a function is prototyped. If this flag is\n\
set, GDB will perform the conversion for a function it considers\n\
unprototyped.\n\
The default is to perform the conversion.\n"),
NULL,
show_coerce_float_to_double_p,
&setlist, &showlist);
add_setshow_boolean_cmd ("unwindonsignal", no_class,
&unwind_on_signal_p, _("\
Set unwinding of stack if a signal is received while in a call dummy."), _("\
Show unwinding of stack if a signal is received while in a call dummy."), _("\
The unwindonsignal lets the user determine what gdb should do if a signal\n\
is received while in a function called from gdb (call dummy). If set, gdb\n\
unwinds the stack and restore the context to what as it was before the call.\n\
The default is to stop in the frame where the signal was received."),
NULL,
show_unwind_on_signal_p,
&setlist, &showlist);
#if defined (NM_NEXTSTEP)
add_setshow_boolean_cmd ("disable-inferior-function-calls", no_class,
&inferior_function_calls_disabled_p, _("\
Set disabling of gdb from running calls in the debugee's context"), _("\
Show disabling of gdb from running calls in the debugee's context"), _("\
The disable-inferior-function-calls setting lets the user prevent gdb from\n\
executing functions in the debugee program's context. Many gdb commands\n\
can result in functions being run in the target program, e.g. malloc or objc\n\
class lookup functions, when you may not expect. It is rare that people need\n\
to disable these inferior function calls."),
NULL,
NULL,
&setlist, &showlist);
#endif
add_setshow_zinteger_cmd ("target-function-call-timeout", class_obscure,
&hand_call_function_timeout, "\
Set a timeout for gdb issued function calls in the target program.", " \
Show the timeout for gdb issued function calls in the target program.", " \
The hand-function-call-timeout sets a watchdog timer on calls made by gdb in\n \
the address space of the program being debugged. The value is in microseconds.\n\
A value of zero disables the timer.",
NULL,
NULL,
&setlist, &showlist);
}