mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 10:05:29 +00:00
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:
parent
d0029796b7
commit
3ae4d1a0ab
12
FAQ.txt
12
FAQ.txt
@ -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
|
||||
-----------------------------------------------------------------
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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. ---*/
|
||||
//:: /*------------------------------------------------------------*/
|
||||
|
||||
@ -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; \
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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
97
include/pub_tool_redir.h
Normal 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 ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
@ -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)
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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");
|
||||
|
||||
3
memcheck/tests/memcmptest.stderr.exp2
Normal file
3
memcheck/tests/memcmptest.stderr.exp2
Normal 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
14
memcheck/tests/strchr.c
Normal 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);
|
||||
}
|
||||
2
memcheck/tests/strchr.vgtest
Normal file
2
memcheck/tests/strchr.vgtest
Normal file
@ -0,0 +1,2 @@
|
||||
prog: strchr
|
||||
vgopts: -q
|
||||
@ -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)/" |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user