[v4] Implement 'catch syscall' for gdbserver
Commit Message
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
> 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.
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
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
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.
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
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.
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
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
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.
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.
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.
>
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?
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.
@@ -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
@@ -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}).
@@ -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);
}
@@ -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. */
@@ -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
@@ -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;
@@ -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
@@ -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);
@@ -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+");
@@ -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 */
@@ -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)
@@ -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
@@ -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);
@@ -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);
@@ -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 }