Implement 'catch syscall' for gdbserver
authorJosh Stone <jistone@redhat.com>
Tue, 12 Jan 2016 20:27:27 +0000 (12:27 -0800)
committerJosh Stone <jistone@redhat.com>
Tue, 12 Jan 2016 20:27:27 +0000 (12:27 -0800)
This adds a new QCatchSyscalls packet to enable 'catch syscall', and new
stop reasons "syscall_entry" and "syscall_return" for those events.  It
is currently only supported on Linux x86 and x86_64.

gdb/ChangeLog:

2016-01-12  Josh Stone  <jistone@redhat.com>
    Philippe Waroquiers  <philippe.waroquiers@skynet.be>

* NEWS (Changes since GDB 7.10): Mention QCatchSyscalls and the
syscall_entry and syscall_return stop reasons.  Mention GDB
support for remote catch syscall.
* remote.c (PACKET_QCatchSyscalls): New enum.
(remote_set_syscall_catchpoint): New function.
(remote_protocol_features): New element for QCatchSyscalls.
(remote_parse_stop_reply): Parse syscall_entry/return stops.
(init_remote_ops): Install remote_set_syscall_catchpoint.
(_initialize_remote): Config QCatchSyscalls.
* linux-nat.h (struct lwp_info) <syscall_state>: Comment typo.

gdb/doc/ChangeLog:

2016-01-12  Josh Stone  <jistone@redhat.com>
    Philippe Waroquiers  <philippe.waroquiers@skynet.be>

* gdb.texinfo (Remote Configuration): List the QCatchSyscalls packet.
(Stop Reply Packets): List the syscall entry and return stop reasons.
(General Query Packets): Describe QCatchSyscalls, and add it to the
table and the detailed list of stub features.

gdb/gdbserver/ChangeLog:

2016-01-12  Josh Stone  <jistone@redhat.com>
    Philippe Waroquiers  <philippe.waroquiers@skynet.be>

* inferiors.h: Include "gdb_vecs.h".
(struct process_info): Add syscalls_to_catch.
* inferiors.c (remove_process): Free syscalls_to_catch.
* remote-utils.c (prepare_resume_reply): Report syscall_entry and
syscall_return stops.
* server.h (UNKNOWN_SYSCALL, ANY_SYSCALL): Define.
* server.c (handle_general_set): Handle QCatchSyscalls.
(handle_query): Report support for QCatchSyscalls.
* target.h (struct target_ops): Add supports_catch_syscall.
(target_supports_catch_syscall): New macro.
* linux-low.h (struct linux_target_ops): Add get_syscall_trapinfo.
(struct lwp_info): Add syscall_state.
* linux-low.c (handle_extended_wait): Mark syscall_state as an entry.
Maintain syscall_state and syscalls_to_catch across exec.
(get_syscall_trapinfo): New function, proxy to the_low_target.
(linux_low_ptrace_options): Enable PTRACE_O_TRACESYSGOOD.
(linux_low_filter_event): Toggle syscall_state entry/return for
syscall traps, and set it ignored for all others.
(gdb_catching_syscalls_p): New function.
(gdb_catch_this_syscall_p): New function.
(linux_wait_1): Handle SYSCALL_SIGTRAP.
(linux_resume_one_lwp_throw): Add PTRACE_SYSCALL possibility.
(linux_supports_catch_syscall): New function.
(linux_target_ops): Install it.
* linux-x86-low.c (x86_get_syscall_trapinfo): New function.
(the_low_target): Install it.

gdb/testsuite/ChangeLog:

2016-01-12  Josh Stone  <jistone@redhat.com>
    Philippe Waroquiers  <philippe.waroquiers@skynet.be>

* gdb.base/catch-syscall.c (do_execve): New variable.
(main): Conditionally trigger an execve.
* gdb.base/catch-syscall.exp: Enable testing for remote targets.
(test_catch_syscall_execve): New, check entry/return across execve.
(do_syscall_tests): Call test_catch_syscall_execve.

19 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/gdbserver/ChangeLog
gdb/gdbserver/inferiors.c
gdb/gdbserver/inferiors.h
gdb/gdbserver/linux-low.c
gdb/gdbserver/linux-low.h
gdb/gdbserver/linux-x86-low.c
gdb/gdbserver/remote-utils.c
gdb/gdbserver/server.c
gdb/gdbserver/server.h
gdb/gdbserver/target.h
gdb/linux-nat.h
gdb/remote.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/catch-syscall.c
gdb/testsuite/gdb.base/catch-syscall.exp

index 74f91d2..4bacf03 100644 (file)
@@ -1,3 +1,17 @@
+2016-01-12  Josh Stone  <jistone@redhat.com>
+           Philippe Waroquiers  <philippe.waroquiers@skynet.be>
+
+       * NEWS (Changes since GDB 7.10): Mention QCatchSyscalls and the
+       syscall_entry and syscall_return stop reasons.  Mention GDB
+       support for remote catch syscall.
+       * remote.c (PACKET_QCatchSyscalls): New enum.
+       (remote_set_syscall_catchpoint): New function.
+       (remote_protocol_features): New element for QCatchSyscalls.
+       (remote_parse_stop_reply): Parse syscall_entry/return stops.
+       (init_remote_ops): Install remote_set_syscall_catchpoint.
+       (_initialize_remote): Config QCatchSyscalls.
+       * linux-nat.h (struct lwp_info) <syscall_state>: Comment typo.
+
 2016-01-12  Yao Qi  <yao.qi@linaro.org>
 
        * nat/linux-ptrace.c (linux_child_function): Cast child_stack
 2016-01-12  Yao Qi  <yao.qi@linaro.org>
 
        * nat/linux-ptrace.c (linux_child_function): Cast child_stack
index e3dd399..85642fb 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -120,6 +120,21 @@ N stop reply
   threads are stopped).  The remote stub reports support for this stop
   reply to GDB's qSupported query.
 
   threads are stopped).  The remote stub reports support for this stop
   reply to GDB's qSupported query.
 
+QCatchSyscalls:1 [;SYSNO]...
+QCatchSyscalls:0
+  Enable ("QCatchSyscalls:1") or disable ("QCatchSyscalls:0")
+  catching syscalls from the inferior process.
+
+syscall_entry stop reason
+  Indicates that a syscall was just called.
+
+syscall_return stop reason
+  Indicates that a syscall just returned.
+
+QCatchSyscalls:1 in qSupported
+  The qSupported packet may now include QCatchSyscalls:1 in the reply
+  to indicate support for catching syscalls.
+
 * Extended-remote exec events
 
   ** GDB now has support for exec events on extended-remote Linux targets.
 * Extended-remote exec events
 
   ** GDB now has support for exec events on extended-remote Linux targets.
@@ -142,6 +157,15 @@ show remote exec-event-feature-packet
      this enables follow-fork-mode, detach-on-fork, follow-exec-mode, and
      fork and exec catchpoints.
 
      this enables follow-fork-mode, detach-on-fork, follow-exec-mode, and
      fork and exec catchpoints.
 
+* Remote syscall events
+
+  ** GDB now has support for catch syscall on remote Linux targets,
+     currently enabled on x86/x86_64 architectures.
+
+set remote catch-syscall-packet
+show remote catch-syscall-packet
+  Set/show the use of the remote catch syscall feature.
+
 * MI changes
 
   ** The -var-set-format command now accepts the zero-hexadecimal
 * MI changes
 
   ** The -var-set-format command now accepts the zero-hexadecimal
index 3255fc3..b4ea0ee 100644 (file)
@@ -1,3 +1,11 @@
+2016-01-12  Josh Stone  <jistone@redhat.com>
+           Philippe Waroquiers  <philippe.waroquiers@skynet.be>
+
+       * gdb.texinfo (Remote Configuration): List the QCatchSyscalls packet.
+       (Stop Reply Packets): List the syscall entry and return stop reasons.
+       (General Query Packets): Describe QCatchSyscalls, and add it to the
+       table and the detailed list of stub features.
+
 2016-01-12  Pedro Alves  <palves@redhat.com>
 
        Remove use of the registered trademark symbol throughout.
 2016-01-12  Pedro Alves  <palves@redhat.com>
 
        Remove use of the registered trademark symbol throughout.
index 89826a4..35065e0 100644 (file)
@@ -20261,6 +20261,10 @@ are:
 @tab @code{qSupported}
 @tab Remote communications parameters
 
 @tab @code{qSupported}
 @tab Remote communications parameters
 
+@item @code{catch-syscalls}
+@tab @code{QCatchSyscalls}
+@tab @code{catch syscall}
+
 @item @code{pass-signals}
 @tab @code{QPassSignals}
 @tab @code{handle @var{signal}}
 @item @code{pass-signals}
 @tab @code{QPassSignals}
 @tab @code{handle @var{signal}}
@@ -35580,6 +35584,11 @@ The currently defined stop reasons are:
 The packet indicates a watchpoint hit, and @var{r} is the data address, in
 hex.
 
 The packet indicates a watchpoint hit, and @var{r} is the data address, in
 hex.
 
+@item syscall_entry
+@itemx syscall_return
+The packet indicates a syscall entry or return, and @var{r} is the
+syscall number, in hex.
+
 @cindex shared library events, remote reply
 @item library
 The packet indicates that the loaded libraries have changed.
 @cindex shared library events, remote reply
 @item library
 The packet indicates that the loaded libraries have changed.
@@ -36072,6 +36081,49 @@ by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
 Use of this packet is controlled by the @code{set non-stop} command; 
 @pxref{Non-Stop Mode}.
 
 Use of this packet is controlled by the @code{set non-stop} command; 
 @pxref{Non-Stop Mode}.
 
+@item QCatchSyscalls:1 @r{[};@var{sysno}@r{]}@dots{}
+@itemx QCatchSyscalls:0
+@cindex catch syscalls from inferior, remote request
+@cindex @samp{QCatchSyscalls} packet
+@anchor{QCatchSyscalls}
+Enable (@samp{QCatchSyscalls:1}) or disable (@samp{QCatchSyscalls:0})
+catching syscalls from the inferior process.
+
+For @samp{QCatchSyscalls:1}, each listed syscall @var{sysno} (encoded
+in hex) should be reported to @value{GDBN}.  If no syscall @var{sysno}
+is listed, every system call should be reported.
+
+Note that if a syscall not in the list is reported, @value{GDBN} will
+still filter the event according to its own list from all corresponding
+@code{catch syscall} commands.  However, it is more efficient to only
+report the requested syscalls.
+
+Multiple @samp{QCatchSyscalls:1} packets do not combine; any earlier
+@samp{QCatchSyscalls:1} list is completely replaced by the new list.
+
+If the inferior process execs, the state of @samp{QCatchSyscalls} is
+kept for the new process too.  On targets where exec may affect syscall
+numbers, for example with exec between 32 and 64-bit processes, the
+client should send a new packet with the new syscall list.
+
+Reply:
+@table @samp
+@item OK
+The request succeeded.
+
+@item E @var{nn}
+An error occurred.  @var{nn} are hex digits.
+
+@item @w{}
+An empty reply indicates that @samp{QCatchSyscalls} is not supported by
+the stub.
+@end table
+
+Use of this packet is controlled by the @code{set remote catch-syscalls}
+command (@pxref{Remote Configuration, set remote catch-syscalls}).
+This packet is not probed by default; the remote stub must request it,
+by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
+
 @item QPassSignals: @var{signal} @r{[};@var{signal}@r{]}@dots{}
 @cindex pass signals to inferior, remote request
 @cindex @samp{QPassSignals} packet
 @item QPassSignals: @var{signal} @r{[};@var{signal}@r{]}@dots{}
 @cindex pass signals to inferior, remote request
 @cindex @samp{QPassSignals} packet
@@ -36523,6 +36575,11 @@ These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab Yes
 
 @tab @samp{-}
 @tab Yes
 
+@item @samp{QCatchSyscalls}
+@tab No
+@tab @samp{-}
+@tab Yes
+
 @item @samp{QPassSignals}
 @tab No
 @tab @samp{-}
 @item @samp{QPassSignals}
 @tab No
 @tab @samp{-}
@@ -36726,6 +36783,10 @@ packet (@pxref{qXfer fdpic loadmap read}).
 The remote stub understands the @samp{QNonStop} packet
 (@pxref{QNonStop}).
 
 The remote stub understands the @samp{QNonStop} packet
 (@pxref{QNonStop}).
 
+@item QCatchSyscalls
+The remote stub understands the @samp{QCatchSyscalls} packet
+(@pxref{QCatchSyscalls}).
+
 @item QPassSignals
 The remote stub understands the @samp{QPassSignals} packet
 (@pxref{QPassSignals}).
 @item QPassSignals
 The remote stub understands the @samp{QPassSignals} packet
 (@pxref{QPassSignals}).
index 3c2d6db..757424c 100644 (file)
@@ -1,3 +1,33 @@
+2016-01-12  Josh Stone  <jistone@redhat.com>
+           Philippe Waroquiers  <philippe.waroquiers@skynet.be>
+
+       * inferiors.h: Include "gdb_vecs.h".
+       (struct process_info): Add syscalls_to_catch.
+       * inferiors.c (remove_process): Free syscalls_to_catch.
+       * remote-utils.c (prepare_resume_reply): Report syscall_entry and
+       syscall_return stops.
+       * server.h (UNKNOWN_SYSCALL, ANY_SYSCALL): Define.
+       * server.c (handle_general_set): Handle QCatchSyscalls.
+       (handle_query): Report support for QCatchSyscalls.
+       * target.h (struct target_ops): Add supports_catch_syscall.
+       (target_supports_catch_syscall): New macro.
+       * linux-low.h (struct linux_target_ops): Add get_syscall_trapinfo.
+       (struct lwp_info): Add syscall_state.
+       * linux-low.c (handle_extended_wait): Mark syscall_state as an entry.
+       Maintain syscall_state and syscalls_to_catch across exec.
+       (get_syscall_trapinfo): New function, proxy to the_low_target.
+       (linux_low_ptrace_options): Enable PTRACE_O_TRACESYSGOOD.
+       (linux_low_filter_event): Toggle syscall_state entry/return for
+       syscall traps, and set it ignored for all others.
+       (gdb_catching_syscalls_p): New function.
+       (gdb_catch_this_syscall_p): New function.
+       (linux_wait_1): Handle SYSCALL_SIGTRAP.
+       (linux_resume_one_lwp_throw): Add PTRACE_SYSCALL possibility.
+       (linux_supports_catch_syscall): New function.
+       (linux_target_ops): Install it.
+       * linux-x86-low.c (x86_get_syscall_trapinfo): New function.
+       (the_low_target): Install it.
+
 2016-01-12  Mike Frysinger  <vapier@gentoo.org>
 
        * acinclude.m4: Include new ../warning.m4 file.
 2016-01-12  Mike Frysinger  <vapier@gentoo.org>
 
        * acinclude.m4: Include new ../warning.m4 file.
index c884b55..4bea4fd 100644 (file)
@@ -339,6 +339,7 @@ remove_process (struct process_info *process)
   free_all_breakpoints (process);
   gdb_assert (find_thread_process (process) == NULL);
   remove_inferior (&all_processes, &process->entry);
   free_all_breakpoints (process);
   gdb_assert (find_thread_process (process) == NULL);
   remove_inferior (&all_processes, &process->entry);
+  VEC_free (int, process->syscalls_to_catch);
   free (process);
 }
 
   free (process);
 }
 
index af65718..00dfe60 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef INFERIORS_H
 #define INFERIORS_H
 
 #ifndef INFERIORS_H
 #define INFERIORS_H
 
+#include "gdb_vecs.h"
+
 /* Generic information for tracking a list of ``inferiors'' - threads,
    processes, etc.  */
 struct inferior_list
 /* Generic information for tracking a list of ``inferiors'' - threads,
    processes, etc.  */
 struct inferior_list
@@ -67,6 +69,10 @@ struct process_info
   /* The list of installed fast tracepoints.  */
   struct fast_tracepoint_jump *fast_tracepoint_jumps;
 
   /* The list of installed fast tracepoints.  */
   struct fast_tracepoint_jump *fast_tracepoint_jumps;
 
+  /* The list of syscalls to report, or just a single element, ANY_SYSCALL,
+     for unfiltered syscall reporting.  */
+  VEC (int) *syscalls_to_catch;
+
   const struct target_desc *tdesc;
 
   /* Private target data.  */
   const struct target_desc *tdesc;
 
   /* Private target data.  */
index 47f0f9d..7c77a2f 100644 (file)
@@ -461,6 +461,11 @@ handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
 
   gdb_assert (event_lwp->waitstatus.kind == TARGET_WAITKIND_IGNORE);
 
 
   gdb_assert (event_lwp->waitstatus.kind == TARGET_WAITKIND_IGNORE);
 
+  /* All extended events we currently use are mid-syscall.  Only
+     PTRACE_EVENT_STOP is delivered more like a signal-stop, but
+     you have to be using PTRACE_SEIZE to get that.  */
+  event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
+
   if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
       || (event == PTRACE_EVENT_CLONE))
     {
   if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
       || (event == PTRACE_EVENT_CLONE))
     {
@@ -611,6 +616,7 @@ handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
   else if (event == PTRACE_EVENT_EXEC && report_exec_events)
     {
       struct process_info *proc;
   else if (event == PTRACE_EVENT_EXEC && report_exec_events)
     {
       struct process_info *proc;
+      VEC (int) *syscalls_to_catch;
       ptid_t event_ptid;
       pid_t event_pid;
 
       ptid_t event_ptid;
       pid_t event_pid;
 
@@ -624,8 +630,12 @@ handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
       event_ptid = ptid_of (event_thr);
       event_pid = ptid_get_pid (event_ptid);
 
       event_ptid = ptid_of (event_thr);
       event_pid = ptid_get_pid (event_ptid);
 
-      /* Delete the execing process and all its threads.  */
+      /* Save the syscall list from the execing process.  */
       proc = get_thread_process (event_thr);
       proc = get_thread_process (event_thr);
+      syscalls_to_catch = proc->syscalls_to_catch;
+      proc->syscalls_to_catch = NULL;
+
+      /* Delete the execing process and all its threads.  */
       linux_mourn (proc);
       current_thread = NULL;
 
       linux_mourn (proc);
       current_thread = NULL;
 
@@ -648,6 +658,14 @@ handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
       event_thr->last_resume_kind = resume_continue;
       event_thr->last_status.kind = TARGET_WAITKIND_IGNORE;
 
       event_thr->last_resume_kind = resume_continue;
       event_thr->last_status.kind = TARGET_WAITKIND_IGNORE;
 
+      /* Update syscall state in the new lwp, effectively mid-syscall too.  */
+      event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
+
+      /* Restore the list to catch.  Don't rely on the client, which is free
+        to avoid sending a new list when the architecture doesn't change.
+        Also, for ANY_SYSCALL, the architecture doesn't really matter.  */
+      proc->syscalls_to_catch = syscalls_to_catch;
+
       /* Report the event.  */
       *orig_event_lwp = event_lwp;
       return 0;
       /* Report the event.  */
       *orig_event_lwp = event_lwp;
       return 0;
@@ -682,6 +700,40 @@ get_pc (struct lwp_info *lwp)
   return pc;
 }
 
   return pc;
 }
 
+/* This function should only be called if LWP got a SYSCALL_SIGTRAP.
+   Fill *SYSNO with the syscall nr trapped.  Fill *SYSRET with the
+   return code.  */
+
+static void
+get_syscall_trapinfo (struct lwp_info *lwp, int *sysno, int *sysret)
+{
+  struct thread_info *saved_thread;
+  struct regcache *regcache;
+
+  if (the_low_target.get_syscall_trapinfo == NULL)
+    {
+      /* If we cannot get the syscall trapinfo, report an unknown
+        system call number and -ENOSYS return value.  */
+      *sysno = UNKNOWN_SYSCALL;
+      *sysret = -ENOSYS;
+      return;
+    }
+
+  saved_thread = current_thread;
+  current_thread = get_lwp_thread (lwp);
+
+  regcache = get_thread_regcache (current_thread, 1);
+  (*the_low_target.get_syscall_trapinfo) (regcache, sysno, sysret);
+
+  if (debug_threads)
+    {
+      debug_printf ("get_syscall_trapinfo sysno %d sysret %d\n",
+                   *sysno, *sysret);
+    }
+
+  current_thread = saved_thread;
+}
+
 /* This function should only be called if LWP got a SIGTRAP.
    The SIGTRAP could mean several things.
 
 /* This function should only be called if LWP got a SIGTRAP.
    The SIGTRAP could mean several things.
 
@@ -2236,6 +2288,8 @@ linux_low_ptrace_options (int attached)
   if (report_exec_events)
     options |= PTRACE_O_TRACEEXEC;
 
   if (report_exec_events)
     options |= PTRACE_O_TRACEEXEC;
 
+  options |= PTRACE_O_TRACESYSGOOD;
+
   return options;
 }
 
   return options;
 }
 
@@ -2364,6 +2418,21 @@ linux_low_filter_event (int lwpid, int wstat)
       child->must_set_ptrace_flags = 0;
     }
 
       child->must_set_ptrace_flags = 0;
     }
 
+  /* Always update syscall_state, even if it will be filtered later.  */
+  if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SYSCALL_SIGTRAP)
+    {
+      child->syscall_state
+       = (child->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY
+          ? TARGET_WAITKIND_SYSCALL_RETURN
+          : TARGET_WAITKIND_SYSCALL_ENTRY);
+    }
+  else
+    {
+      /* Almost all other ptrace-stops are known to be outside of system
+        calls, with further exceptions in handle_extended_wait.  */
+      child->syscall_state = TARGET_WAITKIND_IGNORE;
+    }
+
   /* Be careful to not overwrite stop_pc until
      check_stopped_by_breakpoint is called.  */
   if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
   /* Be careful to not overwrite stop_pc until
      check_stopped_by_breakpoint is called.  */
   if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
@@ -2973,6 +3042,44 @@ filter_exit_event (struct lwp_info *event_child,
   return ptid;
 }
 
   return ptid;
 }
 
+/* Returns 1 if GDB is interested in any event_child syscalls.  */
+
+static int
+gdb_catching_syscalls_p (struct lwp_info *event_child)
+{
+  struct thread_info *thread = get_lwp_thread (event_child);
+  struct process_info *proc = get_thread_process (thread);
+
+  return !VEC_empty (int, proc->syscalls_to_catch);
+}
+
+/* Returns 1 if GDB is interested in the event_child syscall.
+   Only to be called when stopped reason is SYSCALL_SIGTRAP.  */
+
+static int
+gdb_catch_this_syscall_p (struct lwp_info *event_child)
+{
+  int i, iter;
+  int sysno, sysret;
+  struct thread_info *thread = get_lwp_thread (event_child);
+  struct process_info *proc = get_thread_process (thread);
+
+  if (VEC_empty (int, proc->syscalls_to_catch))
+    return 0;
+
+  if (VEC_index (int, proc->syscalls_to_catch, 0) == ANY_SYSCALL)
+    return 1;
+
+  get_syscall_trapinfo (event_child, &sysno, &sysret);
+  for (i = 0;
+       VEC_iterate (int, proc->syscalls_to_catch, i, iter);
+       i++)
+    if (iter == sysno)
+      return 1;
+
+  return 0;
+}
+
 /* Wait for process, returns status.  */
 
 static ptid_t
 /* Wait for process, returns status.  */
 
 static ptid_t
@@ -3307,6 +3414,22 @@ linux_wait_1 (ptid_t ptid,
 
   /* Check whether GDB would be interested in this event.  */
 
 
   /* Check whether GDB would be interested in this event.  */
 
+  /* Check if GDB is interested in this syscall.  */
+  if (WIFSTOPPED (w)
+      && WSTOPSIG (w) == SYSCALL_SIGTRAP
+      && !gdb_catch_this_syscall_p (event_child))
+    {
+      if (debug_threads)
+       {
+         debug_printf ("Ignored syscall for LWP %ld.\n",
+                       lwpid_of (current_thread));
+       }
+
+      linux_resume_one_lwp (event_child, event_child->stepping,
+                           0, NULL);
+      return ignore_event (ourstatus);
+    }
+
   /* If GDB is not interested in this signal, don't stop other
      threads, and don't report it to GDB.  Just resume the inferior
      right away.  We do this for threading-related signals as well as
   /* If GDB is not interested in this signal, don't stop other
      threads, and don't report it to GDB.  Just resume the inferior
      right away.  We do this for threading-related signals as well as
@@ -3559,8 +3682,16 @@ linux_wait_1 (ptid_t ptid,
        }
     }
 
        }
     }
 
-  if (current_thread->last_resume_kind == resume_stop
-      && WSTOPSIG (w) == SIGSTOP)
+  if (WSTOPSIG (w) == SYSCALL_SIGTRAP)
+    {
+      int sysret;
+
+      get_syscall_trapinfo (event_child,
+                           &ourstatus->value.syscall_number, &sysret);
+      ourstatus->kind = event_child->syscall_state;
+    }
+  else if (current_thread->last_resume_kind == resume_stop
+          && WSTOPSIG (w) == SIGSTOP)
     {
       /* A thread that has been requested to stop by GDB with vCont;t,
         and it stopped cleanly, so report as SIG0.  The use of
     {
       /* A thread that has been requested to stop by GDB with vCont;t,
         and it stopped cleanly, so report as SIG0.  The use of
@@ -4017,6 +4148,7 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
   struct thread_info *thread = get_lwp_thread (lwp);
   struct thread_info *saved_thread;
   int fast_tp_collecting;
   struct thread_info *thread = get_lwp_thread (lwp);
   struct thread_info *saved_thread;
   int fast_tp_collecting;
+  int ptrace_request;
   struct process_info *proc = get_thread_process (thread);
 
   /* Note that target description may not be initialised
   struct process_info *proc = get_thread_process (thread);
 
   /* Note that target description may not be initialised
@@ -4204,7 +4336,14 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
   regcache_invalidate_thread (thread);
   errno = 0;
   lwp->stepping = step;
   regcache_invalidate_thread (thread);
   errno = 0;
   lwp->stepping = step;
-  ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (thread),
+  if (step)
+    ptrace_request = PTRACE_SINGLESTEP;
+  else if (gdb_catching_syscalls_p (lwp))
+    ptrace_request = PTRACE_SYSCALL;
+  else
+    ptrace_request = PTRACE_CONT;
+  ptrace (ptrace_request,
+         lwpid_of (thread),
          (PTRACE_TYPE_ARG3) 0,
          /* Coerce to a uintptr_t first to avoid potential gcc warning
             of coercing an 8 byte integer to a 4 byte pointer.  */
          (PTRACE_TYPE_ARG3) 0,
          /* Coerce to a uintptr_t first to avoid potential gcc warning
             of coercing an 8 byte integer to a 4 byte pointer.  */
@@ -6286,6 +6425,13 @@ linux_process_qsupported (char **features, int count)
 }
 
 static int
 }
 
 static int
+linux_supports_catch_syscall (void)
+{
+  return (the_low_target.get_syscall_trapinfo != NULL
+         && linux_supports_tracesysgood ());
+}
+
+static int
 linux_supports_tracepoints (void)
 {
   if (*the_low_target.supports_tracepoints == NULL)
 linux_supports_tracepoints (void)
 {
   if (*the_low_target.supports_tracepoints == NULL)
@@ -7209,7 +7355,8 @@ static struct target_ops linux_target_ops = {
   linux_sw_breakpoint_from_kind,
   linux_proc_tid_get_name,
   linux_breakpoint_kind_from_current_state,
   linux_sw_breakpoint_from_kind,
   linux_proc_tid_get_name,
   linux_breakpoint_kind_from_current_state,
-  linux_supports_software_single_step
+  linux_supports_software_single_step,
+  linux_supports_catch_syscall,
 };
 
 #ifdef HAVE_LINUX_REGSETS
 };
 
 #ifdef HAVE_LINUX_REGSETS
index e80c67f..751eea1 100644 (file)
@@ -240,6 +240,12 @@ struct linux_target_ops
 
   /* See target.h.  */
   int (*supports_hardware_single_step) (void);
 
   /* See target.h.  */
   int (*supports_hardware_single_step) (void);
+
+  /* Fill *SYSNO with the syscall nr trapped.  Fill *SYSRET with the
+     return code.  Only to be called when inferior is stopped
+     due to SYSCALL_SIGTRAP.  */
+  void (*get_syscall_trapinfo) (struct regcache *regcache,
+                               int *sysno, int *sysret);
 };
 
 extern struct linux_target_ops the_low_target;
 };
 
 extern struct linux_target_ops the_low_target;
@@ -278,6 +284,13 @@ struct lwp_info
      event already received in a wait()).  */
   int stopped;
 
      event already received in a wait()).  */
   int stopped;
 
+  /* Signal whether we are in a SYSCALL_ENTRY or
+     in a SYSCALL_RETURN event.
+     Values:
+     - TARGET_WAITKIND_SYSCALL_ENTRY
+     - TARGET_WAITKIND_SYSCALL_RETURN */
+  enum target_waitkind syscall_state;
+
   /* When stopped is set, the last wait status recorded for this lwp.  */
   int last_status;
 
   /* When stopped is set, the last wait status recorded for this lwp.  */
   int last_status;
 
index 0432a86..4a01750 100644 (file)
@@ -1438,6 +1438,31 @@ x86_arch_setup (void)
   current_process ()->tdesc = x86_linux_read_description ();
 }
 
   current_process ()->tdesc = x86_linux_read_description ();
 }
 
+/* Fill *SYSNO and *SYSRET with the syscall nr trapped and the syscall return
+   code.  This should only be called if LWP got a SYSCALL_SIGTRAP.  */
+
+static void
+x86_get_syscall_trapinfo (struct regcache *regcache, int *sysno, int *sysret)
+{
+  int use_64bit = register_size (regcache->tdesc, 0) == 8;
+
+  if (use_64bit)
+    {
+      long l_sysno;
+      long l_sysret;
+
+      collect_register_by_name (regcache, "orig_rax", &l_sysno);
+      collect_register_by_name (regcache, "rax", &l_sysret);
+      *sysno = (int) l_sysno;
+      *sysret = (int) l_sysret;
+    }
+  else
+    {
+      collect_register_by_name (regcache, "orig_eax", sysno);
+      collect_register_by_name (regcache, "eax", sysret);
+    }
+}
+
 static int
 x86_supports_tracepoints (void)
 {
 static int
 x86_supports_tracepoints (void)
 {
@@ -3315,6 +3340,7 @@ struct linux_target_ops the_low_target =
   x86_supports_range_stepping,
   NULL, /* breakpoint_kind_from_current_state */
   x86_supports_hardware_single_step,
   x86_supports_range_stepping,
   NULL, /* breakpoint_kind_from_current_state */
   x86_supports_hardware_single_step,
+  x86_get_syscall_trapinfo,
 };
 
 void
 };
 
 void
index 40bd373..15cdbe1 100644 (file)
@@ -1132,6 +1132,8 @@ prepare_resume_reply (char *buf, ptid_t ptid,
     case TARGET_WAITKIND_VFORK_DONE:
     case TARGET_WAITKIND_EXECD:
     case TARGET_WAITKIND_THREAD_CREATED:
     case TARGET_WAITKIND_VFORK_DONE:
     case TARGET_WAITKIND_EXECD:
     case TARGET_WAITKIND_THREAD_CREATED:
+    case TARGET_WAITKIND_SYSCALL_ENTRY:
+    case TARGET_WAITKIND_SYSCALL_RETURN:
       {
        struct thread_info *saved_thread;
        const char **regp;
       {
        struct thread_info *saved_thread;
        const char **regp;
@@ -1181,6 +1183,16 @@ prepare_resume_reply (char *buf, ptid_t ptid,
 
            sprintf (buf, "T%02xcreate:;", signal);
          }
 
            sprintf (buf, "T%02xcreate:;", signal);
          }
+       else if (status->kind == TARGET_WAITKIND_SYSCALL_ENTRY
+                || status->kind == TARGET_WAITKIND_SYSCALL_RETURN)
+         {
+           enum gdb_signal signal = GDB_SIGNAL_TRAP;
+           const char *event = (status->kind == TARGET_WAITKIND_SYSCALL_ENTRY
+                                ? "syscall_entry" : "syscall_return");
+
+           sprintf (buf, "T%02x%s:%x;", signal, event,
+                    status->value.syscall_number);
+         }
        else
          sprintf (buf, "T%02x", status->value.sig);
 
        else
          sprintf (buf, "T%02x", status->value.sig);
 
index 440c076..301dea9 100644 (file)
@@ -624,6 +624,54 @@ handle_general_set (char *own_buf)
       return;
     }
 
       return;
     }
 
+  if (startswith (own_buf, "QCatchSyscalls:"))
+    {
+      const char *p = own_buf + sizeof ("QCatchSyscalls:") - 1;
+      int enabled = -1;
+      CORE_ADDR sysno;
+      struct process_info *process;
+
+      if (!target_running () || !target_supports_catch_syscall ())
+       {
+         write_enn (own_buf);
+         return;
+       }
+
+      if (strcmp (p, "0") == 0)
+       enabled = 0;
+      else if (p[0] == '1' && (p[1] == ';' || p[1] == '\0'))
+       enabled = 1;
+      else
+       {
+         fprintf (stderr, "Unknown catch-syscalls mode requested: %s\n",
+                  own_buf);
+         write_enn (own_buf);
+         return;
+       }
+
+      process = current_process ();
+      VEC_truncate (int, process->syscalls_to_catch, 0);
+
+      if (enabled)
+       {
+         p += 1;
+         if (*p == ';')
+           {
+             p += 1;
+             while (*p != '\0')
+               {
+                 p = decode_address_to_semicolon (&sysno, p);
+                 VEC_safe_push (int, process->syscalls_to_catch, (int) sysno);
+               }
+           }
+         else
+           VEC_safe_push (int, process->syscalls_to_catch, ANY_SYSCALL);
+       }
+
+      write_ok (own_buf);
+      return;
+    }
+
   if (strcmp (own_buf, "QStartNoAckMode") == 0)
     {
       if (remote_debug)
   if (strcmp (own_buf, "QStartNoAckMode") == 0)
     {
       if (remote_debug)
@@ -2200,6 +2248,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
               "PacketSize=%x;QPassSignals+;QProgramSignals+",
               PBUFSIZ - 1);
 
               "PacketSize=%x;QPassSignals+;QProgramSignals+",
               PBUFSIZ - 1);
 
+      if (target_supports_catch_syscall ())
+       strcat (own_buf, ";QCatchSyscalls+");
+
       if (the_target->qxfer_libraries_svr4 != NULL)
        strcat (own_buf, ";qXfer:libraries-svr4:read+"
                ";augmented-libraries-svr4-read+");
       if (the_target->qxfer_libraries_svr4 != NULL)
        strcat (own_buf, ";qXfer:libraries-svr4:read+"
                ";augmented-libraries-svr4-read+");
index 58cf342..3d78fb3 100644 (file)
@@ -134,4 +134,10 @@ extern void discard_queued_stop_replies (ptid_t ptid);
    as large as the largest register set supported by gdbserver.  */
 #define PBUFSIZ 16384
 
    as large as the largest register set supported by gdbserver.  */
 #define PBUFSIZ 16384
 
+/* Definition for an unknown syscall, used basically in error-cases.  */
+#define UNKNOWN_SYSCALL (-1)
+
+/* Definition for any syscall, used for unfiltered syscall reporting.  */
+#define ANY_SYSCALL (-2)
+
 #endif /* SERVER_H */
 #endif /* SERVER_H */
index cbdb8d9..5af2051 100644 (file)
@@ -467,6 +467,10 @@ struct target_ops
 
   /* Returns true if the target can software single step.  */
   int (*supports_software_single_step) (void);
 
   /* Returns true if the target can software single step.  */
   int (*supports_software_single_step) (void);
+
+  /* Return 1 if the target supports catch syscall, 0 (or leave the
+     callback NULL) otherwise.  */
+  int (*supports_catch_syscall) (void);
 };
 
 extern struct target_ops *the_target;
 };
 
 extern struct target_ops *the_target;
@@ -542,6 +546,10 @@ int kill_inferior (int);
        the_target->process_qsupported (features, count); \
     } while (0)
 
        the_target->process_qsupported (features, count); \
     } while (0)
 
+#define target_supports_catch_syscall()                \
+  (the_target->supports_catch_syscall ?                        \
+   (*the_target->supports_catch_syscall) () : 0)
+
 #define target_supports_tracepoints()                  \
   (the_target->supports_tracepoints                    \
    ? (*the_target->supports_tracepoints) () : 0)
 #define target_supports_tracepoints()                  \
   (the_target->supports_tracepoints                    \
    ? (*the_target->supports_tracepoints) () : 0)
index 7bdeb31..73888e5 100644 (file)
@@ -88,7 +88,7 @@ struct lwp_info
      or to a local variable in lin_lwp_wait.  */
   struct target_waitstatus waitstatus;
 
      or to a local variable in lin_lwp_wait.  */
   struct target_waitstatus waitstatus;
 
-  /* Signal wether we are in a SYSCALL_ENTRY or
+  /* Signal whether we are in a SYSCALL_ENTRY or
      in a SYSCALL_RETURN event.
      Values:
      - TARGET_WAITKIND_SYSCALL_ENTRY
      in a SYSCALL_RETURN event.
      Values:
      - TARGET_WAITKIND_SYSCALL_ENTRY
index 528d863..e825d27 100644 (file)
@@ -1392,6 +1392,7 @@ enum {
   PACKET_qSupported,
   PACKET_qTStatus,
   PACKET_QPassSignals,
   PACKET_qSupported,
   PACKET_qTStatus,
   PACKET_QPassSignals,
+  PACKET_QCatchSyscalls,
   PACKET_QProgramSignals,
   PACKET_qCRC,
   PACKET_qSearch_memory,
   PACKET_QProgramSignals,
   PACKET_qCRC,
   PACKET_qSearch_memory,
@@ -1987,6 +1988,93 @@ remote_pass_signals (struct target_ops *self,
     }
 }
 
     }
 }
 
+/* If 'QCatchSyscalls' is supported, tell the remote stub
+   to report syscalls to GDB.  */
+
+static int
+remote_set_syscall_catchpoint (struct target_ops *self,
+                              int pid, int needed, int any_count,
+                              int table_size, int *table)
+{
+  char *catch_packet;
+  enum packet_result result;
+  int n_sysno = 0;
+
+  if (packet_support (PACKET_QCatchSyscalls) == PACKET_DISABLE)
+    {
+      /* Not supported.  */
+      return 1;
+    }
+
+  if (needed && !any_count)
+    {
+      int i;
+
+      /* Count how many syscalls are to be caught (table[sysno] != 0).  */
+      for (i = 0; i < table_size; i++)
+       {
+         if (table[i] != 0)
+           n_sysno++;
+       }
+    }
+
+  if (remote_debug)
+    {
+      fprintf_unfiltered (gdb_stdlog,
+                         "remote_set_syscall_catchpoint "
+                         "pid %d needed %d any_count %d n_sysno %d\n",
+                         pid, needed, any_count, n_sysno);
+    }
+
+  if (needed)
+    {
+      /* Prepare a packet with the sysno list, assuming max 8+1
+        characters for a sysno.  If the resulting packet size is too
+        big, fallback on the non-selective packet.  */
+      const int maxpktsz = strlen ("QCatchSyscalls:1") + n_sysno * 9 + 1;
+
+      catch_packet = xmalloc (maxpktsz);
+      strcpy (catch_packet, "QCatchSyscalls:1");
+      if (!any_count)
+       {
+         int i;
+         char *p;
+
+         p = catch_packet;
+         p += strlen (p);
+
+         /* Add in catch_packet each syscall to be caught (table[i] != 0).  */
+         for (i = 0; i < table_size; i++)
+           {
+             if (table[i] != 0)
+               p += xsnprintf (p, catch_packet + maxpktsz - p, ";%x", i);
+           }
+       }
+      if (strlen (catch_packet) > get_remote_packet_size ())
+       {
+         /* catch_packet too big.  Fallback to less efficient
+            non selective mode, with GDB doing the filtering.  */
+         catch_packet[sizeof ("QCatchSyscalls:1") - 1] = 0;
+       }
+    }
+  else
+    catch_packet = xstrdup ("QCatchSyscalls:0");
+
+  {
+    struct cleanup *old_chain = make_cleanup (xfree, catch_packet);
+    struct remote_state *rs = get_remote_state ();
+
+    putpkt (catch_packet);
+    getpkt (&rs->buf, &rs->buf_size, 0);
+    result = packet_ok (rs->buf, &remote_protocol_packets[PACKET_QCatchSyscalls]);
+    do_cleanups (old_chain);
+    if (result == PACKET_OK)
+      return 0;
+    else
+      return -1;
+  }
+}
+
 /* If 'QProgramSignals' is supported, tell the remote stub what
    signals it should pass through to the inferior when detaching.  */
 
 /* If 'QProgramSignals' is supported, tell the remote stub what
    signals it should pass through to the inferior when detaching.  */
 
@@ -4467,6 +4555,8 @@ static const struct protocol_feature remote_protocol_features[] = {
     PACKET_qXfer_traceframe_info },
   { "QPassSignals", PACKET_DISABLE, remote_supported_packet,
     PACKET_QPassSignals },
     PACKET_qXfer_traceframe_info },
   { "QPassSignals", PACKET_DISABLE, remote_supported_packet,
     PACKET_QPassSignals },
+  { "QCatchSyscalls", PACKET_DISABLE, remote_supported_packet,
+    PACKET_QCatchSyscalls },
   { "QProgramSignals", PACKET_DISABLE, remote_supported_packet,
     PACKET_QProgramSignals },
   { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
   { "QProgramSignals", PACKET_DISABLE, remote_supported_packet,
     PACKET_QProgramSignals },
   { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -6371,6 +6461,22 @@ Packet: '%s'\n"),
 
          if (strprefix (p, p1, "thread"))
            event->ptid = read_ptid (++p1, &p);
 
          if (strprefix (p, p1, "thread"))
            event->ptid = read_ptid (++p1, &p);
+         else if (strprefix (p, p1, "syscall_entry"))
+           {
+             ULONGEST sysno;
+
+             event->ws.kind = TARGET_WAITKIND_SYSCALL_ENTRY;
+             p = unpack_varlen_hex (++p1, &sysno);
+             event->ws.value.syscall_number = (int) sysno;
+           }
+         else if (strprefix (p, p1, "syscall_return"))
+           {
+             ULONGEST sysno;
+
+             event->ws.kind = TARGET_WAITKIND_SYSCALL_RETURN;
+             p = unpack_varlen_hex (++p1, &sysno);
+             event->ws.value.syscall_number = (int) sysno;
+           }
          else if (strprefix (p, p1, "watch")
                   || strprefix (p, p1, "rwatch")
                   || strprefix (p, p1, "awatch"))
          else if (strprefix (p, p1, "watch")
                   || strprefix (p, p1, "rwatch")
                   || strprefix (p, p1, "awatch"))
@@ -12976,6 +13082,7 @@ Specify the serial device it is connected to\n\
   remote_ops.to_load = remote_load;
   remote_ops.to_mourn_inferior = remote_mourn;
   remote_ops.to_pass_signals = remote_pass_signals;
   remote_ops.to_load = remote_load;
   remote_ops.to_mourn_inferior = remote_mourn;
   remote_ops.to_pass_signals = remote_pass_signals;
+  remote_ops.to_set_syscall_catchpoint = remote_set_syscall_catchpoint;
   remote_ops.to_program_signals = remote_program_signals;
   remote_ops.to_thread_alive = remote_thread_alive;
   remote_ops.to_thread_name = remote_thread_name;
   remote_ops.to_program_signals = remote_program_signals;
   remote_ops.to_thread_alive = remote_thread_alive;
   remote_ops.to_thread_name = remote_thread_name;
@@ -13542,6 +13649,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_QPassSignals],
                         "QPassSignals", "pass-signals", 0);
 
   add_packet_config_cmd (&remote_protocol_packets[PACKET_QPassSignals],
                         "QPassSignals", "pass-signals", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_QCatchSyscalls],
+                        "QCatchSyscalls", "catch-syscalls", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_QProgramSignals],
                         "QProgramSignals", "program-signals", 0);
 
   add_packet_config_cmd (&remote_protocol_packets[PACKET_QProgramSignals],
                         "QProgramSignals", "program-signals", 0);
 
index da9c70a..0d07499 100644 (file)
@@ -1,3 +1,12 @@
+2016-01-12  Josh Stone  <jistone@redhat.com>
+           Philippe Waroquiers  <philippe.waroquiers@skynet.be>
+
+       * gdb.base/catch-syscall.c (do_execve): New variable.
+       (main): Conditionally trigger an execve.
+       * gdb.base/catch-syscall.exp: Enable testing for remote targets.
+       (test_catch_syscall_execve): New, check entry/return across execve.
+       (do_syscall_tests): Call test_catch_syscall_execve.
+
 2016-01-12  Pedro Alves  <palves@redhat.com>
 
        * gdb.base/random-signal.exp (do_test): New procedure, with body
 2016-01-12  Pedro Alves  <palves@redhat.com>
 
        * gdb.base/random-signal.exp (do_test): New procedure, with body
index e65d4a4..98222fa 100644 (file)
@@ -31,13 +31,20 @@ int write_syscall = SYS_write;
 int unknown_syscall = 123456789;
 int exit_group_syscall = SYS_exit_group;
 
 int unknown_syscall = 123456789;
 int exit_group_syscall = SYS_exit_group;
 
+/* Set by the test when it wants execve.  */
+int do_execve = 0;
+
 int
 int
-main (void)
+main (int argc, char *const argv[])
 {
        int fd[2];
        char buf1[2] = "a";
        char buf2[2];
 
 {
        int fd[2];
        char buf1[2] = "a";
        char buf2[2];
 
+       /* Test a simple self-exec, but only on request.  */
+       if (do_execve)
+         execv (*argv, argv);
+
        /* A close() with a wrong argument.  We are only
           interested in the syscall.  */
        close (-1);
        /* A close() with a wrong argument.  We are only
           interested in the syscall.  */
        close (-1);
index 26fb6a5..5679000 100644 (file)
@@ -19,7 +19,7 @@
 # It was written by Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
 # on September/2008.
 
 # It was written by Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
 # on September/2008.
 
-if { [is_remote target] || ![isnative] } then {
+if { ![isnative] } then {
     continue
 }
 
     continue
 }
 
@@ -322,6 +322,28 @@ proc test_catch_syscall_mid_vfork {} {
     }
 }
 
     }
 }
 
+proc test_catch_syscall_execve {} {
+    global gdb_prompt decimal
+
+    with_test_prefix "execve" {
+
+       # Tell the test program we want an execve.
+       gdb_test_no_output "set do_execve = 1"
+
+       # Check for entry/return across the execve, making sure that the
+       # syscall_state isn't lost when turning into a new process.
+       insert_catch_syscall_with_arg "execve"
+       check_continue "execve"
+
+       # Continue to main so extended-remote can read files as needed.
+       # (Otherwise that "Reading" output confuses gdb_continue_to_end.)
+       gdb_continue "main"
+
+       # Now can we finish?
+       check_for_program_end
+    }
+}
+
 proc test_catch_syscall_fail_nodatadir {} {
     with_test_prefix "fail no datadir" {
        # Sanitizing.
 proc test_catch_syscall_fail_nodatadir {} {
     with_test_prefix "fail no datadir" {
        # Sanitizing.
@@ -392,6 +414,9 @@ proc do_syscall_tests {} {
     # Testing the 'catch syscall' command starting mid-vfork.
     if [runto_main] then { test_catch_syscall_mid_vfork }
 
     # Testing the 'catch syscall' command starting mid-vfork.
     if [runto_main] then { test_catch_syscall_mid_vfork }
 
+    # Testing that 'catch syscall' entry/return tracks across execve.
+    if [runto_main] then { test_catch_syscall_execve }
+
     # Testing if the 'catch syscall' command works when switching to
     # different architectures on-the-fly (PR gdb/10737).
     if [runto_main] then { test_catch_syscall_multi_arch }
     # Testing if the 'catch syscall' command works when switching to
     # different architectures on-the-fly (PR gdb/10737).
     if [runto_main] then { test_catch_syscall_multi_arch }