[RFC] reviving 'catch syscall' for gdbserver

Message ID 55F3838A.4010005@redhat.com
State New, archived
Headers

Commit Message

Josh Stone Sept. 12, 2015, 1:44 a.m. UTC
  Hi all,

I've been using Philippe's QCatchSyscalls patch[1] for gdbserver, and
I'd like to reopen the discussion about merging this patch.  Philippe
told me would be fine with me taking this up, as he doesn't have time to
work on it now.

[1] https://sourceware.org/ml/gdb-patches/2013-09/msg00992.html

I've rebased the patch onto current gdb.git master, attached.  Some
things are simplified by various refactoring done in the last two years,
but otherwise I didn't change any capability from what he had.

I haven't included formalities like ChangeLogs here, because I would
like to get basic consensus on the approach first.  I also don't know
the right way to approach authorship here, between what Philippe
originally wrote and my changes since, for what's ultimately committed.

I tested catch-syscall.exp on Fedora 22 x86_64, for targets unix,
native-gdbserver, and native-extended-gdbserver, and all passed.

Philippe pointed me to his last summary of issues, which I'd like to
start the discussion with:
https://sourceware.org/ml/gdb-patches/2013-10/msg00321.html

> * QCatchSyscalls contains target specific numbers (this is the
>   above comment)
>   => have gdbserver handling QCatchSyscalls packet per inferior

Does this still need to be per-inferior?  I do understand that syscall
numbers may differ, e.g. from i686 to x86_64 on the same target.  Are
there any other examples of such things that are dealt with separately?

> * ensure QCatchSyscall packet can (in the future) be extended with
>   a COND_LIST (similar to the Z packets).
>   To do that, I suggest to have the QCatchSyscalls separating syscall
>   numbers with a , rather than a ;
>   (so that a ; can be used later to separate the list of syscalls
>    from the COND_LIST)
>   Note: Luis suggested the alternative to have a packet
>   QInsertCatchPoint:[fork|syscall|exec|...]
>   Then gdbserver will tell in QSupported that it e.g. support
>      QInsertCatchPoint=syscall,fork
> 
>   For what concerns the problem of identifying which catchpoint
>   to remove in the QRemoveCatchPoint: not too sure we need
>   an catch point id for that. We can assume that an QInsertCatchPoint
>   of a certain kind fully replace the previously inserted catchpoint
>   of the same kind. A QRemoveCatchpoint removes completely
>   the catchpoint of the same kind.
>   
>   I can go the QInsertCatchPoint way if it is confirmed this is a better
>   approach.

I notice that fork and vfork events have since been implemented on their
own as simple qSupported flags.  So I guess this idea of a generic
QInsertCatchPoint idea was dropped?

Maybe syscalls should similarly use a qSupported syscall-events flag for
basic enabling.  Then QCatchSyscalls would only be used to select
specific syscalls, sort of how QPassSignals is used.

> * Need to investigate the bug in gdb 'catch syscall' flip/flop logic.
>   If this logic can be fixed, then have gdbserver and gdb using
>   the same logic.

I guess this is referring gdb's simple toggling, vs gdbserver's ENOSYS
method of detecting syscall entry vs return.  Can someone point to the
bug in gdb's flip/flop?  (strace is also a toggler, btw)

FWIW, I think it is possible for Linux to know this precisely.  AFAIK
the only way to catch a syscall return is with PTRACE_SYSCALL from a
syscall entry *or* from one of the mid-syscall events, like a
PTRACE_EVENT_FORK.  Any other time, a syscall stop can only be entry.

I can see how flip/flop might get confused if 'catch syscall' was only
enabled when the target was already stopped on that fork, for instance.
 But if you mark that fork like being in an entry, then it will toggle
to return correctly afterward.  Enabling 'catch syscall' at any other
time should start at IGNORE, so the next is then entry.

But I expect I'm missing things.  I'd like to hear what other demons are
lurking around this...

(I also know Sergio was tinkering with a ptrace patch to distinguish
entry/return precisely in the kernel, which would be nice for all.)

> * extend the stop reply packet to allow to return a
>     "syscall" stop reason that does not specify if this is a syscall
>     entry or exit.
>   I suggest to do this even if a correct flip/flop logic can be
>   found during the previous investigation.
>   This 3rd syscall stop reason allow stubs to report a syscall
>   without necessarily having the logic to differentiate entry
>   from return.

I'm not keen on this, personally.  If a stub can't keep proper track of
this, it seems unlikely that the remote gdb can do it better.

I'm sure I'm probably missing some points from only having skimmed the
rest of the prior threads.  Please do let me know.

I look forward to any feedback!

Thanks,
Josh
  

Comments

Sergio Durigan Junior Sept. 12, 2015, 2:27 a.m. UTC | #1
On Friday, September 11 2015, Josh Stone wrote:

> Hi all,
>
> I've been using Philippe's QCatchSyscalls patch[1] for gdbserver, and
> I'd like to reopen the discussion about merging this patch.  Philippe
> told me would be fine with me taking this up, as he doesn't have time to
> work on it now.
>
> [1] https://sourceware.org/ml/gdb-patches/2013-09/msg00992.html
>
> I've rebased the patch onto current gdb.git master, attached.  Some
> things are simplified by various refactoring done in the last two years,
> but otherwise I didn't change any capability from what he had.
>
> I haven't included formalities like ChangeLogs here, because I would
> like to get basic consensus on the approach first.  I also don't know
> the right way to approach authorship here, between what Philippe
> originally wrote and my changes since, for what's ultimately committed.
>
> I tested catch-syscall.exp on Fedora 22 x86_64, for targets unix,
> native-gdbserver, and native-extended-gdbserver, and all passed.
>
> Philippe pointed me to his last summary of issues, which I'd like to
> start the discussion with:
> https://sourceware.org/ml/gdb-patches/2013-10/msg00321.html

Hi Josh,

First of all, thanks for the patch and for reviving.  This e-mail is not
really an in-depth reply; just a few things I would like to mention from
the top of my head.

>> * QCatchSyscalls contains target specific numbers (this is the
>>   above comment)
>>   => have gdbserver handling QCatchSyscalls packet per inferior
>
> Does this still need to be per-inferior?  I do understand that syscall
> numbers may differ, e.g. from i686 to x86_64 on the same target.  Are
> there any other examples of such things that are dealt with separately?

I pushed:

  <https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=458c8db89f7e9913da6fa67c3df73404375c436b>

  (discussion: <https://sourceware.org/ml/gdb-patches/2014-11/msg00227.html>)

meanwhile, which should help address this issue (though it doesn't fix
the whole problem).  You might be interested in reading
https://sourceware.org/bugzilla/show_bug.cgi?id=10737 as well.

>> * ensure QCatchSyscall packet can (in the future) be extended with
>>   a COND_LIST (similar to the Z packets).
>>   To do that, I suggest to have the QCatchSyscalls separating syscall
>>   numbers with a , rather than a ;
>>   (so that a ; can be used later to separate the list of syscalls
>>    from the COND_LIST)
>>   Note: Luis suggested the alternative to have a packet
>>   QInsertCatchPoint:[fork|syscall|exec|...]
>>   Then gdbserver will tell in QSupported that it e.g. support
>>      QInsertCatchPoint=syscall,fork
>> 
>>   For what concerns the problem of identifying which catchpoint
>>   to remove in the QRemoveCatchPoint: not too sure we need
>>   an catch point id for that. We can assume that an QInsertCatchPoint
>>   of a certain kind fully replace the previously inserted catchpoint
>>   of the same kind. A QRemoveCatchpoint removes completely
>>   the catchpoint of the same kind.
>>   
>>   I can go the QInsertCatchPoint way if it is confirmed this is a better
>>   approach.
>
> I notice that fork and vfork events have since been implemented on their
> own as simple qSupported flags.  So I guess this idea of a generic
> QInsertCatchPoint idea was dropped?

That's my understanding as well.  I'd say go ahead with QCatchSyscalls.

>> * Need to investigate the bug in gdb 'catch syscall' flip/flop logic.
>>   If this logic can be fixed, then have gdbserver and gdb using
>>   the same logic.
>
> I guess this is referring gdb's simple toggling, vs gdbserver's ENOSYS
> method of detecting syscall entry vs return.  Can someone point to the
> bug in gdb's flip/flop?  (strace is also a toggler, btw)
>
> FWIW, I think it is possible for Linux to know this precisely.  AFAIK
> the only way to catch a syscall return is with PTRACE_SYSCALL from a
> syscall entry *or* from one of the mid-syscall events, like a
> PTRACE_EVENT_FORK.  Any other time, a syscall stop can only be entry.
>
> I can see how flip/flop might get confused if 'catch syscall' was only
> enabled when the target was already stopped on that fork, for instance.
>  But if you mark that fork like being in an entry, then it will toggle
> to return correctly afterward.  Enabling 'catch syscall' at any other
> time should start at IGNORE, so the next is then entry.
>
> But I expect I'm missing things.  I'd like to hear what other demons are
> lurking around this...
>
> (I also know Sergio was tinkering with a ptrace patch to distinguish
> entry/return precisely in the kernel, which would be nice for all.)

I'll see if I can have some time this weekend to resume my work on this.

Thanks,
  
Philippe Waroquiers Sept. 15, 2015, 10:01 p.m. UTC | #2
On Fri, 2015-09-11 at 18:44 -0700, Josh Stone wrote:
> Hi all,
> 
> I've been using Philippe's QCatchSyscalls patch[1] for gdbserver, and
> I'd like to reopen the discussion about merging this patch.  Philippe
> told me would be fine with me taking this up, as he doesn't have time to
> work on it now.
Thanks for reviving this.

> I haven't included formalities like ChangeLogs here, because I would
> like to get basic consensus on the approach first.  I also don't know
> the right way to approach authorship here, between what Philippe
> originally wrote and my changes since, for what's ultimately committed.
For authorship : there is no problem to re-use what you want from the
old patch (this old patch is covered by the copyright assignment I
signed).
So, feel free to re-use and commit what you want.
If you think you better keep my name around that, I will be more
than happy with something like:
'(starting from some work done by Philippe)'
 

> 
> I tested catch-syscall.exp on Fedora 22 x86_64, for targets unix,
> native-gdbserver, and native-extended-gdbserver, and all passed.
> 
> Philippe pointed me to his last summary of issues, which I'd like to
> start the discussion with:
> https://sourceware.org/ml/gdb-patches/2013-10/msg00321.html
> 
> > * QCatchSyscalls contains target specific numbers (this is the
> >   above comment)
> >   => have gdbserver handling QCatchSyscalls packet per inferior
> 
> Does this still need to be per-inferior?  I do understand that syscall
> numbers may differ, e.g. from i686 to x86_64 on the same target.  Are
> there any other examples of such things that are dealt with separately?

I am not sure to understand how GDB handles catch syscall 
when it has multiple inferiors of different archs
(in a 'non-remote' setup).
When looking at catch_syscall_command_1, I do not understand
how a single catch syscall command would be translated into
different numbers depending on the arch: catch syscall seems
to just work for the current arch, translating a name to 
a nr, and then working/storing this number (or VEC of numbers).
So, not sure about the behaviour in a 'non-remote' setup.
(unless a 'catch syscall' is associated to an inferior.
Then the 'info breakpoint' does not show that the catch syscall
is for a specific inferior:
6       catchpoint     keep y                      syscall "mprotect" 
7       catchpoint     keep y                      syscall "unlink" 
8       breakpoint     keep y   0x08048457         in main at xadd.c:35 inf 2
As you can see, breakpoint 8 is marked as being for 'inf 2';
while the catchpoint are not associated to an inferior.

In any case, assuming that GDB handles properly catch syscall for
different archs, then I think that the change needed is to update
the catchsyscall packet, so as to include the pid in the packet.
Then at gdbserver side, the list of syscall to catch has to be stored
per inferior (in the  struct process_info of gdbserver/inferiors.h)
instead of being a global variable.

> > * extend the stop reply packet to allow to return a
> >     "syscall" stop reason that does not specify if this is a syscall
> >     entry or exit.
> >   I suggest to do this even if a correct flip/flop logic can be
> >   found during the previous investigation.
> >   This 3rd syscall stop reason allow stubs to report a syscall
> >   without necessarily having the logic to differentiate entry
> >   from return.
> 
> I'm not keen on this, personally.  If a stub can't keep proper track of
> this, it seems unlikely that the remote gdb can do it better.
The idea was to allow a (simple) stub to just report it has
encountered a syscall, when the stub cannot differentiate an entry
from an exit. But I guess we can always add this new stop
reason in the future, when we see this would be needed (and such
a stub is encountered).

Philippe
  
Josh Stone Sept. 19, 2015, 12:28 a.m. UTC | #3
On 09/11/2015 07:27 PM, Sergio Durigan Junior wrote:
> Hi Josh,
> 
> First of all, thanks for the patch and for reviving.  This e-mail is not
> really an in-depth reply; just a few things I would like to mention from
> the top of my head.

Thanks!

>>> * QCatchSyscalls contains target specific numbers (this is the
>>>   above comment)
>>>   => have gdbserver handling QCatchSyscalls packet per inferior
>>
>> Does this still need to be per-inferior?  I do understand that syscall
>> numbers may differ, e.g. from i686 to x86_64 on the same target.  Are
>> there any other examples of such things that are dealt with separately?
> 
> I pushed:
> 
>   <https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=458c8db89f7e9913da6fa67c3df73404375c436b>
> 
>   (discussion: <https://sourceware.org/ml/gdb-patches/2014-11/msg00227.html>)
> 
> meanwhile, which should help address this issue (though it doesn't fix
> the whole problem).  You might be interested in reading
> https://sourceware.org/bugzilla/show_bug.cgi?id=10737 as well.

AIUI, you made the syscall tables per-arch, which is certainly good.
But what happens when there are multiple inferiors of different archs?
Are the syscall catchpoints per-inferior?

It occurred to me that exec switching archs would be a problem, which
Pedro also mentioned in PR10737#c5.  So either gdbserver needs to be
told syscalls by name, and remap them on exec, or the client needs an
exec event so it can update a new QCatchSyscalls set itself.

I suppose the client could catch the exec syscall directly, but surely
it would be better to have a event based on PTRACE_EVENT_EXEC.
Something to add in the style of fork-event?
  
Josh Stone Sept. 19, 2015, 12:42 a.m. UTC | #4
On 09/15/2015 03:01 PM, Philippe Waroquiers wrote:
> On Fri, 2015-09-11 at 18:44 -0700, Josh Stone wrote:
>> Hi all,
>>
>> I've been using Philippe's QCatchSyscalls patch[1] for gdbserver, and
>> I'd like to reopen the discussion about merging this patch.  Philippe
>> told me would be fine with me taking this up, as he doesn't have time to
>> work on it now.
> Thanks for reviving this.
> 
>> I haven't included formalities like ChangeLogs here, because I would
>> like to get basic consensus on the approach first.  I also don't know
>> the right way to approach authorship here, between what Philippe
>> originally wrote and my changes since, for what's ultimately committed.
> For authorship : there is no problem to re-use what you want from the
> old patch (this old patch is covered by the copyright assignment I
> signed).
> So, feel free to re-use and commit what you want.
> If you think you better keep my name around that, I will be more
> than happy with something like:
> '(starting from some work done by Philippe)'

Copyright assignment covers the legal requirement, I guess, but there's
still plain courtesy. :)  I'll try to credit you somewhere.

>> I tested catch-syscall.exp on Fedora 22 x86_64, for targets unix,
>> native-gdbserver, and native-extended-gdbserver, and all passed.
>>
>> Philippe pointed me to his last summary of issues, which I'd like to
>> start the discussion with:
>> https://sourceware.org/ml/gdb-patches/2013-10/msg00321.html
>>
>>> * QCatchSyscalls contains target specific numbers (this is the
>>>   above comment)
>>>   => have gdbserver handling QCatchSyscalls packet per inferior
>>
>> Does this still need to be per-inferior?  I do understand that syscall
>> numbers may differ, e.g. from i686 to x86_64 on the same target.  Are
>> there any other examples of such things that are dealt with separately?
> 
> I am not sure to understand how GDB handles catch syscall 
> when it has multiple inferiors of different archs
> (in a 'non-remote' setup).
> When looking at catch_syscall_command_1, I do not understand
> how a single catch syscall command would be translated into
> different numbers depending on the arch: catch syscall seems
> to just work for the current arch, translating a name to 
> a nr, and then working/storing this number (or VEC of numbers).
> So, not sure about the behaviour in a 'non-remote' setup.
> (unless a 'catch syscall' is associated to an inferior.
> Then the 'info breakpoint' does not show that the catch syscall
> is for a specific inferior:
> 6       catchpoint     keep y                      syscall "mprotect" 
> 7       catchpoint     keep y                      syscall "unlink" 
> 8       breakpoint     keep y   0x08048457         in main at xadd.c:35 inf 2
> As you can see, breakpoint 8 is marked as being for 'inf 2';
> while the catchpoint are not associated to an inferior.

I just noticed in PR10737#c5, Pedro said, "syscall catchpoints is
presently inferior-specific".  But it's not clear to me if he meant the
entire existence of the catchpoint, or just name->NR mapping.

> In any case, assuming that GDB handles properly catch syscall for
> different archs, then I think that the change needed is to update
> the catchsyscall packet, so as to include the pid in the packet.
> Then at gdbserver side, the list of syscall to catch has to be stored
> per inferior (in the  struct process_info of gdbserver/inferiors.h)
> instead of being a global variable.

What about relying on the current target, and use Hg switching?  Or is
it more common for new commands to have an explicit pid?  It doesn't
look like many q packets have a pid, even though many have an action
specific to one target.

>>> * extend the stop reply packet to allow to return a
>>>     "syscall" stop reason that does not specify if this is a syscall
>>>     entry or exit.
>>>   I suggest to do this even if a correct flip/flop logic can be
>>>   found during the previous investigation.
>>>   This 3rd syscall stop reason allow stubs to report a syscall
>>>   without necessarily having the logic to differentiate entry
>>>   from return.
>>
>> I'm not keen on this, personally.  If a stub can't keep proper track of
>> this, it seems unlikely that the remote gdb can do it better.
> The idea was to allow a (simple) stub to just report it has
> encountered a syscall, when the stub cannot differentiate an entry
> from an exit. But I guess we can always add this new stop
> reason in the future, when we see this would be needed (and such
> a stub is encountered).

Yeah, I think that should be left for a demonstrated need.  Even a
simple stub that just toggled entry/return can probably track this
better than gdb could from afar.
  
Philippe Waroquiers Sept. 20, 2015, 4:53 p.m. UTC | #5
On Fri, 2015-09-18 at 17:42 -0700, Josh Stone wrote:
> On 09/15/2015 03:01 PM, Philippe Waroquiers wrote:
> > On Fri, 2015-09-11 at 18:44 -0700, Josh Stone wrote:
> What about relying on the current target, and use Hg switching?  Or is
> it more common for new commands to have an explicit pid?  It doesn't
> look like many q packets have a pid, even though many have an action
> specific to one target.
Yes, that will work, and is effectively used for many
operations/packets.

Note that as far as I can see, gdb + gdbserver + multiple arch
inferiors + breakpoints seems somewhat broken.

E.g. I tried 7.10 gdb+gdbserver --multi, with 2 inferiors
of different archs : x86 (badseg) and amd64 (trivialleak).
The way breakpoints are handled seems buggy to me.
E.g. at some point, I had:
  (gdb) info break
  Num     Type           Disp Enb Address            What
  2       breakpoint     keep y   0x080484f9         in main at badseg.c:13 inf 2
  3       breakpoint     keep y   0x000000000040053a in main at trivialleak.c:11 inf 1
(breakpoints set using *0x....)

So, a breakpoint for 'inf 2' and a breakpoint for 'inf 1'.
Then when doing run, gdb reported:
  Warning:
  Cannot insert breakpoint 2.
  Cannot access memory at address 0x80484f9
So, GDB (wronly IMO) tried to insert a breakpoint aimed at the x86 inferior
in the amd64 inferior.

Same problem when running the x86 target: it could not insert the amd64 breakpoint.
What is even more strange is that after that, the info breakpoints gives:
  (gdb) info break
  Num     Type           Disp Enb Address            What
  2       breakpoint     keep y   0x080484f9         in main at badseg.c:13 inf 2
  3       breakpoint     keep y   0x000000000040053a  inf 2
So, the breakpoint 3 'was lost' for inferior 1, and was 'transferred' to inferior 2.

I also searched for the regexp 'inf [0-9]' in the doc, and could
not find anything. The description for 'What' in the manual of 'info breakpoints'
does only describe file/line number.
So, this 'inf x' in info breakpoint seems not documented.
More generally, I could not find a description of the behaviour of breakpoints
with multiple inferiors and/or multiple archs inferior.

The doc only says: "Many commands will work the same with multiple programs as with a
single program ..."
No more details found.

Philippe
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 0cf51e1858b5..f68de613a393 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,18 @@  show remote multiprocess-extensions-packet
 * Support for reading/writing memory and extracting values on architectures
   whose memory is addressable in units of any integral multiple of 8 bits.
 
+* New remote packets
+
+QCatchSyscalls:1 [;SYSNO]...
+QCatchSyscalls:0
+  Enable ("QCatchSyscalls:1") or disable ("QCatchSyscalls:0")
+  catching syscalls from the inferior process.
+
+* New features in the GDB remote stub, GDBserver
+
+  ** GDBserver now supports catch syscall.  Currently enabled
+     on x86/x86_64 GNU/Linux targets.
+
 *** Changes in GDB 7.10
 
 * Support for process record-replay and reverse debugging on aarch64*-linux*
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index cd0abad9bb11..f26d5f7ab2f8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20129,6 +20129,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}}
@@ -35422,6 +35426,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.
@@ -35870,6 +35879,44 @@  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 member of the list is reported, @value{GDBN}
+will filter it if this syscall is not caught.  It is however more efficient
+to only report the needed syscalls.
+
+Multiple @samp{QCatchSyscalls:1} packets do not
+combine; any earlier @samp{QCatchSyscalls:1} list is completely replaced by the
+new 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
@@ -36278,6 +36325,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{-}
@@ -36466,6 +36518,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/linux-low.c b/gdb/gdbserver/linux-low.c
index 4256bc56bce5..0987dee7443b 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -74,6 +74,11 @@ 
 #define W_STOPCODE(sig) ((sig) << 8 | 0x7f)
 #endif
 
+/* Unlike other extended result codes, WSTOPSIG (status) on
+   PTRACE_O_TRACESYSGOOD syscall events doesn't return SIGTRAP, but
+   instead SIGTRAP with bit 7 set.  */
+#define SYSCALL_SIGTRAP (SIGTRAP | 0x80)
+
 /* This is the kernel's hard limit.  Not to be confused with
    SIGRTMIN.  */
 #ifndef __SIGRTMIN
@@ -601,6 +606,38 @@  get_pc (struct lwp_info *lwp)
   return pc;
 }
 
+/* This function should only be called if LWP got a SIGTRAP_SYSCALL.
+   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 using -1 values.  */
+      *sysno = -1;
+      *sysret = -1;
+      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)
+    fprintf (stderr, "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.
 
@@ -2098,6 +2135,8 @@  linux_low_ptrace_options (int attached)
   if (report_vfork_events)
     options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE);
 
+  options |= PTRACE_O_TRACESYSGOOD;
+
   return options;
 }
 
@@ -2165,8 +2204,18 @@  linux_low_filter_event (int lwpid, int wstat)
     {
       struct process_info *proc;
 
-      /* Architecture-specific setup after inferior is running.  */
       proc = find_process_pid (pid_of (thread));
+
+      /* Set ptrace flags ASAP, so no events can be missed.  */
+      if (child->must_set_ptrace_flags)
+	{
+	  int options = linux_low_ptrace_options (proc->attached);
+
+	  linux_enable_event_reporting (lwpid, options);
+	  child->must_set_ptrace_flags = 0;
+	}
+
+      /* Architecture-specific setup after inferior is running.  */
       if (proc->tdesc == NULL)
 	{
 	  if (proc->attached)
@@ -2195,15 +2244,6 @@  linux_low_filter_event (int lwpid, int wstat)
 	}
     }
 
-  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
-    {
-      struct process_info *proc = find_process_pid (pid_of (thread));
-      int options = linux_low_ptrace_options (proc->attached);
-
-      linux_enable_event_reporting (lwpid, options);
-      child->must_set_ptrace_flags = 0;
-    }
-
   /* Be careful to not overwrite stop_pc until
      check_stopped_by_breakpoint is called.  */
   if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
@@ -2805,6 +2845,29 @@  ignore_event (struct target_waitstatus *ourstatus)
   return null_ptid;
 }
 
+/* Returns 1 if GDB is interested in the event_child syscall.
+   Only to be called when stopped reason is SIGTRAP_SYSCALL.  */
+
+static int
+gdb_catch_this_syscall_p (struct lwp_info *event_child)
+{
+  int i;
+  int sysno, sysret;
+
+  if (!catching_syscalls_p)
+    return 0;
+
+  if (syscalls_to_catch_size == 0)
+    return 1;
+
+  get_syscall_trapinfo (event_child, &sysno, &sysret);
+  for (i = 0; i < syscalls_to_catch_size; i++)
+    if (syscalls_to_catch[i] == sysno)
+      return 1;
+
+  return 0;
+}
+
 /* Wait for process, returns status.  */
 
 static ptid_t
@@ -3110,6 +3173,19 @@  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)
+	fprintf (stderr, "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
@@ -3364,7 +3440,24 @@  linux_wait_1 (ptid_t ptid,
 	}
     }
 
-  if (current_thread->last_resume_kind == resume_stop
+  if (WSTOPSIG (w) == SYSCALL_SIGTRAP)
+    {
+      int sysret;
+
+      get_syscall_trapinfo (event_child,
+			    &ourstatus->value.syscall_number, &sysret);
+      /* Differentiate entry from return using the return code
+	 -ENOSYS.  This is not ideal as a syscall return from a not
+	 implemented syscall will be seen as an entry.  However, this
+	 resists well to enabling/disabling catch syscall at various
+	 moment.  A better way to differentiate entry/return in GDB
+         and GDBSERVER would be nice.  */
+      if (sysret == -ENOSYS)
+	ourstatus->kind = TARGET_WAITKIND_SYSCALL_ENTRY;
+      else
+	ourstatus->kind = TARGET_WAITKIND_SYSCALL_RETURN;
+    }
+  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,
@@ -3782,6 +3875,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
@@ -3967,7 +4061,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 (catching_syscalls_p)
+    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.  */
@@ -6047,6 +6148,13 @@  linux_process_qsupported (const char *query)
 }
 
 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)
@@ -6900,6 +7008,7 @@  static struct target_ops linux_target_ops = {
   linux_common_core_of_thread,
   linux_read_loadmap,
   linux_process_qsupported,
+  linux_supports_catch_syscall,
   linux_supports_tracepoints,
   linux_read_pc,
   linux_write_pc,
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index f8f6e78a0017..05287ef1668e 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -191,6 +191,12 @@  struct linux_target_ops
   /* Hook to support target specific qSupported.  */
   void (*process_qsupported) (const char *);
 
+  /* 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);
+
   /* Returns true if the low target supports tracepoints.  */
   int (*supports_tracepoints) (void);
 
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 20d4257e5fb0..a64ad2fe01a5 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -1432,6 +1432,36 @@  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 SIGTRAP_SYSCALL.  */
+
+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
+    {
+      int l_sysno;
+      int l_sysret;
+
+      collect_register_by_name (regcache, "orig_eax", &l_sysno);
+      collect_register_by_name (regcache, "eax", &l_sysret);
+      *sysno = (int) l_sysno;
+      *sysret = (int) l_sysret;
+    }
+}
+
 static int
 x86_supports_tracepoints (void)
 {
@@ -3283,6 +3313,7 @@  struct linux_target_ops the_low_target =
   x86_linux_new_fork,
   x86_linux_prepare_to_resume,
   x86_linux_process_qsupported,
+  x86_get_syscall_trapinfo,
   x86_supports_tracepoints,
   x86_get_thread_area,
   x86_install_fast_tracepoint_jump_pad,
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 0c4a69364396..060739387568 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1117,6 +1117,8 @@  prepare_resume_reply (char *buf, ptid_t ptid,
     case TARGET_WAITKIND_STOPPED:
     case TARGET_WAITKIND_FORKED:
     case TARGET_WAITKIND_VFORKED:
+    case TARGET_WAITKIND_SYSCALL_ENTRY:
+    case TARGET_WAITKIND_SYSCALL_RETURN:
       {
 	struct thread_info *saved_thread;
 	const char **regp;
@@ -1134,6 +1136,16 @@  prepare_resume_reply (char *buf, ptid_t ptid,
 	    buf = write_ptid (buf, status->value.related_pid);
 	    strcat (buf, ";");
 	  }
+	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 c52cf168d946..27ae4fa0771c 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -73,6 +73,10 @@  int pass_signals[GDB_SIGNAL_LAST];
 int program_signals[GDB_SIGNAL_LAST];
 int program_signals_p;
 
+int catching_syscalls_p;
+int syscalls_to_catch_size;
+int *syscalls_to_catch;
+
 /* The PID of the originally created or attached inferior.  Used to
    send signals to the process when GDB sends us an asynchronous interrupt
    (user hitting Control-C in the client), and to wait for the child to exit
@@ -586,6 +590,46 @@  handle_general_set (char *own_buf)
       return;
     }
 
+  if (startswith (own_buf, "QCatchSyscalls:1"))
+    {
+      int i;
+      const char *p;
+      CORE_ADDR sysno;
+
+      catching_syscalls_p = 1;
+      if (syscalls_to_catch != NULL)
+	{
+	  xfree (syscalls_to_catch);
+	  syscalls_to_catch = NULL;
+	}
+      syscalls_to_catch_size = 0;
+      p = own_buf + strlen("QCatchSyscalls:1");
+      while (*p)
+	{
+	  if (*p++ == ';')
+	    syscalls_to_catch_size++;
+	}
+      if (syscalls_to_catch_size > 0)
+	{
+	  syscalls_to_catch = xmalloc (syscalls_to_catch_size * sizeof (int));
+	  p = strchr(own_buf, ';') + 1;
+	  for (i = 0; i < syscalls_to_catch_size; i++)
+	    {
+	      p = decode_address_to_semicolon (&sysno, p);
+	      syscalls_to_catch[i] = (int) sysno;
+	    }
+	}
+      strcpy (own_buf, "OK");
+      return;
+    }
+
+  if (strcmp ("QCatchSyscalls:0", own_buf) == 0)
+    {
+      catching_syscalls_p = 0;
+      strcpy (own_buf, "OK");
+      return;
+    }
+
   if (startswith (own_buf, "QProgramSignals:"))
     {
       int numsigs = (int) GDB_SIGNAL_LAST, i;
@@ -2124,6 +2168,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 6020d7206d60..08ec11b3dbba 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -77,6 +77,14 @@  extern int pass_signals[];
 extern int program_signals[];
 extern int program_signals_p;
 
+/* Set to 1 if GDB is catching some (or all) syscalls, zero otherwise.  */
+extern int catching_syscalls_p;
+/* syscalls_to_catch is the list of syscalls to report to GDB.
+   If catching_syscalls_p and syscalls_to_catch == NULL, it means
+   all syscalls must be reported.  */
+extern int syscalls_to_catch_size;
+extern int *syscalls_to_catch;
+
 extern int disable_packet_vCont;
 extern int disable_packet_Tthread;
 extern int disable_packet_qC;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 3e3b80fe2c5d..93d8dcc62d80 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -307,6 +307,10 @@  struct target_ops
   /* Target specific qSupported support.  */
   void (*process_qsupported) (const char *);
 
+  /* Return 1 if the target supports catch syscall, 0 (or leave the
+     callback NULL) otherwise.  */
+  int (*supports_catch_syscall) (void);
+
   /* Return 1 if the target supports tracepoints, 0 (or leave the
      callback NULL) otherwise.  */
   int (*supports_tracepoints) (void);
@@ -510,6 +514,10 @@  int kill_inferior (int);
 	the_target->process_qsupported (query);		\
     } 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/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 86386ce70c84..97c324e24932 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1837,6 +1837,7 @@  static struct target_ops win32_target_ops = {
   NULL, /* core_of_thread */
   NULL, /* read_loadmap */
   NULL, /* process_qsupported */
+  NULL, /* supports_catch_syscall */
   NULL, /* supports_tracepoints */
   NULL, /* read_pc */
   NULL, /* write_pc */
diff --git a/gdb/remote.c b/gdb/remote.c
index 3afcaf41c93b..ffb2dd55245f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1317,6 +1317,7 @@  enum {
   PACKET_qSupported,
   PACKET_qTStatus,
   PACKET_QPassSignals,
+  PACKET_QCatchSyscalls,
   PACKET_QProgramSignals,
   PACKET_qCRC,
   PACKET_qSearch_memory,
@@ -1868,6 +1869,99 @@  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, *p;
+  enum packet_result result;
+  int n_sysno = 0;
+
+  if (remote_protocol_packets[PACKET_QCatchSyscalls].support == 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])
+	  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 char *q1 = "QCatchSyscalls:1";
+      int i;
+      const int maxpktsz = strlen (q1) + n_sysno * 9 + 1;
+
+      catch_packet = xmalloc (maxpktsz);
+      strcpy (catch_packet, q1);
+      if (!any_count)
+	{
+	  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])
+		{
+		  xsnprintf (p, catch_packet + maxpktsz - p,
+			     ";%x", i);
+		  p += strlen (p);
+		}
+	    }
+	}
+      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[strlen (q1)] = 0;
+	}
+    }
+  else
+    {
+      catch_packet = xmalloc (strlen ("QCatchSyscalls:0") + 1);
+      strcpy (catch_packet, "QCatchSyscalls:0");
+    }
+
+  {
+    struct remote_state *rs = get_remote_state ();
+    char *buf = rs->buf;
+
+    putpkt (catch_packet);
+    getpkt (&rs->buf, &rs->buf_size, 0);
+    result = packet_ok (buf,
+			&remote_protocol_packets[PACKET_QCatchSyscalls]);
+    xfree (catch_packet);
+    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.  */
 
@@ -4219,6 +4313,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,
@@ -6021,6 +6117,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"))
@@ -12543,6 +12655,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_update_thread_list = remote_update_thread_list;
@@ -13062,6 +13175,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.exp b/gdb/testsuite/gdb.base/catch-syscall.exp
index 499da322267c..26743813919d 100644
--- a/gdb/testsuite/gdb.base/catch-syscall.exp
+++ b/gdb/testsuite/gdb.base/catch-syscall.exp
@@ -19,7 +19,15 @@ 
 # 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
+}
+
+# This shall be updated whenever QCatchSyscalls packet support is implemented
+# on some gdbserver architecture.
+if { [is_remote target]
+     && ![istarget "x86_64-*-linux*"] 
+     && ![istarget "i\[34567\]86-*-linux*"] } {
     continue
 }