A cleanup of the redirection stuff.

- Renamed VG_INTERCEPT as VG_REPLACE_FUNCTION to make its purpose
  clearer.

- Renamed VG_WRAPPER as VG_NOTIFY_ON_LOAD to make its purpose cleare.
  Started calling that stuff "load notification".

- Moved various things into m_redir.c, a much more sensible place for
  them.  This reduced the number of exported functions overall.  Renamed
  intercept_demangle() as Z_decode() as part of this.

- Improved the documentation of this stuff, especially in
  pub_core_redir.c.

- Got --run-libc-freeres=yes working again.  It was doing nothing.

- Renamed vg_inject.so as vg_preload_core.so to match
  vg_preload_<tool>.so

- Renamed vg_intercept.c as vg_preloaded.c.  (I kept the "vg_" prefix
  because this filename can appear in stack traces, so the "vg_" is a
  useful hint for users that it belongs to Valgrind.)

- Removed all the Memcheck-specific calls to add_redirect_sym_to_sym()
  from VG_(setup_redirect_table)(), instead using VG_REPLACE_FUNCTION in
  mac_replace_strmem.c, just like vg_replace_malloc.c.  This is the
  right way to do it.  This required moving some of
  coregrind/pub_core_redir.h into the newly added
  include/pub_tool_redir.h.  add_redirect_sym_to_sym() is no longer
  used...

- Now only handing off symbols to m_redir for inspection/decoding after
  they have been deemed to be interesting by the symbol table reader.

- Factored out commonality between the add_redirect_*_to_* functions
  into add_redirect_X_to_X().

- Added "Zh", meaning '-' ('h' for "hyphen"), to the Z-decoding scheme,
  to handle sonames like "ld-linux-x86-64.so.2".

- Added a FAQ explaining the newly found issue of glibc aliasing 
  sometimes causing the wrong function name to appear in stack traces.

- Added a new regtest strchr.c.  It's possible this will fail on some
  platforms.  If so, an alternative output file can be provided, but
  I'd like to see it in practice first.

It's possible that there will be minor breakage on other
platforms/setups, but it should be minimal and easily fixable.

Plus some ordinary cleanups in symtab.c:

- Removed the old optimisation from VG_(addStr)() whereby it kept track
  of the previous 5 added strings and avoiding duplicating any of them.
  Turns out it was barely having any effect any more, and just
  complicated things.

- Made read_symtab() more readable, by introducing a new variable
  "sym_name" and introducing the auxiliary function
  is_symbol_interesting().

- renamed the module variable 'segInfo' as 'segInfo_list' to make it
  more obvious it's a module variable and not just some ordinary local
  variable (which was an easy mistake to make).

-----------------------------------------------------------------------------

XXX: [later] remove add_redirect_sym_to_sym, and everything related to
     X_to_sym?  (ie. only need X_to_addr)

XXX: better function names?  all those 'resolved' names...
     [later...]




git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3916
This commit is contained in:
Nicholas Nethercote 2005-06-16 03:56:58 +00:00
parent d0029796b7
commit 3ae4d1a0ab
22 changed files with 966 additions and 827 deletions

12
FAQ.txt
View File

@ -245,6 +245,18 @@ Some example sub-traces:
by 0x1D65E007: ???
by 0x8049EE6: main (main.cpp:24)
-----------------------------------------------------------------------------
4.4. The stack traces given by Memcheck (or another tool) seem to
have the wrong function name in them. What's happening?
Occasionally Valgrind stack traces get the wrong function names.
This is caused by glibc using aliases to effectively give one function
two names. Most of the time Valgrind chooses a suitable name, but
very occasionally it gets it wrong.
Examples we know of are printing 'bcmp' instead of 'memcmp', 'index'
instead of 'strchr', and 'rindex' instead of 'strrchr'.
-----------------------------------------------------------------
5. Memcheck doesn't find my bug
-----------------------------------------------------------------

View File

@ -32,7 +32,7 @@ bin_PROGRAMS = \
val_PROGRAMS = \
stage2 \
vg_inject.so
vg_preload_core.so
noinst_HEADERS = \
core.h \
@ -167,12 +167,12 @@ endif
stage2_LDADD= $(stage2_extra) \
-ldl
vg_inject_so_SOURCES = vg_intercept.c
vg_inject_so_CFLAGS = $(AM_CFLAGS) -fpic
vg_inject_so_LDADD = -ldl
vg_inject_so_LDFLAGS = \
vg_preload_core_so_SOURCES = vg_preloaded.c
vg_preload_core_so_CFLAGS = $(AM_CFLAGS) -fpic
vg_preload_core_so_LDADD = -ldl
vg_preload_core_so_LDFLAGS = \
-shared \
-Wl,--soname,vg_inject.so \
-Wl,--soname,vg_preload_core.so \
-Wl,-z,initfirst
MANUAL_DEPS = $(noinst_HEADERS) $(include_HEADERS)

View File

@ -49,10 +49,6 @@
#include "pub_core_scheduler.h" // for types 'ThreadArchState'
/* ---------------------------------------------------------------------
Exports of vg_intercept.c
------------------------------------------------------------------ */
/* These are the internal client request codes. The publically-visible
request codes are also defined in valgrind.h, and similar headers for
some tools. */
@ -67,18 +63,6 @@
A synonym for exit. */
#define VG_USERREQ__LIBC_FREERES_DONE 0x3029
/* Intercept prefix stuff. See
coregrind/m_replace_malloc/vg_replace_malloc.c for details. */
#define VG_INTERCEPT(name) _vgi_##name
#define VG_INTERCEPT_PREFIX "_vgi_"
#define VG_INTERCEPT_PREFIX_LEN 5
/* Not sure what these are for. Todo: clarify */
#define VG_WRAPPER_PREFIX "_vgw_"
#define VG_WRAPPER_PREFIX_LEN 5
#define VG_WRAPPER(name) _vgw_##name
#define VG_WRAPPER_ALIAS(name) "_vgw_" #name
/* ---------------------------------------------------------------------
Exports of vg_syscall.S
------------------------------------------------------------------ */

View File

@ -47,7 +47,7 @@
#include <elf.h> /* ELF defns */
static SegInfo* segInfo = NULL;
static SegInfo* segInfo_list = NULL;
/*------------------------------------------------------------*/
/*--- 32/64-bit parameterisation ---*/
@ -89,9 +89,6 @@ static SegInfo* segInfo = NULL;
/*--- ---*/
/*------------------------------------------------------------*/
static Bool
intercept_demangle(const Char*, Char*, Int);
/* Majorly rewritten Sun 3 Feb 02 to enable loading symbols from
dlopen()ed libraries, which is something that KDE3 does a lot.
@ -132,42 +129,16 @@ static void freeSegInfo ( SegInfo* si )
Char *VG_(addStr) ( SegInfo* si, Char* str, Int len )
{
# define EMPTY NULL
# define NN 5
/* prevN[0] has the most recent, prevN[NN-1] the least recent */
static Char *prevN[NN] = { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY };
static SegInfo* curr_si = NULL;
struct strchunk *chunk;
Int i, space_needed;
Int space_needed;
Char* p;
if (len == -1)
len = VG_(strlen)(str);
/* Avoid gratuitous duplication: if we saw 'str' within the last NN,
* within this segment, return that index. Saves about 200KB in glibc,
* extra time taken is too small to measure. --NJN 2002-Aug-30 */
if (curr_si == si) {
for (i = NN-1; i >= 0; i--) {
if (EMPTY != prevN[i]
&& NULL != si->strchunks
&& 0 == VG_(memcmp)(str, prevN[i], len+1)) {
return prevN[i];
}
}
} else {
/* New segment */
curr_si = si;
for (i = 0; i < NN; i++) prevN[i] = EMPTY;
}
/* Shuffle prevous ones along, put new one in. */
for (i = NN-1; i > 0; i--)
prevN[i] = prevN[i-1];
# undef EMPTY
space_needed = 1 + len;
// Allocate a new strtab chunk if necessary
if (si->strchunks == NULL ||
(si->strchunks->strtab_used + space_needed) > STRCHUNKSIZE) {
chunk = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*chunk));
@ -177,16 +148,15 @@ Char *VG_(addStr) ( SegInfo* si, Char* str, Int len )
}
chunk = si->strchunks;
prevN[0] = &chunk->strtab[chunk->strtab_used];
VG_(memcpy)(prevN[0], str, len);
p = &chunk->strtab[chunk->strtab_used];
VG_(memcpy)(p, str, len);
chunk->strtab[chunk->strtab_used+len] = '\0';
chunk->strtab_used += space_needed;
return prevN[0];
return p;
}
/* Add a symbol to the symbol table. */
static __inline__
void addSym ( SegInfo* si, RiSym* sym )
{
@ -532,6 +502,11 @@ static Int compare_RiSym(void *va, void *vb) {
"foo@GLIBC_2.4.2" is considered shorter than "foobar"), but if two
symbols have the same length, the one with the version string is
preferred. If all else fails, use alphabetical ordering.
Very occasionally this goes wrong (eg. 'memcmp' and 'bcmp' are aliases
in glibc, we choose the 'bcmp' symbol because it's shorter, so we
can misdescribe memcmp() as bcmp()). This is hard to avoid. It's
mentioned in the FAQ file.
*/
static RiSym *prefersym(RiSym *a, RiSym *b)
{
@ -550,6 +525,8 @@ static RiSym *prefersym(RiSym *a, RiSym *b)
if (vpb)
vlenb = vpb - b->name;
TRACE_SYMTAB("choosing between '%s' and '%s'\n", a->name, b->name);
/* Select the shortest unversioned name */
if (vlena < vlenb)
return a;
@ -584,24 +561,10 @@ void canonicaliseSymtab ( SegInfo* si )
VG_(ssort)(si->symtab, si->symtab_used, sizeof(*si->symtab), compare_RiSym);
for (i = 0; i < si->symtab_used; i++) {
if(VG_(strncmp)(si->symtab[i].name, VG_INTERCEPT_PREFIX,
VG_INTERCEPT_PREFIX_LEN) == 0) {
int len = VG_(strlen)(si->symtab[i].name);
char *buf = VG_(arena_malloc)(VG_AR_SYMTAB, len), *colon;
intercept_demangle(si->symtab[i].name, buf, len);
colon = buf + VG_(strlen)(buf) - 1;
while(*colon != ':') colon--;
VG_(strncpy_safely)(si->symtab[i].name, colon+1, len);
VG_(arena_free)(VG_AR_SYMTAB, buf);
}
}
cleanup_more:
/* If two symbols have identical address ranges, favour the
one with the longer name (unless the extra length is junk)
*/
/* If two symbols have identical address ranges, we pick one
using prefersym() (see it for details). */
do {
n_merged = 0;
j = si->symtab_used;
@ -784,7 +747,7 @@ static Int compare_RiLoc(void *va, void *vb) {
static
void canonicaliseLoctab ( SegInfo* si )
{
Int i, j;
Int i, j;
# define SWAP(ty,aa,bb) \
do { ty tt = (aa); (aa) = (bb); (bb) = tt; } while (0);
@ -952,133 +915,67 @@ Bool VG_(is_object_file)(const void *buf)
return False;
}
/* Demangle an intercept symbol into library:func form
eg "_vgi_libcZdsoZd6__ZdlPv" --> "libc.so.6:_ZdlPv"
Uses the Z-encoding scheme described in vg_replace_malloc.c.
Returns True if demangle OK, False otherwise.
*/
static Bool
intercept_demangle(const Char* symbol, Char* result, Int nbytes)
static Bool is_interesting_symbol(SegInfo* si, ElfXX_Sym* sym,
Char* sym_name, Addr sym_addr)
{
# define EMIT(ch) \
do { \
if (j >= nbytes) \
result[j-1] = 0; \
else \
result[j++] = ch; \
} while (0)
/* Figure out if we're interested in the symbol.
Firstly, is it of the right flavour? */
if ( ! ( (ELFXX_ST_BIND(sym->st_info) == STB_GLOBAL ||
ELFXX_ST_BIND(sym->st_info) == STB_LOCAL ||
ELFXX_ST_BIND(sym->st_info) == STB_WEAK)
&&
(ELFXX_ST_TYPE(sym->st_info) == STT_FUNC ||
(VG_(needs).data_syms
&& ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT))
)
)
return False;
Bool error = False;
Int i, j = 0;
Int len = VG_(strlen)(symbol);
if (0) VG_(printf)("idm: %s\n", symbol);
i = VG_INTERCEPT_PREFIX_LEN;
/* Chew though the Z-encoded soname part. */
while (True) {
if (i >= len)
break;
if (symbol[i] == '_')
/* We found the underscore following the Z-encoded soname.
Just copy the rest literally. */
break;
if (symbol[i] != 'Z') {
EMIT(symbol[i]);
i++;
continue;
}
/* We've got a Z-escape. Act accordingly. */
i++;
if (i >= len) {
/* Hmm, Z right at the end. Something's wrong. */
error = True;
EMIT('Z');
break;
}
switch (symbol[i]) {
case 'a': EMIT('*'); break;
case 'p': EMIT('+'); break;
case 'c': EMIT(':'); break;
case 'd': EMIT('.'); break;
case 'u': EMIT('_'); break;
case 's': EMIT(' '); break;
case 'Z': EMIT('Z'); break;
default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
}
i++;
/* Secondly, if it's apparently in a GOT or PLT, it's really
a reference to a symbol defined elsewhere, so ignore it. */
if (si->got_start != 0
&& sym_addr >= si->got_start
&& sym_addr < si->got_start + si->got_size) {
TRACE_SYMTAB("ignore -- in GOT: %s\n", sym_name);
return False;
}
if (error || i >= len || symbol[i] != '_') {
/* Something's wrong. Give up. */
VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
EMIT(0);
if (si->plt_start != 0
&& sym_addr >= si->plt_start
&& sym_addr < si->plt_start + si->plt_size) {
TRACE_SYMTAB("ignore -- in PLT: %s\n", sym_name);
return False;
}
/* Copy the rest of the string verbatim. */
i++;
EMIT(':');
while (True) {
if (i >= len)
break;
EMIT(symbol[i]);
i++;
/* Don't bother if nameless, or zero-sized. */
if (sym->st_name == (ElfXX_Word)NULL
|| /* VG_(strlen)(sym_name) == 0 */
/* equivalent but cheaper ... */
sym_name[0] == 0
|| sym->st_size == 0) {
TRACE_SYMTAB("ignore -- size=0: %s\n", sym_name);
return False;
}
EMIT(0);
if (0) VG_(printf)("%s\n", result);
/* This seems to significantly reduce the number of junk
symbols, and particularly reduces the number of
overlapping address ranges. Don't ask me why ... */
if ((Int)sym->st_value == 0) {
TRACE_SYMTAB( "ignore -- valu=0: %s\n", sym_name);
return False;
}
/* If no part of the symbol falls within the mapped range,
ignore it. */
if (sym_addr+sym->st_size <= si->start
|| sym_addr >= si->start+si->size) {
TRACE_SYMTAB( "ignore -- outside mapped range\n" );
return False;
}
// It is an interesting symbol!
return True;
# undef EMIT
}
static
void handle_intercept( SegInfo* si, Char* symbol, ElfXX_Sym* sym)
{
Bool ok;
Int len = VG_(strlen)(symbol) + 1 - VG_INTERCEPT_PREFIX_LEN;
Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
Char *func;
/* Put "soname:" at the start of lib. */
lib[0] = 's';
lib[1] = 'o';
lib[2] = 'n';
lib[3] = 'a';
lib[4] = 'm';
lib[5] = 'e';
lib[6] = ':';
lib[7] = 0;
ok = intercept_demangle(symbol, lib+7, len);
if (ok) {
func = lib + VG_(strlen)(lib)-1;
while(*func != ':') func--;
*func = '\0';
if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func+1);
VG_(add_redirect_sym_to_addr)(lib, func+1, si->offset + sym->st_value);
}
VG_(arena_free)(VG_AR_SYMTAB, lib);
}
//static
//void handle_wrapper( SegInfo* si, Char* symbol, ElfXX_Sym* sym)
//{
// if (VG_(strcmp)(symbol, STR(VG_WRAPPER(freeres))) == 0)
// VGA_(intercept_libc_freeres_wrapper)((Addr)(si->offset + sym->st_value));
// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
//}
/* Read a symbol table (normal or dynamic) */
static
void read_symtab( SegInfo* si, Char* tab_name, Bool do_intercepts,
@ -1087,9 +984,10 @@ void read_symtab( SegInfo* si, Char* tab_name, Bool do_intercepts,
{
Int i;
Addr sym_addr;
Char* sym_name;
RiSym risym;
Char* t0;
Char* name;
ElfXX_Sym* sym;
if (o_strtab == NULL || o_symtab == NULL) {
Char buf[80];
@ -1105,8 +1003,8 @@ void read_symtab( SegInfo* si, Char* tab_name, Bool do_intercepts,
/* Perhaps should start at i = 1; ELF docs suggest that entry
0 always denotes 'unknown symbol'. */
for (i = 1; i < (Int)(o_symtab_sz/sizeof(ElfXX_Sym)); i++) {
ElfXX_Sym* sym = & o_symtab[i];
# if 1
sym = & o_symtab[i];
sym_name = (Char*)(o_strtab + sym->st_name);
sym_addr = si->offset + sym->st_value;
if (VG_(clo_trace_symtab)) {
@ -1132,107 +1030,32 @@ void read_symtab( SegInfo* si, Char* tab_name, Bool do_intercepts,
VG_(printf)(
": value %p, size %d, name %s\n",
sym_addr, sym->st_size,
( sym->st_name
? ((Char*)o_strtab+sym->st_name)
: (Char*)"NONAME" ) );
( sym->st_name ? sym_name : (Char*)"NONAME" ) );
}
# endif
/*
* Is this symbol a magic valgrind-intercept symbol? If so,
* hand this off to the interceptinator.
*/
if (do_intercepts) {
if (VG_(strncmp)((Char*)o_strtab+sym->st_name,
VG_INTERCEPT_PREFIX,
VG_INTERCEPT_PREFIX_LEN) == 0) {
handle_intercept(si, (Char*)o_strtab+sym->st_name, sym);
}
//else if (VG_(strncmp)((Char*)o_strtab+sym->st_name,
// VG_WRAPPER_PREFIX,
// VG_WRAPPER_PREFIX_LEN) == 0) {
// handle_wrapper(si, (Char*)o_strtab+sym->st_name, sym);
//}
// Record interesting symbols in our symtab.
if ( is_interesting_symbol(si, sym, sym_name, sym_addr) ) {
vg_assert(sym->st_name != 0);
vg_assert(sym_name[0] != 0);
name = VG_(addStr) ( si, sym_name, -1 );
vg_assert(name != NULL);
/*
* Is this symbol a magic valgrind-intercept symbol? If so,
* hand this off to the redir module.
*
* Note: this function can change the symbol name just added to
* the string table. Importantly, it never makes it bigger.
*/
if (do_intercepts) {
VG_(maybe_redir_or_notify)( name, sym_addr );
}
risym.addr = sym_addr;
risym.size = sym->st_size;
risym.name = name;
addSym ( si, &risym );
}
/* Figure out if we're interested in the symbol.
Firstly, is it of the right flavour? */
if ( ! ( (ELFXX_ST_BIND(sym->st_info) == STB_GLOBAL ||
ELFXX_ST_BIND(sym->st_info) == STB_LOCAL ||
ELFXX_ST_BIND(sym->st_info) == STB_WEAK)
&&
(ELFXX_ST_TYPE(sym->st_info) == STT_FUNC ||
(VG_(needs).data_syms
&& ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT))
)
)
continue;
/* Secondly, if it's apparently in a GOT or PLT, it's really
a reference to a symbol defined elsewhere, so ignore it. */
if (si->got_start != 0
&& sym_addr >= si->got_start
&& sym_addr < si->got_start + si->got_size) {
TRACE_SYMTAB("in GOT: %s\n", o_strtab+sym->st_name);
continue;
}
if (si->plt_start != 0
&& sym_addr >= si->plt_start
&& sym_addr < si->plt_start + si->plt_size) {
TRACE_SYMTAB("in PLT: %s\n", o_strtab+sym->st_name);
continue;
}
/* Don't bother if nameless, or zero-sized. */
if (sym->st_name == (ElfXX_Word)NULL
|| /* VG_(strlen)(o_strtab+sym->st_name) == 0 */
/* equivalent but cheaper ... */
* ((UChar*)(o_strtab+sym->st_name)) == 0
|| sym->st_size == 0) {
TRACE_SYMTAB("size=0: %s\n", o_strtab+sym->st_name);
continue;
}
# if 0
/* Avoid _dl_ junk. (Why?) */
/* 01-02-24: disabled until I find out if it really helps. */
if (VG_(strncmp)("_dl_", o_strtab+sym->st_name, 4) == 0
|| VG_(strncmp)("_r_debug",
o_strtab+sym->st_name, 8) == 0) {
TRACE_SYMTAB("_dl_ junk: %s\n", o_strtab+sym->st_name);
continue;
}
# endif
/* This seems to significantly reduce the number of junk
symbols, and particularly reduces the number of
overlapping address ranges. Don't ask me why ... */
if ((Int)sym->st_value == 0) {
TRACE_SYMTAB( "valu=0: %s\n", o_strtab+sym->st_name);
continue;
}
/* If no part of the symbol falls within the mapped range,
ignore it. */
if (sym_addr+sym->st_size <= si->start
|| sym_addr >= si->start+si->size) {
TRACE_SYMTAB( "outside mapped range" );
continue;
}
/* If we reach here, it's an interesting symbol; record it. */
t0 = sym->st_name
? (Char*)(o_strtab+sym->st_name)
: (Char*)"NONAME";
name = VG_(addStr) ( si, t0, -1 );
vg_assert(name != NULL
/* && 0==VG_(strcmp)(t0,&vg_strtab[nmoff]) */ );
/* VG_(printf)("%p + %d: %p %s\n", si->start,
(Int)sym->st_value, sym_addr, t0 ); */
risym.addr = sym_addr;
risym.size = sym->st_size;
risym.name = name;
addSym ( si, &risym );
}
}
@ -1840,15 +1663,19 @@ SegInfo *VG_(read_seg_symbols) ( Segment *seg )
segment list, even if it is a bad ELF file. Ironically,
running this under valgrind itself hides the problem, because
it doesn't recycle pointers... */
// [Nb: the prevN optimization has now been removed from addStr().
// However, when I try reactivating this path of the branch I get
// seg faults... --njn 13-Jun-2005]
freeSegInfo( si );
} else {
si->next = segInfo;
segInfo = si;
// Prepend si to segInfo_list
si->next = segInfo_list;
segInfo_list = si;
canonicaliseSymtab ( si );
canonicaliseLoctab ( si );
canonicaliseSymtab ( si );
canonicaliseLoctab ( si );
canonicaliseScopetab ( si );
canonicaliseCfiSI ( si );
canonicaliseCfiSI ( si );
/* do redirects */
VG_(resolve_seg_redirs)( si );
@ -1870,7 +1697,7 @@ static void unload_symbols ( Addr start, SizeT length )
SegInfo *prev, *curr;
prev = NULL;
curr = segInfo;
curr = segInfo_list;
while (True) {
if (curr == NULL) break;
if (start == curr->start) break;
@ -1890,7 +1717,7 @@ static void unload_symbols ( Addr start, SizeT length )
vg_assert(prev == NULL || prev->next == curr);
if (prev == NULL) {
segInfo = curr->next;
segInfo_list = curr->next;
} else {
prev->next = curr->next;
}
@ -2034,7 +1861,7 @@ static void search_all_loctabs ( Addr ptr, /*OUT*/SegInfo** psi,
VGP_PUSHCC(VgpSearchSyms);
for (si = segInfo; si != NULL; si = si->next) {
for (si = segInfo_list; si != NULL; si = si->next) {
if (si->start <= ptr && ptr < si->start+si->size) {
lno = search_one_loctab ( si, ptr );
if (lno == -1) goto not_found;
@ -2087,7 +1914,7 @@ static void search_all_scopetabs ( Addr ptr,
VGP_PUSHCC(VgpSearchSyms);
for (si = segInfo; si != NULL; si = si->next) {
for (si = segInfo_list; si != NULL; si = si->next) {
if (si->start <= ptr && ptr < si->start+si->size) {
scno = search_one_scopetab ( si, ptr );
if (scno == -1) goto not_found;
@ -2215,7 +2042,7 @@ Bool VG_(get_objname) ( Addr a, Char* buf, Int nbuf )
{
SegInfo* si;
for (si = segInfo; si != NULL; si = si->next) {
for (si = segInfo_list; si != NULL; si = si->next) {
if (si->start <= a && a < si->start+si->size) {
VG_(strncpy_safely)(buf, si->filename, nbuf);
return True;
@ -2230,7 +2057,7 @@ SegInfo* VG_(get_obj) ( Addr a )
{
SegInfo* si;
for (si = segInfo; si != NULL; si = si->next) {
for (si = segInfo_list; si != NULL; si = si->next) {
if (si->start <= a && a < si->start+si->size) {
return si;
}
@ -2633,7 +2460,7 @@ Bool VG_(use_CFI_info) ( /*MOD*/Addr* ipP,
if (0) VG_(printf)("search for %p\n", *ipP);
for (si = segInfo; si != NULL; si = si->next) {
for (si = segInfo_list; si != NULL; si = si->next) {
/* Use the per-SegInfo summary address ranges to skip
inapplicable SegInfos quickly. */
if (si->cfisi_used == 0)
@ -2706,7 +2533,7 @@ Bool VG_(use_CFI_info) ( /*MOD*/Addr* ipP,
const SegInfo* VG_(next_seginfo)(const SegInfo* si)
{
if (si == NULL)
return segInfo;
return segInfo_list;
return si->next;
}
@ -2735,7 +2562,7 @@ VgSectKind VG_(seg_sect_kind)(Addr a)
SegInfo* si;
VgSectKind ret = Vg_SectUnknown;
for(si = segInfo; si != NULL; si = si->next) {
for(si = segInfo_list; si != NULL; si = si->next) {
if (a >= si->start && a < (si->start + si->size)) {
if (0)
VG_(printf)("addr=%p si=%p %s got=%p %d plt=%p %d data=%p %d bss=%p %d\n",

View File

@ -202,7 +202,7 @@ void VG_(env_remove_valgrind_env_stuff)(Char** envp)
buf = VG_(arena_malloc)(VG_AR_CORE, VG_(strlen)(VG_(libdir)) + 20);
// Remove Valgrind-specific entries from LD_*.
VG_(sprintf)(buf, "%s*/vg_inject.so", VG_(libdir));
VG_(sprintf)(buf, "%s*/vg_preload_core.so", VG_(libdir));
mash_colon_env(ld_preload_str, buf);
VG_(sprintf)(buf, "%s*/vgpreload_*.so", VG_(libdir));
mash_colon_env(ld_preload_str, buf);

View File

@ -644,7 +644,7 @@ static Bool scan_colsep(char *colsep, Bool (*func)(const char *))
/* Prepare the client's environment. This is basically a copy of our
environment, except:
LD_PRELOAD=$VALGRINDLIB/vg_inject.so:($VALGRINDLIB/vgpreload_TOOL.so:)?$LD_PRELOAD
LD_PRELOAD=$VALGRINDLIB/vg_preload_core.so:($VALGRINDLIB/vgpreload_TOOL.so:)?$LD_PRELOAD
If this is missing, then it is added.
@ -655,32 +655,32 @@ static Bool scan_colsep(char *colsep, Bool (*func)(const char *))
*/
static char **fix_environment(char **origenv, const char *preload)
{
static const char inject_so[] = "vg_inject.so";
static const char preload_core_so[] = "vg_preload_core.so";
static const char ld_preload[] = "LD_PRELOAD=";
static const char valgrind_clo[] = VALGRINDCLO "=";
static const int ld_preload_len = sizeof(ld_preload)-1;
static const int valgrind_clo_len = sizeof(valgrind_clo)-1;
int ld_preload_done = 0;
char *inject_path;
int inject_path_len;
char *preload_core_path;
int preload_core_path_len;
int vgliblen = strlen(VG_(libdir));
char **cpp;
char **ret;
int envc;
const int preloadlen = (preload == NULL) ? 0 : strlen(preload);
/* Find the vg_inject.so; also make room for the tool preload
/* Find the vg_preload_core.so; also make room for the tool preload
library */
inject_path_len = sizeof(inject_so) + vgliblen + preloadlen + 16;
inject_path = malloc(inject_path_len);
vg_assert(inject_path);
preload_core_path_len = sizeof(preload_core_so) + vgliblen + preloadlen + 16;
preload_core_path = malloc(preload_core_path_len);
vg_assert(preload_core_path);
if (preload)
snprintf(inject_path, inject_path_len, "%s/%s:%s",
VG_(libdir), inject_so, preload);
snprintf(preload_core_path, preload_core_path_len, "%s/%s:%s",
VG_(libdir), preload_core_so, preload);
else
snprintf(inject_path, inject_path_len, "%s/%s",
VG_(libdir), inject_so);
snprintf(preload_core_path, preload_core_path_len, "%s/%s",
VG_(libdir), preload_core_so);
/* Count the original size of the env */
envc = 0; /* trailing NULL */
@ -701,12 +701,12 @@ static char **fix_environment(char **origenv, const char *preload)
/* Walk over the new environment, mashing as we go */
for (cpp = ret; cpp && *cpp; cpp++) {
if (memcmp(*cpp, ld_preload, ld_preload_len) == 0) {
int len = strlen(*cpp) + inject_path_len;
int len = strlen(*cpp) + preload_core_path_len;
char *cp = malloc(len);
vg_assert(cp);
snprintf(cp, len, "%s%s:%s",
ld_preload, inject_path, (*cpp)+ld_preload_len);
ld_preload, preload_core_path, (*cpp)+ld_preload_len);
*cpp = cp;
@ -718,17 +718,16 @@ static char **fix_environment(char **origenv, const char *preload)
/* Add the missing bits */
if (!ld_preload_done) {
int len = ld_preload_len + inject_path_len;
int len = ld_preload_len + preload_core_path_len;
char *cp = malloc(len);
vg_assert(cp);
snprintf(cp, len, "%s%s",
ld_preload, inject_path);
snprintf(cp, len, "%s%s", ld_preload, preload_core_path);
ret[envc++] = cp;
}
free(inject_path);
free(preload_core_path);
ret[envc] = NULL;
return ret;
@ -2798,7 +2797,7 @@ int main(int argc, char **argv, char **envp)
called at program exit. */
static Addr __libc_freeres_wrapper;
void VGA_(intercept_libc_freeres_wrapper)(Addr addr)
void VG_(set_libc_freeres_wrapper_addr)(Addr addr)
{
__libc_freeres_wrapper = addr;
}

View File

@ -35,6 +35,7 @@
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
#include "pub_core_main.h"
#include "pub_core_mallocfree.h"
#include "pub_core_options.h"
#include "pub_core_redir.h"
@ -311,26 +312,27 @@ void VG_(resolve_seg_redirs)(SegInfo *si)
}
}
/* Redirect a lib/symbol reference to a function at lib/symbol */
static void add_redirect_sym_to_sym(const Char *from_lib, const Char *from_sym,
const Char *to_lib, const Char *to_sym)
static void add_redirect_X_to_X(
const Char *from_lib, const Char *from_sym, Addr from_addr,
const Char *to_lib, const Char *to_sym, Addr to_addr
)
{
CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
redir->type = R_REDIRECT;
redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
redir->from_addr = 0;
if (from_lib) redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
if (from_sym) redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
redir->from_addr = from_addr;
redir->to_lib = VG_(arena_strdup)(VG_AR_SYMTAB, to_lib);
redir->to_sym = VG_(arena_strdup)(VG_AR_SYMTAB, to_sym);
redir->to_addr = 0;
if (to_lib) redir->to_lib = VG_(arena_strdup)(VG_AR_SYMTAB, to_lib);
if (to_sym) redir->to_sym = VG_(arena_strdup)(VG_AR_SYMTAB, to_sym);
redir->to_addr = to_addr;
if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
VG_(message)(Vg_UserMsg,
"REDIRECT %s(%s) to %s(%s)",
from_lib, from_sym, to_lib, to_sym);
"REDIRECT %s:%s(%p) to %s:%s(%p)",
from_lib, from_sym, from_addr, to_lib, to_sym, to_addr);
/* Check against all existing segments to see if this redirection
can be resolved immediately */
@ -341,57 +343,26 @@ static void add_redirect_sym_to_sym(const Char *from_lib, const Char *from_sym,
}
}
/* Redirect a lib/symbol reference to a function at addr */
void VG_(add_redirect_sym_to_addr)(const Char *from_lib, const Char *from_sym,
Addr to_addr)
/* Redirect a lib/symbol reference to a function at lib/symbol */
__attribute__((unused))
static void add_redirect_sym_to_sym(const Char *from_lib, const Char *from_sym,
const Char *to_lib, const Char *to_sym)
{
CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
add_redirect_X_to_X(from_lib, from_sym, 0, to_lib, to_sym, 0);
}
redir->type = R_REDIRECT;
redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
redir->from_addr = 0;
redir->to_lib = NULL;
redir->to_sym = NULL;
redir->to_addr = to_addr;
if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
VG_(message)(Vg_UserMsg,
"REDIRECT %s(%s) to %p",
from_lib, from_sym, to_addr);
/* Check against all existing segments to see if this redirection
can be resolved immediately */
if (!resolve_redir_allsegs(redir)) {
/* nope, add to list */
redir->next = unresolved_redir;
unresolved_redir = redir;
}
/* Redirect a lib/symbol reference to a function at addr */
static void add_redirect_sym_to_addr(const Char *from_lib, const Char *from_sym,
Addr to_addr)
{
add_redirect_X_to_X(from_lib, from_sym, 0, NULL, NULL, to_addr);
}
/* Redirect a function at from_addr to a function at to_addr */
void VG_(add_redirect_addr_to_addr)(Addr from_addr, Addr to_addr)
__attribute__((unused)) // It is used, but not on all platforms...
static void add_redirect_addr_to_addr(Addr from_addr, Addr to_addr)
{
CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
redir->type = R_REDIRECT;
redir->from_lib = NULL;
redir->from_sym = NULL;
redir->from_addr = from_addr;
redir->to_lib = NULL;
redir->to_sym = NULL;
redir->to_addr = to_addr;
if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
VG_(message)(Vg_UserMsg,
"REDIRECT %p to %p",
from_addr, to_addr);
add_resolved(redir);
add_redirect_X_to_X(NULL, NULL, from_addr, NULL, NULL, to_addr);
}
CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
@ -443,76 +414,182 @@ Addr VG_(code_redirect)(Addr a)
void VG_(setup_code_redirect_table) ( void )
{
/* Overenthusiastic use of PLT bypassing by the glibc people also
means we need to patch the following functions to our own
implementations of said, in mac_replace_strmem.c.
*/
add_redirect_sym_to_sym("soname:libc.so.6", "stpcpy",
"*vgpreload_memcheck.so*", "stpcpy");
add_redirect_sym_to_sym("soname:ld-linux.so.2", "strlen",
"*vgpreload_memcheck.so*", "strlen");
add_redirect_sym_to_sym("soname:libc.so.6", "strlen",
"*vgpreload_memcheck.so*", "strlen");
add_redirect_sym_to_sym("soname:libc.so.6", "strnlen",
"*vgpreload_memcheck.so*", "strnlen");
add_redirect_sym_to_sym("soname:ld-linux.so.2", "stpcpy",
"*vgpreload_memcheck.so*", "stpcpy");
add_redirect_sym_to_sym("soname:libc.so.6", "stpcpy",
"*vgpreload_memcheck.so*", "stpcpy");
add_redirect_sym_to_sym("soname:libc.so.6", "strchr",
"*vgpreload_memcheck.so*", "strchr");
add_redirect_sym_to_sym("soname:ld-linux.so.2", "strchr",
"*vgpreload_memcheck.so*", "strchr");
/* apparently index is the same thing as strchr */
add_redirect_sym_to_sym("soname:ld-linux.so.2", "index",
"*vgpreload_memcheck.so*", "strchr");
add_redirect_sym_to_sym("soname:libc.so.6", "strchrnul",
"*vgpreload_memcheck.so*", "glibc232_strchrnul");
add_redirect_sym_to_sym("soname:libc.so.6", "rawmemchr",
"*vgpreload_memcheck.so*", "glibc232_rawmemchr");
/* amd64-linux (glibc 2.3.3, SuSE 9.2) */
/* apparently index is the same thing as strchr */
add_redirect_sym_to_sym("soname:libc.so.6", "index",
"*vgpreload_memcheck.so*", "strchr");
add_redirect_sym_to_sym("soname:ld-linux-x86-64.so.2", "index",
"*vgpreload_memcheck.so*", "strchr");
add_redirect_sym_to_sym("soname:libc.so.6", "strcpy",
"*vgpreload_memcheck.so*", "strcpy");
add_redirect_sym_to_sym("soname:ld-linux-x86-64.so.2", "strcmp",
"*vgpreload_memcheck.so*", "strcmp");
add_redirect_sym_to_sym("soname:libc.so.6", "strcmp",
"*vgpreload_memcheck.so*", "strcmp");
add_redirect_sym_to_sym("soname:ld-linux-x86-64.so.2", "strlen",
"*vgpreload_memcheck.so*", "strlen");
#if defined(VGP_x86_linux)
/* Redirect _dl_sysinfo_int80, which is glibc's default system call
routine, to the routine in our trampoline page so that the
special sysinfo unwind hack in m_stacktrace.c will kick in. */
VG_(add_redirect_sym_to_addr)("soname:ld-linux.so.2", "_dl_sysinfo_int80",
VG_(client_trampoline_code)+VG_(tramp_syscall_offset));
add_redirect_sym_to_addr("soname:ld-linux.so.2", "_dl_sysinfo_int80",
VG_(client_trampoline_code)+VG_(tramp_syscall_offset));
#elif defined(VGP_amd64_linux)
/* Redirect vsyscalls to local versions */
VG_(add_redirect_addr_to_addr)(0xFFFFFFFFFF600000ULL,
VG_(client_trampoline_code)+VG_(tramp_gettimeofday_offset));
VG_(add_redirect_addr_to_addr)(0xFFFFFFFFFF600400ULL,
VG_(client_trampoline_code)+VG_(tramp_time_offset));
add_redirect_addr_to_addr(0xFFFFFFFFFF600000ULL,
VG_(client_trampoline_code)+VG_(tramp_gettimeofday_offset));
add_redirect_addr_to_addr(0xFFFFFFFFFF600400ULL,
VG_(client_trampoline_code)+VG_(tramp_time_offset));
#else
# error Unknown platform
#endif
}
/* Z-decode a symbol into library:func form, eg
_vgi_libcZdsoZd6__ZdlPv --> libc.so.6:_ZdlPv
Uses the Z-encoding scheme described in pub_core_redir.h.
Returns True if demangle OK, False otherwise.
*/
static Bool Z_decode(const Char* symbol, Char* result, Int nbytes)
{
# define EMIT(ch) \
do { \
if (j >= nbytes) \
result[j-1] = 0; \
else \
result[j++] = ch; \
} while (0)
Bool error = False;
Int i, j = 0;
Int len = VG_(strlen)(symbol);
if (0) VG_(printf)("idm: %s\n", symbol);
i = VG_REPLACE_FUNCTION_PREFIX_LEN;
/* Chew though the Z-encoded soname part. */
while (True) {
if (i >= len)
break;
if (symbol[i] == '_')
/* We found the underscore following the Z-encoded soname.
Just copy the rest literally. */
break;
if (symbol[i] != 'Z') {
EMIT(symbol[i]);
i++;
continue;
}
/* We've got a Z-escape. Act accordingly. */
i++;
if (i >= len) {
/* Hmm, Z right at the end. Something's wrong. */
error = True;
EMIT('Z');
break;
}
switch (symbol[i]) {
case 'a': EMIT('*'); break;
case 'p': EMIT('+'); break;
case 'c': EMIT(':'); break;
case 'd': EMIT('.'); break;
case 'u': EMIT('_'); break;
case 'h': EMIT('-'); break;
case 's': EMIT(' '); break;
case 'Z': EMIT('Z'); break;
default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
}
i++;
}
if (error || i >= len || symbol[i] != '_') {
/* Something's wrong. Give up. */
VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
EMIT(0);
return False;
}
/* Copy the rest of the string verbatim. */
i++;
EMIT(':');
while (True) {
if (i >= len)
break;
EMIT(symbol[i]);
i++;
}
EMIT(0);
if (0) VG_(printf)("%s\n", result);
return True;
# undef EMIT
}
// Nb: this can change the string pointed to by 'symbol'.
static void handle_replacement_function( Char* symbol, Addr addr )
{
Bool ok;
Int len = VG_(strlen)(symbol) + 1 - VG_REPLACE_FUNCTION_PREFIX_LEN;
Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
Char *func;
// Put "soname:" at the start of lib
VG_(strcpy)(lib, "soname:");
ok = Z_decode(symbol, lib+7, len);
if (ok) {
// lib is "soname:<libname>:<fnname>". Split the string at the 2nd ':'.
func = lib + VG_(strlen)(lib)-1;
while(*func != ':') func--;
*func = '\0';
func++; // Move past the '\0'
// Now lib is "soname:<libname>" and func is "<fnname>".
if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func);
add_redirect_sym_to_addr(lib, func, addr);
// Overwrite the given Z-encoded name with just the fnname.
VG_(strcpy)(symbol, func);
}
VG_(arena_free)(VG_AR_SYMTAB, lib);
}
// This is specifically for stringifying VG_(x) function names. We
// need to do two macroexpansions to get the VG_ macro expanded before
// stringifying.
#define _STR(x) #x
#define STR(x) _STR(x)
static void handle_load_notifier( Char* symbol, Addr addr )
{
if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
VG_(set_libc_freeres_wrapper_addr)(addr);
// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
else
vg_assert2(0, "unrecognised load notification function: %s", symbol);
}
static Bool is_replacement_function(Char* s)
{
return (0 == VG_(strncmp)(s,
VG_REPLACE_FUNCTION_PREFIX,
VG_REPLACE_FUNCTION_PREFIX_LEN));
}
static Bool is_load_notifier(Char* s)
{
return (0 == VG_(strncmp)(s,
VG_NOTIFY_ON_LOAD_PREFIX,
VG_NOTIFY_ON_LOAD_PREFIX_LEN));
}
// Call this for each symbol loaded. It determines if we need to do
// anything special with it. It can modify 'symbol' in-place.
void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
{
if (is_replacement_function(symbol))
handle_replacement_function(symbol, addr);
else
if (is_load_notifier(symbol))
handle_load_notifier(symbol, addr);
}
//:: /*------------------------------------------------------------*/
//:: /*--- General function wrapping. ---*/
//:: /*------------------------------------------------------------*/

View File

@ -30,75 +30,28 @@
*/
/* ---------------------------------------------------------------------
All the code in this file runs on the SIMULATED CPU. It is intended
for various reasons as drop-in replacements for malloc() and friends.
These functions have global scope, but are not intended to be called
directly. See the comments in coregrind/vg_intercept.c for the
gory details.
ALL THE CODE IN THIS FILE RUNS ON THE SIMULATED CPU.
These functions are drop-in replacements for malloc() and friends.
They have global scope, but are not intended to be called directly.
See pub_core_redir.h for the gory details.
This file can be linked into the injected so file for any tool that
wishes to know about calls to malloc(). The tool must define all
This file can be linked into the vg_preload_<tool>.so file for any tool
that wishes to know about calls to malloc(). The tool must define all
the functions that will be called via 'info'.
It's called vg_replace_malloc.c because this filename appears in stack
traces, so we want it to be something that should be obvious what it
means to users.
It is called vg_replace_malloc.c because this filename appears in stack
traces, so we want the name to be (hopefully!) meaningful to users.
------------------------------------------------------------------ */
#include "valgrind.h" /* for VALGRIND_NON_SIMD_CALL[12] */
#include "valgrind.h" // for VALGRIND_NON_SIMD_CALL[12]
#include "core.h"
#include "pub_core_debuginfo.h" // needed for pub_core_redir.h :(
#include "pub_core_mallocfree.h" // for VG_MIN_MALLOC_SZB, VG_AR_CLIENT
#include "pub_core_redir.h" // for VG_REPLACE_FUNCTION
#include "pub_core_replacemalloc.h"
/* The general idea is: you can write a function like this:
retty ENCODE(foo, bar) ( ... args ... )
{
... body ...
}
If foo is a Z-encoded soname and bar is an unencoded fn name, then
the core's symbol-table reading machinery and redirection machinery
will conspire to cause calls to the function 'bar' in object with
soname 'foo' to actually be routed to the function written here.
We use this below to define dozens of replacements of malloc, free,
etc.
The soname must be a Z-encoded bit of text because sonames can
contain dots etc which are not valid symbol names. However, it is
better not to Z-encode the function name, because C++ function names
are already mangled, and we don't want our own Z encoding to interact
badly with them; furthermore there's no point, because mangled C++
function names are by definition already valid symbol names.
It is important that the Z-encoded soname contains no unencoded
underscores, since the intercept-handlers in vg_symtab2.c detect
the end of the soname by looking for the first trailing underscore.
*/
/* We use a Z-encoding scheme here, a la GHC. This is just about
readable enough to make a preprocessor unnecessary.
The intercept-me function names are encoded as
_vgi_zEncodedSoname_fnname
where soname is Z-encoded but fnname isn't.
The Z-encoding scheme is as follows:
* --> Za (asterisk)
+ --> Zp
: --> Zc
. --> Zd
_ --> Zu
(space) --> Zs
Z --> ZZ
*/
#define ENCODE(libname,fnname) VG_INTERCEPT(libname##_##fnname)
/* Some handy mangled names */
/* Some handy Z-encoded names */
#define m_libstc_plus_plus_star libstdcZpZpZa // libstdc++*
#define m_libc_dot_so_dot_6 libcZdsoZd6 // libc.so.6
//#define m_libpgc_dot_so libpgcZdso // libpgc.so
@ -167,8 +120,8 @@ internal_printf(char *format, ...)
*/
#define ALLOC_or_NULL(soname, fnname, vg_replacement) \
\
void* ENCODE(soname,fnname) (SizeT n); \
void* ENCODE(soname,fnname) (SizeT n) \
void* VG_REPLACE_FUNCTION(soname,fnname) (SizeT n); \
void* VG_REPLACE_FUNCTION(soname,fnname) (SizeT n) \
{ \
void* v; \
\
@ -187,8 +140,8 @@ internal_printf(char *format, ...)
*/
#define ALLOC_or_BOMB(soname, fnname, vg_replacement) \
\
void* ENCODE(soname,fnname) (SizeT n); \
void* ENCODE(soname,fnname) (SizeT n) \
void* VG_REPLACE_FUNCTION(soname,fnname) (SizeT n); \
void* VG_REPLACE_FUNCTION(soname,fnname) (SizeT n) \
{ \
void* v; \
\
@ -251,7 +204,7 @@ ALLOC_or_BOMB(m_libc_dot_so_dot_6, __builtin_new, __builtin_new);
#endif
// operator new[](unsigned int), , unmangled for some bizarre reason
// operator new[](unsigned int), unmangled for some bizarre reason
ALLOC_or_BOMB(m_libstc_plus_plus_star, __builtin_vec_new, __builtin_vec_new );
ALLOC_or_BOMB(m_libc_dot_so_dot_6, __builtin_vec_new, __builtin_vec_new );
@ -285,8 +238,8 @@ ALLOC_or_BOMB(m_libc_dot_so_dot_6, __builtin_vec_new, __builtin_vec_new );
*/
#define FREE(soname, fnname, vg_replacement) \
\
void ENCODE(soname,fnname) (void *p); \
void ENCODE(soname,fnname) (void *p) \
void VG_REPLACE_FUNCTION(soname,fnname) (void *p); \
void VG_REPLACE_FUNCTION(soname,fnname) (void *p) \
{ \
MALLOC_TRACE(#vg_replacement "(%p)", p ); \
if (p == NULL) \
@ -328,8 +281,8 @@ FREE(m_libc_dot_so_dot_6, _ZdaPvRKSt9nothrow_t, __builtin_vec_delete );
#define CALLOC(soname, fnname) \
\
void* ENCODE(soname,fnname) ( SizeT nmemb, SizeT size ); \
void* ENCODE(soname,fnname) ( SizeT nmemb, SizeT size ) \
void* VG_REPLACE_FUNCTION(soname,fnname) ( SizeT nmemb, SizeT size ); \
void* VG_REPLACE_FUNCTION(soname,fnname) ( SizeT nmemb, SizeT size ) \
{ \
void* v; \
\
@ -346,8 +299,8 @@ CALLOC(m_libc_dot_so_dot_6, calloc);
#define REALLOC(soname, fnname) \
\
void* ENCODE(soname,fnname) ( void* ptrV, SizeT new_size ); \
void* ENCODE(soname,fnname) ( void* ptrV, SizeT new_size ) \
void* VG_REPLACE_FUNCTION(soname,fnname) ( void* ptrV, SizeT new_size );\
void* VG_REPLACE_FUNCTION(soname,fnname) ( void* ptrV, SizeT new_size ) \
{ \
void* v; \
\
@ -356,9 +309,9 @@ CALLOC(m_libc_dot_so_dot_6, calloc);
if (ptrV == NULL) \
/* We need to call a malloc-like function; so let's use \
one which we know exists. */ \
return ENCODE(libcZdsoZd6,malloc) (new_size); \
return VG_REPLACE_FUNCTION(libcZdsoZd6,malloc) (new_size); \
if (new_size <= 0) { \
ENCODE(libcZdsoZd6,free)(ptrV); \
VG_REPLACE_FUNCTION(libcZdsoZd6,free)(ptrV); \
if (info.clo_trace_malloc) \
internal_printf(" = 0" ); \
return NULL; \
@ -374,8 +327,8 @@ REALLOC(m_libc_dot_so_dot_6, realloc);
#define MEMALIGN(soname, fnname) \
\
void* ENCODE(soname,fnname) ( SizeT alignment, SizeT n ); \
void* ENCODE(soname,fnname) ( SizeT alignment, SizeT n ) \
void* VG_REPLACE_FUNCTION(soname,fnname) ( SizeT alignment, SizeT n ); \
void* VG_REPLACE_FUNCTION(soname,fnname) ( SizeT alignment, SizeT n ) \
{ \
void* v; \
\
@ -400,10 +353,10 @@ MEMALIGN(m_libc_dot_so_dot_6, memalign);
#define VALLOC(soname, fnname) \
\
void* ENCODE(soname,fnname) ( SizeT size ); \
void* ENCODE(soname,fnname) ( SizeT size ) \
void* VG_REPLACE_FUNCTION(soname,fnname) ( SizeT size ); \
void* VG_REPLACE_FUNCTION(soname,fnname) ( SizeT size ) \
{ \
return ENCODE(libcZdsoZd6,memalign)(VKI_PAGE_SIZE, size); \
return VG_REPLACE_FUNCTION(libcZdsoZd6,memalign)(VKI_PAGE_SIZE, size); \
}
VALLOC(m_libc_dot_so_dot_6, valloc);
@ -413,8 +366,8 @@ VALLOC(m_libc_dot_so_dot_6, valloc);
#define MALLOPT(soname, fnname) \
\
int ENCODE(soname, fnname) ( int cmd, int value ); \
int ENCODE(soname, fnname) ( int cmd, int value ) \
int VG_REPLACE_FUNCTION(soname, fnname) ( int cmd, int value ); \
int VG_REPLACE_FUNCTION(soname, fnname) ( int cmd, int value ) \
{ \
/* In glibc-2.2.4, 1 denotes a successful return value for \
mallopt */ \
@ -426,8 +379,8 @@ MALLOPT(m_libc_dot_so_dot_6, mallopt);
#define POSIX_MEMALIGN(soname, fnname) \
\
int ENCODE(soname, fnname) ( void **memptr, SizeT alignment, SizeT size ); \
int ENCODE(soname, fnname) ( void **memptr, SizeT alignment, SizeT size ) \
int VG_REPLACE_FUNCTION(soname, fnname) ( void **memptr, SizeT alignment, SizeT size ); \
int VG_REPLACE_FUNCTION(soname, fnname) ( void **memptr, SizeT alignment, SizeT size ) \
{ \
void *mem; \
\
@ -437,7 +390,7 @@ MALLOPT(m_libc_dot_so_dot_6, mallopt);
|| (alignment & (alignment - 1)) != 0) \
return VKI_EINVAL; \
\
mem = ENCODE(libcZdsoZd6,memalign)(alignment, size); \
mem = VG_REPLACE_FUNCTION(libcZdsoZd6,memalign)(alignment, size); \
\
if (mem != NULL) { \
*memptr = mem; \
@ -452,8 +405,8 @@ POSIX_MEMALIGN(m_libc_dot_so_dot_6, posix_memalign);
#define MALLOC_USABLE_SIZE(soname, fnname) \
\
int ENCODE(soname, fnname) ( void* p ); \
int ENCODE(soname, fnname) ( void* p ) \
int VG_REPLACE_FUNCTION(soname, fnname) ( void* p ); \
int VG_REPLACE_FUNCTION(soname, fnname) ( void* p ) \
{ \
SizeT pszB; \
\
@ -483,8 +436,8 @@ static void panic(const char *str)
#define PANIC(soname, fnname) \
\
void ENCODE(soname, fnname) ( void ); \
void ENCODE(soname, fnname) ( void ) \
void VG_REPLACE_FUNCTION(soname, fnname) ( void ); \
void VG_REPLACE_FUNCTION(soname, fnname) ( void ) \
{ \
panic(#fnname); \
}
@ -516,8 +469,8 @@ struct mallinfo {
#define MALLINFO(soname, fnname) \
\
struct mallinfo ENCODE(soname, fnname) ( void ); \
struct mallinfo ENCODE(soname, fnname) ( void ) \
struct mallinfo VG_REPLACE_FUNCTION(soname, fnname) ( void ); \
struct mallinfo VG_REPLACE_FUNCTION(soname, fnname) ( void ) \
{ \
/* Should really try to return something a bit more meaningful */ \
UInt i; \

View File

@ -2320,8 +2320,8 @@ PRE(sys_execve)
VG_(reap_threads)(tid);
{ // Remove the valgrind-specific stuff from the environment so the
// child doesn't get vg_inject.so, vgpreload.so, etc. This is
// done unconditionally, since if we are tracing the child,
// child doesn't get vg_preload_core.so, vg_preload_TOOL.so, etc.
// This is done unconditionally, since if we are tracing the child,
// stage1/2 will set up the appropriate client environment.
Char** envp = (Char**)ARG3;
if (envp != NULL) {

View File

@ -54,8 +54,8 @@ extern void VG_(start_debugger) ( ThreadId tid );
/* 64-bit counter for the number of basic blocks done. */
extern ULong VG_(bbs_done);
// Set up the libc freeres wrapper (XXX: currently unused -- ?!)
extern void VGA_(intercept_libc_freeres_wrapper)(Addr);
// Set up the libc freeres wrapper
extern void VG_(set_libc_freeres_wrapper_addr)(Addr);
#endif // __PUB_CORE_MAIN_H

View File

@ -33,26 +33,69 @@
//--------------------------------------------------------------------
// PURPOSE: This module deals with:
// - code redirection: intercepting calls to client functions, and
// - code replacement: intercepting calls to client functions, and
// pointing them to a different piece of code.
// - loading notification: telling the core where certain client-space
// functions are when they get loaded.
// - function wrapping: add calls to code before and after client
// functions execute, for inspection and/or modification.
//
// Nb: It's possible that this should be two modules.
// It's possible that this should be two or three modules.
//--------------------------------------------------------------------
/* Redirection machinery */
#include "pub_tool_redir.h"
//--------------------------------------------------------------------
// General
//--------------------------------------------------------------------
// This module needs be told about all the symbols that get loaded, so
// it can check if it needs to do anything special. This is the function
// that does that checking. It modifies 'symbol' in-place by Z-decoding
// it if necessary.
void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr );
//--------------------------------------------------------------------
// Code replacement
//--------------------------------------------------------------------
// See include/pub_tool_redir.h for details on how to do code replacement.
// This is the crucial redirection function. It answers the question:
// should this code address be redirected somewhere else? It's used just
// before translating a basic block.
extern Addr VG_(code_redirect) ( Addr orig );
/* Set up some default redirects */
extern void VG_(setup_code_redirect_table) ( void );
extern void VG_(add_redirect_sym_to_addr)(const Char *from_lib,
const Char *from_sym,
Addr to_addr);
extern void VG_(add_redirect_addr_to_addr)(Addr from_addr, Addr to_addr);
extern void VG_(resolve_seg_redirs)(SegInfo *si);
//--------------------------------------------------------------------
// Loading notification
//--------------------------------------------------------------------
/* Functions named with this macro have the property that the core will
be told what their address is when they are loaded. This can be useful
if the core wants to call them at some point, and so needs to know their
address. This is a weaker but more general mechanism than code
replacement.
Functions named with this macro should be in client space, ie. in
vg_preload_<tool>.h or vg_preload_core.h. */
#define VG_NOTIFY_ON_LOAD(name) _vgw_##name
#define VG_NOTIFY_ON_LOAD_PREFIX "_vgw_"
#define VG_NOTIFY_ON_LOAD_PREFIX_LEN 5
//--------------------------------------------------------------------
// Function wrapping
//--------------------------------------------------------------------
// This is currently not working(?) --njn
/* Wrapping machinery */
enum return_type {
RT_RETURN,

View File

@ -1,5 +1,6 @@
/*--------------------------------------------------------------------*/
/*--- Various things we want to wrap. vg_intercept.c ---*/
/*--- Client-space code for the core. vg_preloaded.c ---*/
/*--------------------------------------------------------------------*/
/*
@ -29,23 +30,29 @@
/* ---------------------------------------------------------------------
ALL THE CODE IN THIS FILE RUNS ON THE SIMULATED CPU. It is
intended for various reasons as drop-in replacements for libc
functions. These functions are not called directly - they're the
targets of code redirection. They're named weirdly so that the
intercept code can find them when the shared object is initially
loaded.
ALL THE CODE IN THIS FILE RUNS ON THE SIMULATED CPU.
These functions are not called directly - they're the targets of code
redirection or load notifications (see pub_core_redir.h for info).
They're named weirdly so that the intercept code can find them when the
shared object is initially loaded.
Note that this filename has the "vg_" prefix because it can appear
in stack traces, and the "vg_" makes it a little clearer that it
originates from Valgrind.
------------------------------------------------------------------ */
#include "valgrind.h"
#include "core.h"
#include "pub_core_debuginfo.h" // needed for pub_core_redir.h :(
#include "pub_core_redir.h"
/* ---------------------------------------------------------------------
Hook for running __libc_freeres once the program exits.
------------------------------------------------------------------ */
void VG_WRAPPER(freeres)( void );
void VG_WRAPPER(freeres)( void )
void VG_NOTIFY_ON_LOAD(freeres)( void );
void VG_NOTIFY_ON_LOAD(freeres)( void )
{
int res;
#ifndef __UCLIBC__
@ -59,6 +66,6 @@ void VG_WRAPPER(freeres)( void )
}
/*--------------------------------------------------------------------*/
/*--- end vg_intercept.c ---*/
/*--- end ---*/
/*--------------------------------------------------------------------*/

View File

@ -342,6 +342,22 @@ Invalid write of size 1
</answer>
</qandaentry>
<qandaentry id="faq.aliases">
<question>
<para>The stack traces given by Memcheck (or another tool) seem to
have the wrong function name in them. What's happening?</para>
</question>
<answer>
<para>Occasionally Valgrind stack traces get the wrong function names.
This is caused by glibc using aliases to effectively give one function two
names. Most of the time Valgrind chooses a suitable name, but very
occasionally it gets it wrong.
Examples we know of are printing 'bcmp' instead of 'memcmp', 'index'
instead of 'strchr', and 'rindex' instead of 'strrchr'.</para>
</answer>
</qandaentry>
</qandaset>
</chapter>

View File

@ -20,6 +20,7 @@ incinc_HEADERS = \
pub_tool_mallocfree.h \
pub_tool_options.h \
pub_tool_profile.h \
pub_tool_redir.h \
pub_tool_replacemalloc.h \
pub_tool_skiplist.h \
pub_tool_stacktrace.h \

97
include/pub_tool_redir.h Normal file
View File

@ -0,0 +1,97 @@
/*--------------------------------------------------------------------*/
/*--- Redirections, etc. pub_tool_redir.h ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2000-2005 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 __PUB_TOOL_REDIR
#define __PUB_TOOL_REDIR
/* The following macros facilitate function replacement, which is one form
of code replacement.
The general idea is: you can write a function like this:
ret_type VG_REPLACE_FUNCTION(zEncodedSoname, fnname) ( ... args ... )
{
... body ...
}
zEncodedSoname should be a Z-encoded soname (see below for Z-encoding
details) and fnname should be an unencoded fn name. The resulting name is
_vgi_zEncodedSoname_fnname
The "_vgi_" is a prefix that gets discarded upon decoding.
When it sees this name, the core's symbol-table reading machinery
and redirection machinery will conspire to cause calls to the function
'fnname' in object with soname 'zEncodedSoname' to actually be routed to
the function written here. We use this below to define dozens of
replacements of malloc, free, etc.
The soname must be a Z-encoded bit of text because sonames can
contain dots etc which are not valid symbol names. But don't Z-encode
the function name, since it will already be a valid symbol name, and the
Z-encoding might screw up the C++ demangling.
Note that the soname can contain '*' as a wildcard meaning "match
anything".
Note also that the replacement function should probably (must be?) in
client space, so it runs on the simulated CPU. So it must be in
either vg_preload_<tool>.so or vg_preload_core.so.
It is important that the Z-encoded soname contains no unencoded
underscores, since the intercept-handlers in vg_symtab2.c detect
the end of the soname by looking for the first trailing underscore.
Z-encoding details: the scheme is like GHC's. It is just about
readable enough to make a preprocessor unnecessary. First the "_vgi_"
prefix is added, and then the following characters are transformed.
* --> Za ('a' for "asterisk")
+ --> Zp
: --> Zc
. --> Zd
_ --> Zu
- --> Zh ('h' for "hyphen")
(space) --> Zs
Z --> ZZ
Everything else is left unchanged.
*/
#define VG_REPLACE_FUNCTION(soname, fnname) _vgi_##soname##_##fnname
#define VG_REPLACE_FUNCTION_PREFIX "_vgi_"
#define VG_REPLACE_FUNCTION_PREFIX_LEN 5
#endif // __PUB_TOOL_REDIR
/*--------------------------------------------------------------------*/
/*--- end ---*/
/*--------------------------------------------------------------------*/

View File

@ -33,6 +33,7 @@
#include "mc_include.h"
#include "memcheck.h"
#include "valgrind.h"
#include "pub_tool_redir.h"
/* ---------------------------------------------------------------------
We have our own versions of these functions for two reasons:
@ -41,6 +42,10 @@
Memcheck and cause spurious value warnings. Our versions are
simpler.
Note that overenthusiastic use of PLT bypassing by the glibc people also
means that we need to patch multiple versions of some of the functions to
our own implementations.
THEY RUN ON THE SIMD CPU!
------------------------------------------------------------------ */
@ -107,304 +112,400 @@ void complain3 ( Char* s, void* dst, const void* src, int n )
RECORD_OVERLAP_ERROR( s, &extra );
}
// GCC complains if we have a non-static function without a previous
// declaration. We don't really need such declarations for these functions
// because we're overriding standard system functions, but this macro puts
// them in anyway just to shut GCC up.
#define DECL(a) a; a
// Some handy Z-encoded names
#define m_libc_so_6 libcZdsoZd6 // libc.so.6
#define m_ld_linux_so_2 ldZhlinuxZdsoZd2 // ld-linux.so.2
#define m_ld_linux_x86_64_so_2 ldZhlinuxZhx86Zh64ZdsoZd2 // ld-linux-x86-64.so.2
DECL( char* strrchr ( const char* s, int c ) )
{
UChar ch = (UChar)((UInt)c);
UChar* p = (UChar*)s;
UChar* last = NULL;
while (True) {
if (*p == ch) last = p;
if (*p == 0) return last;
p++;
#define STRRCHR(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname,fnname)( const char* s, int c ); \
char* VG_REPLACE_FUNCTION(soname,fnname)( const char* s, int c ) \
{ \
UChar ch = (UChar)((UInt)c); \
UChar* p = (UChar*)s; \
UChar* last = NULL; \
while (True) { \
if (*p == ch) last = p; \
if (*p == 0) return last; \
p++; \
} \
}
}
DECL( char* strchr ( const char* s, int c ) )
{
UChar ch = (UChar)((UInt)c);
UChar* p = (UChar*)s;
while (True) {
if (*p == ch) return p;
if (*p == 0) return NULL;
p++;
// Apparently rindex() is the same thing as strrchr()
STRRCHR(m_libc_so_6, strrchr)
STRRCHR(m_libc_so_6, rindex)
STRRCHR(m_ld_linux_so_2, rindex)
#define STRCHR(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname,fnname) ( const char* s, int c ); \
char* VG_REPLACE_FUNCTION(soname,fnname) ( const char* s, int c ) \
{ \
UChar ch = (UChar)((UInt)c); \
UChar* p = (UChar*)s; \
while (True) { \
if (*p == ch) return p; \
if (*p == 0) return NULL; \
p++; \
} \
}
}
DECL( char* strcat ( char* dst, const char* src ) )
{
const Char* src_orig = src;
Char* dst_orig = dst;
while (*dst) dst++;
while (*src) *dst++ = *src++;
*dst = 0;
// Apparently index() is the same thing as strchr()
STRCHR(m_libc_so_6, strchr)
STRCHR(m_ld_linux_so_2, strchr)
STRCHR(m_libc_so_6, index)
STRCHR(m_ld_linux_so_2, index)
STRCHR(m_ld_linux_x86_64_so_2, index)
/* This is a bit redundant, I think; any overlap and the strcat will
go forever... or until a seg fault occurs. */
if (is_overlap(dst_orig,
src_orig,
(Addr)dst-(Addr)dst_orig+1,
(Addr)src-(Addr)src_orig+1))
complain2("strcat", dst_orig, src_orig);
return dst_orig;
}
DECL( char* strncat ( char* dst, const char* src, SizeT n ) )
{
const Char* src_orig = src;
Char* dst_orig = dst;
SizeT m = 0;
while (*dst) dst++;
while (m < n && *src) { m++; *dst++ = *src++; } /* concat <= n chars */
*dst = 0; /* always add null */
/* This checks for overlap after copying, unavoidable without
pre-counting lengths... should be ok */
if (is_overlap(dst_orig,
src_orig,
(Addr)dst-(Addr)dst_orig+1,
(Addr)src-(Addr)src_orig+1))
complain3("strncat", dst_orig, src_orig, n);
return dst_orig;
}
DECL( SizeT strnlen ( const char* str, SizeT n ) )
{
SizeT i = 0;
while (i < n && str[i] != 0) i++;
return i;
}
DECL( SizeT strlen ( const char* str ) )
{
SizeT i = 0;
while (str[i] != 0) i++;
return i;
}
DECL( char* strcpy ( char* dst, const char* src ) )
{
const Char* src_orig = src;
Char* dst_orig = dst;
while (*src) *dst++ = *src++;
*dst = 0;
/* This checks for overlap after copying, unavoidable without
pre-counting length... should be ok */
if (is_overlap(dst_orig,
src_orig,
(Addr)dst-(Addr)dst_orig+1,
(Addr)src-(Addr)src_orig+1))
complain2("strcpy", dst_orig, src_orig);
return dst_orig;
}
DECL( char* strncpy ( char* dst, const char* src, SizeT n ) )
{
const Char* src_orig = src;
Char* dst_orig = dst;
SizeT m = 0;
while (m < n && *src) { m++; *dst++ = *src++; }
/* Check for overlap after copying; all n bytes of dst are relevant,
but only m+1 bytes of src if terminator was found */
if (is_overlap(dst_orig, src_orig, n, (m < n) ? m+1 : n))
complain3("strncpy", dst, src, n);
while (m++ < n) *dst++ = 0; /* must pad remainder with nulls */
return dst_orig;
}
DECL( int strncmp ( const char* s1, const char* s2, SizeT nmax ) )
{
SizeT n = 0;
while (True) {
if (n >= nmax) return 0;
if (*s1 == 0 && *s2 == 0) return 0;
if (*s1 == 0) return -1;
if (*s2 == 0) return 1;
if (*(unsigned char*)s1 < *(unsigned char*)s2) return -1;
if (*(unsigned char*)s1 > *(unsigned char*)s2) return 1;
s1++; s2++; n++;
#define STRCAT(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname,fnname) ( char* dst, const char* src ); \
char* VG_REPLACE_FUNCTION(soname,fnname) ( char* dst, const char* src ) \
{ \
const Char* src_orig = src; \
Char* dst_orig = dst; \
while (*dst) dst++; \
while (*src) *dst++ = *src++; \
*dst = 0; \
\
/* This is a bit redundant, I think; any overlap and the strcat will */ \
/* go forever... or until a seg fault occurs. */ \
if (is_overlap(dst_orig, \
src_orig, \
(Addr)dst-(Addr)dst_orig+1, \
(Addr)src-(Addr)src_orig+1)) \
complain2("strcat", dst_orig, src_orig); \
\
return dst_orig; \
}
}
DECL( int strcmp ( const char* s1, const char* s2 ) )
{
register unsigned char c1;
register unsigned char c2;
while (True) {
c1 = *(unsigned char *)s1;
c2 = *(unsigned char *)s2;
if (c1 != c2) break;
if (c1 == 0) break;
s1++; s2++;
STRCAT(m_libc_so_6, strcat)
#define STRNCAT(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname,fnname) ( char* dst, const char* src, SizeT n ); \
char* VG_REPLACE_FUNCTION(soname,fnname) ( char* dst, const char* src, SizeT n ) \
{ \
const Char* src_orig = src; \
Char* dst_orig = dst; \
SizeT m = 0; \
\
while (*dst) dst++; \
while (m < n && *src) { m++; *dst++ = *src++; } /* concat <= n chars */ \
*dst = 0; /* always add null */ \
\
/* This checks for overlap after copying, unavoidable without */ \
/* pre-counting lengths... should be ok */ \
if (is_overlap(dst_orig, \
src_orig, \
(Addr)dst-(Addr)dst_orig+1, \
(Addr)src-(Addr)src_orig+1)) \
complain3("strncat", dst_orig, src_orig, n); \
\
return dst_orig; \
}
if ((unsigned char)c1 < (unsigned char)c2) return -1;
if ((unsigned char)c1 > (unsigned char)c2) return 1;
return 0;
}
DECL( void* memchr(const void *s, int c, SizeT n) )
{
SizeT i;
UChar c0 = (UChar)c;
UChar* p = (UChar*)s;
for (i = 0; i < n; i++)
if (p[i] == c0) return (void*)(&p[i]);
return NULL;
}
STRNCAT(m_libc_so_6, strncat)
DECL( void* memcpy( void *dst, const void *src, SizeT len ) )
{
register char *d;
register char *s;
if (len == 0)
return dst;
if (is_overlap(dst, src, len, len))
complain3("memcpy", dst, src, len);
if ( dst > src ) {
d = (char *)dst + len - 1;
s = (char *)src + len - 1;
while ( len >= 4 ) {
*d-- = *s--;
*d-- = *s--;
*d-- = *s--;
*d-- = *s--;
len -= 4;
}
while ( len-- ) {
*d-- = *s--;
}
} else if ( dst < src ) {
d = (char *)dst;
s = (char *)src;
while ( len >= 4 ) {
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
len -= 4;
}
while ( len-- ) {
*d++ = *s++;
}
#define STRNLEN(soname, fnname) \
SizeT VG_REPLACE_FUNCTION(soname,fnname) ( const char* str, SizeT n ); \
SizeT VG_REPLACE_FUNCTION(soname,fnname) ( const char* str, SizeT n ) \
{ \
SizeT i = 0; \
while (i < n && str[i] != 0) i++; \
return i; \
}
return dst;
}
DECL( int memcmp ( const void *s1V, const void *s2V, SizeT n ) )
{
int res;
unsigned char a0;
unsigned char b0;
unsigned char* s1 = (unsigned char*)s1V;
unsigned char* s2 = (unsigned char*)s2V;
STRNLEN(m_libc_so_6, strnlen)
while (n != 0) {
a0 = s1[0];
b0 = s2[0];
s1 += 1;
s2 += 1;
res = ((int)a0) - ((int)b0);
if (res != 0)
return res;
n -= 1;
#define STRLEN(soname, fnname) \
SizeT VG_REPLACE_FUNCTION(soname,fnname)( const char* str ); \
SizeT VG_REPLACE_FUNCTION(soname,fnname)( const char* str ) \
{ \
SizeT i = 0; \
while (str[i] != 0) i++; \
return i; \
}
return 0;
}
STRLEN(m_libc_so_6, strlen)
STRLEN(m_ld_linux_so_2, strlen)
STRLEN(m_ld_linux_x86_64_so_2, strlen)
#define STRCPY(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname, fnname) ( char* dst, const char* src ); \
char* VG_REPLACE_FUNCTION(soname, fnname) ( char* dst, const char* src ) \
{ \
const Char* src_orig = src; \
Char* dst_orig = dst; \
\
while (*src) *dst++ = *src++; \
*dst = 0; \
\
/* This checks for overlap after copying, unavoidable without */ \
/* pre-counting length... should be ok */ \
if (is_overlap(dst_orig, \
src_orig, \
(Addr)dst-(Addr)dst_orig+1, \
(Addr)src-(Addr)src_orig+1)) \
complain2("strcpy", dst_orig, src_orig); \
\
return dst_orig; \
}
STRCPY(m_libc_so_6, strcpy)
#define STRNCPY(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname, fnname) ( char* dst, const char* src, SizeT n ); \
char* VG_REPLACE_FUNCTION(soname, fnname) ( char* dst, const char* src, SizeT n ) \
{ \
const Char* src_orig = src; \
Char* dst_orig = dst; \
SizeT m = 0; \
\
while (m < n && *src) { m++; *dst++ = *src++; } \
/* Check for overlap after copying; all n bytes of dst are relevant, */ \
/* but only m+1 bytes of src if terminator was found */ \
if (is_overlap(dst_orig, src_orig, n, (m < n) ? m+1 : n)) \
complain3("strncpy", dst, src, n); \
while (m++ < n) *dst++ = 0; /* must pad remainder with nulls */ \
\
return dst_orig; \
}
STRNCPY(m_libc_so_6, strncpy)
#define STRNCMP(soname, fnname) \
int VG_REPLACE_FUNCTION(soname,fnname) ( const char* s1, const char* s2, SizeT nmax ); \
int VG_REPLACE_FUNCTION(soname,fnname) ( const char* s1, const char* s2, SizeT nmax ) \
{ \
SizeT n = 0; \
while (True) { \
if (n >= nmax) return 0; \
if (*s1 == 0 && *s2 == 0) return 0; \
if (*s1 == 0) return -1; \
if (*s2 == 0) return 1; \
\
if (*(unsigned char*)s1 < *(unsigned char*)s2) return -1; \
if (*(unsigned char*)s1 > *(unsigned char*)s2) return 1; \
\
s1++; s2++; n++; \
} \
}
STRNCMP(m_libc_so_6, strncmp)
#define STRCMP(soname, fnname) \
int VG_REPLACE_FUNCTION(soname,fnname) ( const char* s1, const char* s2 ); \
int VG_REPLACE_FUNCTION(soname,fnname) ( const char* s1, const char* s2 ) \
{ \
register unsigned char c1; \
register unsigned char c2; \
while (True) { \
c1 = *(unsigned char *)s1; \
c2 = *(unsigned char *)s2; \
if (c1 != c2) break; \
if (c1 == 0) break; \
s1++; s2++; \
} \
if ((unsigned char)c1 < (unsigned char)c2) return -1; \
if ((unsigned char)c1 > (unsigned char)c2) return 1; \
return 0; \
}
STRCMP(m_libc_so_6, strcmp)
STRCMP(m_ld_linux_x86_64_so_2, strcmp)
#define MEMCHR(soname, fnname) \
void* VG_REPLACE_FUNCTION(soname,fnname) (const void *s, int c, SizeT n); \
void* VG_REPLACE_FUNCTION(soname,fnname) (const void *s, int c, SizeT n) \
{ \
SizeT i; \
UChar c0 = (UChar)c; \
UChar* p = (UChar*)s; \
for (i = 0; i < n; i++) \
if (p[i] == c0) return (void*)(&p[i]); \
return NULL; \
}
MEMCHR(m_libc_so_6, memchr)
#define MEMCPY(soname, fnname) \
void* VG_REPLACE_FUNCTION(soname,fnname)( void *dst, const void *src, SizeT len ); \
void* VG_REPLACE_FUNCTION(soname,fnname)( void *dst, const void *src, SizeT len ) \
{ \
register char *d; \
register char *s; \
\
if (len == 0) \
return dst; \
\
if (is_overlap(dst, src, len, len)) \
complain3("memcpy", dst, src, len); \
\
if ( dst > src ) { \
d = (char *)dst + len - 1; \
s = (char *)src + len - 1; \
while ( len >= 4 ) { \
*d-- = *s--; \
*d-- = *s--; \
*d-- = *s--; \
*d-- = *s--; \
len -= 4; \
} \
while ( len-- ) { \
*d-- = *s--; \
} \
} else if ( dst < src ) { \
d = (char *)dst; \
s = (char *)src; \
while ( len >= 4 ) { \
*d++ = *s++; \
*d++ = *s++; \
*d++ = *s++; \
*d++ = *s++; \
len -= 4; \
} \
while ( len-- ) { \
*d++ = *s++; \
} \
} \
return dst; \
}
MEMCPY(m_libc_so_6, memcpy)
#define MEMCMP(soname, fnname) \
int VG_REPLACE_FUNCTION(soname,fnname)( const void *s1V, const void *s2V, SizeT n ); \
int VG_REPLACE_FUNCTION(soname,fnname)( const void *s1V, const void *s2V, SizeT n ) \
{ \
int res; \
unsigned char a0; \
unsigned char b0; \
unsigned char* s1 = (unsigned char*)s1V; \
unsigned char* s2 = (unsigned char*)s2V; \
\
while (n != 0) { \
a0 = s1[0]; \
b0 = s2[0]; \
s1 += 1; \
s2 += 1; \
res = ((int)a0) - ((int)b0); \
if (res != 0) \
return res; \
n -= 1; \
} \
return 0; \
}
MEMCMP(m_libc_so_6, memcmp)
MEMCMP(m_libc_so_6, bcmp)
/* Copy SRC to DEST, returning the address of the terminating '\0' in
DEST. (minor variant of strcpy) */
DECL( char* stpcpy ( char* dst, const char* src ) )
{
const Char* src_orig = src;
Char* dst_orig = dst;
while (*src) *dst++ = *src++;
*dst = 0;
/* This checks for overlap after copying, unavoidable without
pre-counting length... should be ok */
if (is_overlap(dst_orig,
src_orig,
(Addr)dst-(Addr)dst_orig+1,
(Addr)src-(Addr)src_orig+1))
complain2("stpcpy", dst_orig, src_orig);
return dst;
}
DECL( void *memset(void *s, Int c, SizeT n) )
{
unsigned char *cp = s;
while(n--)
*cp++ = c;
return s;
}
DECL( void *memmove(void *dstV, const void *srcV, SizeT n) )
{
SizeT i;
Char* dst = (Char*)dstV;
Char* src = (Char*)srcV;
if (dst < src) {
for (i = 0; i < n; i++)
dst[i] = src[i];
#define STPCPY(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname,fnname) ( char* dst, const char* src ); \
char* VG_REPLACE_FUNCTION(soname,fnname) ( char* dst, const char* src ) \
{ \
const Char* src_orig = src; \
Char* dst_orig = dst; \
\
while (*src) *dst++ = *src++; \
*dst = 0; \
\
/* This checks for overlap after copying, unavoidable without */ \
/* pre-counting length... should be ok */ \
if (is_overlap(dst_orig, \
src_orig, \
(Addr)dst-(Addr)dst_orig+1, \
(Addr)src-(Addr)src_orig+1)) \
complain2("stpcpy", dst_orig, src_orig); \
\
return dst; \
}
else
if (dst > src) {
for (i = 0; i < n; i++)
dst[n-i-1] = src[n-i-1];
STPCPY(m_libc_so_6, stpcpy)
STPCPY(m_ld_linux_so_2, stpcpy)
#define MEMSET(soname, fnname) \
void* VG_REPLACE_FUNCTION(soname,fnname)(void *s, Int c, SizeT n); \
void* VG_REPLACE_FUNCTION(soname,fnname)(void *s, Int c, SizeT n) \
{ \
unsigned char *cp = s; \
\
while(n--) \
*cp++ = c; \
\
return s; \
}
return dst;
}
MEMSET(m_libc_so_6, memset)
#define MEMMOVE(soname, fnname) \
void* VG_REPLACE_FUNCTION(soname,fnname)(void *dstV, const void *srcV, SizeT n); \
void* VG_REPLACE_FUNCTION(soname,fnname)(void *dstV, const void *srcV, SizeT n) \
{ \
SizeT i; \
Char* dst = (Char*)dstV; \
Char* src = (Char*)srcV; \
if (dst < src) { \
for (i = 0; i < n; i++) \
dst[i] = src[i]; \
} \
else \
if (dst > src) { \
for (i = 0; i < n; i++) \
dst[n-i-1] = src[n-i-1]; \
} \
return dst; \
}
MEMMOVE(m_libc_so_6, memmove)
/* Find the first occurrence of C in S or the final NUL byte. */
DECL( char* glibc232_strchrnul (const char* s, int c_in) )
{
unsigned char c = (unsigned char) c_in;
unsigned char* char_ptr = (unsigned char *)s;
while (1) {
if (*char_ptr == 0) return char_ptr;
if (*char_ptr == c) return char_ptr;
char_ptr++;
#define GLIBC232_STRCHRNUL(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname,fnname) (const char* s, int c_in); \
char* VG_REPLACE_FUNCTION(soname,fnname) (const char* s, int c_in) \
{ \
unsigned char c = (unsigned char) c_in; \
unsigned char* char_ptr = (unsigned char *)s; \
while (1) { \
if (*char_ptr == 0) return char_ptr; \
if (*char_ptr == c) return char_ptr; \
char_ptr++; \
} \
}
}
GLIBC232_STRCHRNUL(m_libc_so_6, strchrnul)
/* Find the first occurrence of C in S. */
DECL( char* glibc232_rawmemchr (const char* s, int c_in) )
{
unsigned char c = (unsigned char) c_in;
unsigned char* char_ptr = (unsigned char *)s;
while (1) {
if (*char_ptr == c) return char_ptr;
char_ptr++;
#define GLIBC232_RAWMEMCHR(soname, fnname) \
char* VG_REPLACE_FUNCTION(soname,fnname) (const char* s, int c_in); \
char* VG_REPLACE_FUNCTION(soname,fnname) (const char* s, int c_in) \
{ \
unsigned char c = (unsigned char) c_in; \
unsigned char* char_ptr = (unsigned char *)s; \
while (1) { \
if (*char_ptr == c) return char_ptr; \
char_ptr++; \
} \
}
}
GLIBC232_RAWMEMCHR(m_libc_so_6, rawmemchr)
/*--------------------------------------------------------------------*/

View File

@ -47,7 +47,8 @@ EXTRA_DIST = $(noinst_SCRIPTS) \
match-overrun.stderr.exp match-overrun.vgtest match-overrun.supp \
memalign_test.stderr.exp memalign_test.vgtest \
memalign2.stderr.exp memalign2.vgtest \
memcmptest.stderr.exp memcmptest.stdout.exp memcmptest.vgtest \
memcmptest.stderr.exp memcmptest.stderr.exp2 \
memcmptest.stdout.exp memcmptest.vgtest \
mempool.stderr.exp mempool.stderr.exp64 mempool.vgtest \
mismatches.stderr.exp mismatches.stderr.exp64 mismatches.vgtest \
mmaptest.stderr.exp mmaptest.vgtest \
@ -66,6 +67,7 @@ EXTRA_DIST = $(noinst_SCRIPTS) \
sigaltstack.stderr.exp sigaltstack.vgtest \
signal2.stderr.exp signal2.stdout.exp signal2.vgtest \
sigprocmask.stderr.exp sigprocmask.vgtest \
strchr.stderr.exp strchr.vgtest \
str_tester.stderr.exp str_tester.vgtest \
supp1.stderr.exp supp1.vgtest \
supp2.stderr.exp supp2.vgtest \
@ -98,7 +100,7 @@ check_PROGRAMS = \
post-syscall \
realloc1 realloc2 realloc3 \
sigaltstack signal2 sigprocmask \
str_tester supp1 supp2 suppfree \
strchr str_tester supp1 supp2 suppfree \
trivialleak weirdioctl \
mismatches new_override metadata \
vgtest_ume xml1 \
@ -171,6 +173,7 @@ sigaltstack_SOURCES = sigaltstack.c
trivialleak_SOURCES = trivialleak.c
weirdioctl_SOURCES = weirdioctl.c
metadata_SOURCES = metadata.c
strchr_SOURCES = strchr.c
str_tester_SOURCES = str_tester.c
str_tester_CFLAGS = $(AM_CFLAGS) -Wno-shadow
writev_SOURCES = writev.c

View File

@ -1,11 +1,11 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
char* s1;
char* s2;
// An issue here is that in glibc memcmp() and bcmp() are aliases. Valgrind
// chooses the shorter name -- bcmp -- and reports that in the error
// message, even though memcmp() was called. This is hard to avoid.
char *s1, *s2;
int main ( void )
{
s1 = malloc(10); strcpy(s1,"fooble");

View File

@ -0,0 +1,3 @@
Conditional jump or move depends on uninitialised value(s)
at 0x........: bcmp (mac_replace_strmem.c:...)
by 0x........: main (memcmptest.c:13)

14
memcheck/tests/strchr.c Normal file
View File

@ -0,0 +1,14 @@
#include <stdlib.h>
#include <string.h>
// The issue here is the same one in memcmptest -- 'strchr' and 'index' are
// aliases, as are 'strrchr' and 'rindex'. In each case, the shorter name
// gets preferred, ie. 'index' and 'rindex'.
int main(int argc, char* argv[])
{
char* s = malloc(10);
char* a = strchr(s, '\0');
char* b = strrchr(s, '\0');
return ((int)a + (int)b);
}

View File

@ -0,0 +1,2 @@
prog: strchr
vgopts: -q

View File

@ -23,7 +23,7 @@ sed "/For more details, rerun with: -v/d" |
sed "s/vg_replace_malloc.c:[0-9]*/vg_replace_malloc.c:.../" |
# Anonymise vg_intercept lines
sed "s/vg_intercept.c:[0-9]*/vg_intercept.c:.../" |
#sed "s/vg_intercept.c:[0-9]*/vg_intercept.c:.../" |
# Hide suppressed error counts
sed "s/^\(ERROR SUMMARY[^(]*(suppressed: \)[0-9]*\( from \)[0-9]*)$/\10\20)/" |