return 1;
     }
 
+  /* Always update the entry/return state, even if this particular
+     syscall isn't interesting to the core now.  In async mode,
+     the user could install a new catchpoint for this syscall
+     between syscall enter/return, and we'll need to know to
+     report a syscall return if that happens.  */
+  lp->syscall_state = (lp->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY
+                      ? TARGET_WAITKIND_SYSCALL_RETURN
+                      : TARGET_WAITKIND_SYSCALL_ENTRY);
+
   if (catch_syscall_enabled ())
     {
-      /* Always update the entry/return state, even if this particular
-        syscall isn't interesting to the core now.  In async mode,
-        the user could install a new catchpoint for this syscall
-        between syscall enter/return, and we'll need to know to
-        report a syscall return if that happens.  */
-      lp->syscall_state = (lp->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY
-                          ? TARGET_WAITKIND_SYSCALL_RETURN
-                          : TARGET_WAITKIND_SYSCALL_ENTRY);
-
       if (catching_syscall_number (syscall_number))
        {
          /* Alright, an event to report.  */
   struct target_waitstatus *ourstatus = &lp->waitstatus;
   int event = linux_ptrace_get_extended_event (status);
 
+  /* 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.  */
+  lp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
+
   if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
       || event == PTRACE_EVENT_CLONE)
     {
       if (linux_handle_syscall_trap (lp, 1))
        return wait_lwp (lp);
     }
+  else
+    {
+      /* Almost all other ptrace-stops are known to be outside of system
+        calls, with further exceptions in linux_handle_extended_wait.  */
+      lp->syscall_state = TARGET_WAITKIND_IGNORE;
+    }
 
   /* Handle GNU/Linux's extended waitstatus for trace events.  */
   if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
       if (linux_handle_syscall_trap (lp, 0))
        return NULL;
     }
+  else
+    {
+      /* Almost all other ptrace-stops are known to be outside of system
+        calls, with further exceptions in linux_handle_extended_wait.  */
+      lp->syscall_state = TARGET_WAITKIND_IGNORE;
+    }
 
   /* Handle GNU/Linux's extended waitstatus for trace events.  */
   if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
 
 set last_syscall "exit_group"
 set last_syscall_number { }
 
+set vfork_syscalls "(vfork|clone2?)"
+
+set unknown_syscall_number { }
+
 # Internal procedure used to check if, after issuing a 'catch syscall'
 # command (without arguments), the 'info breakpoints' command displays
 # that '"any syscall"' is to be caught.
     gdb_test "info breakpoints" ".*catchpoint.*keep y.*syscalls (.)?${filter_str}(.)?.*" $thistest
 }
 
-# This procedure checks if there was a call to a syscall.
-proc check_call_to_syscall { syscall } {
+# This procedure checks if there was a call to a syscall.  The optional
+# pattern can match syscalls that vary in implementation, like vfork.
+proc check_call_to_syscall { syscall { pattern "" } } {
     global decimal
 
+    if { $pattern eq "" } {
+      set pattern "${syscall}"
+    }
+
     set thistest "program has called $syscall"
-    gdb_test "continue" "Catchpoint $decimal \\(call to syscall .?${syscall}.?\\).*" $thistest
+    gdb_test "continue" "Catchpoint $decimal \\(call to syscall .?${pattern}.?\\).*" $thistest
 }
 
-# This procedure checks if the syscall returned.
-proc check_return_from_syscall { syscall } {
+# This procedure checks if the syscall returned.  The optional pattern
+# can match syscalls that vary in implementation, like vfork.
+proc check_return_from_syscall { syscall { pattern "" } } {
     global decimal
 
+    if { $pattern eq "" } {
+      set pattern "${syscall}"
+    }
+
     set thistest "syscall $syscall has returned"
-    gdb_test "continue" "Catchpoint $decimal \\(returned from syscall ${syscall}\\).*" $thistest
+    gdb_test "continue" "Catchpoint $decimal \\(returned from syscall ${pattern}\\).*" $thistest
 }
 
 # Internal procedure that performs two 'continue' commands and checks if
-# a syscall call AND return occur.
-proc check_continue { syscall } {
+# a syscall call AND return occur.  The optional pattern can match
+# syscalls that vary in implementation, like vfork.
+proc check_continue { syscall { pattern "" } } {
     # Testing if the 'continue' stops at the
     # specified syscall_name.  If it does, then it should
     # first print that the infeior has called the syscall,
     # and after print that the syscall has returned.
 
     # Testing if the inferior has called the syscall.
-    check_call_to_syscall $syscall
+    check_call_to_syscall $syscall $pattern
     # And now, that the syscall has returned.
-    check_return_from_syscall $syscall
+    check_return_from_syscall $syscall $pattern
 }
 
 # Inserts a syscall catchpoint with an argument.
 }
 
 proc test_catch_syscall_without_args {} {
-    global all_syscalls last_syscall decimal
+    global all_syscalls last_syscall vfork_syscalls unknown_syscall_number decimal
 
     with_test_prefix "without arguments" {
        # Trying to set the syscall.
            check_continue $name
        }
 
+       check_continue "vfork" $vfork_syscalls
+
+       with_test_prefix "ENOSYS" {
+           check_continue $unknown_syscall_number
+       }
+
        # At last but not least, we check if the inferior has called
        # the last (exit) syscall.
        check_call_to_syscall $last_syscall
     }
 }
 
+proc test_catch_syscall_skipping_return {} {
+    with_test_prefix "skipping return" {
+       with_test_prefix "entry" {
+           set syscall_name "write"
+
+           insert_catch_syscall_with_arg $syscall_name
+
+           # Let's first reach the entry of the syscall.
+           check_call_to_syscall $syscall_name
+
+           # Now purposely skip the syscall return.
+           delete_breakpoints
+           gdb_test "stepi" ".*" "step over syscall return"
+       }
+
+       # With a naive entry/return toggle, gdb will still think
+       # the target is due for a syscall return.
+
+       with_test_prefix "entry/return" {
+           set syscall_name "read"
+
+           insert_catch_syscall_with_arg $syscall_name
+
+           # Check for entry first, then return.
+           check_continue $syscall_name
+
+           # Can we finish?
+           check_for_program_end
+       }
+    }
+}
+
+proc test_catch_syscall_mid_vfork {} {
+    global gdb_prompt decimal vfork_syscalls
+
+    with_test_prefix "mid-vfork" {
+       # Verify that the system supports "catch vfork".
+       gdb_test "catch vfork" "Catchpoint $decimal \\(vfork\\)" "insert first vfork catchpoint"
+       gdb_test_multiple "continue" "continue to first vfork catchpoint" {
+           -re ".*Your system does not support this type\r\nof catchpoint.*$gdb_prompt $" {
+               unsupported "continue to first vfork catchpoint"
+               return
+           }
+           -re ".*Catchpoint $decimal \\(vforked process $decimal\\).*$gdb_prompt $" {
+               pass "continue to first vfork catchpoint"
+           }
+       }
+
+       # Check that we now reach vfork return only.
+       # (The actual syscall used varies by architecture.)
+       gdb_test "catch syscall" "Catchpoint $decimal \\(any syscall\\)"
+       check_return_from_syscall "vfork" $vfork_syscalls
+
+       # Can we finish?
+       check_for_program_end
+    }
+}
+
 proc test_catch_syscall_fail_nodatadir {} {
     with_test_prefix "fail no datadir" {
        # Sanitizing.
     # This test should not trigger any catchpoints.
     if [runto_main] then { test_catch_syscall_with_wrong_args }
 
-    # Testing the 'catch' syscall command during a restart of
+    # Testing the 'catch syscall' command during a restart of
     # the inferior.
     if [runto_main] then { test_catch_syscall_restarting_inferior }
 
+    # Testing the 'catch syscall' command toggling off past a
+    # syscall return, then resuming entry/return as normal.
+    if [runto_main] then { test_catch_syscall_skipping_return }
+
+    # Testing the 'catch syscall' command starting mid-vfork.
+    if [runto_main] then { test_catch_syscall_mid_vfork }
+
     # 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 }
     with_test_prefix "without args noxml" {
        # We will need the syscall names even not using it because we
        # need to know know many syscalls are in the example file.
-       global all_syscalls last_syscall_number all_syscalls_numbers
+       global decimal all_syscalls last_syscall_number unknown_syscall_number all_syscalls_numbers
 
        delete_breakpoints
 
            }
        }
 
+       check_continue "vfork" $decimal
+
+       with_test_prefix "ENOSYS" {
+           check_continue $unknown_syscall_number
+       }
+
        # At last but not least, we check if the inferior has called
        # the last (exit) syscall.
        check_call_to_syscall $last_syscall_number
 # This procedure fills the vector "all_syscalls_numbers" with the proper
 # numbers for the used syscalls according to the architecture.
 proc fill_all_syscalls_numbers {} {
-    global all_syscalls_numbers last_syscall_number all_syscalls
+    global all_syscalls_numbers last_syscall_number unknown_syscall_number all_syscalls
 
     foreach syscall $all_syscalls {
        lappend all_syscalls_numbers [get_integer_valueof "${syscall}_syscall" -1]
     }
 
     set last_syscall_number [get_integer_valueof "exit_group_syscall" -1]
+    set unknown_syscall_number [get_integer_valueof "unknown_syscall" -1]
 }
 
 # Set up the vector all_syscalls.