mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-17 13:10:12 +08:00
Implement 'catch syscall' for gdbserver
This adds a new QCatchSyscalls packet to enable 'catch syscall', and new stop reasons "syscall_entry" and "syscall_return" for those events. It is currently only supported on Linux x86 and x86_64. gdb/ChangeLog: 2016-01-12 Josh Stone <jistone@redhat.com> Philippe Waroquiers <philippe.waroquiers@skynet.be> * NEWS (Changes since GDB 7.10): Mention QCatchSyscalls and the syscall_entry and syscall_return stop reasons. Mention GDB support for remote catch syscall. * remote.c (PACKET_QCatchSyscalls): New enum. (remote_set_syscall_catchpoint): New function. (remote_protocol_features): New element for QCatchSyscalls. (remote_parse_stop_reply): Parse syscall_entry/return stops. (init_remote_ops): Install remote_set_syscall_catchpoint. (_initialize_remote): Config QCatchSyscalls. * linux-nat.h (struct lwp_info) <syscall_state>: Comment typo. gdb/doc/ChangeLog: 2016-01-12 Josh Stone <jistone@redhat.com> Philippe Waroquiers <philippe.waroquiers@skynet.be> * gdb.texinfo (Remote Configuration): List the QCatchSyscalls packet. (Stop Reply Packets): List the syscall entry and return stop reasons. (General Query Packets): Describe QCatchSyscalls, and add it to the table and the detailed list of stub features. gdb/gdbserver/ChangeLog: 2016-01-12 Josh Stone <jistone@redhat.com> Philippe Waroquiers <philippe.waroquiers@skynet.be> * inferiors.h: Include "gdb_vecs.h". (struct process_info): Add syscalls_to_catch. * inferiors.c (remove_process): Free syscalls_to_catch. * remote-utils.c (prepare_resume_reply): Report syscall_entry and syscall_return stops. * server.h (UNKNOWN_SYSCALL, ANY_SYSCALL): Define. * server.c (handle_general_set): Handle QCatchSyscalls. (handle_query): Report support for QCatchSyscalls. * target.h (struct target_ops): Add supports_catch_syscall. (target_supports_catch_syscall): New macro. * linux-low.h (struct linux_target_ops): Add get_syscall_trapinfo. (struct lwp_info): Add syscall_state. * linux-low.c (handle_extended_wait): Mark syscall_state as an entry. Maintain syscall_state and syscalls_to_catch across exec. (get_syscall_trapinfo): New function, proxy to the_low_target. (linux_low_ptrace_options): Enable PTRACE_O_TRACESYSGOOD. (linux_low_filter_event): Toggle syscall_state entry/return for syscall traps, and set it ignored for all others. (gdb_catching_syscalls_p): New function. (gdb_catch_this_syscall_p): New function. (linux_wait_1): Handle SYSCALL_SIGTRAP. (linux_resume_one_lwp_throw): Add PTRACE_SYSCALL possibility. (linux_supports_catch_syscall): New function. (linux_target_ops): Install it. * linux-x86-low.c (x86_get_syscall_trapinfo): New function. (the_low_target): Install it. gdb/testsuite/ChangeLog: 2016-01-12 Josh Stone <jistone@redhat.com> Philippe Waroquiers <philippe.waroquiers@skynet.be> * gdb.base/catch-syscall.c (do_execve): New variable. (main): Conditionally trigger an execve. * gdb.base/catch-syscall.exp: Enable testing for remote targets. (test_catch_syscall_execve): New, check entry/return across execve. (do_syscall_tests): Call test_catch_syscall_execve.
This commit is contained in:
parent
41549dfbcc
commit
82075af2c1
@ -1,3 +1,17 @@
|
||||
2016-01-12 Josh Stone <jistone@redhat.com>
|
||||
Philippe Waroquiers <philippe.waroquiers@skynet.be>
|
||||
|
||||
* NEWS (Changes since GDB 7.10): Mention QCatchSyscalls and the
|
||||
syscall_entry and syscall_return stop reasons. Mention GDB
|
||||
support for remote catch syscall.
|
||||
* remote.c (PACKET_QCatchSyscalls): New enum.
|
||||
(remote_set_syscall_catchpoint): New function.
|
||||
(remote_protocol_features): New element for QCatchSyscalls.
|
||||
(remote_parse_stop_reply): Parse syscall_entry/return stops.
|
||||
(init_remote_ops): Install remote_set_syscall_catchpoint.
|
||||
(_initialize_remote): Config QCatchSyscalls.
|
||||
* linux-nat.h (struct lwp_info) <syscall_state>: Comment typo.
|
||||
|
||||
2016-01-12 Yao Qi <yao.qi@linaro.org>
|
||||
|
||||
* nat/linux-ptrace.c (linux_child_function): Cast child_stack
|
||||
|
24
gdb/NEWS
24
gdb/NEWS
@ -120,6 +120,21 @@ N stop reply
|
||||
threads are stopped). The remote stub reports support for this stop
|
||||
reply to GDB's qSupported query.
|
||||
|
||||
QCatchSyscalls:1 [;SYSNO]...
|
||||
QCatchSyscalls:0
|
||||
Enable ("QCatchSyscalls:1") or disable ("QCatchSyscalls:0")
|
||||
catching syscalls from the inferior process.
|
||||
|
||||
syscall_entry stop reason
|
||||
Indicates that a syscall was just called.
|
||||
|
||||
syscall_return stop reason
|
||||
Indicates that a syscall just returned.
|
||||
|
||||
QCatchSyscalls:1 in qSupported
|
||||
The qSupported packet may now include QCatchSyscalls:1 in the reply
|
||||
to indicate support for catching syscalls.
|
||||
|
||||
* Extended-remote exec events
|
||||
|
||||
** GDB now has support for exec events on extended-remote Linux targets.
|
||||
@ -142,6 +157,15 @@ show remote exec-event-feature-packet
|
||||
this enables follow-fork-mode, detach-on-fork, follow-exec-mode, and
|
||||
fork and exec catchpoints.
|
||||
|
||||
* Remote syscall events
|
||||
|
||||
** GDB now has support for catch syscall on remote Linux targets,
|
||||
currently enabled on x86/x86_64 architectures.
|
||||
|
||||
set remote catch-syscall-packet
|
||||
show remote catch-syscall-packet
|
||||
Set/show the use of the remote catch syscall feature.
|
||||
|
||||
* MI changes
|
||||
|
||||
** The -var-set-format command now accepts the zero-hexadecimal
|
||||
|
@ -1,3 +1,11 @@
|
||||
2016-01-12 Josh Stone <jistone@redhat.com>
|
||||
Philippe Waroquiers <philippe.waroquiers@skynet.be>
|
||||
|
||||
* gdb.texinfo (Remote Configuration): List the QCatchSyscalls packet.
|
||||
(Stop Reply Packets): List the syscall entry and return stop reasons.
|
||||
(General Query Packets): Describe QCatchSyscalls, and add it to the
|
||||
table and the detailed list of stub features.
|
||||
|
||||
2016-01-12 Pedro Alves <palves@redhat.com>
|
||||
|
||||
Remove use of the registered trademark symbol throughout.
|
||||
|
@ -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}).
|
||||
|
@ -1,3 +1,33 @@
|
||||
2016-01-12 Josh Stone <jistone@redhat.com>
|
||||
Philippe Waroquiers <philippe.waroquiers@skynet.be>
|
||||
|
||||
* inferiors.h: Include "gdb_vecs.h".
|
||||
(struct process_info): Add syscalls_to_catch.
|
||||
* inferiors.c (remove_process): Free syscalls_to_catch.
|
||||
* remote-utils.c (prepare_resume_reply): Report syscall_entry and
|
||||
syscall_return stops.
|
||||
* server.h (UNKNOWN_SYSCALL, ANY_SYSCALL): Define.
|
||||
* server.c (handle_general_set): Handle QCatchSyscalls.
|
||||
(handle_query): Report support for QCatchSyscalls.
|
||||
* target.h (struct target_ops): Add supports_catch_syscall.
|
||||
(target_supports_catch_syscall): New macro.
|
||||
* linux-low.h (struct linux_target_ops): Add get_syscall_trapinfo.
|
||||
(struct lwp_info): Add syscall_state.
|
||||
* linux-low.c (handle_extended_wait): Mark syscall_state as an entry.
|
||||
Maintain syscall_state and syscalls_to_catch across exec.
|
||||
(get_syscall_trapinfo): New function, proxy to the_low_target.
|
||||
(linux_low_ptrace_options): Enable PTRACE_O_TRACESYSGOOD.
|
||||
(linux_low_filter_event): Toggle syscall_state entry/return for
|
||||
syscall traps, and set it ignored for all others.
|
||||
(gdb_catching_syscalls_p): New function.
|
||||
(gdb_catch_this_syscall_p): New function.
|
||||
(linux_wait_1): Handle SYSCALL_SIGTRAP.
|
||||
(linux_resume_one_lwp_throw): Add PTRACE_SYSCALL possibility.
|
||||
(linux_supports_catch_syscall): New function.
|
||||
(linux_target_ops): Install it.
|
||||
* linux-x86-low.c (x86_get_syscall_trapinfo): New function.
|
||||
(the_low_target): Install it.
|
||||
|
||||
2016-01-12 Mike Frysinger <vapier@gentoo.org>
|
||||
|
||||
* acinclude.m4: Include new ../warning.m4 file.
|
||||
|
@ -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,14 @@ handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
|
||||
event_thr->last_resume_kind = resume_continue;
|
||||
event_thr->last_status.kind = TARGET_WAITKIND_IGNORE;
|
||||
|
||||
/* Update syscall state in the new lwp, effectively mid-syscall too. */
|
||||
event_lwp->syscall_state = TARGET_WAITKIND_SYSCALL_ENTRY;
|
||||
|
||||
/* Restore the list to catch. Don't rely on the client, which is free
|
||||
to avoid sending a new list when the architecture doesn't change.
|
||||
Also, for ANY_SYSCALL, the architecture doesn't really matter. */
|
||||
proc->syscalls_to_catch = syscalls_to_catch;
|
||||
|
||||
/* Report the event. */
|
||||
*orig_event_lwp = event_lwp;
|
||||
return 0;
|
||||
@ -682,6 +700,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 +2288,8 @@ linux_low_ptrace_options (int attached)
|
||||
if (report_exec_events)
|
||||
options |= PTRACE_O_TRACEEXEC;
|
||||
|
||||
options |= PTRACE_O_TRACESYSGOOD;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@ -2364,6 +2418,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 +3042,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 +3414,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 +3682,16 @@ linux_wait_1 (ptid_t ptid,
|
||||
}
|
||||
}
|
||||
|
||||
if (current_thread->last_resume_kind == resume_stop
|
||||
&& WSTOPSIG (w) == SIGSTOP)
|
||||
if (WSTOPSIG (w) == SYSCALL_SIGTRAP)
|
||||
{
|
||||
int sysret;
|
||||
|
||||
get_syscall_trapinfo (event_child,
|
||||
&ourstatus->value.syscall_number, &sysret);
|
||||
ourstatus->kind = event_child->syscall_state;
|
||||
}
|
||||
else if (current_thread->last_resume_kind == resume_stop
|
||||
&& WSTOPSIG (w) == SIGSTOP)
|
||||
{
|
||||
/* A thread that has been requested to stop by GDB with vCont;t,
|
||||
and it stopped cleanly, so report as SIG0. The use of
|
||||
@ -4017,6 +4148,7 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
|
||||
struct thread_info *thread = get_lwp_thread (lwp);
|
||||
struct thread_info *saved_thread;
|
||||
int fast_tp_collecting;
|
||||
int ptrace_request;
|
||||
struct process_info *proc = get_thread_process (thread);
|
||||
|
||||
/* Note that target description may not be initialised
|
||||
@ -4204,7 +4336,14 @@ linux_resume_one_lwp_throw (struct lwp_info *lwp,
|
||||
regcache_invalidate_thread (thread);
|
||||
errno = 0;
|
||||
lwp->stepping = step;
|
||||
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. */
|
||||
@ -6285,6 +6424,13 @@ linux_process_qsupported (char **features, int count)
|
||||
the_low_target.process_qsupported (features, 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)
|
||||
{
|
||||
@ -7209,7 +7355,8 @@ static struct target_ops linux_target_ops = {
|
||||
linux_sw_breakpoint_from_kind,
|
||||
linux_proc_tid_get_name,
|
||||
linux_breakpoint_kind_from_current_state,
|
||||
linux_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
|
||||
|
110
gdb/remote.c
110
gdb/remote.c
@ -1392,6 +1392,7 @@ enum {
|
||||
PACKET_qSupported,
|
||||
PACKET_qTStatus,
|
||||
PACKET_QPassSignals,
|
||||
PACKET_QCatchSyscalls,
|
||||
PACKET_QProgramSignals,
|
||||
PACKET_qCRC,
|
||||
PACKET_qSearch_memory,
|
||||
@ -1987,6 +1988,93 @@ remote_pass_signals (struct target_ops *self,
|
||||
}
|
||||
}
|
||||
|
||||
/* If 'QCatchSyscalls' is supported, tell the remote stub
|
||||
to report syscalls to GDB. */
|
||||
|
||||
static int
|
||||
remote_set_syscall_catchpoint (struct target_ops *self,
|
||||
int pid, int needed, int any_count,
|
||||
int table_size, int *table)
|
||||
{
|
||||
char *catch_packet;
|
||||
enum packet_result result;
|
||||
int n_sysno = 0;
|
||||
|
||||
if (packet_support (PACKET_QCatchSyscalls) == PACKET_DISABLE)
|
||||
{
|
||||
/* Not supported. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (needed && !any_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Count how many syscalls are to be caught (table[sysno] != 0). */
|
||||
for (i = 0; i < table_size; i++)
|
||||
{
|
||||
if (table[i] != 0)
|
||||
n_sysno++;
|
||||
}
|
||||
}
|
||||
|
||||
if (remote_debug)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"remote_set_syscall_catchpoint "
|
||||
"pid %d needed %d any_count %d n_sysno %d\n",
|
||||
pid, needed, any_count, n_sysno);
|
||||
}
|
||||
|
||||
if (needed)
|
||||
{
|
||||
/* Prepare a packet with the sysno list, assuming max 8+1
|
||||
characters for a sysno. If the resulting packet size is too
|
||||
big, fallback on the non-selective packet. */
|
||||
const int maxpktsz = strlen ("QCatchSyscalls:1") + n_sysno * 9 + 1;
|
||||
|
||||
catch_packet = xmalloc (maxpktsz);
|
||||
strcpy (catch_packet, "QCatchSyscalls:1");
|
||||
if (!any_count)
|
||||
{
|
||||
int i;
|
||||
char *p;
|
||||
|
||||
p = catch_packet;
|
||||
p += strlen (p);
|
||||
|
||||
/* Add in catch_packet each syscall to be caught (table[i] != 0). */
|
||||
for (i = 0; i < table_size; i++)
|
||||
{
|
||||
if (table[i] != 0)
|
||||
p += xsnprintf (p, catch_packet + maxpktsz - p, ";%x", i);
|
||||
}
|
||||
}
|
||||
if (strlen (catch_packet) > get_remote_packet_size ())
|
||||
{
|
||||
/* catch_packet too big. Fallback to less efficient
|
||||
non selective mode, with GDB doing the filtering. */
|
||||
catch_packet[sizeof ("QCatchSyscalls:1") - 1] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch_packet = xstrdup ("QCatchSyscalls:0");
|
||||
|
||||
{
|
||||
struct cleanup *old_chain = make_cleanup (xfree, catch_packet);
|
||||
struct remote_state *rs = get_remote_state ();
|
||||
|
||||
putpkt (catch_packet);
|
||||
getpkt (&rs->buf, &rs->buf_size, 0);
|
||||
result = packet_ok (rs->buf, &remote_protocol_packets[PACKET_QCatchSyscalls]);
|
||||
do_cleanups (old_chain);
|
||||
if (result == PACKET_OK)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If 'QProgramSignals' is supported, tell the remote stub what
|
||||
signals it should pass through to the inferior when detaching. */
|
||||
|
||||
@ -4467,6 +4555,8 @@ static const struct protocol_feature remote_protocol_features[] = {
|
||||
PACKET_qXfer_traceframe_info },
|
||||
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
|
||||
PACKET_QPassSignals },
|
||||
{ "QCatchSyscalls", PACKET_DISABLE, remote_supported_packet,
|
||||
PACKET_QCatchSyscalls },
|
||||
{ "QProgramSignals", PACKET_DISABLE, remote_supported_packet,
|
||||
PACKET_QProgramSignals },
|
||||
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
|
||||
@ -6371,6 +6461,22 @@ Packet: '%s'\n"),
|
||||
|
||||
if (strprefix (p, p1, "thread"))
|
||||
event->ptid = read_ptid (++p1, &p);
|
||||
else if (strprefix (p, p1, "syscall_entry"))
|
||||
{
|
||||
ULONGEST sysno;
|
||||
|
||||
event->ws.kind = TARGET_WAITKIND_SYSCALL_ENTRY;
|
||||
p = unpack_varlen_hex (++p1, &sysno);
|
||||
event->ws.value.syscall_number = (int) sysno;
|
||||
}
|
||||
else if (strprefix (p, p1, "syscall_return"))
|
||||
{
|
||||
ULONGEST sysno;
|
||||
|
||||
event->ws.kind = TARGET_WAITKIND_SYSCALL_RETURN;
|
||||
p = unpack_varlen_hex (++p1, &sysno);
|
||||
event->ws.value.syscall_number = (int) sysno;
|
||||
}
|
||||
else if (strprefix (p, p1, "watch")
|
||||
|| strprefix (p, p1, "rwatch")
|
||||
|| strprefix (p, p1, "awatch"))
|
||||
@ -12976,6 +13082,7 @@ Specify the serial device it is connected to\n\
|
||||
remote_ops.to_load = remote_load;
|
||||
remote_ops.to_mourn_inferior = remote_mourn;
|
||||
remote_ops.to_pass_signals = remote_pass_signals;
|
||||
remote_ops.to_set_syscall_catchpoint = remote_set_syscall_catchpoint;
|
||||
remote_ops.to_program_signals = remote_program_signals;
|
||||
remote_ops.to_thread_alive = remote_thread_alive;
|
||||
remote_ops.to_thread_name = remote_thread_name;
|
||||
@ -13542,6 +13649,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
|
||||
add_packet_config_cmd (&remote_protocol_packets[PACKET_QPassSignals],
|
||||
"QPassSignals", "pass-signals", 0);
|
||||
|
||||
add_packet_config_cmd (&remote_protocol_packets[PACKET_QCatchSyscalls],
|
||||
"QCatchSyscalls", "catch-syscalls", 0);
|
||||
|
||||
add_packet_config_cmd (&remote_protocol_packets[PACKET_QProgramSignals],
|
||||
"QProgramSignals", "program-signals", 0);
|
||||
|
||||
|
@ -1,3 +1,12 @@
|
||||
2016-01-12 Josh Stone <jistone@redhat.com>
|
||||
Philippe Waroquiers <philippe.waroquiers@skynet.be>
|
||||
|
||||
* gdb.base/catch-syscall.c (do_execve): New variable.
|
||||
(main): Conditionally trigger an execve.
|
||||
* gdb.base/catch-syscall.exp: Enable testing for remote targets.
|
||||
(test_catch_syscall_execve): New, check entry/return across execve.
|
||||
(do_syscall_tests): Call test_catch_syscall_execve.
|
||||
|
||||
2016-01-12 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* gdb.base/random-signal.exp (do_test): New procedure, with body
|
||||
|
@ -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,28 @@ proc test_catch_syscall_mid_vfork {} {
|
||||
}
|
||||
}
|
||||
|
||||
proc test_catch_syscall_execve {} {
|
||||
global gdb_prompt decimal
|
||||
|
||||
with_test_prefix "execve" {
|
||||
|
||||
# Tell the test program we want an execve.
|
||||
gdb_test_no_output "set do_execve = 1"
|
||||
|
||||
# Check for entry/return across the execve, making sure that the
|
||||
# syscall_state isn't lost when turning into a new process.
|
||||
insert_catch_syscall_with_arg "execve"
|
||||
check_continue "execve"
|
||||
|
||||
# Continue to main so extended-remote can read files as needed.
|
||||
# (Otherwise that "Reading" output confuses gdb_continue_to_end.)
|
||||
gdb_continue "main"
|
||||
|
||||
# Now can we finish?
|
||||
check_for_program_end
|
||||
}
|
||||
}
|
||||
|
||||
proc test_catch_syscall_fail_nodatadir {} {
|
||||
with_test_prefix "fail no datadir" {
|
||||
# Sanitizing.
|
||||
@ -392,6 +414,9 @@ proc do_syscall_tests {} {
|
||||
# Testing the 'catch syscall' command starting mid-vfork.
|
||||
if [runto_main] then { test_catch_syscall_mid_vfork }
|
||||
|
||||
# Testing 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 }
|
||||
|
Loading…
Reference in New Issue
Block a user