Files updated, added and removed in order to turn the ERASER branch into HEAD
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1086 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/memcheck/Makefile.am b/memcheck/Makefile.am
index 60553dd..96911ed 100644
--- a/memcheck/Makefile.am
+++ b/memcheck/Makefile.am
@@ -1,15 +1,17 @@
+
+
SUBDIRS = demangle . docs tests
CFLAGS = $(WERROR) -DVG_LIBDIR="\"$(libdir)"\" \
- -Winline -Wall -Wshadow -O -fomit-frame-pointer -g
+ -Winline -Wall -Wshadow -O -fomit-frame-pointer @PREFERRED_STACK_BOUNDARY@ -g
valdir = $(libdir)/valgrind
-LDFLAGS = -Wl,-z -Wl,initfirst
+#LDFLAGS = -Wl,-z -Wl,initfirst
INCLUDES = -I$(srcdir)/demangle
-bin_SCRIPTS = valgrind cachegrind vg_annotate
+bin_SCRIPTS = valgrind vg_annotate
SUPP_FILES = glibc-2.1.supp glibc-2.2.supp xfree-3.supp xfree-4.supp
@@ -26,60 +28,103 @@
PATCHES_APPLIED ACKNOWLEDGEMENTS \
README_KDE3_FOLKS README_PACKAGERS \
README_MISSING_SYSCALL_OR_IOCTL TODO dosyms vg_libpthread.vs \
- valgrind.spec valgrind.spec.in
+ valgrind.spec valgrind.spec.in \
+ vg_profile.c \
+ vg_cachesim_I1.c vg_cachesim_D1.c vg_cachesim_L2.c vg_cachesim_gen.c
-val_PROGRAMS = valgrind.so valgrinq.so libpthread.so
+val_PROGRAMS = \
+ valgrind.so \
+ valgrinq.so \
+ libpthread.so \
+ vgskin_memcheck.so \
+ vgskin_cachesim.so \
+ vgskin_eraser.so \
+ vgskin_addrcheck.so \
+ vgskin_none.so \
+ vgskin_lackey.so \
+ vgskin_corecheck.so
-libpthread_so_SOURCES = vg_libpthread.c vg_libpthread_unimp.c
+libpthread_so_SOURCES = \
+ vg_libpthread.c \
+ vg_libpthread_unimp.c
+libpthread_so_DEPENDENCIES = $(srcdir)/vg_libpthread.vs
+libpthread_so_LDFLAGS = -Werror -fno-omit-frame-pointer -UVG_LIBDIR -shared -fpic -Wl,-version-script $(srcdir)/vg_libpthread.vs
valgrinq_so_SOURCES = vg_valgrinq_dummy.c
+valgrinq_so_LDFLAGS = -shared
valgrind_so_SOURCES = \
vg_clientfuncs.c \
vg_scheduler.c \
- vg_cachesim.c \
vg_clientmalloc.c \
- vg_clientperms.c \
+ vg_default.c \
vg_demangle.c \
vg_dispatch.S \
vg_errcontext.c \
vg_execontext.c \
vg_from_ucode.c \
vg_helpers.S \
+ vg_instrument.c \
vg_main.c \
vg_malloc2.c \
vg_memory.c \
vg_messages.c \
vg_mylibc.c \
vg_procselfmaps.c \
- vg_profile.c \
+ vg_dummy_profile.c \
vg_signals.c \
vg_startup.S \
vg_symtab2.c \
- vg_syscall_mem.c \
+ vg_syscalls.c \
vg_syscall.S \
vg_to_ucode.c \
vg_translate.c \
- vg_transtab.c \
- vg_vtagops.c
-
+ vg_transtab.c
+valgrind_so_LDFLAGS = -Wl,-z -Wl,initfirst -shared
valgrind_so_LDADD = \
demangle/cp-demangle.o \
demangle/cplus-dem.o \
demangle/dyn-string.o \
demangle/safe-ctype.o
+vgskin_memcheck_so_SOURCES = \
+ vg_memcheck.c \
+ vg_memcheck_clientreqs.c \
+ vg_memcheck_errcontext.c \
+ vg_memcheck_from_ucode.c \
+ vg_memcheck_translate.c \
+ vg_memcheck_helpers.S
+vgskin_memcheck_so_LDFLAGS = -shared
+
+vgskin_cachesim_so_SOURCES = vg_cachesim.c
+vgskin_cachesim_so_LDFLAGS = -shared
+
+vgskin_eraser_so_SOURCES = vg_eraser.c
+vgskin_eraser_so_LDFLAGS = -shared
+
+vgskin_addrcheck_so_SOURCES = vg_addrcheck.c
+vgskin_addrcheck_so_LDFLAGS = -shared
+
+vgskin_none_so_SOURCES = vg_none.c
+vgskin_none_so_LDFLAGS = -shared
+
+vgskin_lackey_so_SOURCES = vg_lackey.c
+vgskin_lackey_so_LDFLAGS = -shared
+
+vgskin_corecheck_so_SOURCES = vg_corecheck.c
+vgskin_corecheck_so_LDFLAGS = -shared
+
include_HEADERS = valgrind.h
noinst_HEADERS = \
- vg_cachesim_gen.c \
- vg_cachesim_I1.c \
- vg_cachesim_D1.c \
- vg_cachesim_L2.c \
vg_kerneliface.h \
vg_include.h \
+ vg_skin.h \
vg_constants.h \
- vg_unsafe.h
+ vg_constants_skin.h \
+ vg_unsafe.h \
+ vg_memcheck_include.h \
+ vg_memcheck.h
MANUAL_DEPS = $(noinst_HEADERS) $(include_HEADERS)
@@ -92,19 +137,40 @@
vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS)
$(COMPILE) -fno-omit-frame-pointer -c $<
-valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS)
- $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \
- $(valgrind_so_OBJECTS) $(valgrind_so_LDADD)
+##valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS)
+## $(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \
+## $(valgrind_so_OBJECTS) $(valgrind_so_LDADD)
-valgrinq.so$(EXEEXT): $(valgrinq_so_OBJECTS)
- $(CC) $(CFLAGS) -shared -o valgrinq.so $(valgrinq_so_OBJECTS)
+##valgrinq.so$(EXEEXT): $(valgrinq_so_OBJECTS)
+## $(CC) $(CFLAGS) -shared -o valgrinq.so $(valgrinq_so_OBJECTS)
-libpthread.so$(EXEEXT): $(libpthread_so_OBJECTS) $(srcdir)/vg_libpthread.vs
- $(CC) -Wall -Werror -g -O -shared -fpic -o libpthread.so \
- $(libpthread_so_OBJECTS) \
- -Wl,-version-script $(srcdir)/vg_libpthread.vs
+##libpthread.so$(EXEEXT): $(libpthread_so_OBJECTS) $(srcdir)/vg_libpthread.vs
+## $(CC) -Wall -Werror -g -O -shared -fpic -o libpthread.so \
+## $(libpthread_so_OBJECTS) \
+## -Wl,-version-script $(srcdir)/vg_libpthread.vs
+
+##vgskin_memcheck.so$(EXEEXT): $(vgskin_memcheck_so_OBJECTS)
+## $(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_memcheck.so \
+## $(vgskin_memcheck_so_OBJECTS)
+
+##vgskin_cachesim.so$(EXEEXT): $(vgskin_cachesim_so_OBJECTS)
+## $(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_cachesim.so \
+## $(vgskin_cachesim_so_OBJECTS)
+
+##vgskin_eraser.so$(EXEEXT): $(vgskin_eraser_so_OBJECTS)
+## $(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_eraser.so \
+## $(vgskin_eraser_so_OBJECTS)
+
+##vgskin_none.so$(EXEEXT): $(vgskin_none_so_OBJECTS)
+## $(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_none.so \
+## $(vgskin_none_so_OBJECTS)
+
+##vgskin_lackey.so$(EXEEXT): $(vgskin_lackey_so_OBJECTS)
+## $(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_lackey.so \
+## $(vgskin_lackey_so_OBJECTS)
install-exec-hook:
$(mkinstalldirs) $(DESTDIR)$(valdir)
rm -f $(DESTDIR)$(valdir)/libpthread.so.0
$(LN_S) libpthread.so $(DESTDIR)$(valdir)/libpthread.so.0
+
diff --git a/memcheck/docs/manual.html b/memcheck/docs/manual.html
index b715ee3..95fe840 100644
--- a/memcheck/docs/manual.html
+++ b/memcheck/docs/manual.html
@@ -345,7 +345,7 @@
</pre>
<p>Note that Valgrind also reads options from the environment variable
-<code>$VALGRIND</code>, and processes them before the command-line
+<code>$VALGRIND_OPTS</code>, and processes them before the command-line
options.
<p>Valgrind's default settings succeed in giving reasonable behaviour
@@ -838,8 +838,8 @@
<li>The contents of malloc'd blocks, before you write something
there. In C++, the new operator is a wrapper round malloc, so
if you create an object with new, its fields will be
- uninitialised until you fill them in, which is only Right and
- Proper.</li>
+ uninitialised until you (or the constructor) fill them in, which
+ is only Right and Proper.</li>
</ul>
@@ -1066,16 +1066,16 @@
<p>
<li>The "immediate location" specification. For Value and Addr
- errors, is either the name of the function in which the error
- occurred, or, failing that, the full path the the .so file
- containing the error location. For Param errors, is the name of
- the offending system call parameter. For Free errors, is the
- name of the function doing the freeing (eg, <code>free</code>,
- <code>__builtin_vec_delete</code>, etc)</li><br>
+ errors, it is either the name of the function in which the error
+ occurred, or, failing that, the full path of the .so file or
+ executable containing the error location. For Param errors,
+ is the name of the offending system call parameter. For Free
+ errors, is the name of the function doing the freeing (eg,
+ <code>free</code>, <code>__builtin_vec_delete</code>, etc)</li><br>
<p>
<li>The caller of the above "immediate location". Again, either a
- function or shared-object name.</li><br>
+ function or shared-object/executable name.</li><br>
<p>
<li>Optionally, one or two extra calling-function or object names,
@@ -1083,8 +1083,8 @@
</ul>
<p>
-Locations may be either names of shared objects or wildcards matching
-function names. They begin <code>obj:</code> and <code>fun:</code>
+Locations may be either names of shared objects/executables or wildcards
+matching function names. They begin <code>obj:</code> and <code>fun:</code>
respectively. Function and object names to match against may use the
wildcard characters <code>*</code> and <code>?</code>.
@@ -1617,11 +1617,11 @@
<li>If the new size is smaller, the dropped-off section is marked as
unaddressible. You may only pass to realloc a pointer
- previously issued to you by malloc/calloc/new/realloc.</li><br>
+ previously issued to you by malloc/calloc/realloc.</li><br>
<p>
<li>free/delete: you may only pass to free a pointer previously
- issued to you by malloc/calloc/new/realloc, or the value
+ issued to you by malloc/calloc/realloc, or the value
NULL. Otherwise, Valgrind complains. If the pointer is indeed
valid, Valgrind marks the entire area it points at as
unaddressible, and places the block in the freed-blocks-queue.
@@ -2058,7 +2058,9 @@
<li>Run your program with <code>cachegrind</code> in front of the
normal command line invocation. When the program finishes,
Valgrind will print summary cache statistics. It also collects
- line-by-line information in a file <code>cachegrind.out</code>.
+ line-by-line information in a file
+ <code>cachegrind.out.<i>pid</i></code>, where <code><i>pid</i></code>
+ is the program's process id.
<p>
This step should be done every time you want to collect
information about a new program, a changed program, or about the
@@ -2197,15 +2199,17 @@
As well as printing summary information, Cachegrind also writes
line-by-line cache profiling information to a file named
-<code>cachegrind.out</code>. This file is human-readable, but is best
-interpreted by the accompanying program <code>vg_annotate</code>,
+<code>cachegrind.out.<i>pid</i></code>. This file is human-readable, but is
+best interpreted by the accompanying program <code>vg_annotate</code>,
described in the next section.
<p>
-Things to note about the <code>cachegrind.out</code> file:
+Things to note about the <code>cachegrind.out.<i>pid</i></code> file:
<ul>
<li>It is written every time <code>valgrind --cachesim=yes</code> or
<code>cachegrind</code> is run, and will overwrite any existing
- <code>cachegrind.out</code> in the current directory.</li>
+ <code>cachegrind.out.<i>pid</i></code> in the current directory (but
+ that won't happen very often because it takes some time for process ids
+ to be recycled).</li>
<p>
<li>It can be huge: <code>ls -l</code> generates a file of about
350KB. Browsing a few files and web pages with a Konqueror
@@ -2213,6 +2217,13 @@
of around 15 MB.</li>
</ul>
+Note that older versions of Cachegrind used a log file named
+<code>cachegrind.out</code> (i.e. no <code><i>.pid</i></code> suffix).
+The suffix serves two purposes. Firstly, it means you don't have to rename old
+log files that you don't want to overwrite. Secondly, and more importantly,
+it allows correct profiling with the <code>--trace-children=yes</code> option
+of programs that spawn child processes.
+
<a name="profileflags"></a>
<h3>7.5 Cachegrind options</h3>
Cachegrind accepts all the options that Valgrind does, although some of them
@@ -2245,9 +2256,13 @@
window to be at least 120-characters wide if possible, as the output
lines can be quite long.
<p>
-To get a function-by-function summary, run <code>vg_annotate</code> in
-directory containing a <code>cachegrind.out</code> file. The output
-looks like this:
+To get a function-by-function summary, run <code>vg_annotate
+--<i>pid</i></code> in a directory containing a
+<code>cachegrind.out.<i>pid</i></code> file. The <code>--<i>pid</i></code>
+is required so that <code>vg_annotate</code> knows which log file to use when
+several are present.
+<p>
+The output looks like this:
<pre>
--------------------------------------------------------------------------------
@@ -2468,8 +2483,9 @@
specific enough.
Beware that vg_annotate can take some time to digest large
-<code>cachegrind.out</code> files, eg. 30 seconds or more. Also beware that
-auto-annotation can produce a lot of output if your program is large!
+<code>cachegrind.out.<i>pid</i></code> files, e.g. 30 seconds or more. Also
+beware that auto-annotation can produce a lot of output if your program is
+large!
<h3>7.7 Annotating assembler programs</h3>
@@ -2492,13 +2508,18 @@
<h3>7.8 <code>vg_annotate</code> options</h3>
<ul>
+ <li><code>--<i>pid</i></code></li><p>
+
+ Indicates which <code>cachegrind.out.<i>pid</i></code> file to read.
+ Not actually an option -- it is required.
+
<li><code>-h, --help</code></li><p>
<li><code>-v, --version</code><p>
Help and version, as usual.</li>
<li><code>--sort=A,B,C</code> [default: order in
- <code>cachegrind.out</code>]<p>
+ <code>cachegrind.out.<i>pid</i></code>]<p>
Specifies the events upon which the sorting of the function-by-function
entries will be based. Useful if you want to concentrate on eg. I cache
misses (<code>--sort=I1mr,I2mr</code>), or D cache misses
@@ -2506,10 +2527,10 @@
(<code>--sort=D2mr,I2mr</code>).</li><p>
<li><code>--show=A,B,C</code> [default: all, using order in
- <code>cachegrind.out</code>]<p>
+ <code>cachegrind.out.<i>pid</i></code>]<p>
Specifies which events to show (and the column order). Default is to use
- all present in the <code>cachegrind.out</code> file (and use the order in
- the file).</li><p>
+ all present in the <code>cachegrind.out.<i>pid</i></code> file (and use
+ the order in the file).</li><p>
<li><code>--threshold=X</code> [default: 99%] <p>
Sets the threshold for the function-by-function summary. Functions are
@@ -2547,17 +2568,18 @@
There are a couple of situations in which vg_annotate issues warnings.
<ul>
- <li>If a source file is more recent than the <code>cachegrind.out</code>
- file. This is because the information in <code>cachegrind.out</code> is
- only recorded with line numbers, so if the line numbers change at all in
- the source (eg. lines added, deleted, swapped), any annotations will be
+ <li>If a source file is more recent than the
+ <code>cachegrind.out.<i>pid</i></code> file. This is because the
+ information in <code>cachegrind.out.<i>pid</i></code> is only recorded
+ with line numbers, so if the line numbers change at all in the source
+ (eg. lines added, deleted, swapped), any annotations will be
incorrect.<p>
<li>If information is recorded about line numbers past the end of a file.
This can be caused by the above problem, ie. shortening the source file
- while using an old <code>cachegrind.out</code> file. If this happens,
- the figures for the bogus lines are printed anyway (clearly marked as
- bogus) in case they are important.</li><p>
+ while using an old <code>cachegrind.out.<i>pid</i></code> file. If this
+ happens, the figures for the bogus lines are printed anyway (clearly
+ marked as bogus) in case they are important.</li><p>
</ul>
@@ -2677,6 +2699,13 @@
<blockquote><code>btsl %eax, %edx</code></blockquote>
This should only happen rarely.
+ </li><p>
+
+ <li>FPU instructions with data sizes of 28 and 108 bytes (e.g.
+ <code>fsave</code>) are treated as though they only access 16 bytes.
+ These instructions seem to be rare so hopefully this won't affect
+ accuracy much.
+ </li><p>
</ul>
Another thing worth nothing is that results are very sensitive. Changing the
diff --git a/memcheck/mc_clientreqs.c b/memcheck/mc_clientreqs.c
new file mode 100644
index 0000000..b5284bd
--- /dev/null
+++ b/memcheck/mc_clientreqs.c
@@ -0,0 +1,367 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: for when the client advises ---*/
+/*--- Valgrind about memory permissions. ---*/
+/*--- vg_memcheck_clientreqs.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+
+ 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 "vg_memcheck_include.h"
+
+#include "vg_memcheck.h" /* for VG_USERREQ__* */
+
+
+/*------------------------------------------------------------*/
+/*--- General client block management. ---*/
+/*------------------------------------------------------------*/
+
+/* This is managed as an expanding array of client block descriptors.
+ Indices of live descriptors are issued to the client, so it can ask
+ to free them later. Therefore we cannot slide live entries down
+ over dead ones. Instead we must use free/inuse flags and scan for
+ an empty slot at allocation time. This in turn means allocation is
+ relatively expensive, so we hope this does not happen too often.
+*/
+
+typedef
+ enum { CG_NotInUse, CG_NoAccess, CG_Writable, CG_Readable }
+ CGenBlockKind;
+
+typedef
+ struct {
+ Addr start;
+ UInt size;
+ ExeContext* where;
+ CGenBlockKind kind;
+ }
+ CGenBlock;
+
+/* This subsystem is self-initialising. */
+static UInt vg_cgb_size = 0;
+static UInt vg_cgb_used = 0;
+static CGenBlock* vg_cgbs = NULL;
+
+/* Stats for this subsystem. */
+static UInt vg_cgb_used_MAX = 0; /* Max in use. */
+static UInt vg_cgb_allocs = 0; /* Number of allocs. */
+static UInt vg_cgb_discards = 0; /* Number of discards. */
+static UInt vg_cgb_search = 0; /* Number of searches. */
+
+
+static
+Int vg_alloc_client_block ( void )
+{
+ Int i, sz_new;
+ CGenBlock* cgbs_new;
+
+ vg_cgb_allocs++;
+
+ for (i = 0; i < vg_cgb_used; i++) {
+ vg_cgb_search++;
+ if (vg_cgbs[i].kind == CG_NotInUse)
+ return i;
+ }
+
+ /* Not found. Try to allocate one at the end. */
+ if (vg_cgb_used < vg_cgb_size) {
+ vg_cgb_used++;
+ return vg_cgb_used-1;
+ }
+
+ /* Ok, we have to allocate a new one. */
+ vg_assert(vg_cgb_used == vg_cgb_size);
+ sz_new = (vg_cgbs == NULL) ? 10 : (2 * vg_cgb_size);
+
+ cgbs_new = VG_(malloc)( sz_new * sizeof(CGenBlock) );
+ for (i = 0; i < vg_cgb_used; i++)
+ cgbs_new[i] = vg_cgbs[i];
+
+ if (vg_cgbs != NULL)
+ VG_(free)( vg_cgbs );
+ vg_cgbs = cgbs_new;
+
+ vg_cgb_size = sz_new;
+ vg_cgb_used++;
+ if (vg_cgb_used > vg_cgb_used_MAX)
+ vg_cgb_used_MAX = vg_cgb_used;
+ return vg_cgb_used-1;
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Stack block management. ---*/
+/*------------------------------------------------------------*/
+
+/* This is managed as an expanding array of CStackBlocks. They are
+ packed up against the left-hand end of the array, with no holes.
+ They are kept sorted by the start field, with the [0] having the
+ highest value. This means it's pretty cheap to put new blocks at
+ the end, corresponding to stack pushes, since the additions put
+ blocks on in what is presumably fairly close to strictly descending
+ order. If this assumption doesn't hold the performance
+ consequences will be horrible.
+
+ When the client's %ESP jumps back upwards as the result of a RET
+ insn, we shrink the array backwards from the end, in a
+ guaranteed-cheap linear scan.
+*/
+
+typedef
+ struct {
+ Addr start;
+ UInt size;
+ ExeContext* where;
+ }
+ CStackBlock;
+
+/* This subsystem is self-initialising. */
+static UInt vg_csb_size = 0;
+static UInt vg_csb_used = 0;
+static CStackBlock* vg_csbs = NULL;
+
+/* Stats for this subsystem. */
+static UInt vg_csb_used_MAX = 0; /* Max in use. */
+static UInt vg_csb_allocs = 0; /* Number of allocs. */
+static UInt vg_csb_discards = 0; /* Number of discards. */
+static UInt vg_csb_swaps = 0; /* Number of searches. */
+
+static
+void vg_add_client_stack_block ( ThreadState* tst, Addr aa, UInt sz )
+{
+ UInt i, sz_new;
+ CStackBlock* csbs_new;
+ vg_csb_allocs++;
+
+ /* Ensure there is space for a new block. */
+
+ if (vg_csb_used >= vg_csb_size) {
+
+ /* No; we have to expand the array. */
+ vg_assert(vg_csb_used == vg_csb_size);
+
+ sz_new = (vg_csbs == NULL) ? 10 : (2 * vg_csb_size);
+
+ csbs_new = VG_(malloc)( sz_new * sizeof(CStackBlock) );
+ for (i = 0; i < vg_csb_used; i++)
+ csbs_new[i] = vg_csbs[i];
+
+ if (vg_csbs != NULL)
+ VG_(free)( vg_csbs );
+ vg_csbs = csbs_new;
+
+ vg_csb_size = sz_new;
+ }
+
+ /* Ok, we can use [vg_csb_used]. */
+ vg_csbs[vg_csb_used].start = aa;
+ vg_csbs[vg_csb_used].size = sz;
+ /* Actually running a thread at this point. */
+ vg_csbs[vg_csb_used].where = VG_(get_ExeContext) ( tst );
+ vg_csb_used++;
+
+ if (vg_csb_used > vg_csb_used_MAX)
+ vg_csb_used_MAX = vg_csb_used;
+
+ vg_assert(vg_csb_used <= vg_csb_size);
+
+ /* VG_(printf)("acsb %p %d\n", aa, sz); */
+ SK_(make_noaccess) ( aa, sz );
+
+ /* And make sure that they are in descending order of address. */
+ i = vg_csb_used;
+ while (i > 0 && vg_csbs[i-1].start < vg_csbs[i].start) {
+ CStackBlock tmp = vg_csbs[i-1];
+ vg_csbs[i-1] = vg_csbs[i];
+ vg_csbs[i] = tmp;
+ vg_csb_swaps++;
+ }
+
+# if 1
+ for (i = 1; i < vg_csb_used; i++)
+ vg_assert(vg_csbs[i-1].start >= vg_csbs[i].start);
+# endif
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Externally visible functions. ---*/
+/*------------------------------------------------------------*/
+
+void SK_(show_client_block_stats) ( void )
+{
+ VG_(message)(Vg_DebugMsg,
+ "general CBs: %d allocs, %d discards, %d maxinuse, %d search",
+ vg_cgb_allocs, vg_cgb_discards, vg_cgb_used_MAX, vg_cgb_search
+ );
+ VG_(message)(Vg_DebugMsg,
+ " stack CBs: %d allocs, %d discards, %d maxinuse, %d swap",
+ vg_csb_allocs, vg_csb_discards, vg_csb_used_MAX, vg_csb_swaps
+ );
+}
+
+Bool SK_(client_perm_maybe_describe)( Addr a, AddrInfo* ai )
+{
+ Int i;
+ /* VG_(printf)("try to identify %d\n", a); */
+
+ /* First see if it's a stack block. We do two passes, one exact
+ and one with a bit of slop, so as to try and get the most
+ accurate fix. */
+ for (i = 0; i < vg_csb_used; i++) {
+ if (vg_csbs[i].start <= a
+ && a < vg_csbs[i].start + vg_csbs[i].size) {
+ ai->akind = UserS;
+ ai->blksize = vg_csbs[i].size;
+ ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
+ ai->lastchange = vg_csbs[i].where;
+ return True;
+ }
+ }
+
+ /* No exact match on the stack. Re-do the stack scan with a bit of
+ slop. */
+ for (i = 0; i < vg_csb_used; i++) {
+ if (vg_csbs[i].start - 8 <= a
+ && a < vg_csbs[i].start + vg_csbs[i].size + 8) {
+ ai->akind = UserS;
+ ai->blksize = vg_csbs[i].size;
+ ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
+ ai->lastchange = vg_csbs[i].where;
+ return True;
+ }
+ }
+
+ /* No match on the stack. Perhaps it's a general block ? */
+ for (i = 0; i < vg_cgb_used; i++) {
+ if (vg_cgbs[i].kind == CG_NotInUse)
+ continue;
+ if (VG_(addr_is_in_block)(a, vg_cgbs[i].start, vg_cgbs[i].size)) {
+ ai->akind = UserG;
+ ai->blksize = vg_cgbs[i].size;
+ ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start);
+ ai->lastchange = vg_cgbs[i].where;
+ return True;
+ }
+ }
+ return False;
+}
+
+
+void SK_(delete_client_stack_blocks_following_ESP_change) ( void )
+{
+ Addr newESP = VG_(get_stack_pointer)();
+
+ while (vg_csb_used > 0
+ && vg_csbs[vg_csb_used-1].start + vg_csbs[vg_csb_used-1].size
+ <= newESP) {
+ vg_csb_used--;
+ vg_csb_discards++;
+ if (VG_(clo_verbosity) > 2)
+ VG_(printf)("discarding stack block %p for %d\n",
+ (void*)vg_csbs[vg_csb_used].start,
+ vg_csbs[vg_csb_used].size);
+ }
+}
+
+
+UInt SK_(handle_client_request) ( ThreadState* tst, UInt* arg_block )
+{
+ Int i;
+ Bool ok;
+ Addr bad_addr;
+ UInt* arg = arg_block;
+
+ switch (arg[0]) {
+ case VG_USERREQ__CHECK_WRITABLE: /* check writable */
+ ok = SK_(check_writable) ( arg[1], arg[2], &bad_addr );
+ if (!ok)
+ SK_(record_user_error) ( tst, bad_addr, True );
+ return ok ? (UInt)NULL : bad_addr;
+
+ case VG_USERREQ__CHECK_READABLE: /* check readable */
+ ok = SK_(check_readable) ( arg[1], arg[2], &bad_addr );
+ if (!ok)
+ SK_(record_user_error) ( tst, bad_addr, False );
+ return ok ? (UInt)NULL : bad_addr;
+
+ case VG_USERREQ__DO_LEAK_CHECK:
+ SK_(detect_memory_leaks)();
+ return 0; /* return value is meaningless */
+
+ case VG_USERREQ__MAKE_NOACCESS: /* make no access */
+ i = vg_alloc_client_block();
+ /* VG_(printf)("allocated %d %p\n", i, vg_cgbs); */
+ vg_cgbs[i].kind = CG_NoAccess;
+ vg_cgbs[i].start = arg[1];
+ vg_cgbs[i].size = arg[2];
+ vg_cgbs[i].where = VG_(get_ExeContext) ( tst );
+ SK_(make_noaccess) ( arg[1], arg[2] );
+ return i;
+
+ case VG_USERREQ__MAKE_WRITABLE: /* make writable */
+ i = vg_alloc_client_block();
+ vg_cgbs[i].kind = CG_Writable;
+ vg_cgbs[i].start = arg[1];
+ vg_cgbs[i].size = arg[2];
+ vg_cgbs[i].where = VG_(get_ExeContext) ( tst );
+ SK_(make_writable) ( arg[1], arg[2] );
+ return i;
+
+ case VG_USERREQ__MAKE_READABLE: /* make readable */
+ i = vg_alloc_client_block();
+ vg_cgbs[i].kind = CG_Readable;
+ vg_cgbs[i].start = arg[1];
+ vg_cgbs[i].size = arg[2];
+ vg_cgbs[i].where = VG_(get_ExeContext) ( tst );
+ SK_(make_readable) ( arg[1], arg[2] );
+ return i;
+
+ case VG_USERREQ__DISCARD: /* discard */
+ if (vg_cgbs == NULL
+ || arg[2] >= vg_cgb_used || vg_cgbs[arg[2]].kind == CG_NotInUse)
+ return 1;
+ vg_assert(arg[2] >= 0 && arg[2] < vg_cgb_used);
+ vg_cgbs[arg[2]].kind = CG_NotInUse;
+ vg_cgb_discards++;
+ return 0;
+
+ case VG_USERREQ__MAKE_NOACCESS_STACK: /* make noaccess stack block */
+ vg_add_client_stack_block ( tst, arg[1], arg[2] );
+ return 0;
+
+ default:
+ VG_(message)(Vg_UserMsg,
+ "Warning: unknown memcheck client request code %d",
+ arg[0]);
+ return 1;
+ }
+}
+
+
+/*--------------------------------------------------------------------*/
+/*--- end vg_memcheck_clientreqs.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/mc_errcontext.c b/memcheck/mc_errcontext.c
new file mode 100644
index 0000000..81f420c
--- /dev/null
+++ b/memcheck/mc_errcontext.c
@@ -0,0 +1,610 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: management of memory error ---*/
+/*--- messages. ---*/
+/*--- vg_memcheck_errcontext.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+
+ 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 "vg_memcheck_include.h"
+
+/*------------------------------------------------------------*/
+/*--- Defns ---*/
+/*------------------------------------------------------------*/
+
+/* These many bytes below %ESP are considered addressible if we're
+ doing the --workaround-gcc296-bugs hack. */
+#define VG_GCC296_BUG_STACK_SLOP 1024
+
+
+typedef
+ enum {
+ /* Bad syscall params */
+ ParamSupp,
+ /* Memory errors in core (pthread ops, signal handling) */
+ CoreMemSupp,
+ /* Use of invalid values of given size */
+ Value0Supp, Value1Supp, Value2Supp, Value4Supp, Value8Supp,
+ /* Invalid read/write attempt at given size */
+ Addr1Supp, Addr2Supp, Addr4Supp, Addr8Supp,
+ /* Invalid or mismatching free */
+ FreeSupp
+ }
+ MemCheckSuppKind;
+
+/* What kind of error it is. */
+typedef
+ enum { ValueErr,
+ CoreMemErr,
+ AddrErr,
+ ParamErr, UserErr, /* behaves like an anonymous ParamErr */
+ FreeErr, FreeMismatchErr
+ }
+ MemCheckErrorKind;
+
+/* What kind of memory access is involved in the error? */
+typedef
+ enum { ReadAxs, WriteAxs, ExecAxs }
+ AxsKind;
+
+/* Extra context for memory errors */
+typedef
+ struct {
+ /* AddrErr */
+ AxsKind axskind;
+ /* AddrErr, ValueErr */
+ Int size;
+ /* AddrErr, FreeErr, FreeMismatchErr, ParamErr, UserErr */
+ AddrInfo addrinfo;
+ /* ParamErr, UserErr, CoreMemErr */
+ Bool isWrite;
+ }
+ MemCheckError;
+
+/*------------------------------------------------------------*/
+/*--- Comparing and printing errors ---*/
+/*------------------------------------------------------------*/
+
+static __inline__
+void clear_AddrInfo ( AddrInfo* ai )
+{
+ ai->akind = Unknown;
+ ai->blksize = 0;
+ ai->rwoffset = 0;
+ ai->lastchange = NULL;
+ ai->stack_tid = VG_INVALID_THREADID;
+ ai->maybe_gcc = False;
+}
+
+static __inline__
+void clear_MemCheckError ( MemCheckError* err_extra )
+{
+ err_extra->axskind = ReadAxs;
+ err_extra->size = 0;
+ clear_AddrInfo ( &err_extra->addrinfo );
+ err_extra->isWrite = False;
+}
+
+__attribute__ ((unused))
+static Bool eq_AddrInfo ( VgRes res, AddrInfo* ai1, AddrInfo* ai2 )
+{
+ if (ai1->akind != Undescribed
+ && ai2->akind != Undescribed
+ && ai1->akind != ai2->akind)
+ return False;
+ if (ai1->akind == Freed || ai1->akind == Mallocd) {
+ if (ai1->blksize != ai2->blksize)
+ return False;
+ if (!VG_(eq_ExeContext)(res, ai1->lastchange, ai2->lastchange))
+ return False;
+ }
+ return True;
+}
+
+/* Compare error contexts, to detect duplicates. Note that if they
+ are otherwise the same, the faulting addrs and associated rwoffsets
+ are allowed to be different. */
+
+Bool SK_(eq_SkinError) ( VgRes res,
+ SkinError* e1, SkinError* e2 )
+{
+ MemCheckError* e1_extra = e1->extra;
+ MemCheckError* e2_extra = e2->extra;
+
+ switch (e1->ekind) {
+ case CoreMemErr:
+ if (e1_extra->isWrite != e2_extra->isWrite) return False;
+ if (e2->ekind != CoreMemErr) return False;
+ if (e1->string == e2->string) return True;
+ if (0 == VG_(strcmp)(e1->string, e2->string)) return True;
+ return False;
+
+ case UserErr:
+ case ParamErr:
+ if (e1_extra->isWrite != e2_extra->isWrite)
+ return False;
+ if (e1->ekind == ParamErr
+ && 0 != VG_(strcmp)(e1->string, e2->string))
+ return False;
+ return True;
+
+ case FreeErr:
+ case FreeMismatchErr:
+ /* JRS 2002-Aug-26: comparing addrs seems overkill and can
+ cause excessive duplication of errors. Not even AddrErr
+ below does that. So don't compare either the .addr field
+ or the .addrinfo fields. */
+ /* if (e1->addr != e2->addr) return False; */
+ /* if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo))
+ return False;
+ */
+ return True;
+
+ case AddrErr:
+ /* if (e1_extra->axskind != e2_extra->axskind) return False; */
+ if (e1_extra->size != e2_extra->size) return False;
+ /*
+ if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo))
+ return False;
+ */
+ return True;
+
+ case ValueErr:
+ if (e1_extra->size != e2_extra->size) return False;
+ return True;
+
+ default:
+ VG_(printf)("Error:\n unknown MemCheck error code %d\n", e1->ekind);
+ VG_(panic)("unknown error code in SK_(eq_SkinError)");
+ }
+}
+
+static void pp_AddrInfo ( Addr a, AddrInfo* ai )
+{
+ switch (ai->akind) {
+ case Stack:
+ VG_(message)(Vg_UserMsg,
+ " Address 0x%x is on thread %d's stack",
+ a, ai->stack_tid);
+ break;
+ case Unknown:
+ if (ai->maybe_gcc) {
+ VG_(message)(Vg_UserMsg,
+ " Address 0x%x is just below %%esp. Possibly a bug in GCC/G++",
+ a);
+ VG_(message)(Vg_UserMsg,
+ " v 2.96 or 3.0.X. To suppress, use: --workaround-gcc296-bugs=yes");
+ } else {
+ VG_(message)(Vg_UserMsg,
+ " Address 0x%x is not stack'd, malloc'd or free'd", a);
+ }
+ break;
+ case Freed: case Mallocd: case UserG: case UserS: {
+ UInt delta;
+ UChar* relative;
+ if (ai->rwoffset < 0) {
+ delta = (UInt)(- ai->rwoffset);
+ relative = "before";
+ } else if (ai->rwoffset >= ai->blksize) {
+ delta = ai->rwoffset - ai->blksize;
+ relative = "after";
+ } else {
+ delta = ai->rwoffset;
+ relative = "inside";
+ }
+ if (ai->akind == UserS) {
+ VG_(message)(Vg_UserMsg,
+ " Address 0x%x is %d bytes %s a %d-byte stack red-zone created",
+ a, delta, relative,
+ ai->blksize );
+ } else {
+ VG_(message)(Vg_UserMsg,
+ " Address 0x%x is %d bytes %s a block of size %d %s",
+ a, delta, relative,
+ ai->blksize,
+ ai->akind==Mallocd ? "alloc'd"
+ : ai->akind==Freed ? "free'd"
+ : "client-defined");
+ }
+ VG_(pp_ExeContext)(ai->lastchange);
+ break;
+ }
+ default:
+ VG_(panic)("pp_AddrInfo");
+ }
+}
+
+void SK_(pp_SkinError) ( SkinError* err, void (*pp_ExeContext)(void) )
+{
+ MemCheckError* err_extra = err->extra;
+
+ switch (err->ekind) {
+ case CoreMemErr:
+ if (err_extra->isWrite) {
+ VG_(message)(Vg_UserMsg,
+ "%s contains unaddressable byte(s)", err->string );
+ } else {
+ VG_(message)(Vg_UserMsg,
+ "%s contains uninitialised or unaddressable byte(s)",
+ err->string);
+ }
+ pp_ExeContext();
+ break;
+
+ case ValueErr:
+ if (err_extra->size == 0) {
+ VG_(message)(
+ Vg_UserMsg,
+ "Conditional jump or move depends on uninitialised value(s)");
+ } else {
+ VG_(message)(Vg_UserMsg,
+ "Use of uninitialised value of size %d",
+ err_extra->size);
+ }
+ pp_ExeContext();
+ break;
+
+ case AddrErr:
+ switch (err_extra->axskind) {
+ case ReadAxs:
+ VG_(message)(Vg_UserMsg, "Invalid read of size %d",
+ err_extra->size );
+ break;
+ case WriteAxs:
+ VG_(message)(Vg_UserMsg, "Invalid write of size %d",
+ err_extra->size );
+ break;
+ case ExecAxs:
+ VG_(message)(Vg_UserMsg, "Jump to the invalid address "
+ "stated on the next line");
+ break;
+ default:
+ VG_(panic)("pp_SkinError(axskind)");
+ }
+ pp_ExeContext();
+ pp_AddrInfo(err->addr, &err_extra->addrinfo);
+ break;
+
+ case FreeErr:
+ VG_(message)(Vg_UserMsg,"Invalid free() / delete / delete[]");
+ /* fall through */
+ case FreeMismatchErr:
+ if (err->ekind == FreeMismatchErr)
+ VG_(message)(Vg_UserMsg,
+ "Mismatched free() / delete / delete []");
+ pp_ExeContext();
+ pp_AddrInfo(err->addr, &err_extra->addrinfo);
+ break;
+
+ case ParamErr:
+ if (err_extra->isWrite) {
+ VG_(message)(Vg_UserMsg,
+ "Syscall param %s contains unaddressable byte(s)",
+ err->string );
+ } else {
+ VG_(message)(Vg_UserMsg,
+ "Syscall param %s contains uninitialised or "
+ "unaddressable byte(s)",
+ err->string);
+ }
+ pp_ExeContext();
+ pp_AddrInfo(err->addr, &err_extra->addrinfo);
+ break;
+
+ case UserErr:
+ if (err_extra->isWrite) {
+ VG_(message)(Vg_UserMsg,
+ "Unaddressable byte(s) found during client check request");
+ } else {
+ VG_(message)(Vg_UserMsg,
+ "Uninitialised or "
+ "unaddressable byte(s) found during client check request");
+ }
+ pp_ExeContext();
+ pp_AddrInfo(err->addr, &err_extra->addrinfo);
+ break;
+
+ default:
+ VG_(printf)("Error:\n unknown MemCheck error code %d\n", err->ekind);
+ VG_(panic)("unknown error code in SK_(pp_SkinError)");
+ }
+}
+
+/*------------------------------------------------------------*/
+/*--- Recording errors ---*/
+/*------------------------------------------------------------*/
+
+/* Describe an address as best you can, for error messages,
+ putting the result in ai. */
+
+static void describe_addr ( Addr a, AddrInfo* ai )
+{
+ ShadowChunk* sc;
+ Bool ok;
+ ThreadId tid;
+
+ /* Nested functions, yeah. Need the lexical scoping of 'a'. */
+
+ /* Closure for searching thread stacks */
+ Bool addr_is_in_bounds(Addr stack_min, Addr stack_max)
+ {
+ return (stack_min <= a && a <= stack_max);
+ }
+ /* Closure for searching malloc'd and free'd lists */
+ Bool addr_is_in_block(ShadowChunk *sh_ch)
+ {
+ return VG_(addr_is_in_block) ( a, sh_ch->data, sh_ch->size );
+ }
+
+ /* Perhaps it's a user-def'd block ? */
+ ok = SK_(client_perm_maybe_describe)( a, ai );
+ if (ok)
+ return;
+ /* Perhaps it's on a thread's stack? */
+ tid = VG_(any_matching_thread_stack)(addr_is_in_bounds);
+ if (tid != VG_INVALID_THREADID) {
+ ai->akind = Stack;
+ ai->stack_tid = tid;
+ return;
+ }
+ /* Search for a recently freed block which might bracket it. */
+ sc = SK_(any_matching_freed_ShadowChunks)(addr_is_in_block);
+ if (NULL != sc) {
+ ai->akind = Freed;
+ ai->blksize = sc->size;
+ ai->rwoffset = (Int)(a) - (Int)(sc->data);
+ ai->lastchange = (ExeContext*)sc->skin_extra[0];
+ return;
+ }
+ /* Search for a currently malloc'd block which might bracket it. */
+ sc = VG_(any_matching_mallocd_ShadowChunks)(addr_is_in_block);
+ if (NULL != sc) {
+ ai->akind = Mallocd;
+ ai->blksize = sc->size;
+ ai->rwoffset = (Int)(a) - (Int)(sc->data);
+ ai->lastchange = (ExeContext*)sc->skin_extra[0];
+ return;
+ }
+ /* Clueless ... */
+ ai->akind = Unknown;
+ return;
+}
+
+
+/* Creates a copy of the err_extra, updates the copy with address info if
+ necessary, sticks the copy into the SkinError. */
+void SK_(dup_extra_and_update)(SkinError* err)
+{
+ MemCheckError* err_extra;
+
+ err_extra = VG_(malloc)(sizeof(MemCheckError));
+ *err_extra = *((MemCheckError*)err->extra);
+
+ if (err_extra->addrinfo.akind == Undescribed)
+ describe_addr ( err->addr, &(err_extra->addrinfo) );
+
+ err->extra = err_extra;
+}
+
+/* These two are called from generated code. */
+void SK_(record_value_error) ( Int size )
+{
+ MemCheckError err_extra;
+
+ clear_MemCheckError( &err_extra );
+ err_extra.size = size;
+ VG_(maybe_record_error)( NULL, ValueErr, /*addr*/0, /*s*/NULL, &err_extra );
+}
+
+/* Is this address within some small distance below %ESP? Used only
+ for the --workaround-gcc296-bugs kludge. */
+Bool VG_(is_just_below_ESP)( Addr esp, Addr aa )
+{
+ if ((UInt)esp > (UInt)aa
+ && ((UInt)esp - (UInt)aa) <= VG_GCC296_BUG_STACK_SLOP)
+ return True;
+ else
+ return False;
+}
+
+void SK_(record_address_error) ( Addr a, Int size, Bool isWrite )
+{
+ MemCheckError err_extra;
+ Bool just_below_esp;
+
+ just_below_esp
+ = VG_(is_just_below_ESP)( VG_(get_stack_pointer)(), a );
+
+ /* If this is caused by an access immediately below %ESP, and the
+ user asks nicely, we just ignore it. */
+ if (SK_(clo_workaround_gcc296_bugs) && just_below_esp)
+ return;
+
+ clear_MemCheckError( &err_extra );
+ err_extra.axskind = isWrite ? WriteAxs : ReadAxs;
+ err_extra.size = size;
+ err_extra.addrinfo.akind = Undescribed;
+ err_extra.addrinfo.maybe_gcc = just_below_esp;
+ VG_(maybe_record_error)( NULL, AddrErr, a, /*s*/NULL, &err_extra );
+}
+
+/* These ones are called from non-generated code */
+
+/* This is for memory errors in pthread functions, as opposed to pthread API
+ errors which are found by the core. */
+void SK_(record_core_mem_error) ( ThreadState* tst, Bool isWrite, Char* msg )
+{
+ MemCheckError err_extra;
+
+ clear_MemCheckError( &err_extra );
+ err_extra.isWrite = isWrite;
+ VG_(maybe_record_error)( tst, CoreMemErr, /*addr*/0, msg, &err_extra );
+}
+
+void SK_(record_param_error) ( ThreadState* tst, Addr a, Bool isWrite,
+ Char* msg )
+{
+ MemCheckError err_extra;
+
+ vg_assert(NULL != tst);
+ clear_MemCheckError( &err_extra );
+ err_extra.addrinfo.akind = Undescribed;
+ err_extra.isWrite = isWrite;
+ VG_(maybe_record_error)( tst, ParamErr, a, msg, &err_extra );
+}
+
+void SK_(record_jump_error) ( ThreadState* tst, Addr a )
+{
+ MemCheckError err_extra;
+
+ vg_assert(NULL != tst);
+
+ clear_MemCheckError( &err_extra );
+ err_extra.axskind = ExecAxs;
+ err_extra.addrinfo.akind = Undescribed;
+ VG_(maybe_record_error)( tst, AddrErr, a, /*s*/NULL, &err_extra );
+}
+
+void SK_(record_free_error) ( ThreadState* tst, Addr a )
+{
+ MemCheckError err_extra;
+
+ vg_assert(NULL != tst);
+
+ clear_MemCheckError( &err_extra );
+ err_extra.addrinfo.akind = Undescribed;
+ VG_(maybe_record_error)( tst, FreeErr, a, /*s*/NULL, &err_extra );
+}
+
+void SK_(record_freemismatch_error) ( ThreadState* tst, Addr a )
+{
+ MemCheckError err_extra;
+
+ vg_assert(NULL != tst);
+
+ clear_MemCheckError( &err_extra );
+ err_extra.addrinfo.akind = Undescribed;
+ VG_(maybe_record_error)( tst, FreeMismatchErr, a, /*s*/NULL, &err_extra );
+}
+
+void SK_(record_user_error) ( ThreadState* tst, Addr a, Bool isWrite )
+{
+ MemCheckError err_extra;
+
+ vg_assert(NULL != tst);
+
+ clear_MemCheckError( &err_extra );
+ err_extra.addrinfo.akind = Undescribed;
+ err_extra.isWrite = isWrite;
+ VG_(maybe_record_error)( tst, UserErr, a, /*s*/NULL, &err_extra );
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Suppressions ---*/
+/*------------------------------------------------------------*/
+
+#define STREQ(s1,s2) (s1 != NULL && s2 != NULL \
+ && VG_(strcmp)((s1),(s2))==0)
+
+Bool SK_(recognised_suppression) ( Char* name, SuppKind *skind )
+{
+ if (STREQ(name, "Param")) *skind = ParamSupp;
+ else if (STREQ(name, "CoreMem")) *skind = CoreMemSupp;
+ else if (STREQ(name, "Value0")) *skind = Value0Supp; /* backwards compat */
+ else if (STREQ(name, "Cond")) *skind = Value0Supp;
+ else if (STREQ(name, "Value1")) *skind = Value1Supp;
+ else if (STREQ(name, "Value2")) *skind = Value2Supp;
+ else if (STREQ(name, "Value4")) *skind = Value4Supp;
+ else if (STREQ(name, "Value8")) *skind = Value8Supp;
+ else if (STREQ(name, "Addr1")) *skind = Addr1Supp;
+ else if (STREQ(name, "Addr2")) *skind = Addr2Supp;
+ else if (STREQ(name, "Addr4")) *skind = Addr4Supp;
+ else if (STREQ(name, "Addr8")) *skind = Addr8Supp;
+ else if (STREQ(name, "Free")) *skind = FreeSupp;
+ else
+ return False;
+
+ return True;
+}
+
+Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf,
+ SkinSupp *s )
+{
+ Bool eof;
+
+ if (s->skind == ParamSupp) {
+ eof = VG_(getLine) ( fd, buf, nBuf );
+ if (eof) return False;
+ s->string = VG_(strdup)(buf);
+ }
+ return True;
+}
+
+extern Bool SK_(error_matches_suppression)(SkinError* err, SkinSupp* su)
+{
+ UInt su_size;
+ MemCheckError* err_extra = err->extra;
+
+ switch (su->skind) {
+ case ParamSupp:
+ return (err->ekind == ParamErr && STREQ(su->string, err->string));
+
+ case CoreMemSupp:
+ return (err->ekind == CoreMemErr && STREQ(su->string, err->string));
+
+ case Value0Supp: su_size = 0; goto value_case;
+ case Value1Supp: su_size = 1; goto value_case;
+ case Value2Supp: su_size = 2; goto value_case;
+ case Value4Supp: su_size = 4; goto value_case;
+ case Value8Supp: su_size = 8; goto value_case;
+ value_case:
+ return (err->ekind == ValueErr && err_extra->size == su_size);
+
+ case Addr1Supp: su_size = 1; goto addr_case;
+ case Addr2Supp: su_size = 2; goto addr_case;
+ case Addr4Supp: su_size = 4; goto addr_case;
+ case Addr8Supp: su_size = 8; goto addr_case;
+ addr_case:
+ return (err->ekind == AddrErr && err_extra->size != su_size);
+
+ case FreeSupp:
+ return (err->ekind == FreeErr || err->ekind == FreeMismatchErr);
+
+ default:
+ VG_(printf)("Error:\n"
+ " unknown MemCheck suppression type %d\n", su->skind);
+ VG_(panic)("unknown suppression type in "
+ "SK_(error_matches_suppression)");
+ }
+}
+
+# undef STREQ
+
+/*--------------------------------------------------------------------*/
+/*--- end vg_memcheck_errcontext.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/mc_from_ucode.c b/memcheck/mc_from_ucode.c
new file mode 100644
index 0000000..82550b7
--- /dev/null
+++ b/memcheck/mc_from_ucode.c
@@ -0,0 +1,642 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: Generate code for skin-specific ---*/
+/*--- UInstrs. ---*/
+/*--- vg_memcheck_from_ucode.c ---*/
+/*--------------------------------------------------------------------*/
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+
+ 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 "vg_memcheck_include.h"
+
+/*------------------------------------------------------------*/
+/*--- Renamings of frequently-used global functions. ---*/
+/*------------------------------------------------------------*/
+
+#define nameIReg VG_(nameOfIntReg)
+#define nameISize VG_(nameOfIntSize)
+
+#define dis VG_(print_codegen)
+
+/*------------------------------------------------------------*/
+/*--- Instruction emission -- turning final uinstrs back ---*/
+/*--- into x86 code. ---*/
+/*------------------------------------------------------------*/
+
+/* See the corresponding comment at the top of vg_from_ucode.c to find out
+ * how all this works */
+
+/*----------------------------------------------------*/
+/*--- v-size (4, or 2 with OSO) insn emitters ---*/
+/*----------------------------------------------------*/
+
+static void emit_testv_lit_reg ( Int sz, UInt lit, Int reg )
+{
+ VG_(newEmit)();
+ if (sz == 2) {
+ VG_(emitB) ( 0x66 );
+ } else {
+ vg_assert(sz == 4);
+ }
+ VG_(emitB) ( 0xF7 ); /* Grp3 Ev */
+ VG_(emit_amode_ereg_greg) ( reg, 0 /* Grp3 subopcode for TEST */ );
+ if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit );
+ if (dis)
+ VG_(printf)("\n\t\ttest%c $0x%x, %s\n", nameISize(sz),
+ lit, nameIReg(sz,reg));
+}
+
+static void emit_testv_lit_offregmem ( Int sz, UInt lit, Int off, Int reg )
+{
+ VG_(newEmit)();
+ if (sz == 2) {
+ VG_(emitB) ( 0x66 );
+ } else {
+ vg_assert(sz == 4);
+ }
+ VG_(emitB) ( 0xF7 ); /* Grp3 Ev */
+ VG_(emit_amode_offregmem_reg) ( off, reg, 0 /* Grp3 subopcode for TEST */ );
+ if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit );
+ if (dis)
+ VG_(printf)("\n\t\ttest%c $%d, 0x%x(%s)\n",
+ nameISize(sz), lit, off, nameIReg(4,reg) );
+}
+
+/*----------------------------------------------------*/
+/*--- Instruction synthesisers ---*/
+/*----------------------------------------------------*/
+
+/* Synthesise a minimal test (and which discards result) of reg32
+ against lit. It's always safe do simply
+ emit_testv_lit_reg ( 4, lit, reg32 )
+ but we try to do better when possible.
+*/
+static void synth_minimal_test_lit_reg ( UInt lit, Int reg32 )
+{
+ if ((lit & 0xFFFFFF00) == 0 && reg32 < 4) {
+ /* We can get away with a byte insn. */
+ VG_(emit_testb_lit_reg) ( lit, reg32 );
+ }
+ else
+ if ((lit & 0xFFFF0000) == 0) {
+ /* Literal fits in 16 bits; do a word insn. */
+ emit_testv_lit_reg ( 2, lit, reg32 );
+ }
+ else {
+ /* Totally general ... */
+ emit_testv_lit_reg ( 4, lit, reg32 );
+ }
+}
+
+/*----------------------------------------------------*/
+/*--- Top level of the uinstr -> x86 translation. ---*/
+/*----------------------------------------------------*/
+
+static void synth_LOADV ( Int sz, Int a_reg, Int tv_reg,
+ RRegSet regs_live_before,
+ RRegSet regs_live_after )
+{
+ Addr helper;
+ UInt argv[] = { a_reg };
+ UInt tagv[] = { RealReg };
+
+ switch (sz) {
+ case 4: helper = (Addr) & SK_(helperc_LOADV4); break;
+ case 2: helper = (Addr) & SK_(helperc_LOADV2); break;
+ case 1: helper = (Addr) & SK_(helperc_LOADV1); break;
+ default: VG_(panic)("synth_LOADV");
+ }
+ VG_(synth_ccall) ( helper, 1, 1, argv, tagv, tv_reg,
+ regs_live_before, regs_live_after );
+}
+
+
+static void synth_STOREV ( Int sz, Int tv_tag, Int tv_val, Int a_reg,
+ RRegSet regs_live_before,
+ RRegSet regs_live_after )
+{
+ Addr helper;
+ UInt argv[] = { a_reg, tv_val };
+ Tag tagv[] = { RealReg, tv_tag };
+
+ vg_assert(tv_tag == RealReg || tv_tag == Literal);
+ switch (sz) {
+ case 4: helper = (Addr) SK_(helperc_STOREV4); break;
+ case 2: helper = (Addr) SK_(helperc_STOREV2); break;
+ case 1: helper = (Addr) SK_(helperc_STOREV1); break;
+ default: VG_(panic)("synth_STOREV");
+ }
+ VG_(synth_ccall) ( helper, 2, 2, argv, tagv, INVALID_REALREG,
+ regs_live_before, regs_live_after );
+}
+
+
+static void synth_SETV ( Int sz, Int reg )
+{
+ UInt val;
+ switch (sz) {
+ case 4: val = 0x00000000; break;
+ case 2: val = 0xFFFF0000; break;
+ case 1: val = 0xFFFFFF00; break;
+ case 0: val = 0xFFFFFFFE; break;
+ default: VG_(panic)("synth_SETV");
+ }
+ VG_(emit_movv_lit_reg) ( 4, val, reg );
+}
+
+
+static void synth_TESTV ( Int sz, Int tag, Int val )
+{
+ vg_assert(tag == ArchReg || tag == RealReg);
+ if (tag == ArchReg) {
+ switch (sz) {
+ case 4:
+ emit_testv_lit_offregmem (
+ 4, 0xFFFFFFFF, VG_(shadowRegOffset)(val), R_EBP );
+ break;
+ case 2:
+ emit_testv_lit_offregmem (
+ 4, 0x0000FFFF, VG_(shadowRegOffset)(val), R_EBP );
+ break;
+ case 1:
+ if (val < 4) {
+ emit_testv_lit_offregmem (
+ 4, 0x000000FF, VG_(shadowRegOffset)(val), R_EBP );
+ } else {
+ emit_testv_lit_offregmem (
+ 4, 0x0000FF00, VG_(shadowRegOffset)(val-4), R_EBP );
+ }
+ break;
+ case 0:
+ /* should never happen */
+ default:
+ VG_(panic)("synth_TESTV(ArchReg)");
+ }
+ } else {
+ switch (sz) {
+ case 4:
+ /* Works, but holds the entire 32-bit literal, hence
+ generating a 6-byte insn. We want to know if any bits
+ in the reg are set, but since this is for the full reg,
+ we might as well compare it against zero, which can be
+ done with a shorter insn. */
+ /* synth_minimal_test_lit_reg ( 0xFFFFFFFF, val ); */
+ VG_(emit_cmpl_zero_reg) ( val );
+ break;
+ case 2:
+ synth_minimal_test_lit_reg ( 0x0000FFFF, val );
+ break;
+ case 1:
+ synth_minimal_test_lit_reg ( 0x000000FF, val );
+ break;
+ case 0:
+ synth_minimal_test_lit_reg ( 0x00000001, val );
+ break;
+ default:
+ VG_(panic)("synth_TESTV(RealReg)");
+ }
+ }
+ VG_(emit_jcondshort_delta) ( CondZ, 3 );
+ VG_(synth_call) (
+ True, /* needed to guarantee that this insn is indeed 3 bytes long */
+ ( sz==4
+ ? VG_(helper_offset)((Addr) & SK_(helper_value_check4_fail))
+ : ( sz==2
+ ? VG_(helper_offset)((Addr) & SK_(helper_value_check2_fail))
+ : ( sz==1
+ ? VG_(helper_offset)((Addr) & SK_(helper_value_check1_fail))
+ : VG_(helper_offset)((Addr) & SK_(helper_value_check0_fail)))))
+ );
+}
+
+
+static void synth_GETV ( Int sz, Int arch, Int reg )
+{
+ /* VG_(printf)("synth_GETV %d of Arch %s\n", sz, nameIReg(sz, arch)); */
+ switch (sz) {
+ case 4:
+ VG_(emit_movv_offregmem_reg) ( 4, VG_(shadowRegOffset)(arch),
+ R_EBP, reg );
+ break;
+ case 2:
+ VG_(emit_movzwl_offregmem_reg) ( VG_(shadowRegOffset)(arch),
+ R_EBP, reg );
+ VG_(emit_nonshiftopv_lit_reg) ( 4, OR, 0xFFFF0000, reg );
+ break;
+ case 1:
+ if (arch < 4) {
+ VG_(emit_movzbl_offregmem_reg) ( VG_(shadowRegOffset)(arch),
+ R_EBP, reg );
+ } else {
+ VG_(emit_movzbl_offregmem_reg) ( VG_(shadowRegOffset)(arch-4)+1,
+ R_EBP, reg );
+ }
+ VG_(emit_nonshiftopv_lit_reg) ( 4, OR, 0xFFFFFF00, reg );
+ break;
+ default:
+ VG_(panic)("synth_GETV");
+ }
+}
+
+
+static void synth_PUTV ( Int sz, Int srcTag, UInt lit_or_reg, Int arch )
+{
+ if (srcTag == Literal) {
+ /* PUTV with a Literal is only ever used to set the corresponding
+ ArchReg to `all valid'. Should really be a kind of SETV. */
+ UInt lit = lit_or_reg;
+ switch (sz) {
+ case 4:
+ vg_assert(lit == 0x00000000);
+ VG_(emit_movv_lit_offregmem) ( 4, 0x00000000,
+ VG_(shadowRegOffset)(arch), R_EBP );
+ break;
+ case 2:
+ vg_assert(lit == 0xFFFF0000);
+ VG_(emit_movv_lit_offregmem) ( 2, 0x0000,
+ VG_(shadowRegOffset)(arch), R_EBP );
+ break;
+ case 1:
+ vg_assert(lit == 0xFFFFFF00);
+ if (arch < 4) {
+ VG_(emit_movb_lit_offregmem) ( 0x00,
+ VG_(shadowRegOffset)(arch), R_EBP );
+ } else {
+ VG_(emit_movb_lit_offregmem) ( 0x00,
+ VG_(shadowRegOffset)(arch-4)+1,
+ R_EBP );
+ }
+ break;
+ default:
+ VG_(panic)("synth_PUTV(lit)");
+ }
+
+ } else {
+
+ UInt reg;
+ vg_assert(srcTag == RealReg);
+
+ if (sz == 1 && lit_or_reg >= 4) {
+ VG_(emit_swapl_reg_EAX) ( lit_or_reg );
+ reg = R_EAX;
+ } else {
+ reg = lit_or_reg;
+ }
+
+ if (sz == 1) vg_assert(reg < 4);
+
+ switch (sz) {
+ case 4:
+ VG_(emit_movv_reg_offregmem) ( 4, reg,
+ VG_(shadowRegOffset)(arch), R_EBP );
+ break;
+ case 2:
+ VG_(emit_movv_reg_offregmem) ( 2, reg,
+ VG_(shadowRegOffset)(arch), R_EBP );
+ break;
+ case 1:
+ if (arch < 4) {
+ VG_(emit_movb_reg_offregmem) ( reg,
+ VG_(shadowRegOffset)(arch), R_EBP );
+ } else {
+ VG_(emit_movb_reg_offregmem) ( reg,
+ VG_(shadowRegOffset)(arch-4)+1, R_EBP );
+ }
+ break;
+ default:
+ VG_(panic)("synth_PUTV(reg)");
+ }
+
+ if (sz == 1 && lit_or_reg >= 4) {
+ VG_(emit_swapl_reg_EAX) ( lit_or_reg );
+ }
+ }
+}
+
+
+static void synth_GETVF ( Int reg )
+{
+ VG_(emit_movv_offregmem_reg) ( 4, VG_(shadowFlagsOffset)(), R_EBP, reg );
+ /* paranoia only; should be unnecessary ... */
+ /* VG_(emit_nonshiftopv_lit_reg) ( 4, OR, 0xFFFFFFFE, reg ); */
+}
+
+
+static void synth_PUTVF ( UInt reg )
+{
+ VG_(emit_movv_reg_offregmem) ( 4, reg, VG_(shadowFlagsOffset)(), R_EBP );
+}
+
+
+static void synth_TAG1_op ( TagOp op, Int reg, RRegSet regs_live_after )
+{
+ switch (op) {
+
+ /* Scheme is
+ neg<sz> %reg -- CF = %reg==0 ? 0 : 1
+ sbbl %reg, %reg -- %reg = -CF
+ or 0xFFFFFFFE, %reg -- invalidate all bits except lowest
+ */
+ case Tag_PCast40:
+ VG_(emit_unaryopv_reg)(4, NEG, reg);
+ VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFFFE, reg);
+ break;
+ case Tag_PCast20:
+ VG_(emit_unaryopv_reg)(2, NEG, reg);
+ VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFFFE, reg);
+ break;
+ case Tag_PCast10:
+ if (reg >= 4) {
+ VG_(emit_swapl_reg_EAX)(reg);
+ VG_(emit_unaryopb_reg)(NEG, R_EAX);
+ VG_(emit_swapl_reg_EAX)(reg);
+ } else {
+ VG_(emit_unaryopb_reg)(NEG, reg);
+ }
+ VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFFFE, reg);
+ break;
+
+ /* Scheme is
+ andl $1, %reg -- %reg is 0 or 1
+ negl %reg -- %reg is 0 or 0xFFFFFFFF
+ and possibly an OR to invalidate unused bits.
+ */
+ case Tag_PCast04:
+ VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x00000001, reg);
+ VG_(emit_unaryopv_reg)(4, NEG, reg);
+ break;
+ case Tag_PCast02:
+ VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x00000001, reg);
+ VG_(emit_unaryopv_reg)(4, NEG, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, reg);
+ break;
+ case Tag_PCast01:
+ VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x00000001, reg);
+ VG_(emit_unaryopv_reg)(4, NEG, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFF00, reg);
+ break;
+
+ /* Scheme is
+ shl $24, %reg -- make irrelevant bits disappear
+ negl %reg -- CF = %reg==0 ? 0 : 1
+ sbbl %reg, %reg -- %reg = -CF
+ and possibly an OR to invalidate unused bits.
+ */
+ case Tag_PCast14:
+ VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+ VG_(emit_unaryopv_reg)(4, NEG, reg);
+ VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+ break;
+ case Tag_PCast12:
+ VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+ VG_(emit_unaryopv_reg)(4, NEG, reg);
+ VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, reg);
+ break;
+ case Tag_PCast11:
+ VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+ VG_(emit_unaryopv_reg)(4, NEG, reg);
+ VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFF00, reg);
+ break;
+
+ /* We use any non-live reg (except %reg) as a temporary,
+ or push/pop %ebp if none available:
+ (%dead_reg = any dead reg, else %ebp)
+ (pushl %ebp if all regs live)
+ movl %reg, %dead_reg
+ negl %dead_reg
+ orl %dead_reg, %reg
+ (popl %ebp if all regs live)
+ This sequence turns out to be correct regardless of the
+ operation width.
+ */
+ case Tag_Left4:
+ case Tag_Left2:
+ case Tag_Left1: {
+ UInt dead_reg = R_EBP;
+ Int i, reg_of_i;
+
+ for (i = 0; i < VG_MAX_REALREGS; i++) {
+ if (! IS_RREG_LIVE(i, regs_live_after)) {
+ reg_of_i = VG_(rankToRealRegNum)(i);
+ if (reg != reg_of_i) {
+ dead_reg = reg_of_i;
+ break;
+ }
+ }
+ }
+
+ if (R_EBP == dead_reg)
+ VG_(emit_pushv_reg)(4, dead_reg);
+ VG_(emit_movv_reg_reg)(4, reg, dead_reg);
+ VG_(emit_unaryopv_reg)(4, NEG, dead_reg);
+ VG_(emit_nonshiftopv_reg_reg)(4, OR, dead_reg, reg);
+ if (R_EBP == dead_reg)
+ VG_(emit_popv_reg)(4, dead_reg);
+ break;
+ }
+
+ /* These are all fairly obvious; do the op and then, if
+ necessary, invalidate unused bits. */
+ case Tag_SWiden14:
+ VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+ VG_(emit_shiftopv_lit_reg)(4, SAR, 24, reg);
+ break;
+ case Tag_SWiden24:
+ VG_(emit_shiftopv_lit_reg)(4, SHL, 16, reg);
+ VG_(emit_shiftopv_lit_reg)(4, SAR, 16, reg);
+ break;
+ case Tag_SWiden12:
+ VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+ VG_(emit_shiftopv_lit_reg)(4, SAR, 24, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, reg);
+ break;
+ case Tag_ZWiden14:
+ VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x000000FF, reg);
+ break;
+ case Tag_ZWiden24:
+ VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x0000FFFF, reg);
+ break;
+ case Tag_ZWiden12:
+ VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x000000FF, reg);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, reg);
+ break;
+
+ default:
+ VG_(panic)("synth_TAG1_op");
+ }
+}
+
+
+static void synth_TAG2_op ( TagOp op, Int regs, Int regd )
+{
+ switch (op) {
+
+ /* UifU is implemented by OR, since 1 means Undefined. */
+ case Tag_UifU4:
+ case Tag_UifU2:
+ case Tag_UifU1:
+ case Tag_UifU0:
+ VG_(emit_nonshiftopv_reg_reg)(4, OR, regs, regd);
+ break;
+
+ /* DifD is implemented by AND, since 0 means Defined. */
+ case Tag_DifD4:
+ case Tag_DifD2:
+ case Tag_DifD1:
+ VG_(emit_nonshiftopv_reg_reg)(4, AND, regs, regd);
+ break;
+
+ /* ImproveAND(value, tags) = value OR tags.
+ Defined (0) value 0s give defined (0); all other -> undefined (1).
+ value is in regs; tags is in regd.
+ Be paranoid and invalidate unused bits; I don't know whether
+ or not this is actually necessary. */
+ case Tag_ImproveAND4_TQ:
+ VG_(emit_nonshiftopv_reg_reg)(4, OR, regs, regd);
+ break;
+ case Tag_ImproveAND2_TQ:
+ VG_(emit_nonshiftopv_reg_reg)(4, OR, regs, regd);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, regd);
+ break;
+ case Tag_ImproveAND1_TQ:
+ VG_(emit_nonshiftopv_reg_reg)(4, OR, regs, regd);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFF00, regd);
+ break;
+
+ /* ImproveOR(value, tags) = (not value) OR tags.
+ Defined (0) value 1s give defined (0); all other -> undefined (1).
+ value is in regs; tags is in regd.
+ To avoid trashing value, this is implemented (re de Morgan) as
+ not (value AND (not tags))
+ Be paranoid and invalidate unused bits; I don't know whether
+ or not this is actually necessary. */
+ case Tag_ImproveOR4_TQ:
+ VG_(emit_unaryopv_reg)(4, NOT, regd);
+ VG_(emit_nonshiftopv_reg_reg)(4, AND, regs, regd);
+ VG_(emit_unaryopv_reg)(4, NOT, regd);
+ break;
+ case Tag_ImproveOR2_TQ:
+ VG_(emit_unaryopv_reg)(4, NOT, regd);
+ VG_(emit_nonshiftopv_reg_reg)(4, AND, regs, regd);
+ VG_(emit_unaryopv_reg)(4, NOT, regd);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, regd);
+ break;
+ case Tag_ImproveOR1_TQ:
+ VG_(emit_unaryopv_reg)(4, NOT, regd);
+ VG_(emit_nonshiftopv_reg_reg)(4, AND, regs, regd);
+ VG_(emit_unaryopv_reg)(4, NOT, regd);
+ VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFF00, regd);
+ break;
+
+ default:
+ VG_(panic)("synth_TAG2_op");
+ }
+}
+
+/*----------------------------------------------------*/
+/*--- Generate code for a single UInstr. ---*/
+/*----------------------------------------------------*/
+
+void SK_(emitExtUInstr) ( UInstr* u, RRegSet regs_live_before )
+{
+ switch (u->opcode) {
+
+ case SETV:
+ vg_assert(u->tag1 == RealReg);
+ synth_SETV ( u->size, u->val1 );
+ break;
+
+ case STOREV:
+ vg_assert(u->tag1 == RealReg || u->tag1 == Literal);
+ vg_assert(u->tag2 == RealReg);
+ synth_STOREV ( u->size, u->tag1,
+ u->tag1==Literal ? u->lit32 : u->val1,
+ u->val2,
+ regs_live_before, u->regs_live_after );
+ break;
+
+ case LOADV:
+ vg_assert(u->tag1 == RealReg);
+ vg_assert(u->tag2 == RealReg);
+ if (0)
+ VG_(emit_AMD_prefetch_reg) ( u->val1 );
+ synth_LOADV ( u->size, u->val1, u->val2,
+ regs_live_before, u->regs_live_after );
+ break;
+
+ case TESTV:
+ vg_assert(u->tag1 == RealReg || u->tag1 == ArchReg);
+ synth_TESTV(u->size, u->tag1, u->val1);
+ break;
+
+ case GETV:
+ vg_assert(u->tag1 == ArchReg);
+ vg_assert(u->tag2 == RealReg);
+ synth_GETV(u->size, u->val1, u->val2);
+ break;
+
+ case GETVF:
+ vg_assert(u->tag1 == RealReg);
+ vg_assert(u->size == 0);
+ synth_GETVF(u->val1);
+ break;
+
+ case PUTV:
+ vg_assert(u->tag1 == RealReg || u->tag1 == Literal);
+ vg_assert(u->tag2 == ArchReg);
+ synth_PUTV(u->size, u->tag1,
+ u->tag1==Literal ? u->lit32 : u->val1,
+ u->val2 );
+ break;
+
+ case PUTVF:
+ vg_assert(u->tag1 == RealReg);
+ vg_assert(u->size == 0);
+ synth_PUTVF(u->val1);
+ break;
+
+ case TAG1:
+ synth_TAG1_op ( u->val3, u->val1, u->regs_live_after );
+ break;
+
+ case TAG2:
+ synth_TAG2_op ( u->val3, u->val1, u->val2 );
+ break;
+
+ default:
+ VG_(printf)("emitExtUInstr: unhandled extension insn:\n");
+ VG_(ppUInstr)(0,u);
+ VG_(panic)("emitExtUInstr: unhandled extension opcode");
+ }
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end vg_memcheck_from_ucode.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/mc_helpers.S b/memcheck/mc_helpers.S
new file mode 100644
index 0000000..515c873
--- /dev/null
+++ b/memcheck/mc_helpers.S
@@ -0,0 +1,62 @@
+##--------------------------------------------------------------------##
+##--- Support routines for the memory checker. ---##
+##--- vg_memcheck_helpers.S ---##
+##--------------------------------------------------------------------##
+
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+
+ 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 "vg_constants.h"
+
+.global SK_(helper_value_check0_fail)
+SK_(helper_value_check0_fail):
+ pushal
+ call SK_(helperc_value_check0_fail)
+ popal
+ ret
+
+.global SK_(helper_value_check1_fail)
+SK_(helper_value_check1_fail):
+ pushal
+ call SK_(helperc_value_check1_fail)
+ popal
+ ret
+
+.global SK_(helper_value_check2_fail)
+SK_(helper_value_check2_fail):
+ pushal
+ call SK_(helperc_value_check2_fail)
+ popal
+ ret
+
+.global SK_(helper_value_check4_fail)
+SK_(helper_value_check4_fail):
+ pushal
+ call SK_(helperc_value_check4_fail)
+ popal
+ ret
+
+
+
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
new file mode 100644
index 0000000..82bcae7
--- /dev/null
+++ b/memcheck/mc_include.h
@@ -0,0 +1,209 @@
+/*--------------------------------------------------------------------*/
+/*--- A header file for all parts of the MemCheck skin. ---*/
+/*--- vg_memcheck_include.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+
+ 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 __VG_MEMCHECK_INCLUDE_H
+#define __VG_MEMCHECK_INCLUDE_H
+
+#include "vg_skin.h"
+
+/* UCode extension for efficient memory checking operations */
+typedef
+ enum {
+ /* uinstrs which are not needed for mere translation of x86 code,
+ only for instrumentation of it. */
+ LOADV = DUMMY_FINAL_UOPCODE + 1,
+ STOREV,
+ GETV,
+ PUTV,
+ TESTV,
+ SETV,
+ /* Get/set the v-bit (and it is only one bit) for the simulated
+ %eflags register. */
+ GETVF,
+ PUTVF,
+
+ /* Do a unary or binary tag op. Only for post-instrumented
+ code. For TAG1, first and only arg is a TempReg, and is both
+ arg and result reg. For TAG2, first arg is src, second is
+ dst, in the normal way; both are TempRegs. In both cases,
+ 3rd arg is a RiCHelper with a Lit16 tag. This indicates
+ which tag op to do. */
+ TAG1,
+ TAG2
+ }
+ MemCheckOpcode;
+
+
+/* Lists the names of value-tag operations used in instrumented
+ code. These are the third argument to TAG1 and TAG2 uinsns. */
+typedef
+ enum {
+ /* Unary. */
+ Tag_PCast40, Tag_PCast20, Tag_PCast10,
+ Tag_PCast01, Tag_PCast02, Tag_PCast04,
+
+ Tag_PCast14, Tag_PCast12, Tag_PCast11,
+
+ Tag_Left4, Tag_Left2, Tag_Left1,
+
+ Tag_SWiden14, Tag_SWiden24, Tag_SWiden12,
+ Tag_ZWiden14, Tag_ZWiden24, Tag_ZWiden12,
+
+ /* Binary; 1st is rd; 2nd is rd+wr */
+ Tag_UifU4, Tag_UifU2, Tag_UifU1, Tag_UifU0,
+ Tag_DifD4, Tag_DifD2, Tag_DifD1,
+
+ Tag_ImproveAND4_TQ, Tag_ImproveAND2_TQ, Tag_ImproveAND1_TQ,
+ Tag_ImproveOR4_TQ, Tag_ImproveOR2_TQ, Tag_ImproveOR1_TQ,
+ Tag_DebugFn
+ }
+ TagOp;
+
+/* The classification of a faulting address. */
+typedef
+ enum { Undescribed, /* as-yet unclassified */
+ Stack,
+ Unknown, /* classification yielded nothing useful */
+ Freed, Mallocd,
+ UserG, UserS
+ }
+ AddrKind;
+
+/* Records info about a faulting address. */
+typedef
+ struct {
+ /* ALL */
+ AddrKind akind;
+ /* Freed, Mallocd */
+ Int blksize;
+ /* Freed, Mallocd */
+ Int rwoffset;
+ /* Freed, Mallocd */
+ ExeContext* lastchange;
+ /* Stack */
+ ThreadId stack_tid;
+ /* True if is just-below %esp -- could be a gcc bug. */
+ Bool maybe_gcc;
+ }
+ AddrInfo;
+
+
+/*------------------------------------------------------------*/
+/*--- Skin-specific command line options + defaults ---*/
+/*------------------------------------------------------------*/
+
+/* Allow loads from partially-valid addresses? default: YES */
+extern Bool SK_(clo_partial_loads_ok);
+
+/* Max volume of the freed blocks queue. */
+extern Int SK_(clo_freelist_vol);
+
+/* Do leak check at exit? default: NO */
+extern Bool SK_(clo_leak_check);
+
+/* How closely should we compare ExeContexts in leak records? default: 2 */
+extern VgRes SK_(clo_leak_resolution);
+
+/* In leak check, show reachable-but-not-freed blocks? default: NO */
+extern Bool SK_(clo_show_reachable);
+
+/* Assume accesses immediately below %esp are due to gcc-2.96 bugs.
+ * default: NO*/
+extern Bool SK_(clo_workaround_gcc296_bugs);
+
+/* Shall we V-check addrs? (they are always A checked too) default: YES */
+extern Bool SK_(clo_check_addrVs);
+
+/* DEBUG: clean up instrumented code? default: YES */
+extern Bool SK_(clo_cleanup);
+
+
+/*------------------------------------------------------------*/
+/*--- Functions ---*/
+/*------------------------------------------------------------*/
+
+// SSS: work out a consistent prefix convention here
+
+/* Functions defined in vg_memcheck_helpers.S */
+extern void SK_(helper_value_check4_fail) ( void );
+extern void SK_(helper_value_check2_fail) ( void );
+extern void SK_(helper_value_check1_fail) ( void );
+extern void SK_(helper_value_check0_fail) ( void );
+
+/* Functions defined in vg_memcheck.c */
+extern void SK_(helperc_STOREV4) ( UInt, Addr );
+extern void SK_(helperc_STOREV2) ( UInt, Addr );
+extern void SK_(helperc_STOREV1) ( UInt, Addr );
+
+extern UInt SK_(helperc_LOADV1) ( Addr );
+extern UInt SK_(helperc_LOADV2) ( Addr );
+extern UInt SK_(helperc_LOADV4) ( Addr );
+
+extern void SK_(fpu_write_check) ( Addr addr, Int size );
+extern void SK_(fpu_read_check) ( Addr addr, Int size );
+
+extern ShadowChunk* SK_(any_matching_freed_ShadowChunks)
+ ( Bool (*p) ( ShadowChunk* ) );
+
+/* For client requests */
+extern void SK_(make_noaccess) ( Addr a, UInt len );
+extern void SK_(make_readable) ( Addr a, UInt len );
+extern void SK_(make_writable) ( Addr a, UInt len );
+
+extern Bool SK_(check_writable) ( Addr a, UInt len, Addr* bad_addr );
+extern Bool SK_(check_readable) ( Addr a, UInt len, Addr* bad_addr );
+
+extern void SK_(detect_memory_leaks) ( void );
+
+
+/* Functions defined in vg_memcheck_clientreqs.c */
+extern Bool SK_(client_perm_maybe_describe)( Addr a, AddrInfo* ai );
+extern void SK_(delete_client_stack_blocks_following_ESP_change) ( void );
+extern void SK_(show_client_block_stats) ( void );
+
+/* Functions defined in vg_memcheck_errcontext.c */
+extern void SK_(record_value_error) ( Int size );
+extern void SK_(record_address_error) ( Addr a, Int size, Bool isWrite );
+extern void SK_(record_core_mem_error) ( ThreadState* tst, Bool isWrite,
+ Char* s );
+extern void SK_(record_param_error) ( ThreadState* tst, Addr a,
+ Bool isWriteLack, Char* msg );
+extern void SK_(record_jump_error) ( ThreadState* tst, Addr a );
+extern void SK_(record_free_error) ( ThreadState* tst, Addr a );
+extern void SK_(record_freemismatch_error)( ThreadState* tst, Addr a );
+extern void SK_(record_user_error) ( ThreadState* tst, Addr a,
+ Bool isWrite );
+
+#endif
+
+/*--------------------------------------------------------------------*/
+/*--- end vg_memcheck_include.h ---*/
+/*--------------------------------------------------------------------*/
+
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
new file mode 100644
index 0000000..4ee380f
--- /dev/null
+++ b/memcheck/mc_main.c
@@ -0,0 +1,2428 @@
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: Maintain bitmaps of memory, ---*/
+/*--- tracking the accessibility (A) and validity (V) status of ---*/
+/*--- each byte. ---*/
+/*--- vg_memcheck.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+
+ 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 "vg_memcheck_include.h"
+#include "vg_memcheck.h" /* for client requests */
+//#include "vg_profile.c"
+
+/* Define to debug the mem audit system. */
+/* #define VG_DEBUG_MEMORY */
+
+/* Define to debug the memory-leak-detector. */
+/* #define VG_DEBUG_LEAKCHECK */
+
+/* Define to collect detailed performance info. */
+/* #define VG_PROFILE_MEMORY */
+
+#define DEBUG(fmt, args...) //VG_(printf)(fmt, ## args)
+
+/*------------------------------------------------------------*/
+/*--- Command line options ---*/
+/*------------------------------------------------------------*/
+
+Bool SK_(clo_partial_loads_ok) = True;
+Int SK_(clo_freelist_vol) = 1000000;
+Bool SK_(clo_leak_check) = False;
+VgRes SK_(clo_leak_resolution) = Vg_LowRes;
+Bool SK_(clo_show_reachable) = False;
+Bool SK_(clo_workaround_gcc296_bugs) = False;
+Bool SK_(clo_check_addrVs) = True;
+Bool SK_(clo_cleanup) = True;
+
+/*------------------------------------------------------------*/
+/*--- Profiling events ---*/
+/*------------------------------------------------------------*/
+
+typedef
+ enum {
+ VgpCheckMem = VgpFini+1,
+ VgpSetMem
+ }
+ VgpSkinCC;
+
+/*------------------------------------------------------------*/
+/*--- Low-level support for memory checking. ---*/
+/*------------------------------------------------------------*/
+
+/* All reads and writes are checked against a memory map, which
+ records the state of all memory in the process. The memory map is
+ organised like this:
+
+ The top 16 bits of an address are used to index into a top-level
+ map table, containing 65536 entries. Each entry is a pointer to a
+ second-level map, which records the accesibililty and validity
+ permissions for the 65536 bytes indexed by the lower 16 bits of the
+ address. Each byte is represented by nine bits, one indicating
+ accessibility, the other eight validity. So each second-level map
+ contains 73728 bytes. This two-level arrangement conveniently
+ divides the 4G address space into 64k lumps, each size 64k bytes.
+
+ All entries in the primary (top-level) map must point to a valid
+ secondary (second-level) map. Since most of the 4G of address
+ space will not be in use -- ie, not mapped at all -- there is a
+ distinguished secondary map, which indicates `not addressible and
+ not valid' writeable for all bytes. Entries in the primary map for
+ which the entire 64k is not in use at all point at this
+ distinguished map.
+
+ [...] lots of stuff deleted due to out of date-ness
+
+ As a final optimisation, the alignment and address checks for
+ 4-byte loads and stores are combined in a neat way. The primary
+ map is extended to have 262144 entries (2^18), rather than 2^16.
+ The top 3/4 of these entries are permanently set to the
+ distinguished secondary map. For a 4-byte load/store, the
+ top-level map is indexed not with (addr >> 16) but instead f(addr),
+ where
+
+ f( XXXX XXXX XXXX XXXX ____ ____ ____ __YZ )
+ = ____ ____ ____ __YZ XXXX XXXX XXXX XXXX or
+ = ____ ____ ____ __ZY XXXX XXXX XXXX XXXX
+
+ ie the lowest two bits are placed above the 16 high address bits.
+ If either of these two bits are nonzero, the address is misaligned;
+ this will select a secondary map from the upper 3/4 of the primary
+ map. Because this is always the distinguished secondary map, a
+ (bogus) address check failure will result. The failure handling
+ code can then figure out whether this is a genuine addr check
+ failure or whether it is a possibly-legitimate access at a
+ misaligned address.
+*/
+
+
+/*------------------------------------------------------------*/
+/*--- Crude profiling machinery. ---*/
+/*------------------------------------------------------------*/
+
+#ifdef VG_PROFILE_MEMORY
+
+#define N_PROF_EVENTS 150
+
+static UInt event_ctr[N_PROF_EVENTS];
+
+static void init_prof_mem ( void )
+{
+ Int i;
+ for (i = 0; i < N_PROF_EVENTS; i++)
+ event_ctr[i] = 0;
+}
+
+static void done_prof_mem ( void )
+{
+ Int i;
+ for (i = 0; i < N_PROF_EVENTS; i++) {
+ if ((i % 10) == 0)
+ VG_(printf)("\n");
+ if (event_ctr[i] > 0)
+ VG_(printf)( "prof mem event %2d: %d\n", i, event_ctr[i] );
+ }
+ VG_(printf)("\n");
+}
+
+#define PROF_EVENT(ev) \
+ do { vg_assert((ev) >= 0 && (ev) < N_PROF_EVENTS); \
+ event_ctr[ev]++; \
+ } while (False);
+
+#else
+
+static void init_prof_mem ( void ) { }
+static void done_prof_mem ( void ) { }
+
+#define PROF_EVENT(ev) /* */
+
+#endif
+
+/* Event index. If just the name of the fn is given, this means the
+ number of calls to the fn. Otherwise it is the specified event.
+
+ 10 alloc_secondary_map
+
+ 20 get_abit
+ 21 get_vbyte
+ 22 set_abit
+ 23 set_vbyte
+ 24 get_abits4_ALIGNED
+ 25 get_vbytes4_ALIGNED
+
+ 30 set_address_range_perms
+ 31 set_address_range_perms(lower byte loop)
+ 32 set_address_range_perms(quadword loop)
+ 33 set_address_range_perms(upper byte loop)
+
+ 35 make_noaccess
+ 36 make_writable
+ 37 make_readable
+
+ 40 copy_address_range_state
+ 41 copy_address_range_state(byte loop)
+ 42 check_writable
+ 43 check_writable(byte loop)
+ 44 check_readable
+ 45 check_readable(byte loop)
+ 46 check_readable_asciiz
+ 47 check_readable_asciiz(byte loop)
+
+ 50 make_aligned_word_NOACCESS
+ 51 make_aligned_word_WRITABLE
+
+ 60 helperc_LOADV4
+ 61 helperc_STOREV4
+ 62 helperc_LOADV2
+ 63 helperc_STOREV2
+ 64 helperc_LOADV1
+ 65 helperc_STOREV1
+
+ 70 rim_rd_V4_SLOWLY
+ 71 rim_wr_V4_SLOWLY
+ 72 rim_rd_V2_SLOWLY
+ 73 rim_wr_V2_SLOWLY
+ 74 rim_rd_V1_SLOWLY
+ 75 rim_wr_V1_SLOWLY
+
+ 80 fpu_read
+ 81 fpu_read aligned 4
+ 82 fpu_read aligned 8
+ 83 fpu_read 2
+ 84 fpu_read 10
+
+ 85 fpu_write
+ 86 fpu_write aligned 4
+ 87 fpu_write aligned 8
+ 88 fpu_write 2
+ 89 fpu_write 10
+
+ 90 fpu_read_check_SLOWLY
+ 91 fpu_read_check_SLOWLY(byte loop)
+ 92 fpu_write_check_SLOWLY
+ 93 fpu_write_check_SLOWLY(byte loop)
+
+ 100 is_plausible_stack_addr
+ 101 handle_esp_assignment
+ 102 handle_esp_assignment(-4)
+ 103 handle_esp_assignment(+4)
+ 104 handle_esp_assignment(-12)
+ 105 handle_esp_assignment(-8)
+ 106 handle_esp_assignment(+16)
+ 107 handle_esp_assignment(+12)
+ 108 handle_esp_assignment(0)
+ 109 handle_esp_assignment(+8)
+ 110 handle_esp_assignment(-16)
+ 111 handle_esp_assignment(+20)
+ 112 handle_esp_assignment(-20)
+ 113 handle_esp_assignment(+24)
+ 114 handle_esp_assignment(-24)
+
+ 120 vg_handle_esp_assignment_SLOWLY
+ 121 vg_handle_esp_assignment_SLOWLY(normal; move down)
+ 122 vg_handle_esp_assignment_SLOWLY(normal; move up)
+ 123 vg_handle_esp_assignment_SLOWLY(normal)
+ 124 vg_handle_esp_assignment_SLOWLY(>= HUGE_DELTA)
+*/
+
+/*------------------------------------------------------------*/
+/*--- Function declarations. ---*/
+/*------------------------------------------------------------*/
+
+static UInt vgmext_rd_V4_SLOWLY ( Addr a );
+static UInt vgmext_rd_V2_SLOWLY ( Addr a );
+static UInt vgmext_rd_V1_SLOWLY ( Addr a );
+static void vgmext_wr_V4_SLOWLY ( Addr a, UInt vbytes );
+static void vgmext_wr_V2_SLOWLY ( Addr a, UInt vbytes );
+static void vgmext_wr_V1_SLOWLY ( Addr a, UInt vbytes );
+static void fpu_read_check_SLOWLY ( Addr addr, Int size );
+static void fpu_write_check_SLOWLY ( Addr addr, Int size );
+
+/*------------------------------------------------------------*/
+/*--- Data defns. ---*/
+/*------------------------------------------------------------*/
+
+typedef
+ struct {
+ UChar abits[8192];
+ UChar vbyte[65536];
+ }
+ SecMap;
+
+static SecMap* primary_map[ /*65536*/ 262144 ];
+static SecMap distinguished_secondary_map;
+
+#define IS_DISTINGUISHED_SM(smap) \
+ ((smap) == &distinguished_secondary_map)
+
+#define ENSURE_MAPPABLE(addr,caller) \
+ do { \
+ if (IS_DISTINGUISHED_SM(primary_map[(addr) >> 16])) { \
+ primary_map[(addr) >> 16] = alloc_secondary_map(caller); \
+ /* VG_(printf)("new 2map because of %p\n", addr); */ \
+ } \
+ } while(0)
+
+#define BITARR_SET(aaa_p,iii_p) \
+ do { \
+ UInt iii = (UInt)iii_p; \
+ UChar* aaa = (UChar*)aaa_p; \
+ aaa[iii >> 3] |= (1 << (iii & 7)); \
+ } while (0)
+
+#define BITARR_CLEAR(aaa_p,iii_p) \
+ do { \
+ UInt iii = (UInt)iii_p; \
+ UChar* aaa = (UChar*)aaa_p; \
+ aaa[iii >> 3] &= ~(1 << (iii & 7)); \
+ } while (0)
+
+#define BITARR_TEST(aaa_p,iii_p) \
+ (0 != (((UChar*)aaa_p)[ ((UInt)iii_p) >> 3 ] \
+ & (1 << (((UInt)iii_p) & 7)))) \
+
+
+#define VGM_BIT_VALID 0
+#define VGM_BIT_INVALID 1
+
+#define VGM_NIBBLE_VALID 0
+#define VGM_NIBBLE_INVALID 0xF
+
+#define VGM_BYTE_VALID 0
+#define VGM_BYTE_INVALID 0xFF
+
+#define VGM_WORD_VALID 0
+#define VGM_WORD_INVALID 0xFFFFFFFF
+
+#define VGM_EFLAGS_VALID 0xFFFFFFFE
+#define VGM_EFLAGS_INVALID 0xFFFFFFFF /* not used */
+
+
+static void init_shadow_memory ( void )
+{
+ Int i;
+
+ for (i = 0; i < 8192; i++) /* Invalid address */
+ distinguished_secondary_map.abits[i] = VGM_BYTE_INVALID;
+ for (i = 0; i < 65536; i++) /* Invalid Value */
+ distinguished_secondary_map.vbyte[i] = VGM_BYTE_INVALID;
+
+ /* These entries gradually get overwritten as the used address
+ space expands. */
+ for (i = 0; i < 65536; i++)
+ primary_map[i] = &distinguished_secondary_map;
+
+ /* These ones should never change; it's a bug in Valgrind if they do. */
+ for (i = 65536; i < 262144; i++)
+ primary_map[i] = &distinguished_secondary_map;
+}
+
+void SK_(post_clo_init) ( void )
+{
+}
+
+void SK_(fini) ( void )
+{
+ VG_(print_malloc_stats)();
+
+ if (VG_(clo_verbosity) == 1) {
+ if (!SK_(clo_leak_check))
+ VG_(message)(Vg_UserMsg,
+ "For a detailed leak analysis, rerun with: --leak-check=yes");
+
+ VG_(message)(Vg_UserMsg,
+ "For counts of detected errors, rerun with: -v");
+ }
+ if (SK_(clo_leak_check)) SK_(detect_memory_leaks)();
+
+ done_prof_mem();
+
+ if (0) {
+ VG_(message)(Vg_DebugMsg,
+ "------ Valgrind's client block stats follow ---------------" );
+ SK_(show_client_block_stats)();
+ }
+}
+
+/*------------------------------------------------------------*/
+/*--- Basic bitmap management, reading and writing. ---*/
+/*------------------------------------------------------------*/
+
+/* Allocate and initialise a secondary map. */
+
+static SecMap* alloc_secondary_map ( __attribute__ ((unused))
+ Char* caller )
+{
+ SecMap* map;
+ UInt i;
+ PROF_EVENT(10);
+
+ /* Mark all bytes as invalid access and invalid value. */
+
+ /* It just happens that a SecMap occupies exactly 18 pages --
+ although this isn't important, so the following assert is
+ spurious. */
+ vg_assert(0 == (sizeof(SecMap) % VKI_BYTES_PER_PAGE));
+ map = VG_(get_memory_from_mmap)( sizeof(SecMap), caller );
+
+ for (i = 0; i < 8192; i++)
+ map->abits[i] = VGM_BYTE_INVALID; /* Invalid address */
+ for (i = 0; i < 65536; i++)
+ map->vbyte[i] = VGM_BYTE_INVALID; /* Invalid Value */
+
+ /* VG_(printf)("ALLOC_2MAP(%s)\n", caller ); */
+ return map;
+}
+
+
+/* Basic reading/writing of the bitmaps, for byte-sized accesses. */
+
+static __inline__ UChar get_abit ( Addr a )
+{
+ SecMap* sm = primary_map[a >> 16];
+ UInt sm_off = a & 0xFFFF;
+ PROF_EVENT(20);
+# if 0
+ if (IS_DISTINGUISHED_SM(sm))
+ VG_(message)(Vg_DebugMsg,
+ "accessed distinguished 2ndary (A)map! 0x%x\n", a);
+# endif
+ return BITARR_TEST(sm->abits, sm_off)
+ ? VGM_BIT_INVALID : VGM_BIT_VALID;
+}
+
+static __inline__ UChar get_vbyte ( Addr a )
+{
+ SecMap* sm = primary_map[a >> 16];
+ UInt sm_off = a & 0xFFFF;
+ PROF_EVENT(21);
+# if 0
+ if (IS_DISTINGUISHED_SM(sm))
+ VG_(message)(Vg_DebugMsg,
+ "accessed distinguished 2ndary (V)map! 0x%x\n", a);
+# endif
+ return sm->vbyte[sm_off];
+}
+
+static __inline__ void set_abit ( Addr a, UChar abit )
+{
+ SecMap* sm;
+ UInt sm_off;
+ PROF_EVENT(22);
+ ENSURE_MAPPABLE(a, "set_abit");
+ sm = primary_map[a >> 16];
+ sm_off = a & 0xFFFF;
+ if (abit)
+ BITARR_SET(sm->abits, sm_off);
+ else
+ BITARR_CLEAR(sm->abits, sm_off);
+}
+
+static __inline__ void set_vbyte ( Addr a, UChar vbyte )
+{
+ SecMap* sm;
+ UInt sm_off;
+ PROF_EVENT(23);
+ ENSURE_MAPPABLE(a, "set_vbyte");
+ sm = primary_map[a >> 16];
+ sm_off = a & 0xFFFF;
+ sm->vbyte[sm_off] = vbyte;
+}
+
+
+/* Reading/writing of the bitmaps, for aligned word-sized accesses. */
+
+static __inline__ UChar get_abits4_ALIGNED ( Addr a )
+{
+ SecMap* sm;
+ UInt sm_off;
+ UChar abits8;
+ PROF_EVENT(24);
+# ifdef VG_DEBUG_MEMORY
+ vg_assert(IS_ALIGNED4_ADDR(a));
+# endif
+ sm = primary_map[a >> 16];
+ sm_off = a & 0xFFFF;
+ abits8 = sm->abits[sm_off >> 3];
+ abits8 >>= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */
+ abits8 &= 0x0F;
+ return abits8;
+}
+
+static UInt __inline__ get_vbytes4_ALIGNED ( Addr a )
+{
+ SecMap* sm = primary_map[a >> 16];
+ UInt sm_off = a & 0xFFFF;
+ PROF_EVENT(25);
+# ifdef VG_DEBUG_MEMORY
+ vg_assert(IS_ALIGNED4_ADDR(a));
+# endif
+ return ((UInt*)(sm->vbyte))[sm_off >> 2];
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Setting permissions over address ranges. ---*/
+/*------------------------------------------------------------*/
+
+static void set_address_range_perms ( Addr a, UInt len,
+ UInt example_a_bit,
+ UInt example_v_bit )
+{
+ UChar vbyte, abyte8;
+ UInt vword4, sm_off;
+ SecMap* sm;
+
+ PROF_EVENT(30);
+
+ if (len == 0)
+ return;
+
+ if (len > 100 * 1000 * 1000) {
+ VG_(message)(Vg_UserMsg,
+ "Warning: set address range perms: "
+ "large range %u, a %d, v %d",
+ len, example_a_bit, example_v_bit );
+ }
+
+ VGP_PUSHCC(VgpSetMem);
+
+ /* Requests to change permissions of huge address ranges may
+ indicate bugs in our machinery. 30,000,000 is arbitrary, but so
+ far all legitimate requests have fallen beneath that size. */
+ /* 4 Mar 02: this is just stupid; get rid of it. */
+ /* vg_assert(len < 30000000); */
+
+ /* Check the permissions make sense. */
+ vg_assert(example_a_bit == VGM_BIT_VALID
+ || example_a_bit == VGM_BIT_INVALID);
+ vg_assert(example_v_bit == VGM_BIT_VALID
+ || example_v_bit == VGM_BIT_INVALID);
+ if (example_a_bit == VGM_BIT_INVALID)
+ vg_assert(example_v_bit == VGM_BIT_INVALID);
+
+ /* The validity bits to write. */
+ vbyte = example_v_bit==VGM_BIT_VALID
+ ? VGM_BYTE_VALID : VGM_BYTE_INVALID;
+
+ /* In order that we can charge through the address space at 8
+ bytes/main-loop iteration, make up some perms. */
+ abyte8 = (example_a_bit << 7)
+ | (example_a_bit << 6)
+ | (example_a_bit << 5)
+ | (example_a_bit << 4)
+ | (example_a_bit << 3)
+ | (example_a_bit << 2)
+ | (example_a_bit << 1)
+ | (example_a_bit << 0);
+ vword4 = (vbyte << 24) | (vbyte << 16) | (vbyte << 8) | vbyte;
+
+# ifdef VG_DEBUG_MEMORY
+ /* Do it ... */
+ while (True) {
+ PROF_EVENT(31);
+ if (len == 0) break;
+ set_abit ( a, example_a_bit );
+ set_vbyte ( a, vbyte );
+ a++;
+ len--;
+ }
+
+# else
+ /* Slowly do parts preceding 8-byte alignment. */
+ while (True) {
+ PROF_EVENT(31);
+ if (len == 0) break;
+ if ((a % 8) == 0) break;
+ set_abit ( a, example_a_bit );
+ set_vbyte ( a, vbyte );
+ a++;
+ len--;
+ }
+
+ if (len == 0) {
+ VGP_POPCC(VgpSetMem);
+ return;
+ }
+ vg_assert((a % 8) == 0 && len > 0);
+
+ /* Once aligned, go fast. */
+ while (True) {
+ PROF_EVENT(32);
+ if (len < 8) break;
+ ENSURE_MAPPABLE(a, "set_address_range_perms(fast)");
+ sm = primary_map[a >> 16];
+ sm_off = a & 0xFFFF;
+ sm->abits[sm_off >> 3] = abyte8;
+ ((UInt*)(sm->vbyte))[(sm_off >> 2) + 0] = vword4;
+ ((UInt*)(sm->vbyte))[(sm_off >> 2) + 1] = vword4;
+ a += 8;
+ len -= 8;
+ }
+
+ if (len == 0) {
+ VGP_POPCC(VgpSetMem);
+ return;
+ }
+ vg_assert((a % 8) == 0 && len > 0 && len < 8);
+
+ /* Finish the upper fragment. */
+ while (True) {
+ PROF_EVENT(33);
+ if (len == 0) break;
+ set_abit ( a, example_a_bit );
+ set_vbyte ( a, vbyte );
+ a++;
+ len--;
+ }
+# endif
+
+ /* Check that zero page and highest page have not been written to
+ -- this could happen with buggy syscall wrappers. Today
+ (2001-04-26) had precisely such a problem with __NR_setitimer. */
+ vg_assert(SK_(cheap_sanity_check)());
+ VGP_POPCC(VgpSetMem);
+}
+
+/* Set permissions for address ranges ... */
+
+void SK_(make_noaccess) ( Addr a, UInt len )
+{
+ PROF_EVENT(35);
+ DEBUG("SK_(make_noaccess)(%p, %x)\n", a, len);
+ set_address_range_perms ( a, len, VGM_BIT_INVALID, VGM_BIT_INVALID );
+}
+
+void SK_(make_writable) ( Addr a, UInt len )
+{
+ PROF_EVENT(36);
+ DEBUG("SK_(make_writable)(%p, %x)\n", a, len);
+ set_address_range_perms ( a, len, VGM_BIT_VALID, VGM_BIT_INVALID );
+}
+
+void SK_(make_readable) ( Addr a, UInt len )
+{
+ PROF_EVENT(37);
+ DEBUG("SK_(make_readable)(%p, 0x%x)\n", a, len);
+ set_address_range_perms ( a, len, VGM_BIT_VALID, VGM_BIT_VALID );
+}
+
+/* Block-copy permissions (needed for implementing realloc()). */
+
+static void copy_address_range_state ( Addr src, Addr dst, UInt len )
+{
+ UInt i;
+
+ DEBUG("copy_address_range_state\n");
+
+ PROF_EVENT(40);
+ for (i = 0; i < len; i++) {
+ UChar abit = get_abit ( src+i );
+ UChar vbyte = get_vbyte ( src+i );
+ PROF_EVENT(41);
+ set_abit ( dst+i, abit );
+ set_vbyte ( dst+i, vbyte );
+ }
+}
+
+
+/* Check permissions for address range. If inadequate permissions
+ exist, *bad_addr is set to the offending address, so the caller can
+ know what it is. */
+
+Bool SK_(check_writable) ( Addr a, UInt len, Addr* bad_addr )
+{
+ UInt i;
+ UChar abit;
+ PROF_EVENT(42);
+ for (i = 0; i < len; i++) {
+ PROF_EVENT(43);
+ abit = get_abit(a);
+ if (abit == VGM_BIT_INVALID) {
+ if (bad_addr != NULL) *bad_addr = a;
+ return False;
+ }
+ a++;
+ }
+ return True;
+}
+
+Bool SK_(check_readable) ( Addr a, UInt len, Addr* bad_addr )
+{
+ UInt i;
+ UChar abit;
+ UChar vbyte;
+
+ PROF_EVENT(44);
+ DEBUG("SK_(check_readable)\n");
+ for (i = 0; i < len; i++) {
+ abit = get_abit(a);
+ vbyte = get_vbyte(a);
+ PROF_EVENT(45);
+ if (abit != VGM_BIT_VALID || vbyte != VGM_BYTE_VALID) {
+ if (bad_addr != NULL) *bad_addr = a;
+ return False;
+ }
+ a++;
+ }
+ return True;
+}
+
+
+/* Check a zero-terminated ascii string. Tricky -- don't want to
+ examine the actual bytes, to find the end, until we're sure it is
+ safe to do so. */
+
+Bool SK_(check_readable_asciiz) ( Addr a, Addr* bad_addr )
+{
+ UChar abit;
+ UChar vbyte;
+ PROF_EVENT(46);
+ DEBUG("SK_(check_readable_asciiz)\n");
+ while (True) {
+ PROF_EVENT(47);
+ abit = get_abit(a);
+ vbyte = get_vbyte(a);
+ if (abit != VGM_BIT_VALID || vbyte != VGM_BYTE_VALID) {
+ if (bad_addr != NULL) *bad_addr = a;
+ return False;
+ }
+ /* Ok, a is safe to read. */
+ if (* ((UChar*)a) == 0) return True;
+ a++;
+ }
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Memory event handlers ---*/
+/*------------------------------------------------------------*/
+
+/* Setting permissions for aligned words. This supports fast stack
+ operations. */
+
+static void make_noaccess_aligned ( Addr a, UInt len )
+{
+ SecMap* sm;
+ UInt sm_off;
+ UChar mask;
+ Addr a_past_end = a + len;
+
+ VGP_PUSHCC(VgpSetMem);
+
+ PROF_EVENT(50);
+# ifdef VG_DEBUG_MEMORY
+ vg_assert(IS_ALIGNED4_ADDR(a));
+ vg_assert(IS_ALIGNED4_ADDR(len));
+# endif
+
+ for ( ; a < a_past_end; a += 4) {
+ ENSURE_MAPPABLE(a, "make_noaccess_aligned");
+ sm = primary_map[a >> 16];
+ sm_off = a & 0xFFFF;
+ ((UInt*)(sm->vbyte))[sm_off >> 2] = VGM_WORD_INVALID;
+ mask = 0x0F;
+ mask <<= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */
+ /* mask now contains 1s where we wish to make address bits
+ invalid (1s). */
+ sm->abits[sm_off >> 3] |= mask;
+ }
+ VGP_POPCC(VgpSetMem);
+}
+
+static void make_writable_aligned ( Addr a, UInt len )
+{
+ SecMap* sm;
+ UInt sm_off;
+ UChar mask;
+ Addr a_past_end = a + len;
+
+ VGP_PUSHCC(VgpSetMem);
+
+ PROF_EVENT(51);
+# ifdef VG_DEBUG_MEMORY
+ vg_assert(IS_ALIGNED4_ADDR(a));
+ vg_assert(IS_ALIGNED4_ADDR(len));
+# endif
+
+ for ( ; a < a_past_end; a += 4) {
+ ENSURE_MAPPABLE(a, "make_writable_aligned");
+ sm = primary_map[a >> 16];
+ sm_off = a & 0xFFFF;
+ ((UInt*)(sm->vbyte))[sm_off >> 2] = VGM_WORD_INVALID;
+ mask = 0x0F;
+ mask <<= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */
+ /* mask now contains 1s where we wish to make address bits
+ invalid (0s). */
+ sm->abits[sm_off >> 3] &= ~mask;
+ }
+ VGP_POPCC(VgpSetMem);
+}
+
+
+static
+void check_is_writable ( CorePart part, ThreadState* tst,
+ Char* s, UInt base, UInt size )
+{
+ Bool ok;
+ Addr bad_addr;
+
+ VGP_PUSHCC(VgpCheckMem);
+
+ /* VG_(message)(Vg_DebugMsg,"check is writable: %x .. %x",
+ base,base+size-1); */
+ ok = SK_(check_writable) ( base, size, &bad_addr );
+ if (!ok) {
+ switch (part) {
+ case Vg_CoreSysCall:
+ SK_(record_param_error) ( tst, bad_addr, /*isWrite =*/True, s );
+ break;
+
+ case Vg_CorePThread:
+ case Vg_CoreSignal:
+ SK_(record_core_mem_error)( tst, /*isWrite=*/True, s );
+ break;
+
+ default:
+ VG_(panic)("check_is_readable: Unknown or unexpected CorePart");
+ }
+ }
+
+ VGP_POPCC(VgpCheckMem);
+}
+
+static
+void check_is_readable ( CorePart part, ThreadState* tst,
+ Char* s, UInt base, UInt size )
+{
+ Bool ok;
+ Addr bad_addr;
+
+ VGP_PUSHCC(VgpCheckMem);
+
+ /* VG_(message)(Vg_DebugMsg,"check is readable: %x .. %x",
+ base,base+size-1); */
+ ok = SK_(check_readable) ( base, size, &bad_addr );
+ if (!ok) {
+ switch (part) {
+ case Vg_CoreSysCall:
+ SK_(record_param_error) ( tst, bad_addr, /*isWrite =*/False, s );
+ break;
+
+ case Vg_CorePThread:
+ SK_(record_core_mem_error)( tst, /*isWrite=*/False, s );
+ break;
+
+ /* If we're being asked to jump to a silly address, record an error
+ message before potentially crashing the entire system. */
+ case Vg_CoreTranslate:
+ SK_(record_jump_error)( tst, bad_addr );
+ break;
+
+ default:
+ VG_(panic)("check_is_readable: Unknown or unexpected CorePart");
+ }
+ }
+ VGP_POPCC(VgpCheckMem);
+}
+
+static
+void check_is_readable_asciiz ( CorePart part, ThreadState* tst,
+ Char* s, UInt str )
+{
+ Bool ok = True;
+ Addr bad_addr;
+ /* VG_(message)(Vg_DebugMsg,"check is readable asciiz: 0x%x",str); */
+
+ VGP_PUSHCC(VgpCheckMem);
+
+ vg_assert(part == Vg_CoreSysCall);
+ ok = SK_(check_readable_asciiz) ( (Addr)str, &bad_addr );
+ if (!ok) {
+ SK_(record_param_error) ( tst, bad_addr, /*is_writable =*/False, s );
+ }
+
+ VGP_POPCC(VgpCheckMem);
+}
+
+
+static
+void memcheck_new_mem_startup( Addr a, UInt len, Bool rr, Bool ww, Bool xx )
+{
+ // JJJ: this ignores the permissions and just makes it readable, like the
+ // old code did, AFAICT
+ DEBUG("new_mem_startup(%p, %u, rr=%u, ww=%u, xx=%u)\n", a,len,rr,ww,xx);
+ SK_(make_readable)(a, len);
+}
+
+static
+void memcheck_new_mem_heap ( Addr a, UInt len, Bool is_inited )
+{
+ if (is_inited) {
+ SK_(make_readable)(a, len);
+ } else {
+ SK_(make_writable)(a, len);
+ }
+}
+
+static
+void memcheck_set_perms (Addr a, UInt len,
+ Bool nn, Bool rr, Bool ww, Bool xx)
+{
+ DEBUG("memcheck_set_perms(%p, %u, nn=%u, rr=%u ww=%u, xx=%u)\n",
+ a, len, nn, rr, ww, xx);
+ if (rr) SK_(make_readable)(a, len);
+ else if (ww) SK_(make_writable)(a, len);
+ else SK_(make_noaccess)(a, len);
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Functions called directly from generated code. ---*/
+/*------------------------------------------------------------*/
+
+static __inline__ UInt rotateRight16 ( UInt x )
+{
+ /* Amazingly, gcc turns this into a single rotate insn. */
+ return (x >> 16) | (x << 16);
+}
+
+
+static __inline__ UInt shiftRight16 ( UInt x )
+{
+ return x >> 16;
+}
+
+
+/* Read/write 1/2/4 sized V bytes, and emit an address error if
+ needed. */
+
+/* VG_(helperc_{LD,ST}V{1,2,4}) handle the common case fast.
+ Under all other circumstances, it defers to the relevant _SLOWLY
+ function, which can handle all situations.
+*/
+__attribute__ ((regparm(1)))
+UInt SK_(helperc_LOADV4) ( Addr a )
+{
+# ifdef VG_DEBUG_MEMORY
+ return vgmext_rd_V4_SLOWLY(a);
+# else
+ UInt sec_no = rotateRight16(a) & 0x3FFFF;
+ SecMap* sm = primary_map[sec_no];
+ UInt a_off = (a & 0xFFFF) >> 3;
+ UChar abits = sm->abits[a_off];
+ abits >>= (a & 4);
+ abits &= 15;
+ PROF_EVENT(60);
+ if (abits == VGM_NIBBLE_VALID) {
+ /* Handle common case quickly: a is suitably aligned, is mapped,
+ and is addressible. */
+ UInt v_off = a & 0xFFFF;
+ return ((UInt*)(sm->vbyte))[ v_off >> 2 ];
+ } else {
+ /* Slow but general case. */
+ return vgmext_rd_V4_SLOWLY(a);
+ }
+# endif
+}
+
+__attribute__ ((regparm(2)))
+void SK_(helperc_STOREV4) ( Addr a, UInt vbytes )
+{
+# ifdef VG_DEBUG_MEMORY
+ vgmext_wr_V4_SLOWLY(a, vbytes);
+# else
+ UInt sec_no = rotateRight16(a) & 0x3FFFF;
+ SecMap* sm = primary_map[sec_no];
+ UInt a_off = (a & 0xFFFF) >> 3;
+ UChar abits = sm->abits[a_off];
+ abits >>= (a & 4);
+ abits &= 15;
+ PROF_EVENT(61);
+ if (abits == VGM_NIBBLE_VALID) {
+ /* Handle common case quickly: a is suitably aligned, is mapped,
+ and is addressible. */
+ UInt v_off = a & 0xFFFF;
+ ((UInt*)(sm->vbyte))[ v_off >> 2 ] = vbytes;
+ } else {
+ /* Slow but general case. */
+ vgmext_wr_V4_SLOWLY(a, vbytes);
+ }
+# endif
+}
+
+__attribute__ ((regparm(1)))
+UInt SK_(helperc_LOADV2) ( Addr a )
+{
+# ifdef VG_DEBUG_MEMORY
+ return vgmext_rd_V2_SLOWLY(a);
+# else
+ UInt sec_no = rotateRight16(a) & 0x1FFFF;
+ SecMap* sm = primary_map[sec_no];
+ UInt a_off = (a & 0xFFFF) >> 3;
+ PROF_EVENT(62);
+ if (sm->abits[a_off] == VGM_BYTE_VALID) {
+ /* Handle common case quickly. */
+ UInt v_off = a & 0xFFFF;
+ return 0xFFFF0000
+ |
+ (UInt)( ((UShort*)(sm->vbyte))[ v_off >> 1 ] );
+ } else {
+ /* Slow but general case. */
+ return vgmext_rd_V2_SLOWLY(a);
+ }
+# endif
+}
+
+__attribute__ ((regparm(2)))
+void SK_(helperc_STOREV2) ( Addr a, UInt vbytes )
+{
+# ifdef VG_DEBUG_MEMORY
+ vgmext_wr_V2_SLOWLY(a, vbytes);
+# else
+ UInt sec_no = rotateRight16(a) & 0x1FFFF;
+ SecMap* sm = primary_map[sec_no];
+ UInt a_off = (a & 0xFFFF) >> 3;
+ PROF_EVENT(63);
+ if (sm->abits[a_off] == VGM_BYTE_VALID) {
+ /* Handle common case quickly. */
+ UInt v_off = a & 0xFFFF;
+ ((UShort*)(sm->vbyte))[ v_off >> 1 ] = vbytes & 0x0000FFFF;
+ } else {
+ /* Slow but general case. */
+ vgmext_wr_V2_SLOWLY(a, vbytes);
+ }
+# endif
+}
+
+__attribute__ ((regparm(1)))
+UInt SK_(helperc_LOADV1) ( Addr a )
+{
+# ifdef VG_DEBUG_MEMORY
+ return vgmext_rd_V1_SLOWLY(a);
+# else
+ UInt sec_no = shiftRight16(a);
+ SecMap* sm = primary_map[sec_no];
+ UInt a_off = (a & 0xFFFF) >> 3;
+ PROF_EVENT(64);
+ if (sm->abits[a_off] == VGM_BYTE_VALID) {
+ /* Handle common case quickly. */
+ UInt v_off = a & 0xFFFF;
+ return 0xFFFFFF00
+ |
+ (UInt)( ((UChar*)(sm->vbyte))[ v_off ] );
+ } else {
+ /* Slow but general case. */
+ return vgmext_rd_V1_SLOWLY(a);
+ }
+# endif
+}
+
+__attribute__ ((regparm(2)))
+void SK_(helperc_STOREV1) ( Addr a, UInt vbytes )
+{
+# ifdef VG_DEBUG_MEMORY
+ vgmext_wr_V1_SLOWLY(a, vbytes);
+# else
+ UInt sec_no = shiftRight16(a);
+ SecMap* sm = primary_map[sec_no];
+ UInt a_off = (a & 0xFFFF) >> 3;
+ PROF_EVENT(65);
+ if (sm->abits[a_off] == VGM_BYTE_VALID) {
+ /* Handle common case quickly. */
+ UInt v_off = a & 0xFFFF;
+ ((UChar*)(sm->vbyte))[ v_off ] = vbytes & 0x000000FF;
+ } else {
+ /* Slow but general case. */
+ vgmext_wr_V1_SLOWLY(a, vbytes);
+ }
+# endif
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Fallback functions to handle cases that the above ---*/
+/*--- VG_(helperc_{LD,ST}V{1,2,4}) can't manage. ---*/
+/*------------------------------------------------------------*/
+
+static UInt vgmext_rd_V4_SLOWLY ( Addr a )
+{
+ Bool a0ok, a1ok, a2ok, a3ok;
+ UInt vb0, vb1, vb2, vb3;
+
+ PROF_EVENT(70);
+
+ /* First establish independently the addressibility of the 4 bytes
+ involved. */
+ a0ok = get_abit(a+0) == VGM_BIT_VALID;
+ a1ok = get_abit(a+1) == VGM_BIT_VALID;
+ a2ok = get_abit(a+2) == VGM_BIT_VALID;
+ a3ok = get_abit(a+3) == VGM_BIT_VALID;
+
+ /* Also get the validity bytes for the address. */
+ vb0 = (UInt)get_vbyte(a+0);
+ vb1 = (UInt)get_vbyte(a+1);
+ vb2 = (UInt)get_vbyte(a+2);
+ vb3 = (UInt)get_vbyte(a+3);
+
+ /* Now distinguish 3 cases */
+
+ /* Case 1: the address is completely valid, so:
+ - no addressing error
+ - return V bytes as read from memory
+ */
+ if (a0ok && a1ok && a2ok && a3ok) {
+ UInt vw = VGM_WORD_INVALID;
+ vw <<= 8; vw |= vb3;
+ vw <<= 8; vw |= vb2;
+ vw <<= 8; vw |= vb1;
+ vw <<= 8; vw |= vb0;
+ return vw;
+ }
+
+ /* Case 2: the address is completely invalid.
+ - emit addressing error
+ - return V word indicating validity.
+ This sounds strange, but if we make loads from invalid addresses
+ give invalid data, we also risk producing a number of confusing
+ undefined-value errors later, which confuses the fact that the
+ error arose in the first place from an invalid address.
+ */
+ /* VG_(printf)("%p (%d %d %d %d)\n", a, a0ok, a1ok, a2ok, a3ok); */
+ if (!SK_(clo_partial_loads_ok)
+ || ((a & 3) != 0)
+ || (!a0ok && !a1ok && !a2ok && !a3ok)) {
+ SK_(record_address_error)( a, 4, False );
+ return (VGM_BYTE_VALID << 24) | (VGM_BYTE_VALID << 16)
+ | (VGM_BYTE_VALID << 8) | VGM_BYTE_VALID;
+ }
+
+ /* Case 3: the address is partially valid.
+ - no addressing error
+ - returned V word is invalid where the address is invalid,
+ and contains V bytes from memory otherwise.
+ Case 3 is only allowed if SK_(clo_partial_loads_ok) is True
+ (which is the default), and the address is 4-aligned.
+ If not, Case 2 will have applied.
+ */
+ vg_assert(SK_(clo_partial_loads_ok));
+ {
+ UInt vw = VGM_WORD_INVALID;
+ vw <<= 8; vw |= (a3ok ? vb3 : VGM_BYTE_INVALID);
+ vw <<= 8; vw |= (a2ok ? vb2 : VGM_BYTE_INVALID);
+ vw <<= 8; vw |= (a1ok ? vb1 : VGM_BYTE_INVALID);
+ vw <<= 8; vw |= (a0ok ? vb0 : VGM_BYTE_INVALID);
+ return vw;
+ }
+}
+
+static void vgmext_wr_V4_SLOWLY ( Addr a, UInt vbytes )
+{
+ /* Check the address for validity. */
+ Bool aerr = False;
+ PROF_EVENT(71);
+
+ if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+ if (get_abit(a+1) != VGM_BIT_VALID) aerr = True;
+ if (get_abit(a+2) != VGM_BIT_VALID) aerr = True;
+ if (get_abit(a+3) != VGM_BIT_VALID) aerr = True;
+
+ /* Store the V bytes, remembering to do it little-endian-ly. */
+ set_vbyte( a+0, vbytes & 0x000000FF ); vbytes >>= 8;
+ set_vbyte( a+1, vbytes & 0x000000FF ); vbytes >>= 8;
+ set_vbyte( a+2, vbytes & 0x000000FF ); vbytes >>= 8;
+ set_vbyte( a+3, vbytes & 0x000000FF );
+
+ /* If an address error has happened, report it. */
+ if (aerr)
+ SK_(record_address_error)( a, 4, True );
+}
+
+static UInt vgmext_rd_V2_SLOWLY ( Addr a )
+{
+ /* Check the address for validity. */
+ UInt vw = VGM_WORD_INVALID;
+ Bool aerr = False;
+ PROF_EVENT(72);
+
+ if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+ if (get_abit(a+1) != VGM_BIT_VALID) aerr = True;
+
+ /* Fetch the V bytes, remembering to do it little-endian-ly. */
+ vw <<= 8; vw |= (UInt)get_vbyte(a+1);
+ vw <<= 8; vw |= (UInt)get_vbyte(a+0);
+
+ /* If an address error has happened, report it. */
+ if (aerr) {
+ SK_(record_address_error)( a, 2, False );
+ vw = (VGM_BYTE_INVALID << 24) | (VGM_BYTE_INVALID << 16)
+ | (VGM_BYTE_VALID << 8) | (VGM_BYTE_VALID);
+ }
+ return vw;
+}
+
+static void vgmext_wr_V2_SLOWLY ( Addr a, UInt vbytes )
+{
+ /* Check the address for validity. */
+ Bool aerr = False;
+ PROF_EVENT(73);
+
+ if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+ if (get_abit(a+1) != VGM_BIT_VALID) aerr = True;
+
+ /* Store the V bytes, remembering to do it little-endian-ly. */
+ set_vbyte( a+0, vbytes & 0x000000FF ); vbytes >>= 8;
+ set_vbyte( a+1, vbytes & 0x000000FF );
+
+ /* If an address error has happened, report it. */
+ if (aerr)
+ SK_(record_address_error)( a, 2, True );
+}
+
+static UInt vgmext_rd_V1_SLOWLY ( Addr a )
+{
+ /* Check the address for validity. */
+ UInt vw = VGM_WORD_INVALID;
+ Bool aerr = False;
+ PROF_EVENT(74);
+
+ if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+
+ /* Fetch the V byte. */
+ vw <<= 8; vw |= (UInt)get_vbyte(a+0);
+
+ /* If an address error has happened, report it. */
+ if (aerr) {
+ SK_(record_address_error)( a, 1, False );
+ vw = (VGM_BYTE_INVALID << 24) | (VGM_BYTE_INVALID << 16)
+ | (VGM_BYTE_INVALID << 8) | (VGM_BYTE_VALID);
+ }
+ return vw;
+}
+
+static void vgmext_wr_V1_SLOWLY ( Addr a, UInt vbytes )
+{
+ /* Check the address for validity. */
+ Bool aerr = False;
+ PROF_EVENT(75);
+ if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+
+ /* Store the V bytes, remembering to do it little-endian-ly. */
+ set_vbyte( a+0, vbytes & 0x000000FF );
+
+ /* If an address error has happened, report it. */
+ if (aerr)
+ SK_(record_address_error)( a, 1, True );
+}
+
+
+/* ---------------------------------------------------------------------
+ Called from generated code, or from the assembly helpers.
+ Handlers for value check failures.
+ ------------------------------------------------------------------ */
+
+void SK_(helperc_value_check0_fail) ( void )
+{
+ SK_(record_value_error) ( 0 );
+}
+
+void SK_(helperc_value_check1_fail) ( void )
+{
+ SK_(record_value_error) ( 1 );
+}
+
+void SK_(helperc_value_check2_fail) ( void )
+{
+ SK_(record_value_error) ( 2 );
+}
+
+void SK_(helperc_value_check4_fail) ( void )
+{
+ SK_(record_value_error) ( 4 );
+}
+
+
+/* ---------------------------------------------------------------------
+ FPU load and store checks, called from generated code.
+ ------------------------------------------------------------------ */
+
+__attribute__ ((regparm(2)))
+void SK_(fpu_read_check) ( Addr addr, Int size )
+{
+ /* Ensure the read area is both addressible and valid (ie,
+ readable). If there's an address error, don't report a value
+ error too; but if there isn't an address error, check for a
+ value error.
+
+ Try to be reasonably fast on the common case; wimp out and defer
+ to fpu_read_check_SLOWLY for everything else. */
+
+ SecMap* sm;
+ UInt sm_off, v_off, a_off;
+ Addr addr4;
+
+ PROF_EVENT(80);
+
+# ifdef VG_DEBUG_MEMORY
+ fpu_read_check_SLOWLY ( addr, size );
+# else
+
+ if (size == 4) {
+ if (!IS_ALIGNED4_ADDR(addr)) goto slow4;
+ PROF_EVENT(81);
+ /* Properly aligned. */
+ sm = primary_map[addr >> 16];
+ sm_off = addr & 0xFFFF;
+ a_off = sm_off >> 3;
+ if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow4;
+ /* Properly aligned and addressible. */
+ v_off = addr & 0xFFFF;
+ if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID)
+ goto slow4;
+ /* Properly aligned, addressible and with valid data. */
+ return;
+ slow4:
+ fpu_read_check_SLOWLY ( addr, 4 );
+ return;
+ }
+
+ if (size == 8) {
+ if (!IS_ALIGNED4_ADDR(addr)) goto slow8;
+ PROF_EVENT(82);
+ /* Properly aligned. Do it in two halves. */
+ addr4 = addr + 4;
+ /* First half. */
+ sm = primary_map[addr >> 16];
+ sm_off = addr & 0xFFFF;
+ a_off = sm_off >> 3;
+ if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8;
+ /* First half properly aligned and addressible. */
+ v_off = addr & 0xFFFF;
+ if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID)
+ goto slow8;
+ /* Second half. */
+ sm = primary_map[addr4 >> 16];
+ sm_off = addr4 & 0xFFFF;
+ a_off = sm_off >> 3;
+ if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8;
+ /* Second half properly aligned and addressible. */
+ v_off = addr4 & 0xFFFF;
+ if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID)
+ goto slow8;
+ /* Both halves properly aligned, addressible and with valid
+ data. */
+ return;
+ slow8:
+ fpu_read_check_SLOWLY ( addr, 8 );
+ return;
+ }
+
+ /* Can't be bothered to huff'n'puff to make these (allegedly) rare
+ cases go quickly. */
+ if (size == 2) {
+ PROF_EVENT(83);
+ fpu_read_check_SLOWLY ( addr, 2 );
+ return;
+ }
+
+ if (size == 10) {
+ PROF_EVENT(84);
+ fpu_read_check_SLOWLY ( addr, 10 );
+ return;
+ }
+
+ if (size == 28 || size == 108) {
+ PROF_EVENT(84); /* XXX assign correct event number */
+ fpu_read_check_SLOWLY ( addr, 28 );
+ return;
+ }
+
+ VG_(printf)("size is %d\n", size);
+ VG_(panic)("vgmext_fpu_read_check: unhandled size");
+# endif
+}
+
+
+__attribute__ ((regparm(2)))
+void SK_(fpu_write_check) ( Addr addr, Int size )
+{
+ /* Ensure the written area is addressible, and moan if otherwise.
+ If it is addressible, make it valid, otherwise invalid.
+ */
+
+ SecMap* sm;
+ UInt sm_off, v_off, a_off;
+ Addr addr4;
+
+ PROF_EVENT(85);
+
+# ifdef VG_DEBUG_MEMORY
+ fpu_write_check_SLOWLY ( addr, size );
+# else
+
+ if (size == 4) {
+ if (!IS_ALIGNED4_ADDR(addr)) goto slow4;
+ PROF_EVENT(86);
+ /* Properly aligned. */
+ sm = primary_map[addr >> 16];
+ sm_off = addr & 0xFFFF;
+ a_off = sm_off >> 3;
+ if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow4;
+ /* Properly aligned and addressible. Make valid. */
+ v_off = addr & 0xFFFF;
+ ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID;
+ return;
+ slow4:
+ fpu_write_check_SLOWLY ( addr, 4 );
+ return;
+ }
+
+ if (size == 8) {
+ if (!IS_ALIGNED4_ADDR(addr)) goto slow8;
+ PROF_EVENT(87);
+ /* Properly aligned. Do it in two halves. */
+ addr4 = addr + 4;
+ /* First half. */
+ sm = primary_map[addr >> 16];
+ sm_off = addr & 0xFFFF;
+ a_off = sm_off >> 3;
+ if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8;
+ /* First half properly aligned and addressible. Make valid. */
+ v_off = addr & 0xFFFF;
+ ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID;
+ /* Second half. */
+ sm = primary_map[addr4 >> 16];
+ sm_off = addr4 & 0xFFFF;
+ a_off = sm_off >> 3;
+ if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8;
+ /* Second half properly aligned and addressible. */
+ v_off = addr4 & 0xFFFF;
+ ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID;
+ /* Properly aligned, addressible and with valid data. */
+ return;
+ slow8:
+ fpu_write_check_SLOWLY ( addr, 8 );
+ return;
+ }
+
+ /* Can't be bothered to huff'n'puff to make these (allegedly) rare
+ cases go quickly. */
+ if (size == 2) {
+ PROF_EVENT(88);
+ fpu_write_check_SLOWLY ( addr, 2 );
+ return;
+ }
+
+ if (size == 10) {
+ PROF_EVENT(89);
+ fpu_write_check_SLOWLY ( addr, 10 );
+ return;
+ }
+
+ if (size == 28 || size == 108) {
+ PROF_EVENT(89); /* XXX assign correct event number */
+ fpu_write_check_SLOWLY ( addr, 28 );
+ return;
+ }
+
+ VG_(printf)("size is %d\n", size);
+ VG_(panic)("vgmext_fpu_write_check: unhandled size");
+# endif
+}
+
+
+/* ---------------------------------------------------------------------
+ Slow, general cases for FPU load and store checks.
+ ------------------------------------------------------------------ */
+
+/* Generic version. Test for both addr and value errors, but if
+ there's an addr error, don't report a value error even if it
+ exists. */
+
+void fpu_read_check_SLOWLY ( Addr addr, Int size )
+{
+ Int i;
+ Bool aerr = False;
+ Bool verr = False;
+ PROF_EVENT(90);
+ for (i = 0; i < size; i++) {
+ PROF_EVENT(91);
+ if (get_abit(addr+i) != VGM_BIT_VALID)
+ aerr = True;
+ if (get_vbyte(addr+i) != VGM_BYTE_VALID)
+ verr = True;
+ }
+
+ if (aerr) {
+ SK_(record_address_error)( addr, size, False );
+ } else {
+ if (verr)
+ SK_(record_value_error)( size );
+ }
+}
+
+
+/* Generic version. Test for addr errors. Valid addresses are
+ given valid values, and invalid addresses invalid values. */
+
+void fpu_write_check_SLOWLY ( Addr addr, Int size )
+{
+ Int i;
+ Addr a_here;
+ Bool a_ok;
+ Bool aerr = False;
+ PROF_EVENT(92);
+ for (i = 0; i < size; i++) {
+ PROF_EVENT(93);
+ a_here = addr+i;
+ a_ok = get_abit(a_here) == VGM_BIT_VALID;
+ if (a_ok) {
+ set_vbyte(a_here, VGM_BYTE_VALID);
+ } else {
+ set_vbyte(a_here, VGM_BYTE_INVALID);
+ aerr = True;
+ }
+ }
+ if (aerr) {
+ SK_(record_address_error)( addr, size, True );
+ }
+}
+
+/*------------------------------------------------------------*/
+/*--- Shadow chunks info ---*/
+/*------------------------------------------------------------*/
+
+static __inline__
+void set_where( ShadowChunk* sc, ExeContext* ec )
+{
+ sc->skin_extra[0] = (UInt)ec;
+}
+
+static __inline__
+ExeContext *get_where( ShadowChunk* sc )
+{
+ return (ExeContext*)sc->skin_extra[0];
+}
+
+void SK_(complete_shadow_chunk) ( ShadowChunk* sc, ThreadState* tst )
+{
+ set_where( sc, VG_(get_ExeContext) ( tst ) );
+}
+
+/*------------------------------------------------------------*/
+/*--- Postponing free()ing ---*/
+/*------------------------------------------------------------*/
+
+/* Holds blocks after freeing. */
+static ShadowChunk* vg_freed_list_start = NULL;
+static ShadowChunk* vg_freed_list_end = NULL;
+static Int vg_freed_list_volume = 0;
+
+static __attribute__ ((unused))
+ Int count_freelist ( void )
+{
+ ShadowChunk* sc;
+ Int n = 0;
+ for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
+ n++;
+ return n;
+}
+
+static __attribute__ ((unused))
+ void freelist_sanity ( void )
+{
+ ShadowChunk* sc;
+ Int n = 0;
+ /* VG_(printf)("freelist sanity\n"); */
+ for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
+ n += sc->size;
+ vg_assert(n == vg_freed_list_volume);
+}
+
+/* Put a shadow chunk on the freed blocks queue, possibly freeing up
+ some of the oldest blocks in the queue at the same time. */
+static void add_to_freed_queue ( ShadowChunk* sc )
+{
+ ShadowChunk* sc1;
+
+ /* Put it at the end of the freed list */
+ if (vg_freed_list_end == NULL) {
+ vg_assert(vg_freed_list_start == NULL);
+ vg_freed_list_end = vg_freed_list_start = sc;
+ vg_freed_list_volume = sc->size;
+ } else {
+ vg_assert(vg_freed_list_end->next == NULL);
+ vg_freed_list_end->next = sc;
+ vg_freed_list_end = sc;
+ vg_freed_list_volume += sc->size;
+ }
+ sc->next = NULL;
+
+ /* Release enough of the oldest blocks to bring the free queue
+ volume below vg_clo_freelist_vol. */
+
+ while (vg_freed_list_volume > SK_(clo_freelist_vol)) {
+ /* freelist_sanity(); */
+ vg_assert(vg_freed_list_start != NULL);
+ vg_assert(vg_freed_list_end != NULL);
+
+ sc1 = vg_freed_list_start;
+ vg_freed_list_volume -= sc1->size;
+ /* VG_(printf)("volume now %d\n", vg_freed_list_volume); */
+ vg_assert(vg_freed_list_volume >= 0);
+
+ if (vg_freed_list_start == vg_freed_list_end) {
+ vg_freed_list_start = vg_freed_list_end = NULL;
+ } else {
+ vg_freed_list_start = sc1->next;
+ }
+ sc1->next = NULL; /* just paranoia */
+ VG_(freeShadowChunk) ( sc1 );
+ }
+}
+
+/* Return the first shadow chunk satisfying the predicate p. */
+ShadowChunk* SK_(any_matching_freed_ShadowChunks)
+ ( Bool (*p) ( ShadowChunk* ))
+{
+ ShadowChunk* sc;
+
+ /* No point looking through freed blocks if we're not keeping
+ them around for a while... */
+ for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
+ if (p(sc))
+ return sc;
+
+ return NULL;
+}
+
+void SK_(alt_free) ( ShadowChunk* sc, ThreadState* tst )
+{
+ /* Record where freed */
+ set_where( sc, VG_(get_ExeContext) ( tst ) );
+
+ /* Put it out of harm's way for a while. */
+ add_to_freed_queue ( sc );
+}
+
+/*------------------------------------------------------------*/
+/*--- Low-level address-space scanning, for the leak ---*/
+/*--- detector. ---*/
+/*------------------------------------------------------------*/
+
+static
+jmp_buf memscan_jmpbuf;
+
+static
+void vg_scan_all_valid_memory_sighandler ( Int sigNo )
+{
+ __builtin_longjmp(memscan_jmpbuf, 1);
+}
+
+/* Safely (avoiding SIGSEGV / SIGBUS) scan the entire valid address
+ space and pass the addresses and values of all addressible,
+ defined, aligned words to notify_word. This is the basis for the
+ leak detector. Returns the number of calls made to notify_word. */
+UInt VG_(scan_all_valid_memory) ( void (*notify_word)( Addr, UInt ) )
+{
+ /* All volatile, because some gccs seem paranoid about longjmp(). */
+ volatile UInt res, numPages, page, vbytes, primaryMapNo, nWordsNotified;
+ volatile Addr pageBase, addr;
+ volatile SecMap* sm;
+ volatile UChar abits;
+ volatile UInt page_first_word;
+
+ vki_ksigaction sigbus_saved;
+ vki_ksigaction sigbus_new;
+ vki_ksigaction sigsegv_saved;
+ vki_ksigaction sigsegv_new;
+ vki_ksigset_t blockmask_saved;
+ vki_ksigset_t unblockmask_new;
+
+ /* Temporarily install a new sigsegv and sigbus handler, and make
+ sure SIGBUS, SIGSEGV and SIGTERM are unblocked. (Perhaps the
+ first two can never be blocked anyway?) */
+
+ sigbus_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
+ sigbus_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
+ sigbus_new.ksa_restorer = NULL;
+ res = VG_(ksigemptyset)( &sigbus_new.ksa_mask );
+ vg_assert(res == 0);
+
+ sigsegv_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
+ sigsegv_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
+ sigsegv_new.ksa_restorer = NULL;
+ res = VG_(ksigemptyset)( &sigsegv_new.ksa_mask );
+ vg_assert(res == 0+0);
+
+ res = VG_(ksigemptyset)( &unblockmask_new );
+ res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGBUS );
+ res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGSEGV );
+ res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGTERM );
+ vg_assert(res == 0+0+0);
+
+ res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_new, &sigbus_saved );
+ vg_assert(res == 0+0+0+0);
+
+ res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_new, &sigsegv_saved );
+ vg_assert(res == 0+0+0+0+0);
+
+ res = VG_(ksigprocmask)( VKI_SIG_UNBLOCK, &unblockmask_new, &blockmask_saved );
+ vg_assert(res == 0+0+0+0+0+0);
+
+ /* The signal handlers are installed. Actually do the memory scan. */
+ numPages = 1 << (32-VKI_BYTES_PER_PAGE_BITS);
+ vg_assert(numPages == 1048576);
+ vg_assert(4096 == (1 << VKI_BYTES_PER_PAGE_BITS));
+
+ nWordsNotified = 0;
+
+ for (page = 0; page < numPages; page++) {
+ pageBase = page << VKI_BYTES_PER_PAGE_BITS;
+ primaryMapNo = pageBase >> 16;
+ sm = primary_map[primaryMapNo];
+ if (IS_DISTINGUISHED_SM(sm)) continue;
+ if (__builtin_setjmp(memscan_jmpbuf) == 0) {
+ /* try this ... */
+ page_first_word = * (volatile UInt*)pageBase;
+ /* we get here if we didn't get a fault */
+ /* Scan the page */
+ for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) {
+ abits = get_abits4_ALIGNED(addr);
+ vbytes = get_vbytes4_ALIGNED(addr);
+ if (abits == VGM_NIBBLE_VALID
+ && vbytes == VGM_WORD_VALID) {
+ nWordsNotified++;
+ notify_word ( addr, *(UInt*)addr );
+ }
+ }
+ } else {
+ /* We get here if reading the first word of the page caused a
+ fault, which in turn caused the signal handler to longjmp.
+ Ignore this page. */
+ if (0)
+ VG_(printf)(
+ "vg_scan_all_valid_memory_sighandler: ignoring page at %p\n",
+ (void*)pageBase
+ );
+ }
+ }
+
+ /* Restore signal state to whatever it was before. */
+ res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_saved, NULL );
+ vg_assert(res == 0 +0);
+
+ res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_saved, NULL );
+ vg_assert(res == 0 +0 +0);
+
+ res = VG_(ksigprocmask)( VKI_SIG_SETMASK, &blockmask_saved, NULL );
+ vg_assert(res == 0 +0 +0 +0);
+
+ return nWordsNotified;
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Detecting leaked (unreachable) malloc'd blocks. ---*/
+/*------------------------------------------------------------*/
+
+/* A block is either
+ -- Proper-ly reached; a pointer to its start has been found
+ -- Interior-ly reached; only an interior pointer to it has been found
+ -- Unreached; so far, no pointers to any part of it have been found.
+*/
+typedef
+ enum { Unreached, Interior, Proper }
+ Reachedness;
+
+/* A block record, used for generating err msgs. */
+typedef
+ struct _LossRecord {
+ struct _LossRecord* next;
+ /* Where these lost blocks were allocated. */
+ ExeContext* allocated_at;
+ /* Their reachability. */
+ Reachedness loss_mode;
+ /* Number of blocks and total # bytes involved. */
+ UInt total_bytes;
+ UInt num_blocks;
+ }
+ LossRecord;
+
+
+/* Find the i such that ptr points at or inside the block described by
+ shadows[i]. Return -1 if none found. This assumes that shadows[]
+ has been sorted on the ->data field. */
+
+#ifdef VG_DEBUG_LEAKCHECK
+/* Used to sanity-check the fast binary-search mechanism. */
+static Int find_shadow_for_OLD ( Addr ptr,
+ ShadowChunk** shadows,
+ Int n_shadows )
+
+{
+ Int i;
+ Addr a_lo, a_hi;
+ PROF_EVENT(70);
+ for (i = 0; i < n_shadows; i++) {
+ PROF_EVENT(71);
+ a_lo = shadows[i]->data;
+ a_hi = ((Addr)shadows[i]->data) + shadows[i]->size - 1;
+ if (a_lo <= ptr && ptr <= a_hi)
+ return i;
+ }
+ return -1;
+}
+#endif
+
+
+static Int find_shadow_for ( Addr ptr,
+ ShadowChunk** shadows,
+ Int n_shadows )
+{
+ Addr a_mid_lo, a_mid_hi;
+ Int lo, mid, hi, retVal;
+ PROF_EVENT(70);
+ /* VG_(printf)("find shadow for %p = ", ptr); */
+ retVal = -1;
+ lo = 0;
+ hi = n_shadows-1;
+ while (True) {
+ PROF_EVENT(71);
+
+ /* invariant: current unsearched space is from lo to hi,
+ inclusive. */
+ if (lo > hi) break; /* not found */
+
+ mid = (lo + hi) / 2;
+ a_mid_lo = shadows[mid]->data;
+ a_mid_hi = ((Addr)shadows[mid]->data) + shadows[mid]->size - 1;
+
+ if (ptr < a_mid_lo) {
+ hi = mid-1;
+ continue;
+ }
+ if (ptr > a_mid_hi) {
+ lo = mid+1;
+ continue;
+ }
+ vg_assert(ptr >= a_mid_lo && ptr <= a_mid_hi);
+ retVal = mid;
+ break;
+ }
+
+# ifdef VG_DEBUG_LEAKCHECK
+ vg_assert(retVal == find_shadow_for_OLD ( ptr, shadows, n_shadows ));
+# endif
+ /* VG_(printf)("%d\n", retVal); */
+ return retVal;
+}
+
+
+
+static void sort_malloc_shadows ( ShadowChunk** shadows, UInt n_shadows )
+{
+ Int incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+ 9841, 29524, 88573, 265720,
+ 797161, 2391484 };
+ Int lo = 0;
+ Int hi = n_shadows-1;
+ Int i, j, h, bigN, hp;
+ ShadowChunk* v;
+
+ PROF_EVENT(72);
+ bigN = hi - lo + 1; if (bigN < 2) return;
+ hp = 0; while (incs[hp] < bigN) hp++; hp--;
+
+ for (; hp >= 0; hp--) {
+ PROF_EVENT(73);
+ h = incs[hp];
+ i = lo + h;
+ while (1) {
+ PROF_EVENT(74);
+ if (i > hi) break;
+ v = shadows[i];
+ j = i;
+ while (shadows[j-h]->data > v->data) {
+ PROF_EVENT(75);
+ shadows[j] = shadows[j-h];
+ j = j - h;
+ if (j <= (lo + h - 1)) break;
+ }
+ shadows[j] = v;
+ i++;
+ }
+ }
+}
+
+/* Globals, for the callback used by SK_(detect_memory_leaks). */
+
+static ShadowChunk** vglc_shadows;
+static Int vglc_n_shadows;
+static Reachedness* vglc_reachedness;
+static Addr vglc_min_mallocd_addr;
+static Addr vglc_max_mallocd_addr;
+
+static
+void vg_detect_memory_leaks_notify_addr ( Addr a, UInt word_at_a )
+{
+ Int sh_no;
+ Addr ptr;
+
+ /* Rule out some known causes of bogus pointers. Mostly these do
+ not cause much trouble because only a few false pointers can
+ ever lurk in these places. This mainly stops it reporting that
+ blocks are still reachable in stupid test programs like this
+
+ int main (void) { char* a = malloc(100); return 0; }
+
+ which people seem inordinately fond of writing, for some reason.
+
+ Note that this is a complete kludge. It would be better to
+ ignore any addresses corresponding to valgrind.so's .bss and
+ .data segments, but I cannot think of a reliable way to identify
+ where the .bss segment has been put. If you can, drop me a
+ line.
+ */
+ if (VG_(within_stack)(a)) return;
+ if (VG_(within_m_state_static)(a)) return;
+ if (a == (Addr)(&vglc_min_mallocd_addr)) return;
+ if (a == (Addr)(&vglc_max_mallocd_addr)) return;
+
+ /* OK, let's get on and do something Useful for a change. */
+
+ ptr = (Addr)word_at_a;
+ if (ptr >= vglc_min_mallocd_addr && ptr <= vglc_max_mallocd_addr) {
+ /* Might be legitimate; we'll have to investigate further. */
+ sh_no = find_shadow_for ( ptr, vglc_shadows, vglc_n_shadows );
+ if (sh_no != -1) {
+ /* Found a block at/into which ptr points. */
+ vg_assert(sh_no >= 0 && sh_no < vglc_n_shadows);
+ vg_assert(ptr < vglc_shadows[sh_no]->data
+ + vglc_shadows[sh_no]->size);
+ /* Decide whether Proper-ly or Interior-ly reached. */
+ if (ptr == vglc_shadows[sh_no]->data) {
+ if (0) VG_(printf)("pointer at %p to %p\n", a, word_at_a );
+ vglc_reachedness[sh_no] = Proper;
+ } else {
+ if (vglc_reachedness[sh_no] == Unreached)
+ vglc_reachedness[sh_no] = Interior;
+ }
+ }
+ }
+}
+
+
+void SK_(detect_memory_leaks) ( void )
+{
+ Int i;
+ Int blocks_leaked, bytes_leaked;
+ Int blocks_dubious, bytes_dubious;
+ Int blocks_reachable, bytes_reachable;
+ Int n_lossrecords;
+ UInt bytes_notified;
+
+ LossRecord* errlist;
+ LossRecord* p;
+
+ PROF_EVENT(76);
+
+ /* VG_(get_malloc_shadows) allocates storage for shadows */
+ vglc_shadows = VG_(get_malloc_shadows)( &vglc_n_shadows );
+ if (vglc_n_shadows == 0) {
+ vg_assert(vglc_shadows == NULL);
+ VG_(message)(Vg_UserMsg,
+ "No malloc'd blocks -- no leaks are possible.\n");
+ return;
+ }
+
+ VG_(message)(Vg_UserMsg,
+ "searching for pointers to %d not-freed blocks.",
+ vglc_n_shadows );
+ sort_malloc_shadows ( vglc_shadows, vglc_n_shadows );
+
+ /* Sanity check; assert that the blocks are now in order and that
+ they don't overlap. */
+ for (i = 0; i < vglc_n_shadows-1; i++) {
+ vg_assert( ((Addr)vglc_shadows[i]->data)
+ < ((Addr)vglc_shadows[i+1]->data) );
+ vg_assert( ((Addr)vglc_shadows[i]->data) + vglc_shadows[i]->size
+ < ((Addr)vglc_shadows[i+1]->data) );
+ }
+
+ vglc_min_mallocd_addr = ((Addr)vglc_shadows[0]->data);
+ vglc_max_mallocd_addr = ((Addr)vglc_shadows[vglc_n_shadows-1]->data)
+ + vglc_shadows[vglc_n_shadows-1]->size - 1;
+
+ vglc_reachedness
+ = VG_(malloc)( vglc_n_shadows * sizeof(Reachedness) );
+ for (i = 0; i < vglc_n_shadows; i++)
+ vglc_reachedness[i] = Unreached;
+
+ /* Do the scan of memory. */
+ bytes_notified
+ = VG_(scan_all_valid_memory)( &vg_detect_memory_leaks_notify_addr )
+ * VKI_BYTES_PER_WORD;
+
+ VG_(message)(Vg_UserMsg, "checked %d bytes.", bytes_notified);
+
+ blocks_leaked = bytes_leaked = 0;
+ blocks_dubious = bytes_dubious = 0;
+ blocks_reachable = bytes_reachable = 0;
+
+ for (i = 0; i < vglc_n_shadows; i++) {
+ if (vglc_reachedness[i] == Unreached) {
+ blocks_leaked++;
+ bytes_leaked += vglc_shadows[i]->size;
+ }
+ else if (vglc_reachedness[i] == Interior) {
+ blocks_dubious++;
+ bytes_dubious += vglc_shadows[i]->size;
+ }
+ else if (vglc_reachedness[i] == Proper) {
+ blocks_reachable++;
+ bytes_reachable += vglc_shadows[i]->size;
+ }
+ }
+
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "definitely lost: %d bytes in %d blocks.",
+ bytes_leaked, blocks_leaked );
+ VG_(message)(Vg_UserMsg, "possibly lost: %d bytes in %d blocks.",
+ bytes_dubious, blocks_dubious );
+ VG_(message)(Vg_UserMsg, "still reachable: %d bytes in %d blocks.",
+ bytes_reachable, blocks_reachable );
+
+
+ /* Common up the lost blocks so we can print sensible error
+ messages. */
+
+ n_lossrecords = 0;
+ errlist = NULL;
+ for (i = 0; i < vglc_n_shadows; i++) {
+
+ /* 'where' stored in 'skin_extra' field */
+ ExeContext* where = get_where ( vglc_shadows[i] );
+
+ for (p = errlist; p != NULL; p = p->next) {
+ if (p->loss_mode == vglc_reachedness[i]
+ && VG_(eq_ExeContext) ( SK_(clo_leak_resolution),
+ p->allocated_at,
+ where) ) {
+ break;
+ }
+ }
+ if (p != NULL) {
+ p->num_blocks ++;
+ p->total_bytes += vglc_shadows[i]->size;
+ } else {
+ n_lossrecords ++;
+ p = VG_(malloc)(sizeof(LossRecord));
+ p->loss_mode = vglc_reachedness[i];
+ p->allocated_at = where;
+ p->total_bytes = vglc_shadows[i]->size;
+ p->num_blocks = 1;
+ p->next = errlist;
+ errlist = p;
+ }
+ }
+
+ for (i = 0; i < n_lossrecords; i++) {
+ LossRecord* p_min = NULL;
+ UInt n_min = 0xFFFFFFFF;
+ for (p = errlist; p != NULL; p = p->next) {
+ if (p->num_blocks > 0 && p->total_bytes < n_min) {
+ n_min = p->total_bytes;
+ p_min = p;
+ }
+ }
+ vg_assert(p_min != NULL);
+
+ if ( (!SK_(clo_show_reachable)) && p_min->loss_mode == Proper) {
+ p_min->num_blocks = 0;
+ continue;
+ }
+
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(
+ Vg_UserMsg,
+ "%d bytes in %d blocks are %s in loss record %d of %d",
+ p_min->total_bytes, p_min->num_blocks,
+ p_min->loss_mode==Unreached ? "definitely lost" :
+ (p_min->loss_mode==Interior ? "possibly lost"
+ : "still reachable"),
+ i+1, n_lossrecords
+ );
+ VG_(pp_ExeContext)(p_min->allocated_at);
+ p_min->num_blocks = 0;
+ }
+
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg, "LEAK SUMMARY:");
+ VG_(message)(Vg_UserMsg, " definitely lost: %d bytes in %d blocks.",
+ bytes_leaked, blocks_leaked );
+ VG_(message)(Vg_UserMsg, " possibly lost: %d bytes in %d blocks.",
+ bytes_dubious, blocks_dubious );
+ VG_(message)(Vg_UserMsg, " still reachable: %d bytes in %d blocks.",
+ bytes_reachable, blocks_reachable );
+ if (!SK_(clo_show_reachable)) {
+ VG_(message)(Vg_UserMsg,
+ "Reachable blocks (those to which a pointer was found) are not shown.");
+ VG_(message)(Vg_UserMsg,
+ "To see them, rerun with: --show-reachable=yes");
+ }
+ VG_(message)(Vg_UserMsg, "");
+
+ VG_(free) ( vglc_shadows );
+ VG_(free) ( vglc_reachedness );
+}
+
+
+/* ---------------------------------------------------------------------
+ Sanity check machinery (permanently engaged).
+ ------------------------------------------------------------------ */
+
+/* Check that nobody has spuriously claimed that the first or last 16
+ pages (64 KB) of address space have become accessible. Failure of
+ the following do not per se indicate an internal consistency
+ problem, but they are so likely to that we really want to know
+ about it if so. */
+
+Bool SK_(cheap_sanity_check) ( void )
+{
+ if (IS_DISTINGUISHED_SM(primary_map[0]) &&
+ IS_DISTINGUISHED_SM(primary_map[65535]))
+ return True;
+ else
+ return False;
+}
+
+Bool SK_(expensive_sanity_check) ( void )
+{
+ Int i;
+
+ /* Make sure nobody changed the distinguished secondary. */
+ for (i = 0; i < 8192; i++)
+ if (distinguished_secondary_map.abits[i] != VGM_BYTE_INVALID)
+ return False;
+
+ for (i = 0; i < 65536; i++)
+ if (distinguished_secondary_map.vbyte[i] != VGM_BYTE_INVALID)
+ return False;
+
+ /* Make sure that the upper 3/4 of the primary map hasn't
+ been messed with. */
+ for (i = 65536; i < 262144; i++)
+ if (primary_map[i] != & distinguished_secondary_map)
+ return False;
+
+ return True;
+}
+
+/* ---------------------------------------------------------------------
+ Debugging machinery (turn on to debug). Something of a mess.
+ ------------------------------------------------------------------ */
+
+#if 0
+/* Print the value tags on the 8 integer registers & flag reg. */
+
+static void uint_to_bits ( UInt x, Char* str )
+{
+ Int i;
+ Int w = 0;
+ /* str must point to a space of at least 36 bytes. */
+ for (i = 31; i >= 0; i--) {
+ str[w++] = (x & ( ((UInt)1) << i)) ? '1' : '0';
+ if (i == 24 || i == 16 || i == 8)
+ str[w++] = ' ';
+ }
+ str[w++] = 0;
+ vg_assert(w == 36);
+}
+
+/* Caution! Not vthread-safe; looks in VG_(baseBlock), not the thread
+ state table. */
+
+static void vg_show_reg_tags ( void )
+{
+ Char buf1[36];
+ Char buf2[36];
+ UInt z_eax, z_ebx, z_ecx, z_edx,
+ z_esi, z_edi, z_ebp, z_esp, z_eflags;
+
+ z_eax = VG_(baseBlock)[VGOFF_(sh_eax)];
+ z_ebx = VG_(baseBlock)[VGOFF_(sh_ebx)];
+ z_ecx = VG_(baseBlock)[VGOFF_(sh_ecx)];
+ z_edx = VG_(baseBlock)[VGOFF_(sh_edx)];
+ z_esi = VG_(baseBlock)[VGOFF_(sh_esi)];
+ z_edi = VG_(baseBlock)[VGOFF_(sh_edi)];
+ z_ebp = VG_(baseBlock)[VGOFF_(sh_ebp)];
+ z_esp = VG_(baseBlock)[VGOFF_(sh_esp)];
+ z_eflags = VG_(baseBlock)[VGOFF_(sh_eflags)];
+
+ uint_to_bits(z_eflags, buf1);
+ VG_(message)(Vg_DebugMsg, "efl %\n", buf1);
+
+ uint_to_bits(z_eax, buf1);
+ uint_to_bits(z_ebx, buf2);
+ VG_(message)(Vg_DebugMsg, "eax %s ebx %s\n", buf1, buf2);
+
+ uint_to_bits(z_ecx, buf1);
+ uint_to_bits(z_edx, buf2);
+ VG_(message)(Vg_DebugMsg, "ecx %s edx %s\n", buf1, buf2);
+
+ uint_to_bits(z_esi, buf1);
+ uint_to_bits(z_edi, buf2);
+ VG_(message)(Vg_DebugMsg, "esi %s edi %s\n", buf1, buf2);
+
+ uint_to_bits(z_ebp, buf1);
+ uint_to_bits(z_esp, buf2);
+ VG_(message)(Vg_DebugMsg, "ebp %s esp %s\n", buf1, buf2);
+}
+
+
+/* For debugging only. Scan the address space and touch all allegedly
+ addressible words. Useful for establishing where Valgrind's idea of
+ addressibility has diverged from what the kernel believes. */
+
+static
+void zzzmemscan_notify_word ( Addr a, UInt w )
+{
+}
+
+void zzzmemscan ( void )
+{
+ Int n_notifies
+ = VG_(scan_all_valid_memory)( zzzmemscan_notify_word );
+ VG_(printf)("zzzmemscan: n_bytes = %d\n", 4 * n_notifies );
+}
+#endif
+
+
+
+
+#if 0
+static Int zzz = 0;
+
+void show_bb ( Addr eip_next )
+{
+ VG_(printf)("[%4d] ", zzz);
+ vg_show_reg_tags( &VG_(m_shadow );
+ VG_(translate) ( eip_next, NULL, NULL, NULL );
+}
+#endif /* 0 */
+
+/*------------------------------------------------------------*/
+/*--- Syscall wrappers ---*/
+/*------------------------------------------------------------*/
+
+void* SK_(pre_syscall) ( ThreadId tid, UInt syscallno, Bool isBlocking )
+{
+ Int sane = SK_(cheap_sanity_check)();
+ return (void*)sane;
+}
+
+void SK_(post_syscall) ( ThreadId tid, UInt syscallno,
+ void* pre_result, Int res, Bool isBlocking )
+{
+ Int sane_before_call = (Int)pre_result;
+ Bool sane_after_call = SK_(cheap_sanity_check)();
+
+ if ((Int)sane_before_call && (!sane_after_call)) {
+ VG_(message)(Vg_DebugMsg, "post-syscall: ");
+ VG_(message)(Vg_DebugMsg,
+ "probable sanity check failure for syscall number %d\n",
+ syscallno );
+ VG_(panic)("aborting due to the above ... bye!");
+ }
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Setup ---*/
+/*------------------------------------------------------------*/
+
+void SK_(written_shadow_regs_values)( UInt* gen_reg_value, UInt* eflags_value )
+{
+ *gen_reg_value = VGM_WORD_VALID;
+ *eflags_value = VGM_EFLAGS_VALID;
+}
+
+Bool SK_(process_cmd_line_option)(Char* arg)
+{
+# define STREQ(s1,s2) (0==VG_(strcmp_ws)((s1),(s2)))
+# define STREQN(nn,s1,s2) (0==VG_(strncmp_ws)((s1),(s2),(nn)))
+
+ if (STREQ(arg, "--partial-loads-ok=yes"))
+ SK_(clo_partial_loads_ok) = True;
+ else if (STREQ(arg, "--partial-loads-ok=no"))
+ SK_(clo_partial_loads_ok) = False;
+
+ else if (STREQN(15, arg, "--freelist-vol=")) {
+ SK_(clo_freelist_vol) = (Int)VG_(atoll)(&arg[15]);
+ if (SK_(clo_freelist_vol) < 0) SK_(clo_freelist_vol) = 0;
+ }
+
+ else if (STREQ(arg, "--leak-check=yes"))
+ SK_(clo_leak_check) = True;
+ else if (STREQ(arg, "--leak-check=no"))
+ SK_(clo_leak_check) = False;
+
+ else if (STREQ(arg, "--leak-resolution=low"))
+ SK_(clo_leak_resolution) = Vg_LowRes;
+ else if (STREQ(arg, "--leak-resolution=med"))
+ SK_(clo_leak_resolution) = Vg_MedRes;
+ else if (STREQ(arg, "--leak-resolution=high"))
+ SK_(clo_leak_resolution) = Vg_HighRes;
+
+ else if (STREQ(arg, "--show-reachable=yes"))
+ SK_(clo_show_reachable) = True;
+ else if (STREQ(arg, "--show-reachable=no"))
+ SK_(clo_show_reachable) = False;
+
+ else if (STREQ(arg, "--workaround-gcc296-bugs=yes"))
+ SK_(clo_workaround_gcc296_bugs) = True;
+ else if (STREQ(arg, "--workaround-gcc296-bugs=no"))
+ SK_(clo_workaround_gcc296_bugs) = False;
+
+ else if (STREQ(arg, "--check-addrVs=yes"))
+ SK_(clo_check_addrVs) = True;
+ else if (STREQ(arg, "--check-addrVs=no"))
+ SK_(clo_check_addrVs) = False;
+
+ else if (STREQ(arg, "--cleanup=yes"))
+ SK_(clo_cleanup) = True;
+ else if (STREQ(arg, "--cleanup=no"))
+ SK_(clo_cleanup) = False;
+
+ else
+ return False;
+
+ return True;
+
+#undef STREQ
+#undef STREQN
+}
+
+Char* SK_(usage)(void)
+{
+ return
+" --partial-loads-ok=no|yes too hard to explain here; see manual [yes]\n"
+" --freelist-vol=<number> volume of freed blocks queue [1000000]\n"
+" --leak-check=no|yes search for memory leaks at exit? [no]\n"
+" --leak-resolution=low|med|high\n"
+" amount of bt merging in leak check [low]\n"
+" --show-reachable=no|yes show reachable blocks in leak check? [no]\n"
+" --workaround-gcc296-bugs=no|yes self explanatory [no]\n"
+" --check-addrVs=no|yes experimental lighterweight checking? [yes]\n"
+" yes == Valgrind's original behaviour\n"
+"\n"
+" --cleanup=no|yes improve after instrumentation? [yes]\n";
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Setup ---*/
+/*------------------------------------------------------------*/
+
+void SK_(pre_clo_init)(VgNeeds* needs, VgTrackEvents* track)
+{
+ needs->name = "valgrind";
+ needs->description = "a memory error detector";
+
+ needs->core_errors = True;
+ needs->skin_errors = True;
+ needs->run_libc_freeres = True;
+
+ needs->sizeof_shadow_block = 1;
+
+ needs->basic_block_discards = False;
+ needs->shadow_regs = True;
+ needs->command_line_options = True;
+ needs->client_requests = True;
+ needs->extended_UCode = True;
+ needs->syscall_wrapper = True;
+ needs->alternative_free = True;
+ needs->sanity_checks = True;
+
+ VG_(register_compact_helper)((Addr) & SK_(helper_value_check4_fail));
+ VG_(register_compact_helper)((Addr) & SK_(helper_value_check0_fail));
+ VG_(register_compact_helper)((Addr) & SK_(helperc_STOREV4));
+ VG_(register_compact_helper)((Addr) & SK_(helperc_STOREV1));
+ VG_(register_compact_helper)((Addr) & SK_(helperc_LOADV4));
+ VG_(register_compact_helper)((Addr) & SK_(helperc_LOADV1));
+
+ /* These two made non-compact because 2-byte transactions are rare. */
+ VG_(register_noncompact_helper)((Addr) & SK_(helperc_STOREV2));
+ VG_(register_noncompact_helper)((Addr) & SK_(helperc_LOADV2));
+ VG_(register_noncompact_helper)((Addr) & SK_(fpu_write_check));
+ VG_(register_noncompact_helper)((Addr) & SK_(fpu_read_check));
+ VG_(register_noncompact_helper)((Addr) & SK_(helper_value_check2_fail));
+ VG_(register_noncompact_helper)((Addr) & SK_(helper_value_check1_fail));
+
+ /* Events to track */
+ track->new_mem_startup = & memcheck_new_mem_startup;
+ track->new_mem_heap = & memcheck_new_mem_heap;
+ track->new_mem_stack = & SK_(make_writable);
+ track->new_mem_stack_aligned = & make_writable_aligned;
+ track->new_mem_stack_signal = & SK_(make_writable);
+ track->new_mem_brk = & SK_(make_writable);
+ track->new_mem_mmap = & memcheck_set_perms;
+
+ track->copy_mem_heap = & copy_address_range_state;
+ track->copy_mem_remap = & copy_address_range_state;
+ track->change_mem_mprotect = & memcheck_set_perms;
+
+ track->ban_mem_heap = & SK_(make_noaccess);
+ track->ban_mem_stack = & SK_(make_noaccess);
+
+ track->die_mem_heap = & SK_(make_noaccess);
+ track->die_mem_stack = & SK_(make_noaccess);
+ track->die_mem_stack_aligned = & make_noaccess_aligned;
+ track->die_mem_stack_signal = & SK_(make_noaccess);
+ track->die_mem_brk = & SK_(make_noaccess);
+ track->die_mem_munmap = & SK_(make_noaccess);
+
+ track->bad_free = & SK_(record_free_error);
+ track->mismatched_free = & SK_(record_freemismatch_error);
+
+ track->pre_mem_read = & check_is_readable;
+ track->pre_mem_read_asciiz = & check_is_readable_asciiz;
+ track->pre_mem_write = & check_is_writable;
+ track->post_mem_write = & SK_(make_readable);
+
+ init_shadow_memory();
+
+ init_prof_mem();
+
+ VGP_(register_profile_event) ( VgpSetMem, "set-mem-perms" );
+ VGP_(register_profile_event) ( VgpCheckMem, "check-mem-perms" );
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end vg_memcheck.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/mc_translate.c b/memcheck/mc_translate.c
new file mode 100644
index 0000000..34f9643
--- /dev/null
+++ b/memcheck/mc_translate.c
@@ -0,0 +1,1470 @@
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: instrument UCode to perform ---*/
+/*--- memory checking operations. ---*/
+/*--- vg_memcheck_translate.c ---*/
+/*--------------------------------------------------------------------*/
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+
+ 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 "vg_memcheck_include.h"
+
+/* ---------------------------------------------------------------------
+ Template functions for extending UCode
+ ------------------------------------------------------------------ */
+
+/* Compare this with the restrictions on core instructions in
+ vg_translate.c:VG_(saneUInstr)(). Everything general said there applies
+ here too.
+*/
+Bool SK_(saneExtUInstr)(Bool beforeRA, Bool beforeLiveness, UInstr* u)
+{
+// SSS: duplicating these macros really sucks
+# define LIT0 (u->lit32 == 0)
+# define LIT1 (!(LIT0))
+# define LITm (u->tag1 == Literal ? True : LIT0 )
+# define SZ0 (u->size == 0)
+# define SZi (u->size == 4 || u->size == 2 || u->size == 1)
+# define SZj (u->size == 4 || u->size == 2 || u->size == 1 || u->size == 0)
+# define CC0 (u->flags_r == FlagsEmpty && u->flags_w == FlagsEmpty)
+# define TR1 (beforeRA ? (u->tag1 == TempReg) : (u->tag1 == RealReg))
+# define TR2 (beforeRA ? (u->tag2 == TempReg) : (u->tag2 == RealReg))
+# define A1 (u->tag1 == ArchReg)
+# define A2 (u->tag2 == ArchReg)
+# define L1 (u->tag1 == Literal && u->val1 == 0)
+# define Ls1 (u->tag1 == Lit16)
+# define Ls3 (u->tag3 == Lit16)
+# define TRL1 (TR1 || L1)
+# define TRA1 (TR1 || A1)
+# define N2 (u->tag2 == NoValue)
+# define N3 (u->tag3 == NoValue)
+# define COND0 (u->cond == 0)
+# define EXTRA4b0 (u->extra4b == 0)
+# define SG_WD0 (u->signed_widen == 0)
+# define JMPKIND0 (u->jmpkind == 0)
+# define CCALL0 (u->argc==0 && u->regparms_n==0 && u->has_ret_val==0 && \
+ ( beforeLiveness \
+ ? u->regs_live_after == ALL_RREGS_LIVE \
+ : True ))
+# define XOTHER (COND0 && EXTRA4b0 && SG_WD0 && JMPKIND0 && CCALL0)
+
+ Int n_lits = 0;
+ if (u->tag1 == Literal) n_lits++;
+ if (u->tag2 == Literal) n_lits++;
+ if (u->tag3 == Literal) n_lits++;
+ if (n_lits > 1)
+ return False;
+
+ /* Fields not checked: val1, val2, val3 */
+
+ switch (u->opcode) {
+
+ /* Fields checked: lit32 size flags_r/w tag1 tag2 tag3 (rest) */
+ case LOADV: return LIT0 && SZi && CC0 && TR1 && TR2 && N3 && XOTHER;
+ case STOREV: return LITm && SZi && CC0 && TRL1 && TR2 && N3 && XOTHER;
+ case GETV: return LIT0 && SZi && CC0 && A1 && TR2 && N3 && XOTHER;
+ case PUTV: return LITm && SZi && CC0 && TRL1 && A2 && N3 && XOTHER;
+ case GETVF:
+ case PUTVF: return LIT0 && SZ0 && CC0 && TR1 && N2 && N3 && XOTHER;
+ case TESTV:
+ case SETV: return LIT0 && SZj && CC0 && TRA1 && N2 && N3 && XOTHER;
+ case TAG1: return LIT0 && SZ0 && CC0 && TR1 && N2 && Ls3 && XOTHER;
+ case TAG2: return LIT0 && SZ0 && CC0 && TR1 && TR2 && Ls3 && XOTHER;
+ default:
+ VG_(printf)("unhandled opcode: %u\n", u->opcode);
+ VG_(panic)("SK_(saneExtUInstr): unhandled opcode");
+ }
+# undef LIT0
+# undef LIT1
+# undef LITm
+# undef SZ0
+# undef SZi
+# undef SZj
+# undef CC0
+# undef TR1
+# undef TR2
+# undef A1
+# undef A2
+# undef L1
+# undef Ls1
+# undef Ls3
+# undef TRL1
+# undef TRA1
+# undef N2
+# undef N3
+# undef COND0
+# undef EXTRA4b0
+# undef JMPKIND0
+# undef CCALL0
+# undef XOTHER
+}
+
+static Char* nameOfTagOp ( TagOp h )
+{
+ switch (h) {
+ case Tag_PCast40: return "PCast40";
+ case Tag_PCast20: return "PCast20";
+ case Tag_PCast10: return "PCast10";
+ case Tag_PCast01: return "PCast01";
+ case Tag_PCast02: return "PCast02";
+ case Tag_PCast04: return "PCast04";
+ case Tag_PCast14: return "PCast14";
+ case Tag_PCast12: return "PCast12";
+ case Tag_PCast11: return "PCast11";
+ case Tag_Left4: return "Left4";
+ case Tag_Left2: return "Left2";
+ case Tag_Left1: return "Left1";
+ case Tag_SWiden14: return "SWiden14";
+ case Tag_SWiden24: return "SWiden24";
+ case Tag_SWiden12: return "SWiden12";
+ case Tag_ZWiden14: return "ZWiden14";
+ case Tag_ZWiden24: return "ZWiden24";
+ case Tag_ZWiden12: return "ZWiden12";
+ case Tag_UifU4: return "UifU4";
+ case Tag_UifU2: return "UifU2";
+ case Tag_UifU1: return "UifU1";
+ case Tag_UifU0: return "UifU0";
+ case Tag_DifD4: return "DifD4";
+ case Tag_DifD2: return "DifD2";
+ case Tag_DifD1: return "DifD1";
+ case Tag_ImproveAND4_TQ: return "ImproveAND4_TQ";
+ case Tag_ImproveAND2_TQ: return "ImproveAND2_TQ";
+ case Tag_ImproveAND1_TQ: return "ImproveAND1_TQ";
+ case Tag_ImproveOR4_TQ: return "ImproveOR4_TQ";
+ case Tag_ImproveOR2_TQ: return "ImproveOR2_TQ";
+ case Tag_ImproveOR1_TQ: return "ImproveOR1_TQ";
+ case Tag_DebugFn: return "DebugFn";
+ default: VG_(panic)("vg_nameOfTagOp");
+ }
+}
+
+
+Char* SK_(nameExtUOpcode)(Opcode opc)
+{
+ switch (opc) {
+ case GETVF: return "GETVF";
+ case PUTVF: return "PUTVF";
+ case TAG1: return "TAG1";
+ case TAG2: return "TAG2";
+ case LOADV: return "LOADV";
+ case STOREV: return "STOREV";
+ case GETV: return "GETV";
+ case PUTV: return "PUTV";
+ case TESTV: return "TESTV";
+ case SETV: return "SETV";
+ default:
+ VG_(printf)("unhandled opcode: %u\n", opc);
+ VG_(panic)("SK_(nameExtUOpcode): unhandled case");
+ }
+}
+
+/* ---------------------------------------------------------------------
+ Debugging stuff.
+ ------------------------------------------------------------------ */
+
+void SK_(ppExtUInstr)(UInstr* u)
+{
+ switch (u->opcode) {
+
+ case TAG1:
+ VG_(printf)("\t");
+ VG_(ppUOperand)(u, 1, 4, False);
+ VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 ));
+ VG_(ppUOperand)(u, 1, 4, False);
+ VG_(printf)(" )");
+ break;
+
+ case TAG2:
+ VG_(printf)("\t");
+ VG_(ppUOperand)(u, 2, 4, False);
+ VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 ));
+ VG_(ppUOperand)(u, 1, 4, False);
+ VG_(printf)(", ");
+ VG_(ppUOperand)(u, 2, 4, False);
+ VG_(printf)(" )");
+ break;
+
+ case STOREV: case LOADV:
+ VG_(printf)("\t");
+ VG_(ppUOperand)(u, 1, u->size, u->opcode==LOADV);
+ VG_(printf)(", ");
+ VG_(ppUOperand)(u, 2, u->size, u->opcode==STOREV);
+ break;
+
+ case PUTVF: case GETVF:
+ VG_(printf)("\t");
+ VG_(ppUOperand)(u, 1, 0, False);
+ break;
+
+ case GETV: case PUTV:
+ VG_(printf)("\t");
+ VG_(ppUOperand)(u, 1, u->opcode==PUTV ? 4 : u->size, False);
+ VG_(printf)(", ");
+ VG_(ppUOperand)(u, 2, u->opcode==GETV ? 4 : u->size, False);
+ break;
+
+ case TESTV: case SETV:
+ VG_(printf)("\t");
+ VG_(ppUOperand)(u, 1, u->size, False);
+ break;
+
+ default:
+ VG_(printf)("unhandled opcode: %u\n", u->opcode);
+ VG_(panic)("SK_(ppExtUInstr): unhandled opcode");
+ }
+
+}
+
+Int SK_(getExtRegUsage)(UInstr* u, Tag tag, RegUse* arr)
+{
+# define RD(ono) VG_UINSTR_READS_REG(ono)
+# define WR(ono) VG_UINSTR_WRITES_REG(ono)
+
+ Int n = 0;
+ switch (u->opcode) {
+
+ // JJJ: I don't understand this comment... what about reg alloc? --njn
+
+ /* These sizes are only ever consulted when the instrumentation
+ code is being added, so the following can return
+ manifestly-bogus sizes. */
+
+ case TAG1: RD(1); WR(1); break;
+ case TAG2: RD(1); RD(2); WR(2); break;
+ case LOADV: RD(1); WR(2); break;
+ case STOREV: RD(1); RD(2); break;
+ case GETV: WR(2); break;
+ case PUTV: RD(1); break;
+ case TESTV: RD(1); break;
+ case SETV: WR(1); break;
+ case PUTVF: RD(1); break;
+ case GETVF: WR(1); break;
+
+ default:
+ VG_(printf)("unhandled opcode: %u\n", u->opcode);
+ VG_(panic)("SK_(getExtRegUsage): unhandled opcode");
+ }
+ return n;
+
+# undef RD
+# undef WR
+}
+
+/*------------------------------------------------------------*/
+/*--- New instrumentation machinery. ---*/
+/*------------------------------------------------------------*/
+
+#define uInstr1 VG_(newUInstr1)
+#define uInstr2 VG_(newUInstr2)
+#define uInstr3 VG_(newUInstr3)
+#define uLiteral VG_(setLiteralField)
+#define uCCall VG_(setCCallFields)
+#define newTemp VG_(getNewTemp)
+#define newShadow VG_(getNewShadow)
+
+static
+TagOp get_Tag_ImproveOR_TQ ( Int sz )
+{
+ switch (sz) {
+ case 4: return Tag_ImproveOR4_TQ;
+ case 2: return Tag_ImproveOR2_TQ;
+ case 1: return Tag_ImproveOR1_TQ;
+ default: VG_(panic)("get_Tag_ImproveOR_TQ");
+ }
+}
+
+
+static
+TagOp get_Tag_ImproveAND_TQ ( Int sz )
+{
+ switch (sz) {
+ case 4: return Tag_ImproveAND4_TQ;
+ case 2: return Tag_ImproveAND2_TQ;
+ case 1: return Tag_ImproveAND1_TQ;
+ default: VG_(panic)("get_Tag_ImproveAND_TQ");
+ }
+}
+
+
+static
+TagOp get_Tag_Left ( Int sz )
+{
+ switch (sz) {
+ case 4: return Tag_Left4;
+ case 2: return Tag_Left2;
+ case 1: return Tag_Left1;
+ default: VG_(panic)("get_Tag_Left");
+ }
+}
+
+
+static
+TagOp get_Tag_UifU ( Int sz )
+{
+ switch (sz) {
+ case 4: return Tag_UifU4;
+ case 2: return Tag_UifU2;
+ case 1: return Tag_UifU1;
+ case 0: return Tag_UifU0;
+ default: VG_(panic)("get_Tag_UifU");
+ }
+}
+
+
+static
+TagOp get_Tag_DifD ( Int sz )
+{
+ switch (sz) {
+ case 4: return Tag_DifD4;
+ case 2: return Tag_DifD2;
+ case 1: return Tag_DifD1;
+ default: VG_(panic)("get_Tag_DifD");
+ }
+}
+
+
+static
+TagOp get_Tag_PCast ( Int szs, Int szd )
+{
+ if (szs == 4 && szd == 0) return Tag_PCast40;
+ if (szs == 2 && szd == 0) return Tag_PCast20;
+ if (szs == 1 && szd == 0) return Tag_PCast10;
+ if (szs == 0 && szd == 1) return Tag_PCast01;
+ if (szs == 0 && szd == 2) return Tag_PCast02;
+ if (szs == 0 && szd == 4) return Tag_PCast04;
+ if (szs == 1 && szd == 4) return Tag_PCast14;
+ if (szs == 1 && szd == 2) return Tag_PCast12;
+ if (szs == 1 && szd == 1) return Tag_PCast11;
+ VG_(printf)("get_Tag_PCast(%d,%d)\n", szs, szd);
+ VG_(panic)("get_Tag_PCast");
+}
+
+
+static
+TagOp get_Tag_Widen ( Bool syned, Int szs, Int szd )
+{
+ if (szs == 1 && szd == 2 && syned) return Tag_SWiden12;
+ if (szs == 1 && szd == 2 && !syned) return Tag_ZWiden12;
+
+ if (szs == 1 && szd == 4 && syned) return Tag_SWiden14;
+ if (szs == 1 && szd == 4 && !syned) return Tag_ZWiden14;
+
+ if (szs == 2 && szd == 4 && syned) return Tag_SWiden24;
+ if (szs == 2 && szd == 4 && !syned) return Tag_ZWiden24;
+
+ VG_(printf)("get_Tag_Widen(%d,%d,%d)\n", (Int)syned, szs, szd);
+ VG_(panic)("get_Tag_Widen");
+}
+
+/* Pessimally cast the spec'd shadow from one size to another. */
+static
+void create_PCast ( UCodeBlock* cb, Int szs, Int szd, Int tempreg )
+{
+ if (szs == 0 && szd == 0)
+ return;
+ uInstr3(cb, TAG1, 0, TempReg, tempreg,
+ NoValue, 0,
+ Lit16, get_Tag_PCast(szs,szd));
+}
+
+
+/* Create a signed or unsigned widen of the spec'd shadow from one
+ size to another. The only allowed size transitions are 1->2, 1->4
+ and 2->4. */
+static
+void create_Widen ( UCodeBlock* cb, Bool signed_widen,
+ Int szs, Int szd, Int tempreg )
+{
+ if (szs == szd) return;
+ uInstr3(cb, TAG1, 0, TempReg, tempreg,
+ NoValue, 0,
+ Lit16, get_Tag_Widen(signed_widen,szs,szd));
+}
+
+
+/* Get the condition codes into a new shadow, at the given size. */
+static
+Int create_GETVF ( UCodeBlock* cb, Int sz )
+{
+ Int tt = newShadow(cb);
+ uInstr1(cb, GETVF, 0, TempReg, tt);
+ create_PCast(cb, 0, sz, tt);
+ return tt;
+}
+
+
+/* Save the condition codes from the spec'd shadow. */
+static
+void create_PUTVF ( UCodeBlock* cb, Int sz, Int tempreg )
+{
+ if (sz == 0) {
+ uInstr1(cb, PUTVF, 0, TempReg, tempreg);
+ } else {
+ Int tt = newShadow(cb);
+ uInstr2(cb, MOV, 4, TempReg, tempreg, TempReg, tt);
+ create_PCast(cb, sz, 0, tt);
+ uInstr1(cb, PUTVF, 0, TempReg, tt);
+ }
+}
+
+
+/* Do Left on the spec'd shadow. */
+static
+void create_Left ( UCodeBlock* cb, Int sz, Int tempreg )
+{
+ uInstr3(cb, TAG1, 0,
+ TempReg, tempreg,
+ NoValue, 0,
+ Lit16, get_Tag_Left(sz));
+}
+
+
+/* Do UifU on ts and td, putting the result in td. */
+static
+void create_UifU ( UCodeBlock* cb, Int sz, Int ts, Int td )
+{
+ uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td,
+ Lit16, get_Tag_UifU(sz));
+}
+
+
+/* Do DifD on ts and td, putting the result in td. */
+static
+void create_DifD ( UCodeBlock* cb, Int sz, Int ts, Int td )
+{
+ uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td,
+ Lit16, get_Tag_DifD(sz));
+}
+
+
+/* Do HelpAND on value tval and tag tqqq, putting the result in
+ tqqq. */
+static
+void create_ImproveAND_TQ ( UCodeBlock* cb, Int sz, Int tval, Int tqqq )
+{
+ uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq,
+ Lit16, get_Tag_ImproveAND_TQ(sz));
+}
+
+
+/* Do HelpOR on value tval and tag tqqq, putting the result in
+ tqqq. */
+static
+void create_ImproveOR_TQ ( UCodeBlock* cb, Int sz, Int tval, Int tqqq )
+{
+ uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq,
+ Lit16, get_Tag_ImproveOR_TQ(sz));
+}
+
+
+/* Get the shadow for an operand described by (tag, val). Emit code
+ to do this and return the identity of the shadow holding the
+ result. The result tag is always copied into a new shadow, so it
+ can be modified without trashing the original.*/
+static
+Int /* TempReg */ getOperandShadow ( UCodeBlock* cb,
+ Int sz, Int tag, Int val )
+{
+ Int sh;
+ sh = newShadow(cb);
+ if (tag == TempReg) {
+ uInstr2(cb, MOV, 4, TempReg, SHADOW(val), TempReg, sh);
+ return sh;
+ }
+ if (tag == Literal) {
+ uInstr1(cb, SETV, sz, TempReg, sh);
+ return sh;
+ }
+ if (tag == ArchReg) {
+ uInstr2(cb, GETV, sz, ArchReg, val, TempReg, sh);
+ return sh;
+ }
+ VG_(panic)("getOperandShadow");
+}
+
+/* Create and return an instrumented version of cb_in. Free cb_in
+ before returning. */
+static UCodeBlock* memcheck_instrument ( UCodeBlock* cb_in )
+{
+ UCodeBlock* cb;
+ Int i, j;
+ UInstr* u_in;
+ Int qs, qd, qt, qtt;
+ cb = VG_(allocCodeBlock)();
+ cb->nextTemp = cb_in->nextTemp;
+
+ for (i = 0; i < cb_in->used; i++) {
+ qs = qd = qt = qtt = INVALID_TEMPREG;
+ u_in = &cb_in->instrs[i];
+
+ switch (u_in->opcode) {
+
+ case NOP:
+ break;
+
+ case INCEIP:
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* Loads and stores. Test the V bits for the address. 24
+ Mar 02: since the address is A-checked anyway, there's not
+ really much point in doing the V-check too, unless you
+ think that you might use addresses which are undefined but
+ still addressible. Hence the optionalisation of the V
+ check.
+
+ The LOADV/STOREV does an addressibility check for the
+ address. */
+
+ case LOAD:
+ if (SK_(clo_check_addrVs)) {
+ uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1));
+ uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1));
+ }
+ uInstr2(cb, LOADV, u_in->size,
+ TempReg, u_in->val1,
+ TempReg, SHADOW(u_in->val2));
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ case STORE:
+ if (SK_(clo_check_addrVs)) {
+ uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2));
+ uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2));
+ }
+ uInstr2(cb, STOREV, u_in->size,
+ TempReg, SHADOW(u_in->val1),
+ TempReg, u_in->val2);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* Moving stuff around. Make the V bits follow accordingly,
+ but don't do anything else. */
+
+ case GET:
+ uInstr2(cb, GETV, u_in->size,
+ ArchReg, u_in->val1,
+ TempReg, SHADOW(u_in->val2));
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ case PUT:
+ uInstr2(cb, PUTV, u_in->size,
+ TempReg, SHADOW(u_in->val1),
+ ArchReg, u_in->val2);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ case GETF:
+ /* This is not the smartest way to do it, but should work. */
+ qd = create_GETVF(cb, u_in->size);
+ uInstr2(cb, MOV, 4, TempReg, qd, TempReg, SHADOW(u_in->val1));
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ case PUTF:
+ create_PUTVF(cb, u_in->size, SHADOW(u_in->val1));
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ case MOV:
+ switch (u_in->tag1) {
+ case TempReg:
+ uInstr2(cb, MOV, 4,
+ TempReg, SHADOW(u_in->val1),
+ TempReg, SHADOW(u_in->val2));
+ break;
+ case Literal:
+ uInstr1(cb, SETV, u_in->size,
+ TempReg, SHADOW(u_in->val2));
+ break;
+ default:
+ VG_(panic)("memcheck_instrument: MOV");
+ }
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* Special case of add, where one of the operands is a literal.
+ lea1(t) = t + some literal.
+ Therefore: lea1#(qa) = left(qa)
+ */
+ case LEA1:
+ vg_assert(u_in->size == 4 && !VG_(anyFlagUse)(u_in));
+ qs = SHADOW(u_in->val1);
+ qd = SHADOW(u_in->val2);
+ uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qd);
+ create_Left(cb, u_in->size, qd);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* Another form of add.
+ lea2(ts,tt,shift) = ts + (tt << shift); shift is a literal
+ and is 0,1,2 or 3.
+ lea2#(qs,qt) = left(qs `UifU` (qt << shift)).
+ Note, subtly, that the shift puts zeroes at the bottom of qt,
+ meaning Valid, since the corresponding shift of tt puts
+ zeroes at the bottom of tb.
+ */
+ case LEA2: {
+ Int shift;
+ vg_assert(u_in->size == 4 && !VG_(anyFlagUse)(u_in));
+ switch (u_in->extra4b) {
+ case 1: shift = 0; break;
+ case 2: shift = 1; break;
+ case 4: shift = 2; break;
+ case 8: shift = 3; break;
+ default: VG_(panic)( "memcheck_instrument(LEA2)" );
+ }
+ qs = SHADOW(u_in->val1);
+ qt = SHADOW(u_in->val2);
+ qd = SHADOW(u_in->val3);
+ uInstr2(cb, MOV, 4, TempReg, qt, TempReg, qd);
+ if (shift > 0) {
+ uInstr2(cb, SHL, 4, Literal, 0, TempReg, qd);
+ uLiteral(cb, shift);
+ }
+ create_UifU(cb, 4, qs, qd);
+ create_Left(cb, u_in->size, qd);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+ }
+
+ /* inc#/dec#(qd) = q `UifU` left(qd) = left(qd) */
+ case INC: case DEC:
+ qd = SHADOW(u_in->val1);
+ create_Left(cb, u_in->size, qd);
+ if (u_in->flags_w != FlagsEmpty)
+ create_PUTVF(cb, u_in->size, qd);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* This is a HACK (approximation :-) */
+ /* rcl#/rcr#(qs,qd)
+ = let q0 = pcast-sz-0(qd) `UifU` pcast-sz-0(qs) `UifU` eflags#
+ eflags# = q0
+ qd =pcast-0-sz(q0)
+ Ie, cast everything down to a single bit, then back up.
+ This assumes that any bad bits infect the whole word and
+ the eflags.
+ */
+ case RCL: case RCR:
+ vg_assert(u_in->flags_r != FlagsEmpty);
+ /* The following assertion looks like it makes sense, but is
+ actually wrong. Consider this:
+ rcll %eax
+ imull %eax, %eax
+ The rcll writes O and C but so does the imull, so the O and C
+ write of the rcll is annulled by the prior improvement pass.
+ Noticed by Kevin Ryde <user42@zip.com.au>
+ */
+ /* vg_assert(u_in->flags_w != FlagsEmpty); */
+ qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1);
+ /* We can safely modify qs; cast it to 0-size. */
+ create_PCast(cb, u_in->size, 0, qs);
+ qd = SHADOW(u_in->val2);
+ create_PCast(cb, u_in->size, 0, qd);
+ /* qs is cast-to-0(shift count#), and qd is cast-to-0(value#). */
+ create_UifU(cb, 0, qs, qd);
+ /* qs is now free; reuse it for the flag definedness. */
+ qs = create_GETVF(cb, 0);
+ create_UifU(cb, 0, qs, qd);
+ create_PUTVF(cb, 0, qd);
+ create_PCast(cb, 0, u_in->size, qd);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* for OP in shl shr sar rol ror
+ (qs is shift count#, qd is value to be OP#d)
+ OP(ts,td)
+ OP#(qs,qd)
+ = pcast-1-sz(qs) `UifU` OP(ts,qd)
+ So we apply OP to the tag bits too, and then UifU with
+ the shift count# to take account of the possibility of it
+ being undefined.
+
+ A bit subtle:
+ ROL/ROR rearrange the tag bits as per the value bits.
+ SHL/SHR shifts zeroes into the value, and corresponding
+ zeroes indicating Definedness into the tag.
+ SAR copies the top bit of the value downwards, and therefore
+ SAR also copies the definedness of the top bit too.
+ So in all five cases, we just apply the same op to the tag
+ bits as is applied to the value bits. Neat!
+ */
+ case SHL:
+ case SHR: case SAR:
+ case ROL: case ROR: {
+ Int t_amount = INVALID_TEMPREG;
+ vg_assert(u_in->tag1 == TempReg || u_in->tag1 == Literal);
+ vg_assert(u_in->tag2 == TempReg);
+ qd = SHADOW(u_in->val2);
+
+ /* Make qs hold shift-count# and make
+ t_amount be a TempReg holding the shift count. */
+ if (u_in->tag1 == Literal) {
+ t_amount = newTemp(cb);
+ uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_amount);
+ uLiteral(cb, u_in->lit32);
+ qs = SHADOW(t_amount);
+ uInstr1(cb, SETV, 1, TempReg, qs);
+ } else {
+ t_amount = u_in->val1;
+ qs = SHADOW(u_in->val1);
+ }
+
+ uInstr2(cb, u_in->opcode,
+ u_in->size,
+ TempReg, t_amount,
+ TempReg, qd);
+ qt = newShadow(cb);
+ uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qt);
+ create_PCast(cb, 1, u_in->size, qt);
+ create_UifU(cb, u_in->size, qt, qd);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+ }
+
+ /* One simple tag operation. */
+ case WIDEN:
+ vg_assert(u_in->tag1 == TempReg);
+ create_Widen(cb, u_in->signed_widen, u_in->extra4b, u_in->size,
+ SHADOW(u_in->val1));
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* not#(x) = x (since bitwise independent) */
+ case NOT:
+ vg_assert(u_in->tag1 == TempReg);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* neg#(x) = left(x) (derivable from case for SUB) */
+ case NEG:
+ vg_assert(u_in->tag1 == TempReg);
+ create_Left(cb, u_in->size, SHADOW(u_in->val1));
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* bswap#(x) = bswap(x) */
+ case BSWAP:
+ vg_assert(u_in->tag1 == TempReg);
+ vg_assert(u_in->size == 4);
+ qd = SHADOW(u_in->val1);
+ uInstr1(cb, BSWAP, 4, TempReg, qd);
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* cc2val#(qd) = pcast-0-to-size(eflags#) */
+ case CC2VAL:
+ vg_assert(u_in->tag1 == TempReg);
+ vg_assert(u_in->flags_r != FlagsEmpty);
+ qt = create_GETVF(cb, u_in->size);
+ uInstr2(cb, MOV, 4, TempReg, qt, TempReg, SHADOW(u_in->val1));
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* cmov#(qs,qd) = cmov(qs,qd)
+ That is, do the cmov of tags using the same flags as for
+ the data (obviously). However, first do a test on the
+ validity of the flags.
+ */
+ case CMOV:
+ vg_assert(u_in->size == 4);
+ vg_assert(u_in->tag1 == TempReg);
+ vg_assert(u_in->tag2 == TempReg);
+ vg_assert(u_in->flags_r != FlagsEmpty);
+ vg_assert(u_in->flags_w == FlagsEmpty);
+ qs = SHADOW(u_in->val1);
+ qd = SHADOW(u_in->val2);
+ qt = create_GETVF(cb, 0);
+ uInstr1(cb, TESTV, 0, TempReg, qt);
+ /* qt should never be referred to again. Nevertheless
+ ... */
+ uInstr1(cb, SETV, 0, TempReg, qt);
+
+ uInstr2(cb, CMOV, 4, TempReg, qs, TempReg, qd);
+ LAST_UINSTR(cb).cond = u_in->cond;
+ LAST_UINSTR(cb).flags_r = u_in->flags_r;
+
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* add#/sub#(qs,qd)
+ = qs `UifU` qd `UifU` left(qs) `UifU` left(qd)
+ = left(qs) `UifU` left(qd)
+ = left(qs `UifU` qd)
+ adc#/sbb#(qs,qd)
+ = left(qs `UifU` qd) `UifU` pcast(eflags#)
+ Second arg (dest) is TempReg.
+ First arg (src) is Literal or TempReg or ArchReg.
+ */
+ case ADD: case SUB:
+ case ADC: case SBB:
+ qd = SHADOW(u_in->val2);
+ qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1);
+ create_UifU(cb, u_in->size, qs, qd);
+ create_Left(cb, u_in->size, qd);
+ if (u_in->opcode == ADC || u_in->opcode == SBB) {
+ vg_assert(u_in->flags_r != FlagsEmpty);
+ qt = create_GETVF(cb, u_in->size);
+ create_UifU(cb, u_in->size, qt, qd);
+ }
+ if (u_in->flags_w != FlagsEmpty) {
+ create_PUTVF(cb, u_in->size, qd);
+ }
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* xor#(qs,qd) = qs `UifU` qd */
+ case XOR:
+ qd = SHADOW(u_in->val2);
+ qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1);
+ create_UifU(cb, u_in->size, qs, qd);
+ if (u_in->flags_w != FlagsEmpty) {
+ create_PUTVF(cb, u_in->size, qd);
+ }
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* and#/or#(qs,qd)
+ = (qs `UifU` qd) `DifD` improve(vs,qs)
+ `DifD` improve(vd,qd)
+ where improve is the relevant one of
+ Improve{AND,OR}_TQ
+ Use the following steps, with qt as a temp:
+ qt = improve(vd,qd)
+ qd = qs `UifU` qd
+ qd = qt `DifD` qd
+ qt = improve(vs,qs)
+ qd = qt `DifD` qd
+ */
+ case AND: case OR:
+ vg_assert(u_in->tag1 == TempReg);
+ vg_assert(u_in->tag2 == TempReg);
+ qd = SHADOW(u_in->val2);
+ qs = SHADOW(u_in->val1);
+ qt = newShadow(cb);
+
+ /* qt = improve(vd,qd) */
+ uInstr2(cb, MOV, 4, TempReg, qd, TempReg, qt);
+ if (u_in->opcode == AND)
+ create_ImproveAND_TQ(cb, u_in->size, u_in->val2, qt);
+ else
+ create_ImproveOR_TQ(cb, u_in->size, u_in->val2, qt);
+ /* qd = qs `UifU` qd */
+ create_UifU(cb, u_in->size, qs, qd);
+ /* qd = qt `DifD` qd */
+ create_DifD(cb, u_in->size, qt, qd);
+ /* qt = improve(vs,qs) */
+ uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qt);
+ if (u_in->opcode == AND)
+ create_ImproveAND_TQ(cb, u_in->size, u_in->val1, qt);
+ else
+ create_ImproveOR_TQ(cb, u_in->size, u_in->val1, qt);
+ /* qd = qt `DifD` qd */
+ create_DifD(cb, u_in->size, qt, qd);
+ /* So, finally qd is the result tag. */
+ if (u_in->flags_w != FlagsEmpty) {
+ create_PUTVF(cb, u_in->size, qd);
+ }
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* Machinery to do with supporting CALLM. Copy the start and
+ end markers only to make the result easier to read
+ (debug); they generate no code and have no effect.
+ */
+ case CALLM_S: case CALLM_E:
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* Copy PUSH and POP verbatim. Arg/result absval
+ calculations are done when the associated CALL is
+ processed. CLEAR has no effect on absval calculations but
+ needs to be copied.
+ */
+ case PUSH: case POP: case CLEAR:
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* In short:
+ callm#(a1# ... an#) = (a1# `UifU` ... `UifU` an#)
+ We have to decide on a size to do the computation at,
+ although the choice doesn't affect correctness. We will
+ do a pcast to the final size anyway, so the only important
+ factor is to choose a size which minimises the total
+ number of casts needed. Valgrind: just use size 0,
+ regardless. It may not be very good for performance
+ but does simplify matters, mainly by reducing the number
+ of different pessimising casts which have to be implemented.
+ */
+ case CALLM: {
+ UInstr* uu;
+ Bool res_used;
+
+ /* Now generate the code. Get the final result absval
+ into qt. */
+ qt = newShadow(cb);
+ qtt = newShadow(cb);
+ uInstr1(cb, SETV, 0, TempReg, qt);
+ for (j = i-1; cb_in->instrs[j].opcode != CALLM_S; j--) {
+ uu = & cb_in->instrs[j];
+ if (uu->opcode != PUSH) continue;
+ /* cast via a temporary */
+ uInstr2(cb, MOV, 4, TempReg, SHADOW(uu->val1),
+ TempReg, qtt);
+ create_PCast(cb, uu->size, 0, qtt);
+ create_UifU(cb, 0, qtt, qt);
+ }
+ /* Remembering also that flags read count as inputs. */
+ if (u_in->flags_r != FlagsEmpty) {
+ qtt = create_GETVF(cb, 0);
+ create_UifU(cb, 0, qtt, qt);
+ }
+
+ /* qt now holds the result tag. If any results from the
+ call are used, either by fetching with POP or
+ implicitly by writing the flags, we copy the result
+ absval to the relevant location. If not used, the call
+ must have been for its side effects, so we test qt here
+ and now. Note that this assumes that all values
+ removed by POP continue to be live. So dead args
+ *must* be removed with CLEAR, not by POPping them into
+ a dummy tempreg.
+ */
+ res_used = False;
+ for (j = i+1; cb_in->instrs[j].opcode != CALLM_E; j++) {
+ uu = & cb_in->instrs[j];
+ if (uu->opcode != POP) continue;
+ /* Cast via a temp. */
+ uInstr2(cb, MOV, 4, TempReg, qt, TempReg, qtt);
+ create_PCast(cb, 0, uu->size, qtt);
+ uInstr2(cb, MOV, 4, TempReg, qtt,
+ TempReg, SHADOW(uu->val1));
+ res_used = True;
+ }
+ if (u_in->flags_w != FlagsEmpty) {
+ create_PUTVF(cb, 0, qt);
+ res_used = True;
+ }
+ if (!res_used) {
+ uInstr1(cb, TESTV, 0, TempReg, qt);
+ /* qt should never be referred to again. Nevertheless
+ ... */
+ uInstr1(cb, SETV, 0, TempReg, qt);
+ }
+ VG_(copyUInstr)(cb, u_in);
+ break;
+ }
+ /* Whew ... */
+
+ case JMP:
+ if (u_in->tag1 == TempReg) {
+ uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1));
+ uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1));
+ } else {
+ vg_assert(u_in->tag1 == Literal);
+ }
+ if (u_in->cond != CondAlways) {
+ vg_assert(u_in->flags_r != FlagsEmpty);
+ qt = create_GETVF(cb, 0);
+ uInstr1(cb, TESTV, 0, TempReg, qt);
+ /* qt should never be referred to again. Nevertheless
+ ... */
+ uInstr1(cb, SETV, 0, TempReg, qt);
+ }
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ case JIFZ:
+ uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1));
+ uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1));
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ /* Emit a check on the address used. The value loaded into the
+ FPU is checked by the call to fpu_{read/write}_check(). */
+ case FPU_R: case FPU_W: {
+ Int t_size = INVALID_TEMPREG;
+
+ vg_assert(u_in->tag2 == TempReg);
+ uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2));
+ uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2));
+
+ t_size = newTemp(cb);
+ uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size);
+ uLiteral(cb, u_in->size);
+ uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, t_size);
+ uCCall(cb,
+ u_in->opcode==FPU_R ? (Addr) & SK_(fpu_read_check)
+ : (Addr) & SK_(fpu_write_check),
+ 2, 2, False);
+
+ VG_(copyUInstr)(cb, u_in);
+ break;
+ }
+
+ /* For FPU insns not referencing memory, just copy thru. */
+ case FPU:
+ VG_(copyUInstr)(cb, u_in);
+ break;
+
+ default:
+ VG_(ppUInstr)(0, u_in);
+ VG_(panic)( "memcheck_instrument: unhandled case");
+
+ } /* end of switch (u_in->opcode) */
+
+ } /* end of for loop */
+
+ VG_(freeCodeBlock)(cb_in);
+ return cb;
+}
+
+/*------------------------------------------------------------*/
+/*--- Clean up mem check instrumentation. ---*/
+/*------------------------------------------------------------*/
+
+Bool VG_(clo_memcheck_codegen) = False;
+
+#define dis VG_(print_codegen)
+
+
+#define VGC_IS_SHADOW(tempreg) ((tempreg % 2) == 1)
+#define VGC_UNDEF ((UChar)100)
+#define VGC_VALUE ((UChar)101)
+
+#define NOP_no_msg(uu) \
+ do { VG_(newNOP)(uu); } while (False)
+
+#define NOP_tag1_op(uu) \
+ do { VG_(newNOP)(uu); \
+ if (dis) \
+ VG_(printf)(" at %2d: delete %s due to defd arg\n", \
+ i, nameOfTagOp(u->val3)); \
+ } while (False)
+
+#define SETV_tag1_op(uu,newsz) \
+ do { uu->opcode = SETV; \
+ uu->size = newsz; \
+ uu->tag2 = uu->tag3 = NoValue; \
+ if (dis) \
+ VG_(printf)(" at %2d: convert %s to SETV%d " \
+ "due to defd arg\n", \
+ i, nameOfTagOp(u->val3), newsz); \
+ } while (False)
+
+
+
+/* Run backwards and delete SETVs on shadow temps for which the next
+ action is a write. Needs an env saying whether or not the next
+ action is a write. The supplied UCodeBlock is destructively
+ modified.
+*/
+static void vg_delete_redundant_SETVs ( UCodeBlock* cb )
+{
+ Int i, j, k;
+ Int n_temps = cb->nextTemp;
+ Bool* next_is_write;
+ UInstr* u;
+ RegUse tempUse[3];
+
+ if (n_temps == 0) return;
+
+ next_is_write = VG_(malloc)(n_temps * sizeof(Bool));
+
+ for (i = 0; i < n_temps; i++) next_is_write[i] = True;
+
+ for (i = cb->used-1; i >= 0; i--) {
+ u = &cb->instrs[i];
+
+ /* If we're not checking address V bits, there will be a lot of
+ GETVs, TAG1s and TAG2s calculating values which are never
+ used. These first three cases get rid of them. */
+
+ if (u->opcode == GETV && VGC_IS_SHADOW(u->val2)
+ && next_is_write[u->val2]
+ && !SK_(clo_check_addrVs)) {
+ VG_(newNOP)(u);
+ if (dis)
+ VG_(printf)(" at %2d: delete GETV\n", i);
+ } else
+
+ if (u->opcode == TAG1 && VGC_IS_SHADOW(u->val1)
+ && next_is_write[u->val1]
+ && !SK_(clo_check_addrVs)) {
+ VG_(newNOP)(u);
+ if (dis)
+ VG_(printf)(" at %2d: delete TAG1\n", i);
+ } else
+
+ if (u->opcode == TAG2 && VGC_IS_SHADOW(u->val2)
+ && next_is_write[u->val2]
+ && !SK_(clo_check_addrVs)) {
+ VG_(newNOP)(u);
+ if (dis)
+ VG_(printf)(" at %2d: delete TAG2\n", i);
+ } else
+
+ /* We do the rest of these regardless of whether or not
+ addresses are V-checked. */
+
+ if (u->opcode == MOV && VGC_IS_SHADOW(u->val2)
+ && next_is_write[u->val2]) {
+ /* This MOV is pointless because the target is dead at this
+ point. Delete it. */
+ VG_(newNOP)(u);
+ if (dis)
+ VG_(printf)(" at %2d: delete MOV\n", i);
+ } else
+
+ if (u->opcode == SETV) {
+ if (u->tag1 == TempReg) {
+ vg_assert(VGC_IS_SHADOW(u->val1));
+ if (next_is_write[u->val1]) {
+ /* This write is pointless, so annul it. */
+ VG_(newNOP)(u);
+ if (dis)
+ VG_(printf)(" at %2d: delete SETV\n", i);
+ } else {
+ /* This write has a purpose; don't annul it, but do
+ notice that we did it. */
+ next_is_write[u->val1] = True;
+ }
+
+ }
+
+ } else {
+ /* Find out what this insn does to the temps. */
+ k = VG_(getRegUsage)(u, TempReg, &tempUse[0]);
+ vg_assert(k <= 3);
+ for (j = k-1; j >= 0; j--) {
+ next_is_write[ tempUse[j].num ]
+ = tempUse[j].isWrite;
+ }
+ }
+ }
+}
+
+
+/* Run forwards, propagating and using the is-completely-defined
+ property. This removes a lot of redundant tag-munging code.
+ Unfortunately it requires intimate knowledge of how each uinstr and
+ tagop modifies its arguments. This duplicates knowledge of uinstr
+ tempreg uses embodied in VG_(getRegUsage)(), which is unfortunate.
+ The supplied UCodeBlock* is modified in-place.
+
+ For each value temp, def[] should hold VGC_VALUE.
+
+ For each shadow temp, def[] may hold 4,2,1 or 0 iff that shadow is
+ definitely known to be fully defined at that size. In all other
+ circumstances a shadow's def[] entry is VGC_UNDEF, meaning possibly
+ undefined. In cases of doubt, VGC_UNDEF is always safe.
+*/
+static void vg_propagate_definedness ( UCodeBlock* cb )
+{
+ Int i, j, k, t;
+ Int n_temps = cb->nextTemp;
+ UChar* def;
+ UInstr* u;
+ RegUse tempUse[3];
+
+ if (n_temps == 0) return;
+
+ def = VG_(malloc)(n_temps * sizeof(UChar));
+
+ for (i = 0; i < n_temps; i++)
+ def[i] = VGC_IS_SHADOW(i) ? VGC_UNDEF : VGC_VALUE;
+
+ /* Run forwards, detecting and using the all-defined property. */
+
+ for (i = 0; i < cb->used; i++) {
+ u = &cb->instrs[i];
+ switch (u->opcode) {
+
+ /* Tag-handling uinstrs. */
+
+ /* Deal with these quickly. */
+ case NOP:
+ case INCEIP:
+ break;
+
+ /* Make a tag defined. */
+ case SETV:
+ vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+ def[u->val1] = u->size;
+ break;
+
+ /* Check definedness of a tag. */
+ case TESTV:
+ vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+ if (def[u->val1] <= 4) {
+ vg_assert(def[u->val1] == u->size);
+ NOP_no_msg(u);
+ if (dis)
+ VG_(printf)(" at %2d: delete TESTV on defd arg\n", i);
+ }
+ break;
+
+ /* Applies to both values and tags. Propagate Definedness
+ property through copies. Note that this isn't optional;
+ we *have* to do this to keep def[] correct. */
+ case MOV:
+ vg_assert(u->tag2 == TempReg);
+ if (u->tag1 == TempReg) {
+ if (VGC_IS_SHADOW(u->val1)) {
+ vg_assert(VGC_IS_SHADOW(u->val2));
+ def[u->val2] = def[u->val1];
+ }
+ }
+ break;
+
+ case PUTV:
+ vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+ if (def[u->val1] <= 4) {
+ vg_assert(def[u->val1] == u->size);
+ u->tag1 = Literal;
+ u->val1 = 0;
+ switch (u->size) {
+ case 4: u->lit32 = 0x00000000; break;
+ case 2: u->lit32 = 0xFFFF0000; break;
+ case 1: u->lit32 = 0xFFFFFF00; break;
+ default: VG_(panic)("vg_cleanup(PUTV)");
+ }
+ if (dis)
+ VG_(printf)(
+ " at %2d: propagate definedness into PUTV\n", i);
+ }
+ break;
+
+ case STOREV:
+ vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+ if (def[u->val1] <= 4) {
+ vg_assert(def[u->val1] == u->size);
+ u->tag1 = Literal;
+ u->val1 = 0;
+ switch (u->size) {
+ case 4: u->lit32 = 0x00000000; break;
+ case 2: u->lit32 = 0xFFFF0000; break;
+ case 1: u->lit32 = 0xFFFFFF00; break;
+ default: VG_(panic)("vg_cleanup(STOREV)");
+ }
+ if (dis)
+ VG_(printf)(
+ " at %2d: propagate definedness into STandV\n", i);
+ }
+ break;
+
+ /* Nothing interesting we can do with this, I think. */
+ case PUTVF:
+ break;
+
+ /* Tag handling operations. */
+ case TAG2:
+ vg_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2));
+ vg_assert(u->tag3 == Lit16);
+ /* Ultra-paranoid "type" checking. */
+ switch (u->val3) {
+ case Tag_ImproveAND4_TQ: case Tag_ImproveAND2_TQ:
+ case Tag_ImproveAND1_TQ: case Tag_ImproveOR4_TQ:
+ case Tag_ImproveOR2_TQ: case Tag_ImproveOR1_TQ:
+ vg_assert(u->tag1 == TempReg && !VGC_IS_SHADOW(u->val1));
+ break;
+ default:
+ vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+ break;
+ }
+ switch (u->val3) {
+ Int sz;
+ case Tag_UifU4:
+ sz = 4; goto do_UifU;
+ case Tag_UifU2:
+ sz = 2; goto do_UifU;
+ case Tag_UifU1:
+ sz = 1; goto do_UifU;
+ case Tag_UifU0:
+ sz = 0; goto do_UifU;
+ do_UifU:
+ vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+ vg_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2));
+ if (def[u->val1] <= 4) {
+ /* UifU. The first arg is defined, so result is
+ simply second arg. Delete this operation. */
+ vg_assert(def[u->val1] == sz);
+ NOP_no_msg(u);
+ if (dis)
+ VG_(printf)(
+ " at %2d: delete UifU%d due to defd arg1\n",
+ i, sz);
+ }
+ else
+ if (def[u->val2] <= 4) {
+ /* UifU. The second arg is defined, so result is
+ simply first arg. Copy to second. */
+ vg_assert(def[u->val2] == sz);
+ u->opcode = MOV;
+ u->size = 4;
+ u->tag3 = NoValue;
+ def[u->val2] = def[u->val1];
+ if (dis)
+ VG_(printf)(
+ " at %2d: change UifU%d to MOV due to defd"
+ " arg2\n",
+ i, sz);
+ }
+ break;
+ case Tag_ImproveAND4_TQ:
+ sz = 4; goto do_ImproveAND;
+ case Tag_ImproveAND1_TQ:
+ sz = 1; goto do_ImproveAND;
+ do_ImproveAND:
+ /* Implements Q = T OR Q. So if Q is entirely defined,
+ ie all 0s, we get MOV T, Q. */
+ if (def[u->val2] <= 4) {
+ vg_assert(def[u->val2] == sz);
+ u->size = 4; /* Regardless of sz */
+ u->opcode = MOV;
+ u->tag3 = NoValue;
+ def[u->val2] = VGC_UNDEF;
+ if (dis)
+ VG_(printf)(
+ " at %2d: change ImproveAND%d_TQ to MOV due "
+ "to defd arg2\n",
+ i, sz);
+ }
+ break;
+ default:
+ goto unhandled;
+ }
+ break;
+
+ case TAG1:
+ vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+ if (def[u->val1] > 4) break;
+ /* We now know that the arg to the op is entirely defined.
+ If the op changes the size of the arg, we must replace
+ it with a SETV at the new size. If it doesn't change
+ the size, we can delete it completely. */
+ switch (u->val3) {
+ /* Maintain the same size ... */
+ case Tag_Left4:
+ vg_assert(def[u->val1] == 4);
+ NOP_tag1_op(u);
+ break;
+ case Tag_PCast11:
+ vg_assert(def[u->val1] == 1);
+ NOP_tag1_op(u);
+ break;
+ /* Change size ... */
+ case Tag_PCast40:
+ vg_assert(def[u->val1] == 4);
+ SETV_tag1_op(u,0);
+ def[u->val1] = 0;
+ break;
+ case Tag_PCast14:
+ vg_assert(def[u->val1] == 1);
+ SETV_tag1_op(u,4);
+ def[u->val1] = 4;
+ break;
+ case Tag_PCast12:
+ vg_assert(def[u->val1] == 1);
+ SETV_tag1_op(u,2);
+ def[u->val1] = 2;
+ break;
+ case Tag_PCast10:
+ vg_assert(def[u->val1] == 1);
+ SETV_tag1_op(u,0);
+ def[u->val1] = 0;
+ break;
+ case Tag_PCast02:
+ vg_assert(def[u->val1] == 0);
+ SETV_tag1_op(u,2);
+ def[u->val1] = 2;
+ break;
+ default:
+ goto unhandled;
+ }
+ if (dis)
+ VG_(printf)(
+ " at %2d: delete TAG1 %s due to defd arg\n",
+ i, nameOfTagOp(u->val3));
+ break;
+
+ default:
+ unhandled:
+ /* We don't know how to handle this uinstr. Be safe, and
+ set to VGC_VALUE or VGC_UNDEF all temps written by it. */
+ k = VG_(getRegUsage)(u, TempReg, &tempUse[0]);
+ vg_assert(k <= 3);
+ for (j = 0; j < k; j++) {
+ t = tempUse[j].num;
+ vg_assert(t >= 0 && t < n_temps);
+ if (!tempUse[j].isWrite) {
+ /* t is read; ignore it. */
+ if (0&& VGC_IS_SHADOW(t) && def[t] <= 4)
+ VG_(printf)("ignoring def %d at %s %s\n",
+ def[t],
+ VG_(nameUOpcode)(True, u->opcode),
+ (u->opcode == TAG1 || u->opcode == TAG2)
+ ? nameOfTagOp(u->val3)
+ : (Char*)"");
+ } else {
+ /* t is written; better nullify it. */
+ def[t] = VGC_IS_SHADOW(t) ? VGC_UNDEF : VGC_VALUE;
+ }
+ }
+ }
+ }
+}
+
+
+/* Top level post-MemCheck-instrumentation cleanup function. */
+static void vg_cleanup ( UCodeBlock* cb )
+{
+ vg_propagate_definedness ( cb );
+ vg_delete_redundant_SETVs ( cb );
+}
+
+
+/* Caller will print out final instrumented code if necessary; we
+ print out intermediate instrumented code here if necessary. */
+UCodeBlock* SK_(instrument) ( UCodeBlock* cb, Addr not_used )
+{
+ cb = memcheck_instrument ( cb );
+ if (SK_(clo_cleanup)) {
+ if (dis) {
+ VG_(ppUCodeBlock) ( cb, "Unimproved instrumented UCode:" );
+ VG_(printf)("Instrumentation improvements:\n");
+ }
+ vg_cleanup(cb);
+ if (dis) VG_(printf)("\n");
+ }
+ return cb;
+}
+
+#undef dis
+
+/*--------------------------------------------------------------------*/
+/*--- end vg_memcheck_translate.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/memcheck.h b/memcheck/memcheck.h
new file mode 100644
index 0000000..b126ffb
--- /dev/null
+++ b/memcheck/memcheck.h
@@ -0,0 +1,197 @@
+
+/*
+ ----------------------------------------------------------------
+
+ Notice that the following BSD-style license applies to this one
+ file (vg_memcheck.h) only. The entire rest of Valgrind is licensed
+ under the terms of the GNU General Public License, version 2. See
+ the COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (vg_memcheck.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+
+#ifndef __VG_MEMCHECK_H
+#define __VG_MEMCHECK_H
+
+
+/* This file is for inclusion into client (your!) code.
+
+ You can use these macros to manipulate and query memory permissions
+ inside your own programs.
+
+ See comment near the top of valgrind.h on how to use them.
+*/
+
+#include "valgrind.h"
+
+typedef
+ enum {
+ VG_USERREQ__MAKE_NOACCESS = VG_USERREQ__FINAL_DUMMY_CLIENT_REQUEST + 1,
+ VG_USERREQ__MAKE_WRITABLE,
+ VG_USERREQ__MAKE_READABLE,
+ VG_USERREQ__DISCARD,
+ VG_USERREQ__CHECK_WRITABLE,
+ VG_USERREQ__CHECK_READABLE,
+ VG_USERREQ__MAKE_NOACCESS_STACK,
+ VG_USERREQ__DO_LEAK_CHECK, /* untested */
+ } Vg_MemCheckClientRequest;
+
+
+
+/* Client-code macros to manipulate the state of memory. */
+
+/* Mark memory at _qzz_addr as unaddressible and undefined for
+ _qzz_len bytes. Returns an int handle pertaining to the block
+ descriptions Valgrind will use in subsequent error messages. */
+#define VALGRIND_MAKE_NOACCESS(_qzz_addr,_qzz_len) \
+ ({unsigned int _qzz_res; \
+ VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__MAKE_NOACCESS, \
+ _qzz_addr, _qzz_len, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Similarly, mark memory at _qzz_addr as addressible but undefined
+ for _qzz_len bytes. */
+#define VALGRIND_MAKE_WRITABLE(_qzz_addr,_qzz_len) \
+ ({unsigned int _qzz_res; \
+ VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__MAKE_WRITABLE, \
+ _qzz_addr, _qzz_len, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Similarly, mark memory at _qzz_addr as addressible and defined
+ for _qzz_len bytes. */
+#define VALGRIND_MAKE_READABLE(_qzz_addr,_qzz_len) \
+ ({unsigned int _qzz_res; \
+ VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__MAKE_READABLE, \
+ _qzz_addr, _qzz_len, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Discard a block-description-handle obtained from the above three
+ macros. After this, Valgrind will no longer be able to relate
+ addressing errors to the user-defined block associated with the
+ handle. The permissions settings associated with the handle remain
+ in place. Returns 1 for an invalid handle, 0 for a valid
+ handle. */
+#define VALGRIND_DISCARD(_qzz_blkindex) \
+ ({unsigned int _qzz_res; \
+ VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__DISCARD, \
+ 0, _qzz_blkindex, 0, 0); \
+ _qzz_res; \
+ })
+
+
+/* Client-code macros to check the state of memory. */
+
+/* Check that memory at _qzz_addr is addressible for _qzz_len bytes.
+ If suitable addressibility is not established, Valgrind prints an
+ error message and returns the address of the first offending byte.
+ Otherwise it returns zero. */
+#define VALGRIND_CHECK_WRITABLE(_qzz_addr,_qzz_len) \
+ ({unsigned int _qzz_res; \
+ VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \
+ VG_USERREQ__CHECK_WRITABLE, \
+ _qzz_addr, _qzz_len, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Check that memory at _qzz_addr is addressible and defined for
+ _qzz_len bytes. If suitable addressibility and definedness are not
+ established, Valgrind prints an error message and returns the
+ address of the first offending byte. Otherwise it returns zero. */
+#define VALGRIND_CHECK_READABLE(_qzz_addr,_qzz_len) \
+ ({unsigned int _qzz_res; \
+ VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \
+ VG_USERREQ__CHECK_READABLE, \
+ _qzz_addr, _qzz_len, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Use this macro to force the definedness and addressibility of a
+ value to be checked. If suitable addressibility and definedness
+ are not established, Valgrind prints an error message and returns
+ the address of the first offending byte. Otherwise it returns
+ zero. */
+#define VALGRIND_CHECK_DEFINED(__lvalue) \
+ (void) \
+ VALGRIND_CHECK_READABLE( \
+ (volatile unsigned char *)&(__lvalue), \
+ (unsigned int)(sizeof (__lvalue)))
+
+/* Mark memory, intended to be on the client's stack, at _qzz_addr as
+ unaddressible and undefined for _qzz_len bytes. Does not return a
+ value. The record associated with this setting will be
+ automatically removed by Valgrind when the containing routine
+ exits. */
+#define VALGRIND_MAKE_NOACCESS_STACK(_qzz_addr,_qzz_len) \
+ {unsigned int _qzz_res; \
+ VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \
+ VG_USERREQ__MAKE_NOACCESS_STACK, \
+ _qzz_addr, _qzz_len, 0, 0); \
+ }
+
+
+
+/* Do a memory leak check mid-execution.
+ Currently implemented but untested.
+*/
+#define VALGRIND_DO_LEAK_CHECK \
+ {unsigned int _qzz_res; \
+ VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \
+ VG_USERREQ__DO_LEAK_CHECK, \
+ 0, 0, 0, 0); \
+ }
+
+
+#endif
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
new file mode 100644
index 0000000..a36047c
--- /dev/null
+++ b/memcheck/tests/Makefile.am
@@ -0,0 +1,61 @@
+## Process this file with automake to produce Makefile.in
+
+##---------------------------------------------------------------------------
+## Need more tests:
+## - lots more mmap/munmap/mremap/mprotect ones
+
+## Notes:
+## - HEAD and ERASER stderr.exp are different for signal2 due to lazy vs.
+## strict EIP updating
+##---------------------------------------------------------------------------
+
+noinst_PROGRAMS = \
+ badaddrvalue badfree badjump badloop buflen_check \
+ doublefree errs1 exitprog fprw fwrite inits inline \
+ malloc1 malloc2 manuel1 manuel2 manuel3 \
+ memalign_test memcmptest mmaptest nanoleak pushfpopf \
+ realloc1 realloc2 sigaltstack signal2 supp1 supp2 suppfree \
+ trivialleak tronical weirdioctl \
+ mismatches new_override
+
+CFLAGS = $(WERROR) -Winline -Wall -Wshadow -g
+CXXFLAGS = $(CFLAGS)
+
+# C ones
+badaddrvalue_SOURCES = badaddrvalue.c
+badfree_SOURCES = badfree.c
+badjump_SOURCES = badjump.c
+badloop_SOURCES = badloop.c
+buflen_check_SOURCES = buflen_check.c
+doublefree_SOURCES = doublefree.c
+errs1_SOURCES = errs1.c
+exitprog_SOURCES = exitprog.c
+fprw_SOURCES = fprw.c
+fwrite_SOURCES = fwrite.c
+inits_SOURCES = inits.c
+inline_SOURCES = inline.c
+malloc1_SOURCES = malloc1.c
+malloc2_SOURCES = malloc2.c
+manuel1_SOURCES = manuel1.c
+manuel2_SOURCES = manuel2.c
+manuel3_SOURCES = manuel3.c
+mmaptest_SOURCES = mmaptest.c
+memalign_test_SOURCES = memalign_test.c
+memcmptest_SOURCES = memcmptest.c
+nanoleak_SOURCES = nanoleak.c
+pushfpopf_SOURCES = pushfpopf_c.c pushfpopf_s.s
+realloc1_SOURCES = realloc1.c
+realloc2_SOURCES = realloc2.c
+signal2_SOURCES = signal2.c
+supp1_SOURCES = supp.c
+supp2_SOURCES = supp.c
+suppfree_SOURCES = suppfree.c
+sigaltstack_SOURCES = sigaltstack.c
+trivialleak_SOURCES = trivialleak.c
+tronical_SOURCES = tronical.S
+weirdioctl_SOURCES = weirdioctl.c
+
+# C++ ones
+mismatches_SOURCES = mismatches.cpp
+new_override_SOURCES = new_override.cpp
+
diff --git a/memcheck/tests/badaddrvalue.c b/memcheck/tests/badaddrvalue.c
new file mode 100644
index 0000000..1bb2047
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.c
@@ -0,0 +1,12 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+ char* aa = malloc(8);
+ aa[-1] = 17;
+ if (aa[-1] == 17)
+ printf("17\n"); else printf("not 17\n");
+ return 0;
+}
diff --git a/memcheck/tests/badaddrvalue.stderr.exp b/memcheck/tests/badaddrvalue.stderr.exp
new file mode 100644
index 0000000..8888c85
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.stderr.exp
@@ -0,0 +1,26 @@
+
+Invalid write of size 1
+ at 0x........: main (badaddrvalue.c:8)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badaddrvalue)
+ Address 0x........ is 1 bytes before a block of size 8 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (badaddrvalue.c:7)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badaddrvalue)
+
+Invalid read of size 1
+ at 0x........: main (badaddrvalue.c:9)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badaddrvalue)
+ Address 0x........ is 1 bytes before a block of size 8 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (badaddrvalue.c:7)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badaddrvalue)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 8 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 8 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badaddrvalue.stderr.exp.hd b/memcheck/tests/badaddrvalue.stderr.exp.hd
new file mode 100644
index 0000000..8888c85
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.stderr.exp.hd
@@ -0,0 +1,26 @@
+
+Invalid write of size 1
+ at 0x........: main (badaddrvalue.c:8)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badaddrvalue)
+ Address 0x........ is 1 bytes before a block of size 8 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (badaddrvalue.c:7)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badaddrvalue)
+
+Invalid read of size 1
+ at 0x........: main (badaddrvalue.c:9)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badaddrvalue)
+ Address 0x........ is 1 bytes before a block of size 8 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (badaddrvalue.c:7)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badaddrvalue)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 8 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 8 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badaddrvalue.stdout.exp b/memcheck/tests/badaddrvalue.stdout.exp
new file mode 100644
index 0000000..98d9bcb
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.stdout.exp
@@ -0,0 +1 @@
+17
diff --git a/memcheck/tests/badaddrvalue.vgtest b/memcheck/tests/badaddrvalue.vgtest
new file mode 100644
index 0000000..91187e8
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.vgtest
@@ -0,0 +1 @@
+prog: badaddrvalue
diff --git a/memcheck/tests/badfree-2trace.stderr.exp b/memcheck/tests/badfree-2trace.stderr.exp
new file mode 100644
index 0000000..741fd25
--- /dev/null
+++ b/memcheck/tests/badfree-2trace.stderr.exp
@@ -0,0 +1,16 @@
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (badfree.c:12)
+ Address 0x........ is not stack'd, malloc'd or free'd
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (badfree.c:15)
+ Address 0x........ is on thread 1's stack
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 2 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badfree-2trace.vgtest b/memcheck/tests/badfree-2trace.vgtest
new file mode 100644
index 0000000..8a60456
--- /dev/null
+++ b/memcheck/tests/badfree-2trace.vgtest
@@ -0,0 +1,2 @@
+vgopts: --num-callers=2
+prog: badfree
diff --git a/memcheck/tests/badfree.c b/memcheck/tests/badfree.c
new file mode 100644
index 0000000..3a22567
--- /dev/null
+++ b/memcheck/tests/badfree.c
@@ -0,0 +1,18 @@
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+ void* p = (void*)0x87654321;
+ int q[] = { 1, 2, 3 };
+
+ /* Free a pointer to Never-Never Land */
+ free(p);
+
+ /* Free a pointer to a stack block */
+ free(q);
+
+ return 0;
+}
diff --git a/memcheck/tests/badfree.stderr.exp b/memcheck/tests/badfree.stderr.exp
new file mode 100644
index 0000000..37c9b3e
--- /dev/null
+++ b/memcheck/tests/badfree.stderr.exp
@@ -0,0 +1,20 @@
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (badfree.c:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/badfree)
+ Address 0x........ is not stack'd, malloc'd or free'd
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (badfree.c:15)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/badfree)
+ Address 0x........ is on thread 1's stack
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 2 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badfree.vgtest b/memcheck/tests/badfree.vgtest
new file mode 100644
index 0000000..455863a
--- /dev/null
+++ b/memcheck/tests/badfree.vgtest
@@ -0,0 +1 @@
+prog: badfree
diff --git a/memcheck/tests/badjump.c b/memcheck/tests/badjump.c
new file mode 100644
index 0000000..053663b
--- /dev/null
+++ b/memcheck/tests/badjump.c
@@ -0,0 +1,6 @@
+
+int main ( void )
+{
+ char* p = (char*)0xE000000;
+ return ((int(*)(void)) p) ();
+}
diff --git a/memcheck/tests/badjump.stderr.exp b/memcheck/tests/badjump.stderr.exp
new file mode 100644
index 0000000..1be7f70
--- /dev/null
+++ b/memcheck/tests/badjump.stderr.exp
@@ -0,0 +1,6 @@
+
+Jump to the invalid address stated on the next line
+ at 0x........: ???
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+ Address 0x........ is not stack'd, malloc'd or free'd
diff --git a/memcheck/tests/badjump.vgtest b/memcheck/tests/badjump.vgtest
new file mode 100644
index 0000000..1e82b86
--- /dev/null
+++ b/memcheck/tests/badjump.vgtest
@@ -0,0 +1 @@
+prog: badjump
diff --git a/memcheck/tests/badloop.c b/memcheck/tests/badloop.c
new file mode 100644
index 0000000..8780cf7
--- /dev/null
+++ b/memcheck/tests/badloop.c
@@ -0,0 +1,15 @@
+
+#include <stdio.h>
+
+int main ( void )
+{
+ int a[5];
+ int i, s;
+ a[0] = a[1] = a[3] = a[4] = 0;
+ s = 0;
+ for (i = 0; i < 5; i++)
+ s += a[i];
+ if (s == 377)
+ printf("sum is %d\n", s);
+ return 0;
+}
diff --git a/memcheck/tests/badloop.stderr.exp b/memcheck/tests/badloop.stderr.exp
new file mode 100644
index 0000000..ebfa1c2
--- /dev/null
+++ b/memcheck/tests/badloop.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: main (badloop.c:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/badloop)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badloop.vgtest b/memcheck/tests/badloop.vgtest
new file mode 100644
index 0000000..abd0f39
--- /dev/null
+++ b/memcheck/tests/badloop.vgtest
@@ -0,0 +1 @@
+prog: badloop
diff --git a/memcheck/tests/buflen_check.c b/memcheck/tests/buflen_check.c
new file mode 100644
index 0000000..25f1714
--- /dev/null
+++ b/memcheck/tests/buflen_check.c
@@ -0,0 +1,29 @@
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(void)
+{
+ struct sockaddr name;
+ int res1, res2;
+ int len = 10;
+
+ res1 = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (res1 == 0) {
+ fprintf(stderr, "socket() failed\n");
+ exit(1);
+ }
+
+ /* Valgrind 1.0.X doesn't report the second error */
+ res1 = getsockname(-1, NULL, &len); /* NULL is bogus */
+ res2 = getsockname(-1, &name, NULL); /* NULL is bogus */
+ if (res1 == -1) {
+ fprintf(stderr, "getsockname(1) failed\n");
+ }
+ if (res2 == -1) {
+ fprintf(stderr, "getsockname(2) failed\n");
+ }
+
+ return 0;
+}
+
diff --git a/memcheck/tests/buflen_check.stderr.exp b/memcheck/tests/buflen_check.stderr.exp
new file mode 100644
index 0000000..a1b9b36
--- /dev/null
+++ b/memcheck/tests/buflen_check.stderr.exp
@@ -0,0 +1,20 @@
+
+Syscall param socketcall.getsockname(name) contains unaddressable byte(s)
+ at 0x........: getsockname (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: socket@@GLIBC_2.0 (in /.../tests/memcheck/buflen_check)
+ Address 0x........ is not stack'd, malloc'd or free'd
+
+Syscall param socketcall.getsockname(namelen_in) contains uninitialised or unaddressable byte(s)
+ at 0x........: getsockname (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: socket@@GLIBC_2.0 (in /.../tests/memcheck/buflen_check)
+ Address 0x........ is not stack'd, malloc'd or free'd
+getsockname(1) failed
+getsockname(2) failed
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/buflen_check.stderr.exp.hd b/memcheck/tests/buflen_check.stderr.exp.hd
new file mode 100644
index 0000000..855f51e
--- /dev/null
+++ b/memcheck/tests/buflen_check.stderr.exp.hd
@@ -0,0 +1,14 @@
+
+Syscall param socketcall.getsockname(name) contains unaddressable byte(s)
+ at 0x........: getsockname (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: socket@@GLIBC_2.0 (in /.../tests/memcheck/buflen_check)
+ Address 0x........ is not stack'd, malloc'd or free'd
+getsockname(1) failed
+getsockname(2) failed
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/buflen_check.vgtest b/memcheck/tests/buflen_check.vgtest
new file mode 100644
index 0000000..e14c8f1
--- /dev/null
+++ b/memcheck/tests/buflen_check.vgtest
@@ -0,0 +1 @@
+prog: buflen_check
diff --git a/memcheck/tests/doublefree.c b/memcheck/tests/doublefree.c
new file mode 100644
index 0000000..3c27050
--- /dev/null
+++ b/memcheck/tests/doublefree.c
@@ -0,0 +1,12 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+ int i;
+ void* p = malloc(177);
+ for (i = 0; i < 2; i++)
+ free(p);
+ return 0;
+}
diff --git a/memcheck/tests/doublefree.stderr.exp b/memcheck/tests/doublefree.stderr.exp
new file mode 100644
index 0000000..282523f
--- /dev/null
+++ b/memcheck/tests/doublefree.stderr.exp
@@ -0,0 +1,17 @@
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (doublefree.c:10)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/doublefree)
+ Address 0x........ is 0 bytes inside a block of size 177 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (doublefree.c:10)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/doublefree)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 1 allocs, 2 frees, 177 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/doublefree.vgtest b/memcheck/tests/doublefree.vgtest
new file mode 100644
index 0000000..9c0efac
--- /dev/null
+++ b/memcheck/tests/doublefree.vgtest
@@ -0,0 +1 @@
+prog: doublefree
diff --git a/memcheck/tests/errs1.c b/memcheck/tests/errs1.c
new file mode 100644
index 0000000..754f844
--- /dev/null
+++ b/memcheck/tests/errs1.c
@@ -0,0 +1,17 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+char* p;
+
+void ddd ( void ) { p[-1] += 'z'; }
+void ccc ( void ) { ddd(); }
+void bbb ( void ) { ccc(); }
+void aaa ( void ) { bbb(); }
+
+void zzzzzzz ( void ) { p = malloc(10); }
+void yyy ( void ) { zzzzzzz(); }
+void xxx ( void ) { yyy(); }
+void www ( void ) { xxx(); }
+
+int main ( void ) { www(); aaa(); return 0; }
diff --git a/memcheck/tests/errs1.stderr.exp b/memcheck/tests/errs1.stderr.exp
new file mode 100644
index 0000000..2de4b48
--- /dev/null
+++ b/memcheck/tests/errs1.stderr.exp
@@ -0,0 +1,28 @@
+
+Invalid read of size 1
+ at 0x........: ddd (errs1.c:7)
+ by 0x........: bbb (errs1.c:9)
+ by 0x........: aaa (errs1.c:10)
+ by 0x........: main (errs1.c:17)
+ Address 0x........ is 1 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: zzzzzzz (errs1.c:12)
+ by 0x........: yyy (errs1.c:13)
+ by 0x........: xxx (errs1.c:14)
+
+Invalid write of size 1
+ at 0x........: ddd (errs1.c:7)
+ by 0x........: bbb (errs1.c:9)
+ by 0x........: aaa (errs1.c:10)
+ by 0x........: main (errs1.c:17)
+ Address 0x........ is 1 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: zzzzzzz (errs1.c:12)
+ by 0x........: yyy (errs1.c:13)
+ by 0x........: xxx (errs1.c:14)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 10 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/errs1.vgtest b/memcheck/tests/errs1.vgtest
new file mode 100644
index 0000000..fbe7c76
--- /dev/null
+++ b/memcheck/tests/errs1.vgtest
@@ -0,0 +1 @@
+prog: errs1
diff --git a/memcheck/tests/exitprog.c b/memcheck/tests/exitprog.c
new file mode 100644
index 0000000..3067216
--- /dev/null
+++ b/memcheck/tests/exitprog.c
@@ -0,0 +1,16 @@
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define ZILLION 1000000
+
+int main ( void )
+{
+ int i;
+ char* a = malloc(ZILLION * sizeof(char));
+ for (i = 0; i <= ZILLION; i++) a[i] = 0;
+ a = (char*)177;
+ _exit(1);
+}
diff --git a/memcheck/tests/exitprog.stderr.exp b/memcheck/tests/exitprog.stderr.exp
new file mode 100644
index 0000000..1b30fe0
--- /dev/null
+++ b/memcheck/tests/exitprog.stderr.exp
@@ -0,0 +1,16 @@
+
+Invalid write of size 1
+ at 0x........: main (exitprog.c:15)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/exitprog)
+ Address 0x........ is 0 bytes after a block of size 1000000 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (exitprog.c:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/exitprog)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 1000000 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 1000000 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/exitprog.vgtest b/memcheck/tests/exitprog.vgtest
new file mode 100644
index 0000000..0095028
--- /dev/null
+++ b/memcheck/tests/exitprog.vgtest
@@ -0,0 +1 @@
+prog: exitprog
diff --git a/memcheck/tests/filter_leak_check_size b/memcheck/tests/filter_leak_check_size
new file mode 100755
index 0000000..03def89
--- /dev/null
+++ b/memcheck/tests/filter_leak_check_size
@@ -0,0 +1,4 @@
+#! /bin/sh
+
+./filter_stderr | \
+sed "s/checked [0-9]\+ bytes./checked ... bytes./"
diff --git a/memcheck/tests/filter_stderr b/memcheck/tests/filter_stderr
new file mode 100755
index 0000000..0d5e763
--- /dev/null
+++ b/memcheck/tests/filter_stderr
@@ -0,0 +1,24 @@
+#! /bin/sh
+
+# Skip first four lines (valgrind intro)
+# XXX: be more clever/subtle; eg. if there's just a 1-line error message
+# don't cut it
+
+dir=`dirname $0`
+
+$dir/../filter_stderr_basic |
+
+# Anonymise addresses
+$dir/../filter_addresses |
+
+# Anonymise line numbers in vg_clientfuncs.c
+sed "s/vg_clientfuncs.c:[0-9]\+/vg_clientfuncs.c:.../" |
+
+$dir/../filter_test_paths |
+
+# Anonymise paths like "(in /foo/bar/libc-baz.so)"
+sed "s/(in \/.*libc.*)$/(in \/...libc...)/" |
+
+# Anonymise paths like "__libc_start_main (../foo/bar/libc-quux.c:129)"
+sed "s/__libc_\(.*\) (.*)$/__libc_\1 (...libc...)/"
+
diff --git a/memcheck/tests/fprw.c b/memcheck/tests/fprw.c
new file mode 100644
index 0000000..556d8a0
--- /dev/null
+++ b/memcheck/tests/fprw.c
@@ -0,0 +1,26 @@
+
+/* most of the nasties in this are in the same bb, so you need to run
+with --single-step=yes to get them properly distinguished. */
+
+#include <stdlib.h>
+
+int main ( void )
+{
+ volatile double d;
+ volatile float f;
+ double* dp = malloc(sizeof(double));
+ float* fp = malloc(sizeof(float));
+ int* ip = (int*)0x1234567;
+ d += 1.0;
+ f += 10.0;
+ *dp += 2.0;
+ *fp += 20.0;
+ free(dp);
+ free(fp);
+ *dp += 3.0;
+ *fp += 30.0;
+ free(ip);
+ ip = malloc(sizeof(int));
+ * ((double*)ip) = 1.2 + d;
+ return 0;
+}
diff --git a/memcheck/tests/fprw.stderr.exp b/memcheck/tests/fprw.stderr.exp
new file mode 100644
index 0000000..2137572
--- /dev/null
+++ b/memcheck/tests/fprw.stderr.exp
@@ -0,0 +1,83 @@
+
+Use of uninitialised value of size 8
+ at 0x........: main (fprw.c:14)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Use of uninitialised value of size 4
+ at 0x........: main (fprw.c:15)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Use of uninitialised value of size 8
+ at 0x........: main (fprw.c:16)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Use of uninitialised value of size 4
+ at 0x........: main (fprw.c:17)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid read of size 8
+ at 0x........: main (fprw.c:20)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+ Address 0x........ is 0 bytes inside a block of size 8 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (fprw.c:18)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid write of size 8
+ at 0x........: main (fprw.c:20)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+ Address 0x........ is 0 bytes inside a block of size 8 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (fprw.c:18)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid read of size 4
+ at 0x........: main (fprw.c:21)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+ Address 0x........ is 0 bytes inside a block of size 4 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (fprw.c:19)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid write of size 4
+ at 0x........: main (fprw.c:21)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+ Address 0x........ is 0 bytes inside a block of size 4 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (fprw.c:19)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (fprw.c:22)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+ Address 0x........ is not stack'd, malloc'd or free'd
+
+Invalid write of size 8
+ at 0x........: main (fprw.c:24)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+ Address 0x........ is 0 bytes inside a block of size 4 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (fprw.c:23)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+ERROR SUMMARY: 10 errors from 10 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 4 bytes in 1 blocks.
+malloc/free: 3 allocs, 3 frees, 16 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/fprw.vgtest b/memcheck/tests/fprw.vgtest
new file mode 100644
index 0000000..d44e04a
--- /dev/null
+++ b/memcheck/tests/fprw.vgtest
@@ -0,0 +1,2 @@
+vgopts: --single-step=yes
+prog: fprw
diff --git a/memcheck/tests/fwrite.c b/memcheck/tests/fwrite.c
new file mode 100644
index 0000000..1eec4a4
--- /dev/null
+++ b/memcheck/tests/fwrite.c
@@ -0,0 +1,9 @@
+
+#include <stdlib.h>
+#include <unistd.h>
+int main ( void )
+{
+ char* arr = malloc(10);
+ (void) write( 1 /* stdout */, arr, 10 );
+ return 0;
+}
diff --git a/memcheck/tests/fwrite.stderr.exp b/memcheck/tests/fwrite.stderr.exp
new file mode 100644
index 0000000..9c26de2
--- /dev/null
+++ b/memcheck/tests/fwrite.stderr.exp
@@ -0,0 +1,16 @@
+
+Syscall param write(buf) contains uninitialised or unaddressable byte(s)
+ at 0x........: __libc_write (...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+ Address 0x........ is 0 bytes inside a block of size 10 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (fwrite.c:6)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 10 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/fwrite.stdout.exp b/memcheck/tests/fwrite.stdout.exp
new file mode 100644
index 0000000..cb43b5c
--- /dev/null
+++ b/memcheck/tests/fwrite.stdout.exp
Binary files differ
diff --git a/memcheck/tests/fwrite.vgtest b/memcheck/tests/fwrite.vgtest
new file mode 100644
index 0000000..f43efd0
--- /dev/null
+++ b/memcheck/tests/fwrite.vgtest
@@ -0,0 +1 @@
+prog: fwrite
diff --git a/memcheck/tests/inits.c b/memcheck/tests/inits.c
new file mode 100644
index 0000000..7dd0c93
--- /dev/null
+++ b/memcheck/tests/inits.c
@@ -0,0 +1,20 @@
+
+#include <stdio.h>
+
+/* Static and global vars are inited to zero, non-static local vars aren't. */
+
+int g;
+static int gs;
+
+int main(void)
+{
+ int l;
+ static int ls;
+
+ if (gs == 0xDEADBEEF) printf("1!\n");
+ if (g == 0xDEADBEEF) printf("2!\n");
+ if (ls == 0xDEADBEEF) printf("3!\n");
+ if (l == 0xDEADBEEF) printf("4!\n"); // complains
+
+ return 0;
+}
diff --git a/memcheck/tests/inits.stderr.exp b/memcheck/tests/inits.stderr.exp
new file mode 100644
index 0000000..e703ced
--- /dev/null
+++ b/memcheck/tests/inits.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: main (inits.c:17)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/inits)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/inits.vgtest b/memcheck/tests/inits.vgtest
new file mode 100644
index 0000000..e654dc6
--- /dev/null
+++ b/memcheck/tests/inits.vgtest
@@ -0,0 +1 @@
+prog: inits
diff --git a/memcheck/tests/inline.c b/memcheck/tests/inline.c
new file mode 100644
index 0000000..cb023b2
--- /dev/null
+++ b/memcheck/tests/inline.c
@@ -0,0 +1,21 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+__inline__
+static int addemup ( int* arr )
+{
+ int i, j = 0;
+ for (i = 0; i <= 10; i++)
+ j += arr[i];
+ return j;
+}
+
+int main ( void )
+{
+ int sum;
+ int* a = calloc(10, sizeof(int));
+ sum = addemup(a);
+ printf("sum is %d\n", sum);
+ return 0;
+}
diff --git a/memcheck/tests/inline.stderr.exp b/memcheck/tests/inline.stderr.exp
new file mode 100644
index 0000000..a2225c1
--- /dev/null
+++ b/memcheck/tests/inline.stderr.exp
@@ -0,0 +1,17 @@
+
+Invalid read of size 4
+ at 0x........: addemup (inline.c:10)
+ by 0x........: main (inline.c:18)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: calloc@@GLIBC_2.0 (in /.../tests/memcheck/inline)
+ Address 0x........ is 0 bytes after a block of size 40 alloc'd
+ at 0x........: calloc (vg_clientfuncs.c:...)
+ by 0x........: main (inline.c:17)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: calloc@@GLIBC_2.0 (in /.../tests/memcheck/inline)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 40 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 40 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/inline.stdout.exp b/memcheck/tests/inline.stdout.exp
new file mode 100644
index 0000000..ad1401e
--- /dev/null
+++ b/memcheck/tests/inline.stdout.exp
@@ -0,0 +1 @@
+sum is 0
diff --git a/memcheck/tests/inline.vgtest b/memcheck/tests/inline.vgtest
new file mode 100644
index 0000000..89673b1
--- /dev/null
+++ b/memcheck/tests/inline.vgtest
@@ -0,0 +1 @@
+prog: inline
diff --git a/memcheck/tests/malloc1.c b/memcheck/tests/malloc1.c
new file mode 100644
index 0000000..dff5250
--- /dev/null
+++ b/memcheck/tests/malloc1.c
@@ -0,0 +1,24 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void really ( void );
+
+int main ( void )
+{
+ really();
+ return 0;
+}
+
+void really ( void )
+{
+ int i;
+ char* p = malloc(10);
+ for (i = 0; i < 10; i++)
+ p[i] = 'z';
+ free(p);
+ p[1] = 'z';
+ p = malloc(10);
+ p[2] = 'z';
+ p[-1] = 'z';
+}
diff --git a/memcheck/tests/malloc1.stderr.exp b/memcheck/tests/malloc1.stderr.exp
new file mode 100644
index 0000000..1e4c67f
--- /dev/null
+++ b/memcheck/tests/malloc1.stderr.exp
@@ -0,0 +1,28 @@
+
+Invalid write of size 1
+ at 0x........: really (malloc1.c:20)
+ by 0x........: main (malloc1.c:10)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc1)
+ Address 0x........ is 1 bytes inside a block of size 10 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: really (malloc1.c:19)
+ by 0x........: main (malloc1.c:10)
+ by 0x........: __libc_start_main (...libc...)
+
+Invalid write of size 1
+ at 0x........: really (malloc1.c:23)
+ by 0x........: main (malloc1.c:10)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc1)
+ Address 0x........ is 1 bytes before a block of size 10 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: really (malloc1.c:21)
+ by 0x........: main (malloc1.c:10)
+ by 0x........: __libc_start_main (...libc...)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 10 bytes in 1 blocks.
+malloc/free: 2 allocs, 1 frees, 20 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/malloc1.vgtest b/memcheck/tests/malloc1.vgtest
new file mode 100644
index 0000000..43c402a
--- /dev/null
+++ b/memcheck/tests/malloc1.vgtest
@@ -0,0 +1 @@
+prog: malloc1
diff --git a/memcheck/tests/malloc2.c b/memcheck/tests/malloc2.c
new file mode 100644
index 0000000..44cc7bb
--- /dev/null
+++ b/memcheck/tests/malloc2.c
@@ -0,0 +1,49 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* The original test driver machinery. */
+#define N_TEST_TRANSACTIONS 500
+#define N_TEST_ARR 2000
+
+#define M_TEST_MALLOC 1000
+
+void* test_arr[N_TEST_ARR];
+
+int main ( int argc, char** argv )
+{
+ int i, j, k, nbytes;
+ unsigned char* chp;
+
+ for (i = 0; i < N_TEST_ARR; i++)
+ test_arr[i] = NULL;
+
+ for (i = 0; i < N_TEST_TRANSACTIONS; i++) {
+ j = random() % N_TEST_ARR;
+ if (test_arr[j]) {
+ free(test_arr[j]);
+ test_arr[j] = NULL;
+ } else {
+ nbytes = 1 + random() % M_TEST_MALLOC;
+ if (random()%64 == 32)
+ nbytes *= 17;
+ test_arr[j] = malloc( nbytes );
+ chp = test_arr[j];
+ for (k = 1; k < nbytes; k++)
+ chp[k] = (unsigned char)(k + 99);
+ }
+ }
+
+ for (i = 0; test_arr[i] == NULL; i++) ;
+ free(test_arr[i]);
+ ((char*)test_arr[i])[0] = 0;
+
+ for (i = 0; i < N_TEST_ARR; i++) {
+ if (test_arr[i]) {
+ free(test_arr[i]);
+ test_arr[i] = NULL;
+ }
+ }
+
+ return 0;
+}
diff --git a/memcheck/tests/malloc2.stderr.exp b/memcheck/tests/malloc2.stderr.exp
new file mode 100644
index 0000000..dd86b2d
--- /dev/null
+++ b/memcheck/tests/malloc2.stderr.exp
@@ -0,0 +1,27 @@
+
+Invalid write of size 1
+ at 0x........: main (malloc2.c:39)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc2)
+ Address 0x........ is 0 bytes inside a block of size 429 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (malloc2.c:38)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc2)
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (malloc2.c:43)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc2)
+ Address 0x........ is 0 bytes inside a block of size 429 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (malloc2.c:38)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc2)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 443 allocs, 444 frees, 265463 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/malloc2.vgtest b/memcheck/tests/malloc2.vgtest
new file mode 100644
index 0000000..d2dd1b4
--- /dev/null
+++ b/memcheck/tests/malloc2.vgtest
@@ -0,0 +1 @@
+prog: malloc2
diff --git a/memcheck/tests/manuel1.c b/memcheck/tests/manuel1.c
new file mode 100644
index 0000000..ac1f3c8
--- /dev/null
+++ b/memcheck/tests/manuel1.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+int main ()
+{
+ int x;
+
+ printf ("x = %d\n", x==0xDEADBEEF ? 99 : 88);
+
+ return 0;
+}
diff --git a/memcheck/tests/manuel1.stderr.exp b/memcheck/tests/manuel1.stderr.exp
new file mode 100644
index 0000000..c674937
--- /dev/null
+++ b/memcheck/tests/manuel1.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: main (manuel1.c:7)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/manuel1)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/manuel1.stdout.exp b/memcheck/tests/manuel1.stdout.exp
new file mode 100644
index 0000000..d26cbc9
--- /dev/null
+++ b/memcheck/tests/manuel1.stdout.exp
@@ -0,0 +1 @@
+x = 88
diff --git a/memcheck/tests/manuel1.vgtest b/memcheck/tests/manuel1.vgtest
new file mode 100644
index 0000000..e3ad9c7
--- /dev/null
+++ b/memcheck/tests/manuel1.vgtest
@@ -0,0 +1 @@
+prog: manuel1
diff --git a/memcheck/tests/manuel2.c b/memcheck/tests/manuel2.c
new file mode 100644
index 0000000..3b7135e
--- /dev/null
+++ b/memcheck/tests/manuel2.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <malloc.h>
+
+int main ()
+{
+ int *x;
+
+ printf ("x = %d\n", *x==0xDEADBEEF ? 99 : 88);
+
+ return 0;
+}
diff --git a/memcheck/tests/manuel2.stderr.exp b/memcheck/tests/manuel2.stderr.exp
new file mode 100644
index 0000000..55ff720
--- /dev/null
+++ b/memcheck/tests/manuel2.stderr.exp
@@ -0,0 +1,11 @@
+
+Use of uninitialised value of size 4
+ at 0x........: main (manuel2.c:8)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/manuel2)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/manuel2.stdout.exp b/memcheck/tests/manuel2.stdout.exp
new file mode 100644
index 0000000..d26cbc9
--- /dev/null
+++ b/memcheck/tests/manuel2.stdout.exp
@@ -0,0 +1 @@
+x = 88
diff --git a/memcheck/tests/manuel2.vgtest b/memcheck/tests/manuel2.vgtest
new file mode 100644
index 0000000..1c785a5
--- /dev/null
+++ b/memcheck/tests/manuel2.vgtest
@@ -0,0 +1 @@
+prog: manuel2
diff --git a/memcheck/tests/manuel3.c b/memcheck/tests/manuel3.c
new file mode 100644
index 0000000..ea98fa9
--- /dev/null
+++ b/memcheck/tests/manuel3.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <malloc.h>
+
+int gcc_cant_inline_me ( int );
+
+int main ()
+{
+ int *x, y;
+
+ x = (int *) malloc (sizeof (int));
+
+ y = *x == 173;
+
+ if (gcc_cant_inline_me(y)) { }
+
+ return 0;
+}
+
+/* must be AFTER main */
+int gcc_cant_inline_me ( int n )
+{
+ if (n == 42)
+ return 1; /* forty-two, dudes! */
+ else
+ return 0; /* some other number, dudes! */
+}
+
+
diff --git a/memcheck/tests/manuel3.stderr.exp b/memcheck/tests/manuel3.stderr.exp
new file mode 100644
index 0000000..7a257c3
--- /dev/null
+++ b/memcheck/tests/manuel3.stderr.exp
@@ -0,0 +1,12 @@
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: gcc_cant_inline_me (manuel3.c:22)
+ by 0x........: main (manuel3.c:14)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 4 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 4 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/manuel3.vgtest b/memcheck/tests/manuel3.vgtest
new file mode 100644
index 0000000..0481cc6
--- /dev/null
+++ b/memcheck/tests/manuel3.vgtest
@@ -0,0 +1 @@
+prog: manuel3
diff --git a/memcheck/tests/memalign_test.c b/memcheck/tests/memalign_test.c
new file mode 100644
index 0000000..a24808c
--- /dev/null
+++ b/memcheck/tests/memalign_test.c
@@ -0,0 +1,19 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main ( void )
+{
+ void* a[10];
+ int i;
+ for (i = 0; i < 10; i++) {
+ a[i] = valloc(11111 * (i+1));
+ // printf("I acquire %p\n", a[i]);
+ }
+ for (i = 0; i < 10; i++) {
+ // printf("I release %p\n", a[i]);
+ free(a[i]);
+ }
+ free(a[9]);
+ return 0;
+}
diff --git a/memcheck/tests/memalign_test.stderr.exp b/memcheck/tests/memalign_test.stderr.exp
new file mode 100644
index 0000000..4725928
--- /dev/null
+++ b/memcheck/tests/memalign_test.stderr.exp
@@ -0,0 +1,17 @@
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (memalign_test.c:17)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: valloc@@GLIBC_2.0 (in /.../tests/memcheck/memalign_test)
+ Address 0x........ is 0 bytes inside a block of size 111110 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (memalign_test.c:15)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: valloc@@GLIBC_2.0 (in /.../tests/memcheck/memalign_test)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 10 allocs, 11 frees, 611105 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/memalign_test.vgtest b/memcheck/tests/memalign_test.vgtest
new file mode 100644
index 0000000..56b601c
--- /dev/null
+++ b/memcheck/tests/memalign_test.vgtest
@@ -0,0 +1 @@
+prog: memalign_test
diff --git a/memcheck/tests/memcmptest.c b/memcheck/tests/memcmptest.c
new file mode 100644
index 0000000..83eb2d4
--- /dev/null
+++ b/memcheck/tests/memcmptest.c
@@ -0,0 +1,20 @@
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+char* s1;
+char* s2;
+
+int main ( void )
+{
+ s1 = malloc(10); strcpy(s1,"fooble");
+ s2 = malloc(10); strcpy(s2,"fooble");
+ if (memcmp(s1, s2, 8) != 0)
+ printf("different\n");
+ else
+ printf("same (?!)\n");
+ return 0;
+}
+
+
diff --git a/memcheck/tests/memcmptest.stderr.exp b/memcheck/tests/memcmptest.stderr.exp
new file mode 100644
index 0000000..d7b1c3a
--- /dev/null
+++ b/memcheck/tests/memcmptest.stderr.exp
@@ -0,0 +1,16 @@
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: memcmp (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/memcmptest)
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: memcmp (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/memcmptest)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 20 bytes in 2 blocks.
+malloc/free: 2 allocs, 0 frees, 20 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/memcmptest.stdout.exp b/memcheck/tests/memcmptest.stdout.exp
new file mode 100644
index 0000000..7164804
--- /dev/null
+++ b/memcheck/tests/memcmptest.stdout.exp
@@ -0,0 +1 @@
+same (?!)
diff --git a/memcheck/tests/memcmptest.vgtest b/memcheck/tests/memcmptest.vgtest
new file mode 100644
index 0000000..f31a8f5
--- /dev/null
+++ b/memcheck/tests/memcmptest.vgtest
@@ -0,0 +1 @@
+prog: memcmptest
diff --git a/memcheck/tests/mismatches.cpp b/memcheck/tests/mismatches.cpp
new file mode 100644
index 0000000..857a075
--- /dev/null
+++ b/memcheck/tests/mismatches.cpp
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+
+int main()
+{
+ int* fpointer = (int*)malloc(10);
+ delete fpointer; // should give warning
+ fpointer = (int*)malloc(10);
+ delete [] fpointer; // should give warning
+ fpointer = (int*)malloc(10);
+ free (fpointer); // should work!
+
+ int* nvec = new int[10];
+ delete nvec; // should give a warning
+ nvec = new int[10];
+ free (nvec); // should give a warning
+ nvec = new int[10];
+ delete [] nvec; // should work!
+
+ int* n = new int;
+ delete [] n; // should give a warning
+ n = new int;
+ free(n); // should give a warning
+ n = new int;
+ delete n; // should work!
+
+ return 0;
+}
diff --git a/memcheck/tests/mismatches.stderr.exp b/memcheck/tests/mismatches.stderr.exp
new file mode 100644
index 0000000..caf65dc
--- /dev/null
+++ b/memcheck/tests/mismatches.stderr.exp
@@ -0,0 +1,72 @@
+
+Mismatched free() / delete / delete []
+ at 0x........: __builtin_delete (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:6)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+ Address 0x........ is 0 bytes inside a block of size 10 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:5)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+ at 0x........: __builtin_vec_delete (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:8)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+ Address 0x........ is 0 bytes inside a block of size 10 alloc'd
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:7)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+ at 0x........: __builtin_delete (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:13)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+ Address 0x........ is 0 bytes inside a block of size 40 alloc'd
+ at 0x........: __builtin_vec_new (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:15)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+ Address 0x........ is 0 bytes inside a block of size 40 alloc'd
+ at 0x........: __builtin_vec_new (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:14)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+ at 0x........: __builtin_vec_delete (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:20)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+ Address 0x........ is 0 bytes inside a block of size 4 alloc'd
+ at 0x........: __builtin_new (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:19)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:22)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+ Address 0x........ is 0 bytes inside a block of size 4 alloc'd
+ at 0x........: __builtin_new (vg_clientfuncs.c:...)
+ by 0x........: main (mismatches.cpp:21)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 9 allocs, 9 frees, 162 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/mismatches.vgtest b/memcheck/tests/mismatches.vgtest
new file mode 100644
index 0000000..3a87ef0
--- /dev/null
+++ b/memcheck/tests/mismatches.vgtest
@@ -0,0 +1 @@
+prog: mismatches
diff --git a/memcheck/tests/mmaptest.c b/memcheck/tests/mmaptest.c
new file mode 100644
index 0000000..74a21ed
--- /dev/null
+++ b/memcheck/tests/mmaptest.c
@@ -0,0 +1,15 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int main()
+{
+ int fd;
+
+ mkdir("dir", 0777);
+ fd = open("dir", O_RDONLY);
+ mmap(NULL, 4711, PROT_READ, MAP_PRIVATE, fd, 0);
+ return 0;
+}
diff --git a/memcheck/tests/mmaptest.stderr.exp b/memcheck/tests/mmaptest.stderr.exp
new file mode 100644
index 0000000..c4aa6f0
--- /dev/null
+++ b/memcheck/tests/mmaptest.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/mmaptest.vgtest b/memcheck/tests/mmaptest.vgtest
new file mode 100644
index 0000000..1540c20
--- /dev/null
+++ b/memcheck/tests/mmaptest.vgtest
@@ -0,0 +1 @@
+prog: mmaptest
diff --git a/memcheck/tests/nanoleak.c b/memcheck/tests/nanoleak.c
new file mode 100644
index 0000000..2b7121c
--- /dev/null
+++ b/memcheck/tests/nanoleak.c
@@ -0,0 +1,9 @@
+
+#include <malloc.h>
+
+int main ( void )
+{
+ volatile int* a = malloc(1000);
+ a[0] = 0;
+ return a[0];
+}
diff --git a/memcheck/tests/nanoleak.stderr.exp b/memcheck/tests/nanoleak.stderr.exp
new file mode 100644
index 0000000..8dc3ae7
--- /dev/null
+++ b/memcheck/tests/nanoleak.stderr.exp
@@ -0,0 +1,26 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 1000 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 1000 bytes allocated.
+For counts of detected errors, rerun with: -v
+searching for pointers to 1 not-freed blocks.
+checked ... bytes.
+
+definitely lost: 1000 bytes in 1 blocks.
+possibly lost: 0 bytes in 0 blocks.
+still reachable: 0 bytes in 0 blocks.
+
+1000 bytes in 1 blocks are definitely lost in loss record 1 of 1
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: main (nanoleak.c:6)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+
+LEAK SUMMARY:
+ definitely lost: 1000 bytes in 1 blocks.
+ possibly lost: 0 bytes in 0 blocks.
+ still reachable: 0 bytes in 0 blocks.
+Reachable blocks (those to which a pointer was found) are not shown.
+To see them, rerun with: --show-reachable=yes
+
diff --git a/memcheck/tests/nanoleak.vgtest b/memcheck/tests/nanoleak.vgtest
new file mode 100644
index 0000000..2fadc98
--- /dev/null
+++ b/memcheck/tests/nanoleak.vgtest
@@ -0,0 +1,3 @@
+vgopts: --leak-check=yes
+prog: nanoleak
+stderr_filter: filter_leak_check_size
diff --git a/memcheck/tests/new_override.cpp b/memcheck/tests/new_override.cpp
new file mode 100644
index 0000000..5b48611
--- /dev/null
+++ b/memcheck/tests/new_override.cpp
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+class Test {
+public:
+ int a, b, c, d;
+};
+
+void *operator new(size_t size)
+{
+ void *ret = malloc(size);
+ printf("Here.\n");
+ for (unsigned int i = 0; i < size; i++) ((char *) ret)[i] = 0xFF;
+ return ret;
+}
+
+int main(int argc, char *argv[]) {
+ Test *toto;
+ int i;
+ int j = 0;
+
+ toto = new Test[2];
+
+ for (i = 0; i < 2; i++) {
+ if (toto[i].a) {
+ j++;
+ }
+ //printf("%d : %08x %08x %08x %08x\n", i, toto[i].a, toto[i].b, toto[i].c, toto[i].d);
+ }
+}
diff --git a/memcheck/tests/new_override.stderr.exp b/memcheck/tests/new_override.stderr.exp
new file mode 100644
index 0000000..8ba31b6
--- /dev/null
+++ b/memcheck/tests/new_override.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: main (new_override.cpp:25)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/new_override)
+
+ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 32 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 32 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/new_override.vgtest b/memcheck/tests/new_override.vgtest
new file mode 100644
index 0000000..4f11a5b
--- /dev/null
+++ b/memcheck/tests/new_override.vgtest
@@ -0,0 +1 @@
+prog: new_override
diff --git a/memcheck/tests/pushfpopf.stderr.exp b/memcheck/tests/pushfpopf.stderr.exp
new file mode 100644
index 0000000..8e10fb2
--- /dev/null
+++ b/memcheck/tests/pushfpopf.stderr.exp
@@ -0,0 +1,12 @@
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: fooble (in /.../tests/memcheck/pushfpopf)
+ by 0x........: main (pushfpopf_c.c:12)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: (within /.../tests/memcheck/pushfpopf)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/pushfpopf.stdout.exp b/memcheck/tests/pushfpopf.stdout.exp
new file mode 100644
index 0000000..180f871
--- /dev/null
+++ b/memcheck/tests/pushfpopf.stdout.exp
@@ -0,0 +1 @@
+fooble: result is 22
diff --git a/memcheck/tests/pushfpopf.vgtest b/memcheck/tests/pushfpopf.vgtest
new file mode 100644
index 0000000..f87b791
--- /dev/null
+++ b/memcheck/tests/pushfpopf.vgtest
@@ -0,0 +1 @@
+prog: pushfpopf
diff --git a/memcheck/tests/pushfpopf_c.c b/memcheck/tests/pushfpopf_c.c
new file mode 100644
index 0000000..f45271e
--- /dev/null
+++ b/memcheck/tests/pushfpopf_c.c
@@ -0,0 +1,14 @@
+
+#include <stdio.h>
+
+// in pushfpopf.s
+extern int fooble ( int, int );
+
+int main ( void )
+{
+ int arr[2];
+ arr[0] = 3;
+ // arr[1] = 45;
+ printf("fooble: result is %d\n", fooble(arr[0], arr[1]));
+ return 0;
+}
diff --git a/memcheck/tests/pushfpopf_s.s b/memcheck/tests/pushfpopf_s.s
new file mode 100644
index 0000000..f140995
--- /dev/null
+++ b/memcheck/tests/pushfpopf_s.s
@@ -0,0 +1,38 @@
+ .file "twoparams.c"
+ .version "01.01"
+gcc2_compiled.:
+.text
+ .align 4
+.globl fooble
+ .type fooble,@function
+fooble:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+
+ subl 12(%ebp), %eax
+ # flags are now undef if either operand is
+ # save possibly undef flags on stack
+ pushfl
+
+ movl $0, %eax
+ addl $0, %eax
+ # flags are now definitely defined
+
+ popfl
+ # resulting flag definedness depends on outcome of sub above
+ # should override that created by 0 + 0 above
+
+ # now use the condition codes to generate a value
+ # in a way which will cause undefinedness to get reported
+ jz labelz
+ movl $22, %eax
+ jmp theend
+labelz:
+ movl $33, %eax
+theend:
+ popl %ebp
+ ret
+.Lfe1:
+ .size fooble,.Lfe1-fooble
+ .ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.1 2.96-98)"
diff --git a/memcheck/tests/realloc1.c b/memcheck/tests/realloc1.c
new file mode 100644
index 0000000..a5d1edc
--- /dev/null
+++ b/memcheck/tests/realloc1.c
@@ -0,0 +1,14 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+ int i;
+ char* p = malloc(1);
+ for (i = 2; i < 50; i++) {
+ p = realloc(p, i);
+ p[i-1] = 'z';
+ }
+ return 0;
+}
diff --git a/memcheck/tests/realloc1.stderr.exp b/memcheck/tests/realloc1.stderr.exp
new file mode 100644
index 0000000..14ec594
--- /dev/null
+++ b/memcheck/tests/realloc1.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 49 bytes in 1 blocks.
+malloc/free: 49 allocs, 48 frees, 1225 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/realloc1.vgtest b/memcheck/tests/realloc1.vgtest
new file mode 100644
index 0000000..d0d10d7
--- /dev/null
+++ b/memcheck/tests/realloc1.vgtest
@@ -0,0 +1 @@
+prog: realloc1
diff --git a/memcheck/tests/realloc2.c b/memcheck/tests/realloc2.c
new file mode 100644
index 0000000..c89ff8a
--- /dev/null
+++ b/memcheck/tests/realloc2.c
@@ -0,0 +1,21 @@
+/* This test demonstrated an obscure bug in malloclists handling caused by
+ multiple blocks hashing to the same list and one being overwritten at
+ realloc time due to bad ordering of the things happening. Now runs
+ without error. */
+
+#include <malloc.h>
+#include <stdio.h>
+
+int main ( void )
+{
+ char* p;
+ int i;
+ for (i = 0; i < 10000; i++) {
+ p = malloc(10 + 10 * (i % 100));
+ p = realloc(p, 500);
+ p = realloc(p, 600);
+ free(p);
+ }
+ return 0;
+}
+
diff --git a/memcheck/tests/realloc2.stderr.exp b/memcheck/tests/realloc2.stderr.exp
new file mode 100644
index 0000000..8ed8426
--- /dev/null
+++ b/memcheck/tests/realloc2.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 30000 allocs, 30000 frees, 16050000 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/realloc2.vgtest b/memcheck/tests/realloc2.vgtest
new file mode 100644
index 0000000..0a28b23
--- /dev/null
+++ b/memcheck/tests/realloc2.vgtest
@@ -0,0 +1 @@
+prog: realloc2
diff --git a/memcheck/tests/sigaltstack.c b/memcheck/tests/sigaltstack.c
new file mode 100644
index 0000000..8f31e68
--- /dev/null
+++ b/memcheck/tests/sigaltstack.c
@@ -0,0 +1,38 @@
+
+
+#include <stdio.h>
+#include <malloc.h>
+#include <signal.h>
+
+void sig_handler(int sig){
+ int var;
+ fprintf(stderr, "caught signal, local var is on %p\n", &var);
+}
+
+
+int main(int argv, char** argc) {
+ int res, i;
+ stack_t sigstk;
+ struct sigaction act;
+ sigstk.ss_sp = (char *)malloc(SIGSTKSZ);
+
+ sigstk.ss_size = SIGSTKSZ;
+ sigstk.ss_flags = 0;
+ fprintf(stderr, "calling sigaltstack, stack base is %p\n", sigstk.ss_sp);
+ if (sigaltstack(&sigstk,0)<0) perror("sigaltstack");
+
+ fprintf(stderr,"setting sigaction\n");
+ act.sa_flags=SA_ONSTACK;
+ act.sa_handler=&sig_handler;
+ res = sigaction(SIGUSR1,&act,0);
+ fprintf(stderr, "res = %d\n", res);
+ fprintf(stderr, "raising the signal\n");
+ raise(SIGUSR1);
+
+ /* Loop long enough so valgrind has a forced context switch and
+ actually delivers the signal before the thread exits. */
+ for (i = 0; i < 1000000; i++) ;
+
+ fprintf(stderr, "done\n");
+ return 0;
+}
diff --git a/memcheck/tests/sigaltstack.stderr.exp b/memcheck/tests/sigaltstack.stderr.exp
new file mode 100644
index 0000000..ceeb462
--- /dev/null
+++ b/memcheck/tests/sigaltstack.stderr.exp
@@ -0,0 +1,19 @@
+
+calling sigaltstack, stack base is 0x........
+setting sigaction
+Syscall param sigaction(act) contains uninitialised or unaddressable byte(s)
+ at 0x........: __libc_sigaction (...libc...)
+ by 0x........: main (sigaltstack.c:27)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: sigaltstack@@GLIBC_2.0 (in /.../tests/memcheck/sigaltstack)
+ Address 0x........ is on thread 1's stack
+res = 0
+raising the signal
+caught signal, local var is on 0x........
+done
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 8192 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 8192 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/sigaltstack.vgtest b/memcheck/tests/sigaltstack.vgtest
new file mode 100644
index 0000000..f61eeae
--- /dev/null
+++ b/memcheck/tests/sigaltstack.vgtest
@@ -0,0 +1 @@
+prog: sigaltstack
diff --git a/memcheck/tests/signal2.c b/memcheck/tests/signal2.c
new file mode 100644
index 0000000..a1df705
--- /dev/null
+++ b/memcheck/tests/signal2.c
@@ -0,0 +1,20 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+void sig_hdlr ( int signo )
+{
+ printf ( "caught sig segv\n" );
+ exit(1);
+}
+
+int main ( void )
+{
+ printf ( "installing sig handler\n" );
+ signal(SIGSEGV, sig_hdlr);
+ printf ( "doing bad thing\n" );
+ * (int*) 65536 = 0;
+ printf ( "exited normally ?!\n" );
+ return 0;
+}
diff --git a/memcheck/tests/signal2.stderr.exp b/memcheck/tests/signal2.stderr.exp
new file mode 100644
index 0000000..3ab7302
--- /dev/null
+++ b/memcheck/tests/signal2.stderr.exp
@@ -0,0 +1,12 @@
+
+Invalid write of size 4
+ at 0x........: main (signal2.c:17)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: exit@@GLIBC_2.0 (in /.../tests/memcheck/signal2)
+ Address 0x........ is not stack'd, malloc'd or free'd
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/signal2.stderr.exp.hd b/memcheck/tests/signal2.stderr.exp.hd
new file mode 100644
index 0000000..2cd1fc8
--- /dev/null
+++ b/memcheck/tests/signal2.stderr.exp.hd
@@ -0,0 +1,12 @@
+
+Invalid write of size 4
+ at 0x........: main (signal2.c:16)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: exit@@GLIBC_2.0 (in /.../tests/memcheck/signal2)
+ Address 0x........ is not stack'd, malloc'd or free'd
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/signal2.stdout.exp b/memcheck/tests/signal2.stdout.exp
new file mode 100644
index 0000000..3e16af0
--- /dev/null
+++ b/memcheck/tests/signal2.stdout.exp
@@ -0,0 +1,3 @@
+installing sig handler
+doing bad thing
+caught sig segv
diff --git a/memcheck/tests/signal2.vgtest b/memcheck/tests/signal2.vgtest
new file mode 100644
index 0000000..c301370
--- /dev/null
+++ b/memcheck/tests/signal2.vgtest
@@ -0,0 +1 @@
+prog: signal2
diff --git a/memcheck/tests/supp.c b/memcheck/tests/supp.c
new file mode 100644
index 0000000..50c4a81
--- /dev/null
+++ b/memcheck/tests/supp.c
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+
+int
+main ()
+{
+ int x;
+
+ if (x == 0)
+ return 0;
+ else
+ return 1;
+}
diff --git a/memcheck/tests/supp.supp b/memcheck/tests/supp.supp
new file mode 100644
index 0000000..477c6c4
--- /dev/null
+++ b/memcheck/tests/supp.supp
@@ -0,0 +1,6 @@
+{
+ name_of_this_suppression
+ Cond
+ obj:*supp1
+ fun:__libc_start_main
+}
diff --git a/memcheck/tests/supp1.stderr.exp b/memcheck/tests/supp1.stderr.exp
new file mode 100644
index 0000000..6d763a7
--- /dev/null
+++ b/memcheck/tests/supp1.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/supp1.vgtest b/memcheck/tests/supp1.vgtest
new file mode 100644
index 0000000..31130ff
--- /dev/null
+++ b/memcheck/tests/supp1.vgtest
@@ -0,0 +1,2 @@
+vgopts: --suppressions=supp.supp
+prog: supp1
diff --git a/memcheck/tests/supp2.stderr.exp b/memcheck/tests/supp2.stderr.exp
new file mode 100644
index 0000000..b245f04
--- /dev/null
+++ b/memcheck/tests/supp2.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: main (supp.c:8)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/supp2.vgtest b/memcheck/tests/supp2.vgtest
new file mode 100644
index 0000000..f5200b0
--- /dev/null
+++ b/memcheck/tests/supp2.vgtest
@@ -0,0 +1,2 @@
+vgopts: --suppressions=supp.supp
+prog: supp2
diff --git a/memcheck/tests/suppfree.c b/memcheck/tests/suppfree.c
new file mode 100644
index 0000000..8298f02
--- /dev/null
+++ b/memcheck/tests/suppfree.c
@@ -0,0 +1,30 @@
+
+#include <stdlib.h>
+
+void ddd ( char* x )
+{
+ free(x);
+ free(x);
+}
+
+void ccc (char* x)
+{
+ ddd(x);
+}
+
+void bbb (char* x)
+{
+ ccc(x);
+}
+
+void aaa (char* x)
+{
+ bbb(x);
+}
+
+int main ( void )
+{
+ char* x = malloc(10);
+ aaa(x);
+ return 0;
+}
diff --git a/memcheck/tests/suppfree.stderr.exp b/memcheck/tests/suppfree.stderr.exp
new file mode 100644
index 0000000..149bf84
--- /dev/null
+++ b/memcheck/tests/suppfree.stderr.exp
@@ -0,0 +1,17 @@
+
+Invalid free() / delete / delete[]
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: ddd (suppfree.c:7)
+ by 0x........: ccc (suppfree.c:12)
+ by 0x........: bbb (suppfree.c:17)
+ Address 0x........ is 0 bytes inside a block of size 10 free'd
+ at 0x........: free (vg_clientfuncs.c:...)
+ by 0x........: ddd (suppfree.c:6)
+ by 0x........: ccc (suppfree.c:12)
+ by 0x........: bbb (suppfree.c:17)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 1 allocs, 2 frees, 10 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/suppfree.vgtest b/memcheck/tests/suppfree.vgtest
new file mode 100644
index 0000000..bd38f8a
--- /dev/null
+++ b/memcheck/tests/suppfree.vgtest
@@ -0,0 +1 @@
+prog: suppfree
diff --git a/memcheck/tests/trivialleak.c b/memcheck/tests/trivialleak.c
new file mode 100644
index 0000000..f3a8963
--- /dev/null
+++ b/memcheck/tests/trivialleak.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+
+static void test()
+ {
+ void* leak;
+ int i;
+ for (i = 0; i < 1000; i++)
+ leak = (void*)malloc( 1 );
+ }
+ int main()
+ {
+ test();
+ return 0;
+ }
diff --git a/memcheck/tests/trivialleak.stderr.exp b/memcheck/tests/trivialleak.stderr.exp
new file mode 100644
index 0000000..12bb84b
--- /dev/null
+++ b/memcheck/tests/trivialleak.stderr.exp
@@ -0,0 +1,26 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 1000 bytes in 1000 blocks.
+malloc/free: 1000 allocs, 0 frees, 1000 bytes allocated.
+For counts of detected errors, rerun with: -v
+searching for pointers to 1000 not-freed blocks.
+checked ... bytes.
+
+definitely lost: 1000 bytes in 1000 blocks.
+possibly lost: 0 bytes in 0 blocks.
+still reachable: 0 bytes in 0 blocks.
+
+1000 bytes in 1000 blocks are definitely lost in loss record 1 of 1
+ at 0x........: malloc (vg_clientfuncs.c:...)
+ by 0x........: test (trivialleak.c:8)
+ by 0x........: main (trivialleak.c:13)
+ by 0x........: __libc_start_main (...libc...)
+
+LEAK SUMMARY:
+ definitely lost: 1000 bytes in 1000 blocks.
+ possibly lost: 0 bytes in 0 blocks.
+ still reachable: 0 bytes in 0 blocks.
+Reachable blocks (those to which a pointer was found) are not shown.
+To see them, rerun with: --show-reachable=yes
+
diff --git a/memcheck/tests/trivialleak.vgtest b/memcheck/tests/trivialleak.vgtest
new file mode 100644
index 0000000..c5b68a8
--- /dev/null
+++ b/memcheck/tests/trivialleak.vgtest
@@ -0,0 +1,3 @@
+vgopts: --leak-check=yes
+prog: trivialleak
+stderr_filter: filter_leak_check_size
diff --git a/memcheck/tests/tronical.S b/memcheck/tests/tronical.S
new file mode 100644
index 0000000..030a2af
--- /dev/null
+++ b/memcheck/tests/tronical.S
@@ -0,0 +1,102 @@
+/*
+
+Assembly derived from the following program compiled with -O2.
+This fools Valgrind, causing it to give a false error.
+
+#include <stdio.h>
+
+struct Foo
+{
+ int a1 : 1;
+ int a2 : 1;
+ int a3 : 1;
+ int a4 : 1;
+ int a5 : 1;
+ int a6 : 1;
+ int a7 : 1;
+ int bleh : 1;
+};
+
+struct Foo* foo;
+
+void set()
+{
+ foo->bleh = 1;
+}
+
+void get()
+{
+ if ( foo->bleh == 0 )
+ printf( "blieb\n" );
+}
+
+int main()
+{
+ foo = malloc(sizeof(struct Foo));
+ set();
+
+ get();
+
+ return 0;
+}
+
+*/
+
+ .file "tronical.c"
+ .version "01.01"
+gcc2_compiled.:
+.text
+ .align 4
+.globl set
+ .type set,@function
+set:
+ pushl %ebp
+ movl foo, %eax
+ orb $128, (%eax)
+ movl %esp, %ebp
+ popl %ebp
+ ret
+.Lfe1:
+ .size set,.Lfe1-set
+ .section .rodata.str1.1,"ams",@progbits,1
+.LC0:
+ .string "blieb\n"
+.text
+ .align 4
+.globl get
+ .type get,@function
+get:
+ pushl %ebp
+ movl %esp, %ebp
+ subl $8, %esp
+ movl foo, %eax
+ cmpb $0, (%eax)
+ js .L4
+ subl $12, %esp
+ pushl $.LC0
+ call printf
+ addl $16, %esp
+.L4:
+ leave
+ ret
+.Lfe2:
+ .size get,.Lfe2-get
+ .align 4
+.globl main
+ .type main,@function
+main:
+ pushl %ebp
+ movl %esp, %ebp
+ subl $20, %esp
+ pushl $4
+ call malloc
+ movl %eax, foo
+ call set
+ call get
+ xorl %eax, %eax
+ leave
+ ret
+.Lfe3:
+ .size main,.Lfe3-main
+ .comm foo,4,4
+ .ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.1 2.96-98)"
diff --git a/memcheck/tests/tronical.stderr.exp b/memcheck/tests/tronical.stderr.exp
new file mode 100644
index 0000000..2c63087
--- /dev/null
+++ b/memcheck/tests/tronical.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 4 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 4 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/tronical.vgtest b/memcheck/tests/tronical.vgtest
new file mode 100644
index 0000000..97623ad
--- /dev/null
+++ b/memcheck/tests/tronical.vgtest
@@ -0,0 +1 @@
+prog: tronical
diff --git a/memcheck/tests/weirdioctl.c b/memcheck/tests/weirdioctl.c
new file mode 100644
index 0000000..a78de65
--- /dev/null
+++ b/memcheck/tests/weirdioctl.c
@@ -0,0 +1,44 @@
+
+/* A program which sets a readable fd to have a timeout, and therefore
+ needs --weird-hacks=ioctl-VTIME in order to run without
+ blocking. */
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <termio.h>
+
+int main ( void )
+{
+ int c, i;
+ int res;
+ struct termio tty, oldtty;
+
+ /**
+ ** Save the old tty settings, and get rid of echo
+ ** for the new tty settings
+ **/
+ ioctl(0, TCGETA, &oldtty);
+ tty = oldtty;
+ tty.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
+ tty.c_cc[VMIN] = 0;
+ tty.c_cc[VTIME] = 5;
+ res = ioctl(0, TCSETA, &tty);
+ printf("first ioctl returned %d\n", res);
+
+ /**
+ ** Now do whatever stuff you want non-echoed
+ **/
+ i = 0;
+ while (i++ < 50) {
+ c = getchar();
+ printf("got %d\n", c);
+ }
+
+ /**
+ ** Now reset the old settings
+ **/
+ res = ioctl(0, TCSETA, &oldtty);
+ printf("second ioctl returned %d\n", res);
+
+return 0;
+}
diff --git a/memcheck/tests/weirdioctl.stderr.exp b/memcheck/tests/weirdioctl.stderr.exp
new file mode 100644
index 0000000..7d5c9aa
--- /dev/null
+++ b/memcheck/tests/weirdioctl.stderr.exp
@@ -0,0 +1,12 @@
+
+Syscall param ioctl(TCSET{A,AW,AF}) contains uninitialised or unaddressable byte(s)
+ at 0x........: __ioctl (in /...libc...)
+ by 0x........: __libc_start_main (...libc...)
+ by 0x........: ioctl@@GLIBC_2.0 (in /.../tests/memcheck/weirdioctl)
+ Address 0x........ is on thread 1's stack
+
+ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis, rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/weirdioctl.stdout.exp b/memcheck/tests/weirdioctl.stdout.exp
new file mode 100644
index 0000000..bb65b7c
--- /dev/null
+++ b/memcheck/tests/weirdioctl.stdout.exp
@@ -0,0 +1,52 @@
+first ioctl returned -1
+got 118
+got 103
+got 111
+got 112
+got 116
+got 115
+got 58
+got 32
+got 45
+got 45
+got 119
+got 101
+got 105
+got 114
+got 100
+got 45
+got 104
+got 97
+got 99
+got 107
+got 115
+got 61
+got 105
+got 111
+got 99
+got 116
+got 108
+got 45
+got 86
+got 84
+got 73
+got 77
+got 69
+got 10
+got 112
+got 114
+got 111
+got 103
+got 58
+got 32
+got 32
+got 32
+got 119
+got 101
+got 105
+got 114
+got 100
+got 105
+got 111
+got 99
+second ioctl returned -1
diff --git a/memcheck/tests/weirdioctl.vgtest b/memcheck/tests/weirdioctl.vgtest
new file mode 100644
index 0000000..e8d8630
--- /dev/null
+++ b/memcheck/tests/weirdioctl.vgtest
@@ -0,0 +1,3 @@
+vgopts: --weird-hacks=ioctl-VTIME
+prog: weirdioctl
+args: < weirdioctl.vgtest