[v2] malloc: Remove malloc hooks (Bug 23328)

Message ID ddf39fca-aa0a-9b14-ee78-a1bfa1225623@redhat.com
State New, archived
Headers

Commit Message

Carlos O'Donell June 30, 2018, 2:57 a.m. UTC
  v2
- I worked with DJ to remove/finalize __morecore, __after_morecore_hook,
and __default_morecore, along with fixing a bug with the mcheck support
(an application requesting mcheck via MALLOC_CHECK_=3 or tunables or
mallopt that *didn't* preload libmalloc-extras.so could abort with
memory corruption).
- Turned all the hooks into compat symbols so no new ABI will have them,
but used GLIBC_PRIVATE symbols to keep libmalloc-extras.so working.
- Fixed up the manual.
- Added a NEWS entry.

From dd6867aca22e0b0b02e94bd30e2cfddb48a1aade Mon Sep 17 00:00:00 2001
From: Carlos O'Donell <carlos@redhat.com>
Date: Thu, 21 Jun 2018 16:18:36 -0400
Subject: [PATCH] malloc: Remove malloc hooks (Bug 23328)

This commit removes the following malloc functionality: __free_hook,
__malloc_hook, __realloc_hook, __memalign_hook, __morecore,
__default_morecore, __after_morecore_hook.  The hooks are removed from
the public header, and changed to compat symbols.  The hooks
themselves are removed from the glibc malloc implementation.  The
manual is updated to remove all references to the hooks.  Lastly the
hooks are kept, even for new ABIs, as GLIBC_PRIVATE symbols in order
to allow the dependent APIs to continue to work e.g. mtrace via the
preloaded libmalloc-extras.so.  The morecore-related functionality
is all changed to compat symbols and is no longer used, but overall
code cleanup and refactoring of __default_morecore is left for
later.

For applications that require the hooks the functionality is moved
entirely to a new shared library: libmalloc-extras.so.  Applications
should preload libmalloc-extras.so to get back the lost functionality.
Static applications are not supported, but continue to operate with
APIs doing nothing or returning errors depending on their semantics.
The manual is updated to reference the new libmalloc-extras.so.

The APIs that use the hooks are left e.g. mtrace, muntrace, mprobe,
mcheck, mcheck_pedantic, mcheck_check_all, mallwatch, tr_break. These
APIs could one day be implemented differently, and libmalloc-extras.so
might no longer be needed.

Removing the hooks should improve the security (no more writable
function pointers), performance (no checking of the function
pointers), and maintainability of glibc's malloc (avoids complexity of
the hooks and calling back into malloc from them).

For users who require these hooks, or the services behind them e.g.
mtrace, the hooks are re-implemented in a separate libmalloc-extras.so
that can be preloaded via LD_PRELOAD.  This new libmalloc-extras.so
interposes the malloc API and implements the hook functionality.  The
functionality which is no longer supported is that provided by
__morecore, and __after_morecore_hook. The morecore related hooking is
entirely removed, and __default_morecore is always called with no
interposition possible (now named __glibc_morecore).  Applications
using the morecore hooks will find their hooks are no longer called
and will need to switch to a malloc interposer.

In the case of tests like tst-leaks1-static-mem it was not possible to
test the new hooks because they are only usable by dynamically loaded
binaries.  The interposable library is not made available for static
linkage.  The APIs that have not been deprecated, like mcheck, can
still be called by static applications, but they will not do anything
useful, and that is within the bounds of the API description.

There are some cases where not doing anything useful will result in a
crash.  Take for example the case of calling mcheck () to look for
consistency errors.  Previously they would be caught by the registered
function, but in the new glibc the application actually aborts because
the checker does nothing unless libmalloc-extras.so is preloaded.  All
of these cases are already undefined behaviour.  Wethere the
registered function is called or the application aborts is a QoI issue
at that point.

These changes could be backported to older branches by dropping the
compat symbol and public header changes.

Regression tested on x86_64 with no failures.

Example new/finalized symbol set for x86_64:

New libmalloc-extras.so interface:
	__glibc_malloc_check_request@@GLIBC_PRIVATE
	__glibc_malloc_check_enable@@GLIBC_PRIVATE
	__glibc_malloc_hook@@GLIBC_PRIVATE
	__glibc_realloc_hook@@GLIBC_PRIVATE
	__glibc_memalign_hook@@GLIBC_PRIVATE
	__glibc_free_hook@@GLIBC_PRIVATE

Note: The hooks are exported GLIBC_PRIVATE such that new machines
being implemented can drop the deprected symbols, and still have
libmalloc-extras.so continue to work using the private interface.

Finalized libc.so.6 interfaces:
	__malloc_hook@GLIBC_2.2.5 (alias to __glibc_* version)
	__memalign_hook@GLIBC_2.2.5 (likewise)
	__realloc_hook@GLIBC_2.2.5 (likewise)
	__free_hook@GLIBC_2.2.5 (likewise)
	__morecore@GLIBC_2.2.5 (unused)
	__default_morecore@GLIBC_2.2.5 (default)
	__after_morecore_hook@GLIBC_2.2.5 (unused)

Signed-off-by: DJ Delorie <dj@redhat.com>
Signed-off-by: Carlos O'Donell <carlos@redhat.com>
---
 ChangeLog                                  | 135 +++++
 Makeconfig                                 |   2 +-
 NEWS                                       |  26 +
 catgets/Makefile                           |   3 +-
 elf/Makefile                               |  17 +-
 iconvdata/Makefile                         |   3 +-
 include/stdlib.h                           |   3 -
 intl/tst-gettext.sh                        |   1 +
 libio/Makefile                             |   9 +-
 localedata/Makefile                        |   3 +-
 malloc/Makefile                            |  28 +-
 malloc/Versions                            |   8 +
 malloc/hooks.c                             |  74 ++-
 malloc/malloc-extras.c                     | 792 +++++++++++++++++++++++++++++
 malloc/malloc-hooks.h                      |   8 +
 malloc/malloc-internal.h                   |   4 +
 malloc/malloc.c                            | 111 ++--
 malloc/malloc.h                            |  25 -
 malloc/mcheck.c                            | 364 +------------
 malloc/morecore.c                          |   6 +-
 malloc/mtrace.c                            | 281 +---------
 malloc/tst-malloc-usable-static-tunables.c |   1 -
 malloc/tst-malloc-usable-static.c          |   1 -
 malloc/tst-malloc-weak-usable-static.c     |   1 +
 malloc/tst-malloc-weak-usable.c            |  60 +++
 malloc/tst-mtrace.sh                       |   1 +
 manual/memory.texi                         | 239 +--------
 misc/Makefile                              |   6 +-
 nptl/Makefile                              |   6 +-
 posix/Makefile                             |  39 +-
 posix/tst-vfork3.c                         |   6 +
 resolv/Makefile                            |   9 +-
 stdio-common/Makefile                      |   6 +-
 33 files changed, 1260 insertions(+), 1018 deletions(-)
 create mode 100644 malloc/malloc-extras.c
 delete mode 100644 malloc/tst-malloc-usable-static-tunables.c
 delete mode 100644 malloc/tst-malloc-usable-static.c
 create mode 100644 malloc/tst-malloc-weak-usable-static.c
 create mode 100644 malloc/tst-malloc-weak-usable.c
  

Comments

Florian Weimer July 2, 2018, 2:02 p.m. UTC | #1
* Carlos O'Donell:

> +/* Call a hook function FUNC, and if recusion is detected return
> +   with value ERV.  In addition we check for
> +   __glibc_malloc_check_requested to see if the runtime is waiting
> +   to know if the hooks are active and if malloc_usable_size can
> +   use the more accurate checking sizes for chunks.  */
> +#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);	\
> +      if (__glibc_malloc_check_request)				\
> +	__glibc_malloc_check_enable ();				\
> +    }
> +
> +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 (__glibc_malloc_hook);
> +  if (__builtin_expect (hook != NULL, 0))
> +    return (*hook) (sz, RETURN_ADDRESS (0));

Should this use atomic_load_relaxed instead?

> +  HOOK (malloc, NULL);

I think you can simplify HOOK considerably if you always initialize
all function pointers.  Then you don't need to use atomics there
because pthread_create always initializes malloc before creating new
threads.
  
Florian Weimer July 2, 2018, 2:10 p.m. UTC | #2
* Carlos O'Donell:

> For applications that require the hooks the functionality is moved
> entirely to a new shared library: libmalloc-extras.so.  Applications
> should preload libmalloc-extras.so to get back the lost functionality.
> Static applications are not supported, but continue to operate with
> APIs doing nothing or returning errors depending on their semantics.

Usuallly, we remove functionality which is turned into compat symbols
outright.  Why aren't we doing this here?
  
Florian Weimer July 2, 2018, 2:16 p.m. UTC | #3
* Carlos O'Donell:

> diff --git a/catgets/Makefile b/catgets/Makefile
> index 36f6a226c3..941e5cb4ea 100644
> --- a/catgets/Makefile
> +++ b/catgets/Makefile
> @@ -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

Should this be $(objpfx-common)malloc/libmalloc-extras.so instead?

Anyway, you should add a dependency on libmalloc-extras.so to
tst-catgets.out, or perhaps even link tst-catgets against
libmalloc-extras.so, so that the test is rerun once the DSO changes.
(This applies to other uses of mtrace, of course.)
  
Carlos O'Donell July 6, 2018, 2:59 p.m. UTC | #4
On 07/02/2018 10:10 AM, Florian Weimer wrote:
> * Carlos O'Donell:
> 
>> For applications that require the hooks the functionality is moved
>> entirely to a new shared library: libmalloc-extras.so.  Applications
>> should preload libmalloc-extras.so to get back the lost functionality.
>> Static applications are not supported, but continue to operate with
>> APIs doing nothing or returning errors depending on their semantics.
> 
> Usuallly, we remove functionality which is turned into compat symbols
> outright.  Why aren't we doing this here?
 All functionality which is turned into compat symbols is no longer
available to static applications.

What I meant by the last sentence is that if a static application calls
mallopt with M_CHECK_ACTION set to 3, that will do nothing, because
the hooks required to implement this action are no longer doing anything,
and you can't preload libmalloc-extras.so to support this usage.

The APIs which "optionally" catch errors no longer catch errors because
their underlying implementation, the hooks, has been removed.

A static application can no longer access the hooks because they don't
exist in any header, but can still call functions which need the hooks
to operate correctly. Until we reimplement the behaviour without hooks
the functions aren't going to do anything useful in a static program.

Does that answer your question?
  
Carlos O'Donell July 21, 2018, 1:38 a.m. UTC | #5
On 06/29/2018 10:57 PM, Carlos O'Donell wrote:
> v2
> - I worked with DJ to remove/finalize __morecore, __after_morecore_hook,
> and __default_morecore, along with fixing a bug with the mcheck support
> (an application requesting mcheck via MALLOC_CHECK_=3 or tunables or
> mallopt that *didn't* preload libmalloc-extras.so could abort with
> memory corruption).
> - Turned all the hooks into compat symbols so no new ABI will have them,
> but used GLIBC_PRIVATE symbols to keep libmalloc-extras.so working.
> - Fixed up the manual.
> - Added a NEWS entry.
Florian,

Your review included a few suggestions:
- Cleanup the HOOK macro in malloc-extras.c, which could be done now
  or later as a cleanup of the legacy implementation (Discussed by
  you and DJ).
- I clarified to you my point about some APIs like mallopt remain,
  but some options do nothing now (we can't entirely remove mallopt).
- A quick cleanup of $(objpfx) -> $(objpfx-common).

Did you have any further review?

Joseph,

Does this design of this patch implement what you thought would be
an appropriate solution to deprecating the hooks? Part of this design
is based on your feedback that you wanted to see some kind of shared
library that provided the features that the hooks used to provide,
and that's what we do here as we finalize (make compat symbolis) the
hooks and other APIs.

Ping.

https://www.sourceware.org/ml/libc-alpha/2018-06/msg01025.html

I would like to consider this for 2.28, because I want to see these
symbols removed. Every release that goes by with them is another
machine ABI that includes them (RISC-V, and next C-SKY).

I'm open to derision and shouts of "No, put it in 2.29." :-)
  
Florian Weimer July 23, 2018, 2:13 p.m. UTC | #6
On 06/30/2018 04:57 AM, Carlos O'Donell wrote:

> +	(Hooks for Malloc): Remove.

The manual no longer builds:

./memory.texi:1522: @pxref reference to nonexistent node `Hooks for Malloc'

That's from here:

> If the named file is successfully opened, @code{mtrace} installs special
> handlers for the functions @code{malloc}, @code{realloc}, and
> @code{free} (@pxref{Hooks for Malloc}).  From then on, all uses of these
> functions are traced and protocolled into the file.  There is now of
> course a speed penalty for all calls to the traced functions so tracing
> should not be enabled during normal use.

Thanks,
Florian
  
Florian Weimer July 23, 2018, 2:15 p.m. UTC | #7
On 07/21/2018 03:38 AM, Carlos O'Donell wrote:
> On 06/29/2018 10:57 PM, Carlos O'Donell wrote:
>> v2
>> - I worked with DJ to remove/finalize __morecore, __after_morecore_hook,
>> and __default_morecore, along with fixing a bug with the mcheck support
>> (an application requesting mcheck via MALLOC_CHECK_=3 or tunables or
>> mallopt that *didn't* preload libmalloc-extras.so could abort with
>> memory corruption).
>> - Turned all the hooks into compat symbols so no new ABI will have them,
>> but used GLIBC_PRIVATE symbols to keep libmalloc-extras.so working.
>> - Fixed up the manual.
>> - Added a NEWS entry.
> Florian,
> 
> Your review included a few suggestions:
> - Cleanup the HOOK macro in malloc-extras.c, which could be done now
>    or later as a cleanup of the legacy implementation (Discussed by
>    you and DJ).
> - I clarified to you my point about some APIs like mallopt remain,
>    but some options do nothing now (we can't entirely remove mallopt).
> - A quick cleanup of $(objpfx) -> $(objpfx-common).
> 
> Did you have any further review?

Yes, missing dependencies for the LD_PRELOAD module libmalloc-extras.so, 
and an explicit discussion why we use LD_PRELOAD and not a straight 
link.  There's also the matter of the name of the libmalloc-extras.so 
library.

The manual doesn't built, ether.

Thanks,
Florian
  
Florian Weimer July 23, 2018, 2:17 p.m. UTC | #8
Should we install libmalloc-extras.so under that name?

Or should it be libmalloc-extras.so.0 (soname) without a 
libmalloc-extras.so symbolic link, to discourage new applications from 
linking against it, so making it more obvious that this is intended for 
backwards compatibility with old applications?

Thanks,
Florian
  
Joseph Myers July 23, 2018, 3:54 p.m. UTC | #9
On Fri, 20 Jul 2018, Carlos O'Donell wrote:

> Joseph,
> 
> Does this design of this patch implement what you thought would be
> an appropriate solution to deprecating the hooks? Part of this design
> is based on your feedback that you wanted to see some kind of shared
> library that provided the features that the hooks used to provide,
> and that's what we do here as we finalize (make compat symbolis) the
> hooks and other APIs.

Having a library providing the functionality required to use mtrace 
accords with my expectations, yes.  (This is not a more general design 
review of the patch.)
  
Joseph Myers July 23, 2018, 3:59 p.m. UTC | #10
On Mon, 23 Jul 2018, Florian Weimer wrote:

> Should we install libmalloc-extras.so under that name?
> 
> Or should it be libmalloc-extras.so.0 (soname) without a libmalloc-extras.so
> symbolic link, to discourage new applications from linking against it, so
> making it more obvious that this is intended for backwards compatibility with
> old applications?

I don't think it's something to discourage applications from linking 
against.  Among other things, it's for any use of mtrace, which is useful 
for both new and old applications as a light-weight memory checker for 
uses similar to those in glibc's own testsuite.
  
Carlos O'Donell July 23, 2018, 6:27 p.m. UTC | #11
On 07/23/2018 11:59 AM, Joseph Myers wrote:
> On Mon, 23 Jul 2018, Florian Weimer wrote:
> 
>> Should we install libmalloc-extras.so under that name?
>>
>> Or should it be libmalloc-extras.so.0 (soname) without a libmalloc-extras.so
>> symbolic link, to discourage new applications from linking against it, so
>> making it more obvious that this is intended for backwards compatibility with
>> old applications?
> 
> I don't think it's something to discourage applications from linking 
> against.  Among other things, it's for any use of mtrace, which is useful 
> for both new and old applications as a light-weight memory checker for 
> uses similar to those in glibc's own testsuite.

I'm slightly in agreement with Joseph here, the use of libmalloc-extras.so
might turn out to be useful mechanism for deploying this kind of code for
now and into the future.

The intent being that glibc ships it's own malloc interposer which provides
all the features we think you would need to debug glibc malloc issues. The
interposer might even be a recompiled version of glibc's malloc (similar to
your own attempts to split libm out into it's own library).

Cheers,
Carlos.
  
Carlos O'Donell July 23, 2018, 6:29 p.m. UTC | #12
On 07/23/2018 11:54 AM, Joseph Myers wrote:
> On Fri, 20 Jul 2018, Carlos O'Donell wrote:
> 
>> Joseph,
>>
>> Does this design of this patch implement what you thought would be
>> an appropriate solution to deprecating the hooks? Part of this design
>> is based on your feedback that you wanted to see some kind of shared
>> library that provided the features that the hooks used to provide,
>> and that's what we do here as we finalize (make compat symbolis) the
>> hooks and other APIs.
> 
> Having a library providing the functionality required to use mtrace 
> accords with my expectations, yes.  (This is not a more general design 
> review of the patch.)

OK, thank you, I just wanted to make sure that we were still in line with
what you were expecting. You expressed an opinion on the patches originally
and so I counted you among those developers with a concerned opinion on the
class of changes we were making.

I think there is a class of API which will always be useful, like mtrace
and muntrace, but by default they will do nothing, then you preload libmalloc-extras.so
and they start working. We have enough technology in place from the tracer
that DJ/Florian/I built that we can deliver a thread-safe version that is
very fast (faster for local use than lttng-ust, but with less features).

Cheers,
Carlos.
  
Florian Weimer July 23, 2018, 6:45 p.m. UTC | #13
On 07/23/2018 08:27 PM, Carlos O'Donell wrote:
> On 07/23/2018 11:59 AM, Joseph Myers wrote:
>> On Mon, 23 Jul 2018, Florian Weimer wrote:
>>
>>> Should we install libmalloc-extras.so under that name?
>>>
>>> Or should it be libmalloc-extras.so.0 (soname) without a libmalloc-extras.so
>>> symbolic link, to discourage new applications from linking against it, so
>>> making it more obvious that this is intended for backwards compatibility with
>>> old applications?
>>
>> I don't think it's something to discourage applications from linking
>> against.  Among other things, it's for any use of mtrace, which is useful
>> for both new and old applications as a light-weight memory checker for
>> uses similar to those in glibc's own testsuite.
> 
> I'm slightly in agreement with Joseph here, the use of libmalloc-extras.so
> might turn out to be useful mechanism for deploying this kind of code for
> now and into the future.

This wasn't obvious from the patch.

If there is a promise of ABI stability for the library (rather than just 
interposing bits in libc.so.6), we need ABI checking for it, and we 
should add a soname version.  That's currently missing from the patch.

Thanks,
Florian
  
Carlos O'Donell July 23, 2018, 6:52 p.m. UTC | #14
On 07/23/2018 02:45 PM, Florian Weimer wrote:
> On 07/23/2018 08:27 PM, Carlos O'Donell wrote:
>> On 07/23/2018 11:59 AM, Joseph Myers wrote:
>>> On Mon, 23 Jul 2018, Florian Weimer wrote:
>>> 
>>>> Should we install libmalloc-extras.so under that name?
>>>> 
>>>> Or should it be libmalloc-extras.so.0 (soname) without a
>>>> libmalloc-extras.so symbolic link, to discourage new
>>>> applications from linking against it, so making it more obvious
>>>> that this is intended for backwards compatibility with old
>>>> applications?
>>> 
>>> I don't think it's something to discourage applications from
>>> linking against.  Among other things, it's for any use of mtrace,
>>> which is useful for both new and old applications as a
>>> light-weight memory checker for uses similar to those in glibc's
>>> own testsuite.
>> 
>> I'm slightly in agreement with Joseph here, the use of
>> libmalloc-extras.so might turn out to be useful mechanism for
>> deploying this kind of code for now and into the future.
> 
> This wasn't obvious from the patch.
> 
> If there is a promise of ABI stability for the library (rather than
> just interposing bits in libc.so.6), we need ABI checking for it, and
> we should add a soname version.  That's currently missing from the
> patch.

I'm not sure what you mean by this.

I envision this will always be an interposer.

The core ABI stability will be provided by libc.so.6.

The interposer will provide the functional implementation, such that
the default libc.so.6 implementation is a stub that does nothing.

Does that make sense?

Cheers,
Carlos.
  
Florian Weimer July 23, 2018, 6:54 p.m. UTC | #15
On 07/23/2018 08:52 PM, Carlos O'Donell wrote:
> On 07/23/2018 02:45 PM, Florian Weimer wrote:
>> On 07/23/2018 08:27 PM, Carlos O'Donell wrote:
>>> On 07/23/2018 11:59 AM, Joseph Myers wrote:
>>>> On Mon, 23 Jul 2018, Florian Weimer wrote:
>>>>
>>>>> Should we install libmalloc-extras.so under that name?
>>>>>
>>>>> Or should it be libmalloc-extras.so.0 (soname) without a
>>>>> libmalloc-extras.so symbolic link, to discourage new
>>>>> applications from linking against it, so making it more obvious
>>>>> that this is intended for backwards compatibility with old
>>>>> applications?
>>>>
>>>> I don't think it's something to discourage applications from
>>>> linking against.  Among other things, it's for any use of mtrace,
>>>> which is useful for both new and old applications as a
>>>> light-weight memory checker for uses similar to those in glibc's
>>>> own testsuite.
>>>
>>> I'm slightly in agreement with Joseph here, the use of
>>> libmalloc-extras.so might turn out to be useful mechanism for
>>> deploying this kind of code for now and into the future.
>>
>> This wasn't obvious from the patch.
>>
>> If there is a promise of ABI stability for the library (rather than
>> just interposing bits in libc.so.6), we need ABI checking for it, and
>> we should add a soname version.  That's currently missing from the
>> patch.
> 
> I'm not sure what you mean by this.
> 
> I envision this will always be an interposer.
> 
> The core ABI stability will be provided by libc.so.6.
> 
> The interposer will provide the functional implementation, such that
> the default libc.so.6 implementation is a stub that does nothing.
> 
> Does that make sense?

If we support linking against the interposer, it has an ABI, which needs 
to be checked independently.  Merely verifying against libc.so.6 is not 
enough because if you remove a symbol from the interposer, it will break 
existing binaries due to the dynamic linker currently performing soname 
matching for versioned symbols which are not interposed.

Thanks,
Florian
  
Carlos O'Donell July 23, 2018, 7:16 p.m. UTC | #16
On 07/23/2018 02:54 PM, Florian Weimer wrote:
> On 07/23/2018 08:52 PM, Carlos O'Donell wrote:
>> On 07/23/2018 02:45 PM, Florian Weimer wrote:
>>> On 07/23/2018 08:27 PM, Carlos O'Donell wrote:
>>>> On 07/23/2018 11:59 AM, Joseph Myers wrote:
>>>>> On Mon, 23 Jul 2018, Florian Weimer wrote:
>>>>> 
>>>>>> Should we install libmalloc-extras.so under that name?
>>>>>> 
>>>>>> Or should it be libmalloc-extras.so.0 (soname) without a 
>>>>>> libmalloc-extras.so symbolic link, to discourage new 
>>>>>> applications from linking against it, so making it more
>>>>>> obvious that this is intended for backwards compatibility
>>>>>> with old applications?
>>>>> 
>>>>> I don't think it's something to discourage applications from 
>>>>> linking against.  Among other things, it's for any use of
>>>>> mtrace, which is useful for both new and old applications as
>>>>> a light-weight memory checker for uses similar to those in
>>>>> glibc's own testsuite.
>>>> 
>>>> I'm slightly in agreement with Joseph here, the use of 
>>>> libmalloc-extras.so might turn out to be useful mechanism for 
>>>> deploying this kind of code for now and into the future.
>>> 
>>> This wasn't obvious from the patch.
>>> 
>>> If there is a promise of ABI stability for the library (rather
>>> than just interposing bits in libc.so.6), we need ABI checking
>>> for it, and we should add a soname version.  That's currently
>>> missing from the patch.
>> 
>> I'm not sure what you mean by this.
>> 
>> I envision this will always be an interposer.
>> 
>> The core ABI stability will be provided by libc.so.6.
>> 
>> The interposer will provide the functional implementation, such
>> that the default libc.so.6 implementation is a stub that does
>> nothing.
>> 
>> Does that make sense?
> 
> If we support linking against the interposer, it has an ABI, which
> needs to be checked independently.  Merely verifying against
> libc.so.6 is not enough because if you remove a symbol from the
> interposer, it will break existing binaries due to the dynamic linker
> currently performing soname matching for versioned symbols which are
> not interposed.

That is a very good point.

I don't want to commit to this, and I don't think we should until we've
discussed this in more detail.

We can always support Joseph's position at a later date with more knowledge
gained from supporting the implementation of libmalloc-extras.so.0, adding
a stable ABI is easier than removing one :}


Cheers,
Carlos.
  
Carlos O'Donell July 23, 2018, 7:51 p.m. UTC | #17
On 07/23/2018 11:59 AM, Joseph Myers wrote:
> On Mon, 23 Jul 2018, Florian Weimer wrote:
> 
>> Should we install libmalloc-extras.so under that name?
>>
>> Or should it be libmalloc-extras.so.0 (soname) without a libmalloc-extras.so
>> symbolic link, to discourage new applications from linking against it, so
>> making it more obvious that this is intended for backwards compatibility with
>> old applications?
> 
> I don't think it's something to discourage applications from linking 
> against.  Among other things, it's for any use of mtrace, which is useful 
> for both new and old applications as a light-weight memory checker for 
> uses similar to those in glibc's own testsuite.

Let me be clear here.

While I like Joseph's idea that we may wish to deploy libmalloc-extras.so as
something to aspire to that we can provide as an implementation with it's own
ABI, it is *not* what I think we should deploy for 2.28.

Right now for 2.28 I think we should deploy, as Florian suggests, a
libmalloc-extras.so.0, with no so symlink. This DSO is truly for interposition
only. New and old applications can use LD_PRELOAD for now.

Until we have more experience with libmalloc-extras.so I do *not* want to provide
ABI guarantees around this object. It has been designed and tested purely as an
interposer.

Can we agree on this first step?

I'm not opposed to following up in 2.29 with a clear discussion of exactly what
ABI we would provide for libmalloc-extras.so.

Cheers,
Carlos.
  
Joseph Myers July 23, 2018, 8:10 p.m. UTC | #18
On Mon, 23 Jul 2018, Carlos O'Donell wrote:

> While I like Joseph's idea that we may wish to deploy libmalloc-extras.so as
> something to aspire to that we can provide as an implementation with it's own
> ABI, it is *not* what I think we should deploy for 2.28.

My view is simply that we should provide this in some form for users 
wishing to use mtrace etc. - not that it should be usable in a more 
specific way such as by linking instead of interposition.
  
Carlos O'Donell July 23, 2018, 8:46 p.m. UTC | #19
On 07/23/2018 04:10 PM, Joseph Myers wrote:
> On Mon, 23 Jul 2018, Carlos O'Donell wrote:
> 
>> While I like Joseph's idea that we may wish to deploy libmalloc-extras.so as
>> something to aspire to that we can provide as an implementation with it's own
>> ABI, it is *not* what I think we should deploy for 2.28.
> 
> My view is simply that we should provide this in some form for users 
> wishing to use mtrace etc. - not that it should be usable in a more 
> specific way such as by linking instead of interposition.

OK, thank you for the clarification.

With that in mind I'll continue to follow a staggered approach:

* A release with libmalloc-extras.so.0 for LD_PRELOAD (2.28)
* Gain experience with libmalloc-extras.so.0 usage (Future)
* Potentially provide a libmalloc-extras.so for direct linking (Future)

The notion of providing an "official" interposer still appeals to me
from the perspective that it can deliver features we would not normally
turn on in regular libc.so.6's malloc API.

Cheers,
Carlos.
  
Florian Weimer July 24, 2018, 7:21 a.m. UTC | #20
On 07/23/2018 10:46 PM, Carlos O'Donell wrote:

> With that in mind I'll continue to follow a staggered approach:
> 
> * A release with libmalloc-extras.so.0 for LD_PRELOAD (2.28)
> * Gain experience with libmalloc-extras.so.0 usage (Future)
> * Potentially provide a libmalloc-extras.so for direct linking (Future)
> 
> The notion of providing an "official" interposer still appeals to me
> from the perspective that it can deliver features we would not normally
> turn on in regular libc.so.6's malloc API.

Okay, then we should provide a libmalloc-extras.so.0 which is properly 
isolated (no public internal symbols), has proper symbol versions 
(matching those of libc.so.6), without default symbol versions.

The current DSO does not use symbol versioning and exports internals 
such as mtrace_extras_ctor.  These things really need to be fixed.

Thanks,
Florian
  

Patch

diff --git a/ChangeLog b/ChangeLog
index 3371a9b670..b55a14a386 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,138 @@ 
+2018-06-29  DJ Delorie  <dj@redhat.com>
+	    Carlos O'Donell  <carlos@redhat.com>
+
+	[BZ #23328]
+	* Makeconfig (built-modules): Add libmalloc-extras.
+	* NEWS: Describe deprecation of hooks.
+	* catgets/Makefile (tst-catgets-ENV): Preload libmalloc-extras.so.
+	* elf/Makefile (tests-special): Remove tst-leaks1-static-mem.out.
+	(noload-ENV): Preload libmalloc-extras.so.
+	($(objpfx)tst-leaks1-static-mem.out): Remove.
+	(tst-leaks1-ENV): Likewise.
+	* iconvdata/Makefile (tst-loading-ENV): Likewise.
+	* include/stdlib.h: Remove __default_morecore prototype.
+	* intl/tst-gettext.sh: Preload libmalloc-extras.so.
+	* libio/Makefile (test-fmemopen-ENV): Likewise.
+	(tst-fopenloc-ENV): Likewise.
+	(tst-bz22415-ENV): Likewise.
+	* localedata/Makefile (tst-leaks-ENV): Likewise.
+	* malloc/Makefile (tests): Add tst-malloc-weak-usable.
+	(tests-static): Add tst-malloc-weak-usable-static.
+	Remove tst-malloc-usable-static.
+	[ifneq (no,$(have-tunables))] (tests-static): Remove
+	tst-malloc-usable-tunables-static.
+	(extra-libs): Add libmalloc-extras.
+	(libmalloc-extras-routines): Define.
+	(LDLIBS-libmalloc-extras.so): Define.
+	(LDFLAGS-malloc-extras.so): Define.
+	($(objpfx)libmalloc-extras.so): Define.
+	(tst-mtrace-ENV): Preload libmalloc-extras.so.
+	(tst-malloc-usable-ENV): Likewise.
+	(tst-malloc-usable-tunables-ENV): Likewise.
+	(tst-malloc-weak-usable-ENV): Define.
+	(tst-malloc-weak-usable-static-ENV): Define.
+	(tst-dynarray-ENV): Likewise.
+	(tst-dynarray-fail-ENV): Likewise.
+	* malloc/Versions (libc.GLIBC_PRIVATE): Add __glibc_free_hook,
+	__glibc_malloc_hook, __glibc_realloc_hook, __glibc_memalign_hook,
+	__glibc_malloc_check_request, __glibC_malloc_check_enable.
+	* malloc/hooks.c: Define declrations for malloc_check,
+	free_check, realloc_check, and memalign_check.
+	(malloc_hook_ini): Call interposable API and use local hooks.
+	(realloc_hook_ini): Likewise.
+	(memalign_hook_ini): Likewise.
+	(__glibc_malloc_check_request): New global data.
+	(__malloc_check_init): Install but don't enable hooks.
+	(__glibc_malloc_check_enable): New function.
+	(mem2mem_check): Adjust comment.
+	(mem2chunk_check): Adjust comment.
+	(malloc_set_state): Use local hooks.
+	* malloc/malloc-extras.c: New file.
+	* malloc/malloc-hooks.h: Add prototypes for __glibc_free_hook,
+	__glibc_malloc_hook, __glibc_realloc_hook, __glibc_memalign_hook,
+	__glibc_malloc_check_request, __glibc_malloc_check_enable.
+	* malloc/malloc-internal.h: Add prototypes for __malloc_check_init,
+	__glibc_morecore.
+	* malloc/malloc.c: Remove prototypes for __default_morecore,
+	mem2mem_check, top_check, malloc_check, free_check, realloc_check, and
+	memalign_check.
+	(MORECORE): Call __glibc_morecore directly.
+	(__morecore): Declare as compat symbol conditional on
+	SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28).
+	(__free_hook): Rename to...
+	(__glibc_free_hook): ...this.  And declare as compat symbol
+	__free_hook conditional on
+	SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28).
+	(__malloc_hook): Rename to...
+	(__glibc_malloc_hook): ...this.  And declare as compat symbol
+	__malloc_hook conditional on
+	SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28).
+	(__realloc_hook): Rename to...
+	(__glibc_realloc_hook): ...this. And declare as compat symbol
+	__glibc_realloc_hook conditional on
+	SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28).
+	(__memalign_hook): Rename to...
+	(__glibc_memalign_hook): ...this. And declare as compat symbol
+	__glibc_memalign_hook conditional on
+	SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28).
+	(__after_morecore_hook): Declare as compat symbol conditional on
+	SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28).
+	(sysmalloc): Do not call __after_morecore_hook.
+	(systrim): Likewise.
+	(__libc_malloc): Call ptmalloc_init() and remove hook calls.
+	(__libc_free): Likewise.
+	(__libc_realloc): Likewise.
+	(__libc_calloc): Likewise.
+	(_mid_memaligin): Don't call __memalign_hook.
+	* malloc/malloc.h: Remove public hook declarations for __free_hook,
+	__malloc_hook, __realloc_hook, __memalign_hook, and
+	__after_morecore_hook.  Remove delcaration of __default_morecore,
+	and __morecore.
+	* malloc/mcheck.c: Remove most code, leaving an empty implementation
+	of the APIs for mcheck_check_all, mcheck, mcheck_pedantic, and mprobe.
+	* malloc/morecore.c (__default_morecore): Rename to...
+	(__glibc_morecore): ...this. Define as compat symbol
+	__default_morecore conditional on
+	SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28).
+	* malloc/mtrace.c: Remove most code, leaving an empty implementation
+	of the APIs for mallwatch, tr_break, mtrace, and muntrace.
+	* malloc/tst-malloc-usable-static-tunables.c: Remove file.
+	* malloc/tst-malloc-usable-static.c: Remove file.
+	* malloc/tst-malloc-weak-usable-static.c: New file.
+	* malloc/tst-malloc-weak-usable.c: New file.
+	* malloc/tst-mtrace.sh: Preload libmalloc-extras.so.
+	* manual/memory.texi (Summary of Malloc): Remove "Hooks for Malloc".
+	(Hooks for Malloc): Remove.
+	(mcheck): Remove @mtasuconst{:malloc_hooks} safety not.
+	(mprobe): Likewise.
+	(mtrace): Likewise.
+	(muntrace): Likewise.
+	(Heap Consistency Checking): Document need for libmalloc-extra.so.
+	(Allocation Debugging): Likewise.
+	* misc/Makefile (tst-error1-ENV): Likewise.
+	(tst-allocate_once-ENV): Likewise.
+	* nptl/Makefile (tst-atfork2-ENV): Likewise.
+	(tst-stack3-ENV): Likewise.
+	* posix/Makefile (tst-fnmatch-ENV): Likewise.
+	(bug-regex2-ENV): Likewise.
+	(bug-regex14-ENV): Likewise.
+	(bug-regex21-ENV): Likewise.
+	(bug-regex31-ENV): Likewise.
+	(bug-regex36-ENV): Likewise.
+	(tst-vfork3-ENV): Likewise.
+	(tst-rxspencer-no-utf8-ENV): Likewise.
+	(tst-pcre-ENV): Likewise.
+	(tst-boost-ENV): Likewise.
+	(bug-ga2-ENV): Likewise.
+	(bug-glob2-ENV): Likewise.
+	(tst-glob-tilde-ENV): Likewise.
+	* posix/tst-vfork3.c (do_prepare): Unset LD_PRELOAD.
+	* resolv/Makefile (tst-leaks-ENV): Preload libmalloc-extras.so.
+	(tst-leaks2-ENV): Likewise.
+	(tst-resolv-res_ninit-ENV): Likewise.
+	* stdio-common/Makefile (tst-printf-bz18872-ENV): Likewise.
+	(tst-vfprintf-width-prec-ENV): Likewise.
+
 2018-06-29  DJ Delorie  <dj@redhat.com>
 	    Carlos O'Donell  <carlos@redhat.com>
 
diff --git a/Makeconfig b/Makeconfig
index 608ffe648c..ece6f13198 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -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)) \
diff --git a/NEWS b/NEWS
index a27dd371a3..ef036c8791 100644
--- a/NEWS
+++ b/NEWS
@@ -58,6 +58,32 @@  Major new features:
 
 Deprecated and removed features, and other changes affecting compatibility:
 
+* The malloc function redirection hooks for __free_hook, __malloc_hook,
+  __realloc_hook and __memalign_hook and their functionality has been
+  deprecated and is no longer available to newly compiled applications.
+  In addition the __morecore, __default_morecore, and
+  __after_morecore_hook which allow overriding some of the basic
+  allocation mechanisms in malloc have also been deprected and are no
+  longer available to newly compiled applications.  Applications already
+  using these malloc hooks can continue to operate by prelodaing, via
+  LD_PRELOAD, the provided libmalloc-extras.so shared object.  This
+  shared object implements a malloc interposer that provides all the
+  features previously provided by the hooks (with the exception of
+  __morcore and __after_morecore_hook which are unused).  This includes
+  all of the functionality provided by mcheck, mcheck_check_pedantic,
+  mcheck_check_all, mprobe, MALLOC_CHECK_, mallopt's M_CHECK_ACTION set
+  to non-zero, mtrace and muntrace.  The malloc hooks are deprecated
+  because they are not thread safe, pose a security risk, and impose
+  restrictions on the malloc internals which impact maintenance and
+  future performance enhancements.  The entire malloc family of APIs
+  must be implemented by an interposing library, and partial
+  implementation using the hooks are no longer supported in libc.so.
+  Lastly, all of the functionality described here is no longer supported
+  for statically linked applications.  In statically linked
+  applications, just like in legacy applications that don't preload
+  libmalloc-extras.so, the impacted APIs will do nothing, or return
+  appropriate error codes depending on their semantics.
+
 * The nonstandard header files <libio.h> and <_G_config.h> are no longer
   installed.  Software that was using either header should be updated to
   use standard <stdio.h> interfaces instead.
diff --git a/catgets/Makefile b/catgets/Makefile
index 36f6a226c3..941e5cb4ea 100644
--- a/catgets/Makefile
+++ b/catgets/Makefile
@@ -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.
diff --git a/elf/Makefile b/elf/Makefile
index 41cc3681be..d61bbecab8 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -205,7 +205,7 @@  endif
 tests += $(tests-execstack-$(have-z-execstack))
 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
@@ -812,7 +812,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
@@ -1227,11 +1228,15 @@  $(objpfx)tst-leaks1-mem.out: $(objpfx)tst-leaks1.out
 	$(evaluate-test)
 
 $(objpfx)tst-leaks1-static: $(common-objpfx)dlfcn/libdl.a
-$(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-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1.mtrace \
+	LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
+# Static applications no longer support tracing.  All of the hooks used
+# for tracing have been removed.  However, we can still test that the
+# application runs using the existing API calls without crashing.
+# Note that there is no tst-leaks1-static-mem.out target which runs
+# mtrace in this case because no *.mtrace file would be generated, again
+# we are just testing that the application itself doesn't crash.
 tst-leaks1-static-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1-static.mtrace
 
 $(objpfx)tst-addr1: $(libdl)
diff --git a/iconvdata/Makefile b/iconvdata/Makefile
index 06e161d9b8..3930890903 100644
--- a/iconvdata/Makefile
+++ b/iconvdata/Makefile
@@ -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)
diff --git a/include/stdlib.h b/include/stdlib.h
index 114e12d255..bd3dcf4950 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -285,9 +285,6 @@  libc_hidden_proto (__qfcvt_r)
 #  define MB_CUR_MAX (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MB_CUR_MAX))
 # endif
 
-extern void *__default_morecore (ptrdiff_t) __THROW;
-libc_hidden_proto (__default_morecore)
-
 struct abort_msg_s
 {
   unsigned int size;
diff --git a/intl/tst-gettext.sh b/intl/tst-gettext.sh
index 0c65583149..c49f61586c 100755
--- a/intl/tst-gettext.sh
+++ b/intl/tst-gettext.sh
@@ -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
diff --git a/libio/Makefile b/libio/Makefile
index 64d283e512..6781f6f8b2 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -154,9 +154,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
diff --git a/localedata/Makefile b/localedata/Makefile
index d51064adec..71e87c88d6 100644
--- a/localedata/Makefile
+++ b/localedata/Makefile
@@ -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)
diff --git a/malloc/Makefile b/malloc/Makefile
index 7d54bad866..98ca0c56fc 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -38,11 +38,12 @@  tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-malloc_info \
 	 tst-malloc-too-large \
 	 tst-malloc-stats-cancellation \
+	 tst-malloc-weak-usable
 
 tests-static := \
 	 tst-interpose-static-nothread \
 	 tst-interpose-static-thread \
-	 tst-malloc-usable-static \
+	 tst-malloc-weak-usable-static
 
 tests-internal := tst-mallocstate tst-scratch_buffer
 
@@ -54,7 +55,6 @@  tests-internal += \
 
 ifneq (no,$(have-tunables))
 tests += tst-malloc-usable-tunables
-tests-static += tst-malloc-usable-static-tunables
 endif
 
 tests += $(tests-static)
@@ -78,7 +78,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 +143,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,11 +194,14 @@  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-static-ENV = $(tst-malloc-usable-ENV)
-tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3
-tst-malloc-usable-static-tunables-ENV = $(tst-malloc-usable-tunables-ENV)
+tst-malloc-usable-ENV = MALLOC_CHECK_=3 LD_PRELOAD=$(objpfx)libmalloc-extras.so
+tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 \
+	LD_PRELOAD=$(objpfx)libmalloc-extras.so
+# Do not preload libmalloc-extras.so for tst-malloc-weak-usable* tests.
+tst-malloc-weak-usable-ENV = MALLOC_CHECK_=3
+tst-malloc-weak-usable-static-ENV = MALLOC_CHECK_=3
 
 ifeq ($(experimental-malloc),yes)
 CPPFLAGS-malloc.c += -DUSE_TCACHE=1
@@ -239,12 +247,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)
diff --git a/malloc/Versions b/malloc/Versions
index 2357cff3da..4a7967e790 100644
--- a/malloc/Versions
+++ b/malloc/Versions
@@ -92,5 +92,13 @@  libc {
     __libc_alloc_buffer_copy_bytes;
     __libc_alloc_buffer_copy_string;
     __libc_alloc_buffer_create_failure;
+
+    # Implementation of libmalloc-extras.so.
+    __glibc_free_hook;
+    __glibc_malloc_hook;
+    __glibc_realloc_hook;
+    __glibc_memalign_hook;
+    __glibc_malloc_check_request;
+    __glibc_malloc_check_enable;
   }
 }
diff --git a/malloc/hooks.c b/malloc/hooks.c
index ae7305b036..d854c0bbba 100644
--- a/malloc/hooks.c
+++ b/malloc/hooks.c
@@ -21,46 +21,82 @@ 
    corrupt pointer is detected: do nothing (0), print an error message
    (1), or call abort() (2). */
 
+/* Forward declarations of the check functions for use by the hooks.  */
+static void *malloc_check(size_t sz, const void *caller);
+static void free_check(void *mem, const void *caller);
+static void *realloc_check(void *oldmem, size_t bytes,
+			   const void *caller);
+static void *memalign_check(size_t alignment, size_t bytes,
+			    const void *caller);
+
 /* Hooks for debugging versions.  The initial hooks just call the
    initialization routine, then do the normal work. */
 
 static void *
 malloc_hook_ini (size_t sz, const void *caller)
 {
-  __malloc_hook = NULL;
+  __glibc_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 *
 realloc_hook_ini (void *ptr, size_t sz, const void *caller)
 {
-  __malloc_hook = NULL;
-  __realloc_hook = NULL;
+  __glibc_malloc_hook = NULL;
+  __glibc_realloc_hook = NULL;
   ptmalloc_init ();
-  return __libc_realloc (ptr, sz);
+  return realloc (ptr, sz);
 }
 
 static void *
 memalign_hook_ini (size_t alignment, size_t sz, const void *caller)
 {
-  __memalign_hook = NULL;
+  __glibc_memalign_hook = NULL;
   ptmalloc_init ();
-  return __libc_memalign (alignment, sz);
+  return memalign (alignment, sz);
 }
 
 /* Whether we are using malloc checking.  */
 static int using_malloc_checking;
 
-/* Activate a standard set of debugging hooks. */
+/* In __malloc_check_init we do not enable checking, but we install
+   the hooks.  The hooks are no longer used unless libmalloc-extras.so
+   is preloaded.  Therefore we leave the hooks disabled until
+   the library tells us it has enabled them.  The value is a simple
+   protocol, it starts 0, we install the check hooks and set it to 1,
+   the interposing implementation calls __glibc_malloc_check_enable
+   when ready, and we set using_malloc_checking to 1 and
+   __glibc_malloc_check_request to zero.  That completes the transition
+   between libc.so and libmalloc-extras.so.  */
+int __glibc_malloc_check_request;
+
+/* Install a standard set of debugging hooks. */
 void
 __malloc_check_init (void)
+{
+  /* Don't enable checking yet...  */
+  using_malloc_checking = 0;
+  /* Request that libmalloc-extras.so enable checking once it has
+     been loaded.  */
+  __glibc_malloc_check_request = 1;
+  __glibc_malloc_hook = malloc_check;
+  __glibc_free_hook = free_check;
+  __glibc_realloc_hook = realloc_check;
+  __glibc_memalign_hook = memalign_check;
+}
+
+/* Enable the installed debugging hooks.
+   Called from libmalloc-extras.so.  */
+void
+__glibc_malloc_check_enable (void)
 {
   using_malloc_checking = 1;
-  __malloc_hook = malloc_check;
-  __free_hook = free_check;
-  __realloc_hook = realloc_check;
-  __memalign_hook = memalign_check;
+  __glibc_malloc_check_request = 0;
 }
 
 /* A simple, standard set of debugging hooks.  Overhead is `only' one
@@ -109,8 +145,7 @@  malloc_check_get_size (mchunkptr p)
 }
 
 /* Instrument a chunk with overrun detector byte(s) and convert it
-   into a user pointer with requested size req_sz. */
-
+   into a user pointer with requested size req_sz.  */
 static void *
 mem2mem_check (void *ptr, size_t req_sz)
 {
@@ -142,8 +177,7 @@  mem2mem_check (void *ptr, size_t req_sz)
 }
 
 /* Convert a pointer to be free()d or realloc()ed to a valid chunk
-   pointer.  If the provided pointer is not valid, return NULL. */
-
+   pointer.  If the provided pointer is not valid, return NULL.  */
 static mchunkptr
 mem2chunk_check (void *mem, unsigned char **magic_p)
 {
@@ -453,10 +487,10 @@  malloc_set_state (void *msptr)
      cannot be more than one thread when we reach this point.  */
 
   /* Disable the malloc hooks (and malloc checking).  */
-  __malloc_hook = NULL;
-  __realloc_hook = NULL;
-  __free_hook = NULL;
-  __memalign_hook = NULL;
+  __glibc_malloc_hook = NULL;
+  __glibc_realloc_hook = NULL;
+  __glibc_free_hook = NULL;
+  __glibc_memalign_hook = NULL;
   using_malloc_checking = 0;
 
   /* Patch the dumped heap.  We no longer try to integrate into the
diff --git a/malloc/malloc-extras.c b/malloc/malloc-extras.c
new file mode 100644
index 0000000000..38735d2a56
--- /dev/null
+++ b/malloc/malloc-extras.c
@@ -0,0 +1,792 @@ 
+/* 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 <stdarg.h>
+
+#include <libc-internal.h>
+
+#include "malloc-internal.h"
+#include "malloc-hooks.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;
+
+static void
+flood (void *ptr, int val, size_t size)
+{
+  char *cp = ptr;
+  while (size--)
+    *cp++ = val;
+}
+
+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;
+}
+
+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;
+    }
+  fprintf (stderr, "mcheck: %s", msg);
+  fflush (stderr);
+  abort ();
+}
+
+/* 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;
+}
+
+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 ("");
+}
+
+static int recursing = 0;
+
+/* Call a hook function FUNC, and if recusion is detected return
+   with value ERV.  In addition we check for
+   __glibc_malloc_check_requested to see if the runtime is waiting
+   to know if the hooks are active and if malloc_usable_size can
+   use the more accurate checking sizes for chunks.  */
+#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);	\
+      if (__glibc_malloc_check_request)				\
+	__glibc_malloc_check_enable ();				\
+    }
+
+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 (__glibc_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 (__glibc_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 (__glibc_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 (__glibc_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 (__glibc_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 ();
+}
+
+void
+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);
+    }
+}
+
+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;
+
+  fprintf (f, "= End\n");
+  fclose (f);
+}
+
+/*======================================================================*/
+/* user interface */
+
+void __attribute__ ((constructor))
+mtrace_extras_ctor (void)
+{
+  /* Enable checking if it was requested early.  */
+  if (__glibc_malloc_check_request)
+    __glibc_malloc_check_enable ();
+}
+
+void __attribute__ ((destructor))
+mtrace_extras_dtor (void)
+{
+  if (mallstream)
+    {
+      __libc_freeres ();
+      fprintf (mallstream, "= Unload?\n");
+    }
+}
diff --git a/malloc/malloc-hooks.h b/malloc/malloc-hooks.h
index f09d2318f4..4abb9c2824 100644
--- a/malloc/malloc-hooks.h
+++ b/malloc/malloc-hooks.h
@@ -20,5 +20,13 @@ 
 #define _MALLOC_HOOKS_H
 
 void (*__malloc_initialize_hook) (void);
+extern void (* __glibc_free_hook) (void *__ptr, const void *);
+extern void *(* __glibc_malloc_hook)(size_t __size, const void *);
+extern void *(* __glibc_realloc_hook)(void *__ptr, size_t __size, const void *);
+extern void *(* __glibc_memalign_hook)(size_t __alignment, size_t __size, const void *);
+
+/* Private APIs for libmalloc-extras.so interaction with hooks.  */
+extern int __glibc_malloc_check_request;
+void __glibc_malloc_check_enable (void);
 
 #endif  /* _MALLOC_HOOKS_H */
diff --git a/malloc/malloc-internal.h b/malloc/malloc-internal.h
index 9cee0fb2d7..5e2e610cd3 100644
--- a/malloc/malloc-internal.h
+++ b/malloc/malloc-internal.h
@@ -94,4 +94,8 @@  check_mul_overflow_size_t (size_t left, size_t right, size_t *result)
 #endif
 }
 
+void __malloc_check_init (void) __THROW attribute_hidden;
+
+void *__glibc_morecore (ptrdiff_t increment) attribute_hidden;
+
 #endif /* _MALLOC_INTERNAL_H */
diff --git a/malloc/malloc.c b/malloc/malloc.c
index e247c77b7d..b642971e4e 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -357,11 +357,12 @@  __malloc_assert (const char *assertion, const char *file, unsigned int line,
 
 
 /* Definition for getting more memory from the OS.  */
-#define MORECORE         (*__morecore)
+#define MORECORE         (__glibc_morecore)
 #define MORECORE_FAILURE 0
-void * __default_morecore (ptrdiff_t);
-void *(*__morecore)(ptrdiff_t) = __default_morecore;
-
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28)
+void *(*__morecore)(ptrdiff_t) = __glibc_morecore;
+compat_symbol (libc, __morecore, __morecore, GLIBC_2_0);
+#endif
 
 #include <string.h>
 
@@ -998,20 +999,11 @@  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);
 #endif
 
-static void*   malloc_check(size_t sz, const void *caller);
-static void      free_check(void* mem, const void *caller);
-static void*   realloc_check(void* oldmem, size_t bytes,
-			       const void *caller);
-static void*   memalign_check(size_t alignment, size_t bytes,
-				const void *caller);
-
 /* ------------------ MMAP support ------------------  */
 
 
@@ -1831,6 +1823,13 @@  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;
@@ -1839,24 +1838,33 @@  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,
+void weak_variable (*__glibc_free_hook) (void *__ptr,
                                    const void *) = NULL;
-void *weak_variable (*__malloc_hook)
+void *weak_variable (*__glibc_malloc_hook)
   (size_t __size, const void *) = malloc_hook_ini;
-void *weak_variable (*__realloc_hook)
+void *weak_variable (*__glibc_realloc_hook)
   (void *__ptr, size_t __size, const void *)
   = realloc_hook_ini;
-void *weak_variable (*__memalign_hook)
+void *weak_variable (*__glibc_memalign_hook)
   (size_t __alignment, size_t __size, const void *)
   = memalign_hook_ini;
+
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28)
 void weak_variable (*__after_morecore_hook) (void) = NULL;
 
+/* When building the shared library we include compat symbols for the
+   hooks.  When building a static application we don't, but we also
+   don't define the hooks in any public header, so static applications
+   should not compile.  */
+compat_symbol (libc, __glibc_free_hook, __free_hook, GLIBC_2_0);
+compat_symbol (libc, __glibc_malloc_hook, __malloc_hook, GLIBC_2_0);
+compat_symbol (libc, __glibc_realloc_hook, __realloc_hook, GLIBC_2_0);
+compat_symbol (libc, __glibc_memalign_hook, __memalign_hook, GLIBC_2_0);
+compat_symbol (libc, __after_morecore_hook, __after_morecore_hook, GLIBC_2_0);
+#endif
+
+
 /* This function is called from the arena shutdown hook, to free the
    thread cache (if it exists).  */
 static void tcache_thread_shutdown (void);
@@ -2474,14 +2482,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
@@ -2618,13 +2619,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 */
@@ -2782,10 +2776,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);
@@ -3013,10 +3003,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;
@@ -3071,13 +3059,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;
@@ -3117,10 +3100,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)
@@ -3246,11 +3227,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);
@@ -3369,17 +3345,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;
 
diff --git a/malloc/malloc.h b/malloc/malloc.h
index 3e7c447be1..b8234cf90b 100644
--- a/malloc/malloc.h
+++ b/malloc/malloc.h
@@ -71,14 +71,6 @@  extern void *valloc (size_t __size) __THROW __attribute_malloc__ __wur;
    __size to nearest pagesize. */
 extern void *pvalloc (size_t __size) __THROW __attribute_malloc__ __wur;
 
-/* Underlying allocation function; successive calls should return
-   contiguous pieces of memory.  */
-extern void *(*__morecore) (ptrdiff_t __size);
-
-/* Default value of `__morecore'.  */
-extern void *__default_morecore (ptrdiff_t __size)
-__THROW __attribute_malloc__;
-
 /* SVID2/XPG mallinfo structure */
 
 struct mallinfo
@@ -139,23 +131,6 @@  extern void malloc_stats (void) __THROW;
 /* Output information about state of allocator to stream FP.  */
 extern int malloc_info (int __options, FILE *__fp) __THROW;
 
-/* Hooks for debugging and user-defined versions. */
-extern void (*__MALLOC_HOOK_VOLATILE __free_hook) (void *__ptr,
-                                                   const void *)
-__MALLOC_DEPRECATED;
-extern void *(*__MALLOC_HOOK_VOLATILE __malloc_hook)(size_t __size,
-                                                     const void *)
-__MALLOC_DEPRECATED;
-extern void *(*__MALLOC_HOOK_VOLATILE __realloc_hook)(void *__ptr,
-                                                      size_t __size,
-                                                      const void *)
-__MALLOC_DEPRECATED;
-extern void *(*__MALLOC_HOOK_VOLATILE __memalign_hook)(size_t __alignment,
-                                                       size_t __size,
-                                                       const void *)
-__MALLOC_DEPRECATED;
-extern void (*__MALLOC_HOOK_VOLATILE __after_morecore_hook) (void);
-
 /* Activate a standard set of debugging hooks. */
 extern void __malloc_check_init (void) __THROW __MALLOC_DEPRECATED;
 
diff --git a/malloc/mcheck.c b/malloc/mcheck.c
index dc04a6391a..a0006c560a 100644
--- a/malloc/mcheck.c
+++ b/malloc/mcheck.c
@@ -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;
 }
diff --git a/malloc/morecore.c b/malloc/morecore.c
index 165de7e386..b8242ceaff 100644
--- a/malloc/morecore.c
+++ b/malloc/morecore.c
@@ -42,7 +42,7 @@  libc_hidden_proto (__sbrk)
    and return the start of data space, or NULL on errors.
    If INCREMENT is negative, shrink data space.  */
 void *
-__default_morecore (ptrdiff_t increment)
+__glibc_morecore (ptrdiff_t increment)
 {
   void *result = (void *) __sbrk (increment);
   if (result == (void *) -1)
@@ -50,4 +50,6 @@  __default_morecore (ptrdiff_t increment)
 
   return result;
 }
-libc_hidden_def (__default_morecore)
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28)
+compat_symbol (libc, __glibc_morecore, __default_morecore, GLIBC_2_0);
+#endif
diff --git a/malloc/mtrace.c b/malloc/mtrace.c
index 9064f209ec..dd92e3f92d 100644
--- a/malloc/mtrace.c
+++ b/malloc/mtrace.c
@@ -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,20 @@  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!  */
+   don't forget to set a breakpoint on tr_break!  In all of these cases
+   we call mtrace() and that is interposed by libmalloc-extras.so.  */
 
 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);
-    }
+  return;
 }
 
 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);
+  return;
 }
diff --git a/malloc/tst-malloc-usable-static-tunables.c b/malloc/tst-malloc-usable-static-tunables.c
deleted file mode 100644
index 8907db01a5..0000000000
--- a/malloc/tst-malloc-usable-static-tunables.c
+++ /dev/null
@@ -1 +0,0 @@ 
-#include <malloc/tst-malloc-usable.c>
diff --git a/malloc/tst-malloc-usable-static.c b/malloc/tst-malloc-usable-static.c
deleted file mode 100644
index 8907db01a5..0000000000
--- a/malloc/tst-malloc-usable-static.c
+++ /dev/null
@@ -1 +0,0 @@ 
-#include <malloc/tst-malloc-usable.c>
diff --git a/malloc/tst-malloc-weak-usable-static.c b/malloc/tst-malloc-weak-usable-static.c
new file mode 100644
index 0000000000..a8b9a6652f
--- /dev/null
+++ b/malloc/tst-malloc-weak-usable-static.c
@@ -0,0 +1 @@ 
+#include <malloc/tst-malloc-weak-usable.c>
diff --git a/malloc/tst-malloc-weak-usable.c b/malloc/tst-malloc-weak-usable.c
new file mode 100644
index 0000000000..70f939851b
--- /dev/null
+++ b/malloc/tst-malloc-weak-usable.c
@@ -0,0 +1,60 @@ 
+/* Ensure that malloc_usable_size returns the request size with
+   MALLOC_CHECK_ exported to a positive value and no preloaded
+   libmalloc-extras.so.
+
+   Copyright (C) 2012-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 <malloc.h>
+#include <string.h>
+#include <stdio.h>
+
+/* In this test we are purposely not loading libmalloc-extras.so
+   to test that the malloc_usable_size() correctly falls back to
+   the chunk size, and does not try to parse the check data which
+   is not present.  */
+
+static int
+do_test (void)
+{
+  size_t usable_size;
+  void *p = malloc (5);
+  if (!p)
+    {
+      printf ("memory allocation failed\n");
+      return 1;
+    }
+
+  usable_size = malloc_usable_size (p);
+
+  /* Set a reasonably wide test check here.  The point is that
+     without the checking code we are going to get the chunk
+     size back, and that might vary by architecture in theory.
+     On a 64-bit arch it should be no larger than 32-bytes given
+     the present struct layout, and likely 24 bytes.  */
+  if (usable_size < 5 || usable_size > 32)
+    {
+      printf ("malloc_usable_size: expected > 5 || < 32 but got %zu\n", usable_size);
+      return 1;
+    }
+
+  memset (p, 0, usable_size);
+  free (p);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/malloc/tst-mtrace.sh b/malloc/tst-mtrace.sh
index 9c50c0675d..926c96e3bb 100755
--- a/malloc/tst-mtrace.sh
+++ b/malloc/tst-mtrace.sh
@@ -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
 
diff --git a/manual/memory.texi b/manual/memory.texi
index a1435aad1a..0997412dd7 100644
--- a/manual/memory.texi
+++ b/manual/memory.texi
@@ -328,8 +328,6 @@  any time (or never).
 * Malloc Tunable Parameters::   Use @code{mallopt} to adjust allocation
                                  parameters.
 * Heap Consistency Checking::   Automatic checking for errors.
-* Hooks for Malloc::            You can use these hooks for debugging
-				 programs that use @code{malloc}.
 * Statistics of Malloc::        Getting information about how much
 				 memory your program is using.
 * Summary of Malloc::           Summary of @code{malloc} and related functions.
@@ -347,21 +345,21 @@  this function is in @file{stdlib.h}.
 @standards{ISO, malloc.h}
 @standards{ISO, stdlib.h}
 @safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{} @acsmem{}}}
-@c Malloc hooks and __morecore pointers, as well as such parameters as
-@c max_n_mmaps and max_mmapped_mem, are accessed without guards, so they
-@c could pose a thread safety issue; in order to not declare malloc
-@c MT-unsafe, it's modifying the hooks and parameters while multiple
-@c threads are active that is regarded as unsafe.  An arena's next field
-@c is initialized and never changed again, except for main_arena's,
-@c that's protected by list_lock; next_free is only modified while
-@c list_lock is held too.  All other data members of an arena, as well
-@c as the metadata of the memory areas assigned to it, are only modified
-@c while holding the arena's mutex (fastbin pointers use catomic ops
-@c because they may be modified by free without taking the arena's
-@c lock).  Some reassurance was needed for fastbins, for it wasn't clear
-@c how they were initialized.  It turns out they are always
-@c zero-initialized: main_arena's, for being static data, and other
-@c arena's, for being just-mmapped memory.
+
+@c Malloc parameters such as max_n_mmaps and max_mmapped_mem, are
+@c accessed without guards, so they could pose a thread safety issue; in
+@c order to not declare malloc MT-unsafe, it's modifying the hooks and
+@c parameters while multiple threads are active that is regarded as
+@c unsafe.  An arena's next field is initialized and never changed
+@c again, except for main_arena's, that's protected by list_lock;
+@c next_free is only modified while list_lock is held too.  All other
+@c data members of an arena, as well as the metadata of the memory areas
+@c assigned to it, are only modified while holding the arena's mutex
+@c (fastbin pointers use catomic ops because they may be modified by
+@c free without taking the arena's lock).  Some reassurance was needed
+@c for fastbins, for it wasn't clear how they were initialized.  It
+@c turns out they are always zero-initialized: main_arena's, for being
+@c static data, and other arena's, for being just-mmapped memory.
 
 @c Leaking file descriptors and memory in case of cancellation is
 @c unavoidable without disabling cancellation, but the lock situation is
@@ -375,7 +373,6 @@  this function is in @file{stdlib.h}.
 
 @c __libc_malloc @asulock @aculock @acsfd @acsmem
 @c  force_reg ok
-@c  *malloc_hook unguarded
 @c  arena_lock @asulock @aculock @acsfd @acsmem
 @c   mutex_lock @asulock @aculock
 @c   arena_get2 @asulock @aculock @acsfd @acsmem
@@ -506,11 +503,8 @@  this function is in @file{stdlib.h}.
 @c    set_foot dup ok
 @c    contiguous ok
 @c    MORECORE ok
-@c     *__morecore ok unguarded
-@c      __default_morecore
 @c       sbrk ok
 @c    force_reg dup ok
-@c    *__after_morecore_hook unguarded
 @c    set_noncontiguous ok
 @c    malloc_printerr dup ok
 @c    _int_free (have_lock) @acsfd @acsmem [@asulock @aculock]
@@ -543,7 +537,6 @@  this function is in @file{stdlib.h}.
 @c     malloc_consolidate dup ok
 @c     systrim ok
 @c      MORECORE dup ok
-@c      *__after_morecore_hook dup unguarded
 @c      set_head dup ok
 @c      check_malloc_state ok/disabled
 @c     top dup ok
@@ -695,7 +688,6 @@  The prototype for this function is in @file{stdlib.h}.
 @c   each gets a signal whose handler free()s large (non-fastbin-able)
 @c   blocks from each other's arena, we deadlock; this is a more general
 @c   case of @asulock.
-@c  *__free_hook unguarded
 @c  mem2chunk ok
 @c  chunk_is_mmapped ok, chunk bits not modified after allocation
 @c  chunksize ok
@@ -764,7 +756,6 @@  You can make the block longer by calling @code{realloc} or
 @c issues arise, plus the realloc hook, also accessed without guards.
 
 @c __libc_realloc @asulock @aculock @acsfd @acsmem
-@c  *__realloc_hook unguarded
 @c  __libc_free dup @asulock @aculock @acsfd @acsmem
 @c  __libc_malloc dup @asulock @aculock @acsfd @acsmem
 @c  mem2chunk dup ok
@@ -885,7 +876,6 @@  is declared in @file{stdlib.h}.
 @c Same caveats as malloc.
 
 @c __libc_calloc @asulock @aculock @acsfd @acsmem
-@c  *__malloc_hook dup unguarded
 @c  memset dup ok
 @c  arena_get @asulock @aculock @acsfd @acsmem
 @c   arena_lock dup @asulock @aculock @acsfd @acsmem
@@ -967,7 +957,6 @@  portability to modern non-POSIX systems than @code{posix_memalign}.
 @c _int_memalign, with the arena still locked.
 
 @c __libc_memalign @asulock @aculock @acsfd @acsmem
-@c  *__memalign_hook dup unguarded
 @c  __libc_malloc dup @asulock @aculock @acsfd @acsmem
 @c  arena_get dup @asulock @aculock @acsfd @acsmem
 @c  _int_memalign @acsfd @acsmem
@@ -1056,9 +1045,7 @@  systems that do not support @w{ISO C11}.
 @c   next_env_entry ok
 @c   strcspn dup ok
 @c   __libc_mallopt dup @mtasuconst:mallopt [setting mp_]
-@c   __malloc_check_init @mtasuconst:malloc_hooks [setting hooks]
 @c   *__malloc_initialize_hook unguarded, ok
-@c  *__memalign_hook dup ok, unguarded
 @c  arena_get dup @asulock @aculock @acsfd @acsmem
 @c  _int_valloc @acsfd @acsmem
 @c   malloc_consolidate dup ok
@@ -1206,9 +1193,13 @@  using the @code{mcheck} function.  This function is a GNU extension,
 declared in @file{mcheck.h}.
 @pindex mcheck.h
 
+The functionality associated with @code{MALLOC_CHECK_} or @code{mcheck}
+is disabled by default.  To enable the functionality you must preload
+the @code{libmalloc-extras.so} library.
+
 @deftypefun int mcheck (void (*@var{abortfn}) (enum mcheck_status @var{status}))
 @standards{GNU, mcheck.h}
-@safety{@prelim{}@mtunsafe{@mtasurace{:mcheck} @mtasuconst{:malloc_hooks}}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
+@safety{@prelim{}@mtunsafe{@mtasurace{:mcheck}}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
 @c The hooks must be set up before malloc is first used, which sort of
 @c implies @mtuinit/@asuinit but since the function is a no-op if malloc
 @c was already used, that doesn't pose any safety issues.  The actual
@@ -1261,7 +1252,7 @@  must be called before the first such function.
 @end deftypefun
 
 @deftypefun {enum mcheck_status} mprobe (void *@var{pointer})
-@safety{@prelim{}@mtunsafe{@mtasurace{:mcheck} @mtasuconst{:malloc_hooks}}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
+@safety{@prelim{}@mtunsafe{@mtasurace{:mcheck}}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
 @c The linked list of headers may be modified concurrently by other
 @c threads, and it may find a partial update if called from a signal
 @c handler.  It's mostly read only, so cancelling it might be safe, but
@@ -1334,173 +1325,6 @@  compatibility.  Both @code{MALLOC_CHECK_} and @samp{-lmcheck} should
 uncover the same bugs - but using @code{MALLOC_CHECK_} you don't need to
 recompile your application.
 
-@node Hooks for Malloc
-@subsubsection Memory Allocation Hooks
-@cindex allocation hooks, for @code{malloc}
-
-@Theglibc{} lets you modify the behavior of @code{malloc},
-@code{realloc}, and @code{free} by specifying appropriate hook
-functions.  You can use these hooks to help you debug programs that use
-dynamic memory allocation, for example.
-
-The hook variables are declared in @file{malloc.h}.
-@pindex malloc.h
-
-@defvar __malloc_hook
-@standards{GNU, malloc.h}
-The value of this variable is a pointer to the function that
-@code{malloc} uses whenever it is called.  You should define this
-function to look like @code{malloc}; that is, like:
-
-@smallexample
-void *@var{function} (size_t @var{size}, const void *@var{caller})
-@end smallexample
-
-The value of @var{caller} is the return address found on the stack when
-the @code{malloc} function was called.  This value allows you to trace
-the memory consumption of the program.
-@end defvar
-
-@defvar __realloc_hook
-@standards{GNU, malloc.h}
-The value of this variable is a pointer to function that @code{realloc}
-uses whenever it is called.  You should define this function to look
-like @code{realloc}; that is, like:
-
-@smallexample
-void *@var{function} (void *@var{ptr}, size_t @var{size}, const void *@var{caller})
-@end smallexample
-
-The value of @var{caller} is the return address found on the stack when
-the @code{realloc} function was called.  This value allows you to trace the
-memory consumption of the program.
-@end defvar
-
-@defvar __free_hook
-@standards{GNU, malloc.h}
-The value of this variable is a pointer to function that @code{free}
-uses whenever it is called.  You should define this function to look
-like @code{free}; that is, like:
-
-@smallexample
-void @var{function} (void *@var{ptr}, const void *@var{caller})
-@end smallexample
-
-The value of @var{caller} is the return address found on the stack when
-the @code{free} function was called.  This value allows you to trace the
-memory consumption of the program.
-@end defvar
-
-@defvar __memalign_hook
-@standards{GNU, malloc.h}
-The value of this variable is a pointer to function that @code{aligned_alloc},
-@code{memalign}, @code{posix_memalign} and @code{valloc} use whenever they
-are called.  You should define this function to look like @code{aligned_alloc};
-that is, like:
-
-@smallexample
-void *@var{function} (size_t @var{alignment}, size_t @var{size}, const void *@var{caller})
-@end smallexample
-
-The value of @var{caller} is the return address found on the stack when
-the @code{aligned_alloc}, @code{memalign}, @code{posix_memalign} or
-@code{valloc} functions are called.  This value allows you to trace the
-memory consumption of the program.
-@end defvar
-
-You must make sure that the function you install as a hook for one of
-these functions does not call that function recursively without restoring
-the old value of the hook first!  Otherwise, your program will get stuck
-in an infinite recursion.  Before calling the function recursively, one
-should make sure to restore all the hooks to their previous value.  When
-coming back from the recursive call, all the hooks should be resaved
-since a hook might modify itself.
-
-An issue to look out for is the time at which the malloc hook functions
-can be safely installed.  If the hook functions call the malloc-related
-functions recursively, it is necessary that malloc has already properly
-initialized itself at the time when @code{__malloc_hook} etc. is
-assigned to.  On the other hand, if the hook functions provide a
-complete malloc implementation of their own, it is vital that the hooks
-are assigned to @emph{before} the very first @code{malloc} call has
-completed, because otherwise a chunk obtained from the ordinary,
-un-hooked malloc may later be handed to @code{__free_hook}, for example.
-
-Here is an example showing how to use @code{__malloc_hook} and
-@code{__free_hook} properly.  It installs a function that prints out
-information every time @code{malloc} or @code{free} is called.  We just
-assume here that @code{realloc} and @code{memalign} are not used in our
-program.
-
-@smallexample
-/* Prototypes for __malloc_hook, __free_hook */
-#include <malloc.h>
-
-/* Prototypes for our hooks.  */
-static void my_init_hook (void);
-static void *my_malloc_hook (size_t, const void *);
-static void my_free_hook (void*, const void *);
-
-static void
-my_init (void)
-@{
-  old_malloc_hook = __malloc_hook;
-  old_free_hook = __free_hook;
-  __malloc_hook = my_malloc_hook;
-  __free_hook = my_free_hook;
-@}
-
-static void *
-my_malloc_hook (size_t size, const void *caller)
-@{
-  void *result;
-  /* Restore all old hooks */
-  __malloc_hook = old_malloc_hook;
-  __free_hook = old_free_hook;
-  /* Call recursively */
-  result = malloc (size);
-  /* Save underlying hooks */
-  old_malloc_hook = __malloc_hook;
-  old_free_hook = __free_hook;
-  /* @r{@code{printf} might call @code{malloc}, so protect it too.} */
-  printf ("malloc (%u) returns %p\n", (unsigned int) size, result);
-  /* Restore our own hooks */
-  __malloc_hook = my_malloc_hook;
-  __free_hook = my_free_hook;
-  return result;
-@}
-
-static void
-my_free_hook (void *ptr, const void *caller)
-@{
-  /* Restore all old hooks */
-  __malloc_hook = old_malloc_hook;
-  __free_hook = old_free_hook;
-  /* Call recursively */
-  free (ptr);
-  /* Save underlying hooks */
-  old_malloc_hook = __malloc_hook;
-  old_free_hook = __free_hook;
-  /* @r{@code{printf} might call @code{free}, so protect it too.} */
-  printf ("freed pointer %p\n", ptr);
-  /* Restore our own hooks */
-  __malloc_hook = my_malloc_hook;
-  __free_hook = my_free_hook;
-@}
-
-main ()
-@{
-  my_init ();
-  @dots{}
-@}
-@end smallexample
-
-The @code{mcheck} function (@pxref{Heap Consistency Checking}) works by
-installing such hooks.
-
-@c __morecore, __after_morecore_hook are undocumented
-@c It's not clear whether to document them.
-
 @node Statistics of Malloc
 @subsubsection Statistics for Memory Allocation with @code{malloc}
 
@@ -1632,19 +1456,6 @@  Tell @code{malloc} to perform occasional consistency checks on
 dynamically allocated memory, and to call @var{abortfn} when an
 inconsistency is found.  @xref{Heap Consistency Checking}.
 
-@item void *(*__malloc_hook) (size_t @var{size}, const void *@var{caller})
-A pointer to a function that @code{malloc} uses whenever it is called.
-
-@item void *(*__realloc_hook) (void *@var{ptr}, size_t @var{size}, const void *@var{caller})
-A pointer to a function that @code{realloc} uses whenever it is called.
-
-@item void (*__free_hook) (void *@var{ptr}, const void *@var{caller})
-A pointer to a function that @code{free} uses whenever it is called.
-
-@item void (*__memalign_hook) (size_t @var{size}, size_t @var{alignment}, const void *@var{caller})
-A pointer to a function that @code{aligned_alloc}, @code{memalign},
-@code{posix_memalign} and @code{valloc} use whenever they are called.
-
 @item struct mallinfo mallinfo (void)
 Return information about the current dynamic memory usage.
 @xref{Statistics of Malloc}.
@@ -1667,6 +1478,10 @@  the location.  To do this the application must be started in a special
 mode which is enabled by an environment variable.  There are no speed
 penalties for the program if the debugging mode is not enabled.
 
+The allocation debugging functionality is only safe for use in
+single-threaded programs and requires preloading
+@code{libmalloc-extras.so}.
+
 @menu
 * Tracing malloc::               How to install the tracing functionality.
 * Using the Memory Debugger::    Example programs excerpts.
@@ -1679,7 +1494,7 @@  penalties for the program if the debugging mode is not enabled.
 
 @deftypefun void mtrace (void)
 @standards{GNU, mcheck.h}
-@safety{@prelim{}@mtunsafe{@mtsenv{} @mtasurace{:mtrace} @mtasuconst{:malloc_hooks} @mtuinit{}}@asunsafe{@asuinit{} @ascuheap{} @asucorrupt{} @asulock{}}@acunsafe{@acuinit{} @acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@safety{@prelim{}@mtunsafe{@mtsenv{} @mtasurace{:mtrace} @mtuinit{}}@asunsafe{@asuinit{} @ascuheap{} @asucorrupt{} @asulock{}}@acunsafe{@acuinit{} @acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
 @c Like the mcheck hooks, these are not designed with thread safety in
 @c mind, because the hook pointers are temporarily modified without
 @c regard to other threads, signals or cancellation.
@@ -1715,7 +1530,7 @@  systems.  The prototype can be found in @file{mcheck.h}.
 
 @deftypefun void muntrace (void)
 @standards{GNU, mcheck.h}
-@safety{@prelim{}@mtunsafe{@mtasurace{:mtrace} @mtasuconst{:malloc_hooks} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{}}@acunsafe{@acucorrupt{} @acsmem{} @aculock{} @acsfd{}}}
+@safety{@prelim{}@mtunsafe{@mtasurace{:mtrace} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{}}@acunsafe{@acucorrupt{} @acsmem{} @aculock{} @acsfd{}}}
 
 @c muntrace @mtasurace:mtrace @mtslocale @asucorrupt @ascuheap @acucorrupt @acsmem @aculock @acsfd
 @c  fprintf (fputs) dup @mtslocale @asucorrupt @ascuheap @acsmem @aculock @acucorrupt
diff --git a/misc/Makefile b/misc/Makefile
index b7be2bc19a..901b287468 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -135,13 +135,15 @@  $(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 > $@; \
 	$(evaluate-test)
 
-tst-allocate_once-ENV = MALLOC_TRACE=$(objpfx)tst-allocate_once.mtrace
+tst-allocate_once-ENV = MALLOC_TRACE=$(objpfx)tst-allocate_once.mtrace \
+	LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so
 $(objpfx)tst-allocate_once-mem.out: $(objpfx)tst-allocate_once.out
 	$(common-objpfx)malloc/mtrace $(objpfx)tst-allocate_once.mtrace > $@; \
 	$(evaluate-test)
diff --git a/nptl/Makefile b/nptl/Makefile
index 2f2bb0569d..4ee04ef620 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -529,10 +529,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)
diff --git a/posix/Makefile b/posix/Makefile
index 989e42e1f8..8efa11bc34 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -278,43 +278,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 > $@; \
@@ -323,18 +330,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)
@@ -347,15 +357,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 > $@; \
diff --git a/posix/tst-vfork3.c b/posix/tst-vfork3.c
index 0449b2486a..3ab6bdce49 100644
--- a/posix/tst-vfork3.c
+++ b/posix/tst-vfork3.c
@@ -147,6 +147,12 @@  do_prepare (void)
     FAIL_EXIT1 ("out of memory");
   strcpy (stpcpy (tmpdirname, test_dir), "/tst-vfork3.XXXXXX");
 
+  /* We have preloaded libmalloc-extras.so to implement mtrace().
+     We unset LD_PRELOAD since the children run via /bin/sh which
+     uses the system libc which may lack the integration required
+     by libmalloc-extras.so.  */
+  unsetenv("LD_PRELOAD");
+
   tmpdirname = mkdtemp (tmpdirname);
   if (tmpdirname == NULL)
     FAIL_EXIT1 ("could not create temporary directory");
diff --git a/resolv/Makefile b/resolv/Makefile
index ea395ac3eb..2668ee1c54 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -152,17 +152,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 > $@; \
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 738a3cead0..bdb8578c40 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -91,9 +91,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)' > $@; \