|
From: <sv...@va...> - 2013-12-01 14:56:44
|
Author: philippe
Date: Sun Dec 1 14:56:28 2013
New Revision: 13743
Log:
Fix 326462 Refactor vgdb to isolate invoker stuff into separate module
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
Added:
trunk/coregrind/vgdb-invoker-none.c
trunk/coregrind/vgdb-invoker-ptrace.c
trunk/coregrind/vgdb.h
Modified:
trunk/NEWS
trunk/coregrind/Makefile.am
trunk/coregrind/m_gdbserver/README_DEVELOPERS
trunk/coregrind/vgdb.c
trunk/gdbserver_tests/make_local_links
trunk/gdbserver_tests/mcinvokeRU.vgtest
trunk/gdbserver_tests/mcinvokeWS.vgtest
trunk/gdbserver_tests/nlcontrolc.vgtest
trunk/gdbserver_tests/nlsigvgdb.vgtest
Modified: trunk/NEWS
==============================================================================
--- trunk/NEWS (original)
+++ trunk/NEWS Sun Dec 1 14:56:28 2013
@@ -24,7 +24,7 @@
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
Modified: trunk/coregrind/Makefile.am
==============================================================================
--- trunk/coregrind/Makefile.am (original)
+++ trunk/coregrind/Makefile.am Sun Dec 1 14:56:28 2013
@@ -59,7 +59,22 @@
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)
Modified: trunk/coregrind/m_gdbserver/README_DEVELOPERS
==============================================================================
--- trunk/coregrind/m_gdbserver/README_DEVELOPERS (original)
+++ trunk/coregrind/m_gdbserver/README_DEVELOPERS Sun Dec 1 14:56:28 2013
@@ -165,7 +165,7 @@
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 @@
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 @@
(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
Added: trunk/coregrind/vgdb-invoker-none.c
==============================================================================
--- trunk/coregrind/vgdb-invoker-none.c (added)
+++ trunk/coregrind/vgdb-invoker-none.c Sun Dec 1 14:56:28 2013
@@ -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)
+{
+}
Added: trunk/coregrind/vgdb-invoker-ptrace.c
==============================================================================
--- trunk/coregrind/vgdb-invoker-ptrace.c (added)
+++ trunk/coregrind/vgdb-invoker-ptrace.c Sun Dec 1 14:56:28 2013
@@ -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;
+}
Modified: trunk/coregrind/vgdb.c
==============================================================================
--- trunk/coregrind/vgdb.c (original)
+++ trunk/coregrind/vgdb.c Sun Dec 1 14:56:28 2013
@@ -26,34 +26,28 @@
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
@@ -72,75 +66,18 @@
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 @@
}
-#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_o...
[truncated message content] |