From: Julian Seward Date: Sat, 2 Apr 2005 17:38:59 +0000 (+0000) Subject: A major overhaul of how malloc/free intercepts are done. The general X-Git-Tag: svn/VALGRIND_3_0_0~808 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1a2c48a114a78bb6b6287b21bf66db0bfe6fe353;p=thirdparty%2Fvalgrind.git A major overhaul of how malloc/free intercepts are done. The general idea is the same -- write functions with special names encoding sonames and fn names, and have the redir mechanism notice them. However the way the functions are generated is significantly changed: * The name mangling scheme has been replaced with one which is just about simple enough not to need a preprocessing phase. Hence vg_replace_malloc.c.base is replaced by vg_replace_malloc.c, and the preprocessor disappears. The demangler in vg_symtab2.c changes accordingly. * Kill off the horrendous LIBALIAS macro. In return we have to enumerate all the redirections longhand, but this is not a big deal. * Remove use of the GNUisms "attribute alias" and "attribute protected". * Remove the hardwired assumption that any C++ new/new[]/etc symbols we might want to intercept are mangled in GNU style. * Add more comments. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3510 --- diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 50450e8857..ed1d7ed0ec 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -37,10 +37,10 @@ noinst_HEADERS = \ EXTRA_DIST = \ valgrind.vs \ gen_toolint.pl toolfuncs.def \ - gen_intercepts.pl vg_replace_malloc.c.base + gen_intercepts.pl BUILT_SOURCES = vg_toolint.c vg_toolint.h -CLEANFILES = vg_toolint.c vg_toolint.h vg_replace_malloc.c +CLEANFILES = vg_toolint.c vg_toolint.h valgrind_SOURCES = \ ume.c \ @@ -61,6 +61,7 @@ stage2_SOURCES = \ vg_demangle.c \ vg_errcontext.c \ vg_hashtable.c \ + vg_replace_malloc.c \ vg_main.c \ vg_malloc2.c \ vg_memory.c \ @@ -114,10 +115,6 @@ endif stage2_LDADD= $(stage2_extra) -ldl -vg_replace_malloc.c: $(srcdir)/gen_intercepts.pl $(srcdir)/vg_replace_malloc.c.base - rm -f $@ - $(PERL) $(srcdir)/gen_intercepts.pl < $(srcdir)/vg_replace_malloc.c.base > $@ - vg_toolint.c: $(srcdir)/gen_toolint.pl $(srcdir)/toolfuncs.def ./Makefile rm -f $@ $(PERL) $(srcdir)/gen_toolint.pl callwrap < $(srcdir)/toolfuncs.def > $@ || rm -f $@ diff --git a/coregrind/core.h b/coregrind/core.h index 04889d16e9..3204d11460 100644 --- a/coregrind/core.h +++ b/coregrind/core.h @@ -468,15 +468,18 @@ extern void VG_(print_all_arena_stats) ( void ); A synonym for exit. */ #define VG_USERREQ__LIBC_FREERES_DONE 0x3029 -#define VG_INTERCEPT_PREFIX "_vgi__" -#define VG_INTERCEPT_PREFIX_LEN 6 -#define VG_INTERCEPT(name) _vgi__##name -#define VG_INTERCEPT_ALIAS(name) "_vgi__" #name - -#define VG_WRAPPER_PREFIX "_vgw__" -#define VG_WRAPPER_PREFIX_LEN 6 -#define VG_WRAPPER(name) _vgw__##name -#define VG_WRAPPER_ALIAS(name) "_vgw__" #name +/* Intercept prefix stuff. See coregrind/vg_replace_malloc.c for + details. Unfortunately the "_vgi_" literal is also hardcoded in + that file, so if you change this one you must also change the other + one. */ +#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 struct vg_mallocfunc_info { diff --git a/coregrind/gen_intercepts.pl b/coregrind/gen_intercepts.pl deleted file mode 100644 index 18fed7c7e6..0000000000 --- a/coregrind/gen_intercepts.pl +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/perl -w - -# 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. - -use strict; - -while(<>) { - if(/VG_INTERCEPT\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/) { - my $ver = $1 . ":" . $2; - ($ver =~ /J/) and die "Argh, 'J' in intercept muck..."; - $ver =~ s/\*/J2A/g; - $ver =~ s/\+/J2B/g; - $ver =~ s/\-/J2D/g; - $ver =~ s/\./J2E/g; - $ver =~ s/\:/J3A/g; - s/VG_INTERCEPT\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/VG_INTERCEPT($ver)/g; - } elsif(/VG_INTERCEPT_ALIAS\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/) { - my $ver = $1 . ":" . $2; - ($ver =~ /J/) and die "Argh, 'J' in intercept muck..."; - $ver =~ s/\*/J2A/g; - $ver =~ s/\+/J2B/g; - $ver =~ s/\-/J2D/g; - $ver =~ s/\./J2E/g; - $ver =~ s/\:/J3A/g; - s/VG_INTERCEPT_ALIAS\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/VG_INTERCEPT_ALIAS($ver)/g; - } - print $_; -} diff --git a/coregrind/vg_replace_malloc.c b/coregrind/vg_replace_malloc.c new file mode 100644 index 0000000000..0fd0cf3a43 --- /dev/null +++ b/coregrind/vg_replace_malloc.c @@ -0,0 +1,525 @@ + +/*--------------------------------------------------------------------*/ +/*--- 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" + +/* 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 +*/ + + +/* It would be nice to be able to write VG_INTERCEPT_PREFIX instead of + "_vgi_" here, but I can't figure out how to get cpp to cooperate. + If you change this "_vgi_" you should also change the definitiion + of VG_INTERCEPT_PREFIX in core.h accordingly. +*/ +#define ENCODE(libname,fnname) _vgi_##libname##_##fnname + + +/* Some handy mangled 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 + +/* 2 Apr 05: the Portland Group compiler, which uses cfront/ARM style + mangling, could be supported properly by the redirects in this + module. Except we can't because it doesn't put its allocation + functions in libpgc.so but instead hardwires them into the + compilation unit holding main(), which makes them impossible to + intercept directly. Fortunately those fns seem to route everything + through to malloc/free. +*/ + +extern void _exit(int); + +/*------------------------------------------------------------*/ +/*--- Replacing malloc() et al ---*/ +/*------------------------------------------------------------*/ + +/* This struct is initially empty. Before the first use of any of + these functions, we make a client request which fills in the + fields. +*/ +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. +*/ + +/* Generate a replacement for 'fnname' in object 'soname', which calls + 'vg_replacement' to allocate memory. If that fails, return NULL. +*/ +#define ALLOC_or_NULL(soname, fnname, vg_replacement) \ + \ + void* ENCODE(soname,fnname) (SizeT n); \ + void* ENCODE(soname,fnname) (SizeT n) \ + { \ + void* v; \ + \ + MALLOC_TRACE(#fnname "(%llu)", (ULong)n ); \ + MAYBE_SLOPPIFY(n); \ + if (!init_done) init(); \ + \ + v = (void*)VALGRIND_NON_SIMD_CALL1( info.tl_##vg_replacement, n ); \ + MALLOC_TRACE(" = %p", v ); \ + return v; \ + } + + +/* Generate a replacement for 'fnname' in object 'soname', which calls + 'vg_replacement' to allocate memory. If that fails, it bombs the + system. +*/ +#define ALLOC_or_BOMB(soname, fnname, vg_replacement) \ + \ + void* ENCODE(soname,fnname) (SizeT n); \ + void* ENCODE(soname,fnname) (SizeT n) \ + { \ + void* v; \ + \ + MALLOC_TRACE(#fnname "(%llu)", (ULong)n ); \ + MAYBE_SLOPPIFY(n); \ + if (!init_done) init(); \ + \ + v = (void*)VALGRIND_NON_SIMD_CALL1( info.tl_##vg_replacement, 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; \ + } + +// Each of these lines generates a replacement function: +// (from_so, from_fn, v's replacement) + +// malloc +ALLOC_or_NULL(m_libstc_plus_plus_star, malloc, malloc); +ALLOC_or_NULL(m_libc_dot_so_dot_6, malloc, malloc); +//ALLOC_or_NULL(m_libpgc_dot_so, malloc, malloc); + +// operator new(unsigned int), GNU mangling +ALLOC_or_BOMB(m_libstc_plus_plus_star, builtin_new, __builtin_new); +ALLOC_or_BOMB(m_libc_dot_so_dot_6, builtin_new, __builtin_new); + +ALLOC_or_BOMB(m_libstc_plus_plus_star, __builtin_new, __builtin_new); +ALLOC_or_BOMB(m_libc_dot_so_dot_6, __builtin_new, __builtin_new); + +ALLOC_or_BOMB(m_libstc_plus_plus_star, _Znwj, __builtin_new); +ALLOC_or_BOMB(m_libc_dot_so_dot_6, _Znwj, __builtin_new); + +// operator new(unsigned int), ARM/cfront mangling +//ALLOC_or_BOMB(m_libpgc_dot_so, __nw__FUi, __builtin_new); + +// operator new(unsigned, std::nothrow_t const&), GNU mangling +ALLOC_or_NULL(m_libstc_plus_plus_star, _ZnwjRKSt9nothrow_t, __builtin_new); +ALLOC_or_NULL(m_libc_dot_so_dot_6, _ZnwjRKSt9nothrow_t, __builtin_new); + +// operator new[](unsigned int), GNU mangling +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 ); +ALLOC_or_BOMB(m_libstc_plus_plus_star, _Znaj, __builtin_vec_new ); +ALLOC_or_BOMB(m_libc_dot_so_dot_6, _Znaj, __builtin_vec_new ); + +// operator new[](unsigned, std::nothrow_t const&), GNU mangling +ALLOC_or_NULL(m_libstc_plus_plus_star, _ZnajRKSt9nothrow_t, __builtin_vec_new ); +ALLOC_or_NULL(m_libc_dot_so_dot_6, _ZnajRKSt9nothrow_t, __builtin_vec_new ); + + +/* Generate a replacement for 'fnname' in object 'soname', which calls + 'vg_replacement' to free previously allocated memory. +*/ +#define FREE(soname, fnname, vg_replacement) \ + \ + void ENCODE(soname,fnname) (void *p); \ + void ENCODE(soname,fnname) (void *p) \ + { \ + MALLOC_TRACE(#vg_replacement "(%p)", p ); \ + if (p == NULL) \ + return; \ + if (!init_done) init(); \ + (void)VALGRIND_NON_SIMD_CALL1( info.tl_##vg_replacement, p ); \ + } + +// free +FREE(m_libstc_plus_plus_star, free, free ); +FREE(m_libc_dot_so_dot_6, free, free ); + +// cfree +FREE(m_libstc_plus_plus_star, cfree, free ); +FREE(m_libc_dot_so_dot_6, cfree, free ); + +// do we really need these? +FREE(m_libstc_plus_plus_star, __builtin_delete, __builtin_delete ); +FREE(m_libc_dot_so_dot_6, __builtin_delete, __builtin_delete ); + +// operator delete(void*), GNU mangling +FREE(m_libstc_plus_plus_star, _ZdlPv, __builtin_delete ); +FREE(m_libc_dot_so_dot_6, _ZdlPv, __builtin_delete ); + +// operator delete(void*, std::nothrow_t const&), GNU mangling +FREE(m_libstc_plus_plus_star, _ZdlPvRKSt9nothrow_t, __builtin_delete ); +FREE(m_libc_dot_so_dot_6, _ZdlPvRKSt9nothrow_t, __builtin_delete ); + +// operator delete[](void*), GNU mangling +FREE(m_libstc_plus_plus_star, __builtin_vec_delete, __builtin_vec_delete ); +FREE(m_libc_dot_so_dot_6, __builtin_vec_delete, __builtin_vec_delete ); +FREE(m_libstc_plus_plus_star, _ZdaPv, __builtin_vec_delete ); +FREE(m_libc_dot_so_dot_6, _ZdaPv, __builtin_vec_delete ); + +// operator delete[](void*, std::nothrow_t const&), GNU mangling +FREE(m_libstc_plus_plus_star, _ZdaPvRKSt9nothrow_t, __builtin_vec_delete ); +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* 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; \ + } + +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* v; \ + \ + MALLOC_TRACE("realloc(%p,%llu)", ptrV, (ULong)new_size ); \ + MAYBE_SLOPPIFY(new_size); \ + \ + 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); \ + if (new_size <= 0) { \ + ENCODE(libcZdsoZd6,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; \ + } + +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* 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; \ + } + +MEMALIGN(m_libc_dot_so_dot_6, memalign); + + +#define VALLOC(soname, fnname) \ + \ + void* ENCODE(soname,fnname) ( SizeT size ); \ + void* ENCODE(soname,fnname) ( SizeT size ) \ + { \ + return ENCODE(libcZdsoZd6,memalign)(VKI_PAGE_SIZE, size); \ + } + +VALLOC(m_libc_dot_so_dot_6, valloc); + + +/* Various compatibility wrapper functions, for glibc and libstdc++. */ + +#define MALLOPT(soname, fnname) \ + \ + int ENCODE(soname, fnname) ( int cmd, int value ); \ + int ENCODE(soname, fnname) ( int cmd, int value ) \ + { \ + /* In glibc-2.2.4, 1 denotes a successful return value for \ + mallopt */ \ + return 1; \ + } + +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 ) \ + { \ + 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; \ + \ + mem = ENCODE(libcZdsoZd6,memalign)(alignment, size); \ + \ + if (mem != NULL) { \ + *memptr = mem; \ + return 0; \ + } \ + \ + return VKI_ENOMEM; \ + } + +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 ) \ + { \ + 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; \ + } + +MALLOC_USABLE_SIZE(m_libc_dot_so_dot_6, malloc_usable_size); + + +/* 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'; +} + +#define PANIC(soname, fnname) \ + \ + void ENCODE(soname, fnname) ( void ); \ + void ENCODE(soname, fnname) ( void ) \ + { \ + panic(#fnname); \ + } + +PANIC(m_libc_dot_so_dot_6, pvalloc); +PANIC(m_libc_dot_so_dot_6, malloc_stats); +PANIC(m_libc_dot_so_dot_6, malloc_trim); +PANIC(m_libc_dot_so_dot_6, malloc_get_state); +PANIC(m_libc_dot_so_dot_6, malloc_set_state); + + +/* Yet another ugly hack. Cannot include 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 */ +}; + +#define MALLINFO(soname, fnname) \ + \ + struct mallinfo ENCODE(soname, fnname) ( void ); \ + struct mallinfo ENCODE(soname, fnname) ( 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; \ + } + +MALLINFO(m_libc_dot_so_dot_6, mallinfo); + + +/* 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 ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/vg_replace_malloc.c.base b/coregrind/vg_replace_malloc.c.base deleted file mode 100644 index 602a9116c2..0000000000 --- a/coregrind/vg_replace_malloc.c.base +++ /dev/null @@ -1,365 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- 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 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 ---*/ -/*--------------------------------------------------------------------*/ diff --git a/coregrind/vg_symtab2.c b/coregrind/vg_symtab2.c index 066b3d891c..391688e207 100644 --- a/coregrind/vg_symtab2.c +++ b/coregrind/vg_symtab2.c @@ -799,73 +799,121 @@ Bool VG_(is_object_file)(const void *buf) return False; } -/* - * Demangle an intercept symbol into library:func form +/* 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) { - int i, j = 0; - int len = VG_(strlen)(symbol); +# 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_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; - for(i = VG_INTERCEPT_PREFIX_LEN; i < len; i++) { - if(symbol[i] == 'J') { + if (symbol[i] != 'Z') { + EMIT(symbol[i]); i++; - vg_assert('J' != symbol[i]); - if((symbol[i] >= '0' && symbol[i] <= '9') || - (symbol[i] >= 'a' && symbol[i] <= 'f') || - (symbol[i] >= 'A' && symbol[i] <= 'F')) - { - int x = symbol[i++]; - int y = symbol[i]; - if(x >= '0' && x <= '9') { - x -= '0'; - } else if(x >= 'a' && x <= 'f') { - x -= 'a'; - } else if(x >= 'A' && x <= 'F') { - x -= 'A'; - } - if(y >= '0' && y <= '9') { - y -= '0'; - } else if(y >= 'a' && y <= 'f') { - y = y - 'a' + 10; - } else if(y >= 'A' && y <= 'F') { - y = y - 'A' + 10; - } else { - return False; - } - result[j] = (x << 4) | y; - } else { - return False; - } - } else { - result[j] = symbol[i]; + continue; } - if(j >= nbytes) { - result[j] = '\0'; - return True; + + /* 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; } - j++; + 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++; + } + + 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++; } - result[j] = '\0'; + + EMIT(0); + if (0) VG_(printf)("%s\n", result); return True; + +# undef EMIT } static void handle_intercept( SegInfo* si, Char* symbol, ElfXX_Sym* sym) { - Int len = VG_(strlen)(symbol) + 1 - VG_INTERCEPT_PREFIX_LEN; - Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len); + Bool ok; + Int len = VG_(strlen)(symbol) + 1 - VG_INTERCEPT_PREFIX_LEN; + Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8); Char *func; - intercept_demangle(symbol, lib, len); - func = lib + VG_(strlen)(lib)-1; - - while(*func != ':') func--; - *func = '\0'; + /* 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_(add_redirect_sym_to_addr)(lib, func+1, si->offset + sym->st_value); VG_(arena_free)(VG_AR_SYMTAB, lib); }