[v4] Implement 'catch syscall' for gdbserver

Message ID 1452308954-13679-1-git-send-email-jistone@redhat.com
State New, archived
Headers

Commit Message

Josh Stone Jan. 9, 2016, 3:09 a.m. UTC
  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-08  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-08  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 detailed list of stub features.

gdb/gdbserver/ChangeLog:

2016-01-08  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-08  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.
---
 gdb/NEWS                                 |  24 +++++
 gdb/doc/gdb.texinfo                      |  61 ++++++++++++
 gdb/gdbserver/inferiors.c                |   1 +
 gdb/gdbserver/inferiors.h                |   6 ++
 gdb/gdbserver/linux-low.c                | 155 ++++++++++++++++++++++++++++++-
 gdb/gdbserver/linux-low.h                |  13 +++
 gdb/gdbserver/linux-x86-low.c            |  26 ++++++
 gdb/gdbserver/remote-utils.c             |  12 +++
 gdb/gdbserver/server.c                   |  51 ++++++++++
 gdb/gdbserver/server.h                   |   6 ++
 gdb/gdbserver/target.h                   |   8 ++
 gdb/linux-nat.h                          |   2 +-
 gdb/remote.c                             | 110 ++++++++++++++++++++++
 gdb/testsuite/gdb.base/catch-syscall.c   |   9 +-
 gdb/testsuite/gdb.base/catch-syscall.exp |  31 ++++++-
 15 files changed, 507 insertions(+), 8 deletions(-)
  

Comments

Eli Zaretskii Jan. 9, 2016, 7:36 a.m. UTC | #1
> From: Josh Stone <jistone@redhat.com>
> Cc: philippe.waroquiers@skynet.be, sergiodj@redhat.com, palves@redhat.com,
>         eliz@gnu.org, xdje42@gmail.com, scox@redhat.com,
>         Josh Stone <jistone@redhat.com>
> Date: Fri,  8 Jan 2016 19:09:14 -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-08  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-08  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 detailed list of stub features.

OK for the documentation parts.

Thanks.
  
Philippe Waroquiers Jan. 11, 2016, 5:46 p.m. UTC | #2
On Fri, 2016-01-08 at 19:09 -0800, Josh Stone wrote:
> 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.
I did some basic tests of the v4 patch on x86,
and also with a (patched) valgrind gdbserver.
No problem encountered.

So, if/when this patch is committed in gdb, I will also commit the catch
syscall support in the valgrind gdbserver (which will provide
catch syscall on all valgrind architectures i.e.
x86/amd64/ppc32/ppc64/mips32/mips64/tilegx/s390x/arm/arm64).

So, awaiting commit ...

Philippe
  
Pedro Alves Jan. 12, 2016, 12:05 p.m. UTC | #3
On 01/09/2016 03:09 AM, Josh Stone wrote:

> 
> 2016-01-08  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 detailed list of stub features.
> 

"table of detailed", I think.


> @@ -648,6 +658,12 @@ 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;
>  
> +      /* Update syscall state in the new lwp, effectively mid-syscall too.
> +	 The client really should send a new list to catch, in case the
> +	 architecture changed, but for ANY_SYSCALL it doesn't matter.  */
> +      event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
> +      proc->syscalls_to_catch = syscalls_to_catch;

The tone of this comment sounds to me as if the client should always
send a new list, just in case, but for some odd reason it sometimes doesn't.

I think we want to convey the opposite, like:

         /* 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;

>  
>  static int
> +linux_supports_catch_syscall (void)
> +{
> +  return (the_low_target.get_syscall_trapinfo != NULL
> +	  && linux_supports_tracesysgood());

Space: "linux_supports_tracesysgood ()"



>  
> +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"
> +
> +	# Remotes that don't track exec may report the raw SIGTRAP for it.
> +	# If we use stepi now, we'll get a consistent trap for all targets.
> +	gdb_test "stepi" ".*" "step after execve"

Why is it important to do this raw SIGTRAP handling?  What happens if you don't
do this?  Won't those targets already FAIL the check_continue tests?

Thanks,
Pedro Alves
  
Josh Stone Jan. 12, 2016, 7:10 p.m. UTC | #4
On 01/12/2016 04:05 AM, Pedro Alves wrote:
> On 01/09/2016 03:09 AM, Josh Stone wrote:
> 
>>
>> 2016-01-08  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 detailed list of stub features.
>>
> 
> "table of detailed", I think.

I'm referring to two hunks:
- the table: Feature Name / Value Required / Default / Probe Allowed
- the list below it, "currently defined stub features, in more detail"

Maybe I just need another article, "to the table and the detailed list"

>> @@ -648,6 +658,12 @@ 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;
>>  
>> +      /* Update syscall state in the new lwp, effectively mid-syscall too.
>> +	 The client really should send a new list to catch, in case the
>> +	 architecture changed, but for ANY_SYSCALL it doesn't matter.  */
>> +      event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
>> +      proc->syscalls_to_catch = syscalls_to_catch;
> 
> The tone of this comment sounds to me as if the client should always
> send a new list, just in case, but for some odd reason it sometimes doesn't.
> 
> I think we want to convey the opposite, like:
> 
>          /* 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;

Sure, I'll take your rewrite verbatim, if you don't mind.

>>  static int
>> +linux_supports_catch_syscall (void)
>> +{
>> +  return (the_low_target.get_syscall_trapinfo != NULL
>> +	  && linux_supports_tracesysgood());
> 
> Space: "linux_supports_tracesysgood ()"

OK

>> +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"
>> +
>> +	# Remotes that don't track exec may report the raw SIGTRAP for it.
>> +	# If we use stepi now, we'll get a consistent trap for all targets.
>> +	gdb_test "stepi" ".*" "step after execve"
> 
> Why is it important to do this raw SIGTRAP handling?  What happens if you don't
> do this?  Won't those targets already FAIL the check_continue tests?

Just in case, the context from Linux man ptrace:

  If the PTRACE_O_TRACEEXEC option is not in effect for the execing
  tracee, and if the tracee was PTRACE_ATTACHed rather that
  PTRACE_SEIZEd, the kernel delivers an extra SIGTRAP to the tracee
  after execve(2) returns.  This is an ordinary signal (similar to
  one which can be generated by kill -TRAP), not a special kind of
  ptrace-stop.

Since that's a signal-stop *after* execve returns, the check_continue
will have succeeded already.

The check_continue is really the only bit I care about for this test
anyway.  The rest is just trying to finish the target process cleanly.
I was having trouble matching consistent output since plain remote was
getting that SIGTRAP, but extended-remote would use exec events and not
report anything extra.  Adding the stepi made both stop the same way.

This is moot now, since plain remotes are now tracking exec events too.
 I developed this test just before that went in last month. :)
I just tried with that stepi commented out, and the test still passes on
local, remote, and extended-remote, so I'll remove it.
  
Pedro Alves Jan. 12, 2016, 7:22 p.m. UTC | #5
On 01/12/2016 07:10 PM, Josh Stone wrote:
> On 01/12/2016 04:05 AM, Pedro Alves wrote:
>> On 01/09/2016 03:09 AM, Josh Stone wrote:
>>
>>>
>>> 2016-01-08  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 detailed list of stub features.
>>>
>>
>> "table of detailed", I think.
> 
> I'm referring to two hunks:
> - the table: Feature Name / Value Required / Default / Probe Allowed
> - the list below it, "currently defined stub features, in more detail"
> 
> Maybe I just need another article, "to the table and the detailed list"

Ah.  Yes, that way I think wouldn't have been confused.

> 
>>> @@ -648,6 +658,12 @@ 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;
>>>  
>>> +      /* Update syscall state in the new lwp, effectively mid-syscall too.
>>> +	 The client really should send a new list to catch, in case the
>>> +	 architecture changed, but for ANY_SYSCALL it doesn't matter.  */
>>> +      event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
>>> +      proc->syscalls_to_catch = syscalls_to_catch;
>>
>> The tone of this comment sounds to me as if the client should always
>> send a new list, just in case, but for some odd reason it sometimes doesn't.
>>
>> I think we want to convey the opposite, like:
>>
>>          /* 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;
> 
> Sure, I'll take your rewrite verbatim, if you don't mind.

Certainly don't mind.


> 
>>>  static int
>>> +linux_supports_catch_syscall (void)
>>> +{
>>> +  return (the_low_target.get_syscall_trapinfo != NULL
>>> +	  && linux_supports_tracesysgood());
>>
>> Space: "linux_supports_tracesysgood ()"
> 
> OK
> 
>>> +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"
>>> +
>>> +	# Remotes that don't track exec may report the raw SIGTRAP for it.
>>> +	# If we use stepi now, we'll get a consistent trap for all targets.
>>> +	gdb_test "stepi" ".*" "step after execve"
>>
>> Why is it important to do this raw SIGTRAP handling?  What happens if you don't
>> do this?  Won't those targets already FAIL the check_continue tests?
> 
> Just in case, the context from Linux man ptrace:
> 
>   If the PTRACE_O_TRACEEXEC option is not in effect for the execing
>   tracee, and if the tracee was PTRACE_ATTACHed rather that
>   PTRACE_SEIZEd, the kernel delivers an extra SIGTRAP to the tracee
>   after execve(2) returns.  This is an ordinary signal (similar to
>   one which can be generated by kill -TRAP), not a special kind of
>   ptrace-stop.
> 
> Since that's a signal-stop *after* execve returns, the check_continue
> will have succeeded already.

Still can't see how that step would help -- check_continue does two
"continue"s.  So one would stop at the random SIGTRAP, and FAIL,
and another would lose control of the inferior, probably running
to end.

> 
> The check_continue is really the only bit I care about for this test
> anyway.  The rest is just trying to finish the target process cleanly.
> I was having trouble matching consistent output since plain remote was
> getting that SIGTRAP, but extended-remote would use exec events and not
> report anything extra.  Adding the stepi made both stop the same way.
> 
> This is moot now, since plain remotes are now tracking exec events too.
>  I developed this test just before that went in last month. :)
> I just tried with that stepi commented out, and the test still passes on
> local, remote, and extended-remote, so I'll remove it.

OK, yes, let's drop it then.  :-)

Patch is OK with these changes, BTW.

Thanks,
Pedro Alves
  
Josh Stone Jan. 12, 2016, 8:01 p.m. UTC | #6
On 01/12/2016 11:22 AM, Pedro Alves wrote:
> On 01/12/2016 07:10 PM, Josh Stone wrote:
>> On 01/12/2016 04:05 AM, Pedro Alves wrote:
>>> On 01/09/2016 03:09 AM, Josh Stone wrote:
>>>
>>>>
>>>> 2016-01-08  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 detailed list of stub features.
>>>>
>>>
>>> "table of detailed", I think.
>>
>> I'm referring to two hunks:
>> - the table: Feature Name / Value Required / Default / Probe Allowed
>> - the list below it, "currently defined stub features, in more detail"
>>
>> Maybe I just need another article, "to the table and the detailed list"
> 
> Ah.  Yes, that way I think wouldn't have been confused.
> 
>>
>>>> @@ -648,6 +658,12 @@ 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;
>>>>  
>>>> +      /* Update syscall state in the new lwp, effectively mid-syscall too.
>>>> +	 The client really should send a new list to catch, in case the
>>>> +	 architecture changed, but for ANY_SYSCALL it doesn't matter.  */
>>>> +      event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
>>>> +      proc->syscalls_to_catch = syscalls_to_catch;
>>>
>>> The tone of this comment sounds to me as if the client should always
>>> send a new list, just in case, but for some odd reason it sometimes doesn't.
>>>
>>> I think we want to convey the opposite, like:
>>>
>>>          /* 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;
>>
>> Sure, I'll take your rewrite verbatim, if you don't mind.
> 
> Certainly don't mind.
> 
> 
>>
>>>>  static int
>>>> +linux_supports_catch_syscall (void)
>>>> +{
>>>> +  return (the_low_target.get_syscall_trapinfo != NULL
>>>> +	  && linux_supports_tracesysgood());
>>>
>>> Space: "linux_supports_tracesysgood ()"
>>
>> OK
>>
>>>> +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"
>>>> +
>>>> +	# Remotes that don't track exec may report the raw SIGTRAP for it.
>>>> +	# If we use stepi now, we'll get a consistent trap for all targets.
>>>> +	gdb_test "stepi" ".*" "step after execve"
>>>
>>> Why is it important to do this raw SIGTRAP handling?  What happens if you don't
>>> do this?  Won't those targets already FAIL the check_continue tests?
>>
>> Just in case, the context from Linux man ptrace:
>>
>>   If the PTRACE_O_TRACEEXEC option is not in effect for the execing
>>   tracee, and if the tracee was PTRACE_ATTACHed rather that
>>   PTRACE_SEIZEd, the kernel delivers an extra SIGTRAP to the tracee
>>   after execve(2) returns.  This is an ordinary signal (similar to
>>   one which can be generated by kill -TRAP), not a special kind of
>>   ptrace-stop.
>>
>> Since that's a signal-stop *after* execve returns, the check_continue
>> will have succeeded already.
> 
> Still can't see how that step would help -- check_continue does two
> "continue"s.  So one would stop at the random SIGTRAP, and FAIL,
> and another would lose control of the inferior, probably running
> to end.

The first continue hits syscall_entry:execve.  The second continue hits
syscall_return:execve.  The second is the bug I fixed -- if it forgot
syscall_state across exec, the return was reported like another entry.

After that, there might be an impending SIGTRAP stop, depending on
whether PTRACE_O_TRACEEXEC is active.  The step was a way of just
effecting a SIGTRAP for everyone, so it was easy to match.

>> The check_continue is really the only bit I care about for this test
>> anyway.  The rest is just trying to finish the target process cleanly.
>> I was having trouble matching consistent output since plain remote was
>> getting that SIGTRAP, but extended-remote would use exec events and not
>> report anything extra.  Adding the stepi made both stop the same way.
>>
>> This is moot now, since plain remotes are now tracking exec events too.
>>  I developed this test just before that went in last month. :)
>> I just tried with that stepi commented out, and the test still passes on
>> local, remote, and extended-remote, so I'll remove it.
> 
> OK, yes, let's drop it then.  :-)
> 
> Patch is OK with these changes, BTW.

Great!  I'll make these final tweaks and push it out.
  
Yao Qi March 29, 2016, 2:26 p.m. UTC | #7
Josh Stone <jistone@redhat.com> writes:

> Great!  I'll make these final tweaks and push it out.

Hi Josh,
This commit causes some fails on s39x, and ppc, as shown in buildbot.  I
saw them in aarch64 test as well.  Could you take a look?

https://sourceware.org/ml/gdb-testers/2016-q1/msg01544.html
https://sourceware.org/ml/gdb-testers/2016-q1/msg01601.html
  
Josh Stone March 29, 2016, 6:12 p.m. UTC | #8
On 03/29/2016 07:26 AM, Yao Qi wrote:
> Hi Josh,
> This commit causes some fails on s39x, and ppc, as shown in buildbot.  I
> saw them in aarch64 test as well.  Could you take a look?
> 
> https://sourceware.org/ml/gdb-testers/2016-q1/msg01544.html
> https://sourceware.org/ml/gdb-testers/2016-q1/msg01601.html

Hmm.  That new test is meant to make sure that the syscall entry/return
state is not lost across an execve.  In the failure I knew, the execve
return was interpreted as an additional syscall entry.  But in the cases
you've shown, it didn't catch syscall return from execve at all!

I will investigate more...

Josh
  
Josh Stone March 29, 2016, 11:49 p.m. UTC | #9
On 03/29/2016 11:12 AM, Josh Stone wrote:
> On 03/29/2016 07:26 AM, Yao Qi wrote:
>> Hi Josh,
>> This commit causes some fails on s39x, and ppc, as shown in buildbot.  I
>> saw them in aarch64 test as well.  Could you take a look?
>>
>> https://sourceware.org/ml/gdb-testers/2016-q1/msg01544.html
>> https://sourceware.org/ml/gdb-testers/2016-q1/msg01601.html
> 
> Hmm.  That new test is meant to make sure that the syscall entry/return
> state is not lost across an execve.  In the failure I knew, the execve
> return was interpreted as an additional syscall entry.  But in the cases
> you've shown, it didn't catch syscall return from execve at all!
> 
> I will investigate more...

So, it seems those architectures don't preserve their original syscall
numbers across an execve.

$ gdb -ex 'catch syscall execve' -ex 'run' -ex 'catch syscall' \
  -ex 'continue' --args sh -c /bin/true

PPC64 and Aarch64 both read their syscall numbers from registers, and
here they both get 0 ("restart_syscall" and "io_setup" respectively).
S390X tries to decode it from the SVC instruction at PC-2, which will
definitely fail after an execve -- gdb reports syscall -1.

So when the catchpoint is only for execve, they continue past this one
since the number doesn't look like execve.

The good news is that all three do call it a syscall *return*, which was
the main point of this particular test.  If there's no objection, I can
try to update the test to work more like my command above, matching any
syscall at all on the return side of execve.
  
Yao Qi March 30, 2016, 12:23 p.m. UTC | #10
Josh Stone <jistone@redhat.com> writes:

Hi Josh,
Thanks for the looking into this...

> So, it seems those architectures don't preserve their original syscall
> numbers across an execve.
>
> $ gdb -ex 'catch syscall execve' -ex 'run' -ex 'catch syscall' \
>   -ex 'continue' --args sh -c /bin/true
>
> PPC64 and Aarch64 both read their syscall numbers from registers, and
> here they both get 0 ("restart_syscall" and "io_setup" respectively).
> S390X tries to decode it from the SVC instruction at PC-2, which will
> definitely fail after an execve -- gdb reports syscall -1.

I think it reveals a bug on getting syscall number.  If the register
having syscall number isn't preserved across the syscall, GDB should
read syscall number somewhere else.

>
> So when the catchpoint is only for execve, they continue past this one
> since the number doesn't look like execve.
>
> The good news is that all three do call it a syscall *return*, which was
> the main point of this particular test.  If there's no objection, I can
> try to update the test to work more like my command above, matching any
> syscall at all on the return side of execve.
  
Josh Stone March 31, 2016, 1:09 a.m. UTC | #11
On 03/30/2016 05:23 AM, Yao Qi wrote:
> Josh Stone <jistone@redhat.com> writes:
> 
> Hi Josh,
> Thanks for the looking into this...
> 
>> So, it seems those architectures don't preserve their original syscall
>> numbers across an execve.
>>
>> $ gdb -ex 'catch syscall execve' -ex 'run' -ex 'catch syscall' \
>>   -ex 'continue' --args sh -c /bin/true
>>
>> PPC64 and Aarch64 both read their syscall numbers from registers, and
>> here they both get 0 ("restart_syscall" and "io_setup" respectively).
>> S390X tries to decode it from the SVC instruction at PC-2, which will
>> definitely fail after an execve -- gdb reports syscall -1.
> 
> I think it reveals a bug on getting syscall number.  If the register
> having syscall number isn't preserved across the syscall, GDB should
> read syscall number somewhere else.

Well, sure, but I have no idea where that somewhere else could be.

Note these architectures do work for other syscall returns.  It's just
that execve is a bit special by switching the whole process out.

I suppose we could try to save the number on syscall entry, and just
report that again when it returns.  But it's not 100% sure that we'll
see every entry first.  For instance, one could 'catch execve' first,
which will continue until PTRACE_EVENT_EXEC mid-syscall, then turn on
'catch syscall' and see what returns.
(This is similar to what test_catch_syscall_mid_vfork checks.)


BTW, even x86 is a little suspect if you cross compat modes.  The number
is preserved in orig_rax, but if you exec'ed from a 64-bit process to
32-bit, that number would still be the 64-bit NR_execve.  It happens to
still apparently work in that case because gdb isn't reloading its
syscall mapping.  But continue and it next gets:

Catchpoint 2 (call to syscall recvfrom), 0xf7ff29b9 in brk () from
/lib/ld-linux.so.2

i.e. 32-bit syscall brk is incorrectly called recvfrom.

>> So when the catchpoint is only for execve, they continue past this one
>> since the number doesn't look like execve.
>>
>> The good news is that all three do call it a syscall *return*, which was
>> the main point of this particular test.  If there's no objection, I can
>> try to update the test to work more like my command above, matching any
>> syscall at all on the return side of execve.
>
  
Yao Qi April 1, 2016, 1:05 p.m. UTC | #12
Josh Stone <jistone@redhat.com> writes:

> Well, sure, but I have no idea where that somewhere else could be.
>
> Note these architectures do work for other syscall returns.  It's just
> that execve is a bit special by switching the whole process out.

I talked with a kernel people, looks GDB can't fetch the syscall number
on execve exit.

>
> I suppose we could try to save the number on syscall entry, and just
> report that again when it returns.  But it's not 100% sure that we'll

Yes, that is what I am thinking too.

> see every entry first.  For instance, one could 'catch execve' first,
> which will continue until PTRACE_EVENT_EXEC mid-syscall, then turn on
> 'catch syscall' and see what returns.
> (This is similar to what test_catch_syscall_mid_vfork checks.)

Yeah, we can't guarantee syscall enter comes into first.  I doubt
'lp->syscall_state' is confused in this case too, no?
  
Josh Stone April 1, 2016, 4:38 p.m. UTC | #13
On 04/01/2016 06:05 AM, Yao Qi wrote:
> Josh Stone <jistone@redhat.com> writes:
> 
>> Well, sure, but I have no idea where that somewhere else could be.
>>
>> Note these architectures do work for other syscall returns.  It's just
>> that execve is a bit special by switching the whole process out.
> 
> I talked with a kernel people, looks GDB can't fetch the syscall number
> on execve exit.
> 
>>
>> I suppose we could try to save the number on syscall entry, and just
>> report that again when it returns.  But it's not 100% sure that we'll
> 
> Yes, that is what I am thinking too.

I guess then it's a question whether we try to save this as much as
possible, or only for cases that look like execve (on entry).

>> see every entry first.  For instance, one could 'catch execve' first,
>> which will continue until PTRACE_EVENT_EXEC mid-syscall, then turn on
>> 'catch syscall' and see what returns.
>> (This is similar to what test_catch_syscall_mid_vfork checks.)
> 
> Yeah, we can't guarantee syscall enter comes into first.  I doubt
> 'lp->syscall_state' is confused in this case too, no?

This is one of the things I fixed.  We know that events like
PTRACE_EVENT_EXEC are mid-syscall, even if we didn't see the entry, so a
syscall event that follows must be a return.  And other ptrace stops are
never mid-syscall, so a syscall that follows those must be an entry.
This is set in linux_handle_extended_wait and wait_lwp.
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 484d98d24143..adb4d9790065 100644
--- 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.
 
+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.
@@ -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.
 
+* 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
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 0778383280a7..e18b27ec4315 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20261,6 +20261,10 @@  are:
 @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}}
@@ -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.
 
+@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.
@@ -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}.
 
+@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
@@ -36523,6 +36575,11 @@  These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab Yes
 
+@item @samp{QCatchSyscalls}
+@tab No
+@tab @samp{-}
+@tab Yes
+
 @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}).
 
+@item QCatchSyscalls
+The remote stub understands the @samp{QCatchSyscalls} packet
+(@pxref{QCatchSyscalls}).
+
 @item QPassSignals
 The remote stub understands the @samp{QPassSignals} packet
 (@pxref{QPassSignals}).
diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c
index c884b55f383a..4bea4fd7a91b 100644
--- a/gdb/gdbserver/inferiors.c
+++ b/gdb/gdbserver/inferiors.c
@@ -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);
+  VEC_free (int, process->syscalls_to_catch);
   free (process);
 }
 
diff --git a/gdb/gdbserver/inferiors.h b/gdb/gdbserver/inferiors.h
index af65718ad4f9..00dfe60e0c5f 100644
--- a/gdb/gdbserver/inferiors.h
+++ b/gdb/gdbserver/inferiors.h
@@ -19,6 +19,8 @@ 
 #ifndef INFERIORS_H
 #define INFERIORS_H
 
+#include "gdb_vecs.h"
+
 /* 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 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.  */
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 4f8f57392101..c787309b53a0 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -461,6 +461,11 @@  handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
 
   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))
     {
@@ -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;
+      VEC (int) *syscalls_to_catch;
       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);
 
-      /* Delete the execing process and all its threads.  */
+      /* Save the syscall list from the execing process.  */
       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;
 
@@ -648,6 +658,12 @@  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;
 
+      /* Update syscall state in the new lwp, effectively mid-syscall too.
+	 The client really should send a new list to catch, in case the
+	 architecture changed, but for ANY_SYSCALL it doesn't matter.  */
+      event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
+      proc->syscalls_to_catch = syscalls_to_catch;
+
       /* Report the event.  */
       *orig_event_lwp = event_lwp;
       return 0;
@@ -682,6 +698,40 @@  get_pc (struct lwp_info *lwp)
   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.
 
@@ -2236,6 +2286,8 @@  linux_low_ptrace_options (int attached)
   if (report_exec_events)
     options |= PTRACE_O_TRACEEXEC;
 
+  options |= PTRACE_O_TRACESYSGOOD;
+
   return options;
 }
 
@@ -2364,6 +2416,21 @@  linux_low_filter_event (int lwpid, int wstat)
       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
@@ -2973,6 +3040,44 @@  filter_exit_event (struct lwp_info *event_child,
   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
@@ -3307,6 +3412,22 @@  linux_wait_1 (ptid_t ptid,
 
   /* 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
@@ -3559,8 +3680,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
@@ -4017,6 +4146,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;
+  int ptrace_request;
   struct process_info *proc = get_thread_process (thread);
 
   /* Note that target description may not be initialised
@@ -4204,7 +4334,14 @@  linux_resume_one_lwp_throw (struct lwp_info *lwp,
   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.  */
@@ -6286,6 +6423,13 @@  linux_process_qsupported (char **features, int count)
 }
 
 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)
@@ -7209,7 +7353,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_supports_software_single_step
+  linux_supports_software_single_step,
+  linux_supports_catch_syscall,
 };
 
 #ifdef HAVE_LINUX_REGSETS
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index e80c67f54097..751eea18eaf8 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -240,6 +240,12 @@  struct linux_target_ops
 
   /* 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;
@@ -278,6 +284,13 @@  struct lwp_info
      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;
 
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 0432a86aab2b..4a01750c1724 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -1438,6 +1438,31 @@  x86_arch_setup (void)
   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)
 {
@@ -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_get_syscall_trapinfo,
 };
 
 void
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index c5f4647052c0..ec69b63c32c6 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -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_SYSCALL_ENTRY:
+    case TARGET_WAITKIND_SYSCALL_RETURN:
       {
 	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);
 	  }
+	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);
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index fe7195ddab09..13fe159e4bef 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -624,6 +624,54 @@  handle_general_set (char *own_buf)
       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)
@@ -2200,6 +2248,9 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	       "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+");
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 58cf342ed368..3d78fb319514 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -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
 
+/* 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 */
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index cbdb8d98d221..5af205139d1d 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -467,6 +467,10 @@  struct target_ops
 
   /* 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;
@@ -542,6 +546,10 @@  int kill_inferior (int);
 	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)
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index 7bdeb314a663..73888e5f3364 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -88,7 +88,7 @@  struct lwp_info
      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
diff --git a/gdb/remote.c b/gdb/remote.c
index 528d863762cc..e825d27d8405 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1392,6 +1392,7 @@  enum {
   PACKET_qSupported,
   PACKET_qTStatus,
   PACKET_QPassSignals,
+  PACKET_QCatchSyscalls,
   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.  */
 
@@ -4467,6 +4555,8 @@  static const struct protocol_feature remote_protocol_features[] = {
     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,
@@ -6371,6 +6461,22 @@  Packet: '%s'\n"),
 
 	  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"))
@@ -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_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;
@@ -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_QCatchSyscalls],
+			 "QCatchSyscalls", "catch-syscalls", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_QProgramSignals],
 			 "QProgramSignals", "program-signals", 0);
 
diff --git a/gdb/testsuite/gdb.base/catch-syscall.c b/gdb/testsuite/gdb.base/catch-syscall.c
index e65d4a4906c3..98222fa78819 100644
--- a/gdb/testsuite/gdb.base/catch-syscall.c
+++ b/gdb/testsuite/gdb.base/catch-syscall.c
@@ -31,13 +31,20 @@  int write_syscall = SYS_write;
 int unknown_syscall = 123456789;
 int exit_group_syscall = SYS_exit_group;
 
+/* Set by the test when it wants execve.  */
+int do_execve = 0;
+
 int
-main (void)
+main (int argc, char *const argv[])
 {
 	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);
diff --git a/gdb/testsuite/gdb.base/catch-syscall.exp b/gdb/testsuite/gdb.base/catch-syscall.exp
index 26fb6a513a42..70a75553e945 100644
--- a/gdb/testsuite/gdb.base/catch-syscall.exp
+++ b/gdb/testsuite/gdb.base/catch-syscall.exp
@@ -19,7 +19,7 @@ 
 # 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
 }
 
@@ -322,6 +322,32 @@  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"
+
+	# Remotes that don't track exec may report the raw SIGTRAP for it.
+	# If we use stepi now, we'll get a consistent trap for all targets.
+	gdb_test "stepi" ".*" "step after 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.
@@ -392,6 +418,9 @@  proc do_syscall_tests {} {
     # 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 }