/* Target-struct-independent code to start (run) and stop an inferior process.
- Copyright 1986, 1987, 1988, 1989, 1991, 1992, 1993, 1994
+ Copyright 1986, 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996
Free Software Foundation, Inc.
This file is part of GDB.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "defs.h"
-#include <string.h>
+#include "gdb_string.h"
#include <ctype.h>
#include "symtab.h"
#include "frame.h"
#include "gdbcore.h"
#include "gdbcmd.h"
#include "target.h"
-#include "thread.h"
+#include "gdbthread.h"
#include "annotate.h"
#include <signal.h>
-/* unistd.h is needed to #define X_OK */
-#ifdef USG
-#include <unistd.h>
-#else
-#include <sys/file.h>
-#endif
-
/* Prototypes for local functions */
static void signals_info PARAMS ((char *, int));
static int hook_stop_stub PARAMS ((char *));
+static void delete_breakpoint_current_contents PARAMS ((PTR));
+
/* GET_LONGJMP_TARGET returns the PC at which longjmp() will resume the
program. It needs to examine the jmp_buf argument and extract the PC
from it. The return value is non-zero on success, zero otherwise. */
#define SKIP_TRAMPOLINE_CODE(pc) 0
#endif
+/* Dynamic function trampolines are similar to solib trampolines in that they
+ are between the caller and the callee. The difference is that when you
+ enter a dynamic trampoline, you can't determine the callee's address. Some
+ (usually complex) code needs to run in the dynamic trampoline to figure out
+ the callee's address. This macro is usually called twice. First, when we
+ enter the trampoline (looks like a normal function call at that point). It
+ should return the PC of a point within the trampoline where the callee's
+ address is known. Second, when we hit the breakpoint, this routine returns
+ the callee's address. At that point, things proceed as per a step resume
+ breakpoint. */
+
+#ifndef DYNAMIC_TRAMPOLINE_NEXTPC
+#define DYNAMIC_TRAMPOLINE_NEXTPC(pc) 0
+#endif
+
/* For SVR4 shared libraries, each call goes through a small piece of
trampoline code in the ".plt" section. IN_SOLIB_CALL_TRAMPOLINE evaluates
to nonzero if we are current stopped in one of these. */
#define IN_SOLIB_RETURN_TRAMPOLINE(pc,name) 0
#endif
+/* On MIPS16, a function that returns a floating point value may call
+ a library helper function to copy the return value to a floating point
+ register. The IGNORE_HELPER_CALL macro returns non-zero if we
+ should ignore (i.e. step over) this function call. */
+#ifndef IGNORE_HELPER_CALL
+#define IGNORE_HELPER_CALL(pc) 0
+#endif
+
/* On some systems, the PC may be left pointing at an instruction that won't
actually be executed. This is usually indicated by a bit in the PSW. If
we find ourselves in such a state, then we step the target beyond the
static int trap_expected;
+#ifdef SOLIB_ADD
+/* Nonzero if we want to give control to the user when we're notified
+ of shared library events by the dynamic linker. */
+static int stop_on_solib_events;
+#endif
+
#ifdef HP_OS_BUG
/* Nonzero if the next time we try to continue the inferior, it will
step one instruction and generate a spurious trace trap.
static int stop_print_frame;
-#ifdef NO_SINGLE_STEP
-extern int one_stepped; /* From machine dependent code */
-extern void single_step (); /* Same. */
-#endif /* NO_SINGLE_STEP */
-
\f
/* Things to clean up if we QUIT out of resume (). */
/* ARGSUSED */
step one instruction before inserting breakpoints
so that we do not stop right away. */
- if (breakpoint_here_p (read_pc ()))
+ if (read_pc () == stop_pc && breakpoint_here_p (read_pc ()))
oneproc = 1;
#ifdef STEP_SKIPS_DELAY
struct cleanup *old_cleanups;
struct target_waitstatus w;
int another_trap;
- int random_signal;
+ int random_signal = 0;
CORE_ADDR stop_func_start;
CORE_ADDR stop_func_end;
char *stop_func_name;
- CORE_ADDR prologue_pc = 0, tmp;
+#if 0
+ CORE_ADDR prologue_pc = 0;
+#endif
+ CORE_ADDR tmp;
struct symtab_and_line sal;
int remove_breakpoints_on_following_step = 0;
int current_line;
else
pid = target_wait (-1, &w);
+ /* Gross.
+
+ We goto this label from elsewhere in wait_for_inferior when we want
+ to continue the main loop without calling "wait" and trashing the
+ waitstatus contained in W. */
have_waited:
flush_cached_frames ();
(unsigned int)w.value.integer);
else
printf_filtered ("\nProgram exited normally.\n");
+
+ /* Record the exit code in the convenience variable $_exitcode, so
+ that the user can inspect this again later. */
+ set_internalvar (lookup_internalvar ("_exitcode"),
+ value_from_longest (builtin_type_int,
+ (LONGEST) w.value.integer));
gdb_flush (gdb_stdout);
target_mourn_inferior ();
#ifdef NO_SINGLE_STEP
another thread. If so, then step that thread past the breakpoint,
and continue it. */
- if (stop_signal == TARGET_SIGNAL_TRAP
- && breakpoints_inserted
- && breakpoint_here_p (stop_pc - DECR_PC_AFTER_BREAK))
+ if (stop_signal == TARGET_SIGNAL_TRAP)
{
- random_signal = 0;
- if (!breakpoint_thread_match (stop_pc - DECR_PC_AFTER_BREAK, pid))
- {
- /* Saw a breakpoint, but it was hit by the wrong thread. Just continue. */
- write_pc_pid (stop_pc - DECR_PC_AFTER_BREAK, pid);
+#ifdef NO_SINGLE_STEP
+ if (one_stepped)
+ random_signal = 0;
+ else
+#endif
+ if (breakpoints_inserted
+ && breakpoint_here_p (stop_pc - DECR_PC_AFTER_BREAK))
+ {
+ random_signal = 0;
+ if (!breakpoint_thread_match (stop_pc - DECR_PC_AFTER_BREAK, pid))
+ {
+ /* Saw a breakpoint, but it was hit by the wrong thread. Just continue. */
+ write_pc_pid (stop_pc - DECR_PC_AFTER_BREAK, pid);
- remove_breakpoints ();
- target_resume (pid, 1, TARGET_SIGNAL_0); /* Single step */
- /* FIXME: What if a signal arrives instead of the single-step
- happening? */
+ remove_breakpoints ();
+ target_resume (pid, 1, TARGET_SIGNAL_0); /* Single step */
+ /* FIXME: What if a signal arrives instead of the single-step
+ happening? */
- if (target_wait_hook)
- target_wait_hook (pid, &w);
- else
- target_wait (pid, &w);
- insert_breakpoints ();
+ if (target_wait_hook)
+ target_wait_hook (pid, &w);
+ else
+ target_wait (pid, &w);
+ insert_breakpoints ();
- /* We need to restart all the threads now. */
- target_resume (-1, 0, TARGET_SIGNAL_0);
- continue;
- }
+ /* We need to restart all the threads now. */
+ target_resume (-1, 0, TARGET_SIGNAL_0);
+ continue;
+ }
+ }
}
else
random_signal = 1;
/* It's a SIGTRAP or a signal we're interested in. Switch threads,
and fall into the rest of wait_for_inferior(). */
+ /* Save infrun state for the old thread. */
+ save_infrun_state (inferior_pid, prev_pc,
+ prev_func_start, prev_func_name,
+ trap_expected, step_resume_breakpoint,
+ through_sigtramp_breakpoint,
+ step_range_start, step_range_end,
+ step_frame_address, handling_longjmp,
+ another_trap);
+
inferior_pid = pid;
+
+ /* Load infrun state for the new thread. */
+ load_infrun_state (inferior_pid, &prev_pc,
+ &prev_func_start, &prev_func_name,
+ &trap_expected, &step_resume_breakpoint,
+ &through_sigtramp_breakpoint,
+ &step_range_start, &step_range_end,
+ &step_frame_address, &handling_longjmp,
+ &another_trap);
printf_filtered ("[Switching to %s]\n", target_pid_to_str (pid));
flush_cached_frames ();
- trap_expected = 0;
- if (step_resume_breakpoint)
- {
- delete_breakpoint (step_resume_breakpoint);
- step_resume_breakpoint = NULL;
- }
-
- /* Not sure whether we need to blow this away too,
- but probably it is like the step-resume
- breakpoint. */
- if (through_sigtramp_breakpoint)
- {
- delete_breakpoint (through_sigtramp_breakpoint);
- through_sigtramp_breakpoint = NULL;
- }
- prev_pc = 0;
- prev_func_name = NULL;
- step_range_start = 0;
- step_range_end = 0;
- step_frame_address = 0;
- handling_longjmp = 0;
- another_trap = 0;
}
#ifdef NO_SINGLE_STEP
if (INSTRUCTION_NULLIFIED)
{
- resume (1, 0);
- continue;
+ struct target_waitstatus tmpstatus;
+
+ registers_changed ();
+ target_resume (pid, 1, TARGET_SIGNAL_0);
+
+ /* We may have received a signal that we want to pass to
+ the inferior; therefore, we must not clobber the waitstatus
+ in W. So we call wait ourselves, then continue the loop
+ at the "have_waited" label. */
+ if (target_wait_hook)
+ target_wait_hook (pid, &tmpstatus);
+ else
+ target_wait (pid, &tmpstatus);
+
+
+ goto have_waited;
}
#ifdef HAVE_STEPPABLE_WATCHPOINT
#endif
stop_func_start = 0;
+ stop_func_end = 0;
stop_func_name = 0;
/* Don't care about return value; stop_func_start and stop_func_name
will both be 0 if it doesn't work. */
/* See if there is a breakpoint at the current PC. */
stop_bpstat = bpstat_stop_status
(&stop_pc,
-#if DECR_PC_AFTER_BREAK
+ (DECR_PC_AFTER_BREAK ?
/* Notice the case of stepping through a jump
that lands just after a breakpoint.
Don't confuse that with hitting the breakpoint.
and 2) the pc before the last insn does not match
the address of the breakpoint before the current pc. */
(prev_pc != stop_pc - DECR_PC_AFTER_BREAK
- && CURRENTLY_STEPPING ())
-#else /* DECR_PC_AFTER_BREAK zero */
- 0
-#endif /* DECR_PC_AFTER_BREAK zero */
+ && CURRENTLY_STEPPING ()) :
+ 0)
);
/* Following in case break condition called a
function. */
another_trap = 1;
break;
+ case BPSTAT_WHAT_CHECK_SHLIBS:
+#ifdef SOLIB_ADD
+ {
+ extern int auto_solib_add;
+
+ /* Remove breakpoints, we eventually want to step over the
+ shlib event breakpoint, and SOLIB_ADD might adjust
+ breakpoint addresses via breakpoint_re_set. */
+ if (breakpoints_inserted)
+ remove_breakpoints ();
+ breakpoints_inserted = 0;
+
+ /* Check for any newly added shared libraries if we're
+ supposed to be adding them automatically. */
+ if (auto_solib_add)
+ {
+ /* Switch terminal for any messages produced by
+ breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+ SOLIB_ADD (NULL, 0, NULL);
+ target_terminal_inferior ();
+ }
+
+ /* Try to reenable shared library breakpoints, additional
+ code segments in shared libraries might be mapped in now. */
+ re_enable_breakpoints_in_shlibs ();
+
+ /* If requested, stop when the dynamic linker notifies
+ gdb of events. This allows the user to get control
+ and place breakpoints in initializer routines for
+ dynamically loaded objects (among other things). */
+ if (stop_on_solib_events)
+ {
+ stop_print_frame = 0;
+ goto stop_stepping;
+ }
+ else
+ {
+ /* We want to step over this breakpoint, then keep going. */
+ another_trap = 1;
+ break;
+ }
+ }
+#endif
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */
/* If stepping through a line, keep going if still within it. */
if (stop_pc >= step_range_start
&& stop_pc < step_range_end
+#if 0
+/* I haven't a clue what might trigger this clause, and it seems wrong anyway,
+ so I've disabled it until someone complains. -Stu 10/24/95 */
+
/* The step range might include the start of the
function, so if we are at the start of the
step range and either the stack or frame pointers
&& !(stop_pc == step_range_start
&& FRAME_FP (get_current_frame ())
&& (read_sp () INNER_THAN step_sp
- || FRAME_FP (get_current_frame ()) != step_frame_address)))
+ || FRAME_FP (get_current_frame ()) != step_frame_address))
+#endif
+)
{
/* We might be doing a BPSTAT_WHAT_SINGLE and getting a signal.
So definately need to check for sigtramp here. */
/* Did we just take a signal? */
if (IN_SIGTRAMP (stop_pc, stop_func_name)
- && !IN_SIGTRAMP (prev_pc, prev_func_name))
+ && !IN_SIGTRAMP (prev_pc, prev_func_name)
+ && read_sp () INNER_THAN step_sp)
{
/* We've just taken a signal; go until we are back to
the point where we took it and one more. */
{
struct symtab_and_line sr_sal;
+ INIT_SAL (&sr_sal); /* initialize to zeroes */
sr_sal.pc = prev_pc;
- sr_sal.symtab = NULL;
- sr_sal.line = 0;
/* We could probably be setting the frame to
step_frame_address; I don't think anyone thought to try it. */
step_resume_breakpoint =
goto keep_going;
}
-#if 1
+#if 0
+ /* I disabled this test because it was too complicated and slow. The
+ SKIP_PROLOGUE was especially slow, because it caused unnecessary
+ prologue examination on various architectures. The code in the #else
+ clause has been tested on the Sparc, Mips, PA, and Power
+ architectures, so it's pretty likely to be correct. -Stu 10/24/95 */
+
/* See if we left the step range due to a subroutine call that
we should proceed to the end of. */
SKIP_PROLOGUE (prologue_pc);
}
- if ((/* Might be a non-recursive call. If the symbols are missing
- enough that stop_func_start == prev_func_start even though
- they are really two functions, we will treat some calls as
- jumps. */
- stop_func_start != prev_func_start
-
- /* Might be a recursive call if either we have a prologue
- or the call instruction itself saves the PC on the stack. */
- || prologue_pc != stop_func_start
- || read_sp () != step_sp)
+ if (!(step_sp INNER_THAN read_sp ()) /* don't mistake (sig)return as a call */
+ && (/* Might be a non-recursive call. If the symbols are missing
+ enough that stop_func_start == prev_func_start even though
+ they are really two functions, we will treat some calls as
+ jumps. */
+ stop_func_start != prev_func_start
+
+ /* Might be a recursive call if either we have a prologue
+ or the call instruction itself saves the PC on the stack. */
+ || prologue_pc != stop_func_start
+ || read_sp () != step_sp)
&& (/* PC is completely out of bounds of any known objfiles. Treat
like a subroutine call. */
! stop_func_start
handling_longjmp stuff is working. */
))
#else
-/* This is experimental code which greatly simplifies the subroutine call
- test. I've actually tested on the Alpha, and it works great. -Stu */
-
- if (in_prologue (stop_pc, NULL)
- || (prev_func_start != 0
- && stop_func_start == 0))
+ /* This test is a much more streamlined, (but hopefully correct)
+ replacement for the code above. It's been tested on the Sparc,
+ Mips, PA, and Power architectures with good results. */
+
+ if (stop_pc == stop_func_start /* Quick test */
+ || in_prologue (stop_pc, stop_func_start)
+ || IN_SOLIB_CALL_TRAMPOLINE (stop_pc, stop_func_name)
+ || stop_func_start == 0)
#endif
+
{
/* It's a subroutine call. */
break;
}
- if (step_over_calls > 0)
+ if (step_over_calls > 0 || IGNORE_HELPER_CALL (stop_pc))
/* We're doing a "next". */
goto step_over_function;
tmp = SKIP_TRAMPOLINE_CODE (stop_pc);
if (tmp != 0)
stop_func_start = tmp;
+ else
+ {
+ tmp = DYNAMIC_TRAMPOLINE_NEXTPC (stop_pc);
+ if (tmp)
+ {
+ struct symtab_and_line xxx;
+ /* Why isn't this s_a_l called "sr_sal", like all of the
+ other s_a_l's where this code is duplicated? */
+ INIT_SAL (&xxx); /* initialize to zeroes */
+ xxx.pc = tmp;
+ step_resume_breakpoint =
+ set_momentary_breakpoint (xxx, NULL, bp_step_resume);
+ insert_breakpoints ();
+ goto keep_going;
+ }
+ }
/* If we have line number information for the function we
are thinking of stepping into, step into it.
{
/* Set a special breakpoint after the return */
struct symtab_and_line sr_sal;
+
+ INIT_SAL (&sr_sal); /* initialize to zeroes */
sr_sal.pc =
ADDR_BITS_REMOVE
(SAVED_PC_AFTER_CALL (get_current_frame ()));
- sr_sal.symtab = NULL;
- sr_sal.line = 0;
step_resume_breakpoint =
set_momentary_breakpoint (sr_sal, get_current_frame (),
bp_step_resume);
{
struct symtab_and_line sr_sal;
+ INIT_SAL (&sr_sal); /* initialize to zeroes */
sr_sal.pc = stop_func_start;
- sr_sal.symtab = NULL;
- sr_sal.line = 0;
/* Do not specify what the fp should be when we stop
since on some machines the prologue
is where the new fp value is established. */
/* And put the step-breakpoint there and go until there. */
struct symtab_and_line sr_sal;
+ INIT_SAL (&sr_sal); /* initialize to zeroes */
sr_sal.pc = tmp;
- sr_sal.symtab = NULL;
- sr_sal.line = 0;
/* Do not specify what the fp should be when we stop
since on some machines the prologue
is where the new fp value is established. */
}
step_range_start = sal.pc;
step_range_end = sal.end;
+ step_frame_address = FRAME_FP (get_current_frame ());
+ current_line = sal.line;
+ current_symtab = sal.symtab;
goto keep_going;
check_sigtramp2:
if (trap_expected
&& IN_SIGTRAMP (stop_pc, stop_func_name)
- && !IN_SIGTRAMP (prev_pc, prev_func_name))
+ && !IN_SIGTRAMP (prev_pc, prev_func_name)
+ && read_sp () INNER_THAN step_sp)
{
/* What has happened here is that we have just stepped the inferior
with a signal (because it is a signal which shouldn't make
it says "exceedingly difficult"). */
struct symtab_and_line sr_sal;
+ INIT_SAL (&sr_sal); /* initialize to zeroes */
sr_sal.pc = prev_pc;
- sr_sal.symtab = NULL;
- sr_sal.line = 0;
/* We perhaps could set the frame if we kept track of what
the frame corresponding to prev_pc was. But we don't,
so don't. */
target_terminal_ours ();
+ if (stop_bpstat
+ && stop_bpstat->breakpoint_at
+ && stop_bpstat->breakpoint_at->type == bp_shlib_event)
+ printf_filtered ("Stopped due to shared library event\n");
+
/* Look up the hook_stop and run it if it exists. */
if (stop_command->hook)
signal_print[TARGET_SIGNAL_POLL] = 0;
signal_stop[TARGET_SIGNAL_URG] = 0;
signal_print[TARGET_SIGNAL_URG] = 0;
+
+#ifdef SOLIB_ADD
+ add_show_from_set
+ (add_set_cmd ("stop-on-solib-events", class_support, var_zinteger,
+ (char *) &stop_on_solib_events,
+ "Set stopping for shared library events.\n\
+If nonzero, gdb will give control to the user when the dynamic linker\n\
+notifies gdb of shared library events. The most common event of interest\n\
+to the user would be loading/unloading of a new library.\n",
+ &setlist),
+ &showlist);
+#endif
}