blob: 602a9116c296f7c39de7c8cee298b3c0bbdaab5c [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Replacements for malloc() et al, which run on the simulated ---*/
/*--- CPU. vg_replace_malloc.c ---*/
/*--------------------------------------------------------------------*/
/*
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.
*/
/* ---------------------------------------------------------------------
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.
This file can be linked into the injected so file for any tool that
wishes to know about calls to malloc(). It should define functions
TL_(malloc) et al that will be called.
------------------------------------------------------------------ */
#include "valgrind.h" /* for VALGRIND_NON_SIMD_CALL[12] */
#include "core.h"
// Nb: the last line is repeated -- once for the declaration, once for the
// definition. If we don't have the declaration there GCC complains.
#define LIBALIAS(ret, name, args) \
ret VG_INTERCEPT(soname:libstdc++*, __libc_##name) args \
__attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, ##name)), \
visibility("protected"))); \
ret VG_INTERCEPT(soname:libc.so.6, __libc_##name) args \
__attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, ##name)), \
visibility("protected"))); \
ret VG_INTERCEPT(soname:libstdc++*, __##name) args \
__attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, ##name)), \
visibility("protected"))); \
ret VG_INTERCEPT(soname:libc.so.6, __##name) args \
__attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, ##name)), \
visibility("protected"))); \
ret VG_INTERCEPT(soname:libstdc++*, ##name) args \
__attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, ##name)), \
visibility("protected"))); \
ret VG_INTERCEPT(soname:libc.so.6, ##name) args; \
ret VG_INTERCEPT(soname:libc.so.6, ##name) args
extern void _exit(int);
/*------------------------------------------------------------*/
/*--- 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));
// Functions for printing from code within Valgrind, but which runs on the
// sim'd CPU. They must be functions rather than macros so that va_list can
// be used.
// Nb: at one point, these were used by multiple files that run on the sim'd
// CPU, and so were *defined* in core.h with the 'weak' attribute. That was
// pretty ugly. It's much better if this is the only file that needs them.
__attribute__((format(__printf__, 1, 2)))
static int
internal_printf(char *format, ...)
{
UWord _qzz_res = 0;
va_list vargs;
va_start(vargs, format);
VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__INTERNAL_PRINTF,
(UWord)format, (UWord)vargs, 0, 0);
va_end(vargs);
return _qzz_res;
}
#define MALLOC_TRACE(format, args...) \
if (info.clo_trace_malloc) \
internal_printf(format, ## args )
#define MAYBE_SLOPPIFY(n) \
if (info.clo_sloppy_malloc) { \
n = (n+(VG_SLOPPY_MALLOC_SZB-1)) & ~(VG_SLOPPY_MALLOC_SZB-1); \
}
/* 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. But ALL client calls to malloc() and
friends wind up here eventually. 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. */
#define ALLOC(fff, vgfff) \
LIBALIAS(void *, fff, (SizeT n)) \
{ \
void* v; \
\
MALLOC_TRACE(#fff "(%llu)", (ULong)n ); \
MAYBE_SLOPPIFY(n); \
if (!init_done) init(); \
\
v = (void*)VALGRIND_NON_SIMD_CALL1( info.tl_##vgfff, n ); \
MALLOC_TRACE(" = %p", v ); \
return v; \
}
#define ALLOC2(fff, vgfff) \
LIBALIAS(void *, fff, (SizeT n)) \
{ \
void* v; \
\
MALLOC_TRACE(#fff "(%llu)", (ULong)n ); \
MAYBE_SLOPPIFY(n); \
if (!init_done) init(); \
\
v = (void*)VALGRIND_NON_SIMD_CALL1( info.tl_##vgfff, n ); \
MALLOC_TRACE(" = %p", v ); \
if (NULL == v) { \
VALGRIND_PRINTF_BACKTRACE( \
"new/new[] failed and should throw an exception, but Valgrind\n" \
" cannot throw exceptions and so is aborting instead. Sorry."); \
_exit(1); \
} \
return v; \
}
ALLOC( malloc, malloc );
ALLOC2(__builtin_new, __builtin_new );
ALLOC2(_Znwj, __builtin_new );
// operator new(unsigned, std::nothrow_t const&)
ALLOC( _ZnwjRKSt9nothrow_t, __builtin_new );
ALLOC2(__builtin_vec_new, __builtin_vec_new );
ALLOC2(_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.tl_##vgfff, p ); \
}
FREE( free, free );
FREE( cfree, free );
FREE( __builtin_delete, __builtin_delete );
FREE( _ZdlPv, __builtin_delete );
// operator delete(void*, std::nothrow_t const&)
FREE( _ZdlPvRKSt9nothrow_t, __builtin_delete );
FREE( __builtin_vec_delete, __builtin_vec_delete );
FREE( _ZdaPv, __builtin_vec_delete );
// operator delete[](void*, std::nothrow_t const&)
FREE( _ZdaPvRKSt9nothrow_t, __builtin_vec_delete );
LIBALIAS(void*, calloc, ( SizeT nmemb, SizeT size ))
{
void* v;
MALLOC_TRACE("calloc(%llu,%llu)", (ULong)nmemb, (ULong)size );
MAYBE_SLOPPIFY(size);
if (!init_done) init();
v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_calloc, nmemb, size );
MALLOC_TRACE(" = %p", v );
return v;
}
LIBALIAS(void*, realloc, ( void* ptrV, SizeT new_size ))
{
void* v;
MALLOC_TRACE("realloc(%p,%llu)", ptrV, (ULong)new_size );
MAYBE_SLOPPIFY(new_size);
if (ptrV == NULL)
return VG_INTERCEPT(soname:libc.so.6, malloc)(new_size);
if (new_size <= 0) {
VG_INTERCEPT(soname:libc.so.6, free)(ptrV);
if (info.clo_trace_malloc)
internal_printf(" = 0" );
return NULL;
}
if (!init_done) init();
v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_realloc, ptrV, new_size );
MALLOC_TRACE(" = %p", v );
return v;
}
LIBALIAS(void*, memalign, ( SizeT alignment, SizeT n ))
{
void* v;
MALLOC_TRACE("memalign(al %llu, size %llu)", (ULong)alignment, (ULong)n );
MAYBE_SLOPPIFY(n);
// Round up to minimum alignment if necessary.
if (alignment < VG_MIN_MALLOC_SZB) alignment = VG_MIN_MALLOC_SZB;
// Round up to nearest power-of-two if necessary (like glibc).
while (0 != (alignment & (alignment - 1))) alignment++;
if (!init_done) init();
v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_memalign, alignment, n );
MALLOC_TRACE(" = %p", v );
return v;
}
LIBALIAS(void*, valloc, ( SizeT size ))
{
return VG_INTERCEPT(soname:libc.so.6, memalign)(VKI_PAGE_SIZE, size);
}
/* Various compatibility wrapper functions, for glibc and libstdc++. */
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, SizeT alignment, SizeT 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 = VG_INTERCEPT(soname:libc.so.6, memalign)(alignment, size);
if (mem != NULL) {
*memptr = mem;
return 0;
}
return VKI_ENOMEM /*12*/ /*ENOMEM*/;
}
LIBALIAS(int, malloc_usable_size, ( void* p ))
{
SizeT pszB;
MALLOC_TRACE("malloc_usable_size(%p)", p );
if (NULL == p)
return 0;
if (!init_done) init();
pszB = (SizeT)VALGRIND_NON_SIMD_CALL2( info.arena_payload_szB,
VG_AR_CLIENT, p );
MALLOC_TRACE(" = %llu", (ULong)pszB );
return pszB;
}
/* Bomb out if we get any of these. */
static void panic(const char *str)
{
VALGRIND_PRINTF_BACKTRACE("Program aborting because of call to %s", str);
_exit(99);
*(int *)0 = 'x';
}
// As for LIBALIAS, we have the declaration to shut GCC up.
#define PANIC(x) \
void VG_INTERCEPT(soname:libc.so.6, ## x)(void); \
void VG_INTERCEPT(soname:libc.so.6, ## 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;
}
/* All the code in here is unused until this function is called */
static void init(void)
{
int res;
if (init_done)
return;
init_done = 1;
VALGRIND_MAGIC_SEQUENCE(res, -1, VG_USERREQ__GET_MALLOCFUNCS, &info,
0, 0, 0);
}
/*--------------------------------------------------------------------*/
/*--- end vg_replace_malloc.c ---*/
/*--------------------------------------------------------------------*/