From: Philippe Waroquiers Date: Fri, 22 Jan 2016 21:06:23 +0000 (+0000) Subject: Implement support for 'catch syscall' in gdbserver. X-Git-Tag: svn/VALGRIND_3_12_0~253 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9c66c9607be876f9aa54955341ce0fa9f221992e;p=thirdparty%2Fvalgrind.git Implement support for 'catch syscall' in gdbserver. Note that catch syscall implies to use the soon to be released gdb 7.11 version. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15770 --- diff --git a/NEWS b/NEWS index fff9f76ec6..f3e65e2b9b 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,12 @@ n-i-bz Improved thread startup time significantly on non-Linux platforms. "nouserintercepts" can be any non-existing library name). This new functionality is not implemented for darwin/macosx. +* New and modified GDB server monitor features: + + - Valgrind's gdbserver now accepts the command 'catch syscall'. + Note that you must have a GDB >= 7.11 to use 'catch syscall' with + gdbserver. + * ==================== FIXED BUGS ==================== The following bugs have been fixed or resolved. Note that "n-i-bz" diff --git a/coregrind/m_gdbserver/m_gdbserver.c b/coregrind/m_gdbserver/m_gdbserver.c index d5cc8eae49..99122d28d1 100644 --- a/coregrind/m_gdbserver/m_gdbserver.c +++ b/coregrind/m_gdbserver/m_gdbserver.c @@ -1012,6 +1012,44 @@ Bool VG_(gdbserver_report_signal) (vki_siginfo_t *info, ThreadId tid) } } +Bool catching_syscalls = False; // True if catching all or some syscalls. +/* If catching_syscalls is True, then syscalls_to_catch_size == 0 means + to catch all syscalls. Otherwise, it is the size of the syscalls_to_catch + array. */ +Int syscalls_to_catch_size = 0; +Int *syscalls_to_catch; +static Bool catch_this_syscall (Int sysno) +{ + Int i; + + if (syscalls_to_catch_size == 0) + return True; + + for (i = 0; i < syscalls_to_catch_size; i++) + if (syscalls_to_catch[i] == sysno) + return True; + + return False; +} + +void VG_(gdbserver_report_syscall) (Bool before, UWord sysno, ThreadId tid) +{ + dlog(4, "VG_(gdbserver_report_syscall) before %d sysno %lu tid %d\n", + before, sysno, tid); + + if (UNLIKELY(catching_syscalls)) { + if (!remote_connected()) { + dlog(2, "not connected => no report\n"); + } + + if (catch_this_syscall ((Int)sysno)) { + /* let gdbserver do some work */ + gdbserver_syscall_encountered (before, (Int)sysno); + call_gdbserver (tid, signal_reason); + } + } +} + void VG_(gdbserver_exit) (ThreadId tid, VgSchedReturnCode tids_schedretcode) { dlog(1, "VG core calling VG_(gdbserver_exit) tid %u will exit\n", tid); @@ -1022,11 +1060,13 @@ void VG_(gdbserver_exit) (ThreadId tid, VgSchedReturnCode tids_schedretcode) vg_assert (0); case VgSrc_ExitThread: case VgSrc_ExitProcess: - gdbserver_process_exit_encountered ('W', VG_(threads)[tid].os_state.exitcode); + gdbserver_process_exit_encountered + ('W', VG_(threads)[tid].os_state.exitcode); call_gdbserver (tid, exit_reason); break; case VgSrc_FatalSig: - gdbserver_process_exit_encountered ('X', VG_(threads)[tid].os_state.fatalsig); + gdbserver_process_exit_encountered + ('X', VG_(threads)[tid].os_state.fatalsig); call_gdbserver (tid, exit_reason); break; default: diff --git a/coregrind/m_gdbserver/remote-utils.c b/coregrind/m_gdbserver/remote-utils.c index f6f3d0bcf3..2d4320c9c1 100644 --- a/coregrind/m_gdbserver/remote-utils.c +++ b/coregrind/m_gdbserver/remote-utils.c @@ -1154,6 +1154,14 @@ void prepare_resume_reply (char *buf, char status, unsigned char sig) *buf++ = ';'; } + if (valgrind_stopped_by_syscall () >= 0) { + VG_(sprintf) (buf, "%s:%x;", + valgrind_stopped_before_syscall () + ? "syscall_entry" : "syscall_return", + valgrind_stopped_by_syscall ()); + buf += strlen (buf); + } + while (*regp) { buf = outreg (find_regno (*regp), buf); regp ++; diff --git a/coregrind/m_gdbserver/server.c b/coregrind/m_gdbserver/server.c index ea2bdc849a..2f26178e72 100644 --- a/coregrind/m_gdbserver/server.c +++ b/coregrind/m_gdbserver/server.c @@ -624,6 +624,52 @@ void handle_set (char *arg_own_buf, int *new_packet_len_p) return; } + if (strcmp ("QCatchSyscalls:0", arg_own_buf) == 0) { + dlog (3, "catch syscall all off\n"); + catching_syscalls = False; + write_ok (arg_own_buf); + return; + } + + const char *q1 = "QCatchSyscalls:1"; + if (strncmp (q1, arg_own_buf, strlen(q1)) == 0) { + Int i; + const char *p; + + if (syscalls_to_catch != NULL) { + free (syscalls_to_catch); + syscalls_to_catch = NULL; + } + syscalls_to_catch_size = 0; + p = arg_own_buf + strlen(q1); + while (*p) { + if (*p++ == ';') + syscalls_to_catch_size++; + } + if (syscalls_to_catch_size > 0) { + CORE_ADDR sysno; + char *from, *to; + + syscalls_to_catch = malloc (syscalls_to_catch_size * sizeof (int)); + + from = strchr (arg_own_buf, ';') + 1; + for (i = 0; i < syscalls_to_catch_size; i++) { + to = strchr (from, ';'); + if (to == NULL) + to = arg_own_buf + strlen (arg_own_buf); + decode_address (&sysno, from, to - from); + syscalls_to_catch[i] = (Int)sysno; + dlog(4, "catch syscall sysno %d\n", (int)sysno); + from = to; + if (*from == ';') from++; + } + } else + dlog (4, "catch syscall all sysno\n"); + catching_syscalls = True; + write_ok (arg_own_buf); + return; + } + if (strncmp ("QPassSignals:", arg_own_buf, 13) == 0) { int i; char *from, *to; @@ -1042,6 +1088,7 @@ void handle_query (char *arg_own_buf, int *new_packet_len_p) strcat (arg_own_buf, ";QStartNoAckMode+"); strcat (arg_own_buf, ";QPassSignals+"); + strcat (arg_own_buf, ";QCatchSyscalls+"); if (VG_(client_auxv)) strcat (arg_own_buf, ";qXfer:auxv:read+"); diff --git a/coregrind/m_gdbserver/server.h b/coregrind/m_gdbserver/server.h index d2bbfd7c49..1c2d8612e3 100644 --- a/coregrind/m_gdbserver/server.h +++ b/coregrind/m_gdbserver/server.h @@ -116,6 +116,14 @@ extern Addr thumb_pc (Addr pc); or tid 1 otherwise). */ extern ThreadId vgdb_interrupted_tid; +/* True if GDB is catching client syscalls. */ +extern Bool catching_syscalls; + +/* Size of the syscalls_to_catch. Only useful if catching_syscalls True. + syscalls_to_catch_size 0 means all syscalls are caught. */ +extern Int syscalls_to_catch_size; +extern Int *syscalls_to_catch; + /*------------ end of interface to low level gdbserver */ diff --git a/coregrind/m_gdbserver/target.c b/coregrind/m_gdbserver/target.c index 3d54b7503b..488730a0f9 100644 --- a/coregrind/m_gdbserver/target.c +++ b/coregrind/m_gdbserver/target.c @@ -177,6 +177,26 @@ Bool gdbserver_deliver_signal (vki_siginfo_t *info) return vki_signal_to_deliver.si_signo != 0; } +static Bool before_syscall; +static Int sysno_to_report = -1; +void gdbserver_syscall_encountered (Bool before, Int sysno) +{ + before_syscall = before; + sysno_to_report = sysno; +} + +Int valgrind_stopped_by_syscall (void) +{ + return sysno_to_report; +} + +Bool valgrind_stopped_before_syscall() +{ + vg_assert (sysno_to_report >= 0); + return before_syscall; +} + + static unsigned char exit_status_to_report; static int exit_code_to_report; void gdbserver_process_exit_encountered (unsigned char status, Int code) @@ -249,6 +269,12 @@ void valgrind_resume (struct thread_resume *resume_info) C2v(stopped_data_address)); VG_(set_watchpoint_stop_address) ((Addr) 0); } + if (valgrind_stopped_by_syscall () >= 0) { + dlog(1, "clearing stopped by syscall %d\n", + valgrind_stopped_by_syscall ()); + gdbserver_syscall_encountered (False, -1); + } + vki_signal_to_deliver.si_signo = resume_info->sig; /* signal was reported to GDB, GDB told us to resume execution. So, reset the signal to report to 0. */ diff --git a/coregrind/m_gdbserver/target.h b/coregrind/m_gdbserver/target.h index 2ea8de9ca6..9a8b5f2b4e 100644 --- a/coregrind/m_gdbserver/target.h +++ b/coregrind/m_gdbserver/target.h @@ -144,11 +144,23 @@ extern int valgrind_stopped_by_watchpoint (void); returns 0 otherwise. */ extern CORE_ADDR valgrind_stopped_data_address (void); + +/* Inform GDB (if needed) that client is before (or after) syscall sysno. + sysno -1 is used to clear the fact that a syscall has been encountered. */ +extern void gdbserver_syscall_encountered (Bool before, Int sysno); + +/* >= 0 if valgrind stopped due to syscall, -1 if not stopped due to syscall. */ +extern Int valgrind_stopped_by_syscall (void); + +/* if valgrind_stopped_by_syscall() >= 0, tells if stopped before or after + syscall. */ +extern Bool valgrind_stopped_before_syscall (void); + /* True if gdbserver is single stepping the valgrind process */ -extern Bool valgrind_single_stepping(void); +extern Bool valgrind_single_stepping (void); /* Set Valgrind in single stepping mode or not according to Bool. */ -extern void valgrind_set_single_stepping(Bool); +extern void valgrind_set_single_stepping (Bool); /* -------------------------------------------------------------------------- */ /* ----------------- Examining/modifying data while stopped ----------------- */ diff --git a/coregrind/m_syswrap/syswrap-main.c b/coregrind/m_syswrap/syswrap-main.c index f4d1fdd47f..054891f7eb 100644 --- a/coregrind/m_syswrap/syswrap-main.c +++ b/coregrind/m_syswrap/syswrap-main.c @@ -50,6 +50,7 @@ #include "pub_core_machine.h" #include "pub_core_mallocfree.h" #include "pub_core_syswrap.h" +#include "pub_core_gdbserver.h" // VG_(gdbserver_report_syscall) #include "priv_types_n_macros.h" #include "priv_syswrap-main.h" @@ -1906,6 +1907,9 @@ void VG_(client_syscall) ( ThreadId tid, UInt trc ) &layout, &sci->args, &sci->status, &sci->flags ); + /* If needed, gdbserver will report syscall entry to GDB */ + VG_(gdbserver_report_syscall)(True, sysno, tid); + /* The pre-handler may have modified: sci->args sci->status @@ -2063,6 +2067,9 @@ void VG_(client_syscall) ( ThreadId tid, UInt trc ) if (!(sci->flags & SfNoWriteResult)) putSyscallStatusIntoGuestState( tid, &sci->status, &tst->arch.vex ); + /* If needed, gdbserver will report syscall return to GDB */ + VG_(gdbserver_report_syscall)(False, sysno, tid); + /* Situation now: - the guest state is now correctly modified following the syscall - modified args, original args and syscall status are still diff --git a/coregrind/pub_core_gdbserver.h b/coregrind/pub_core_gdbserver.h index fa69863667..eca92a0dda 100644 --- a/coregrind/pub_core_gdbserver.h +++ b/coregrind/pub_core_gdbserver.h @@ -119,6 +119,13 @@ extern Bool VG_(gdbserver_report_signal) (vki_siginfo_t *info, ThreadId tid); extern void VG_(gdbserver_report_fatal_signal) (const vki_siginfo_t *info, ThreadId tid); +// To be called by core before and after a client syscall. +// If GDB has asked to observe the syscall, control will be given to GDB. +// When Before is True, it is a report before the syscall, +// False means a report after the syscall. +extern void VG_(gdbserver_report_syscall) (Bool before, UWord sysno, + ThreadId tid); + /* Entry point invoked by scheduler.c to execute the request VALGRIND_CLIENT_MONITOR_COMMAND. Returns True if command was not recognised. */