mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 10:05:29 +00:00
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 git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13743
This commit is contained in:
parent
e9b6e1c9be
commit
b8ed3a5b65
2
NEWS
2
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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
56
coregrind/vgdb-invoker-none.c
Normal file
56
coregrind/vgdb-invoker-none.c
Normal file
@ -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)
|
||||
{
|
||||
}
|
||||
917
coregrind/vgdb-invoker-ptrace.c
Normal file
917
coregrind/vgdb-invoker-ptrace.c
Normal file
@ -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;
|
||||
}
|
||||
1023
coregrind/vgdb.c
1023
coregrind/vgdb.c
File diff suppressed because it is too large
Load Diff
103
coregrind/vgdb.h
Normal file
103
coregrind/vgdb.h
Normal file
@ -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 ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user