mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-04 10:21:20 +00:00
Valgrind's dependency on the dynamic linker for getting started, and instead takes things into its own hands. This checkin doesn't add much in the way of new functionality, but it is the basis for all future work on Valgrind. It allows us much more flexibility in implementation, and well as increasing the reliability of Valgrind by protecting it more from its clients. This patch requires some changes to tools to update them to the changes in the tool API, but they are straightforward. See the posting "Heads up: Full Virtualization" on valgrind-developers for a more complete description of this change and its effects on you. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2118
374 lines
10 KiB
C
374 lines
10 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Replacements for malloc() et al, which run on the simulated ---*/
|
|
/*--- CPU. vg_replace_malloc.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, an extensible x86 protected-mode
|
|
emulator for monitoring program execution on x86-Unixes.
|
|
|
|
Copyright (C) 2000-2003 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.
|
|
*/
|
|
|
|
/* ---------------------------------------------------------------------
|
|
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 visibility (obviously) and
|
|
have no prototypes in vg_include.h, since they are not intended to
|
|
be called from within Valgrind.
|
|
|
|
This file can be #included into a skin that wishes to know about
|
|
calls to malloc(). It should define functions SK_(malloc) et al
|
|
that will be called.
|
|
------------------------------------------------------------------ */
|
|
|
|
#include "valgrind.h" /* for VALGRIND_NON_SIMD_CALL[12] */
|
|
#include "vg_include.h"
|
|
#include "vg_skin.h"
|
|
|
|
/* Create an alias */
|
|
#define ALIAS(ret, name, args, toname) \
|
|
ret name args __attribute__((alias(#toname), visibility("protected")))
|
|
|
|
/* Declare a function, along with libc's various aliases */
|
|
#define LIBALIAS(ret, name, args) \
|
|
ALIAS(ret, __##name, args, name); \
|
|
ALIAS(ret, __libc_##name, args, name); \
|
|
ret name args
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Replacing malloc() et al ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
static struct vg_mallocfunc_info info;
|
|
static int init_done;
|
|
|
|
/* Startup hook - called as init section */
|
|
static void init(void) __attribute__((constructor));
|
|
|
|
/* Below are new versions of malloc, __builtin_new, free,
|
|
__builtin_delete, calloc, realloc, memalign, and friends.
|
|
|
|
None of these functions are called directly - they are not meant to
|
|
be found by the dynamic linker. They get called because
|
|
vg_replace_malloc installs a bunch of code redirects which causes
|
|
Valgrind to use these functions rather than the ones they're
|
|
replacing. That said, we certainly don't mind if the linker finds
|
|
them, because it makes our life easier with respect to startup
|
|
initialization order (we can't guarantee that our init routine will
|
|
necessarily be called early enough to do the redirects before
|
|
someone wants to allocate).
|
|
*/
|
|
|
|
#define MALLOC_TRACE(format, args...) \
|
|
if (info.clo_trace_malloc) \
|
|
VALGRIND_INTERNAL_PRINTF(format, ## args )
|
|
|
|
#define MAYBE_SLOPPIFY(n) \
|
|
if (info.clo_sloppy_malloc) { \
|
|
n = (n+3) & ~3; \
|
|
}
|
|
|
|
/* ALL calls to malloc() and friends wind up here. */
|
|
#define ALLOC(fff, vgfff) \
|
|
LIBALIAS(void *, fff, (Int n)) \
|
|
{ \
|
|
void* v; \
|
|
\
|
|
MALLOC_TRACE(#fff "(%d)", n ); \
|
|
MAYBE_SLOPPIFY(n); \
|
|
if (!init_done) init(); \
|
|
\
|
|
v = (void*)VALGRIND_NON_SIMD_CALL1( info.sk_##vgfff, n ); \
|
|
MALLOC_TRACE(" = %p", v ); \
|
|
return v; \
|
|
}
|
|
ALLOC( malloc, malloc );
|
|
ALLOC( __builtin_new, __builtin_new );
|
|
ALLOC( _Znwj, __builtin_new );
|
|
|
|
// operator new(unsigned, std::nothrow_t const&)
|
|
ALLOC( _ZnwjRKSt9nothrow_t, __builtin_new );
|
|
|
|
ALLOC( __builtin_vec_new, __builtin_vec_new );
|
|
ALLOC( _Znaj, __builtin_vec_new );
|
|
|
|
// operator new[](unsigned, std::nothrow_t const&
|
|
ALLOC( _ZnajRKSt9nothrow_t, __builtin_vec_new );
|
|
|
|
#define FREE(fff, vgfff) \
|
|
LIBALIAS(void, fff, (void *p)) \
|
|
{ \
|
|
MALLOC_TRACE(#fff "(%p)", p ); \
|
|
if (p == NULL) \
|
|
return; \
|
|
if (!init_done) init(); \
|
|
(void)VALGRIND_NON_SIMD_CALL1( info.sk_##vgfff, p ); \
|
|
}
|
|
FREE( free, free );
|
|
FREE( __builtin_delete, __builtin_delete );
|
|
FREE( _ZdlPv, __builtin_delete );
|
|
FREE( __builtin_vec_delete, __builtin_vec_delete );
|
|
FREE( _ZdaPv, __builtin_vec_delete );
|
|
|
|
LIBALIAS(void*, calloc, ( Int nmemb, Int size ))
|
|
{
|
|
void* v;
|
|
|
|
MALLOC_TRACE("calloc(%d,%d)", nmemb, size );
|
|
MAYBE_SLOPPIFY(size);
|
|
|
|
if (!init_done) init();
|
|
v = (void*)VALGRIND_NON_SIMD_CALL2( info.sk_calloc, nmemb, size );
|
|
MALLOC_TRACE(" = %p", v );
|
|
return v;
|
|
}
|
|
|
|
LIBALIAS(void*, realloc, ( void* ptrV, Int new_size ))
|
|
{
|
|
void* v;
|
|
|
|
MALLOC_TRACE("realloc(%p,%d)", ptrV, new_size );
|
|
MAYBE_SLOPPIFY(new_size);
|
|
|
|
if (ptrV == NULL)
|
|
return malloc(new_size);
|
|
if (new_size <= 0) {
|
|
free(ptrV);
|
|
if (info.clo_trace_malloc)
|
|
VALGRIND_INTERNAL_PRINTF(" = 0" );
|
|
return NULL;
|
|
}
|
|
if (!init_done) init();
|
|
v = (void*)VALGRIND_NON_SIMD_CALL2( info.sk_realloc, ptrV, new_size );
|
|
MALLOC_TRACE(" = %p", v );
|
|
return v;
|
|
}
|
|
|
|
|
|
LIBALIAS(void*, memalign, ( Int alignment, Int n ))
|
|
{
|
|
void* v;
|
|
|
|
MALLOC_TRACE("memalign(al %d, size %d)", alignment, n );
|
|
MAYBE_SLOPPIFY(n);
|
|
|
|
if (!init_done) init();
|
|
v = (void*)VALGRIND_NON_SIMD_CALL2( info.sk_memalign, alignment, n );
|
|
MALLOC_TRACE(" = %p", v );
|
|
return v;
|
|
}
|
|
|
|
|
|
LIBALIAS(void*, valloc, ( Int size ))
|
|
{
|
|
return memalign(VKI_BYTES_PER_PAGE, size);
|
|
}
|
|
|
|
|
|
/* Various compatibility wrapper functions, for glibc and libstdc++. */
|
|
|
|
/* Don't just alias free, otherwise people could get confused seeing
|
|
cfree rather than free in error output */
|
|
LIBALIAS(void, cfree, ( void* p ) )
|
|
{
|
|
free(p);
|
|
}
|
|
|
|
LIBALIAS(int, mallopt, ( int cmd, int value ))
|
|
{
|
|
/* In glibc-2.2.4, 1 denotes a successful return value for mallopt */
|
|
return 1;
|
|
}
|
|
|
|
|
|
LIBALIAS(int, posix_memalign, ( void **memptr, UInt alignment, UInt size ))
|
|
{
|
|
void *mem;
|
|
|
|
/* Test whether the alignment argument is valid. It must be a power of
|
|
two multiple of sizeof (void *). */
|
|
if (alignment % sizeof (void *) != 0 || (alignment & (alignment - 1)) != 0)
|
|
return VKI_EINVAL /*22*/ /*EINVAL*/;
|
|
|
|
mem = memalign (alignment, size);
|
|
|
|
if (mem != NULL) {
|
|
*memptr = mem;
|
|
return 0;
|
|
}
|
|
|
|
return VKI_ENOMEM /*12*/ /*ENOMEM*/;
|
|
}
|
|
|
|
LIBALIAS(int, malloc_usable_size, ( void* p ))
|
|
{
|
|
Int pszB;
|
|
|
|
MALLOC_TRACE("malloc_usable_size(%p)", p );
|
|
if (NULL == p)
|
|
return 0;
|
|
|
|
if (!init_done) init();
|
|
pszB = (Int)VALGRIND_NON_SIMD_CALL2( info.arena_payload_szB,
|
|
VG_AR_CLIENT, p );
|
|
MALLOC_TRACE(" = %d", pszB );
|
|
|
|
return pszB;
|
|
}
|
|
|
|
|
|
/* Bomb out if we get any of these. */
|
|
|
|
extern void _exit(int);
|
|
|
|
static void panic(const char *str)
|
|
{
|
|
VALGRIND_PRINTF_BACKTRACE("Program aborting because of call to %s", str);
|
|
|
|
_exit(99);
|
|
*(int *)0 = 'x';
|
|
}
|
|
|
|
#define PANIC(x) \
|
|
void x(void) \
|
|
{ \
|
|
panic(#x); \
|
|
}
|
|
|
|
PANIC(pvalloc);
|
|
PANIC(malloc_stats);
|
|
PANIC(malloc_trim);
|
|
PANIC(malloc_get_state);
|
|
PANIC(malloc_set_state);
|
|
|
|
|
|
/* Yet another ugly hack. Cannot include <malloc.h> because we
|
|
implement functions implemented there with different signatures.
|
|
This struct definition MUST match the system one. */
|
|
|
|
/* SVID2/XPG mallinfo structure */
|
|
struct mallinfo {
|
|
int arena; /* total space allocated from system */
|
|
int ordblks; /* number of non-inuse chunks */
|
|
int smblks; /* unused -- always zero */
|
|
int hblks; /* number of mmapped regions */
|
|
int hblkhd; /* total space in mmapped regions */
|
|
int usmblks; /* unused -- always zero */
|
|
int fsmblks; /* unused -- always zero */
|
|
int uordblks; /* total allocated space */
|
|
int fordblks; /* total non-inuse space */
|
|
int keepcost; /* top-most, releasable (via malloc_trim) space */
|
|
};
|
|
|
|
LIBALIAS(struct mallinfo, mallinfo, ( void ))
|
|
{
|
|
/* Should really try to return something a bit more meaningful */
|
|
UInt i;
|
|
struct mallinfo mi;
|
|
UChar* pmi = (UChar*)(&mi);
|
|
for (i = 0; i < sizeof(mi); i++)
|
|
pmi[i] = 0;
|
|
return mi;
|
|
}
|
|
|
|
static const struct {
|
|
const Char *libname;
|
|
Addr func;
|
|
} replacements[] =
|
|
{
|
|
#define E(pfx, x) { pfx #x, (Addr)x }
|
|
#define R(x) E("", x), E("__libc_", x), E("__", x)
|
|
|
|
/* alloc */
|
|
R(malloc),
|
|
R(__builtin_new),
|
|
R(_Znwj),
|
|
R(_ZnwjRKSt9nothrow_t), /* operator new(unsigned, std::nothrow_t const&) */
|
|
R(__builtin_vec_new),
|
|
R(_Znaj),
|
|
R(_ZnajRKSt9nothrow_t), /* operator new[](unsigned, std::nothrow_t const& */
|
|
R(calloc),
|
|
R(realloc),
|
|
R(memalign),
|
|
R(valloc),
|
|
R(cfree),
|
|
R(posix_memalign),
|
|
|
|
/* free */
|
|
R(free),
|
|
R(__builtin_delete),
|
|
R(_ZdlPv),
|
|
R(__builtin_vec_delete),
|
|
R(_ZdaPv),
|
|
|
|
/* misc */
|
|
R(mallopt),
|
|
R(malloc_usable_size),
|
|
R(mallinfo),
|
|
|
|
/* bad */
|
|
R(pvalloc),
|
|
R(malloc_stats),
|
|
R(malloc_trim),
|
|
R(malloc_get_state),
|
|
R(malloc_set_state),
|
|
#undef R
|
|
#undef S
|
|
#undef E
|
|
};
|
|
|
|
/* All the code in here is unused until this function is called */
|
|
|
|
static void init(void)
|
|
{
|
|
int i;
|
|
int res;
|
|
|
|
if (init_done)
|
|
return;
|
|
|
|
init_done = 1;
|
|
|
|
VALGRIND_MAGIC_SEQUENCE(res, -1, VG_USERREQ__GET_MALLOCFUNCS, &info, 0, 0, 0);
|
|
|
|
for(i = 0; i < sizeof(replacements)/sizeof(*replacements); i++) {
|
|
#if 0
|
|
/* doesn't seem much point - ld-linux.so will have already used
|
|
malloc/free before we run */
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0, VG_USERREQ__REGISTER_REDIRECT_ADDR,
|
|
"soname:ld-linux.so.2", replacements[i].libname,
|
|
replacements[i].func, 0);
|
|
#endif
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0, VG_USERREQ__REGISTER_REDIRECT_ADDR,
|
|
"soname:libc.so.6", replacements[i].libname,
|
|
replacements[i].func, 0);
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0, VG_USERREQ__REGISTER_REDIRECT_ADDR,
|
|
"soname:libstdc++*", replacements[i].libname,
|
|
replacements[i].func, 0);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end vg_replace_malloc.c ---*/
|
|
/*--------------------------------------------------------------------*/
|