]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Fix 326462 Refactor vgdb to isolate invoker stuff into separate module
authorPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sun, 1 Dec 2013 14:56:28 +0000 (14:56 +0000)
committerPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sun, 1 Dec 2013 14:56:28 +0000 (14:56 +0000)
vgdb.c contained (conditionally compiled) "invoker" code to have ptrace syscalls
used to allow gdb/vgdb to connect to a valgrind process blocked in a syscall.
This "invoker" code is ptrace based.
Not all platforms are using ptrace.
=> refactor vgdb so as allow invoker code to be added more cleanly
for non ptrace based platforms (e.g. Darwin, Solaris).

* add file vgdb.h for:
   - definition of the vgdb-invoker interface
   - common declarations between vgdb.c and vgdb-invoker implementations
* move ptrace related code from vgdb.c to new file vgdb-invoker-ptrace.c
* new file vgdb-invoker-none.c containing an empty invoker implementation
  used on platforms that do not (yet) have a invoker implementation
  (e.g. android and darwin).
* modified Makefile.am to use one of the vgdb-invoker-*.c file depending
  on the platform.
* small changes related to changing ptraceinvoker to invoker in various files:
  gdbserver_tests/make_local_links, gdbserver_tests/nlcontrolc.vgtest,
  gdbserver_tests/mcinvokeRU.vgtest, gdbserver_tests/nlsigvgdb.vgtest
  gdbserver_tests/mcinvokeWS.vgtest, coregrind/m_gdbserver/README_DEVELOPERS

Patch from Ivo Raisr, slightly modified

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13743

12 files changed:
NEWS
coregrind/Makefile.am
coregrind/m_gdbserver/README_DEVELOPERS
coregrind/vgdb-invoker-none.c [new file with mode: 0644]
coregrind/vgdb-invoker-ptrace.c [new file with mode: 0644]
coregrind/vgdb.c
coregrind/vgdb.h [new file with mode: 0644]
gdbserver_tests/make_local_links
gdbserver_tests/mcinvokeRU.vgtest
gdbserver_tests/mcinvokeWS.vgtest
gdbserver_tests/nlcontrolc.vgtest
gdbserver_tests/nlsigvgdb.vgtest

diff --git a/NEWS b/NEWS
index d10919f6802071f01ea1f148fa09715b5ed13bf3..01f47d7da15594c3d22a879a549e3c45eea64459 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -24,7 +24,7 @@ To see details of a given bug, visit
   https://bugs.kde.org/show_bug.cgi?id=XXXXXX
 where XXXXXX is the bug number as listed below.
 
-
+326462  Refactor vgdb to isolate invoker stuff into separate module
 326983  Clear direction flag after tests on amd64.
 327238  Callgrind Assertion 'passed <= last_bb->cjmp_count' failed
 327837  dwz compressed alternate .debug_info and .debug_str not read correctly
index fd84ebc18910cb6a92cc5d515397ea28cda1d191..792d94a82eba4f24e7f353627ccc0e86e7e99c15 100644 (file)
@@ -59,7 +59,22 @@ valgrind_CFLAGS += -static
 valgrind_LDFLAGS   += -Wl,-z,noexecstack
 endif
 
+
 vgdb_SOURCES = vgdb.c
+if VGCONF_OS_IS_LINUX
+if VGCONF_PLATVARIANT_IS_ANDROID
+vgdb_SOURCES += vgdb-invoker-none.c
+else
+vgdb_SOURCES += vgdb-invoker-ptrace.c
+endif
+endif
+if VGCONF_OS_IS_DARWIN
+# Some darwin specific stuff is needed as ptrace is not
+# fully supported on MacOS. Till we find someone courageous
+# having access to Darwin, 'none' implementation is used.
+vgdb_SOURCES += vgdb-invoker-none.c
+endif
+
 vgdb_CPPFLAGS  = $(AM_CPPFLAGS_PRI)
 vgdb_CFLAGS    = $(AM_CFLAGS_PRI)
 vgdb_CCASFLAGS = $(AM_CCASFLAGS_PRI)
index 3cf740f49487debd8c824f79eb04cdf5b2830933..58c9269e03e93151b91b557494612774359d0eaa 100644 (file)
@@ -165,7 +165,7 @@ will check after 100ms if the characters it has written have been read
 by valgrind. If not, vgdb will force the invocation of the gdbserver
 code inside the valgrind process.
 
-This forced invocation is implemented using the ptrace system call:
+On Linux, this forced invocation is implemented using the ptrace system call:
 using ptrace, vgdb will cause the valgrind process to call the
 gdbserver code.
 
@@ -272,10 +272,12 @@ Then adapt the set of functions needed to initialize the structure
 Optional but heavily recommended:
 To have a proper wake up of a Valgrind process with all threads
 blocked in a system call, some architecture specific code
-has to be done in vgdb.c : search for PTRACEINVOKER processor symbol
-to see what has to be completed.
+has to be done in vgdb-invoker-*.c.
+Typically, for a linux system supporting ptrace, you have to modify
+vgdb-invoker-ptrace.c.
 
-For Linux based platforms, all the ptrace calls should be ok.
+For Linux based platforms, all the ptrace calls in vgdb-invoker-ptrace.c
+should be ok.
 The only thing needed is the code needed to "push a dummy call" on the stack,
 i.e. assign the relevant registers in the struct user_regs_struct, and push
 values on the stack according to the ABI.
@@ -337,7 +339,6 @@ TODO and/or additional nice things to have
     (such as search leaks)?
 
 
-
 * currently jump(s) and inferior call(s) are somewhat dangerous
   when called from a block not yet instrumented : instead
   of continuing till the next Imark, where there will be a
diff --git a/coregrind/vgdb-invoker-none.c b/coregrind/vgdb-invoker-none.c
new file mode 100644 (file)
index 0000000..9b6b90c
--- /dev/null
@@ -0,0 +1,56 @@
+/*--------------------------------------------------------------------*/
+/*--- Empty implementation of vgdb invoker subsystem.              ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2011-2013 Philippe Waroquiers
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "vgdb.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+
+void invoker_restrictions_msg(void)
+{
+   fprintf(stderr, 
+           "Note: vgdb invoker not implemented on this platform.\n"
+           "For more info: read user manual section"
+           " 'Limitations of the Valgrind gdbserver'.\n");
+}
+
+void invoker_cleanup_restore_and_detach(void *v_pid)
+{
+   DEBUG(1, "invoker_cleanup_restore_and_detach");
+}
+
+Bool invoker_invoke_gdbserver(pid_t pid)
+{
+   DEBUG(2, "invoker_invoke_gdbserver not implemented\n");
+   /* Returning True signals to not retry (too soon) to invoke. */
+   return True;
+}
+
+void invoker_valgrind_dying(void)
+{
+}
diff --git a/coregrind/vgdb-invoker-ptrace.c b/coregrind/vgdb-invoker-ptrace.c
new file mode 100644 (file)
index 0000000..71f88b7
--- /dev/null
@@ -0,0 +1,917 @@
+/*--------------------------------------------------------------------*/
+/*--- Implementation of vgdb invoker subsystem via ptrace() calls. ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2011-2013 Philippe Waroquiers
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "config.h"
+
+#include "vgdb.h"
+#include "pub_core_threadstate.h"
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/time.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#if VEX_HOST_WORDSIZE == 8
+typedef Addr64 CORE_ADDR;
+#elif VEX_HOST_WORDSIZE == 4
+typedef Addr32 CORE_ADDR;
+#else
+# error "unexpected wordsize"
+#endif
+
+#if VEX_HOST_WORDSIZE == 8
+typedef Addr64 PTRACE_XFER_TYPE;
+typedef void* PTRACE_ARG3_TYPE;
+#elif VEX_HOST_WORDSIZE == 4
+typedef Addr32 PTRACE_XFER_TYPE;
+typedef void* PTRACE_ARG3_TYPE;
+#else
+# error "unexpected wordsize"
+#endif
+
+/* True if we have continued pid_of_save_regs after PTRACE_ATTACH. */
+static Bool pid_of_save_regs_continued = False;
+
+/* True when loss of connection indicating that the Valgrind
+   process is dying. */
+static Bool dying = False;
+
+/* ptrace_(read|write)_memory are modified extracts of linux-low.c
+   from gdb 6.6. Copyrighted FSF */
+/* Copy LEN bytes from valgrind memory starting at MEMADDR
+   to vgdb memory starting at MYADDR.  */
+static
+int ptrace_read_memory (pid_t inferior_pid, CORE_ADDR memaddr,
+                        void *myaddr, size_t len)
+{
+   register int i;
+   /* Round starting address down to longword boundary.  */
+   register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
+   /* Round ending address up; get number of longwords that makes.  */
+   register int count
+      = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
+      / sizeof (PTRACE_XFER_TYPE);
+   /* Allocate buffer of that many longwords.  */
+   register PTRACE_XFER_TYPE *buffer
+      = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
+   
+   /* Read all the longwords */
+   for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) {
+      errno = 0;
+      buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, 
+                          (PTRACE_ARG3_TYPE) addr, 0);
+      if (errno)
+         return errno;
+   }
+   
+   /* Copy appropriate bytes out of the buffer.  */
+   memcpy (myaddr, 
+           (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), len);
+   
+   return 0;
+}
+
+/* Copy LEN bytes of data from vgdb memory at MYADDR
+   to valgrind memory at MEMADDR.
+   On failure (cannot write the valgrind memory)
+   returns the value of errno.  */
+__attribute__((unused)) /* not used on all platforms */
+static
+int ptrace_write_memory (pid_t inferior_pid, CORE_ADDR memaddr, 
+                         const void *myaddr, size_t len)
+{
+   register int i;
+   /* Round starting address down to longword boundary.  */
+   register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
+   /* Round ending address up; get number of longwords that makes.  */
+   register int count
+      = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) 
+      / sizeof (PTRACE_XFER_TYPE);
+   /* Allocate buffer of that many longwords.  */
+   register PTRACE_XFER_TYPE *buffer 
+      = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
+   
+   if (debuglevel >= 1) {
+      DEBUG (1, "Writing ");
+      for (i = 0; i < len; i++)
+         PDEBUG (1, "%02x", ((const unsigned char*)myaddr)[i]);
+      PDEBUG(1, " to %p\n", (void *) memaddr);
+   }
+   
+   /* Fill start and end extra bytes of buffer with existing memory data.  */
+   
+   buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_pid,
+                       (PTRACE_ARG3_TYPE) addr, 0);
+   
+   if (count > 1) {
+      buffer[count - 1]
+         = ptrace (PTRACE_PEEKTEXT, inferior_pid,
+                   (PTRACE_ARG3_TYPE) (addr + (count - 1)
+                                       * sizeof (PTRACE_XFER_TYPE)),
+                   0);
+   }
+   
+   /* Copy data to be written over corresponding part of buffer */
+   
+   memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), 
+           myaddr, len);
+   
+   /* Write the entire buffer.  */
+   
+   for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) {
+      errno = 0;
+      ptrace (PTRACE_POKETEXT, inferior_pid, 
+              (PTRACE_ARG3_TYPE) addr, buffer[i]);
+      if (errno)
+         return errno;
+   }
+   
+   return 0;
+}
+
+/* subset of VG_(threads) needed for vgdb ptrace.
+   This is initialized when process is attached. */
+typedef struct {
+   ThreadStatus status;
+   Int lwpid;
+}
+VgdbThreadState;
+static VgdbThreadState vgdb_threads[VG_N_THREADS];
+
+static const
+HChar* name_of_ThreadStatus ( ThreadStatus status )
+{
+   switch (status) {
+   case VgTs_Empty:     return "VgTs_Empty";
+   case VgTs_Init:      return "VgTs_Init";
+   case VgTs_Runnable:  return "VgTs_Runnable";
+   case VgTs_WaitSys:   return "VgTs_WaitSys";
+   case VgTs_Yielding:  return "VgTs_Yielding";
+   case VgTs_Zombie:    return "VgTs_Zombie";
+   default:             return "VgTs_???";
+  }
+}
+
+static 
+char *status_image (int status)
+{
+   static char result[256];
+   int sz = 0;
+#define APPEND(...) sz += snprintf (result+sz, 256 - sz - 1, __VA_ARGS__)
+  
+   result[0] = 0;
+
+   if (WIFEXITED(status))
+      APPEND ("WIFEXITED %d ", WEXITSTATUS(status));
+   
+   if (WIFSIGNALED(status)) {
+      APPEND ("WIFSIGNALED %d ", WTERMSIG(status));
+      if (WCOREDUMP(status)) APPEND ("WCOREDUMP ");
+   }
+
+   if (WIFSTOPPED(status))
+      APPEND ("WIFSTOPPED %d ", WSTOPSIG(status));
+
+#ifdef WIFCONTINUED
+   if (WIFCONTINUED(status))
+      APPEND ("WIFCONTINUED ");
+#endif
+
+   return result;
+#undef APPEND
+}
+
+/* Wait till the process pid is reported as stopped with signal_expected.
+   If other signal(s) than signal_expected are received, waitstopped
+   will pass them to pid, waiting for signal_expected to stop pid.
+   Returns True when process is in stopped state with signal_expected.
+   Returns False if a problem was encountered while waiting for pid
+   to be stopped.
+
+   If pid is reported as being dead/exited, waitstopped will return False.
+*/
+static
+Bool waitstopped (pid_t pid, int signal_expected, const char *msg)
+{
+   pid_t p;
+   int status = 0;
+   int signal_received;
+   int res;
+
+   while (1) {
+      DEBUG(1, "waitstopped %s before waitpid signal_expected %d\n",
+            msg, signal_expected);
+      p = waitpid(pid, &status, __WALL);
+      DEBUG(1, "after waitpid pid %d p %d status 0x%x %s\n", pid, p, 
+            status, status_image (status));
+      if (p != pid) {
+         ERROR(errno, "%s waitpid pid %d in waitstopped %d status 0x%x %s\n", 
+               msg, pid, p, status, status_image (status));
+         return False;
+      }
+
+      if (WIFEXITED(status)) {
+         shutting_down = True;
+         return False;
+      }
+
+      assert (WIFSTOPPED(status));
+      signal_received = WSTOPSIG(status);
+      if (signal_received == signal_expected)
+         break;
+
+      /* pid received a signal which is not the signal we are waiting for.
+         We continue pid, transmitting this signal. */
+      DEBUG(1, "waitstopped PTRACE_CONT with signal %d\n", signal_received);
+      res = ptrace (PTRACE_CONT, pid, NULL, signal_received);
+      if (res != 0) {
+         ERROR(errno, "waitstopped PTRACE_CONT\n");
+         return False;
+      }
+   }
+
+   return True;
+}
+
+/* Stops the given pid, wait for the process to be stopped.
+   Returns True if succesful, False otherwise.
+   msg is used in tracing and error reporting. */
+static
+Bool stop (pid_t pid, const char *msg)
+{
+   long res;
+
+   DEBUG(1, "%s SIGSTOP pid %d\n", msg, pid);
+   res = kill (pid, SIGSTOP);
+   if (res != 0) {
+      ERROR(errno, "%s SIGSTOP pid %d %ld\n", msg, pid, res);
+      return False;
+   }
+         
+   return waitstopped (pid, SIGSTOP, msg);
+
+}
+
+/* Attaches to given pid, wait for the process to be stopped.
+   Returns True if succesful, False otherwise.
+   msg is used in tracing and error reporting. */
+static
+Bool attach (pid_t pid, const char *msg)
+{
+   long res;
+   static Bool output_error = True;
+   static Bool initial_attach = True;
+   // For a ptrace_scope protected system, we do not want to output 
+   // repetitively attach error. We will output once an error
+   // for the initial_attach. Once the 1st attach has succeeded, we
+   // again show all errors.
+
+   DEBUG(1, "%s PTRACE_ATTACH pid %d\n", msg, pid);
+   res = ptrace (PTRACE_ATTACH, pid, NULL, NULL);
+   if (res != 0) {
+      if (output_error || debuglevel > 0) {
+         ERROR(errno, "%s PTRACE_ATTACH pid %d %ld\n", msg, pid, res);
+         if (initial_attach)
+            output_error = False;
+      }
+      return False;
+   }
+
+   initial_attach = False;
+   output_error = True;
+   return waitstopped(pid, SIGSTOP, msg);
+}
+
+/* once we are attached to the pid, get the list of threads and stop 
+   them all.
+   Returns True if all threads properly suspended, False otherwise. */
+static
+Bool acquire_and_suspend_threads (pid_t pid)
+{
+   int i;
+   int rw;
+   Bool pid_found = False;
+   Addr vgt;
+   int sz_tst;
+   int off_status;
+   int off_lwpid;
+   int nr_live_threads = 0;
+
+   if (shared32 != NULL) {
+      vgt = shared32->threads;
+      sz_tst = shared32->sizeof_ThreadState;
+      off_status = shared32->offset_status;
+      off_lwpid = shared32->offset_lwpid;
+   }
+   else if (shared64 != NULL) {
+      vgt = shared64->threads;
+      sz_tst = shared64->sizeof_ThreadState;
+      off_status = shared64->offset_status;
+      off_lwpid = shared64->offset_lwpid;
+   } else {
+      assert (0);
+   }
+
+   /* note: the entry 0 is unused */
+   for (i = 1; i < VG_N_THREADS; i++) {
+      vgt += sz_tst;
+      rw = ptrace_read_memory(pid, vgt+off_status,
+                              &(vgdb_threads[i].status),
+                              sizeof(ThreadStatus));
+      if (rw != 0) {
+         ERROR(rw, "status ptrace_read_memory\n");
+         return False;
+      }
+      
+      rw = ptrace_read_memory(pid, vgt+off_lwpid,
+                              &(vgdb_threads[i].lwpid),
+                              sizeof(Int));
+      if (rw != 0) {
+         ERROR(rw, "lwpid ptrace_read_memory\n");
+         return False;
+      }
+      
+      if (vgdb_threads[i].status != VgTs_Empty) {
+         DEBUG(1, "found tid %d status %s lwpid %d\n",
+               i, name_of_ThreadStatus(vgdb_threads[i].status),
+               vgdb_threads[i].lwpid);
+         nr_live_threads++;
+         if (vgdb_threads[i].lwpid <= 1) {
+            if (vgdb_threads[i].lwpid == 0 
+                && vgdb_threads[i].status == VgTs_Init) {
+               DEBUG(1, "not set lwpid tid %d status %s lwpid %d\n",
+                     i, name_of_ThreadStatus(vgdb_threads[i].status),
+                     vgdb_threads[i].lwpid);
+            } else {
+               ERROR(1, "unexpected lwpid tid %d status %s lwpid %d\n",
+                     i, name_of_ThreadStatus(vgdb_threads[i].status),
+                     vgdb_threads[i].lwpid);
+            }
+            /* in case we have a VtTs_Init thread with lwpid not yet set,
+               we try again later. */
+            return False;
+         }
+         if (vgdb_threads[i].lwpid == pid) {
+            assert (!pid_found);
+            assert (i == 1);
+            pid_found = True;
+         } else {
+            if (!attach(vgdb_threads[i].lwpid, "attach_thread")) {
+                 ERROR(0, "ERROR attach pid %d tid %d\n", 
+                       vgdb_threads[i].lwpid, i);
+               return False;
+            }
+         }
+      }
+   }
+   /* If we found no thread, it means the process is stopping, and
+      we better do not force anything to happen during that. */
+   if (nr_live_threads > 0)
+      return True;
+   else
+      return False;
+}
+
+static
+void detach_from_all_threads (pid_t pid)
+{
+   int i;
+   long res;
+   Bool pid_found = False;
+
+   /* detach from all the threads  */
+   for (i = 1; i < VG_N_THREADS; i++) {
+      if (vgdb_threads[i].status != VgTs_Empty) {
+         if (vgdb_threads[i].status == VgTs_Init
+             && vgdb_threads[i].lwpid == 0) {
+            DEBUG(1, "skipping PTRACE_DETACH pid %d tid %d status %s\n",
+                  vgdb_threads[i].lwpid, i, 
+                  name_of_ThreadStatus (vgdb_threads[i].status));
+         } else {
+            if (vgdb_threads[i].lwpid == pid) {
+               assert (!pid_found);
+               pid_found = True;
+            }
+            DEBUG(1, "PTRACE_DETACH pid %d tid %d status %s\n",
+                  vgdb_threads[i].lwpid, i, 
+                  name_of_ThreadStatus (vgdb_threads[i].status));
+            res = ptrace (PTRACE_DETACH, vgdb_threads[i].lwpid, NULL, NULL);
+            if (res != 0) {
+               ERROR(errno, "PTRACE_DETACH pid %d tid %d status %s res %ld\n", 
+                     vgdb_threads[i].lwpid, i,
+                     name_of_ThreadStatus (vgdb_threads[i].status),
+                     res);
+            }
+         }
+      }
+   }
+
+   if (!pid_found && pid) {
+      /* No threads are live. Process is busy stopping.
+         We need to detach from pid explicitely. */
+      DEBUG(1, "no thread live => PTRACE_DETACH pid %d\n", pid);
+      res = ptrace (PTRACE_DETACH, pid, NULL, NULL);
+      if (res != 0)
+         ERROR(errno, "PTRACE_DETACH pid %d res %ld\n", pid, res);
+   }
+}
+
+// if > 0, pid for which registers have to be restored.
+static int pid_of_save_regs = 0;
+static struct user user_save;
+
+// The below indicates if ptrace_getregs (and ptrace_setregs) can be used.
+// Note that some linux versions are defining PTRACE_GETREGS but using
+// it gives back EIO.
+// has_working_ptrace_getregs can take the following values:
+//  -1 : PTRACE_GETREGS is defined
+//       runtime check not yet done.
+//   0 : PTRACE_GETREGS runtime check has failed.
+//   1 : PTRACE_GETREGS defined and runtime check ok.
+#ifdef HAVE_PTRACE_GETREGS
+static int has_working_ptrace_getregs = -1;
+#endif
+
+/* Get the registers from pid into regs.
+   regs_bsz value gives the length of *regs. 
+   Returns True if all ok, otherwise False. */
+static
+Bool getregs (pid_t pid, void *regs, long regs_bsz)
+{
+   DEBUG(1, "getregs regs_bsz %ld\n", regs_bsz);
+#  ifdef HAVE_PTRACE_GETREGS
+   if (has_working_ptrace_getregs) {
+      // Platforms having GETREGS
+      long res;
+      DEBUG(1, "getregs PTRACE_GETREGS\n");
+      res = ptrace (PTRACE_GETREGS, pid, NULL, regs);
+      if (res == 0) {
+         if (has_working_ptrace_getregs == -1) {
+            // First call to PTRACE_GETREGS succesful =>
+            has_working_ptrace_getregs = 1;
+            DEBUG(1, "detected a working PTRACE_GETREGS\n");
+         }
+         assert (has_working_ptrace_getregs == 1);
+         return True;
+      }
+      else if (has_working_ptrace_getregs == 1) {
+         // We had a working call, but now it fails.
+         // This is unexpected.
+         ERROR(errno, "PTRACE_GETREGS %ld\n", res);
+         return False;
+      } else {
+         // Check this is the first call:
+         assert (has_working_ptrace_getregs == -1);
+         if (errno == EIO) {
+            DEBUG(1, "detected a broken PTRACE_GETREGS with EIO\n");
+            has_working_ptrace_getregs = 0;
+            // Fall over to the PTRACE_PEEKUSER case.
+         } else {
+            ERROR(errno, "broken PTRACE_GETREGS unexpected errno %ld\n", res);
+            return False;
+         }
+      }
+   }
+#  endif
+
+   // We assume  PTRACE_PEEKUSER is defined everywhere.
+   {
+#     ifdef PT_ENDREGS
+      long peek_bsz = PT_ENDREGS;
+      assert (peek_bsz <= regs_bsz);
+#     else
+      long peek_bsz = regs_bsz-1;
+#     endif
+      char *pregs = (char *) regs;
+      long offset;
+      errno = 0;
+      DEBUG(1, "getregs PTRACE_PEEKUSER(s) peek_bsz %ld\n", peek_bsz);
+      for (offset = 0; offset < peek_bsz; offset = offset + sizeof(long)) {
+         *(long *)(pregs+offset) = ptrace(PTRACE_PEEKUSER, pid, offset, NULL);
+         if (errno != 0) {
+            ERROR(errno, "PTRACE_PEEKUSER offset %ld\n", offset);
+            return False;
+         }
+      }
+      return True;
+   }
+
+   // If neither PTRACE_GETREGS not PTRACE_PEEKUSER have returned,
+   // then we are in serious trouble.
+   assert (0);
+}
+
+/* Set the registers of pid to regs.
+   regs_bsz value gives the length of *regs. 
+   Returns True if all ok, otherwise False. */
+static
+Bool setregs (pid_t pid, void *regs, long regs_bsz)
+{
+   DEBUG(1, "setregs regs_bsz %ld\n", regs_bsz);
+// Note : the below is checking for GETREGS, not SETREGS
+// as if one is defined and working, the other one should also work.
+#  ifdef HAVE_PTRACE_GETREGS
+   if (has_working_ptrace_getregs) {
+      // Platforms having SETREGS
+      long res;
+      // setregs can never be called before getregs has done a runtime check.
+      assert (has_working_ptrace_getregs == 1);
+      DEBUG(1, "setregs PTRACE_SETREGS\n");
+      res = ptrace (PTRACE_SETREGS, pid, NULL, regs);
+      if (res != 0) {
+         ERROR(errno, "PTRACE_SETREGS %ld\n", res);
+         return False;
+      }
+      return True;
+   }
+#  endif
+
+   {
+      char *pregs = (char *) regs;
+      long offset;
+      long res;
+#     ifdef PT_ENDREGS
+      long peek_bsz = PT_ENDREGS;
+      assert (peek_bsz <= regs_bsz);
+#     else
+      long peek_bsz = regs_bsz-1;
+#     endif
+      errno = 0;
+      DEBUG(1, "setregs PTRACE_POKEUSER(s) %ld\n", peek_bsz);
+      for (offset = 0; offset < peek_bsz; offset = offset + sizeof(long)) {
+         res = ptrace(PTRACE_POKEUSER, pid, offset, *(long*)(pregs+offset));
+         if (errno != 0) {
+            ERROR(errno, "PTRACE_POKEUSER offset %ld res %ld\n", offset, res);
+            return False;
+         }
+      }
+      return True;
+   }
+
+   // If neither PTRACE_SETREGS not PTRACE_POKEUSER have returned,
+   // then we are in serious trouble.
+   assert (0);
+}
+
+/* Restore the registers to the saved value, then detaches from all threads */
+static
+void restore_and_detach (pid_t pid)
+{
+   if (pid_of_save_regs) {
+      /* In case the 'main pid' has been continued, we need to stop it
+         before resetting the registers. */
+      if (pid_of_save_regs_continued) {
+         pid_of_save_regs_continued = False;
+         if (!stop(pid_of_save_regs, "sigstop before reset regs"))
+            DEBUG(0, "Could not sigstop before reset");
+      }
+
+      DEBUG(1, "setregs restore registers pid %d\n", pid_of_save_regs);
+      if (!setregs(pid_of_save_regs, &user_save.regs, sizeof(user_save.regs))) {
+         ERROR(errno, "setregs restore registers pid %d after cont\n",
+               pid_of_save_regs);
+      }
+      pid_of_save_regs = 0;
+   } else {
+      DEBUG(1, "PTRACE_SETREGS restore registers: no pid\n");
+   }
+   detach_from_all_threads(pid);
+}
+
+Bool invoker_invoke_gdbserver (pid_t pid)
+{
+   long res;
+   Bool stopped;
+   struct user user_mod;
+   Addr sp;
+   /* A specific int value is passed to invoke_gdbserver, to check
+      everything goes according to the plan. */
+   const int check = 0x8BADF00D; // ate bad food.
+
+   const Addr bad_return = 0;
+   // A bad return address will be pushed on the stack.
+   // The function invoke_gdbserver cannot return. If ever it returns, a NULL
+   // address pushed on the stack should ensure this is detected.
+
+   /* Not yet attached. If problem, vgdb can abort,
+      no cleanup needed. */
+
+   DEBUG(1, "attach to 'main' pid %d\n", pid);
+   if (!attach(pid, "attach main pid")) {
+      ERROR(0, "error attach main pid %d\n", pid);
+      return False;
+   }
+
+   /* Now, we are attached. If problem, detach and return. */
+
+   if (!acquire_and_suspend_threads(pid)) {
+      detach_from_all_threads(pid);
+      /* if the pid does not exist anymore, we better stop */
+      if (kill(pid, 0) != 0)
+        XERROR (errno, "invoke_gdbserver: check for pid %d existence failed\n",
+                pid);
+      return False;
+   }
+
+   if (!getregs(pid, &user_mod.regs, sizeof(user_mod.regs))) {
+      detach_from_all_threads(pid);
+      return False;
+   }
+   user_save = user_mod;
+
+#if defined(VGA_x86)
+   sp = user_mod.regs.esp;
+#elif defined(VGA_amd64)
+   sp = user_mod.regs.rsp;
+   if (shared32 != NULL) {
+     /* 64bit vgdb speaking with a 32bit executable.
+        To have system call restart properly, we need to sign extend rax.
+        For more info:
+        web search '[patch] Fix syscall restarts for amd64->i386 biarch'
+        e.g. http://sourceware.org/ml/gdb-patches/2009-11/msg00592.html */
+     *(long *)&user_save.regs.rax = *(int*)&user_save.regs.rax;
+     DEBUG(1, "Sign extending %8.8lx to %8.8lx\n",
+           user_mod.regs.rax, user_save.regs.rax);
+   }
+#elif defined(VGA_arm)
+   sp = user_mod.regs.uregs[13];
+#elif defined(VGA_ppc32)
+   sp = user_mod.regs.gpr[1];
+#elif defined(VGA_ppc64)
+   sp = user_mod.regs.gpr[1];
+#elif defined(VGA_s390x)
+   sp = user_mod.regs.gprs[15];
+#elif defined(VGA_mips32)
+   long long *p = (long long *)user_mod.regs;
+   sp = p[29];
+#elif defined(VGA_mips64)
+   sp = user_mod.regs[29];
+#else
+   I_die_here : (sp) architecture missing in vgdb.c
+#endif
+
+
+   // the magic below is derived from spying what gdb sends to
+   // the (classical) gdbserver when invoking a C function.
+   if (shared32 != NULL) {
+      // vgdb speaking with a 32bit executable.
+#if   defined(VGA_x86) || defined(VGA_amd64)
+      const int regsize = 4;
+      int rw;
+      /* push check arg on the stack */
+      sp = sp - regsize;
+      DEBUG(1, "push check arg ptrace_write_memory\n");
+      assert(regsize == sizeof(check));
+      rw = ptrace_write_memory(pid, sp, 
+                               &check, 
+                               regsize);
+      if (rw != 0) {
+         ERROR(rw, "push check arg ptrace_write_memory");
+         detach_from_all_threads(pid);
+         return False;
+      }
+
+      sp = sp - regsize;
+      DEBUG(1, "push bad_return return address ptrace_write_memory\n");
+      // Note that for a 64 bits vgdb, only 4 bytes of NULL bad_return
+      // are written.
+      rw = ptrace_write_memory(pid, sp, 
+                               &bad_return,
+                               regsize);
+      if (rw != 0) {
+         ERROR(rw, "push bad_return return address ptrace_write_memory");
+         detach_from_all_threads(pid);
+         return False;
+      }
+#if   defined(VGA_x86)
+      /* set ebp, esp, eip and orig_eax to invoke gdbserver */
+      // compiled in 32bits, speaking with a 32bits exe
+      user_mod.regs.ebp = sp; // bp set to sp
+      user_mod.regs.esp = sp;
+      user_mod.regs.eip = shared32->invoke_gdbserver;
+      user_mod.regs.orig_eax = -1L;
+#elif defined(VGA_amd64)
+      /* set ebp, esp, eip and orig_eax to invoke gdbserver */
+      // compiled in 64bits, speaking with a 32bits exe
+      user_mod.regs.rbp = sp; // bp set to sp
+      user_mod.regs.rsp = sp;
+      user_mod.regs.rip = shared32->invoke_gdbserver;
+      user_mod.regs.orig_rax = -1L;
+#else
+      I_die_here : not x86 or amd64 in x86/amd64 section/
+#endif
+
+#elif defined(VGA_ppc32) || defined(VGA_ppc64)
+      user_mod.regs.nip = shared32->invoke_gdbserver;
+      user_mod.regs.trap = -1L;
+      /* put check arg in register 3 */
+      user_mod.regs.gpr[3] = check;
+      /* put NULL return address in Link Register */
+      user_mod.regs.link = bad_return;
+
+#elif defined(VGA_arm)
+      /* put check arg in register 0 */
+      user_mod.regs.uregs[0] = check;
+      /* put NULL return address in Link Register */
+      user_mod.regs.uregs[14] = bad_return;
+      user_mod.regs.uregs[15] = shared32->invoke_gdbserver;
+
+#elif defined(VGA_s390x)
+      XERROR(0, "(fn32) s390x has no 32bits implementation");
+#elif defined(VGA_mips32)
+      /* put check arg in register 4 */
+      p[4] = check;
+      /* put NULL return address in ra */
+      p[31] = bad_return;
+      p[34] = shared32->invoke_gdbserver;
+      p[25] = shared32->invoke_gdbserver;
+      /* make stack space for args */
+      p[29] = sp - 32;
+
+#elif defined(VGA_mips64)
+      assert(0); // cannot vgdb a 32 bits executable with a 64 bits exe
+#else
+      I_die_here : architecture missing in vgdb.c
+#endif
+      }
+
+   else if (shared64 != NULL) {
+#if defined(VGA_x86)
+      assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe
+#elif defined(VGA_amd64)
+      // vgdb speaking with a 64 bit executable.
+      const int regsize = 8;
+      int rw;
+      
+      /* give check arg in rdi */
+      user_mod.regs.rdi = check;
+
+      /* push return address on stack : return to breakaddr */
+      sp = sp - regsize;
+      DEBUG(1, "push bad_return return address ptrace_write_memory\n");
+      rw = ptrace_write_memory(pid, sp, 
+                               &bad_return,
+                               sizeof(bad_return));
+      if (rw != 0) {
+         ERROR(rw, "push bad_return return address ptrace_write_memory");
+         detach_from_all_threads(pid);
+         return False;
+      }
+
+      /* set rbp, rsp, rip and orig_rax to invoke gdbserver */
+      user_mod.regs.rbp = sp; // bp set to sp
+      user_mod.regs.rsp = sp;
+      user_mod.regs.rip = shared64->invoke_gdbserver;
+      user_mod.regs.orig_rax = -1L;
+
+#elif defined(VGA_arm)
+      assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe
+#elif defined(VGA_ppc32)
+      assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe
+#elif defined(VGA_ppc64)
+      Addr64 func_addr;
+      Addr64 toc_addr;
+      int rw;
+      rw = ptrace_read_memory(pid, shared64->invoke_gdbserver,
+                              &func_addr,
+                              sizeof(Addr64));
+      if (rw != 0) {
+         ERROR(rw, "ppc64 read func_addr\n");
+         detach_from_all_threads(pid);
+         return False;
+      }
+      rw = ptrace_read_memory(pid, shared64->invoke_gdbserver+8,
+                              &toc_addr,
+                              sizeof(Addr64));
+      if (rw != 0) {
+         ERROR(rw, "ppc64 read toc_addr\n");
+         detach_from_all_threads(pid);
+         return False;
+      }
+      // We are not pushing anything on the stack, so it is not
+      // very clear why the sp has to be decreased, but it seems
+      // needed. The ppc64 ABI might give some lights on this ?
+      user_mod.regs.gpr[1] = sp - 220;
+      user_mod.regs.gpr[2] = toc_addr;
+      user_mod.regs.nip = func_addr;
+      user_mod.regs.trap = -1L;
+      /* put check arg in register 3 */
+      user_mod.regs.gpr[3] = check;
+      /* put bad_return return address in Link Register */
+      user_mod.regs.link = bad_return;
+#elif defined(VGA_s390x)
+      /* put check arg in register r2 */
+      user_mod.regs.gprs[2] = check;
+      /* bad_return Return address is in r14 */
+      user_mod.regs.gprs[14] = bad_return;
+      /* minimum stack frame */
+      sp = sp - 160;
+      user_mod.regs.gprs[15] = sp;
+      /* set program counter */
+      user_mod.regs.psw.addr = shared64->invoke_gdbserver;
+#elif defined(VGA_mips32)
+      assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe
+#elif defined(VGA_mips64)
+      /* put check arg in register 4 */
+      user_mod.regs[4] = check;
+      /* put NULL return address in ra */
+      user_mod.regs[31] = bad_return;
+      user_mod.regs[34] = shared64->invoke_gdbserver;
+      user_mod.regs[25] = shared64->invoke_gdbserver;
+#else
+      I_die_here: architecture missing in vgdb.c
+#endif
+   }
+   else {
+      assert(0);
+   }
+   
+   if (!setregs(pid, &user_mod.regs, sizeof(user_mod.regs))) {
+      detach_from_all_threads(pid);
+      return False;
+   }
+   /* Now that we have modified the registers, we set
+      pid_of_save_regs to indicate that restore_and_detach
+      must restore the registers in case of cleanup. */
+   pid_of_save_regs = pid;
+   pid_of_save_regs_continued = False;
+      
+
+   /* We PTRACE_CONT-inue pid. 
+      Either gdbserver will be invoked directly (if all
+      threads are interruptible) or gdbserver will be
+      called soon by the scheduler. In the first case,
+      pid will stop on the break inserted above when
+      gdbserver returns. In the 2nd case, the break will
+      be encountered directly. */
+   DEBUG(1, "PTRACE_CONT to invoke\n");
+   res = ptrace (PTRACE_CONT, pid, NULL, NULL);
+   if (res != 0) {
+      ERROR(errno, "PTRACE_CONT\n");
+      restore_and_detach(pid);
+      return False;
+   }
+   pid_of_save_regs_continued = True;
+   /* Wait for SIGSTOP generated by m_gdbserver.c give_control_back_to_vgdb */
+   stopped = waitstopped (pid, SIGSTOP,
+                          "waitpid status after PTRACE_CONT to invoke");
+   if (stopped) {
+      /* Here pid has properly stopped on the break. */
+      pid_of_save_regs_continued = False;
+      restore_and_detach(pid);
+      return True;
+   } else {
+      /* Whatever kind of problem happened. We shutdown. */
+      shutting_down = True;
+      return False;
+   }
+}
+
+void invoker_cleanup_restore_and_detach(void *v_pid)
+{
+   DEBUG(1, "invoker_cleanup_restore_and_detach dying: %d\n", dying);
+   if (!dying)
+      restore_and_detach(*(int*)v_pid);
+}
+
+void invoker_restrictions_msg(void)
+{
+}
+
+void invoker_valgrind_dying(void)
+{
+   /* Avoid messing up with registers of valgrind when it is dying. */
+   pid_of_save_regs_continued = False;
+   dying = True;
+}
index 485ad5d1a67db89806cb95e0ff65dd127095a075..08633bbc488c223ee407fc74b1cc561cdd5f00c4 100644 (file)
    The GNU General Public License is contained in the file COPYING.
 */
 
-#include "pub_core_basics.h"
-#include "pub_core_vki.h"
-#include "pub_core_libcsetjmp.h"
-#include "pub_core_threadstate.h"
-#include "pub_core_gdbserver.h"
+#include "vgdb.h"
+
 #include "config.h"
 
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
-#include <unistd.h>
-#include <string.h>
 #include <poll.h>
 #include <pthread.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <errno.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/socket.h>
+#include <string.h>
+#include <unistd.h>
 #include <netinet/in.h>
-#include <arpa/inet.h>
 #include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/wait.h>
-#include <assert.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
 /* vgdb has two usages:
    1. relay application between gdb and the gdbserver embedded in valgrind.
    2. standalone to send monitor commands to a running valgrind-ified process
    As a standalone utility, vgdb builds command packets to write to valgrind,
    sends it and reads the reply. The same two threads are used to write/read.
    Once all the commands are sent and their replies received, vgdb will exit.
-   
 */
 
-/* define PTRACEINVOKER to compile the ptrace related code
-   which ensures a valgrind process blocked in a system call
-   can be "waken up". PTRACEINVOKER implies some architecture
-   specific code and/or some OS specific code. */
-#if defined(VGA_arm) || defined(VGA_x86) || defined(VGA_amd64) \
-    || defined(VGA_ppc32) || defined(VGA_ppc64) || defined(VGA_s390x) \
-    || defined(VGA_mips32) || defined(VGA_mips64)
-#define PTRACEINVOKER
-#else
-I_die_here : (PTRACEINVOKER) architecture missing in vgdb.c
-#endif
-
-/* Some darwin specific stuff is needed as ptrace is not
-   fully supported on MacOS. Till we find someone courageous
-   having access to Darwin, there is no PTRACEINVOKER. */
-#if defined(VGO_darwin)
-#undef PTRACEINVOKER
-#endif
-
-#if defined(VGPV_arm_linux_android) || defined(VGPV_x86_linux_android)
-#undef PTRACEINVOKER
-#endif
-
-#if defined(PTRACEINVOKER)
-#include <sys/user.h>
-#endif
-
-
-// Outputs information for the user about ptrace not working.
-static void ptrace_restrictions_msg(void);
-
-static int debuglevel;
-static struct timeval dbgtv;
-/* if level <= debuglevel, print timestamp, then print provided by debug info */
-#define DEBUG(level, ...) (level <= debuglevel ?                        \
-                           gettimeofday(&dbgtv, NULL),                  \
-                           fprintf(stderr, "%ld.%6.6ld ",               \
-                                   (long int)dbgtv.tv_sec,              \
-                                   (long int)dbgtv.tv_usec),            \
-                           fprintf(stderr, __VA_ARGS__),fflush(stderr)  \
-                           : 0)
-
-/* same as DEBUG but does not print time stamp info */
-#define PDEBUG(level, ...) (level <= debuglevel ?                       \
-                            fprintf(stderr, __VA_ARGS__),fflush(stderr) \
-                            : 0)
-
-/* if errno != 0, 
-   report the errno and fprintf the ... varargs on stderr. */
-#define ERROR(errno, ...) ((errno == 0 ? 0 : perror("syscall failed")), \
-                           fprintf(stderr, __VA_ARGS__),                \
-                           fflush(stderr))
-/* same as ERROR, but also exits with status 1 */
-#define XERROR(errno, ...) ((errno == 0 ? 0 : perror("syscall failed")), \
-                            fprintf(stderr, __VA_ARGS__),                \
-                            fflush(stderr),                              \
-                            exit(1))
-
+int debuglevel;
+struct timeval dbgtv;
 static char *vgdb_prefix = NULL;
 
 /* Will be set to True when any condition indicating we have to shutdown
    is encountered. */
-static Bool shutting_down = False;
+Bool shutting_down = False;
 
-static VgdbShared32 *shared32;
-static VgdbShared64 *shared64;
+VgdbShared32 *shared32;
+VgdbShared64 *shared64;
 #define VS_written_by_vgdb (shared32 != NULL ?        \
                             shared32->written_by_vgdb \
                             : shared64->written_by_vgdb)
@@ -266,898 +203,11 @@ void map_vgdbshared (char* shared_mem)
 
 }
 
-#if VEX_HOST_WORDSIZE == 8
-typedef Addr64 CORE_ADDR;
-typedef Addr64 PTRACE_XFER_TYPE;
-typedef void* PTRACE_ARG3_TYPE;
-#elif VEX_HOST_WORDSIZE == 4
-typedef Addr32 CORE_ADDR;
-typedef Addr32 PTRACE_XFER_TYPE;
-typedef void* PTRACE_ARG3_TYPE;
-#else
-# error "unexpected wordsize"
-#endif
-
-static Bool pid_of_save_regs_continued = False;
-// True if we have continued pid_of_save_regs after PTRACE_ATTACH
-
-static Bool dying = False;
-// Set to True when loss of connection indicating that the Valgrind
-// process is dying.
-
-/* To be called when connection with valgrind is lost.  In case we
-have lost the connection, it means that Valgrind has closed the
-connection and is busy exiting. We can't and don't have to stop it in
-this case. */
-static
-void valgrind_dying(void)
-{
-   pid_of_save_regs_continued = False;
-   dying = True;
-}
-
-
-#ifdef PTRACEINVOKER
-/* ptrace_(read|write)_memory are modified extracts of linux-low.c
-   from gdb 6.6. Copyrighted FSF */
-/* Copy LEN bytes from valgrind memory starting at MEMADDR
-   to vgdb memory starting at MYADDR.  */
-
-static
-int ptrace_read_memory (pid_t inferior_pid, CORE_ADDR memaddr,
-                        void *myaddr, int len)
-{
-   register int i;
-   /* Round starting address down to longword boundary.  */
-   register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
-   /* Round ending address up; get number of longwords that makes.  */
-   register int count
-      = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
-      / sizeof (PTRACE_XFER_TYPE);
-   /* Allocate buffer of that many longwords.  */
-   register PTRACE_XFER_TYPE *buffer
-      = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
-   
-   /* Read all the longwords */
-   for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) {
-      errno = 0;
-      buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, 
-                          (PTRACE_ARG3_TYPE) addr, 0);
-      if (errno)
-         return errno;
-   }
-   
-   /* Copy appropriate bytes out of the buffer.  */
-   memcpy (myaddr, 
-           (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), len);
-   
-   return 0;
-}
-
-/* Copy LEN bytes of data from vgdb memory at MYADDR
-   to valgrind memory at MEMADDR.
-   On failure (cannot write the valgrind memory)
-   returns the value of errno.  */
-__attribute__((unused)) /* not used on all platforms */
-static
-int ptrace_write_memory (pid_t inferior_pid, CORE_ADDR memaddr, 
-                         const void *myaddr, int len)
-{
-   register int i;
-   /* Round starting address down to longword boundary.  */
-   register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
-   /* Round ending address up; get number of longwords that makes.  */
-   register int count
-      = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) 
-      / sizeof (PTRACE_XFER_TYPE);
-   /* Allocate buffer of that many longwords.  */
-   register PTRACE_XFER_TYPE *buffer 
-      = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
-   
-   if (debuglevel >= 1) {
-      DEBUG (1, "Writing ");
-      for (i = 0; i < len; i++)
-         PDEBUG (1, "%02x", ((const unsigned char*)myaddr)[i]);
-      PDEBUG(1, " to %p\n", (void *) memaddr);
-   }
-   
-   /* Fill start and end extra bytes of buffer with existing memory data.  */
-   
-   buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_pid,
-                       (PTRACE_ARG3_TYPE) addr, 0);
-   
-   if (count > 1) {
-      buffer[count - 1]
-         = ptrace (PTRACE_PEEKTEXT, inferior_pid,
-                   (PTRACE_ARG3_TYPE) (addr + (count - 1)
-                                       * sizeof (PTRACE_XFER_TYPE)),
-                   0);
-   }
-   
-   /* Copy data to be written over corresponding part of buffer */
-   
-   memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), 
-           myaddr, len);
-   
-   /* Write the entire buffer.  */
-   
-   for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) {
-      errno = 0;
-      ptrace (PTRACE_POKETEXT, inferior_pid, 
-              (PTRACE_ARG3_TYPE) addr, buffer[i]);
-      if (errno)
-         return errno;
-   }
-   
-   return 0;
-}
-
-/* subset of VG_(threads) needed for vgdb ptrace.
-   This is initialized when process is attached. */
-typedef struct {
-   ThreadStatus status;
-   Int lwpid;
-}
-VgdbThreadState;
-static VgdbThreadState vgdb_threads[VG_N_THREADS];
-
-static const
-HChar* name_of_ThreadStatus ( ThreadStatus status )
-{
-   switch (status) {
-   case VgTs_Empty:     return "VgTs_Empty";
-   case VgTs_Init:      return "VgTs_Init";
-   case VgTs_Runnable:  return "VgTs_Runnable";
-   case VgTs_WaitSys:   return "VgTs_WaitSys";
-   case VgTs_Yielding:  return "VgTs_Yielding";
-   case VgTs_Zombie:    return "VgTs_Zombie";
-   default:             return "VgTs_???";
-  }
-}
-
-static 
-char *status_image (int status)
-{
-   static char result[256];
-   int sz = 0;
-#define APPEND(...) sz += snprintf (result+sz, 256 - sz - 1, __VA_ARGS__)
-  
-   result[0] = 0;
-
-   if (WIFEXITED(status))
-      APPEND ("WIFEXITED %d ", WEXITSTATUS(status));
-   
-   if (WIFSIGNALED(status)) {
-      APPEND ("WIFSIGNALED %d ", WTERMSIG(status));
-      if (WCOREDUMP(status)) APPEND ("WCOREDUMP ");
-   }
-
-   if (WIFSTOPPED(status))
-      APPEND ("WIFSTOPPED %d ", WSTOPSIG(status));
-
-#ifdef WIFCONTINUED
-   if (WIFCONTINUED(status))
-      APPEND ("WIFCONTINUED ");
-#endif
-
-   return result;
-#undef APPEND
-}
-
-/* Wait till the process pid is reported as stopped with signal_expected.
-   If other signal(s) than signal_expected are received, waitstopped
-   will pass them to pid, waiting for signal_expected to stop pid.
-   Returns True when process is in stopped state with signal_expected.
-   Returns False if a problem was encountered while waiting for pid
-   to be stopped.
-
-   If pid is reported as being dead/exited, waitstopped will return False.
-*/
-static
-Bool waitstopped (int pid, int signal_expected, const char *msg)
-{
-   pid_t p;
-   int status = 0;
-   int signal_received;
-   int res;
-
-   while (1) {
-      DEBUG(1, "waitstopped %s before waitpid signal_expected %d\n",
-            msg, signal_expected);
-      p = waitpid(pid, &status, __WALL);
-      DEBUG(1, "after waitpid pid %d p %d status 0x%x %s\n", pid, p, 
-            status, status_image (status));
-      if (p != pid) {
-         ERROR(errno, "%s waitpid pid %d in waitstopped %d status 0x%x %s\n", 
-               msg, pid, p, status, status_image (status));
-         return False;
-      }
-
-      if (WIFEXITED(status)) {
-         shutting_down = True;
-         return False;
-      }
-
-      assert (WIFSTOPPED(status));
-      signal_received = WSTOPSIG(status);
-      if (signal_received == signal_expected)
-         break;
-
-      /* pid received a signal which is not the signal we are waiting for.
-         We continue pid, transmitting this signal. */
-      DEBUG(1, "waitstopped PTRACE_CONT with signal %d\n", signal_received);
-      res = ptrace (PTRACE_CONT, pid, NULL, signal_received);
-      if (res != 0) {
-         ERROR(errno, "waitstopped PTRACE_CONT\n");
-         return False;
-      }
-   }
-
-   return True;
-}
-
-/* Stops the given pid, wait for the process to be stopped.
-   Returns True if succesful, False otherwise.
-   msg is used in tracing and error reporting. */
-static
-Bool stop (int pid, const char *msg)
-{
-   long res;
-
-   DEBUG(1, "%s SIGSTOP pid %d\n", msg, pid);
-   res = kill (pid, SIGSTOP);
-   if (res != 0) {
-      ERROR(errno, "%s SIGSTOP pid %d %ld\n", msg, pid, res);
-      return False;
-   }
-         
-   return waitstopped (pid, SIGSTOP, msg);
-
-}
-
-/* Attaches to given pid, wait for the process to be stopped.
-   Returns True if succesful, False otherwise.
-   msg is used in tracing and error reporting. */
-static
-Bool attach (int pid, const char *msg)
-{
-   long res;
-   static Bool output_error = True;
-   static Bool initial_attach = True;
-   // For a ptrace_scope protected system, we do not want to output 
-   // repetitively attach error. We will output once an error
-   // for the initial_attach. Once the 1st attach has succeeded, we
-   // again show all errors.
-
-   DEBUG(1, "%s PTRACE_ATTACH pid %d\n", msg, pid);
-   res = ptrace (PTRACE_ATTACH, pid, NULL, NULL);
-   if (res != 0) {
-      if (output_error || debuglevel > 0) {
-         ERROR(errno, "%s PTRACE_ATTACH pid %d %ld\n", msg, pid, res);
-         if (initial_attach)
-            output_error = False;
-      }
-      return False;
-   }
-
-   initial_attach = False;
-   output_error = True;
-   return waitstopped(pid, SIGSTOP, msg);
-}
-
-/* once we are attached to the pid, get the list of threads and stop 
-   them all.
-   Returns True if all threads properly suspended, False otherwise. */
-static
-Bool acquire_and_suspend_threads(int pid)
-{
-   int i;
-   int rw;
-   Bool pid_found = False;
-   Addr vgt;
-   int sz_tst;
-   int off_status;
-   int off_lwpid;
-   int nr_live_threads = 0;
-
-   if (shared32 != NULL) {
-      vgt = shared32->threads;
-      sz_tst = shared32->sizeof_ThreadState;
-      off_status = shared32->offset_status;
-      off_lwpid = shared32->offset_lwpid;
-   }
-   else if (shared64 != NULL) {
-      vgt = shared64->threads;
-      sz_tst = shared64->sizeof_ThreadState;
-      off_status = shared64->offset_status;
-      off_lwpid = shared64->offset_lwpid;
-   } else {
-      assert (0);
-   }
-
-   /* note: the entry 0 is unused */
-   for (i = 1; i < VG_N_THREADS; i++) {
-      vgt += sz_tst;
-      rw = ptrace_read_memory(pid, vgt+off_status,
-                              &(vgdb_threads[i].status),
-                              sizeof(ThreadStatus));
-      if (rw != 0) {
-         ERROR(rw, "status ptrace_read_memory\n");
-         return False;
-      }
-      
-      rw = ptrace_read_memory(pid, vgt+off_lwpid,
-                              &(vgdb_threads[i].lwpid),
-                              sizeof(Int));
-      if (rw != 0) {
-         ERROR(rw, "lwpid ptrace_read_memory\n");
-         return False;
-      }
-      
-      if (vgdb_threads[i].status != VgTs_Empty) {
-         DEBUG(1, "found tid %d status %s lwpid %d\n",
-               i, name_of_ThreadStatus(vgdb_threads[i].status),
-               vgdb_threads[i].lwpid);
-         nr_live_threads++;
-         if (vgdb_threads[i].lwpid <= 1) {
-            if (vgdb_threads[i].lwpid == 0 
-                && vgdb_threads[i].status == VgTs_Init) {
-               DEBUG(1, "not set lwpid tid %d status %s lwpid %d\n",
-                     i, name_of_ThreadStatus(vgdb_threads[i].status),
-                     vgdb_threads[i].lwpid);
-            } else {
-               ERROR(1, "unexpected lwpid tid %d status %s lwpid %d\n",
-                     i, name_of_ThreadStatus(vgdb_threads[i].status),
-                     vgdb_threads[i].lwpid);
-            }
-            /* in case we have a VtTs_Init thread with lwpid not yet set,
-               we try again later. */
-            return False;
-         }
-         if (vgdb_threads[i].lwpid == pid) {
-            assert (!pid_found);
-            assert (i == 1);
-            pid_found = True;
-         } else {
-            if (!attach(vgdb_threads[i].lwpid, "attach_thread")) {
-                 ERROR(0, "ERROR attach pid %d tid %d\n", 
-                       vgdb_threads[i].lwpid, i);
-               return False;
-            }
-         }
-      }
-   }
-   /* If we found no thread, it means the process is stopping, and
-      we better do not force anything to happen during that. */
-   if (nr_live_threads > 0)
-      return True;
-   else
-      return False;
-}
-
-static
-void detach_from_all_threads(int pid)
-{
-   int i;
-   long res;
-   Bool pid_found = False;
-
-   /* detach from all the threads  */
-   for (i = 1; i < VG_N_THREADS; i++) {
-      if (vgdb_threads[i].status != VgTs_Empty) {
-         if (vgdb_threads[i].status == VgTs_Init
-             && vgdb_threads[i].lwpid == 0) {
-            DEBUG(1, "skipping PTRACE_DETACH pid %d tid %d status %s\n",
-                  vgdb_threads[i].lwpid, i, 
-                  name_of_ThreadStatus (vgdb_threads[i].status));
-         } else {
-            if (vgdb_threads[i].lwpid == pid) {
-               assert (!pid_found);
-               pid_found = True;
-            }
-            DEBUG(1, "PTRACE_DETACH pid %d tid %d status %s\n",
-                  vgdb_threads[i].lwpid, i, 
-                  name_of_ThreadStatus (vgdb_threads[i].status));
-            res = ptrace (PTRACE_DETACH, vgdb_threads[i].lwpid, NULL, NULL);
-            if (res != 0) {
-               ERROR(errno, "PTRACE_DETACH pid %d tid %d status %s res %ld\n", 
-                     vgdb_threads[i].lwpid, i,
-                     name_of_ThreadStatus (vgdb_threads[i].status),
-                     res);
-            }
-         }
-      }
-   }
-
-   if (!pid_found && pid) {
-      /* No threads are live. Process is busy stopping.
-         We need to detach from pid explicitely. */
-      DEBUG(1, "no thread live => PTRACE_DETACH pid %d\n", pid);
-      res = ptrace (PTRACE_DETACH, pid, NULL, NULL);
-      if (res != 0)
-         ERROR(errno, "PTRACE_DETACH pid %d res %ld\n", pid, res);
-   }
-}
-
-// if > 0, pid for which registers have to be restored.
-static int pid_of_save_regs = 0;
-static struct user user_save;
-
-// The below indicates if ptrace_getregs (and ptrace_setregs) can be used.
-// Note that some linux versions are defining PTRACE_GETREGS but using
-// it gives back EIO.
-// has_working_ptrace_getregs can take the following values:
-//  -1 : PTRACE_GETREGS is defined
-//       runtime check not yet done.
-//   0 : PTRACE_GETREGS runtime check has failed.
-//   1 : PTRACE_GETREGS defined and runtime check ok.
-#ifdef HAVE_PTRACE_GETREGS
-static int has_working_ptrace_getregs = -1;
-#endif
-
-/* Get the registers from pid into regs.
-   regs_bsz value gives the length of *regs. 
-   Returns True if all ok, otherwise False. */
-static
-Bool getregs (int pid, void *regs, long regs_bsz)
-{
-   DEBUG(1, "getregs regs_bsz %ld\n", regs_bsz);
-#  ifdef HAVE_PTRACE_GETREGS
-   if (has_working_ptrace_getregs) {
-      // Platforms having GETREGS
-      long res;
-      DEBUG(1, "getregs PTRACE_GETREGS\n");
-      res = ptrace (PTRACE_GETREGS, pid, NULL, regs);
-      if (res == 0) {
-         if (has_working_ptrace_getregs == -1) {
-            // First call to PTRACE_GETREGS succesful =>
-            has_working_ptrace_getregs = 1;
-            DEBUG(1, "detected a working PTRACE_GETREGS\n");
-         }
-         assert (has_working_ptrace_getregs == 1);
-         return True;
-      }
-      else if (has_working_ptrace_getregs == 1) {
-         // We had a working call, but now it fails.
-         // This is unexpected.
-         ERROR(errno, "PTRACE_GETREGS %ld\n", res);
-         return False;
-      } else {
-         // Check this is the first call:
-         assert (has_working_ptrace_getregs == -1);
-         if (errno == EIO) {
-            DEBUG(1, "detected a broken PTRACE_GETREGS with EIO\n");
-            has_working_ptrace_getregs = 0;
-            // Fall over to the PTRACE_PEEKUSER case.
-         } else {
-            ERROR(errno, "broken PTRACE_GETREGS unexpected errno %ld\n", res);
-            return False;
-         }
-      }
-   }
-#  endif
-
-   // We assume  PTRACE_PEEKUSER is defined everywhere.
-   {
-#     ifdef PT_ENDREGS
-      long peek_bsz = PT_ENDREGS;
-      assert (peek_bsz <= regs_bsz);
-#     else
-      long peek_bsz = regs_bsz-1;
-#     endif
-      char *pregs = (char *) regs;
-      long offset;
-      errno = 0;
-      DEBUG(1, "getregs PTRACE_PEEKUSER(s) peek_bsz %ld\n", peek_bsz);
-      for (offset = 0; offset < peek_bsz; offset = offset + sizeof(long)) {
-         *(long *)(pregs+offset) = ptrace(PTRACE_PEEKUSER, pid, offset, NULL);
-         if (errno != 0) {
-            ERROR(errno, "PTRACE_PEEKUSER offset %ld\n", offset);
-            return False;
-         }
-      }
-      return True;
-   }
-
-   // If neither PTRACE_GETREGS not PTRACE_PEEKUSER have returned,
-   // then we are in serious trouble.
-   assert (0);
-}
-
-/* Set the registers of pid to regs.
-   regs_bsz value gives the length of *regs. 
-   Returns True if all ok, otherwise False. */
-static
-Bool setregs (int pid, void *regs, long regs_bsz)
-{
-   DEBUG(1, "setregs regs_bsz %ld\n", regs_bsz);
-// Note : the below is checking for GETREGS, not SETREGS
-// as if one is defined and working, the other one should also work.
-#  ifdef HAVE_PTRACE_GETREGS
-   if (has_working_ptrace_getregs) {
-      // Platforms having SETREGS
-      long res;
-      // setregs can never be called before getregs has done a runtime check.
-      assert (has_working_ptrace_getregs == 1);
-      DEBUG(1, "setregs PTRACE_SETREGS\n");
-      res = ptrace (PTRACE_SETREGS, pid, NULL, regs);
-      if (res != 0) {
-         ERROR(errno, "PTRACE_SETREGS %ld\n", res);
-         return False;
-      }
-      return True;
-   }
-#  endif
-
-   {
-      char *pregs = (char *) regs;
-      long offset;
-      long res;
-#     ifdef PT_ENDREGS
-      long peek_bsz = PT_ENDREGS;
-      assert (peek_bsz <= regs_bsz);
-#     else
-      long peek_bsz = regs_bsz-1;
-#     endif
-      errno = 0;
-      DEBUG(1, "setregs PTRACE_POKEUSER(s) %ld\n", peek_bsz);
-      for (offset = 0; offset < peek_bsz; offset = offset + sizeof(long)) {
-         res = ptrace(PTRACE_POKEUSER, pid, offset, *(long*)(pregs+offset));
-         if (errno != 0) {
-            ERROR(errno, "PTRACE_POKEUSER offset %ld res %ld\n", offset, res);
-            return False;
-         }
-      }
-      return True;
-   }
-
-   // If neither PTRACE_SETREGS not PTRACE_POKEUSER have returned,
-   // then we are in serious trouble.
-   assert (0);
-}
-
-/* Restore the registers to the saved value, then detaches from all threads */
-static
-void restore_and_detach(int pid)
-{
-   if (pid_of_save_regs) {
-      /* In case the 'main pid' has been continued, we need to stop it
-         before resetting the registers. */
-      if (pid_of_save_regs_continued) {
-         pid_of_save_regs_continued = False;
-         if (!stop(pid_of_save_regs, "sigstop before reset regs"))
-            DEBUG(0, "Could not sigstop before reset");
-      }
-
-      DEBUG(1, "setregs restore registers pid %d\n", pid_of_save_regs);
-      if (!setregs(pid_of_save_regs, &user_save.regs, sizeof(user_save.regs))) {
-         ERROR(errno, "setregs restore registers pid %d after cont\n",
-               pid_of_save_regs);
-      }
-      pid_of_save_regs = 0;
-   } else {
-      DEBUG(1, "PTRACE_SETREGS restore registers: no pid\n");
-   }
-   detach_from_all_threads(pid);
-}
-
-/* Ensures that the gdbserver code is invoked by pid.
-   If an error occurs, resets to the valgrind process
-   to the state it has before being ptrace-d.
-   Returns True if invoke successful, False otherwise.
-*/
-static
-Bool invoke_gdbserver (int pid)
-{
-   static Bool ptrace_restrictions_msg_given = False;
-   long res;
-   Bool stopped;
-   struct user user_mod;
-   Addr sp;
-   /* A specific int value is passed to invoke_gdbserver, to check
-      everything goes according to the plan. */
-   const int check = 0x8BADF00D; // ate bad food.
-
-   const Addr bad_return = 0;
-   // A bad return address will be pushed on the stack.
-   // The function invoke_gdbserver cannot return. If ever it returns, a NULL
-   // address pushed on the stack should ensure this is detected.
-
-   /* Not yet attached. If problem, vgdb can abort,
-      no cleanup needed. */
-
-   DEBUG(1, "attach to 'main' pid %d\n", pid);
-   if (!attach(pid, "attach main pid")) {
-      if (!ptrace_restrictions_msg_given) {
-         ptrace_restrictions_msg_given = True;
-         ERROR(0, "error attach main pid %d\n", pid);
-         ptrace_restrictions_msg();
-      }
-      return False;
-   }
-
-   /* Now, we are attached. If problem, detach and return. */
-
-   if (!acquire_and_suspend_threads(pid)) {
-      detach_from_all_threads(pid);
-      /* if the pid does not exist anymore, we better stop */
-      if (kill(pid, 0) != 0)
-        XERROR (errno, "invoke_gdbserver: check for pid %d existence failed\n",
-                pid);
-      return False;
-   }
-
-   if (!getregs(pid, &user_mod.regs, sizeof(user_mod.regs))) {
-      detach_from_all_threads(pid);
-      return False;
-   }
-   user_save = user_mod;
-
-#if defined(VGA_x86)
-   sp = user_mod.regs.esp;
-#elif defined(VGA_amd64)
-   sp = user_mod.regs.rsp;
-   if (shared32 != NULL) {
-     /* 64bit vgdb speaking with a 32bit executable.
-        To have system call restart properly, we need to sign extend rax.
-        For more info:
-        web search '[patch] Fix syscall restarts for amd64->i386 biarch'
-        e.g. http://sourceware.org/ml/gdb-patches/2009-11/msg00592.html */
-     *(long *)&user_save.regs.rax = *(int*)&user_save.regs.rax;
-     DEBUG(1, "Sign extending %8.8lx to %8.8lx\n",
-           user_mod.regs.rax, user_save.regs.rax);
-   }
-#elif defined(VGA_arm)
-   sp = user_mod.regs.uregs[13];
-#elif defined(VGA_ppc32)
-   sp = user_mod.regs.gpr[1];
-#elif defined(VGA_ppc64)
-   sp = user_mod.regs.gpr[1];
-#elif defined(VGA_s390x)
-   sp = user_mod.regs.gprs[15];
-#elif defined(VGA_mips32)
-   long long *p = (long long *)user_mod.regs;
-   sp = p[29];
-#elif defined(VGA_mips64)
-   sp = user_mod.regs[29];
-#else
-   I_die_here : (sp) architecture missing in vgdb.c
-#endif
-
-
-   // the magic below is derived from spying what gdb sends to
-   // the (classical) gdbserver when invoking a C function.
-   if (shared32 != NULL) {
-      // vgdb speaking with a 32bit executable.
-#if   defined(VGA_x86) || defined(VGA_amd64)
-      const int regsize = 4;
-      int rw;
-      /* push check arg on the stack */
-      sp = sp - regsize;
-      DEBUG(1, "push check arg ptrace_write_memory\n");
-      assert(regsize == sizeof(check));
-      rw = ptrace_write_memory(pid, sp, 
-                               &check, 
-                               regsize);
-      if (rw != 0) {
-         ERROR(rw, "push check arg ptrace_write_memory");
-         detach_from_all_threads(pid);
-         return False;
-      }
-
-      sp = sp - regsize;
-      DEBUG(1, "push bad_return return address ptrace_write_memory\n");
-      // Note that for a 64 bits vgdb, only 4 bytes of NULL bad_return
-      // are written.
-      rw = ptrace_write_memory(pid, sp, 
-                               &bad_return,
-                               regsize);
-      if (rw != 0) {
-         ERROR(rw, "push bad_return return address ptrace_write_memory");
-         detach_from_all_threads(pid);
-         return False;
-      }
-#if   defined(VGA_x86)
-      /* set ebp, esp, eip and orig_eax to invoke gdbserver */
-      // compiled in 32bits, speaking with a 32bits exe
-      user_mod.regs.ebp = sp; // bp set to sp
-      user_mod.regs.esp = sp;
-      user_mod.regs.eip = shared32->invoke_gdbserver;
-      user_mod.regs.orig_eax = -1L;
-#elif defined(VGA_amd64)
-      /* set ebp, esp, eip and orig_eax to invoke gdbserver */
-      // compiled in 64bits, speaking with a 32bits exe
-      user_mod.regs.rbp = sp; // bp set to sp
-      user_mod.regs.rsp = sp;
-      user_mod.regs.rip = shared32->invoke_gdbserver;
-      user_mod.regs.orig_rax = -1L;
-#else
-      I_die_here : not x86 or amd64 in x86/amd64 section/
-#endif
-
-#elif defined(VGA_ppc32) || defined(VGA_ppc64)
-      user_mod.regs.nip = shared32->invoke_gdbserver;
-      user_mod.regs.trap = -1L;
-      /* put check arg in register 3 */
-      user_mod.regs.gpr[3] = check;
-      /* put NULL return address in Link Register */
-      user_mod.regs.link = bad_return;
-
-#elif defined(VGA_arm)
-      /* put check arg in register 0 */
-      user_mod.regs.uregs[0] = check;
-      /* put NULL return address in Link Register */
-      user_mod.regs.uregs[14] = bad_return;
-      user_mod.regs.uregs[15] = shared32->invoke_gdbserver;
-
-#elif defined(VGA_s390x)
-      XERROR(0, "(fn32) s390x has no 32bits implementation");
-#elif defined(VGA_mips32)
-      /* put check arg in register 4 */
-      p[4] = check;
-      /* put NULL return address in ra */
-      p[31] = bad_return;
-      p[34] = shared32->invoke_gdbserver;
-      p[25] = shared32->invoke_gdbserver;
-      /* make stack space for args */
-      p[29] = sp - 32;
-
-#elif defined(VGA_mips64)
-      assert(0); // cannot vgdb a 32 bits executable with a 64 bits exe
-#else
-      I_die_here : architecture missing in vgdb.c
-#endif
-      }
-
-   else if (shared64 != NULL) {
-#if defined(VGA_x86)
-      assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe
-#elif defined(VGA_amd64)
-      // vgdb speaking with a 64 bit executable.
-      const int regsize = 8;
-      int rw;
-      
-      /* give check arg in rdi */
-      user_mod.regs.rdi = check;
-
-      /* push return address on stack : return to breakaddr */
-      sp = sp - regsize;
-      DEBUG(1, "push bad_return return address ptrace_write_memory\n");
-      rw = ptrace_write_memory(pid, sp, 
-                               &bad_return,
-                               sizeof(bad_return));
-      if (rw != 0) {
-         ERROR(rw, "push bad_return return address ptrace_write_memory");
-         detach_from_all_threads(pid);
-         return False;
-      }
-
-      /* set rbp, rsp, rip and orig_rax to invoke gdbserver */
-      user_mod.regs.rbp = sp; // bp set to sp
-      user_mod.regs.rsp = sp;
-      user_mod.regs.rip = shared64->invoke_gdbserver;
-      user_mod.regs.orig_rax = -1L;
-
-#elif defined(VGA_arm)
-      assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe
-#elif defined(VGA_ppc32)
-      assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe
-#elif defined(VGA_ppc64)
-      Addr64 func_addr;
-      Addr64 toc_addr;
-      int rw;
-      rw = ptrace_read_memory(pid, shared64->invoke_gdbserver,
-                              &func_addr,
-                              sizeof(Addr64));
-      if (rw != 0) {
-         ERROR(rw, "ppc64 read func_addr\n");
-         detach_from_all_threads(pid);
-         return False;
-      }
-      rw = ptrace_read_memory(pid, shared64->invoke_gdbserver+8,
-                              &toc_addr,
-                              sizeof(Addr64));
-      if (rw != 0) {
-         ERROR(rw, "ppc64 read toc_addr\n");
-         detach_from_all_threads(pid);
-         return False;
-      }
-      // We are not pushing anything on the stack, so it is not
-      // very clear why the sp has to be decreased, but it seems
-      // needed. The ppc64 ABI might give some lights on this ?
-      user_mod.regs.gpr[1] = sp - 220;
-      user_mod.regs.gpr[2] = toc_addr;
-      user_mod.regs.nip = func_addr;
-      user_mod.regs.trap = -1L;
-      /* put check arg in register 3 */
-      user_mod.regs.gpr[3] = check;
-      /* put bad_return return address in Link Register */
-      user_mod.regs.link = bad_return;
-#elif defined(VGA_s390x)
-      /* put check arg in register r2 */
-      user_mod.regs.gprs[2] = check;
-      /* bad_return Return address is in r14 */
-      user_mod.regs.gprs[14] = bad_return;
-      /* minimum stack frame */
-      sp = sp - 160;
-      user_mod.regs.gprs[15] = sp;
-      /* set program counter */
-      user_mod.regs.psw.addr = shared64->invoke_gdbserver;
-#elif defined(VGA_mips32)
-      assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe
-#elif defined(VGA_mips64)
-      /* put check arg in register 4 */
-      user_mod.regs[4] = check;
-      /* put NULL return address in ra */
-      user_mod.regs[31] = bad_return;
-      user_mod.regs[34] = shared64->invoke_gdbserver;
-      user_mod.regs[25] = shared64->invoke_gdbserver;
-#else
-      I_die_here: architecture missing in vgdb.c
-#endif
-   }
-   else {
-      assert(0);
-   }
-   
-   if (!setregs(pid, &user_mod.regs, sizeof(user_mod.regs))) {
-      detach_from_all_threads(pid);
-      return False;
-   }
-   /* Now that we have modified the registers, we set
-      pid_of_save_regs to indicate that restore_and_detach
-      must restore the registers in case of cleanup. */
-   pid_of_save_regs = pid;
-   pid_of_save_regs_continued = False;
-      
-
-   /* We PTRACE_CONT-inue pid. 
-      Either gdbserver will be invoked directly (if all
-      threads are interruptible) or gdbserver will be
-      called soon by the scheduler. In the first case,
-      pid will stop on the break inserted above when
-      gdbserver returns. In the 2nd case, the break will
-      be encountered directly. */
-   DEBUG(1, "PTRACE_CONT to invoke\n");
-   res = ptrace (PTRACE_CONT, pid, NULL, NULL);
-   if (res != 0) {
-      ERROR(errno, "PTRACE_CONT\n");
-      restore_and_detach(pid);
-      return False;
-   }
-   pid_of_save_regs_continued = True;
-   /* Wait for SIGSTOP generated by m_gdbserver.c give_control_back_to_vgdb */
-   stopped = waitstopped (pid, SIGSTOP,
-                          "waitpid status after PTRACE_CONT to invoke");
-   if (stopped) {
-      /* Here pid has properly stopped on the break. */
-      pid_of_save_regs_continued = False;
-      restore_and_detach(pid);
-      return True;
-   } else {
-      /* Whatever kind of problem happened. We shutdown */
-      shutting_down = True;
-      return False;
-   }
-}
-#endif
-
-static 
-void cleanup_restore_and_detach(void *v_pid)
-{
-   DEBUG(1, "cleanup_restore_and_detach dying: %d\n", dying);
-#ifdef PTRACEINVOKER
-   if (!dying)
-      restore_and_detach(*(int*)v_pid);
-#endif
-}
-
 /* This function loops till shutting_down becomes true.  In this loop,
    it verifies if valgrind process is reading the characters written
    by vgdb.  The verification is done every max_invoke_ms ms.  If
-   valgrind is not reading characters, it will use invoke_gdbserver
-   (if PTRACE_INVOKER is defined) to ensure that the gdbserver code is
-   called soon by valgrind. */
+   valgrind is not reading characters, it will use invoker_invoke_gdbserver
+   to ensure that the gdbserver code is called soon by valgrind. */
 static int max_invoke_ms = 100;
 #define NEVER 99999999
 static int cmd_time_out = NEVER;
@@ -1175,7 +225,7 @@ void *invoke_gdbserver_in_valgrind(void *v_pid)
    int invoked_written = -1;
    unsigned int usecs;
 
-   pthread_cleanup_push(cleanup_restore_and_detach, v_pid);
+   pthread_cleanup_push(invoker_cleanup_restore_and_detach, v_pid);
 
    while (!shutting_down) {
       written_by_vgdb_before_sleep = VS_written_by_vgdb;
@@ -1240,19 +290,15 @@ void *invoke_gdbserver_in_valgrind(void *v_pid)
                        pid, cmd_time_out);
          }
          if (max_invoke_ms > 0 && timercmp (&now, &invoke_time, >=)) {
-            #if defined(PTRACEINVOKER)
             /* only need to wake up if the nr written has changed since
                last invoke. */
             if (invoked_written != written_by_vgdb_before_sleep) {
-               if (invoke_gdbserver(pid)) {
+               if (invoker_invoke_gdbserver(pid)) {
                   /* If invoke succesful, no need to invoke again
                      for the same value of written_by_vgdb_before_sleep. */
                   invoked_written = written_by_vgdb_before_sleep;
                }
             }
-            #else
-            DEBUG(2, "invoke_gdbserver via ptrace not (yet) implemented\n");
-            #endif
          }
       } else {
          // Something happened => restart timer check.
@@ -1645,7 +691,7 @@ void received_signal (int signum)
       sigalrm++;
 #if defined(VGPV_arm_linux_android) || defined(VGPV_x86_linux_android)
       /* Android has no pthread_cancel. As it also does not have
-         PTRACE_INVOKER, there is no need for cleanup action.
+         an invoker implementation, there is no need for cleanup action.
          So, we just do nothing. */
       DEBUG(1, "sigalrm received, no action on android\n");
 #else
@@ -1849,19 +895,19 @@ void gdb_relay (int pid)
          if (pollfds[ck].revents & POLLERR) {
             DEBUG(1, "connection %s fd %d POLLERR error condition\n",
                      ppConnectionKind(ck), pollfds[ck].fd);
-            valgrind_dying();
+            invoker_valgrind_dying();
             shutting_down = True;
          }
          if (pollfds[ck].revents & POLLHUP) {
             DEBUG(1, "connection %s fd %d POLLHUP error condition\n",
                   ppConnectionKind(ck), pollfds[ck].fd);
-            valgrind_dying();
+            invoker_valgrind_dying();
             shutting_down = True;
          }
          if (pollfds[ck].revents & POLLNVAL) {
             DEBUG(1, "connection %s fd %d POLLNVAL error condition\n",
                   ppConnectionKind(ck), pollfds[ck].fd);
-            valgrind_dying();
+            invoker_valgrind_dying();
             shutting_down = True;
          }
       }
@@ -1950,7 +996,7 @@ void standalone_send_commands(int pid,
          if (buflen < 0) {
             ERROR (0, "error reading packet\n");
             if (buflen == -2)
-               valgrind_dying();
+               invoker_valgrind_dying();
             break;
          }
          if (strlen(buf) == 0) {
@@ -2033,19 +1079,6 @@ void report_pid (int pid, Bool on_stdout)
    fflush((on_stdout ? stdout : stderr));
 }
 
-/* Possibly produces additional usage information documenting the
-   ptrace restrictions. */
-static
-void ptrace_restrictions_msg(void)
-{
-#  ifndef PTRACEINVOKER
-   fprintf(stderr, 
-           "Note: ptrace invoker not implemented\n"
-           "For more info: read user manual section"
-           " 'Limitations of the Valgrind gdbserver'\n");
-#  endif
-}
-
 static
 void usage(void)
 {
@@ -2083,7 +1116,7 @@ void usage(void)
 "  To get help from the Valgrind gdbserver, use vgdb help\n"
 "\n", vgdb_prefix_default()
            );
-   ptrace_restrictions_msg();  
+   invoker_restrictions_msg();  
 }
 
 /* If show_list, outputs on stdout the list of Valgrind processes with gdbserver activated.
diff --git a/coregrind/vgdb.h b/coregrind/vgdb.h
new file mode 100644 (file)
index 0000000..65b96a7
--- /dev/null
@@ -0,0 +1,103 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Declarations common for vgdb and implementations             ---*/
+/*--- of vgdb-invoker.                                      vgdb.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2011-2013 Philippe Waroquiers
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#ifndef __VGDB_H
+#define __VGDB_H
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_gdbserver.h"
+
+#include <sys/types.h>
+
+extern int debuglevel;
+extern struct timeval dbgtv;
+/* if level <= debuglevel, print timestamp, then print provided by debug info */
+#define DEBUG(level, ...) (level <= debuglevel ?                        \
+                           gettimeofday(&dbgtv, NULL),                  \
+                           fprintf(stderr, "%ld.%6.6ld ",               \
+                                   (long int)dbgtv.tv_sec,              \
+                                   (long int)dbgtv.tv_usec),            \
+                           fprintf(stderr, __VA_ARGS__),fflush(stderr)  \
+                           : 0)
+
+/* same as DEBUG but does not print time stamp info */
+#define PDEBUG(level, ...) (level <= debuglevel ?                       \
+                            fprintf(stderr, __VA_ARGS__),fflush(stderr) \
+                            : 0)
+
+/* if errno != 0, 
+   report the errno and fprintf the ... varargs on stderr. */
+#define ERROR(errno, ...) ((errno == 0 ? 0 : perror("syscall failed")), \
+                           fprintf(stderr, __VA_ARGS__),                \
+                           fflush(stderr))
+/* same as ERROR, but also exits with status 1 */
+#define XERROR(errno, ...) ((errno == 0 ? 0 : perror("syscall failed")), \
+                            fprintf(stderr, __VA_ARGS__),                \
+                            fflush(stderr),                              \
+                            exit(1))
+
+/* Will be set to True when any condition indicating we have to shutdown
+   is encountered. */
+extern Bool shutting_down;
+
+extern VgdbShared32 *shared32;
+extern VgdbShared64 *shared64;
+
+/*--------------------------------------------------------------------*/
+/*--- Below is vgdb-invoker interface which must be implemented by ---*/
+/*--- all vgdb-invoker implementations.                            ---*/
+/*--------------------------------------------------------------------*/
+
+/* Possibly produces additional usage information documenting the
+   invoker restrictions. */
+void invoker_restrictions_msg(void);
+
+/* Restore the registers to the saved value, then detaches from all threads.
+   Used as a cleanup handler for thread cancellation. */
+void invoker_cleanup_restore_and_detach(void *v_pid);
+
+/* Ensures that the gdbserver code is invoked by pid.
+   If an error occurs, resets the valgrind process
+   to the state it had before being invoked.
+   Returns True if invoke successful, False otherwise. */
+Bool invoker_invoke_gdbserver(pid_t pid);
+
+/* Called when connection with valgrind is lost.  In case we
+   have lost the connection, it means that Valgrind has closed the
+   connection and is busy exiting. We can't and don't have to stop it in
+   this case. */
+void invoker_valgrind_dying(void);
+
+#endif // __VGDB_H
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                          ---*/
+/*--------------------------------------------------------------------*/
index 73a2f0017da0ba654f8f7296f8925f6a605fecd0..cbdfef5e43175a6a8ee0b112bdb4a04f8ee595d4 100755 (executable)
@@ -99,11 +99,11 @@ ln -f -s ../coregrind/vgdb gdbserver_tests/vgdb
 # if ptrace not implemented in vgdb or OS restricts the initial attach,
 # some tests would block for a loooonnnng time.
 if gdbserver_tests/vgdb --help 2>&1 |
-    grep -e 'ptrace invoker not implemented' > /dev/null
+    grep -e 'invoker not implemented' > /dev/null
 then
-    rm -f gdbserver_tests/vgdb.ptraceinvoker
+    rm -f gdbserver_tests/vgdb.invoker
 else
-    touch gdbserver_tests/vgdb.ptraceinvoker
+    touch gdbserver_tests/vgdb.invoker
 fi
 
 # cleanup the possibly big garbage previously collected output
index 44a9e5f851b23b154dee3d4cdaf9fa1a10363ef2..1d56b7fdb6ad30d9dfa822a22f82b43233b4d4fb 100644 (file)
@@ -4,8 +4,8 @@ prog: sleepers
 args: 1 0 1000000000 B-B-B-B-
 vgopts: --tool=memcheck --vgdb=yes --vgdb-prefix=./vgdb-prefix-mcinvokeRU
 stderr_filter: filter_make_empty
-# as the Valgrind process is always busy, we do not need the vgdb.ptraceinvoker prereq.
-# We even disable ptrace invoker to avoid spurious attach error message
+# as the Valgrind process is always busy, we do not need the vgdb.invoker prereq.
+# We even disable invoker to avoid spurious attach error message
 # on kernels where ptrace is restricted.
 progB: invoker
 argsB: 10 --vgdb-prefix=./vgdb-prefix-mcinvokeRU --max-invoke-ms=0 --wait=60 -c v.wait 0
index 24ecad45f8c814a102fc977d93e34f28321beb4f..ced90fa1831a24edba7a9b3a9dc511c68ff2c0e9 100644 (file)
@@ -4,7 +4,7 @@ prog: sleepers
 args: 1 10000000 0 -S-S-S-S
 vgopts: --tool=memcheck --vgdb=yes --vgdb-prefix=./vgdb-prefix-mcinvokeWS
 stderr_filter: filter_make_empty
-prereq: test -f vgdb.ptraceinvoker
+prereq: test -f vgdb.invoker
 progB: invoker
 argsB: 10 --vgdb-prefix=./vgdb-prefix-mcinvokeWS --wait=60 -c v.wait 0
 # if the --wait is not enough, the test will fail or block
index 64d21e317e49563927b0666f3ba366b1f2fbec61..8ff83555d8b28f8f77a6dda97d98d8275d2033b0 100644 (file)
@@ -10,7 +10,7 @@ prog: sleepers
 args: 1000000000 1000000000 1000000000 BSBSBSBS
 vgopts: --tool=none --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-nlcontrolc
 stderr_filter: filter_stderr
-prereq: test -e gdb -a -f vgdb.ptraceinvoker
+prereq: test -e gdb -a -f vgdb.invoker
 progB: gdb
 argsB: --quiet -l 60 --nx ./sleepers
 stdinB: nlcontrolc.stdinB.gdb
index 596cefabbc562a6bed5c512dcdb230007cef9d54..f488748e4e3a307fed8db19ba0ac424d242fddc7 100644 (file)
@@ -7,7 +7,7 @@ prog: sleepers
 args: 1 10000000 0 -S-S-S-S
 vgopts: --tool=none --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-nlsigvgdb
 stderr_filter: filter_stderr
-prereq: test -e gdb -a -f vgdb.ptraceinvoker
+prereq: test -e gdb -a -f vgdb.invoker
 progB: gdb
 argsB: --quiet -l 60 --nx ./sleepers
 stdinB: nlsigvgdb.stdinB.gdb