@@ -929,7 +929,7 @@ libio-include = -I$(..)libio
built-modules = iconvprogs iconvdata ldconfig lddlibc4 libmemusage \
libSegFault libpcprofile librpcsvc locale-programs \
memusagestat nonlib nscd extramodules libnldbl libsupport \
- testsuite
+ testsuite libmalloc-extras
in-module = $(subst -,_,$(firstword $(libof-$(basename $(@F))) \
$(libof-$(<F)) \
@@ -56,7 +56,8 @@ generated += tst-catgets.mtrace tst-catgets-mem.out
generated-dirs += de
-tst-catgets-ENV = NLSPATH="$(objpfx)%l/%N.cat" LANG=de MALLOC_TRACE=$(objpfx)tst-catgets.mtrace
+tst-catgets-ENV = NLSPATH="$(objpfx)%l/%N.cat" LANG=de MALLOC_TRACE=$(objpfx)tst-catgets.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
ifeq ($(run-built-tests),yes)
# This test just checks whether the program produces any error or not.
@@ -204,7 +204,7 @@ endif
endif
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)tst-leaks1-mem.out \
- $(objpfx)tst-leaks1-static-mem.out $(objpfx)noload-mem.out \
+ $(objpfx)noload-mem.out \
$(objpfx)tst-ldconfig-X.out
endif
tlsmod17a-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
@@ -802,7 +802,8 @@ $(objpfx)noload.out: $(objpfx)testobj5.so
$(objpfx)noload-mem.out: $(objpfx)noload.out
$(common-objpfx)malloc/mtrace $(objpfx)noload.mtrace > $@; \
$(evaluate-test)
-noload-ENV = MALLOC_TRACE=$(objpfx)noload.mtrace
+noload-ENV = MALLOC_TRACE=$(objpfx)noload.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
LDFLAGS-nodelete = -rdynamic
LDFLAGS-nodelmod1.so = -Wl,--enable-new-dtags,-z,nodelete
@@ -1206,8 +1207,10 @@ $(objpfx)tst-leaks1-static-mem.out: $(objpfx)tst-leaks1-static.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1-static.mtrace > $@; \
$(evaluate-test)
-tst-leaks1-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1.mtrace
-tst-leaks1-static-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1-static.mtrace
+tst-leaks1-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
+tst-leaks1-static-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1-static.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-addr1: $(libdl)
@@ -292,7 +292,8 @@ cpp-srcs-left := $(modules) $(generated-modules) $(libJIS-routines) \
lib := iconvdata
include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left))
-tst-loading-ENV = MALLOC_TRACE=$(objpfx)tst-loading.mtrace
+tst-loading-ENV = MALLOC_TRACE=$(objpfx)tst-loading.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)mtrace-tst-loading.out: $(objpfx)tst-loading.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-loading.mtrace > $@; \
$(evaluate-test)
@@ -50,6 +50,7 @@ msgfmt -o ${objpfx}domaindir/existing-locale/LC_TIME/existing-time-domain.mo \
${test_program_prefix_before_env} \
${run_program_env} \
MALLOC_TRACE=$malloc_trace \
+LD_PRELOAD=${common_objpfx}/malloc/libmalloc-extras.so \
LOCPATH=${objpfx}localedir:${common_objpfx}localedata \
${test_program_prefix_after_env} \
${objpfx}tst-gettext > ${objpfx}tst-gettext.out ${objpfx}domaindir
@@ -152,9 +152,12 @@ CFLAGS-tst_putwc.c += -DOBJPFX=\"$(objpfx)\"
tst_wprintf2-ARGS = "Some Text"
-test-fmemopen-ENV = MALLOC_TRACE=$(objpfx)test-fmemopen.mtrace
-tst-fopenloc-ENV = MALLOC_TRACE=$(objpfx)tst-fopenloc.mtrace
-tst-bz22415-ENV = MALLOC_TRACE=$(objpfx)tst-bz22415.mtrace
+test-fmemopen-ENV = MALLOC_TRACE=$(objpfx)test-fmemopen.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
+tst-fopenloc-ENV = MALLOC_TRACE=$(objpfx)tst-fopenloc.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
+tst-bz22415-ENV = MALLOC_TRACE=$(objpfx)tst-bz22415.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
generated += test-fmemopen.mtrace test-fmemopen.check
generated += tst-fopenloc.mtrace tst-fopenloc.check
@@ -385,7 +385,8 @@ $(INSTALL-SUPPORTED-LOCALES): install-locales-dir
tst-setlocale-ENV = LC_ALL=ja_JP.EUC-JP
tst-wctype-ENV = LC_ALL=ja_JP.EUC-JP
-tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace
+tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace \
+ LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so
$(objpfx)mtrace-tst-leaks.out: $(objpfx)tst-leaks.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-leaks.mtrace > $@; \
$(evaluate-test)
@@ -41,8 +41,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
tests-static := \
tst-interpose-static-nothread \
- tst-interpose-static-thread \
- tst-malloc-usable-static \
+ tst-interpose-static-thread
tests-internal := tst-mallocstate tst-scratch_buffer
@@ -53,8 +52,8 @@ tests-internal += \
tst-dynarray-at-fail \
ifneq (no,$(have-tunables))
-tests += tst-malloc-usable-tunables
-tests-static += tst-malloc-usable-static-tunables
+# tests += tst-malloc-usable-tunables
+# tests-static += tst-malloc-usable-static-tunables
endif
tests += $(tests-static)
@@ -78,7 +77,7 @@ install-lib := libmcheck.a
non-lib.a := libmcheck.a
# Additional library.
-extra-libs = libmemusage
+extra-libs = libmemusage libmalloc-extras
extra-libs-others = $(extra-libs)
# Helper objects for some tests.
@@ -143,6 +142,11 @@ $(objpfx)memusagestat.o: sysincludes = # nothing
endif
endif
+libmalloc-extras-routines = malloc-extras
+LDLIBS-libmalloc-extras.so = $(shared-thread-library)
+LDFLAGS-malloc-extras.so = -Wl,-z,nodelete
+$(objpfx)libmalloc-extras.so: $(libdl)
+
# Another goal which can be used to override the configure decision.
.PHONY: do-memusagestat
do-memusagestat: $(objpfx)memusagestat
@@ -189,10 +193,12 @@ endif
endif
endif
+tst-mtrace-ENV = LD_PRELOAD=$(objpfx)libmalloc-extras.so
tst-mcheck-ENV = MALLOC_CHECK_=3
-tst-malloc-usable-ENV = MALLOC_CHECK_=3
+tst-malloc-usable-ENV = MALLOC_CHECK_=3 LD_PRELOAD=$(objpfx)libmalloc-extras.so
tst-malloc-usable-static-ENV = $(tst-malloc-usable-ENV)
-tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3
+tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 \
+ LD_PRELOAD=$(objdir)libmalloc-extras.so
tst-malloc-usable-static-tunables-ENV = $(tst-malloc-usable-tunables-ENV)
ifeq ($(experimental-malloc),yes)
@@ -239,12 +245,14 @@ $(objpfx)tst-interpose-static-nothread: $(objpfx)tst-interpose-aux-nothread.o
$(objpfx)tst-interpose-static-thread: \
$(objpfx)tst-interpose-aux-thread.o $(static-thread-library)
-tst-dynarray-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray.mtrace
+tst-dynarray-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-dynarray-mem.out: $(objpfx)tst-dynarray.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray.mtrace > $@; \
$(evaluate-test)
-tst-dynarray-fail-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray-fail.mtrace
+tst-dynarray-fail-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray-fail.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-dynarray-fail-mem.out: $(objpfx)tst-dynarray-fail.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray-fail.mtrace > $@; \
$(evaluate-test)
@@ -92,5 +92,8 @@ libc {
__libc_alloc_buffer_copy_bytes;
__libc_alloc_buffer_copy_string;
__libc_alloc_buffer_create_failure;
+
+ __glibc_mtrace_hook;
+ __glibc_mtrace_hook_enabled;
}
}
@@ -29,7 +29,11 @@ malloc_hook_ini (size_t sz, const void *caller)
{
__malloc_hook = NULL;
ptmalloc_init ();
- return __libc_malloc (sz);
+ /* We can't call __libc_malloc because it doesn't support hooks; we
+ have to re-call the interposed malloc which does, if it's present
+ (which it should be, else we wouldn't get here in the first
+ place. */
+ return malloc (sz);
}
static void *
@@ -38,7 +42,7 @@ realloc_hook_ini (void *ptr, size_t sz, const void *caller)
__malloc_hook = NULL;
__realloc_hook = NULL;
ptmalloc_init ();
- return __libc_realloc (ptr, sz);
+ return realloc (ptr, sz);
}
static void *
@@ -46,7 +50,7 @@ memalign_hook_ini (size_t alignment, size_t sz, const void *caller)
{
__memalign_hook = NULL;
ptmalloc_init ();
- return __libc_memalign (alignment, sz);
+ return memalign (alignment, sz);
}
/* Whether we are using malloc checking. */
new file mode 100644
@@ -0,0 +1,814 @@
+/* Provide malloc hook functionality outside of libc.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <atomic.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <malloc.h>
+#include <errno.h>
+#include <libintl.h>
+#include <shlib-compat.h>
+#include <libc-internal.h>
+#include <stdarg.h>
+
+#include "malloc-internal.h"
+#include "mcheck.h"
+
+static void *(*next_malloc) (size_t) = NULL;
+static void (*next_free) (void *) = NULL;
+static void *(*next_realloc) (void *, size_t) = NULL;
+static void *(*next_calloc) (size_t, size_t) = NULL;
+static void *(*next_memalign) (size_t, size_t) = NULL;
+static size_t (*next_malloc_usable_size) (void *) = NULL;
+
+#define TRACE_BUFFER_SIZE 512
+
+static FILE *mallstream;
+static const char mallenv[] = "MALLOC_TRACE";
+static char *malloc_trace_buffer;
+
+__libc_lock_define_initialized (static, lock);
+
+#define TRACE_LOCK(caller, info) \
+ caller = RETURN_ADDRESS (0); \
+ dladdr (caller, &info); \
+ __libc_lock_lock (lock);
+
+#define TRACE_LOCK_N(caller, info) \
+ dladdr (caller, &info); \
+ __libc_lock_lock (lock);
+
+#define TRACE_UNLOCK() \
+ __libc_lock_unlock (lock);
+
+
+/*======================================================================*/
+/* mtrace support. */
+
+static void
+tr_where (const void *caller, Dl_info *info)
+{
+ if (caller != NULL)
+ {
+ if (info != NULL)
+ {
+ fprintf (mallstream, "@ %s%s",
+ info->dli_fname ? : "", info->dli_fname ? ":" : "");
+
+ if (info->dli_sname != NULL)
+ {
+ if (caller > (const void *) info->dli_saddr)
+ fprintf (mallstream, "(%s+0x%lx)", info->dli_sname,
+ (long unsigned)(caller - (const void *) info->dli_saddr));
+ else
+ fprintf (mallstream, "(%s-0x%lx)", info->dli_sname,
+ (long unsigned)((const void *) info->dli_saddr - caller));
+ }
+
+ fprintf (mallstream, "[%p] ", caller);
+ }
+ else
+ fprintf (mallstream, "@ [%p] ", caller);
+ }
+}
+
+
+/*======================================================================*/
+/* mcheck support. */
+
+/* Function to call when something awful happens. */
+static void (*abortfunc) (enum mcheck_status);
+
+static int __malloc_extras_initialized = 0;
+
+/* Arbitrary magical numbers. */
+#define MAGICWORD 0xfedabeeb
+#define MAGICFREE 0xd8675309
+#define MAGICBYTE ((char) 0xd7)
+#define MALLOCFLOOD ((char) 0x93)
+#define FREEFLOOD ((char) 0x95)
+
+struct hdr
+{
+ size_t size; /* Exact size requested by user. */
+ unsigned long int magic; /* Magic number to check header integrity. */
+ struct hdr *prev;
+ struct hdr *next;
+ void *block; /* Real block allocated, for memalign. */
+ unsigned long int magic2; /* Extra, keeps us doubleword aligned. */
+};
+
+/* This is the beginning of the list of all memory blocks allocated.
+ It is only constructed if the pedantic testing is requested. */
+static struct hdr *root;
+
+static int mcheck_used;
+
+/* Nonzero if pedentic checking of all blocks is requested. */
+static int pedantic;
+
+//#if defined _LIBC || defined STDC_HEADERS || defined USG
+//# include <string.h>
+//# define flood memset
+//#else
+static void flood (void *, int, size_t);
+static void
+flood (void *ptr, int val, size_t size)
+{
+ char *cp = ptr;
+ while (size--)
+ *cp++ = val;
+}
+//#endif
+
+static enum mcheck_status
+checkhdr (const struct hdr *hdr)
+{
+ enum mcheck_status status;
+
+ if (!mcheck_used)
+ /* Maybe the mcheck used is disabled? This happens when we find
+ an error and report it. */
+ return MCHECK_OK;
+
+ switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next))
+ {
+ default:
+ status = MCHECK_HEAD;
+ break;
+ case MAGICFREE:
+ status = MCHECK_FREE;
+ break;
+ case MAGICWORD:
+ if (((char *) &hdr[1])[hdr->size] != MAGICBYTE)
+ status = MCHECK_TAIL;
+ else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD)
+ status = MCHECK_HEAD;
+ else
+ status = MCHECK_OK;
+ break;
+ }
+ if (status != MCHECK_OK)
+ {
+ mcheck_used = 0;
+ (*abortfunc) (status);
+ mcheck_used = 1;
+ }
+ return status;
+}
+
+void
+mcheck_check_all (void)
+{
+ /* Walk through all the active blocks and test whether they were tampered
+ with. */
+ struct hdr *runp = root;
+
+ /* Temporarily turn off the checks. */
+ pedantic = 0;
+
+ while (runp != NULL)
+ {
+ (void) checkhdr (runp);
+
+ runp = runp->next;
+ }
+
+ /* Turn checks on again. */
+ pedantic = 1;
+}
+#ifdef _LIBC
+libc_hidden_def (mcheck_check_all)
+#endif
+
+static void
+unlink_blk (struct hdr *ptr)
+{
+ if (ptr->next != NULL)
+ {
+ ptr->next->prev = ptr->prev;
+ ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev
+ + (uintptr_t) ptr->next->next);
+ }
+ if (ptr->prev != NULL)
+ {
+ ptr->prev->next = ptr->next;
+ ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev
+ + (uintptr_t) ptr->prev->next);
+ }
+ else
+ root = ptr->next;
+}
+
+static void
+link_blk (struct hdr *hdr)
+{
+ hdr->prev = NULL;
+ hdr->next = root;
+ root = hdr;
+ hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next;
+
+ /* And the next block. */
+ if (hdr->next != NULL)
+ {
+ hdr->next->prev = hdr;
+ hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr
+ + (uintptr_t) hdr->next->next);
+ }
+}
+
+__attribute__ ((noreturn))
+static void
+mabort (enum mcheck_status status)
+{
+ const char *msg;
+ switch (status)
+ {
+ case MCHECK_OK:
+ msg = _ ("memory is consistent, library is buggy\n");
+ break;
+ case MCHECK_HEAD:
+ msg = _ ("memory clobbered before allocated block\n");
+ break;
+ case MCHECK_TAIL:
+ msg = _ ("memory clobbered past end of allocated block\n");
+ break;
+ case MCHECK_FREE:
+ msg = _ ("block freed twice\n");
+ break;
+ default:
+ msg = _ ("bogus mcheck_status, library is buggy\n");
+ break;
+ }
+#ifdef _LIBC
+ __libc_fatal (msg);
+#else
+ fprintf (stderr, "mcheck: %s", msg);
+ fflush (stderr);
+ abort ();
+#endif
+}
+
+/* Memory barrier so that GCC does not optimize out the argument. */
+#define malloc_opt_barrier(x) \
+ ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; })
+
+int
+mcheck (void (*func) (enum mcheck_status))
+{
+ abortfunc = (func != NULL) ? func : &mabort;
+ setbuf (stdout, NULL);
+
+ /* These hooks may not be safely inserted if malloc is already in use. */
+ if (__malloc_extras_initialized <= 0 && !mcheck_used)
+ {
+ /* We call malloc () once here to ensure it is initialized. */
+ void *p = malloc (0);
+ p = realloc (p, 16);
+ void *p2 = memalign (4, 0);
+ /* GCC might optimize out the malloc/free pair without a barrier. */
+ p = malloc_opt_barrier (p);
+ p2 = malloc_opt_barrier (p2);
+ free (p);
+ free (p2);
+
+ mcheck_used = 1;
+ }
+
+ return mcheck_used ? 0 : -1;
+}
+#ifdef _LIBC
+libc_hidden_def (mcheck)
+#endif
+
+int
+mcheck_pedantic (void (*func) (enum mcheck_status))
+{
+ int res = mcheck (func);
+ if (res == 0)
+ pedantic = 1;
+ return res;
+}
+
+enum mcheck_status
+mprobe (void *ptr)
+{
+ return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED;
+}
+
+/* For these, a round/align of zero means "don't". */
+
+#define cfa() __builtin_return_address (0)
+
+#define MCHECK_PRE(size, align_ptr_to) \
+ if (mcheck_used) \
+ { \
+ if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1) - align_ptr_to) \
+ { \
+ __set_errno (ENOMEM); \
+ return NULL; \
+ } \
+ size += align_ptr_to; \
+ size += sizeof (struct hdr) + 1; \
+ }
+
+
+#define MCHECK_POST_NOFLOOD(ptr, original_size, align_to) \
+ if (mcheck_used) \
+ { \
+ void *block = (void *) ptr; \
+ struct hdr *hdr = (struct hdr *) block; \
+ ptr = (void *) (hdr + 1); \
+ if (align_to) \
+ { \
+ ptr = PTR_ALIGN_UP (ptr, align_to); \
+ hdr = (struct hdr *)ptr - 1; \
+ } \
+ hdr->size = original_size; \
+ link_blk (hdr); \
+ hdr->block = (void *) block; \
+ hdr->magic2 = (uintptr_t) block ^ MAGICWORD; \
+ ((char *) &hdr[1])[original_size] = MAGICBYTE; \
+ }
+
+
+#define MCHECK_POST(ptr, original_size, align_to) \
+ if (mcheck_used) \
+ { \
+ MCHECK_POST_NOFLOOD (ptr, original_size, align_to); \
+ flood (ptr, MALLOCFLOOD, original_size); \
+ }
+
+#define MCHECK_PREFREE(ptr) \
+ if (mcheck_used && ptr != NULL) \
+ { \
+ struct hdr *hdr = ((struct hdr *) ptr) - 1; \
+ checkhdr (hdr); \
+ hdr->magic = MAGICFREE; \
+ hdr->magic2 = MAGICFREE; \
+ unlink_blk (hdr); \
+ hdr->prev = hdr->next = NULL; \
+ flood (ptr, FREEFLOOD, hdr->size); \
+ ptr = hdr->block; \
+ }
+
+/*======================================================================*/
+/* mallwatch, hooks */
+
+/* Address to breakpoint on accesses to... */
+void *mallwatch = (void *) (-1);
+
+/* This function is called when the block being alloc'd, realloc'd, or
+ freed has an address matching the variable "mallwatch". In a debugger,
+ set "mallwatch" to the address of interest, then put a breakpoint on
+ tr_break. */
+
+extern void tr_break (void) __THROW;
+libc_hidden_proto (tr_break)
+void __attribute__ ((noinline))
+tr_break (void)
+{
+ /* Never optimize it away either. */
+ asm ("");
+}
+libc_hidden_def (tr_break)
+
+static int recursing = 0;
+
+#define HOOK(func, erv) \
+ if (next_##func == NULL) \
+ { \
+ int r; \
+ r = atomic_load_acquire (&recursing); \
+ if (r) \
+ return erv; \
+ __typeof__ (next_##func) tmp; \
+ atomic_add (&recursing, 1); \
+ tmp = dlsym (RTLD_NEXT, #func); \
+ atomic_store_relaxed (&next_##func, tmp); \
+ if (tmp == NULL) \
+ abort (); \
+ atomic_add (&recursing, -1); \
+ atomic_store_relaxed (&__malloc_extras_initialized, 1); \
+ }
+
+void *
+malloc (size_t sz)
+{
+ void *rv;
+ size_t original_size = sz;
+ void *caller;
+ Dl_info info;
+
+ void *(*hook) (size_t, const void *)
+ = atomic_forced_read (__malloc_hook);
+ if (__builtin_expect (hook != NULL, 0))
+ return (*hook) (sz, RETURN_ADDRESS (0));
+
+ HOOK (malloc, NULL);
+
+ MCHECK_PRE (sz, 0);
+
+ rv = next_malloc (sz);
+
+ MCHECK_POST (rv, original_size, 0);
+
+ if (mallstream)
+ {
+ TRACE_LOCK (caller, info);
+ tr_where (caller, &info);
+ /* We could be printing a NULL here; that's OK. */
+ fprintf (mallstream, "+ %p %#lx\n", rv, (unsigned long int) original_size);
+ TRACE_UNLOCK ();
+ }
+
+ if (rv == mallwatch)
+ tr_break ();
+
+ return rv;
+}
+
+void
+free (void *ptr)
+{
+ void *caller;
+ Dl_info info;
+
+ if (ptr == NULL)
+ return;
+
+ void (*hook) (void *, const void *)
+ = atomic_forced_read (__free_hook);
+ if (__builtin_expect (hook != NULL, 0))
+ {
+ (*hook)(ptr, RETURN_ADDRESS (0));
+ return;
+ }
+
+ HOOK (free, );
+
+ if (mallstream && ptr)
+ {
+ TRACE_LOCK (caller, info);
+ tr_where (caller, &info);
+ /* We could be printing a NULL here; that's OK. */
+ fprintf (mallstream, "- %p\n", ptr);
+ TRACE_UNLOCK ();
+ }
+
+ if (ptr == mallwatch)
+ tr_break ();
+
+ MCHECK_PREFREE (ptr);
+
+ next_free (ptr);
+
+ return;
+}
+
+void *
+realloc (void *ptr, size_t sz)
+{
+ void *rv;
+ size_t original_size = sz;
+ void *caller;
+ Dl_info info;
+
+ void *(*hook) (void *, size_t, const void *) =
+ atomic_forced_read (__realloc_hook);
+ if (__builtin_expect (hook != NULL, 0))
+ return (*hook)(ptr, sz, RETURN_ADDRESS (0));
+
+ HOOK (realloc, NULL);
+
+ if (ptr == mallwatch)
+ tr_break ();
+
+ MCHECK_PREFREE (ptr);
+ MCHECK_PRE (sz, 0);
+
+ rv = next_realloc (ptr, sz);
+
+ MCHECK_POST (ptr, original_size, 0);
+
+ if (mallstream)
+ {
+ TRACE_LOCK (caller, info);
+ tr_where (caller, &info);
+
+ if (rv == NULL)
+ {
+ if (original_size != 0)
+ /* Failed realloc. */
+ fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) original_size);
+ else
+ fprintf (mallstream, "- %p\n", ptr);
+ }
+ else if (ptr == NULL)
+ fprintf (mallstream, "+ %p %#lx\n", rv, (unsigned long int) original_size);
+ else
+ {
+ fprintf (mallstream, "< %p\n", ptr);
+ tr_where (caller, &info);
+ fprintf (mallstream, "> %p %#lx\n", rv, (unsigned long int) original_size);
+ }
+
+ TRACE_UNLOCK ();
+ }
+
+ if (rv == mallwatch)
+ tr_break ();
+
+ return rv;
+}
+
+void *
+calloc (size_t sz1, size_t sz2)
+{
+ void *rv;
+ size_t original_size;
+ size_t sz = sz1 * sz2;
+ void *caller;
+ Dl_info info;
+
+#define HALF_INTERNAL_SIZE_T \
+ (((size_t) 1) << (8 * sizeof (size_t) / 2))
+ if (__builtin_expect ((sz1 | sz2) >= HALF_INTERNAL_SIZE_T, 0))
+ {
+ if (sz1 != 0 && sz / sz1 != sz2)
+ {
+ __set_errno (ENOMEM);
+ return 0;
+ }
+ }
+
+ original_size = sz;
+
+ void *(*hook) (size_t, const void *) =
+ atomic_forced_read (__malloc_hook);
+ if (__builtin_expect (hook != NULL, 0))
+ {
+ rv = (*hook)(sz, RETURN_ADDRESS (0));
+ if (rv == 0)
+ return 0;
+
+ memset (rv, 0, sz);
+
+ if (rv == mallwatch)
+ tr_break ();
+
+ return rv;
+ }
+
+ HOOK (calloc, NULL);
+
+ MCHECK_PRE (sz, 0);
+
+ rv = next_calloc (sz, 1);
+
+ MCHECK_POST_NOFLOOD (rv, sz, 0);
+
+ if (mallstream)
+ {
+ TRACE_LOCK (caller, info);
+ tr_where (caller, &info);
+ /* We could be printing a NULL here; that's OK. */
+ fprintf (mallstream, "+ %p %#lx\n", rv, (unsigned long int) original_size);
+ TRACE_UNLOCK ();
+ }
+
+ if (rv == mallwatch)
+ tr_break ();
+
+ return rv;
+}
+
+static void *
+memalign_common (size_t sz1, size_t sz2, void *caller)
+{
+ void *rv;
+ size_t original_size = sz2;
+ Dl_info info;
+
+ void *(*hook) (size_t, size_t, const void *) =
+ atomic_forced_read (__memalign_hook);
+ if (__builtin_expect (hook != NULL, 0))
+ return (*hook)(sz1, sz2, RETURN_ADDRESS (0));
+
+ HOOK (memalign, NULL);
+
+ MCHECK_PRE (sz2, sz1);
+
+ rv = next_memalign (sz1, sz2);
+
+ MCHECK_POST (rv, original_size, sz1);
+
+ if (mallstream)
+ {
+ TRACE_LOCK_N (caller, info);
+ tr_where (caller, &info);
+ /* We could be printing a NULL here; that's OK. */
+ fprintf (mallstream, "+ %p %#lx\n", rv, (unsigned long int) original_size);
+ TRACE_UNLOCK ();
+ }
+
+ if (rv == mallwatch)
+ tr_break ();
+
+ return rv;
+}
+
+void *
+memalign (size_t sz1, size_t sz2)
+{
+ return memalign_common (sz1, sz2, RETURN_ADDRESS (0));
+}
+
+int
+posix_memalign (void **memptr, size_t alignment, size_t size)
+{
+ void *rvptr;
+
+ /* Test whether the SIZE argument is valid. It must be a power of
+ two multiple of sizeof (void *). */
+ if (alignment % sizeof (void *) != 0
+ || !powerof2 (alignment / sizeof (void *))
+ || alignment == 0)
+ return EINVAL;
+
+ rvptr = memalign_common (alignment, size, RETURN_ADDRESS (0));
+
+ if (rvptr)
+ {
+ *memptr = rvptr;
+ return 0;
+ }
+
+ return ENOMEM;
+}
+
+static size_t remembered_pagesize = 0;
+
+static inline size_t
+pagesize (void)
+{
+ size_t rv = atomic_load_relaxed (&remembered_pagesize);
+ if (remembered_pagesize == 0)
+ {
+ rv = sysconf (_SC_PAGESIZE);
+ atomic_store_relaxed (&remembered_pagesize, rv);
+ }
+ return rv;
+}
+
+void *
+valloc (size_t sz)
+{
+ return memalign_common (sysconf (_SC_PAGESIZE), sz, RETURN_ADDRESS (0));
+}
+
+void *
+pvalloc (size_t sz)
+{
+ size_t rounded_bytes = ALIGN_UP (sz, pagesize ());
+ return memalign_common (pagesize (), rounded_bytes, RETURN_ADDRESS (0));
+}
+
+size_t
+malloc_usable_size (void *ptr)
+{
+ size_t rv;
+
+ HOOK (malloc_usable_size, 0);
+
+ if (!mcheck_used)
+ rv = next_malloc_usable_size (ptr);
+ else
+ {
+ struct hdr *hdr = (struct hdr *) ptr;
+ -- hdr;
+ rv = hdr->size;
+ }
+
+ return rv;
+}
+
+/*======================================================================*/
+
+/* This function gets called to make sure all memory the library
+ allocates get freed and so does not irritate the user when studying
+ the mtrace output. */
+static void
+release_libc_mem (void)
+{
+ /* Only call the free function if we still are running in mtrace mode. */
+ if (mallstream != NULL)
+ __libc_freeres ();
+}
+
+
+static void
+local_mtrace (void)
+{
+ char *mallfile;
+ static int added_atexit_handler = 0;
+
+ /* Don't panic if we're called more than once. */
+ if (mallstream != NULL)
+ {
+ fprintf (mallstream, "= Restart?\n");
+ return;
+ }
+
+ mallfile = getenv (mallenv);
+ if (mallfile != NULL || mallwatch != NULL)
+ {
+ char *mtb = malloc (TRACE_BUFFER_SIZE);
+ if (mtb == NULL)
+ return;
+
+ mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
+ if (mallstream != NULL)
+ {
+ /* Be sure it doesn't malloc its buffer! */
+ malloc_trace_buffer = mtb;
+ setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
+ fprintf (mallstream, "= Start\n");
+
+ if (!added_atexit_handler)
+ {
+ added_atexit_handler = 1;
+ atexit ((void (*)(void))release_libc_mem);
+ }
+
+ }
+ else
+ free (mtb);
+ }
+}
+
+static void
+local_muntrace (void)
+{
+ if (mallstream == NULL)
+ return;
+
+ /* Do the reverse of what done in mtrace: first reset the hooks and
+ MALLSTREAM, and only after that write the trailer and close the
+ file. */
+ FILE *f = mallstream;
+ mallstream = NULL;
+
+ fprintf (f, "= End\n");
+ fclose (f);
+}
+
+/*======================================================================*/
+/* user interface */
+
+extern void (*__glibc_mtrace_hook)(int);
+extern int __glibc_mtrace_hook_enabled;
+
+void
+__glibc_mtrace_hook_func (int enable_it)
+{
+ if (enable_it)
+ local_mtrace ();
+ else
+ local_muntrace ();
+}
+
+void __attribute__ ((constructor))
+mtrace_extras_ctor (void)
+{
+ __glibc_mtrace_hook = __glibc_mtrace_hook_func;
+ if (__glibc_mtrace_hook_enabled)
+ __glibc_mtrace_hook (1);
+}
+
+void __attribute__ ((destructor))
+mtrace_extras_dtor (void)
+{
+ if (mallstream)
+ {
+ __libc_freeres ();
+ fprintf (mallstream, "= Unload?\n");
+ }
+}
@@ -463,6 +463,12 @@ void *(*__morecore)(ptrdiff_t) = __default_morecore;
# define HAVE_MALLOC_INIT_HOOK 0
#endif
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_24)
+# define HAVE_MALLOC_HOOKS 1
+#else
+# define HAVE_MALLOC_HOOKS 0
+#endif
+
/*
This version of malloc supports the standard SVID/XPG mallinfo
@@ -1014,8 +1020,6 @@ static void* _mid_memalign(size_t, size_t, void *);
static void malloc_printerr(const char *str) __attribute__ ((noreturn));
-static void* mem2mem_check(void *p, size_t sz);
-static void top_check(void);
static void munmap_chunk(mchunkptr p);
#if HAVE_MREMAP
static mchunkptr mremap_chunk(mchunkptr p, size_t new_size);
@@ -1847,6 +1851,12 @@ static void malloc_consolidate (mstate);
# define weak_variable weak_function
#endif
+#if HAVE_MALLOC_INIT_HOOK
+void weak_variable (*__malloc_initialize_hook) (void) = NULL;
+compat_symbol (libc, __malloc_initialize_hook,
+ __malloc_initialize_hook, GLIBC_2_0);
+#endif
+
/* Forward declarations. */
static void *malloc_hook_ini (size_t sz,
const void *caller) __THROW;
@@ -1855,11 +1865,6 @@ static void *realloc_hook_ini (void *ptr, size_t sz,
static void *memalign_hook_ini (size_t alignment, size_t sz,
const void *caller) __THROW;
-#if HAVE_MALLOC_INIT_HOOK
-void weak_variable (*__malloc_initialize_hook) (void) = NULL;
-compat_symbol (libc, __malloc_initialize_hook,
- __malloc_initialize_hook, GLIBC_2_0);
-#endif
void weak_variable (*__free_hook) (void *__ptr,
const void *) = NULL;
@@ -2490,14 +2495,7 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av)
LIBC_PROBE (memory_sbrk_more, 2, brk, size);
}
- if (brk != (char *) (MORECORE_FAILURE))
- {
- /* Call the `morecore' hook if necessary. */
- void (*hook) (void) = atomic_forced_read (__after_morecore_hook);
- if (__builtin_expect (hook != NULL, 0))
- (*hook)();
- }
- else
+ if (brk == (char *) (MORECORE_FAILURE))
{
/*
If have mmap, try using it as a backup when MORECORE fails or
@@ -2634,13 +2632,6 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av)
correction = 0;
snd_brk = (char *) (MORECORE (0));
}
- else
- {
- /* Call the `morecore' hook if necessary. */
- void (*hook) (void) = atomic_forced_read (__after_morecore_hook);
- if (__builtin_expect (hook != NULL, 0))
- (*hook)();
- }
}
/* handle non-contiguous cases */
@@ -2798,10 +2789,6 @@ systrim (size_t pad, mstate av)
*/
MORECORE (-extra);
- /* Call the `morecore' hook if necessary. */
- void (*hook) (void) = atomic_forced_read (__after_morecore_hook);
- if (__builtin_expect (hook != NULL, 0))
- (*hook)();
new_brk = (char *) (MORECORE (0));
LIBC_PROBE (memory_sbrk_less, 2, new_brk, extra);
@@ -3029,10 +3016,8 @@ __libc_malloc (size_t bytes)
mstate ar_ptr;
void *victim;
- void *(*hook) (size_t, const void *)
- = atomic_forced_read (__malloc_hook);
- if (__builtin_expect (hook != NULL, 0))
- return (*hook)(bytes, RETURN_ADDRESS (0));
+ if (__malloc_initialized < 0)
+ ptmalloc_init ();
#if USE_TCACHE
/* int_free also calls request2size, be careful to not pad twice. */
size_t tbytes;
@@ -3087,13 +3072,8 @@ __libc_free (void *mem)
mstate ar_ptr;
mchunkptr p; /* chunk corresponding to mem */
- void (*hook) (void *, const void *)
- = atomic_forced_read (__free_hook);
- if (__builtin_expect (hook != NULL, 0))
- {
- (*hook)(mem, RETURN_ADDRESS (0));
- return;
- }
+ if (__malloc_initialized < 0)
+ ptmalloc_init ();
if (mem == 0) /* free(0) has no effect */
return;
@@ -3133,10 +3113,8 @@ __libc_realloc (void *oldmem, size_t bytes)
void *newp; /* chunk to return */
- void *(*hook) (void *, size_t, const void *) =
- atomic_forced_read (__realloc_hook);
- if (__builtin_expect (hook != NULL, 0))
- return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));
+ if (__malloc_initialized < 0)
+ ptmalloc_init ();
#if REALLOC_ZERO_BYTES_FREES
if (bytes == 0 && oldmem != NULL)
@@ -3262,11 +3240,6 @@ _mid_memalign (size_t alignment, size_t bytes, void *address)
mstate ar_ptr;
void *p;
- void *(*hook) (size_t, size_t, const void *) =
- atomic_forced_read (__memalign_hook);
- if (__builtin_expect (hook != NULL, 0))
- return (*hook)(alignment, bytes, address);
-
/* If we need less alignment than we give anyway, just relay to malloc. */
if (alignment <= MALLOC_ALIGNMENT)
return __libc_malloc (bytes);
@@ -3385,17 +3358,8 @@ __libc_calloc (size_t n, size_t elem_size)
}
}
- void *(*hook) (size_t, const void *) =
- atomic_forced_read (__malloc_hook);
- if (__builtin_expect (hook != NULL, 0))
- {
- sz = bytes;
- mem = (*hook)(sz, RETURN_ADDRESS (0));
- if (mem == 0)
- return 0;
-
- return memset (mem, 0, sz);
- }
+ if (__malloc_initialized < 0)
+ ptmalloc_init ();
sz = bytes;
@@ -27,374 +27,19 @@
# include <errno.h>
#endif
-/* Old hook values. */
-static void (*old_free_hook)(void *ptr, const void *);
-static void *(*old_malloc_hook) (size_t size, const void *);
-static void *(*old_memalign_hook) (size_t alignment, size_t size,
- const void *);
-static void *(*old_realloc_hook) (void *ptr, size_t size,
- const void *);
-
-/* Function to call when something awful happens. */
-static void (*abortfunc) (enum mcheck_status);
-
-/* Arbitrary magical numbers. */
-#define MAGICWORD 0xfedabeeb
-#define MAGICFREE 0xd8675309
-#define MAGICBYTE ((char) 0xd7)
-#define MALLOCFLOOD ((char) 0x93)
-#define FREEFLOOD ((char) 0x95)
-
-struct hdr
-{
- size_t size; /* Exact size requested by user. */
- unsigned long int magic; /* Magic number to check header integrity. */
- struct hdr *prev;
- struct hdr *next;
- void *block; /* Real block allocated, for memalign. */
- unsigned long int magic2; /* Extra, keeps us doubleword aligned. */
-};
-
-/* This is the beginning of the list of all memory blocks allocated.
- It is only constructed if the pedantic testing is requested. */
-static struct hdr *root;
-
-static int mcheck_used;
-
-/* Nonzero if pedentic checking of all blocks is requested. */
-static int pedantic;
-
-#if defined _LIBC || defined STDC_HEADERS || defined USG
-# include <string.h>
-# define flood memset
-#else
-static void flood (void *, int, size_t);
-static void
-flood (void *ptr, int val, size_t size)
-{
- char *cp = ptr;
- while (size--)
- *cp++ = val;
-}
-#endif
-
-static enum mcheck_status
-checkhdr (const struct hdr *hdr)
-{
- enum mcheck_status status;
-
- if (!mcheck_used)
- /* Maybe the mcheck used is disabled? This happens when we find
- an error and report it. */
- return MCHECK_OK;
-
- switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next))
- {
- default:
- status = MCHECK_HEAD;
- break;
- case MAGICFREE:
- status = MCHECK_FREE;
- break;
- case MAGICWORD:
- if (((char *) &hdr[1])[hdr->size] != MAGICBYTE)
- status = MCHECK_TAIL;
- else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD)
- status = MCHECK_HEAD;
- else
- status = MCHECK_OK;
- break;
- }
- if (status != MCHECK_OK)
- {
- mcheck_used = 0;
- (*abortfunc) (status);
- mcheck_used = 1;
- }
- return status;
-}
-
void
mcheck_check_all (void)
{
- /* Walk through all the active blocks and test whether they were tampered
- with. */
- struct hdr *runp = root;
-
- /* Temporarily turn off the checks. */
- pedantic = 0;
-
- while (runp != NULL)
- {
- (void) checkhdr (runp);
-
- runp = runp->next;
- }
-
- /* Turn checks on again. */
- pedantic = 1;
}
#ifdef _LIBC
libc_hidden_def (mcheck_check_all)
#endif
-static void
-unlink_blk (struct hdr *ptr)
-{
- if (ptr->next != NULL)
- {
- ptr->next->prev = ptr->prev;
- ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev
- + (uintptr_t) ptr->next->next);
- }
- if (ptr->prev != NULL)
- {
- ptr->prev->next = ptr->next;
- ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev
- + (uintptr_t) ptr->prev->next);
- }
- else
- root = ptr->next;
-}
-
-static void
-link_blk (struct hdr *hdr)
-{
- hdr->prev = NULL;
- hdr->next = root;
- root = hdr;
- hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next;
-
- /* And the next block. */
- if (hdr->next != NULL)
- {
- hdr->next->prev = hdr;
- hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr
- + (uintptr_t) hdr->next->next);
- }
-}
-static void
-freehook (void *ptr, const void *caller)
-{
- if (pedantic)
- mcheck_check_all ();
- if (ptr)
- {
- struct hdr *hdr = ((struct hdr *) ptr) - 1;
- checkhdr (hdr);
- hdr->magic = MAGICFREE;
- hdr->magic2 = MAGICFREE;
- unlink_blk (hdr);
- hdr->prev = hdr->next = NULL;
- flood (ptr, FREEFLOOD, hdr->size);
- ptr = hdr->block;
- }
- __free_hook = old_free_hook;
- if (old_free_hook != NULL)
- (*old_free_hook)(ptr, caller);
- else
- free (ptr);
- __free_hook = freehook;
-}
-
-static void *
-mallochook (size_t size, const void *caller)
-{
- struct hdr *hdr;
-
- if (pedantic)
- mcheck_check_all ();
-
- if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1))
- {
- __set_errno (ENOMEM);
- return NULL;
- }
-
- __malloc_hook = old_malloc_hook;
- if (old_malloc_hook != NULL)
- hdr = (struct hdr *) (*old_malloc_hook)(sizeof (struct hdr) + size + 1,
- caller);
- else
- hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1);
- __malloc_hook = mallochook;
- if (hdr == NULL)
- return NULL;
-
- hdr->size = size;
- link_blk (hdr);
- hdr->block = hdr;
- hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
- ((char *) &hdr[1])[size] = MAGICBYTE;
- flood ((void *) (hdr + 1), MALLOCFLOOD, size);
- return (void *) (hdr + 1);
-}
-
-static void *
-memalignhook (size_t alignment, size_t size,
- const void *caller)
-{
- struct hdr *hdr;
- size_t slop;
- char *block;
-
- if (pedantic)
- mcheck_check_all ();
-
- slop = (sizeof *hdr + alignment - 1) & - alignment;
-
- if (size > ~((size_t) 0) - (slop + 1))
- {
- __set_errno (ENOMEM);
- return NULL;
- }
-
- __memalign_hook = old_memalign_hook;
- if (old_memalign_hook != NULL)
- block = (*old_memalign_hook)(alignment, slop + size + 1, caller);
- else
- block = memalign (alignment, slop + size + 1);
- __memalign_hook = memalignhook;
- if (block == NULL)
- return NULL;
-
- hdr = ((struct hdr *) (block + slop)) - 1;
-
- hdr->size = size;
- link_blk (hdr);
- hdr->block = (void *) block;
- hdr->magic2 = (uintptr_t) block ^ MAGICWORD;
- ((char *) &hdr[1])[size] = MAGICBYTE;
- flood ((void *) (hdr + 1), MALLOCFLOOD, size);
- return (void *) (hdr + 1);
-}
-
-static void *
-reallochook (void *ptr, size_t size, const void *caller)
-{
- if (size == 0)
- {
- freehook (ptr, caller);
- return NULL;
- }
-
- struct hdr *hdr;
- size_t osize;
-
- if (pedantic)
- mcheck_check_all ();
-
- if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1))
- {
- __set_errno (ENOMEM);
- return NULL;
- }
-
- if (ptr)
- {
- hdr = ((struct hdr *) ptr) - 1;
- osize = hdr->size;
-
- checkhdr (hdr);
- unlink_blk (hdr);
- if (size < osize)
- flood ((char *) ptr + size, FREEFLOOD, osize - size);
- }
- else
- {
- osize = 0;
- hdr = NULL;
- }
- __free_hook = old_free_hook;
- __malloc_hook = old_malloc_hook;
- __memalign_hook = old_memalign_hook;
- __realloc_hook = old_realloc_hook;
- if (old_realloc_hook != NULL)
- hdr = (struct hdr *) (*old_realloc_hook)((void *) hdr,
- sizeof (struct hdr) + size + 1,
- caller);
- else
- hdr = (struct hdr *) realloc ((void *) hdr,
- sizeof (struct hdr) + size + 1);
- __free_hook = freehook;
- __malloc_hook = mallochook;
- __memalign_hook = memalignhook;
- __realloc_hook = reallochook;
- if (hdr == NULL)
- return NULL;
-
- hdr->size = size;
- link_blk (hdr);
- hdr->block = hdr;
- hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
- ((char *) &hdr[1])[size] = MAGICBYTE;
- if (size > osize)
- flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
- return (void *) (hdr + 1);
-}
-
-__attribute__ ((noreturn))
-static void
-mabort (enum mcheck_status status)
-{
- const char *msg;
- switch (status)
- {
- case MCHECK_OK:
- msg = _ ("memory is consistent, library is buggy\n");
- break;
- case MCHECK_HEAD:
- msg = _ ("memory clobbered before allocated block\n");
- break;
- case MCHECK_TAIL:
- msg = _ ("memory clobbered past end of allocated block\n");
- break;
- case MCHECK_FREE:
- msg = _ ("block freed twice\n");
- break;
- default:
- msg = _ ("bogus mcheck_status, library is buggy\n");
- break;
- }
-#ifdef _LIBC
- __libc_fatal (msg);
-#else
- fprintf (stderr, "mcheck: %s", msg);
- fflush (stderr);
- abort ();
-#endif
-}
-
-/* Memory barrier so that GCC does not optimize out the argument. */
-#define malloc_opt_barrier(x) \
- ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; })
int
mcheck (void (*func) (enum mcheck_status))
{
- abortfunc = (func != NULL) ? func : &mabort;
-
- /* These hooks may not be safely inserted if malloc is already in use. */
- if (__malloc_initialized <= 0 && !mcheck_used)
- {
- /* We call malloc() once here to ensure it is initialized. */
- void *p = malloc (0);
- /* GCC might optimize out the malloc/free pair without a barrier. */
- p = malloc_opt_barrier (p);
- free (p);
-
- old_free_hook = __free_hook;
- __free_hook = freehook;
- old_malloc_hook = __malloc_hook;
- __malloc_hook = mallochook;
- old_memalign_hook = __memalign_hook;
- __memalign_hook = memalignhook;
- old_realloc_hook = __realloc_hook;
- __realloc_hook = reallochook;
- mcheck_used = 1;
- }
-
- return mcheck_used ? 0 : -1;
+ return 0;
}
#ifdef _LIBC
libc_hidden_def (mcheck)
@@ -403,14 +48,11 @@ libc_hidden_def (mcheck)
int
mcheck_pedantic (void (*func) (enum mcheck_status))
{
- int res = mcheck (func);
- if (res == 0)
- pedantic = 1;
- return res;
+ return 0;
}
enum mcheck_status
mprobe (void *ptr)
{
- return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED;
+ return MCHECK_DISABLED;
}
@@ -37,30 +37,12 @@
#include <dso_handle.h>
#include <libio/iolibio.h>
-#define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
-#define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
#include <kernel-features.h>
-#define TRACE_BUFFER_SIZE 512
-
-static FILE *mallstream;
-static const char mallenv[] = "MALLOC_TRACE";
-static char *malloc_trace_buffer;
-
-__libc_lock_define_initialized (static, lock);
-
/* Address to breakpoint on accesses to... */
void *mallwatch;
-/* Old hook values. */
-static void (*tr_old_free_hook) (void *ptr, const void *);
-static void *(*tr_old_malloc_hook) (size_t size, const void *);
-static void *(*tr_old_realloc_hook) (void *ptr, size_t size,
- const void *);
-static void *(*tr_old_memalign_hook) (size_t __alignment, size_t __size,
- const void *);
-
/* This function is called when the block being alloc'd, realloc'd, or
freed has an address matching the variable "mallwatch". In a debugger,
set "mallwatch" to the address of interest, then put a breakpoint on
@@ -74,275 +56,26 @@ tr_break (void)
}
libc_hidden_def (tr_break)
-static void
-tr_where (const void *caller, Dl_info *info)
-{
- if (caller != NULL)
- {
- if (info != NULL)
- {
- char *buf = (char *) "";
- if (info->dli_sname != NULL)
- {
- size_t len = strlen (info->dli_sname);
- buf = alloca (len + 6 + 2 * sizeof (void *));
-
- buf[0] = '(';
- __stpcpy (_fitoa (caller >= (const void *) info->dli_saddr
- ? caller - (const void *) info->dli_saddr
- : (const void *) info->dli_saddr - caller,
- __stpcpy (__mempcpy (buf + 1, info->dli_sname,
- len),
- caller >= (void *) info->dli_saddr
- ? "+0x" : "-0x"),
- 16, 0),
- ")");
- }
-
- fprintf (mallstream, "@ %s%s%s[%p] ",
- info->dli_fname ? : "", info->dli_fname ? ":" : "",
- buf, caller);
- }
- else
- fprintf (mallstream, "@ [%p] ", caller);
- }
-}
-
-static Dl_info *
-lock_and_info (const void *caller, Dl_info *mem)
-{
- if (caller == NULL)
- return NULL;
-
- Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL;
-
- __libc_lock_lock (lock);
-
- return res;
-}
-
-static void
-tr_freehook (void *ptr, const void *caller)
-{
- if (ptr == NULL)
- return;
-
- Dl_info mem;
- Dl_info *info = lock_and_info (caller, &mem);
- tr_where (caller, info);
- /* Be sure to print it first. */
- fprintf (mallstream, "- %p\n", ptr);
- if (ptr == mallwatch)
- {
- __libc_lock_unlock (lock);
- tr_break ();
- __libc_lock_lock (lock);
- }
- __free_hook = tr_old_free_hook;
- if (tr_old_free_hook != NULL)
- (*tr_old_free_hook)(ptr, caller);
- else
- free (ptr);
- __free_hook = tr_freehook;
- __libc_lock_unlock (lock);
-}
-
-static void *
-tr_mallochook (size_t size, const void *caller)
-{
- void *hdr;
-
- Dl_info mem;
- Dl_info *info = lock_and_info (caller, &mem);
-
- __malloc_hook = tr_old_malloc_hook;
- if (tr_old_malloc_hook != NULL)
- hdr = (void *) (*tr_old_malloc_hook)(size, caller);
- else
- hdr = (void *) malloc (size);
- __malloc_hook = tr_mallochook;
-
- tr_where (caller, info);
- /* We could be printing a NULL here; that's OK. */
- fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
-
- __libc_lock_unlock (lock);
-
- if (hdr == mallwatch)
- tr_break ();
-
- return hdr;
-}
-
-static void *
-tr_reallochook (void *ptr, size_t size, const void *caller)
-{
- void *hdr;
-
- if (ptr == mallwatch)
- tr_break ();
-
- Dl_info mem;
- Dl_info *info = lock_and_info (caller, &mem);
-
- __free_hook = tr_old_free_hook;
- __malloc_hook = tr_old_malloc_hook;
- __realloc_hook = tr_old_realloc_hook;
- if (tr_old_realloc_hook != NULL)
- hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller);
- else
- hdr = (void *) realloc (ptr, size);
- __free_hook = tr_freehook;
- __malloc_hook = tr_mallochook;
- __realloc_hook = tr_reallochook;
-
- tr_where (caller, info);
- if (hdr == NULL)
- {
- if (size != 0)
- /* Failed realloc. */
- fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
- else
- fprintf (mallstream, "- %p\n", ptr);
- }
- else if (ptr == NULL)
- fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
- else
- {
- fprintf (mallstream, "< %p\n", ptr);
- tr_where (caller, info);
- fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
- }
-
- __libc_lock_unlock (lock);
-
- if (hdr == mallwatch)
- tr_break ();
-
- return hdr;
-}
-
-static void *
-tr_memalignhook (size_t alignment, size_t size, const void *caller)
-{
- void *hdr;
-
- Dl_info mem;
- Dl_info *info = lock_and_info (caller, &mem);
-
- __memalign_hook = tr_old_memalign_hook;
- __malloc_hook = tr_old_malloc_hook;
- if (tr_old_memalign_hook != NULL)
- hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller);
- else
- hdr = (void *) memalign (alignment, size);
- __memalign_hook = tr_memalignhook;
- __malloc_hook = tr_mallochook;
-
- tr_where (caller, info);
- /* We could be printing a NULL here; that's OK. */
- fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
-
- __libc_lock_unlock (lock);
-
- if (hdr == mallwatch)
- tr_break ();
-
- return hdr;
-}
-
-
-#ifdef _LIBC
-
-/* This function gets called to make sure all memory the library
- allocates get freed and so does not irritate the user when studying
- the mtrace output. */
-static void __libc_freeres_fn_section
-release_libc_mem (void)
-{
- /* Only call the free function if we still are running in mtrace mode. */
- if (mallstream != NULL)
- __libc_freeres ();
-}
-#endif
-
-
/* We enable tracing if either the environment variable MALLOC_TRACE
is set, or if the variable mallwatch has been patched to an address
that the debugging user wants us to stop on. When patching mallwatch,
don't forget to set a breakpoint on tr_break! */
+void (*__glibc_mtrace_hook)(int) = NULL;
+int __glibc_mtrace_hook_enabled = 0;
+
void
mtrace (void)
{
-#ifdef _LIBC
- static int added_atexit_handler;
-#endif
- char *mallfile;
-
- /* Don't panic if we're called more than once. */
- if (mallstream != NULL)
- return;
-
-#ifdef _LIBC
- /* When compiling the GNU libc we use the secure getenv function
- which prevents the misuse in case of SUID or SGID enabled
- programs. */
- mallfile = __libc_secure_getenv (mallenv);
-#else
- mallfile = getenv (mallenv);
-#endif
- if (mallfile != NULL || mallwatch != NULL)
- {
- char *mtb = malloc (TRACE_BUFFER_SIZE);
- if (mtb == NULL)
- return;
-
- mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
- if (mallstream != NULL)
- {
- /* Be sure it doesn't malloc its buffer! */
- malloc_trace_buffer = mtb;
- setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
- fprintf (mallstream, "= Start\n");
- tr_old_free_hook = __free_hook;
- __free_hook = tr_freehook;
- tr_old_malloc_hook = __malloc_hook;
- __malloc_hook = tr_mallochook;
- tr_old_realloc_hook = __realloc_hook;
- __realloc_hook = tr_reallochook;
- tr_old_memalign_hook = __memalign_hook;
- __memalign_hook = tr_memalignhook;
-#ifdef _LIBC
- if (!added_atexit_handler)
- {
- added_atexit_handler = 1;
- __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
- __dso_handle);
- }
-#endif
- }
- else
- free (mtb);
- }
+ if (__glibc_mtrace_hook)
+ __glibc_mtrace_hook(1);
+ __glibc_mtrace_hook_enabled = 1;
}
void
muntrace (void)
{
- if (mallstream == NULL)
- return;
-
- /* Do the reverse of what done in mtrace: first reset the hooks and
- MALLSTREAM, and only after that write the trailer and close the
- file. */
- FILE *f = mallstream;
- mallstream = NULL;
- __free_hook = tr_old_free_hook;
- __malloc_hook = tr_old_malloc_hook;
- __realloc_hook = tr_old_realloc_hook;
- __memalign_hook = tr_old_memalign_hook;
-
- fprintf (f, "= End\n");
- fclose (f);
+ if (__glibc_mtrace_hook)
+ __glibc_mtrace_hook(0);
+ __glibc_mtrace_hook_enabled = 0;
}
@@ -30,6 +30,7 @@ trap "rm -f ${common_objpfx}malloc/tst-mtrace.leak; exit 1" 1 2 15
${test_program_prefix_before_env} \
${run_program_env} \
MALLOC_TRACE=${common_objpfx}malloc/tst-mtrace.leak \
+LD_PRELOAD=${common_objpfx}/malloc/libmalloc-extras.so \
${test_program_prefix_after_env} \
${common_objpfx}malloc/tst-mtrace || status=1
@@ -132,7 +132,8 @@ $(objpfx)libg.a: $(dep-dummy-lib); $(make-dummy-lib)
$(objpfx)tst-tsearch: $(libm)
-tst-error1-ENV = MALLOC_TRACE=$(objpfx)tst-error1.mtrace
+tst-error1-ENV = MALLOC_TRACE=$(objpfx)tst-error1.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
tst-error1-ARGS = $(objpfx)tst-error1.out
$(objpfx)tst-error1-mem.out: $(objpfx)tst-error1.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-error1.mtrace > $@; \
@@ -528,10 +528,12 @@ tst-umask1-ARGS = $(objpfx)tst-umask1.temp
$(objpfx)tst-atfork2: $(libdl) $(shared-thread-library)
LDFLAGS-tst-atfork2 = -rdynamic
-tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace
+tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-atfork2mod.so: $(shared-thread-library)
-tst-stack3-ENV = MALLOC_TRACE=$(objpfx)tst-stack3.mtrace
+tst-stack3-ENV = MALLOC_TRACE=$(objpfx)tst-stack3.mtrace \
+ LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so
$(objpfx)tst-stack3-mem.out: $(objpfx)tst-stack3.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-stack3.mtrace > $@; \
$(evaluate-test)
@@ -277,43 +277,50 @@ annexc-CFLAGS = -O
$(objpfx)annexc: annexc.c
$(native-compile)
-tst-fnmatch-ENV += MALLOC_TRACE=$(objpfx)tst-fnmatch.mtrace
+tst-fnmatch-ENV += MALLOC_TRACE=$(objpfx)tst-fnmatch.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-fnmatch-mem.out: $(objpfx)tst-fnmatch.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-fnmatch.mtrace > $@; \
$(evaluate-test)
-bug-regex2-ENV = MALLOC_TRACE=$(objpfx)bug-regex2.mtrace
+bug-regex2-ENV = MALLOC_TRACE=$(objpfx)bug-regex2.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)bug-regex2-mem.out: $(objpfx)bug-regex2.out
$(common-objpfx)malloc/mtrace $(objpfx)bug-regex2.mtrace > $@; \
$(evaluate-test)
-bug-regex14-ENV = MALLOC_TRACE=$(objpfx)bug-regex14.mtrace
+bug-regex14-ENV = MALLOC_TRACE=$(objpfx)bug-regex14.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)bug-regex14-mem.out: $(objpfx)bug-regex14.out
$(common-objpfx)malloc/mtrace $(objpfx)bug-regex14.mtrace > $@; \
$(evaluate-test)
-bug-regex21-ENV = MALLOC_TRACE=$(objpfx)bug-regex21.mtrace
+bug-regex21-ENV = MALLOC_TRACE=$(objpfx)bug-regex21.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)bug-regex21-mem.out: $(objpfx)bug-regex21.out
$(common-objpfx)malloc/mtrace $(objpfx)bug-regex21.mtrace > $@; \
$(evaluate-test)
-bug-regex31-ENV = MALLOC_TRACE=$(objpfx)bug-regex31.mtrace
+bug-regex31-ENV = MALLOC_TRACE=$(objpfx)bug-regex31.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)bug-regex31-mem.out: $(objpfx)bug-regex31.out
$(common-objpfx)malloc/mtrace $(objpfx)bug-regex31.mtrace > $@; \
$(evaluate-test)
-bug-regex36-ENV = MALLOC_TRACE=$(objpfx)bug-regex36.mtrace
+bug-regex36-ENV = MALLOC_TRACE=$(objpfx)bug-regex36.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)bug-regex36-mem.out: $(objpfx)bug-regex36.out
$(common-objpfx)malloc/mtrace $(objpfx)bug-regex36.mtrace > $@; \
$(evaluate-test)
-tst-vfork3-ENV = MALLOC_TRACE=$(objpfx)tst-vfork3.mtrace
+tst-vfork3-ENV = MALLOC_TRACE=$(objpfx)tst-vfork3.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-vfork3-mem.out: $(objpfx)tst-vfork3.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-vfork3.mtrace > $@; \
@@ -322,18 +329,21 @@ $(objpfx)tst-vfork3-mem.out: $(objpfx)tst-vfork3.out
# tst-rxspencer.mtrace is not generated, only
# tst-rxspencer-no-utf8.mtrace, since otherwise the file has almost
# 100M and takes very long time to process.
-tst-rxspencer-no-utf8-ENV += MALLOC_TRACE=$(objpfx)tst-rxspencer-no-utf8.mtrace
+tst-rxspencer-no-utf8-ENV += MALLOC_TRACE=$(objpfx)tst-rxspencer-no-utf8.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-rxspencer-no-utf8-mem.out: $(objpfx)tst-rxspencer-no-utf8.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-rxspencer-no-utf8.mtrace \
> $@; \
$(evaluate-test)
-tst-pcre-ENV = MALLOC_TRACE=$(objpfx)tst-pcre.mtrace
+tst-pcre-ENV = MALLOC_TRACE=$(objpfx)tst-pcre.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-pcre-mem.out: $(objpfx)tst-pcre.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-pcre.mtrace > $@; \
$(evaluate-test)
-tst-boost-ENV = MALLOC_TRACE=$(objpfx)tst-boost.mtrace
+tst-boost-ENV = MALLOC_TRACE=$(objpfx)tst-boost.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-boost-mem.out: $(objpfx)tst-boost.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-boost.mtrace > $@; \
$(evaluate-test)
@@ -346,15 +356,18 @@ $(objpfx)bug-ga2-mem.out: $(objpfx)bug-ga2.out
$(common-objpfx)malloc/mtrace $(objpfx)bug-ga2.mtrace > $@; \
$(evaluate-test)
-bug-ga2-ENV = MALLOC_TRACE=$(objpfx)bug-ga2.mtrace
+bug-ga2-ENV = MALLOC_TRACE=$(objpfx)bug-ga2.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
-bug-glob2-ENV = MALLOC_TRACE=$(objpfx)bug-glob2.mtrace
+bug-glob2-ENV = MALLOC_TRACE=$(objpfx)bug-glob2.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)bug-glob2-mem.out: $(objpfx)bug-glob2.out
$(common-objpfx)malloc/mtrace $(objpfx)bug-glob2.mtrace > $@; \
$(evaluate-test)
-tst-glob-tilde-ENV = MALLOC_TRACE=$(objpfx)tst-glob-tilde.mtrace
+tst-glob-tilde-ENV = MALLOC_TRACE=$(objpfx)tst-glob-tilde.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-glob-tilde-mem.out: $(objpfx)tst-glob-tilde.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-glob-tilde.mtrace > $@; \
@@ -147,6 +147,8 @@ do_prepare (void)
FAIL_EXIT1 ("out of memory");
strcpy (stpcpy (tmpdirname, test_dir), "/tst-vfork3.XXXXXX");
+ unsetenv("LD_PRELOAD");
+
tmpdirname = mkdtemp (tmpdirname);
if (tmpdirname == NULL)
FAIL_EXIT1 ("could not create temporary directory");
@@ -140,17 +140,20 @@ $(objpfx)tst-res_hconf_reorder: $(libdl) $(shared-thread-library)
tst-res_hconf_reorder-ENV = RESOLV_REORDER=on
$(objpfx)tst-leaks: $(objpfx)libresolv.so
-tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace
+tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace \
+ LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so
$(objpfx)mtrace-tst-leaks.out: $(objpfx)tst-leaks.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-leaks.mtrace > $@; \
$(evaluate-test)
-tst-leaks2-ENV = MALLOC_TRACE=$(objpfx)tst-leaks2.mtrace
+tst-leaks2-ENV = MALLOC_TRACE=$(objpfx)tst-leaks2.mtrace \
+ LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so
$(objpfx)mtrace-tst-leaks2.out: $(objpfx)tst-leaks2.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-leaks2.mtrace > $@; \
$(evaluate-test)
-tst-resolv-res_ninit-ENV = MALLOC_TRACE=$(objpfx)tst-resolv-res_ninit.mtrace
+tst-resolv-res_ninit-ENV = MALLOC_TRACE=$(objpfx)tst-resolv-res_ninit.mtrace \
+ LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so
$(objpfx)mtrace-tst-resolv-res_ninit.out: $(objpfx)tst-resolv-res_ninit.out
$(common-objpfx)malloc/mtrace \
$(objpfx)tst-resolv-res_ninit.mtrace > $@; \
@@ -90,9 +90,11 @@ $(objpfx)tst-swprintf.out: $(gen-locales)
$(objpfx)tst-vfprintf-mbs-prec.out: $(gen-locales)
endif
-tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace
+tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
tst-vfprintf-width-prec-ENV = \
- MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace
+ MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \
+ LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' > $@; \