Implement a GDB server in Valgrind. See #214909.
(Philippe Waroquiers, philippe.waroquiers@skynet.be)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11727 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/Makefile.am b/Makefile.am
index 61fddf5..be6e8b7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -25,6 +25,7 @@
TEST_EXP_TOOLS = exp-bbv
endif
+
# Put docs last because building the HTML is slow and we want to get
# everything else working before we try it.
SUBDIRS = \
@@ -36,6 +37,7 @@
$(EXP_TOOLS) \
tests \
perf \
+ gdbserver_tests \
auxprogs \
mpi \
docs
@@ -72,11 +74,15 @@
## Preprend @PERL@ because tests/vg_regtest isn't executable
regtest: check
- @PERL@ tests/vg_regtest $(TEST_TOOLS) $(TEST_EXP_TOOLS)
+ gdbserver_tests/make_local_links $(GDB)
+ @PERL@ tests/vg_regtest gdbserver_tests $(TEST_TOOLS) $(TEST_EXP_TOOLS)
nonexp-regtest: check
@PERL@ tests/vg_regtest $(TEST_TOOLS)
exp-regtest: check
- @PERL@ tests/vg_regtest $(TEST_EXP_TOOLS)
+ @PERL@ tests/vg_regtest gdbserver_tests $(TEST_EXP_TOOLS)
+# Nb: gdbserver_tests are put in exp-regtest rather than nonexp-regtest
+# because they are tested with various valgrind tools, so might be using
+# an experimental tool.
## Preprend @PERL@ because tests/vg_perf isn't executable
perf: check
diff --git a/callgrind/docs/cl-manual.xml b/callgrind/docs/cl-manual.xml
index 3f8330e..cb79bdc 100644
--- a/callgrind/docs/cl-manual.xml
+++ b/callgrind/docs/cl-manual.xml
@@ -1075,6 +1075,32 @@
</sect1>
+<sect1 id="cl-manual.monitor-commands" xreflabel="Callgrind Monitor Commands">
+<title>Callgrind Monitor Commands</title>
+<para>The Callgrind tool provides monitor commands handled by the Valgrind
+gdbserver (see <xref linkend="manual-core.gdbserver-commandhandling"/>).
+</para>
+
+<itemizedlist>
+ <listitem>
+ <para><varname>ct.dump [<dump_hint>]</varname> requests to dump the
+ profile data. </para>
+ </listitem>
+
+ <listitem>
+ <para><varname>ct.zero</varname> requests to zero the profile data
+ counters. </para>
+ </listitem>
+
+ <listitem>
+ <para>It would be nice to have some more callgrind monitor
+ commands such as e.g. toggle collect and start instrumentation.
+ </para>
+ </listitem>
+
+</itemizedlist>
+</sect1>
+
<sect1 id="cl-manual.clientrequests" xreflabel="Client request reference">
<title>Callgrind specific client requests</title>
diff --git a/callgrind/main.c b/callgrind/main.c
index a07c453..7de2655 100644
--- a/callgrind/main.c
+++ b/callgrind/main.c
@@ -36,6 +36,7 @@
#include "global.h"
#include "pub_tool_threadstate.h"
+#include "pub_tool_gdbserver.h"
#include "cg_branchpred.c"
@@ -1355,11 +1356,56 @@
reason, state ? "ON" : "OFF");
}
+static void print_monitor_help ( void )
+{
+ VG_(gdb_printf) ("\n");
+ VG_(gdb_printf) ("callgrind monitor commands:\n");
+ VG_(gdb_printf) (" ct.dump [<dump_hint>]\n");
+ VG_(gdb_printf) (" dump counters\n");
+ VG_(gdb_printf) (" ct.zero\n");
+ VG_(gdb_printf) (" zero counters\n");
+ VG_(gdb_printf) ("\n");
+}
+
+/* return True if request recognised, False otherwise */
+static Bool handle_gdb_monitor_command (ThreadId tid, Char *req)
+{
+ Char* wcmd;
+ Char s[VG_(strlen(req))]; /* copy for strtok_r */
+ Char *ssaveptr;
+
+ VG_(strcpy) (s, req);
+
+ wcmd = VG_(strtok_r) (s, " ", &ssaveptr);
+ switch (VG_(keyword_id) ("help ct.dump ct.zero",
+ wcmd, kwd_report_duplicated_matches)) {
+ case -2: /* multiple matches */
+ return True;
+ case -1: /* not found */
+ return False;
+ case 0: /* help */
+ print_monitor_help();
+ return True;
+ case 1: { /* ct.dump */
+ CLG_(dump_profile)(req, False);
+ return True;
+ }
+ case 2: { /* ct.zero */
+ CLG_(zero_all_cost)(False);
+ return True;
+ }
+
+ default:
+ tl_assert(0);
+ return False;
+ }
+}
static
Bool CLG_(handle_client_request)(ThreadId tid, UWord *args, UWord *ret)
{
- if (!VG_IS_TOOL_USERREQ('C','T',args[0]))
+ if (!VG_IS_TOOL_USERREQ('C','T',args[0])
+ && VG_USERREQ__GDB_MONITOR_COMMAND != args[0])
return False;
switch(args[0]) {
@@ -1399,6 +1445,14 @@
*ret = 0; /* meaningless */
break;
+ case VG_USERREQ__GDB_MONITOR_COMMAND: {
+ Bool handled = handle_gdb_monitor_command (tid, (Char*)args[1]);
+ if (handled)
+ *ret = 1;
+ else
+ *ret = 0;
+ return handled;
+ }
default:
return False;
}
diff --git a/configure.in b/configure.in
index 667064e..a0df063 100644
--- a/configure.in
+++ b/configure.in
@@ -1907,6 +1907,7 @@
tests/vg_regtest
perf/Makefile
perf/vg_perf
+ gdbserver_tests/Makefile
include/Makefile
auxprogs/Makefile
mpi/Makefile
diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am
index 57fa593..43d8683 100644
--- a/coregrind/Makefile.am
+++ b/coregrind/Makefile.am
@@ -31,6 +31,7 @@
bin_PROGRAMS = \
valgrind \
+ vgdb \
no_op_client_for_valgrind
if VGCONF_OS_IS_LINUX
@@ -58,6 +59,12 @@
valgrind_LDFLAGS += -Wl,-read_only_relocs -Wl,suppress
endif
+vgdb_SOURCES = vgdb.c
+vgdb_CPPFLAGS = $(AM_CPPFLAGS_PRI)
+vgdb_CFLAGS = $(AM_CFLAGS_PRI)
+vgdb_CCASFLAGS = $(AM_CCASFLAGS_PRI)
+vgdb_LDFLAGS = $(AM_CFLAGS_PRI) -lpthread
+
no_op_client_for_valgrind_SOURCES = no_op_client_for_valgrind.c
no_op_client_for_valgrind_CPPFLAGS = $(AM_CPPFLAGS_PRI)
no_op_client_for_valgrind_CFLAGS = $(AM_CFLAGS_PRI)
@@ -147,6 +154,7 @@
pub_core_dispatch_asm.h \
pub_core_errormgr.h \
pub_core_execontext.h \
+ pub_core_gdbserver.h \
pub_core_hashtable.h \
pub_core_initimg.h \
pub_core_libcbase.h \
@@ -202,6 +210,12 @@
m_demangle/demangle.h \
m_demangle/safe-ctype.h \
m_demangle/vg_libciface.h \
+ m_gdbserver/regcache.h \
+ m_gdbserver/regdef.h \
+ m_gdbserver/server.h \
+ m_gdbserver/target.h \
+ m_gdbserver/valgrind_low.h \
+ m_gdbserver/gdb/signals.h \
m_initimg/priv_initimg_pathscan.h \
m_initimg/simple_huffman.c \
m_scheduler/priv_sema.h \
@@ -291,12 +305,29 @@
m_dispatch/dispatch-amd64-linux.S \
m_dispatch/dispatch-ppc32-linux.S \
m_dispatch/dispatch-ppc64-linux.S \
- m_dispatch/dispatch-arm-linux.S \
+ m_dispatch/dispatch-arm-linux.S \
m_dispatch/dispatch-s390x-linux.S \
m_dispatch/dispatch-ppc32-aix5.S \
m_dispatch/dispatch-ppc64-aix5.S \
m_dispatch/dispatch-x86-darwin.S \
m_dispatch/dispatch-amd64-darwin.S \
+ m_gdbserver/m_gdbserver.c \
+ m_gdbserver/inferiors.c \
+ m_gdbserver/m_gdbserver.c \
+ m_gdbserver/regcache.c \
+ m_gdbserver/remote-utils.c \
+ m_gdbserver/server.c \
+ m_gdbserver/signals.c \
+ m_gdbserver/target.c \
+ m_gdbserver/utils.c \
+ m_gdbserver/valgrind-low.c \
+ m_gdbserver/valgrind-low-x86.c \
+ m_gdbserver/valgrind-low-amd64.c \
+ m_gdbserver/valgrind-low-arm.c \
+ m_gdbserver/valgrind-low-ppc32.c \
+ m_gdbserver/valgrind-low-ppc64.c \
+ m_gdbserver/valgrind-low-s390x.c \
+ m_gdbserver/version.c \
m_initimg/initimg-linux.c \
m_initimg/initimg-aix5.c \
m_initimg/initimg-darwin.c \
@@ -312,7 +343,7 @@
m_sigframe/sigframe-amd64-linux.c \
m_sigframe/sigframe-ppc32-linux.c \
m_sigframe/sigframe-ppc64-linux.c \
- m_sigframe/sigframe-arm-linux.c \
+ m_sigframe/sigframe-arm-linux.c \
m_sigframe/sigframe-s390x-linux.c \
m_sigframe/sigframe-ppc32-aix5.c \
m_sigframe/sigframe-ppc64-aix5.c \
@@ -322,7 +353,7 @@
m_syswrap/syscall-amd64-linux.S \
m_syswrap/syscall-ppc32-linux.S \
m_syswrap/syscall-ppc64-linux.S \
- m_syswrap/syscall-arm-linux.S \
+ m_syswrap/syscall-arm-linux.S \
m_syswrap/syscall-s390x-linux.S \
m_syswrap/syscall-ppc32-aix5.S \
m_syswrap/syscall-ppc64-aix5.S \
@@ -338,7 +369,7 @@
m_syswrap/syswrap-amd64-linux.c \
m_syswrap/syswrap-ppc32-linux.c \
m_syswrap/syswrap-ppc64-linux.c \
- m_syswrap/syswrap-arm-linux.c \
+ m_syswrap/syswrap-arm-linux.c \
m_syswrap/syswrap-s390x-linux.c \
m_syswrap/syswrap-ppc32-aix5.c \
m_syswrap/syswrap-ppc64-aix5.c \
@@ -434,10 +465,103 @@
endif
#----------------------------------------------------------------------------
+# gdbserver xml target descriptions
+#----------------------------------------------------------------------------
+pkglib_DATA =
+
+if VGCONF_ARCHS_INCLUDE_ARM
+pkglib_DATA += m_gdbserver/arm-core-valgrind-s1.xml \
+ m_gdbserver/arm-core-valgrind-s2.xml \
+ m_gdbserver/arm-core.xml \
+ m_gdbserver/arm-vfpv3-valgrind-s1.xml \
+ m_gdbserver/arm-vfpv3-valgrind-s2.xml \
+ m_gdbserver/arm-vfpv3.xml \
+ m_gdbserver/arm-with-vfpv3-valgrind.xml \
+ m_gdbserver/arm-with-vfpv3.xml
+endif
+
+if VGCONF_ARCHS_INCLUDE_X86
+pkglib_DATA += m_gdbserver/32bit-core-valgrind-s1.xml \
+ m_gdbserver/32bit-core-valgrind-s2.xml \
+ m_gdbserver/32bit-core.xml \
+ m_gdbserver/32bit-sse-valgrind-s1.xml \
+ m_gdbserver/32bit-sse-valgrind-s2.xml \
+ m_gdbserver/32bit-sse.xml
+if VGCONF_OS_IS_LINUX
+pkglib_DATA += m_gdbserver/32bit-linux-valgrind-s1.xml \
+ m_gdbserver/32bit-linux-valgrind-s2.xml \
+ m_gdbserver/32bit-linux.xml \
+ m_gdbserver/i386-linux-valgrind.xml
+endif
+if VGCONF_OS_IS_DARWIN
+pkglib_DATA += m_gdbserver/i386-coresse-valgrind.xml
+endif
+endif
+
+if VGCONF_ARCHS_INCLUDE_AMD64
+pkglib_DATA += m_gdbserver/64bit-core-valgrind-s1.xml \
+ m_gdbserver/64bit-core-valgrind-s2.xml \
+ m_gdbserver/64bit-core.xml \
+ m_gdbserver/64bit-sse-valgrind-s1.xml \
+ m_gdbserver/64bit-sse-valgrind-s2.xml \
+ m_gdbserver/64bit-sse.xml
+
+if VGCONF_OS_IS_LINUX
+pkglib_DATA += m_gdbserver/64bit-linux-valgrind-s1.xml \
+ m_gdbserver/64bit-linux-valgrind-s2.xml \
+ m_gdbserver/64bit-linux.xml \
+ m_gdbserver/amd64-linux-valgrind.xml
+endif
+if VGCONF_OS_IS_DARWIN
+pkglib_DATA += m_gdbserver/amd64-coresse-valgrind.xml
+endif
+endif
+
+if VGCONF_ARCHS_INCLUDE_PPC32
+pkglib_DATA += m_gdbserver/power-altivec-valgrind-s1.xml \
+ m_gdbserver/power-altivec-valgrind-s2.xml \
+ m_gdbserver/power-altivec.xml \
+ m_gdbserver/power-core.xml \
+ m_gdbserver/power-fpu-valgrind-s1.xml \
+ m_gdbserver/power-fpu-valgrind-s2.xml \
+ m_gdbserver/power-fpu.xml \
+ m_gdbserver/power-linux-valgrind-s1.xml \
+ m_gdbserver/power-linux-valgrind-s2.xml \
+ m_gdbserver/power-linux.xml \
+ m_gdbserver/powerpc-altivec32l-valgrind.xml \
+ m_gdbserver/powerpc-altivec32l.xml
+endif
+
+if VGCONF_ARCHS_INCLUDE_PPC64
+pkglib_DATA += m_gdbserver/power64-core-valgrind-s1.xml \
+ m_gdbserver/power64-core-valgrind-s2.xml \
+ m_gdbserver/power64-core.xml \
+ m_gdbserver/power64-linux-valgrind-s1.xml \
+ m_gdbserver/power64-linux-valgrind-s2.xml \
+ m_gdbserver/power64-linux.xml \
+ m_gdbserver/powerpc-altivec64l-valgrind.xml \
+ m_gdbserver/powerpc-altivec64l.xml
+if ! VGCONF_ARCHS_INCLUDE_PPC32
+pkglib_DATA += m_gdbserver/power-altivec-valgrind-s1.xml \
+ m_gdbserver/power-altivec-valgrind-s2.xml \
+ m_gdbserver/power-altivec.xml \
+ m_gdbserver/power-fpu-valgrind-s1.xml \
+ m_gdbserver/power-fpu-valgrind-s2.xml \
+ m_gdbserver/power-fpu.xml
+endif
+endif
+
+
+#----------------------------------------------------------------------------
# General stuff
#----------------------------------------------------------------------------
all-local: inplace-noinst_PROGRAMS inplace-noinst_DSYMS
+ mkdir -p $(inplacedir); \
+ for f in $(pkglib_DATA); do \
+ rm -f $(inplacedir)/$$f; \
+ ln -f -s ../$(subdir)/$$f $(inplacedir); \
+ done
clean-local: clean-noinst_DSYMS
diff --git a/coregrind/m_aspacemgr/aspacemgr-linux.c b/coregrind/m_aspacemgr/aspacemgr-linux.c
index cc092ce..9765ba8 100644
--- a/coregrind/m_aspacemgr/aspacemgr-linux.c
+++ b/coregrind/m_aspacemgr/aspacemgr-linux.c
@@ -2488,11 +2488,11 @@
/* Map a file at an unconstrained address for V, and update the
- segment array accordingly. This is used by V for transiently
- mapping in object files to read their debug info. */
+ segment array accordingly. Use the provided flags */
-SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot,
- Int fd, Off64T offset )
+static SysRes VG_(am_mmap_file_float_valgrind_flags) ( SizeT length, UInt prot,
+ UInt flags,
+ Int fd, Off64T offset )
{
SysRes sres;
NSegment seg;
@@ -2520,7 +2520,7 @@
any resulting failure immediately. */
sres = VG_(am_do_mmap_NO_NOTIFY)(
advised, length, prot,
- VKI_MAP_FIXED|VKI_MAP_PRIVATE,
+ flags,
fd, offset
);
if (sr_isError(sres))
@@ -2556,7 +2556,25 @@
AM_SANITY_CHECK;
return sres;
}
+/* Map privately a file at an unconstrained address for V, and update the
+ segment array accordingly. This is used by V for transiently
+ mapping in object files to read their debug info. */
+SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot,
+ Int fd, Off64T offset )
+{
+ return VG_(am_mmap_file_float_valgrind_flags) (length, prot,
+ VKI_MAP_FIXED|VKI_MAP_PRIVATE,
+ fd, offset );
+}
+
+extern SysRes VG_(am_shared_mmap_file_float_valgrind)
+ ( SizeT length, UInt prot, Int fd, Off64T offset )
+{
+ return VG_(am_mmap_file_float_valgrind_flags) (length, prot,
+ VKI_MAP_FIXED|VKI_MAP_SHARED,
+ fd, offset );
+}
/* --- --- munmap helper --- --- */
diff --git a/coregrind/m_errormgr.c b/coregrind/m_errormgr.c
index bb3247a..98e9d53 100644
--- a/coregrind/m_errormgr.c
+++ b/coregrind/m_errormgr.c
@@ -36,6 +36,7 @@
#include "pub_core_debuginfo.h"
#include "pub_core_errormgr.h"
#include "pub_core_execontext.h"
+#include "pub_core_gdbserver.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcfile.h"
@@ -494,7 +495,18 @@
/* Should be assured by caller */
vg_assert( ! VG_(clo_xml) );
+ /* if user wants to debug from a certain error nr, then wait for gdb/vgdb */
+ if (VG_(clo_vgdb) != Vg_VgdbNo
+ && allow_db_attach
+ && VG_(dyn_vgdb_error) <= n_errs_found) {
+ VG_(umsg)("(action on error) vgdb me ... \n");
+ VG_(gdbserver)( err->tid );
+ VG_(umsg)("Continuing ...\n");
+ }
+
/* Perhaps we want a debugger attach at this point? */
+ /* GDBTD ??? maybe we should/could remove the below assuming the
+ gdbserver interface is better ??? */
if (allow_db_attach &&
VG_(is_action_requested)( "Attach to debugger", & VG_(clo_db_attach) ))
{
@@ -540,13 +552,13 @@
attach (and detach), and optionally prints a suppression; both
of these may require user input.
*/
-static void pp_Error ( Error* err, Bool allow_db_attach )
+static void pp_Error ( Error* err, Bool allow_db_attach, Bool xml )
{
/* If this fails, you probably specified your tool's method
dictionary incorrectly. */
vg_assert(VG_(needs).tool_errors);
- if (VG_(clo_xml)) {
+ if (xml) {
/* Note, allow_db_attach is ignored in here. */
@@ -718,7 +730,8 @@
}
/* Move p to the front of the list so that future searches
- for it are faster. */
+ for it are faster. It also allows to print the last
+ error (see VG_(show_last_error). */
if (p_prev != NULL) {
vg_assert(p_prev->next == p);
p_prev->next = p->next;
@@ -780,7 +793,7 @@
n_err_contexts++;
n_errs_found++;
/* Actually show the error; more complex than you might think. */
- pp_Error( p, /*allow_db_attach*/True );
+ pp_Error( p, /*allow_db_attach*/True, VG_(clo_xml) );
/* update stats */
n_errs_shown++;
} else {
@@ -825,7 +838,7 @@
if (print_error) {
/* Actually show the error; more complex than you might think. */
- pp_Error(&err, allow_db_attach);
+ pp_Error(&err, allow_db_attach, VG_(clo_xml));
/* update stats */
n_errs_shown++;
}
@@ -881,20 +894,19 @@
return any_supp;
}
-
/* Show all the errors that occurred, and possibly also the
suppressions used. */
-void VG_(show_all_errors) ( void )
+void VG_(show_all_errors) ( Int verbosity, Bool xml )
{
Int i, n_min;
Error *p, *p_min;
Bool any_supp;
- if (VG_(clo_verbosity) == 0)
+ if (verbosity == 0)
return;
/* If we're printing XML, just show the suppressions and stop. */
- if (VG_(clo_xml)) {
+ if (xml) {
(void)show_used_suppressions();
return;
}
@@ -905,13 +917,15 @@
n_errs_found, n_err_contexts,
n_errs_suppressed, n_supp_contexts );
- if (VG_(clo_verbosity) <= 1)
+ if (verbosity <= 1)
return;
// We do the following only at -v or above, and only in non-XML
// mode
- /* Print the contexts in order of increasing error count. */
+ /* Print the contexts in order of increasing error count.
+ Once an error is shown, we add a huge value to its count to filter it
+ out. After having shown all errors, we reset count to the original value. */
for (i = 0; i < n_err_contexts; i++) {
n_min = (1 << 30) - 1;
p_min = NULL;
@@ -928,10 +942,10 @@
VG_(umsg)("\n");
VG_(umsg)("%d errors in context %d of %d:\n",
p_min->count, i+1, n_err_contexts);
- pp_Error( p_min, False/*allow_db_attach*/ );
+ pp_Error( p_min, False/*allow_db_attach*/, False /* xml */ );
// We're not printing XML -- we'd have exited above if so.
- vg_assert(! VG_(clo_xml));
+ vg_assert(! xml);
if ((i+1 == VG_(clo_dump_error))) {
StackTrace ips = VG_(get_ExeContext_StackTrace)(p_min->where);
@@ -941,9 +955,16 @@
/*allow redir?*/True);
}
- p_min->count = 1 << 30;
+ p_min->count = p_min->count + (1 << 30);
}
+ /* reset the counts, otherwise a 2nd call does not show anything anymore */
+ for (p = errors; p != NULL; p = p->next) {
+ if (p->count >= (1 << 30))
+ p->count = p->count - (1 << 30);
+ }
+
+
any_supp = show_used_suppressions();
if (any_supp)
@@ -956,6 +977,16 @@
n_supp_contexts );
}
+void VG_(show_last_error) ( void )
+{
+ if (n_err_contexts == 0) {
+ VG_(umsg)("No errors yet\n");
+ return;
+ }
+
+ pp_Error( errors, False/*allow_db_attach*/, False/*xml*/ );
+}
+
/* Show occurrence counts of all errors, in XML form. */
void VG_(show_error_counts_as_XML) ( void )
diff --git a/coregrind/m_gdbserver/32bit-core-valgrind-s1.xml b/coregrind/m_gdbserver/32bit-core-valgrind-s1.xml
new file mode 100644
index 0000000..9a0582f
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-core-valgrind-s1.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.core.valgrind.s1">
+ <flags id="i386_eflags" size="4">
+ <field name="CF" start="0" end="0"/>
+ <field name="" start="1" end="1"/>
+ <field name="PF" start="2" end="2"/>
+ <field name="AF" start="4" end="4"/>
+ <field name="ZF" start="6" end="6"/>
+ <field name="SF" start="7" end="7"/>
+ <field name="TF" start="8" end="8"/>
+ <field name="IF" start="9" end="9"/>
+ <field name="DF" start="10" end="10"/>
+ <field name="OF" start="11" end="11"/>
+ <field name="NT" start="14" end="14"/>
+ <field name="RF" start="16" end="16"/>
+ <field name="VM" start="17" end="17"/>
+ <field name="AC" start="18" end="18"/>
+ <field name="VIF" start="19" end="19"/>
+ <field name="VIP" start="20" end="20"/>
+ <field name="ID" start="21" end="21"/>
+ </flags>
+
+ <reg name="eaxs1" bitsize="32" type="int32"/>
+ <reg name="ecxs1" bitsize="32" type="int32"/>
+ <reg name="edxs1" bitsize="32" type="int32"/>
+ <reg name="ebxs1" bitsize="32" type="int32"/>
+ <reg name="esps1" bitsize="32" type="data_ptr"/>
+ <reg name="ebps1" bitsize="32" type="data_ptr"/>
+ <reg name="esis1" bitsize="32" type="int32"/>
+ <reg name="edis1" bitsize="32" type="int32"/>
+
+ <reg name="eips1" bitsize="32" type="code_ptr"/>
+ <reg name="eflagss1" bitsize="32" type="i386_eflags"/>
+ <reg name="css1" bitsize="32" type="int32"/>
+ <reg name="sss1" bitsize="32" type="int32"/>
+ <reg name="dss1" bitsize="32" type="int32"/>
+ <reg name="ess1" bitsize="32" type="int32"/>
+ <reg name="fss1" bitsize="32" type="int32"/>
+ <reg name="gss1" bitsize="32" type="int32"/>
+
+ <reg name="st0s1" bitsize="80" type="i387_ext"/>
+ <reg name="st1s1" bitsize="80" type="i387_ext"/>
+ <reg name="st2s1" bitsize="80" type="i387_ext"/>
+ <reg name="st3s1" bitsize="80" type="i387_ext"/>
+ <reg name="st4s1" bitsize="80" type="i387_ext"/>
+ <reg name="st5s1" bitsize="80" type="i387_ext"/>
+ <reg name="st6s1" bitsize="80" type="i387_ext"/>
+ <reg name="st7s1" bitsize="80" type="i387_ext"/>
+
+ <reg name="fctrls1" bitsize="32" type="int" group="float"/>
+ <reg name="fstats1" bitsize="32" type="int" group="float"/>
+ <reg name="ftags1" bitsize="32" type="int" group="float"/>
+ <reg name="fisegs1" bitsize="32" type="int" group="float"/>
+ <reg name="fioffs1" bitsize="32" type="int" group="float"/>
+ <reg name="fosegs1" bitsize="32" type="int" group="float"/>
+ <reg name="fooffs1" bitsize="32" type="int" group="float"/>
+ <reg name="fops1" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/32bit-core-valgrind-s2.xml b/coregrind/m_gdbserver/32bit-core-valgrind-s2.xml
new file mode 100644
index 0000000..1b272c5
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-core-valgrind-s2.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.core.valgrind.s2">
+ <flags id="i386_eflags" size="4">
+ <field name="CF" start="0" end="0"/>
+ <field name="" start="1" end="1"/>
+ <field name="PF" start="2" end="2"/>
+ <field name="AF" start="4" end="4"/>
+ <field name="ZF" start="6" end="6"/>
+ <field name="SF" start="7" end="7"/>
+ <field name="TF" start="8" end="8"/>
+ <field name="IF" start="9" end="9"/>
+ <field name="DF" start="10" end="10"/>
+ <field name="OF" start="11" end="11"/>
+ <field name="NT" start="14" end="14"/>
+ <field name="RF" start="16" end="16"/>
+ <field name="VM" start="17" end="17"/>
+ <field name="AC" start="18" end="18"/>
+ <field name="VIF" start="19" end="19"/>
+ <field name="VIP" start="20" end="20"/>
+ <field name="ID" start="21" end="21"/>
+ </flags>
+
+ <reg name="eaxs2" bitsize="32" type="int32"/>
+ <reg name="ecxs2" bitsize="32" type="int32"/>
+ <reg name="edxs2" bitsize="32" type="int32"/>
+ <reg name="ebxs2" bitsize="32" type="int32"/>
+ <reg name="esps2" bitsize="32" type="data_ptr"/>
+ <reg name="ebps2" bitsize="32" type="data_ptr"/>
+ <reg name="esis2" bitsize="32" type="int32"/>
+ <reg name="edis2" bitsize="32" type="int32"/>
+
+ <reg name="eips2" bitsize="32" type="code_ptr"/>
+ <reg name="eflagss2" bitsize="32" type="i386_eflags"/>
+ <reg name="css2" bitsize="32" type="int32"/>
+ <reg name="sss2" bitsize="32" type="int32"/>
+ <reg name="dss2" bitsize="32" type="int32"/>
+ <reg name="ess2" bitsize="32" type="int32"/>
+ <reg name="fss2" bitsize="32" type="int32"/>
+ <reg name="gss2" bitsize="32" type="int32"/>
+
+ <reg name="st0s2" bitsize="80" type="i387_ext"/>
+ <reg name="st1s2" bitsize="80" type="i387_ext"/>
+ <reg name="st2s2" bitsize="80" type="i387_ext"/>
+ <reg name="st3s2" bitsize="80" type="i387_ext"/>
+ <reg name="st4s2" bitsize="80" type="i387_ext"/>
+ <reg name="st5s2" bitsize="80" type="i387_ext"/>
+ <reg name="st6s2" bitsize="80" type="i387_ext"/>
+ <reg name="st7s2" bitsize="80" type="i387_ext"/>
+
+ <reg name="fctrls2" bitsize="32" type="int" group="float"/>
+ <reg name="fstats2" bitsize="32" type="int" group="float"/>
+ <reg name="ftags2" bitsize="32" type="int" group="float"/>
+ <reg name="fisegs2" bitsize="32" type="int" group="float"/>
+ <reg name="fioffs2" bitsize="32" type="int" group="float"/>
+ <reg name="fosegs2" bitsize="32" type="int" group="float"/>
+ <reg name="fooffs2" bitsize="32" type="int" group="float"/>
+ <reg name="fops2" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/32bit-core.xml b/coregrind/m_gdbserver/32bit-core.xml
new file mode 100644
index 0000000..4d0377e
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-core.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.core">
+ <flags id="i386_eflags" size="4">
+ <field name="CF" start="0" end="0"/>
+ <field name="" start="1" end="1"/>
+ <field name="PF" start="2" end="2"/>
+ <field name="AF" start="4" end="4"/>
+ <field name="ZF" start="6" end="6"/>
+ <field name="SF" start="7" end="7"/>
+ <field name="TF" start="8" end="8"/>
+ <field name="IF" start="9" end="9"/>
+ <field name="DF" start="10" end="10"/>
+ <field name="OF" start="11" end="11"/>
+ <field name="NT" start="14" end="14"/>
+ <field name="RF" start="16" end="16"/>
+ <field name="VM" start="17" end="17"/>
+ <field name="AC" start="18" end="18"/>
+ <field name="VIF" start="19" end="19"/>
+ <field name="VIP" start="20" end="20"/>
+ <field name="ID" start="21" end="21"/>
+ </flags>
+
+ <reg name="eax" bitsize="32" type="int32"/>
+ <reg name="ecx" bitsize="32" type="int32"/>
+ <reg name="edx" bitsize="32" type="int32"/>
+ <reg name="ebx" bitsize="32" type="int32"/>
+ <reg name="esp" bitsize="32" type="data_ptr"/>
+ <reg name="ebp" bitsize="32" type="data_ptr"/>
+ <reg name="esi" bitsize="32" type="int32"/>
+ <reg name="edi" bitsize="32" type="int32"/>
+
+ <reg name="eip" bitsize="32" type="code_ptr"/>
+ <reg name="eflags" bitsize="32" type="i386_eflags"/>
+ <reg name="cs" bitsize="32" type="int32"/>
+ <reg name="ss" bitsize="32" type="int32"/>
+ <reg name="ds" bitsize="32" type="int32"/>
+ <reg name="es" bitsize="32" type="int32"/>
+ <reg name="fs" bitsize="32" type="int32"/>
+ <reg name="gs" bitsize="32" type="int32"/>
+
+ <reg name="st0" bitsize="80" type="i387_ext"/>
+ <reg name="st1" bitsize="80" type="i387_ext"/>
+ <reg name="st2" bitsize="80" type="i387_ext"/>
+ <reg name="st3" bitsize="80" type="i387_ext"/>
+ <reg name="st4" bitsize="80" type="i387_ext"/>
+ <reg name="st5" bitsize="80" type="i387_ext"/>
+ <reg name="st6" bitsize="80" type="i387_ext"/>
+ <reg name="st7" bitsize="80" type="i387_ext"/>
+
+ <reg name="fctrl" bitsize="32" type="int" group="float"/>
+ <reg name="fstat" bitsize="32" type="int" group="float"/>
+ <reg name="ftag" bitsize="32" type="int" group="float"/>
+ <reg name="fiseg" bitsize="32" type="int" group="float"/>
+ <reg name="fioff" bitsize="32" type="int" group="float"/>
+ <reg name="foseg" bitsize="32" type="int" group="float"/>
+ <reg name="fooff" bitsize="32" type="int" group="float"/>
+ <reg name="fop" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/32bit-linux-valgrind-s1.xml b/coregrind/m_gdbserver/32bit-linux-valgrind-s1.xml
new file mode 100644
index 0000000..fdf23f0
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-linux-valgrind-s1.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.linux.valgrind.s1">
+ <reg name="orig_eaxs1" bitsize="32" type="int" regnum="83"/>
+</feature>
diff --git a/coregrind/m_gdbserver/32bit-linux-valgrind-s2.xml b/coregrind/m_gdbserver/32bit-linux-valgrind-s2.xml
new file mode 100644
index 0000000..137e3af
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-linux-valgrind-s2.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.linux.valgrind.s2">
+ <reg name="orig_eaxs2" bitsize="32" type="int" regnum="125"/>
+</feature>
diff --git a/coregrind/m_gdbserver/32bit-linux.xml b/coregrind/m_gdbserver/32bit-linux.xml
new file mode 100644
index 0000000..975daf9
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-linux.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.linux">
+ <reg name="orig_eax" bitsize="32" type="int" regnum="41"/>
+</feature>
diff --git a/coregrind/m_gdbserver/32bit-sse-valgrind-s1.xml b/coregrind/m_gdbserver/32bit-sse-valgrind-s1.xml
new file mode 100644
index 0000000..1a368c4
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-sse-valgrind-s1.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.sse.valgrind.s1">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v2i64" type="int64" count="2"/>
+ <union id="vec128">
+ <field name="v4_float" type="v4f"/>
+ <field name="v2_double" type="v2d"/>
+ <field name="v16_int8" type="v16i8"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v2_int64" type="v2i64"/>
+ <field name="uint128" type="uint128"/>
+ </union>
+ <flags id="i386_mxcsr" size="4">
+ <field name="IE" start="0" end="0"/>
+ <field name="DE" start="1" end="1"/>
+ <field name="ZE" start="2" end="2"/>
+ <field name="OE" start="3" end="3"/>
+ <field name="UE" start="4" end="4"/>
+ <field name="PE" start="5" end="5"/>
+ <field name="DAZ" start="6" end="6"/>
+ <field name="IM" start="7" end="7"/>
+ <field name="DM" start="8" end="8"/>
+ <field name="ZM" start="9" end="9"/>
+ <field name="OM" start="10" end="10"/>
+ <field name="UM" start="11" end="11"/>
+ <field name="PM" start="12" end="12"/>
+ <field name="FZ" start="15" end="15"/>
+ </flags>
+
+ <reg name="xmm0s1" bitsize="128" type="vec128"/>
+ <reg name="xmm1s1" bitsize="128" type="vec128"/>
+ <reg name="xmm2s1" bitsize="128" type="vec128"/>
+ <reg name="xmm3s1" bitsize="128" type="vec128"/>
+ <reg name="xmm4s1" bitsize="128" type="vec128"/>
+ <reg name="xmm5s1" bitsize="128" type="vec128"/>
+ <reg name="xmm6s1" bitsize="128" type="vec128"/>
+ <reg name="xmm7s1" bitsize="128" type="vec128"/>
+
+ <reg name="mxcsrs1" bitsize="32" type="i386_mxcsr" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/32bit-sse-valgrind-s2.xml b/coregrind/m_gdbserver/32bit-sse-valgrind-s2.xml
new file mode 100644
index 0000000..c69da70
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-sse-valgrind-s2.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.sse.valgrind.s2">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v2i64" type="int64" count="2"/>
+ <union id="vec128">
+ <field name="v4_float" type="v4f"/>
+ <field name="v2_double" type="v2d"/>
+ <field name="v16_int8" type="v16i8"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v2_int64" type="v2i64"/>
+ <field name="uint128" type="uint128"/>
+ </union>
+ <flags id="i386_mxcsr" size="4">
+ <field name="IE" start="0" end="0"/>
+ <field name="DE" start="1" end="1"/>
+ <field name="ZE" start="2" end="2"/>
+ <field name="OE" start="3" end="3"/>
+ <field name="UE" start="4" end="4"/>
+ <field name="PE" start="5" end="5"/>
+ <field name="DAZ" start="6" end="6"/>
+ <field name="IM" start="7" end="7"/>
+ <field name="DM" start="8" end="8"/>
+ <field name="ZM" start="9" end="9"/>
+ <field name="OM" start="10" end="10"/>
+ <field name="UM" start="11" end="11"/>
+ <field name="PM" start="12" end="12"/>
+ <field name="FZ" start="15" end="15"/>
+ </flags>
+
+ <reg name="xmm0s2" bitsize="128" type="vec128"/>
+ <reg name="xmm1s2" bitsize="128" type="vec128"/>
+ <reg name="xmm2s2" bitsize="128" type="vec128"/>
+ <reg name="xmm3s2" bitsize="128" type="vec128"/>
+ <reg name="xmm4s2" bitsize="128" type="vec128"/>
+ <reg name="xmm5s2" bitsize="128" type="vec128"/>
+ <reg name="xmm6s2" bitsize="128" type="vec128"/>
+ <reg name="xmm7s2" bitsize="128" type="vec128"/>
+
+ <reg name="mxcsrs2" bitsize="32" type="i386_mxcsr" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/32bit-sse.xml b/coregrind/m_gdbserver/32bit-sse.xml
new file mode 100644
index 0000000..cca94b3
--- /dev/null
+++ b/coregrind/m_gdbserver/32bit-sse.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.sse">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v2i64" type="int64" count="2"/>
+ <union id="vec128">
+ <field name="v4_float" type="v4f"/>
+ <field name="v2_double" type="v2d"/>
+ <field name="v16_int8" type="v16i8"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v2_int64" type="v2i64"/>
+ <field name="uint128" type="uint128"/>
+ </union>
+ <flags id="i386_mxcsr" size="4">
+ <field name="IE" start="0" end="0"/>
+ <field name="DE" start="1" end="1"/>
+ <field name="ZE" start="2" end="2"/>
+ <field name="OE" start="3" end="3"/>
+ <field name="UE" start="4" end="4"/>
+ <field name="PE" start="5" end="5"/>
+ <field name="DAZ" start="6" end="6"/>
+ <field name="IM" start="7" end="7"/>
+ <field name="DM" start="8" end="8"/>
+ <field name="ZM" start="9" end="9"/>
+ <field name="OM" start="10" end="10"/>
+ <field name="UM" start="11" end="11"/>
+ <field name="PM" start="12" end="12"/>
+ <field name="FZ" start="15" end="15"/>
+ </flags>
+
+ <reg name="xmm0" bitsize="128" type="vec128" regnum="32"/>
+ <reg name="xmm1" bitsize="128" type="vec128"/>
+ <reg name="xmm2" bitsize="128" type="vec128"/>
+ <reg name="xmm3" bitsize="128" type="vec128"/>
+ <reg name="xmm4" bitsize="128" type="vec128"/>
+ <reg name="xmm5" bitsize="128" type="vec128"/>
+ <reg name="xmm6" bitsize="128" type="vec128"/>
+ <reg name="xmm7" bitsize="128" type="vec128"/>
+
+ <reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-core-valgrind-s1.xml b/coregrind/m_gdbserver/64bit-core-valgrind-s1.xml
new file mode 100644
index 0000000..67b497f
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-core-valgrind-s1.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.core.valgrind.s1">
+ <flags id="i386_eflags" size="4">
+ <field name="CF" start="0" end="0"/>
+ <field name="" start="1" end="1"/>
+ <field name="PF" start="2" end="2"/>
+ <field name="AF" start="4" end="4"/>
+ <field name="ZF" start="6" end="6"/>
+ <field name="SF" start="7" end="7"/>
+ <field name="TF" start="8" end="8"/>
+ <field name="IF" start="9" end="9"/>
+ <field name="DF" start="10" end="10"/>
+ <field name="OF" start="11" end="11"/>
+ <field name="NT" start="14" end="14"/>
+ <field name="RF" start="16" end="16"/>
+ <field name="VM" start="17" end="17"/>
+ <field name="AC" start="18" end="18"/>
+ <field name="VIF" start="19" end="19"/>
+ <field name="VIP" start="20" end="20"/>
+ <field name="ID" start="21" end="21"/>
+ </flags>
+
+ <reg name="raxs1" bitsize="64" type="int64"/>
+ <reg name="rbxs1" bitsize="64" type="int64"/>
+ <reg name="rcxs1" bitsize="64" type="int64"/>
+ <reg name="rdxs1" bitsize="64" type="int64"/>
+ <reg name="rsis1" bitsize="64" type="int64"/>
+ <reg name="rdis1" bitsize="64" type="int64"/>
+ <reg name="rbps1" bitsize="64" type="data_ptr"/>
+ <reg name="rsps1" bitsize="64" type="data_ptr"/>
+ <reg name="r8s1" bitsize="64" type="int64"/>
+ <reg name="r9s1" bitsize="64" type="int64"/>
+ <reg name="r10s1" bitsize="64" type="int64"/>
+ <reg name="r11s1" bitsize="64" type="int64"/>
+ <reg name="r12s1" bitsize="64" type="int64"/>
+ <reg name="r13s1" bitsize="64" type="int64"/>
+ <reg name="r14s1" bitsize="64" type="int64"/>
+ <reg name="r15s1" bitsize="64" type="int64"/>
+
+ <reg name="rips1" bitsize="64" type="code_ptr"/>
+ <reg name="eflagss1" bitsize="32" type="i386_eflags"/>
+ <reg name="css1" bitsize="32" type="int32"/>
+ <reg name="sss1" bitsize="32" type="int32"/>
+ <reg name="dss1" bitsize="32" type="int32"/>
+ <reg name="ess1" bitsize="32" type="int32"/>
+ <reg name="fss1" bitsize="32" type="int32"/>
+ <reg name="gss1" bitsize="32" type="int32"/>
+
+ <reg name="st0s1" bitsize="80" type="i387_ext"/>
+ <reg name="st1s1" bitsize="80" type="i387_ext"/>
+ <reg name="st2s1" bitsize="80" type="i387_ext"/>
+ <reg name="st3s1" bitsize="80" type="i387_ext"/>
+ <reg name="st4s1" bitsize="80" type="i387_ext"/>
+ <reg name="st5s1" bitsize="80" type="i387_ext"/>
+ <reg name="st6s1" bitsize="80" type="i387_ext"/>
+ <reg name="st7s1" bitsize="80" type="i387_ext"/>
+
+ <reg name="fctrls1" bitsize="32" type="int" group="float"/>
+ <reg name="fstats1" bitsize="32" type="int" group="float"/>
+ <reg name="ftags1" bitsize="32" type="int" group="float"/>
+ <reg name="fisegs1" bitsize="32" type="int" group="float"/>
+ <reg name="fioffs1" bitsize="32" type="int" group="float"/>
+ <reg name="fosegs1" bitsize="32" type="int" group="float"/>
+ <reg name="fooffs1" bitsize="32" type="int" group="float"/>
+ <reg name="fops1" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-core-valgrind-s2.xml b/coregrind/m_gdbserver/64bit-core-valgrind-s2.xml
new file mode 100644
index 0000000..14f2726
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-core-valgrind-s2.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.core.valgrind.s2">
+ <flags id="i386_eflags" size="4">
+ <field name="CF" start="0" end="0"/>
+ <field name="" start="1" end="1"/>
+ <field name="PF" start="2" end="2"/>
+ <field name="AF" start="4" end="4"/>
+ <field name="ZF" start="6" end="6"/>
+ <field name="SF" start="7" end="7"/>
+ <field name="TF" start="8" end="8"/>
+ <field name="IF" start="9" end="9"/>
+ <field name="DF" start="10" end="10"/>
+ <field name="OF" start="11" end="11"/>
+ <field name="NT" start="14" end="14"/>
+ <field name="RF" start="16" end="16"/>
+ <field name="VM" start="17" end="17"/>
+ <field name="AC" start="18" end="18"/>
+ <field name="VIF" start="19" end="19"/>
+ <field name="VIP" start="20" end="20"/>
+ <field name="ID" start="21" end="21"/>
+ </flags>
+
+ <reg name="raxs2" bitsize="64" type="int64"/>
+ <reg name="rbxs2" bitsize="64" type="int64"/>
+ <reg name="rcxs2" bitsize="64" type="int64"/>
+ <reg name="rdxs2" bitsize="64" type="int64"/>
+ <reg name="rsis2" bitsize="64" type="int64"/>
+ <reg name="rdis2" bitsize="64" type="int64"/>
+ <reg name="rbps2" bitsize="64" type="data_ptr"/>
+ <reg name="rsps2" bitsize="64" type="data_ptr"/>
+ <reg name="r8s2" bitsize="64" type="int64"/>
+ <reg name="r9s2" bitsize="64" type="int64"/>
+ <reg name="r10s2" bitsize="64" type="int64"/>
+ <reg name="r11s2" bitsize="64" type="int64"/>
+ <reg name="r12s2" bitsize="64" type="int64"/>
+ <reg name="r13s2" bitsize="64" type="int64"/>
+ <reg name="r14s2" bitsize="64" type="int64"/>
+ <reg name="r15s2" bitsize="64" type="int64"/>
+
+ <reg name="rips2" bitsize="64" type="code_ptr"/>
+ <reg name="eflagss2" bitsize="32" type="i386_eflags"/>
+ <reg name="css2" bitsize="32" type="int32"/>
+ <reg name="sss2" bitsize="32" type="int32"/>
+ <reg name="dss2" bitsize="32" type="int32"/>
+ <reg name="ess2" bitsize="32" type="int32"/>
+ <reg name="fss2" bitsize="32" type="int32"/>
+ <reg name="gss2" bitsize="32" type="int32"/>
+
+ <reg name="st0s2" bitsize="80" type="i387_ext"/>
+ <reg name="st1s2" bitsize="80" type="i387_ext"/>
+ <reg name="st2s2" bitsize="80" type="i387_ext"/>
+ <reg name="st3s2" bitsize="80" type="i387_ext"/>
+ <reg name="st4s2" bitsize="80" type="i387_ext"/>
+ <reg name="st5s2" bitsize="80" type="i387_ext"/>
+ <reg name="st6s2" bitsize="80" type="i387_ext"/>
+ <reg name="st7s2" bitsize="80" type="i387_ext"/>
+
+ <reg name="fctrls2" bitsize="32" type="int" group="float"/>
+ <reg name="fstats2" bitsize="32" type="int" group="float"/>
+ <reg name="ftags2" bitsize="32" type="int" group="float"/>
+ <reg name="fisegs2" bitsize="32" type="int" group="float"/>
+ <reg name="fioffs2" bitsize="32" type="int" group="float"/>
+ <reg name="fosegs2" bitsize="32" type="int" group="float"/>
+ <reg name="fooffs2" bitsize="32" type="int" group="float"/>
+ <reg name="fops2" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-core.xml b/coregrind/m_gdbserver/64bit-core.xml
new file mode 100644
index 0000000..8cfe3fe
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-core.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.core">
+ <flags id="i386_eflags" size="4">
+ <field name="CF" start="0" end="0"/>
+ <field name="" start="1" end="1"/>
+ <field name="PF" start="2" end="2"/>
+ <field name="AF" start="4" end="4"/>
+ <field name="ZF" start="6" end="6"/>
+ <field name="SF" start="7" end="7"/>
+ <field name="TF" start="8" end="8"/>
+ <field name="IF" start="9" end="9"/>
+ <field name="DF" start="10" end="10"/>
+ <field name="OF" start="11" end="11"/>
+ <field name="NT" start="14" end="14"/>
+ <field name="RF" start="16" end="16"/>
+ <field name="VM" start="17" end="17"/>
+ <field name="AC" start="18" end="18"/>
+ <field name="VIF" start="19" end="19"/>
+ <field name="VIP" start="20" end="20"/>
+ <field name="ID" start="21" end="21"/>
+ </flags>
+
+ <reg name="rax" bitsize="64" type="int64"/>
+ <reg name="rbx" bitsize="64" type="int64"/>
+ <reg name="rcx" bitsize="64" type="int64"/>
+ <reg name="rdx" bitsize="64" type="int64"/>
+ <reg name="rsi" bitsize="64" type="int64"/>
+ <reg name="rdi" bitsize="64" type="int64"/>
+ <reg name="rbp" bitsize="64" type="data_ptr"/>
+ <reg name="rsp" bitsize="64" type="data_ptr"/>
+ <reg name="r8" bitsize="64" type="int64"/>
+ <reg name="r9" bitsize="64" type="int64"/>
+ <reg name="r10" bitsize="64" type="int64"/>
+ <reg name="r11" bitsize="64" type="int64"/>
+ <reg name="r12" bitsize="64" type="int64"/>
+ <reg name="r13" bitsize="64" type="int64"/>
+ <reg name="r14" bitsize="64" type="int64"/>
+ <reg name="r15" bitsize="64" type="int64"/>
+
+ <reg name="rip" bitsize="64" type="code_ptr"/>
+ <reg name="eflags" bitsize="32" type="i386_eflags"/>
+ <reg name="cs" bitsize="32" type="int32"/>
+ <reg name="ss" bitsize="32" type="int32"/>
+ <reg name="ds" bitsize="32" type="int32"/>
+ <reg name="es" bitsize="32" type="int32"/>
+ <reg name="fs" bitsize="32" type="int32"/>
+ <reg name="gs" bitsize="32" type="int32"/>
+
+ <reg name="st0" bitsize="80" type="i387_ext"/>
+ <reg name="st1" bitsize="80" type="i387_ext"/>
+ <reg name="st2" bitsize="80" type="i387_ext"/>
+ <reg name="st3" bitsize="80" type="i387_ext"/>
+ <reg name="st4" bitsize="80" type="i387_ext"/>
+ <reg name="st5" bitsize="80" type="i387_ext"/>
+ <reg name="st6" bitsize="80" type="i387_ext"/>
+ <reg name="st7" bitsize="80" type="i387_ext"/>
+
+ <reg name="fctrl" bitsize="32" type="int" group="float"/>
+ <reg name="fstat" bitsize="32" type="int" group="float"/>
+ <reg name="ftag" bitsize="32" type="int" group="float"/>
+ <reg name="fiseg" bitsize="32" type="int" group="float"/>
+ <reg name="fioff" bitsize="32" type="int" group="float"/>
+ <reg name="foseg" bitsize="32" type="int" group="float"/>
+ <reg name="fooff" bitsize="32" type="int" group="float"/>
+ <reg name="fop" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-linux-valgrind-s1.xml b/coregrind/m_gdbserver/64bit-linux-valgrind-s1.xml
new file mode 100644
index 0000000..fc1c2dd
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-linux-valgrind-s1.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.linux.valgrind.s1">
+ <reg name="orig_raxs1" bitsize="64" type="int" regnum="115"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-linux-valgrind-s2.xml b/coregrind/m_gdbserver/64bit-linux-valgrind-s2.xml
new file mode 100644
index 0000000..452ddec
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-linux-valgrind-s2.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.linux.valgrind.s2">
+ <reg name="orig_raxs2" bitsize="64" type="int" regnum="173"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-linux.xml b/coregrind/m_gdbserver/64bit-linux.xml
new file mode 100644
index 0000000..8609272
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-linux.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.linux">
+ <reg name="orig_rax" bitsize="64" type="int" regnum="57"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-sse-valgrind-s1.xml b/coregrind/m_gdbserver/64bit-sse-valgrind-s1.xml
new file mode 100644
index 0000000..9db6c74
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-sse-valgrind-s1.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.sse.valgrind.s1">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v2i64" type="int64" count="2"/>
+ <union id="vec128">
+ <field name="v4_float" type="v4f"/>
+ <field name="v2_double" type="v2d"/>
+ <field name="v16_int8" type="v16i8"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v2_int64" type="v2i64"/>
+ <field name="uint128" type="uint128"/>
+ </union>
+ <flags id="i386_mxcsr" size="4">
+ <field name="IE" start="0" end="0"/>
+ <field name="DE" start="1" end="1"/>
+ <field name="ZE" start="2" end="2"/>
+ <field name="OE" start="3" end="3"/>
+ <field name="UE" start="4" end="4"/>
+ <field name="PE" start="5" end="5"/>
+ <field name="DAZ" start="6" end="6"/>
+ <field name="IM" start="7" end="7"/>
+ <field name="DM" start="8" end="8"/>
+ <field name="ZM" start="9" end="9"/>
+ <field name="OM" start="10" end="10"/>
+ <field name="UM" start="11" end="11"/>
+ <field name="PM" start="12" end="12"/>
+ <field name="FZ" start="15" end="15"/>
+ </flags>
+
+ <reg name="xmm0s1" bitsize="128" type="vec128"/>
+ <reg name="xmm1s1" bitsize="128" type="vec128"/>
+ <reg name="xmm2s1" bitsize="128" type="vec128"/>
+ <reg name="xmm3s1" bitsize="128" type="vec128"/>
+ <reg name="xmm4s1" bitsize="128" type="vec128"/>
+ <reg name="xmm5s1" bitsize="128" type="vec128"/>
+ <reg name="xmm6s1" bitsize="128" type="vec128"/>
+ <reg name="xmm7s1" bitsize="128" type="vec128"/>
+ <reg name="xmm8s1" bitsize="128" type="vec128"/>
+ <reg name="xmm9s1" bitsize="128" type="vec128"/>
+ <reg name="xmm10s1" bitsize="128" type="vec128"/>
+ <reg name="xmm11s1" bitsize="128" type="vec128"/>
+ <reg name="xmm12s1" bitsize="128" type="vec128"/>
+ <reg name="xmm13s1" bitsize="128" type="vec128"/>
+ <reg name="xmm14s1" bitsize="128" type="vec128"/>
+ <reg name="xmm15s1" bitsize="128" type="vec128"/>
+
+ <reg name="mxcsrs1" bitsize="32" type="i386_mxcsr" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-sse-valgrind-s2.xml b/coregrind/m_gdbserver/64bit-sse-valgrind-s2.xml
new file mode 100644
index 0000000..189910e
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-sse-valgrind-s2.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.sse.valgrind.s2">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v2i64" type="int64" count="2"/>
+ <union id="vec128">
+ <field name="v4_float" type="v4f"/>
+ <field name="v2_double" type="v2d"/>
+ <field name="v16_int8" type="v16i8"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v2_int64" type="v2i64"/>
+ <field name="uint128" type="uint128"/>
+ </union>
+ <flags id="i386_mxcsr" size="4">
+ <field name="IE" start="0" end="0"/>
+ <field name="DE" start="1" end="1"/>
+ <field name="ZE" start="2" end="2"/>
+ <field name="OE" start="3" end="3"/>
+ <field name="UE" start="4" end="4"/>
+ <field name="PE" start="5" end="5"/>
+ <field name="DAZ" start="6" end="6"/>
+ <field name="IM" start="7" end="7"/>
+ <field name="DM" start="8" end="8"/>
+ <field name="ZM" start="9" end="9"/>
+ <field name="OM" start="10" end="10"/>
+ <field name="UM" start="11" end="11"/>
+ <field name="PM" start="12" end="12"/>
+ <field name="FZ" start="15" end="15"/>
+ </flags>
+
+ <reg name="xmm0s2" bitsize="128" type="vec128"/>
+ <reg name="xmm1s2" bitsize="128" type="vec128"/>
+ <reg name="xmm2s2" bitsize="128" type="vec128"/>
+ <reg name="xmm3s2" bitsize="128" type="vec128"/>
+ <reg name="xmm4s2" bitsize="128" type="vec128"/>
+ <reg name="xmm5s2" bitsize="128" type="vec128"/>
+ <reg name="xmm6s2" bitsize="128" type="vec128"/>
+ <reg name="xmm7s2" bitsize="128" type="vec128"/>
+ <reg name="xmm8s2" bitsize="128" type="vec128"/>
+ <reg name="xmm9s2" bitsize="128" type="vec128"/>
+ <reg name="xmm10s2" bitsize="128" type="vec128"/>
+ <reg name="xmm11s2" bitsize="128" type="vec128"/>
+ <reg name="xmm12s2" bitsize="128" type="vec128"/>
+ <reg name="xmm13s2" bitsize="128" type="vec128"/>
+ <reg name="xmm14s2" bitsize="128" type="vec128"/>
+ <reg name="xmm15s2" bitsize="128" type="vec128"/>
+
+ <reg name="mxcsrs2" bitsize="32" type="i386_mxcsr" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/64bit-sse.xml b/coregrind/m_gdbserver/64bit-sse.xml
new file mode 100644
index 0000000..d7f7925
--- /dev/null
+++ b/coregrind/m_gdbserver/64bit-sse.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.sse">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v2i64" type="int64" count="2"/>
+ <union id="vec128">
+ <field name="v4_float" type="v4f"/>
+ <field name="v2_double" type="v2d"/>
+ <field name="v16_int8" type="v16i8"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v2_int64" type="v2i64"/>
+ <field name="uint128" type="uint128"/>
+ </union>
+ <flags id="i386_mxcsr" size="4">
+ <field name="IE" start="0" end="0"/>
+ <field name="DE" start="1" end="1"/>
+ <field name="ZE" start="2" end="2"/>
+ <field name="OE" start="3" end="3"/>
+ <field name="UE" start="4" end="4"/>
+ <field name="PE" start="5" end="5"/>
+ <field name="DAZ" start="6" end="6"/>
+ <field name="IM" start="7" end="7"/>
+ <field name="DM" start="8" end="8"/>
+ <field name="ZM" start="9" end="9"/>
+ <field name="OM" start="10" end="10"/>
+ <field name="UM" start="11" end="11"/>
+ <field name="PM" start="12" end="12"/>
+ <field name="FZ" start="15" end="15"/>
+ </flags>
+
+ <reg name="xmm0" bitsize="128" type="vec128" regnum="40"/>
+ <reg name="xmm1" bitsize="128" type="vec128"/>
+ <reg name="xmm2" bitsize="128" type="vec128"/>
+ <reg name="xmm3" bitsize="128" type="vec128"/>
+ <reg name="xmm4" bitsize="128" type="vec128"/>
+ <reg name="xmm5" bitsize="128" type="vec128"/>
+ <reg name="xmm6" bitsize="128" type="vec128"/>
+ <reg name="xmm7" bitsize="128" type="vec128"/>
+ <reg name="xmm8" bitsize="128" type="vec128"/>
+ <reg name="xmm9" bitsize="128" type="vec128"/>
+ <reg name="xmm10" bitsize="128" type="vec128"/>
+ <reg name="xmm11" bitsize="128" type="vec128"/>
+ <reg name="xmm12" bitsize="128" type="vec128"/>
+ <reg name="xmm13" bitsize="128" type="vec128"/>
+ <reg name="xmm14" bitsize="128" type="vec128"/>
+ <reg name="xmm15" bitsize="128" type="vec128"/>
+
+ <reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/README_DEVELOPERS b/coregrind/m_gdbserver/README_DEVELOPERS
new file mode 100644
index 0000000..2334078
--- /dev/null
+++ b/coregrind/m_gdbserver/README_DEVELOPERS
@@ -0,0 +1,433 @@
+This file contains various notes/ideas/history/... related
+to gdbserver in valgrind.
+
+How to use Valgrind gdbserver ?
+-------------------------------
+This is described in the Valgrind user manual.
+Before reading the below, you better read the user manual first.
+
+What is gdbserver ?
+-------------------
+gdb debugger typically is used to debug a process running
+on the same machine : gdb uses system calls (such as ptrace)
+to fetch data from the process being debugged
+or to change data in the process
+or interrupt the process
+or ...
+
+gdb can also debug processes running in a different computer
+(e.g. it can debug a process running on a small real time
+board).
+
+gdb does this by sending some commands (e.g. using tcp/ip) to a piece
+of code running on the remote computer. This piece of code (called a
+gdb stub in small boards, or gdbserver when the remote computer runs
+an OS such as GNU/linux) will provide a set of commands allowing gdb
+to remotely debug the process. Examples of commands are: "get the
+registers", "get the list of running threads", "read xxx bytes at
+address yyyyyyyy", etc. The definition of all these commands and the
+associated replies is the gdb remote serial protocol, which is
+documented in Appendix D of gdb user manual.
+
+The standard gdb distribution has a standalone gdbserver (a small
+executable) which implements this protocol and the needed system calls
+to allow gdb to remotely debug process running on a linux or MacOS or
+...
+
+Activation of gdbserver code inside valgrind
+--------------------------------------------
+The gdbserver code (from gdb 6.6, GPL2+) has been modified so as to
+link it with valgrind and allow the valgrind guest process to be
+debugged by a gdb speaking to this gdbserver embedded in valgrind.
+The ptrace system calls inside gdbserver have been replaced by reading
+the state of the guest.
+
+The gdbserver functionality is activated with valgrind command line
+options. If gdbserver is not enabled, then the impact on valgrind
+runtime is minimal: basically it just checks at startup the command
+line option to see that there is nothing to do for what concerns gdb
+server: there is a "if gdbserver is active" check in the translate
+function of translate.c and an "if" in the valgrind scheduler.
+If the valgrind gdbserver is activated (--vgdb=yes), the impact
+is minimal (from time to time, the valgrind scheduler checks a counter
+in memory). Option --vgdb-poll=yyyyy controls how often the scheduler
+will do a (somewhat) more heavy check to see if gdbserver needs to
+stop execution of the guest to allow debugging.
+If valgrind gdbserver is activated with --vgdb=full, then
+each instruction is instrumented with an additional call to a dirty
+helper.
+
+How does gdbserver code interacts with valgrind ?
+-------------------------------------------------
+When an error is reported, the gdbserver code is called. It reads
+commands from gdb using read system call on a FIFO (e.g. a command
+such as "get the registers"). It executes the command (e.g. fetches
+the registers from the guest state) and writes the reply (e.g. a
+packet containing the register data). When gdb instructs gdbserver to
+"continue", the control is returned to valgrind, which then continues
+to execute guest code. The FIFOs used to communication between
+valgrind and gdb are created at startup if gdbserver is activated
+according to the --vgdb=no/yes/full command line option.
+
+How are signals "handled" ?
+---------------------------
+When a signal is to be given to the guest, valgrind core first calls
+gdbserver (if a gdb is currently connected to valgrind, otherwise the
+signal is delivered immediately). If gdb instructs to give the signal
+to the process, the signal is delivered to the guest. Otherwise, the
+signal is ignored (not given to the guest). The user can
+with gdb further decide to pass (or not pass) the signal.
+Note that some (fatal) signals cannot be ignored.
+
+How are "break/step/stepi/next/..." implemented ?
+-------------------------------------------------
+When a break is put by gdb on an instruction, a command is sent to the
+gdbserver in valgrind. This causes the basic block of this instruction
+to be discarded and then re-instrumented so as to insert calls to a
+dirty helper which calls the gdb server code. When a block is
+instrumented for gdbserver, all the "jump targets" of this block are
+invalidated, so as to allow step/stepi/next to properly work: these
+blocks will themselves automatically be re-instrumented for gdbserver
+if they are jumped to.
+The valgrind gdbserver remembers which blocks have been instrumented
+due to this "lazy 'jump targets' debugging instrumentation" so as to
+discard these "debugging translation" when gdb instructs to continue
+the execution normally.
+The blocks in which an explicit break has been put by the user
+are kept instrumented for gdbserver.
+(but note that by default, gdb removes all breaks when the
+process is stopped, and re-inserts all breaks when the process
+is continued). This behaviour can be changed using the gdb
+command 'set breakpoint always-inserted'.
+
+How are watchpoints implemented ?
+---------------------------------
+Watchpoints implies support from the tool to detect that
+a location is read and/or written. Currently, only memcheck
+supports this : when a watchpoint is placed, memcheck changes
+the addressability bits of the watched memory zone to be unacessible.
+Before an access, memcheck then detects an error, but sees this error
+is due to a watchpoint and gives the control back to gdb.
+Stopping on the exact instruction for a write watchpoint implies
+to use --vgdb=full. This is because the error is detected by memcheck
+before modifying the value. gdb checks that the value has not changed
+and so "does not believe" the information that the write watchpoint
+was triggered, and continues the execution. At the next watchpoint
+occurence, gdb sees the value has changed. But the watchpoints are all
+reported "off by one". To avoid this, Valgrind gdbserver must
+terminate the current instruction before reporting the write watchpoint.
+Terminating precisely the current instruction implies to have
+instrumented all the instructions of the block for gdbserver even
+if there is no break in this block. This is ensured by --vgdb=full.
+See m_gdbserver.c Bool VG_(is_watched) where watchpoint handling
+is implemented.
+
+How is the Valgrind gdbserver receiving commands/packets from gdb ?
+-------------------------------------------------------------------
+The embedded gdbserver reads gdb commands on a named pipe having
+(by default) the name /tmp/vgdb-pipe-from-vgdb-to-%d
+where %d will be replaced by the pid.
+The embedded gdbserver will reply to gdb commands on a named pipe
+/tmp/vgdb-pipe-to-vgdb-from-%d
+
+gdb does not speak directly with gdbserver in valgrind: a relay application
+called vgdb is needed between gdb and the valgrind-ified process.
+gdb writes commands on the stdin of vgdb. vgdb reads these
+commands and writes them on FIFO /tmp/vgdb-pipe-from-vgdb-to-%d.
+vgdb reads replies on FIFO /tmp/vgdb-pipe-to-vgdb-from-%d and writes
+them on its stdout.
+
+Note: The solution of named pipes was preferred to tcp ip connections as
+it allows a discovery of which valgrind-ified processes are ready to accept
+command by looking at files starting with the /tmp/vgdb-pipe- prefix
+(changeable by a command line option).
+Also, the usual unix protections are protecting
+the valgrind process against other users sending commands.
+The relay process also takes into account the wake up of the valgrind
+process in case all threads are blocked in a system call.
+The relay process can also be used in a shell to send commands
+without a gdb (this allows to have a standard mechanism to control
+valgrind tools from the command line, rather than specialized mechanism
+e.g. in callgrind).
+
+How is gdbserver activated if all Valgrind threads are blocked in a syscall ?
+-----------------------------------------------------------------------------
+vgdb relays characters from gdb to valgrind. The scheduler will from
+time to time check if gdbserver has to handle incoming characters.
+(the check is efficient i.e. most of the time consists in checking
+a counter in (shared) memory).
+
+However, it might be that all the threads in the valgrind process are
+blocked in a system call. In such a case, no polling will be done by
+the valgrind scheduler (as no activity takes place). By default, vgdb
+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:
+using ptrace, vgdb will cause the valgrind process to call the
+gdbserver code.
+
+This wake up is *not* done using signals as this would imply to
+implement a syscall restart logic in valgrind for all system
+calls. When using ptrace as above, the linux kernel is responsible to
+restart the system call.
+
+This wakeup is also *not* implemented by having a "system thread"
+started by valgrind as this would transform all non-threaded programs
+in threaded programs when running under valgrind. Also, such a 'system
+thread' for gdbserver was tried by Greg Parker in the early MacOS
+port, and was unreliable.
+
+So, the ptrace based solution was chosen instead.
+
+There used to be some bugs in the kernel when using ptrace on
+a process blocked in a system call : the symptom is that the system
+call fails with an unknown errno 512. This typically happens
+with a vgdb in 64bits ptrace-ing a 32 bits process.
+A bypass for old kernels has been integrated in vgdb.c (sign extend
+register rax).
+
+At least on a fedora core 12 (kernel 2.6.32), syscall restart of read
+and select are working ok and red-hat 5.3 (an old kernel), everything
+works properly.
+
+Need to investigate if darwin and/or AIX can similarly do syscall
+restart with ptrace.
+
+The vgdb argument --max-invoke-ms=xxx allows to control the nr of
+milli-seconds after which vgdb will force the invocation of gdbserver
+code. If xxx is 0, this disables the forced invocation.
+Also, disabling this ptrace mechanism is necessary in case you are
+debugging the valgrind code at the same time as debugging the guest
+process using gdbserver.
+
+Do not kill -9 vgdb while it has interrupted the valgrind process,
+otherwise the valgrind process will very probably stay stopped or die.
+
+
+Implementation is based on the gdbserver code from gdb 6.6
+----------------------------------------------------------
+The gdbserver implementation is derived from the gdbserver included
+in the gdb distribution.
+The files originating from gdb are : inferiors.c, regcache.[ch],
+regdef.h, remote-utils.c, server.[ch], signals.c, target.[ch], utils.c,
+version.c.
+valgrind-low-* are inspired from gdb files.
+
+This code had to be changed to integrate properly within valgrind
+(e.g. no libc usage). Some of these changes have been ensured by
+using the preprocessor to replace calls by valgrind equivalent,
+e.g. #define memcpy(...) VG_(memcpy) (...).
+
+Some "control flow" changes are due to the fact that gdbserver inside
+valgrind must return the control to valgrind when the 'debugged'
+process has to run, while in a classical gdbserver usage, the
+gdbserver process waits for a debugged process to stop on a break or
+similar. This has implied to have some variables to remember the
+state of gdbserver before returning to valgrind (search for
+resume_packet_needed in server.c) and "goto" the place where gdbserver
+expects a stopped process to return control to gdbserver.
+
+How does a tool need to be changed to be "debuggable" ?
+-------------------------------------------------------
+There is no need to modify a tool to have it "debuggable" via
+gdbserver : e.g. reports of errors, break etc will work "out of the
+box". If an interactive usage of tool client requests or similar is
+desired for a tool, then simple code can be written for that via a
+specific client request VG_USERREQ__GDB_MONITOR_COMMAND code. The tool
+function "handle_client_request" must then parse the string received
+in argument and call the expected valgrind or tool code. See
+e.g. massif ms_handle_client_request as an example.
+
+
+Automatic regression tests:
+---------------------------
+Automatic Valgrind gdbserver tests are in the directory
+$(top_srcdir)/gdbserver_tests.
+Read $(top_srcdir)/gdbserver_tests/README_DEVELOPPERS for more
+info about testing.
+
+How to integrate support for a new architecture xxx?
+----------------------------------------------------
+Let's imagine a new architecture hal9000 has to be supported.
+
+Mandatory:
+The main thing to do is to make a file valgrind-low-hal9000.c.
+Start from an existing file (e.g. valgrind-low-x86.c).
+The data structures 'struct reg regs'
+and 'const char *expedite_regs' are build from files
+in the gdb sources, e.g. for an new arch hal9000
+ cd gdb/regformats
+ ./regdat.sh reg-hal9000.dat hal9000
+
+From the generated file hal9000, you copy/paste in
+valgrind-low-hal9000.c the two needed data structures and change their
+name to 'regs' and 'expedite_regs'
+
+Then adapt the set of functions needed to initialize the structure
+'static struct valgrind_target_ops low_target'.
+
+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.
+
+For Linux based platforms, all the ptrace calls 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.
+
+For other platforms (i.e. Macos), more work is needed as the ptrace calls
+on Macos are either different and/or incomplete (and so, 'Mach' specific
+things are needed e.g. to attach to threads etc).
+A courageous Mac aficionado is welcome on this aspect.
+
+Optional:
+To let gdb see the Valgrind shadow registers, xml description
+files have to be provided + valgrind-low-hal9000.c has
+to give the top xml file.
+Start from the xml files found in the gdb distribution directory
+gdb/features. You need to duplicate and modify these files to provide
+shadow1 and shadow2 register sets description.
+
+Modify coregrind/Makefile.am:
+ add valgrind-low-hal9000.c
+ If you have target xml description, also add them in pkglib_DATA
+
+
+A not handled comment given by Julian at FOSDEM.
+------------------------------------------------
+* the check for vgdb-poll in scheduler.c could/should be moved to another place:
+ instead of having it in run_thread_for_a_while
+ the vgdb poll check could be in VG_(scheduler).
+ (not clear to me why one is better than the other ???)
+
+TODO and/or additional nice things to have
+------------------------------------------
+* many options can be changed on-line without problems.
+ => would be nice to have a vg.option command that would evaluate
+ its arguments like the startup options of m_main.c and tool clo processing.
+
+* have a mc.who_points_at <address> | <loss_record_nr>
+ that would describe the addresses where a pointer is found
+ to address (or address leaked at loss_record_nr>)
+ This would allow to interactively searching who is "keeping" a piece
+ of memory.
+
+* some GDBTD in the code
+
+(GDBTD = GDB To Do = something still to look at and/or a question)
+
+* All architectures and platforms are done.
+ But there are still some "GDBTD" to convert between gdb registers
+ and VEX registers :
+ e.g. some registers in x86 or amd64 that I could not
+ translate to VEX registers. Someone with a good knowledge
+ of these architectures might complete this
+ (see the GDBTD in valgrind-low-*.c)
+
+* "hardware" watchpoint (read/write/access watchpoints) are implemented
+ but can't persuade gdb to insert a hw watchpoint of what valgrind
+ supports (i.e. of whatever length).
+ The reason why gdb does not accept a hardware watch of let's say
+ 10 bytes is:
+default_region_ok_for_hw_watchpoint (addr=134520360, len=10) at target.c:2738
+2738 return (len <= gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT);
+#0 default_region_ok_for_hw_watchpoint (addr=134520360, len=10)
+ at target.c:2738
+2738 return (len <= gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT);
+#1 0x08132e65 in can_use_hardware_watchpoint (v=0x85a8ef0)
+ at breakpoint.c:8300
+8300 if (!target_region_ok_for_hw_watchpoint (vaddr, len))
+#2 0x0813bd17 in watch_command_1 (arg=0x84169f0 "", accessflag=2,
+ from_tty=<value optimized out>) at breakpoint.c:8140
+ A small patch in gdb remote.c allowed to control the remote target watchpoint
+ length limit. This patch is to be submitted.
+
+* Currently, at least on recent linux kernel, vgdb can properly wake
+ up a valgrind process which is blocked in system calls. Maybe we
+ need to see till which kernel version the ptrace + syscall restart
+ is broken, and put the default value of --max-invoke-ms to 0 in this
+ case.
+
+* more client requests can be programmed in various tools. Currently,
+ there are only a few standard valgrind or memcheck client requests
+ implemented.
+ vg.suppression [generate|add|delete] might be an interesting command:
+ generate would output a suppression, add/delete would add a suppression
+ in memory for the last (or selected?) error.
+ vg.break on fn calls/entry/exit + commands associated to it
+ (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
+ debugger call that can properly jump at an instruction boundary,
+ the jump/call will quit the "middle" of an instruction.
+ We could detect if the current block is instrumented by a trick
+ like this:
+ /* Each time helperc_CallDebugger is called, we will store
+ the address from which is it called and the nr of bbs_done
+ when called. This allows to detect that gdbserver is called
+ from a block which is instrumented. */
+ static HWord CallDebugger_addr;
+ static ULong CallDebugger_bbs_done;
+
+ Bool VG_(gdbserver_current_IP_instrumented) (ThreadId tid)
+ {
+ if (VG_(get_IP) (tid) != CallDebugger_addr
+ || CallDebugger_bbs_done != VG_(bbs_done)())
+ return False;
+ return True;
+ }
+
+ Alternatively, we ensure we can re-instrument the current
+ block for gdbserver while executing it.
+ Something like:
+ keep current block till the end of the current instruction, then
+ go back to scheduler.
+ Unsure if and how this is do-able.
+
+
+* ensure that all non static symbols of gdbserver files are #define
+ xxxxx VG_(xxxxx) ???? Is this really needed ? I have tried to put in
+ a test program variables and functions with the same name as valgrind
+ stuff, and everything seems to be ok.
+ I see that all exported symbols in valgrind have a unique prefix
+ created with VG_ or MC_ or ...
+ This is not done for the "gdb gdbserver code", where I have kept
+ the original names. Is this a problem ? I could not create
+ a "symbol" collision between the user symbol and the valgrind
+ core gdbserver symbol.
+
+* currently, gdbserver can only stop/continue the whole process. It
+ might be interesting to have a fine-grained thread control (vCont
+ packet) maybe for tools such as helgrind, drd. This would allow the
+ user to stop/resume specific threads. Also, maybe this would solve
+ the following problem: wait for a breakpoint to be encountered,
+ switch thread, next. This sometimes causes an internal error in gdb,
+ probably because gdb believes the current thread will be continued ?
+
+* would be nice to have some more tests.
+
+* better valgrind target support in gdb (see comments of Tom Tromey).
+
+
+-------- description of how gdb invokes a function in the inferior
+to call a function in the inferior (below is for x86):
+gdb writes ESP and EBP to have some more stack space
+push a return address equal to 0x8048390 <_start>
+puts a break at 0x8048390
+put address of the function to call (e.g. hello_world in EIP (0x8048444))
+continue
+break encountered at 0x8048391 (90 after decrement)
+ => report stop to gdb
+ => gdb restores esp/ebp/eip to what it was (eg. 0x804848C)
+ => gdb "s" => causes the EIP to go to the new EIP (i.e. 0x804848C)
+ gdbserver tells "resuming from 0x804848c"
+ "stop pc is 0x8048491" => informed gdb of this
+
diff --git a/coregrind/m_gdbserver/amd64-coresse-valgrind.xml b/coregrind/m_gdbserver/amd64-coresse-valgrind.xml
new file mode 100644
index 0000000..3008d5f
--- /dev/null
+++ b/coregrind/m_gdbserver/amd64-coresse-valgrind.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- AMD64 - core and sse. -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>i386:x86-64</architecture>
+ <xi:include href="64bit-core.xml"/>
+ <xi:include href="64bit-sse.xml"/>
+ <xi:include href="64bit-core-valgrind-s1.xml"/>
+ <xi:include href="64bit-sse-valgrind-s1.xml"/>
+ <xi:include href="64bit-core-valgrind-s2.xml"/>
+ <xi:include href="64bit-sse-valgrind-s2.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/amd64-linux-valgrind.xml b/coregrind/m_gdbserver/amd64-linux-valgrind.xml
new file mode 100644
index 0000000..a18e557
--- /dev/null
+++ b/coregrind/m_gdbserver/amd64-linux-valgrind.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- AMD64 - Includes Linux-only special "register". -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>i386:x86-64</architecture>
+ <osabi>GNU/Linux</osabi>
+ <xi:include href="64bit-core.xml"/>
+ <xi:include href="64bit-sse.xml"/>
+ <xi:include href="64bit-linux.xml"/>
+ <xi:include href="64bit-core-valgrind-s1.xml"/>
+ <xi:include href="64bit-sse-valgrind-s1.xml"/>
+ <xi:include href="64bit-linux-valgrind-s1.xml"/>
+ <xi:include href="64bit-core-valgrind-s2.xml"/>
+ <xi:include href="64bit-sse-valgrind-s2.xml"/>
+ <xi:include href="64bit-linux-valgrind-s2.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/arm-core-valgrind-s1.xml b/coregrind/m_gdbserver/arm-core-valgrind-s1.xml
new file mode 100644
index 0000000..cc033a0
--- /dev/null
+++ b/coregrind/m_gdbserver/arm-core-valgrind-s1.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.core.valgrind.s1">
+ <reg name="r0s1" bitsize="32"/>
+ <reg name="r1s1" bitsize="32"/>
+ <reg name="r2s1" bitsize="32"/>
+ <reg name="r3s1" bitsize="32"/>
+ <reg name="r4s1" bitsize="32"/>
+ <reg name="r5s1" bitsize="32"/>
+ <reg name="r6s1" bitsize="32"/>
+ <reg name="r7s1" bitsize="32"/>
+ <reg name="r8s1" bitsize="32"/>
+ <reg name="r9s1" bitsize="32"/>
+ <reg name="r10s1" bitsize="32"/>
+ <reg name="r11s1" bitsize="32"/>
+ <reg name="r12s1" bitsize="32"/>
+ <reg name="sps1" bitsize="32" type="data_ptr"/>
+ <reg name="lrs1" bitsize="32"/>
+ <reg name="pcs1" bitsize="32" type="code_ptr"/>
+
+ <!-- The CPSR is register 25, rather than register 16, because
+ the FPA registers historically were placed between the PC
+ and the CPSR in the "g" packet. -->
+ <reg name="cpsrs1" bitsize="32" regnum="25"/>
+</feature>
diff --git a/coregrind/m_gdbserver/arm-core-valgrind-s2.xml b/coregrind/m_gdbserver/arm-core-valgrind-s2.xml
new file mode 100644
index 0000000..9c3aa69
--- /dev/null
+++ b/coregrind/m_gdbserver/arm-core-valgrind-s2.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.core.valgrind.s2">
+ <reg name="r0s2" bitsize="32"/>
+ <reg name="r1s2" bitsize="32"/>
+ <reg name="r2s2" bitsize="32"/>
+ <reg name="r3s2" bitsize="32"/>
+ <reg name="r4s2" bitsize="32"/>
+ <reg name="r5s2" bitsize="32"/>
+ <reg name="r6s2" bitsize="32"/>
+ <reg name="r7s2" bitsize="32"/>
+ <reg name="r8s2" bitsize="32"/>
+ <reg name="r9s2" bitsize="32"/>
+ <reg name="r10s2" bitsize="32"/>
+ <reg name="r11s2" bitsize="32"/>
+ <reg name="r12s2" bitsize="32"/>
+ <reg name="sps2" bitsize="32" type="data_ptr"/>
+ <reg name="lrs2" bitsize="32"/>
+ <reg name="pcs2" bitsize="32" type="code_ptr"/>
+
+ <!-- The CPSR is register 25, rather than register 16, because
+ the FPA registers historically were placed between the PC
+ and the CPSR in the "g" packet. -->
+ <reg name="cpsrs2" bitsize="32" regnum="25"/>
+</feature>
diff --git a/coregrind/m_gdbserver/arm-core.xml b/coregrind/m_gdbserver/arm-core.xml
new file mode 100644
index 0000000..1624901
--- /dev/null
+++ b/coregrind/m_gdbserver/arm-core.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.core">
+ <reg name="r0" bitsize="32"/>
+ <reg name="r1" bitsize="32"/>
+ <reg name="r2" bitsize="32"/>
+ <reg name="r3" bitsize="32"/>
+ <reg name="r4" bitsize="32"/>
+ <reg name="r5" bitsize="32"/>
+ <reg name="r6" bitsize="32"/>
+ <reg name="r7" bitsize="32"/>
+ <reg name="r8" bitsize="32"/>
+ <reg name="r9" bitsize="32"/>
+ <reg name="r10" bitsize="32"/>
+ <reg name="r11" bitsize="32"/>
+ <reg name="r12" bitsize="32"/>
+ <reg name="sp" bitsize="32" type="data_ptr"/>
+ <reg name="lr" bitsize="32"/>
+ <reg name="pc" bitsize="32" type="code_ptr"/>
+
+ <!-- The CPSR is register 25, rather than register 16, because
+ the FPA registers historically were placed between the PC
+ and the CPSR in the "g" packet. -->
+ <reg name="cpsr" bitsize="32" regnum="25"/>
+</feature>
diff --git a/coregrind/m_gdbserver/arm-vfpv3-valgrind-s1.xml b/coregrind/m_gdbserver/arm-vfpv3-valgrind-s1.xml
new file mode 100644
index 0000000..619f73f
--- /dev/null
+++ b/coregrind/m_gdbserver/arm-vfpv3-valgrind-s1.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.vfp.valgrind.s1">
+ <reg name="d0s1" bitsize="64" type="ieee_double"/>
+ <reg name="d1s1" bitsize="64" type="ieee_double"/>
+ <reg name="d2s1" bitsize="64" type="ieee_double"/>
+ <reg name="d3s1" bitsize="64" type="ieee_double"/>
+ <reg name="d4s1" bitsize="64" type="ieee_double"/>
+ <reg name="d5s1" bitsize="64" type="ieee_double"/>
+ <reg name="d6s1" bitsize="64" type="ieee_double"/>
+ <reg name="d7s1" bitsize="64" type="ieee_double"/>
+ <reg name="d8s1" bitsize="64" type="ieee_double"/>
+ <reg name="d9s1" bitsize="64" type="ieee_double"/>
+ <reg name="d10s1" bitsize="64" type="ieee_double"/>
+ <reg name="d11s1" bitsize="64" type="ieee_double"/>
+ <reg name="d12s1" bitsize="64" type="ieee_double"/>
+ <reg name="d13s1" bitsize="64" type="ieee_double"/>
+ <reg name="d14s1" bitsize="64" type="ieee_double"/>
+ <reg name="d15s1" bitsize="64" type="ieee_double"/>
+ <reg name="d16s1" bitsize="64" type="ieee_double"/>
+ <reg name="d17s1" bitsize="64" type="ieee_double"/>
+ <reg name="d18s1" bitsize="64" type="ieee_double"/>
+ <reg name="d19s1" bitsize="64" type="ieee_double"/>
+ <reg name="d20s1" bitsize="64" type="ieee_double"/>
+ <reg name="d21s1" bitsize="64" type="ieee_double"/>
+ <reg name="d22s1" bitsize="64" type="ieee_double"/>
+ <reg name="d23s1" bitsize="64" type="ieee_double"/>
+ <reg name="d24s1" bitsize="64" type="ieee_double"/>
+ <reg name="d25s1" bitsize="64" type="ieee_double"/>
+ <reg name="d26s1" bitsize="64" type="ieee_double"/>
+ <reg name="d27s1" bitsize="64" type="ieee_double"/>
+ <reg name="d28s1" bitsize="64" type="ieee_double"/>
+ <reg name="d29s1" bitsize="64" type="ieee_double"/>
+ <reg name="d30s1" bitsize="64" type="ieee_double"/>
+ <reg name="d31s1" bitsize="64" type="ieee_double"/>
+
+ <reg name="fpscrs1" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/arm-vfpv3-valgrind-s2.xml b/coregrind/m_gdbserver/arm-vfpv3-valgrind-s2.xml
new file mode 100644
index 0000000..c0e8677
--- /dev/null
+++ b/coregrind/m_gdbserver/arm-vfpv3-valgrind-s2.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.vfp.valgrind.s2">
+ <reg name="d0s2" bitsize="64" type="ieee_double"/>
+ <reg name="d1s2" bitsize="64" type="ieee_double"/>
+ <reg name="d2s2" bitsize="64" type="ieee_double"/>
+ <reg name="d3s2" bitsize="64" type="ieee_double"/>
+ <reg name="d4s2" bitsize="64" type="ieee_double"/>
+ <reg name="d5s2" bitsize="64" type="ieee_double"/>
+ <reg name="d6s2" bitsize="64" type="ieee_double"/>
+ <reg name="d7s2" bitsize="64" type="ieee_double"/>
+ <reg name="d8s2" bitsize="64" type="ieee_double"/>
+ <reg name="d9s2" bitsize="64" type="ieee_double"/>
+ <reg name="d10s2" bitsize="64" type="ieee_double"/>
+ <reg name="d11s2" bitsize="64" type="ieee_double"/>
+ <reg name="d12s2" bitsize="64" type="ieee_double"/>
+ <reg name="d13s2" bitsize="64" type="ieee_double"/>
+ <reg name="d14s2" bitsize="64" type="ieee_double"/>
+ <reg name="d15s2" bitsize="64" type="ieee_double"/>
+ <reg name="d16s2" bitsize="64" type="ieee_double"/>
+ <reg name="d17s2" bitsize="64" type="ieee_double"/>
+ <reg name="d18s2" bitsize="64" type="ieee_double"/>
+ <reg name="d19s2" bitsize="64" type="ieee_double"/>
+ <reg name="d20s2" bitsize="64" type="ieee_double"/>
+ <reg name="d21s2" bitsize="64" type="ieee_double"/>
+ <reg name="d22s2" bitsize="64" type="ieee_double"/>
+ <reg name="d23s2" bitsize="64" type="ieee_double"/>
+ <reg name="d24s2" bitsize="64" type="ieee_double"/>
+ <reg name="d25s2" bitsize="64" type="ieee_double"/>
+ <reg name="d26s2" bitsize="64" type="ieee_double"/>
+ <reg name="d27s2" bitsize="64" type="ieee_double"/>
+ <reg name="d28s2" bitsize="64" type="ieee_double"/>
+ <reg name="d29s2" bitsize="64" type="ieee_double"/>
+ <reg name="d30s2" bitsize="64" type="ieee_double"/>
+ <reg name="d31s2" bitsize="64" type="ieee_double"/>
+
+ <reg name="fpscrs2" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/arm-vfpv3.xml b/coregrind/m_gdbserver/arm-vfpv3.xml
new file mode 100644
index 0000000..d0e9a59
--- /dev/null
+++ b/coregrind/m_gdbserver/arm-vfpv3.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.vfp">
+ <reg name="d0" bitsize="64" type="ieee_double"/>
+ <reg name="d1" bitsize="64" type="ieee_double"/>
+ <reg name="d2" bitsize="64" type="ieee_double"/>
+ <reg name="d3" bitsize="64" type="ieee_double"/>
+ <reg name="d4" bitsize="64" type="ieee_double"/>
+ <reg name="d5" bitsize="64" type="ieee_double"/>
+ <reg name="d6" bitsize="64" type="ieee_double"/>
+ <reg name="d7" bitsize="64" type="ieee_double"/>
+ <reg name="d8" bitsize="64" type="ieee_double"/>
+ <reg name="d9" bitsize="64" type="ieee_double"/>
+ <reg name="d10" bitsize="64" type="ieee_double"/>
+ <reg name="d11" bitsize="64" type="ieee_double"/>
+ <reg name="d12" bitsize="64" type="ieee_double"/>
+ <reg name="d13" bitsize="64" type="ieee_double"/>
+ <reg name="d14" bitsize="64" type="ieee_double"/>
+ <reg name="d15" bitsize="64" type="ieee_double"/>
+ <reg name="d16" bitsize="64" type="ieee_double"/>
+ <reg name="d17" bitsize="64" type="ieee_double"/>
+ <reg name="d18" bitsize="64" type="ieee_double"/>
+ <reg name="d19" bitsize="64" type="ieee_double"/>
+ <reg name="d20" bitsize="64" type="ieee_double"/>
+ <reg name="d21" bitsize="64" type="ieee_double"/>
+ <reg name="d22" bitsize="64" type="ieee_double"/>
+ <reg name="d23" bitsize="64" type="ieee_double"/>
+ <reg name="d24" bitsize="64" type="ieee_double"/>
+ <reg name="d25" bitsize="64" type="ieee_double"/>
+ <reg name="d26" bitsize="64" type="ieee_double"/>
+ <reg name="d27" bitsize="64" type="ieee_double"/>
+ <reg name="d28" bitsize="64" type="ieee_double"/>
+ <reg name="d29" bitsize="64" type="ieee_double"/>
+ <reg name="d30" bitsize="64" type="ieee_double"/>
+ <reg name="d31" bitsize="64" type="ieee_double"/>
+
+ <reg name="fpscr" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/coregrind/m_gdbserver/arm-with-vfpv3-valgrind.xml b/coregrind/m_gdbserver/arm-with-vfpv3-valgrind.xml
new file mode 100644
index 0000000..6501c9b
--- /dev/null
+++ b/coregrind/m_gdbserver/arm-with-vfpv3-valgrind.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <xi:include href="arm-core.xml"/>
+ <xi:include href="arm-vfpv3.xml"/>
+ <xi:include href="arm-core-valgrind-s1.xml"/>
+ <xi:include href="arm-vfpv3-valgrind-s1.xml"/>
+ <xi:include href="arm-core-valgrind-s2.xml"/>
+ <xi:include href="arm-vfpv3-valgrind-s2.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/arm-with-vfpv3.xml b/coregrind/m_gdbserver/arm-with-vfpv3.xml
new file mode 100644
index 0000000..319da1a
--- /dev/null
+++ b/coregrind/m_gdbserver/arm-with-vfpv3.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <xi:include href="arm-core.xml"/>
+ <xi:include href="arm-vfpv3.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/gdb/signals.h b/coregrind/m_gdbserver/gdb/signals.h
new file mode 100644
index 0000000..c240f6b
--- /dev/null
+++ b/coregrind/m_gdbserver/gdb/signals.h
@@ -0,0 +1,238 @@
+/* Target signal numbers for GDB and the GDB remote protocol.
+ Copyright 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
+ 1997, 1998, 1999, 2000, 2001, 2002
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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. */
+
+#ifndef GDB_SIGNALS_H
+#define GDB_SIGNALS_H
+
+/* The numbering of these signals is chosen to match traditional unix
+ signals (insofar as various unices use the same numbers, anyway).
+ It is also the numbering of the GDB remote protocol. Other remote
+ protocols, if they use a different numbering, should make sure to
+ translate appropriately.
+
+ Since these numbers have actually made it out into other software
+ (stubs, etc.), you mustn't disturb the assigned numbering. If you
+ need to add new signals here, add them to the end of the explicitly
+ numbered signals, at the comment marker. Add them unconditionally,
+ not within any #if or #ifdef.
+
+ This is based strongly on Unix/POSIX signals for several reasons:
+ (1) This set of signals represents a widely-accepted attempt to
+ represent events of this sort in a portable fashion, (2) we want a
+ signal to make it from wait to child_wait to the user intact, (3) many
+ remote protocols use a similar encoding. However, it is
+ recognized that this set of signals has limitations (such as not
+ distinguishing between various kinds of SIGSEGV, or not
+ distinguishing hitting a breakpoint from finishing a single step).
+ So in the future we may get around this either by adding additional
+ signals for breakpoint, single-step, etc., or by adding signal
+ codes; the latter seems more in the spirit of what BSD, System V,
+ etc. are doing to address these issues. */
+
+/* For an explanation of what each signal means, see
+ signals.c. */
+
+enum target_signal
+ {
+ /* Used some places (e.g. stop_signal) to record the concept that
+ there is no signal. */
+ TARGET_SIGNAL_0 = 0,
+ TARGET_SIGNAL_FIRST = 0,
+ TARGET_SIGNAL_HUP = 1,
+ TARGET_SIGNAL_INT = 2,
+ TARGET_SIGNAL_QUIT = 3,
+ TARGET_SIGNAL_ILL = 4,
+ TARGET_SIGNAL_TRAP = 5,
+ TARGET_SIGNAL_ABRT = 6,
+ TARGET_SIGNAL_EMT = 7,
+ TARGET_SIGNAL_FPE = 8,
+ TARGET_SIGNAL_KILL = 9,
+ TARGET_SIGNAL_BUS = 10,
+ TARGET_SIGNAL_SEGV = 11,
+ TARGET_SIGNAL_SYS = 12,
+ TARGET_SIGNAL_PIPE = 13,
+ TARGET_SIGNAL_ALRM = 14,
+ TARGET_SIGNAL_TERM = 15,
+ TARGET_SIGNAL_URG = 16,
+ TARGET_SIGNAL_STOP = 17,
+ TARGET_SIGNAL_TSTP = 18,
+ TARGET_SIGNAL_CONT = 19,
+ TARGET_SIGNAL_CHLD = 20,
+ TARGET_SIGNAL_TTIN = 21,
+ TARGET_SIGNAL_TTOU = 22,
+ TARGET_SIGNAL_IO = 23,
+ TARGET_SIGNAL_XCPU = 24,
+ TARGET_SIGNAL_XFSZ = 25,
+ TARGET_SIGNAL_VTALRM = 26,
+ TARGET_SIGNAL_PROF = 27,
+ TARGET_SIGNAL_WINCH = 28,
+ TARGET_SIGNAL_LOST = 29,
+ TARGET_SIGNAL_USR1 = 30,
+ TARGET_SIGNAL_USR2 = 31,
+ TARGET_SIGNAL_PWR = 32,
+ /* Similar to SIGIO. Perhaps they should have the same number. */
+ TARGET_SIGNAL_POLL = 33,
+ TARGET_SIGNAL_WIND = 34,
+ TARGET_SIGNAL_PHONE = 35,
+ TARGET_SIGNAL_WAITING = 36,
+ TARGET_SIGNAL_LWP = 37,
+ TARGET_SIGNAL_DANGER = 38,
+ TARGET_SIGNAL_GRANT = 39,
+ TARGET_SIGNAL_RETRACT = 40,
+ TARGET_SIGNAL_MSG = 41,
+ TARGET_SIGNAL_SOUND = 42,
+ TARGET_SIGNAL_SAK = 43,
+ TARGET_SIGNAL_PRIO = 44,
+ TARGET_SIGNAL_REALTIME_33 = 45,
+ TARGET_SIGNAL_REALTIME_34 = 46,
+ TARGET_SIGNAL_REALTIME_35 = 47,
+ TARGET_SIGNAL_REALTIME_36 = 48,
+ TARGET_SIGNAL_REALTIME_37 = 49,
+ TARGET_SIGNAL_REALTIME_38 = 50,
+ TARGET_SIGNAL_REALTIME_39 = 51,
+ TARGET_SIGNAL_REALTIME_40 = 52,
+ TARGET_SIGNAL_REALTIME_41 = 53,
+ TARGET_SIGNAL_REALTIME_42 = 54,
+ TARGET_SIGNAL_REALTIME_43 = 55,
+ TARGET_SIGNAL_REALTIME_44 = 56,
+ TARGET_SIGNAL_REALTIME_45 = 57,
+ TARGET_SIGNAL_REALTIME_46 = 58,
+ TARGET_SIGNAL_REALTIME_47 = 59,
+ TARGET_SIGNAL_REALTIME_48 = 60,
+ TARGET_SIGNAL_REALTIME_49 = 61,
+ TARGET_SIGNAL_REALTIME_50 = 62,
+ TARGET_SIGNAL_REALTIME_51 = 63,
+ TARGET_SIGNAL_REALTIME_52 = 64,
+ TARGET_SIGNAL_REALTIME_53 = 65,
+ TARGET_SIGNAL_REALTIME_54 = 66,
+ TARGET_SIGNAL_REALTIME_55 = 67,
+ TARGET_SIGNAL_REALTIME_56 = 68,
+ TARGET_SIGNAL_REALTIME_57 = 69,
+ TARGET_SIGNAL_REALTIME_58 = 70,
+ TARGET_SIGNAL_REALTIME_59 = 71,
+ TARGET_SIGNAL_REALTIME_60 = 72,
+ TARGET_SIGNAL_REALTIME_61 = 73,
+ TARGET_SIGNAL_REALTIME_62 = 74,
+ TARGET_SIGNAL_REALTIME_63 = 75,
+
+ /* Used internally by Solaris threads. See signal(5) on Solaris. */
+ TARGET_SIGNAL_CANCEL = 76,
+
+ /* Yes, this pains me, too. But LynxOS didn't have SIG32, and now
+ GNU/Linux does, and we can't disturb the numbering, since it's
+ part of the remote protocol. Note that in some GDB's
+ TARGET_SIGNAL_REALTIME_32 is number 76. */
+ TARGET_SIGNAL_REALTIME_32,
+ /* Yet another pain, IRIX 6 has SIG64. */
+ TARGET_SIGNAL_REALTIME_64,
+ /* Yet another pain, GNU/Linux MIPS might go up to 128. */
+ TARGET_SIGNAL_REALTIME_65,
+ TARGET_SIGNAL_REALTIME_66,
+ TARGET_SIGNAL_REALTIME_67,
+ TARGET_SIGNAL_REALTIME_68,
+ TARGET_SIGNAL_REALTIME_69,
+ TARGET_SIGNAL_REALTIME_70,
+ TARGET_SIGNAL_REALTIME_71,
+ TARGET_SIGNAL_REALTIME_72,
+ TARGET_SIGNAL_REALTIME_73,
+ TARGET_SIGNAL_REALTIME_74,
+ TARGET_SIGNAL_REALTIME_75,
+ TARGET_SIGNAL_REALTIME_76,
+ TARGET_SIGNAL_REALTIME_77,
+ TARGET_SIGNAL_REALTIME_78,
+ TARGET_SIGNAL_REALTIME_79,
+ TARGET_SIGNAL_REALTIME_80,
+ TARGET_SIGNAL_REALTIME_81,
+ TARGET_SIGNAL_REALTIME_82,
+ TARGET_SIGNAL_REALTIME_83,
+ TARGET_SIGNAL_REALTIME_84,
+ TARGET_SIGNAL_REALTIME_85,
+ TARGET_SIGNAL_REALTIME_86,
+ TARGET_SIGNAL_REALTIME_87,
+ TARGET_SIGNAL_REALTIME_88,
+ TARGET_SIGNAL_REALTIME_89,
+ TARGET_SIGNAL_REALTIME_90,
+ TARGET_SIGNAL_REALTIME_91,
+ TARGET_SIGNAL_REALTIME_92,
+ TARGET_SIGNAL_REALTIME_93,
+ TARGET_SIGNAL_REALTIME_94,
+ TARGET_SIGNAL_REALTIME_95,
+ TARGET_SIGNAL_REALTIME_96,
+ TARGET_SIGNAL_REALTIME_97,
+ TARGET_SIGNAL_REALTIME_98,
+ TARGET_SIGNAL_REALTIME_99,
+ TARGET_SIGNAL_REALTIME_100,
+ TARGET_SIGNAL_REALTIME_101,
+ TARGET_SIGNAL_REALTIME_102,
+ TARGET_SIGNAL_REALTIME_103,
+ TARGET_SIGNAL_REALTIME_104,
+ TARGET_SIGNAL_REALTIME_105,
+ TARGET_SIGNAL_REALTIME_106,
+ TARGET_SIGNAL_REALTIME_107,
+ TARGET_SIGNAL_REALTIME_108,
+ TARGET_SIGNAL_REALTIME_109,
+ TARGET_SIGNAL_REALTIME_110,
+ TARGET_SIGNAL_REALTIME_111,
+ TARGET_SIGNAL_REALTIME_112,
+ TARGET_SIGNAL_REALTIME_113,
+ TARGET_SIGNAL_REALTIME_114,
+ TARGET_SIGNAL_REALTIME_115,
+ TARGET_SIGNAL_REALTIME_116,
+ TARGET_SIGNAL_REALTIME_117,
+ TARGET_SIGNAL_REALTIME_118,
+ TARGET_SIGNAL_REALTIME_119,
+ TARGET_SIGNAL_REALTIME_120,
+ TARGET_SIGNAL_REALTIME_121,
+ TARGET_SIGNAL_REALTIME_122,
+ TARGET_SIGNAL_REALTIME_123,
+ TARGET_SIGNAL_REALTIME_124,
+ TARGET_SIGNAL_REALTIME_125,
+ TARGET_SIGNAL_REALTIME_126,
+ TARGET_SIGNAL_REALTIME_127,
+
+ TARGET_SIGNAL_INFO,
+
+ /* Some signal we don't know about. */
+ TARGET_SIGNAL_UNKNOWN,
+
+ /* Use whatever signal we use when one is not specifically specified
+ (for passing to proceed and so on). */
+ TARGET_SIGNAL_DEFAULT,
+
+ /* Mach exceptions. In versions of GDB before 5.2, these were just before
+ TARGET_SIGNAL_INFO if you were compiling on a Mach host (and missing
+ otherwise). */
+ TARGET_EXC_BAD_ACCESS,
+ TARGET_EXC_BAD_INSTRUCTION,
+ TARGET_EXC_ARITHMETIC,
+ TARGET_EXC_EMULATION,
+ TARGET_EXC_SOFTWARE,
+ TARGET_EXC_BREAKPOINT,
+
+ /* If you are adding a new signal, add it just above this comment. */
+
+ /* Last and unused enum value, for sizing arrays, etc. */
+ TARGET_SIGNAL_LAST
+ };
+
+#endif /* #ifndef GDB_SIGNALS_H */
diff --git a/coregrind/m_gdbserver/i386-coresse-valgrind.xml b/coregrind/m_gdbserver/i386-coresse-valgrind.xml
new file mode 100644
index 0000000..1ec7c9e
--- /dev/null
+++ b/coregrind/m_gdbserver/i386-coresse-valgrind.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- I386 with SSE -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>i386</architecture>
+ <xi:include href="32bit-core.xml"/>
+ <xi:include href="32bit-sse.xml"/>
+ <xi:include href="32bit-core-valgrind-s1.xml"/>
+ <xi:include href="32bit-sse-valgrind-s1.xml"/>
+ <xi:include href="32bit-core-valgrind-s2.xml"/>
+ <xi:include href="32bit-sse-valgrind-s2.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/i386-linux-valgrind.xml b/coregrind/m_gdbserver/i386-linux-valgrind.xml
new file mode 100644
index 0000000..8720440
--- /dev/null
+++ b/coregrind/m_gdbserver/i386-linux-valgrind.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- I386 with SSE - Includes Linux-only special "register". -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>i386</architecture>
+ <osabi>GNU/Linux</osabi>
+ <xi:include href="32bit-core.xml"/>
+ <xi:include href="32bit-sse.xml"/>
+ <xi:include href="32bit-linux.xml"/>
+ <xi:include href="32bit-core-valgrind-s1.xml"/>
+ <xi:include href="32bit-sse-valgrind-s1.xml"/>
+ <xi:include href="32bit-linux-valgrind-s1.xml"/>
+ <xi:include href="32bit-core-valgrind-s2.xml"/>
+ <xi:include href="32bit-sse-valgrind-s2.xml"/>
+ <xi:include href="32bit-linux-valgrind-s2.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/inferiors.c b/coregrind/m_gdbserver/inferiors.c
new file mode 100644
index 0000000..cdfea0b
--- /dev/null
+++ b/coregrind/m_gdbserver/inferiors.c
@@ -0,0 +1,227 @@
+/* Inferior process information for the remote server for GDB.
+ Copyright (C) 2002, 2005, 2011
+ Free Software Foundation, Inc.
+
+ Contributed by MontaVista Software.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+
+struct thread_info
+{
+ struct inferior_list_entry entry;
+ void *target_data;
+ void *regcache_data;
+ unsigned int gdb_id;
+};
+
+struct inferior_list all_threads;
+
+struct thread_info *current_inferior;
+
+#define get_thread(inf) ((struct thread_info *)(inf))
+
+void add_inferior_to_list (struct inferior_list *list,
+ struct inferior_list_entry *new_inferior)
+{
+ new_inferior->next = NULL;
+ if (list->tail != NULL)
+ list->tail->next = new_inferior;
+ else
+ list->head = new_inferior;
+ list->tail = new_inferior;
+}
+
+void for_each_inferior (struct inferior_list *list,
+ void (*action) (struct inferior_list_entry *))
+{
+ struct inferior_list_entry *cur = list->head, *next;
+
+ while (cur != NULL) {
+ next = cur->next;
+ (*action) (cur);
+ cur = next;
+ }
+}
+
+void change_inferior_id (struct inferior_list *list,
+ unsigned long new_id)
+{
+ if (list->head != list->tail)
+ error ("tried to change thread ID after multiple threads are created\n");
+
+ list->head->id = new_id;
+}
+
+void remove_inferior (struct inferior_list *list,
+ struct inferior_list_entry *entry)
+{
+ struct inferior_list_entry **cur;
+
+ if (list->head == entry) {
+ list->head = entry->next;
+ if (list->tail == entry)
+ list->tail = list->head;
+ return;
+ }
+
+ cur = &list->head;
+ while (*cur && (*cur)->next != entry)
+ cur = &(*cur)->next;
+
+ if (*cur == NULL)
+ return;
+
+ (*cur)->next = entry->next;
+
+ if (list->tail == entry)
+ list->tail = *cur;
+}
+
+void add_thread (unsigned long thread_id, void *target_data, unsigned int gdb_id)
+{
+ struct thread_info *new_thread
+ = (struct thread_info *) malloc (sizeof (*new_thread));
+
+ VG_(memset) (new_thread, 0, sizeof (*new_thread));
+
+ new_thread->entry.id = thread_id;
+
+ add_inferior_to_list (&all_threads, & new_thread->entry);
+
+ if (current_inferior == NULL)
+ current_inferior = new_thread;
+
+ new_thread->target_data = target_data;
+ set_inferior_regcache_data (new_thread, new_register_cache ());
+ new_thread->gdb_id = gdb_id;
+}
+
+unsigned int thread_id_to_gdb_id (unsigned long thread_id)
+{
+ struct inferior_list_entry *inf = all_threads.head;
+
+ while (inf != NULL) {
+ struct thread_info *thread = get_thread (inf);
+ if (inf->id == thread_id)
+ return thread->gdb_id;
+ inf = inf->next;
+ }
+
+ return 0;
+}
+
+unsigned int thread_to_gdb_id (struct thread_info *thread)
+{
+ return thread->gdb_id;
+}
+
+struct thread_info * gdb_id_to_thread (unsigned int gdb_id)
+{
+ struct inferior_list_entry *inf = all_threads.head;
+
+ while (inf != NULL) {
+ struct thread_info *thread = get_thread (inf);
+ if (thread->gdb_id == gdb_id)
+ return thread;
+ inf = inf->next;
+ }
+
+ return NULL;
+}
+
+unsigned long gdb_id_to_thread_id (unsigned int gdb_id)
+{
+ struct thread_info *thread = gdb_id_to_thread (gdb_id);
+
+ return thread ? thread->entry.id : 0;
+}
+
+static
+void free_one_thread (struct inferior_list_entry *inf)
+{
+ struct thread_info *thread = get_thread (inf);
+ free_register_cache (inferior_regcache_data (thread));
+ free (thread);
+}
+
+void remove_thread (struct thread_info *thread)
+{
+ remove_inferior (&all_threads, (struct inferior_list_entry *) thread);
+ free_one_thread (&thread->entry);
+}
+
+void clear_inferiors (void)
+{
+ for_each_inferior (&all_threads, free_one_thread);
+
+ all_threads.head = all_threads.tail = NULL;
+}
+
+struct inferior_list_entry * find_inferior (struct inferior_list *list,
+ int (*func)
+ (struct inferior_list_entry *,
+ void *),
+ void *arg)
+{
+ struct inferior_list_entry *inf = list->head;
+
+ while (inf != NULL) {
+ if ((*func) (inf, arg))
+ return inf;
+ inf = inf->next;
+ }
+
+ return NULL;
+}
+
+struct inferior_list_entry * find_inferior_id (struct inferior_list *list,
+ unsigned long id)
+{
+ struct inferior_list_entry *inf = list->head;
+
+ while (inf != NULL) {
+ if (inf->id == id)
+ return inf;
+ inf = inf->next;
+ }
+
+ return NULL;
+}
+
+void * inferior_target_data (struct thread_info *inferior)
+{
+ return inferior->target_data;
+}
+
+void set_inferior_target_data (struct thread_info *inferior, void *data)
+{
+ inferior->target_data = data;
+}
+
+void * inferior_regcache_data (struct thread_info *inferior)
+{
+ return inferior->regcache_data;
+}
+
+void set_inferior_regcache_data (struct thread_info *inferior, void *data)
+{
+ inferior->regcache_data = data;
+}
diff --git a/coregrind/m_gdbserver/m_gdbserver.c b/coregrind/m_gdbserver/m_gdbserver.c
new file mode 100644
index 0000000..cfde922
--- /dev/null
+++ b/coregrind/m_gdbserver/m_gdbserver.c
@@ -0,0 +1,1289 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Handle remote gdb protocol. m_gdbserver.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2011 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 "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_debuglog.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_gdbserver.h"
+#include "pub_core_options.h"
+#include "pub_core_libcsetjmp.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_tool_hashtable.h"
+#include "pub_core_libcassert.h"
+#include "pub_tool_libcbase.h"
+#include "pub_core_libcsignal.h"
+#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry)
+#include "pub_tool_debuginfo.h"
+#include "pub_core_scheduler.h"
+#include "pub_core_syswrap.h"
+
+#include "server.h"
+
+Int VG_(dyn_vgdb_error);
+
+/* forward declarations */
+VG_REGPARM(1)
+void VG_(helperc_CallDebugger) ( HWord iaddr );
+VG_REGPARM(1)
+void VG_(helperc_invalidate_if_not_gdbserved) ( Addr addr );
+static void invalidate_current_ip (ThreadId tid, char *who);
+
+/* reasons of call to call_gdbserver. */
+typedef
+ enum {
+ init_reason, // initialises gdbserver resources
+ vgdb_reason, // gdbserver invocation by vgdb doing ptrace
+ core_reason, // gdbserver invocation by core (e.g. error encountered)
+ break_reason, // break encountered
+ watch_reason, // watchpoint detected by tool
+ signal_reason} // signal encountered
+ CallReason;
+
+static char* ppCallReason(CallReason reason)
+{
+ switch (reason) {
+ case init_reason: return "init_reason";
+ case vgdb_reason: return "vgdb_reason";
+ case core_reason: return "core_reason";
+ case break_reason: return "break_reason";
+ case watch_reason: return "watch_reason";
+ case signal_reason: return "signal_reason";
+ default: vg_assert (0);
+ }
+}
+
+/* An instruction instrumented for gdbserver looks like this:
+ 1. Ist_Mark (0x1234)
+ 2. helperc_CallDebugger (0x1234)
+ This will give control to gdb if there is a break at 0x1234
+ or if we are single stepping
+ 3. ... here the real IR for the instruction at 0x1234
+
+ When there is a break at 0x1234:
+ if user does "continue" or "step" or similar,
+ then - the call to debugger returns
+ - valgrind executes at 3. the real IR(s) for 0x1234
+
+ if as part of helperc_CallDebugger, the user calls
+ some code in gdb e.g print hello_world()
+ then - gdb prepares a dummy stack frame with a specific
+ return address (typically it uses _start) and
+ inserts a break at this address
+ - gdb then puts in EIP the address of hello_world()
+ - gdb then continues (so the helperc_CallDebugger
+ returns)
+ - call_gdbserver() function will then return the
+ control to the scheduler (using VG_MINIMAL_LONGJMP)
+ to allow the block of the new EIP
+ to be executed.
+ - hello_world code is executed.
+ - when hello_world() returns, it returns to
+ _start and encounters the break at _start.
+ - gdb then removes this break, put 0x1234 in EIP
+ and does a "step". This causes to jump from
+ _start to 0x1234, where the call to
+ helperc_CallDebugger is redone.
+ - This is all ok, the user can then give new gdb
+ commands.
+
+ However, when continue is given, address 0x1234 is to
+ be executed: gdb gives a single step, which must not
+ report again the break at 0x1234. To avoid a 2nd report
+ of the same break, the below tells that the next
+ helperc_CallDebugger call must ignore a break/stop at
+ this address.
+*/
+static Addr ignore_this_break_once = 0;
+
+
+static void call_gdbserver ( ThreadId tid , CallReason reason);
+
+/* convert from CORE_ADDR to void* */
+static
+void* C2v(CORE_ADDR addr)
+{
+ return (void*) addr;
+}
+
+/* Describes the address addr (for debugging/printing purposes).
+ Last two results are kept. A third call will replace the
+ oldest result. */
+static char* sym (Addr addr, Bool is_code)
+{
+ static char buf[2][200];
+ static int w = 0;
+ PtrdiffT offset;
+ if (w == 2) w = 0;
+ if (is_code) {
+ VG_(describe_IP) (addr, buf[w], 200);
+ } else {
+ VG_(get_datasym_and_offset) (addr, buf[w], 200, &offset);
+ }
+ return buf[w++];
+}
+
+/* Each time gdbserver is called, gdbserver_called is incremented
+ gdbserver_exited is incremented when gdbserver is asked to exit */
+static int gdbserver_called = 0;
+static int gdbserver_exited = 0;
+
+typedef
+ enum {
+ GS_break,
+ GS_jump
+ }
+ GS_Kind;
+
+typedef
+ struct _GS_Address {
+ struct _GS_Address* next;
+ Addr addr;
+ GS_Kind kind;
+ }
+ GS_Address;
+
+/* gs_addresses contains a list of all addresses that have been invalidated
+ because they have been (or must be) instrumented for gdbserver.
+ An entry is added in this table when there is a break at this
+ address (kind == GS_break) or if this address is the jump target of an
+ exit of a block that has been instrumented for gdbserver while
+ single stepping (kind == GS_jump).
+ When gdbserver is not single stepping anymore, all GS_jump entries
+ are removed, their translations are invalidated.
+*/
+static VgHashTable gs_addresses = NULL;
+
+static void add_gs_address (Addr addr, GS_Kind kind, char* from)
+{
+ GS_Address *p;
+
+ p = VG_(arena_malloc)(VG_AR_CORE, from, sizeof(GS_Address));
+ p->addr = addr;
+ p->kind = kind;
+ VG_(HT_add_node)(gs_addresses, p);
+ VG_(discard_translations) (addr, 1, from);
+}
+
+static void remove_gs_address (GS_Address* g, char* from)
+{
+ VG_(HT_remove) (gs_addresses, g->addr);
+ VG_(discard_translations) (g->addr, 1, from);
+ VG_(arena_free) (VG_AR_CORE, g);
+}
+
+char* VG_(ppPointKind) (PointKind kind)
+{
+ switch(kind) {
+ case software_breakpoint: return "software_breakpoint";
+ case hardware_breakpoint: return "hardware_breakpoint";
+ case write_watchpoint: return "write_watchpoint";
+ case read_watchpoint: return "read_watchpoint";
+ case access_watchpoint: return "access_watchpoint";
+ default: vg_assert(0);
+ }
+}
+
+typedef
+ struct _GS_Watch {
+ struct _GS_Watch* next;
+ Addr addr;
+ SizeT len;
+ PointKind kind;
+ }
+ GS_Watch;
+
+/* gs_watches contains a list of all addresses+len that are being watched. */
+static VgHashTable gs_watches = NULL;
+
+
+/* protocol spec tells the below must be idempotent. */
+static void breakpoint (Bool insert, CORE_ADDR addr)
+{
+ GS_Address *g;
+
+ g = VG_(HT_lookup) (gs_addresses, (UWord)addr);
+ if (insert) {
+ /* insert a breakpoint at addr or upgrade its kind */
+ if (g == NULL) {
+ add_gs_address (addr, GS_break, "m_gdbserver breakpoint insert");
+ } else {
+ /* already gdbserved. Normally, it must be because of a jump.
+ However, due to idempotent or if connection with gdb was
+ lost (kept breaks from the previous gdb), if already existing,
+ we just upgrade its kind. */
+ g->kind = GS_break;
+ }
+ } else {
+ /* delete a breakpoint at addr or downgrade its kind */
+ if (g != NULL && g->kind == GS_break) {
+ if (valgrind_single_stepping()) {
+ /* keep gdbserved instrumentation while single stepping */
+ g->kind = GS_jump;
+ } else {
+ remove_gs_address (g, "m_gdbserver breakpoint remove");
+ }
+ } else {
+ dlog (1, "remove break addr %p %s\n",
+ C2v(addr), (g == NULL ?
+ "NULL" :
+ (g->kind == GS_jump ? "GS_jump" : "GS_break")));
+ }
+ }
+}
+
+static Bool (*tool_watchpoint) (PointKind kind,
+ Bool insert,
+ Addr addr,
+ SizeT len) = NULL;
+void VG_(needs_watchpoint) (Bool (*watchpoint) (PointKind kind,
+ Bool insert,
+ Addr addr,
+ SizeT len))
+{
+ tool_watchpoint = watchpoint;
+}
+
+Bool VG_(gdbserver_point) (PointKind kind, Bool insert,
+ CORE_ADDR addr, int len)
+{
+ Bool res;
+ GS_Watch *g;
+ Bool is_code = kind == software_breakpoint || kind == hardware_breakpoint;
+
+ dlog(1, "%s %s at addr %p %s\n",
+ (insert ? "insert" : "remove"),
+ VG_(ppPointKind) (kind),
+ C2v(addr),
+ sym(addr, is_code));
+
+ if (is_code) {
+ breakpoint (insert, addr);
+ return True;
+ }
+
+ vg_assert (kind == access_watchpoint
+ || kind == read_watchpoint
+ || kind == write_watchpoint);
+
+ if (tool_watchpoint == NULL)
+ return False;
+
+ res = (*tool_watchpoint) (kind, insert, addr, len);
+ if (!res)
+ return False; /* error or unsupported */
+
+ g = VG_(HT_lookup) (gs_watches, (UWord)addr);
+ if (insert) {
+ if (g == NULL) {
+ g = VG_(arena_malloc)(VG_AR_CORE, "gdbserver_point watchpoint",
+ sizeof(GS_Watch));
+ g->addr = addr;
+ g->len = len;
+ g->kind = kind;
+ VG_(HT_add_node)(gs_watches, g);
+ } else {
+ g->kind = kind;
+ }
+ } else {
+ vg_assert (g != NULL);
+ VG_(HT_remove) (gs_watches, g->addr);
+ VG_(arena_free) (VG_AR_CORE, g);
+ }
+ return True;
+}
+
+Bool VG_(is_watched)(PointKind kind, Addr addr, Int szB)
+{
+ GS_Watch* g;
+ Bool watched = False;
+ const ThreadId tid = VG_(running_tid);
+
+ if (!gdbserver_called)
+ return False;
+
+ Addr to = addr + szB; // semi-open interval [addr, to[
+
+ vg_assert (kind == access_watchpoint
+ || kind == read_watchpoint
+ || kind == write_watchpoint);
+ dlog(1, "tid %d VG_(is_watched) %s addr %p szB %d\n",
+ tid, VG_(ppPointKind) (kind), C2v(addr), szB);
+ VG_(HT_ResetIter) (gs_watches);
+ while ((g = VG_(HT_Next) (gs_watches))) {
+ switch (g->kind) {
+ case software_breakpoint:
+ case hardware_breakpoint:
+ break;
+ case access_watchpoint:
+ case read_watchpoint:
+ case write_watchpoint:
+ if (to <= g->addr || addr >= (g->addr + g->len))
+ /* If no overlap, examine next watchpoint: */
+ continue;
+
+ watched = True; /* We have an overlap */
+
+ /* call gdbserver if access kind reported by the tool
+ matches the watchpoint kind. */
+ if (kind == access_watchpoint
+ || g->kind == access_watchpoint
+ || g->kind == kind) {
+ /* Watchpoint encountered.
+ If this is a read watchpoint, we directly call gdbserver
+ to report it to gdb.
+ Otherwise, for a write watchpoint, we have to finish
+ the instruction so as to modify the value.
+ If we do not finish the instruction, then gdb sees no
+ value change and continues.
+ For a read watchpoint, we better call gdbserver directly:
+ in case the current block is not gdbserved, Valgrind
+ will execute instructions till the next block. */
+
+ /* set the watchpoint stop address to the first read or written. */
+ if (g->addr <= addr) {
+ VG_(set_watchpoint_stop_address) (addr);
+ } else {
+ VG_(set_watchpoint_stop_address) (g->addr);
+ }
+
+ if (kind == write_watchpoint) {
+ /* Let Valgrind stop as early as possible after this instruction
+ by switching to Single Stepping mode. */
+ valgrind_set_single_stepping (True);
+ invalidate_current_ip (tid, "m_gdbserver write watchpoint");
+ } else {
+ call_gdbserver (tid, watch_reason);
+ VG_(set_watchpoint_stop_address) ((Addr) 0);
+ }
+ return True; // we are watched here.
+ }
+ break;
+ default:
+ vg_assert (0);
+ }
+ }
+ return watched;
+}
+
+/* Returns the reason for which gdbserver instrumentation is needed */
+static VgVgdb VG_(gdbserver_instrumentation_needed) (VexGuestExtents* vge)
+{
+ GS_Address* g;
+ int e;
+
+ if (!gdbserver_called)
+ return Vg_VgdbNo;
+
+ if (valgrind_single_stepping()) {
+ dlog(2, "gdbserver_instrumentation_needed due to single stepping\n");
+ return Vg_VgdbYes;
+ }
+
+ if (VG_(clo_vgdb) == Vg_VgdbYes && VG_(HT_count_nodes) (gs_addresses) == 0)
+ return Vg_VgdbNo;
+
+ /* We assume we do not have a huge nr of breakpoints.
+ Otherwise, we need something more efficient e.g.
+ a sorted list of breakpoints or associate extents to it or ...
+ */
+ VG_(HT_ResetIter) (gs_addresses);
+ while ((g = VG_(HT_Next) (gs_addresses))) {
+ for (e = 0; e < vge->n_used; e++) {
+ if (g->addr >= vge->base[e] && g->addr < vge->base[e] + vge->len[e]) {
+ dlog(2,
+ "gdbserver_instrumentation_needed %p %s reason %s\n",
+ C2v(g->addr), sym(g->addr, /* is_code */ True),
+ (g->kind == GS_jump ? "GS_jump" : "GS_break"));
+ return Vg_VgdbYes;
+ }
+ }
+ }
+
+ if (VG_(clo_vgdb) == Vg_VgdbFull) {
+ dlog(4, "gdbserver_instrumentation_needed"
+ " due to VG_(clo_vgdb) == Vg_VgdbFull\n");
+ return Vg_VgdbFull;
+ }
+
+
+ return Vg_VgdbNo;
+}
+
+// Clear gdbserved_addresses in gs_addresses.
+// If clear_only_jumps, clears only the addresses that are served
+// for jump reasons.
+// Otherwise, clear all the addresses.
+// Cleared addresses are invalidated so as to have them re-translated.
+static void clear_gdbserved_addresses(Bool clear_only_jumps)
+{
+ GS_Address** ag;
+ UInt n_elems;
+ int i;
+
+ dlog(1,
+ "clear_gdbserved_addresses: scanning hash table nodes %d\n",
+ VG_(HT_count_nodes) (gs_addresses));
+ ag = (GS_Address**) VG_(HT_to_array) (gs_addresses, &n_elems);
+ for (i = 0; i < n_elems; i++)
+ if (!clear_only_jumps || ag[i]->kind == GS_jump)
+ remove_gs_address (ag[i], "clear_gdbserved_addresses");
+ VG_(free) (ag);
+}
+
+// Clear watched addressed in gs_watches
+static void clear_watched_addresses(void)
+{
+ GS_Watch** ag;
+ UInt n_elems;
+ int i;
+
+ dlog(1,
+ "clear_watched_addresses: scanning hash table nodes %d\n",
+ VG_(HT_count_nodes) (gs_watches));
+ ag = (GS_Watch**) VG_(HT_to_array) (gs_watches, &n_elems);
+ for (i = 0; i < n_elems; i++) {
+ if (!VG_(gdbserver_point) (ag[i]->kind,
+ /* insert */ False,
+ ag[i]->addr,
+ ag[i]->len)) {
+ vg_assert (0);
+ }
+ }
+ VG_(free) (ag);
+}
+
+static void invalidate_if_jump_not_yet_gdbserved (Addr addr, char* from)
+{
+ if (VG_(HT_lookup) (gs_addresses, (UWord)addr))
+ return;
+ add_gs_address (addr, GS_jump, from);
+}
+
+static void invalidate_current_ip (ThreadId tid, char *who)
+{
+ invalidate_if_jump_not_yet_gdbserved (VG_(get_IP) (tid), who);
+}
+
+/* when fork is done, various cleanup is needed in the child process.
+ In particular, child must have its own connection to avoid stealing
+ data from its parent */
+static void gdbserver_cleanup_in_child_after_fork(ThreadId me)
+{
+ dlog(1, "thread %d gdbserver_cleanup_in_child_after_fork pid %d\n",
+ me, VG_(getpid) ());
+
+ /* finish connection inheritated from parent */
+ remote_finish(reset_after_fork);
+
+ /* ensure next call to gdbserver will be considered as a brand
+ new call that will initialize a fresh gdbserver. */
+ if (gdbserver_called) {
+ gdbserver_called = 0;
+ vg_assert (gs_addresses != NULL);
+ vg_assert (gs_watches != NULL);
+ clear_gdbserved_addresses(/* clear only jumps */ False);
+ VG_(HT_destruct) (gs_addresses);
+ gs_addresses = NULL;
+ clear_watched_addresses();
+ VG_(HT_destruct) (gs_watches);
+ gs_watches = NULL;
+ } else {
+ vg_assert (gs_addresses == NULL);
+ vg_assert (gs_watches == NULL);
+ }
+}
+
+/* If reason is init_reason, creates the connection resources (e.g.
+ the FIFOs) to allow a gdb connection to be detected by polling
+ using remote_desc_activity.
+ Otherwise (other reasons):
+ If connection with gdb not yet opened, opens the connection with gdb.
+ reads gdb remote protocol packets and executes the requested commands.
+*/
+static void call_gdbserver ( ThreadId tid , CallReason reason)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ int stepping;
+ Addr saved_pc;
+
+ dlog(1,
+ "entering call_gdbserver %s ... pid %d tid %d status %s "
+ "sched_jmpbuf_valid %d\n",
+ ppCallReason (reason),
+ VG_(getpid) (), tid, VG_(name_of_ThreadStatus)(tst->status),
+ tst->sched_jmpbuf_valid);
+
+ vg_assert(VG_(is_valid_tid)(tid));
+ saved_pc = VG_(get_IP) (tid);
+
+ if (gdbserver_exited) {
+ dlog(0, "call_gdbserver called when gdbserver_exited %d\n",
+ gdbserver_exited);
+ return;
+ }
+
+ if (gdbserver_called == 0) {
+ vg_assert (gs_addresses == NULL);
+ vg_assert (gs_watches == NULL);
+ gs_addresses = VG_(HT_construct)( "gdbserved_addresses" );
+ gs_watches = VG_(HT_construct)( "gdbserved_watches" );
+ VG_(atfork)(NULL, NULL, gdbserver_cleanup_in_child_after_fork);
+ }
+ vg_assert (gs_addresses != NULL);
+ vg_assert (gs_watches != NULL);
+
+ gdbserver_called++;
+
+ /* call gdbserver_init if this is the first call to gdbserver. */
+ if (gdbserver_called == 1)
+ gdbserver_init();
+
+ if (reason == init_reason || gdbserver_called == 1)
+ remote_open(VG_(clo_vgdb_prefix));
+
+ /* if the call reason is to initialize, then return control to
+ valgrind. After this initialization, gdbserver will be called
+ again either if there is an error detected by valgrind or
+ if vgdb sends data to the valgrind process. */
+ if (reason == init_reason) {
+ return;
+ }
+
+ stepping = valgrind_single_stepping();
+
+ server_main();
+
+ ignore_this_break_once = valgrind_get_ignore_break_once();
+ if (ignore_this_break_once)
+ dlog(1, "!!! will ignore_this_break_once %s\n",
+ sym(ignore_this_break_once, /* is_code */ True));
+
+
+ if (valgrind_single_stepping()) {
+ /* we are single stepping. If we were not stepping on entry,
+ then invalidate the current program counter so as to properly
+ do single step. In case the program counter was changed by
+ gdb, this will also invalidate the target address we will
+ jump to. */
+ if (!stepping && tid != 0) {
+ invalidate_current_ip (tid, "m_gdbserver single step");
+ }
+ } else {
+ /* We are not single stepping. If we were stepping on entry,
+ then clear the gdbserved addresses. This will cause all
+ these gdbserved blocks to be invalidated so that they can be
+ re-translated without being gdbserved. */
+ if (stepping)
+ clear_gdbserved_addresses(/* clear only jumps */ True);
+ }
+
+ /* can't do sanity check at beginning. At least the stack
+ check is not yet possible. */
+ if (gdbserver_called > 1)
+ VG_(sanity_check_general) (/* force_expensive */ False);
+
+ /* If the PC has been changed by gdb, then we VG_MINIMAL_LONGJMP to
+ the scheduler to execute the block of the new PC.
+ Otherwise we just return to continue executing the
+ current block. */
+ if (VG_(get_IP) (tid) != saved_pc) {
+ dlog(1, "tid %d %s PC changed from %s to %s\n",
+ tid, VG_(name_of_ThreadStatus) (tst->status),
+ sym(saved_pc, /* is_code */ True),
+ sym(VG_(get_IP) (tid), /* is_code */ True));
+ if (tst->status == VgTs_Yielding) {
+ SysRes sres;
+ VG_(memset)(&sres, 0, sizeof(SysRes));
+ VG_(acquire_BigLock)(tid, "gdbsrv VG_MINIMAL_LONGJMP");
+ }
+ if (tst->sched_jmpbuf_valid) {
+ /* resume scheduler */
+ VG_MINIMAL_LONGJMP(tst->sched_jmpbuf);
+ }
+ /* else continue to run */
+ }
+ /* continue to run */
+}
+
+/* busy > 0 when gdbserver is currently being called.
+ busy is used to to avoid vgdb invoking gdbserver
+ while gdbserver by Valgrind. */
+static volatile int busy = 0;
+
+void VG_(gdbserver) ( ThreadId tid )
+{
+ busy++;
+ /* called by the rest of valgrind for
+ --vgdb-error=0 reason
+ or by scheduler "poll/debug/interrupt" reason
+ or to terminate. */
+ if (tid != 0) {
+ call_gdbserver (tid, core_reason);
+ } else {
+ if (gdbserver_called == 0) {
+ dlog(1, "VG_(gdbserver) called to terminate, nothing to terminate\n");
+ } else if (gdbserver_exited) {
+ dlog(0, "VG_(gdbserver) called to terminate again %d\n",
+ gdbserver_exited);
+ } else {
+ gdbserver_terminate();
+ gdbserver_exited++;
+ }
+ }
+ busy--;
+}
+
+// nr of invoke_gdbserver while gdbserver is already executing.
+static int interrupts_while_busy = 0;
+
+// nr of invoke_gdbserver while gdbserver is not executing.
+static int interrupts_non_busy = 0;
+
+// nr of invoke_gdbserver when some threads are not interruptible.
+static int interrupts_non_interruptible = 0;
+
+/* When all threads are blocked in a system call, the Valgrind
+ scheduler cannot poll the shared memory for gdbserver activity. In
+ such a case, vgdb will force the invokation of gdbserver using
+ ptrace. To do that, vgdb 'pushes' a call to invoke_gdbserver
+ on the stack using ptrace. invoke_gdbserver must not return.
+ Instead, it must call give_control_back_to_vgdb.
+ vgdb expects to receive a SIGTRAP, which this function generates.
+ When vgdb gets this SIGTRAP, it knows invoke_gdbserver call
+ is finished and can reset the Valgrind process in the state prior to
+ the 'pushed call' (using ptrace again).
+ This all works well. However, the user must avoid
+ 'kill-9ing' vgdb during such a pushed call, otherwise
+ the SIGTRAP generated below will be seen by the Valgrind core,
+ instead of being handled by vgdb. When the Valgrind core gets
+ such a SIGTRAP, it will assert. */
+
+static void give_control_back_to_vgdb(void)
+{
+ /* cause a SIGTRAP to be sent to ourself, so that vgdb takes control.
+ vgdb will then restore the stack so as to resume the activity
+ before the ptrace (typically do_syscall_WRK). */
+ if (VG_(kill)(VG_(getpid)(), VKI_SIGTRAP) != 0)
+ vg_assert2(0, "SIGTRAP for vgdb could not be generated\n");
+
+ /* If we arrive here, it means a call was pushed on the stack
+ by vgdb, but during this call, vgdb and/or connection
+ died. Alternatively, it is a bug in the vgdb<=>Valgrind gdbserver
+ ptrace handling. */
+ vg_assert2(0,
+ "vgdb did not took control. Did you kill vgdb ?\n"
+ "busy %d vgdb_interrupted_tid %d\n",
+ busy, vgdb_interrupted_tid);
+}
+
+/* Using ptrace calls, vgdb will force an invocation of gdbserver.
+ VG_(invoke_gdbserver) is the entry point called through the
+ vgdb ptrace technique. */
+void VG_(invoke_gdbserver) ( int check )
+{
+ /* ******* Avoid non-reentrant function call from here .....
+ till the ".... till here" below. */
+
+ /* We need to determine the state of the various threads to decide
+ if we directly invoke gdbserver or if we rather indicate to the
+ scheduler to invoke the gdbserver. To decide that, it is
+ critical to avoid any "coregrind" function call as the ptrace
+ might have stopped the process in the middle of this (possibly)
+ non-rentrant function. So, it is only when all threads are in
+ an "interruptible" state that we can safely invoke
+ gdbserver. Otherwise, we let the valgrind scheduler invoke
+ gdbserver at the next poll. This poll will be made very soon
+ thanks to a call to VG_(force_vgdb_poll). */
+ int n_tid;
+
+ vg_assert (check == 0x8BADF00D);
+
+ if (busy) {
+ interrupts_while_busy++;
+ give_control_back_to_vgdb();
+ }
+ interrupts_non_busy++;
+
+ /* check if all threads are in an "interruptible" state. If yes,
+ we invoke gdbserver. Otherwise, we tell the scheduler to wake up
+ asap. */
+ for (n_tid = 1; n_tid < VG_N_THREADS; n_tid++) {
+ switch (VG_(threads)[n_tid].status) {
+ /* interruptible states. */
+ case VgTs_WaitSys:
+ case VgTs_Yielding:
+ if (vgdb_interrupted_tid == 0) vgdb_interrupted_tid = n_tid;
+ break;
+
+ case VgTs_Empty:
+ case VgTs_Zombie:
+ break;
+
+ /* non interruptible states. */
+ case VgTs_Init:
+ case VgTs_Runnable:
+ interrupts_non_interruptible++;
+ VG_(force_vgdb_poll) ();
+ give_control_back_to_vgdb();
+
+ default: vg_assert(0);
+ }
+ }
+
+ /* .... till here.
+ From here onwards, function calls are ok: it is
+ safe to call valgrind core functions: all threads are blocked in
+ a system call or are yielding or ... */
+ dlog(1, "invoke_gdbserver running_tid %d vgdb_interrupted_tid %d\n",
+ VG_(running_tid), vgdb_interrupted_tid);
+ call_gdbserver (vgdb_interrupted_tid, vgdb_reason);
+ vgdb_interrupted_tid = 0;
+ dlog(1,
+ "exit invoke_gdbserver running_tid %d\n", VG_(running_tid));
+ give_control_back_to_vgdb();
+
+ vg_assert2(0, "end of invoke_gdbserver reached");
+
+}
+
+Bool VG_(gdbserver_activity) (ThreadId tid)
+{
+ Bool ret;
+ busy++;
+ if (!gdbserver_called)
+ call_gdbserver (tid, init_reason);
+ switch (remote_desc_activity("VG_(gdbserver_activity)")) {
+ case 0: ret = False; break;
+ case 1: ret = True; break;
+ case 2: call_gdbserver (tid, init_reason); ret = False; break;
+ default: vg_assert (0);
+ }
+ busy--;
+ return ret;
+}
+
+Bool VG_(gdbserver_report_signal) (Int sigNo, ThreadId tid)
+{
+ dlog(1, "signal %d tid %d\n", sigNo, tid);
+
+ /* if gdbserver is currently not connected, then signal
+ is to be given to the process */
+ if (!remote_connected()) {
+ dlog(1, "not connected => pass\n");
+ return True;
+ }
+ if (pass_signals[sigNo]) {
+ dlog(1, "pass_signals => pass\n");
+ return False;
+ }
+
+ /* indicate to gdbserver that there is a signal */
+ gdbserver_signal_encountered (sigNo);
+
+ /* let gdbserver do some work, e.g. show the signal to the user */
+ call_gdbserver (tid, signal_reason);
+
+ /* ask gdbserver what is the final decision */
+ if (gdbserver_deliver_signal (sigNo)) {
+ dlog(1, "gdbserver deliver signal\n");
+ return True;
+ } else {
+ dlog(1, "gdbserver ignore signal\n");
+ return False;
+ }
+}
+
+// Check if single_stepping or if there is a break requested at iaddr.
+// If yes, call debugger
+VG_REGPARM(1)
+void VG_(helperc_CallDebugger) ( HWord iaddr )
+{
+ GS_Address* g;
+
+ // For Vg_VgdbFull, after a fork, we might have calls to this helper
+ // while gdbserver is not yet initialized.
+ if (!gdbserver_called)
+ return;
+
+ if (valgrind_single_stepping() ||
+ ((g = VG_(HT_lookup) (gs_addresses, (UWord)iaddr)) &&
+ (g->kind == GS_break))) {
+ if (iaddr == ignore_this_break_once) {
+ dlog(1, "ignoring ignore_this_break_once %s\n",
+ sym(ignore_this_break_once, /* is_code */ True));
+ ignore_this_break_once = 0;
+ } else {
+ call_gdbserver (VG_(get_running_tid)(), break_reason);
+ }
+ }
+}
+
+/* software_breakpoint support --------------------------------------*/
+/* When a block is instrumented for gdbserver, single step and breaks
+ will be obeyed in this block. However, if a jump to another block
+ is executed while single_stepping is active, we must ensure that
+ this block is also instrumented. For this, when a block is
+ instrumented for gdbserver while single_stepping, the target of all
+ the Jump instructions in this block will be checked to verify if
+ the block is already instrumented for gdbserver. The below will
+ ensure that if not already instrumented for gdbserver, the target
+ block translation containing addr will be invalidated. The list of
+ gdbserved Addr will also be kept so that translations can be
+ dropped automatically by gdbserver when going out of single step
+ mode.
+
+ Call the below at translation time if the jump target is a constant.
+ Otherwise, rather use VG_(add_stmt_call_invalidate_if_not_gdbserved).
+
+ To instrument the target exit statement, you can call
+ VG_(add_stmt_call_invalidate_exit_target_if_not_gdbserved) rather
+ than check the kind of target exit. */
+static void VG_(invalidate_if_not_gdbserved) (Addr addr)
+{
+ if (valgrind_single_stepping())
+ invalidate_if_jump_not_yet_gdbserved
+ (addr, "gdbserver target jump (instrument)");
+}
+
+// same as VG_(invalidate_if_not_gdbserved) but is intended to be called
+// at runtime (only difference is the invalidate reason which traces
+// it is at runtime)
+VG_REGPARM(1)
+void VG_(helperc_invalidate_if_not_gdbserved) ( Addr addr )
+{
+ if (valgrind_single_stepping())
+ invalidate_if_jump_not_yet_gdbserved
+ (addr, "gdbserver target jump (runtime)");
+}
+
+static void VG_(add_stmt_call_invalidate_if_not_gdbserved)
+ ( IRSB* sb_in,
+ VexGuestLayout* layout,
+ VexGuestExtents* vge,
+ IRTemp jmp,
+ IRSB* irsb)
+{
+
+ void* fn;
+ HChar* nm;
+ IRExpr** args;
+ Int nargs;
+ IRDirty* di;
+
+ fn = &VG_(helperc_invalidate_if_not_gdbserved);
+ nm = "VG_(helperc_invalidate_if_not_gdbserved)";
+ args = mkIRExprVec_1(IRExpr_RdTmp (jmp));
+ nargs = 1;
+
+ di = unsafeIRDirty_0_N( nargs/*regparms*/, nm,
+ VG_(fnptr_to_fnentry)( fn ), args );
+
+ di->nFxState = 0;
+
+ addStmtToIRSB(irsb, IRStmt_Dirty(di));
+}
+
+/* software_breakpoint support --------------------------------------*/
+/* If a tool wants to allow gdbserver to do something at Addr, then
+ VG_(add_stmt_call_gdbserver) will add in IRSB a call to a helper
+ function. This helper function will check if the process must be
+ stopped at the instruction Addr: either there is a break at Addr or
+ the process is being single-stepped. Typical usage of the below is to
+ instrument an Ist_IMark to allow the debugger to interact at any
+ instruction being executed. As soon as there is one break in a block,
+ then to allow single stepping in this block (and possible insertions
+ of other breaks in the same sb_in while the process is stopped), a
+ debugger statement will be inserted for all instructions of a block. */
+static void VG_(add_stmt_call_gdbserver)
+ (IRSB* sb_in, /* block being translated */
+ VexGuestLayout* layout,
+ VexGuestExtents* vge,
+ IRType gWordTy, IRType hWordTy,
+ Addr iaddr, /* Addr of instruction being instrumented */
+ IRSB* irsb) /* irsb block to which call is added */
+{
+ void* fn;
+ HChar* nm;
+ IRExpr** args;
+ Int nargs;
+ IRDirty* di;
+
+ /* first store the address in the program counter so that the check
+ done by VG_(helperc_CallDebugger) will be based on the correct
+ program counter. We might make this more efficient by rather
+ searching for assignement to program counter and instrumenting
+ that but the below is easier and I guess that the optimiser will
+ remove the redundant store. And in any case, when debugging a
+ piece of code, the efficiency requirement is not critical: very
+ few blocks will be instrumented for debugging. */
+
+ addStmtToIRSB(irsb, IRStmt_Put(layout->offset_IP , mkIRExpr_HWord(iaddr)));
+
+ fn = &VG_(helperc_CallDebugger);
+ nm = "VG_(helperc_CallDebugger)";
+ args = mkIRExprVec_1(mkIRExpr_HWord (iaddr));
+ nargs = 1;
+
+ di = unsafeIRDirty_0_N( nargs/*regparms*/, nm,
+ VG_(fnptr_to_fnentry)( fn ), args );
+
+ /* Note: in fact, a debugger call can read whatever register
+ or memory. It can also write whatever register or memory.
+ So, in theory, we have to indicate the whole universe
+ can be read and modified. It is however not critical
+ to indicate precisely what is being read/written
+ as such indications are needed for tool error detection
+ and we do not want to have errors being detected for
+ gdb interactions. */
+
+ di->nFxState = 2;
+ di->fxState[0].fx = Ifx_Read;
+ di->fxState[0].offset = layout->offset_SP;
+ di->fxState[0].size = layout->sizeof_SP;
+ di->fxState[1].fx = Ifx_Modify;
+ di->fxState[1].offset = layout->offset_IP;
+ di->fxState[1].size = layout->sizeof_IP;
+
+ addStmtToIRSB(irsb, IRStmt_Dirty(di));
+
+}
+
+
+/* Invalidate the target of the exit if needed:
+ If target is constant, it is invalidated at translation time.
+ Otherwise, a call to a helper function is generated to invalidate
+ the translation at run time.
+ The below is thus calling either VG_(invalidate_if_not_gdbserved)
+ or VG_(add_stmt_call_invalidate_if_not_gdbserved). */
+static void VG_(add_stmt_call_invalidate_exit_target_if_not_gdbserved)
+ (IRSB* sb_in,
+ VexGuestLayout* layout,
+ VexGuestExtents* vge,
+ IRType gWordTy,
+ IRSB* irsb)
+{
+ if (sb_in->next->tag == Iex_Const) {
+ VG_(invalidate_if_not_gdbserved) (gWordTy == Ity_I64 ?
+ sb_in->next->Iex.Const.con->Ico.U64
+ : sb_in->next->Iex.Const.con->Ico.U32);
+ } else if (sb_in->next->tag == Iex_RdTmp) {
+ VG_(add_stmt_call_invalidate_if_not_gdbserved)
+ (sb_in, layout, vge, sb_in->next->Iex.RdTmp.tmp, irsb);
+ } else {
+ vg_assert (0); /* unexpected expression tag in exit. */
+ }
+}
+
+IRSB* VG_(instrument_for_gdbserver_if_needed)
+ (IRSB* sb_in,
+ VexGuestLayout* layout,
+ VexGuestExtents* vge,
+ IRType gWordTy, IRType hWordTy)
+{
+ IRSB* sb_out;
+ Int i;
+ const VgVgdb instr_needed = VG_(gdbserver_instrumentation_needed) (vge);
+
+ if (instr_needed == Vg_VgdbNo)
+ return sb_in;
+
+
+ /* here, we need to instrument for gdbserver */
+ sb_out = deepCopyIRSBExceptStmts(sb_in);
+
+ for (i = 0; i < sb_in->stmts_used; i++) {
+ IRStmt* st = sb_in->stmts[i];
+
+ if (!st || st->tag == Ist_NoOp) continue;
+
+ if (st->tag == Ist_Exit && instr_needed == Vg_VgdbYes) {
+ VG_(invalidate_if_not_gdbserved)
+ (hWordTy == Ity_I64 ?
+ st->Ist.Exit.dst->Ico.U64 :
+ st->Ist.Exit.dst->Ico.U32);
+ }
+ addStmtToIRSB( sb_out, st );
+ if (st->tag == Ist_IMark) {
+ /* For an Ist_Mark, add a call to debugger. */
+ switch (instr_needed) {
+ case Vg_VgdbNo: vg_assert (0);
+ case Vg_VgdbYes:
+ case Vg_VgdbFull:
+ VG_(add_stmt_call_gdbserver) ( sb_in, layout, vge,
+ gWordTy, hWordTy,
+ st->Ist.IMark.addr,
+ sb_out);
+ /* There is an optimisation possible here for Vg_VgdbFull:
+ Put a guard ensuring we only call gdbserver if 'FullCallNeeded'.
+ FullCallNeeded would be set to 1 we have just switched on
+ Single Stepping or have just encountered a watchpoint
+ or have just inserted a breakpoint.
+ (as gdb by default removes and re-insert breakpoints), we would
+ need to also implement the notion of 'breakpoint pending removal'
+ to remove at the next 'continue/step' packet. */
+ break;
+ default: vg_assert (0);
+ }
+ }
+ }
+
+ if (instr_needed == Vg_VgdbYes) {
+ VG_(add_stmt_call_invalidate_exit_target_if_not_gdbserved) (sb_in,
+ layout, vge,
+ gWordTy,
+ sb_out);
+ }
+
+ return sb_out;
+}
+
+struct mon_out_buf {
+ char buf[DATASIZ+1];
+ int next;
+ UInt ret;
+};
+
+static void mon_out (HChar c, void *opaque)
+{
+ struct mon_out_buf *b = (struct mon_out_buf *) opaque;
+ b->ret++;
+ b->buf[b->next] = c;
+ b->next++;
+ if (b->next == DATASIZ) {
+ b->buf[b->next] = '\0';
+ monitor_output(b->buf);
+ b->next = 0;
+ }
+}
+UInt VG_(gdb_printf) ( const HChar *format, ... )
+{
+ struct mon_out_buf b;
+
+ b.next = 0;
+ b.ret = 0;
+
+ va_list vargs;
+ va_start(vargs, format);
+ VG_(vcbprintf) (mon_out, &b, format, vargs);
+ va_end(vargs);
+
+ if (b.next > 0) {
+ b.buf[b.next] = '\0';
+ monitor_output(b.buf);
+ }
+ return b.ret;
+}
+
+Int VG_(keyword_id) (Char* keywords, Char* input_word, kwd_report_error report)
+{
+ const Int il = (input_word == NULL ? 0 : VG_(strlen) (input_word));
+ Char iw[il+1];
+ Char kwds[VG_(strlen)(keywords)+1];
+ Char *kwdssaveptr;
+
+ Char* kw; /* current keyword, its length, its position */
+ Int kwl;
+ Int kpos = -1;
+
+ Int pass;
+ /* pass 0 = search, optional pass 1 = output message multiple matches */
+
+ Int pass1needed = 0;
+
+ Int partial_match = -1;
+ Int full_match = -1;
+
+ if (input_word == NULL) {
+ iw[0] = 0;
+ partial_match = 0; /* to force an empty string to cause an error */
+ } else {
+ VG_(strcpy) (iw, input_word);
+ }
+
+ for (pass = 0; pass < 2; pass++) {
+ VG_(strcpy) (kwds, keywords);
+ if (pass == 1)
+ VG_(gdb_printf) ("%s can match",
+ (il == 0 ? "<empty string>" : (char *) iw));
+ for (kw = VG_(strtok_r) (kwds, " ", &kwdssaveptr);
+ kw != NULL;
+ kw = VG_(strtok_r) (NULL, " ", &kwdssaveptr)) {
+ kwl = VG_(strlen) (kw);
+ kpos++;
+
+ if (il > kwl) {
+ ; /* ishtar !~ is */
+ } else if (il == kwl) {
+ if (VG_(strcmp) (kw, iw) == 0) {
+ /* exact match */
+ if (pass == 1)
+ VG_(gdb_printf) (" %s", kw);
+ if (full_match != -1)
+ pass1needed++;
+ full_match = kpos;
+ }
+ } else {
+ /* il < kwl */
+ if (VG_(strncmp) (iw, kw, il) == 0) {
+ /* partial match */
+ if (pass == 1)
+ VG_(gdb_printf) (" %s", kw);
+ if (partial_match != -1)
+ pass1needed++;
+ partial_match = kpos;
+ }
+ }
+ }
+ /* check for success or for no match at all */
+ if (pass1needed == 0) {
+ if (full_match != -1) {
+ return full_match;
+ } else {
+ if (report == kwd_report_all && partial_match == -1) {
+ VG_(gdb_printf) ("%s does not match any of '%s'\n",
+ iw, keywords);
+ }
+ return partial_match;
+ }
+ }
+
+ /* here we have duplicated match error */
+ if (pass == 1 || report == kwd_report_none) {
+ if (report != kwd_report_none) {
+ VG_(gdb_printf) ("\n");
+ }
+ if (partial_match != -1 || full_match != -1)
+ return -2;
+ else
+ return -1;
+ }
+ }
+ /* UNREACHED */
+ vg_assert (0);
+}
+
+/* True if string can be a 0x number */
+static Bool is_zero_x (Char *s)
+{
+ if (strlen (s) >= 3 && s[0] == '0' && s[1] == 'x')
+ return True;
+ else
+ return False;
+}
+
+/* True if string can be a 0b number */
+static Bool is_zero_b (Char *s)
+{
+ if (strlen (s) >= 3 && s[0] == '0' && s[1] == 'b')
+ return True;
+ else
+ return False;
+}
+
+void VG_(strtok_get_address_and_size) (Addr* address,
+ SizeT* szB,
+ Char **ssaveptr)
+{
+ Char* wa;
+ Char* ws;
+ Char* endptr;
+ UChar *ppc;
+
+ wa = VG_(strtok_r) (NULL, " ", ssaveptr);
+ ppc = wa;
+ if (ppc == NULL || !VG_(parse_Addr) (&ppc, address)) {
+ VG_(gdb_printf) ("missing or malformed address\n");
+ *address = (Addr) 0;
+ *szB = 0;
+ return;
+ }
+ ws = VG_(strtok_r) (NULL, " ", ssaveptr);
+ if (ws == NULL) {
+ /* Do nothing, i.e. keep current value of szB. */ ;
+ } else if (is_zero_x (ws)) {
+ *szB = VG_(strtoull16) (ws, &endptr);
+ } else if (is_zero_b (ws)) {
+ Int j;
+ Char *parsews = ws;
+ Int n_bits = VG_(strlen) (ws) - 2;
+ *szB = 0;
+ ws = NULL; // assume the below loop gives a correct nr.
+ for (j = 0; j < n_bits; j++) {
+ if ('0' == parsews[j+2]) { /* do nothing */ }
+ else if ('1' == parsews[j+2]) *szB |= (1 << (n_bits-j-1));
+ else {
+ /* report malformed binary integer */
+ ws = parsews;
+ endptr = ws + j + 2;
+ break;
+ }
+ }
+ } else {
+ *szB = VG_(strtoull10) (ws, &endptr);
+ }
+
+ if (ws != NULL && *endptr != '\0') {
+ VG_(gdb_printf) ("malformed integer, expecting "
+ "hex 0x..... or dec ...... or binary .....b\n");
+ *address = (Addr) 0;
+ *szB = 0;
+ return;
+ }
+}
+
+void VG_(gdbserver_status_output)(void)
+{
+ const int nr_gdbserved_addresses
+ = (gs_addresses == NULL ? -1 : VG_(HT_count_nodes) (gs_addresses));
+ const int nr_watchpoints
+ = (gs_watches == NULL ? -1 : VG_(HT_count_nodes) (gs_watches));
+ remote_utils_output_status();
+ VG_(umsg)
+ ("nr of calls to gdbserver: %d\n"
+ "single stepping %d\n"
+ "interrupts intr_tid %d gs_non_busy %d gs_busy %d tid_non_intr %d\n"
+ "gdbserved addresses %d (-1 = not initialized)\n"
+ "watchpoints %d (-1 = not initialized)\n"
+ "vgdb-error %d\n",
+ gdbserver_called,
+ valgrind_single_stepping(),
+
+ vgdb_interrupted_tid,
+ interrupts_non_busy,
+ interrupts_while_busy,
+ interrupts_non_interruptible,
+
+ nr_gdbserved_addresses,
+ nr_watchpoints,
+ VG_(dyn_vgdb_error));
+}
diff --git a/coregrind/m_gdbserver/power-altivec-valgrind-s1.xml b/coregrind/m_gdbserver/power-altivec-valgrind-s1.xml
new file mode 100644
index 0000000..8073622
--- /dev/null
+++ b/coregrind/m_gdbserver/power-altivec-valgrind-s1.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.altivec-valgrind-s1">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <union id="vec128">
+ <field name="uint128" type="uint128"/>
+ <field name="v4_float" type="v4f"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v16_int8" type="v16i8"/>
+ </union>
+
+ <reg name="vr0s1" bitsize="128" type="vec128"/>
+ <reg name="vr1s1" bitsize="128" type="vec128"/>
+ <reg name="vr2s1" bitsize="128" type="vec128"/>
+ <reg name="vr3s1" bitsize="128" type="vec128"/>
+ <reg name="vr4s1" bitsize="128" type="vec128"/>
+ <reg name="vr5s1" bitsize="128" type="vec128"/>
+ <reg name="vr6s1" bitsize="128" type="vec128"/>
+ <reg name="vr7s1" bitsize="128" type="vec128"/>
+ <reg name="vr8s1" bitsize="128" type="vec128"/>
+ <reg name="vr9s1" bitsize="128" type="vec128"/>
+ <reg name="vr10s1" bitsize="128" type="vec128"/>
+ <reg name="vr11s1" bitsize="128" type="vec128"/>
+ <reg name="vr12s1" bitsize="128" type="vec128"/>
+ <reg name="vr13s1" bitsize="128" type="vec128"/>
+ <reg name="vr14s1" bitsize="128" type="vec128"/>
+ <reg name="vr15s1" bitsize="128" type="vec128"/>
+ <reg name="vr16s1" bitsize="128" type="vec128"/>
+ <reg name="vr17s1" bitsize="128" type="vec128"/>
+ <reg name="vr18s1" bitsize="128" type="vec128"/>
+ <reg name="vr19s1" bitsize="128" type="vec128"/>
+ <reg name="vr20s1" bitsize="128" type="vec128"/>
+ <reg name="vr21s1" bitsize="128" type="vec128"/>
+ <reg name="vr22s1" bitsize="128" type="vec128"/>
+ <reg name="vr23s1" bitsize="128" type="vec128"/>
+ <reg name="vr24s1" bitsize="128" type="vec128"/>
+ <reg name="vr25s1" bitsize="128" type="vec128"/>
+ <reg name="vr26s1" bitsize="128" type="vec128"/>
+ <reg name="vr27s1" bitsize="128" type="vec128"/>
+ <reg name="vr28s1" bitsize="128" type="vec128"/>
+ <reg name="vr29s1" bitsize="128" type="vec128"/>
+ <reg name="vr30s1" bitsize="128" type="vec128"/>
+ <reg name="vr31s1" bitsize="128" type="vec128"/>
+
+ <reg name="vscrs1" bitsize="32" group="vector"/>
+ <reg name="vrsaves1" bitsize="32" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-altivec-valgrind-s2.xml b/coregrind/m_gdbserver/power-altivec-valgrind-s2.xml
new file mode 100644
index 0000000..fe3a427
--- /dev/null
+++ b/coregrind/m_gdbserver/power-altivec-valgrind-s2.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.altivec-valgrind-s2">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <union id="vec128">
+ <field name="uint128" type="uint128"/>
+ <field name="v4_float" type="v4f"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v16_int8" type="v16i8"/>
+ </union>
+
+ <reg name="vr0s2" bitsize="128" type="vec128"/>
+ <reg name="vr1s2" bitsize="128" type="vec128"/>
+ <reg name="vr2s2" bitsize="128" type="vec128"/>
+ <reg name="vr3s2" bitsize="128" type="vec128"/>
+ <reg name="vr4s2" bitsize="128" type="vec128"/>
+ <reg name="vr5s2" bitsize="128" type="vec128"/>
+ <reg name="vr6s2" bitsize="128" type="vec128"/>
+ <reg name="vr7s2" bitsize="128" type="vec128"/>
+ <reg name="vr8s2" bitsize="128" type="vec128"/>
+ <reg name="vr9s2" bitsize="128" type="vec128"/>
+ <reg name="vr10s2" bitsize="128" type="vec128"/>
+ <reg name="vr11s2" bitsize="128" type="vec128"/>
+ <reg name="vr12s2" bitsize="128" type="vec128"/>
+ <reg name="vr13s2" bitsize="128" type="vec128"/>
+ <reg name="vr14s2" bitsize="128" type="vec128"/>
+ <reg name="vr15s2" bitsize="128" type="vec128"/>
+ <reg name="vr16s2" bitsize="128" type="vec128"/>
+ <reg name="vr17s2" bitsize="128" type="vec128"/>
+ <reg name="vr18s2" bitsize="128" type="vec128"/>
+ <reg name="vr19s2" bitsize="128" type="vec128"/>
+ <reg name="vr20s2" bitsize="128" type="vec128"/>
+ <reg name="vr21s2" bitsize="128" type="vec128"/>
+ <reg name="vr22s2" bitsize="128" type="vec128"/>
+ <reg name="vr23s2" bitsize="128" type="vec128"/>
+ <reg name="vr24s2" bitsize="128" type="vec128"/>
+ <reg name="vr25s2" bitsize="128" type="vec128"/>
+ <reg name="vr26s2" bitsize="128" type="vec128"/>
+ <reg name="vr27s2" bitsize="128" type="vec128"/>
+ <reg name="vr28s2" bitsize="128" type="vec128"/>
+ <reg name="vr29s2" bitsize="128" type="vec128"/>
+ <reg name="vr30s2" bitsize="128" type="vec128"/>
+ <reg name="vr31s2" bitsize="128" type="vec128"/>
+
+ <reg name="vscrs2" bitsize="32" group="vector"/>
+ <reg name="vrsaves2" bitsize="32" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-altivec.xml b/coregrind/m_gdbserver/power-altivec.xml
new file mode 100644
index 0000000..45d31af
--- /dev/null
+++ b/coregrind/m_gdbserver/power-altivec.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.altivec">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <union id="vec128">
+ <field name="uint128" type="uint128"/>
+ <field name="v4_float" type="v4f"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v16_int8" type="v16i8"/>
+ </union>
+
+ <reg name="vr0" bitsize="128" type="vec128"/>
+ <reg name="vr1" bitsize="128" type="vec128"/>
+ <reg name="vr2" bitsize="128" type="vec128"/>
+ <reg name="vr3" bitsize="128" type="vec128"/>
+ <reg name="vr4" bitsize="128" type="vec128"/>
+ <reg name="vr5" bitsize="128" type="vec128"/>
+ <reg name="vr6" bitsize="128" type="vec128"/>
+ <reg name="vr7" bitsize="128" type="vec128"/>
+ <reg name="vr8" bitsize="128" type="vec128"/>
+ <reg name="vr9" bitsize="128" type="vec128"/>
+ <reg name="vr10" bitsize="128" type="vec128"/>
+ <reg name="vr11" bitsize="128" type="vec128"/>
+ <reg name="vr12" bitsize="128" type="vec128"/>
+ <reg name="vr13" bitsize="128" type="vec128"/>
+ <reg name="vr14" bitsize="128" type="vec128"/>
+ <reg name="vr15" bitsize="128" type="vec128"/>
+ <reg name="vr16" bitsize="128" type="vec128"/>
+ <reg name="vr17" bitsize="128" type="vec128"/>
+ <reg name="vr18" bitsize="128" type="vec128"/>
+ <reg name="vr19" bitsize="128" type="vec128"/>
+ <reg name="vr20" bitsize="128" type="vec128"/>
+ <reg name="vr21" bitsize="128" type="vec128"/>
+ <reg name="vr22" bitsize="128" type="vec128"/>
+ <reg name="vr23" bitsize="128" type="vec128"/>
+ <reg name="vr24" bitsize="128" type="vec128"/>
+ <reg name="vr25" bitsize="128" type="vec128"/>
+ <reg name="vr26" bitsize="128" type="vec128"/>
+ <reg name="vr27" bitsize="128" type="vec128"/>
+ <reg name="vr28" bitsize="128" type="vec128"/>
+ <reg name="vr29" bitsize="128" type="vec128"/>
+ <reg name="vr30" bitsize="128" type="vec128"/>
+ <reg name="vr31" bitsize="128" type="vec128"/>
+
+ <reg name="vscr" bitsize="32" group="vector"/>
+ <reg name="vrsave" bitsize="32" group="vector"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-core.xml b/coregrind/m_gdbserver/power-core.xml
new file mode 100644
index 0000000..9acb3ad
--- /dev/null
+++ b/coregrind/m_gdbserver/power-core.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.core">
+ <reg name="r0" bitsize="32" type="uint32"/>
+ <reg name="r1" bitsize="32" type="uint32"/>
+ <reg name="r2" bitsize="32" type="uint32"/>
+ <reg name="r3" bitsize="32" type="uint32"/>
+ <reg name="r4" bitsize="32" type="uint32"/>
+ <reg name="r5" bitsize="32" type="uint32"/>
+ <reg name="r6" bitsize="32" type="uint32"/>
+ <reg name="r7" bitsize="32" type="uint32"/>
+ <reg name="r8" bitsize="32" type="uint32"/>
+ <reg name="r9" bitsize="32" type="uint32"/>
+ <reg name="r10" bitsize="32" type="uint32"/>
+ <reg name="r11" bitsize="32" type="uint32"/>
+ <reg name="r12" bitsize="32" type="uint32"/>
+ <reg name="r13" bitsize="32" type="uint32"/>
+ <reg name="r14" bitsize="32" type="uint32"/>
+ <reg name="r15" bitsize="32" type="uint32"/>
+ <reg name="r16" bitsize="32" type="uint32"/>
+ <reg name="r17" bitsize="32" type="uint32"/>
+ <reg name="r18" bitsize="32" type="uint32"/>
+ <reg name="r19" bitsize="32" type="uint32"/>
+ <reg name="r20" bitsize="32" type="uint32"/>
+ <reg name="r21" bitsize="32" type="uint32"/>
+ <reg name="r22" bitsize="32" type="uint32"/>
+ <reg name="r23" bitsize="32" type="uint32"/>
+ <reg name="r24" bitsize="32" type="uint32"/>
+ <reg name="r25" bitsize="32" type="uint32"/>
+ <reg name="r26" bitsize="32" type="uint32"/>
+ <reg name="r27" bitsize="32" type="uint32"/>
+ <reg name="r28" bitsize="32" type="uint32"/>
+ <reg name="r29" bitsize="32" type="uint32"/>
+ <reg name="r30" bitsize="32" type="uint32"/>
+ <reg name="r31" bitsize="32" type="uint32"/>
+
+ <reg name="pc" bitsize="32" type="code_ptr" regnum="64"/>
+ <reg name="msr" bitsize="32" type="uint32"/>
+ <reg name="cr" bitsize="32" type="uint32"/>
+ <reg name="lr" bitsize="32" type="code_ptr"/>
+ <reg name="ctr" bitsize="32" type="uint32"/>
+ <reg name="xer" bitsize="32" type="uint32"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-fpu-valgrind-s1.xml b/coregrind/m_gdbserver/power-fpu-valgrind-s1.xml
new file mode 100644
index 0000000..01b852e
--- /dev/null
+++ b/coregrind/m_gdbserver/power-fpu-valgrind-s1.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.fpu-valgrind-s1">
+ <reg name="f0s1" bitsize="64" type="ieee_double" regnum="32"/>
+ <reg name="f1s1" bitsize="64" type="ieee_double"/>
+ <reg name="f2s1" bitsize="64" type="ieee_double"/>
+ <reg name="f3s1" bitsize="64" type="ieee_double"/>
+ <reg name="f4s1" bitsize="64" type="ieee_double"/>
+ <reg name="f5s1" bitsize="64" type="ieee_double"/>
+ <reg name="f6s1" bitsize="64" type="ieee_double"/>
+ <reg name="f7s1" bitsize="64" type="ieee_double"/>
+ <reg name="f8s1" bitsize="64" type="ieee_double"/>
+ <reg name="f9s1" bitsize="64" type="ieee_double"/>
+ <reg name="f10s1" bitsize="64" type="ieee_double"/>
+ <reg name="f11s1" bitsize="64" type="ieee_double"/>
+ <reg name="f12s1" bitsize="64" type="ieee_double"/>
+ <reg name="f13s1" bitsize="64" type="ieee_double"/>
+ <reg name="f14s1" bitsize="64" type="ieee_double"/>
+ <reg name="f15s1" bitsize="64" type="ieee_double"/>
+ <reg name="f16s1" bitsize="64" type="ieee_double"/>
+ <reg name="f17s1" bitsize="64" type="ieee_double"/>
+ <reg name="f18s1" bitsize="64" type="ieee_double"/>
+ <reg name="f19s1" bitsize="64" type="ieee_double"/>
+ <reg name="f20s1" bitsize="64" type="ieee_double"/>
+ <reg name="f21s1" bitsize="64" type="ieee_double"/>
+ <reg name="f22s1" bitsize="64" type="ieee_double"/>
+ <reg name="f23s1" bitsize="64" type="ieee_double"/>
+ <reg name="f24s1" bitsize="64" type="ieee_double"/>
+ <reg name="f25s1" bitsize="64" type="ieee_double"/>
+ <reg name="f26s1" bitsize="64" type="ieee_double"/>
+ <reg name="f27s1" bitsize="64" type="ieee_double"/>
+ <reg name="f28s1" bitsize="64" type="ieee_double"/>
+ <reg name="f29s1" bitsize="64" type="ieee_double"/>
+ <reg name="f30s1" bitsize="64" type="ieee_double"/>
+ <reg name="f31s1" bitsize="64" type="ieee_double"/>
+
+ <reg name="fpscrs1" bitsize="32" group="float" regnum="70"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-fpu-valgrind-s2.xml b/coregrind/m_gdbserver/power-fpu-valgrind-s2.xml
new file mode 100644
index 0000000..2db1a4a
--- /dev/null
+++ b/coregrind/m_gdbserver/power-fpu-valgrind-s2.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.fpu-valgrind-s2">
+ <reg name="f0s2" bitsize="64" type="ieee_double" regnum="32"/>
+ <reg name="f1s2" bitsize="64" type="ieee_double"/>
+ <reg name="f2s2" bitsize="64" type="ieee_double"/>
+ <reg name="f3s2" bitsize="64" type="ieee_double"/>
+ <reg name="f4s2" bitsize="64" type="ieee_double"/>
+ <reg name="f5s2" bitsize="64" type="ieee_double"/>
+ <reg name="f6s2" bitsize="64" type="ieee_double"/>
+ <reg name="f7s2" bitsize="64" type="ieee_double"/>
+ <reg name="f8s2" bitsize="64" type="ieee_double"/>
+ <reg name="f9s2" bitsize="64" type="ieee_double"/>
+ <reg name="f10s2" bitsize="64" type="ieee_double"/>
+ <reg name="f11s2" bitsize="64" type="ieee_double"/>
+ <reg name="f12s2" bitsize="64" type="ieee_double"/>
+ <reg name="f13s2" bitsize="64" type="ieee_double"/>
+ <reg name="f14s2" bitsize="64" type="ieee_double"/>
+ <reg name="f15s2" bitsize="64" type="ieee_double"/>
+ <reg name="f16s2" bitsize="64" type="ieee_double"/>
+ <reg name="f17s2" bitsize="64" type="ieee_double"/>
+ <reg name="f18s2" bitsize="64" type="ieee_double"/>
+ <reg name="f19s2" bitsize="64" type="ieee_double"/>
+ <reg name="f20s2" bitsize="64" type="ieee_double"/>
+ <reg name="f21s2" bitsize="64" type="ieee_double"/>
+ <reg name="f22s2" bitsize="64" type="ieee_double"/>
+ <reg name="f23s2" bitsize="64" type="ieee_double"/>
+ <reg name="f24s2" bitsize="64" type="ieee_double"/>
+ <reg name="f25s2" bitsize="64" type="ieee_double"/>
+ <reg name="f26s2" bitsize="64" type="ieee_double"/>
+ <reg name="f27s2" bitsize="64" type="ieee_double"/>
+ <reg name="f28s2" bitsize="64" type="ieee_double"/>
+ <reg name="f29s2" bitsize="64" type="ieee_double"/>
+ <reg name="f30s2" bitsize="64" type="ieee_double"/>
+ <reg name="f31s2" bitsize="64" type="ieee_double"/>
+
+ <reg name="fpscrs2" bitsize="32" group="float" regnum="70"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-fpu.xml b/coregrind/m_gdbserver/power-fpu.xml
new file mode 100644
index 0000000..d896b47
--- /dev/null
+++ b/coregrind/m_gdbserver/power-fpu.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.fpu">
+ <reg name="f0" bitsize="64" type="ieee_double" regnum="32"/>
+ <reg name="f1" bitsize="64" type="ieee_double"/>
+ <reg name="f2" bitsize="64" type="ieee_double"/>
+ <reg name="f3" bitsize="64" type="ieee_double"/>
+ <reg name="f4" bitsize="64" type="ieee_double"/>
+ <reg name="f5" bitsize="64" type="ieee_double"/>
+ <reg name="f6" bitsize="64" type="ieee_double"/>
+ <reg name="f7" bitsize="64" type="ieee_double"/>
+ <reg name="f8" bitsize="64" type="ieee_double"/>
+ <reg name="f9" bitsize="64" type="ieee_double"/>
+ <reg name="f10" bitsize="64" type="ieee_double"/>
+ <reg name="f11" bitsize="64" type="ieee_double"/>
+ <reg name="f12" bitsize="64" type="ieee_double"/>
+ <reg name="f13" bitsize="64" type="ieee_double"/>
+ <reg name="f14" bitsize="64" type="ieee_double"/>
+ <reg name="f15" bitsize="64" type="ieee_double"/>
+ <reg name="f16" bitsize="64" type="ieee_double"/>
+ <reg name="f17" bitsize="64" type="ieee_double"/>
+ <reg name="f18" bitsize="64" type="ieee_double"/>
+ <reg name="f19" bitsize="64" type="ieee_double"/>
+ <reg name="f20" bitsize="64" type="ieee_double"/>
+ <reg name="f21" bitsize="64" type="ieee_double"/>
+ <reg name="f22" bitsize="64" type="ieee_double"/>
+ <reg name="f23" bitsize="64" type="ieee_double"/>
+ <reg name="f24" bitsize="64" type="ieee_double"/>
+ <reg name="f25" bitsize="64" type="ieee_double"/>
+ <reg name="f26" bitsize="64" type="ieee_double"/>
+ <reg name="f27" bitsize="64" type="ieee_double"/>
+ <reg name="f28" bitsize="64" type="ieee_double"/>
+ <reg name="f29" bitsize="64" type="ieee_double"/>
+ <reg name="f30" bitsize="64" type="ieee_double"/>
+ <reg name="f31" bitsize="64" type="ieee_double"/>
+
+ <reg name="fpscr" bitsize="32" group="float" regnum="70"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-linux-valgrind-s1.xml b/coregrind/m_gdbserver/power-linux-valgrind-s1.xml
new file mode 100644
index 0000000..a02dd8e
--- /dev/null
+++ b/coregrind/m_gdbserver/power-linux-valgrind-s1.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.linux-valgrind-s1">
+ <reg name="orig_r3s1" bitsize="32" regnum="71"/>
+ <reg name="traps1" bitsize="32"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-linux-valgrind-s2.xml b/coregrind/m_gdbserver/power-linux-valgrind-s2.xml
new file mode 100644
index 0000000..59f6ee3
--- /dev/null
+++ b/coregrind/m_gdbserver/power-linux-valgrind-s2.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.linux-valgrind-s2">
+ <reg name="orig_r3s2" bitsize="32" regnum="71"/>
+ <reg name="traps2" bitsize="32"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power-linux.xml b/coregrind/m_gdbserver/power-linux.xml
new file mode 100644
index 0000000..b8b7519
--- /dev/null
+++ b/coregrind/m_gdbserver/power-linux.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.linux">
+ <reg name="orig_r3" bitsize="32" regnum="71"/>
+ <reg name="trap" bitsize="32"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power64-core-valgrind-s1.xml b/coregrind/m_gdbserver/power64-core-valgrind-s1.xml
new file mode 100644
index 0000000..f6296bf
--- /dev/null
+++ b/coregrind/m_gdbserver/power64-core-valgrind-s1.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.core-valgrind-s1">
+ <reg name="r0s1" bitsize="64" type="uint64"/>
+ <reg name="r1s1" bitsize="64" type="uint64"/>
+ <reg name="r2s1" bitsize="64" type="uint64"/>
+ <reg name="r3s1" bitsize="64" type="uint64"/>
+ <reg name="r4s1" bitsize="64" type="uint64"/>
+ <reg name="r5s1" bitsize="64" type="uint64"/>
+ <reg name="r6s1" bitsize="64" type="uint64"/>
+ <reg name="r7s1" bitsize="64" type="uint64"/>
+ <reg name="r8s1" bitsize="64" type="uint64"/>
+ <reg name="r9s1" bitsize="64" type="uint64"/>
+ <reg name="r10s1" bitsize="64" type="uint64"/>
+ <reg name="r11s1" bitsize="64" type="uint64"/>
+ <reg name="r12s1" bitsize="64" type="uint64"/>
+ <reg name="r13s1" bitsize="64" type="uint64"/>
+ <reg name="r14s1" bitsize="64" type="uint64"/>
+ <reg name="r15s1" bitsize="64" type="uint64"/>
+ <reg name="r16s1" bitsize="64" type="uint64"/>
+ <reg name="r17s1" bitsize="64" type="uint64"/>
+ <reg name="r18s1" bitsize="64" type="uint64"/>
+ <reg name="r19s1" bitsize="64" type="uint64"/>
+ <reg name="r20s1" bitsize="64" type="uint64"/>
+ <reg name="r21s1" bitsize="64" type="uint64"/>
+ <reg name="r22s1" bitsize="64" type="uint64"/>
+ <reg name="r23s1" bitsize="64" type="uint64"/>
+ <reg name="r24s1" bitsize="64" type="uint64"/>
+ <reg name="r25s1" bitsize="64" type="uint64"/>
+ <reg name="r26s1" bitsize="64" type="uint64"/>
+ <reg name="r27s1" bitsize="64" type="uint64"/>
+ <reg name="r28s1" bitsize="64" type="uint64"/>
+ <reg name="r29s1" bitsize="64" type="uint64"/>
+ <reg name="r30s1" bitsize="64" type="uint64"/>
+ <reg name="r31s1" bitsize="64" type="uint64"/>
+
+ <reg name="pcs1" bitsize="64" type="code_ptr" regnum="64"/>
+ <reg name="msrs1" bitsize="64" type="uint64"/>
+ <reg name="crs1" bitsize="32" type="uint32"/>
+ <reg name="lrs1" bitsize="64" type="code_ptr"/>
+ <reg name="ctrs1" bitsize="64" type="uint64"/>
+ <reg name="xers1" bitsize="32" type="uint32"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power64-core-valgrind-s2.xml b/coregrind/m_gdbserver/power64-core-valgrind-s2.xml
new file mode 100644
index 0000000..663232e
--- /dev/null
+++ b/coregrind/m_gdbserver/power64-core-valgrind-s2.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.core-valgrind-s2">
+ <reg name="r0s2" bitsize="64" type="uint64"/>
+ <reg name="r1s2" bitsize="64" type="uint64"/>
+ <reg name="r2s2" bitsize="64" type="uint64"/>
+ <reg name="r3s2" bitsize="64" type="uint64"/>
+ <reg name="r4s2" bitsize="64" type="uint64"/>
+ <reg name="r5s2" bitsize="64" type="uint64"/>
+ <reg name="r6s2" bitsize="64" type="uint64"/>
+ <reg name="r7s2" bitsize="64" type="uint64"/>
+ <reg name="r8s2" bitsize="64" type="uint64"/>
+ <reg name="r9s2" bitsize="64" type="uint64"/>
+ <reg name="r10s2" bitsize="64" type="uint64"/>
+ <reg name="r11s2" bitsize="64" type="uint64"/>
+ <reg name="r12s2" bitsize="64" type="uint64"/>
+ <reg name="r13s2" bitsize="64" type="uint64"/>
+ <reg name="r14s2" bitsize="64" type="uint64"/>
+ <reg name="r15s2" bitsize="64" type="uint64"/>
+ <reg name="r16s2" bitsize="64" type="uint64"/>
+ <reg name="r17s2" bitsize="64" type="uint64"/>
+ <reg name="r18s2" bitsize="64" type="uint64"/>
+ <reg name="r19s2" bitsize="64" type="uint64"/>
+ <reg name="r20s2" bitsize="64" type="uint64"/>
+ <reg name="r21s2" bitsize="64" type="uint64"/>
+ <reg name="r22s2" bitsize="64" type="uint64"/>
+ <reg name="r23s2" bitsize="64" type="uint64"/>
+ <reg name="r24s2" bitsize="64" type="uint64"/>
+ <reg name="r25s2" bitsize="64" type="uint64"/>
+ <reg name="r26s2" bitsize="64" type="uint64"/>
+ <reg name="r27s2" bitsize="64" type="uint64"/>
+ <reg name="r28s2" bitsize="64" type="uint64"/>
+ <reg name="r29s2" bitsize="64" type="uint64"/>
+ <reg name="r30s2" bitsize="64" type="uint64"/>
+ <reg name="r31s2" bitsize="64" type="uint64"/>
+
+ <reg name="pcs2" bitsize="64" type="code_ptr" regnum="64"/>
+ <reg name="msrs2" bitsize="64" type="uint64"/>
+ <reg name="crs2" bitsize="32" type="uint32"/>
+ <reg name="lrs2" bitsize="64" type="code_ptr"/>
+ <reg name="ctrs2" bitsize="64" type="uint64"/>
+ <reg name="xers2" bitsize="32" type="uint32"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power64-core.xml b/coregrind/m_gdbserver/power64-core.xml
new file mode 100644
index 0000000..e0a6ee3
--- /dev/null
+++ b/coregrind/m_gdbserver/power64-core.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.core">
+ <reg name="r0" bitsize="64" type="uint64"/>
+ <reg name="r1" bitsize="64" type="uint64"/>
+ <reg name="r2" bitsize="64" type="uint64"/>
+ <reg name="r3" bitsize="64" type="uint64"/>
+ <reg name="r4" bitsize="64" type="uint64"/>
+ <reg name="r5" bitsize="64" type="uint64"/>
+ <reg name="r6" bitsize="64" type="uint64"/>
+ <reg name="r7" bitsize="64" type="uint64"/>
+ <reg name="r8" bitsize="64" type="uint64"/>
+ <reg name="r9" bitsize="64" type="uint64"/>
+ <reg name="r10" bitsize="64" type="uint64"/>
+ <reg name="r11" bitsize="64" type="uint64"/>
+ <reg name="r12" bitsize="64" type="uint64"/>
+ <reg name="r13" bitsize="64" type="uint64"/>
+ <reg name="r14" bitsize="64" type="uint64"/>
+ <reg name="r15" bitsize="64" type="uint64"/>
+ <reg name="r16" bitsize="64" type="uint64"/>
+ <reg name="r17" bitsize="64" type="uint64"/>
+ <reg name="r18" bitsize="64" type="uint64"/>
+ <reg name="r19" bitsize="64" type="uint64"/>
+ <reg name="r20" bitsize="64" type="uint64"/>
+ <reg name="r21" bitsize="64" type="uint64"/>
+ <reg name="r22" bitsize="64" type="uint64"/>
+ <reg name="r23" bitsize="64" type="uint64"/>
+ <reg name="r24" bitsize="64" type="uint64"/>
+ <reg name="r25" bitsize="64" type="uint64"/>
+ <reg name="r26" bitsize="64" type="uint64"/>
+ <reg name="r27" bitsize="64" type="uint64"/>
+ <reg name="r28" bitsize="64" type="uint64"/>
+ <reg name="r29" bitsize="64" type="uint64"/>
+ <reg name="r30" bitsize="64" type="uint64"/>
+ <reg name="r31" bitsize="64" type="uint64"/>
+
+ <reg name="pc" bitsize="64" type="code_ptr" regnum="64"/>
+ <reg name="msr" bitsize="64" type="uint64"/>
+ <reg name="cr" bitsize="32" type="uint32"/>
+ <reg name="lr" bitsize="64" type="code_ptr"/>
+ <reg name="ctr" bitsize="64" type="uint64"/>
+ <reg name="xer" bitsize="32" type="uint32"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power64-linux-valgrind-s1.xml b/coregrind/m_gdbserver/power64-linux-valgrind-s1.xml
new file mode 100644
index 0000000..7f1d0ac
--- /dev/null
+++ b/coregrind/m_gdbserver/power64-linux-valgrind-s1.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.linux-valgrind-s1">
+ <reg name="orig_r3s1" bitsize="64" regnum="71"/>
+ <reg name="traps1" bitsize="64"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power64-linux-valgrind-s2.xml b/coregrind/m_gdbserver/power64-linux-valgrind-s2.xml
new file mode 100644
index 0000000..007bd04
--- /dev/null
+++ b/coregrind/m_gdbserver/power64-linux-valgrind-s2.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.linux-valgrind-s2">
+ <reg name="orig_r3s2" bitsize="64" regnum="71"/>
+ <reg name="traps2" bitsize="64"/>
+</feature>
diff --git a/coregrind/m_gdbserver/power64-linux.xml b/coregrind/m_gdbserver/power64-linux.xml
new file mode 100644
index 0000000..a72a058
--- /dev/null
+++ b/coregrind/m_gdbserver/power64-linux.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.linux">
+ <reg name="orig_r3" bitsize="64" regnum="71"/>
+ <reg name="trap" bitsize="64"/>
+</feature>
diff --git a/coregrind/m_gdbserver/powerpc-altivec32l-valgrind.xml b/coregrind/m_gdbserver/powerpc-altivec32l-valgrind.xml
new file mode 100644
index 0000000..07094a4
--- /dev/null
+++ b/coregrind/m_gdbserver/powerpc-altivec32l-valgrind.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only
+ view of the PowerPC. Includes Linux-only special "registers" and AltiVec
+ vector registers. -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>powerpc:common</architecture>
+ <xi:include href="power-core.xml"/>
+ <xi:include href="power-fpu.xml"/>
+ <xi:include href="power-linux.xml"/>
+ <xi:include href="power-altivec.xml"/>
+ <xi:include href="power-core-valgrind-s1.xml"/>
+ <xi:include href="power-fpu-valgrind-s1.xml"/>
+ <xi:include href="power-linux-valgrind-s1.xml"/>
+ <xi:include href="power-altivec-valgrind-s1.xml"/>
+ <xi:include href="power-core-valgrind-s2.xml"/>
+ <xi:include href="power-fpu-valgrind-s2.xml"/>
+ <xi:include href="power-linux-valgrind-s2.xml"/>
+ <xi:include href="power-altivec-valgrind-s2.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/powerpc-altivec32l.xml b/coregrind/m_gdbserver/powerpc-altivec32l.xml
new file mode 100644
index 0000000..8d77e10
--- /dev/null
+++ b/coregrind/m_gdbserver/powerpc-altivec32l.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only
+ view of the PowerPC. Includes Linux-only special "registers" and AltiVec
+ vector registers. -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>powerpc:common</architecture>
+ <xi:include href="power-core.xml"/>
+ <xi:include href="power-fpu.xml"/>
+ <xi:include href="power-linux.xml"/>
+ <xi:include href="power-altivec.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/powerpc-altivec64l-valgrind.xml b/coregrind/m_gdbserver/powerpc-altivec64l-valgrind.xml
new file mode 100644
index 0000000..a2cd615
--- /dev/null
+++ b/coregrind/m_gdbserver/powerpc-altivec64l-valgrind.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only
+ view of the PowerPC. Includes Linux-only special "registers" and AltiVec
+ vector registers. -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>powerpc:common64</architecture>
+ <xi:include href="power64-core.xml"/>
+ <xi:include href="power-fpu.xml"/>
+ <xi:include href="power64-linux.xml"/>
+ <xi:include href="power-altivec.xml"/>
+ <xi:include href="power64-core-valgrind-s1.xml"/>
+ <xi:include href="power-fpu-valgrind-s1.xml"/>
+ <xi:include href="power64-linux-valgrind-s1.xml"/>
+ <xi:include href="power-altivec-valgrind-s1.xml"/>
+ <xi:include href="power64-core-valgrind-s2.xml"/>
+ <xi:include href="power-fpu-valgrind-s2.xml"/>
+ <xi:include href="power64-linux-valgrind-s2.xml"/>
+ <xi:include href="power-altivec-valgrind-s2.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/powerpc-altivec64l.xml b/coregrind/m_gdbserver/powerpc-altivec64l.xml
new file mode 100644
index 0000000..d06dad9
--- /dev/null
+++ b/coregrind/m_gdbserver/powerpc-altivec64l.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only
+ view of the PowerPC. Includes Linux-only special "registers" and AltiVec
+ vector registers. -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+ <architecture>powerpc:common64</architecture>
+ <xi:include href="power64-core.xml"/>
+ <xi:include href="power-fpu.xml"/>
+ <xi:include href="power64-linux.xml"/>
+ <xi:include href="power-altivec.xml"/>
+</target>
diff --git a/coregrind/m_gdbserver/regcache.c b/coregrind/m_gdbserver/regcache.c
new file mode 100644
index 0000000..a920231
--- /dev/null
+++ b/coregrind/m_gdbserver/regcache.c
@@ -0,0 +1,263 @@
+/* Register support routines for the remote server for GDB.
+ Copyright (C) 2001, 2002, 2004, 2005, 2011
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "regdef.h"
+
+/* The private data for the register cache. Note that we have one
+ per inferior; this is primarily for simplicity, as the performance
+ benefit is minimal. */
+
+struct inferior_regcache_data
+{
+ int registers_valid;
+ unsigned char *registers;
+ Bool *register_supplied; /* set to True once it has been supplied */
+};
+
+static int register_bytes;
+
+static struct reg *reg_defs;
+static int num_registers;
+
+const char **gdbserver_expedite_regs;
+
+static
+struct inferior_regcache_data * get_regcache (struct thread_info *inf,
+ int fetch)
+{
+ struct inferior_regcache_data *regcache;
+
+ regcache = (struct inferior_regcache_data *) inferior_regcache_data (inf);
+
+ if (regcache == NULL)
+ fatal ("no register cache\n");
+
+ /* FIXME - fetch registers for INF */
+ if (fetch && regcache->registers_valid == 0) {
+ fetch_inferior_registers (0);
+ regcache->registers_valid = 1;
+ }
+
+ return regcache;
+}
+
+void regcache_invalidate_one (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct inferior_regcache_data *regcache;
+
+ regcache = (struct inferior_regcache_data *) inferior_regcache_data (thread);
+
+ if (regcache->registers_valid) {
+ struct thread_info *saved_inferior = current_inferior;
+
+ current_inferior = thread;
+ store_inferior_registers (-1);
+ current_inferior = saved_inferior;
+ }
+
+ regcache->registers_valid = 0;
+}
+
+void regcache_invalidate ()
+{
+ for_each_inferior (&all_threads, regcache_invalidate_one);
+}
+
+int registers_length (void)
+{
+ return 2 * register_bytes;
+}
+
+void *new_register_cache (void)
+{
+ struct inferior_regcache_data *regcache;
+
+ regcache = malloc (sizeof (*regcache));
+
+ /* Make sure to zero-initialize the register cache when it is created,
+ in case there are registers the target never fetches. This way they'll
+ read as zero instead of garbage. */
+ regcache->registers = calloc (1, register_bytes);
+ if (regcache->registers == NULL)
+ fatal ("Could not allocate register cache.\n");
+
+ regcache->register_supplied = calloc (1, num_registers);
+ if (regcache->register_supplied == NULL)
+ fatal ("Could not allocate register_supplied cache.\n");
+
+ regcache->registers_valid = 0;
+
+ return regcache;
+}
+
+void free_register_cache (void *regcache_p)
+{
+ struct inferior_regcache_data *regcache
+ = (struct inferior_regcache_data *) regcache_p;
+
+ free (regcache->registers);
+ free (regcache->register_supplied);
+ free (regcache);
+}
+
+/* if a regcache exists for entry, reallocate it.
+ This is needed if the shadow registers are added.
+ In such a case, a 2nd call to set_register_cache is done
+ which will cause the reallocation of already created caches. */
+static
+void regcache_realloc_one (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct inferior_regcache_data *regcache;
+
+ regcache = (struct inferior_regcache_data *) inferior_regcache_data (thread);
+
+ if (regcache) {
+ free_register_cache (regcache);
+ set_inferior_regcache_data (thread, new_register_cache ());
+ }
+}
+
+void set_register_cache (struct reg *regs, int n)
+{
+ int offset, i;
+
+ reg_defs = regs;
+ num_registers = n;
+
+ offset = 0;
+ for (i = 0; i < n; i++) {
+ regs[i].offset = offset;
+ offset += regs[i].size;
+ }
+
+ register_bytes = offset / 8;
+
+ for_each_inferior (&all_threads, regcache_realloc_one);
+}
+
+void registers_to_string (char *buf)
+{
+ unsigned char *registers = get_regcache (current_inferior, 1)->registers;
+
+ convert_int_to_ascii (registers, buf, register_bytes);
+}
+
+void registers_from_string (char *buf)
+{
+ int len = strlen (buf);
+ unsigned char *registers = get_regcache (current_inferior, 1)->registers;
+
+ if (len != register_bytes * 2) {
+ warning ("Wrong sized register packet (expected %d bytes, got %d)\n",
+ 2*register_bytes, len);
+ if (len > register_bytes * 2)
+ len = register_bytes * 2;
+ }
+ convert_ascii_to_int (buf, registers, len / 2);
+}
+
+int find_regno (const char *name)
+{
+ int i;
+
+ for (i = 0; i < num_registers; i++)
+ if (!strcmp (name, reg_defs[i].name))
+ return i;
+ fatal ("Unknown register %s requested\n", name);
+ return -1;
+}
+
+struct reg *find_register_by_number (int n)
+{
+ return ®_defs[n];
+}
+
+int register_size (int n)
+{
+ return reg_defs[n].size / 8;
+}
+
+static
+unsigned char *register_data (int n, int fetch)
+{
+ unsigned char *registers
+ = get_regcache (current_inferior, fetch)->registers;
+
+ return registers + (reg_defs[n].offset / 8);
+}
+static
+unsigned char *register_data_for_supply (int n, int fetch, Bool *mod)
+{
+ struct inferior_regcache_data * cache
+ = get_regcache (current_inferior, fetch);
+ unsigned char *registers = cache->registers;
+
+ if (cache->register_supplied[n])
+ *mod = False;
+ else
+ *mod = True;
+ cache->register_supplied[n] = True;
+ return registers + (reg_defs[n].offset / 8);
+}
+
+void supply_register (int n, const void *buf, Bool *mod)
+{
+ Bool new;
+ VG_(dmemcpy) (register_data_for_supply (n, 0, &new),
+ buf, register_size (n), mod);
+ if (new)
+ *mod = True;
+}
+
+void supply_register_from_string (int n, const char *buf, Bool *mod)
+{
+ Bool new;
+ unsigned char bytes_register[register_size (n)];
+ convert_ascii_to_int ((char *) buf, bytes_register, register_size (n));
+ VG_(dmemcpy) (register_data_for_supply (n, 0, &new),
+ bytes_register, register_size (n), mod);
+ if (new)
+ *mod = True;
+}
+
+void supply_register_by_name (const char *name, const void *buf, Bool *mod)
+{
+ supply_register (find_regno (name), buf, mod);
+}
+
+void collect_register (int n, void *buf)
+{
+ VG_(memcpy) (buf, register_data (n, 1), register_size (n));
+}
+
+void collect_register_as_string (int n, char *buf)
+{
+ convert_int_to_ascii (register_data (n, 1), buf, register_size (n));
+}
+
+void collect_register_by_name (const char *name, void *buf)
+{
+ collect_register (find_regno (name), buf);
+}
diff --git a/coregrind/m_gdbserver/regcache.h b/coregrind/m_gdbserver/regcache.h
new file mode 100644
index 0000000..2f17d0a
--- /dev/null
+++ b/coregrind/m_gdbserver/regcache.h
@@ -0,0 +1,80 @@
+/* Register support routines for the remote server for GDB.
+ Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#ifndef REGCACHE_H
+#define REGCACHE_H
+
+struct inferior_list_entry;
+
+/* Create a new register cache for INFERIOR. */
+
+void *new_register_cache (void);
+
+/* Release all memory associated with the register cache for INFERIOR. */
+
+void free_register_cache (void *regcache);
+
+/* Invalidate cached registers for one or all threads. */
+
+void regcache_invalidate_one (struct inferior_list_entry *);
+void regcache_invalidate (void);
+
+/* Convert all registers to a string in the currently specified remote
+ format. */
+
+void registers_to_string (char *buf);
+
+/* Convert a string to register values and fill our register cache. */
+
+void registers_from_string (char *buf);
+
+/* Return the size in bytes of a string-encoded register packet. */
+
+int registers_length (void);
+
+/* Return a pointer to the description of register ``n''. */
+
+struct reg *find_register_by_number (int n);
+
+int register_size (int n);
+
+int find_regno (const char *name);
+
+extern const char **gdbserver_expedite_regs;
+
+/* *mod set to True if *buf provides a new value. */
+void supply_register (int n, const void *buf, Bool *mod);
+
+/* Reads register data from buf (hex string in target byte order)
+ and stores it in the register cache.
+ *mod set to True if *buf provides a new value. */
+void supply_register_from_string (int n, const char *buf, Bool *mod);
+
+/* *mod set to True if *buf provides a new value. */
+void supply_register_by_name (const char *name, const void *buf, Bool *mod);
+
+void collect_register (int n, void *buf);
+
+void collect_register_as_string (int n, char *buf);
+
+void collect_register_by_name (const char *name, void *buf);
+
+#endif /* REGCACHE_H */
diff --git a/coregrind/m_gdbserver/regdef.h b/coregrind/m_gdbserver/regdef.h
new file mode 100644
index 0000000..60400fd
--- /dev/null
+++ b/coregrind/m_gdbserver/regdef.h
@@ -0,0 +1,47 @@
+/* Register protocol definition structures for the GNU Debugger
+ Copyright 2001, 2002 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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. */
+
+#ifndef REGDEF_H
+#define REGDEF_H
+
+struct reg
+{
+ /* The name of this register - NULL for pad entries. */
+ const char *name;
+
+ /* At the moment, both of the following bit counts must be divisible
+ by eight (to match the representation as two hex digits) and divisible
+ by the size of a byte (to match the layout of each register in
+ memory). */
+
+ /* The offset (in bits) of the value of this register in the buffer. */
+ int offset;
+
+ /* The size (in bits) of the value of this register, as transmitted. */
+ int size;
+};
+
+/* Set the current remote protocol and register cache according to the array
+ ``regs'', with ``n'' elements. */
+
+void set_register_cache (struct reg *regs, int n);
+
+#endif /* REGDEF_H */
diff --git a/coregrind/m_gdbserver/remote-utils.c b/coregrind/m_gdbserver/remote-utils.c
new file mode 100644
index 0000000..eb2f011
--- /dev/null
+++ b/coregrind/m_gdbserver/remote-utils.c
@@ -0,0 +1,1036 @@
+/* Remote utility routines for the remote server for GDB.
+ Copyright (C) 1986, 1989, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ 2001, 2002, 2003, 2004, 2005, 2006, 2011
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_vkiscnums.h"
+#include "pub_core_libcsignal.h"
+#include "pub_core_options.h"
+
+#include "server.h"
+
+# if defined(VGO_linux)
+#include <sys/prctl.h>
+# endif
+
+Bool noack_mode;
+
+static int readchar (int single);
+
+void remote_utils_output_status(void);
+
+static int remote_desc;
+
+static VgdbShared *shared;
+static int last_looked_cntr = -1;
+static struct vki_pollfd remote_desc_pollfdread_activity;
+#define INVALID_DESCRIPTOR -1
+
+/* for a gdbserver embedded in valgrind, we read from a FIFO and write
+ to another FIFO So, we need two descriptors */
+static int write_remote_desc = INVALID_DESCRIPTOR;
+static int pid_from_to_creator;
+/* only this pid will remove the FIFOs: if an exec fails, we must avoid
+ that the exiting child believes it has to remove the FIFOs of its parent */
+static int mknod_done = 0;
+
+static char *from_gdb = NULL;
+static char *to_gdb = NULL;
+static char *shared_mem = NULL;
+
+static
+int open_fifo (char *side, char *path, int flags)
+{
+ SysRes o;
+ int fd;
+ dlog(1, "Opening %s side %s\n", side, path);
+ o = VG_(open) (path, flags, 0);
+ if (sr_isError (o)) {
+ sr_perror(o, "open fifo %s\n", path);
+ fatal ("valgrind: fatal error: vgdb FIFO cannot be opened.\n");
+ } else {
+ fd = sr_Res(o);
+ dlog(1, "result fd %d\n", fd);
+ }
+ fd = VG_(safe_fd)(fd);
+ dlog(1, "result safe_fd %d\n", fd);
+ if (fd == -1)
+ fatal("safe_fd for vgdb FIFO failed\n");
+ return fd;
+}
+
+void remote_utils_output_status(void)
+{
+ if (shared == NULL)
+ VG_(umsg)("remote communication not initialized\n");
+ else
+ VG_(umsg)("shared->written_by_vgdb %d shared->seen_by_valgrind %d\n",
+ shared->written_by_vgdb, shared->seen_by_valgrind);
+}
+
+/* Returns 0 if vgdb and connection state looks good,
+ otherwise returns an int value telling which check failed. */
+static
+int vgdb_state_looks_bad(char* where)
+{
+ if (VG_(kill)(shared->vgdb_pid, 0) != 0)
+ return 1; // vgdb process does not exist anymore.
+
+ if (remote_desc_activity(where) == 2)
+ return 2; // check for error on remote desc shows a problem
+
+ if (remote_desc == INVALID_DESCRIPTOR)
+ return 3; // after check, remote_desc not ok anymore
+
+ return 0; // all is ok.
+}
+
+/* On systems that defines PR_SET_PTRACER, verify if ptrace_scope is
+ is permissive enough for vgdb. Otherwise, call set_ptracer.
+ This is especially aimed at Ubuntu >= 10.10 which has added
+ the ptrace_scope context. */
+static
+void set_ptracer(void)
+{
+#ifdef PR_SET_PTRACER
+ SysRes o;
+ char *ptrace_scope_setting_file = "/proc/sys/kernel/yama/ptrace_scope";
+ int fd;
+ char ptrace_scope;
+ int ret;
+
+ o = VG_(open) (ptrace_scope_setting_file, VKI_O_RDONLY, 0);
+ if (sr_isError(o)) {
+ sr_perror(o, "error VG_(open) %s\n", ptrace_scope_setting_file);
+ /* can't read setting. Assuming ptrace can be called by vgdb. */
+ return;
+ }
+ fd = sr_Res(o);
+ if (VG_(read) (fd, &ptrace_scope, 1) == 1) {
+ dlog(1, "ptrace_scope %c\n", ptrace_scope);
+ if (ptrace_scope != '0') {
+ /* insufficient default ptrace_scope.
+ Indicate to the kernel that we accept to be
+ ptraced by our vgdb. */
+ ret = VG_(prctl) (PR_SET_PTRACER, shared->vgdb_pid, 0, 0, 0);
+ dlog(1, "set_ptracer to vgdb_pid %d result %d\n",
+ shared->vgdb_pid, ret);
+ }
+ } else {
+ dlog(0, "Could not read the ptrace_scope setting from %s\n",
+ ptrace_scope_setting_file);
+ }
+
+ VG_(close) (fd);
+#endif
+}
+
+/* returns 1 if one or more poll "errors" is set.
+ Errors are: VKI_POLLERR or VKI_POLLHUP or VKI_POLLNAL */
+static
+int poll_cond (short revents)
+{
+ return (revents & (VKI_POLLERR | VKI_POLLHUP | VKI_POLLNVAL));
+}
+
+/* Ensures we have a valid write file descriptor.
+ Returns 1 if we have a valid write file descriptor,
+ 0 if the write fd could not be opened. */
+static
+int ensure_write_remote_desc(void)
+{
+ struct vki_pollfd write_remote_desc_ok;
+ int ret;
+ if (write_remote_desc != INVALID_DESCRIPTOR) {
+ write_remote_desc_ok.fd = write_remote_desc;
+ write_remote_desc_ok.events = VKI_POLLOUT;
+ write_remote_desc_ok.revents = 0;
+ ret = VG_(poll)(&write_remote_desc_ok, 1, 0);
+ if (ret && poll_cond(write_remote_desc_ok.revents)) {
+ dlog(1, "POLLcond %d closing write_remote_desc %d\n",
+ write_remote_desc_ok.revents, write_remote_desc);
+ VG_(close) (write_remote_desc);
+ write_remote_desc = INVALID_DESCRIPTOR;
+ }
+ }
+ if (write_remote_desc == INVALID_DESCRIPTOR) {
+ /* open_fifo write will block if the receiving vgdb
+ process is dead. So, let's check for vgdb state to
+ be reasonably sure someone is reading on the other
+ side of the fifo. */
+ if (!vgdb_state_looks_bad("bad?@ensure_write_remote_desc")) {
+ set_ptracer();
+ write_remote_desc = open_fifo ("write", to_gdb, VKI_O_WRONLY);
+ }
+ }
+
+ return (write_remote_desc != INVALID_DESCRIPTOR);
+}
+
+#if defined(VGO_darwin)
+#define VKI_S_IFIFO 0010000
+#endif
+static
+void safe_mknod (char *nod)
+{
+ SysRes m;
+ m = VG_(mknod) (nod, VKI_S_IFIFO|0666, 0);
+ if (sr_isError (m)) {
+ if (sr_Err (m) == VKI_EEXIST) {
+ if (VG_(clo_verbosity) > 1) {
+ VG_(umsg)("%s already created\n", nod);
+ }
+ } else {
+ sr_perror(m, "mknod %s\n", nod);
+ VG_(umsg) ("valgrind: fatal error: vgdb FIFOs cannot be created.\n");
+ VG_(exit)(1);
+ }
+ }
+}
+
+/* Open a connection to a remote debugger.
+ NAME is the filename used for communication.
+ For Valgrind, name is the prefix for the two read and write FIFOs
+ The two FIFOs names will be build by appending
+ -from-vgdb-to-pid and -to-vgdb-from-pid
+ with pid being the pidnr of the valgrind process These two FIFOs
+ will be created if not existing yet. They will be removed when
+ the gdbserver connection is closed or the process exits */
+
+void remote_open (char *name)
+{
+ int save_fcntl_flags;
+ VgdbShared vgdbinit =
+ {0, 0, 0, (Addr) VG_(invoke_gdbserver),
+ (Addr) VG_(threads), sizeof(ThreadState),
+ offsetof(ThreadState, status),
+ offsetof(ThreadState, os_state) + offsetof(ThreadOSstate, lwpid)};
+ const int pid = VG_(getpid)();
+ const int name_default = strcmp(name, VG_CLO_VGDB_PREFIX_DEFAULT) == 0;
+ Addr addr_shared;
+ SysRes o;
+ int shared_mem_fd = INVALID_DESCRIPTOR;
+
+ if (from_gdb != NULL)
+ free (from_gdb);
+ from_gdb = malloc (strlen(name) + 30);
+ if (to_gdb != NULL)
+ free (to_gdb);
+ to_gdb = malloc (strlen(name) + 30);
+ if (shared_mem != NULL)
+ free (shared_mem);
+ shared_mem = malloc (strlen(name) + 30);
+ /* below 3 lines must match the equivalent in vgdb.c */
+ VG_(sprintf) (from_gdb, "%s-from-vgdb-to-%d", name, pid);
+ VG_(sprintf) (to_gdb, "%s-to-vgdb-from-%d", name, pid);
+ VG_(sprintf) (shared_mem, "%s-shared-mem-vgdb-%d", name, pid);
+ if (VG_(clo_verbosity) > 1) {
+ VG_(umsg)("embedded gdbserver: reading from %s\n", from_gdb);
+ VG_(umsg)("embedded gdbserver: writing to %s\n", to_gdb);
+ VG_(umsg)("embedded gdbserver: shared mem %s\n", shared_mem);
+ VG_(umsg)("CONTROL ME using: vgdb --pid=%d%s%s ...command...\n",
+ pid, (name_default ? "" : " --vgdb="),
+ (name_default ? "" : name));
+ VG_(umsg)("DEBUG ME using: (gdb) target remote | vgdb --pid=%d%s%s\n",
+ pid, (name_default ? "" : " --vgdb="),
+ (name_default ? "" : name));
+ VG_(umsg)(" --pid optional if only one valgrind process is running\n");
+ }
+
+ if (!mknod_done) {
+ mknod_done++;
+ safe_mknod(from_gdb);
+ safe_mknod(to_gdb);
+
+ pid_from_to_creator = pid;
+
+ o = VG_(open) (shared_mem, VKI_O_CREAT|VKI_O_RDWR, 0666);
+ if (sr_isError (o)) {
+ sr_perror(o, "cannot create shared_mem file %s\n", shared_mem);
+ fatal("");
+ } else {
+ shared_mem_fd = sr_Res(o);
+ }
+
+ if (VG_(write)(shared_mem_fd, &vgdbinit, sizeof(VgdbShared))
+ != sizeof(VgdbShared)) {
+ fatal("error writing %d bytes to shared mem %s\n",
+ (int) sizeof(VgdbShared), shared_mem);
+ }
+ shared_mem_fd = VG_(safe_fd)(shared_mem_fd);
+ if (shared_mem_fd == -1) {
+ fatal("safe_fd for vgdb shared_mem %s failed\n", shared_mem);
+ }
+ {
+ SysRes res = VG_(am_shared_mmap_file_float_valgrind)
+ (sizeof(VgdbShared), VKI_PROT_READ|VKI_PROT_WRITE,
+ shared_mem_fd, (Off64T)0);
+ if (sr_isError(res)) {
+ sr_perror(res, "error VG_(am_shared_mmap_file_float_valgrind) %s\n",
+ shared_mem);
+ fatal("");
+ }
+ addr_shared = sr_Res (res);
+ }
+ shared = (VgdbShared*) addr_shared;
+ }
+
+ /* we open the read side FIFO in non blocking mode
+ We then set the fd in blocking mode.
+ Opening in non-blocking read mode always succeeds while opening
+ in non-blocking write mode succeeds only if the fifo is already
+ opened in read mode. So, we wait till we have read the first
+ character from the read side before opening the write side. */
+ remote_desc = open_fifo ("read", from_gdb, VKI_O_RDONLY|VKI_O_NONBLOCK);
+ save_fcntl_flags = VG_(fcntl) (remote_desc, VKI_F_GETFL, 0);
+ VG_(fcntl) (remote_desc, VKI_F_SETFL, save_fcntl_flags & ~VKI_O_NONBLOCK);
+ remote_desc_pollfdread_activity.fd = remote_desc;
+ remote_desc_pollfdread_activity.events = VKI_POLLIN;
+ remote_desc_pollfdread_activity.revents = 0;
+}
+
+/* sync_gdb_connection wait a time long enough to let the connection
+ be properly closed if needed when closing the connection (in case
+ of detach or error), if we reopen it too quickly, it seems there
+ are some events queued in the kernel concerning the "old"
+ connection/remote_desc which are discovered with poll or select on
+ the "new" connection/remote_desc. We bypass this by waiting some
+ time to let a proper cleanup to be donex */
+void sync_gdb_connection(void)
+{
+ VG_(poll)(0, 0, 100);
+}
+
+static
+char * ppFinishReason (FinishReason reason)
+{
+ switch (reason) {
+ case orderly_finish: return "orderly_finish";
+ case reset_after_error: return "reset_after_error";
+ case reset_after_fork: return "reset_after_fork";
+ default: vg_assert (0);
+ }
+}
+
+void remote_finish (FinishReason reason)
+{
+ dlog(1, "remote_finish (reason %s) %d %d\n",
+ ppFinishReason(reason), remote_desc, write_remote_desc);
+ reset_valgrind_sink(ppFinishReason(reason));
+ if (write_remote_desc != INVALID_DESCRIPTOR)
+ VG_(close) (write_remote_desc);
+ write_remote_desc = INVALID_DESCRIPTOR;
+ if (remote_desc != INVALID_DESCRIPTOR) {
+ remote_desc_pollfdread_activity.fd = INVALID_DESCRIPTOR;
+ remote_desc_pollfdread_activity.events = 0;
+ remote_desc_pollfdread_activity.revents = 0;
+ VG_(close) (remote_desc);
+ }
+ remote_desc = INVALID_DESCRIPTOR;
+ noack_mode = False;
+
+ /* ensure the child will create its own FIFOs */
+ if (reason == reset_after_fork)
+ mknod_done = 0;
+
+ if (reason == reset_after_error)
+ sync_gdb_connection();
+}
+
+/* orderly close, cleans up everything */
+void remote_close (void)
+{
+ const int pid = VG_(getpid)();
+ remote_finish(orderly_finish);
+ if (pid == pid_from_to_creator) {
+ dlog(1, "unlinking\n %s\n %s\n %s\n",
+ from_gdb, to_gdb, shared_mem);
+ if (VG_(unlink) (from_gdb) == -1)
+ warning ("could not unlink %s\n", from_gdb);
+ if (VG_(unlink) (to_gdb) == -1)
+ warning ("could not unlink %s\n", to_gdb);
+ if (VG_(unlink) (shared_mem) == -1)
+ warning ("could not unlink %s\n", shared_mem);
+ }
+ else {
+ dlog(1, "not creator => not unlinking %s and %s\n", from_gdb, to_gdb);
+ }
+ free (from_gdb);
+ free (to_gdb);
+}
+
+Bool remote_connected(void)
+{
+ return write_remote_desc != INVALID_DESCRIPTOR;
+}
+
+/* cleanup after an error detected by poll_cond */
+static
+void error_poll_cond(void)
+{
+ /* if we will close the connection, we assume either that
+ all characters have been seen or that they will be dropped. */
+ shared->seen_by_valgrind = shared->written_by_vgdb;
+ remote_finish(reset_after_error);
+}
+
+/* remote_desc_activity might be used at high frequency if the user
+ gives a small value to --vgdb-poll. So, the function avoids
+ doing repetitively system calls by rather looking at the
+ counter values maintained in shared memory by vgdb. */
+int remote_desc_activity(char *msg)
+{
+ int ret;
+ const int looking_at = shared->written_by_vgdb;
+ if (shared->seen_by_valgrind == looking_at)
+ // if (last_looked_cntr == looking_at)
+ return 0;
+ if (remote_desc == INVALID_DESCRIPTOR)
+ return 0;
+
+ /* poll the remote desc */
+ remote_desc_pollfdread_activity.revents = 0;
+ ret = VG_(poll) (&remote_desc_pollfdread_activity, 1, 0);
+ if (ret && poll_cond(remote_desc_pollfdread_activity.revents)) {
+ dlog(1, "POLLcond %d remote_desc_pollfdread %d\n",
+ remote_desc_pollfdread_activity.revents, remote_desc);
+ error_poll_cond();
+ ret = 2;
+ }
+ dlog(1,
+ "remote_desc_activity %s %d last_looked_cntr %d looking_at %d"
+ " shared->written_by_vgdb %d shared->seen_by_valgrind %d"
+ " ret %d\n",
+ msg, remote_desc, last_looked_cntr, looking_at,
+ shared->written_by_vgdb, shared->seen_by_valgrind,
+ ret);
+ /* if no error from poll, indicate we have "seen" up to looking_at */
+ if (ret != 2)
+ last_looked_cntr = looking_at;
+ return ret;
+}
+
+/* Convert hex digit A to a number. */
+
+static
+int fromhex (int a)
+{
+ if (a >= '0' && a <= '9')
+ return a - '0';
+ else if (a >= 'a' && a <= 'f')
+ return a - 'a' + 10;
+ else
+ error ("Reply contains invalid hex digit 0x%x\n", a);
+ return 0;
+}
+
+int unhexify (char *bin, const char *hex, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (hex[0] == 0 || hex[1] == 0) {
+ /* Hex string is short, or of uneven length.
+ Return the count that has been converted so far. */
+ return i;
+ }
+ *bin++ = fromhex (hex[0]) * 16 + fromhex (hex[1]);
+ hex += 2;
+ }
+ return i;
+}
+
+void decode_address (CORE_ADDR *addrp, const char *start, int len)
+{
+ CORE_ADDR addr;
+ char ch;
+ int i;
+
+ addr = 0;
+ for (i = 0; i < len; i++) {
+ ch = start[i];
+ addr = addr << 4;
+ addr = addr | (fromhex (ch) & 0x0f);
+ }
+ *addrp = addr;
+}
+
+/* Convert number NIB to a hex digit. */
+
+static
+int tohex (int nib)
+{
+ if (nib < 10)
+ return '0' + nib;
+ else
+ return 'a' + nib - 10;
+}
+
+int hexify (char *hex, const char *bin, int count)
+{
+ int i;
+
+ /* May use a length, or a nul-terminated string as input. */
+ if (count == 0)
+ count = strlen (bin);
+
+ for (i = 0; i < count; i++) {
+ *hex++ = tohex ((*bin >> 4) & 0xf);
+ *hex++ = tohex (*bin++ & 0xf);
+ }
+ *hex = 0;
+ return i;
+}
+
+/* Convert BUFFER, binary data at least LEN bytes long, into escaped
+ binary data in OUT_BUF. Set *OUT_LEN to the length of the data
+ encoded in OUT_BUF, and return the number of bytes in OUT_BUF
+ (which may be more than *OUT_LEN due to escape characters). The
+ total number of bytes in the output buffer will be at most
+ OUT_MAXLEN. */
+
+int
+remote_escape_output (const gdb_byte *buffer, int len,
+ gdb_byte *out_buf, int *out_len,
+ int out_maxlen)
+{
+ int input_index, output_index;
+
+ output_index = 0;
+ for (input_index = 0; input_index < len; input_index++) {
+ gdb_byte b = buffer[input_index];
+
+ if (b == '$' || b == '#' || b == '}' || b == '*') {
+ /* These must be escaped. */
+ if (output_index + 2 > out_maxlen)
+ break;
+ out_buf[output_index++] = '}';
+ out_buf[output_index++] = b ^ 0x20;
+ } else {
+ if (output_index + 1 > out_maxlen)
+ break;
+ out_buf[output_index++] = b;
+ }
+ }
+
+ *out_len = input_index;
+ return output_index;
+}
+
+/* Convert BUFFER, escaped data LEN bytes long, into binary data
+ in OUT_BUF. Return the number of bytes written to OUT_BUF.
+ Raise an error if the total number of bytes exceeds OUT_MAXLEN.
+
+ This function reverses remote_escape_output. It allows more
+ escaped characters than that function does, in particular because
+ '*' must be escaped to avoid the run-length encoding processing
+ in reading packets. */
+
+static
+int remote_unescape_input (const gdb_byte *buffer, int len,
+ gdb_byte *out_buf, int out_maxlen)
+{
+ int input_index, output_index;
+ int escaped;
+
+ output_index = 0;
+ escaped = 0;
+ for (input_index = 0; input_index < len; input_index++) {
+ gdb_byte b = buffer[input_index];
+
+ if (output_index + 1 > out_maxlen)
+ error ("Received too much data (len %d) from the target.\n", len);
+
+ if (escaped) {
+ out_buf[output_index++] = b ^ 0x20;
+ escaped = 0;
+ } else if (b == '}') {
+ escaped = 1;
+ } else {
+ out_buf[output_index++] = b;
+ }
+ }
+
+ if (escaped)
+ error ("Unmatched escape character in target response.\n");
+
+ return output_index;
+}
+
+/* Look for a sequence of characters which can be run-length encoded.
+ If there are any, update *CSUM and *P. Otherwise, output the
+ single character. Return the number of characters consumed. */
+
+static
+int try_rle (char *buf, int remaining, unsigned char *csum, char **p)
+{
+ int n;
+
+ /* Always output the character. */
+ *csum += buf[0];
+ *(*p)++ = buf[0];
+
+ /* Don't go past '~'. */
+ if (remaining > 97)
+ remaining = 97;
+
+ for (n = 1; n < remaining; n++)
+ if (buf[n] != buf[0])
+ break;
+
+ /* N is the index of the first character not the same as buf[0].
+ buf[0] is counted twice, so by decrementing N, we get the number
+ of characters the RLE sequence will replace. */
+ n--;
+
+ if (n < 3)
+ return 1;
+
+ /* Skip the frame characters. The manual says to skip '+' and '-'
+ also, but there's no reason to. Unfortunately these two unusable
+ characters double the encoded length of a four byte zero
+ value. */
+ while (n + 29 == '$' || n + 29 == '#')
+ n--;
+
+ *csum += '*';
+ *(*p)++ = '*';
+ *csum += n + 29;
+ *(*p)++ = n + 29;
+
+ return n + 1;
+}
+
+/* Send a packet to the remote machine, with error checking.
+ The data of the packet is in BUF, and the length of the
+ packet is in CNT. Returns >= 0 on success, -1 otherwise. */
+
+int putpkt_binary (char *buf, int cnt)
+{
+ int i;
+ unsigned char csum = 0;
+ char *buf2;
+ char *p;
+ int cc;
+
+ buf2 = malloc (PBUFSIZ);
+
+ /* Copy the packet into buffer BUF2, encapsulating it
+ and giving it a checksum. */
+
+ p = buf2;
+ *p++ = '$';
+
+ for (i = 0; i < cnt;)
+ i += try_rle (buf + i, cnt - i, &csum, &p);
+
+ *p++ = '#';
+ *p++ = tohex ((csum >> 4) & 0xf);
+ *p++ = tohex (csum & 0xf);
+
+ *p = '\0';
+
+ /* we might have to write a pkt when out FIFO not yet/anymore opened */
+ if (!ensure_write_remote_desc()) {
+ warning ("putpkt(write) error: no write_remote_desc\n");
+ return -1;
+ }
+
+ /* Send it once (noack_mode)
+ or send it over and over until we get a positive ack. */
+
+ do {
+ if (VG_(write) (write_remote_desc, buf2, p - buf2) != p - buf2) {
+ warning ("putpkt(write) error\n");
+ return -1;
+ }
+
+ if (noack_mode)
+ dlog(1, "putpkt (\"%s\"); [no ack]\n", buf2);
+ else
+ dlog(1,"putpkt (\"%s\"); [looking for ack]\n", buf2);
+
+ if (noack_mode)
+ break;
+
+ cc = readchar (1);
+ if (cc > 0)
+ dlog(1, "[received '%c' (0x%x)]\n", cc, cc);
+
+ if (cc <= 0) {
+ if (cc == 0)
+ dlog(1, "putpkt(read): Got EOF\n");
+ else
+ warning ("putpkt(read) error\n");
+
+ free (buf2);
+ return -1;
+ }
+
+ /* Check for an input interrupt while we're here. */
+ if (cc == '\003')
+ (*the_target->send_signal) (VKI_SIGINT);
+ }
+ while (cc != '+');
+
+ free (buf2);
+ return 1; /* Success! */
+}
+
+/* Send a packet to the remote machine, with error checking. The data
+ of the packet is in BUF, and the packet should be a NUL-terminated
+ string. Returns >= 0 on success, -1 otherwise. */
+
+int putpkt (char *buf)
+{
+ return putpkt_binary (buf, strlen (buf));
+}
+
+void monitor_output (char *s)
+{
+ const int len = strlen(s);
+ char *buf = malloc(1 + 2*len + 1);
+
+ buf[0] = 'O';
+ hexify(buf+1, s, len);
+ if (putpkt (buf) < 0) {
+ /* We probably have lost the connection with vgdb. */
+ reset_valgrind_sink("Error writing monitor output");
+ /* write again after reset */
+ VG_(printf) ("%s", s);
+ }
+
+ free (buf);
+}
+
+/* Returns next char from remote GDB. -1 if error. */
+/* if single, only one character maximum can be read with
+ read system call. Otherwise, when reading an ack character
+ we might pile up the next gdb command in the static buf.
+ The read loop is then blocked in poll till gdb times out. */
+static
+int readchar (int single)
+{
+ static unsigned char buf[PBUFSIZ];
+ static int bufcnt = 0;
+ static unsigned char *bufp;
+ int ret;
+
+ if (bufcnt-- > 0)
+ return *bufp++;
+
+ if (remote_desc == INVALID_DESCRIPTOR)
+ return -1;
+
+ /* No characters available in buf =>
+ wait for some characters to arrive */
+ remote_desc_pollfdread_activity.revents = 0;
+ ret = VG_(poll)(&remote_desc_pollfdread_activity, 1, -1);
+ if (ret != 1) {
+ dlog(0, "readchar: poll got %d\n", ret);
+ return -1;
+ }
+ if (single)
+ bufcnt = VG_(read) (remote_desc, buf, 1);
+ else
+ bufcnt = VG_(read) (remote_desc, buf, sizeof (buf));
+
+ if (bufcnt <= 0) {
+ if (bufcnt == 0)
+ dlog (1, "readchar: Got EOF\n");
+ else
+ warning ("readchar read error\n");
+
+ return -1;
+ }
+
+ shared->seen_by_valgrind += bufcnt;
+
+ /* If we have received a character and we do not yet have a
+ connection, we better open our "write" fifo to let vgdb open its
+ read fifo side */
+ if (write_remote_desc == INVALID_DESCRIPTOR
+ && !ensure_write_remote_desc()) {
+ dlog(1, "reachar: write_remote_desc could not be created");
+ }
+
+ bufp = buf;
+ bufcnt--;
+
+ if (poll_cond(remote_desc_pollfdread_activity.revents)) {
+ dlog(1, "readchar: POLLcond got %d\n",
+ remote_desc_pollfdread_activity.revents);
+ error_poll_cond();
+ }
+
+ return *bufp++;
+}
+
+
+/* Read a packet from the remote machine, with error checking,
+ and store it in BUF. Returns length of packet, or negative if error. */
+
+int getpkt (char *buf)
+{
+ char *bp;
+ unsigned char csum, c1, c2;
+ int c;
+
+ while (1) {
+ csum = 0;
+
+ while (1) {
+ c = readchar (0);
+ if (c == '$')
+ break;
+ dlog(1, "[getpkt: discarding char '%c']\n", c);
+ if (c < 0)
+ return -1;
+ }
+
+ bp = buf;
+ while (1) {
+ c = readchar (0);
+ if (c < 0)
+ return -1;
+ if (c == '#')
+ break;
+ *bp++ = c;
+ csum += c;
+ }
+ *bp = 0;
+
+ c1 = fromhex (readchar (0));
+ c2 = fromhex (readchar (0));
+
+ if (csum == (c1 << 4) + c2)
+ break;
+
+ dlog (0, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
+ (c1 << 4) + c2, csum, buf);
+ if (!ensure_write_remote_desc()) {
+ dlog(1, "getpkt(write nack) no write_remote_desc");
+ }
+ VG_(write) (write_remote_desc, "-", 1);
+ }
+
+ if (noack_mode)
+ dlog(1, "getpkt (\"%s\"); [no ack] \n", buf);
+ else
+ dlog(1, "getpkt (\"%s\"); [sending ack] \n", buf);
+
+ if (!noack_mode) {
+ if (!ensure_write_remote_desc()) {
+ dlog(1, "getpkt(write ack) no write_remote_desc");
+ }
+ VG_(write) (write_remote_desc, "+", 1);
+ dlog(1, "[sent ack]\n");
+ }
+
+ return bp - buf;
+}
+
+void write_ok (char *buf)
+{
+ buf[0] = 'O';
+ buf[1] = 'K';
+ buf[2] = '\0';
+}
+
+void write_enn (char *buf)
+{
+ /* Some day, we should define the meanings of the error codes... */
+ buf[0] = 'E';
+ buf[1] = '0';
+ buf[2] = '1';
+ buf[3] = '\0';
+}
+
+void convert_int_to_ascii (unsigned char *from, char *to, int n)
+{
+ int nib;
+ int ch;
+ while (n--) {
+ ch = *from++;
+ nib = ((ch & 0xf0) >> 4) & 0x0f;
+ *to++ = tohex (nib);
+ nib = ch & 0x0f;
+ *to++ = tohex (nib);
+ }
+ *to++ = 0;
+}
+
+
+void convert_ascii_to_int (char *from, unsigned char *to, int n)
+{
+ int nib1, nib2;
+ while (n--) {
+ nib1 = fromhex (*from++);
+ nib2 = fromhex (*from++);
+ *to++ = (((nib1 & 0x0f) << 4) & 0xf0) | (nib2 & 0x0f);
+ }
+}
+
+static
+char * outreg (int regno, char *buf)
+{
+ if ((regno >> 12) != 0)
+ *buf++ = tohex ((regno >> 12) & 0xf);
+ if ((regno >> 8) != 0)
+ *buf++ = tohex ((regno >> 8) & 0xf);
+ *buf++ = tohex ((regno >> 4) & 0xf);
+ *buf++ = tohex (regno & 0xf);
+ *buf++ = ':';
+ collect_register_as_string (regno, buf);
+ buf += 2 * register_size (regno);
+ *buf++ = ';';
+
+ return buf;
+}
+
+void prepare_resume_reply (char *buf, char status, unsigned char sig)
+{
+ int nib;
+
+ *buf++ = status;
+
+ nib = ((sig & 0xf0) >> 4);
+ *buf++ = tohex (nib);
+ nib = sig & 0x0f;
+ *buf++ = tohex (nib);
+
+ if (status == 'T') {
+ const char **regp = gdbserver_expedite_regs;
+
+ if (the_target->stopped_by_watchpoint != NULL
+ && (*the_target->stopped_by_watchpoint) ()) {
+ CORE_ADDR addr;
+ int i;
+
+ strncpy (buf, "watch:", 6);
+ buf += 6;
+
+ addr = (*the_target->stopped_data_address) ();
+
+ /* Convert each byte of the address into two hexadecimal chars.
+ Note that we take sizeof (void *) instead of sizeof (addr);
+ this is to avoid sending a 64-bit address to a 32-bit GDB. */
+ for (i = sizeof (void *) * 2; i > 0; i--) {
+ *buf++ = tohex ((addr >> (i - 1) * 4) & 0xf);
+ }
+ *buf++ = ';';
+ }
+
+ while (*regp) {
+ buf = outreg (find_regno (*regp), buf);
+ regp ++;
+ }
+
+ {
+ unsigned int gdb_id_from_wait;
+
+ /* FIXME right place to set this? */
+ thread_from_wait =
+ ((struct inferior_list_entry *)current_inferior)->id;
+ gdb_id_from_wait = thread_to_gdb_id (current_inferior);
+
+ dlog(1, "Writing resume reply for %ld\n", thread_from_wait);
+ /* This if (1) ought to be unnecessary. But remote_wait in GDB
+ will claim this event belongs to inferior_ptid if we do not
+ specify a thread, and there's no way for gdbserver to know
+ what inferior_ptid is. */
+ if (1 || old_thread_from_wait != thread_from_wait) {
+ general_thread = thread_from_wait;
+ VG_(sprintf) (buf, "thread:%x;", gdb_id_from_wait);
+ buf += strlen (buf);
+ old_thread_from_wait = thread_from_wait;
+ }
+ }
+ }
+ /* For W and X, we're done. */
+ *buf++ = 0;
+}
+
+void decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
+{
+ int i = 0, j = 0;
+ char ch;
+ *mem_addr_ptr = *len_ptr = 0;
+
+ while ((ch = from[i++]) != ',') {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ for (j = 0; j < 4; j++) {
+ if ((ch = from[i++]) == 0)
+ break;
+ *len_ptr = *len_ptr << 4;
+ *len_ptr |= fromhex (ch) & 0x0f;
+ }
+}
+
+void decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
+ unsigned char *to)
+{
+ int i = 0;
+ char ch;
+ *mem_addr_ptr = *len_ptr = 0;
+
+ while ((ch = from[i++]) != ',') {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ while ((ch = from[i++]) != ':') {
+ *len_ptr = *len_ptr << 4;
+ *len_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ convert_ascii_to_int (&from[i++], to, *len_ptr);
+}
+
+int decode_X_packet (char *from, int packet_len, CORE_ADDR *mem_addr_ptr,
+ unsigned int *len_ptr, unsigned char *to)
+{
+ int i = 0;
+ char ch;
+ *mem_addr_ptr = *len_ptr = 0;
+
+ while ((ch = from[i++]) != ',') {
+ *mem_addr_ptr = *mem_addr_ptr << 4;
+ *mem_addr_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ while ((ch = from[i++]) != ':') {
+ *len_ptr = *len_ptr << 4;
+ *len_ptr |= fromhex (ch) & 0x0f;
+ }
+
+ if (remote_unescape_input ((const gdb_byte *) &from[i], packet_len - i,
+ to, *len_ptr) != *len_ptr)
+ return -1;
+
+ return 0;
+}
+
diff --git a/coregrind/m_gdbserver/server.c b/coregrind/m_gdbserver/server.c
new file mode 100644
index 0000000..2d3ce90
--- /dev/null
+++ b/coregrind/m_gdbserver/server.c
@@ -0,0 +1,971 @@
+/* Main code for remote server for GDB.
+ Copyright (C) 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003,
+ 2004, 2005, 2006, 2011
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "regdef.h"
+#include "pub_core_options.h"
+#include "pub_core_translate.h"
+#include "pub_core_mallocfree.h"
+
+unsigned long cont_thread;
+unsigned long general_thread;
+unsigned long step_thread;
+unsigned long thread_from_wait;
+unsigned long old_thread_from_wait;
+
+/* for a gdbserver integrated in valgrind, resuming the process consists
+ in returning the control to valgrind.
+ Then at the next error or break or ..., valgrind calls gdbserver again.
+ A resume packet must then be built.
+ resume_packet_needed records the fact that the next call to gdbserver
+ must send a resume packet to gdb. */
+static Bool resume_packet_needed = False;
+
+VG_MINIMAL_JMP_BUF(toplevel);
+
+/* Decode a qXfer read request. Return 0 if everything looks OK,
+ or -1 otherwise. */
+
+static
+int decode_xfer_read (char *buf, char **annex, CORE_ADDR *ofs, unsigned int *len)
+{
+ /* Extract and NUL-terminate the annex. */
+ *annex = buf;
+ while (*buf && *buf != ':')
+ buf++;
+ if (*buf == '\0')
+ return -1;
+ *buf++ = 0;
+
+ /* After the read/write marker and annex, qXfer looks like a
+ traditional 'm' packet. */
+ decode_m_packet (buf, ofs, len);
+
+ return 0;
+}
+
+/* Write the response to a successful qXfer read. Returns the
+ length of the (binary) data stored in BUF, corresponding
+ to as much of DATA/LEN as we could fit. IS_MORE controls
+ the first character of the response. */
+static
+int write_qxfer_response (char *buf, unsigned char *data, int len, int is_more)
+{
+ int out_len;
+
+ if (is_more)
+ buf[0] = 'm';
+ else
+ buf[0] = 'l';
+
+ return remote_escape_output (data, len, (unsigned char *) buf + 1, &out_len,
+ PBUFSIZ - POVERHSIZ - 1) + 1;
+}
+
+static Bool initial_valgrind_sink_saved = False;
+/* True <=> valgrind log sink saved in initial_valgrind_sink */
+static OutputSink initial_valgrind_sink;
+
+static Bool command_output_to_log = False;
+/* True <=> command output goes to log instead of gdb */
+
+void reset_valgrind_sink(char *info)
+{
+ if (VG_(log_output_sink).fd != initial_valgrind_sink.fd
+ && initial_valgrind_sink_saved) {
+ VG_(log_output_sink).fd = initial_valgrind_sink.fd;
+ VG_(umsg) ("Reset valgrind output to log (%s)\n",
+ (info = NULL ? "" : info));
+ }
+}
+
+static
+void kill_request (char *msg)
+{
+ VG_(umsg) ("%s", msg);
+ remote_close();
+ VG_(exit) (0);
+}
+
+/* handle_gdb_valgrind_command handles the provided mon string command.
+ If command is recognised, return 1 else return 0.
+ Note that in case of ambiguous command, 1 is returned.
+
+ *sink_wanted_at_return is modified if one of the commands
+ 'vg.set *_output' is handled.
+*/
+static
+int handle_gdb_valgrind_command (char* mon, OutputSink* sink_wanted_at_return)
+{
+ UWord ret = 0;
+ char s[strlen(mon)+1]; /* copy for strtok_r */
+ char* wcmd;
+ Char* ssaveptr;
+ char* endptr;
+ int kwdid;
+ int int_value;
+
+ vg_assert (initial_valgrind_sink_saved);
+
+ strcpy (s, mon);
+ wcmd = strtok_r (s, " ", &ssaveptr);
+ /* NB: if possible, avoid introducing a new command below which
+ starts with the same 4 first letters as an already existing
+ command. This ensures a shorter abbreviation for the user. */
+ switch (VG_(keyword_id) ("help vg.set vg.info vg.wait vg.kill vg.translate",
+ wcmd, kwd_report_duplicated_matches)) {
+ case -2:
+ ret = 1;
+ break;
+ case -1:
+ break;
+ case 0: /* help */
+ ret = 1;
+ wcmd = strtok_r (NULL, " ", &ssaveptr);
+ if (wcmd == NULL) {
+ int_value = 0;
+ } else {
+ switch (VG_(keyword_id) ("debug", wcmd, kwd_report_all)) {
+ case -2: int_value = 0; break;
+ case -1: int_value = 0; break;
+ case 0: int_value = 1; break;
+ default: tl_assert (0);
+ }
+ }
+
+ VG_(gdb_printf) (
+"general valgrind monitor commands:\n"
+" help [debug] : monitor command help. With debug: + debugging commands\n"
+" vg.wait [<ms>] : sleep <ms> (default 0) then continue\n"
+" vg.info all_errors : show all errors found so far\n"
+" vg.info last_error : show last error found\n"
+" vg.info n_errs_found : show the nr of errors found so far\n"
+" vg.kill : kill the Valgrind process\n"
+" vg.set gdb_output : set valgrind output to gdb\n"
+" vg.set log_output : set valgrind output to log\n"
+" vg.set mixed_output : set valgrind output to log, interactive output to gdb\n"
+" vg.set vgdb-error <errornr> : debug me at error >= <errornr> \n");
+ if (int_value) { VG_(gdb_printf) (
+"debugging valgrind internals monitor commands:\n"
+" vg.info gdbserver_status : show gdbserver status\n"
+" vg.info memory : show valgrind heap memory stats\n"
+" vg.set debuglog <level> : set valgrind debug log level to <level>\n"
+" vg.translate <addr> [<traceflags>] : debug translation of <addr> with <traceflags>\n"
+" (default traceflags 0b00100000 : show after instrumentation)\n"
+" An additional flag 0b100000000 allows to show gdbserver instrumentation\n");
+ }
+ break;
+ case 1: /* vg.set */
+ ret = 1;
+ wcmd = strtok_r (NULL, " ", &ssaveptr);
+ switch (kwdid = VG_(keyword_id)
+ ("vgdb-error debuglog gdb_output log_output mixed_output",
+ wcmd, kwd_report_all)) {
+ case -2:
+ case -1:
+ break;
+ case 0: /* vgdb-error */
+ case 1: /* debuglog */
+ wcmd = strtok_r (NULL, " ", &ssaveptr);
+ if (wcmd == NULL) {
+ int_value = 0;
+ endptr = "empty"; /* to report an error below */
+ } else {
+ int_value = strtol (wcmd, &endptr, 10);
+ }
+ if (*endptr != '\0') {
+ VG_(gdb_printf) ("missing or malformed integer value\n");
+ } else if (kwdid == 0) {
+ VG_(gdb_printf) ("vgdb-error value changed from %d to %d\n",
+ VG_(dyn_vgdb_error), int_value);
+ VG_(dyn_vgdb_error) = int_value;
+ } else if (kwdid == 1) {
+ VG_(gdb_printf) ("debuglog value changed from %d to %d\n",
+ VG_(debugLog_getLevel)(), int_value);
+ VG_(debugLog_startup) (int_value, "gdbsrv");
+ } else {
+ vg_assert (0);
+ }
+ break;
+ case 2: /* gdb_output */
+ (*sink_wanted_at_return).fd = -2;
+ command_output_to_log = False;
+ VG_(gdb_printf) ("valgrind output will go to gdb\n");
+ break;
+ case 3: /* log_output */
+ (*sink_wanted_at_return).fd = initial_valgrind_sink.fd;
+ command_output_to_log = True;
+ VG_(gdb_printf) ("valgrind output will go to log\n");
+ break;
+ case 4: /* mixed output */
+ (*sink_wanted_at_return).fd = initial_valgrind_sink.fd;
+ command_output_to_log = False;
+ VG_(gdb_printf)
+ ("valgrind output will go to log, interactive output will go to gdb\n");
+ break;
+ default:
+ vg_assert (0);
+ }
+ break;
+ case 2: /* vg.info */ {
+ ret = 1;
+ wcmd = strtok_r (NULL, " ", &ssaveptr);
+ switch (kwdid = VG_(keyword_id)
+ ("all_errors n_errs_found last_error gdbserver_status memory",
+ wcmd, kwd_report_all)) {
+ case -2:
+ case -1:
+ break;
+ case 0: // all_errors
+ // A verbosity of minimum 2 is needed to show the errors.
+ VG_(show_all_errors)(/* verbosity */ 2, /* xml */ False);
+ break;
+ case 1: // n_errs_found
+ VG_(gdb_printf) ("n_errs_found %d (vgdb-error %d)\n",
+ VG_(get_n_errs_found) (),
+ VG_(dyn_vgdb_error));
+ break;
+ case 2: // last_error
+ VG_(show_last_error)();
+ break;
+ case 3: // gdbserver_status
+ VG_(gdbserver_status_output)();
+ break;
+ case 4: /* memory */
+ VG_(print_all_arena_stats) ();
+ if (VG_(clo_profile_heap))
+ VG_(print_arena_cc_analysis) ();
+ ret = 1;
+ break;
+ default:
+ vg_assert(0);
+ }
+ break;
+ }
+ case 3: /* vg.wait */
+ wcmd = strtok_r (NULL, " ", &ssaveptr);
+ if (wcmd != NULL) {
+ int_value = strtol (wcmd, &endptr, 10);
+ VG_(gdb_printf) ("gdbserver: continuing in %d ms ...\n", int_value);
+ VG_(poll)(NULL, 0, int_value);
+ }
+ VG_(gdb_printf) ("gdbserver: continuing after wait ...\n");
+ ret = 1;
+ break;
+ case 4: /* vg.kill */
+ kill_request ("monitor command request to kill this process\n");
+ break;
+ case 5: { /* vg.translate */
+ Addr address;
+ SizeT verbosity = 0x20;
+
+ ret = 1;
+
+ VG_(strtok_get_address_and_size) (&address, &verbosity, &ssaveptr);
+ if (address != (Addr) 0 || verbosity != 0) {
+ /* we need to force the output to log for the translation trace,
+ as low level VEX tracing cannot be redirected to gdb. */
+ int saved_command_output_to_log = command_output_to_log;
+ int saved_fd = VG_(log_output_sink).fd;
+ Bool single_stepping_on_entry = valgrind_single_stepping();
+ int vex_verbosity = verbosity & 0xff;
+ VG_(log_output_sink).fd = initial_valgrind_sink.fd;
+ if ((verbosity & 0x100) && !single_stepping_on_entry) {
+ valgrind_set_single_stepping(True);
+ // to force gdbserver instrumentation.
+ }
+ VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to debugging*/,
+ address,
+ /*debugging*/True,
+ (Int) vex_verbosity,
+ /*bbs_done*/0,
+ /*allow redir?*/True);
+ if ((verbosity & 0x100) && !single_stepping_on_entry) {
+ valgrind_set_single_stepping(False);
+ // reset single stepping.
+ }
+ command_output_to_log = saved_command_output_to_log;
+ VG_(log_output_sink).fd = saved_fd;
+ }
+ break;
+ }
+
+ default:
+ vg_assert (0);
+ }
+ return ret;
+}
+
+/* handle_gdb_monitor_command handles the provided mon string command,
+ which can be either a "standard" valgrind monitor command
+ or a tool specific monitor command.
+ If command recognised, return 1 else return 0.
+ Note that in case of ambiguous command, 1 is returned.
+*/
+static
+int handle_gdb_monitor_command (char* mon)
+{
+ UWord ret = 0;
+ UWord tool_ret = 0;
+ // initially, we assume that when returning, the desired sink is the
+ // one we have when entering. It can however be changed by the standard
+ // valgrind command handling.
+ OutputSink sink_wanted_at_return = VG_(log_output_sink);
+
+ if (!initial_valgrind_sink_saved) {
+ /* first time we enter here, we save the valgrind default log sink */
+ initial_valgrind_sink = sink_wanted_at_return;
+ initial_valgrind_sink_saved = True;
+ }
+
+ if (!command_output_to_log)
+ VG_(log_output_sink).fd = -2; /* redirect to monitor_output */
+
+ ret = handle_gdb_valgrind_command (mon, &sink_wanted_at_return);
+
+ /* Even if command was recognised by valgrind core, we call the
+ tool command handler : this is needed to handle help command
+ and/or to let the tool do some additional processing of a
+ valgrind standard command. Note however that if valgrind
+ recognised the command, we will always return success. */
+ if (VG_(needs).client_requests) {
+ /* If the tool reports an error when handling a monitor command,
+ we need to avoid calling gdbserver during this command
+ handling. So, we temporarily set VG_(dyn_vgdb_error) to
+ a huge value to ensure m_errormgr.c does not call gdbserver. */
+ Int save_dyn_vgdb_error = VG_(dyn_vgdb_error);
+ UWord arg[2];
+ VG_(dyn_vgdb_error) = 999999999;
+ arg[0] = (UWord) VG_USERREQ__GDB_MONITOR_COMMAND;
+ arg[1] = (UWord) mon;
+ VG_TDICT_CALL(tool_handle_client_request, VG_(running_tid), arg,
+ &tool_ret);
+ VG_(dyn_vgdb_error) = save_dyn_vgdb_error;
+ }
+
+ /* restore or set the desired output */
+ VG_(log_output_sink).fd = sink_wanted_at_return.fd;
+ if (ret | tool_ret)
+ return 1;
+ else
+ return 0;
+}
+
+
+/* Handle all of the extended 'Q' packets. */
+static
+void handle_set (char *arg_own_buf, int *new_packet_len_p)
+{
+ if (strcmp ("QStartNoAckMode", arg_own_buf) == 0) {
+ noack_mode = True;
+ write_ok (arg_own_buf);
+ return;
+ }
+
+ if (strncmp ("QPassSignals:", arg_own_buf, 13) == 0) {
+ int i;
+ char *from, *to;
+ char *end = arg_own_buf + strlen(arg_own_buf);
+ CORE_ADDR sig;
+ for (i = 0; i < TARGET_SIGNAL_LAST; i++)
+ pass_signals[i] = 0;
+
+ from = arg_own_buf + 13;
+ while (from < end) {
+ to = strchr(from, ';');
+ if (to == NULL) to = end;
+ decode_address (&sig, from, to - from);
+ pass_signals[(int)sig] = 1;
+ dlog(1, "pass_signal %d\n", (int)sig);
+ from = to;
+ if (*from == ';') from++;
+ }
+ write_ok (arg_own_buf);
+ return;
+ }
+ /* Otherwise we didn't know what packet it was. Say we didn't
+ understand it. */
+ arg_own_buf[0] = 0;
+}
+
+/* Handle all of the extended 'q' packets. */
+static
+void handle_query (char *arg_own_buf, int *new_packet_len_p)
+{
+ static struct inferior_list_entry *thread_ptr;
+
+ /* qRcmd, monitor command handling. */
+ if (strncmp ("qRcmd,", arg_own_buf, 6) == 0) {
+ char *p = arg_own_buf + 6;
+ int cmdlen = strlen(p)/2;
+ char cmd[cmdlen+1];
+
+ if (unhexify (cmd, p, cmdlen) != cmdlen) {
+ write_enn (arg_own_buf);
+ return;
+ }
+ cmd[cmdlen] = '\0';
+
+ if (handle_gdb_monitor_command (cmd)) {
+ /* In case the command is from a standalone vgdb,
+ connection will be closed soon => flush the output. */
+ VG_(message_flush) ();
+ write_ok (arg_own_buf);
+ return;
+ } else {
+ /* cmd not recognised */
+ VG_(gdb_printf)
+ ("command '%s' not recognised\n"
+ "In gdb, try 'monitor help'\n"
+ "In a shell, try 'vgdb help'\n",
+ cmd);
+ write_ok (arg_own_buf);
+ return;
+ }
+ }
+
+ /* provide some valgrind specific info in return to qThreadExtraInfo. */
+ if (strncmp ("qThreadExtraInfo,", arg_own_buf, 17) == 0) {
+ unsigned long gdb_id;
+ struct thread_info *ti;
+ ThreadState *tst;
+ char status[100];
+
+ gdb_id = strtoul (&arg_own_buf[17], NULL, 16);
+ ti = gdb_id_to_thread (gdb_id);
+ if (ti != NULL) {
+ tst = (ThreadState *) inferior_target_data (ti);
+ /* Additional info is the tid and the thread status. */
+ VG_(snprintf) (status, sizeof(status), "tid %d %s",
+ tst->tid,
+ VG_(name_of_ThreadStatus)(tst->status));
+ hexify (arg_own_buf, status, strlen(status));
+ return;
+ } else {
+ write_enn (arg_own_buf);
+ return;
+ }
+ }
+
+ if (strcmp ("qAttached", arg_own_buf) == 0) {
+ /* tell gdb to always detach, never kill the process */
+ arg_own_buf[0] = '1';
+ arg_own_buf[1] = 0;
+ return;
+ }
+
+ if (strcmp ("qSymbol::", arg_own_buf) == 0) {
+ /* We have no symbol to read. */
+ write_ok (arg_own_buf);
+ return;
+ }
+
+ if (strcmp ("qfThreadInfo", arg_own_buf) == 0) {
+ thread_ptr = all_threads.head;
+ VG_(sprintf) (arg_own_buf, "m%x",
+ thread_to_gdb_id ((struct thread_info *)thread_ptr));
+ thread_ptr = thread_ptr->next;
+ return;
+ }
+
+ if (strcmp ("qsThreadInfo", arg_own_buf) == 0) {
+ if (thread_ptr != NULL) {
+ VG_(sprintf) (arg_own_buf, "m%x",
+ thread_to_gdb_id ((struct thread_info *)thread_ptr));
+ thread_ptr = thread_ptr->next;
+ return;
+ } else {
+ VG_(sprintf) (arg_own_buf, "l");
+ return;
+ }
+ }
+
+ if ( ((*the_target->target_xml)() != NULL
+ || (*the_target->shadow_target_xml)() != NULL)
+ && strncmp ("qXfer:features:read:", arg_own_buf, 20) == 0) {
+ CORE_ADDR ofs;
+ unsigned int len, doc_len;
+ char *annex = NULL;
+ // First, the annex is extracted from the packet received.
+ // Then, it is replaced by the corresponding file name.
+ int fd;
+
+ /* Grab the annex, offset, and length. */
+ if (decode_xfer_read (arg_own_buf + 20, &annex, &ofs, &len) < 0) {
+ strcpy (arg_own_buf, "E00");
+ return;
+ }
+
+ if (strcmp (annex, "target.xml") == 0) {
+ annex = NULL; // to replace it by the corresponding filename.
+
+ /* If VG_(clo_vgdb_shadow_registers), try to use
+ shadow_target_xml. Fallback to target_xml
+ if not defined. */
+ if (VG_(clo_vgdb_shadow_registers)) {
+ annex = (*the_target->shadow_target_xml)();
+ if (annex != NULL)
+ /* Ensure the shadow registers are initialized. */
+ initialize_shadow_low(True);
+ }
+ if (annex == NULL)
+ annex = (*the_target->target_xml)();
+ if (annex == NULL) {
+ strcpy (arg_own_buf, "E00");
+ return;
+ }
+ }
+
+ {
+ char doc[VG_(strlen)(VG_(libdir)) + 1 + VG_(strlen)(annex)];
+ struct vg_stat stat_doc;
+ char toread[len];
+ int len_read;
+
+ VG_(sprintf)(doc, "%s/%s", VG_(libdir), annex);
+ fd = VG_(fd_open) (doc, VKI_O_RDONLY, 0);
+ if (fd == -1) {
+ strcpy (arg_own_buf, "E00");
+ return;
+ }
+ if (VG_(fstat) (fd, &stat_doc) != 0) {
+ VG_(close) (fd);
+ strcpy (arg_own_buf, "E00");
+ return;
+ }
+ doc_len = stat_doc.size;
+
+ if (len > PBUFSIZ - POVERHSIZ)
+ len = PBUFSIZ - POVERHSIZ;
+
+ if (ofs > doc_len) {
+ write_enn (arg_own_buf);
+ VG_(close) (fd);
+ return;
+ }
+ VG_(lseek) (fd, ofs, VKI_SEEK_SET);
+ len_read = VG_(read) (fd, toread, len);
+ *new_packet_len_p = write_qxfer_response (arg_own_buf, toread,
+ len_read, ofs + len_read < doc_len);
+ VG_(close) (fd);
+ return;
+ }
+ }
+
+ /* Protocol features query. */
+ if (strncmp ("qSupported", arg_own_buf, 10) == 0
+ && (arg_own_buf[10] == ':' || arg_own_buf[10] == '\0')) {
+ VG_(sprintf) (arg_own_buf, "PacketSize=%x", PBUFSIZ - 1);
+ /* Note: max packet size including frame and checksum, but without
+ trailing null byte, which is not sent/received. */
+
+ strcat (arg_own_buf, ";QStartNoAckMode+");
+ strcat (arg_own_buf, ";QPassSignals+");
+
+ if ((*the_target->target_xml)() != NULL
+ || (*the_target->shadow_target_xml)() != NULL) {
+ strcat (arg_own_buf, ";qXfer:features:read+");
+ /* if a new gdb connects to us, we have to reset the register
+ set to the normal register sets to allow this new gdb to
+ decide to use or not the shadow registers.
+
+ Note that the reset is only done for gdb that are sending
+ qSupported packets. If a user first connected with a recent
+ gdb using shadow registers and then with a very old gdb
+ that does not use qSupported packet, then the old gdb will
+ not properly connect. */
+ initialize_shadow_low(False);
+ }
+ return;
+ }
+
+ /* Otherwise we didn't know what packet it was. Say we didn't
+ understand it. */
+ arg_own_buf[0] = 0;
+}
+
+/* Handle all of the extended 'v' packets. */
+static
+void handle_v_requests (char *arg_own_buf, char *status, int *signal)
+{
+ /* vcont packet code from gdb 6.6 removed */
+
+ /* Otherwise we didn't know what packet it was. Say we didn't
+ understand it. */
+ arg_own_buf[0] = 0;
+ return;
+}
+
+static
+void myresume (int step, int sig)
+{
+ struct thread_resume resume_info[2];
+ int n = 0;
+
+ if (step || sig || (cont_thread != 0 && cont_thread != -1)) {
+ resume_info[0].thread
+ = ((struct inferior_list_entry *) current_inferior)->id;
+ resume_info[0].step = step;
+ resume_info[0].sig = sig;
+ resume_info[0].leave_stopped = 0;
+ n++;
+ }
+ resume_info[n].thread = -1;
+ resume_info[n].step = 0;
+ resume_info[n].sig = 0;
+ resume_info[n].leave_stopped = (cont_thread != 0 && cont_thread != -1);
+
+ resume_packet_needed = True;
+ (*the_target->resume) (resume_info);
+}
+
+/* server_main global variables */
+static char *own_buf;
+static unsigned char *mem_buf;
+
+void gdbserver_init (void)
+{
+ dlog(1, "gdbserver_init gdbserver embedded in valgrind: %s\n", version);
+ noack_mode = False;
+ initialize_low ();
+ own_buf = malloc (PBUFSIZ);
+ mem_buf = malloc (PBUFSIZ);
+}
+
+void gdbserver_terminate (void)
+{
+ /* last call to gdbserver is cleanup call */
+ if (VG_MINIMAL_SETJMP(toplevel)) {
+ dlog(0, "error caused VG_MINIMAL_LONGJMP to gdbserver_terminate\n");
+ return;
+ }
+ remote_close();
+}
+
+void server_main (void)
+{
+ static char status;
+ static int signal;
+
+ char ch;
+ int i = 0;
+ unsigned int len;
+ CORE_ADDR mem_addr;
+
+ signal = mywait (&status);
+ if (VG_MINIMAL_SETJMP(toplevel)) {
+ dlog(0, "error caused VG_MINIMAL_LONGJMP to server_main\n");
+ }
+ while (1) {
+ unsigned char sig;
+ int packet_len;
+ int new_packet_len = -1;
+
+ if (resume_packet_needed) {
+ resume_packet_needed = False;
+ prepare_resume_reply (own_buf, status, signal);
+ putpkt (own_buf);
+ }
+
+ packet_len = getpkt (own_buf);
+ if (packet_len <= 0)
+ break;
+
+ i = 0;
+ ch = own_buf[i++];
+ switch (ch) {
+ case 'Q':
+ handle_set (own_buf, &new_packet_len);
+ break;
+ case 'q':
+ handle_query (own_buf, &new_packet_len);
+ break;
+ case 'd':
+ /* set/unset debugging is done through valgrind debug level. */
+ own_buf[0] = '\0';
+ break;
+ case 'D':
+ reset_valgrind_sink("gdb detaching from process");
+
+ /* When detaching or kill the process, gdb expects to get
+ an packet OK back. Any other output will make gdb
+ believes detach did not work. */
+ write_ok (own_buf);
+ putpkt (own_buf);
+ remote_finish (reset_after_error);
+ remote_open (VG_(clo_vgdb_prefix));
+ myresume (0, 0);
+ resume_packet_needed = False;
+ return;
+ case '!':
+ /* We can not use the extended protocol with valgrind,
+ because we can not restart the running
+ program. So return unrecognized. */
+ own_buf[0] = '\0';
+ break;
+ case '?':
+ prepare_resume_reply (own_buf, status, signal);
+ break;
+ case 'H':
+ if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') {
+ unsigned long gdb_id, thread_id;
+
+ gdb_id = strtoul (&own_buf[2], NULL, 16);
+ thread_id = gdb_id_to_thread_id (gdb_id);
+ if (thread_id == 0) {
+ write_enn (own_buf);
+ break;
+ }
+
+ if (own_buf[1] == 'g') {
+ general_thread = thread_id;
+ set_desired_inferior (1);
+ } else if (own_buf[1] == 'c') {
+ cont_thread = thread_id;
+ } else if (own_buf[1] == 's') {
+ step_thread = thread_id;
+ }
+
+ write_ok (own_buf);
+ } else {
+ /* Silently ignore it so that gdb can extend the protocol
+ without compatibility headaches. */
+ own_buf[0] = '\0';
+ }
+ break;
+ case 'g':
+ set_desired_inferior (1);
+ registers_to_string (own_buf);
+ break;
+ case 'G':
+ set_desired_inferior (1);
+ registers_from_string (&own_buf[1]);
+ write_ok (own_buf);
+ break;
+ case 'P': {
+ int regno;
+ char *regbytes;
+ Bool mod;
+ ThreadState *tst;
+ regno = strtol(&own_buf[1], NULL, 16);
+ regbytes = strchr(&own_buf[0], '=') + 1;
+ set_desired_inferior (1);
+ tst = (ThreadState *) inferior_target_data (current_inferior);
+ /* Only accept changing registers in "runnable state3.
+ In fact, it would be ok to change most of the registers
+ except a few "sensitive" registers such as the PC, SP, BP.
+ We assume we do not need to very specific here, and that we
+ can just refuse all of these. */
+ if (tst->status == VgTs_Runnable || tst->status == VgTs_Yielding) {
+ supply_register_from_string (regno, regbytes, &mod);
+ write_ok (own_buf);
+ } else {
+ /* at least from gdb 6.6 onwards, an E. error
+ reply is shown to the user. So, we do an error
+ msg which both is accepted by gdb as an error msg
+ and is readable by the user. */
+ VG_(sprintf)
+ (own_buf,
+"E.\n"
+"ERROR changing register %s regno %d\n"
+"gdb commands changing registers (pc, sp, ...) (e.g. 'jump',\n"
+"set pc, calling from gdb a function in the debugged process, ...)\n"
+"can only be accepted if the thread is VgTs_Runnable or VgTs_Yielding state\n"
+"Thread status is %s\n",
+ find_register_by_number (regno)->name, regno,
+ VG_(name_of_ThreadStatus)(tst->status));
+ if (VG_(clo_verbosity) > 1)
+ VG_(umsg) ("%s\n", own_buf);
+ }
+ break;
+ }
+ case 'm':
+ decode_m_packet (&own_buf[1], &mem_addr, &len);
+ if (read_inferior_memory (mem_addr, mem_buf, len) == 0)
+ convert_int_to_ascii (mem_buf, own_buf, len);
+ else
+ write_enn (own_buf);
+ break;
+ case 'M':
+ decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
+ if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
+ write_ok (own_buf);
+ else
+ write_enn (own_buf);
+ break;
+ case 'X':
+ if (decode_X_packet (&own_buf[1], packet_len - 1,
+ &mem_addr, &len, mem_buf) < 0
+ || write_inferior_memory (mem_addr, mem_buf, len) != 0)
+ write_enn (own_buf);
+ else
+ write_ok (own_buf);
+ break;
+ case 'C':
+ convert_ascii_to_int (own_buf + 1, &sig, 1);
+ if (target_signal_to_host_p (sig))
+ signal = target_signal_to_host (sig);
+ else
+ signal = 0;
+ set_desired_inferior (0);
+ myresume (0, signal);
+ return; // return control to valgrind
+ case 'S':
+ convert_ascii_to_int (own_buf + 1, &sig, 1);
+ if (target_signal_to_host_p (sig))
+ signal = target_signal_to_host (sig);
+ else
+ signal = 0;
+ set_desired_inferior (0);
+ myresume (1, signal);
+ return; // return control to valgrind
+ case 'c':
+ set_desired_inferior (0);
+ myresume (0, 0);
+ return; // return control to valgrind
+ case 's':
+ set_desired_inferior (0);
+ myresume (1, 0);
+ return; // return control to valgrind
+ case 'Z': {
+ char *lenptr;
+ char *dataptr;
+ CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16);
+ int zlen = strtol (lenptr + 1, &dataptr, 16);
+ char type = own_buf[1];
+
+ if (the_target->insert_watchpoint == NULL
+ || (type < '0' || type > '4')) {
+ /* No watchpoint support or not a watchpoint command;
+ unrecognized either way. */
+ own_buf[0] = '\0';
+ } else {
+ int res;
+
+ res = (*the_target->insert_watchpoint) (type, addr, zlen);
+ if (res == 0)
+ write_ok (own_buf);
+ else if (res == 1)
+ /* Unsupported. */
+ own_buf[0] = '\0';
+ else
+ write_enn (own_buf);
+ }
+ break;
+ }
+ case 'z': {
+ char *lenptr;
+ char *dataptr;
+ CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16);
+ int zlen = strtol (lenptr + 1, &dataptr, 16);
+ char type = own_buf[1];
+
+ if (the_target->remove_watchpoint == NULL
+ || (type < '0' || type > '4')) {
+ /* No watchpoint support or not a watchpoint command;
+ unrecognized either way. */
+ own_buf[0] = '\0';
+ } else {
+ int res;
+
+ res = (*the_target->remove_watchpoint) (type, addr, zlen);
+ if (res == 0)
+ write_ok (own_buf);
+ else if (res == 1)
+ /* Unsupported. */
+ own_buf[0] = '\0';
+ else
+ write_enn (own_buf);
+ }
+ break;
+ }
+ case 'k':
+ kill_request("Gdb request to kill this process\n");
+ break;
+ case 'T': {
+ unsigned long gdb_id, thread_id;
+
+ gdb_id = strtoul (&own_buf[1], NULL, 16);
+ thread_id = gdb_id_to_thread_id (gdb_id);
+ if (thread_id == 0) {
+ write_enn (own_buf);
+ break;
+ }
+
+ if (mythread_alive (thread_id))
+ write_ok (own_buf);
+ else
+ write_enn (own_buf);
+ break;
+ }
+ case 'R':
+ /* Restarting the inferior is only supported in the
+ extended protocol.
+ => It is a request we don't understand. Respond with an
+ empty packet so that gdb knows that we don't support this
+ request. */
+ own_buf[0] = '\0';
+ break;
+ case 'v':
+ /* Extended (long) request. */
+ handle_v_requests (own_buf, &status, &signal);
+ break;
+ default:
+ /* It is a request we don't understand. Respond with an
+ empty packet so that gdb knows that we don't support this
+ request. */
+ own_buf[0] = '\0';
+ break;
+ }
+
+ if (new_packet_len != -1)
+ putpkt_binary (own_buf, new_packet_len);
+ else
+ putpkt (own_buf);
+
+ if (status == 'W')
+ VG_(umsg) ("\nChild exited with status %d\n", signal);
+ if (status == 'X')
+ VG_(umsg) ("\nChild terminated with signal = 0x%x (%s)\n",
+ target_signal_to_host (signal),
+ target_signal_to_name (signal));
+ if (status == 'W' || status == 'X') {
+ VG_(umsg) ("Process exiting\n");
+ VG_(exit) (0);
+ }
+ }
+
+ /* We come here when getpkt fails => close the connection,
+ and re-open. Then return control to valgrind.
+ We return the control to valgrind as we assume that
+ the connection was closed due to vgdb having finished
+ to execute a command. */
+ if (VG_(clo_verbosity) > 1)
+ VG_(umsg) ("Remote side has terminated connection. "
+ "GDBserver will reopen the connection.\n");
+ remote_finish (reset_after_error);
+ remote_open (VG_(clo_vgdb_prefix));
+ myresume (0, 0);
+ resume_packet_needed = False;
+ return;
+}
diff --git a/coregrind/m_gdbserver/server.h b/coregrind/m_gdbserver/server.h
new file mode 100644
index 0000000..2b6ae0e
--- /dev/null
+++ b/coregrind/m_gdbserver/server.h
@@ -0,0 +1,371 @@
+/* Common definitions for remote server for GDB.
+ Copyright (C) 1993, 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005,
+ 2006
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#ifndef SERVER_H
+#define SERVER_H
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_xarray.h"
+#include "pub_core_clientstate.h"
+#include "pub_core_debuglog.h"
+#include "pub_core_errormgr.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcfile.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_syscall.h"
+#include "pub_tool_libcproc.h"
+#include "pub_core_tooliface.h"
+#include "pub_tool_libcassert.h"
+#include "pub_tool_libcbase.h"
+#include "pub_tool_options.h"
+#include "pub_core_gdbserver.h"
+#include "pub_tool_libcsetjmp.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_vki.h"
+#include "valgrind.h"
+
+/*------------- interface m_gdbserver <=> low level gdbserver */
+
+/* Initializes gdbserver. After a call to gdbserver_init, vgdb
+ can contact the gdbserver embedded in valgrind.
+ The rest of the low level gdbserver interface can only
+ be called */
+extern void gdbserver_init (void);
+
+extern void server_main (void);
+
+/* To be called to indicate that gdbserver usage is finished.
+ Resources (e.g. FIFOs) will be destroyed. */
+extern void gdbserver_terminate (void);
+
+
+/* Output string s to the gdb debugging this process or to vgdb.
+ Do not call this directly. Rather use VG_(monitor_print)
+ to output something to gdb, use normal valgrind messaging
+ (e.g. VG_(umsg)) to send output that can either go
+ to gdb or to log. */
+extern void monitor_output (char *s);
+
+/* returns 0 if there is no connection or no event on the connection
+ with gdb.
+ returns 1 if there are some data which has been received from gdb
+ and that must (still) be handled.
+ returns 2 if remote_desc_activity detected the connection has been
+ lost and should be reopened.
+ msg is used for debug logging.*/
+extern int remote_desc_activity(char *msg);
+
+/* output some status of gdbserver communication */
+extern void remote_utils_output_status(void);
+
+/* True if there is a connection with gdb. */
+extern Bool remote_connected(void);
+
+/* Finish the connection with gdb and reset_valgrind_sink.
+ Keeps the FIFOs and shared mem so as to allow connection
+ to be reopened. */
+extern void remote_finish(FinishReason reason);
+
+/* If Valgrind sink was changed by gdbserver:
+ Resets the valgrind sink to before the changes done by gdbserver,
+ and does VG_(umsg). If info != NULL, info added in VG_(usmg). */
+extern void reset_valgrind_sink(char* info);
+
+
+/* True if gdbserver is single stepping the valgrind process */
+extern Bool valgrind_single_stepping(void);
+
+/* Set Valgrind in single stepping mode or not according to Bool. */
+extern void valgrind_set_single_stepping(Bool);
+
+/* gets the addr at which a (possible) break must be ignored once.
+ If there is no such break to be ignored once, 0 is returned.
+ This is needed for the following case:
+ The user sets a break at address AAA.
+ The break is encountered. Then the user does stepi
+ (i.e. step one instruction).
+ In such a case, the already encountered break must be ignored
+ to ensure the stepi will advance by one instruction: a "break"
+ is implemented in valgrind by some helper code just after the
+ instruction mark at which the break is set. This helper code
+ verifies if either there is a break at the current PC
+ or if we are in stepping mode. If we are in stepping mode,
+ the already encountered break must be ignored once to advance
+ to the next instruction.
+ ??? need to check if this is *really* needed. */
+extern Addr valgrind_get_ignore_break_once(void);
+
+/* When addr > 0, ensures the next stop reply packet informs
+ gdb about the encountered watchpoint.
+ Use addr 0x0 to reset. */
+extern void VG_(set_watchpoint_stop_address) (Addr addr);
+
+/* when invoked by vgdb using ptrace, contains the tid chosen
+ by vgdb (if vgdb gives a tid different of 0: a 0 tid by
+ vgdb means use the running_tid if there is one running
+ or tid 1 otherwise). */
+extern ThreadId vgdb_interrupted_tid;
+
+/*------------ end of interface to low level gdbserver */
+
+
+#define dlog(level, ...) \
+ do { if (UNLIKELY(VG_(debugLog_getLevel)() >= level)) \
+ VG_(debugLog) (level, "gdbsrv",__VA_ARGS__); } \
+ while (0)
+
+
+/* vki only defines VKI_POLLIN but even not on all OS.
+ Below is from linux bits/poll.h */
+#ifndef VKI_POLLIN
+#define VKI_POLLIN 0x0001
+#endif
+#define VKI_POLLPRI 0x0002
+#define VKI_POLLOUT 0x0004
+#define VKI_POLLERR 0x0008
+#define VKI_POLLHUP 0x0010
+#define VKI_POLLNVAL 0x0020
+
+/* a bunch of macros to avoid libc usage in valgrind-ified gdbserver */
+#define strcmp(s1,s2) VG_(strcmp) ((Char *)(s1),(Char *)(s2))
+#define strncmp(s1,s2,nmax) VG_(strncmp) ((Char *)(s1),(Char *)(s2),nmax)
+#define strcat(s1,s2) VG_(strcat) ((Char *)(s1),(Char *)(s2))
+#define strcpy(s1,s2) VG_(strcpy) ((Char *)(s1),(Char *)(s2))
+#define strncpy(s1,s2,nmax) VG_(strncpy) ((Char *)(s1),(Char *)(s2),nmax)
+#define strlen(s) VG_(strlen) ((Char *)(s))
+#define strtok(p,s) (char *) VG_(strtok) ((Char *)(p),(Char *)(s))
+#define strtok_r(p,s,ss) (char *) VG_(strtok_r) ((Char *)(p),(Char *)(s),(Char **)(ss))
+#define strchr(s,c) (char *) VG_(strchr) ((Char *)(s),c)
+/* strtol and strtoul supports base 16 or else assumes it is base 10 */
+#define strtol(s,r,b) ((b) == 16 ? \
+ VG_(strtoll16) ((Char *)(s),(Char **)(r)) \
+ : VG_(strtoll10) ((Char *)(s),(Char **)(r)))
+#define strtoul(s,r,b) ((b) == 16 ? \
+ VG_(strtoull16) ((Char *)(s),(Char **)(r)) \
+ : VG_(strtoull10) ((Char *)(s),(Char **)(r)))
+
+#define malloc(sz) VG_(arena_malloc) (VG_AR_CORE, "gdbsrv", sz)
+#define calloc(n,sz) VG_(arena_calloc) (VG_AR_CORE, "gdbsrv", n, sz)
+#define realloc(p,size) VG_(arena_realloc) (VG_AR_CORE, "gdbsrv", p, size)
+#define strdup(s) (char *) VG_(arena_strdup) (VG_AR_CORE, "gdbsrv", (Char *)(s))
+#define free(b) VG_(arena_free) (VG_AR_CORE, b)
+
+#ifndef ATTR_NORETURN
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7))
+#define ATTR_NORETURN __attribute__ ((noreturn))
+#else
+#define ATTR_NORETURN /* nothing */
+#endif
+#endif
+
+#ifndef ATTR_FORMAT
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 4))
+#define ATTR_FORMAT(type, x, y) __attribute__ ((format(type, x, y)))
+#else
+#define ATTR_FORMAT(type, x, y) /* nothing */
+#endif
+#endif
+
+/* A type used for binary buffers. */
+typedef unsigned char gdb_byte;
+
+typedef Addr CORE_ADDR;
+
+/* Generic information for tracking a list of ``inferiors'' - threads,
+ processes, etc. */
+struct inferior_list
+{
+ struct inferior_list_entry *head;
+ struct inferior_list_entry *tail;
+};
+struct inferior_list_entry
+{
+ unsigned long id;
+ struct inferior_list_entry *next;
+};
+
+/* Opaque type for user-visible threads. */
+struct thread_info;
+
+#include "regcache.h"
+#include "gdb/signals.h"
+
+/* signal handling with gdbserver: before delivering a signal,
+ call gdbserver_signal_encountered then give control to
+ gdbserver by calling call_gdbserver.
+ On return, call gdbserver_deliver_signal to effectively
+ deliver the signal or not. */
+extern void gdbserver_signal_encountered (Int sigNo);
+/* between these two calls, call call_gdbserver */
+/* If gdbserver_deliver_signal True, then gdb did not ask
+ to ignore the signal, so signal can be delivered to the guest. */
+extern Bool gdbserver_deliver_signal (Int sigNo);
+
+/* To optimise signal handling, gdb can instruct gdbserver to
+ not stop on some signals. In the below, a 1 indicates the signal
+ has to be passed directly to the guest, without asking gdb.
+ A 0 indicates gdb has to be consulted to see if signal has
+ or has not to be passed. The gdb consultation is to
+ be done using the above two functions. */
+int pass_signals[TARGET_SIGNAL_LAST];
+
+
+#include "target.h"
+
+/* Target-specific functions */
+
+void initialize_low (void);
+
+/* initialize or re-initialize the register set of the low target.
+ if shadow_mode, then (re-)define the normal and valgrind shadow registers
+ else (re-)define only the normal registers. */
+void initialize_shadow_low (Bool shadow_mode);
+
+/* From inferiors.c. */
+
+extern struct inferior_list all_threads;
+void add_inferior_to_list (struct inferior_list *list,
+ struct inferior_list_entry *new_inferior);
+void for_each_inferior (struct inferior_list *list,
+ void (*action) (struct inferior_list_entry *));
+extern struct thread_info *current_inferior;
+void remove_inferior (struct inferior_list *list,
+ struct inferior_list_entry *entry);
+void remove_thread (struct thread_info *thread);
+void add_thread (unsigned long thread_id, void *target_data, unsigned int);
+unsigned int thread_id_to_gdb_id (unsigned long);
+unsigned int thread_to_gdb_id (struct thread_info *);
+unsigned long gdb_id_to_thread_id (unsigned int);
+struct thread_info *gdb_id_to_thread (unsigned int);
+void clear_inferiors (void);
+struct inferior_list_entry *find_inferior (struct inferior_list *,
+ int (*func) (struct
+ inferior_list_entry *,
+ void *),
+ void *arg);
+struct inferior_list_entry *find_inferior_id (struct inferior_list *list,
+ unsigned long id);
+void *inferior_target_data (struct thread_info *);
+void set_inferior_target_data (struct thread_info *, void *);
+void *inferior_regcache_data (struct thread_info *);
+void set_inferior_regcache_data (struct thread_info *, void *);
+void change_inferior_id (struct inferior_list *list,
+ unsigned long new_id);
+
+/* Public variables in server.c */
+
+extern unsigned long cont_thread;
+extern unsigned long general_thread;
+extern unsigned long step_thread;
+extern unsigned long thread_from_wait;
+extern unsigned long old_thread_from_wait;
+
+extern VG_MINIMAL_JMP_BUF(toplevel);
+
+/* From remote-utils.c */
+
+extern Bool noack_mode;
+int putpkt (char *buf);
+int putpkt_binary (char *buf, int len);
+int getpkt (char *buf);
+void remote_open (char *name);
+void remote_close (void);
+
+void sync_gdb_connection (void);
+void write_ok (char *buf);
+void write_enn (char *buf);
+void convert_ascii_to_int (char *from, unsigned char *to, int n);
+void convert_int_to_ascii (unsigned char *from, char *to, int n);
+void prepare_resume_reply (char *buf, char status, unsigned char sig);
+
+void decode_address (CORE_ADDR *addrp, const char *start, int len);
+void decode_m_packet (char *from, CORE_ADDR * mem_addr_ptr,
+ unsigned int *len_ptr);
+void decode_M_packet (char *from, CORE_ADDR * mem_addr_ptr,
+ unsigned int *len_ptr, unsigned char *to);
+int decode_X_packet (char *from, int packet_len, CORE_ADDR * mem_addr_ptr,
+ unsigned int *len_ptr, unsigned char *to);
+
+int unhexify (char *bin, const char *hex, int count);
+int hexify (char *hex, const char *bin, int count);
+int remote_escape_output (const gdb_byte *buffer, int len,
+ gdb_byte *out_buf, int *out_len,
+ int out_maxlen);
+
+/* Functions from ``signals.c''. */
+enum target_signal target_signal_from_host (int hostsig);
+int target_signal_to_host_p (enum target_signal oursig);
+int target_signal_to_host (enum target_signal oursig);
+char *target_signal_to_name (enum target_signal);
+
+/* Functions from utils.c */
+
+/* error is like VG_(umsg), then VG_MINIMAL_LONGJMP to gdbserver toplevel. */
+void error (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2);
+/* first output a description of the error inside sr, then like VG_(umsg). */
+void sr_perror (SysRes sr,char *string,...) ATTR_FORMAT (printf, 2, 3);
+/* fatal is like VG_(umsg), then exit(1). */
+void fatal (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2);
+/* warning is like VG_(umsg). */
+void warning (const char *string,...) ATTR_FORMAT (printf, 1, 2);
+
+/* Functions from the register cache definition. */
+
+void init_registers (void);
+
+/* Maximum number of bytes to read/write at once. The value here
+ is chosen to fill up a packet (the headers account for the 32). */
+#define MAXBUFBYTES(N) (((N)-32)/2)
+
+/* PBUFSIZ : Buffers size for transferring memory, registers, etc.
+ Must be big enough to hold all the registers, at least.
+ Must be at least big as 2*DATASIZ + 5:
+ 1 : packet begin ($ or %)
+ + 2*DATASIZ : encoded string
+ + 1 : packet end (#)
+ + 2 : packet checksum
+ + 1 : \0
+
+ Max value gdb likes is 16384.
+
+ Note that what is sent/received to/from gdb does
+ not have a trailing null byte. We are adding 1 here to allow
+ null terminating the strings e.g. for printf.
+
+ => packet Packet OVERHead SIZe is 5:*/
+
+/* keep PBUFSIZ value in sync with vgdb.c */
+#define PBUFSIZ 16384
+#define POVERHSIZ 5
+
+/* Max size of a string encoded in a packet. Hex Encoding can
+ multiply the size by 2 (trailing null byte not sent). */
+#define DATASIZ ((PBUFSIZ-POVERHSIZ)/2)
+
+/* Version information, from version.c. */
+extern const char version[];
+
+#endif /* SERVER_H */
diff --git a/coregrind/m_gdbserver/signals.c b/coregrind/m_gdbserver/signals.c
new file mode 100644
index 0000000..07b0c24
--- /dev/null
+++ b/coregrind/m_gdbserver/signals.c
@@ -0,0 +1,744 @@
+/* Target signal translation functions for GDB.
+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2011 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+
+#if defined(VGO_darwin)
+// ???? darwin signal.h defines SIGPOLL conditionnally ????
+#ifndef SIGPOLL
+#define SIGPOLL 7
+#endif
+#endif
+
+enum target_signal target_signal_from_name (char *name);
+enum target_signal target_signal_from_command (int num);
+
+/* This table must match in order and size the signals in enum target_signal
+ in gdb/signals.h. */
+/* *INDENT-OFF* */
+static struct {
+ char *name;
+ char *string;
+} signals [] =
+ {
+ {"0", "Signal 0"},
+ {"SIGHUP", "Hangup"},
+ {"SIGINT", "Interrupt"},
+ {"SIGQUIT", "Quit"},
+ {"SIGILL", "Illegal instruction"},
+ {"SIGTRAP", "Trace/breakpoint trap"},
+ {"SIGABRT", "Aborted"},
+ {"SIGEMT", "Emulation trap"},
+ {"SIGFPE", "Arithmetic exception"},
+ {"SIGKILL", "Killed"},
+ {"SIGBUS", "Bus error"},
+ {"SIGSEGV", "Segmentation fault"},
+ {"SIGSYS", "Bad system call"},
+ {"SIGPIPE", "Broken pipe"},
+ {"SIGALRM", "Alarm clock"},
+ {"SIGTERM", "Terminated"},
+ {"SIGURG", "Urgent I/O condition"},
+ {"SIGSTOP", "Stopped (signal)"},
+ {"SIGTSTP", "Stopped (user)"},
+ {"SIGCONT", "Continued"},
+ {"SIGCHLD", "Child status changed"},
+ {"SIGTTIN", "Stopped (tty input)"},
+ {"SIGTTOU", "Stopped (tty output)"},
+ {"SIGIO", "I/O possible"},
+ {"SIGXCPU", "CPU time limit exceeded"},
+ {"SIGXFSZ", "File size limit exceeded"},
+ {"SIGVTALRM", "Virtual timer expired"},
+ {"SIGPROF", "Profiling timer expired"},
+ {"SIGWINCH", "Window size changed"},
+ {"SIGLOST", "Resource lost"},
+ {"SIGUSR1", "User defined signal 1"},
+ {"SIGUSR2", "User defined signal 2"},
+ {"SIGPWR", "Power fail/restart"},
+ {"SIGPOLL", "Pollable event occurred"},
+ {"SIGWIND", "SIGWIND"},
+ {"SIGPHONE", "SIGPHONE"},
+ {"SIGWAITING", "Process's LWPs are blocked"},
+ {"SIGLWP", "Signal LWP"},
+ {"SIGDANGER", "Swap space dangerously low"},
+ {"SIGGRANT", "Monitor mode granted"},
+ {"SIGRETRACT", "Need to relinquish monitor mode"},
+ {"SIGMSG", "Monitor mode data available"},
+ {"SIGSOUND", "Sound completed"},
+ {"SIGSAK", "Secure attention"},
+ {"SIGPRIO", "SIGPRIO"},
+ {"SIG33", "Real-time event 33"},
+ {"SIG34", "Real-time event 34"},
+ {"SIG35", "Real-time event 35"},
+ {"SIG36", "Real-time event 36"},
+ {"SIG37", "Real-time event 37"},
+ {"SIG38", "Real-time event 38"},
+ {"SIG39", "Real-time event 39"},
+ {"SIG40", "Real-time event 40"},
+ {"SIG41", "Real-time event 41"},
+ {"SIG42", "Real-time event 42"},
+ {"SIG43", "Real-time event 43"},
+ {"SIG44", "Real-time event 44"},
+ {"SIG45", "Real-time event 45"},
+ {"SIG46", "Real-time event 46"},
+ {"SIG47", "Real-time event 47"},
+ {"SIG48", "Real-time event 48"},
+ {"SIG49", "Real-time event 49"},
+ {"SIG50", "Real-time event 50"},
+ {"SIG51", "Real-time event 51"},
+ {"SIG52", "Real-time event 52"},
+ {"SIG53", "Real-time event 53"},
+ {"SIG54", "Real-time event 54"},
+ {"SIG55", "Real-time event 55"},
+ {"SIG56", "Real-time event 56"},
+ {"SIG57", "Real-time event 57"},
+ {"SIG58", "Real-time event 58"},
+ {"SIG59", "Real-time event 59"},
+ {"SIG60", "Real-time event 60"},
+ {"SIG61", "Real-time event 61"},
+ {"SIG62", "Real-time event 62"},
+ {"SIG63", "Real-time event 63"},
+ {"SIGCANCEL", "LWP internal signal"},
+ {"SIG32", "Real-time event 32"},
+ {"SIG64", "Real-time event 64"},
+ {"SIG65", "Real-time event 65"},
+ {"SIG66", "Real-time event 66"},
+ {"SIG67", "Real-time event 67"},
+ {"SIG68", "Real-time event 68"},
+ {"SIG69", "Real-time event 69"},
+ {"SIG70", "Real-time event 70"},
+ {"SIG71", "Real-time event 71"},
+ {"SIG72", "Real-time event 72"},
+ {"SIG73", "Real-time event 73"},
+ {"SIG74", "Real-time event 74"},
+ {"SIG75", "Real-time event 75"},
+ {"SIG76", "Real-time event 76"},
+ {"SIG77", "Real-time event 77"},
+ {"SIG78", "Real-time event 78"},
+ {"SIG79", "Real-time event 79"},
+ {"SIG80", "Real-time event 80"},
+ {"SIG81", "Real-time event 81"},
+ {"SIG82", "Real-time event 82"},
+ {"SIG83", "Real-time event 83"},
+ {"SIG84", "Real-time event 84"},
+ {"SIG85", "Real-time event 85"},
+ {"SIG86", "Real-time event 86"},
+ {"SIG87", "Real-time event 87"},
+ {"SIG88", "Real-time event 88"},
+ {"SIG89", "Real-time event 89"},
+ {"SIG90", "Real-time event 90"},
+ {"SIG91", "Real-time event 91"},
+ {"SIG92", "Real-time event 92"},
+ {"SIG93", "Real-time event 93"},
+ {"SIG94", "Real-time event 94"},
+ {"SIG95", "Real-time event 95"},
+ {"SIG96", "Real-time event 96"},
+ {"SIG97", "Real-time event 97"},
+ {"SIG98", "Real-time event 98"},
+ {"SIG99", "Real-time event 99"},
+ {"SIG100", "Real-time event 100"},
+ {"SIG101", "Real-time event 101"},
+ {"SIG102", "Real-time event 102"},
+ {"SIG103", "Real-time event 103"},
+ {"SIG104", "Real-time event 104"},
+ {"SIG105", "Real-time event 105"},
+ {"SIG106", "Real-time event 106"},
+ {"SIG107", "Real-time event 107"},
+ {"SIG108", "Real-time event 108"},
+ {"SIG109", "Real-time event 109"},
+ {"SIG110", "Real-time event 110"},
+ {"SIG111", "Real-time event 111"},
+ {"SIG112", "Real-time event 112"},
+ {"SIG113", "Real-time event 113"},
+ {"SIG114", "Real-time event 114"},
+ {"SIG115", "Real-time event 115"},
+ {"SIG116", "Real-time event 116"},
+ {"SIG117", "Real-time event 117"},
+ {"SIG118", "Real-time event 118"},
+ {"SIG119", "Real-time event 119"},
+ {"SIG120", "Real-time event 120"},
+ {"SIG121", "Real-time event 121"},
+ {"SIG122", "Real-time event 122"},
+ {"SIG123", "Real-time event 123"},
+ {"SIG124", "Real-time event 124"},
+ {"SIG125", "Real-time event 125"},
+ {"SIG126", "Real-time event 126"},
+ {"SIG127", "Real-time event 127"},
+
+ {"SIGINFO", "Information request"},
+
+ {NULL, "Unknown signal"},
+ {NULL, "Internal error: printing TARGET_SIGNAL_DEFAULT"},
+
+ /* Mach exceptions */
+ {"EXC_BAD_ACCESS", "Could not access memory"},
+ {"EXC_BAD_INSTRUCTION", "Illegal instruction/operand"},
+ {"EXC_ARITHMETIC", "Arithmetic exception"},
+ {"EXC_EMULATION", "Emulation instruction"},
+ {"EXC_SOFTWARE", "Software generated exception"},
+ {"EXC_BREAKPOINT", "Breakpoint"},
+
+ /* Last entry, used to check whether the table is the right size. */
+ {NULL, "TARGET_SIGNAL_MAGIC"}
+ };
+/* *INDENT-ON* */
+
+
+
+/* Return the name for a signal. */
+char *target_signal_to_name (enum target_signal sig)
+{
+ if ((sig >= TARGET_SIGNAL_FIRST) && (sig <= TARGET_SIGNAL_LAST)
+ && signals[sig].name != NULL)
+ return signals[sig].name;
+ else
+ /* I think the code which prints this will always print it along
+ with the string, so no need to be verbose (very old comment). */
+ return "?";
+}
+
+/* Given a name, return its signal. */
+enum target_signal target_signal_from_name (char *name)
+{
+ enum target_signal sig;
+
+ /* It's possible we also should allow "SIGCLD" as well as "SIGCHLD"
+ for TARGET_SIGNAL_SIGCHLD. SIGIOT, on the other hand, is more
+ questionable; seems like by now people should call it SIGABRT
+ instead. */
+
+ /* This ugly cast brought to you by the native VAX compiler. */
+ for (sig = TARGET_SIGNAL_HUP;
+ sig < TARGET_SIGNAL_LAST;
+ sig = (enum target_signal) ((int) sig + 1))
+ if (signals[sig].name != NULL
+ && strcmp (name, signals[sig].name) == 0)
+ return sig;
+ return TARGET_SIGNAL_UNKNOWN;
+}
+
+
+/* The following functions are to help certain targets deal
+ with the signal/waitstatus stuff. They could just as well be in
+ a file called native-utils.c or unixwaitstatus-utils.c or whatever. */
+
+/* Convert host signal to our signals. */
+enum target_signal target_signal_from_host (int hostsig)
+{
+ /* A switch statement would make sense but would require special kludges
+ to deal with the cases where more than one signal has the same number. */
+
+ if (hostsig == 0)
+ return TARGET_SIGNAL_0;
+
+#if defined (VKI_SIGHUP)
+ if (hostsig == VKI_SIGHUP)
+ return TARGET_SIGNAL_HUP;
+#endif
+#if defined (VKI_SIGINT)
+ if (hostsig == VKI_SIGINT)
+ return TARGET_SIGNAL_INT;
+#endif
+#if defined (VKI_SIGQUIT)
+ if (hostsig == VKI_SIGQUIT)
+ return TARGET_SIGNAL_QUIT;
+#endif
+#if defined (VKI_SIGILL)
+ if (hostsig == VKI_SIGILL)
+ return TARGET_SIGNAL_ILL;
+#endif
+#if defined (VKI_SIGTRAP)
+ if (hostsig == VKI_SIGTRAP)
+ return TARGET_SIGNAL_TRAP;
+#endif
+#if defined (VKI_SIGABRT)
+ if (hostsig == VKI_SIGABRT)
+ return TARGET_SIGNAL_ABRT;
+#endif
+#if defined (VKI_SIGEMT)
+ if (hostsig == VKI_SIGEMT)
+ return TARGET_SIGNAL_EMT;
+#endif
+#if defined (VKI_SIGFPE)
+ if (hostsig == VKI_SIGFPE)
+ return TARGET_SIGNAL_FPE;
+#endif
+#if defined (VKI_SIGKILL)
+ if (hostsig == VKI_SIGKILL)
+ return TARGET_SIGNAL_KILL;
+#endif
+#if defined (VKI_SIGBUS)
+ if (hostsig == VKI_SIGBUS)
+ return TARGET_SIGNAL_BUS;
+#endif
+#if defined (VKI_SIGSEGV)
+ if (hostsig == VKI_SIGSEGV)
+ return TARGET_SIGNAL_SEGV;
+#endif
+#if defined (VKI_SIGSYS)
+ if (hostsig == VKI_SIGSYS)
+ return TARGET_SIGNAL_SYS;
+#endif
+#if defined (VKI_SIGPIPE)
+ if (hostsig == VKI_SIGPIPE)
+ return TARGET_SIGNAL_PIPE;
+#endif
+#if defined (VKI_SIGALRM)
+ if (hostsig == VKI_SIGALRM)
+ return TARGET_SIGNAL_ALRM;
+#endif
+#if defined (VKI_SIGTERM)
+ if (hostsig == VKI_SIGTERM)
+ return TARGET_SIGNAL_TERM;
+#endif
+#if defined (VKI_SIGUSR1)
+ if (hostsig == VKI_SIGUSR1)
+ return TARGET_SIGNAL_USR1;
+#endif
+#if defined (VKI_SIGUSR2)
+ if (hostsig == VKI_SIGUSR2)
+ return TARGET_SIGNAL_USR2;
+#endif
+#if defined (VKI_SIGCLD)
+ if (hostsig == VKI_SIGCLD)
+ return TARGET_SIGNAL_CHLD;
+#endif
+#if defined (VKI_SIGCHLD)
+ if (hostsig == VKI_SIGCHLD)
+ return TARGET_SIGNAL_CHLD;
+#endif
+#if defined (VKI_SIGPWR)
+ if (hostsig == VKI_SIGPWR)
+ return TARGET_SIGNAL_PWR;
+#endif
+#if defined (VKI_SIGWINCH)
+ if (hostsig == VKI_SIGWINCH)
+ return TARGET_SIGNAL_WINCH;
+#endif
+#if defined (VKI_SIGURG)
+ if (hostsig == VKI_SIGURG)
+ return TARGET_SIGNAL_URG;
+#endif
+#if defined (VKI_SIGIO)
+ if (hostsig == VKI_SIGIO)
+ return TARGET_SIGNAL_IO;
+#endif
+#if defined (VKI_SIGPOLL)
+ if (hostsig == VKI_SIGPOLL)
+ return TARGET_SIGNAL_POLL;
+#endif
+#if defined (VKI_SIGSTOP)
+ if (hostsig == VKI_SIGSTOP)
+ return TARGET_SIGNAL_STOP;
+#endif
+#if defined (VKI_SIGTSTP)
+ if (hostsig == VKI_SIGTSTP)
+ return TARGET_SIGNAL_TSTP;
+#endif
+#if defined (VKI_SIGCONT)
+ if (hostsig == VKI_SIGCONT)
+ return TARGET_SIGNAL_CONT;
+#endif
+#if defined (VKI_SIGTTIN)
+ if (hostsig == VKI_SIGTTIN)
+ return TARGET_SIGNAL_TTIN;
+#endif
+#if defined (VKI_SIGTTOU)
+ if (hostsig == VKI_SIGTTOU)
+ return TARGET_SIGNAL_TTOU;
+#endif
+#if defined (VKI_SIGVTALRM)
+ if (hostsig == VKI_SIGVTALRM)
+ return TARGET_SIGNAL_VTALRM;
+#endif
+#if defined (VKI_SIGPROF)
+ if (hostsig == VKI_SIGPROF)
+ return TARGET_SIGNAL_PROF;
+#endif
+#if defined (VKI_SIGXCPU)
+ if (hostsig == VKI_SIGXCPU)
+ return TARGET_SIGNAL_XCPU;
+#endif
+#if defined (VKI_SIGXFSZ)
+ if (hostsig == VKI_SIGXFSZ)
+ return TARGET_SIGNAL_XFSZ;
+#endif
+#if defined (VKI_SIGWIND)
+ if (hostsig == VKI_SIGWIND)
+ return TARGET_SIGNAL_WIND;
+#endif
+#if defined (VKI_SIGPHONE)
+ if (hostsig == VKI_SIGPHONE)
+ return TARGET_SIGNAL_PHONE;
+#endif
+#if defined (VKI_SIGLOST)
+ if (hostsig == VKI_SIGLOST)
+ return TARGET_SIGNAL_LOST;
+#endif
+#if defined (VKI_SIGWAITING)
+ if (hostsig == VKI_SIGWAITING)
+ return TARGET_SIGNAL_WAITING;
+#endif
+#if defined (VKI_SIGCANCEL)
+ if (hostsig == VKI_SIGCANCEL)
+ return TARGET_SIGNAL_CANCEL;
+#endif
+#if defined (VKI_SIGLWP)
+ if (hostsig == VKI_SIGLWP)
+ return TARGET_SIGNAL_LWP;
+#endif
+#if defined (VKI_SIGDANGER)
+ if (hostsig == VKI_SIGDANGER)
+ return TARGET_SIGNAL_DANGER;
+#endif
+#if defined (VKI_SIGGRANT)
+ if (hostsig == VKI_SIGGRANT)
+ return TARGET_SIGNAL_GRANT;
+#endif
+#if defined (VKI_SIGRETRACT)
+ if (hostsig == VKI_SIGRETRACT)
+ return TARGET_SIGNAL_RETRACT;
+#endif
+#if defined (VKI_SIGMSG)
+ if (hostsig == VKI_SIGMSG)
+ return TARGET_SIGNAL_MSG;
+#endif
+#if defined (VKI_SIGSOUND)
+ if (hostsig == VKI_SIGSOUND)
+ return TARGET_SIGNAL_SOUND;
+#endif
+#if defined (VKI_SIGSAK)
+ if (hostsig == VKI_SIGSAK)
+ return TARGET_SIGNAL_SAK;
+#endif
+#if defined (VKI_SIGPRIO)
+ if (hostsig == VKI_SIGPRIO)
+ return TARGET_SIGNAL_PRIO;
+#endif
+
+ /* Mach exceptions. Assumes that the values for EXC_ are positive! */
+#if defined (EXC_BAD_ACCESS) && defined (_NSIG)
+ if (hostsig == _NSIG + EXC_BAD_ACCESS)
+ return TARGET_EXC_BAD_ACCESS;
+#endif
+#if defined (EXC_BAD_INSTRUCTION) && defined (_NSIG)
+ if (hostsig == _NSIG + EXC_BAD_INSTRUCTION)
+ return TARGET_EXC_BAD_INSTRUCTION;
+#endif
+#if defined (EXC_ARITHMETIC) && defined (_NSIG)
+ if (hostsig == _NSIG + EXC_ARITHMETIC)
+ return TARGET_EXC_ARITHMETIC;
+#endif
+#if defined (EXC_EMULATION) && defined (_NSIG)
+ if (hostsig == _NSIG + EXC_EMULATION)
+ return TARGET_EXC_EMULATION;
+#endif
+#if defined (EXC_SOFTWARE) && defined (_NSIG)
+ if (hostsig == _NSIG + EXC_SOFTWARE)
+ return TARGET_EXC_SOFTWARE;
+#endif
+#if defined (EXC_BREAKPOINT) && defined (_NSIG)
+ if (hostsig == _NSIG + EXC_BREAKPOINT)
+ return TARGET_EXC_BREAKPOINT;
+#endif
+
+#if defined (VKI_SIGINFO)
+ if (hostsig == VKI_SIGINFO)
+ return TARGET_SIGNAL_INFO;
+#endif
+
+ return TARGET_SIGNAL_UNKNOWN;
+}
+
+/* Convert a OURSIG (an enum target_signal) to the form used by the
+ target operating system (refered to as the ``host'') or zero if the
+ equivalent host signal is not available. Set/clear OURSIG_OK
+ accordingly. */
+
+static
+int do_target_signal_to_host (enum target_signal oursig,
+ int *oursig_ok)
+{
+ *oursig_ok = 1;
+ switch (oursig) {
+ case TARGET_SIGNAL_0:
+ return 0;
+
+#if defined (VKI_SIGHUP)
+ case TARGET_SIGNAL_HUP:
+ return VKI_SIGHUP;
+#endif
+#if defined (VKI_SIGINT)
+ case TARGET_SIGNAL_INT:
+ return VKI_SIGINT;
+#endif
+#if defined (VKI_SIGQUIT)
+ case TARGET_SIGNAL_QUIT:
+ return VKI_SIGQUIT;
+#endif
+#if defined (VKI_SIGILL)
+ case TARGET_SIGNAL_ILL:
+ return VKI_SIGILL;
+#endif
+#if defined (VKI_SIGTRAP)
+ case TARGET_SIGNAL_TRAP:
+ return VKI_SIGTRAP;
+#endif
+#if defined (VKI_SIGABRT)
+ case TARGET_SIGNAL_ABRT:
+ return VKI_SIGABRT;
+#endif
+#if defined (VKI_SIGEMT)
+ case TARGET_SIGNAL_EMT:
+ return VKI_SIGEMT;
+#endif
+#if defined (VKI_SIGFPE)
+ case TARGET_SIGNAL_FPE:
+ return VKI_SIGFPE;
+#endif
+#if defined (VKI_SIGKILL)
+ case TARGET_SIGNAL_KILL:
+ return VKI_SIGKILL;
+#endif
+#if defined (VKI_SIGBUS)
+ case TARGET_SIGNAL_BUS:
+ return VKI_SIGBUS;
+#endif
+#if defined (VKI_SIGSEGV)
+ case TARGET_SIGNAL_SEGV:
+ return VKI_SIGSEGV;
+#endif
+#if defined (VKI_SIGSYS)
+ case TARGET_SIGNAL_SYS:
+ return VKI_SIGSYS;
+#endif
+#if defined (VKI_SIGPIPE)
+ case TARGET_SIGNAL_PIPE:
+ return VKI_SIGPIPE;
+#endif
+#if defined (VKI_SIGALRM)
+ case TARGET_SIGNAL_ALRM:
+ return VKI_SIGALRM;
+#endif
+#if defined (VKI_SIGTERM)
+ case TARGET_SIGNAL_TERM:
+ return VKI_SIGTERM;
+#endif
+#if defined (VKI_SIGUSR1)
+ case TARGET_SIGNAL_USR1:
+ return VKI_SIGUSR1;
+#endif
+#if defined (VKI_SIGUSR2)
+ case TARGET_SIGNAL_USR2:
+ return VKI_SIGUSR2;
+#endif
+#if defined (VKI_SIGCHLD) || defined (VKI_SIGCLD)
+ case TARGET_SIGNAL_CHLD:
+#if defined (VKI_SIGCHLD)
+ return VKI_SIGCHLD;
+#else
+ return VKI_SIGCLD;
+#endif
+#endif /* SIGCLD or SIGCHLD */
+#if defined (VKI_SIGPWR)
+ case TARGET_SIGNAL_PWR:
+ return VKI_SIGPWR;
+#endif
+#if defined (VKI_SIGWINCH)
+ case TARGET_SIGNAL_WINCH:
+ return VKI_SIGWINCH;
+#endif
+#if defined (VKI_SIGURG)
+ case TARGET_SIGNAL_URG:
+ return VKI_SIGURG;
+#endif
+#if defined (VKI_SIGIO)
+ case TARGET_SIGNAL_IO:
+ return VKI_SIGIO;
+#endif
+#if defined (VKI_SIGPOLL)
+ case TARGET_SIGNAL_POLL:
+ return VKI_SIGPOLL;
+#endif
+#if defined (VKI_SIGSTOP)
+ case TARGET_SIGNAL_STOP:
+ return VKI_SIGSTOP;
+#endif
+#if defined (VKI_SIGTSTP)
+ case TARGET_SIGNAL_TSTP:
+ return VKI_SIGTSTP;
+#endif
+#if defined (VKI_SIGCONT)
+ case TARGET_SIGNAL_CONT:
+ return VKI_SIGCONT;
+#endif
+#if defined (VKI_SIGTTIN)
+ case TARGET_SIGNAL_TTIN:
+ return VKI_SIGTTIN;
+#endif
+#if defined (VKI_SIGTTOU)
+ case TARGET_SIGNAL_TTOU:
+ return VKI_SIGTTOU;
+#endif
+#if defined (VKI_SIGVTALRM)
+ case TARGET_SIGNAL_VTALRM:
+ return VKI_SIGVTALRM;
+#endif
+#if defined (VKI_SIGPROF)
+ case TARGET_SIGNAL_PROF:
+ return VKI_SIGPROF;
+#endif
+#if defined (VKI_SIGXCPU)
+ case TARGET_SIGNAL_XCPU:
+ return VKI_SIGXCPU;
+#endif
+#if defined (VKI_SIGXFSZ)
+ case TARGET_SIGNAL_XFSZ:
+ return VKI_SIGXFSZ;
+#endif
+#if defined (VKI_SIGWIND)
+ case TARGET_SIGNAL_WIND:
+ return VKI_SIGWIND;
+#endif
+#if defined (VKI_SIGPHONE)
+ case TARGET_SIGNAL_PHONE:
+ return VKI_SIGPHONE;
+#endif
+#if defined (VKI_SIGLOST)
+ case TARGET_SIGNAL_LOST:
+ return VKI_SIGLOST;
+#endif
+#if defined (VKI_SIGWAITING)
+ case TARGET_SIGNAL_WAITING:
+ return VKI_SIGWAITING;
+#endif
+#if defined (VKI_SIGCANCEL)
+ case TARGET_SIGNAL_CANCEL:
+ return VKI_SIGCANCEL;
+#endif
+#if defined (VKI_SIGLWP)
+ case TARGET_SIGNAL_LWP:
+ return VKI_SIGLWP;
+#endif
+#if defined (VKI_SIGDANGER)
+ case TARGET_SIGNAL_DANGER:
+ return VKI_SIGDANGER;
+#endif
+#if defined (VKI_SIGGRANT)
+ case TARGET_SIGNAL_GRANT:
+ return VKI_SIGGRANT;
+#endif
+#if defined (VKI_SIGRETRACT)
+ case TARGET_SIGNAL_RETRACT:
+ return VKI_SIGRETRACT;
+#endif
+#if defined (VKI_SIGMSG)
+ case TARGET_SIGNAL_MSG:
+ return VKI_SIGMSG;
+#endif
+#if defined (VKI_SIGSOUND)
+ case TARGET_SIGNAL_SOUND:
+ return VKI_SIGSOUND;
+#endif
+#if defined (VKI_SIGSAK)
+ case TARGET_SIGNAL_SAK:
+ return VKI_SIGSAK;
+#endif
+#if defined (VKI_SIGPRIO)
+ case TARGET_SIGNAL_PRIO:
+ return VKI_SIGPRIO;
+#endif
+
+ /* Mach exceptions. Assumes that the values for EXC_ are positive! */
+#if defined (EXC_BAD_ACCESS) && defined (_NSIG)
+ case TARGET_EXC_BAD_ACCESS:
+ return _NSIG + EXC_BAD_ACCESS;
+#endif
+#if defined (EXC_BAD_INSTRUCTION) && defined (_NSIG)
+ case TARGET_EXC_BAD_INSTRUCTION:
+ return _NSIG + EXC_BAD_INSTRUCTION;
+#endif
+#if defined (EXC_ARITHMETIC) && defined (_NSIG)
+ case TARGET_EXC_ARITHMETIC:
+ return _NSIG + EXC_ARITHMETIC;
+#endif
+#if defined (EXC_EMULATION) && defined (_NSIG)
+ case TARGET_EXC_EMULATION:
+ return _NSIG + EXC_EMULATION;
+#endif
+#if defined (EXC_SOFTWARE) && defined (_NSIG)
+ case TARGET_EXC_SOFTWARE:
+ return _NSIG + EXC_SOFTWARE;
+#endif
+#if defined (EXC_BREAKPOINT) && defined (_NSIG)
+ case TARGET_EXC_BREAKPOINT:
+ return _NSIG + EXC_BREAKPOINT;
+#endif
+
+#if defined (VKI_SIGINFO)
+ case TARGET_SIGNAL_INFO:
+ return VKI_SIGINFO;
+#endif
+
+ default:
+ *oursig_ok = 0;
+ return 0;
+ }
+}
+
+int target_signal_to_host_p (enum target_signal oursig)
+{
+ int oursig_ok;
+ do_target_signal_to_host (oursig, &oursig_ok);
+ return oursig_ok;
+}
+
+int target_signal_to_host (enum target_signal oursig)
+{
+ int oursig_ok;
+ int targ_signo = do_target_signal_to_host (oursig, &oursig_ok);
+ if (!oursig_ok) {
+ /* The user might be trying to do "signal SIGSAK" where this system
+ doesn't have SIGSAK. */
+ warning ("Signal %s does not exist on this system.\n",
+ target_signal_to_name (oursig));
+ return 0;
+ } else {
+ return targ_signo;
+ }
+}
+
+/* In some circumstances we allow a command to specify a numeric
+ signal. The idea is to keep these circumstances limited so that
+ users (and scripts) develop portable habits. For comparison,
+ POSIX.2 `kill' requires that 1,2,3,6,9,14, and 15 work (and using a
+ numeric signal at all is obsolescent. We are slightly more
+ lenient and allow 1-15 which should match host signal numbers on
+ most systems. Use of symbolic signal names is strongly encouraged. */
+
+enum target_signal target_signal_from_command (int num)
+{
+ if (num >= 1 && num <= 15)
+ return (enum target_signal) num;
+ error ("Only signals 1-15 are valid as numeric signals.\n\
+Use \"info signals\" for a list of symbolic signals.\n");
+}
diff --git a/coregrind/m_gdbserver/target.c b/coregrind/m_gdbserver/target.c
new file mode 100644
index 0000000..ae61c94
--- /dev/null
+++ b/coregrind/m_gdbserver/target.c
@@ -0,0 +1,121 @@
+/* Target operations for the remote server for GDB.
+ Copyright (C) 2002, 2004, 2005, 2011
+ Free Software Foundation, Inc.
+
+ Contributed by MontaVista Software.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+
+struct target_ops *the_target;
+
+void set_desired_inferior (int use_general)
+{
+ struct thread_info *found;
+
+ if (use_general == 1) {
+ found = (struct thread_info *) find_inferior_id (&all_threads,
+ general_thread);
+ } else {
+ found = NULL;
+
+ /* If we are continuing any (all) thread(s), use step_thread
+ to decide which thread to step and/or send the specified
+ signal to. */
+ if ((step_thread != 0 && step_thread != -1)
+ && (cont_thread == 0 || cont_thread == -1))
+ found = (struct thread_info *) find_inferior_id (&all_threads,
+ step_thread);
+
+ if (found == NULL)
+ found = (struct thread_info *) find_inferior_id (&all_threads,
+ cont_thread);
+ }
+
+ if (found == NULL)
+ current_inferior = (struct thread_info *) all_threads.head;
+ else
+ current_inferior = found;
+ {
+ ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
+ ThreadId tid = tst->tid;
+ dlog(1, "set_desired_inferior use_general %d found %p tid %d lwpid %d\n",
+ use_general, found, tid, tst->os_state.lwpid);
+ }
+}
+
+int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ int res;
+ res = (*the_target->read_memory) (memaddr, myaddr, len);
+ return res;
+}
+
+int write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len)
+{
+ /* Lacking cleanups, there is some potential for a memory leak if the
+ write fails and we go through error(). Make sure that no more than
+ one buffer is ever pending by making BUFFER static. */
+ static unsigned char *buffer = 0;
+ int res;
+
+ if (buffer != NULL)
+ free (buffer);
+
+ buffer = malloc (len);
+ VG_(memcpy) (buffer, myaddr, len);
+ res = (*the_target->write_memory) (memaddr, buffer, len);
+ free (buffer);
+ buffer = NULL;
+
+ return res;
+}
+
+void set_target_ops (struct target_ops *target)
+{
+ the_target = (struct target_ops *) malloc (sizeof (*the_target));
+ VG_(memcpy) (the_target, target, sizeof (*the_target));
+}
+
+void* VG_(dmemcpy) ( void *d, const void *s, SizeT sz, Bool *mod )
+{
+ if (VG_(memcmp) (d, s, sz)) {
+ *mod = True;
+ return VG_(memcpy) (d, s, sz);
+ } else {
+ *mod = False;
+ return d;
+ }
+}
+
+void VG_(transfer) (void *valgrind,
+ void *gdbserver,
+ transfer_direction dir,
+ SizeT sz,
+ Bool *mod)
+{
+ if (dir == valgrind_to_gdbserver)
+ VG_(dmemcpy) (gdbserver, valgrind, sz, mod);
+ else if (dir == gdbserver_to_valgrind)
+ VG_(dmemcpy) (valgrind, gdbserver, sz, mod);
+ else
+ vg_assert (0);
+}
diff --git a/coregrind/m_gdbserver/target.h b/coregrind/m_gdbserver/target.h
new file mode 100644
index 0000000..d657438
--- /dev/null
+++ b/coregrind/m_gdbserver/target.h
@@ -0,0 +1,176 @@
+/* Target operations for the remote server for GDB.
+ Copyright (C) 2002, 2003, 2004, 2005
+ Free Software Foundation, Inc.
+
+ Contributed by MontaVista Software.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#ifndef TARGET_H
+#define TARGET_H
+
+/* This structure describes how to resume a particular thread (or
+ all threads) based on the client's request. If thread is -1, then
+ this entry applies to all threads. These are generally passed around
+ as an array, and terminated by a thread == -1 entry. */
+
+struct thread_resume
+{
+ unsigned long thread;
+
+ /* If non-zero, leave this thread stopped. */
+ int leave_stopped;
+
+ /* If non-zero, we want to single-step. */
+ int step;
+
+ /* If non-zero, send this signal when we resume. */
+ int sig;
+};
+
+struct target_ops
+{
+ /* Return 1 iff the thread with process ID PID is alive. */
+
+ int (*thread_alive) (unsigned long pid);
+
+ /* Resume the inferior process. */
+
+ void (*resume) (struct thread_resume *resume_info);
+
+ /* Wait for the inferior process to change state.
+
+ STATUS will be filled in with a response code to send to GDB.
+
+ Returns the signal which caused the process to stop, in the
+ remote protocol numbering (e.g. TARGET_SIGNAL_STOP), or the
+ exit code as an integer if *STATUS is 'W'. */
+
+ unsigned char (*wait) (char *status);
+
+ /* Fetch registers from the inferior process.
+
+ If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */
+
+ void (*fetch_registers) (int regno);
+
+ /* Store registers to the inferior process.
+
+ If REGNO is -1, store all registers; otherwise, store at least REGNO. */
+
+ void (*store_registers) (int regno);
+
+ /* Read memory from the inferior process. This should generally be
+ called through read_inferior_memory, which handles breakpoint shadowing.
+
+ Read LEN bytes at MEMADDR into a buffer at MYADDR.
+
+ Returns 0 on success and errno on failure. */
+
+ int (*read_memory) (CORE_ADDR memaddr, unsigned char *myaddr, int len);
+
+ /* Write memory to the inferior process. This should generally be
+ called through write_inferior_memory, which handles breakpoint shadowing.
+
+ Write LEN bytes from the buffer at MYADDR to MEMADDR.
+
+ Returns 0 on success and errno on failure. */
+
+ int (*write_memory) (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len);
+
+ /* Send a signal to the inferior process, however is appropriate. */
+ void (*send_signal) (int);
+
+ /* Returns the name of the xml target description file.
+ returns NULL if no xml target description available. */
+ char* (*target_xml)(void);
+
+ /* Same but describes also the shadow registers. */
+ char* (*shadow_target_xml)(void);
+
+ /* Insert and remove a hardware watchpoint.
+ Returns 0 on success, -1 on failure and 1 on unsupported.
+ The type is coded as follows:
+ 2 = write watchpoint
+ 3 = read watchpoint
+ 4 = access watchpoint
+ */
+
+ int (*insert_watchpoint) (char type, CORE_ADDR addr, int len);
+ int (*remove_watchpoint) (char type, CORE_ADDR addr, int len);
+
+ /* Returns 1 if target was stopped due to a watchpoint hit, 0 otherwise. */
+
+ int (*stopped_by_watchpoint) (void);
+
+ /* Returns the address associated with the watchpoint that hit, if any;
+ returns 0 otherwise. */
+
+ CORE_ADDR (*stopped_data_address) (void);
+
+};
+
+extern struct target_ops *the_target;
+
+void set_target_ops (struct target_ops *);
+
+#define detach_inferior() \
+ (*the_target->detach) ()
+
+#define mythread_alive(pid) \
+ (*the_target->thread_alive) (pid)
+
+#define fetch_inferior_registers(regno) \
+ (*the_target->fetch_registers) (regno)
+
+#define store_inferior_registers(regno) \
+ (*the_target->store_registers) (regno)
+
+#define mywait(statusp) \
+ (*the_target->wait) (statusp)
+
+int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len);
+
+int write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len);
+
+void set_desired_inferior (int id);
+
+/* like memcpy but first check if content of destination and source
+ differs. If no difference, no copy is done, *mod set to False.
+ If different; copy is done, *mod set to True. */
+extern void* VG_(dmemcpy) ( void *d, const void *s, SizeT sz, Bool *mod );
+
+typedef
+ enum {
+ valgrind_to_gdbserver,
+ gdbserver_to_valgrind} transfer_direction;
+
+// According to dir, calls VG_(dmemcpy)
+// to copy data from/to valgrind to/from gdbserver.
+// If the transferred data differs from what is currently stored,
+// sets *mod to True otherwise set *mod to False.
+extern void VG_(transfer) (void *valgrind,
+ void *gdbserver,
+ transfer_direction dir,
+ SizeT sz,
+ Bool *mod);
+
+#endif /* TARGET_H */
diff --git a/coregrind/m_gdbserver/utils.c b/coregrind/m_gdbserver/utils.c
new file mode 100644
index 0000000..57db175
--- /dev/null
+++ b/coregrind/m_gdbserver/utils.c
@@ -0,0 +1,88 @@
+/* General utility routines for the remote server for GDB.
+ Copyright (C) 1986, 1989, 1993, 1995, 1996, 1997, 1999, 2000, 2002, 2003,
+ 2011
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+/* Generally useful subroutines used throughout the program. */
+
+/* Print the system error message for sr.
+ Then print the rest of the args. */
+void sr_perror (SysRes sr,char *string,...)
+{
+ va_list args;
+ if (sr_isError (sr))
+ VG_(umsg) ("error %ld %s\n", sr_Err(sr), VG_(strerror) (sr_Err(sr)));
+ else
+ VG_(umsg) ("sr_perror called with no error!!!\n");
+ va_start (args, string);
+ VG_(vmessage) ( Vg_UserMsg, string, args );
+ va_end (args);
+}
+
+/* Print an error message and return to command level.
+ STRING is the error message, used as a fprintf string,
+ and ARG is passed as an argument to it. */
+
+void error (const char *string,...)
+{
+ va_list args;
+ va_start (args, string);
+ VG_(vmessage) ( Vg_UserMsg, string, args );
+ va_end(args);
+ VG_MINIMAL_LONGJMP(toplevel);
+}
+
+/* Print an error message and exit reporting failure.
+ This is for a error that we cannot continue from.
+ STRING and ARG are passed to fprintf. */
+
+/* VARARGS */
+void fatal (const char *string,...)
+{
+ va_list args;
+ va_start (args, string);
+ VG_(vmessage) ( Vg_UserMsg, string, args );
+ va_end (args);
+ VG_(exit) (1);
+}
+
+/* VARARGS */
+void warning (const char *string,...)
+{
+ va_list args;
+ va_start (args, string);
+ VG_(vmessage) ( Vg_UserMsg, string, args );
+ va_end (args);
+}
+
+#if 0
+/* print timestamp */
+static
+void dbgts(void)
+{
+ struct vki_timeval dbgtv;
+ SysRes res;
+ res = VG_(do_syscall2)(__NR_gettimeofday, (UWord)&dbgtv, (UWord)NULL);
+ // gettimeofday(&dbgtv, NULL);
+ dlog(0, "%ld.%6ld ", dbgtv.tv_sec, dbgtv.tv_usec);
+}
+#endif
diff --git a/coregrind/m_gdbserver/valgrind-low-amd64.c b/coregrind/m_gdbserver/valgrind-low-amd64.c
new file mode 100644
index 0000000..0dc0382
--- /dev/null
+++ b/coregrind/m_gdbserver/valgrind-low-amd64.c
@@ -0,0 +1,306 @@
+/* Low level interface to valgrind, for the remote server for GDB integrated
+ in valgrind.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+ This file is part of VALGRIND.
+ It has been inspired from a file from gdbserver in gdb 6.6.
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "target.h"
+#include "regdef.h"
+#include "regcache.h"
+
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_machine.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_core_gdbserver.h"
+
+#include "valgrind_low.h"
+
+#include "libvex_guest_amd64.h"
+/* GDBTD: ??? have a cleaner way to get the f80 <> f64 conversion functions */
+/* below include needed for conversion f80 <> f64 */
+#include "../../VEX/priv/guest_generic_x87.h"
+
+/* below loosely inspired from file generated with gdb regdat.sh */
+
+struct reg regs[] = {
+ { "rax", 0, 64 },
+ { "rbx", 64, 64 },
+ { "rcx", 128, 64 },
+ { "rdx", 192, 64 },
+ { "rsi", 256, 64 },
+ { "rdi", 320, 64 },
+ { "rbp", 384, 64 },
+ { "rsp", 448, 64 },
+ { "r8", 512, 64 },
+ { "r9", 576, 64 },
+ { "r10", 640, 64 },
+ { "r11", 704, 64 },
+ { "r12", 768, 64 },
+ { "r13", 832, 64 },
+ { "r14", 896, 64 },
+ { "r15", 960, 64 },
+ { "rip", 1024, 64 },
+ { "eflags", 1088, 32 },
+ { "cs", 1120, 32 },
+ { "ss", 1152, 32 },
+ { "ds", 1184, 32 },
+ { "es", 1216, 32 },
+ { "fs", 1248, 32 },
+ { "gs", 1280, 32 },
+ { "st0", 1312, 80 },
+ { "st1", 1392, 80 },
+ { "st2", 1472, 80 },
+ { "st3", 1552, 80 },
+ { "st4", 1632, 80 },
+ { "st5", 1712, 80 },
+ { "st6", 1792, 80 },
+ { "st7", 1872, 80 },
+ { "fctrl", 1952, 32 },
+ { "fstat", 1984, 32 },
+ { "ftag", 2016, 32 },
+ { "fiseg", 2048, 32 },
+ { "fioff", 2080, 32 },
+ { "foseg", 2112, 32 },
+ { "fooff", 2144, 32 },
+ { "fop", 2176, 32 },
+ { "xmm0", 2208, 128 },
+ { "xmm1", 2336, 128 },
+ { "xmm2", 2464, 128 },
+ { "xmm3", 2592, 128 },
+ { "xmm4", 2720, 128 },
+ { "xmm5", 2848, 128 },
+ { "xmm6", 2976, 128 },
+ { "xmm7", 3104, 128 },
+ { "xmm8", 3232, 128 },
+ { "xmm9", 3360, 128 },
+ { "xmm10", 3488, 128 },
+ { "xmm11", 3616, 128 },
+ { "xmm12", 3744, 128 },
+ { "xmm13", 3872, 128 },
+ { "xmm14", 4000, 128 },
+ { "xmm15", 4128, 128 },
+ { "mxcsr", 4256, 32 },
+#if defined(VGO_linux)
+ { "orig_rax", 4288, 64 }
+#endif
+};
+static const char *expedite_regs[] = { "rbp", "rsp", "rip", 0 };
+#define num_regs (sizeof (regs) / sizeof (regs[0]))
+
+static
+CORE_ADDR get_pc (void)
+{
+ unsigned long pc;
+
+ collect_register_by_name ("rip", &pc);
+
+ dlog(1, "stop pc is %p\n", (void *) pc);
+ return pc;
+}
+
+static
+void set_pc (CORE_ADDR newpc)
+{
+ Bool mod;
+ supply_register_by_name ("rip", &newpc, &mod);
+ if (mod)
+ dlog(1, "set pc to %p\n", C2v (newpc));
+ else
+ dlog(1, "set pc not changed %p\n", C2v (newpc));
+}
+
+/* store registers in the guest state (gdbserver_to_valgrind)
+ or fetch register from the guest state (valgrind_to_gdbserver). */
+static
+void transfer_register (ThreadId tid, int abs_regno, void * buf,
+ transfer_direction dir, int size, Bool *mod)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ int set = abs_regno / num_regs;
+ int regno = abs_regno % num_regs;
+ *mod = False;
+
+ VexGuestAMD64State* amd64 = (VexGuestAMD64State*) get_arch (set, tst);
+
+ switch (regno) {
+ // numbers here have to match the order of regs above.
+ // Attention: gdb order does not match valgrind order.
+ case 0: VG_(transfer) (&amd64->guest_RAX, buf, dir, size, mod); break;
+ case 1: VG_(transfer) (&amd64->guest_RBX, buf, dir, size, mod); break;
+ case 2: VG_(transfer) (&amd64->guest_RCX, buf, dir, size, mod); break;
+ case 3: VG_(transfer) (&amd64->guest_RDX, buf, dir, size, mod); break;
+ case 4: VG_(transfer) (&amd64->guest_RSI, buf, dir, size, mod); break;
+ case 5: VG_(transfer) (&amd64->guest_RDI, buf, dir, size, mod); break;
+ case 6: VG_(transfer) (&amd64->guest_RBP, buf, dir, size, mod); break;
+ case 7: VG_(transfer) (&amd64->guest_RSP, buf, dir, size, mod); break;
+ case 8: VG_(transfer) (&amd64->guest_R8, buf, dir, size, mod); break;
+ case 9: VG_(transfer) (&amd64->guest_R9, buf, dir, size, mod); break;
+ case 10: VG_(transfer) (&amd64->guest_R10, buf, dir, size, mod); break;
+ case 11: VG_(transfer) (&amd64->guest_R11, buf, dir, size, mod); break;
+ case 12: VG_(transfer) (&amd64->guest_R12, buf, dir, size, mod); break;
+ case 13: VG_(transfer) (&amd64->guest_R13, buf, dir, size, mod); break;
+ case 14: VG_(transfer) (&amd64->guest_R14, buf, dir, size, mod); break;
+ case 15: VG_(transfer) (&amd64->guest_R15, buf, dir, size, mod); break;
+ case 16:
+ VG_(transfer) (&amd64->guest_RIP, buf, dir, size, mod);
+ if (*mod && VG_(debugLog_getLevel)() > 2) {
+ char bufimage [2*sizeof(amd64->guest_IP_AT_SYSCALL) + 1];
+ heximage (bufimage,
+ (char *) &amd64->guest_IP_AT_SYSCALL,
+ sizeof(amd64->guest_IP_AT_SYSCALL));
+ dlog(3, "guest_IP_AT_SYSCALL %s\n", bufimage);
+ }
+ break;
+ case 17:
+ if (dir == valgrind_to_gdbserver) {
+ ULong rflags;
+ /* we can only retrieve the real flags (set 0)
+ retrieving shadow flags is not ok */
+ if (set == 0)
+ rflags = LibVEX_GuestAMD64_get_rflags (amd64);
+ else
+ rflags = 0;
+ VG_(transfer) (&rflags, buf, dir, size, mod);
+ } else {
+ *mod = False; //GDBTD? how do we store rflags in libvex_guest_amd64.h ???
+ }
+ break;
+ case 18: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_CS, buf, dir, size, mod);
+ case 19: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_SS, buf, dir, size, mod);
+ case 20: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_DS, buf, dir, size, mod);
+ case 21: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_ES, buf, dir, size, mod);
+ case 22: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_FS, buf, dir, size, mod);
+ case 23: VG_(transfer) (&amd64->guest_GS_0x60, buf, dir, size, mod); break;
+ case 24:
+ case 25:
+ case 26:
+ case 27: /* register 24 to 31 are float registers 80 bits but 64 bits in valgrind */
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ if (dir == valgrind_to_gdbserver) {
+ UChar fpreg80[10];
+ convert_f64le_to_f80le ((UChar *)&amd64->guest_FPREG[regno-16],
+ fpreg80);
+ VG_(transfer) (&fpreg80, buf, dir, sizeof(fpreg80), mod);
+ } else {
+ ULong fpreg64;
+ convert_f80le_to_f64le (buf, (UChar *)&fpreg64);
+ VG_(transfer) (&amd64->guest_FPREG[regno-16], &fpreg64,
+ dir, sizeof(fpreg64), mod);
+ }
+ break;
+ case 32:
+ if (dir == valgrind_to_gdbserver) {
+ // vex only models the rounding bits (see libvex_guest_amd64.h)
+ UWord value = 0x037f;
+ value |= amd64->guest_FPROUND << 10;
+ VG_(transfer)(&value, buf, dir, size, mod);
+ } else {
+ *mod = False; // GDBTD???? VEX equivalent fcrtl
+ }
+ break;
+ case 33:
+ if (dir == valgrind_to_gdbserver) {
+ UWord value = amd64->guest_FC3210;
+ value |= (amd64->guest_FTOP & 7) << 11;
+ VG_(transfer)(&value, buf, dir, size, mod);
+ } else {
+ *mod = False; // GDBTD???? VEX equivalent fstat
+ }
+ break;
+ case 34:
+ if (dir == valgrind_to_gdbserver) {
+ // vex doesn't model these precisely
+ UWord value =
+ ((amd64->guest_FPTAG[0] ? 0 : 3) << 0) |
+ ((amd64->guest_FPTAG[1] ? 0 : 3) << 2) |
+ ((amd64->guest_FPTAG[2] ? 0 : 3) << 4) |
+ ((amd64->guest_FPTAG[3] ? 0 : 3) << 6) |
+ ((amd64->guest_FPTAG[4] ? 0 : 3) << 8) |
+ ((amd64->guest_FPTAG[5] ? 0 : 3) << 10) |
+ ((amd64->guest_FPTAG[6] ? 0 : 3) << 12) |
+ ((amd64->guest_FPTAG[7] ? 0 : 3) << 14);
+ VG_(transfer)(&value, buf, dir, size, mod);
+ } else {
+ *mod = False; // GDBTD???? VEX equivalent ftag
+ }
+ break;
+ case 35: *mod = False; break; // GDBTD ??? equivalent of fiseg
+ case 36: *mod = False; break; // GDBTD ??? equivalent of fioff
+ case 37: *mod = False; break; // GDBTD ??? equivalent of foseg
+ case 38: *mod = False; break; // GDBTD ??? equivalent of fooff
+ case 39: *mod = False; break; // GDBTD ??? equivalent of fop
+ case 40: VG_(transfer) (&amd64->guest_XMM0, buf, dir, size, mod); break;
+ case 41: VG_(transfer) (&amd64->guest_XMM1, buf, dir, size, mod); break;
+ case 42: VG_(transfer) (&amd64->guest_XMM2, buf, dir, size, mod); break;
+ case 43: VG_(transfer) (&amd64->guest_XMM3, buf, dir, size, mod); break;
+ case 44: VG_(transfer) (&amd64->guest_XMM4, buf, dir, size, mod); break;
+ case 45: VG_(transfer) (&amd64->guest_XMM5, buf, dir, size, mod); break;
+ case 46: VG_(transfer) (&amd64->guest_XMM6, buf, dir, size, mod); break;
+ case 47: VG_(transfer) (&amd64->guest_XMM7, buf, dir, size, mod); break;
+ case 48: VG_(transfer) (&amd64->guest_XMM8, buf, dir, size, mod); break;
+ case 49: VG_(transfer) (&amd64->guest_XMM9, buf, dir, size, mod); break;
+ case 50: VG_(transfer) (&amd64->guest_XMM10, buf, dir, size, mod); break;
+ case 51: VG_(transfer) (&amd64->guest_XMM11, buf, dir, size, mod); break;
+ case 52: VG_(transfer) (&amd64->guest_XMM12, buf, dir, size, mod); break;
+ case 53: VG_(transfer) (&amd64->guest_XMM13, buf, dir, size, mod); break;
+ case 54: VG_(transfer) (&amd64->guest_XMM14, buf, dir, size, mod); break;
+ case 55: VG_(transfer) (&amd64->guest_XMM15, buf, dir, size, mod); break;
+ case 56:
+ if (dir == valgrind_to_gdbserver) {
+ // vex only models the rounding bits (see libvex_guest_x86.h)
+ UWord value = 0x1f80;
+ value |= amd64->guest_SSEROUND << 13;
+ VG_(transfer)(&value, buf, dir, size, mod);
+ } else {
+ *mod = False; // GDBTD???? VEX equivalent mxcsr
+ }
+ break;
+ case 57: *mod = False; break; // GDBTD???? VEX equivalent { "orig_rax"},
+ default: vg_assert(0);
+ }
+}
+
+static struct valgrind_target_ops low_target = {
+ num_regs,
+ regs,
+ 7, //RSP
+ transfer_register,
+ get_pc,
+ set_pc,
+ "amd64",
+ NULL, // target_xml not needed.
+#if defined(VGO_linux)
+ "amd64-linux-valgrind.xml"
+#else
+ "amd64-coresse-valgrind.xml"
+#endif
+};
+
+void amd64_init_architecture (struct valgrind_target_ops *target)
+{
+ *target = low_target;
+ set_register_cache (regs, num_regs);
+ gdbserver_expedite_regs = expedite_regs;
+}
diff --git a/coregrind/m_gdbserver/valgrind-low-arm.c b/coregrind/m_gdbserver/valgrind-low-arm.c
new file mode 100644
index 0000000..d4cec3c
--- /dev/null
+++ b/coregrind/m_gdbserver/valgrind-low-arm.c
@@ -0,0 +1,237 @@
+/* Low level interface to valgrind, for the remote server for GDB integrated
+ in valgrind.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+ This file is part of VALGRIND.
+ It has been inspired from a file from gdbserver in gdb 6.6.
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "target.h"
+#include "regdef.h"
+#include "regcache.h"
+
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_machine.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_core_gdbserver.h"
+
+#include "valgrind_low.h"
+
+#include "libvex_guest_arm.h"
+
+struct reg regs[] = {
+ { "r0", 0, 32 },
+ { "r1", 32, 32 },
+ { "r2", 64, 32 },
+ { "r3", 96, 32 },
+ { "r4", 128, 32 },
+ { "r5", 160, 32 },
+ { "r6", 192, 32 },
+ { "r7", 224, 32 },
+ { "r8", 256, 32 },
+ { "r9", 288, 32 },
+ { "r10", 320, 32 },
+ { "r11", 352, 32 },
+ { "r12", 384, 32 },
+ { "sp", 416, 32 },
+ { "lr", 448, 32 },
+ { "pc", 480, 32 },
+ { "", 512, 0 }, // It seems these entries are needed
+ { "", 512, 0 }, // as previous versions of arm <-> gdb placed
+ { "", 512, 0 }, // some floating point registers here. So, cpsr
+ { "", 512, 0 }, // must be register 25.
+ { "", 512, 0 },
+ { "", 512, 0 },
+ { "", 512, 0 },
+ { "", 512, 0 },
+ { "", 512, 0 },
+ { "cpsr", 512, 32 },
+ { "d0", 544, 64 },
+ { "d1", 608, 64 },
+ { "d2", 672, 64 },
+ { "d3", 736, 64 },
+ { "d4", 800, 64 },
+ { "d5", 864, 64 },
+ { "d6", 928, 64 },
+ { "d7", 992, 64 },
+ { "d8", 1056, 64 },
+ { "d9", 1120, 64 },
+ { "d10", 1184, 64 },
+ { "d11", 1248, 64 },
+ { "d12", 1312, 64 },
+ { "d13", 1376, 64 },
+ { "d14", 1440, 64 },
+ { "d15", 1504, 64 },
+ { "d16", 1568, 64 },
+ { "d17", 1632, 64 },
+ { "d18", 1696, 64 },
+ { "d19", 1760, 64 },
+ { "d20", 1824, 64 },
+ { "d21", 1888, 64 },
+ { "d22", 1952, 64 },
+ { "d23", 2016, 64 },
+ { "d24", 2080, 64 },
+ { "d25", 2144, 64 },
+ { "d26", 2208, 64 },
+ { "d27", 2272, 64 },
+ { "d28", 2336, 64 },
+ { "d29", 2400, 64 },
+ { "d30", 2464, 64 },
+ { "d31", 2528, 64 },
+ { "fpscr", 2592, 32 }
+};
+static const char *expedite_regs[] = { "r11", "sp", "pc", 0 };
+#define num_regs (sizeof (regs) / sizeof (regs[0]))
+
+static
+CORE_ADDR get_pc (void)
+{
+ unsigned long pc;
+
+ collect_register_by_name ("pc", &pc);
+
+ dlog(1, "stop pc is %p\n", (void *) pc);
+ return pc;
+}
+
+static
+void set_pc (CORE_ADDR newpc)
+{
+ Bool mod;
+ supply_register_by_name ("pc", &newpc, &mod);
+ if (mod)
+ dlog(1, "set pc to %p\n", C2v (newpc));
+ else
+ dlog(1, "set pc not changed %p\n", C2v (newpc));
+}
+
+/* store registers in the guest state (gdbserver_to_valgrind)
+ or fetch register from the guest state (valgrind_to_gdbserver). */
+static
+void transfer_register (ThreadId tid, int abs_regno, void * buf,
+ transfer_direction dir, int size, Bool *mod)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ int set = abs_regno / num_regs;
+ int regno = abs_regno % num_regs;
+ *mod = False;
+
+ VexGuestARMState* arm = (VexGuestARMState*) get_arch (set, tst);
+
+ switch (regno) {
+ // numbers here have to match the order of regs above
+ // Attention: gdb order does not match valgrind order.
+ case 0: VG_(transfer) (&arm->guest_R0, buf, dir, size, mod); break;
+ case 1: VG_(transfer) (&arm->guest_R1, buf, dir, size, mod); break;
+ case 2: VG_(transfer) (&arm->guest_R2, buf, dir, size, mod); break;
+ case 3: VG_(transfer) (&arm->guest_R3, buf, dir, size, mod); break;
+ case 4: VG_(transfer) (&arm->guest_R4, buf, dir, size, mod); break;
+ case 5: VG_(transfer) (&arm->guest_R5, buf, dir, size, mod); break;
+ case 6: VG_(transfer) (&arm->guest_R6, buf, dir, size, mod); break;
+ case 7: VG_(transfer) (&arm->guest_R7, buf, dir, size, mod); break;
+ case 8: VG_(transfer) (&arm->guest_R8, buf, dir, size, mod); break;
+ case 9: VG_(transfer) (&arm->guest_R9, buf, dir, size, mod); break;
+ case 10: VG_(transfer) (&arm->guest_R10, buf, dir, size, mod); break;
+ case 11: VG_(transfer) (&arm->guest_R11, buf, dir, size, mod); break;
+ case 12: VG_(transfer) (&arm->guest_R12, buf, dir, size, mod); break;
+ case 13: VG_(transfer) (&arm->guest_R13, buf, dir, size, mod); break;
+ case 14: VG_(transfer) (&arm->guest_R14, buf, dir, size, mod); break;
+ case 15: VG_(transfer) (&arm->guest_R15T, buf, dir, size, mod); break;
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20: /* 9 "empty registers". See struct reg regs above. */
+ case 21:
+ case 22:
+ case 23:
+ case 24: *mod = False; break;
+ case 25: {
+ UInt cpsr = LibVEX_GuestARM_get_cpsr (arm);
+ if (dir == valgrind_to_gdbserver) {
+ VG_(transfer) (&cpsr, buf, dir, size, mod);
+ } else {
+# if 0
+ UInt newcpsr;
+ VG_(transfer) (&newcpsr, buf, dir, size, mod);
+ *mod = newcpsr != cpsr;
+ // GDBTD ???? see FIXME in guest_arm_helpers.c
+ LibVEX_GuestARM_put_flags (newcpsr, arm);
+# else
+ *mod = False;
+# endif
+ }
+ break;
+ }
+ case 26: VG_(transfer) (&arm->guest_D0, buf, dir, size, mod); break;
+ case 27: VG_(transfer) (&arm->guest_D1, buf, dir, size, mod); break;
+ case 28: VG_(transfer) (&arm->guest_D2, buf, dir, size, mod); break;
+ case 29: VG_(transfer) (&arm->guest_D3, buf, dir, size, mod); break;
+ case 30: VG_(transfer) (&arm->guest_D4, buf, dir, size, mod); break;
+ case 31: VG_(transfer) (&arm->guest_D5, buf, dir, size, mod); break;
+ case 32: VG_(transfer) (&arm->guest_D6, buf, dir, size, mod); break;
+ case 33: VG_(transfer) (&arm->guest_D7, buf, dir, size, mod); break;
+ case 34: VG_(transfer) (&arm->guest_D8, buf, dir, size, mod); break;
+ case 35: VG_(transfer) (&arm->guest_D9, buf, dir, size, mod); break;
+ case 36: VG_(transfer) (&arm->guest_D10, buf, dir, size, mod); break;
+ case 37: VG_(transfer) (&arm->guest_D11, buf, dir, size, mod); break;
+ case 38: VG_(transfer) (&arm->guest_D12, buf, dir, size, mod); break;
+ case 39: VG_(transfer) (&arm->guest_D13, buf, dir, size, mod); break;
+ case 40: VG_(transfer) (&arm->guest_D14, buf, dir, size, mod); break;
+ case 41: VG_(transfer) (&arm->guest_D15, buf, dir, size, mod); break;
+ case 42: VG_(transfer) (&arm->guest_D16, buf, dir, size, mod); break;
+ case 43: VG_(transfer) (&arm->guest_D17, buf, dir, size, mod); break;
+ case 44: VG_(transfer) (&arm->guest_D18, buf, dir, size, mod); break;
+ case 45: VG_(transfer) (&arm->guest_D19, buf, dir, size, mod); break;
+ case 46: VG_(transfer) (&arm->guest_D20, buf, dir, size, mod); break;
+ case 47: VG_(transfer) (&arm->guest_D21, buf, dir, size, mod); break;
+ case 48: VG_(transfer) (&arm->guest_D22, buf, dir, size, mod); break;
+ case 49: VG_(transfer) (&arm->guest_D23, buf, dir, size, mod); break;
+ case 50: VG_(transfer) (&arm->guest_D24, buf, dir, size, mod); break;
+ case 51: VG_(transfer) (&arm->guest_D25, buf, dir, size, mod); break;
+ case 52: VG_(transfer) (&arm->guest_D26, buf, dir, size, mod); break;
+ case 53: VG_(transfer) (&arm->guest_D27, buf, dir, size, mod); break;
+ case 54: VG_(transfer) (&arm->guest_D28, buf, dir, size, mod); break;
+ case 55: VG_(transfer) (&arm->guest_D29, buf, dir, size, mod); break;
+ case 56: VG_(transfer) (&arm->guest_D30, buf, dir, size, mod); break;
+ case 57: VG_(transfer) (&arm->guest_D31, buf, dir, size, mod); break;
+ case 58: VG_(transfer) (&arm->guest_FPSCR, buf, dir, size, mod); break;
+ default: vg_assert(0);
+ }
+}
+
+static struct valgrind_target_ops low_target = {
+ num_regs,
+ regs,
+ 13, //SP
+ transfer_register,
+ get_pc,
+ set_pc,
+ "arm",
+ "arm-with-vfpv3.xml",
+ "arm-with-vfpv3-valgrind.xml"
+};
+
+void arm_init_architecture (struct valgrind_target_ops *target)
+{
+ *target = low_target;
+ set_register_cache (regs, num_regs);
+ gdbserver_expedite_regs = expedite_regs;
+}
diff --git a/coregrind/m_gdbserver/valgrind-low-ppc32.c b/coregrind/m_gdbserver/valgrind-low-ppc32.c
new file mode 100644
index 0000000..a7e282e
--- /dev/null
+++ b/coregrind/m_gdbserver/valgrind-low-ppc32.c
@@ -0,0 +1,343 @@
+/* Low level interface to valgrind, for the remote server for GDB integrated
+ in valgrind.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+ This file is part of VALGRIND.
+ It has been inspired from a file from gdbserver in gdb 6.6.
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "target.h"
+#include "regdef.h"
+#include "regcache.h"
+
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_machine.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_core_gdbserver.h"
+
+#include "valgrind_low.h"
+
+#include "libvex_guest_ppc32.h"
+
+/* this is only the basic set of registers.
+ Need to look at what is the exact ppc32 model to support.
+*/
+struct reg regs[] = {
+ { "r0", 0, 32 },
+ { "r1", 32, 32 },
+ { "r2", 64, 32 },
+ { "r3", 96, 32 },
+ { "r4", 128, 32 },
+ { "r5", 160, 32 },
+ { "r6", 192, 32 },
+ { "r7", 224, 32 },
+ { "r8", 256, 32 },
+ { "r9", 288, 32 },
+ { "r10", 320, 32 },
+ { "r11", 352, 32 },
+ { "r12", 384, 32 },
+ { "r13", 416, 32 },
+ { "r14", 448, 32 },
+ { "r15", 480, 32 },
+ { "r16", 512, 32 },
+ { "r17", 544, 32 },
+ { "r18", 576, 32 },
+ { "r19", 608, 32 },
+ { "r20", 640, 32 },
+ { "r21", 672, 32 },
+ { "r22", 704, 32 },
+ { "r23", 736, 32 },
+ { "r24", 768, 32 },
+ { "r25", 800, 32 },
+ { "r26", 832, 32 },
+ { "r27", 864, 32 },
+ { "r28", 896, 32 },
+ { "r29", 928, 32 },
+ { "r30", 960, 32 },
+ { "r31", 992, 32 },
+ { "f0", 1024, 64 },
+ { "f1", 1088, 64 },
+ { "f2", 1152, 64 },
+ { "f3", 1216, 64 },
+ { "f4", 1280, 64 },
+ { "f5", 1344, 64 },
+ { "f6", 1408, 64 },
+ { "f7", 1472, 64 },
+ { "f8", 1536, 64 },
+ { "f9", 1600, 64 },
+ { "f10", 1664, 64 },
+ { "f11", 1728, 64 },
+ { "f12", 1792, 64 },
+ { "f13", 1856, 64 },
+ { "f14", 1920, 64 },
+ { "f15", 1984, 64 },
+ { "f16", 2048, 64 },
+ { "f17", 2112, 64 },
+ { "f18", 2176, 64 },
+ { "f19", 2240, 64 },
+ { "f20", 2304, 64 },
+ { "f21", 2368, 64 },
+ { "f22", 2432, 64 },
+ { "f23", 2496, 64 },
+ { "f24", 2560, 64 },
+ { "f25", 2624, 64 },
+ { "f26", 2688, 64 },
+ { "f27", 2752, 64 },
+ { "f28", 2816, 64 },
+ { "f29", 2880, 64 },
+ { "f30", 2944, 64 },
+ { "f31", 3008, 64 },
+ { "pc", 3072, 32 },
+ { "msr", 3104, 32 },
+ { "cr", 3136, 32 },
+ { "lr", 3168, 32 },
+ { "ctr", 3200, 32 },
+ { "xer", 3232, 32 },
+ { "fpscr", 3264, 32 },
+ { "orig_r3", 3296, 32 },
+ { "trap", 3328, 32 },
+ { "vr0", 3360, 128 },
+ { "vr1", 3488, 128 },
+ { "vr2", 3616, 128 },
+ { "vr3", 3744, 128 },
+ { "vr4", 3872, 128 },
+ { "vr5", 4000, 128 },
+ { "vr6", 4128, 128 },
+ { "vr7", 4256, 128 },
+ { "vr8", 4384, 128 },
+ { "vr9", 4512, 128 },
+ { "vr10", 4640, 128 },
+ { "vr11", 4768, 128 },
+ { "vr12", 4896, 128 },
+ { "vr13", 5024, 128 },
+ { "vr14", 5152, 128 },
+ { "vr15", 5280, 128 },
+ { "vr16", 5408, 128 },
+ { "vr17", 5536, 128 },
+ { "vr18", 5664, 128 },
+ { "vr19", 5792, 128 },
+ { "vr20", 5920, 128 },
+ { "vr21", 6048, 128 },
+ { "vr22", 6176, 128 },
+ { "vr23", 6304, 128 },
+ { "vr24", 6432, 128 },
+ { "vr25", 6560, 128 },
+ { "vr26", 6688, 128 },
+ { "vr27", 6816, 128 },
+ { "vr28", 6944, 128 },
+ { "vr29", 7072, 128 },
+ { "vr30", 7200, 128 },
+ { "vr31", 7328, 128 },
+ { "vscr", 7456, 32 },
+ { "vrsave", 7488, 32 }
+};
+static const char *expedite_regs[] = { "r1", "pc", 0 };
+#define num_regs (sizeof (regs) / sizeof (regs[0]))
+
+static
+CORE_ADDR get_pc (void)
+{
+ unsigned long pc;
+
+ collect_register_by_name ("pc", &pc);
+
+ dlog(1, "stop pc is %p\n", (void *) pc);
+ return pc;
+}
+
+static
+void set_pc (CORE_ADDR newpc)
+{
+ Bool mod;
+ supply_register_by_name ("pc", &newpc, &mod);
+ if (mod)
+ dlog(1, "set pc to %p\n", C2v (newpc));
+ else
+ dlog(1, "set pc not changed %p\n", C2v (newpc));
+}
+
+/* store registers in the guest state (gdbserver_to_valgrind)
+ or fetch register from the guest state (valgrind_to_gdbserver). */
+static
+void transfer_register (ThreadId tid, int abs_regno, void * buf,
+ transfer_direction dir, int size, Bool *mod)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ int set = abs_regno / num_regs;
+ int regno = abs_regno % num_regs;
+ *mod = False;
+
+ VexGuestPPC32State* ppc32 = (VexGuestPPC32State*) get_arch (set, tst);
+
+ switch (regno) {
+ // numbers here have to match the order of regs above
+ // Attention: gdb order does not match valgrind order.
+ case 0: VG_(transfer) (&ppc32->guest_GPR0, buf, dir, size, mod); break;
+ case 1: VG_(transfer) (&ppc32->guest_GPR1, buf, dir, size, mod); break;
+ case 2: VG_(transfer) (&ppc32->guest_GPR2, buf, dir, size, mod); break;
+ case 3: VG_(transfer) (&ppc32->guest_GPR3, buf, dir, size, mod); break;
+ case 4: VG_(transfer) (&ppc32->guest_GPR4, buf, dir, size, mod); break;
+ case 5: VG_(transfer) (&ppc32->guest_GPR5, buf, dir, size, mod); break;
+ case 6: VG_(transfer) (&ppc32->guest_GPR6, buf, dir, size, mod); break;
+ case 7: VG_(transfer) (&ppc32->guest_GPR7, buf, dir, size, mod); break;
+ case 8: VG_(transfer) (&ppc32->guest_GPR8, buf, dir, size, mod); break;
+ case 9: VG_(transfer) (&ppc32->guest_GPR9, buf, dir, size, mod); break;
+ case 10: VG_(transfer) (&ppc32->guest_GPR10, buf, dir, size, mod); break;
+ case 11: VG_(transfer) (&ppc32->guest_GPR11, buf, dir, size, mod); break;
+ case 12: VG_(transfer) (&ppc32->guest_GPR12, buf, dir, size, mod); break;
+ case 13: VG_(transfer) (&ppc32->guest_GPR13, buf, dir, size, mod); break;
+ case 14: VG_(transfer) (&ppc32->guest_GPR14, buf, dir, size, mod); break;
+ case 15: VG_(transfer) (&ppc32->guest_GPR15, buf, dir, size, mod); break;
+ case 16: VG_(transfer) (&ppc32->guest_GPR16, buf, dir, size, mod); break;
+ case 17: VG_(transfer) (&ppc32->guest_GPR17, buf, dir, size, mod); break;
+ case 18: VG_(transfer) (&ppc32->guest_GPR18, buf, dir, size, mod); break;
+ case 19: VG_(transfer) (&ppc32->guest_GPR19, buf, dir, size, mod); break;
+ case 20: VG_(transfer) (&ppc32->guest_GPR20, buf, dir, size, mod); break;
+ case 21: VG_(transfer) (&ppc32->guest_GPR21, buf, dir, size, mod); break;
+ case 22: VG_(transfer) (&ppc32->guest_GPR22, buf, dir, size, mod); break;
+ case 23: VG_(transfer) (&ppc32->guest_GPR23, buf, dir, size, mod); break;
+ case 24: VG_(transfer) (&ppc32->guest_GPR24, buf, dir, size, mod); break;
+ case 25: VG_(transfer) (&ppc32->guest_GPR25, buf, dir, size, mod); break;
+ case 26: VG_(transfer) (&ppc32->guest_GPR26, buf, dir, size, mod); break;
+ case 27: VG_(transfer) (&ppc32->guest_GPR27, buf, dir, size, mod); break;
+ case 28: VG_(transfer) (&ppc32->guest_GPR28, buf, dir, size, mod); break;
+ case 29: VG_(transfer) (&ppc32->guest_GPR29, buf, dir, size, mod); break;
+ case 30: VG_(transfer) (&ppc32->guest_GPR30, buf, dir, size, mod); break;
+ case 31: VG_(transfer) (&ppc32->guest_GPR31, buf, dir, size, mod); break;
+ case 32: VG_(transfer) (&ppc32->guest_VSR0, buf, dir, size, mod); break;
+ case 33: VG_(transfer) (&ppc32->guest_VSR1, buf, dir, size, mod); break;
+ case 34: VG_(transfer) (&ppc32->guest_VSR2, buf, dir, size, mod); break;
+ case 35: VG_(transfer) (&ppc32->guest_VSR3, buf, dir, size, mod); break;
+ case 36: VG_(transfer) (&ppc32->guest_VSR4, buf, dir, size, mod); break;
+ case 37: VG_(transfer) (&ppc32->guest_VSR5, buf, dir, size, mod); break;
+ case 38: VG_(transfer) (&ppc32->guest_VSR6, buf, dir, size, mod); break;
+ case 39: VG_(transfer) (&ppc32->guest_VSR7, buf, dir, size, mod); break;
+ case 40: VG_(transfer) (&ppc32->guest_VSR8, buf, dir, size, mod); break;
+ case 41: VG_(transfer) (&ppc32->guest_VSR9, buf, dir, size, mod); break;
+ case 42: VG_(transfer) (&ppc32->guest_VSR10, buf, dir, size, mod); break;
+ case 43: VG_(transfer) (&ppc32->guest_VSR11, buf, dir, size, mod); break;
+ case 44: VG_(transfer) (&ppc32->guest_VSR12, buf, dir, size, mod); break;
+ case 45: VG_(transfer) (&ppc32->guest_VSR13, buf, dir, size, mod); break;
+ case 46: VG_(transfer) (&ppc32->guest_VSR14, buf, dir, size, mod); break;
+ case 47: VG_(transfer) (&ppc32->guest_VSR15, buf, dir, size, mod); break;
+ case 48: VG_(transfer) (&ppc32->guest_VSR16, buf, dir, size, mod); break;
+ case 49: VG_(transfer) (&ppc32->guest_VSR17, buf, dir, size, mod); break;
+ case 50: VG_(transfer) (&ppc32->guest_VSR18, buf, dir, size, mod); break;
+ case 51: VG_(transfer) (&ppc32->guest_VSR19, buf, dir, size, mod); break;
+ case 52: VG_(transfer) (&ppc32->guest_VSR20, buf, dir, size, mod); break;
+ case 53: VG_(transfer) (&ppc32->guest_VSR21, buf, dir, size, mod); break;
+ case 54: VG_(transfer) (&ppc32->guest_VSR22, buf, dir, size, mod); break;
+ case 55: VG_(transfer) (&ppc32->guest_VSR23, buf, dir, size, mod); break;
+ case 56: VG_(transfer) (&ppc32->guest_VSR24, buf, dir, size, mod); break;
+ case 57: VG_(transfer) (&ppc32->guest_VSR25, buf, dir, size, mod); break;
+ case 58: VG_(transfer) (&ppc32->guest_VSR26, buf, dir, size, mod); break;
+ case 59: VG_(transfer) (&ppc32->guest_VSR27, buf, dir, size, mod); break;
+ case 60: VG_(transfer) (&ppc32->guest_VSR28, buf, dir, size, mod); break;
+ case 61: VG_(transfer) (&ppc32->guest_VSR29, buf, dir, size, mod); break;
+ case 62: VG_(transfer) (&ppc32->guest_VSR30, buf, dir, size, mod); break;
+ case 63: VG_(transfer) (&ppc32->guest_VSR31, buf, dir, size, mod); break;
+ case 64: VG_(transfer) (&ppc32->guest_CIA, buf, dir, size, mod); break;
+ case 65: *mod = False; break; // VEX does not model Machine State Register
+ case 66: {
+ UInt cr = LibVEX_GuestPPC32_get_CR (ppc32);
+ if (dir == valgrind_to_gdbserver) {
+ VG_(transfer) (&cr, buf, dir, size, mod);
+ } else {
+ UInt newcr;
+ VG_(transfer) (&newcr, buf, dir, size, mod);
+ *mod = newcr != cr;
+ LibVEX_GuestPPC32_put_CR (newcr, ppc32);
+ }
+ break;
+ }
+ case 67: VG_(transfer) (&ppc32->guest_LR, buf, dir, size, mod); break;
+ case 68: VG_(transfer) (&ppc32->guest_CTR, buf, dir, size, mod); break;
+ case 69: {
+ UInt xer = LibVEX_GuestPPC32_get_XER (ppc32);
+ if (dir == valgrind_to_gdbserver) {
+ VG_(transfer) (&xer, buf, dir, size, mod);
+ } else {
+ UInt newxer;
+ VG_(transfer) (&newxer, buf, dir, size, mod);
+ *mod = newxer != xer;
+ LibVEX_GuestPPC32_put_XER (newxer, ppc32);
+ }
+ break;
+ }
+ case 70: VG_(transfer) (&ppc32->guest_FPROUND, buf, dir, size, mod); break;
+ case 71: *mod = False; break; // GDBTD???? VEX { "orig_r3", 3296, 32 },
+ case 72: *mod = False; break; // GDBTD???? VEX { "trap", 3328, 32 },
+ case 73: VG_(transfer) (&ppc32->guest_VSR32, buf, dir, size, mod); break;
+ case 74: VG_(transfer) (&ppc32->guest_VSR33, buf, dir, size, mod); break;
+ case 75: VG_(transfer) (&ppc32->guest_VSR34, buf, dir, size, mod); break;
+ case 76: VG_(transfer) (&ppc32->guest_VSR35, buf, dir, size, mod); break;
+ case 77: VG_(transfer) (&ppc32->guest_VSR36, buf, dir, size, mod); break;
+ case 78: VG_(transfer) (&ppc32->guest_VSR37, buf, dir, size, mod); break;
+ case 79: VG_(transfer) (&ppc32->guest_VSR38, buf, dir, size, mod); break;
+ case 80: VG_(transfer) (&ppc32->guest_VSR39, buf, dir, size, mod); break;
+ case 81: VG_(transfer) (&ppc32->guest_VSR40, buf, dir, size, mod); break;
+ case 82: VG_(transfer) (&ppc32->guest_VSR41, buf, dir, size, mod); break;
+ case 83: VG_(transfer) (&ppc32->guest_VSR42, buf, dir, size, mod); break;
+ case 84: VG_(transfer) (&ppc32->guest_VSR43, buf, dir, size, mod); break;
+ case 85: VG_(transfer) (&ppc32->guest_VSR44, buf, dir, size, mod); break;
+ case 86: VG_(transfer) (&ppc32->guest_VSR45, buf, dir, size, mod); break;
+ case 87: VG_(transfer) (&ppc32->guest_VSR46, buf, dir, size, mod); break;
+ case 88: VG_(transfer) (&ppc32->guest_VSR47, buf, dir, size, mod); break;
+ case 89: VG_(transfer) (&ppc32->guest_VSR48, buf, dir, size, mod); break;
+ case 90: VG_(transfer) (&ppc32->guest_VSR49, buf, dir, size, mod); break;
+ case 91: VG_(transfer) (&ppc32->guest_VSR50, buf, dir, size, mod); break;
+ case 92: VG_(transfer) (&ppc32->guest_VSR51, buf, dir, size, mod); break;
+ case 93: VG_(transfer) (&ppc32->guest_VSR52, buf, dir, size, mod); break;
+ case 94: VG_(transfer) (&ppc32->guest_VSR53, buf, dir, size, mod); break;
+ case 95: VG_(transfer) (&ppc32->guest_VSR54, buf, dir, size, mod); break;
+ case 96: VG_(transfer) (&ppc32->guest_VSR55, buf, dir, size, mod); break;
+ case 97: VG_(transfer) (&ppc32->guest_VSR56, buf, dir, size, mod); break;
+ case 98: VG_(transfer) (&ppc32->guest_VSR57, buf, dir, size, mod); break;
+ case 99: VG_(transfer) (&ppc32->guest_VSR58, buf, dir, size, mod); break;
+ case 100: VG_(transfer) (&ppc32->guest_VSR59, buf, dir, size, mod); break;
+ case 101: VG_(transfer) (&ppc32->guest_VSR60, buf, dir, size, mod); break;
+ case 102: VG_(transfer) (&ppc32->guest_VSR61, buf, dir, size, mod); break;
+ case 103: VG_(transfer) (&ppc32->guest_VSR62, buf, dir, size, mod); break;
+ case 104: VG_(transfer) (&ppc32->guest_VSR63, buf, dir, size, mod); break;
+ case 105: VG_(transfer) (&ppc32->guest_VSCR, buf, dir, size, mod); break;
+ case 106: VG_(transfer) (&ppc32->guest_VRSAVE, buf, dir, size, mod); break;
+ default: vg_assert(0);
+ }
+}
+
+static struct valgrind_target_ops low_target = {
+ num_regs,
+ regs,
+ 1, //r1
+ transfer_register,
+ get_pc,
+ set_pc,
+ "ppc32",
+ "powerpc-altivec32l.xml",
+ "powerpc-altivec32l-valgrind.xml"
+};
+
+void ppc32_init_architecture (struct valgrind_target_ops *target)
+{
+ *target = low_target;
+ set_register_cache (regs, num_regs);
+ gdbserver_expedite_regs = expedite_regs;
+}
+
diff --git a/coregrind/m_gdbserver/valgrind-low-ppc64.c b/coregrind/m_gdbserver/valgrind-low-ppc64.c
new file mode 100644
index 0000000..f2fdbec
--- /dev/null
+++ b/coregrind/m_gdbserver/valgrind-low-ppc64.c
@@ -0,0 +1,339 @@
+/* Low level interface to valgrind, for the remote server for GDB integrated
+ in valgrind.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+ This file is part of VALGRIND.
+ It has been inspired from a file from gdbserver in gdb 6.6.
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "target.h"
+#include "regdef.h"
+#include "regcache.h"
+
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_machine.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_core_gdbserver.h"
+
+#include "valgrind_low.h"
+
+#include "libvex_guest_ppc64.h"
+
+struct reg regs[] = {
+ { "r0", 0, 64 },
+ { "r1", 64, 64 },
+ { "r2", 128, 64 },
+ { "r3", 192, 64 },
+ { "r4", 256, 64 },
+ { "r5", 320, 64 },
+ { "r6", 384, 64 },
+ { "r7", 448, 64 },
+ { "r8", 512, 64 },
+ { "r9", 576, 64 },
+ { "r10", 640, 64 },
+ { "r11", 704, 64 },
+ { "r12", 768, 64 },
+ { "r13", 832, 64 },
+ { "r14", 896, 64 },
+ { "r15", 960, 64 },
+ { "r16", 1024, 64 },
+ { "r17", 1088, 64 },
+ { "r18", 1152, 64 },
+ { "r19", 1216, 64 },
+ { "r20", 1280, 64 },
+ { "r21", 1344, 64 },
+ { "r22", 1408, 64 },
+ { "r23", 1472, 64 },
+ { "r24", 1536, 64 },
+ { "r25", 1600, 64 },
+ { "r26", 1664, 64 },
+ { "r27", 1728, 64 },
+ { "r28", 1792, 64 },
+ { "r29", 1856, 64 },
+ { "r30", 1920, 64 },
+ { "r31", 1984, 64 },
+ { "f0", 2048, 64 },
+ { "f1", 2112, 64 },
+ { "f2", 2176, 64 },
+ { "f3", 2240, 64 },
+ { "f4", 2304, 64 },
+ { "f5", 2368, 64 },
+ { "f6", 2432, 64 },
+ { "f7", 2496, 64 },
+ { "f8", 2560, 64 },
+ { "f9", 2624, 64 },
+ { "f10", 2688, 64 },
+ { "f11", 2752, 64 },
+ { "f12", 2816, 64 },
+ { "f13", 2880, 64 },
+ { "f14", 2944, 64 },
+ { "f15", 3008, 64 },
+ { "f16", 3072, 64 },
+ { "f17", 3136, 64 },
+ { "f18", 3200, 64 },
+ { "f19", 3264, 64 },
+ { "f20", 3328, 64 },
+ { "f21", 3392, 64 },
+ { "f22", 3456, 64 },
+ { "f23", 3520, 64 },
+ { "f24", 3584, 64 },
+ { "f25", 3648, 64 },
+ { "f26", 3712, 64 },
+ { "f27", 3776, 64 },
+ { "f28", 3840, 64 },
+ { "f29", 3904, 64 },
+ { "f30", 3968, 64 },
+ { "f31", 4032, 64 },
+ { "pc", 4096, 64 },
+ { "msr", 4160, 64 },
+ { "cr", 4224, 32 },
+ { "lr", 4256, 64 },
+ { "ctr", 4320, 64 },
+ { "xer", 4384, 32 },
+ { "fpscr", 4416, 32 },
+ { "orig_r3", 4448, 64 },
+ { "trap", 4512, 64 },
+ { "vr0", 4576, 128 },
+ { "vr1", 4704, 128 },
+ { "vr2", 4832, 128 },
+ { "vr3", 4960, 128 },
+ { "vr4", 5088, 128 },
+ { "vr5", 5216, 128 },
+ { "vr6", 5344, 128 },
+ { "vr7", 5472, 128 },
+ { "vr8", 5600, 128 },
+ { "vr9", 5728, 128 },
+ { "vr10", 5856, 128 },
+ { "vr11", 5984, 128 },
+ { "vr12", 6112, 128 },
+ { "vr13", 6240, 128 },
+ { "vr14", 6368, 128 },
+ { "vr15", 6496, 128 },
+ { "vr16", 6624, 128 },
+ { "vr17", 6752, 128 },
+ { "vr18", 6880, 128 },
+ { "vr19", 7008, 128 },
+ { "vr20", 7136, 128 },
+ { "vr21", 7264, 128 },
+ { "vr22", 7392, 128 },
+ { "vr23", 7520, 128 },
+ { "vr24", 7648, 128 },
+ { "vr25", 7776, 128 },
+ { "vr26", 7904, 128 },
+ { "vr27", 8032, 128 },
+ { "vr28", 8160, 128 },
+ { "vr29", 8288, 128 },
+ { "vr30", 8416, 128 },
+ { "vr31", 8544, 128 },
+ { "vscr", 8672, 32 },
+ { "vrsave", 8704, 32 },
+};
+static const char *expedite_regs[] = { "r1", "pc", 0 };
+#define num_regs (sizeof (regs) / sizeof (regs[0]))
+
+static
+CORE_ADDR get_pc (void)
+{
+ unsigned long pc;
+
+ collect_register_by_name ("pc", &pc);
+
+ dlog(1, "stop pc is %p\n", (void *) pc);
+ return pc;
+}
+
+static
+void set_pc (CORE_ADDR newpc)
+{
+ Bool mod;
+ supply_register_by_name ("pc", &newpc, &mod);
+ if (mod)
+ dlog(1, "set pc to %p\n", C2v (newpc));
+ else
+ dlog(1, "set pc not changed %p\n", C2v (newpc));
+}
+
+/* store registers in the guest state (gdbserver_to_valgrind)
+ or fetch register from the guest state (valgrind_to_gdbserver). */
+static
+void transfer_register (ThreadId tid, int abs_regno, void * buf,
+ transfer_direction dir, int size, Bool *mod)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ int set = abs_regno / num_regs;
+ int regno = abs_regno % num_regs;
+ *mod = False;
+
+ VexGuestPPC64State* ppc64 = (VexGuestPPC64State*) get_arch (set, tst);
+
+ switch (regno) {
+ // numbers here have to match the order of regs above
+ // Attention: gdb order does not match valgrind order.
+ case 0: VG_(transfer) (&ppc64->guest_GPR0, buf, dir, size, mod); break;
+ case 1: VG_(transfer) (&ppc64->guest_GPR1, buf, dir, size, mod); break;
+ case 2: VG_(transfer) (&ppc64->guest_GPR2, buf, dir, size, mod); break;
+ case 3: VG_(transfer) (&ppc64->guest_GPR3, buf, dir, size, mod); break;
+ case 4: VG_(transfer) (&ppc64->guest_GPR4, buf, dir, size, mod); break;
+ case 5: VG_(transfer) (&ppc64->guest_GPR5, buf, dir, size, mod); break;
+ case 6: VG_(transfer) (&ppc64->guest_GPR6, buf, dir, size, mod); break;
+ case 7: VG_(transfer) (&ppc64->guest_GPR7, buf, dir, size, mod); break;
+ case 8: VG_(transfer) (&ppc64->guest_GPR8, buf, dir, size, mod); break;
+ case 9: VG_(transfer) (&ppc64->guest_GPR9, buf, dir, size, mod); break;
+ case 10: VG_(transfer) (&ppc64->guest_GPR10, buf, dir, size, mod); break;
+ case 11: VG_(transfer) (&ppc64->guest_GPR11, buf, dir, size, mod); break;
+ case 12: VG_(transfer) (&ppc64->guest_GPR12, buf, dir, size, mod); break;
+ case 13: VG_(transfer) (&ppc64->guest_GPR13, buf, dir, size, mod); break;
+ case 14: VG_(transfer) (&ppc64->guest_GPR14, buf, dir, size, mod); break;
+ case 15: VG_(transfer) (&ppc64->guest_GPR15, buf, dir, size, mod); break;
+ case 16: VG_(transfer) (&ppc64->guest_GPR16, buf, dir, size, mod); break;
+ case 17: VG_(transfer) (&ppc64->guest_GPR17, buf, dir, size, mod); break;
+ case 18: VG_(transfer) (&ppc64->guest_GPR18, buf, dir, size, mod); break;
+ case 19: VG_(transfer) (&ppc64->guest_GPR19, buf, dir, size, mod); break;
+ case 20: VG_(transfer) (&ppc64->guest_GPR20, buf, dir, size, mod); break;
+ case 21: VG_(transfer) (&ppc64->guest_GPR21, buf, dir, size, mod); break;
+ case 22: VG_(transfer) (&ppc64->guest_GPR22, buf, dir, size, mod); break;
+ case 23: VG_(transfer) (&ppc64->guest_GPR23, buf, dir, size, mod); break;
+ case 24: VG_(transfer) (&ppc64->guest_GPR24, buf, dir, size, mod); break;
+ case 25: VG_(transfer) (&ppc64->guest_GPR25, buf, dir, size, mod); break;
+ case 26: VG_(transfer) (&ppc64->guest_GPR26, buf, dir, size, mod); break;
+ case 27: VG_(transfer) (&ppc64->guest_GPR27, buf, dir, size, mod); break;
+ case 28: VG_(transfer) (&ppc64->guest_GPR28, buf, dir, size, mod); break;
+ case 29: VG_(transfer) (&ppc64->guest_GPR29, buf, dir, size, mod); break;
+ case 30: VG_(transfer) (&ppc64->guest_GPR30, buf, dir, size, mod); break;
+ case 31: VG_(transfer) (&ppc64->guest_GPR31, buf, dir, size, mod); break;
+ case 32: VG_(transfer) (&ppc64->guest_VSR0, buf, dir, size, mod); break;
+ case 33: VG_(transfer) (&ppc64->guest_VSR1, buf, dir, size, mod); break;
+ case 34: VG_(transfer) (&ppc64->guest_VSR2, buf, dir, size, mod); break;
+ case 35: VG_(transfer) (&ppc64->guest_VSR3, buf, dir, size, mod); break;
+ case 36: VG_(transfer) (&ppc64->guest_VSR4, buf, dir, size, mod); break;
+ case 37: VG_(transfer) (&ppc64->guest_VSR5, buf, dir, size, mod); break;
+ case 38: VG_(transfer) (&ppc64->guest_VSR6, buf, dir, size, mod); break;
+ case 39: VG_(transfer) (&ppc64->guest_VSR7, buf, dir, size, mod); break;
+ case 40: VG_(transfer) (&ppc64->guest_VSR8, buf, dir, size, mod); break;
+ case 41: VG_(transfer) (&ppc64->guest_VSR9, buf, dir, size, mod); break;
+ case 42: VG_(transfer) (&ppc64->guest_VSR10, buf, dir, size, mod); break;
+ case 43: VG_(transfer) (&ppc64->guest_VSR11, buf, dir, size, mod); break;
+ case 44: VG_(transfer) (&ppc64->guest_VSR12, buf, dir, size, mod); break;
+ case 45: VG_(transfer) (&ppc64->guest_VSR13, buf, dir, size, mod); break;
+ case 46: VG_(transfer) (&ppc64->guest_VSR14, buf, dir, size, mod); break;
+ case 47: VG_(transfer) (&ppc64->guest_VSR15, buf, dir, size, mod); break;
+ case 48: VG_(transfer) (&ppc64->guest_VSR16, buf, dir, size, mod); break;
+ case 49: VG_(transfer) (&ppc64->guest_VSR17, buf, dir, size, mod); break;
+ case 50: VG_(transfer) (&ppc64->guest_VSR18, buf, dir, size, mod); break;
+ case 51: VG_(transfer) (&ppc64->guest_VSR19, buf, dir, size, mod); break;
+ case 52: VG_(transfer) (&ppc64->guest_VSR20, buf, dir, size, mod); break;
+ case 53: VG_(transfer) (&ppc64->guest_VSR21, buf, dir, size, mod); break;
+ case 54: VG_(transfer) (&ppc64->guest_VSR22, buf, dir, size, mod); break;
+ case 55: VG_(transfer) (&ppc64->guest_VSR23, buf, dir, size, mod); break;
+ case 56: VG_(transfer) (&ppc64->guest_VSR24, buf, dir, size, mod); break;
+ case 57: VG_(transfer) (&ppc64->guest_VSR25, buf, dir, size, mod); break;
+ case 58: VG_(transfer) (&ppc64->guest_VSR26, buf, dir, size, mod); break;
+ case 59: VG_(transfer) (&ppc64->guest_VSR27, buf, dir, size, mod); break;
+ case 60: VG_(transfer) (&ppc64->guest_VSR28, buf, dir, size, mod); break;
+ case 61: VG_(transfer) (&ppc64->guest_VSR29, buf, dir, size, mod); break;
+ case 62: VG_(transfer) (&ppc64->guest_VSR30, buf, dir, size, mod); break;
+ case 63: VG_(transfer) (&ppc64->guest_VSR31, buf, dir, size, mod); break;
+ case 64: VG_(transfer) (&ppc64->guest_CIA, buf, dir, size, mod); break;
+ case 65: *mod = False; break; // VEX does not model Machine State Register
+ case 66: {
+ UInt cr = LibVEX_GuestPPC64_get_CR (ppc64);
+ if (dir == valgrind_to_gdbserver) {
+ VG_(transfer) (&cr, buf, dir, size, mod);
+ } else {
+ UInt newcr;
+ VG_(transfer) (&newcr, buf, dir, size, mod);
+ *mod = newcr != cr;
+ LibVEX_GuestPPC64_put_CR (newcr, ppc64);
+ }
+ break;
+ }
+ case 67: VG_(transfer) (&ppc64->guest_LR, buf, dir, size, mod); break;
+ case 68: VG_(transfer) (&ppc64->guest_CTR, buf, dir, size, mod); break;
+ case 69: {
+ UInt xer = LibVEX_GuestPPC64_get_XER (ppc64);
+ if (dir == valgrind_to_gdbserver) {
+ VG_(transfer) (&xer, buf, dir, size, mod);
+ } else {
+ UInt newxer;
+ VG_(transfer) (&newxer, buf, dir, size, mod);
+ *mod = newxer != xer;
+ LibVEX_GuestPPC64_put_XER (newxer, ppc64);
+ }
+ break;
+ }
+ case 70: VG_(transfer) (&ppc64->guest_FPROUND, buf, dir, size, mod); break;
+ case 71: *mod = False; break; // GDBTD???? VEX { "orig_r3", 4448, 64 },
+ case 72: *mod = False; break; // GDBTD???? VEX { "trap", 4512, 64 },
+ case 73: VG_(transfer) (&ppc64->guest_VSR32, buf, dir, size, mod); break;
+ case 74: VG_(transfer) (&ppc64->guest_VSR33, buf, dir, size, mod); break;
+ case 75: VG_(transfer) (&ppc64->guest_VSR34, buf, dir, size, mod); break;
+ case 76: VG_(transfer) (&ppc64->guest_VSR35, buf, dir, size, mod); break;
+ case 77: VG_(transfer) (&ppc64->guest_VSR36, buf, dir, size, mod); break;
+ case 78: VG_(transfer) (&ppc64->guest_VSR37, buf, dir, size, mod); break;
+ case 79: VG_(transfer) (&ppc64->guest_VSR38, buf, dir, size, mod); break;
+ case 80: VG_(transfer) (&ppc64->guest_VSR39, buf, dir, size, mod); break;
+ case 81: VG_(transfer) (&ppc64->guest_VSR40, buf, dir, size, mod); break;
+ case 82: VG_(transfer) (&ppc64->guest_VSR41, buf, dir, size, mod); break;
+ case 83: VG_(transfer) (&ppc64->guest_VSR42, buf, dir, size, mod); break;
+ case 84: VG_(transfer) (&ppc64->guest_VSR43, buf, dir, size, mod); break;
+ case 85: VG_(transfer) (&ppc64->guest_VSR44, buf, dir, size, mod); break;
+ case 86: VG_(transfer) (&ppc64->guest_VSR45, buf, dir, size, mod); break;
+ case 87: VG_(transfer) (&ppc64->guest_VSR46, buf, dir, size, mod); break;
+ case 88: VG_(transfer) (&ppc64->guest_VSR47, buf, dir, size, mod); break;
+ case 89: VG_(transfer) (&ppc64->guest_VSR48, buf, dir, size, mod); break;
+ case 90: VG_(transfer) (&ppc64->guest_VSR49, buf, dir, size, mod); break;
+ case 91: VG_(transfer) (&ppc64->guest_VSR50, buf, dir, size, mod); break;
+ case 92: VG_(transfer) (&ppc64->guest_VSR51, buf, dir, size, mod); break;
+ case 93: VG_(transfer) (&ppc64->guest_VSR52, buf, dir, size, mod); break;
+ case 94: VG_(transfer) (&ppc64->guest_VSR53, buf, dir, size, mod); break;
+ case 95: VG_(transfer) (&ppc64->guest_VSR54, buf, dir, size, mod); break;
+ case 96: VG_(transfer) (&ppc64->guest_VSR55, buf, dir, size, mod); break;
+ case 97: VG_(transfer) (&ppc64->guest_VSR56, buf, dir, size, mod); break;
+ case 98: VG_(transfer) (&ppc64->guest_VSR57, buf, dir, size, mod); break;
+ case 99: VG_(transfer) (&ppc64->guest_VSR58, buf, dir, size, mod); break;
+ case 100: VG_(transfer) (&ppc64->guest_VSR59, buf, dir, size, mod); break;
+ case 101: VG_(transfer) (&ppc64->guest_VSR60, buf, dir, size, mod); break;
+ case 102: VG_(transfer) (&ppc64->guest_VSR61, buf, dir, size, mod); break;
+ case 103: VG_(transfer) (&ppc64->guest_VSR62, buf, dir, size, mod); break;
+ case 104: VG_(transfer) (&ppc64->guest_VSR63, buf, dir, size, mod); break;
+ case 105: VG_(transfer) (&ppc64->guest_VSCR, buf, dir, size, mod); break;
+ case 106: VG_(transfer) (&ppc64->guest_VRSAVE, buf, dir, size, mod); break;
+ default: vg_assert(0);
+ }
+}
+
+static struct valgrind_target_ops low_target = {
+ num_regs,
+ regs,
+ 1, //r1
+ transfer_register,
+ get_pc,
+ set_pc,
+ "ppc64",
+ "powerpc-altivec64l.xml",
+ "powerpc-altivec64l-valgrind.xml"
+};
+
+void ppc64_init_architecture (struct valgrind_target_ops *target)
+{
+ *target = low_target;
+ set_register_cache (regs, num_regs);
+ gdbserver_expedite_regs = expedite_regs;
+}
diff --git a/coregrind/m_gdbserver/valgrind-low-s390x.c b/coregrind/m_gdbserver/valgrind-low-s390x.c
new file mode 100644
index 0000000..3e6b53c
--- /dev/null
+++ b/coregrind/m_gdbserver/valgrind-low-s390x.c
@@ -0,0 +1,205 @@
+/* Low level interface to valgrind, for the remote server for GDB integrated
+ in valgrind.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+ This file is part of VALGRIND.
+ It has been inspired from a file from gdbserver in gdb 6.6.
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "target.h"
+#include "regdef.h"
+#include "regcache.h"
+
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_machine.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_core_gdbserver.h"
+
+#include "valgrind_low.h"
+
+#include "libvex_guest_s390x.h"
+
+struct reg regs[] = {
+ { "pswm", 0, 64 },
+ { "pswa", 64, 64 },
+ { "r0", 128, 64 },
+ { "r1", 192, 64 },
+ { "r2", 256, 64 },
+ { "r3", 320, 64 },
+ { "r4", 384, 64 },
+ { "r5", 448, 64 },
+ { "r6", 512, 64 },
+ { "r7", 576, 64 },
+ { "r8", 640, 64 },
+ { "r9", 704, 64 },
+ { "r10", 768, 64 },
+ { "r11", 832, 64 },
+ { "r12", 896, 64 },
+ { "r13", 960, 64 },
+ { "r14", 1024, 64 },
+ { "r15", 1088, 64 },
+ { "acr0", 1152, 32 },
+ { "acr1", 1184, 32 },
+ { "acr2", 1216, 32 },
+ { "acr3", 1248, 32 },
+ { "acr4", 1280, 32 },
+ { "acr5", 1312, 32 },
+ { "acr6", 1344, 32 },
+ { "acr7", 1376, 32 },
+ { "acr8", 1408, 32 },
+ { "acr9", 1440, 32 },
+ { "acr10", 1472, 32 },
+ { "acr11", 1504, 32 },
+ { "acr12", 1536, 32 },
+ { "acr13", 1568, 32 },
+ { "acr14", 1600, 32 },
+ { "acr15", 1632, 32 },
+ { "fpc", 1664, 32 },
+ { "f0", 1696, 64 },
+ { "f1", 1760, 64 },
+ { "f2", 1824, 64 },
+ { "f3", 1888, 64 },
+ { "f4", 1952, 64 },
+ { "f5", 2016, 64 },
+ { "f6", 2080, 64 },
+ { "f7", 2144, 64 },
+ { "f8", 2208, 64 },
+ { "f9", 2272, 64 },
+ { "f10", 2336, 64 },
+ { "f11", 2400, 64 },
+ { "f12", 2464, 64 },
+ { "f13", 2528, 64 },
+ { "f14", 2592, 64 },
+ { "f15", 2656, 64 },
+};
+static const char *expedite_regs[] = { "r14", "r15", "pswa", 0 };
+#define num_regs (sizeof (regs) / sizeof (regs[0]))
+
+static
+CORE_ADDR get_pc (void)
+{
+ unsigned long pc;
+
+ collect_register_by_name ("pswa", &pc);
+
+ dlog(1, "stop pc is %p\n", (void *) pc);
+ return pc;
+}
+
+static
+void set_pc (CORE_ADDR newpc)
+{
+ Bool mod;
+ supply_register_by_name ("pswa", &newpc, &mod);
+ if (mod)
+ dlog(1, "set pc to %p\n", C2v (newpc));
+ else
+ dlog(1, "set pc not changed %p\n", C2v (newpc));
+}
+
+/* store registers in the guest state (gdbserver_to_valgrind)
+ or fetch register from the guest state (valgrind_to_gdbserver). */
+static
+void transfer_register (ThreadId tid, int abs_regno, void * buf,
+ transfer_direction dir, int size, Bool *mod)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ int set = abs_regno / num_regs;
+ int regno = abs_regno % num_regs;
+ *mod = False;
+
+ VexGuestS390XState* s390x = (VexGuestS390XState*) get_arch (set, tst);
+
+ switch (regno) {
+ // numbers here have to match the order of regs above
+ // Attention: gdb order does not match valgrind order.
+ case 0: *mod = False; //GDBTD??? { "pswm", 0, 64 },
+ case 1: VG_(transfer) (&s390x->guest_IA, buf, dir, size, mod); break;
+ case 2: VG_(transfer) (&s390x->guest_r0, buf, dir, size, mod); break;
+ case 3: VG_(transfer) (&s390x->guest_r1, buf, dir, size, mod); break;
+ case 4: VG_(transfer) (&s390x->guest_r2, buf, dir, size, mod); break;
+ case 5: VG_(transfer) (&s390x->guest_r3, buf, dir, size, mod); break;
+ case 6: VG_(transfer) (&s390x->guest_r4, buf, dir, size, mod); break;
+ case 7: VG_(transfer) (&s390x->guest_r5, buf, dir, size, mod); break;
+ case 8: VG_(transfer) (&s390x->guest_r6, buf, dir, size, mod); break;
+ case 9: VG_(transfer) (&s390x->guest_r7, buf, dir, size, mod); break;
+ case 10: VG_(transfer) (&s390x->guest_r8, buf, dir, size, mod); break;
+ case 11: VG_(transfer) (&s390x->guest_r9, buf, dir, size, mod); break;
+ case 12: VG_(transfer) (&s390x->guest_r10, buf, dir, size, mod); break;
+ case 13: VG_(transfer) (&s390x->guest_r11, buf, dir, size, mod); break;
+ case 14: VG_(transfer) (&s390x->guest_r12, buf, dir, size, mod); break;
+ case 15: VG_(transfer) (&s390x->guest_r13, buf, dir, size, mod); break;
+ case 16: VG_(transfer) (&s390x->guest_r14, buf, dir, size, mod); break;
+ case 17: VG_(transfer) (&s390x->guest_r15, buf, dir, size, mod); break;
+ case 18: VG_(transfer) (&s390x->guest_a0, buf, dir, size, mod); break;
+ case 19: VG_(transfer) (&s390x->guest_a1, buf, dir, size, mod); break;
+ case 20: VG_(transfer) (&s390x->guest_a2, buf, dir, size, mod); break;
+ case 21: VG_(transfer) (&s390x->guest_a3, buf, dir, size, mod); break;
+ case 22: VG_(transfer) (&s390x->guest_a4, buf, dir, size, mod); break;
+ case 23: VG_(transfer) (&s390x->guest_a5, buf, dir, size, mod); break;
+ case 24: VG_(transfer) (&s390x->guest_a6, buf, dir, size, mod); break;
+ case 25: VG_(transfer) (&s390x->guest_a7, buf, dir, size, mod); break;
+ case 26: VG_(transfer) (&s390x->guest_a8, buf, dir, size, mod); break;
+ case 27: VG_(transfer) (&s390x->guest_a9, buf, dir, size, mod); break;
+ case 28: VG_(transfer) (&s390x->guest_a10, buf, dir, size, mod); break;
+ case 29: VG_(transfer) (&s390x->guest_a11, buf, dir, size, mod); break;
+ case 30: VG_(transfer) (&s390x->guest_a12, buf, dir, size, mod); break;
+ case 31: VG_(transfer) (&s390x->guest_a13, buf, dir, size, mod); break;
+ case 32: VG_(transfer) (&s390x->guest_a14, buf, dir, size, mod); break;
+ case 33: VG_(transfer) (&s390x->guest_a15, buf, dir, size, mod); break;
+ case 34: VG_(transfer) (&s390x->guest_fpc, buf, dir, size, mod); break;
+ case 35: VG_(transfer) (&s390x->guest_f0, buf, dir, size, mod); break;
+ case 36: VG_(transfer) (&s390x->guest_f1, buf, dir, size, mod); break;
+ case 37: VG_(transfer) (&s390x->guest_f2, buf, dir, size, mod); break;
+ case 38: VG_(transfer) (&s390x->guest_f3, buf, dir, size, mod); break;
+ case 39: VG_(transfer) (&s390x->guest_f4, buf, dir, size, mod); break;
+ case 40: VG_(transfer) (&s390x->guest_f5, buf, dir, size, mod); break;
+ case 41: VG_(transfer) (&s390x->guest_f6, buf, dir, size, mod); break;
+ case 42: VG_(transfer) (&s390x->guest_f7, buf, dir, size, mod); break;
+ case 43: VG_(transfer) (&s390x->guest_f8, buf, dir, size, mod); break;
+ case 44: VG_(transfer) (&s390x->guest_f9, buf, dir, size, mod); break;
+ case 45: VG_(transfer) (&s390x->guest_f10, buf, dir, size, mod); break;
+ case 46: VG_(transfer) (&s390x->guest_f11, buf, dir, size, mod); break;
+ case 47: VG_(transfer) (&s390x->guest_f12, buf, dir, size, mod); break;
+ case 48: VG_(transfer) (&s390x->guest_f13, buf, dir, size, mod); break;
+ case 49: VG_(transfer) (&s390x->guest_f14, buf, dir, size, mod); break;
+ case 50: VG_(transfer) (&s390x->guest_f15, buf, dir, size, mod); break;
+ default: vg_assert(0);
+ }
+}
+
+static struct valgrind_target_ops low_target = {
+ num_regs,
+ regs,
+ 17, //sp = r15, which is register offset 17 in regs
+ transfer_register,
+ get_pc,
+ set_pc,
+ "s390x",
+ NULL, // target_xml not needed.
+ NULL // no xml shadow target description (yet?)
+};
+
+void s390x_init_architecture (struct valgrind_target_ops *target)
+{
+ *target = low_target;
+ set_register_cache (regs, num_regs);
+ gdbserver_expedite_regs = expedite_regs;
+}
diff --git a/coregrind/m_gdbserver/valgrind-low-x86.c b/coregrind/m_gdbserver/valgrind-low-x86.c
new file mode 100644
index 0000000..89ef329
--- /dev/null
+++ b/coregrind/m_gdbserver/valgrind-low-x86.c
@@ -0,0 +1,276 @@
+/* Low level interface to valgrind, for the remote server for GDB integrated
+ in valgrind.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+ This file is part of VALGRIND.
+ It has been inspired from a file from gdbserver in gdb 6.6.
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "target.h"
+#include "regdef.h"
+#include "regcache.h"
+
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_machine.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_core_gdbserver.h"
+
+#include "valgrind_low.h"
+
+#include "libvex_guest_x86.h"
+/* GDBTD: ??? have a cleaner way to get the f80 <> f64 conversion functions */
+/* below include needed for conversion f80 <> f64 */
+#include "../../VEX/priv/guest_generic_x87.h"
+
+
+/* below loosely inspired from file generated with gdb regdat.sh */
+
+static struct reg regs[] = {
+ { "eax", 0, 32 },
+ { "ecx", 32, 32 },
+ { "edx", 64, 32 },
+ { "ebx", 96, 32 },
+ { "esp", 128, 32 },
+ { "ebp", 160, 32 },
+ { "esi", 192, 32 },
+ { "edi", 224, 32 },
+ { "eip", 256, 32 },
+ { "eflags", 288, 32 },
+ { "cs", 320, 32 },
+ { "ss", 352, 32 },
+ { "ds", 384, 32 },
+ { "es", 416, 32 },
+ { "fs", 448, 32 },
+ { "gs", 480, 32 },
+ { "st0", 512, 80 },
+ { "st1", 592, 80 },
+ { "st2", 672, 80 },
+ { "st3", 752, 80 },
+ { "st4", 832, 80 },
+ { "st5", 912, 80 },
+ { "st6", 992, 80 },
+ { "st7", 1072, 80 },
+ { "fctrl", 1152, 32 },
+ { "fstat", 1184, 32 },
+ { "ftag", 1216, 32 },
+ { "fiseg", 1248, 32 },
+ { "fioff", 1280, 32 },
+ { "foseg", 1312, 32 },
+ { "fooff", 1344, 32 },
+ { "fop", 1376, 32 },
+ { "xmm0", 1408, 128 },
+ { "xmm1", 1536, 128 },
+ { "xmm2", 1664, 128 },
+ { "xmm3", 1792, 128 },
+ { "xmm4", 1920, 128 },
+ { "xmm5", 2048, 128 },
+ { "xmm6", 2176, 128 },
+ { "xmm7", 2304, 128 },
+ { "mxcsr", 2432, 32 },
+#if defined(VGO_linux)
+ { "orig_eax", 2464, 32 }
+#endif
+};
+static const char *expedite_regs[] = { "ebp", "esp", "eip", 0 };
+#define num_regs (sizeof (regs) / sizeof (regs[0]))
+
+static
+CORE_ADDR get_pc (void)
+{
+ unsigned long pc;
+
+ collect_register_by_name ("eip", &pc);
+
+ dlog(1, "stop pc is %p\n", (void *) pc);
+ return pc;
+}
+
+static
+void set_pc (CORE_ADDR newpc)
+{
+ Bool mod;
+ supply_register_by_name ("eip", &newpc, &mod);
+ if (mod)
+ dlog(1, "set pc to %p\n", C2v (newpc));
+ else
+ dlog(1, "set pc not changed %p\n", C2v (newpc));
+}
+
+/* store registers in the guest state (gdbserver_to_valgrind)
+ or fetch register from the guest state (valgrind_to_gdbserver). */
+static
+void transfer_register (ThreadId tid, int abs_regno, void * buf,
+ transfer_direction dir, int size, Bool *mod)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ int set = abs_regno / num_regs;
+ int regno = abs_regno % num_regs;
+ *mod = False;
+
+ VexGuestX86State* x86 = (VexGuestX86State*) get_arch (set, tst);
+
+ switch (regno) {
+ // numbers here have to match the order of regs above
+ // Attention: gdb order does not match valgrind order.
+ case 0: VG_(transfer) (&x86->guest_EAX, buf, dir, size, mod); break;
+ case 1: VG_(transfer) (&x86->guest_ECX, buf, dir, size, mod); break;
+ case 2: VG_(transfer) (&x86->guest_EDX, buf, dir, size, mod); break;
+ case 3: VG_(transfer) (&x86->guest_EBX, buf, dir, size, mod); break;
+ case 4: VG_(transfer) (&x86->guest_ESP, buf, dir, size, mod); break;
+ case 5: VG_(transfer) (&x86->guest_EBP, buf, dir, size, mod); break;
+ case 6: VG_(transfer) (&x86->guest_ESI, buf, dir, size, mod); break;
+ case 7: VG_(transfer) (&x86->guest_EDI, buf, dir, size, mod); break;
+ case 8:
+ VG_(transfer) (&x86->guest_EIP, buf, dir, size, mod);
+ if (*mod && VG_(debugLog_getLevel)() > 2) {
+ char bufimage [2*sizeof(x86->guest_IP_AT_SYSCALL) + 1];
+ heximage (bufimage,
+ (char *) &x86->guest_IP_AT_SYSCALL,
+ sizeof(x86->guest_IP_AT_SYSCALL));
+ dlog(3, "guest_IP_AT_SYSCALL %s\n", bufimage);
+ }
+ break;
+ case 9:
+ if (dir == valgrind_to_gdbserver) {
+ UInt eflags;
+ /* we can only retrieve the real flags (set 0)
+ retrieving shadow flags is not ok */
+ if (set == 0)
+ eflags = LibVEX_GuestX86_get_eflags (x86);
+ else
+ eflags = 0;
+ VG_(transfer) (&eflags, buf, dir, size, mod); break;
+ } else {
+ *mod = False; //GDBTD? how do we store eflags in libvex_guest_x86.h ???
+ }
+ break;
+ case 10: VG_(transfer) (&x86->guest_CS, buf, dir, size, mod); break;
+ case 11: VG_(transfer) (&x86->guest_SS, buf, dir, size, mod); break;
+ case 12: VG_(transfer) (&x86->guest_DS, buf, dir, size, mod); break;
+ case 13: VG_(transfer) (&x86->guest_ES, buf, dir, size, mod); break;
+ case 14: VG_(transfer) (&x86->guest_FS, buf, dir, size, mod); break;
+ case 15: VG_(transfer) (&x86->guest_GS, buf, dir, size, mod); break;
+ case 16:
+ case 17:
+ case 18:
+ case 19: /* register 16 to 23 are float registers 80 bits but 64 bits in valgrind */
+ case 20:
+ case 21:
+ case 22:
+ case 23: {
+ if (dir == valgrind_to_gdbserver) {
+ UChar fpreg80[10];
+ convert_f64le_to_f80le ((UChar *)&x86->guest_FPREG[regno-16],
+ fpreg80);
+ VG_(transfer) (&fpreg80, buf, dir, sizeof(fpreg80), mod);
+ } else {
+ ULong fpreg64;
+ convert_f80le_to_f64le (buf, (UChar *)&fpreg64);
+ VG_(transfer) (&x86->guest_FPREG[regno-16], &fpreg64,
+ dir, sizeof(fpreg64), mod);
+ }
+ break;
+ }
+ case 24:
+ if (dir == valgrind_to_gdbserver) {
+ // vex only models the rounding bits (see libvex_guest_x86.h)
+ UWord value = 0x037f;
+ value |= x86->guest_FPROUND << 10;
+ VG_(transfer)(&value, buf, dir, size, mod);
+ } else {
+ *mod = False; // GDBTD???? VEX { "fctrl", 1152, 32 },
+ }
+ break;
+ case 25:
+ if (dir == valgrind_to_gdbserver) {
+ UWord value = x86->guest_FC3210;
+ value |= (x86->guest_FTOP & 7) << 11;
+ VG_(transfer)(&value, buf, dir, size, mod);
+ } else {
+ *mod = False; // GDBTD???? VEX { "fstat", 1184, 32 },
+ }
+ break;
+ case 26:
+ if (dir == valgrind_to_gdbserver) {
+ // vex doesn't model these precisely
+ UWord value =
+ ((x86->guest_FPTAG[0] ? 0 : 3) << 0) |
+ ((x86->guest_FPTAG[1] ? 0 : 3) << 2) |
+ ((x86->guest_FPTAG[2] ? 0 : 3) << 4) |
+ ((x86->guest_FPTAG[3] ? 0 : 3) << 6) |
+ ((x86->guest_FPTAG[4] ? 0 : 3) << 8) |
+ ((x86->guest_FPTAG[5] ? 0 : 3) << 10) |
+ ((x86->guest_FPTAG[6] ? 0 : 3) << 12) |
+ ((x86->guest_FPTAG[7] ? 0 : 3) << 14);
+ VG_(transfer)(&value, buf, dir, size, mod);
+ } else {
+ *mod = False; // GDBTD???? VEX { "ftag", 1216, 32 },
+ }
+ break;
+ case 27: *mod = False; break; // GDBTD???? VEX { "fiseg", 1248, 32 },
+ case 28: *mod = False; break; // GDBTD???? VEX { "fioff", 1280, 32 },
+ case 29: *mod = False; break; // GDBTD???? VEX { "foseg", 1312, 32 },
+ case 30: *mod = False; break; // GDBTD???? VEX { "fooff", 1344, 32 },
+ case 31: *mod = False; break; // GDBTD???? VEX { "fop", 1376, 32 },
+ case 32: VG_(transfer) (&x86->guest_XMM0, buf, dir, size, mod); break;
+ case 33: VG_(transfer) (&x86->guest_XMM1, buf, dir, size, mod); break;
+ case 34: VG_(transfer) (&x86->guest_XMM2, buf, dir, size, mod); break;
+ case 35: VG_(transfer) (&x86->guest_XMM3, buf, dir, size, mod); break;
+ case 36: VG_(transfer) (&x86->guest_XMM4, buf, dir, size, mod); break;
+ case 37: VG_(transfer) (&x86->guest_XMM5, buf, dir, size, mod); break;
+ case 38: VG_(transfer) (&x86->guest_XMM6, buf, dir, size, mod); break;
+ case 39: VG_(transfer) (&x86->guest_XMM7, buf, dir, size, mod); break;
+ case 40:
+ if (dir == valgrind_to_gdbserver) {
+ // vex only models the rounding bits (see libvex_guest_x86.h)
+ UWord value = 0x1f80;
+ value |= x86->guest_SSEROUND << 13;
+ VG_(transfer)(&value, buf, dir, size, mod);
+ } else {
+ *mod = False; // GDBTD???? VEX { "mxcsr", 2432, 32 },
+ }
+ break;
+ case 41: *mod = False; break; // GDBTD???? VEX { "orig_eax", 2464, 32 },
+ default: vg_assert(0);
+ }
+}
+
+static struct valgrind_target_ops low_target = {
+ num_regs,
+ regs,
+ 4, //ESP
+ transfer_register,
+ get_pc,
+ set_pc,
+ "i386",
+ NULL, // target_xml not needed.
+#if defined(VGO_linux)
+ "i386-linux-valgrind.xml"
+#else
+ "i386-coresse-valgrind.xml"
+#endif
+};
+
+void x86_init_architecture (struct valgrind_target_ops *target)
+{
+ *target = low_target;
+ set_register_cache (regs, num_regs);
+ gdbserver_expedite_regs = expedite_regs;
+}
diff --git a/coregrind/m_gdbserver/valgrind-low.c b/coregrind/m_gdbserver/valgrind-low.c
new file mode 100644
index 0000000..fc376fa
--- /dev/null
+++ b/coregrind/m_gdbserver/valgrind-low.c
@@ -0,0 +1,639 @@
+/* Low level interface to valgrind, for the remote server for GDB integrated
+ in valgrind.
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+ This file is part of VALGRIND.
+ It has been inspired from a file from gdbserver in gdb 6.6.
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "server.h"
+#include "target.h"
+#include "regdef.h"
+#include "regcache.h"
+#include "valgrind_low.h"
+#include "gdb/signals.h"
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_machine.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_core_gdbserver.h"
+#include "pub_tool_debuginfo.h"
+
+/* the_low_target defines the architecture specific aspects depending
+ on the cpu */
+static struct valgrind_target_ops the_low_target;
+
+/* builds an image of bin according to byte order of the architecture
+ Useful for register and int image */
+char* heximage (char *buf, char *bin, int count)
+{
+#if defined(VGA_x86) || defined(VGA_amd64)
+ char rev[count];
+ /* note: no need for trailing \0, length is known with count */
+ int i;
+ for (i = 0; i < count; i++)
+ rev[i] = bin[count - i - 1];
+ hexify (buf, rev, count);
+#else
+ hexify (buf, bin, count);
+#endif
+ return buf;
+}
+
+void* C2v(CORE_ADDR addr)
+{
+ return (void*) addr;
+}
+
+static
+char *image_ptid(unsigned long ptid)
+{
+ static char result[100];
+ VG_(sprintf) (result, "id %ld", ptid);
+ return result;
+}
+#define get_thread(inf) ((struct thread_info *)(inf))
+static
+void remove_thread_if_not_in_vg_threads (struct inferior_list_entry *inf)
+{
+ struct thread_info *thread = get_thread (inf);
+ if (!VG_(lwpid_to_vgtid)(thread_to_gdb_id(thread))) {
+ dlog(1, "removing gdb ptid %s\n",
+ image_ptid(thread_to_gdb_id(thread)));
+ remove_thread (thread);
+ }
+}
+
+/* synchronize threads known by valgrind and threads known by gdbserver */
+static
+void valgrind_update_threads (int pid)
+{
+ ThreadId tid;
+ ThreadState *ts;
+ unsigned long ptid;
+ struct thread_info *ti;
+
+ /* call remove_thread for all gdb threads not in valgrind threads */
+ for_each_inferior (&all_threads, remove_thread_if_not_in_vg_threads);
+
+ /* call add_thread for all valgrind threads not known in gdb all_threads */
+ for (tid = 1; tid < VG_N_THREADS; tid++) {
+
+#define LOCAL_THREAD_TRACE " ti* %p vgtid %d status %s as gdb ptid %s lwpid %d\n", \
+ ti, tid, VG_(name_of_ThreadStatus) (ts->status), \
+ image_ptid (ptid), ts->os_state.lwpid
+
+ if (VG_(is_valid_tid) (tid)) {
+ ts = VG_(get_ThreadState) (tid);
+ ptid = ts->os_state.lwpid;
+ ti = gdb_id_to_thread (ptid);
+ if (!ti) {
+ /* we do not report the threads which are not yet fully
+ initialized otherwise this creates duplicated threads
+ in gdb: once with pid xxx lwpid 0, then after that
+ with pid xxx lwpid yyy. */
+ if (ts->status != VgTs_Init) {
+ dlog(1, "adding_thread" LOCAL_THREAD_TRACE);
+ add_thread (ptid, ts, ptid);
+ }
+ } else {
+ dlog(2, "(known thread)" LOCAL_THREAD_TRACE);
+ }
+ }
+#undef LOCAL_THREAD_TRACE
+ }
+}
+
+/* Return nonzero if the given thread is still alive. */
+static
+int valgrind_thread_alive (unsigned long tid)
+{
+ struct thread_info *ti = gdb_id_to_thread(tid);
+ ThreadState *tst;
+
+ if (ti != NULL) {
+ tst = (ThreadState *) inferior_target_data (ti);
+ return tst->status != VgTs_Zombie;
+ }
+ else {
+ return 0;
+ }
+}
+
+/* allocate and build a register structure containing the shadow registers.
+ reg_defs is the normal registers, n is their numbers */
+static
+struct reg* build_shadow_arch (struct reg *reg_defs, int n) {
+ int i, r;
+ static char *postfix[3] = { "", "s1", "s2" };
+ struct reg *new_regs = malloc(3 * n * sizeof(reg_defs[0]));
+ int reg_set_len = reg_defs[n-1].offset + reg_defs[n-1].size;
+
+ for (i = 0; i < 3; i++) {
+ for (r = 0; r < n; r++) {
+ new_regs[i*n + r].name = malloc(strlen(reg_defs[r].name)
+ + strlen (postfix[i]) + 1);
+ strcpy (new_regs[i*n + r].name, reg_defs[r].name);
+ strcat (new_regs[i*n + r].name, postfix[i]);
+ new_regs[i*n + r].offset = i*reg_set_len + reg_defs[r].offset;
+ new_regs[i*n + r].size = reg_defs[r].size;
+ dlog(1,
+ "%10s Nr %d offset(bit) %d offset(byte) %d size(bit) %d\n",
+ new_regs[i*n + r].name, i*n + r, new_regs[i*n + r].offset,
+ (new_regs[i*n + r].offset) / 8, new_regs[i*n + r].size);
+ }
+ }
+
+ return new_regs;
+}
+
+/* Fetch one register from valgrind VEX guest state. */
+static
+void fetch_register (int regno)
+{
+ int size;
+ ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
+ ThreadId tid = tst->tid;
+
+ if (regno >= the_low_target.num_regs) {
+ dlog(0, "error fetch_register regno %d max %d\n",
+ regno, the_low_target.num_regs);
+ return;
+ }
+ size = register_size (regno);
+ {
+ Bool mod;
+ char buf [size];
+ VG_(memset) (buf, 0, size); // registers not fetched will be seen as 0.
+ (*the_low_target.transfer_register) (tid, regno, buf,
+ valgrind_to_gdbserver, size, &mod);
+ // Note: the *mod received from transfer_register is not interesting.
+ // We are interested to see if the register data in the register cache is modified.
+ supply_register (regno, buf, &mod);
+ if (mod && VG_(debugLog_getLevel)() > 1) {
+ char bufimage [2*size + 1];
+ heximage (bufimage, buf, size);
+ dlog(2, "fetched register %d size %d name %s value %s tid %d status %s\n",
+ regno, size, the_low_target.reg_defs[regno].name, bufimage,
+ tid, VG_(name_of_ThreadStatus) (tst->status));
+ }
+ }
+}
+
+/* Fetch all registers, or just one, from the child process. */
+static
+void usr_fetch_inferior_registers (int regno)
+{
+ if (regno == -1 || regno == 0)
+ for (regno = 0; regno < the_low_target.num_regs; regno++)
+ fetch_register (regno);
+ else
+ fetch_register (regno);
+}
+
+/* Store our register values back into the inferior.
+ If REGNO is -1, do this for all registers.
+ Otherwise, REGNO specifies which register (so we can save time). */
+static
+void usr_store_inferior_registers (int regno)
+{
+ int size;
+ ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
+ ThreadId tid = tst->tid;
+
+ if (regno >= 0) {
+
+ if (regno >= the_low_target.num_regs) {
+ dlog(0, "error store_register regno %d max %d\n",
+ regno, the_low_target.num_regs);
+ return;
+ }
+
+ size = register_size (regno);
+ {
+ Bool mod;
+ Addr old_SP, new_SP;
+ char buf[size];
+
+ if (regno == the_low_target.stack_pointer_regno) {
+ /* When the stack pointer register is changed such that
+ the stack is extended, we better inform the tool of the
+ stack increase. This is needed in particular to avoid
+ spurious Memcheck errors during Inferior calls. So, we
+ save in old_SP the SP before the change. A change of
+ stack pointer is also assumed to have initialised this
+ new stack space. For the typical example of an inferior
+ call, gdb writes arguments on the stack, and then
+ changes the stack pointer. As the stack increase tool
+ function might mark it as undefined, we have to call it
+ at the good moment. */
+ VG_(memset) ((void *) &old_SP, 0, size);
+ (*the_low_target.transfer_register) (tid, regno, (void *) &old_SP,
+ valgrind_to_gdbserver, size, &mod);
+ }
+
+ VG_(memset) (buf, 0, size);
+ collect_register (regno, buf);
+ (*the_low_target.transfer_register) (tid, regno, buf,
+ gdbserver_to_valgrind, size, &mod);
+ if (mod && VG_(debugLog_getLevel)() > 1) {
+ char bufimage [2*size + 1];
+ heximage (bufimage, buf, size);
+ dlog(2,
+ "stored register %d size %d name %s value %s "
+ "tid %d status %s\n",
+ regno, size, the_low_target.reg_defs[regno].name, bufimage,
+ tid, VG_(name_of_ThreadStatus) (tst->status));
+ }
+ if (regno == the_low_target.stack_pointer_regno) {
+ VG_(memcpy) (&new_SP, buf, size);
+ if (old_SP > new_SP) {
+ Word delta = (Word)new_SP - (Word)old_SP;
+ dlog(1,
+ " stack increase by stack pointer changed from %p to %p "
+ "delta %ld\n",
+ (void*) old_SP, (void *) new_SP,
+ delta);
+ VG_TRACK( new_mem_stack_w_ECU, new_SP, -delta, 0 );
+ VG_TRACK( new_mem_stack, new_SP, -delta );
+ if (VG_(tdict).track_post_mem_write) {
+ VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid,
+ new_SP, -delta);
+ }
+ }
+ }
+ }
+ }
+ else {
+ for (regno = 0; regno < the_low_target.num_regs; regno++)
+ usr_store_inferior_registers (regno);
+ }
+}
+
+static
+void valgrind_fetch_registers (int regno)
+{
+ usr_fetch_inferior_registers (regno);
+}
+
+static
+void valgrind_store_registers (int regno)
+{
+ usr_store_inferior_registers (regno);
+}
+
+/* Copy LEN bytes from inferior's memory starting at MEMADDR
+ to debugger memory starting at MYADDR. */
+
+static
+int valgrind_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ const void *sourceaddr = C2v (memaddr);
+ dlog(2, "reading memory %p size %d\n", sourceaddr, len);
+ if (!VG_(am_is_valid_for_client_or_free_or_resvn) ((Addr) sourceaddr,
+ len, VKI_PROT_READ)) {
+ dlog(1, "error reading memory %p size %d\n", sourceaddr, len);
+ return -1;
+ }
+ VG_(memcpy) (myaddr, sourceaddr, len);
+ return 0;
+}
+
+/* Copy LEN bytes of data from debugger memory at MYADDR
+ to inferior's memory at MEMADDR.
+ On failure (cannot write the inferior)
+ returns the value of errno. */
+
+static
+int valgrind_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+ void *targetaddr = C2v (memaddr);
+ dlog(2, "writing memory %p size %d\n", targetaddr, len);
+ if (!VG_(am_is_valid_for_client_or_free_or_resvn) ((Addr)targetaddr,
+ len, VKI_PROT_WRITE)) {
+ dlog(1, "error writing memory %p size %d\n", targetaddr, len);
+ return -1;
+ }
+ if (len > 0) {
+ VG_(memcpy) (targetaddr, myaddr, len);
+ if (VG_(tdict).track_post_mem_write) {
+ /* Inform the tool of the post memwrite. Note that we do the
+ minimum necessary to avoid complains from e.g.
+ memcheck. The idea is that the debugger is as least
+ intrusive as possible. So, we do not inform of the pre
+ mem write (and in any case, this would cause problems with
+ memcheck that does not like our CorePart in
+ pre_mem_write. */
+ ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
+ ThreadId tid = tst->tid;
+ VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid, (Addr) targetaddr, len );
+ }
+ }
+ return 0;
+}
+
+/* insert or remove a breakpoint */
+static
+int valgrind_point (Bool insert, char type, CORE_ADDR addr, int len)
+{
+ PointKind kind;
+ switch (type) {
+ case '0': /* implemented by inserting checks at each instruction in sb */
+ kind = software_breakpoint;
+ break;
+ case '1': /* hw breakpoint, same implementation as sw breakpoint */
+ kind = hardware_breakpoint;
+ break;
+ case '2':
+ kind = write_watchpoint;
+ break;
+ case '3':
+ kind = read_watchpoint;
+ break;
+ case '4':
+ kind = access_watchpoint;
+ break;
+ default:
+ vg_assert (0);
+ }
+
+ /* Attention: gdbserver convention differs: 0 means ok; 1 means not ok */
+ if (VG_(gdbserver_point) (kind, insert, addr, len))
+ return 0;
+ else
+ return 1; /* error or unsupported */
+}
+
+static
+void valgrind_send_signal (int sig)
+{
+ dlog(1, "valgrind_send_signal %d called ????\n", sig);
+}
+
+static
+char* valgrind_target_xml (void)
+{
+ return (char *) the_low_target.target_xml;
+}
+
+static
+char* valgrind_shadow_target_xml (void)
+{
+ return (char *) the_low_target.shadow_target_xml;
+}
+
+static
+int valgrind_insert_point (char type, CORE_ADDR addr, int len)
+{
+ return valgrind_point (/* insert */ True, type, addr, len);
+}
+
+static
+int valgrind_remove_point (char type, CORE_ADDR addr, int len)
+{
+ return valgrind_point (/* insert*/ False, type, addr, len);
+}
+
+static CORE_ADDR stopped_data_address = 0;
+void VG_(set_watchpoint_stop_address) (Addr addr)
+{
+ stopped_data_address = addr;
+}
+
+static
+int valgrind_stopped_by_watchpoint (void)
+{
+ return stopped_data_address != 0;
+}
+
+static
+CORE_ADDR valgrind_stopped_data_address (void)
+{
+ return stopped_data_address;
+}
+
+/* pc at which we last stopped */
+static CORE_ADDR stop_pc;
+
+/* pc at which we resume.
+ If stop_pc != resume_pc, it means
+ gdb/gdbserver has changed the pc so as to have either
+ a "continue by jumping at that address"
+ or a "continue at that address to call some code from gdb".
+*/
+static CORE_ADDR resume_pc;
+
+static int signal_to_report;
+
+void gdbserver_signal_encountered (Int sigNo)
+{
+ signal_to_report = sigNo;
+}
+
+static int signal_to_deliver;
+Bool gdbserver_deliver_signal (Int sigNo)
+{
+ return sigNo == signal_to_deliver;
+}
+
+static
+char* sym (Addr addr)
+{
+ static char buf[200];
+ VG_(describe_IP) (addr, buf, 200);
+ return buf;
+}
+
+ThreadId vgdb_interrupted_tid = 0;
+/* called to wait for the process to stop */
+static
+unsigned char valgrind_wait (char *ourstatus)
+{
+ int pid;
+ unsigned long wptid;
+ ThreadState *tst;
+ enum target_signal sig;
+
+ pid = VG_(getpid) ();
+ dlog(1, "enter valgrind_wait pid %d\n", pid);
+
+ regcache_invalidate();
+ valgrind_update_threads(pid);
+
+ /* in valgrind, we consider that a wait always succeeds with STOPPED 'T'
+ and with a signal TRAP (i.e. a breakpoint), unless there is
+ a signal to report. */
+ *ourstatus = 'T';
+ if (signal_to_report == 0)
+ sig = TARGET_SIGNAL_TRAP;
+ else
+ sig = target_signal_from_host(signal_to_report);
+
+ if (vgdb_interrupted_tid != 0)
+ tst = VG_(get_ThreadState) (vgdb_interrupted_tid);
+ else
+ tst = VG_(get_ThreadState) (VG_(running_tid));
+ wptid = tst->os_state.lwpid;
+ /* we can only change the current_inferior when the wptid references
+ an existing thread. Otherwise, we are still in the init phase.
+ (hack similar to main thread hack in valgrind_update_threads) */
+ if (tst->os_state.lwpid)
+ current_inferior = gdb_id_to_thread (wptid);
+ stop_pc = (*the_low_target.get_pc) ();
+
+ dlog(1,
+ "exit valgrind_wait returns ptid %s stop_pc %s signal %d\n",
+ image_ptid (wptid), sym (stop_pc), sig);
+ return sig;
+}
+
+/* 0 => not single stepping.
+ 1 => single stepping asked by gdb
+ 2 => single stepping asked by valgrind (watchpoint) */
+static int stepping = 0;
+
+/* called when the process is to be resumed */
+static
+void valgrind_resume (struct thread_resume *resume_info)
+{
+ dlog(1,
+ "resume_info thread %ld leave_stopped %d step %d sig %d stepping %d\n",
+ resume_info->thread,
+ resume_info->leave_stopped,
+ resume_info->step,
+ resume_info->sig,
+ stepping);
+ if (valgrind_stopped_by_watchpoint()) {
+ dlog(1, "clearing watchpoint stopped_data_address %p\n",
+ C2v(stopped_data_address));
+ VG_(set_watchpoint_stop_address) ((Addr) 0);
+ }
+ signal_to_deliver = resume_info->sig;
+
+ stepping = resume_info->step;
+ resume_pc = (*the_low_target.get_pc) ();
+ if (resume_pc != stop_pc) {
+ dlog(1,
+ "stop_pc %p changed to be resume_pc %s\n",
+ C2v(stop_pc), sym(resume_pc));
+ }
+ regcache_invalidate();
+}
+
+Addr valgrind_get_ignore_break_once(void)
+{
+ if (valgrind_single_stepping())
+ return resume_pc;
+ else
+ return 0;
+}
+
+
+void valgrind_set_single_stepping(Bool set)
+{
+ if (set)
+ stepping = 2;
+ else
+ stepping = 0;
+}
+
+Bool valgrind_single_stepping(void)
+{
+ if (stepping)
+ return True;
+ else
+ return False;
+}
+
+static struct target_ops valgrind_target_ops = {
+ valgrind_thread_alive,
+ valgrind_resume,
+ valgrind_wait,
+ valgrind_fetch_registers,
+ valgrind_store_registers,
+ valgrind_read_memory,
+ valgrind_write_memory,
+ valgrind_send_signal,
+ valgrind_target_xml,
+ valgrind_shadow_target_xml,
+ valgrind_insert_point,
+ valgrind_remove_point,
+ valgrind_stopped_by_watchpoint,
+ valgrind_stopped_data_address,
+};
+
+
+/* returns a pointer to the architecture state corresponding to
+ the provided register set: 0 => normal guest registers,
+ 1 => shadow1
+ 2 => shadow2
+*/
+VexGuestArchState* get_arch (int set, ThreadState* tst)
+{
+ switch (set) {
+ case 0: return &tst->arch.vex;
+ case 1: return &tst->arch.vex_shadow1;
+ case 2: return &tst->arch.vex_shadow2;
+ default: vg_assert(0);
+ }
+}
+
+static int non_shadow_num_regs = 0;
+static struct reg *non_shadow_reg_defs = NULL;
+void initialize_shadow_low(Bool shadow_mode)
+{
+ if (non_shadow_reg_defs == NULL) {
+ non_shadow_reg_defs = the_low_target.reg_defs;
+ non_shadow_num_regs = the_low_target.num_regs;
+ }
+
+ regcache_invalidate();
+ if (the_low_target.reg_defs != non_shadow_reg_defs) {
+ free (the_low_target.reg_defs);
+ }
+ if (shadow_mode) {
+ the_low_target.num_regs = 3 * non_shadow_num_regs;
+ the_low_target.reg_defs = build_shadow_arch (non_shadow_reg_defs, non_shadow_num_regs);
+ } else {
+ the_low_target.num_regs = non_shadow_num_regs;
+ the_low_target.reg_defs = non_shadow_reg_defs;
+ }
+ set_register_cache (the_low_target.reg_defs, the_low_target.num_regs);
+}
+
+void initialize_low(void)
+{
+ set_target_ops (&valgrind_target_ops);
+
+#if defined(VGA_x86)
+ x86_init_architecture(&the_low_target);
+#elif defined(VGA_amd64)
+ amd64_init_architecture(&the_low_target);
+#elif defined(VGA_arm)
+ arm_init_architecture(&the_low_target);
+#elif defined(VGA_ppc32)
+ ppc32_init_architecture(&the_low_target);
+#elif defined(VGA_ppc64)
+ ppc64_init_architecture(&the_low_target);
+#elif defined(VGA_s390x)
+ s390x_init_architecture(&the_low_target);
+#else
+ architecture missing in valgrind-low.c
+#endif
+
+}
diff --git a/coregrind/m_gdbserver/valgrind_low.h b/coregrind/m_gdbserver/valgrind_low.h
new file mode 100644
index 0000000..6817110
--- /dev/null
+++ b/coregrind/m_gdbserver/valgrind_low.h
@@ -0,0 +1,91 @@
+/* Definitions of interface to the "low" (arch specific) functions
+ needed for interfacing the Valgrind gdbserver with the Valgrind
+ guest.
+
+ Copyright (C) 2011
+ Free Software Foundation, Inc.
+
+ This file has been inspired from a file that is part of GDB.
+ It has been modified to integrate it in valgrind
+
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#ifndef VALGRIND_LOW_H
+#define VALGRIND_LOW_H
+
+/* defines the characteristics of the "low" valgrind target architecture.
+ In other words, struct valgrind_target_ops defines the functions and
+ data which are specific to the architecture (x86 or amd64 or
+ ppc32 or ...). */
+struct valgrind_target_ops
+{
+ int num_regs;
+ struct reg *reg_defs;
+
+ int stack_pointer_regno;
+ /* register number of the stack pointer register */
+
+ /* transfer the register regno from/to valgrind (guest state)
+ to/from buf
+ according to transfer_direction.
+ *mod set to True if destination content is modified by the transfer
+ otherwise it is set to False. */
+ void (*transfer_register) (ThreadId tid, int regno, void * buf,
+ transfer_direction dir, int size, Bool *mod);
+
+
+ CORE_ADDR (*get_pc) (void);
+ void (*set_pc) (CORE_ADDR newpc);
+
+ /* What string to report to GDB when it asks for the architecture,
+ or NULL not to answer. */
+ const char *arch_string;
+
+ /* Description of the set of registers.
+ For some architectures (e.g. arm), it is mandatory
+ to give a description of the registers, otherwise
+ gdb does not understand the reply to the 'g' packet
+ (which is used to get the registers). */
+ const char *target_xml;
+
+ /* Same as target_xml, but describes also the two shadow
+ registers set.
+ This is mandatory to use the option --vgdb-shadow-registers=yes. */
+ const char *shadow_target_xml;
+};
+
+
+/* convert from CORE_ADDR to void* */
+extern void* C2v(CORE_ADDR addr);
+
+/* builds an image of bin according to byte order of the architecture
+ Useful for register and int image */
+extern char* heximage (char *buf, char *bin, int count);
+
+/* returns a pointer to the architecture state corresponding to
+ the provided register set: 0 => normal guest registers,
+ 1 => shadow1
+ 2 => shadow2 */
+VexGuestArchState* get_arch (int set, ThreadState* tst);
+
+extern void x86_init_architecture (struct valgrind_target_ops *target);
+extern void amd64_init_architecture (struct valgrind_target_ops *target);
+extern void arm_init_architecture (struct valgrind_target_ops *target);
+extern void ppc32_init_architecture (struct valgrind_target_ops *target);
+extern void ppc64_init_architecture (struct valgrind_target_ops *target);
+extern void s390x_init_architecture (struct valgrind_target_ops *target);
+
+#endif
diff --git a/coregrind/m_gdbserver/version.c b/coregrind/m_gdbserver/version.c
new file mode 100644
index 0000000..7b2af40
--- /dev/null
+++ b/coregrind/m_gdbserver/version.c
@@ -0,0 +1,2 @@
+#include "server.h"
+const char version[] = "gdbserver protocol box extracted from gdb 6.6";
diff --git a/coregrind/m_libcbase.c b/coregrind/m_libcbase.c
index 98ff96b..6d6a274 100644
--- a/coregrind/m_libcbase.c
+++ b/coregrind/m_libcbase.c
@@ -89,6 +89,31 @@
return n;
}
+ULong VG_(strtoull10) ( Char* str, Char** endptr )
+{
+ Bool converted = False;
+ ULong n = 0;
+ Long digit = 0;
+ Char* str0 = str;
+
+ // Skip leading whitespace.
+ while (VG_(isspace)(*str)) str++;
+
+ // Allow a leading '+'.
+ if (*str == '+') { str++; }
+
+ while (is_dec_digit(*str, &digit)) {
+ converted = True; // Ok, we've actually converted a digit.
+ n = 10*n + digit;
+ str++;
+ }
+
+ if (!converted) str = str0; // If nothing converted, endptr points to
+ // the start of the string.
+ if (endptr) *endptr = str; // Record first failing character.
+ return n;
+}
+
Long VG_(strtoll16) ( Char* str, Char** endptr )
{
Bool neg = False, converted = False;
@@ -122,6 +147,39 @@
return n;
}
+ULong VG_(strtoull16) ( Char* str, Char** endptr )
+{
+ Bool converted = False;
+ ULong n = 0;
+ Long digit = 0;
+ Char* str0 = str;
+
+ // Skip leading whitespace.
+ while (VG_(isspace)(*str)) str++;
+
+ // Allow a leading '+'.
+ if (*str == '+') { str++; }
+
+ // Allow leading "0x", but only if there's a hex digit
+ // following it.
+ if (*str == '0'
+ && (*(str+1) == 'x' || *(str+1) == 'X')
+ && is_hex_digit( *(str+2), &digit )) {
+ str += 2;
+ }
+
+ while (is_hex_digit(*str, &digit)) {
+ converted = True; // Ok, we've actually converted a digit.
+ n = 16*n + digit;
+ str++;
+ }
+
+ if (!converted) str = str0; // If nothing converted, endptr points to
+ // the start of the string.
+ if (endptr) *endptr = str; // Record first failing character.
+ return n;
+}
+
double VG_(strtod) ( Char* str, Char** endptr )
{
Bool neg = False;
@@ -356,6 +414,88 @@
return NULL;
}
+/* (code copied from glib then updated to valgrind types) */
+static Char *olds;
+Char *
+VG_(strtok) (Char *s, const Char *delim)
+{
+ return VG_(strtok_r) (s, delim, &olds);
+}
+
+Char *
+VG_(strtok_r) (Char* s, const Char* delim, Char** saveptr)
+{
+ Char *token;
+
+ if (s == NULL)
+ s = *saveptr;
+
+ /* Scan leading delimiters. */
+ s += VG_(strspn (s, delim));
+ if (*s == '\0')
+ {
+ *saveptr = s;
+ return NULL;
+ }
+
+ /* Find the end of the token. */
+ token = s;
+ s = VG_(strpbrk (token, delim));
+ if (s == NULL)
+ /* This token finishes the string. */
+ *saveptr = token + VG_(strlen) (token);
+ else
+ {
+ /* Terminate the token and make OLDS point past it. */
+ *s = '\0';
+ *saveptr = s + 1;
+ }
+ return token;
+}
+
+static Bool isHex ( UChar c )
+{
+ return ((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F'));
+}
+
+static UInt fromHex ( UChar c )
+{
+ if (c >= '0' && c <= '9')
+ return (UInt)c - (UInt)'0';
+ if (c >= 'a' && c <= 'f')
+ return 10 + (UInt)c - (UInt)'a';
+ if (c >= 'A' && c <= 'F')
+ return 10 + (UInt)c - (UInt)'A';
+ /*NOTREACHED*/
+ // ??? need to vg_assert(0);
+ return 0;
+}
+
+Bool VG_(parse_Addr) ( UChar** ppc, Addr* result )
+{
+ Int used, limit = 2 * sizeof(Addr);
+ if (**ppc != '0')
+ return False;
+ (*ppc)++;
+ if (**ppc != 'x')
+ return False;
+ (*ppc)++;
+ *result = 0;
+ used = 0;
+ while (isHex(**ppc)) {
+ // ??? need to vg_assert(d < fromHex(**ppc));
+ *result = ((*result) << 4) | fromHex(**ppc);
+ (*ppc)++;
+ used++;
+ if (used > limit) return False;
+ }
+ if (used == 0)
+ return False;
+ return True;
+}
+
SizeT VG_(strspn) ( const Char* s, const Char* accpt )
{
const Char *p, *a;
diff --git a/coregrind/m_libcfile.c b/coregrind/m_libcfile.c
index 8c1dbc9..2b9e445 100644
--- a/coregrind/m_libcfile.c
+++ b/coregrind/m_libcfile.c
@@ -108,6 +108,17 @@
# endif
}
+SysRes VG_(mknod) ( const Char* pathname, Int mode, UWord dev )
+{
+# if defined(VGO_linux) || defined(VGO_aix5) || defined(VGO_darwin)
+ SysRes res = VG_(do_syscall3)(__NR_mknod,
+ (UWord)pathname, mode, dev);
+# else
+# error Unknown OS
+# endif
+ return res;
+}
+
SysRes VG_(open) ( const Char* pathname, Int flags, Int mode )
{
# if defined(VGO_linux) || defined(VGO_aix5)
@@ -122,6 +133,16 @@
return res;
}
+Int VG_(fd_open) (const Char* pathname, Int flags, Int mode)
+{
+ SysRes sr;
+ sr = VG_(open) (pathname, flags, mode);
+ if (sr_isError (sr))
+ return -1;
+ else
+ return sr_Res (sr);
+}
+
void VG_(close) ( Int fd )
{
/* Hmm. Return value is not checked. That's uncool. */
@@ -444,6 +465,14 @@
return True;
}
+Int VG_(poll) (struct vki_pollfd *fds, Int nfds, Int timeout)
+{
+ SysRes res;
+ res = VG_(do_syscall3)(__NR_poll, (UWord)fds, nfds, timeout);
+ return sr_isError(res) ? -1 : sr_Res(res);
+}
+
+
Int VG_(readlink) (const Char* path, Char* buf, UInt bufsiz)
{
SysRes res;
diff --git a/coregrind/m_libcprint.c b/coregrind/m_libcprint.c
index dee9e56..fb2544d 100644
--- a/coregrind/m_libcprint.c
+++ b/coregrind/m_libcprint.c
@@ -31,6 +31,7 @@
#include "pub_core_basics.h"
#include "pub_core_vki.h"
#include "pub_core_debuglog.h"
+#include "pub_core_gdbserver.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcfile.h" // VG_(write)(), VG_(write_socket)()
@@ -47,7 +48,10 @@
/* The destination sinks for normal and XML output. These have their
initial values here; they are set to final values by
m_main.main_process_cmd_line_options(). See comment at the top of
- that function for the associated logic. */
+ that function for the associated logic.
+ After startup, the gdbserver monitor command might temporarily
+ set the fd of log_output_sink to -2 to indicate that output is
+ to be given to gdb rather than output to the startup fd */
OutputSink VG_(log_output_sink) = { 2, False }; /* 2 = stderr */
OutputSink VG_(xml_output_sink) = { -1, False }; /* disabled */
@@ -70,6 +74,8 @@
any more output. */
if (sink->fd >= 0)
VG_(write)( sink->fd, msg, nbytes );
+ else if (sink->fd == -2)
+ VG_(gdb_printf)("%s", msg);
}
}
@@ -107,7 +113,7 @@
const HChar *format, va_list vargs )
{
UInt ret = 0;
- if (b->sink->fd >= 0) {
+ if (b->sink->fd >= 0 || b->sink->fd == -2) {
ret = VG_(debugLog_vprintf)
( add_to__printf_buf, b, format, vargs );
}
diff --git a/coregrind/m_libcproc.c b/coregrind/m_libcproc.c
index dbedac2..96a233d 100644
--- a/coregrind/m_libcproc.c
+++ b/coregrind/m_libcproc.c
@@ -418,6 +418,21 @@
return sr_isError(res) ? -1 : sr_Res(res);
}
+/* Support for prctl. */
+Int VG_(prctl) (Int option,
+ ULong arg2, ULong arg3, ULong arg4, ULong arg5)
+{
+ SysRes res = VG_(mk_SysRes_Error)(VKI_ENOSYS);
+# if defined(VGO_linux)
+ /* res = prctl( option, arg2, arg3, arg4, arg5 ); */
+ res = VG_(do_syscall5)(__NR_prctl, (UWord) option,
+ (UWord) arg2, (UWord) arg3, (UWord) arg4,
+ (UWord) arg5);
+# endif
+
+ return sr_isError(res) ? -1 : sr_Res(res);
+}
+
/* ---------------------------------------------------------------------
pids, etc
------------------------------------------------------------------ */
diff --git a/coregrind/m_main.c b/coregrind/m_main.c
index 7f80f65..b27b5ad 100644
--- a/coregrind/m_main.c
+++ b/coregrind/m_main.c
@@ -41,6 +41,7 @@
#include "pub_core_debuglog.h"
#include "pub_core_errormgr.h"
#include "pub_core_execontext.h"
+#include "pub_core_gdbserver.h"
#include "pub_core_initimg.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
@@ -129,6 +130,9 @@
" but check the argv[] entries for children, rather\n"
" than the exe name, to make a follow/no-follow decision\n"
" --child-silent-after-fork=no|yes omit child output between fork & exec? [no]\n"
+" --vgdb=no|yes|full activate gdbserver? [yes]\n"
+" full is slower but provides precise watchpoint/step\n"
+" --vgdb-error=<number> invoke gdbserver after <number> errors [%d] \n"
" --track-fds=no|yes track open file descriptors? [no]\n"
" --time-stamp=no|yes add timestamps to log messages? [no]\n"
" --log-fd=<number> log messages to file descriptor [2=stderr]\n"
@@ -173,6 +177,9 @@
" and use it to print better error messages in\n"
" tools that make use of it (Memcheck, Helgrind,\n"
" DRD) [no]\n"
+" --vgdb-poll=<number> gdbserver poll max every <number> basic blocks [%d] \n"
+" --vgdb-shadow-registers=no|yes let gdb see the shadow registers [no]\n"
+" --vgdb-prefix=<prefix> prefix for vgdb FIFOs [%s]\n"
" --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]\n"
" --sim-hints=hint1,hint2,... known hints:\n"
" lax-ioctls, enable-outer [none]\n"
@@ -252,8 +259,10 @@
VG_(log_output_sink).fd = 1;
VG_(log_output_sink).is_socket = False;
- /* 'usage1' expects one char* argument and one SizeT argument. */
- VG_(printf)(usage1, gdb_path, VG_MIN_MALLOC_SZB);
+ /* 'usage1' expects two int, two char* argument, and one SizeT argument. */
+ VG_(printf)(usage1,
+ VG_(clo_vgdb_error), gdb_path, VG_MIN_MALLOC_SZB,
+ VG_(clo_vgdb_poll), VG_(clo_vgdb_prefix));
if (VG_(details).name) {
VG_(printf)(" user options for %s:\n", VG_(details).name);
if (VG_(needs).command_line_options)
@@ -457,6 +466,14 @@
else if VG_BOOL_CLO(arg, "--stats", VG_(clo_stats)) {}
else if VG_BOOL_CLO(arg, "--xml", VG_(clo_xml)) {}
+ else if VG_XACT_CLO(arg, "--vgdb=no", VG_(clo_vgdb), Vg_VgdbNo) {}
+ else if VG_XACT_CLO(arg, "--vgdb=yes", VG_(clo_vgdb), Vg_VgdbYes) {}
+ else if VG_XACT_CLO(arg, "--vgdb=full", VG_(clo_vgdb), Vg_VgdbFull) {}
+ else if VG_INT_CLO (arg, "--vgdb-poll", VG_(clo_vgdb_poll)) {}
+ else if VG_INT_CLO (arg, "--vgdb-error", VG_(clo_vgdb_error)) {}
+ else if VG_STR_CLO (arg, "--vgdb-prefix", VG_(clo_vgdb_prefix)) {}
+ else if VG_BOOL_CLO(arg, "--vgdb-shadow-registers",
+ VG_(clo_vgdb_shadow_registers)) {}
else if VG_BOOL_CLO(arg, "--db-attach", VG_(clo_db_attach)) {}
else if VG_BOOL_CLO(arg, "--demangle", VG_(clo_demangle)) {}
else if VG_BOOL_CLO(arg, "--error-limit", VG_(clo_error_limit)) {}
@@ -671,6 +688,8 @@
if (VG_(clo_verbosity) < 0)
VG_(clo_verbosity) = 0;
+ VG_(dyn_vgdb_error) = VG_(clo_vgdb_error);
+
if (VG_(clo_gen_suppressions) > 0 &&
!VG_(needs).core_errors && !VG_(needs).tool_errors) {
VG_(fmsg_bad_option)("--gen-suppressions=yes",
@@ -2426,7 +2445,7 @@
/* In XML mode, this merely prints the used suppressions. */
if (VG_(needs).core_errors || VG_(needs).tool_errors)
- VG_(show_all_errors)();
+ VG_(show_all_errors)(VG_(clo_verbosity), VG_(clo_xml));
if (VG_(clo_xml)) {
VG_(printf_xml)("\n");
@@ -2461,6 +2480,10 @@
/* Flush any output cached by previous calls to VG_(message). */
VG_(message_flush)();
+ /* terminate gdbserver if ever it was started. We terminate it here so that it get
+ the output above if output was redirected to gdb */
+ VG_(gdbserver) (0);
+
/* Ok, finally exit in the os-specific way, according to the scheduler's
return code. In short, if the (last) thread exited by calling
sys_exit, do likewise; if the (last) thread stopped due to a fatal
diff --git a/coregrind/m_options.c b/coregrind/m_options.c
index 888e2c7..59ec1fe 100644
--- a/coregrind/m_options.c
+++ b/coregrind/m_options.c
@@ -46,6 +46,11 @@
VexControl VG_(clo_vex_control);
Bool VG_(clo_error_limit) = True;
Int VG_(clo_error_exitcode) = 0;
+VgVgdb VG_(clo_vgdb) = Vg_VgdbYes;
+Int VG_(clo_vgdb_poll) = 5000;
+Int VG_(clo_vgdb_error) = 999999999;
+Char* VG_(clo_vgdb_prefix) = VG_CLO_VGDB_PREFIX_DEFAULT;
+Bool VG_(clo_vgdb_shadow_registers) = False;
Bool VG_(clo_db_attach) = False;
Char* VG_(clo_db_command) = GDB_PATH " -nw %f %p";
Int VG_(clo_gen_suppressions) = 0;
diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c
index 0a3d82f..6d06947 100644
--- a/coregrind/m_scheduler/scheduler.c
+++ b/coregrind/m_scheduler/scheduler.c
@@ -67,6 +67,7 @@
#include "pub_core_clreq.h" // for VG_USERREQ__*
#include "pub_core_dispatch.h"
#include "pub_core_errormgr.h" // For VG_(get_n_errs_found)()
+#include "pub_core_gdbserver.h" // for VG_(gdbserver) and VG_(gdbserver_activity)
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
@@ -113,6 +114,13 @@
/* 64-bit counter for the number of basic blocks done. */
static ULong bbs_done = 0;
+/* Counter to see if vgdb activity is to be verified.
+ When nr of bbs done reaches vgdb_next_poll, scheduler will
+ poll for gdbserver activity. VG_(force_vgdb_poll) and
+ VG_(disable_vgdb_poll) allows the valgrind core (e.g. m_gdbserver)
+ to control when the next poll will be done. */
+static ULong vgdb_next_poll;
+
/* Forwards */
static void do_client_request ( ThreadId tid );
static void scheduler_sanity ( ThreadId tid );
@@ -684,6 +692,20 @@
# endif
}
+// NO_VGDB_POLL value ensures vgdb is not polled, while
+// VGDB_POLL_ASAP ensures that the next scheduler call
+// will cause a poll.
+#define NO_VGDB_POLL 0xffffffffffffffffULL
+#define VGDB_POLL_ASAP 0x0ULL
+
+void VG_(disable_vgdb_poll) (void )
+{
+ vgdb_next_poll = NO_VGDB_POLL;
+}
+void VG_(force_vgdb_poll) ( void )
+{
+ vgdb_next_poll = VGDB_POLL_ASAP;
+}
/* Run the thread tid for a while, and return a VG_TRC_* value
indicating why VG_(run_innerloop) stopped. */
@@ -769,6 +791,16 @@
// Tell the tool this thread has stopped running client code
VG_TRACK( stop_client_code, tid, bbs_done );
+ if (bbs_done >= vgdb_next_poll) {
+ if (VG_(clo_vgdb_poll))
+ vgdb_next_poll = bbs_done + (ULong)VG_(clo_vgdb_poll);
+ else
+ /* value was changed due to gdbserver invocation via ptrace */
+ vgdb_next_poll = NO_VGDB_POLL;
+ if (VG_(gdbserver_activity) (tid))
+ VG_(gdbserver) (tid);
+ }
+
return trc;
}
@@ -854,6 +886,11 @@
return retval;
}
+ULong VG_(bbs_done) (void)
+{
+ return bbs_done;
+}
+
/* ---------------------------------------------------------------------
The scheduler proper.
@@ -959,10 +996,69 @@
{
UInt trc;
ThreadState *tst = VG_(get_ThreadState)(tid);
+ static Bool vgdb_startup_action_done = False;
if (VG_(clo_trace_sched))
print_sched_event(tid, "entering VG_(scheduler)");
+ /* Do vgdb initialization (but once). Only the first (main) task
+ starting up will do the below.
+ Initialize gdbserver earlier than at the first
+ thread VG_(scheduler) is causing problems:
+ * at the end of VG_(scheduler_init_phase2) :
+ The main thread is in VgTs_Init state, but in a not yet
+ consistent state => the thread cannot be reported to gdb
+ (e.g. causes an assert in LibVEX_GuestX86_get_eflags when giving
+ back the guest registers to gdb).
+ * at end of valgrind_main, just
+ before VG_(main_thread_wrapper_NORETURN)(1) :
+ The main thread is still in VgTs_Init state but in a
+ more advanced state. However, the thread state is not yet
+ completely initialized : a.o., the os_state is not yet fully
+ set => the thread is then not properly reported to gdb,
+ which is then confused (causing e.g. a duplicate thread be
+ shown, without thread id).
+ * it would be possible to initialize gdbserver "lower" in the
+ call stack (e.g. in VG_(main_thread_wrapper_NORETURN)) but
+ these are platform dependent and the place at which
+ the thread state is completely initialized is not
+ specific anymore to the main thread (so a similar "do it only
+ once" would be needed).
+
+ => a "once only" initialization here is the best compromise. */
+ if (!vgdb_startup_action_done) {
+ vg_assert(tid == 1); // it must be the main thread.
+ vgdb_startup_action_done = True;
+ if (VG_(clo_vgdb) != Vg_VgdbNo) {
+ /* If we have to poll, ensures we do an initial poll at first
+ scheduler call. Otherwise, ensure no poll (unless interrupted
+ by ptrace). */
+ if (VG_(clo_vgdb_poll))
+ VG_(force_vgdb_poll) ();
+ else
+ VG_(disable_vgdb_poll) ();
+
+ vg_assert (VG_(dyn_vgdb_error) == VG_(clo_vgdb_error));
+ /* As we are initializing, VG_(dyn_vgdb_error) can't have been
+ changed yet. */
+
+ if (VG_(dyn_vgdb_error) == 0) {
+ /* The below call allows gdb to attach at startup
+ before the first guest instruction is executed. */
+ VG_(umsg)("(action at startup) vgdb me ... \n");
+ VG_(gdbserver)(1);
+ } else {
+ /* User has activated gdbserver => initialize now the FIFOs
+ to let vgdb/gdb contact us either via the scheduler poll
+ mechanism or via vgdb ptrace-ing valgrind. */
+ if (VG_(gdbserver_activity) (1))
+ VG_(gdbserver) (1);
+ }
+ } else {
+ VG_(disable_vgdb_poll) ();
+ }
+ }
+
/* set the proper running signal mask */
block_signals();
diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c
index 105c02a..8f4bbda 100644
--- a/coregrind/m_signals.c
+++ b/coregrind/m_signals.c
@@ -191,6 +191,11 @@
VG_(sigtimedwait_zero). This is trivial on Linux, since it's just a
syscall. But on Darwin and AIX, we have to cobble together the
functionality in a tedious, longwinded and probably error-prone way.
+
+ Finally, if a gdb is debugging the process under valgrind,
+ the signal can be ignored if gdb tells this. So, before resuming the
+ scheduler/delivering the signal, a call to VG_(gdbserver_report_signal)
+ is done. If this returns True, the signal is delivered.
*/
#include "pub_core_basics.h"
@@ -204,6 +209,7 @@
#include "pub_core_aspacemgr.h"
#include "pub_core_debugger.h" // For VG_(start_debugger)
#include "pub_core_errormgr.h"
+#include "pub_core_gdbserver.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
@@ -656,11 +662,14 @@
static SKSS skss;
-static Bool is_sig_ign(Int sigNo)
+/* returns True if signal is to be ignored.
+ To check this, possibly call gdbserver with tid. */
+static Bool is_sig_ign(Int sigNo, ThreadId tid)
{
vg_assert(sigNo >= 1 && sigNo <= _VKI_NSIG);
- return scss.scss_per_sig[sigNo].scss_handler == VKI_SIG_IGN;
+ return scss.scss_per_sig[sigNo].scss_handler == VKI_SIG_IGN
+ || !VG_(gdbserver_report_signal) (sigNo, tid);
}
/* ---------------------------------------------------------------------
@@ -1795,6 +1804,9 @@
info.si_code = si_code;
info.VKI_SIGINFO_si_addr = (void*)addr;
+ /* even if gdbserver indicates to ignore the signal, we will deliver it */
+ VG_(gdbserver_report_signal) (VKI_SIGSEGV, tid);
+
/* If they're trying to block the signal, force it to be delivered */
if (VG_(sigismember)(&VG_(threads)[tid].sig_mask, VKI_SIGSEGV))
VG_(set_default_handler)(VKI_SIGSEGV);
@@ -1833,8 +1845,12 @@
info.si_code = VKI_ILL_ILLOPC; /* jrs: no idea what this should be */
info.VKI_SIGINFO_si_addr = (void*)addr;
- resume_scheduler(tid);
- deliver_signal(tid, &info, NULL);
+ if (VG_(gdbserver_report_signal) (VKI_SIGILL, tid)) {
+ resume_scheduler(tid);
+ deliver_signal(tid, &info, NULL);
+ }
+ else
+ resume_scheduler(tid);
}
// Synthesise a SIGBUS.
@@ -1854,8 +1870,12 @@
in .si_addr. Oh well. */
/* info.VKI_SIGINFO_si_addr = (void*)addr; */
- resume_scheduler(tid);
- deliver_signal(tid, &info, NULL);
+ if (VG_(gdbserver_report_signal) (VKI_SIGBUS, tid)) {
+ resume_scheduler(tid);
+ deliver_signal(tid, &info, NULL);
+ }
+ else
+ resume_scheduler(tid);
}
// Synthesise a SIGTRAP.
@@ -1890,8 +1910,12 @@
# endif
/* fixs390: do we need to do anything here for s390 ? */
- resume_scheduler(tid);
- deliver_signal(tid, &info, &uc);
+ if (VG_(gdbserver_report_signal) (VKI_SIGTRAP, tid)) {
+ resume_scheduler(tid);
+ deliver_signal(tid, &info, &uc);
+ }
+ else
+ resume_scheduler(tid);
}
/* Make a signal pending for a thread, for later delivery.
@@ -2070,7 +2094,7 @@
/* (2) */
/* Set up the thread's state to deliver a signal */
- if (!is_sig_ign(info->si_signo))
+ if (!is_sig_ign(info->si_signo, tid))
deliver_signal(tid, info, uc);
/* It's crucial that (1) and (2) happen in the order (1) then (2)
@@ -2342,10 +2366,15 @@
}
if (VG_(in_generated_code)) {
- /* Can't continue; must longjmp back to the scheduler and thus
- enter the sighandler immediately. */
- deliver_signal(tid, info, uc);
- resume_scheduler(tid);
+ if (VG_(gdbserver_report_signal) (sigNo, tid)
+ || VG_(sigismember)(&tst->sig_mask, sigNo)) {
+ /* Can't continue; must longjmp back to the scheduler and thus
+ enter the sighandler immediately. */
+ deliver_signal(tid, info, uc);
+ resume_scheduler(tid);
+ }
+ else
+ resume_scheduler(tid);
}
/* If resume_scheduler returns or its our fault, it means we
@@ -2545,7 +2574,7 @@
/* OK, something to do; deliver it */
if (VG_(clo_trace_signals))
VG_(dmsg)("Polling found signal %d for tid %d\n", sip->si_signo, tid);
- if (!is_sig_ign(sip->si_signo))
+ if (!is_sig_ign(sip->si_signo, tid))
deliver_signal(tid, sip, NULL);
else if (VG_(clo_trace_signals))
VG_(dmsg)(" signal %d ignored\n", sip->si_signo);
diff --git a/coregrind/m_translate.c b/coregrind/m_translate.c
index 5fa2dd1..17d1b69 100644
--- a/coregrind/m_translate.c
+++ b/coregrind/m_translate.c
@@ -59,6 +59,7 @@
#include "pub_core_execontext.h" // VG_(make_depth_1_ExeContext_from_Addr)
+#include "pub_core_gdbserver.h" // VG_(tool_instrument_then_gdbserver_if_needed)
/*------------------------------------------------------------*/
/*--- Stats ---*/
@@ -210,6 +211,30 @@
return mkIRExpr_HWord( (HWord)ecu );
}
+/* When gdbserver is activated, the translation of a block must
+ first be done by the tool function, then followed by a pass
+ which (if needed) instruments the code for gdbserver.
+*/
+static
+IRSB* tool_instrument_then_gdbserver_if_needed ( VgCallbackClosure* closureV,
+ IRSB* sb_in,
+ VexGuestLayout* layout,
+ VexGuestExtents* vge,
+ IRType gWordTy,
+ IRType hWordTy )
+{
+ return VG_(instrument_for_gdbserver_if_needed)
+ (VG_(tdict).tool_instrument (closureV,
+ sb_in,
+ layout,
+ vge,
+ gWordTy,
+ hWordTy),
+ layout,
+ vge,
+ gWordTy,
+ hWordTy);
+}
/* For tools that want to know about SP changes, this pass adds
in the appropriate hooks. We have to do it after the tool's
@@ -1350,11 +1375,19 @@
if (seg != NULL) {
/* There's some kind of segment at the requested place, but we
aren't allowed to execute code here. */
- VG_(synth_fault_perms)(tid, addr);
+ if (debugging_translation)
+ VG_(printf)("translations not allowed here (segment not executable)"
+ "(0x%llx)\n", addr);
+ else
+ VG_(synth_fault_perms)(tid, addr);
} else {
/* There is no segment at all; we are attempting to execute in
the middle of nowhere. */
- VG_(synth_fault_mapping)(tid, addr);
+ if (debugging_translation)
+ VG_(printf)("translations not allowed here (no segment)"
+ "(0x%llx)\n", addr);
+ else
+ VG_(synth_fault_mapping)(tid, addr);
}
return False;
}
@@ -1460,7 +1493,9 @@
IRSB*(*f)(VgCallbackClosure*,
IRSB*,VexGuestLayout*,VexGuestExtents*,
IRType,IRType)
- = VG_(tdict).tool_instrument;
+ = VG_(clo_vgdb) != Vg_VgdbNo
+ ? tool_instrument_then_gdbserver_if_needed
+ : VG_(tdict).tool_instrument;
IRSB*(*g)(void*,
IRSB*,VexGuestLayout*,VexGuestExtents*,
IRType,IRType)
diff --git a/coregrind/pub_core_aspacemgr.h b/coregrind/pub_core_aspacemgr.h
index 484303e..c93b64a 100644
--- a/coregrind/pub_core_aspacemgr.h
+++ b/coregrind/pub_core_aspacemgr.h
@@ -283,12 +283,18 @@
extern SysRes VG_(am_sbrk_anon_float_valgrind)( SizeT cszB );
-/* Map a file at an unconstrained address for V, and update the
+/* Map privately a file at an unconstrained address for V, and update the
segment array accordingly. This is used by V for transiently
mapping in object files to read their debug info. */
extern SysRes VG_(am_mmap_file_float_valgrind)
( SizeT length, UInt prot, Int fd, Off64T offset );
+/* Map shared a file at an unconstrained address for V, and update the
+ segment array accordingly. This is used by V for communicating
+ with vgdb. */
+extern SysRes VG_(am_shared_mmap_file_float_valgrind)
+ ( SizeT length, UInt prot, Int fd, Off64T offset );
+
/* Unmap the given address range and update the segment array
accordingly. This fails if the range isn't valid for the client.
If *need_discard is True after a successful return, the caller
diff --git a/coregrind/pub_core_errormgr.h b/coregrind/pub_core_errormgr.h
index 2b72b03..c3462e0 100644
--- a/coregrind/pub_core_errormgr.h
+++ b/coregrind/pub_core_errormgr.h
@@ -51,7 +51,14 @@
extern void VG_(load_suppressions) ( void );
-extern void VG_(show_all_errors) ( void );
+// if verbosity == 0, print nothing.
+// else if xml print suppressions used (in xml format)
+// else if verbosity == 1 print Error summary
+// else print all errors and suppressions used.
+extern void VG_(show_all_errors) ( Int verbosity, Bool xml );
+
+/* Print (in readable format) the last error that occured. */
+extern void VG_(show_last_error) ( void );
extern void VG_(show_error_counts_as_XML) ( void );
diff --git a/coregrind/pub_core_gdbserver.h b/coregrind/pub_core_gdbserver.h
new file mode 100644
index 0000000..27ed10e
--- /dev/null
+++ b/coregrind/pub_core_gdbserver.h
@@ -0,0 +1,186 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Handle remote gdb protocol. pub_core_gdbserver.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2011 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 __PUB_CORE_GDBSERVER_H
+#define __PUB_CORE_GDBSERVER_H
+
+#include "pub_tool_gdbserver.h"
+
+// True if there is some activity from vgdb
+// If it returns True, then extern void VG_(gdbserver) can be called
+// to handle this incoming vgdb request.
+extern Bool VG_(gdbserver_activity) (ThreadId tid);
+
+
+/* Called by low level to insert or remove a break or watch point.
+ Break or watch point implementation is done using help from the tool.
+ break point support implies some (small) specific instrumentation
+ taken in charge for all tools by m_translate.c.
+
+ Write/read/access watchpoint can only be provided by tools which are
+ tracking addressability and/or accessibility of memory
+ (so typically memcheck can provide it). Note that memcheck addressability
+ bits do not differentiate between read and write accessibility.
+ However, when accessing unaddressable byte, memcheck can differentiate
+ reads from write, thereby providing read/write or access watchpoints.
+
+ Note that gdbserver assumes that software breakpoint is supported
+ (as this will be done by re-instrumenting the code).
+ Note that len is ignored for sofware breakpoints. hardware_breakpoint
+ are not supported.
+
+ Returns True if the point has properly been inserted or removed
+ Returns False otherwise. */
+Bool VG_(gdbserver_point) (PointKind kind, Bool insert,
+ Addr addr, int len);
+
+/* Entry point invoked by vgdb when it uses ptrace to cause a gdbserver
+ invocation. A magic value is passed by vgdb in check as a verification
+ that the call has been properly pushed by vgdb. */
+extern void VG_(invoke_gdbserver) ( int check );
+
+// To be called before delivering a signal.
+// Returns True if gdb user asks to pass the signal to the client.
+// Note that if the below returns True, the signal might
+// still be ignored if this is the action desired by the
+// guest program.
+extern Bool VG_(gdbserver_report_signal) (Int signo, ThreadId tid);
+
+/* software_breakpoint, single step and jump support ------------------------*/
+/* VG_(instrument_for_gdbserver_if_needed) allows to do "standard and easy"
+ instrumentation for gdbserver.
+ VG_(instrument_for_gdbserver_if_needed) does the following:
+ * checks if gdbserver instrumentation is needed for vge.
+ * if no gdbserver instrumentation needed,
+ returns sb_in
+ * otherwise
+ It will instrument sb_in to allow gdbserver to properly
+ handle breakpoints and single_stepping in sb_in.
+ All the target jumps of sb_in will also be invalidated
+ if these are not yet instrumented for gdbserver.
+ This allows to have single_step working, using a lazily
+ translation of the blocks which are being single stepped
+ in.
+
+ The typical usage of this function is to call it on the block
+ instrumented by the tool instrument function i.e. :
+ return VG_(instrument_for_gdbserver_if_needed) (sb_out,
+ layout,
+ vge,
+ gWordTy,
+ hWordTy);
+ where sb_out is the block instrumented by the tool.
+
+ If the block contains a call to a dirty helper that indirectly
+ calls gdbserver, then this dirty helper can (indirectly) change
+ the IP. This implies to jump to this IP after the call to
+ gdbserver. */
+extern IRSB* VG_(instrument_for_gdbserver_if_needed)
+ (IRSB* sb_in, /* block to be instrumented */
+ VexGuestLayout* layout,
+ VexGuestExtents* vge,
+ IRType gWordTy, IRType hWordTy);
+
+/* reason for which gdbserver connection must be finished */
+typedef
+ enum {
+ orderly_finish,
+ reset_after_error,
+ reset_after_fork} FinishReason;
+
+/* output various gdbserver statistics and status. */
+extern void VG_(gdbserver_status_output)(void);
+
+/* Shared structure between vgdb and the process running
+ under valgrind.
+ We define two variants: a 32 bit and a 64 bit.
+ The valgrind process will use the appropriate size,
+ according to the architecture.
+ vgdb will use what the valgrind process is using. */
+/* The below takes care that sizes will be 32 or 64 bits,
+ whatever the architecture. A.o., vgdb.c cannot use directly
+ the types from pub_core_threadstate.h as we want vgdb.c to
+ be independent of the arch it is debugging in case of bi-arch
+ Valgrind (e.g. x86 and amd64). So, the valgrind process must
+ give all the needed info/offset to vgdb in the below structure. */
+
+typedef
+ struct {
+ // PID of the vgdb that last connected to the Valgrind gdbserver.
+ // It will be set by vgdb after connecting.
+ int vgdb_pid;
+
+ // nr of bytes vgdb has written to valgrind
+ volatile int written_by_vgdb;
+ // nr of bytes seen by valgrind
+ volatile int seen_by_valgrind;
+
+ // address at which gdbserver can be invoked
+ Addr32 invoke_gdbserver;
+
+ // address of VG_(threads) and various sizes
+ // and offset needed by vgdb.
+ Addr32 threads;
+ int sizeof_ThreadState;
+ int offset_status;
+ int offset_lwpid;
+ } VgdbShared32;
+
+/* Same as VgdbShared32 but for 64 bits arch. */
+typedef
+ struct {
+ int vgdb_pid;
+
+ volatile int written_by_vgdb;
+ volatile int seen_by_valgrind;
+
+ Addr64 invoke_gdbserver;
+
+ Addr64 threads;
+ int sizeof_ThreadState;
+ int offset_status;
+ int offset_lwpid;
+ } VgdbShared64;
+
+// The below typedef makes the life of valgrind easier.
+// vgdb must however work explicitely with the specific 32 or 64 bits version.
+
+#if VEX_HOST_WORDSIZE == 8
+typedef VgdbShared64 VgdbShared;
+#elif VEX_HOST_WORDSIZE == 4
+typedef VgdbShared32 VgdbShared;
+#else
+# error "unexpected wordsize"
+#endif
+
+
+#endif // __PUB_CORE_GDBSERVER_H
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h
index ecfbd9b..bb7cb6c 100644
--- a/coregrind/pub_core_options.h
+++ b/coregrind/pub_core_options.h
@@ -54,6 +54,27 @@
default: 0 (no, return the application's exit code in the normal
way. */
extern Int VG_(clo_error_exitcode);
+
+typedef
+ enum {
+ Vg_VgdbNo, // Do not activate gdbserver.
+ Vg_VgdbYes, // Activate gdbserver (default).
+ Vg_VgdbFull, // ACtivate gdbserver in full mode, allowing
+ // a precise handling of watchpoints and single stepping
+ // at any moment.
+ }
+ VgVgdb;
+/* if != Vg_VgdbNo, allows valgrind to serve vgdb/gdb. */
+extern VgVgdb VG_(clo_vgdb);
+/* if > 0, checks every VG_(clo_vgdb_poll) BBS if vgdb wants to be served. */
+extern Int VG_(clo_vgdb_poll);
+/* prefix for the named pipes (FIFOs) used by vgdb/gdb to communicate with valgrind */
+extern Char* VG_(clo_vgdb_prefix);
+/* if True, gdbserver in valgrind will expose a target description containing
+ shadow registers */
+extern Bool VG_(clo_vgdb_shadow_registers);
+#define VG_CLO_VGDB_PREFIX_DEFAULT "/tmp/vgdb-pipe"
+
/* Enquire about whether to attach to a debugger at errors? default: NO */
extern Bool VG_(clo_db_attach);
/* The debugger command? default: whatever gdb ./configure found */
diff --git a/coregrind/pub_core_scheduler.h b/coregrind/pub_core_scheduler.h
index 73773d7..0458aaa 100644
--- a/coregrind/pub_core_scheduler.h
+++ b/coregrind/pub_core_scheduler.h
@@ -95,6 +95,14 @@
Addr clstack_end,
SizeT clstack_size );
+// Allows to disable the polling done to detect vgdb input
+// or to force a poll at next scheduler call.
+extern void VG_(disable_vgdb_poll) (void );
+extern void VG_(force_vgdb_poll) ( void );
+
+/* nr of bbs done since startup. */
+extern ULong VG_(bbs_done) (void);
+
/* Stats ... */
extern void VG_(print_scheduler_stats) ( void );
diff --git a/coregrind/vgdb.c b/coregrind/vgdb.c
new file mode 100644
index 0000000..fffc7cc
--- /dev/null
+++ b/coregrind/vgdb.c
@@ -0,0 +1,2141 @@
+/*--------------------------------------------------------------------*/
+/*--- Relay between gdb and gdbserver embedded in valgrind vgdb.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2011 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 "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 <unistd.h>
+#include <string.h>
+#include <poll.h>
+#include <pthread.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/mman.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include "assert.h"
+#include <sys/user.h>
+
+# if defined(VGO_linux)
+#include <sys/prctl.h>
+# endif
+
+/* 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
+
+ It is made of a main program which reads arguments. If no
+ arguments are given or only --pid and --vgdb-prefix, then usage 1 is
+ assumed.
+
+ As relay application, vgdb reads bytes from gdb on stdin and
+ writes these bytes to valgrind. Bytes read from valgrind are
+ written to gdb on stdout. Read/Write from/to valgrind is done
+ using FIFOs. There is one thread reading from stdin, writing to
+ valgrind on a FIFO. There is one thread reading from valgrind on a
+ FIFO, writing to gdb on stdout
+
+ 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)
+#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
+
+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))
+
+static char *vgdb_prefix = "/tmp/vgdb-pipe";
+
+/* Will be set to True when any condition indicating we have to shutdown
+ is encountered. */
+static Bool shutting_down = False;
+
+static VgdbShared32 *shared32;
+static VgdbShared64 *shared64;
+#define VS_written_by_vgdb (shared32 != NULL ? \
+ shared32->written_by_vgdb \
+ : shared64->written_by_vgdb)
+#define VS_seen_by_valgrind (shared32 != NULL ? \
+ shared32->seen_by_valgrind \
+ : shared64->seen_by_valgrind)
+
+#define VS_vgdb_pid (shared32 != NULL ? shared32->vgdb_pid : shared64->vgdb_pid)
+
+/* Calls malloc (size). Exits if memory can't be allocated. */
+static
+void *vmalloc(size_t size)
+{
+ void * mem = malloc(size);
+ if (mem == NULL)
+ XERROR (errno, "can't allocate memory\n");
+ return mem;
+}
+
+/* Calls realloc (size). Exits if memory can't be allocated. */
+static
+void *vrealloc(void *ptr,size_t size)
+{
+ void * mem = realloc(ptr, size);
+ if (mem == NULL)
+ XERROR (errno, "can't reallocate memory\n");
+ return mem;
+}
+
+/* add nrw to the written_by_vgdb field of shared32 or shared64 */
+static
+void add_written(int nrw)
+{
+ if (shared32 != NULL)
+ shared32->written_by_vgdb += nrw;
+ else if (shared64 != NULL)
+ shared64->written_by_vgdb += nrw;
+ else
+ assert(0);
+}
+
+static int shared_mem_fd = -1;
+static
+void map_vgdbshared (char* shared_mem)
+{
+ struct stat fdstat;
+ void **s;
+ shared_mem_fd = open(shared_mem, O_RDWR);
+ /* shared_mem_fd will not be closed till vgdb exits. */
+
+ if (shared_mem_fd == -1)
+ XERROR (errno, "error opening %s shared memory file\n", shared_mem);
+
+ if (fstat(shared_mem_fd, &fdstat) != 0)
+ XERROR (errno, "fstat");
+
+ if (fdstat.st_size == sizeof(VgdbShared64))
+ s = (void*) &shared64;
+ else if (fdstat.st_size == sizeof(VgdbShared32))
+ s = (void*) &shared32;
+ else
+#if VEX_HOST_WORDSIZE == 8
+ XERROR (0,
+ "error size shared memory file %s.\n"
+ "expecting size %d (64bits) or %d (32bits) got %ld.\n",
+ shared_mem,
+ (int) sizeof(VgdbShared64), (int) sizeof(VgdbShared32),
+ (long int)fdstat.st_size);
+#elif VEX_HOST_WORDSIZE == 4
+ XERROR (0,
+ "error size shared memory file %s.\n"
+ "expecting size %d (32bits) got %ld.\n",
+ shared_mem,
+ (int) sizeof(VgdbShared32),
+ fdstat.st_size);
+#else
+# error "unexpected wordsize"
+#endif
+
+#if VEX_HOST_WORDSIZE == 4
+ if (shared64 != NULL)
+ XERROR (0, "cannot use 32 bits vgdb with a 64bits valgrind process\n");
+ /* But we can use a 64 bits vgdb with a 32 bits valgrind */
+#endif
+
+ *s = (void*) mmap (NULL, fdstat.st_size,
+ PROT_READ|PROT_WRITE, MAP_SHARED,
+ shared_mem_fd, 0);
+
+ if (*s == (void *) -1)
+ XERROR (errno, "error mmap shared memory file %s\n", shared_mem);
+
+}
+
+#if VEX_HOST_WORDSIZE == 8
+typedef Addr64 CORE_ADDR;
+typedef Addr64 PTRACE_XFER_TYPE;
+typedef void* PTRACE_ARG3_TYPE;
+#elif VEX_HOST_WORDSIZE == 4
+typedef Addr32 CORE_ADDR;
+typedef Addr32 PTRACE_XFER_TYPE;
+typedef void* PTRACE_ARG3_TYPE;
+#else
+# error "unexpected wordsize"
+#endif
+
+static Bool pid_of_save_regs_continued = False;
+// True if we have continued pid_of_save_regs after PTRACE_ATTACH
+
+static Bool dying = False;
+// Set to True when loss of connection indicating that the Valgrind
+// process is dying.
+
+/* To be called when connection with valgrind is lost. In case we
+have lost the connection, it means that Valgrind has closed the
+connection and is busy exiting. We can't and don't have to stop it in
+this case. */
+static
+void valgrind_dying(void)
+{
+ pid_of_save_regs_continued = False;
+ dying = True;
+}
+
+
+#ifdef PTRACEINVOKER
+/* ptrace_(read|write)_memory are modified extracts of linux-low.c
+ from gdb 6.6. Copyrighted FSF */
+/* Copy LEN bytes from inferior's memory starting at MEMADDR
+ to debugger memory starting at MYADDR. */
+
+static
+int ptrace_read_memory (pid_t inferior_pid, CORE_ADDR memaddr,
+ unsigned char *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 debugger memory at MYADDR
+ to inferior's memory at MEMADDR.
+ On failure (cannot write the inferior)
+ returns the value of errno. */
+
+static
+int ptrace_write_memory (pid_t inferior_pid, CORE_ADDR memaddr,
+ const unsigned char *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", (unsigned)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));
+
+ if (WIFCONTINUED(status))
+ APPEND ("WIFCONTINUED ");
+
+ 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, 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, 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, char *msg)
+{
+ long res;
+
+ DEBUG(1, "%s PTRACE_ATTACH pid %d\n", msg, pid);
+ res = ptrace (PTRACE_ATTACH, pid, NULL, NULL);
+ if (res != 0) {
+ ERROR(errno, "%s PTRACE_ATTACH pid %d %ld\n", msg, pid, res);
+ return False;
+ }
+
+ 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,
+ (unsigned char *)&(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,
+ (unsigned char *)&(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;
+
+/* Get the registers from pid into regs.
+ Returns True if all ok, otherwise False. */
+static
+Bool getregs (int pid, void *regs)
+{
+# ifdef VGA_s390x
+ char *pregs = (char *) regs;
+ long offset;
+ errno = 0;
+ DEBUG(1, "getregs PTRACE_PEEKUSER(s)\n");
+ for (offset = 0; offset < PT_ENDREGS; 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;
+# else
+ // Platforms having GETREGS
+ long res;
+ DEBUG(1, "getregs PTRACE_GETREGS\n");
+ res = ptrace (PTRACE_GETREGS, pid, NULL, regs);
+ if (res != 0) {
+ ERROR(errno, "PTRACE_GETREGS %ld\n", res);
+ return False;
+ }
+ return True;
+# endif
+}
+
+/* Set the registers of pid to regs.
+ Returns True if all ok, otherwise False. */
+static
+Bool setregs (int pid, void *regs)
+{
+# ifdef VGA_s390x
+ char *pregs = (char *) regs;
+ long offset;
+ long res;
+ errno = 0;
+ DEBUG(1, "setregs PTRACE_POKEUSER(s)\n");
+ for (offset = 0; offset < PT_ENDREGS; 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;
+# else
+ // Platforms having SETREGS
+ long res;
+ 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
+}
+
+/* Restore the registers to the saved value, then detaches from all threads */
+static
+void restore_and_detach(int pid)
+{
+ if (pid_of_save_regs) {
+ /* In case the 'main pid' has been continued, we need to stop it
+ before resetting the registers. */
+ if (pid_of_save_regs_continued) {
+ pid_of_save_regs_continued = False;
+ if (!stop(pid_of_save_regs, "sigstop before reset regs"))
+ DEBUG(0, "Could not sigstop before reset");
+ }
+
+ DEBUG(1, "setregs restore registers pid %d\n", pid_of_save_regs);
+ if (!setregs(pid_of_save_regs, &user_save.regs)) {
+ ERROR(errno, "setregs restore registers pid %d after cont\n",
+ pid_of_save_regs);
+ }
+ pid_of_save_regs = 0;
+ } else {
+ DEBUG(1, "PTRACE_SETREGS restore registers: no pid\n");
+ }
+ detach_from_all_threads(pid);
+}
+
+/* Ensures that the gdbserver code is invoked by pid.
+ If an error occurs, resets to the valgrind process
+ to the state it has before being ptrace-d.
+ Returns True if invoke successful, False otherwise.
+*/
+static
+Bool invoke_gdbserver (int pid)
+{
+ 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.
+
+ On Ubuntu>= 10.10, a /proc setting can disable ptrace.
+ So, Valgrind has to SET_PTRACER this vgdb. Once this
+ is done, this vgdb can ptrace the valgrind process. */
+
+ 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)) {
+ 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];
+#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,
+ (unsigned char *) &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,
+ (unsigned char *) &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");
+#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,
+ (unsigned char *) &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,
+ (unsigned char *)&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,
+ (unsigned char *)&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;
+#else
+ I_die_here: architecture missing in vgdb.c
+#endif
+ }
+ else {
+ assert(0);
+ }
+
+ if (!setregs(pid, &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;
+
+ stopped = waitstopped (pid, SIGTRAP,
+ "waitpid status after PTRACE_CONT to invoke");
+ if (stopped) {
+ /* Here pid has properly stopped on the break. */
+ pid_of_save_regs_continued = False;
+ restore_and_detach(pid);
+ return True;
+ } else {
+ /* Whatever kind of problem happened. We shutdown */
+ shutting_down = True;
+ return False;
+ }
+}
+#endif
+
+static
+void cleanup_restore_and_detach(void *v_pid)
+{
+ DEBUG(1, "cleanup_restore_and_detach dying: %d\n", dying);
+#ifdef PTRACEINVOKER
+ if (!dying)
+ restore_and_detach(*(int*)v_pid);
+#endif
+}
+
+/* This function loops till shutting_down becomes true. In this loop,
+ it verifies if valgrind process is reading the characters written
+ by vgdb. The verification is done every max_invoke_ms ms. If
+ valgrind is not reading characters, it will use invoke_gdbserver
+ (if PTRACE_INVOKER is defined) to ensure that the gdbserver code is
+ called soon by valgrind. */
+static int max_invoke_ms = 100;
+static
+void *invoke_gdbserver_in_valgrind(void *v_pid)
+{
+ int pid = *(int *)v_pid;
+ int written_by_vgdb_before_sleep;
+ int seen_by_valgrind_before_sleep;
+
+ int invoked_written = -1;
+
+ pthread_cleanup_push(cleanup_restore_and_detach, v_pid);
+
+ while (!shutting_down) {
+ written_by_vgdb_before_sleep = VS_written_by_vgdb;
+ seen_by_valgrind_before_sleep = VS_seen_by_valgrind;
+ DEBUG(3,
+ "written_by_vgdb_before_sleep %d "
+ "seen_by_valgrind_before_sleep %d\n",
+ written_by_vgdb_before_sleep,
+ seen_by_valgrind_before_sleep);
+ if (usleep(1000 * max_invoke_ms) != 0) {
+ if (errno == EINTR)
+ continue;
+ XERROR (errno, "error usleep\n");
+ }
+ /* if nothing happened during our sleep, let's try to wake up valgrind */
+ if (written_by_vgdb_before_sleep == VS_written_by_vgdb
+ && seen_by_valgrind_before_sleep == VS_seen_by_valgrind
+ && VS_written_by_vgdb > VS_seen_by_valgrind) {
+ DEBUG(2,
+ "after sleep "
+ "written_by_vgdb %d "
+ "seen_by_valgrind %d "
+ "invoked_written %d\n",
+ VS_written_by_vgdb,
+ VS_seen_by_valgrind,
+ invoked_written);
+ /* if the pid does not exist anymore, we better stop */
+ if (kill(pid, 0) != 0)
+ XERROR (errno,
+ "invoke_gdbserver_in_valgrind: "
+ "check for pid %d existence failed\n", pid);
+
+ #if defined(PTRACEINVOKER)
+ /* only need to wake up if the nr written has changed since
+ last invoke. */
+ if (invoked_written != written_by_vgdb_before_sleep) {
+ if (invoke_gdbserver(pid)) {
+ /* If invoke succesful, no need to invoke again
+ for the same value of written_by_vgdb_before_sleep. */
+ invoked_written = written_by_vgdb_before_sleep;
+ }
+ }
+ #else
+ DEBUG(2, "invoke_gdbserver via ptrace not (yet) implemented\n");
+ #endif
+ }
+ }
+ pthread_cleanup_pop(0);
+ return NULL;
+}
+
+static
+int open_fifo (char* name, int flags, char* desc)
+{
+ int fd;
+ DEBUG(1, "opening %s %s\n", name, desc);
+ fd = open(name, flags);
+ if (fd == -1)
+ XERROR (errno, "error opening %s %s\n", name, desc);
+
+ DEBUG(1, "opened %s %s fd %d\n", name, desc, fd);
+ return fd;
+}
+
+/* acquire a lock on the first byte of the given fd. If not successful,
+ exits with error.
+ This allows to avoid having two vgdb speaking with the same Valgrind
+ gdbserver as this causes serious headaches to the protocol. */
+static
+void acquire_lock (int fd, int valgrind_pid)
+{
+ if (lockf(fd, F_TLOCK, 1) < 0) {
+ if (errno == EAGAIN || errno == EACCES) {
+ XERROR(errno,
+ "Cannot acquire lock.\n"
+ "Probably vgdb pid %d already speaks with Valgrind pid %d\n",
+ VS_vgdb_pid,
+ valgrind_pid);
+ } else {
+ XERROR(errno, "cannot acquire lock.\n");
+ }
+ }
+
+ /* Here, we have the lock. It will be released when fd will be closed. */
+ /* We indicate our pid to Valgrind gdbserver */
+ if (shared32 != NULL)
+ shared32->vgdb_pid = getpid();
+ else if (shared64 != NULL)
+ shared64->vgdb_pid = getpid();
+ else
+ assert(0);
+}
+
+#define PBUFSIZ 16384 /* keep in sync with server.h */
+
+/* read some characters from fd.
+ Returns the nr of characters read, -1 if error.
+ desc is a string used in tracing */
+static
+int read_buf (int fd, char* buf, char* desc)
+{
+ int nrread;
+ DEBUG(2, "reading %s\n", desc);
+ nrread = read(fd, buf, PBUFSIZ);
+ if (nrread == -1) {
+ ERROR (errno, "error reading %s\n", desc);
+ return -1;
+ }
+ buf[nrread] = '\0';
+ DEBUG(2, "read %s %s\n", desc, buf);
+ return nrread;
+}
+
+/* write size bytes from buf to fd.
+ desc is a description of the action for which the write is done.
+ If notify, then add size to the shared cntr indicating to the
+ valgrind process that there is new data.
+ Returns True if write is ok, False if there was a problem. */
+static
+Bool write_buf(int fd, char* buf, int size, char* desc, Bool notify)
+{
+ int nrwritten;
+ int nrw;
+ DEBUG(2, "writing %s len %d %s notify: %d\n", desc, size, buf, notify);
+ nrwritten = 0;
+ while (nrwritten < size) {
+ nrw = write (fd, buf+nrwritten, size - nrwritten);
+ if (nrw == -1) {
+ ERROR(errno, "error write %s\n", desc);
+ return False;
+ }
+ nrwritten = nrwritten + nrw;
+ if (notify)
+ add_written(nrw);
+ }
+ return True;
+}
+
+typedef enum {
+ FROM_GDB,
+ TO_GDB,
+ FROM_PID,
+ TO_PID } ConnectionKind;
+static const int NumConnectionKind = TO_PID+1;
+static
+char *ppConnectionKind (ConnectionKind con)
+{
+ switch (con) {
+ case FROM_GDB: return "FROM_GDB";
+ case TO_GDB: return "TO_GDB";
+ case FROM_PID: return "FROM_PID";
+ case TO_PID: return "TO_PID";
+ default: return "invalid connection kind";
+ }
+}
+
+static char *shared_mem;
+
+static const int from_gdb = 0;
+static char *from_gdb_to_pid; /* fifo name to write gdb command to pid */
+/* Returns True in case read/write operations were done properly.
+ Returns False in case of error.
+ to_pid is the file descriptor to write to the process pid. */
+static
+Bool read_from_gdb_write_to_pid(int to_pid)
+{
+ char buf[PBUFSIZ];
+ int nrread;
+
+ nrread = read_buf(from_gdb, buf, "from gdb on stdin");
+ if (nrread <= 0) {
+ if (nrread == 0)
+ DEBUG(1, "read 0 bytes from gdb => assume exit\n");
+ else
+ DEBUG(1, "error reading bytes from gdb\n");
+ close (from_gdb);
+ shutting_down = True;
+ return False;
+ }
+ return write_buf(to_pid, buf, nrread, "to_pid", /* notify */ True);
+}
+
+static const int to_gdb = 1;
+static char *to_gdb_from_pid; /* fifo name to read pid replies */
+/* Returns True in case read/write operations were done properly.
+ Returns False in case of error.
+ from_pid is the file descriptor to read data from the process pid. */
+static
+Bool read_from_pid_write_to_gdb(int from_pid)
+{
+ char buf[PBUFSIZ];
+ int nrread;
+
+ nrread = read_buf(from_pid, buf, "from pid");
+ if (nrread <= 0) {
+ if (nrread == 0)
+ DEBUG(1, "read 0 bytes from pid => assume exit\n");
+ else
+ DEBUG(1, "error reading bytes from pid\n");
+ close (from_pid);
+ shutting_down = True;
+ return False;
+ }
+ return write_buf(to_gdb, buf, nrread, "to_gdb", /* notify */ False);
+}
+
+/* prepares the FIFOs filenames, map the shared memory. */
+static
+void prepare_fifos_and_shared_mem(int pid)
+{
+ from_gdb_to_pid = vmalloc (strlen(vgdb_prefix) + 30);
+ to_gdb_from_pid = vmalloc (strlen(vgdb_prefix) + 30);
+ shared_mem = vmalloc (strlen(vgdb_prefix) + 30);
+ /* below 3 lines must match the equivalent in remote-utils.c */
+ sprintf(from_gdb_to_pid, "%s-from-vgdb-to-%d", vgdb_prefix, pid);
+ sprintf(to_gdb_from_pid, "%s-to-vgdb-from-%d", vgdb_prefix, pid);
+ sprintf(shared_mem, "%s-shared-mem-vgdb-%d", vgdb_prefix, pid);
+ DEBUG (1, "vgdb: using %s %s %s\n",
+ from_gdb_to_pid, to_gdb_from_pid, shared_mem);
+
+ map_vgdbshared(shared_mem);
+}
+
+/* Convert hex digit A to a number. */
+
+static int
+fromhex (int a)
+{
+ if (a >= '0' && a <= '9')
+ return a - '0';
+ else if (a >= 'a' && a <= 'f')
+ return a - 'a' + 10;
+ else
+ XERROR(0, "Reply contains invalid hex digit %c\n", a);
+ return 0;
+}
+
+/* Returns next char from fd. -1 if error, -2 if EOF.
+ NB: must always call it with the same fd */
+static int
+readchar (int fd)
+{
+ static unsigned char buf[PBUFSIZ];
+ static int bufcnt = 0;
+ static unsigned char *bufp;
+
+ if (bufcnt-- > 0)
+ return *bufp++;
+
+ bufcnt = read (fd, buf, sizeof (buf));
+
+ if (bufcnt <= 0) {
+ if (bufcnt == 0) {
+ fprintf (stderr, "readchar: Got EOF\n");
+ return -2;
+ } else {
+ ERROR (errno, "readchar\n");
+ return -1;
+ }
+ }
+
+ bufp = buf;
+ bufcnt--;
+ return *bufp++;
+}
+
+/* Read a packet from fromfd, with error checking,
+ and store it in BUF.
+ Returns length of packet, or -1 if error or -2 if EOF.
+ Writes ack on ackfd */
+
+static int
+getpkt (char *buf, int fromfd, int ackfd)
+{
+ char *bp;
+ unsigned char csum, c1, c2;
+ int c;
+
+ while (1) {
+ csum = 0;
+
+ while (1) {
+ c = readchar (fromfd);
+ if (c == '$')
+ break;
+ DEBUG(2, "[getpkt: discarding char '%c']\n", c);
+ if (c < 0)
+ return c;
+ }
+
+ bp = buf;
+ while (1) {
+ c = readchar (fromfd);
+ if (c < 0)
+ return c;
+ if (c == '#')
+ break;
+ if (c == '*') {
+ int repeat;
+ int r;
+ int prev;
+ prev = *(bp-1);
+ csum += c;
+ repeat = readchar (fromfd);
+ csum += repeat;
+ for (r = 0; r < repeat - 29; r ++)
+ *bp++ = prev;
+ } else {
+ *bp++ = c;
+ csum += c;
+ }
+ }
+ *bp = 0;
+
+ c1 = fromhex (readchar (fromfd));
+ c2 = fromhex (readchar (fromfd));
+
+ if (csum == (c1 << 4) + c2)
+ break;
+
+ fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
+ (c1 << 4) + c2, csum, buf);
+ if (write (ackfd, "-", 1) != 1)
+ ERROR(0, "error when writing - (nack)\n");
+ else
+ add_written(1);
+ }
+
+ DEBUG(2, "getpkt (\"%s\"); [sending ack] \n", buf);
+ if (write (ackfd, "+", 1) != 1)
+ ERROR(0, "error when writing + (ack)\n");
+ else
+ add_written(1);
+ return bp - buf;
+}
+
+static int sigint = 0;
+static int sigterm = 0;
+static int sigpipe = 0;
+static int sighup = 0;
+static int sigusr1 = 0;
+static int sigalrm = 0;
+static int sigusr1_fd = -1;
+static pthread_t invoke_gdbserver_in_valgrind_thread;
+
+static
+void received_signal (int signum)
+{
+ if (signum == SIGINT)
+ sigint++;
+ else if (signum == SIGUSR1) {
+ sigusr1++;
+ if (sigusr1_fd >= 0) {
+ char control_c = '\003';
+ write_buf(sigusr1_fd, &control_c, 1,
+ "write \\003 on SIGUSR1", /* notify */ True);
+ }
+ }
+ else if (signum == SIGTERM) {
+ shutting_down = True;
+ sigterm++;
+ } else if (signum == SIGHUP) {
+ shutting_down = True;
+ sighup++;
+ } else if (signum == SIGPIPE) {
+ sigpipe++;
+ } else if (signum == SIGALRM) {
+ sigalrm++;
+ DEBUG(1, "pthread_cancel invoke_gdbserver_in_valgrind_thread\n");
+ /* Note: we cannot directly invoke restore_and_detach : this must
+ be done by the thread that has attached.
+ We have in this thread pushed a cleanup handler that will
+ cleanup what is needed. */
+ pthread_cancel(invoke_gdbserver_in_valgrind_thread);
+ } else {
+ ERROR(0, "unexpected signal %d\n", signum);
+ }
+}
+
+/* install the signal handlers allowing e.g. vgdb to cleanup in
+ case of termination. */
+static
+void install_handlers(void)
+{
+ struct sigaction action, oldaction;
+
+ action.sa_handler = received_signal;
+ sigemptyset (&action.sa_mask);
+ action.sa_flags = 0;
+
+ /* SIGINT: when user types C-c in gdb, this sends
+ a SIGINT to vgdb + causes a character to be sent to remote gdbserver.
+ The later is enough to wakeup the valgrind process. */
+ if (sigaction (SIGINT, &action, &oldaction) != 0)
+ XERROR (errno, "vgdb error sigaction SIGINT\n");
+ /* We might do something more intelligent than just
+ reporting this SIGINT E.g. behave similarly to the gdb: two
+ control-C without feedback from the debugged process would
+ mean to stop debugging it. */
+
+ /* SIGUSR1: this is used to facilitate automatic testing. When
+ vgdb receives this signal, it will simulate the user typing C-c. */
+ if (sigaction (SIGUSR1, &action, &oldaction) != 0)
+ XERROR (errno, "vgdb error sigaction SIGUSR1\n");
+
+
+ /* SIGTERM: can receive this signal (e.g. from gdb) to terminate vgdb
+ when detaching or similar. A clean shutdown will be done as both
+ the read and write side will detect an end of file. */
+ if (sigaction (SIGTERM, &action, &oldaction) != 0)
+ XERROR (errno, "vgdb error sigaction SIGTERM\n");
+
+ /* SIGPIPE: can receive this signal when gdb detaches or kill the
+ process debugged: gdb will close its pipes to vgdb. vgdb
+ must resist to this signal to allow a clean shutdown. */
+ if (sigaction (SIGPIPE, &action, &oldaction) != 0)
+ XERROR (errno, "vgdb error sigaction SIGPIPE\n");
+
+ /* SIGALRM: in case invoke thread is blocked, alarm is used
+ to cleanup. */
+ if (sigaction (SIGALRM, &action, &oldaction) != 0)
+ XERROR (errno, "vgdb error sigaction SIGALRM\n");
+}
+
+/* close the FIFOs provided connections, terminate the invoker thread. */
+static
+void close_connection(int to_pid, int from_pid)
+{
+ DEBUG(1, "nr received signals: sigint %d sigterm %d sighup %d sigpipe %d\n",
+ sigint, sigterm, sighup, sigpipe);
+ /* Note that we do not forward sigterm to the valgrind process:
+ a sigterm signal is (probably) received from gdb if the user wants to
+ kill the debugged process. The kill instruction has been given to
+ the valgrind process, which should execute a clean exit. */
+
+ /* We first close the connection to pid. The pid will then
+ terminates its gdbserver work. We keep the from pid
+ fifo opened till the invoker thread is finished.
+ This allows the gdbserver to finish sending its last reply. */
+ if (close(to_pid) != 0)
+ ERROR(errno, "close to_pid\n");
+
+ /* if there is a task that was busy trying to wake up valgrind
+ process, we wait for it to be terminated otherwise threads
+ in the valgrind process can stay stopped if vgdb main
+ exits before the invoke thread had time to detach from
+ all valgrind threads. */
+ if (max_invoke_ms > 0) {
+ int join;
+
+ /* It is surprisingly complex to properly shutdown or exit the
+ valgrind process in which gdbserver has been invoked through
+ ptrace. In the normal case (gdb detaches from the process,
+ or process is continued), the valgrind process will reach the
+ breakpoint place. Using ptrace, vgdb will ensure the
+ previous activity of the process is resumed (e.g. restart a
+ blocking system call). The special case is when gdb asks the
+ valgrind process to exit (using either the "kill" command or
+ "monitor exit"). In such a case, the valgrind process will
+ call exit. But a ptraced process will be blocked in exit,
+ waiting for the ptracing process to detach or die. vgdb
+ cannot detach unconditionally as otherwise, in the normal
+ case, the valgrind process would die abnormally with SIGTRAP
+ (as vgdb would not be there to catch it). vgdb can also not
+ die unconditionally otherwise again, similar problem. So, we
+ assume that most of the time, we arrive here in the normal
+ case, and so, the breakpoint has been encountered by the
+ valgrind process, so the invoker thread will exit and the
+ join will succeed. For the "kill" case, we cause an alarm
+ signal to be sent after a few seconds. This means that in the
+ normal case, the gdbserver code in valgrind process must have
+ returned the control in less than the alarm nr of seconds,
+ otherwise, valgrind will die abnormally with SIGTRAP. */
+ (void) alarm (3);
+
+ DEBUG(1, "joining with invoke_gdbserver_in_valgrind_thread\n");
+ join = pthread_join(invoke_gdbserver_in_valgrind_thread, NULL);
+ if (join != 0)
+ XERROR
+ (join,
+ "vgdb error pthread_join invoke_gdbserver_in_valgrind_thread\n");
+ }
+ if (close(from_pid) != 0)
+ ERROR(errno, "close from_pid\n");
+}
+
+/* Relay data between gdb and Valgrind gdbserver, till EOF or an
+ error is encountered. */
+static
+void gdb_relay (int pid)
+{
+ int from_pid = -1; /* fd to read from pid */
+ int to_pid = -1; /* fd to write to pid */
+
+ int shutdown_loop = 0;
+ fprintf (stderr, "relaying data between gdb and process %d\n", pid);
+ fflush (stderr);
+
+ if (max_invoke_ms > 0)
+ pthread_create(&invoke_gdbserver_in_valgrind_thread, NULL,
+ invoke_gdbserver_in_valgrind, (void *) &pid);
+ to_pid = open_fifo(from_gdb_to_pid, O_WRONLY, "write to pid");
+ acquire_lock (shared_mem_fd, pid);
+
+ from_pid = open_fifo (to_gdb_from_pid, O_RDONLY|O_NONBLOCK,
+ "read mode from pid");
+
+ sigusr1_fd = to_pid; /* allow simulating user typing control-c */
+
+ while (1) {
+ ConnectionKind ck;
+ int ret;
+ struct pollfd pollfds[NumConnectionKind];
+
+ /* watch data written by gdb, watch POLLERR on both gdb fd */
+ pollfds[FROM_GDB].fd = from_gdb;
+ pollfds[FROM_GDB].events = POLLIN;
+ pollfds[FROM_GDB].revents = 0;
+ pollfds[TO_GDB].fd = to_gdb;
+ pollfds[TO_GDB].events = 0;
+ pollfds[TO_GDB].revents = 0;
+
+ /* watch data written by pid, watch POLLERR on both pid fd */
+ pollfds[FROM_PID].fd = from_pid;
+ pollfds[FROM_PID].events = POLLIN;
+ pollfds[FROM_PID].revents = 0;
+ pollfds[TO_PID].fd = to_pid;
+ pollfds[TO_PID].events = 0;
+ pollfds[TO_PID].revents = 0;
+
+ ret = poll(pollfds,
+ NumConnectionKind,
+ (shutting_down ?
+ 1 /* one second */
+ : -1 /* infinite */));
+ DEBUG(2, "poll ret %d errno %d\n", ret, errno);
+
+ /* check for unexpected error */
+ if (ret <= 0 && errno != EINTR) {
+ ERROR (errno, "unexpected poll ret %d\n", ret);
+ shutting_down = True;
+ break;
+ }
+
+ /* check for data to read */
+ for (ck = 0; ck < NumConnectionKind; ck ++) {
+ if (pollfds[ck].revents & POLLIN) {
+ switch (ck) {
+ case FROM_GDB:
+ if (!read_from_gdb_write_to_pid(to_pid))
+ shutting_down = True;
+ break;
+ case FROM_PID:
+ if (!read_from_pid_write_to_gdb(from_pid))
+ shutting_down = True;
+ break;
+ default: XERROR(0, "unexpected POLLIN on %s\n",
+ ppConnectionKind(ck));
+ }
+ }
+ }
+
+ /* check for an fd being in error condition */
+ for (ck = 0; ck < NumConnectionKind; ck ++) {
+ if (pollfds[ck].revents & POLLERR) {
+ DEBUG(1, "connection %s fd %d POLLERR error condition\n",
+ ppConnectionKind(ck), pollfds[ck].fd);
+ valgrind_dying();
+ shutting_down = True;
+ }
+ if (pollfds[ck].revents & POLLHUP) {
+ DEBUG(1, "connection %s fd %d POLLHUP error condition\n",
+ ppConnectionKind(ck), pollfds[ck].fd);
+ valgrind_dying();
+ shutting_down = True;
+ }
+ if (pollfds[ck].revents & POLLNVAL) {
+ DEBUG(1, "connection %s fd %d POLLNVAL error condition\n",
+ ppConnectionKind(ck), pollfds[ck].fd);
+ valgrind_dying();
+ shutting_down = True;
+ }
+ }
+
+ if (shutting_down) {
+ /* we let some time to the final packets to be transferred */
+ shutdown_loop++;
+ if (shutdown_loop > 3)
+ break;
+ }
+ }
+ close_connection(to_pid, from_pid);
+}
+
+static int packet_len_for_command(char *cmd)
+{
+ /* cmd will be send as a packet $qRcmd,xxxx....................xx#cc */
+ return 7+ 2*strlen(cmd) +3 + 1;
+}
+
+/* hyper-minimal protocol implementation that
+ sends the provided commands (using qRcmd packets)
+ and read and display their replies. */
+static
+void standalone_send_commands(int pid,
+ int last_command,
+ char *commands[] )
+{
+ int from_pid = -1; /* fd to read from pid */
+ int to_pid = -1; /* fd to write to pid */
+
+ int i;
+ int hi;
+ unsigned char hex[3];
+ unsigned char cksum;
+ unsigned char *hexcommand;
+ unsigned char buf[PBUFSIZ];
+ int buflen;
+ int nc;
+
+
+ if (max_invoke_ms > 0)
+ pthread_create(&invoke_gdbserver_in_valgrind_thread, NULL,
+ invoke_gdbserver_in_valgrind, (void *) &pid);
+
+ to_pid = open_fifo(from_gdb_to_pid, O_WRONLY, "write to pid");
+ acquire_lock (shared_mem_fd, pid);
+
+ /* first send a C-c \003 to pid, so that it wakes up the process
+ After that, we can open the fifo from the pid in read mode
+ We then start to wait for packets (normally first a resume reply)
+ At that point, we send our command and expect replies */
+ buf[0] = '\003';
+ write_buf(to_pid, buf, 1, "write \\003 to wake up", /* notify */ True);
+ from_pid = open_fifo(to_gdb_from_pid, O_RDONLY,
+ "read cmd result from pid");
+
+ for (nc = 0; nc <= last_command; nc++) {
+ fprintf (stderr, "sending command %s to pid %d\n", commands[nc], pid);
+ fflush (stderr);
+
+ /* prepare hexcommand $qRcmd,xxxx....................xx#cc */
+ hexcommand = vmalloc (packet_len_for_command(commands[nc]));
+ hexcommand[0] = 0;
+ strcat (hexcommand, "$qRcmd,");
+ for (i = 0; i < strlen(commands[nc]); i++) {
+ sprintf(hex, "%02x", commands[nc][i]);
+ strcat (hexcommand, hex);
+ }
+ /* checksum (but without the $) */
+ cksum = 0;
+ for (hi = 1; hi < strlen(hexcommand); hi++)
+ cksum+=hexcommand[hi];
+ strcat(hexcommand, "#");
+ sprintf(hex, "%02x", cksum);
+ strcat(hexcommand, hex);
+ write_buf(to_pid, hexcommand, strlen(hexcommand),
+ "writing hex command to pid", /* notify */ True);
+
+ /* we exit of the below loop explicitely when the command has
+ been handled or because a signal handler will set
+ shutting_down. */
+ while (!shutting_down) {
+ buflen = getpkt(buf, from_pid, to_pid);
+ if (buflen < 0) {
+ ERROR (0, "error reading packet\n");
+ if (buflen == -2)
+ valgrind_dying();
+ break;
+ }
+ if (strlen(buf) == 0) {
+ DEBUG(0, "empty packet rcvd (packet qRcmd not recognised?)\n");
+ break;
+ }
+ if (strcmp(buf, "OK") == 0) {
+ DEBUG(1, "OK packet rcvd\n");
+ break;
+ }
+ if (buf[0] == 'E') {
+ DEBUG(0,
+ "E NN error packet rcvd: %s (unknown monitor command?)\n",
+ buf);
+ break;
+ }
+ if (buf[0] == 'W') {
+ DEBUG(0, "W stopped packet rcvd: %s\n", buf);
+ break;
+ }
+ if (buf[0] == 'T') {
+ DEBUG(1, "T resume reply packet received: %s\n", buf);
+ continue;
+ }
+
+ /* must be here an O packet with hex encoded string reply
+ => decode and print it */
+ if (buf[0] != 'O') {
+ DEBUG(0, "expecting O packet, received: %s\n", buf);
+ continue;
+ }
+ {
+ char buf_print[buflen/2 + 1];
+ for (i = 1; i < buflen; i = i + 2)
+ buf_print[i/2] = (fromhex(*(buf+i)) << 4)
+ + fromhex(*(buf+i+1));
+ buf_print[buflen/2] = 0;
+ printf("%s", buf_print);
+ fflush(stdout);
+ }
+ }
+ free (hexcommand);
+ }
+ shutting_down = True;
+
+ close_connection(to_pid, from_pid);
+}
+
+/* report to user the existence of a vgdb-able valgrind process
+ with given pid */
+static
+void report_pid (int pid)
+{
+ char cmdline_file[100];
+ char cmdline[1000];
+ int fd;
+ int i, sz;
+
+ sprintf(cmdline_file, "/proc/%d/cmdline", pid);
+ fd = open (cmdline_file, O_RDONLY);
+ if (fd == -1) {
+ DEBUG(1, "error opening cmdline file %s %s\n",
+ cmdline_file, strerror(errno));
+ sprintf(cmdline, "(could not obtain process command line)");
+ } else {
+ sz = read(fd, cmdline, 1000);
+ for (i = 0; i < sz; i++)
+ if (cmdline[i] == 0)
+ cmdline[i] = ' ';
+ cmdline[sz] = 0;
+ }
+ fprintf(stderr, "use --pid=%d for %s\n", pid, cmdline);
+ fflush(stderr);
+}
+
+/* Eventually produces additional usage information documenting the
+ ptrace restrictions. */
+static
+void ptrace_restrictions(void)
+{
+# ifdef PR_SET_PTRACER
+ char *ptrace_scope_setting_file = "/proc/sys/kernel/yama/ptrace_scope";
+ int fd = -1;
+ char ptrace_scope = 'X';
+ fd = open (ptrace_scope_setting_file, O_RDONLY, 0);
+ if (fd >= 0 && (read (fd, &ptrace_scope, 1) == 1) && (ptrace_scope != '0')) {
+ fprintf (stderr,
+ "Note: your kernel restricts ptrace invoker using %s\n"
+ "vgdb will only be able to attach to a Valgrind process\n"
+ "blocked in a system call *after* an initial successful attach\n",
+ ptrace_scope_setting_file);
+ } else if (ptrace_scope == 'X') {
+ fprintf(stderr, "Could not determine ptrace scope from %s\n",
+ ptrace_scope_setting_file);
+ }
+ if (fd >= 0)
+ close (fd);
+# endif
+
+# ifndef PTRACEINVOKER
+ fprintf(stderr,
+ "Note: ptrace invoker not implemented\n"
+ "For more info: read user manual section"
+ " 'Limitations of the Valgrind gdbserver'\n");
+# endif
+}
+
+static
+void usage(void)
+{
+ fprintf(stderr,
+"Usage: vgdb [OPTION]... [[-c] COMMAND]...\n"
+"vgdb (valgrind gdb) has two usages\n"
+" 1. standalone to send monitor commands to a Valgrind gdbserver.\n"
+" The OPTION(s) must be followed by the command to send\n"
+" To send more than one command, separate the commands with -c\n"
+" 2. relay application between gdb and a Valgrind gdbserver.\n"
+" Only OPTION(s) can be given.\n"
+"\n"
+" OPTIONS are [--pid=<number>] [--vgdb-prefix=<prefix>]\n"
+" [--max-invoke-ms=<number>] [--wait=<number>] [-d] -D]\n"
+" --pid arg must be given if multiple Valgrind gdbservers are found.\n"
+" --vgdb-prefix arg must be given to both Valgrind and vgdb utility\n"
+" if you want to change the default prefix for the FIFOs communication\n"
+" between the Valgrind gdbserver and vgdb.\n"
+" --wait arg tells vgdb to check during the specified number\n"
+" of seconds if a Valgrind gdbserver can be found.\n"
+" --max-invoke-ms gives the nr of milli-seconds after which vgdb will force\n"
+" the invocation of the Valgrind gdbserver (if the Valgrind process\n"
+" is blocked in a system call).\n"
+" -d arg tells to show debug info. Multiple -d args for more debug info\n"
+" -D arg tells to show shared mem status and then exit.\n"
+"\n"
+ );
+ ptrace_restrictions();
+}
+
+/* If arg_pid == -1, waits maximum check_trials seconds to discover
+ a valgrind pid appearing.
+ Otherwise verify arg_pid is valid and corresponds to a Valgrind process
+ with gdbserver activated.
+
+ Returns the pid to work with
+ or exits in case of error (e.g. no pid found corresponding to arg_pid */
+
+static
+int search_arg_pid(int arg_pid, int check_trials)
+{
+ int i;
+ int pid = -1;
+
+ if (arg_pid == 0 || arg_pid < -1) {
+ fprintf (stderr, "vgdb error: invalid pid %d given\n", arg_pid);
+ exit (1);
+ } else {
+ /* search for a matching named fifo.
+ If we have been given a pid, we will check that the matching FIFO is
+ there (or wait the nr of check_trials for this to appear).
+ If no pid has been given, then if we find only one FIFO,
+ we will use this to build the pid to use.
+ If we find multiple processes with valid FIFO, we report them and will
+ exit with an error. */
+ DIR *vgdb_dir;
+ char *vgdb_dir_name = vmalloc (strlen (vgdb_prefix) + 3);
+ struct dirent *f;
+ int is;
+ int nr_valid_pid = 0;
+ const char *suffix = "-from-vgdb-to-"; /* followed by pid */
+ char *vgdb_format = vmalloc (strlen(vgdb_prefix) + strlen(suffix) + 1);
+
+ strcpy (vgdb_format, vgdb_prefix);
+ strcat (vgdb_format, suffix);
+
+ strcpy (vgdb_dir_name, vgdb_prefix);
+
+ for (is = strlen(vgdb_prefix) - 1; is >= 0; is--)
+ if (vgdb_dir_name[is] == '/') {
+ vgdb_dir_name[is+1] = '\0';
+ break;
+ }
+ if (strlen(vgdb_dir_name) == 0)
+ strcpy (vgdb_dir_name, "./");
+
+ DEBUG(1, "searching pid in directory %s format %s\n",
+ vgdb_dir_name, vgdb_format);
+
+ /* try to find FIFOs with valid pid.
+ On exit of the loop, pid is set to:
+ -1 if no FIFOs matching a running process is found
+ -2 if multiple FIFOs of running processes are found
+ otherwise it is set to the (only) pid found that can be debugged
+ */
+ for (i = 0; i < check_trials; i++) {
+ DEBUG(1, "check_trial %d \n", i);
+ if (i > 0)
+ /* wait one second before checking again */
+ sleep(1);
+
+ vgdb_dir = opendir (vgdb_dir_name);
+ if (vgdb_dir == NULL)
+ XERROR (errno,
+ "vgdb error: opening directory %s searching vgdb fifo\n",
+ vgdb_dir_name);
+
+ errno = 0; /* avoid complain if vgdb_dir is empty */
+ while ((f = readdir (vgdb_dir))) {
+ struct stat st;
+ char pathname[strlen(vgdb_dir_name) + strlen(f->d_name)];
+ char *wrongpid;
+ int newpid;
+
+ strcpy (pathname, vgdb_dir_name);
+ strcat (pathname, f->d_name);
+ DEBUG(3, "trying %s\n", pathname);
+ if (stat (pathname, &st) != 0) {
+ if (debuglevel >= 3)
+ ERROR (errno, "vgdb error: stat %s searching vgdb fifo\n",
+ pathname);
+ } else if (S_ISFIFO (st.st_mode)) {
+ DEBUG(3, "trying %s\n", pathname);
+ if (strncmp (pathname, vgdb_format,
+ strlen (vgdb_format)) == 0) {
+ newpid = strtol(pathname + strlen (vgdb_format),
+ &wrongpid, 10);
+ if (*wrongpid == '\0' && newpid > 0
+ && kill (newpid, 0) == 0) {
+ nr_valid_pid++;
+ if (arg_pid != -1) {
+ if (arg_pid == newpid) {
+ pid = newpid;
+ }
+ } else if (nr_valid_pid > 1) {
+ if (nr_valid_pid == 2) {
+ fprintf
+ (stderr,
+ "no --pid= arg given"
+ " and multiple valgrind pids found:\n");
+ report_pid (pid);
+ }
+ pid = -2;
+ report_pid (newpid);
+ } else {
+ pid = newpid;
+ }
+ }
+ }
+ }
+ errno = 0; /* avoid complain if at the end of vgdb_dir */
+ }
+ if (f == NULL && errno != 0)
+ XERROR (errno, "vgdb error: reading directory %s for vgdb fifo\n",
+ vgdb_dir_name);
+
+ closedir (vgdb_dir);
+ if (pid != -1)
+ break;
+ }
+
+ free (vgdb_dir_name);
+ free (vgdb_format);
+ }
+
+ if (pid == -1) {
+ if (arg_pid == -1)
+ fprintf (stderr, "vgdb error: no FIFO found and no pid given\n");
+ else
+ fprintf (stderr, "vgdb error: no FIFO found matching pid %d\n",
+ arg_pid);
+ exit (1);
+ }
+ else if (pid == -2) {
+ /* no arg_pid given, multiple FIFOs found */
+ exit (1);
+ }
+ else {
+ return pid;
+ }
+}
+
+/* return true if the numeric value of an option of the
+ form --xxxxxxxxx=<number> could properly be extracted
+ from arg. If True is returned, *value contains the
+ extracted value.*/
+static
+Bool numeric_val(char* arg, int *value)
+{
+ const char *eq_pos = strchr(arg, '=');
+ char *wrong;
+
+ if (eq_pos == NULL)
+ return False;
+
+ *value = strtol(eq_pos+1, &wrong, 10);
+ if (*wrong)
+ return False;
+
+ return True;
+}
+
+/* true if arg matches the provided option */
+static
+Bool is_opt(char* arg, char *option)
+{
+ int option_len = strlen(option);
+ if (option[option_len-1] == '=')
+ return (0 == strncmp(option, arg, option_len));
+ else
+ return (0 == strcmp(option, arg));
+}
+
+/* Parse command lines options. If error(s), exits.
+ Otherwise returns the options in *p_... args.
+ commands must be big enough for the commands extracted from argv.
+ On return, *p_last_command gives the position in commands where
+ the last command has been allocated (using vmalloc). */
+static
+void parse_options(int argc, char** argv,
+ Bool *p_show_shared_mem,
+ int *p_arg_pid,
+ int *p_check_trials,
+ int *p_last_command,
+ char *commands[])
+{
+ Bool show_shared_mem = False;
+ int arg_pid = -1;
+ int check_trials = 1;
+ int last_command = -1;
+
+ int i;
+ int arg_errors = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (is_opt(argv[i], "--help") || is_opt(argv[i], "-h")) {
+ usage();
+ exit(0);
+ } else if (is_opt(argv[i], "-d")) {
+ debuglevel++;
+ } else if (is_opt(argv[i], "-D")) {
+ show_shared_mem = True;
+ } else if (is_opt(argv[i], "--pid=")) {
+ int newpid;
+ if (!numeric_val(argv[i], &newpid)) {
+ fprintf (stderr, "invalid pid argument %s\n", argv[i]);
+ arg_errors++;
+ } else if (arg_pid != -1) {
+ fprintf (stderr, "multiple pid arguments given\n");
+ arg_errors++;
+ } else {
+ arg_pid = newpid;
+ }
+ } else if (is_opt(argv[i], "--wait=")) {
+ if (!numeric_val(argv[i], &check_trials)) {
+ fprintf (stderr, "invalid wait argument %s\n", argv[i]);
+ arg_errors++;
+ }
+ } else if (is_opt(argv[i], "--max-invoke-ms=")) {
+ if (!numeric_val(argv[i], &max_invoke_ms)) {
+ fprintf (stderr, "invalid max-invoke-ms argument %s\n", argv[i]);
+ arg_errors++;
+ }
+ } else if (is_opt(argv[i], "--vgdb-prefix=")) {
+ vgdb_prefix = argv[i] + 14;
+ } else if (is_opt(argv[i], "-c")) {
+ last_command++;
+ commands[last_command] = vmalloc (1);
+ commands[last_command][0] = '\0';
+ } else if (0 == strncmp(argv[i], "-", 1)) {
+ fprintf (stderr, "unknown or invalid argument %s\n", argv[i]);
+ arg_errors++;
+ } else {
+ int len;
+ if (last_command == -1) {
+ /* only one command, no -c command indicator */
+ last_command++;
+ commands[last_command] = vmalloc (1);
+ commands[last_command][0] = '\0';
+ }
+ len = strlen(commands[last_command]);
+ commands[last_command] = vrealloc (commands[last_command],
+ len + 1 + strlen(argv[i]) + 1);
+ if (len > 0)
+ strcat (commands[last_command], " ");
+ strcat (commands[last_command], argv[i]);
+ if (packet_len_for_command(commands[last_command]) > PBUFSIZ) {
+ fprintf (stderr, "command %s too long\n", commands[last_command]);
+ arg_errors++;
+ }
+
+ }
+ }
+ if (arg_errors > 0) {
+ fprintf (stderr, "args error. Try `vgdb --help` for more information\n");
+ exit(1);
+ }
+
+ *p_show_shared_mem = show_shared_mem;
+ *p_arg_pid = arg_pid;
+ *p_check_trials = check_trials;
+ *p_last_command = last_command;
+}
+
+int main(int argc, char** argv)
+{
+ int i;
+ int pid;
+
+ Bool show_shared_mem;
+ int arg_pid;
+ int check_trials;
+ int last_command;
+ char *commands[argc]; // we will never have more commands than args.
+
+ parse_options(argc, argv,
+ &show_shared_mem,
+ &arg_pid,
+ &check_trials,
+ &last_command,
+ commands);
+
+ /* when we are working as a relay for gdb, handle some signals by
+ only reporting them (according to debug level). Also handle these
+ when ptrace will be used: vgdb must clean up the ptrace effect before
+ dying. */
+ if (max_invoke_ms > 0 || last_command == -1)
+ install_handlers();
+
+ pid = search_arg_pid (arg_pid, check_trials);
+
+ prepare_fifos_and_shared_mem(pid);
+
+ if (show_shared_mem) {
+ fprintf(stderr,
+ "vgdb %d "
+ "written_by_vgdb %d "
+ "seen_by_valgrind %d\n"
+ "vgdb pid %d\n",
+ VS_vgdb_pid,
+ VS_written_by_vgdb,
+ VS_seen_by_valgrind,
+ VS_vgdb_pid);
+ exit (0);
+ }
+
+ if (last_command >= 0) {
+ standalone_send_commands(pid, last_command, commands);
+ } else {
+ gdb_relay(pid);
+ }
+
+
+ free (from_gdb_to_pid);
+ free (to_gdb_from_pid);
+ free (shared_mem);
+
+ for (i = 0; i <= last_command; i++)
+ free (commands[i]);
+ return 0;
+}
diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml
index 0fb6c9d..acc8f73 100644
--- a/docs/xml/manual-core.xml
+++ b/docs/xml/manual-core.xml
@@ -720,6 +720,49 @@
</listitem>
</varlistentry>
+ <varlistentry id="opt.vgdb" xreflabel="--vgdb">
+ <term>
+ <option><![CDATA[--vgdb=<no|yes|full> [default: yes] ]]></option>
+ </term>
+ <listitem>
+ <para>Valgrind will enable its embedded gdbserver if value yes
+ or full is given. This allows an
+ external <computeroutput>gdb</computeroutput> debuggger to debug
+ your program running under Valgrind. See
+ <xref linkend="manual-core.gdbserver"/> for a detailed
+ description.
+ </para>
+
+ <para> If the embedded gdbserver is enabled but no gdb is
+ currently being used, the <xref linkend="manual-core.vgdb"/>
+ command line utility can send "monitor commands" to Valgrind
+ from a shell. The Valgrind core provides a set of
+ <xref linkend="manual-core.valgrind-monitor-commands"/>. A tool
+ can optionally provide tool specific monitor commands, which are
+ documented in the tool specific chapter.
+ </para>
+
+ <para>The value 'full' has a significant overhead
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.vgdb-error" xreflabel="--vgdb-error">
+ <term>
+ <option><![CDATA[--vgdb-error=<number> [default: 999999999] ]]></option>
+ </term>
+ <listitem>
+ <para> Use this option when the Valgrind gdbserver is enabled with
+ <option>--vgdb</option> yes or full value. Tools that report
+ errors will invoke the embedded gdbserver for each error above
+ number. The value 0 will cause gdbserver to be invoked before
+ executing your program. This is typically used to insert gdb
+ breakpoints before execution, and will also work with tools that
+ do not report errors, such as Massif.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="opt.track-fds" xreflabel="--track-fds">
<term>
<option><![CDATA[--track-fds=<yes|no> [default: no] ]]></option>
@@ -1140,6 +1183,13 @@
debugger, quit from it, and the program will continue. Trying to
continue from inside the debugger doesn't work.</para>
+ <para>
+ Note : if you use gdb, a more powerful debugging support is
+ provided by the <option>--vgdb</option> yes or full value,
+ allowing among others to insert breakpoints, continue from
+ inside the debugger, etc.
+ </para>
+
<para><varname>C Ret</varname> or <varname>c Ret</varname> causes
Valgrind not to start a debugger, and not to ask again.</para>
</listitem>
@@ -1473,6 +1523,57 @@
</listitem>
</varlistentry>
+ <varlistentry id="opt.vgdb-poll" xreflabel="--vgdb-poll">
+ <term>
+ <option><![CDATA[--vgdb-poll=<number> [default: 5000] ]]></option>
+ </term>
+ <listitem>
+ <para> As part of its main loop, the Valgrind scheduler will
+ poll to check if some activity (such as an external command or
+ some input from a gdb) has to be handled by gdbserver. This
+ activity poll will be done after having run the given number of
+ basic blocks (or slightly more than the given number of basic
+ blocks). This poll is quite cheap so the default value is set
+ relatively low. You might further decrease this value if vgdb
+ cannot use ptrace system call to interrupt Valgrind if all
+ threads are (most of the time) blocked in a system call.
+ </para>
+ <para> GDBTD??? unclear why we have sometimes slightly more BB:
+ it seems that from time to time, some BB are run outside of
+ run_thread_for_a_while. Maybe this is due to block chasing ? I
+ do not think this is a problem, as I never saw more than a few
+ additional basic blocks being run without being visible in the
+ blocks executed by run_thread_for_a_while.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.vgdb-shadow-registers" xreflabel="--vgdb-shadow-registers">
+ <term>
+ <option><![CDATA[--vgdb-shadow-registers=no|yes [default: no] ]]></option>
+ </term>
+ <listitem>
+ <para> When activated, gdbserver will expose the Valgrind shadow registers
+ to gdb. With this, the value of the Valgrind shadow registers can be examined
+ or changed using gdb. Exposing shadows registers only works with a gdb version
+ >= 7.1.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.vgdb-prefix" xreflabel="--vgdb-prefix">
+ <term>
+ <option><![CDATA[--vgdb-prefix=<prefix> [default: /tmp/vgdb-pipe] ]]></option>
+ </term>
+ <listitem>
+ <para> To communicate with gdb/vgdb, the Valgrind gdbserver
+ creates 3 files (2 named FIFOs and a mmap shared memory
+ file). The prefix option controls the directory and prefix for
+ the creation of these files.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="opt.run-libc-freeres" xreflabel="--run-libc-freeres">
<term>
<option><![CDATA[--run-libc-freeres=<yes|no> [default: yes] ]]></option>
@@ -1630,6 +1731,13 @@
Valgrind itself. You shouldn't need to use them in the normal run of
things. If you wish to see the list, use the
<option>--help-debug</option> option.</para>
+
+<para>If you wish to debug your program rather than debugging
+Valgrind itself, then you should use the options
+<option>--vgdb=yes</option> or <option>--vgdb=full</option>
+or <option>--db-attach=yes</option>.
+</para>
+
<!-- end of xi:include in the manpage -->
</sect2>
@@ -1692,7 +1800,869 @@
</sect1>
+<sect1 id="manual-core.gdbserver"
+ xreflabel="Debugging your program using Valgrind gdbserver and gdb">
+<title>Debugging your program using Valgrind gdbserver and gdb</title>
+<para>A program running under Valgrind is not executed directly by the
+CPU. It rather runs on a synthetic CPU provided by Valgrind. This is
+why a debugger cannot debug your program under Valgrind the usual way.
+</para>
+<para>
+This section describes the special way gdb can interact with the
+Valgrind gdbserver to provide a fully debuggable program under
+Valgrind. Used in this way, gdb also provides an interactive usage of
+Valgrind core or tool functionalities (such as incremental leak search
+under Memcheck, on-demand Massif snapshot production, ...).
+</para>
+
+<sect2 id="manual-core.gdbserver-simple"
+ xreflabel="gdbserver simple example">
+<title>Quick Start : debugging in 3 steps</title>
+
+<para>If you want to debug a program with gdb when using Memcheck
+tool, start Valgrind the following way:
+<screen><![CDATA[
+valgrind --vgdb=yes --vgdb-error=0 prog
+]]></screen></para>
+
+<para>In another window, start a gdb the following way:
+<screen><![CDATA[
+gdb prog
+]]></screen></para>
+
+<para>Then give the following command to gdb:
+<screen><![CDATA[
+(gdb) target remote | vgdb
+]]></screen></para>
+
+<para>You can now debug your program e.g. by inserting a breakpoint
+and then using the gdb 'continue' command.</para>
+
+<para> The above quick start is enough for a basic usage of the
+Valgrind gdbserver. Read the sections below to learn about the
+advanced functionalities provided by the combination of Valgrind and
+gdb. Note that the option --vgdb=yes can be omitted, as this is the
+default value.
+</para>
+
+</sect2>
+
+<sect2 id="manual-core.gdbserver-concept"
+ xreflabel="gdbserver">
+<title>Valgrind gdbserver concept</title>
+<para>The gdb debugger is typically used to debug a process running on
+the same machine : gdb uses system calls to do actions such as read
+the values of the process variables or registers. This technique only
+allows gdb to debug a program running on the same computer.
+</para>
+
+<para>Gdb can also debug processes running on a different computer.
+For this, gdb defines a protocol (i.e. a set of query and reply
+packets) that allows to e.g. fetch the value of memory or registers,
+to set breakpoints, etc. A gdbserver is an implementation of this
+'gdb remote debugging' protocol. To debug a process running on a
+remote computer, a gdbserver (sometimes also called a gdb stub) must
+run at the remote computer side.
+</para>
+
+<para>The Valgrind core integrates an embedded gdbserver
+implementation, which is activated using <option>--vgdb=yes</option>
+or <option>--vgdb=full</option>. This gdbserver allows the process
+running on the Valgrind synthetic CPU to be debugged 'remotely' by gdb
+: gdb sends protocol query packets (such as 'get registers values') to
+the Valgrind embedded gdbserver. The embedded gdbserver executes the
+queries (for example, it will get the registers values of the
+synthetic CPU) and give the result back to gdb.
+</para>
+
+<para> Gdb can use various ways (tcp/ip, serial line, ...) to send and
+receive the remote protocol packets to a gdbserver. In the case of the
+Valgrind gdbserver, gdb communicates using a pipe and the
+<xref linkend="manual-core.vgdb"/> command as a relay application. If
+no gdb is currently being used, vgdb can also be used to send monitor
+commands to the Valgrind gdbserver from the shell command line.
+</para>
+
+</sect2>
+
+<sect2 id="manual-core.gdbserver-gdb"
+ xreflabel="Connecting gdb to a Valgrind gdbserver">
+<title>Connecting gdb to a Valgrind gdbserver</title>
+<para>To debug a program <filename>prog</filename> running under
+Valgrind, ensures that the Valgrind gdbserver is activated
+(i.e. --vgdb=yes or --vgdb=full). The option
+<![CDATA[--vgdb-error=<number> ]]> can be used to ask an invocation of
+the gdbserver for each error above number. A zero value will cause an
+invocation of the Valgrind gdbserver at startup, allowing to insert
+breakpoints before starting the execution. Example:
+<screen><![CDATA[
+valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog
+]]></screen></para>
+
+<para>With the above command, the Valgrind gdbserver is invoked at startup
+and indicates it is waiting for a connection from a gdb:</para>
+
+<programlisting><![CDATA[
+==2418== Memcheck, a memory error detector
+==2418== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
+==2418== Using Valgrind-3.7.0.SVN and LibVEX; rerun with -h for copyright info
+==2418== Command: ./prog
+==2418==
+==2418== (action at startup) vgdb me ...
+]]></programlisting>
+
+
+<para>A gdb in another window can then be connected to the Valgrind gdbserver.
+For this, gdb must be started on the program <filename>prog</filename>:
+<screen><![CDATA[
+gdb ./prog
+]]></screen></para>
+
+
+<para>You then indicate to gdb that a remote target debugging is to be done:
+<screen><![CDATA[
+(gdb) target remote | vgdb
+]]></screen>
+gdb then starts a vgdb relay application to communicate with the
+Valgrind embedded gdbserver:</para>
+
+<programlisting><![CDATA[
+(gdb) target remote | vgdb
+Remote debugging using | vgdb
+relaying data between gdb and process 2418
+Reading symbols from /lib/ld-linux.so.2...done.
+Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so.debug...done.
+Loaded symbols for /lib/ld-linux.so.2
+[Switching to Thread 2418]
+0x001f2850 in _start () from /lib/ld-linux.so.2
+(gdb)
+]]></programlisting>
+
+<para> In case vgdb detects that multiple Valgrind gdbserver can be connected
+to, it will exit after reporting the list of the debuggable Valgrind
+processes and their PIDs. You can then relaunch the gdb 'target' command, but
+specifying the process id of the process you want to debug:
+</para>
+
+<programlisting><![CDATA[
+(gdb) target remote | vgdb
+Remote debugging using | vgdb
+no --pid= arg given and multiple valgrind pids found:
+use --pid=2479 for valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog
+use --pid=2481 for valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog
+use --pid=2483 for valgrind --vgdb=yes --vgdb-error=0 ./another_prog
+Remote communication error: Resource temporarily unavailable.
+(gdb) target remote | vgdb --pid=2479
+Remote debugging using | vgdb --pid=2479
+relaying data between gdb and process 2479
+Reading symbols from /lib/ld-linux.so.2...done.
+Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so.debug...done.
+Loaded symbols for /lib/ld-linux.so.2
+[Switching to Thread 2479]
+0x001f2850 in _start () from /lib/ld-linux.so.2
+(gdb)
+]]></programlisting>
+
+<para>Once gdb is connected to the Valgrind gdbserver, gdb can be used
+similarly to a native debugging session:</para>
+ <itemizedlist>
+ <listitem>
+ <para> Breakpoints can be inserted or deleted. </para>
+ </listitem>
+ <listitem>
+ <para> Variables and registers values can be examined or modified.
+ </para>
+ </listitem>
+ <listitem>
+ <para> Signal handling can be configured (printing, ignoring, ...).
+ </para>
+ </listitem>
+ <listitem>
+ <para> Execution can be controlled (continue, step, next, stepi, ...).
+ </para>
+ </listitem>
+ <listitem>
+ <para> Program execution can be interrupted using Control-C. </para>
+ </listitem>
+ <listitem>
+ <para> ... </para>
+ </listitem>
+ </itemizedlist>
+
+<para> Refer to the gdb user manual for a complete list of gdb functionalities.
+</para>
+
+</sect2>
+
+<sect2 id="manual-core.gdbserver-commandhandling"
+ xreflabel="Monitor command handling by the Valgrind gdbserver">
+<title>Monitor command handling by the Valgrind gdbserver</title>
+
+<para> The Valgrind gdbserver provides a set of additional specific
+functionalities through "monitor commands". Such monitor commands can
+be sent from the gdb command line or from the shell command line. See
+<xref linkend="manual-core.valgrind-monitor-commands"/> for the list
+of the Valgrind core monitor commands.
+</para>
+
+<para> Each tool can also provide tool specific monitor commands. An
+example of a tool specific monitor command is the Memcheck monitor
+command <computeroutput>mc.leak_check any full
+reachable</computeroutput>. This requests a full reporting of the
+allocated memory blocks. To have this leak check executed, use the gdb
+command:
+<screen><![CDATA[
+(gdb) monitor mc.leak_check any full reachable
+]]></screen>
+</para>
+
+<para> gdb will send the mc.leak_check command to the Valgrind gdbserver. The
+Valgrind gdbserver will either execute the monitor command itself (if
+it recognises a Valgrind core monitor command) or let the tool execute the
+tool specific monitor commands:
+</para>
+<programlisting><![CDATA[
+(gdb) monitor mc.leak_check any full reachable
+==2418== 100 bytes in 1 blocks are still reachable in loss record 1 of 1
+==2418== at 0x4006E9E: malloc (vg_replace_malloc.c:236)
+==2418== by 0x804884F: main (prog.c:88)
+==2418==
+==2418== LEAK SUMMARY:
+==2418== definitely lost: 0 bytes in 0 blocks
+==2418== indirectly lost: 0 bytes in 0 blocks
+==2418== possibly lost: 0 bytes in 0 blocks
+==2418== still reachable: 100 bytes in 1 blocks
+==2418== suppressed: 0 bytes in 0 blocks
+==2418==
+(gdb)
+]]></programlisting>
+
+<para> Like for the gdb commands, the Valgrind gdbserver will accept
+abbreviated monitor command names and arguments, as long as the given
+abbreviation is non ambiguous. For example, the above mc.leak_check
+command can also be typed as:
+<screen><![CDATA[
+(gdb) mo mc.l a f r
+]]></screen>
+
+The letters <computeroutput>mo</computeroutput> are recognised by gdb as being
+<computeroutput>monitor</computeroutput>. So, gdb sends the
+string <computeroutput>mc.l a f r</computeroutput> to the Valgrind
+gdbserver. The letters provided in this string are unambiguous for the
+Valgrind gdbserver. So, this will give the same output as the non
+abbreviated command and arguments. If the provided abbreviation is
+ambiguous, the Valgrind gdbserver will report the list of commands (or
+argument values) that can match:
+<programlisting><![CDATA[
+(gdb) mo mc. a r f
+mc. can match mc.get_vbits mc.leak_check mc.make_memory mc.check_memory
+(gdb)
+]]></programlisting>
+</para>
+
+<para> Instead of sending a monitor command from gdb, you can also
+send these from a shell command line. For example, the below command lines
+given in a shell will cause the same leak search to be executed by the
+process 3145:
+<screen><![CDATA[
+vgdb --pid=3145 mc.leak_check any full reachable
+vgdb --pid=3145 mc.l a f r
+]]></screen></para>
+
+<para>Note that the Valgrind gdbserver automatically continues the
+execution of the program after a standalone invocation of
+vgdb. Monitor commands sent from gdb do not cause the program to
+continue: the program execution is controlled explicitely using gdb
+commands such as 'continue' or 'next'.</para>
+
+</sect2>
+
+<sect2 id="manual-core.gdbserver-threads"
+ xreflabel="Valgrind gdbserver thread info">
+<title>Valgrind gdbserver thread info</title>
+
+<para> The Valgrind gdbserver enriches the output of the
+gdb <computeroutput>info threads</computeroutput> with Valgrind
+specific information. The operating system thread number is followed
+by the Valgrind 'tid' and the Valgrind scheduler thread state:</para>
+
+<programlisting><![CDATA[
+(gdb) info threads
+ 4 Thread 6239 (tid 4 VgTs_Yielding) 0x001f2832 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
+* 3 Thread 6238 (tid 3 VgTs_Runnable) make_error (s=0x8048b76 "called from London") at prog.c:20
+ 2 Thread 6237 (tid 2 VgTs_WaitSys) 0x001f2832 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
+ 1 Thread 6234 (tid 1 VgTs_Yielding) main (argc=1, argv=0xbedcc274) at prog.c:105
+(gdb)
+]]></programlisting>
+
+</sect2>
+
+<sect2 id="manual-core.gdbserver-shadowregisters"
+ xreflabel="Examining and modifying Valgrind shadow registers">
+<title>Examining and modifying Valgrind shadow registers</title>
+
+<para> When the option <![CDATA[--vgdb-shadow-registers=yes ]]> is
+given, the Valgrind gdbserver will let gdb examine and/or modify the
+Valgrind shadow registers. A gdb version >= 7.1 is needed for this
+to work.</para>
+
+<para> For each CPU register, the Valgrind core maintains two
+shadow registers. These shadow registers can be accessed from
+gdb by giving a postfix s1 or s2 for respectively the first
+and second shadow registers. As an example, the x86 register
+<computeroutput>eax</computeroutput> and its two shadow
+registers can be examined using the following commands:</para>
+
+<programlisting><![CDATA[
+(gdb) p $eax
+$1 = 0
+(gdb) p $eaxs1
+$2 = 0
+(gdb) p $eaxs2
+$3 = 0
+(gdb)
+]]></programlisting>
+
+</sect2>
+
+
+<sect2 id="manual-core.gdbserver-limitations"
+ xreflabel="Limitations of the Valgrind gdbserver">
+<title>Limitations of the Valgrind gdbserver</title>
+
+<para>Debugging with the Valgrind gdbserver is very similar to native
+debugging. The implementation of the Valgrind gdbserver is quite
+complete, and so provides most of the gdb debugging facilities. There
+are however some limitations or particularities described in details
+in this section:</para>
+ <itemizedlist>
+ <listitem>
+ <para> Precision of 'stopped at instruction'.</para>
+ <para>Gdb commands such as 'step', 'next', 'stepi', breakpoints,
+ watchpoints, ... will stop the execution of the process. With
+ the option --vgdb=yes, the process might not stop at the exact
+ instruction needed. Instead, it might continue execution of the
+ current block and stop at one of the following blocks. This is
+ linked to the fact that Valgrind gdbserver has to instrument a
+ block to allow stopping at the exact instruction requested.
+ Currently, re-instrumenting the current block being executed is
+ not supported. So, if the action requested by gdb (e.g. single
+ stepping or inserting a breakpoint) implies to re-instrument the
+ current block, the gdb action might not be executed precisely.
+ </para>
+ <para> This limitation will be triggered when the current block
+ being executed has not (yet) been instrumented for debugging.
+ This typically happens when the gdbserver is activated due to the
+ tool reporting an error or to a watchpoint. If the gdbserver
+ block has been activated following a breakpoint (or if a
+ breakpoint has been inserted in the block before its execution),
+ then the block has already been instrumented for debugging.
+ </para>
+ <para> If you use the option --vgdb=full, then gdb 'stop actions'
+ will always be obeyed precisely, but this implies that each
+ instruction will be instrumented with an additional call to a
+ gdbserver helper function, which implies some overhead compared
+ to --vgdb=no. Option --vgdb=yes has neglectible overhead compared
+ to --vgdb=no.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>Hardware watchpoint support by the Valgrind
+ gdbserver.</para>
+
+ <para> The Valgrind gdbserver can simulate hardware watchpoints
+ (but only if the tool provides the support for this). Currently,
+ only Memcheck provides hardware watchpoint simulation. The
+ hardware watchpoint simulation provided by Memcheck is much
+ faster that gdb software watchpoints (which are implemented by
+ gdb checking the value of the watched zone(s) after each
+ instruction). Hardware watchpoint simulation also provides read
+ watchpoints. The hardware watchpoint simulation by Memcheck has
+ some limitations compared to the real hardware
+ watchpoints. However, the number and length of simulated
+ watchpoints are not limited.
+ </para>
+ <para> Typically, the number of (real) hardware watchpoint is
+ limited. For example, the x86 architecture supports a maximum of
+ 4 hardware watchpoints, each watchpoint watching 1, 2, 4 or 8
+ bytes. The Valgrind gdbserver does not have a limitation on the
+ number of simulated hardware watchpoints. It also has no
+ limitation on the length of the memory zone being
+ watched. However, gdb currently does not (yet) understand that
+ Valgrind gdbserver watchpoints have no length limit. A gdb patch
+ providing a command 'set remote hardware-watchpoint-length-limit'
+ has been developped. The integration of this patch in gdb would
+ allow to fully use the flexibility of the Valgrind gdbserver
+ simulated hardware watchpoints (is there a gdb developper reading
+ this ?).
+ </para>
+ <para> Memcheck implements hardware watchpoint simulation by
+ marking the watched zone(s) as being unaddressable. In case a
+ hardware watchpoint is removed, the zone is marked as addressable
+ and defined. Hardware watchpoint simulation of addressable
+ undefined memory zones will properly work, but will have as a
+ side effect to mark the zone as defined when the watchpoint is
+ removed.</para>
+ <para> Write watchpoints might not be reported at the instruction
+ which is modifying the value unless option --vgdb=full is
+ given. Read watchpoints will always be reported at the exact
+ instruction reading the watched memory.</para>
+ <para> It is better to avoid using hardware watchpoint of not
+ addressable (yet) memory: in such a case, gdb will fallback to
+ extremely slow software watchpoints. Also, if you do not quit gdb
+ between two debugging sessions, the hardware watchpoints of the
+ previous sessions will be re-inserted as software watchpoints if
+ the watched memory zone is not addressable at program startup.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para> Stepping inside shared libraries on ARM.</para>
+ <para> For a not (yet?) clear reason, stepping inside a shared
+ library on ARM might fail. The bypass is to use the ldd command
+ to find the list of shared libraries and their loading address
+ and inform gdb of the loading address using the gdb command
+ 'add-symbol-file'. Example (for a ./p executable):
+ <programlisting><![CDATA[
+(gdb) shell ldd ./p
+ libc.so.6 => /lib/libc.so.6 (0x4002c000)
+ /lib/ld-linux.so.3 (0x40000000)
+(gdb) add-symbol-file /lib/libc.so.6 0x4002c000
+add symbol table from file "/lib/libc.so.6" at
+ .text_addr = 0x4002c000
+(y or n) y
+Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
+(gdb)
+]]></programlisting>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para> gdb version needed for ARM and PPC32/64.</para>
+ <para> You must use a gdb version which is able to read XML
+ target description sent by gdbserver (this is the standard setup
+ if the gdb was configured on a computer with the expat
+ library). If your gdb was not configured with XML support, it
+ will report an error message when using the target
+ command. Debugging will not work because gdb will then not be
+ able to fetch the registers from the Valgrind gdbserver.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para> Stack unwinding on PPC32/PPC64. </para>
+ <para> On PPC32/PPC64, stack unwinding for leaf functions
+ (i.e. functions not calling other functions) does work properly
+ only with <option>--vex-iropt-precise-memory-exns=yes</option>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para> Breakpoint encountered multiple times. </para>
+ <para> Some instructions (e.g. the x86 "rep movsb")
+ are translated by Valgrind using a loop. If a breakpoint is placed
+ on such an instruction, the breakpoint will be encountered
+ multiple times (i.e. once for each step of the "implicit" loop
+ implementing the instruction).
+ </para>
+ </listitem>
+
+ <listitem>
+ <para> Execution of Inferior function calls by the Valgrind
+ gdbserver. </para>
+
+ <para> gdb allows the user to "call" functions inside the process
+ being debugged. Such calls are named 'Inferior calls' in the gdb
+ terminology. A typical usage of an 'Inferior call' is to execute
+ a function that outputs a readable image of a complex data
+ structure. To make an Inferior call, use the gdb 'print' command
+ followed by the function to call and its arguments. As an
+ example, the following gdb command causes an Inferior call to the
+ libc printf function to be executed by (and inside) the process
+ being debugged:
+ </para>
+ <programlisting><![CDATA[
+(gdb) p printf("process being debugged has pid %d\n", getpid())
+$5 = 36
+(gdb)
+]]></programlisting>
+
+ <para>The Valgrind gdbserver accepts Inferior function
+ calls. During Inferior calls, the Valgrind tool will report
+ errors as usual. If you do not want to have such errors stopping
+ the execution of the Inferior call, you can use 'vg.set
+ vgdb-error' to set a big value before the call, and reset the
+ value after the Inferior call.</para>
+
+ <para>To execute Inferior calls, gdb changes registers such as
+ the program counter, and then continues the execution of the
+ program. In a multi-thread program, all threads are continued,
+ not only the thread instructed to make an Inferior call. If
+ another thread reports an error or encounters a break, the
+ evaluation of the Inferior call is abandonned.</para>
+
+ <para> Note that Inferior function calls is a powerful gdb
+ functionality but it has to be used with caution. For example, if
+ the program being debugged is stopped inside the function printf,
+ 'forcing' a recursive call to printf via an Inferior call will
+ very probably create problems. The Valgrind tool might also add
+ another level of complexity to Inferior calls, e.g. by reporting
+ tool errors during the Inferior call or due to the
+ instrumentation done.
+ </para>
+
+ </listitem>
+
+ <listitem>
+ <para>Connecting to or interrupting a Valgrind process blocked in
+ a system call.</para>
+
+ <para> Connecting to or interrupting a Valgrind process blocked
+ in a system call is depending on ptrace system call, which might
+ be disabled on your kernel. </para>
+
+ <para> At regular interval, after having executed some basic
+ blocks, the Valgrind scheduler checks if some input is to be
+ handled by the Valgrind gdbserver. However, this check is only
+ done if at least one thread of the process is executing (enough)
+ basic blocks. If all the threads of the process are blocked in a
+ system call, then no basic blocks are being executed, and the
+ Valgrind scheduler will not invoke the Valgrind gdbserver. In
+ such a case, the vgdb relay application will 'force' the Valgrind
+ gdbserver to be invoked, without the intervention of the Valgrind
+ scheduler.
+ </para>
+
+ <para> Such forced invocation of the Valgrind gdbserver is
+ implemented by vgdb using ptrace system calls. On a properly
+ implemented kernel, the ptrace calls done by vgdb will not
+ influence the behaviour of the program running under Valgrind. In
+ case of unexpected impact, giving the option --max-invoke-ms=0 to
+ the vgdb relay application will disable the usage of ptrace
+ system call. The consequence of disabling ptrace system call in
+ vgdb is that a Valgrind process blocked in a system call cannot
+ be waken up or interrupted from gdb till it executes (enough)
+ basic blocks to let the scheduler poll invoke the gdbserver..
+ </para>
+ <para>When ptrace is disabled in vgdb, you might increase the
+ responsiveness of the Valgrind gdbserver to commands or
+ interrupts by giving a lower value to the option --vgdb-poll: if
+ your application is most of the time blocked in a system call,
+ using a very low value for vgdb-poll will cause a faster
+ invocation of gdbserver. As the gdbserver poll done by the
+ scheduler is very efficient, the more frequent check by the
+ scheduler should not cause significant performance degradation.
+ </para>
+ <para>When ptrace is disabled in vgdb, a query packet sent by gdb
+ might take a significant time to be handled by the Valgrind
+ gdbserver. In such a case, gdb might encounter a protocol
+ timeout. To avoid having gdb encountering such a timeout error,
+ you can increase the value of this timeout by using the gdb
+ command 'set remotetimeout'.
+ </para>
+
+ <para> Ubuntu version >= 10.10 can also restrict the scope of
+ ptrace to the children of the process calling ptrace. As the
+ Valgrind process is not a child of vgdb, such restricted scope
+ causes ptrace system call to fail. To avoid that, when Valgrind
+ gdbserver receives the first packet from a vgdb, it calls
+ prctl(PR_SET_PTRACER, vgdb_pid, 0, 0, 0) to ensure vgdb can use
+ ptrace. Once vgdb_pid has been set as ptracer, vgdb can then
+ properly force the invocation of Valgrind gdbserver when
+ needed. To ensure the vgdb is set as ptracer before the Valgrind
+ process could be blocked in a system call, connect your gdb to
+ the Valgrind gdbserver at startup (i.e. use --vgdb-error=0).
+ Note that this 'set ptracer' is not solving the problem for the
+ connection of a standalone vgdb: the first command to be sent by
+ a standalone vgdb must wake up the Valgrind process before
+ Valgrind gdbserver will set vgdb as ptracer.
+ </para>
+
+ <para> Unblocking a process blocked in a system call is
+ not implemented on Darwin. So, waiting for vgdb on Darwin to
+ be enhanced, you cannot connect/interrupt a process blocked
+ in a system call on Darwin.
+ </para>
+
+ </listitem>
+
+ <listitem>
+ <para> Changing registers of a thread.</para>
+ <para> The Valgrind gdbserver only accepts to modify the values
+ of the registers of a thread when the thread is in status
+ Runnable or Yielding. In other states (typically, WaitSys), changing
+ registers values will not be accepted. This among others ensures
+ that Inferior calls are not executed for a thread which is in a
+ system call : the Valgrind gdbserver does not implement system
+ call restart.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para> gdb functionalities not supported.</para>
+ <para> gdb provides an awful lot of debugging functionalities.
+ At least the following are not supported: reversible debugging,
+ tracepoints.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para> Unknown limitations or problems.</para>
+ <para> The combination of gdb, Valgrind and the Valgrind
+ gdbserver has for sure some still unknown other
+ limitations/problems but we do not know about these unknown
+ limitations/problems :). If you encounter such (annoying)
+ limitations or problems, feel free to report a bug. But first
+ verify if the limitation or problem is not inherent to gdb or the
+ gdb remote protocol e.g. by checking the behaviour with the
+ standard gdbserver part of the gdb package.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="manual-core.vgdb"
+ xreflabel="vgdb">
+<title>vgdb command line options</title>
+<para> Usage: vgdb [OPTION]... [[-c] COMMAND]...</para>
+
+<para> vgdb (Valgrind to gdb) has two usages:</para>
+<orderedlist>
+ <listitem id="manual-core.vgdb-standalone" xreflabel="vgdb standalone">
+ <para>As a standalone utility, it is used from a shell command
+ line to send monitor commands to a process running under
+ Valgrind. For this usage, the vgdb OPTION(s) must be followed by
+ the monitor command to send. To send more than one command,
+ separate them with the -c option.
+ </para>
+ </listitem>
+
+ <listitem id="manual-core.vgdb-relay" xreflabel="vgdb relay">
+ <para>In combination with gdb 'target remote |' command, it is
+ used as the relay application between gdb and the Valgrind
+ gdbserver. For this usage, only OPTION(s) can be given, no
+ command can be given.
+ </para>
+ </listitem>
+
+</orderedlist>
+
+<para><computeroutput>vgdb</computeroutput> accepts the following
+options:</para>
+<itemizedlist>
+ <listitem>
+ <para><option>--pid=<number></option>: specifies the pid of
+ the process to which vgdb must connect to. This option is useful
+ in case more than one Valgrind gdbserver can be connected to. If
+ --pid argument is not given and multiple Valgrind gdbserver
+ processes are running, vgdb will report the list of such processes
+ and then exit.</para>
+ </listitem>
+
+ <listitem>
+ <para><option>--vgdb-prefix</option> must be given to both
+ Valgrind and vgdb utility if you want to change the default prefix
+ for the FIFOs communication between the Valgrind gdbserver and
+ vgdb. </para>
+ </listitem>
+
+ <listitem>
+ <para><option>--max-invoke-ms=<number></option> gives the
+ number of milli-seconds after which vgdb will force the invocation
+ of gdbserver embedded in valgrind. Default value is 100
+ milli-seconds. A value of 0 disables the forced invocation.
+ </para>
+
+ <para>If you specify a big value here, you might need to increase
+ the gdb remote timeout. The default value of the gdb remotetimeout
+ is 2 seconds. You should ensure that the gdb remotetimeout (in
+ seconds) is bigger than the max-invoke-ms value. For example, for
+ a 5000 --max-invoke-ms, the following gdb command will set a value
+ big enough:
+ <screen><![CDATA[
+ (gdb) set remotetimeout 6
+ ]]></screen>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para><option>--wait=<number></option> instructs vgdb to
+ check during the specified number of seconds if a Valgrind
+ gdbserver can be found. This allows to start a vgdb before the
+ Valgrind gdbserver is started. This option will be more useful in
+ combination with a --vgdb-prefix unique for the process you want
+ to wait for. Also, if you use the --wait argument in the gdb
+ 'target remote' command, you must set the gdb remotetimeout to a
+ value bigger than the --wait argument value. See option
+ --max-invoke-ms for an example of setting this remotetimeout
+ value.</para>
+ </listitem>
+
+ <listitem>
+ <para><option>-c</option> To give more than one command, separate
+ the commands by an option -c. Example:
+ <screen><![CDATA[
+vgdb vg.set log_output -c mc.leak_check any
+]]></screen></para>
+ </listitem>
+
+ <listitem>
+ <para><option>-d</option> instructs vgdb to produce debugging
+ output. Give multiple -d args for more debug info.</para>
+ </listitem>
+
+ <listitem>
+ <para><option>-D</option> instructs vgdb to show the state of the
+ shared memory used by the Valgrind gdbserver. vgdb will exit after
+ having shown the Valgrind gdbserver shared memory state.</para>
+ </listitem>
+</itemizedlist>
+
+</sect1>
+
+
+<sect1 id="manual-core.valgrind-monitor-commands"
+ xreflabel="Valgrind monitor commands">
+<title>Valgrind monitor commands</title>
+
+<para>The Valgrind monitor commands are available whatever the
+tool. They can be sent either from a shell command line (using a
+standalone vgdb) or from gdb (using the gdb 'monitor' command).</para>
+
+<itemizedlist>
+ <listitem>
+ <para><varname>help [debug]</varname> instructs Valgrind gdbserver
+ to give the list of all monitor commands of the Valgrind core and
+ of the tool. The optional 'debug' argument tells to also give help
+ for the monitor commands aimed at Valgrind internals debugging.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.info all_errors</varname> shows all errors found
+ so far.</para>
+ </listitem>
+ <listitem>
+ <para><varname>vg.info last_error</varname> shows the last error
+ found.</para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.info n_errs_found</varname> shows the nr of
+ errors found so far and the current value of the --vgdb-error
+ argument.</para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.set {gdb_output | log_output |
+ mixed_output}</varname> allows to redirect the Valgrind output
+ (e.g. the errors detected by the tool). By default, the setting is
+ mixed_output. </para>
+
+ <para>With mixed_output, the Valgrind output goes to the Valgrind
+ log (typically stderr) while the output of the interactive gdb
+ monitor commands (e.g. vg.info last_error) is displayed by
+ gdb.</para>
+
+ <para>With gdb_output, both the Valgrind output and the
+ interactive gdb monitor commands output is displayed by
+ gdb.</para>
+
+ <para>With log_output, both the Valgrind output and the
+ interactive gdb monitor commands output go to the Valgrind
+ log.</para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.wait [ms (default 0)]</varname> instructs
+ Valgrind gdbserver to sleep 'ms' milli-seconds and then
+ continue. When sent from a standalone vgdb, if this is the last
+ command, the Valgrind process will continue the execution of the
+ guest process. The typical usage of this is to use vgdb to send a
+ "no-op" command to a Valgrind gdbserver so as to continue the
+ execution of the guess process.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.kill;</varname> requests the gdbserver to kill
+ the process. This can be used from a standalone vgdb to properly
+ kill a Valgrind process which is currently expecting a vgdb
+ connection.</para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.set vgdb-error <errornr></varname>
+ dynamically changes the value of the --vgdb-error argument. A
+ typical usage of this is to start with --vgdb-error=0 on the
+ command line, then set a few breakpoints, set the vgdb-error value
+ to a huge value and continue execution.</para>
+ </listitem>
+
+</itemizedlist>
+
+<para>The below Valgrind monitor commands are useful to investigate
+the behaviour of Valgrind or Valgrind gdbserver in case of problem or
+bug.</para>
+
+<itemizedlist>
+
+ <listitem>
+ <para><varname>vg.info gdbserver_status</varname> shows the
+ gdbserver status. In case of problem (e.g. of communications),
+ this gives the value of some relevant Valgrind gdbserver internal
+ variables. Note that the variables related to breakpoints and
+ watchpoints (e.g. the nr of gdbserved addresses and the nr of
+ watchpoints) will be zero, as gdb by default removes all
+ watchpoints and breakpoints when execution stops, and re-inserts
+ them when resuming the execution of the debugged process. You can
+ change this gdb behaviour by using the gdb command 'set breakpoint
+ always-inserted on'.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.info memory</varname> shows the statistics of
+ the Valgrind heap management. If
+ option <option>--profile-heap=yes=yes</option> was given, detailed
+ statistics will be output.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.set debuglog <intvalue></varname> sets the
+ valgrind debug log level to <intvalue>. This allows to
+ dynamically change the log level of Valgrind e.g. when a problem
+ is detected.</para>
+ </listitem>
+
+ <listitem>
+ <para><varname>vg.translate <address>
+ [<traceflags>]</varname> traces the translation of the block
+ containing address with the given trace flags. The traceflags is a
+ bit pattern similar to the --trace-flags option. It can be given
+ in hexadecimal (e.g. 0x20) or decimal (e.g. 32) or in binary 1s
+ and 0s bit (e.g. 0b00100000). The default value of the traceflags
+ is 0b00100000, corresponding to 'show after instrumentation'. Note
+ that the output of this command always goes to the Valgrind
+ log. The additional bit flag 0b100000000 traces in addition the
+ gdbserver specific instrumentation. Note that bit can only enable
+ the addition of the gdbserver instrumentation in the trace.
+ Keeping this flag to 0 will not disable the tracing of the
+ gdbserver instrumentation if it is active for another reason
+ (e.g. because there is a breakpoint at this address or because
+ gdbserver is in single stepping mode). </para>
+ </listitem>
+
+</itemizedlist>
+
+</sect1>
<sect1 id="manual-core.pthreads" xreflabel="Support for Threads">
<title>Support for Threads</title>
diff --git a/gdbserver_tests/Makefile.am b/gdbserver_tests/Makefile.am
new file mode 100644
index 0000000..6d99519
--- /dev/null
+++ b/gdbserver_tests/Makefile.am
@@ -0,0 +1,86 @@
+
+include $(top_srcdir)/Makefile.tool-tests.am
+
+dist_noinst_SCRIPTS = \
+ invoker simulate_control_c make_local_links \
+ filter_gdb filter_make_empty \
+ filter_memcheck_monitor filter_stderr filter_vgdb
+
+EXTRA_DIST = \
+ mcbreak.stderrB.exp \
+ mcbreak.stderr.exp \
+ mcbreak.stdinB.gdb \
+ mcbreak.stdoutB.exp \
+ mcbreak.stdout.exp \
+ mcbreak.vgtest \
+ mcclean_after_fork.stderrB.exp \
+ mcclean_after_fork.stderr.exp \
+ mcclean_after_fork.stdinB.gdb \
+ mcclean_after_fork.stdoutB.exp \
+ mcclean_after_fork.vgtest \
+ mchelp.stderrB.exp \
+ mchelp.stderr.exp \
+ mchelp.stdoutB.exp \
+ mchelp.vgtest \
+ mcinfcallRU.stderrB.exp \
+ mcinfcallRU.stderr.exp \
+ mcinfcallRU.stdinB.gdb \
+ mcinfcallRU.vgtest \
+ mcinfcallWSRU.stderrB.exp \
+ mcinfcallWSRU.stderr.exp \
+ mcinfcallWSRU.stdinB.gdb \
+ mcinfcallWSRU.vgtest \
+ mcinvokeRU.stderrB.exp \
+ mcinvokeRU.stderr.exp \
+ mcinvokeRU.stdoutB.exp \
+ mcinvokeRU.vgtest \
+ mcinvokeWS.stderrB.exp \
+ mcinvokeWS.stderr.exp \
+ mcinvokeWS.stdoutB.exp \
+ mcinvokeWS.vgtest \
+ mcleak.stderrB.exp \
+ mcleak.stderr.exp \
+ mcleak.stdinB.gdb \
+ mcleak.stdoutB.exp \
+ mcleak.vgtest \
+ mcsignopass.stderrB.exp \
+ mcsignopass.stderr.exp \
+ mcsignopass.stdinB.gdb \
+ mcsignopass.stdoutB.exp \
+ mcsignopass.vgtest \
+ mcsigpass.stderrB.exp \
+ mcsigpass.stderr.exp \
+ mcsigpass.stdinB.gdb \
+ mcsigpass.stdoutB.exp \
+ mcsigpass.vgtest \
+ mcvabits.stderrB.exp \
+ mcvabits.stderr.exp \
+ mcvabits.stdinB.gdb \
+ mcvabits.stdoutB.exp \
+ mcvabits.vgtest \
+ mcwatchpoints.stderrB.exp \
+ mcwatchpoints.stderr.exp \
+ mcwatchpoints.stdinB.gdb \
+ mcwatchpoints.stdoutB.exp \
+ mcwatchpoints.vgtest \
+ mssnapshot.stderrB.exp \
+ mssnapshot.stderr.exp \
+ mssnapshot.stdinB.gdb \
+ mssnapshot.stdoutB.exp \
+ mssnapshot.vgtest \
+ nlcontrolc.stderrB.exp \
+ nlcontrolc.stderr.exp \
+ nlcontrolc.stdinB.gdb \
+ nlcontrolc.stdoutB.exp \
+ nlcontrolc.vgtest
+
+check_PROGRAMS = \
+ clean_after_fork \
+ sleepers \
+ t \
+ watchpoints
+
+AM_CFLAGS += $(AM_FLAG_M3264_PRI)
+AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
+
+LDADD = -lpthread
diff --git a/gdbserver_tests/README_DEVELOPPERS b/gdbserver_tests/README_DEVELOPPERS
new file mode 100644
index 0000000..e9c5e11
--- /dev/null
+++ b/gdbserver_tests/README_DEVELOPPERS
@@ -0,0 +1,203 @@
+Automatic regression tests:
+---------------------------
+The Valgrind gdbserver automatic tests are by default
+executed as part of the Valgrind test suite:
+ make regtest
+
+By default, these tests are running with the gdb found at
+Valgrind configure time.
+
+If you want to test with another gdb version, you can do:
+ make regtest GDB=/path/to/another/gdb
+
+or (to just run the gdbserver tests with another gdb):
+ cd gdbserver_tests
+ make check
+ cd ..
+ gdbserver_tests/make_local_links /path/to/another/gdb
+ perl tests/vg_regtest gdbserver_tests
+
+The minimum version to use the Valgrind gdbserver is gdb >= 6.5.
+ Previous versions do not have the 'target remote |' command. It
+ would be possible to use an older version by having a tcp relay
+ application between gdb and vgdb (or, alternatively, change vgdb
+ so that it could read/write from/to a tcpip socket rather than
+ read/write from stdin/stdout.
+
+The tests have been run on various platforms using gdb 7.2
+and on some platforms gdb 7.0.
+Some gdb tests implies a gdb >= 7.2. (these are automatically disabled
+if testing with a lower version).
+
+Some tests implies to have a vgdb "ptrace invoker" capable.
+
+The prerequisite are established during make regtest (using marker files).
+Each test verifies the prerequisite using the prereq: line.
+
+
+Naming conventions:
+-------------------
+
+The gdbserver tests are done with various Valgrind tools. A gdbserver
+test using a tool xxxxxx should have its name starting with the 'short
+two letters code' of this tool. For example, the test mcvabits.vgtest
+is using Memcheck tool, while the test mssnapshot.vgtest is using
+massif tool.
+
+Typically, a gdbserver test implies to launch two programs:
+ prog: a program running under valgrind
+ progB: another program (typically, either gdb or vgdb standalone)
+The conventions about how to specify the 2nd program in a .vgtest
+are explained in the file ../tests/vg_regtest.in
+Many tests are using gdb as progB. The input for gdb is named
+xxxxxx.stdinB.gdb.
+
+One day, we might imagine to run tests in parallel.
+So, each test gets its own '--vgdb-prefix' argument.
+This also help to avoid interactions between two successive tests:
+if a previous test stayed blocked, the vgdb of the next test
+will not report an error due to multiple possible FIFOs.
+
+
+Rational for test approach
+--------------------------
+Two approaches have been looked at:
+ V: use the 'vg_regtest' approach used by the rest of Valgrind tests
+ G: use the gdb Dejagnu test framework.
+
+Advantages of V: much simpler that G, known by Valgrind developpers,
+no additional dependency for the Valgrind build and test.
+
+Disadvantages of V: not well suited to testing of interactive tools,
+very unflexible way to test the results (everything is in "template"
+files), templates contains often irrelevant data for the test but it
+can make the test fail. After writing 13 tests, it looks like the
+template diff approach is quite fragile (e.g. changing the gdb version
+and/or the OS version influences the output of irrelevant things which
+are part of the template).
+
+A more flexible template filtering is needed.
+Maybe something like:
+The program under test is outputting its instructions to be filtered in
+special markers e.g.
+#pushf filter_addresses | filter_messages
+... some output
+#pushf an_additional_filter
+... some other output, filtered by both the first and second push
+#popf
+... here output filtered only by the first pushf
+#popf
+
+Advantages of G: much more powerful, well suited to test a gdb with a
+gdbserver, tests can verify specifically some output without being
+impacted by other output, allow to test Valgrind gdbserver with the
+all of the gdb test suite (but a lot of tests will rather test gdb
+than Valgrind gdbserver).
+
+Disadvantages: not an easy beast to understand and master, running the
+whole gdb testsuite with Valgrind gdbserver looks to be a challenge.
+
+Currently, tests are written with vg_regtest. Approach G will be looked at it
+again (e.g. to complement V) once a basic set of tests are available.
+
+
+Manual tests still to automate:
+-------------------------------
+
+Validate monitor commands abbreviation recognition
+***************************************************
+mo vg.info all_errors # to show all errors recorded so far
+mo vg.i a # the same
+mo v # must give an error: v can match vg.set vg.info
+mo vg # the same vg
+mo vg. # the same vg.
+
+test of gdb detaching or dying
+******************************
+valgrind --vgdb=yes --vgdb-error=0 --vgdb-poll=500 ./t
+
+in another window
+
+gdb ./t
+set remotetimeout 100
+target remote|vgdb
+detach valgrind continues
+target remote|vgdb reattach
+detach valgrind continues
+target remote|vgdb reattach
+monitor vg.wait no effect
+
+
+
+test of valgrind/gdb output redirection
+***************************************
+valgrind --vgdb=yes --vgdb-error=1 --vgdb-poll=500 ./t
+
+in another window
+
+**** command to type*** ****** expected behaviour
+gdb ./t
+set remotetimeout 1000
+target remote | vgdb
+mo vg.set vgdb-error 1000 # so that valgrind does stop only at error 1000 and after
+mo vg.set gdb_output # to have further valgrind errors output in gdb
+c # continue, some errors will appear
+C-c # interrupt program
+mo vg.set log_output # to set back the valgrind output to normal process log output
+mo mc.l # leak output to appear in log of process
+mo vg.set mixed_output
+mo mc.l # leak output to appear in gdb
+
+
+
+test with a big executable: firefox
+***********************************
+valgrind --vgdb=yes --vgdb-error=1000 --vgdb-poll=50000 --trace-children=yes firefox 2>&1 | tee f.out
+
+wait for some messages from the "big" firefox executable to appear.
+Then:
+
+gdb /usr/lib/firefox-3.5/firefox
+target remote | vgdb
+... then you can do various next/print/bt/bt full/break/... to see it is working
+
+bulk test with the above
+************************
+to verify there is no race condition/no reentrance problem
+between gdbserver code and valgrind:
+start firefox like in the previous test.
+In another window, do:
+ while true
+ do
+ vgdb mc.leak
+ sleep 1
+ done
+
+NB: this will make firefox run extremely slow, as it will do a leak
+search every second.
+
+
+Test of "jump" functionality
+----------------------------
+... to be done : put two breaks, jump over one.
+... same but when error is encountered
+
+
+* test with --max-invoke-ms=0
+-----------------------------
+valgrind --vgdb=yes ./t
+... wait till you see "petachounok sleeping 4 of 15
+then try to gdb it
+
+!!!! this often causes gdb to report a protocol timeout.
+use gdb set remotetimeout <a big time> to avoid that.
+The symptoms of a timeout are:
+ (gdb) tar rem|vgdb --max-invoke-ms=0
+ Remote debugging using |vgdb --max-invoke-ms=0
+ relaying data between gdb and process 2930
+ Ignoring packet error, continuing...
+ warning: unrecognized item "timeout" in "qSupported" response
+
+* tests of shadow registers
+----------------------------
+Show/modify shadow registers
diff --git a/gdbserver_tests/clean_after_fork.c b/gdbserver_tests/clean_after_fork.c
new file mode 100644
index 0000000..3ef0f67
--- /dev/null
+++ b/gdbserver_tests/clean_after_fork.c
@@ -0,0 +1,34 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+main()
+{
+ int mem = 0;
+ int pid;
+
+ pid = fork();
+ if (pid == -1) {
+ mem = 1;
+ perror("fork");
+ exit(1);
+ }
+
+ if (pid == 0) {
+ if (mem == 0)
+ exit(0);
+ else
+ exit(1);
+ } else {
+ int ret;
+ int status;
+ while((ret = waitpid(pid, &status, 0)) != pid) {
+ if (errno != EINTR) {
+ perror("waitpid");
+ exit(1);
+ }
+ }
+ mem = status;
+ }
+ if (mem == 0)
+ printf("mem is zero\n");
+}
diff --git a/gdbserver_tests/filter_gdb b/gdbserver_tests/filter_gdb
new file mode 100755
index 0000000..9dd7a3b
--- /dev/null
+++ b/gdbserver_tests/filter_gdb
@@ -0,0 +1,95 @@
+#! /bin/sh
+
+# filter the output of gdb.
+
+dir=`dirname $0`
+
+$dir/filter_stderr |
+
+# Anonymise addresses
+$dir/../tests/filter_addresses |
+
+# memcheck stuff
+$dir/filter_memcheck_monitor |
+
+
+# Anonymise or remove :
+# initial tty control character sent by gdb 7.0
+# remove missing debuginfos
+# vgdb message
+# pid numbers
+# Thread numbers
+# delete thread switches
+# info threads output (e.g. which thread is running and syscall)
+# delete Reading symbols file lines
+# delete Loaded symbols file lines
+# initial break message
+# remove gdb prompts.
+# remove gdb continuation prompts.
+# remove gdb done prompts.
+# a 'general' system calls stack trace part
+# a more specialised system call select stack trace part
+# (on 32 bits, we have an int_80, on 64 bits, directly select)
+# and yet another (gdb 7.0 way) to get a system call
+# and yet another (gdb 7.0 arm way) to get a system call
+# and cleanup some lines for a system call (on ubuntu 10 64 bits)
+# (pay attention : there are tab characters there in)
+# + yet another way to get a select system call
+# which registers can't be modified
+# special transform for arm/ppc watchpoints which have an additional address
+# at the beginning
+# special transform for backtrace of arm division by zero which gives
+# a location in the nptl lib rather than our sources (same as
+# standard gdb gdbserver) gdb 7.0
+# same special transform but for gdb 7.2
+# delete lines telling that some memory can't be accessed: this is
+# a.o. produced by gdb 7.2 on arm (same with standard gdbserver)
+# delete empty lines (the last line (only made of prompts) sometimes
+# finishes with a new line, sometimes not ???).
+sed -e 's/^\[?1034hReading symbols/Reading symbols/' \
+ -e '/Missing separate debuginfos, use: debuginfo-install/d' \
+ -e 's/\(relaying data between gdb and process \)[0-9][0-9]*/\1..../' \
+ -e 's/pid [0-9][0-9]*/pid ..../g' \
+ -e 's/Thread [0-9][0-9]*/Thread ..../g' \
+ -e '/\[Switching to Thread ....\]/d' \
+ -e 's/^\([ \* ] [0-9] Thread .... (tid [0-9] VgTs_WaitSys) 0x........ in\).*$/\1 syscall .../' \
+ -e 's/#[0-9]\( 0x........ in sleeper_or_burner\)/#.\1/' \
+ -e '/^Reading symbols from .*\.\.\.done\./d' \
+ -e '/^Loaded symbols for .*$/d' \
+ -e '1,8s/_start () from [^ ][^ ]*/_start () from ...start file.../' \
+ -e '1,8s/\(0x........ in\) ?? () from \/lib\/ld-linux\.so\../\1 _start () from ...start file.../' \
+ -e '1,8s/\(0x........ in\) ?? () from \/lib\/ld\.so\../\1 _start () from ...start file.../' \
+ -e '1,8s/\(0x........ in\) ?? () from \/lib64\/ld64\.so\../\1 _start () from ...start file.../' \
+ -e '1,8s/\(0x........ in\) ?? () from \/lib64\/ld-linux-x86-64\.so\../\1 _start () from ...start file.../' \
+ -e '1,8s/\(0x........ in\) ?? ()$/\1 _start () from ...start file.../' \
+ -e 's/(gdb) //g' \
+ -e 's/^>[> ]*//' \
+ -e '/^done\.$/d' \
+ -e 's/in _dl_sysinfo_int80 () from \/lib\/ld-linux.so.*/in syscall .../' \
+ -e 's/in _dl_sysinfo_int80 ()/in syscall .../' \
+ -e '/^ from \/lib\/ld-linux.so.*$/d' \
+ -e 's/\(0x........\) in ?? () from \/lib\/ld-linux\.so\../\1 in syscall .../' \
+ -e 's/\(0x........\) in ?? () from \/lib64\/tls\/libc\.so\../\1 in syscall .../' \
+ -e 's/in \(.__\)\{0,1\}select () from \/.*$/in syscall .../' \
+ -e '/^ from \/lib\/libc.so.*$/d' \
+ -e 's/in select ()$/in syscall .../' \
+ -e 's/in select () at \.\.\/sysdeps\/unix\/syscall-template\.S.*$/in syscall .../' \
+ -e '/^[ ]*at \.\.\/sysdeps\/unix\/syscall-template\.S/d' \
+ -e '/^[ ]*in \.\.\/sysdeps\/unix\/syscall-template\.S/d' \
+ -e '/^[1-9][0-9]*[ ]*\.\.\/sysdeps\/unix\/syscall-template\.S/d' \
+ -e '/^[1-9][0-9]*[ ]in *\.\.\/sysdeps\/unix\/syscall-template\.S/d' \
+ -e 's/\(Could not write register \)".*"/\1 "xxx"/' \
+ -e 's/\(ERROR changing register \).*$/\1 xxx regno y/' \
+ -e 's/0x........ in \(main (argc=1, argv=0x........) at watchpoints.c:[24][3689]\)/\1/' \
+ -e 's/0x........ in \(main () at clean_after_fork.c:32\)/\1/' \
+ -e 's/0x........ in \*__GI_raise (sig=8)/0x........ in test4 () at faultstatus.c:120/' \
+ -e 's/ at ..\/nptl\/sysdeps\/unix\/sysv\/linux\/raise.c:[0-9]*/120 volatile int v = 44\/zero();/' \
+ -e '/ in ..\/nptl\/sysdeps\/unix\/sysv\/linux\/raise.c/d' \
+ -e 's/0x........ in \.\{0,1\}raise () from \/lib[0-9]\{0,2\}\/libc\.so\../0x........ in test4 () at faultstatus.c:120\n120 volatile int v = 44\/zero();'/ \
+ -e '/Cannot access memory at address 0x......../d' \
+ -e '/^$/d' |
+
+# join together two lines that gdb 7.1 splits in two (???)
+# (in a separate sed, as the below influences the behaviour of the other expressions)
+sed -e :a -e '$!N;s/\n at sleepers.c:39/ at sleepers.c:39/;ta' -e 'P;D'
+
diff --git a/gdbserver_tests/filter_make_empty b/gdbserver_tests/filter_make_empty
new file mode 100755
index 0000000..f22998b
--- /dev/null
+++ b/gdbserver_tests/filter_make_empty
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+# A filter filtering everything.
+
+# To still allow to see what is happening, the content
+# is copied to a file, together with date and process.
+
+ps -lf -p $PPID >> garbage.filtered.out
+date >> garbage.filtered.out
+cat >> garbage.filtered.out
diff --git a/gdbserver_tests/filter_memcheck_monitor b/gdbserver_tests/filter_memcheck_monitor
new file mode 100755
index 0000000..9a50336
--- /dev/null
+++ b/gdbserver_tests/filter_memcheck_monitor
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+# used to filter memcheck output shown by vgdb.
+
+dir=`dirname $0`
+
+$dir/../memcheck/tests/filter_stderr |
+
+# filter vgdb messages
+$dir/filter_vgdb |
+
+
+# Bypass a s390x kernel bug which makes faultstatus test3 fail. In our case, we are
+# not interested in checking the si_code, but rather the signal passing
+# in mcsig(no)pass
+sed -e 's/Test 3: FAIL: expected si_code==2, not 128/Test 3: PASS/'
diff --git a/gdbserver_tests/filter_stderr b/gdbserver_tests/filter_stderr
new file mode 100755
index 0000000..1d26ab7
--- /dev/null
+++ b/gdbserver_tests/filter_stderr
@@ -0,0 +1,6 @@
+#! /bin/sh
+
+dir=`dirname $0`
+
+$dir/../tests/filter_stderr_basic |
+sed -e '/^Copyright (C) /d'
diff --git a/gdbserver_tests/filter_vgdb b/gdbserver_tests/filter_vgdb
new file mode 100755
index 0000000..71b3511
--- /dev/null
+++ b/gdbserver_tests/filter_vgdb
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+dir=`dirname $0`
+
+$dir/filter_stderr |
+
+# Anonymise addresses
+$dir/../tests/filter_addresses |
+
+# filter vgdb process id, pid
+# gdb 7.2 sometimes tries to access address 0x0
+# (same as with standard gdbserver)
+sed -e 's/\(relaying data between gdb and process \)[0-9][0-9]*/\1..../' \
+ -e 's/\(sending command .* to pid \)[0-9][0-9]*/\1..../' \
+ -e '/Cannot access memory at address 0x......../d'
diff --git a/gdbserver_tests/invoker b/gdbserver_tests/invoker
new file mode 100755
index 0000000..b9fba58
--- /dev/null
+++ b/gdbserver_tests/invoker
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# invoker is used to test the invocation of gdbserver.
+# The first argument is the nr of times vgdb has to be called.
+# rest of args are given to vgdb
+# At the end of the loop, an additional call is done
+# but adding " -c vg.kill" to kill Valgrind process.
+
+LOOPS=$1
+shift
+
+i=0
+while [ $i -lt $LOOPS ]
+do
+ ./vgdb "$@"
+ i=`expr $i + 1`
+done
+
+./vgdb "$@" -c vg.kill
diff --git a/gdbserver_tests/make_local_links b/gdbserver_tests/make_local_links
new file mode 100755
index 0000000..f7291d6
--- /dev/null
+++ b/gdbserver_tests/make_local_links
@@ -0,0 +1,58 @@
+#! /bin/sh
+
+# (must be called from the valgrind top source dir).
+#
+# Make local links in the gdbserver_tests directory
+# so that tests needing gdb can be disabled if
+# a tool old version of gdb is provided or if no gdb is
+# provided.
+#
+# The vgdb link is needed either for gdb tests
+# or for standalone vgdb tests.
+
+ln -f -s $1 gdbserver_tests/gdb
+if [ -x gdbserver_tests/gdb ]
+then
+ # Try to extract the gdb version.
+ VERSIONLINE=`gdbserver_tests/gdb --version | head -1`
+ VERSION=`echo $VERSIONLINE |
+ sed -e 's/[^0-9\.]//g' -e 's/\./ /g'`
+
+ # We need at least a 6.5 version to run any gdb test
+ VERSIONOK=`echo $VERSION |
+ awk '{ if ( ($1 >= 7) || (($1 == 6) && ($2 >= 5)) ) print "version ok"}'`
+ if [ "$VERSIONOK" = "" ]
+ then
+ echo "gdbserver tests suppressed as $1 version is not >= 6.5: " $VERSIONLINE
+ rm gdbserver_tests/gdb
+ fi
+
+ # We need at least a 7.2 version for gdb tests using eval command
+ VERSIONOK=`echo $VERSION |
+ awk '{ if ( ($1 >= 8) || (($1 == 7) && ($2 >= 2)) ) print "version ok"}'`
+ if [ "$VERSIONOK" = "" ]
+ then
+ echo "gdbserver eval tests suppressed as $1 version is not >= 7.2: " $VERSIONLINE
+ rm -f gdbserver_tests/gdb.eval
+ else
+ touch gdbserver_tests/gdb.eval
+ fi
+else
+ echo "gdbserver gdb tests suppressed as $1 is not executable"
+fi
+
+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' \
+ -e 'kernel restricts ptrace invoker' > /dev/null
+then
+ rm -f gdbserver_tests/vgdb.ptraceinvoker
+else
+ touch gdbserver_tests/vgdb.ptraceinvoker
+fi
+
+# cleanup the possibly big garbage previously collected output
+rm -f gdbserver_tests/garbage.filtered.out
diff --git a/gdbserver_tests/mcbreak.stderr.exp b/gdbserver_tests/mcbreak.stderr.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mcbreak.stderr.exp
diff --git a/gdbserver_tests/mcbreak.stderrB.exp b/gdbserver_tests/mcbreak.stderrB.exp
new file mode 100644
index 0000000..d5eaecf
--- /dev/null
+++ b/gdbserver_tests/mcbreak.stderrB.exp
@@ -0,0 +1,7 @@
+relaying data between gdb and process ....
+vgdb-error value changed from 0 to 999999
+vgdb-error value changed from 999999 to 0
+n_errs_found 1 (vgdb-error 0)
+vgdb-error value changed from 0 to 0
+monitor command request to kill this process
+Remote connection closed
diff --git a/gdbserver_tests/mcbreak.stdinB.gdb b/gdbserver_tests/mcbreak.stdinB.gdb
new file mode 100644
index 0000000..a154925
--- /dev/null
+++ b/gdbserver_tests/mcbreak.stdinB.gdb
@@ -0,0 +1,68 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcbreak
+monitor vg.set vgdb-error 999999
+#
+define checkstep
+ set $old_pc=$pc
+ step
+ if $old_pc == $pc
+ echo Bizarre the oldpc has not changed after step\n
+ print $oldpc
+ print $pc
+ else
+ echo old_pc has changed after step\n
+ end
+end
+#
+# break1 and break2
+break t.c:112
+break t.c:117
+#
+continue
+# first break encountered.
+checkstep
+checkstep
+checkstep
+#
+monitor vg.set vgdb-error 0
+#
+next
+print whoami("first")
+print undefined
+print i
+checkstep
+checkstep
+next
+print whoami("second")
+print undefined
+print i
+next
+print whoami("third")
+print undefined
+print i
+next
+print whoami("fourth")
+print undefined
+print i
+# modify sleeps so as to have a shorter test:
+print sleeps=1
+#
+print whoami("after next: inferior call pushed from mcbreak.stdinB.gdb")
+continue
+#
+# encountered second break
+step
+finish
+# delete all breaks
+delete
+continue
+monitor vg.info n_errs_found
+# inferior call "in the middle" of an instruction is not working at least
+# on all platforms, so comment the below.
+# print whoami("after error: inferior call pushed from mcbreak.stdinB.gdb")
+checkstep
+monitor vg.set vgdb-error 0
+continue
+# stop the process a.o. to avoid non deterministic output
+monitor vg.kill
+quit
diff --git a/gdbserver_tests/mcbreak.stdout.exp b/gdbserver_tests/mcbreak.stdout.exp
new file mode 100644
index 0000000..8907c6f
--- /dev/null
+++ b/gdbserver_tests/mcbreak.stdout.exp
@@ -0,0 +1,8 @@
+pid .... Thread .... first
+pid .... Thread .... second
+pid .... Thread .... third
+pid .... Thread .... fourth
+pid .... Thread .... after next: inferior call pushed from mcbreak.stdinB.gdb
+pid .... Thread .... called from level
+called from level int_und is not zero
+pid .... Thread .... called from main
diff --git a/gdbserver_tests/mcbreak.stdoutB.exp b/gdbserver_tests/mcbreak.stdoutB.exp
new file mode 100644
index 0000000..2c51b39
--- /dev/null
+++ b/gdbserver_tests/mcbreak.stdoutB.exp
@@ -0,0 +1,56 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcbreak
+0x........ in _start () from ...start file...
+Breakpoint 1 at 0x........: file t.c, line 112.
+Breakpoint 2 at 0x........: file t.c, line 117.
+Continuing.
+Breakpoint 1, main (argc=1, argv=0x........) at t.c:112
+112 breakme(__LINE__); //break1
+breakme (line=112) at t.c:100
+100 if (line > 1000)
+old_pc has changed after step
+102 }
+old_pc has changed after step
+main (argc=1, argv=0x........) at t.c:113
+113 for (i = len-1; i >= 0; i=i-2)
+old_pc has changed after step
+114 undefined[i] = undef;
+$1 = void
+$2 = "undefined"
+$3 = 8
+113 for (i = len-1; i >= 0; i=i-2)
+old_pc has changed after step
+114 undefined[i] = undef;
+old_pc has changed after step
+113 for (i = len-1; i >= 0; i=i-2)
+$4 = void
+$5 = "undefi?e?"
+$6 = 6
+114 undefined[i] = undef;
+$7 = void
+$8 = "undefi?e?"
+$9 = 4
+113 for (i = len-1; i >= 0; i=i-2)
+$10 = void
+$11 = "unde?i?e?"
+$12 = 4
+$13 = 1
+$14 = void
+Continuing.
+Breakpoint 2, main (argc=1, argv=0x........) at t.c:117
+117 breakme(__LINE__); //break2
+breakme (line=117) at t.c:100
+100 if (line > 1000)
+Run till exit from #0 breakme (line=117) at t.c:100
+main (argc=1, argv=0x........) at t.c:119
+119 if (argc > 1)
+Delete all breakpoints? (y or n) [answered Y; input not from terminal]
+Continuing.
+Program received signal SIGTRAP, Trace/breakpoint trap.
+0x........ in make_error (s=0x........ "called from level") at t.c:40
+40 if (int_und == 0)
+43 printf ("%s int_und is not zero\n", s);
+old_pc has changed after step
+Continuing.
+Program received signal SIGTRAP, Trace/breakpoint trap.
+0x........ in make_error (s=0x........ "called from main") at t.c:40
+40 if (int_und == 0)
diff --git a/gdbserver_tests/mcbreak.vgtest b/gdbserver_tests/mcbreak.vgtest
new file mode 100644
index 0000000..b79f41a
--- /dev/null
+++ b/gdbserver_tests/mcbreak.vgtest
@@ -0,0 +1,11 @@
+# test execution control (break, next, step) and inferior calls
+# when stopped on these events
+prog: t
+vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcbreak
+stdout_filter: filter_gdb
+stderr_filter: filter_make_empty
+progB: gdb
+argsB: --quiet -l 60 --nx ./t
+stdinB: mcbreak.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_memcheck_monitor
diff --git a/gdbserver_tests/mcclean_after_fork.stderr.exp b/gdbserver_tests/mcclean_after_fork.stderr.exp
new file mode 100644
index 0000000..0e8e017
--- /dev/null
+++ b/gdbserver_tests/mcclean_after_fork.stderr.exp
@@ -0,0 +1,12 @@
+
+(action at startup) vgdb me ...
+
+HEAP SUMMARY:
+ in use at exit: 0 bytes in 0 blocks
+ total heap usage: 0 allocs, 0 frees, 0 bytes allocated
+
+For a detailed leak analysis, rerun with: --leak-check=full
+
+For counts of detected and suppressed errors, rerun with: -v
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+Reset valgrind output to log (orderly_finish)
diff --git a/gdbserver_tests/mcclean_after_fork.stderrB.exp b/gdbserver_tests/mcclean_after_fork.stderrB.exp
new file mode 100644
index 0000000..995b42f
--- /dev/null
+++ b/gdbserver_tests/mcclean_after_fork.stderrB.exp
@@ -0,0 +1,4 @@
+relaying data between gdb and process ....
+vgdb-error value changed from 0 to 999999
+monitor command request to kill this process
+Remote connection closed
diff --git a/gdbserver_tests/mcclean_after_fork.stdinB.gdb b/gdbserver_tests/mcclean_after_fork.stdinB.gdb
new file mode 100644
index 0000000..145ad93
--- /dev/null
+++ b/gdbserver_tests/mcclean_after_fork.stdinB.gdb
@@ -0,0 +1,24 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcclean_after_fork
+monitor vg.set vgdb-error 999999
+#
+# put a break in main, and then a watch
+# also put breaks in code that only the child will execute.
+# These breaks should not be encountered.
+break clean_after_fork.c:9
+break clean_after_fork.c:18
+break clean_after_fork.c:20
+#
+continue
+# first break encountered.
+# put a read watchpoint on mem
+# we expect that the read watchpoint is not triggered in the child
+# (as we expect it will be cleared at fork).
+rwatch mem
+#
+continue
+#
+# we should now have encountered the read watchpoint in the parent.
+# let's kill the parent:
+monitor vg.kill
+quit
diff --git a/gdbserver_tests/mcclean_after_fork.stdoutB.exp b/gdbserver_tests/mcclean_after_fork.stdoutB.exp
new file mode 100644
index 0000000..8c6a8d3
--- /dev/null
+++ b/gdbserver_tests/mcclean_after_fork.stdoutB.exp
@@ -0,0 +1,14 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcclean_after_fork
+0x........ in _start () from ...start file...
+Breakpoint 1 at 0x........: file clean_after_fork.c, line 9.
+Breakpoint 2 at 0x........: file clean_after_fork.c, line 18.
+Breakpoint 3 at 0x........: file clean_after_fork.c, line 20.
+Continuing.
+Breakpoint 1, main () at clean_after_fork.c:9
+9 pid = fork();
+Hardware read watchpoint 4: mem
+Continuing.
+Hardware read watchpoint 4: mem
+Value = 0
+main () at clean_after_fork.c:32
+32 if (mem == 0)
diff --git a/gdbserver_tests/mcclean_after_fork.vgtest b/gdbserver_tests/mcclean_after_fork.vgtest
new file mode 100644
index 0000000..d953ed7
--- /dev/null
+++ b/gdbserver_tests/mcclean_after_fork.vgtest
@@ -0,0 +1,9 @@
+# test cleanup of break and watchpoints after fork
+prog: clean_after_fork
+vgopts: --tool=memcheck --vgdb=full --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcclean_after_fork
+stderr_filter: filter_memcheck_monitor
+progB: gdb
+argsB: --quiet -l 60 --nx ./clean_after_fork
+stdinB: mcclean_after_fork.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_memcheck_monitor
diff --git a/gdbserver_tests/mchelp.stderr.exp b/gdbserver_tests/mchelp.stderr.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mchelp.stderr.exp
diff --git a/gdbserver_tests/mchelp.stderrB.exp b/gdbserver_tests/mchelp.stderrB.exp
new file mode 100644
index 0000000..55888e3
--- /dev/null
+++ b/gdbserver_tests/mchelp.stderrB.exp
@@ -0,0 +1,5 @@
+sending command help to pid ....
+sending command help debug to pid ....
+sending command vg.kill to pid ....
+readchar: Got EOF
+error reading packet
diff --git a/gdbserver_tests/mchelp.stdoutB.exp b/gdbserver_tests/mchelp.stdoutB.exp
new file mode 100644
index 0000000..6499f33
--- /dev/null
+++ b/gdbserver_tests/mchelp.stdoutB.exp
@@ -0,0 +1,66 @@
+general valgrind monitor commands:
+ help [debug] : monitor command help. With debug: + debugging commands
+ vg.wait [<ms>] : sleep <ms> (default 0) then continue
+ vg.info all_errors : show all errors found so far
+ vg.info last_error : show last error found
+ vg.info n_errs_found : show the nr of errors found so far
+ vg.kill : kill the Valgrind process
+ vg.set gdb_output : set valgrind output to gdb
+ vg.set log_output : set valgrind output to log
+ vg.set mixed_output : set valgrind output to log, interactive output to gdb
+ vg.set vgdb-error <errornr> : debug me at error >= <errornr>
+
+memcheck monitor commands:
+ mc.get_vbits <addr> [<len>]
+ returns validity bits for <len> (or 1) bytes at <addr>
+ bit values 0 = valid, 1 = invalid, __ = unaddressable byte
+ Example: mc.get_vbits 0x........ 10
+ mc.make_memory [noaccess|undefined
+ |defined|ifaddressabledefined] <addr> [<len>]
+ mark <len> (or 1) bytes at <addr> with the given accessibility
+ mc.check_memory [addressable|defined] <addr> [<len>]
+ check that <len> (or 1) bytes at <addr> have the given accessibility
+ and outputs a description of <addr>
+ mc.leak_check [full*|summary]
+ [reachable|leakpossible*|definiteleak]
+ * = defaults
+ Examples: mc.leak_check
+ mc.leak_check any summary
+
+general valgrind monitor commands:
+ help [debug] : monitor command help. With debug: + debugging commands
+ vg.wait [<ms>] : sleep <ms> (default 0) then continue
+ vg.info all_errors : show all errors found so far
+ vg.info last_error : show last error found
+ vg.info n_errs_found : show the nr of errors found so far
+ vg.kill : kill the Valgrind process
+ vg.set gdb_output : set valgrind output to gdb
+ vg.set log_output : set valgrind output to log
+ vg.set mixed_output : set valgrind output to log, interactive output to gdb
+ vg.set vgdb-error <errornr> : debug me at error >= <errornr>
+debugging valgrind internals monitor commands:
+ vg.info gdbserver_status : show gdbserver status
+ vg.info memory : show valgrind heap memory stats
+ vg.set debuglog <level> : set valgrind debug log level to <level>
+ vg.translate <addr> [<traceflags>] : debug translation of <addr> with <traceflags>
+ (default traceflags 0b00100000 : show after instrumentation)
+ An additional flag 0b100000000 allows to show gdbserver instrumentation
+
+memcheck monitor commands:
+ mc.get_vbits <addr> [<len>]
+ returns validity bits for <len> (or 1) bytes at <addr>
+ bit values 0 = valid, 1 = invalid, __ = unaddressable byte
+ Example: mc.get_vbits 0x........ 10
+ mc.make_memory [noaccess|undefined
+ |defined|ifaddressabledefined] <addr> [<len>]
+ mark <len> (or 1) bytes at <addr> with the given accessibility
+ mc.check_memory [addressable|defined] <addr> [<len>]
+ check that <len> (or 1) bytes at <addr> have the given accessibility
+ and outputs a description of <addr>
+ mc.leak_check [full*|summary]
+ [reachable|leakpossible*|definiteleak]
+ * = defaults
+ Examples: mc.leak_check
+ mc.leak_check any summary
+
+monitor command request to kill this process
diff --git a/gdbserver_tests/mchelp.vgtest b/gdbserver_tests/mchelp.vgtest
new file mode 100644
index 0000000..9f88c49
--- /dev/null
+++ b/gdbserver_tests/mchelp.vgtest
@@ -0,0 +1,9 @@
+# test the memcheck monitor help
+prog: t
+vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mchelp
+stdout_filter: filter_make_empty
+stderr_filter: filter_make_empty
+progB: vgdb
+argsB: --wait=60 --vgdb-prefix=./vgdb-prefix-mchelp -c help -c help debug -c vg.kill
+stdoutB_filter: filter_memcheck_monitor
+stderrB_filter: filter_vgdb
diff --git a/gdbserver_tests/mcinfcallRU.stderr.exp b/gdbserver_tests/mcinfcallRU.stderr.exp
new file mode 100644
index 0000000..c1dd15b
--- /dev/null
+++ b/gdbserver_tests/mcinfcallRU.stderr.exp
@@ -0,0 +1,7 @@
+loops/sleep_ms/burn/threads_spec: 1 0 2000000000 B-B-B-B-
+Brussels ready to sleep and/or burn
+London ready to sleep and/or burn
+Petaouchnok ready to sleep and/or burn
+main ready to sleep and/or burn
+pid .... Thread .... inferior call pushed from gdb in mcinfcallRU.stdinB.gdb
+Reset valgrind output to log (orderly_finish)
diff --git a/gdbserver_tests/mcinfcallRU.stderrB.exp b/gdbserver_tests/mcinfcallRU.stderrB.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mcinfcallRU.stderrB.exp
diff --git a/gdbserver_tests/mcinfcallRU.stdinB.gdb b/gdbserver_tests/mcinfcallRU.stdinB.gdb
new file mode 100644
index 0000000..08a149b
--- /dev/null
+++ b/gdbserver_tests/mcinfcallRU.stdinB.gdb
@@ -0,0 +1,21 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcinfcallRU
+monitor vg.set vgdb-error 999999
+#
+# We will interrupt in a few seconds (be sure all tasks are in
+# Runnable/Yielding state). We need to wait enough seconds to be sure
+# Valgrind has started to execute the threads.
+# On a heavily loaded slow arm gcc compile farm system, 5 seconds
+# was not enough.
+shell ./simulate_control_c --vgdb-prefix=./vgdb-prefix-mcinfcallRU 10
+#
+continue
+info threads
+thread apply all bt full
+# Would like to call this for all threads with 'thread apply all', but due to unfair scheduling,
+# the below can either take a long time and/or have threads finished
+# before they have a chance to execute the whoami
+# thread apply all
+print whoami("inferior call pushed from gdb in mcinfcallRU.stdinB.gdb")
+monitor vg.kill
+quit
diff --git a/gdbserver_tests/mcinfcallRU.vgtest b/gdbserver_tests/mcinfcallRU.vgtest
new file mode 100644
index 0000000..957458b
--- /dev/null
+++ b/gdbserver_tests/mcinfcallRU.vgtest
@@ -0,0 +1,15 @@
+# test inferior calls when all threads are in Runnable or Yielding mode
+prog: sleepers
+args: 1 0 2000000000 B-B-B-B-
+vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcinfcallRU
+# filter_gdb to replace pid and Thread numbers.
+# Well, the test will verify we have 4 calls (for each thread)
+# but no way to check that this is effectively in the 4 different threads.
+stderr_filter: filter_gdb
+# Disable on Darwin: inferior call rejected as it cannot find malloc.
+prereq: ../tests/os_test linux
+progB: gdb
+argsB: --quiet -l 60 --nx ./sleepers
+stdinB: mcinfcallRU.stdinB.gdb
+stdoutB_filter: filter_make_empty
+stderrB_filter: filter_make_empty
diff --git a/gdbserver_tests/mcinfcallWSRU.stderr.exp b/gdbserver_tests/mcinfcallWSRU.stderr.exp
new file mode 100644
index 0000000..227f862
--- /dev/null
+++ b/gdbserver_tests/mcinfcallWSRU.stderr.exp
@@ -0,0 +1,8 @@
+loops/sleep_ms/burn/threads_spec: 100 100000000 1000000000 -S-SB-B-
+Brussels ready to sleep and/or burn
+London ready to sleep and/or burn
+Petaouchnok ready to sleep and/or burn
+main ready to sleep and/or burn
+pid .... Thread .... thread 1 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb
+pid .... Thread .... thread 4 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb
+Reset valgrind output to log (orderly_finish)
diff --git a/gdbserver_tests/mcinfcallWSRU.stderrB.exp b/gdbserver_tests/mcinfcallWSRU.stderrB.exp
new file mode 100644
index 0000000..2e954db
--- /dev/null
+++ b/gdbserver_tests/mcinfcallWSRU.stderrB.exp
@@ -0,0 +1,48 @@
+relaying data between gdb and process ....
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcinfcallWSRU
+0x........ in _start () from ...start file...
+vgdb-error value changed from 0 to 999999
+Breakpoint 1 at 0x........: file sleepers.c, line 72.
+Continuing.
+[New Thread ....]
+Breakpoint 1, sleeper_or_burner (v=0x........) at sleepers.c:72
+72 int i = 0;
+Continuing.
+[New Thread ....]
+Breakpoint 1, sleeper_or_burner (v=0x........) at sleepers.c:72
+72 int i = 0;
+Continuing.
+[New Thread ....]
+Breakpoint 1, sleeper_or_burner (v=0x........) at sleepers.c:72
+72 int i = 0;
+Continuing.
+Breakpoint 1, sleeper_or_burner (v=0x........) at sleepers.c:72
+72 int i = 0;
+Continuing.
+Program received signal SIGTRAP, Trace/breakpoint trap.
+0x........ in do_burn () at sleepers.c:39
+39 for (i = 0; i < burn; i++) loopnr++;
+[Switching to thread 1 (Thread ....)]#0 0x........ in do_burn () at sleepers.c:39
+39 for (i = 0; i < burn; i++) loopnr++;
+$1 = void
+[Switching to thread 2 (Thread ....)]#0 0x........ in syscall ...
+Could not write register "xxx"; remote failure reply 'E.
+ERROR changing register xxx regno y
+gdb commands changing registers (pc, sp, ...) (e.g. 'jump',
+set pc, calling from gdb a function in the debugged process, ...)
+can only be accepted if the thread is VgTs_Runnable or VgTs_Yielding state
+Thread status is VgTs_WaitSys
+'
+[Switching to thread 3 (Thread ....)]#0 0x........ in syscall ...
+Could not write register "xxx"; remote failure reply 'E.
+ERROR changing register xxx regno y
+gdb commands changing registers (pc, sp, ...) (e.g. 'jump',
+set pc, calling from gdb a function in the debugged process, ...)
+can only be accepted if the thread is VgTs_Runnable or VgTs_Yielding state
+Thread status is VgTs_WaitSys
+'
+[Switching to thread 4 (Thread ....)]#0 0x........ in do_burn () at sleepers.c:39
+39 for (i = 0; i < burn; i++) loopnr++;
+$2 = void
+monitor command request to kill this process
+Remote connection closed
diff --git a/gdbserver_tests/mcinfcallWSRU.stdinB.gdb b/gdbserver_tests/mcinfcallWSRU.stdinB.gdb
new file mode 100644
index 0000000..8429dde
--- /dev/null
+++ b/gdbserver_tests/mcinfcallWSRU.stdinB.gdb
@@ -0,0 +1,28 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcinfcallWSRU
+monitor vg.set vgdb-error 999999
+#
+# ensure all threads are known
+break sleeper_or_burner
+continue
+continue
+continue
+continue
+#
+# Here the 4 threads have been started.
+# We will interrupt in a few seconds (be sure all tasks are in Runnable/Yielding state
+# or in WaitSys state.
+shell ./simulate_control_c --vgdb-prefix=./vgdb-prefix-mcinfcallWSRU 10
+#
+continue
+#
+thread 1
+print whoami("thread 1 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb")
+thread 2
+print whoami("thread 2 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb")
+thread 3
+print whoami("thread 3 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb")
+thread 4
+print whoami("thread 4 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb")
+monitor vg.kill
+quit
diff --git a/gdbserver_tests/mcinfcallWSRU.vgtest b/gdbserver_tests/mcinfcallWSRU.vgtest
new file mode 100644
index 0000000..859ba17
--- /dev/null
+++ b/gdbserver_tests/mcinfcallWSRU.vgtest
@@ -0,0 +1,13 @@
+# test inferior calls when some threads are in Runnable or Yielding mode,
+# some threads are in WaitSys.
+prog: sleepers
+args: 100 100000000 1000000000 -S-SB-B-
+vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcinfcallWSRU
+# Disable on Darwin: inferior call rejected as it cannot find malloc.
+prereq: ../tests/os_test linux
+# filter_gdb to replace pid and Thread numbers.
+stderr_filter: filter_gdb
+progB: gdb
+argsB: --quiet -l 60 --nx 1>&2 ./sleepers
+stdinB: mcinfcallWSRU.stdinB.gdb
+stderrB_filter: filter_gdb
diff --git a/gdbserver_tests/mcinvokeRU.stderr.exp b/gdbserver_tests/mcinvokeRU.stderr.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mcinvokeRU.stderr.exp
diff --git a/gdbserver_tests/mcinvokeRU.stderrB.exp b/gdbserver_tests/mcinvokeRU.stderrB.exp
new file mode 100644
index 0000000..41e660b
--- /dev/null
+++ b/gdbserver_tests/mcinvokeRU.stderrB.exp
@@ -0,0 +1,14 @@
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.kill to pid ....
+readchar: Got EOF
+error reading packet
diff --git a/gdbserver_tests/mcinvokeRU.stdoutB.exp b/gdbserver_tests/mcinvokeRU.stdoutB.exp
new file mode 100644
index 0000000..e12ed90
--- /dev/null
+++ b/gdbserver_tests/mcinvokeRU.stdoutB.exp
@@ -0,0 +1,23 @@
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+monitor command request to kill this process
diff --git a/gdbserver_tests/mcinvokeRU.vgtest b/gdbserver_tests/mcinvokeRU.vgtest
new file mode 100644
index 0000000..ab933bb
--- /dev/null
+++ b/gdbserver_tests/mcinvokeRU.vgtest
@@ -0,0 +1,12 @@
+# test that vgdb can invoke a process when all threads are in Runnable or Yielding mode
+# If the test goes wrong, it might consume CPU during a long time.
+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.
+progB: invoker
+argsB: 10 --vgdb-prefix=./vgdb-prefix-mcinvokeRU --wait=60 -c vg.wait 0
+# if the --wait is not enough, the test will fail or block.
+stdoutB_filter: filter_memcheck_monitor
+stderrB_filter: filter_vgdb
diff --git a/gdbserver_tests/mcinvokeWS.stderr.exp b/gdbserver_tests/mcinvokeWS.stderr.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mcinvokeWS.stderr.exp
diff --git a/gdbserver_tests/mcinvokeWS.stderrB.exp b/gdbserver_tests/mcinvokeWS.stderrB.exp
new file mode 100644
index 0000000..41e660b
--- /dev/null
+++ b/gdbserver_tests/mcinvokeWS.stderrB.exp
@@ -0,0 +1,14 @@
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.wait 0 to pid ....
+sending command vg.kill to pid ....
+readchar: Got EOF
+error reading packet
diff --git a/gdbserver_tests/mcinvokeWS.stdoutB.exp b/gdbserver_tests/mcinvokeWS.stdoutB.exp
new file mode 100644
index 0000000..e12ed90
--- /dev/null
+++ b/gdbserver_tests/mcinvokeWS.stdoutB.exp
@@ -0,0 +1,23 @@
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+gdbserver: continuing in 0 ms ...
+gdbserver: continuing after wait ...
+monitor command request to kill this process
diff --git a/gdbserver_tests/mcinvokeWS.vgtest b/gdbserver_tests/mcinvokeWS.vgtest
new file mode 100644
index 0000000..1be48e8
--- /dev/null
+++ b/gdbserver_tests/mcinvokeWS.vgtest
@@ -0,0 +1,12 @@
+# test that vgdb can invoke a process when all threads are in WaitSys mode.
+# If the test goes wrong, it might be blocked during 10000 seconds.
+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
+progB: invoker
+argsB: 10 --vgdb-prefix=./vgdb-prefix-mcinvokeWS --wait=60 -c vg.wait 0
+# if the --wait is not enough, the test will fail or block
+stdoutB_filter: filter_memcheck_monitor
+stderrB_filter: filter_vgdb
diff --git a/gdbserver_tests/mcleak.stderr.exp b/gdbserver_tests/mcleak.stderr.exp
new file mode 100644
index 0000000..734ff4a
--- /dev/null
+++ b/gdbserver_tests/mcleak.stderr.exp
@@ -0,0 +1,40 @@
+(action at startup) vgdb me ...
+expecting details 10 bytes reachable
+10 bytes in 1 blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+expecting to have NO details
+expecting details +10 bytes lost, +21 bytes reachable
+expecting details +65 bytes reachable
+expecting to have NO details
+expecting details +10 bytes reachable
+expecting details -10 bytes reachable, +10 bytes lost
+expecting details -10 bytes lost, +10 bytes reachable
+expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable
+finished
+leaked: 32 bytes in 1 blocks
+dubious: 0 bytes in 0 blocks
+reachable: 64 bytes in 3 blocks
+suppressed: 0 bytes in 0 blocks
+10 bytes in 1 blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+21 bytes in 1 blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:23)
+ by 0x........: main (leak-delta.c:60)
+
+32 bytes in 1 blocks are definitely lost in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:28)
+ by 0x........: main (leak-delta.c:60)
+
+33 bytes in 1 blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:28)
+ by 0x........: main (leak-delta.c:60)
+
diff --git a/gdbserver_tests/mcleak.stderrB.exp b/gdbserver_tests/mcleak.stderrB.exp
new file mode 100644
index 0000000..081cde7
--- /dev/null
+++ b/gdbserver_tests/mcleak.stderrB.exp
@@ -0,0 +1,58 @@
+relaying data between gdb and process ....
+vgdb-error value changed from 0 to 999999
+10 bytes in 1 blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+10 (+10) bytes in 1 (+1) blocks are definitely lost in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+21 (+21) bytes in 1 (+1) blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:23)
+ by 0x........: main (leak-delta.c:60)
+
+65 (+65) bytes in 2 (+2) blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:28)
+ by 0x........: main (leak-delta.c:60)
+
+10 (+10) bytes in 1 (+1) blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+0 (-10) bytes in 0 (-1) blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+10 (+10) bytes in 1 (+1) blocks are definitely lost in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+0 (-10) bytes in 0 (-1) blocks are definitely lost in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+10 (+10) bytes in 1 (+1) blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:14)
+ by 0x........: main (leak-delta.c:60)
+
+32 (+32) bytes in 1 (+1) blocks are definitely lost in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:28)
+ by 0x........: main (leak-delta.c:60)
+
+33 (-32) bytes in 1 (-1) blocks are still reachable in loss record ... of ...
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: f (leak-delta.c:28)
+ by 0x........: main (leak-delta.c:60)
+
+Remote connection closed
diff --git a/gdbserver_tests/mcleak.stdinB.gdb b/gdbserver_tests/mcleak.stdinB.gdb
new file mode 100644
index 0000000..5cdcbe9
--- /dev/null
+++ b/gdbserver_tests/mcleak.stdinB.gdb
@@ -0,0 +1,75 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcleak
+monitor vg.set vgdb-error 999999
+#
+#
+# insert break:
+break breakme
+#
+# continue till each break and execute via gdb the leak search as done in the C code.
+continue
+#
+#
+# fprintf(stderr, "expecting details 10 bytes reachable\n"); fflush(stderr); breakme();
+up
+monitor mc.leak_check any reachable full
+continue
+# VALGRIND_DO_LEAK_CHECK;
+#
+# fprintf(stderr, "expecting to have NO details\n"); fflush(stderr);
+up
+monitor mc.leak_check increased reachable full
+continue
+# VALGRIND_DO_ADDED_LEAK_CHECK;
+#
+# b10--; // lose b10
+# b21 = malloc (21);
+# fprintf(stderr, "expecting details +10 bytes lost, +21 bytes reachable\n"); fflush(stderr); breakme();
+up
+monitor mc.leak_check increased reachable full
+continue
+# VALGRIND_DO_ADDED_LEAK_CHECK;
+#
+# for (i = 0; i < 2; i ++)
+# b32_33[i] = malloc (32+i);
+# fprintf(stderr, "expecting details +65 bytes reachable\n"); fflush(stderr); breakme();
+up
+monitor mc.leak_check increased reachable full
+continue
+# VALGRIND_DO_ADDED_LEAK_CHECK;
+#
+# fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme();
+up
+monitor mc.leak_check increased reachable full
+continue
+# VALGRIND_DO_ADDED_LEAK_CHECK;
+#
+# b10++;
+# fprintf(stderr, "expecting details +10 bytes reachable\n"); fflush(stderr); breakme();
+up
+monitor mc.leak_check increased reachable full
+continue
+# VALGRIND_DO_ADDED_LEAK_CHECK;
+#
+# b10--;
+# fprintf(stderr, "expecting details -10 bytes reachable, +10 bytes lost\n"); fflush(stderr); breakme();
+up
+monitor mc.leak_check changed reachable full
+continue
+# VALGRIND_DO_CHANGED_LEAK_CHECK;
+#
+# b10++;
+# fprintf(stderr, "expecting details -10 bytes lost, +10 bytes reachable\n"); fflush(stderr); breakme();
+up
+monitor mc.leak_check changed reachable full
+continue
+# VALGRIND_DO_CHANGED_LEAK_CHECK;
+#
+# b32_33[0]--;
+# fprintf(stderr, "expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable\n"); fflush(stderr); breakme();
+up
+monitor mc.leak_check changed reachable full
+continue
+# VALGRIND_DO_CHANGED_LEAK_CHECK;
+#
+quit
diff --git a/gdbserver_tests/mcleak.stdoutB.exp b/gdbserver_tests/mcleak.stdoutB.exp
new file mode 100644
index 0000000..b3e1aec
--- /dev/null
+++ b/gdbserver_tests/mcleak.stdoutB.exp
@@ -0,0 +1,49 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcleak
+0x........ in _start () from ...start file...
+Breakpoint 1 at 0x........: file leak-delta.c, line 9.
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:16
+16 fprintf(stderr, "expecting details 10 bytes reachable\n"); fflush(stderr); breakme();
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:19
+19 fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme();
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:24
+24 fprintf(stderr, "expecting details +10 bytes lost, +21 bytes reachable\n"); fflush(stderr); breakme();
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:29
+29 fprintf(stderr, "expecting details +65 bytes reachable\n"); fflush(stderr); breakme();
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:32
+32 fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme();
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:36
+36 fprintf(stderr, "expecting details +10 bytes reachable\n"); fflush(stderr); breakme();
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:40
+40 fprintf(stderr, "expecting details -10 bytes reachable, +10 bytes lost\n"); fflush(stderr); breakme();
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:44
+44 fprintf(stderr, "expecting details -10 bytes lost, +10 bytes reachable\n"); fflush(stderr); breakme();
+Continuing.
+Breakpoint 1, breakme () at leak-delta.c:9
+9 static void breakme() {};
+#1 0x........ in f () at leak-delta.c:48
+48 fprintf(stderr, "expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable\n"); fflush(stderr); breakme();
+Continuing.
diff --git a/gdbserver_tests/mcleak.vgtest b/gdbserver_tests/mcleak.vgtest
new file mode 100644
index 0000000..3cdd1ea
--- /dev/null
+++ b/gdbserver_tests/mcleak.vgtest
@@ -0,0 +1,12 @@
+# test the memcheck leak functionality.
+prog: ../memcheck/tests/leak-delta
+vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcleak -q --leak-check=yes --show-reachable=yes --leak-resolution=high
+# temorarily disabled, waiting for leak-delta test program (next patch)
+prereq: test -x ../memcheck/tests/leak-delta
+stdout_filter: filter_memcheck_monitor
+stderr_filter: filter_memcheck_monitor
+progB: gdb
+argsB: --quiet -l 60 --nx ../memcheck/tests/leak-delta
+stdinB: mcleak.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_memcheck_monitor
diff --git a/gdbserver_tests/mcsignopass.stderr.exp b/gdbserver_tests/mcsignopass.stderr.exp
new file mode 100644
index 0000000..f60f30e
--- /dev/null
+++ b/gdbserver_tests/mcsignopass.stderr.exp
@@ -0,0 +1,20 @@
+
+(action at startup) vgdb me ...
+Test 1: Invalid write of size 4
+ at 0x........: test1 (faultstatus.c:105)
+ by 0x........: main (faultstatus.c:168)
+ Address 0x........ is not stack'd, malloc'd or (recently) free'd
+
+ PASS
+Test 2: PASS
+Test 3: PASS
+Test 4: PASS
+
+HEAP SUMMARY:
+ in use at exit: 0 bytes in 0 blocks
+ total heap usage: 0 allocs, 0 frees, 0 bytes allocated
+
+For a detailed leak analysis, rerun with: --leak-check=full
+
+For counts of detected and suppressed errors, rerun with: -v
+ERROR SUMMARY: 11 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/gdbserver_tests/mcsignopass.stderrB.exp b/gdbserver_tests/mcsignopass.stderrB.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mcsignopass.stderrB.exp
diff --git a/gdbserver_tests/mcsignopass.stdinB.gdb b/gdbserver_tests/mcsignopass.stdinB.gdb
new file mode 100644
index 0000000..44a5c08
--- /dev/null
+++ b/gdbserver_tests/mcsignopass.stdinB.gdb
@@ -0,0 +1,41 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcsignopass
+monitor vg.set vgdb-error 999999
+#
+# instruct gdb to not pass (i.e. ignore) these signals.
+#
+# Trap the below signals, we make them stop and then continue.
+# For SIGSEGV, we make it continue a few times, till we pass it.
+handle SIGSEGV nopass print stop
+handle SIGBUS pass print stop
+handle SIGFPE pass print stop
+#
+continue
+#
+# SIGTRAP : caused by invalid write error detected by memcheck
+continue
+#
+# SIGSEGV can't be ignored, so it is re-signaled. We continue many times
+# to be sure it is this signal which is re-signalled. Then will pass it.
+continue
+continue
+continue
+continue
+continue
+continue
+continue
+continue
+continue
+#
+# Change handling so that we just see the 2nd SIGSEGV
+handle SIGSEGV pass print nostop
+continue
+#
+# SIGBUS will be shown and passed:
+continue
+#
+# then SIGFPE is shown and passed:
+continue
+#
+# program will exit
+quit
diff --git a/gdbserver_tests/mcsignopass.stdoutB.exp b/gdbserver_tests/mcsignopass.stdoutB.exp
new file mode 100644
index 0000000..70ff5ba
--- /dev/null
+++ b/gdbserver_tests/mcsignopass.stdoutB.exp
@@ -0,0 +1,64 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcsignopass
+0x........ in _start () from ...start file...
+Signal Stop Print Pass to program Description
+SIGSEGV Yes Yes No Segmentation fault
+Signal Stop Print Pass to program Description
+SIGBUS Yes Yes Yes Bus error
+Signal Stop Print Pass to program Description
+SIGFPE Yes Yes Yes Arithmetic exception
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Signal Stop Print Pass to program Description
+SIGSEGV No Yes Yes Segmentation fault
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+Program received signal SIGBUS, Bus error.
+0x........ in test3 () at faultstatus.c:115
+115 mapping[FILESIZE+10];
+Continuing.
+Program received signal SIGFPE, Arithmetic exception.
+0x........ in test4 () at faultstatus.c:120
+120 volatile int v = 44/zero();
+Continuing.
diff --git a/gdbserver_tests/mcsignopass.vgtest b/gdbserver_tests/mcsignopass.vgtest
new file mode 100644
index 0000000..6568c62
--- /dev/null
+++ b/gdbserver_tests/mcsignopass.vgtest
@@ -0,0 +1,15 @@
+# test the signal handling, when signals are *not* passed to the Valgrind guest.
+# We detect this two ways:
+# the gdb output will not contain the signal handling
+# faultstatus C code will report a failure for the signal not passed
+# (i.e. SIGBUG, Test 3). Other tests will be succesful, because signals
+# are eventually passed.
+prog: ../none/tests/faultstatus
+vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcsignopass
+stderr_filter: filter_memcheck_monitor
+progB: gdb
+argsB: --quiet -l 60 --nx ../none/tests/faultstatus
+stdinB: mcsignopass.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_make_empty
+
diff --git a/gdbserver_tests/mcsigpass.stderr.exp b/gdbserver_tests/mcsigpass.stderr.exp
new file mode 100644
index 0000000..af2b53a
--- /dev/null
+++ b/gdbserver_tests/mcsigpass.stderr.exp
@@ -0,0 +1,20 @@
+
+(action at startup) vgdb me ...
+Test 1: Invalid write of size 4
+ at 0x........: test1 (faultstatus.c:105)
+ by 0x........: main (faultstatus.c:168)
+ Address 0x........ is not stack'd, malloc'd or (recently) free'd
+
+ PASS
+Test 2: PASS
+Test 3: PASS
+Test 4: PASS
+
+HEAP SUMMARY:
+ in use at exit: 0 bytes in 0 blocks
+ total heap usage: 0 allocs, 0 frees, 0 bytes allocated
+
+For a detailed leak analysis, rerun with: --leak-check=full
+
+For counts of detected and suppressed errors, rerun with: -v
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
diff --git a/gdbserver_tests/mcsigpass.stderrB.exp b/gdbserver_tests/mcsigpass.stderrB.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mcsigpass.stderrB.exp
diff --git a/gdbserver_tests/mcsigpass.stdinB.gdb b/gdbserver_tests/mcsigpass.stdinB.gdb
new file mode 100644
index 0000000..ebd0cc7
--- /dev/null
+++ b/gdbserver_tests/mcsigpass.stdinB.gdb
@@ -0,0 +1,25 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcsigpass
+monitor vg.set vgdb-error 999999
+#
+# After this continue, we will receive 5 signals.
+continue
+#
+# SIGTRAP : caused by invalid write error detected by memcheck
+continue
+#
+# SIGSEGV : line 99
+continue
+#
+# SIGSEGV : line 104
+continue
+#
+# SIGBUS : line 109
+continue
+#
+# SIGFPE : line 114
+continue
+#
+# program will exit
+quit
+
diff --git a/gdbserver_tests/mcsigpass.stdoutB.exp b/gdbserver_tests/mcsigpass.stdoutB.exp
new file mode 100644
index 0000000..e924592
--- /dev/null
+++ b/gdbserver_tests/mcsigpass.stdoutB.exp
@@ -0,0 +1,19 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcsigpass
+0x........ in _start () from ...start file...
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test1 () at faultstatus.c:105
+105 *BADADDR = 'x';
+Continuing.
+Program received signal SIGSEGV, Segmentation fault.
+0x........ in test2 () at faultstatus.c:110
+110 mapping[0] = 'x';
+Continuing.
+Program received signal SIGBUS, Bus error.
+0x........ in test3 () at faultstatus.c:115
+115 mapping[FILESIZE+10];
+Continuing.
+Program received signal SIGFPE, Arithmetic exception.
+0x........ in test4 () at faultstatus.c:120
+120 volatile int v = 44/zero();
+Continuing.
diff --git a/gdbserver_tests/mcsigpass.vgtest b/gdbserver_tests/mcsigpass.vgtest
new file mode 100644
index 0000000..6cb0e3f
--- /dev/null
+++ b/gdbserver_tests/mcsigpass.vgtest
@@ -0,0 +1,10 @@
+# test the signal handling, when signals are passed to the Valgrind guest.
+prog: ../none/tests/faultstatus
+vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcsigpass
+stderr_filter: filter_memcheck_monitor
+progB: gdb
+argsB: --quiet -l 60 --nx ../none/tests/faultstatus
+stdinB: mcsigpass.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_make_empty
+
diff --git a/gdbserver_tests/mcvabits.stderr.exp b/gdbserver_tests/mcvabits.stderr.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mcvabits.stderr.exp
diff --git a/gdbserver_tests/mcvabits.stderrB.exp b/gdbserver_tests/mcvabits.stderrB.exp
new file mode 100644
index 0000000..6b4635e
--- /dev/null
+++ b/gdbserver_tests/mcvabits.stderrB.exp
@@ -0,0 +1,55 @@
+relaying data between gdb and process ....
+vgdb-error value changed from 0 to 999999
+Address 0x........ len 10 addressable
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+Address 0x........ len 10 defined
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+00000000 00000000 0000
+Address 0x........ len 10 addressable
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+Address 0x........ len 10 not defined:
+Uninitialised value at 0x........
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+ff00ff00 ff00ff00 ff00
+Address 0x........ len 10 addressable
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+Address 0x........ len 10 not defined:
+Uninitialised value at 0x........
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+ff000000 0000ff00 ff00
+Address 0x........ len 10 addressable
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+Address 0x........ len 10 not defined:
+Uninitialised value at 0x........
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+ff00ffff ffffff00 ff00
+Address 0x........ len 2 addressable
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+Address 0x........ len 2 not defined:
+Uninitialised value at 0x........
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+ff00
+Address 0x........ len 2 not addressable:
+bad address 0x........
+ Address 0x........ is 2 bytes inside data symbol "undefined"
+Address 0x........ len 2 not addressable:
+bad address 0x........
+ Address 0x........ is 2 bytes inside data symbol "undefined"
+____
+Address 0x........ len 2 has 2 bytes unaddressable
+Address 0x........ len 6 addressable
+ Address 0x........ is 4 bytes inside data symbol "undefined"
+Address 0x........ len 6 not defined:
+Uninitialised value at 0x........
+ Address 0x........ is 4 bytes inside data symbol "undefined"
+ffffff00 ff00
+Address 0x........ len 10 not addressable:
+bad address 0x........
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+Address 0x........ len 10 not addressable:
+bad address 0x........
+ Address 0x........ is 0 bytes inside data symbol "undefined"
+0000____ 00000000 0000
+Address 0x........ len 10 has 2 bytes unaddressable
+monitor command request to kill this process
+Remote connection closed
diff --git a/gdbserver_tests/mcvabits.stdinB.gdb b/gdbserver_tests/mcvabits.stdinB.gdb
new file mode 100644
index 0000000..7aa423a
--- /dev/null
+++ b/gdbserver_tests/mcvabits.stdinB.gdb
@@ -0,0 +1,73 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcvabits
+monitor vg.set vgdb-error 999999
+#
+#
+# insert break:
+break breakme
+#
+# continue till //1break:
+continue
+#
+# up to main:
+up
+#
+# print local string variables:
+print main_name
+print undefined
+# save address of undefined
+set $0xundefined = &undefined
+#
+# Verif A-bits, V-bits, Get V-bits: A,V,G [0..9]
+eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined
+eval "monitor mc.check_memory defined 0x%x 10", $0xundefined
+eval "monitor mc.get_vbits 0x%x 10", $0xundefined
+#
+# continue till //2break:
+continue
+#
+# A,V,G [0..9] after the undefinition of some bytes by executable:
+eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined
+eval "monitor mc.check_memory defined 0x%x 10", $0xundefined
+eval "monitor mc.get_vbits 0x%x 10", $0xundefined
+#
+# Redefine [2..4]
+set $0xundefined_2 = (char*)$0xundefined + 2
+eval "monitor mc.make_memory defined 0x%x 3", $0xundefined_2
+# A,V,G
+eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined
+eval "monitor mc.check_memory defined 0x%x 10", $0xundefined
+eval "monitor mc.get_vbits 0x%x 10", $0xundefined
+#
+# Undefine [2..5]
+eval "monitor mc.make_memory undefined 0x%x 4", $0xundefined_2
+# A,V,G [0..9]
+eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined
+eval "monitor mc.check_memory defined 0x%x 10", $0xundefined
+eval "monitor mc.get_vbits 0x%x 10", $0xundefined
+#
+# noaccess [2..3]
+eval "monitor mc.make_memory noaccess 0x%x 2", $0xundefined_2
+# A,V,G [0..1]
+eval "monitor mc.check_memory addressable 0x%x 2", $0xundefined
+eval "monitor mc.check_memory defined 0x%x 2", $0xundefined
+eval "monitor mc.get_vbits 0x%x 2", $0xundefined
+# A,V,G [2..3]
+eval "monitor mc.check_memory addressable 0x%x 2", $0xundefined_2
+eval "monitor mc.check_memory defined 0x%x 2", $0xundefined_2
+eval "monitor mc.get_vbits 0x%x 2", $0xundefined_2
+# A,V,G [4..9]
+set $0xundefined_4 = (char*) $0xundefined_2 + 2
+eval "monitor mc.check_memory addressable 0x%x 6", $0xundefined_4
+eval "monitor mc.check_memory defined 0x%x 6", $0xundefined_4
+eval "monitor mc.get_vbits 0x%x 6", $0xundefined_4
+#
+# ifaddressabledefined undefined[0..9]
+eval "monitor mc.make_memory ifaddressabledefined 0x%x 10", $0xundefined
+# A,V,G
+eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined
+eval "monitor mc.check_memory defined 0x%x 10", $0xundefined
+eval "monitor mc.get_vbits 0x%x 10", $0xundefined
+#
+monitor vg.kill
+quit
diff --git a/gdbserver_tests/mcvabits.stdoutB.exp b/gdbserver_tests/mcvabits.stdoutB.exp
new file mode 100644
index 0000000..68df7e1
--- /dev/null
+++ b/gdbserver_tests/mcvabits.stdoutB.exp
@@ -0,0 +1,13 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcvabits
+0x........ in _start () from ...start file...
+Breakpoint 1 at 0x........: file t.c, line 100.
+Continuing.
+Breakpoint 1, breakme (line=112) at t.c:100
+100 if (line > 1000)
+#1 0x........ in main (argc=1, argv=0x........) at t.c:112
+112 breakme(__LINE__); //break1
+$1 = 0x........ "main name"
+$2 = "undefined"
+Continuing.
+Breakpoint 1, breakme (line=117) at t.c:100
+100 if (line > 1000)
diff --git a/gdbserver_tests/mcvabits.vgtest b/gdbserver_tests/mcvabits.vgtest
new file mode 100644
index 0000000..689c26f
--- /dev/null
+++ b/gdbserver_tests/mcvabits.vgtest
@@ -0,0 +1,12 @@
+# test the memcheck V and A bits monitor functionality
+prog: t
+vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcvabits
+stdout_filter: filter_make_empty
+stderr_filter: filter_make_empty
+prereq: test -e ./gdb.eval
+progB: gdb
+argsB: --quiet -l 60 --nx ./t
+stdinB: mcvabits.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_memcheck_monitor
+
diff --git a/gdbserver_tests/mcwatchpoints.stderr.exp b/gdbserver_tests/mcwatchpoints.stderr.exp
new file mode 100644
index 0000000..b73233a
--- /dev/null
+++ b/gdbserver_tests/mcwatchpoints.stderr.exp
@@ -0,0 +1,25 @@
+
+(action at startup) vgdb me ...
+breakme function called from line 19
+before reading 0/4/8
+u: Expected value at 0
+f: Expected value at 4
+d: Expected value at 8
+before writing 0
+before writing 4
+before writing 8
+after writing 8
+value UndeFineD
+before rewriting 0
+before rewriting 4
+before rewriting 8
+value 0nde4ine8
+
+HEAP SUMMARY:
+ in use at exit: 0 bytes in 0 blocks
+ total heap usage: 0 allocs, 0 frees, 0 bytes allocated
+
+For a detailed leak analysis, rerun with: --leak-check=full
+
+For counts of detected and suppressed errors, rerun with: -v
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/gdbserver_tests/mcwatchpoints.stderrB.exp b/gdbserver_tests/mcwatchpoints.stderrB.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mcwatchpoints.stderrB.exp
diff --git a/gdbserver_tests/mcwatchpoints.stdinB.gdb b/gdbserver_tests/mcwatchpoints.stdinB.gdb
new file mode 100644
index 0000000..5ec79fa
--- /dev/null
+++ b/gdbserver_tests/mcwatchpoints.stdinB.gdb
@@ -0,0 +1,24 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcwatchpoints
+monitor vg.set vgdb-error 999999
+#
+#
+# insert break:
+break breakme
+#
+# continue till //break1:
+continue
+#
+# insert the watchpoints
+rwatch undefined[0]
+awatch undefined[4]
+watch undefined[8]
+#
+# now we should encounter 4 break points
+continue
+continue
+continue
+continue
+del
+continue
+quit
diff --git a/gdbserver_tests/mcwatchpoints.stdoutB.exp b/gdbserver_tests/mcwatchpoints.stdoutB.exp
new file mode 100644
index 0000000..7c6e09d
--- /dev/null
+++ b/gdbserver_tests/mcwatchpoints.stdoutB.exp
@@ -0,0 +1,33 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcwatchpoints
+0x........ in _start () from ...start file...
+Breakpoint 1 at 0x........: file watchpoints.c, line 7.
+Continuing.
+Breakpoint 1, breakme (line=19) at watchpoints.c:7
+7 fprintf(stderr, "breakme function called from line %d\n", line);
+Hardware read watchpoint 2: undefined[0]
+Hardware access (read/write) watchpoint 3: undefined[4]
+Hardware watchpoint 4: undefined[8]
+Continuing.
+Hardware read watchpoint 2: undefined[0]
+Value = 117 'u'
+main (argc=1, argv=0x........) at watchpoints.c:23
+23 if (undefined[0] == 'u')
+Continuing.
+Hardware access (read/write) watchpoint 3: undefined[4]
+Value = 102 'f'
+main (argc=1, argv=0x........) at watchpoints.c:28
+28 if (undefined[4] == 'f')
+Continuing.
+Hardware access (read/write) watchpoint 3: undefined[4]
+Old value = 102 'f'
+New value = 70 'F'
+main (argc=1, argv=0x........) at watchpoints.c:46
+46 fprintf(stderr, "before writing 8\n");
+Continuing.
+Hardware watchpoint 4: undefined[8]
+Old value = 100 'd'
+New value = 68 'D'
+main (argc=1, argv=0x........) at watchpoints.c:49
+49 fprintf(stderr, "after writing 8\n");
+Delete all breakpoints? (y or n) [answered Y; input not from terminal]
+Continuing.
diff --git a/gdbserver_tests/mcwatchpoints.vgtest b/gdbserver_tests/mcwatchpoints.vgtest
new file mode 100644
index 0000000..72714eb
--- /dev/null
+++ b/gdbserver_tests/mcwatchpoints.vgtest
@@ -0,0 +1,12 @@
+# test the memcheck watchpoint functionality
+# Note: we need --vgdb=full to stop at the instruction following the watchpoint.
+prog: watchpoints
+vgopts: --tool=memcheck --vgdb=full --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcwatchpoints
+stdout_filter: filter_make_empty
+stderr_filter: filter_memcheck_monitor
+progB: gdb
+argsB: --quiet -l 60 --nx ./watchpoints
+stdinB: mcwatchpoints.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_make_empty
+
diff --git a/gdbserver_tests/mssnapshot.stderr.exp b/gdbserver_tests/mssnapshot.stderr.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/mssnapshot.stderr.exp
diff --git a/gdbserver_tests/mssnapshot.stderrB.exp b/gdbserver_tests/mssnapshot.stderrB.exp
new file mode 100644
index 0000000..4b7a012
--- /dev/null
+++ b/gdbserver_tests/mssnapshot.stderrB.exp
@@ -0,0 +1,22 @@
+relaying data between gdb and process ....
+vgdb-error value changed from 0 to 999999
+general valgrind monitor commands:
+ help [debug] : monitor command help. With debug: + debugging commands
+ vg.wait [<ms>] : sleep <ms> (default 0) then continue
+ vg.info all_errors : show all errors found so far
+ vg.info last_error : show last error found
+ vg.info n_errs_found : show the nr of errors found so far
+ vg.kill : kill the Valgrind process
+ vg.set gdb_output : set valgrind output to gdb
+ vg.set log_output : set valgrind output to log
+ vg.set mixed_output : set valgrind output to log, interactive output to gdb
+ vg.set vgdb-error <errornr> : debug me at error >= <errornr>
+
+massif monitor commands:
+ ms.snapshot [<filename>] [detailed]
+ takes a snapshot and saves it in <filename>
+ default <filename> is massif.vgdb.out
+ if present, detailed argument indicates to take a detailed snapshot
+
+monitor command request to kill this process
+Remote connection closed
diff --git a/gdbserver_tests/mssnapshot.stdinB.gdb b/gdbserver_tests/mssnapshot.stdinB.gdb
new file mode 100644
index 0000000..42b56fe
--- /dev/null
+++ b/gdbserver_tests/mssnapshot.stdinB.gdb
@@ -0,0 +1,21 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mssnapshot
+monitor vg.set vgdb-error 999999
+#
+#
+# insert break:
+break main
+#
+# continue till main
+continue
+#
+# test the massif help
+monitor help
+#
+# test non detailed and detailed snapshot
+monitor ms.snapshot
+monitor ms.snapshot detailed
+#
+#
+monitor vg.kill
+quit
diff --git a/gdbserver_tests/mssnapshot.stdoutB.exp b/gdbserver_tests/mssnapshot.stdoutB.exp
new file mode 100644
index 0000000..274eab0
--- /dev/null
+++ b/gdbserver_tests/mssnapshot.stdoutB.exp
@@ -0,0 +1,6 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mssnapshot
+0x........ in _start () from ...start file...
+Breakpoint 1 at 0x........: file t.c, line 105.
+Continuing.
+Breakpoint 1, main (argc=1, argv=0x........) at t.c:105
+105 char *main_name = "main name";
diff --git a/gdbserver_tests/mssnapshot.vgtest b/gdbserver_tests/mssnapshot.vgtest
new file mode 100644
index 0000000..a11a04d
--- /dev/null
+++ b/gdbserver_tests/mssnapshot.vgtest
@@ -0,0 +1,10 @@
+# test the memcheck monitor help
+prog: t
+vgopts: --tool=massif --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mssnapshot
+stdout_filter: filter_make_empty
+stderr_filter: filter_make_empty
+progB: gdb
+argsB: --quiet -l 60 --nx ./t
+stdinB: mssnapshot.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_vgdb
diff --git a/gdbserver_tests/nlcontrolc.stderr.exp b/gdbserver_tests/nlcontrolc.stderr.exp
new file mode 100644
index 0000000..f6eef78
--- /dev/null
+++ b/gdbserver_tests/nlcontrolc.stderr.exp
@@ -0,0 +1,9 @@
+Nulgrind, the minimal Valgrind tool
+
+(action at startup) vgdb me ...
+loops/sleep_ms/burn/threads_spec: 1000000000 1000000000 1000000000 BSBSBSBS
+Brussels ready to sleep and/or burn
+London ready to sleep and/or burn
+Petaouchnok ready to sleep and/or burn
+main ready to sleep and/or burn
+
diff --git a/gdbserver_tests/nlcontrolc.stderrB.exp b/gdbserver_tests/nlcontrolc.stderrB.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gdbserver_tests/nlcontrolc.stderrB.exp
diff --git a/gdbserver_tests/nlcontrolc.stdinB.gdb b/gdbserver_tests/nlcontrolc.stdinB.gdb
new file mode 100644
index 0000000..53a8d68
--- /dev/null
+++ b/gdbserver_tests/nlcontrolc.stdinB.gdb
@@ -0,0 +1,38 @@
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-nlcontrolc
+monitor vg.set vgdb-error 999999
+#
+#
+# simulate control-c in a few seconds
+shell ./simulate_control_c --vgdb-prefix=./vgdb-prefix-nlcontrolc 10
+#
+continue
+#
+# Here, all tasks should be blocked in a loooonnnng select, all in WaitSys
+info threads
+# We will unblock them by changing their timeout argument
+# To avoid going into the frame where the timeval arg is,
+# it has been defined as global variables, as the nr
+# of calls on the stack differs between 32bits and 64bits,
+# and/or between OS.
+# ensure select finishes in a few milliseconds max:
+p t[0].tv_sec = 0
+p t[1].tv_sec = 0
+p t[2].tv_sec = 0
+p t[3].tv_sec = 0
+#
+# We will change the burning parameters in a few seconds
+shell ./simulate_control_c --vgdb-prefix=./vgdb-prefix-nlcontrolc 10
+#
+continue
+#
+# Threads are burning cpu now
+# We would like to test info threads here, but which thread are Runnable or Yielding
+# is unpredictable.
+# info threads
+p burn = 0
+p loops = 0
+p report_finished = 0
+continue
+# and the process should stop very quickly now
+quit
diff --git a/gdbserver_tests/nlcontrolc.stdoutB.exp b/gdbserver_tests/nlcontrolc.stdoutB.exp
new file mode 100644
index 0000000..a79f5d2
--- /dev/null
+++ b/gdbserver_tests/nlcontrolc.stdoutB.exp
@@ -0,0 +1,24 @@
+Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-nlcontrolc
+0x........ in _start () from ...start file...
+Continuing.
+Program received signal SIGTRAP, Trace/breakpoint trap.
+0x........ in syscall ...
+[New Thread ....]
+[New Thread ....]
+[New Thread ....]
+ 4 Thread .... (tid 4 VgTs_WaitSys) 0x........ in syscall ...
+ 3 Thread .... (tid 3 VgTs_WaitSys) 0x........ in syscall ...
+ 2 Thread .... (tid 2 VgTs_WaitSys) 0x........ in syscall ...
+* 1 Thread .... (tid 1 VgTs_WaitSys) 0x........ in syscall ...
+$1 = 0
+$2 = 0
+$3 = 0
+$4 = 0
+Continuing.
+Program received signal SIGTRAP, Trace/breakpoint trap.
+0x........ in do_burn () at sleepers.c:39
+39 for (i = 0; i < burn; i++) loopnr++;
+$5 = 0
+$6 = 0
+$7 = 0
+Continuing.
diff --git a/gdbserver_tests/nlcontrolc.vgtest b/gdbserver_tests/nlcontrolc.vgtest
new file mode 100644
index 0000000..6c05901
--- /dev/null
+++ b/gdbserver_tests/nlcontrolc.vgtest
@@ -0,0 +1,20 @@
+# test :
+# info threads valgrind specific output
+# the user can control-c an process with all threads in WaitSys
+# and modify some variables
+# the user can control-c an process with all threads in Running/Yielding
+# and modify some variables
+# sleepers is started with argument so that it will compute during ages.
+# The variable modifications means it will exit in a reasonable time.
+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 -f vgdb.ptraceinvoker
+progB: gdb
+argsB: --quiet -l 60 --nx ./sleepers
+stdinB: nlcontrolc.stdinB.gdb
+#stdoutB_filter: /bin/cat
+stdoutB_filter: filter_gdb
+#stderrB_filter: /bin/cat
+stderrB_filter: filter_make_empty
diff --git a/gdbserver_tests/simulate_control_c b/gdbserver_tests/simulate_control_c
new file mode 100755
index 0000000..cea1e59
--- /dev/null
+++ b/gdbserver_tests/simulate_control_c
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+# simulate control_c by sending SIGUSR1 to the vgdb using prefix $1 in $2 seconds
+VGDBPID=`./vgdb -D $1 2>&1 | awk '/vgdb pid/ {print $3}'`
+if [ "$VGDBPID" = "" ]
+then
+ echo "simulate_control_c could not determine the vgdb pid with " $1
+ exit 1
+fi
+(sleep $2; kill -10 $VGDBPID) &
+
diff --git a/gdbserver_tests/sleepers.c b/gdbserver_tests/sleepers.c
new file mode 100644
index 0000000..cea20cb
--- /dev/null
+++ b/gdbserver_tests/sleepers.c
@@ -0,0 +1,187 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sched.h>
+
+static int loops = 15; // each thread+main will do this amount of loop
+static int sleepms = 1000; // in each loop, will sleep "sleepms" milliseconds
+static int burn = 0; // after each sleep, will burn cpu in a tight 'burn' loop
+
+
+static pid_t gettid()
+{
+#ifdef __NR_gettid
+ return syscall(__NR_gettid);
+#else
+ return getpid();
+#endif
+}
+
+// will be invoked from gdb.
+static void whoami(char *msg)
+{
+ fprintf(stderr, "pid %d Thread %d %s\n", getpid(), gettid(), msg);
+ fflush(stderr);
+}
+
+
+static void do_burn ()
+{
+ int i;
+ int loopnr = 0;
+ // one single line for the below, to ensure interrupt on this line.
+ for (i = 0; i < burn; i++) loopnr++;
+}
+
+static int thread_ready = 0;
+static pthread_cond_t ready = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
+static void signal_ready (void)
+{
+ int rc;
+ rc = pthread_mutex_lock(&ready_mutex);
+ if (rc != 0)
+ fprintf(stderr, "signal_ready lock error %d_n", rc);
+ thread_ready = 1;
+ rc = pthread_cond_signal(&ready);
+ if (rc != 0)
+ fprintf(stderr, "signal_ready signal error %d_n", rc);
+ rc = pthread_mutex_unlock(&ready_mutex);
+ if (rc != 0)
+ fprintf(stderr, "signal_ready unlock error %d_n", rc);
+}
+
+struct spec {
+ char *name;
+ int sleep;
+ int burn;
+ int t;
+};
+static struct timeval t[4];
+static int nr_sleeper_or_burner = 0;
+static volatile int report_finished = 1;
+// set to 0 to have no finish msg (as order is non-deterministic)
+static void *sleeper_or_burner(void *v)
+{
+ int i = 0;
+ struct spec* s = (struct spec*)v;
+
+ fprintf(stderr, "%s ready to sleep and/or burn\n", s->name);
+ fflush (stderr);
+ signal_ready();
+ nr_sleeper_or_burner++;
+
+ for (i = 0; i < loops; i++) {
+ if (sleepms > 0 && s->sleep) {
+ t[s->t].tv_sec = sleepms / 1000;
+ t[s->t].tv_usec = (sleepms % 1000) * 1000;
+ select (0, NULL, NULL, NULL, &t[s->t]);
+ }
+ if (burn > 0 && s->burn)
+ do_burn();
+ }
+ if (report_finished) {
+ fprintf(stderr, "%s finished to sleep and/or burn\n", s->name);
+ fflush (stderr);
+ }
+ return NULL;
+}
+
+// wait till a thread signals it is ready
+static void wait_ready(void)
+{
+ int rc;
+ rc = pthread_mutex_lock(&ready_mutex);
+ if (rc != 0)
+ fprintf(stderr, "wait_ready lock error %d_n", rc);
+ while (! thread_ready && rc == 0) {
+ rc = pthread_cond_wait(&ready, &ready_mutex);
+ if (rc != 0)
+ fprintf(stderr, "wait_ready wait error %d_n", rc);
+ }
+ thread_ready = 0;
+ rc = pthread_mutex_unlock(&ready_mutex);
+ if (rc != 0)
+ fprintf(stderr, "wait_ready unlock error %d_n", rc);
+}
+
+// We will lock ourselves on one single cpu.
+// This bypasses the unfairness of the Valgrind scheduler
+// when a multi-cpu machine has enough cpu to run all the
+// threads wanting to burn cpu.
+static void setaffinity(void)
+{
+#ifdef VGO_linux
+ cpu_set_t single_cpu;
+ CPU_ZERO(&single_cpu);
+ CPU_SET(1, &single_cpu);
+ (void) sched_setaffinity(0, sizeof(single_cpu), &single_cpu);
+#endif
+ // GDBTD: equivalent for Darwin ?
+}
+
+int main (int argc, char *argv[])
+{
+ char *threads_spec;
+ pthread_t ebbr, egll, zzzz;
+ struct spec b, l, p, m;
+ char *some_mem = malloc(100);
+ setaffinity();
+
+ if (argc > 1)
+ loops = atoi(argv[1]);
+
+ if (argc > 2)
+ sleepms = atoi(argv[2]);
+
+ if (argc > 3)
+ burn = atoll(argv[3]);
+
+ if (argc > 4)
+ threads_spec = argv[4];
+ else
+ threads_spec = "BSBSBSBS";
+
+ fprintf(stderr, "loops/sleep_ms/burn/threads_spec: %d %d %d %s\n",
+ loops, sleepms, burn, threads_spec);
+ fflush(stderr);
+
+ b.name = "Brussels";
+ b.burn = *threads_spec++ == 'B';
+ b.sleep = *threads_spec++ == 'S';
+ b.t = 1;
+ pthread_create(&ebbr, NULL, sleeper_or_burner, &b);
+ wait_ready();
+
+ l.name = "London";
+ l.burn = *threads_spec++ == 'B';
+ l.sleep = *threads_spec++ == 'S';
+ l.t = 2;
+ pthread_create(&egll, NULL, sleeper_or_burner, &l);
+ wait_ready();
+
+ p.name = "Petaouchnok";
+ p.burn = *threads_spec++ == 'B';
+ p.sleep = *threads_spec++ == 'S';
+ p.t = 3;
+ pthread_create(&zzzz, NULL, sleeper_or_burner, &p);
+ wait_ready();
+
+ m.name = "main";
+ m.burn = *threads_spec++ == 'B';
+ m.sleep = *threads_spec++ == 'S';
+ m.t = 0;
+ sleeper_or_burner(&m);
+
+ pthread_join(ebbr, NULL);
+ pthread_join(egll, NULL);
+ pthread_join(zzzz, NULL);
+
+ return 0;
+}
diff --git a/gdbserver_tests/t.c b/gdbserver_tests/t.c
new file mode 100644
index 0000000..8600480
--- /dev/null
+++ b/gdbserver_tests/t.c
@@ -0,0 +1,153 @@
+#include <string.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include "../memcheck/memcheck.h"
+int using_threads = 0; /* test collision with a global in gdbserver */
+/* we will undefine one char on two */
+static char undefined[10] = "undefined";
+
+#define LOOPS 10000000
+static int loopmain, loopt1, loopt2;
+
+static double pi = 3.14159265358979323846264338327950288;
+
+static pid_t gettid()
+{
+#ifdef __NT_gettid
+ return syscall(__NR_gettid);
+#else
+ return getpid();
+#endif
+}
+static void whoami(char *msg)
+{
+ printf("pid %d Thread %d %s\n", getpid(), gettid(), msg); fflush(stdout);
+}
+
+static int int_und;
+static int sleeps = 15;
+static void make_error (char *s)
+{
+ char *make_error_name = "make_error name";
+ char c;
+ double pi2 = 2.0 * pi;
+ whoami(s);
+ if (int_und == 0)
+ printf ("%s int_und is zero %d\n", s, int_und);
+ else
+ printf ("%s int_und is not zero\n", s);
+ fflush(stdout);
+}
+
+static void level ()
+{
+ char *level_name = "level name";
+ make_error ("called from level");
+}
+
+static void loops (int *loopnr)
+{
+ int i, j;
+ for (i = 0; i < LOOPS; i++)
+ for (j = 0; i < LOOPS; i++)
+ *loopnr++;
+}
+
+static void *brussels_fn(void *v)
+{
+ char *brussels_name = "Brussels";
+ make_error ("called from Brussels");
+ loopt1 = 1;
+ while (! (loopt1 && loopt2 && loopmain))
+ loopt1++;
+ loops (&loopt1);
+ return NULL;
+}
+static void *london_fn(void *v)
+{
+ char *london_name = "London";
+ make_error ("called from London");
+ loopt2 = 1;
+ while (! (loopt1 && loopt2 && loopmain))
+ loopt2++;
+ loops (&loopt2);
+ sleep(10);
+ return NULL;
+}
+static void *petaouchnok_fn(void *v)
+{
+ char *petaouchnok_name = "Petaouchnok";
+ struct timeval t;
+ int i;
+ for (i = 1; i <= sleeps; i++) {
+ t.tv_sec = 5;
+ t.tv_usec = 0;
+ fprintf (stderr, "Petaouchnok sleep nr %d out of %d sleeping 5 seconds\n",
+ i, sleeps);
+ fflush(stderr);
+ select (0, NULL, NULL, NULL, &t);
+ }
+ return NULL;
+}
+static void leaf(void) {}
+static void breakme(int line)
+{
+ if (line > 1000)
+ leaf(); // ensures not leaf, as ppc unwind implies VEX iropt precise exns
+}
+int main (int argc, char *argv[])
+{
+ char *main_name = "main name";
+ pthread_t ebbr, egll, zzzz;
+ int i = 1234;
+ char undef = '?';
+ char *some_mem = malloc(100);
+ VALGRIND_MAKE_MEM_UNDEFINED(&undef, 1);
+ int len = strlen(undefined);
+ breakme(__LINE__); //break1
+ for (i = len-1; i >= 0; i=i-2)
+ undefined[i] = undef;
+ *(char*)&int_und = undef;
+
+ breakme(__LINE__); //break2
+
+ if (argc > 1)
+ sleeps = atoi(argv[1]);
+
+ level();
+ make_error ("called from main");
+
+ pthread_create(&ebbr, NULL, brussels_fn, NULL);
+ pthread_create(&egll, NULL, london_fn, NULL);
+ pthread_create(&zzzz, NULL, petaouchnok_fn, NULL);
+
+ loopmain = 1;
+ while (! (loopt1 && loopt2 && loopmain))
+ loopmain++;
+ for (i = 0; i < LOOPS; i++) {
+ loopmain++;
+
+ if (loopmain == 10000)
+ make_error ("in main loop");
+ }
+
+ pthread_join(ebbr, NULL);
+
+ make_error ("called from main (the end, before joining t3)");
+
+ pthread_join(zzzz, NULL);
+
+ if (argc > 2) {
+ for (i = 0; i < 100; i++)
+ if ((*(&undef + i*4000) == 0) || (*(&undef - i*4000) == 0)) {
+ printf ("there are some null bytes here and there %d\n", i);
+ fflush(stdout);
+ }
+ }
+ exit(0);
+}
diff --git a/gdbserver_tests/watchpoints.c b/gdbserver_tests/watchpoints.c
new file mode 100644
index 0000000..ae05d8f
--- /dev/null
+++ b/gdbserver_tests/watchpoints.c
@@ -0,0 +1,67 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static void breakme(int line)
+{
+ fprintf(stderr, "breakme function called from line %d\n", line);
+ fflush(stderr);
+}
+static char undefined[10] = "undefined";
+int main (int argc, char *argv[])
+{
+
+ /* we will test
+ read watchpoint at 0,
+ read/write watchpoints at 4
+ write watchpoints at 8 */
+
+ breakme(__LINE__); //break1
+
+ /* We verify read watchpoints are triggered at 0 and 4, not at 8 */
+ fprintf(stderr, "before reading 0/4/8\n");
+ if (undefined[0] == 'u')
+ fprintf(stderr, "u: Expected value at 0\n");
+ else
+ fprintf(stderr, "u: Unexpected value at 0\n");
+
+ if (undefined[4] == 'f')
+ fprintf(stderr, "f: Expected value at 4\n");
+ else
+ fprintf(stderr, "f: Unexpected value at 4\n");
+
+ if (undefined[8] == 'd')
+ fprintf(stderr, "d: Expected value at 8\n");
+ else
+ fprintf(stderr, "d: Unexpected value at 8\n");
+
+
+ /* We verify write watchpoints are triggered at 4 and 8, not at 0 */
+ fprintf(stderr, "before writing 0\n");
+ undefined[0] = 'U';
+
+ fprintf(stderr, "before writing 4\n");
+ undefined[4] = 'F';
+
+ fprintf(stderr, "before writing 8\n");
+ undefined[8] = 'D';
+
+ fprintf(stderr, "after writing 8\n");
+
+ /* after having remove the watchpoints, check we can read and write
+ without break. */
+ fprintf(stderr, "value %s\n", undefined);
+
+ fprintf(stderr, "before rewriting 0\n");
+ undefined[0] = '0';
+
+ fprintf(stderr, "before rewriting 4\n");
+ undefined[4] = '4';
+
+ fprintf(stderr, "before rewriting 8\n");
+ undefined[8] = '8';
+
+ fprintf(stderr, "value %s\n", undefined);
+
+ exit(0);
+}
diff --git a/include/pub_tool_gdbserver.h b/include/pub_tool_gdbserver.h
new file mode 100644
index 0000000..514bcaa
--- /dev/null
+++ b/include/pub_tool_gdbserver.h
@@ -0,0 +1,179 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Handle remote gdb protocol. pub_tool_gdbserver.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2011 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 __PUB_TOOL_GDBSERVER_H
+#define __PUB_TOOL_GDBSERVER_H
+
+#include "libvex.h"
+#include "libvex_ir.h"
+
+//--------------------------------------------------------------------
+// PURPOSE: This module provides the support to have a gdb
+// connecting to a valgrind process using remote gdb protocol. It provides
+// * A function to allow a tool (or the valgrind core) to
+// wait for a gdb to connect and then handle gdb commands.
+// Typically, this can be used to let the user debug the process
+// when valgrind reports an error.
+// * A function allowing to instrument the code to support gdb breakpoints.
+// * A function allowing the tool to support watchpoints.
+// * A utility function to help implementing the processing of the
+// gdb_monitor_command strings.
+
+
+// Function to be used by tool or coregrind to allow a gdb to connect
+// to this process.
+// Calling VG_(gdbserver) with tid > 0 means to let a debugger attach
+// to the valgrind process. gdbserver will report to gdb that the
+// process stopped in thread tid.
+// tid == 0 indicates to stop gdbserver and report to gdb
+// that the valgrind-ified process has exited.
+//--------------------------------------------------------------------
+extern void VG_(gdbserver) ( ThreadId tid );
+
+/* VG_(dyn_vgdb_error) gets its initial value from
+ VG_(clo_vgdb_error). It can be changed after initial command
+ processing in order to enable/disable the call to VG_(gdbserver) in
+ m_errormgr.c. The main reasons to change the below is either
+ because the user updates it via a monitor command or to
+ (temporarily) avoid calling gdbserver for error reporting during
+ monitor command handling.
+*/
+extern Int VG_(dyn_vgdb_error);
+
+/* defines the various kinds of breakpoints that gdbserver
+ might ask to insert/remove. Note that the below matches
+ the gdbserver protocol definition. The level of support
+ of the various breakpoint kinds depends on the tool.
+
+ For the moment, it is unclear how a tool would implement
+ hardware_breakpoint in valgrind :).
+
+ software_breakpoint implies some (small) specific
+ instrumentation to be done for gdbserver. This instrumentation
+ is implemented for all tools in m_translate.c.
+
+ write/read/access watchpoints can only be done by tools
+ which are maintaining some notion of address accessibility
+ as part of their instrumentation. watchpoints can then
+ be done by marking the watched address(es) as not accessible.
+ But instead of giving back an error (or whatever the tool
+ wants to do with unaccessible mechanism), the tool must then
+ just call gdbserver. See memcheck for an example of reusing
+ accessibility for watchpoint support.
+*/
+typedef
+ enum {
+ software_breakpoint,
+ hardware_breakpoint,
+ write_watchpoint,
+ read_watchpoint,
+ access_watchpoint } PointKind;
+extern char* VG_(ppPointKind) (PointKind kind);
+
+
+/* watchpoint support --------------------------------------*/
+/* True if one or more bytes in [addr, addr+len[ are being watched by
+ gdbserver for write or read or access.
+ In addition, VG_(is_watched) will invoke gdbserver if
+ the access provided by the tool matches the watchpoint kind.
+ For this, the tool must pass the kind of access it has detected:
+ write_watchpoint indicates the tool has detected a write
+ read_watchpoint indicates the tool has detected a read
+ access_watchpoint indicates the tool has detected an access but does
+ not know if this is a read or a write
+*/
+extern Bool VG_(is_watched)(PointKind kind, Addr addr, Int szB);
+
+extern void VG_(needs_watchpoint) (
+ // indicates the given Addr/len is being watched (insert)
+ // or not watched anymore (! insert).
+ // gdbserver will maintain the list of watched addresses.
+ // The tool can use VG_(is_watched) to verify if an
+ // access to an Addr is in one of the watched intervals.
+ // Must return True if the watchpoint has been properly inserted or
+ // removed. False if not supported.
+ // Note that an address can only be watched for a single kind.
+ // The tool must be ready to be called successively with
+ // multiple kinds for the same addr and len and with
+ // different kinds. The last kind must replace the previous values.
+ // Behaviour with multiple watches having overlapping addr+len
+ // is undefined.
+ Bool (*watchpoint) (PointKind kind, Bool insert, Addr addr, SizeT len)
+);
+
+
+// can be used during the processing of the VG_USERREQ__GDB_MONITOR_COMMAND
+// tool client request to output information to gdb or vgdb.
+extern UInt VG_(gdb_printf) ( const HChar *format, ... ) PRINTF_CHECK(1, 2);
+
+/* Utility functions to (e.g.) parse gdb monitor commands.
+
+ keywords is a set of keywords separated by a space
+ keyword_id will search for the keyword starting with the string input_word
+ and return its position.
+ It returns -1 if no keyword matches.
+ It returns -2 if two or more keywords are starting with input_word
+ and none of these matches exactly input_word
+ Example with keywords = "hello world here is hell" :
+ input_word result
+ ---------- ------
+ paradise => -1
+ i => 3
+ hell => 4
+ hel => -2
+ ishtar => -1
+
+ report indicates when to output an error msg with VG_(gdb_printf).
+ kwd_report_none : no error is reported.
+ kwd_report_all : the error msg will show all possible keywords
+ kwd_report_duplicated_matches : the error msg will show only the
+ ambiguous matches.
+*/
+typedef
+ enum {
+ kwd_report_none,
+ kwd_report_all,
+ kwd_report_duplicated_matches } kwd_report_error;
+extern Int VG_(keyword_id) (Char* keywords, Char* input_word,
+ kwd_report_error report);
+
+/* Extract an address and (optionally) a size from the string
+ currently being parsed by strtok_r (see pub_tool_libcbase.h).
+ If no size in the string, keeps the current value of szB.
+ Returns address 0 and szB 0 if there is an error. Reports to the
+ user problems via VG_(gdb_printf). */
+extern void VG_(strtok_get_address_and_size) (Addr* address,
+ SizeT* szB,
+ Char **ssaveptr);
+
+#endif // __PUB_TOOL_GDBSERVER_H
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
diff --git a/include/pub_tool_libcbase.h b/include/pub_tool_libcbase.h
index 2897235..0e61691 100644
--- a/include/pub_tool_libcbase.h
+++ b/include/pub_tool_libcbase.h
@@ -63,6 +63,8 @@
// If you really want that behaviour, you can use "VG_(strtoll10)(str, NULL)".
extern Long VG_(strtoll10) ( Char* str, Char** endptr );
extern Long VG_(strtoll16) ( Char* str, Char** endptr );
+extern ULong VG_(strtoull10) ( Char* str, Char** endptr );
+extern ULong VG_(strtoull16) ( Char* str, Char** endptr );
// Convert a string to a double. After leading whitespace is ignored, a
// '+' or '-' is allowed, and then it accepts a non-empty sequence of
@@ -97,6 +99,16 @@
extern SizeT VG_(strspn) ( const Char* s, const Char* accpt );
extern SizeT VG_(strcspn) ( const Char* s, const char* reject );
+/* strtok* functions and some parsing utilities. */
+extern Char* VG_(strtok_r) (Char* s, const Char* delim, Char** saveptr);
+extern Char* VG_(strtok) (Char* s, const Char* delim);
+
+/* Parse a 32- or 64-bit hex number, including leading 0x, from string
+ starting at *ppc, putting result in *result, and return True. Or
+ fail, in which case *ppc and *result are undefined, and return
+ False. */
+extern Bool VG_(parse_Addr) ( UChar** ppc, Addr* result );
+
/* Like strncpy(), but if 'src' is longer than 'ndest' inserts a '\0' as the
last character. */
extern void VG_(strncpy_safely) ( Char* dest, const Char* src, SizeT ndest );
diff --git a/include/pub_tool_libcfile.h b/include/pub_tool_libcfile.h
index 8f08cd2..4b63a53 100644
--- a/include/pub_tool_libcfile.h
+++ b/include/pub_tool_libcfile.h
@@ -68,7 +68,11 @@
ULong ctime_nsec;
};
+extern SysRes VG_(mknod) ( const Char* pathname, Int mode, UWord dev );
extern SysRes VG_(open) ( const Char* pathname, Int flags, Int mode );
+/* fd_open words like the open(2) system call:
+ returns fd if success, -1 otherwise */
+extern Int VG_(fd_open) (const Char* pathname, Int flags, Int mode);
extern void VG_(close) ( Int fd );
extern Int VG_(read) ( Int fd, void* buf, Int count);
extern Int VG_(write) ( Int fd, const void* buf, Int count);
@@ -82,6 +86,8 @@
extern Int VG_(rename) ( const Char* old_name, const Char* new_name );
extern Int VG_(unlink) ( const Char* file_name );
+extern Int VG_(poll) (struct vki_pollfd *fds, Int nfds, Int timeout);
+
extern Int VG_(readlink)( const Char* path, Char* buf, UInt bufsize );
extern Int VG_(getdents)( Int fd, struct vki_dirent *dirp, UInt count );
diff --git a/include/pub_tool_libcproc.h b/include/pub_tool_libcproc.h
index 2770dda..423a7f7 100644
--- a/include/pub_tool_libcproc.h
+++ b/include/pub_tool_libcproc.h
@@ -58,11 +58,13 @@
extern void VG_(execv) ( Char* filename, Char** argv );
/* ---------------------------------------------------------------------
- Resource limits
+ Resource limits and capabilities
------------------------------------------------------------------ */
extern Int VG_(getrlimit) ( Int resource, struct vki_rlimit *rlim );
extern Int VG_(setrlimit) ( Int resource, const struct vki_rlimit *rlim );
+extern Int VG_(prctl) (Int option,
+ ULong arg2, ULong arg3, ULong arg4, ULong arg5);
/* ---------------------------------------------------------------------
pids, etc
diff --git a/include/pub_tool_options.h b/include/pub_tool_options.h
index 7f85492..3dffd42 100644
--- a/include/pub_tool_options.h
+++ b/include/pub_tool_options.h
@@ -146,6 +146,12 @@
/* Show tool and core statistics */
extern Bool VG_(clo_stats);
+/* wait for vgdb/gdb after reporting that amount of error.
+ Note that this is the initial value provided from the command line.
+ The real value is maintained in VG_(dyn_vgdb_error) and
+ can be changed dynamically.*/
+extern Int VG_(clo_vgdb_error);
+
/* Emit all messages as XML? default: NO */
/* If clo_xml is set, various other options are set in a non-default
way. See vg_main.c and mc_main.c. */
diff --git a/include/valgrind.h b/include/valgrind.h
index 8933e0f..10f3d9b 100644
--- a/include/valgrind.h
+++ b/include/valgrind.h
@@ -4843,6 +4843,10 @@
errors. */
VG_USERREQ__COUNT_ERRORS = 0x1201,
+ /* Allows a string (gdb monitor command) to be passed to the tool
+ Used for interaction with vgdb/gdb */
+ VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202,
+
/* These are useful and can be interpreted by any tool that
tracks malloc() et al, by using vg_replace_malloc.c. */
VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301,
diff --git a/include/vki/vki-s390x-linux.h b/include/vki/vki-s390x-linux.h
index 7d554fd..ccc73a4 100644
--- a/include/vki/vki-s390x-linux.h
+++ b/include/vki/vki-s390x-linux.h
@@ -296,6 +296,7 @@
extend change to end
of growsup vma */
+#define VKI_MAP_SHARED 0x0001 /* Share changes */
#define VKI_MAP_PRIVATE 0x0002 /* */
#define VKI_MAP_FIXED 0x0010 /* */
#define VKI_MAP_ANONYMOUS 0x0020 /* */
@@ -314,6 +315,7 @@
#define VKI_O_NOCTTY 00000400 /* not fcntl */
#define VKI_O_TRUNC 00001000 /* not fcntl */
#define VKI_O_APPEND 00002000
+#define VKI_O_NONBLOCK 00004000
#define VKI_AT_FDCWD -100
@@ -605,6 +607,7 @@
short revents;
};
+#define VKI_POLLIN 0x0001
//----------------------------------------------------------------------
// From linux-2.6.16.60/include/asm-s390/ptrace.h
diff --git a/massif/docs/ms-manual.xml b/massif/docs/ms-manual.xml
index 51f872b..f9cc1ff 100644
--- a/massif/docs/ms-manual.xml
+++ b/massif/docs/ms-manual.xml
@@ -858,6 +858,21 @@
</sect1>
+<sect1 id="ms-manual.monitor-commands" xreflabel="Massif Monitor Commands">
+<title>Massif Monitor Commands</title>
+<para>The Massif tool provides monitor commands handled by the Valgrind
+gdbserver (see <xref linkend="manual-core.gdbserver-commandhandling"/>).
+</para>
+
+<itemizedlist>
+ <listitem>
+ <para><varname>ms.snapshot [<filename>] [detailed]</varname> requests to take
+ a snapshot and save it in the given <filename> (default massif.vgdb.out).
+ If present, the 'detailed' argument indicates to take a detailed snapshot.
+ </para>
+ </listitem>
+</itemizedlist>
+</sect1>
<sect1 id="ms-manual.clientreqs" xreflabel="Client requests">
<title>Massif Client Requests</title>
diff --git a/massif/ms_main.c b/massif/ms_main.c
index 67702cd..6bc4925 100644
--- a/massif/ms_main.c
+++ b/massif/ms_main.c
@@ -180,6 +180,7 @@
#include "pub_tool_tooliface.h"
#include "pub_tool_xarray.h"
#include "pub_tool_clientstate.h"
+#include "pub_tool_gdbserver.h"
#include "valgrind.h" // For {MALLOC,FREE}LIKE_BLOCK
@@ -1977,6 +1978,21 @@
//--- Client Requests ---//
//------------------------------------------------------------//
+static void print_monitor_help ( void )
+{
+ VG_(gdb_printf) ("\n");
+ VG_(gdb_printf) ("massif monitor commands:\n");
+ VG_(gdb_printf) (" ms.snapshot [<filename>] [detailed]\n");
+ VG_(gdb_printf) (" takes a snapshot and saves it in <filename>\n");
+ VG_(gdb_printf) (" default <filename> is massif.vgdb.out\n");
+ VG_(gdb_printf) (" if present, detailed argument indicates to take a detailed snapshot\n");
+ VG_(gdb_printf) ("\n");
+}
+
+
+/* Forward declaration.
+ return True if request recognised, False otherwise */
+static Bool handle_gdb_monitor_command (ThreadId tid, Char *req);
static Bool ms_handle_client_request ( ThreadId tid, UWord* argv, UWord* ret )
{
switch (argv[0]) {
@@ -2003,6 +2019,15 @@
*ret = 0;
return True;
}
+ case VG_USERREQ__GDB_MONITOR_COMMAND: {
+ Bool handled = handle_gdb_monitor_command (tid, (Char*)argv[1]);
+ if (handled)
+ *ret = 1;
+ else
+ *ret = 0;
+ return handled;
+ }
+
default:
*ret = 0;
return False;
@@ -2287,19 +2312,13 @@
}
}
-static void write_snapshots_to_file(void)
+static void write_snapshots_to_file(Char* massif_out_file,
+ Snapshot snapshots_array[],
+ Int nr_elements)
{
Int i, fd;
SysRes sres;
- // Setup output filename. Nb: it's important to do this now, ie. as late
- // as possible. If we do it at start-up and the program forks and the
- // output file format string contains a %p (pid) specifier, both the
- // parent and child will incorrectly write to the same file; this
- // happened in 3.3.0.
- Char* massif_out_file =
- VG_(expand_file_name)("--massif-out-file", clo_massif_out_file);
-
sres = VG_(open)(massif_out_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY,
VKI_S_IRUSR|VKI_S_IWUSR);
if (sr_isError(sres)) {
@@ -2307,11 +2326,9 @@
// between multiple cachegrinded processes?), give up now.
VG_(umsg)("error: can't open output file '%s'\n", massif_out_file );
VG_(umsg)(" ... so profiling results will be missing.\n");
- VG_(free)(massif_out_file);
return;
} else {
fd = sr_Res(sres);
- VG_(free)(massif_out_file);
}
// Print massif-specific options that were used.
@@ -2342,12 +2359,75 @@
FP("time_unit: %s\n", TimeUnit_to_string(clo_time_unit));
- for (i = 0; i < next_snapshot_i; i++) {
- Snapshot* snapshot = & snapshots[i];
+ for (i = 0; i < nr_elements; i++) {
+ Snapshot* snapshot = & snapshots_array[i];
pp_snapshot(fd, snapshot, i); // Detailed snapshot!
}
+ VG_(close) (fd);
}
+static void write_snapshots_array_to_file(void)
+{
+ // Setup output filename. Nb: it's important to do this now, ie. as late
+ // as possible. If we do it at start-up and the program forks and the
+ // output file format string contains a %p (pid) specifier, both the
+ // parent and child will incorrectly write to the same file; this
+ // happened in 3.3.0.
+ Char* massif_out_file =
+ VG_(expand_file_name)("--massif-out-file", clo_massif_out_file);
+ write_snapshots_to_file (massif_out_file, snapshots, next_snapshot_i);
+ VG_(free)(massif_out_file);
+}
+
+static Bool handle_gdb_monitor_command (ThreadId tid, Char *req)
+{
+ Char* wcmd;
+ Char s[VG_(strlen(req))]; /* copy for strtok_r */
+ Char *ssaveptr;
+
+ VG_(strcpy) (s, req);
+
+ wcmd = VG_(strtok_r) (s, " ", &ssaveptr);
+ switch (VG_(keyword_id) ("help ms.snapshot",
+ wcmd, kwd_report_duplicated_matches)) {
+ case -2: /* multiple matches */
+ return True;
+ case -1: /* not found */
+ return False;
+ case 0: /* help */
+ print_monitor_help();
+ return True;
+ case 1: { /* ms.snapshot */
+ Char* kw;
+ Char* filename = NULL;
+ Bool detailed = False;
+ Snapshot snapshot;
+
+ for (kw = VG_(strtok_r) (NULL, " ", &ssaveptr);
+ kw != NULL;
+ kw = VG_(strtok_r) (NULL, " ", &ssaveptr)) {
+ if (filename == NULL)
+ filename = kw;
+ else if (0 == VG_(strncmp)(kw, "detailed", VG_(strlen) (kw)))
+ detailed = True;
+ else {
+ VG_(gdb_printf) ("invalid 2nd arg\n");
+ return True;
+ }
+ }
+ clear_snapshot(&snapshot, /* do_sanity_check */ False);
+ take_snapshot(&snapshot, Normal, get_time(), detailed);
+ write_snapshots_to_file ((filename == NULL) ? (Char*) "massif.vgdb.out" : filename,
+ &snapshot,
+ 1);
+ delete_snapshot(&snapshot);
+ return True;
+ }
+ default:
+ tl_assert(0);
+ return False;
+ }
+}
//------------------------------------------------------------//
//--- Finalisation ---//
@@ -2356,7 +2436,7 @@
static void ms_fini(Int exit_status)
{
// Output.
- write_snapshots_to_file();
+ write_snapshots_array_to_file();
// Stats
tl_assert(n_xpts > 0); // always have alloc_xpt
diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml
index 433e2e0..8c6294c 100644
--- a/memcheck/docs/mc-manual.xml
+++ b/memcheck/docs/mc-manual.xml
@@ -1270,7 +1270,126 @@
</sect2>
</sect1>
+<sect1 id="mc-manual.monitor-commands" xreflabel="Memcheck Monitor Commands">
+<title>Memcheck Monitor Commands</title>
+<para>The Memcheck tool provides monitor commands handled by the Valgrind
+gdbserver (see <xref linkend="manual-core.gdbserver-commandhandling"/>).
+</para>
+<itemizedlist>
+ <listitem>
+ <para><varname>mc.get_vbits <addr> [<len>]</varname>
+ outputs the validity bits for the range of <len> (default 1)
+ bytes at <addr>. The validity of each byte of the range is
+ given using two hexadecimal digits. These hexadecimal digits are
+ encoding the validity of each bit of the corresponding byte, using
+ 0 if the bit is valid and 1 if the bit is invalid. In the
+ following example, 'string10' is an array of 10 characters in
+ which one byte on two is undefined. If a byte is not addressable,
+ its validity bits are replaced by __. In the below example, the byte 6
+ is not addressable.</para>
+<programlisting><![CDATA[
+(gdb) p &string10
+$4 = (char (*)[10]) 0x8049e28
+(gdb) monitor mc.get_vbits 0x8049e28 10
+ff00ff00 ff__ff00 ff00
+(gdb)
+]]></programlisting>
+ </listitem>
+
+ <listitem>
+ <para><varname>mc.make_memory [noaccess|undefined|defined|ifaddressabledefined] <addr> [<len>]</varname>
+ marks the range of <len> (default 1) bytes at <addr>
+ with the given accessibility. Marking with 'noaccess' changes the
+ (A) bits of the range to be not addressable. Marking with
+ 'undefined' or 'defined' are changing the definedness of the
+ range. 'ifaddressabledefined' marks the range as defined but only
+ if the range is addressable. In the following example, the first
+ byte of the 'string10' is marked as defined.
+ </para>
+<programlisting><![CDATA[
+(gdb) monitor mc.make_memory defined 0x8049e28 1
+(gdb) monitor mc.get_vbits 0x8049e28 10
+0000ff00 ff00ff00 ff00
+(gdb)
+]]></programlisting>
+ </listitem>
+
+ <listitem>
+ <para><varname>mc.check_memory [addressable|defined] <addr>
+ [<len>]</varname> checks that the range of <len>
+ (default 1) bytes at <addr> has the given accessibility. It
+ then outputs a description of <addr>. In the below case, a
+ detailed description is given as the option --read-var-info=yes
+ was used to start Valgrind.
+ </para>
+<programlisting><![CDATA[
+(gdb) monitor mc.check_memory defined 0x8049e28 1
+Address 0x8049E28 len 1 defined
+==14698== Location 0x8049e28 is 0 bytes inside string10[0],
+==14698== declared at prog.c:10, in frame #0 of thread 1
+(gdb)
+]]></programlisting>
+ </listitem>
+
+ <listitem>
+ <para><varname>mc.leak_check
+ [full*|summary] [reachable|leakpossible*|definiteleak]</varname>
+ starts a leak checking. The * in the arguments above indicates the
+ default value. </para>
+
+ <para> If the first argument is 'summary', only a summary of
+ the leak search is given.
+ </para>
+
+ <para>The second argument controls which entries are output
+ for a 'full' leak search. The value 'definiteleak' indicates to
+ output only the definitely leaked blocks. The value 'leakpossible'
+ will output in addition the possibly leaked blocks. The value
+ 'reachable' will output all blocks (reachable, possibly leaked,
+ definitely leaked).
+ </para>
+ <para>The below is an example of using the mc.leak_check monitor
+ command on the leak-cases Memcheck regression tests.</para>
+<programlisting><![CDATA[
+(gdb) monitor mc.leak_check full leakpossible
+==14729== 16 bytes in 1 blocks are possibly lost in loss record 13 of 16
+==14729== at 0x4006E9E: malloc (vg_replace_malloc.c:236)
+==14729== by 0x80484D5: mk (leak-cases.c:52)
+==14729== by 0x804855F: f (leak-cases.c:81)
+==14729== by 0x80488F5: main (leak-cases.c:107)
+==14729==
+==14729== LEAK SUMMARY:
+==14729== definitely lost: 32 bytes in 2 blocks
+==14729== indirectly lost: 16 bytes in 1 blocks
+==14729== possibly lost: 32 bytes in 2 blocks
+==14729== still reachable: 96 bytes in 6 blocks
+==14729== suppressed: 0 bytes in 0 blocks
+==14729== Reachable blocks (those to which a pointer was found) are not shown.
+==14729== To see them, rerun with: --leak-check=full --show-reachable=yes
+==14729==
+(gdb) mo mc.l
+==14729== LEAK SUMMARY:
+==14729== definitely lost: 32 bytes in 2 blocks
+==14729== indirectly lost: 16 bytes in 1 blocks
+==14729== possibly lost: 32 bytes in 2 blocks
+==14729== still reachable: 96 bytes in 6 blocks
+==14729== suppressed: 0 bytes in 0 blocks
+==14729== Reachable blocks (those to which a pointer was found) are not shown.
+==14729== To see them, rerun with: --leak-check=full --show-reachable=yes
+==14729==
+(gdb)
+]]></programlisting>
+ <para>Note that when using the Valgrind gdbserver, it is not
+ needed to rerun with --leak-check=full --show-reachable=yes to see
+ the reachable blocks. You can obtain the same information without
+ rerunning by using the gdb command 'monitor mc.leak_check full
+ reachable' (or, using abbreviation: 'mo mc.l f r').
+ </para>
+ </listitem>
+</itemizedlist>
+
+</sect1>
<sect1 id="mc-manual.clientreqs" xreflabel="Client requests">
<title>Client Requests</title>
diff --git a/memcheck/mc_errors.c b/memcheck/mc_errors.c
index e09d514..e013328 100644
--- a/memcheck/mc_errors.c
+++ b/memcheck/mc_errors.c
@@ -30,6 +30,7 @@
*/
#include "pub_tool_basics.h"
+#include "pub_tool_gdbserver.h"
#include "pub_tool_hashtable.h" // For mc_include.h
#include "pub_tool_libcbase.h"
#include "pub_tool_libcassert.h"
@@ -787,6 +788,9 @@
if (MC_(in_ignored_range)(a))
return;
+ if (VG_(is_watched)( (isWrite ? write_watchpoint : read_watchpoint), a, szB))
+ return;
+
# if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
/* AIX zero-page handling. On AIX, reads from page zero are,
bizarrely enough, legitimate. Writes to page zero aren't,
@@ -1182,6 +1186,15 @@
return;
}
+void MC_(pp_describe_addr) ( Addr a )
+{
+ AddrInfo ai;
+
+ ai.tag = Addr_Undescribed;
+ describe_addr (a, &ai);
+ mc_pp_AddrInfo (a, &ai, /* maybe_gcc */ False);
+}
+
/* Fill in *origin_ec as specified by otag, or NULL it out if otag
does not refer to a known origin. */
static void update_origin ( /*OUT*/ExeContext** origin_ec,
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
index ece6eac..d6edbf2 100644
--- a/memcheck/mc_include.h
+++ b/memcheck/mc_include.h
@@ -355,6 +355,9 @@
Bool print_record,
Bool count_error );
+/* prints a description of address a */
+void MC_(pp_describe_addr) (Addr a);
+
/* Is this address in a user-specified "ignored range" ? */
Bool MC_(in_ignored_range) ( Addr a );
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index 4712221..0cdb4a5 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -32,6 +32,7 @@
#include "pub_tool_basics.h"
#include "pub_tool_aspacemgr.h"
+#include "pub_tool_gdbserver.h"
#include "pub_tool_hashtable.h" // For mc_include.h
#include "pub_tool_libcbase.h"
#include "pub_tool_libcassert.h"
@@ -1049,67 +1050,17 @@
return False;
}
-
-/* Parse a 32- or 64-bit hex number, including leading 0x, from string
- starting at *ppc, putting result in *result, and return True. Or
- fail, in which case *ppc and *result are undefined, and return
- False. */
-
-static Bool isHex ( UChar c )
-{
- return ((c >= '0' && c <= '9') ||
- (c >= 'a' && c <= 'f') ||
- (c >= 'A' && c <= 'F'));
-}
-
-static UInt fromHex ( UChar c )
-{
- if (c >= '0' && c <= '9')
- return (UInt)c - (UInt)'0';
- if (c >= 'a' && c <= 'f')
- return 10 + (UInt)c - (UInt)'a';
- if (c >= 'A' && c <= 'F')
- return 10 + (UInt)c - (UInt)'A';
- /*NOTREACHED*/
- tl_assert(0);
- return 0;
-}
-
-static Bool parse_Addr ( UChar** ppc, Addr* result )
-{
- Int used, limit = 2 * sizeof(Addr);
- if (**ppc != '0')
- return False;
- (*ppc)++;
- if (**ppc != 'x')
- return False;
- (*ppc)++;
- *result = 0;
- used = 0;
- while (isHex(**ppc)) {
- UInt d = fromHex(**ppc);
- tl_assert(d < 16);
- *result = ((*result) << 4) | fromHex(**ppc);
- (*ppc)++;
- used++;
- if (used > limit) return False;
- }
- if (used == 0)
- return False;
- return True;
-}
-
-/* Parse two such numbers separated by a dash, or fail. */
+/* Parse two Addr separated by a dash, or fail. */
static Bool parse_range ( UChar** ppc, Addr* result1, Addr* result2 )
{
- Bool ok = parse_Addr(ppc, result1);
+ Bool ok = VG_(parse_Addr) (ppc, result1);
if (!ok)
return False;
if (**ppc != '-')
return False;
(*ppc)++;
- ok = parse_Addr(ppc, result2);
+ ok = VG_(parse_Addr) (ppc, result2);
if (!ok)
return False;
return True;
@@ -4513,17 +4464,20 @@
Addr a,
Addr vbits,
SizeT szB,
- Bool setting /* True <=> set vbits, False <=> get vbits */
+ Bool setting, /* True <=> set vbits, False <=> get vbits */
+ Bool is_client_request /* True <=> real user request
+ False <=> internal call from gdbserver */
)
{
SizeT i;
Bool ok;
UChar vbits8;
- /* Check that arrays are addressible before doing any getting/setting. */
+ /* Check that arrays are addressible before doing any getting/setting.
+ vbits to be checked only for real user request. */
for (i = 0; i < szB; i++) {
if (VA_BITS2_NOACCESS == get_vabits2(a + i) ||
- VA_BITS2_NOACCESS == get_vabits2(vbits + i)) {
+ (is_client_request && VA_BITS2_NOACCESS == get_vabits2(vbits + i))) {
return 3;
}
}
@@ -4542,8 +4496,9 @@
tl_assert(ok);
((UChar*)vbits)[i] = vbits8;
}
- // The bytes in vbits[] have now been set, so mark them as such.
- MC_(make_mem_defined)(vbits, szB);
+ if (is_client_request)
+ // The bytes in vbits[] have now been set, so mark them as such.
+ MC_(make_mem_defined)(vbits, szB);
}
return 1;
@@ -4974,6 +4929,220 @@
);
}
+static void print_monitor_help ( void )
+{
+ VG_(gdb_printf)
+ (
+"\n"
+"memcheck monitor commands:\n"
+" mc.get_vbits <addr> [<len>]\n"
+" returns validity bits for <len> (or 1) bytes at <addr>\n"
+" bit values 0 = valid, 1 = invalid, __ = unaddressable byte\n"
+" Example: mc.get_vbits 0x8049c78 10\n"
+" mc.make_memory [noaccess|undefined\n"
+" |defined|ifaddressabledefined] <addr> [<len>]\n"
+" mark <len> (or 1) bytes at <addr> with the given accessibility\n"
+" mc.check_memory [addressable|defined] <addr> [<len>]\n"
+" check that <len> (or 1) bytes at <addr> have the given accessibility\n"
+" and outputs a description of <addr>\n"
+" mc.leak_check [full*|summary]\n"
+" [reachable|leakpossible*|definiteleak]\n"
+" * = defaults\n"
+" Examples: mc.leak_check\n"
+" mc.leak_check any summary\n"
+"\n");
+}
+
+/* return True if request recognised, False otherwise */
+static Bool handle_gdb_monitor_command (ThreadId tid, Char *req)
+{
+ Char* wcmd;
+ Char s[VG_(strlen(req))]; /* copy for strtok_r */
+ Char *ssaveptr;
+
+ VG_(strcpy) (s, req);
+
+ wcmd = VG_(strtok_r) (s, " ", &ssaveptr);
+ /* NB: if possible, avoid introducing a new command below which
+ starts with the same 4 first letters as an already existing
+ command. This ensures a shorter abbreviation for the user. */
+ switch (VG_(keyword_id)
+ ("help mc.get_vbits mc.leak_check mc.make_memory mc.check_memory",
+ wcmd, kwd_report_duplicated_matches)) {
+ case -2: /* multiple matches */
+ return True;
+ case -1: /* not found */
+ return False;
+ case 0: /* help */
+ print_monitor_help();
+ return True;
+ case 1: { /* mc.get_vbits */
+ Addr address;
+ SizeT szB = 1;
+ VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr);
+ if (szB != 0) {
+ UChar vbits;
+ Int i;
+ Int unaddressable = 0;
+ for (i = 0; i < szB; i++) {
+ Int res = mc_get_or_set_vbits_for_client
+ (address+i, (Addr) &vbits, 1,
+ False, /* get them */
+ False /* is client request */ );
+ if ((i % 32) == 0 && i != 0)
+ VG_(gdb_printf) ("\n");
+ else if ((i % 4) == 0 && i != 0)
+ VG_(gdb_printf) (" ");
+ if (res == 1) {
+ VG_(gdb_printf) ("%02x", vbits);
+ } else {
+ tl_assert(3 == res);
+ unaddressable++;
+ VG_(gdb_printf) ("__");
+ }
+ }
+ if ((i % 80) != 0)
+ VG_(gdb_printf) ("\n");
+ if (unaddressable) {
+ VG_(gdb_printf)
+ ("Address %p len %ld has %d bytes unaddressable\n",
+ (void *)address, szB, unaddressable);
+ }
+ }
+ return True;
+ }
+ case 2: { /* mc.leak_check */
+ Int err = 0;
+ Bool save_clo_show_reachable = MC_(clo_show_reachable);
+ Bool save_clo_show_possibly_lost = MC_(clo_show_possibly_lost);
+ Char* kw;
+
+ LeakCheckMode mode;
+
+ MC_(clo_show_reachable) = False;
+ mode = LC_Full;
+
+ for (kw = VG_(strtok_r) (NULL, " ", &ssaveptr);
+ kw != NULL;
+ kw = VG_(strtok_r) (NULL, " ", &ssaveptr)) {
+ switch (VG_(keyword_id)
+ ("full summary "
+ "reachable leakpossible definiteleak",
+ kw, kwd_report_all)) {
+ case -2: err++; break;
+ case -1: err++; break;
+ case 0: mode = LC_Full; break;
+ case 1: mode = LC_Summary; break;
+ case 2: MC_(clo_show_reachable) = True;
+ MC_(clo_show_possibly_lost) = True; break;
+ case 3: MC_(clo_show_reachable) = False;
+ MC_(clo_show_possibly_lost) = True; break;
+ case 4: MC_(clo_show_reachable) = False;
+ MC_(clo_show_possibly_lost) = False; break;
+ default: tl_assert (0);
+ }
+ }
+ if (!err)
+ MC_(detect_memory_leaks)(tid, mode);
+
+ MC_(clo_show_reachable) = save_clo_show_reachable;
+ MC_(clo_show_possibly_lost) = save_clo_show_possibly_lost;
+ return True;
+ }
+
+ case 3: { /* mc.make_memory */
+ Addr address;
+ SizeT szB = 1;
+ int kwdid = VG_(keyword_id)
+ ("noaccess undefined defined ifaddressabledefined",
+ VG_(strtok_r) (NULL, " ", &ssaveptr), kwd_report_all);
+ VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr);
+ if (address == (Addr) 0 && szB == 0) return True;
+ switch (kwdid) {
+ case -2: break;
+ case -1: break;
+ case 0: MC_(make_mem_noaccess) (address, szB); break;
+ case 1: make_mem_undefined_w_tid_and_okind ( address, szB, tid,
+ MC_OKIND_USER ); break;
+ case 2: MC_(make_mem_defined) ( address, szB ); break;
+ case 3: make_mem_defined_if_addressable ( address, szB ); break;;
+ default: tl_assert(0);
+ }
+ return True;
+ }
+
+ case 4: { /* mc.check_memory */
+ Addr address;
+ SizeT szB = 1;
+ Addr bad_addr;
+ UInt okind;
+ char* src;
+ UInt otag;
+ UInt ecu;
+ ExeContext* origin_ec;
+ MC_ReadResult res;
+
+ int kwdid = VG_(keyword_id)
+ ("addressable defined",
+ VG_(strtok_r) (NULL, " ", &ssaveptr), kwd_report_all);
+ VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr);
+ if (address == (Addr) 0 && szB == 0) return True;
+ switch (kwdid) {
+ case -2: break;
+ case -1: break;
+ case 0:
+ if (is_mem_addressable ( address, szB, &bad_addr ))
+ VG_(gdb_printf) ("Address %p len %ld addressable\n",
+ (void *)address, szB);
+ else
+ VG_(gdb_printf)
+ ("Address %p len %ld not addressable:\nbad address %p\n",
+ (void *)address, szB, (void *) bad_addr);
+ MC_(pp_describe_addr) (address);
+ break;
+ case 1: res = is_mem_defined ( address, szB, &bad_addr, &otag );
+ if (MC_AddrErr == res)
+ VG_(gdb_printf)
+ ("Address %p len %ld not addressable:\nbad address %p\n",
+ (void *)address, szB, (void *) bad_addr);
+ else if (MC_ValueErr == res) {
+ okind = otag & 3;
+ switch (okind) {
+ case MC_OKIND_STACK:
+ src = " was created by a stack allocation"; break;
+ case MC_OKIND_HEAP:
+ src = " was created by a heap allocation"; break;
+ case MC_OKIND_USER:
+ src = " was created by a client request"; break;
+ case MC_OKIND_UNKNOWN:
+ src = ""; break;
+ default: tl_assert(0);
+ }
+ VG_(gdb_printf)
+ ("Address %p len %ld not defined:\n"
+ "Uninitialised value at %p%s\n",
+ (void *)address, szB, (void *) bad_addr, src);
+ ecu = otag & ~3;
+ if (VG_(is_plausible_ECU)(ecu)) {
+ origin_ec = VG_(get_ExeContext_from_ECU)( ecu );
+ VG_(pp_ExeContext)( origin_ec );
+ }
+ }
+ else
+ VG_(gdb_printf) ("Address %p len %ld defined\n",
+ (void *)address, szB);
+ MC_(pp_describe_addr) (address);
+ break;
+ default: tl_assert(0);
+ }
+ return True;
+ }
+
+ default:
+ tl_assert(0);
+ return False;
+ }
+}
/*------------------------------------------------------------*/
/*--- Client requests ---*/
@@ -4996,7 +5165,8 @@
&& VG_USERREQ__MEMPOOL_TRIM != arg[0]
&& VG_USERREQ__MOVE_MEMPOOL != arg[0]
&& VG_USERREQ__MEMPOOL_CHANGE != arg[0]
- && VG_USERREQ__MEMPOOL_EXISTS != arg[0])
+ && VG_USERREQ__MEMPOOL_EXISTS != arg[0]
+ && VG_USERREQ__GDB_MONITOR_COMMAND != arg[0])
return False;
switch (arg[0]) {
@@ -5074,12 +5244,16 @@
case VG_USERREQ__GET_VBITS:
*ret = mc_get_or_set_vbits_for_client
- ( arg[1], arg[2], arg[3], False /* get them */ );
+ ( arg[1], arg[2], arg[3],
+ False /* get them */,
+ True /* is client request */ );
break;
case VG_USERREQ__SET_VBITS:
*ret = mc_get_or_set_vbits_for_client
- ( arg[1], arg[2], arg[3], True /* set them */ );
+ ( arg[1], arg[2], arg[3],
+ True /* set them */,
+ True /* is client request */ );
break;
case VG_USERREQ__COUNT_LEAKS: { /* count leaked bytes */
@@ -5215,6 +5389,14 @@
return True;
}
+ case VG_USERREQ__GDB_MONITOR_COMMAND: {
+ Bool handled = handle_gdb_monitor_command (tid, (Char*)arg[1]);
+ if (handled)
+ *ret = 1;
+ else
+ *ret = 0;
+ return handled;
+ }
default:
VG_(message)(
@@ -5790,6 +5972,22 @@
}
}
+/* mark the given addr/len unaddressable for watchpoint implementation
+ The PointKind will be handled at access time */
+static Bool mc_mark_unaddressable_for_watchpoint (PointKind kind, Bool insert,
+ Addr addr, SizeT len)
+{
+ /* GDBTD this is somewhat fishy. We might rather have to save the previous
+ accessibility and definedness in gdbserver so as to allow restoring it
+ properly. Currently, we assume that the user only watches things
+ which are properly addressable and defined */
+ if (insert)
+ MC_(make_mem_noaccess) (addr, len);
+ else
+ MC_(make_mem_defined) (addr, len);
+ return True;
+}
+
static void mc_pre_clo_init(void)
{
VG_(details_name) ("Memcheck");
@@ -5935,6 +6133,8 @@
VG_(track_post_reg_write) ( mc_post_reg_write );
VG_(track_post_reg_write_clientcall_return)( mc_post_reg_write_clientcall );
+ VG_(needs_watchpoint) ( mc_mark_unaddressable_for_watchpoint );
+
init_shadow_memory();
MC_(malloc_list) = VG_(HT_construct)( "MC_(malloc_list)" );
MC_(mempool_list) = VG_(HT_construct)( "MC_(mempool_list)" );
diff --git a/none/tests/cmdline1.stdout.exp b/none/tests/cmdline1.stdout.exp
index fadaa59..641304a 100644
--- a/none/tests/cmdline1.stdout.exp
+++ b/none/tests/cmdline1.stdout.exp
@@ -16,6 +16,9 @@
but check the argv[] entries for children, rather
than the exe name, to make a follow/no-follow decision
--child-silent-after-fork=no|yes omit child output between fork & exec? [no]
+ --vgdb=no|yes|full activate gdbserver? [yes]
+ full is slower but provides precise watchpoint/step
+ --vgdb-error=<number> invoke gdbserver after <number> errors [999999999]
--track-fds=no|yes track open file descriptors? [no]
--time-stamp=no|yes add timestamps to log messages? [no]
--log-fd=<number> log messages to file descriptor [2=stderr]
@@ -60,6 +63,9 @@
and use it to print better error messages in
tools that make use of it (Memcheck, Helgrind,
DRD) [no]
+ --vgdb-poll=<number> gdbserver poll max every <number> basic blocks [5000]
+ --vgdb-shadow-registers=no|yes let gdb see the shadow registers [no]
+ --vgdb-prefix=<prefix> prefix for vgdb FIFOs [/tmp/vgdb-pipe]
--run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]
--sim-hints=hint1,hint2,... known hints:
lax-ioctls, enable-outer [none]
diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp
index 6603e25..e66858d 100644
--- a/none/tests/cmdline2.stdout.exp
+++ b/none/tests/cmdline2.stdout.exp
@@ -16,6 +16,9 @@
but check the argv[] entries for children, rather
than the exe name, to make a follow/no-follow decision
--child-silent-after-fork=no|yes omit child output between fork & exec? [no]
+ --vgdb=no|yes|full activate gdbserver? [yes]
+ full is slower but provides precise watchpoint/step
+ --vgdb-error=<number> invoke gdbserver after <number> errors [999999999]
--track-fds=no|yes track open file descriptors? [no]
--time-stamp=no|yes add timestamps to log messages? [no]
--log-fd=<number> log messages to file descriptor [2=stderr]
@@ -60,6 +63,9 @@
and use it to print better error messages in
tools that make use of it (Memcheck, Helgrind,
DRD) [no]
+ --vgdb-poll=<number> gdbserver poll max every <number> basic blocks [5000]
+ --vgdb-shadow-registers=no|yes let gdb see the shadow registers [no]
+ --vgdb-prefix=<prefix> prefix for vgdb FIFOs [/tmp/vgdb-pipe]
--run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]
--sim-hints=hint1,hint2,... known hints:
lax-ioctls, enable-outer [none]
diff --git a/none/tests/valgrind_cpp_test.cpp b/none/tests/valgrind_cpp_test.cpp
index f4edade..41a522b 100644
--- a/none/tests/valgrind_cpp_test.cpp
+++ b/none/tests/valgrind_cpp_test.cpp
@@ -9,9 +9,9 @@
#include "pub_tool_libcbase.h"
#include "pub_tool_mallocfree.h"
#include "pub_tool_libcprint.h"
+#include "pub_tool_vki.h"
#include "pub_tool_libcfile.h"
#include "pub_tool_libcproc.h"
-#include "pub_tool_vki.h"
#include "pub_tool_threadstate.h"
#include "pub_tool_errormgr.h"
#include "pub_tool_options.h"
diff --git a/tests/vg_regtest.in b/tests/vg_regtest.in
index adf3b9a..68e3138 100755
--- a/tests/vg_regtest.in
+++ b/tests/vg_regtest.in
@@ -55,12 +55,21 @@
# multiple are allowed)
# - stdout_filter: <filter to run stdout through> (default: none)
# - stderr_filter: <filter to run stderr through> (default: ./filter_stderr)
+#
+# - progB: <prog to run in parallel with prog> (default: none)
+# - argsB: <args for progB> (default: none)
+# - stdinB: <input file for progB> (default: none)
+# - stdoutB_filter: <filter progB stdout through> (default: none)
+# - stderrB_filter: <filter progB stderr through> (default: ./filter_stderr)
+#
# - prereq: <prerequisite command> (default: none)
# - post: <post-test check command> (default: none)
# - cleanup: <post-test cleanup cmd> (default: none)
#
+# If prog or probB is a relative path, it will be prefix with the test directory.
# Note that filters are necessary for stderr results to filter out things that
# always change, eg. process id numbers.
+# Note that if a progB is specified, it is started in background (before prog).
#
# Expected stdout (filtered) is kept in <test>.stdout.exp* (can be more
# than one expected output). It can be missing if it would be empty. Expected
@@ -68,8 +77,13 @@
# one stderr.exp* file. Any .exp* file that ends in '~' or '#' is ignored;
# this is because Emacs creates temporary files of these names.
#
+# Expected output for progB is handled similarly, except that
+# expected stdout and stderr for progB are in <test>.stdoutB.exp*
+# and <test>.stderrB.exp*.
+#
# If results don't match, the output can be found in <test>.std<strm>.out,
# and the diff between expected and actual in <test>.std<strm>.diff*.
+# (for progB, in <test>.std<strm>2.out and <test>.std<strm>2.diff*).
#
# The prerequisite command, if present, works like this:
# - if it returns 0 the test is run
@@ -112,6 +126,11 @@
my $args; # test prog args
my $stdout_filter; # filter program to run stdout results file through
my $stderr_filter; # filter program to run stderr results file through
+my $progB; # Same but for progB
+my $argsB; #
+my $stdoutB_filter; #
+my $stderrB_filter; #
+my $stdinB; # Input file for progB
my $prereq; # prerequisite test to satisfy before running test
my $post; # check command after running test
my $cleanup; # cleanup command to run
@@ -119,7 +138,9 @@
my @failures; # List of failed tests
my $num_tests_done = 0;
-my %num_failures = (stderr => 0, stdout => 0, post => 0);
+my %num_failures = (stderr => 0, stdout => 0,
+ stderrB => 0, stdoutB => 0,
+ post => 0);
# Default valgrind to use is this build tree's (uninstalled) one
my $valgrind = "./coregrind/valgrind";
@@ -204,12 +225,16 @@
my ($f) = @_;
# Defaults.
- ($vgopts, $prog, $args) = ("", undef, "");
- ($stdout_filter, $stderr_filter) = (undef, undef);
- ($prereq, $post, $cleanup) = (undef, undef, undef);
+ ($vgopts, $prog, $args) = ("", undef, "");
+ ($stdout_filter, $stderr_filter) = (undef, undef);
+ ($progB, $argsB, $stdinB) = (undef, "", undef);
+ ($stdoutB_filter, $stderrB_filter) = (undef, undef);
+ ($prereq, $post, $cleanup) = (undef, undef, undef);
# Every test directory must have a "filter_stderr"
$stderr_filter = validate_program(".", $default_stderr_filter, 1, 1);
+ $stderrB_filter = validate_program(".", $default_stderr_filter, 1, 1);
+
open(INPUTFILE, "< $f") || die "File $f not openable\n";
@@ -228,6 +253,16 @@
$stdout_filter = validate_program(".", $1, 1, 1);
} elsif ($line =~ /^\s*stderr_filter:\s*(.*)$/) {
$stderr_filter = validate_program(".", $1, 1, 1);
+ } elsif ($line =~ /^\s*progB:\s*(.*)$/) {
+ $progB = validate_program(".", $1, 0, 0);
+ } elsif ($line =~ /^\s*argsB:\s*(.*)$/) {
+ $argsB = $1;
+ } elsif ($line =~ /^\s*stdinB:\s*(.*)$/) {
+ $stdinB = $1;
+ } elsif ($line =~ /^\s*stdoutB_filter:\s*(.*)$/) {
+ $stdoutB_filter = validate_program(".", $1, 1, 1);
+ } elsif ($line =~ /^\s*stderrB_filter:\s*(.*)$/) {
+ $stderrB_filter = validate_program(".", $1, 1, 1);
} elsif ($line =~ /^\s*prereq:\s*(.*)$/) {
$prereq = $1;
} elsif ($line =~ /^\s*post:\s*(.*)$/) {
@@ -333,8 +368,27 @@
}
}
- printf("%-16s valgrind $extraopts $vgopts $prog $args\n", "$name:");
+ if (defined $progB) {
+ # If there is a progB, let's start it in background:
+ printf("%-16s valgrind $extraopts $vgopts $prog $args (progB: $progB $argsB)\n",
+ "$name:");
+ # progB.done used to detect child has finished. See below.
+ # Note: redirection of stdout and stderr is before $progB to allow argsB
+ # to e.g. redirect stdoutB to stderrB
+ if (defined $stdinB) {
+ mysystem("(rm -f progB.done;"
+ . " < $stdinB > $name.stdoutB.out 2> $name.stderrB.out $progB $argsB;"
+ . "touch progB.done) &");
+ } else {
+ mysystem("(rm -f progB.done;"
+ . " > $name.stdoutB.out 2> $name.stderrB.out $progB $argsB;"
+ . "touch progB.done) &");
+ }
+ } else {
+ printf("%-16s valgrind $extraopts $vgopts $prog $args\n", "$name:");
+ }
+
# Pass the appropriate --tool option for the directory (can be overridden
# by an "args:" line, though). Set both VALGRIND_LIB and
# VALGRIND_LIB_INNER in case this Valgrind was configured with
@@ -363,6 +417,38 @@
(0 != scalar @stderr_exps) or die "Could not find `$name.stderr.exp*'\n";
do_diffs($fullname, $name, "stderr", \@stderr_exps);
+ if (defined $progB) {
+ # wait for the child to be finished
+ # tried things such as:
+ # wait;
+ # $SIG{CHLD} = sub { wait };
+ # but nothing worked:
+ # e.g. running mssnapshot.vgtest in a loop failed from time to time
+ # due to some missing output (not yet written?).
+ # So, we search progB.done during max 100 times 100 millisecond.
+ my $count;
+ for ($count = 1; $count <= 100; $count++) {
+ (-f "progB.done") or select(undef, undef, undef, 0.100);
+ }
+ # Filter stdout
+ if (defined $stdoutB_filter) {
+ mysystem("$stdoutB_filter < $name.stdoutB.out > $tmp");
+ rename($tmp, "$name.stdoutB.out");
+ }
+ # Find all the .stdoutB.exp files. If none, use /dev/null.
+ my @stdoutB_exps = <$name.stdoutB.exp*>;
+ @stdoutB_exps = ( "/dev/null" ) if (0 == scalar @stdoutB_exps);
+ do_diffs($fullname, $name, "stdoutB", \@stdoutB_exps);
+
+ # Filter stderr
+ mysystem("$stderrB_filter < $name.stderrB.out > $tmp");
+ rename($tmp, "$name.stderrB.out");
+ # Find all the .stderrB.exp files. At least one must exist.
+ my @stderrB_exps = <$name.stderrB.exp*>;
+ (0 != scalar @stderrB_exps) or die "Could not find `$name.stderrB.exp*'\n";
+ do_diffs($fullname, $name, "stderrB", \@stderrB_exps);
+ }
+
# Maybe do post-test check
if (defined $post) {
if (mysystem("$post > $name.post.out") != 0) {
@@ -449,10 +535,13 @@
my $x = ( $num_tests_done == 1 ? "test" : "tests" );
printf("\n== %d test%s, %d stderr failure%s, %d stdout failure%s, "
+ . "%d stderrB failure%s, %d stdoutB failure%s, "
. "%d post failure%s ==\n",
$num_tests_done, plural($num_tests_done),
$num_failures{"stderr"}, plural($num_failures{"stderr"}),
$num_failures{"stdout"}, plural($num_failures{"stdout"}),
+ $num_failures{"stderrB"}, plural($num_failures{"stderrB"}),
+ $num_failures{"stdoutB"}, plural($num_failures{"stdoutB"}),
$num_failures{"post"}, plural($num_failures{"post"}));
foreach my $failure (@failures) {
@@ -508,6 +597,8 @@
if (0 == $num_failures{"stdout"} &&
0 == $num_failures{"stderr"} &&
+ 0 == $num_failures{"stdoutB"} &&
+ 0 == $num_failures{"stderrB"} &&
0 == $num_failures{"post"}) {
exit 0;
} else {