"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"
}
}
+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);
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:
*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 ++;
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;
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+");
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 */
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)
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. */
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 ----------------- */
#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"
&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
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
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. */