Add Euro character.
[kopensolaris-gnu/glibc.git] / malloc / malloc.c
index c2a94a4..03d68b7 100644 (file)
@@ -1,5 +1,5 @@
 /* Malloc implementation for multiple threads without lock contention.
-   Copyright (C) 1996 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Wolfram Gloger <wmglo@dent.med.uni-muenchen.de>
    and Doug Lea <dl@cs.oswego.edu>, 1996.
@@ -19,7 +19,7 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
-/* V2.6.4-pt2 Sat Dec 14 1996
+/* V2.6.4-pt3 Thu Feb 20 1997
 
   This work is mainly derived from malloc-2.6.4 by Doug Lea
   <dl@cs.oswego.edu>, which is available from:
 
   Maximum overhead wastage per allocated chunk: normally 15 bytes
 
-       Alignnment demands, plus the minimum allocatable size restriction
+       Alignment demands, plus the minimum allocatable size restriction
        make the normal worst-case wastage 15 bytes (i.e., up to 15
        more bytes will be allocated than were requested in malloc), with
        two exceptions:
      Define to enable debugging. Adds fairly extensive assertion-based
      checking to help track down memory errors, but noticeably slows down
      execution.
-  MALLOC_HOOKS              (default: NOT defined)
+  MALLOC_HOOKS             (default: NOT defined)
      Define to enable support run-time replacement of the allocation
      functions through user-defined `hooks'.
   REALLOC_ZERO_BYTES_FREES (default: NOT defined)
 
 #if __STD_C
 # include <stddef.h>   /* for size_t */
-# if defined(_LIBC) || defined(MALLOC_HOOKS)
-#  include <stdlib.h>  /* for getenv() */
+# if defined _LIBC || defined MALLOC_HOOKS
+#  include <stdlib.h>  /* for getenv(), abort() */
 # endif
 #else
 # include <sys/types.h>
@@ -315,13 +315,6 @@ extern "C" {
 
 #include <stdio.h>    /* needed for malloc_stats */
 
-/* We must not pollute the name space in the GNU libc.  */
-#ifdef _LIBC
-#define malloc_stats __malloc_stats
-#define malloc_usable_size __malloc_usable_size
-#define malloc_trim __malloc_trim
-#endif
-
 
 /*
   Compile-time options
@@ -343,7 +336,7 @@ extern "C" {
     checking is fairly extensive, and will slow down execution
     noticeably. Calling malloc_stats or mallinfo with MALLOC_DEBUG set will
     attempt to check every non-mmapped allocated and free chunk in the
-    course of computing the summmaries. (By nature, mmapped regions
+    course of computing the summaries. (By nature, mmapped regions
     cannot be checked very much automatically.)
 
     Setting MALLOC_DEBUG may also be helpful if you are trying to modify
@@ -500,6 +493,10 @@ do {                                                                          \
 #endif
 
 
+#ifndef LACKS_UNISTD_H
+#  include <unistd.h>
+#endif
+
 /*
   Define HAVE_MMAP to optionally make malloc() use mmap() to
   allocate very large blocks.  These will be returned to the
@@ -507,7 +504,9 @@ do {                                                                          \
 */
 
 #ifndef HAVE_MMAP
-#define HAVE_MMAP 1
+# ifdef _POSIX_MAPPED_FILES
+#  define HAVE_MMAP 1
+# endif
 #endif
 
 /*
@@ -517,7 +516,7 @@ do {                                                                          \
 */
 
 #ifndef HAVE_MREMAP
-#define HAVE_MREMAP defined(__linux__)
+#define HAVE_MREMAP defined(__linux__) && !defined(__arm__)
 #endif
 
 #if HAVE_MMAP
@@ -530,6 +529,14 @@ do {                                                                          \
 #define MAP_ANONYMOUS MAP_ANON
 #endif
 
+#ifndef MAP_NORESERVE
+# ifdef MAP_AUTORESRV
+#  define MAP_NORESERVE MAP_AUTORESRV
+# else
+#  define MAP_NORESERVE 0
+# endif
+#endif
+
 #endif /* HAVE_MMAP */
 
 /*
@@ -540,10 +547,6 @@ do {                                                                          \
   bsd/gnu getpagesize.h
 */
 
-#ifndef LACKS_UNISTD_H
-#  include <unistd.h>
-#endif
-
 #ifndef malloc_getpagesize
 #  ifdef _SC_PAGESIZE         /* some SVR4 systems omit an underscore */
 #    ifndef _SC_PAGE_SIZE
@@ -654,7 +657,7 @@ do {                                                                          \
       might set to a value close to the average size of a process
       (program) running on your system.  Releasing this much memory
       would allow such a process to run in memory.  Generally, it's
-      worth it to tune for trimming rather tham memory mapping when a
+      worth it to tune for trimming rather than memory mapping when a
       program undergoes phases where several large chunks are
       allocated and released in ways that can reuse each other's
       storage, perhaps mixed with phases where there are no such
@@ -827,12 +830,12 @@ do {                                                                          \
 #if __STD_C
 
 Void_t * __default_morecore (ptrdiff_t);
-static Void_t *(*__morecore)(ptrdiff_t) = __default_morecore;
+Void_t *(*__morecore)(ptrdiff_t) = __default_morecore;
 
 #else
 
 Void_t * __default_morecore ();
-static Void_t *(*__morecore)() = __default_morecore;
+Void_t *(*__morecore)() = __default_morecore;
 
 #endif
 
@@ -842,6 +845,7 @@ static Void_t *(*__morecore)() = __default_morecore;
 #define mmap    __mmap
 #define munmap  __munmap
 #define mremap  __mremap
+#define mprotect __mprotect
 #undef malloc_getpagesize
 #define malloc_getpagesize __getpagesize()
 
@@ -878,6 +882,11 @@ extern Void_t*     sbrk();
 #define pvALLOc         __libc_pvalloc
 #define mALLINFo        __libc_mallinfo
 #define mALLOPt         __libc_mallopt
+#define mALLOC_STATs    __malloc_stats
+#define mALLOC_USABLE_SIZe __malloc_usable_size
+#define mALLOC_TRIm     __malloc_trim
+#define mALLOC_GET_STATe __malloc_get_state
+#define mALLOC_SET_STATe __malloc_set_state
 
 #else
 
@@ -890,6 +899,11 @@ extern Void_t*     sbrk();
 #define pvALLOc         pvalloc
 #define mALLINFo        mallinfo
 #define mALLOPt         mallopt
+#define mALLOC_STATs    malloc_stats
+#define mALLOC_USABLE_SIZe malloc_usable_size
+#define mALLOC_TRIm     malloc_trim
+#define mALLOC_GET_STATe malloc_get_state
+#define mALLOC_SET_STATe malloc_set_state
 
 #endif
 
@@ -908,15 +922,16 @@ Void_t* vALLOc(size_t);
 Void_t* pvALLOc(size_t);
 Void_t* cALLOc(size_t, size_t);
 void    cfree(Void_t*);
-int     __malloc_trim(size_t);
-int     malloc_trim(size_t);
-size_t  __malloc_usable_size(Void_t*);
-size_t  malloc_usable_size(Void_t*);
-void    __malloc_stats(void);
-void    malloc_stats(void);
+int     mALLOC_TRIm(size_t);
+size_t  mALLOC_USABLE_SIZe(Void_t*);
+void    mALLOC_STATs(void);
 int     mALLOPt(int, int);
 struct mallinfo mALLINFo(void);
-#else
+Void_t* mALLOC_GET_STATe(void);
+int     mALLOC_SET_STATe(Void_t*);
+
+#else /* !__STD_C */
+
 #ifndef _LIBC
 void    ptmalloc_init();
 #endif
@@ -928,15 +943,15 @@ Void_t* vALLOc();
 Void_t* pvALLOc();
 Void_t* cALLOc();
 void    cfree();
-int     __malloc_trim();
-int     malloc_trim();
-size_t  _malloc_usable_size();
-size_t  malloc_usable_size();
-void    __malloc_stats();
-void    malloc_stats();
+int     mALLOC_TRIm();
+size_t  mALLOC_USABLE_SIZe();
+void    mALLOC_STATs();
 int     mALLOPt();
 struct mallinfo mALLINFo();
-#endif
+Void_t* mALLOC_GET_STATe();
+int     mALLOC_SET_STATe();
+
+#endif /* __STD_C */
 
 
 #ifdef __cplusplus
@@ -999,7 +1014,7 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     the malloc code, but "mem" is the pointer that is returned to the
     user.  "Nextchunk" is the beginning of the next contiguous chunk.
 
-    Chunks always begin on even word boundries, so the mem portion
+    Chunks always begin on even word boundaries, so the mem portion
     (which is returned to the user) is also on an even word boundary, and
     thus double-word aligned.
 
@@ -1144,7 +1159,7 @@ typedef struct _arena {
 } arena;
 
 
-/* A heap is a single contiguous memory region holding (coalescable)
+/* A heap is a single contiguous memory region holding (coalesceable)
    malloc_chunks.  It is allocated with mmap() and always starts at an
    address aligned to HEAP_MAX_SIZE.  Not used unless compiling for
    multiple threads. */
@@ -1163,21 +1178,31 @@ typedef struct _heap_info {
 
 #if __STD_C
 
-static void      chunk_free(arena *ar_ptr, mchunkptr p);
-static mchunkptr chunk_alloc(arena *ar_ptr, INTERNAL_SIZE_T size);
+static void      chunk_free(arena *ar_ptr, mchunkptr p) internal_function;
+static mchunkptr chunk_alloc(arena *ar_ptr, INTERNAL_SIZE_T size)
+     internal_function;
 static mchunkptr chunk_realloc(arena *ar_ptr, mchunkptr oldp,
-                              INTERNAL_SIZE_T oldsize, INTERNAL_SIZE_T nb);
+                               INTERNAL_SIZE_T oldsize, INTERNAL_SIZE_T nb)
+     internal_function;
 static mchunkptr chunk_align(arena *ar_ptr, INTERNAL_SIZE_T nb,
-                            size_t alignment);
-static int       main_trim(size_t pad);
+                             size_t alignment) internal_function;
+static int       main_trim(size_t pad) internal_function;
+#ifndef NO_THREADS
+static int       heap_trim(heap_info *heap, size_t pad) internal_function;
+#endif
+#if defined _LIBC || defined MALLOC_HOOKS
+static Void_t*   malloc_check(size_t sz, const Void_t *caller);
+static void      free_check(Void_t* mem, const Void_t *caller);
+static Void_t*   realloc_check(Void_t* oldmem, size_t bytes,
+                              const Void_t *caller);
+static Void_t*   memalign_check(size_t alignment, size_t bytes,
+                               const Void_t *caller);
 #ifndef NO_THREADS
-static int       heap_trim(heap_info *heap, size_t pad);
+static Void_t*   malloc_starter(size_t sz, const Void_t *caller);
+static void      free_starter(Void_t* mem, const Void_t *caller);
+static Void_t*   malloc_atfork(size_t sz, const Void_t *caller);
+static void      free_atfork(Void_t* mem, const Void_t *caller);
 #endif
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
-static Void_t*   malloc_check(size_t sz);
-static void      free_check(Void_t* mem);
-static Void_t*   realloc_check(Void_t* oldmem, size_t bytes);
-static Void_t*   memalign_check(size_t alignment, size_t bytes);
 #endif
 
 #else
@@ -1190,13 +1215,26 @@ static int       main_trim();
 #ifndef NO_THREADS
 static int       heap_trim();
 #endif
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+#if defined _LIBC || defined MALLOC_HOOKS
 static Void_t*   malloc_check();
 static void      free_check();
 static Void_t*   realloc_check();
 static Void_t*   memalign_check();
+#ifndef NO_THREADS
+static Void_t*   malloc_starter();
+static void      free_starter();
+static Void_t*   malloc_atfork();
+static void      free_atfork();
+#endif
+#endif
+
 #endif
 
+/* On some platforms we can compile internal, not exported functions better.
+   Let the environment provide a macro and define it to be empty if it
+   is not available.  */
+#ifndef internal_function
+# define internal_function
 #endif
 
 \f
@@ -1361,13 +1399,13 @@ static Void_t*   memalign_check();
   Indexing into bins
 */
 
-#define bin_index(sz)                                                          \
-(((((unsigned long)(sz)) >> 9) ==    0) ?       (((unsigned long)(sz)) >>  3): \
- ((((unsigned long)(sz)) >> 9) <=    4) ?  56 + (((unsigned long)(sz)) >>  6): \
- ((((unsigned long)(sz)) >> 9) <=   20) ?  91 + (((unsigned long)(sz)) >>  9): \
- ((((unsigned long)(sz)) >> 9) <=   84) ? 110 + (((unsigned long)(sz)) >> 12): \
- ((((unsigned long)(sz)) >> 9) <=  340) ? 119 + (((unsigned long)(sz)) >> 15): \
- ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18): \
+#define bin_index(sz)                                                         \
+(((((unsigned long)(sz)) >> 9) ==    0) ?       (((unsigned long)(sz)) >>  3):\
+ ((((unsigned long)(sz)) >> 9) <=    4) ?  56 + (((unsigned long)(sz)) >>  6):\
+ ((((unsigned long)(sz)) >> 9) <=   20) ?  91 + (((unsigned long)(sz)) >>  9):\
+ ((((unsigned long)(sz)) >> 9) <=   84) ? 110 + (((unsigned long)(sz)) >> 12):\
+ ((((unsigned long)(sz)) >> 9) <=  340) ? 119 + (((unsigned long)(sz)) >> 15):\
+ ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18):\
                                           126)
 /*
   bins for chunks < 512 are all spaced 8 bytes apart, and hold
@@ -1434,7 +1472,7 @@ static arena main_arena = {
  IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119),
  IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127)
     },
-    NULL, /* next */
+    &main_arena, /* next */
     0, /* size */
 #if THREAD_STATS
     0, 0, 0, /* stat_lock_direct, stat_lock_loop, stat_lock_wait */
@@ -1489,7 +1527,93 @@ static unsigned long max_mmapped_mem = 0;
 
 
 \f
+#ifndef _LIBC
+#define weak_variable
+#else
+/* In GNU libc we want the hook variables to be weak definitions to
+   avoid a problem with Emacs.  */
+#define weak_variable weak_function
+#endif
+
+/* Already initialized? */
+int __malloc_initialized = -1;
+
+
+#ifndef NO_THREADS
+
+/* The following two functions are registered via thread_atfork() to
+   make sure that the mutexes remain in a consistent state in the
+   fork()ed version of a thread.  Also adapt the malloc and free hooks
+   temporarily, because the `atfork' handler mechanism may use
+   malloc/free internally (e.g. in LinuxThreads). */
+
+#if defined _LIBC || defined MALLOC_HOOKS
+static __malloc_ptr_t (*save_malloc_hook) __MALLOC_P ((size_t __size,
+                                                      const __malloc_ptr_t));
+static void           (*save_free_hook) __MALLOC_P ((__malloc_ptr_t __ptr,
+                                                    const __malloc_ptr_t));
+static Void_t*        save_arena;
+#endif
+
+static void
+ptmalloc_lock_all __MALLOC_P((void))
+{
+  arena *ar_ptr;
+
+  (void)mutex_lock(&list_lock);
+  for(ar_ptr = &main_arena;;) {
+    (void)mutex_lock(&ar_ptr->mutex);
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+#if defined _LIBC || defined MALLOC_HOOKS
+  save_malloc_hook = __malloc_hook;
+  save_free_hook = __free_hook;
+  __malloc_hook = malloc_atfork;
+  __free_hook = free_atfork;
+  /* Only the current thread may perform malloc/free calls now. */
+  tsd_getspecific(arena_key, save_arena);
+  tsd_setspecific(arena_key, (Void_t*)0);
+#endif
+}
+
+static void
+ptmalloc_unlock_all __MALLOC_P((void))
+{
+  arena *ar_ptr;
+
+#if defined _LIBC || defined MALLOC_HOOKS
+  tsd_setspecific(arena_key, save_arena);
+  __malloc_hook = save_malloc_hook;
+  __free_hook = save_free_hook;
+#endif
+  for(ar_ptr = &main_arena;;) {
+    (void)mutex_unlock(&ar_ptr->mutex);
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+  (void)mutex_unlock(&list_lock);
+}
 
+static void
+ptmalloc_init_all __MALLOC_P((void))
+{
+  arena *ar_ptr;
+
+#if defined _LIBC || defined MALLOC_HOOKS
+  tsd_setspecific(arena_key, save_arena);
+  __malloc_hook = save_malloc_hook;
+  __free_hook = save_free_hook;
+#endif
+  for(ar_ptr = &main_arena;;) {
+    (void)mutex_init(&ar_ptr->mutex);
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+  (void)mutex_init(&list_lock);
+}
+
+#endif
 
 /* Initialization routine. */
 #if defined(_LIBC)
@@ -1504,100 +1628,154 @@ void
 ptmalloc_init __MALLOC_P((void))
 #endif
 {
-  static int first = 1;
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+#if defined _LIBC || defined MALLOC_HOOKS
   const char* s;
 #endif
 
-  if(!first) return;
-  first = 0;
-#if defined(_LIBC)
+  if(__malloc_initialized >= 0) return;
+  __malloc_initialized = 0;
+#ifndef NO_THREADS
+#if defined _LIBC || defined MALLOC_HOOKS
+  /* With some threads implementations, creating thread-specific data
+     or initializing a mutex may call malloc() itself.  Provide a
+     simple starter version (realloc() won't work). */
+  save_malloc_hook = __malloc_hook;
+  save_free_hook = __free_hook;
+  __malloc_hook = malloc_starter;
+  __free_hook = free_starter;
+#endif
+#ifdef _LIBC
   /* Initialize the pthreads interface. */
   if (__pthread_initialize != NULL)
     __pthread_initialize();
 #endif
-#ifndef NO_THREADS
   mutex_init(&main_arena.mutex);
   mutex_init(&list_lock);
   tsd_key_create(&arena_key, NULL);
   tsd_setspecific(arena_key, (Void_t *)&main_arena);
-#endif
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+  thread_atfork(ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_init_all);
+#endif /* !defined NO_THREADS */
+#if defined _LIBC || defined MALLOC_HOOKS
+  if((s = getenv("MALLOC_TRIM_THRESHOLD_")))
+    mALLOPt(M_TRIM_THRESHOLD, atoi(s));
+  if((s = getenv("MALLOC_TOP_PAD_")))
+    mALLOPt(M_TOP_PAD, atoi(s));
+  if((s = getenv("MALLOC_MMAP_THRESHOLD_")))
+    mALLOPt(M_MMAP_THRESHOLD, atoi(s));
+  if((s = getenv("MALLOC_MMAP_MAX_")))
+    mALLOPt(M_MMAP_MAX, atoi(s));
   s = getenv("MALLOC_CHECK_");
+#ifndef NO_THREADS
+  __malloc_hook = save_malloc_hook;
+  __free_hook = save_free_hook;
+#endif
   if(s) {
-    if(s[0]) mallopt(M_CHECK_ACTION, (int)(s[0] - '0'));
-    malloc_check_init();
+    if(s[0]) mALLOPt(M_CHECK_ACTION, (int)(s[0] - '0'));
+    __malloc_check_init();
   }
   if(__malloc_initialize_hook != NULL)
     (*__malloc_initialize_hook)();
 #endif
+  __malloc_initialized = 1;
 }
 
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+/* There are platforms (e.g. Hurd) with a link-time hook mechanism. */
+#ifdef thread_atfork_static
+thread_atfork_static(ptmalloc_lock_all, ptmalloc_unlock_all, \
+                     ptmalloc_init_all)
+#endif
+
+#if defined _LIBC || defined MALLOC_HOOKS
 
 /* Hooks for debugging versions.  The initial hooks just call the
    initialization routine, then do the normal work. */
 
 static Void_t*
 #if __STD_C
-malloc_hook_ini(size_t sz)
+malloc_hook_ini(size_t sz, const __malloc_ptr_t caller)
 #else
-malloc_hook_ini(sz) size_t sz;
+malloc_hook_ini(sz, caller)
+     size_t sz; const __malloc_ptr_t caller;
 #endif
 {
   __malloc_hook = NULL;
-  __realloc_hook = NULL;
-  __memalign_hook = NULL;
   ptmalloc_init();
   return mALLOc(sz);
 }
 
 static Void_t*
 #if __STD_C
-realloc_hook_ini(Void_t* ptr, size_t sz)
+realloc_hook_ini(Void_t* ptr, size_t sz, const __malloc_ptr_t caller)
 #else
-realloc_hook_ini(ptr, sz) Void_t* ptr; size_t sz;
+realloc_hook_ini(ptr, sz, caller)
+     Void_t* ptr; size_t sz; const __malloc_ptr_t caller;
 #endif
 {
   __malloc_hook = NULL;
   __realloc_hook = NULL;
-  __memalign_hook = NULL;
   ptmalloc_init();
   return rEALLOc(ptr, sz);
 }
 
 static Void_t*
 #if __STD_C
-memalign_hook_ini(size_t sz, size_t alignment)
+memalign_hook_ini(size_t sz, size_t alignment, const __malloc_ptr_t caller)
 #else
-memalign_hook_ini(sz, alignment) size_t sz; size_t alignment;
+memalign_hook_ini(sz, alignment, caller)
+     size_t sz; size_t alignment; const __malloc_ptr_t caller;
 #endif
 {
-  __malloc_hook = NULL;
-  __realloc_hook = NULL;
   __memalign_hook = NULL;
   ptmalloc_init();
   return mEMALIGn(sz, alignment);
 }
 
-void (*__malloc_initialize_hook) __MALLOC_P ((void)) = NULL;
-void (*__free_hook) __MALLOC_P ((__malloc_ptr_t __ptr)) = NULL;
-__malloc_ptr_t (*__malloc_hook)
- __MALLOC_P ((size_t __size)) = malloc_hook_ini;
-__malloc_ptr_t (*__realloc_hook)
- __MALLOC_P ((__malloc_ptr_t __ptr, size_t __size)) = realloc_hook_ini;
-__malloc_ptr_t (*__memalign_hook)
- __MALLOC_P ((size_t __size, size_t __alignment)) = memalign_hook_ini;
+void weak_variable (*__malloc_initialize_hook) __MALLOC_P ((void)) = NULL;
+void weak_variable (*__free_hook) __MALLOC_P ((__malloc_ptr_t __ptr,
+                                              const __malloc_ptr_t)) = NULL;
+__malloc_ptr_t weak_variable (*__malloc_hook)
+ __MALLOC_P ((size_t __size, const __malloc_ptr_t)) = malloc_hook_ini;
+__malloc_ptr_t weak_variable (*__realloc_hook)
+ __MALLOC_P ((__malloc_ptr_t __ptr, size_t __size, const __malloc_ptr_t))
+     = realloc_hook_ini;
+__malloc_ptr_t weak_variable (*__memalign_hook)
+ __MALLOC_P ((size_t __size, size_t __alignment, const __malloc_ptr_t))
+     = memalign_hook_ini;
+void weak_variable (*__after_morecore_hook) __MALLOC_P ((void)) = NULL;
+
+/* Whether we are using malloc checking.  */
+static int using_malloc_checking;
+
+/* A flag that is set by malloc_set_state, to signal that malloc checking
+   must not be enabled on the request from the user (via the MALLOC_CHECK_
+   environment variable).  It is reset by __malloc_check_init to tell
+   malloc_set_state that the user has requested malloc checking.
+
+   The purpose of this flag is to make sure that malloc checking is not
+   enabled when the heap to be restored was constructed without malloc
+   checking, and thus does not contain the required magic bytes.
+   Otherwise the heap would be corrupted by calls to free and realloc.  If
+   it turns out that the heap was created with malloc checking and the
+   user has requested it malloc_set_state just calls __malloc_check_init
+   again to enable it.  On the other hand, reusing such a heap without
+   further malloc checking is safe.  */
+static int disallow_malloc_check;
 
 /* Activate a standard set of debugging hooks. */
 void
-malloc_check_init()
+__malloc_check_init()
 {
+  if (disallow_malloc_check) {
+    disallow_malloc_check = 0;
+    return;
+  }
+  using_malloc_checking = 1;
   __malloc_hook = malloc_check;
   __free_hook = free_check;
   __realloc_hook = realloc_check;
   __memalign_hook = memalign_check;
-  fprintf(stderr, "Using debugging hooks\n");
+  if(check_action == 1)
+    fprintf(stderr, "malloc: using debugging hooks\n");
 }
 
 #endif
@@ -1614,22 +1792,28 @@ malloc_check_init()
 
 static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
 
-#define MMAP(size, prot) ((dev_zero_fd < 0) ? \
+#define MMAP(addr, size, prot, flags) ((dev_zero_fd < 0) ? \
  (dev_zero_fd = open("/dev/zero", O_RDWR), \
-  mmap(0, (size), (prot), MAP_PRIVATE, dev_zero_fd, 0)) : \
-   mmap(0, (size), (prot), MAP_PRIVATE, dev_zero_fd, 0))
+  mmap((addr), (size), (prot), (flags), dev_zero_fd, 0)) : \
+   mmap((addr), (size), (prot), (flags), dev_zero_fd, 0))
 
 #else
 
-#define MMAP(size, prot) \
- (mmap(0, (size), (prot), MAP_PRIVATE|MAP_ANONYMOUS, -1, 0))
+#define MMAP(addr, size, prot, flags) \
+ (mmap((addr), (size), (prot), (flags)|MAP_ANONYMOUS, -1, 0))
 
 #endif
 
+#if defined __GNUC__ && __GNUC__ >= 2
+/* This function is only called from one place, inline it.  */
+inline
+#endif
+static mchunkptr
+internal_function
 #if __STD_C
-static mchunkptr mmap_chunk(size_t size)
+mmap_chunk(size_t size)
 #else
-static mchunkptr mmap_chunk(size) size_t size;
+mmap_chunk(size) size_t size;
 #endif
 {
   size_t page_mask = malloc_getpagesize - 1;
@@ -1642,8 +1826,8 @@ static mchunkptr mmap_chunk(size) size_t size;
    */
   size = (size + SIZE_SZ + page_mask) & ~page_mask;
 
-  p = (mchunkptr)MMAP(size, PROT_READ|PROT_WRITE);
-  if(p == (mchunkptr)-1) return 0;
+  p = (mchunkptr)MMAP(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE);
+  if(p == (mchunkptr) MAP_FAILED) return 0;
 
   n_mmaps++;
   if (n_mmaps > max_n_mmaps) max_n_mmaps = n_mmaps;
@@ -1749,6 +1933,7 @@ static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size;
    of the page size. */
 
 static heap_info *
+internal_function
 #if __STD_C
 new_heap(size_t size)
 #else
@@ -1760,13 +1945,22 @@ new_heap(size) size_t size;
   unsigned long ul;
   heap_info *h;
 
-  if(size < HEAP_MIN_SIZE)
+  if(size+top_pad < HEAP_MIN_SIZE)
     size = HEAP_MIN_SIZE;
-  size = (size + page_mask) & ~page_mask;
-  if(size > HEAP_MAX_SIZE)
+  else if(size+top_pad <= HEAP_MAX_SIZE)
+    size += top_pad;
+  else if(size > HEAP_MAX_SIZE)
     return 0;
-  p1 = (char *)MMAP(HEAP_MAX_SIZE<<1, PROT_NONE);
-  if(p1 == (char *)-1)
+  else
+    size = HEAP_MAX_SIZE;
+  size = (size + page_mask) & ~page_mask;
+
+  /* A memory region aligned to a multiple of HEAP_MAX_SIZE is needed.
+     No swap space needs to be reserved for the following large
+     mapping (on Linux, this is the case for all non-writable mappings
+     anyway). */
+  p1 = (char *)MMAP(0, HEAP_MAX_SIZE<<1, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE);
+  if(p1 == MAP_FAILED)
     return 0;
   p2 = (char *)(((unsigned long)p1 + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE-1));
   ul = p2 - p1;
@@ -1806,7 +2000,10 @@ grow_heap(h, diff) heap_info *h; long diff;
     new_size = (long)h->size + diff;
     if(new_size < (long)sizeof(*h))
       return -1;
-    if(mprotect((char *)h + new_size, -diff, PROT_NONE) != 0)
+    /* Try to re-map the extra heap space freshly to save memory, and 
+       make it inaccessible. */ 
+    if((char *)MMAP((char *)h + new_size, -diff, PROT_NONE, 
+                   MAP_PRIVATE|MAP_FIXED) == (char *) MAP_FAILED)
       return -2;
   }
   h->size = new_size;
@@ -1820,20 +2017,20 @@ grow_heap(h, diff) heap_info *h; long diff;
 /* arena_get() acquires an arena and locks the corresponding mutex.
    First, try the one last locked successfully by this thread.  (This
    is the common case and handled with a macro for speed.)  Then, loop
-   over the singly linked list of arenas.  If no arena is readily
-   available, create a new one.  */
+   once over the circularly linked list of arenas.  If no arena is
+   readily available, create a new one. */
 
 #define arena_get(ptr, size) do { \
   Void_t *vptr = NULL; \
   ptr = (arena *)tsd_getspecific(arena_key, vptr); \
   if(ptr && !mutex_trylock(&ptr->mutex)) { \
     THREAD_STAT(++(ptr->stat_lock_direct)); \
-  } else \
+  } else \
     ptr = arena_get2(ptr, (size)); \
-  } \
 } while(0)
 
 static arena *
+internal_function
 #if __STD_C
 arena_get2(arena *a_tsd, size_t size)
 #else
@@ -1846,17 +2043,38 @@ arena_get2(a_tsd, size) arena *a_tsd; size_t size;
   int i;
   unsigned long misalign;
 
-  /* Check the singly-linked list for unlocked arenas. */
-  if(a_tsd) {
-    for(a = a_tsd->next; a; a = a->next) {
-      if(!mutex_trylock(&a->mutex))
-        goto done;
+  if(!a_tsd)
+    a = a_tsd = &main_arena;
+  else {
+    a = a_tsd->next;
+    if(!a) {
+      /* This can only happen while initializing the new arena. */
+      (void)mutex_lock(&main_arena.mutex);
+      THREAD_STAT(++(main_arena.stat_lock_wait));
+      return &main_arena;
     }
   }
-  for(a = &main_arena; a != a_tsd; a = a->next) {
-    if(!mutex_trylock(&a->mutex))
-      goto done;
+
+  /* Check the global, circularly linked list for available arenas. */
+ repeat:
+  do {
+    if(!mutex_trylock(&a->mutex)) {
+      THREAD_STAT(++(a->stat_lock_loop));
+      tsd_setspecific(arena_key, (Void_t *)a);
+      return a;
+    }
+    a = a->next;
+  } while(a != a_tsd);
+
+  /* If not even the list_lock can be obtained, try again.  This can
+     happen during `atfork', or for example on systems where thread
+     creation makes it temporarily impossible to obtain _any_
+     locks. */
+  if(mutex_trylock(&list_lock)) {
+    a = a_tsd;
+    goto repeat;
   }
+  (void)mutex_unlock(&list_lock);
 
   /* Nothing immediately available, so generate a new arena. */
   h = new_heap(size + (sizeof(*h) + sizeof(*a) + MALLOC_ALIGNMENT));
@@ -1865,7 +2083,9 @@ arena_get2(a_tsd, size) arena *a_tsd; size_t size;
   a = h->ar_ptr = (arena *)(h+1);
   for(i=0; i<NAV; i++)
     init_bin(a, i);
+  a->next = NULL;
   a->size = h->size;
+  tsd_setspecific(arena_key, (Void_t *)a);
   mutex_init(&a->mutex);
   i = mutex_lock(&a->mutex); /* remember result */
 
@@ -1886,9 +2106,7 @@ arena_get2(a_tsd, size) arena *a_tsd; size_t size;
   if(i) /* locking failed; keep arena for further attempts later */
     return 0;
 
-done:
   THREAD_STAT(++(a->stat_lock_loop));
-  tsd_setspecific(arena_key, (Void_t *)a);
   return a;
 }
 
@@ -1941,7 +2159,10 @@ static void do_check_chunk(ar_ptr, p) arena *ar_ptr; mchunkptr p;
   if(ar_ptr != &main_arena) {
     heap_info *heap = heap_for_ptr(p);
     assert(heap->ar_ptr == ar_ptr);
-    assert((char *)p + sz <= (char *)heap + heap->size);
+    if(p != top(ar_ptr)) 
+      assert((char *)p + sz <= (char *)heap + heap->size); 
+    else 
+      assert((char *)p + sz == (char *)heap + heap->size);
     return;
   }
 #endif
@@ -2140,10 +2361,16 @@ arena *ar_ptr; mchunkptr p; INTERNAL_SIZE_T s;
   Main interface to sbrk (but see also malloc_trim).
 */
 
+#if defined __GNUC__ && __GNUC__ >= 2
+/* This function is called only from one place, inline it.  */
+inline
+#endif
+static void
+internal_function
 #if __STD_C
-static void malloc_extend_top(arena *ar_ptr, INTERNAL_SIZE_T nb)
+malloc_extend_top(arena *ar_ptr, INTERNAL_SIZE_T nb)
 #else
-static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
+malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
 #endif
 {
   unsigned long pagesz   = malloc_getpagesize;
@@ -2178,6 +2405,12 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
         (brk < old_end && old_top != initial_top(&main_arena)))
       return;
 
+#if defined _LIBC || defined MALLOC_HOOKS
+    /* Call the `morecore' hook if necessary.  */
+    if (__after_morecore_hook)
+      (*__after_morecore_hook) ();
+#endif
+
     sbrked_mem += sbrk_size;
 
     if (brk == old_end) { /* can just add bytes to current top */
@@ -2206,6 +2439,12 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
       new_brk = (char*)(MORECORE (correction));
       if (new_brk == (char*)(MORECORE_FAILURE)) return;
 
+#if defined _LIBC || defined MALLOC_HOOKS
+      /* Call the `morecore' hook if necessary.  */
+      if (__after_morecore_hook)
+        (*__after_morecore_hook) ();
+#endif
+
       sbrked_mem += correction;
 
       top(&main_arena) = (mchunkptr)brk;
@@ -2245,7 +2484,7 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
     }
 
     /* A new heap must be created. */
-    heap = new_heap(nb + top_pad + (MINSIZE + sizeof(*heap)));
+    heap = new_heap(nb + (MINSIZE + sizeof(*heap)));
     if(!heap)
       return;
     heap->ar_ptr = ar_ptr;
@@ -2339,7 +2578,7 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
          mallocs with other sbrk calls.
 
 
-      All allocations are made from the the `lowest' part of any found
+      All allocations are made from the `lowest' part of any found
       chunk. (The implementation invariant is that prev_inuse is
       always true of any allocated chunk; i.e., that each allocated
       chunk borders either a previously allocated and still in-use chunk,
@@ -2357,25 +2596,39 @@ Void_t* mALLOc(bytes) size_t bytes;
   INTERNAL_SIZE_T nb; /* padded request size */
   mchunkptr victim;
 
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+#if defined _LIBC || defined MALLOC_HOOKS
   if (__malloc_hook != NULL) {
     Void_t* result;
 
-    result = (*__malloc_hook)(bytes);
+#if defined __GNUC__ && __GNUC__ >= 2
+    result = (*__malloc_hook)(bytes, __builtin_return_address (0));
+#else
+    result = (*__malloc_hook)(bytes, NULL);
+#endif
     return result;
   }
 #endif
 
   nb = request2size(bytes);
-  arena_get(ar_ptr, nb + top_pad);
+  arena_get(ar_ptr, nb);
   if(!ar_ptr)
     return 0;
   victim = chunk_alloc(ar_ptr, nb);
   (void)mutex_unlock(&ar_ptr->mutex);
-  return victim ? chunk2mem(victim) : 0;
+  if(!victim) {
+    /* Maybe the failure is due to running out of mmapped areas. */
+    if(ar_ptr != &main_arena) {
+      (void)mutex_lock(&main_arena.mutex);
+      victim = chunk_alloc(&main_arena, nb);
+      (void)mutex_unlock(&main_arena.mutex);
+    }
+    if(!victim) return 0;
+  }
+  return chunk2mem(victim);
 }
 
 static mchunkptr
+internal_function
 #if __STD_C
 chunk_alloc(arena *ar_ptr, INTERNAL_SIZE_T nb)
 #else
@@ -2641,9 +2894,13 @@ void fREe(mem) Void_t* mem;
   arena *ar_ptr;
   mchunkptr p;                          /* chunk corresponding to mem */
 
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+#if defined _LIBC || defined MALLOC_HOOKS
   if (__free_hook != NULL) {
-    (*__free_hook)(mem);
+#if defined __GNUC__ && __GNUC__ >= 2
+    (*__free_hook)(mem, __builtin_return_address (0));
+#else
+    (*__free_hook)(mem, NULL);
+#endif
     return;
   }
 #endif
@@ -2677,6 +2934,7 @@ void fREe(mem) Void_t* mem;
 }
 
 static void
+internal_function
 #if __STD_C
 chunk_free(arena *ar_ptr, mchunkptr p)
 #else
@@ -2734,8 +2992,6 @@ chunk_free(ar_ptr, p) arena *ar_ptr; mchunkptr p;
     return;
   }
 
-  set_head(next, nextsz);                    /* clear inuse bit */
-
   islr = 0;
 
   if (!(hd & PREV_INUSE))                    /* consolidate backward */
@@ -2762,12 +3018,29 @@ chunk_free(ar_ptr, p) arena *ar_ptr; mchunkptr p;
     }
     else
       unlink(next, bck, fwd);
+
+    next = chunk_at_offset(p, sz);
   }
+  else
+    set_head(next, nextsz);                  /* clear inuse bit */
 
   set_head(p, sz | PREV_INUSE);
-  set_foot(p, sz);
+  next->prev_size = sz;
   if (!islr)
     frontlink(ar_ptr, p, sz, idx, bck, fwd);
+
+#ifndef NO_THREADS
+  /* Check whether the heap containing top can go away now. */
+  if(next->size < MINSIZE &&
+     (unsigned long)sz > trim_threshold &&
+     ar_ptr != &main_arena) {                /* fencepost */
+    heap_info* heap = heap_for_ptr(top(ar_ptr));
+
+    if(top(ar_ptr) == chunk_at_offset(heap, sizeof(*heap)) &&
+       heap->prev == heap_for_ptr(p))
+      heap_trim(heap, top_pad);
+  }
+#endif
 }
 
 
@@ -2824,11 +3097,15 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
 
   mchunkptr newp;             /* chunk to return */
 
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+#if defined _LIBC || defined MALLOC_HOOKS
   if (__realloc_hook != NULL) {
     Void_t* result;
 
-    result = (*__realloc_hook)(oldmem, bytes);
+#if defined __GNUC__ && __GNUC__ >= 2
+    result = (*__realloc_hook)(oldmem, bytes, __builtin_return_address (0));
+#else
+    result = (*__realloc_hook)(oldmem, bytes, NULL);
+#endif
     return result;
   }
 #endif
@@ -2877,8 +3154,10 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
   (void)mutex_lock(&ar_ptr->mutex);
 #endif
 
+#ifndef NO_THREADS
   /* As in malloc(), remember this arena for the next allocation. */
   tsd_setspecific(arena_key, (Void_t *)ar_ptr);
+#endif
 
   newp = chunk_realloc(ar_ptr, oldp, oldsize, nb);
 
@@ -2887,9 +3166,10 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
 }
 
 static mchunkptr
+internal_function
 #if __STD_C
 chunk_realloc(arena* ar_ptr, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
-             INTERNAL_SIZE_T nb)
+              INTERNAL_SIZE_T nb)
 #else
 chunk_realloc(ar_ptr, oldp, oldsize, nb)
 arena* ar_ptr; mchunkptr oldp; INTERNAL_SIZE_T oldsize, nb;
@@ -3003,8 +3283,16 @@ arena* ar_ptr; mchunkptr oldp; INTERNAL_SIZE_T oldsize, nb;
 
     newp = chunk_alloc (ar_ptr, nb);
 
-    if (newp == 0)  /* propagate failure */
-      return 0;
+    if (newp == 0) {
+      /* Maybe the failure is due to running out of mmapped areas. */
+      if (ar_ptr != &main_arena) {
+        (void)mutex_lock(&main_arena.mutex);
+        newp = chunk_alloc(&main_arena, nb);
+        (void)mutex_unlock(&main_arena.mutex);
+      }
+      if (newp == 0) /* propagate failure */
+        return 0;
+    }
 
     /* Avoid copy if newp is next chunk after oldp. */
     /* (This can only happen when new chunk is sbrk'ed.) */
@@ -3076,11 +3364,16 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
   INTERNAL_SIZE_T    nb;      /* padded  request size */
   mchunkptr p;
 
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+#if defined _LIBC || defined MALLOC_HOOKS
   if (__memalign_hook != NULL) {
     Void_t* result;
 
-    result = (*__memalign_hook)(alignment, bytes);
+#if defined __GNUC__ && __GNUC__ >= 2
+    result = (*__memalign_hook)(alignment, bytes,
+                               __builtin_return_address (0));
+#else
+    result = (*__memalign_hook)(alignment, bytes, NULL);
+#endif
     return result;
   }
 #endif
@@ -3099,10 +3392,20 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
     return 0;
   p = chunk_align(ar_ptr, nb, alignment);
   (void)mutex_unlock(&ar_ptr->mutex);
-  return p ? chunk2mem(p) : NULL;
+  if(!p) {
+    /* Maybe the failure is due to running out of mmapped areas. */
+    if(ar_ptr != &main_arena) {
+      (void)mutex_lock(&main_arena.mutex);
+      p = chunk_align(&main_arena, nb, alignment);
+      (void)mutex_unlock(&main_arena.mutex);
+    }
+    if(!p) return 0;
+  }
+  return chunk2mem(p);
 }
 
 static mchunkptr
+internal_function
 #if __STD_C
 chunk_align(arena* ar_ptr, INTERNAL_SIZE_T nb, size_t alignment)
 #else
@@ -3239,16 +3542,22 @@ Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size;
   INTERNAL_SIZE_T sz, csz, oldtopsize;
   Void_t* mem;
 
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+#if defined _LIBC || defined MALLOC_HOOKS
   if (__malloc_hook != NULL) {
     sz = n * elem_size;
-    mem = (*__malloc_hook)(sz);
-#ifdef HAVE_MEMCPY
-    memset(mem, 0, sz);
+#if defined __GNUC__ && __GNUC__ >= 2
+    mem = (*__malloc_hook)(sz, __builtin_return_address (0));
 #else
-    while(sz > 0) mem[--sz] = 0; /* rather inefficient */
+    mem = (*__malloc_hook)(sz, NULL);
 #endif
+    if(mem == 0)
+      return 0;
+#ifdef HAVE_MEMSET
+    return memset(mem, 0, sz);
+#else
+    while(sz > 0) ((char*)mem)[--sz] = 0; /* rather inefficient */
     return mem;
+#endif
   }
 #endif
 
@@ -3267,31 +3576,34 @@ Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size;
   /* Only clearing follows, so we can unlock early. */
   (void)mutex_unlock(&ar_ptr->mutex);
 
-  if (p == 0)
-    return 0;
-  else
-  {
-    mem = chunk2mem(p);
+  if (p == 0) {
+    /* Maybe the failure is due to running out of mmapped areas. */
+    if(ar_ptr != &main_arena) {
+      (void)mutex_lock(&main_arena.mutex);
+      p = chunk_alloc(&main_arena, sz);
+      (void)mutex_unlock(&main_arena.mutex);
+    }
+    if (p == 0) return 0;
+  }
+  mem = chunk2mem(p);
 
-    /* Two optional cases in which clearing not necessary */
+  /* Two optional cases in which clearing not necessary */
 
 #if HAVE_MMAP
-    if (chunk_is_mmapped(p)) return mem;
+  if (chunk_is_mmapped(p)) return mem;
 #endif
 
-    csz = chunksize(p);
+  csz = chunksize(p);
 
 #if MORECORE_CLEARS
-    if (p == oldtop && csz > oldtopsize)
-    {
-      /* clear only the bytes from non-freshly-sbrked memory */
-      csz = oldtopsize;
-    }
+  if (p == oldtop && csz > oldtopsize) {
+    /* clear only the bytes from non-freshly-sbrked memory */
+    csz = oldtopsize;
+  }
 #endif
 
-    MALLOC_ZERO(mem, csz - SIZE_SZ);
-    return mem;
-  }
+  MALLOC_ZERO(mem, csz - SIZE_SZ);
+  return mem;
 }
 
 /*
@@ -3338,9 +3650,9 @@ void cfree(mem) Void_t *mem;
 */
 
 #if __STD_C
-int malloc_trim(size_t pad)
+int mALLOC_TRIm(size_t pad)
 #else
-int malloc_trim(pad) size_t pad;
+int mALLOC_TRIm(pad) size_t pad;
 #endif
 {
   int res;
@@ -3354,6 +3666,7 @@ int malloc_trim(pad) size_t pad;
 /* Trim the main arena. */
 
 static int
+internal_function
 #if __STD_C
 main_trim(size_t pad)
 #else
@@ -3382,6 +3695,12 @@ main_trim(pad) size_t pad;
 
   new_brk = (char*)(MORECORE (-extra));
 
+#if defined _LIBC || defined MALLOC_HOOKS
+  /* Call the `morecore' hook if necessary.  */
+  if (__after_morecore_hook)
+    (*__after_morecore_hook) ();
+#endif
+
   if (new_brk == (char*)(MORECORE_FAILURE)) { /* sbrk failed? */
     /* Try to figure out what we have */
     current_brk = (char*)(MORECORE (0));
@@ -3405,6 +3724,7 @@ main_trim(pad) size_t pad;
 #ifndef NO_THREADS
 
 static int
+internal_function
 #if __STD_C
 heap_trim(heap_info *heap, size_t pad)
 #else
@@ -3474,9 +3794,9 @@ heap_trim(heap, pad) heap_info *heap; size_t pad;
 */
 
 #if __STD_C
-size_t malloc_usable_size(Void_t* mem)
+size_t mALLOC_USABLE_SIZe(Void_t* mem)
 #else
-size_t malloc_usable_size(mem) Void_t* mem;
+size_t mALLOC_USABLE_SIZe(mem) Void_t* mem;
 #endif
 {
   mchunkptr p;
@@ -3539,6 +3859,7 @@ malloc_update_mallinfo(ar_ptr, mi) arena *ar_ptr; struct mallinfo *mi;
 
   mi->arena = ar_ptr->size;
   mi->ordblks = navail;
+  mi->smblks = mi->usmblks = mi->fsmblks = 0; /* clear unused fields */
   mi->uordblks = ar_ptr->size - avail;
   mi->fordblks = avail;
   mi->hblks = n_mmaps;
@@ -3589,7 +3910,7 @@ dump_heap(heap) heap_info *heap;
 
   malloc_stats:
 
-    For all arenas seperately and in total, prints on stderr the
+    For all arenas separately and in total, prints on stderr the
     amount of space obtained from the system, and the current number
     of bytes allocated via malloc (or realloc, etc) but not yet
     freed. (Note that this is the number of bytes allocated, not the
@@ -3602,7 +3923,7 @@ dump_heap(heap) heap_info *heap;
 
 */
 
-void malloc_stats()
+void mALLOC_STATs()
 {
   int i;
   arena *ar_ptr;
@@ -3612,7 +3933,7 @@ void malloc_stats()
   long stat_lock_direct = 0, stat_lock_loop = 0, stat_lock_wait = 0;
 #endif
 
-  for(i=0, ar_ptr = &main_arena; ar_ptr; ar_ptr = ar_ptr->next, i++) {
+  for(i=0, ar_ptr = &main_arena;; i++) {
     malloc_update_mallinfo(ar_ptr, &mi);
     fprintf(stderr, "Arena %d:\n", i);
     fprintf(stderr, "system bytes     = %10u\n", (unsigned int)mi.arena);
@@ -3626,12 +3947,21 @@ void malloc_stats()
 #endif
 #if !defined(NO_THREADS) && MALLOC_DEBUG > 1
     if(ar_ptr != &main_arena) {
-      heap_info *heap = heap_for_ptr(top(ar_ptr));
+      heap_info *heap;
+      (void)mutex_lock(&ar_ptr->mutex);
+      heap = heap_for_ptr(top(ar_ptr));
       while(heap) { dump_heap(heap); heap = heap->prev; }
+      (void)mutex_unlock(&ar_ptr->mutex);
     }
 #endif
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
   }
+#if HAVE_MMAP
   fprintf(stderr, "Total (incl. mmap):\n");
+#else
+  fprintf(stderr, "Total:\n");
+#endif
   fprintf(stderr, "system bytes     = %10u\n", system_b);
   fprintf(stderr, "in use bytes     = %10u\n", in_use_b);
 #ifdef NO_THREADS
@@ -3639,6 +3969,7 @@ void malloc_stats()
 #endif
 #if HAVE_MMAP
   fprintf(stderr, "max mmap regions = %10u\n", (unsigned int)max_n_mmaps);
+  fprintf(stderr, "max mmap bytes   = %10lu\n", max_mmapped_mem);
 #endif
 #if THREAD_STATS
   fprintf(stderr, "heaps created    = %10d\n",  stat_n_heaps);
@@ -3660,7 +3991,9 @@ struct mallinfo mALLINFo()
   struct mallinfo mi;
   Void_t *vptr = NULL;
 
+#ifndef NO_THREADS
   tsd_getspecific(arena_key, vptr);
+#endif
   malloc_update_mallinfo((vptr ? (arena*)vptr : &main_arena), &mi);
   return mi;
 }
@@ -3712,41 +4045,200 @@ int mALLOPt(param_number, value) int param_number; int value;
       return 0;
   }
 }
+
 \f
-#ifdef _LIBC
-weak_alias (__libc_calloc, __calloc) weak_alias (__libc_calloc, calloc)
-weak_alias (__libc_free, __cfree) weak_alias (__libc_free, cfree)
-weak_alias (__libc_free, __free) weak_alias (__libc_free, free)
-weak_alias (__libc_malloc, __malloc) weak_alias (__libc_malloc, malloc)
-weak_alias (__libc_memalign, __memalign) weak_alias (__libc_memalign, memalign)
-weak_alias (__libc_realloc, __realloc) weak_alias (__libc_realloc, realloc)
-weak_alias (__libc_valloc, __valloc) weak_alias (__libc_valloc, valloc)
-weak_alias (__libc_pvalloc, __pvalloc) weak_alias (__libc_pvalloc, pvalloc)
-weak_alias (__libc_mallinfo, __mallinfo) weak_alias (__libc_mallinfo, mallinfo)
-weak_alias (__libc_mallopt, __mallopt) weak_alias (__libc_mallopt, mallopt)
 
-#undef malloc_stats
-weak_alias (__malloc_stats, malloc_stats)
-#undef malloc_usable_size
-weak_alias (__malloc_usable_size, malloc_usable_size)
-#undef malloc_trim
-weak_alias (__malloc_trim, malloc_trim)
+/* Get/set state: malloc_get_state() records the current state of all
+   malloc variables (_except_ for the actual heap contents and `hook'
+   function pointers) in a system dependent, opaque data structure.
+   This data structure is dynamically allocated and can be free()d
+   after use.  malloc_set_state() restores the state of all malloc
+   variables to the previously obtained state.  This is especially
+   useful when using this malloc as part of a shared library, and when
+   the heap contents are saved/restored via some other method.  The
+   primary example for this is GNU Emacs with its `dumping' procedure.
+   `Hook' function pointers are never saved or restored by these
+   functions. */
+
+#define MALLOC_STATE_MAGIC   0x444c4541l
+#define MALLOC_STATE_VERSION (0*0x100l + 1l) /* major*0x100 + minor */
+
+struct malloc_state {
+  long          magic;
+  long          version;
+  mbinptr       av[NAV * 2 + 2];
+  char*         sbrk_base;
+  int           sbrked_mem_bytes;
+  unsigned long trim_threshold;
+  unsigned long top_pad;
+  unsigned int  n_mmaps_max;
+  unsigned long mmap_threshold;
+  int           check_action;
+  unsigned long max_sbrked_mem;
+  unsigned long max_total_mem;
+  unsigned int  n_mmaps;
+  unsigned int  max_n_mmaps;
+  unsigned long mmapped_mem;
+  unsigned long max_mmapped_mem;
+  int          using_malloc_checking;
+};
+
+Void_t*
+mALLOC_GET_STATe()
+{
+  struct malloc_state* ms;
+  int i;
+  mbinptr b;
+
+  ms = (struct malloc_state*)mALLOc(sizeof(*ms));
+  if (!ms)
+    return 0;
+  (void)mutex_lock(&main_arena.mutex);
+  ms->magic = MALLOC_STATE_MAGIC;
+  ms->version = MALLOC_STATE_VERSION;
+  ms->av[0] = main_arena.av[0];
+  ms->av[1] = main_arena.av[1];
+  for(i=0; i<NAV; i++) {
+    b = bin_at(&main_arena, i);
+    if(first(b) == b)
+      ms->av[2*i+2] = ms->av[2*i+3] = 0; /* empty bin (or initial top) */
+    else {
+      ms->av[2*i+2] = first(b);
+      ms->av[2*i+3] = last(b);
+    }
+  }
+  ms->sbrk_base = sbrk_base;
+  ms->sbrked_mem_bytes = sbrked_mem;
+  ms->trim_threshold = trim_threshold;
+  ms->top_pad = top_pad;
+  ms->n_mmaps_max = n_mmaps_max;
+  ms->mmap_threshold = mmap_threshold;
+  ms->check_action = check_action;
+  ms->max_sbrked_mem = max_sbrked_mem;
+#ifdef NO_THREADS
+  ms->max_total_mem = max_total_mem;
+#else
+  ms->max_total_mem = 0;
+#endif
+  ms->n_mmaps = n_mmaps;
+  ms->max_n_mmaps = max_n_mmaps;
+  ms->mmapped_mem = mmapped_mem;
+  ms->max_mmapped_mem = max_mmapped_mem;
+#if defined _LIBC || defined MALLOC_HOOKS
+  ms->using_malloc_checking = using_malloc_checking;
+#else
+  ms->using_malloc_checking = 0;
+#endif
+  (void)mutex_unlock(&main_arena.mutex);
+  return (Void_t*)ms;
+}
+
+int
+#if __STD_C
+mALLOC_SET_STATe(Void_t* msptr)
+#else
+mALLOC_SET_STATe(msptr) Void_t* msptr;
 #endif
+{
+  struct malloc_state* ms = (struct malloc_state*)msptr;
+  int i;
+  mbinptr b;
+
+#if defined _LIBC || defined MALLOC_HOOKS
+  disallow_malloc_check = 1;
+#endif
+  ptmalloc_init();
+  if(ms->magic != MALLOC_STATE_MAGIC) return -1;
+  /* Must fail if the major version is too high. */
+  if((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl)) return -2;
+  (void)mutex_lock(&main_arena.mutex);
+  main_arena.av[0] = ms->av[0];
+  main_arena.av[1] = ms->av[1];
+  for(i=0; i<NAV; i++) {
+    b = bin_at(&main_arena, i);
+    if(ms->av[2*i+2] == 0)
+      first(b) = last(b) = b;
+    else {
+      first(b) = ms->av[2*i+2];
+      last(b) = ms->av[2*i+3];
+      if(i > 0) {
+        /* Make sure the links to the `av'-bins in the heap are correct. */
+        first(b)->bk = b;
+        last(b)->fd = b;
+      }
+    }
+  }
+  sbrk_base = ms->sbrk_base;
+  sbrked_mem = ms->sbrked_mem_bytes;
+  trim_threshold = ms->trim_threshold;
+  top_pad = ms->top_pad;
+  n_mmaps_max = ms->n_mmaps_max;
+  mmap_threshold = ms->mmap_threshold;
+  check_action = ms->check_action;
+  max_sbrked_mem = ms->max_sbrked_mem;
+#ifdef NO_THREADS
+  max_total_mem = ms->max_total_mem;
+#endif
+  n_mmaps = ms->n_mmaps;
+  max_n_mmaps = ms->max_n_mmaps;
+  mmapped_mem = ms->mmapped_mem;
+  max_mmapped_mem = ms->max_mmapped_mem;
+  /* add version-dependent code here */
+  if (ms->version >= 1) {
+#if defined _LIBC || defined MALLOC_HOOKS
+    /* Check whether it is safe to enable malloc checking.  */
+    if (ms->using_malloc_checking && !using_malloc_checking &&
+       !disallow_malloc_check)
+      __malloc_check_init ();
+#endif
+  }
+
+  (void)mutex_unlock(&main_arena.mutex);
+  return 0;
+}
+
 \f
 
-#if defined(_LIBC) || defined(MALLOC_HOOKS)
+#if defined _LIBC || defined MALLOC_HOOKS
 
 /* A simple, standard set of debugging hooks.  Overhead is `only' one
    byte per chunk; still this will catch most cases of double frees or
-   overruns. */
+   overruns.  The goal here is to avoid obscure crashes due to invalid
+   usage, unlike in the MALLOC_DEBUG code. */
 
-#define MAGICBYTE ((char)0xd7)
+#define MAGICBYTE(p) ( ( ((size_t)p >> 3) ^ ((size_t)p >> 11)) & 0xFF )
+
+/* Instrument a chunk with overrun detector byte(s) and convert it
+   into a user pointer with requested size sz. */
+
+static Void_t*
+#if __STD_C
+chunk2mem_check(mchunkptr p, size_t sz)
+#else
+chunk2mem_check(p, sz) mchunkptr p; size_t sz;
+#endif
+{
+  unsigned char* m_ptr = (unsigned char*)chunk2mem(p);
+  size_t i;
+
+  for(i = chunksize(p) - (chunk_is_mmapped(p) ? 2*SIZE_SZ+1 : SIZE_SZ+1);
+      i > sz;
+      i -= 0xFF) {
+    if(i-sz < 0x100) {
+      m_ptr[i] = (unsigned char)(i-sz);
+      break;
+    }
+    m_ptr[i] = 0xFF;
+  }
+  m_ptr[sz] = MAGICBYTE(p);
+  return (Void_t*)m_ptr;
+}
 
 /* Convert a pointer to be free()d or realloc()ed to a valid chunk
-   pointer.  If the provided pointer is not valid, return NULL.  The
-   goal here is to avoid crashes, unlike in the MALLOC_DEBUG code. */
+   pointer.  If the provided pointer is not valid, return NULL. */
 
 static mchunkptr
+internal_function
 #if __STD_C
 mem2chunk_check(Void_t* mem)
 #else
@@ -3754,76 +4246,125 @@ mem2chunk_check(mem) Void_t* mem;
 #endif
 {
   mchunkptr p;
-  INTERNAL_SIZE_T sz;
+  INTERNAL_SIZE_T sz, c;
+  unsigned char magic;
 
   p = mem2chunk(mem);
   if(!aligned_OK(p)) return NULL;
   if( (char*)p>=sbrk_base && (char*)p<(sbrk_base+sbrked_mem) ) {
-    /* Must be a chunk in conventional memory. */
+    /* Must be a chunk in conventional heap memory. */
     if(chunk_is_mmapped(p) ||
        ( (sz = chunksize(p)), ((char*)p + sz)>=(sbrk_base+sbrked_mem) ) ||
-       sz<MINSIZE || sz&MALLOC_ALIGN_MASK || !inuse(p) ) return NULL;
-    if(*((char*)p + sz + (SIZE_SZ-1)) != MAGICBYTE) return NULL;
-    *((char*)p + sz + (SIZE_SZ-1)) = 0;
+       sz<MINSIZE || sz&MALLOC_ALIGN_MASK || !inuse(p) ||
+       ( !prev_inuse(p) && (p->prev_size&MALLOC_ALIGN_MASK ||
+                            (long)prev_chunk(p)<(long)sbrk_base ||
+                            next_chunk(prev_chunk(p))!=p) ))
+      return NULL;
+    magic = MAGICBYTE(p);
+    for(sz += SIZE_SZ-1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
+      if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
+    }
+    ((unsigned char*)p)[sz] ^= 0xFF;
   } else {
     unsigned long offset, page_mask = malloc_getpagesize-1;
 
-    /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of two
+    /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of-two
        alignment relative to the beginning of a page.  Check this
        first. */
     offset = (unsigned long)mem & page_mask;
     if((offset!=MALLOC_ALIGNMENT && offset!=0 && offset!=0x10 &&
-       offset!=0x20 && offset!=0x40 && offset!=0x80 && offset!=0x100 &&
-       offset!=0x200 && offset!=0x400 && offset!=0x800 && offset!=0x1000 &&
-       offset<0x2000) ||
+        offset!=0x20 && offset!=0x40 && offset!=0x80 && offset!=0x100 &&
+        offset!=0x200 && offset!=0x400 && offset!=0x800 && offset!=0x1000 &&
+        offset<0x2000) ||
        !chunk_is_mmapped(p) || (p->size & PREV_INUSE) ||
        ( (((unsigned long)p - p->prev_size) & page_mask) != 0 ) ||
        ( (sz = chunksize(p)), ((p->prev_size + sz) & page_mask) != 0 ) )
       return NULL;
-    if(*((char*)p + sz - 1) != MAGICBYTE) return NULL;
-    *((char*)p + sz - 1) = 0;
+    magic = MAGICBYTE(p);
+    for(sz -= 1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
+      if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
+    }
+    ((unsigned char*)p)[sz] ^= 0xFF;
   }
   return p;
 }
 
+/* Check for corruption of the top chunk, and try to recover if
+   necessary. */
+
+static int
+#if __STD_C
+top_check(void)
+#else
+top_check()
+#endif
+{
+  mchunkptr t = top(&main_arena);
+  char* brk, * new_brk;
+  INTERNAL_SIZE_T front_misalign, sbrk_size;
+  unsigned long pagesz = malloc_getpagesize;
+
+  if((char*)t + chunksize(t) == sbrk_base + sbrked_mem ||
+     t == initial_top(&main_arena)) return 0;
+
+  switch(check_action) {
+  case 1:
+    fprintf(stderr, "malloc: top chunk is corrupt\n");
+    break;
+  case 2:
+    abort();
+  }
+  /* Try to set up a new top chunk. */
+  brk = MORECORE(0);
+  front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK;
+  if (front_misalign > 0)
+    front_misalign = MALLOC_ALIGNMENT - front_misalign;
+  sbrk_size = front_misalign + top_pad + MINSIZE;
+  sbrk_size += pagesz - ((unsigned long)(brk + sbrk_size) & (pagesz - 1));
+  new_brk = (char*)(MORECORE (sbrk_size));
+  if (new_brk == (char*)(MORECORE_FAILURE)) return -1;
+  sbrked_mem = (new_brk - sbrk_base) + sbrk_size;
+
+  top(&main_arena) = (mchunkptr)(brk + front_misalign);
+  set_head(top(&main_arena), (sbrk_size - front_misalign) | PREV_INUSE);
+
+  return 0;
+}
+
 static Void_t*
 #if __STD_C
-malloc_check(size_t sz)
+malloc_check(size_t sz, const Void_t *caller)
 #else
-malloc_check(sz) size_t sz;
+malloc_check(sz, caller) size_t sz; const Void_t *caller;
 #endif
 {
   mchunkptr victim;
   INTERNAL_SIZE_T nb = request2size(sz + 1);
 
   (void)mutex_lock(&main_arena.mutex);
-  victim = chunk_alloc(&main_arena, nb);
+  victim = (top_check() >= 0) ? chunk_alloc(&main_arena, nb) : NULL;
   (void)mutex_unlock(&main_arena.mutex);
   if(!victim) return NULL;
-  nb = chunksize(victim);
-  if(chunk_is_mmapped(victim))
-    --nb;
-  else
-    nb += SIZE_SZ - 1;
-  *((char*)victim + nb) = MAGICBYTE;
-  return chunk2mem(victim);
+  return chunk2mem_check(victim, sz);
 }
 
 static void
 #if __STD_C
-free_check(Void_t* mem)
+free_check(Void_t* mem, const Void_t *caller)
 #else
-free_check(mem) Void_t* mem;
+free_check(mem, caller) Void_t* mem; const Void_t *caller;
 #endif
 {
   mchunkptr p;
 
   if(!mem) return;
+  (void)mutex_lock(&main_arena.mutex);
   p = mem2chunk_check(mem);
   if(!p) {
+    (void)mutex_unlock(&main_arena.mutex);
     switch(check_action) {
     case 1:
-      fprintf(stderr, "free(): invalid pointer %lx!\n", (long)(mem));
+      fprintf(stderr, "free(): invalid pointer %p!\n", mem);
       break;
     case 2:
       abort();
@@ -3832,42 +4373,47 @@ free_check(mem) Void_t* mem;
   }
 #if HAVE_MMAP
   if (chunk_is_mmapped(p)) {
+    (void)mutex_unlock(&main_arena.mutex);
     munmap_chunk(p);
     return;
   }
 #endif
-  (void)mutex_lock(&main_arena.mutex);
+#if 0 /* Erase freed memory. */
+  memset(mem, 0, chunksize(p) - (SIZE_SZ+1));
+#endif
   chunk_free(&main_arena, p);
   (void)mutex_unlock(&main_arena.mutex);
 }
 
 static Void_t*
 #if __STD_C
-realloc_check(Void_t* oldmem, size_t bytes)
+realloc_check(Void_t* oldmem, size_t bytes, const Void_t *caller)
 #else
-realloc_check(oldmem, bytes) Void_t* oldmem; size_t bytes;
+realloc_check(oldmem, bytes, caller)
+     Void_t* oldmem; size_t bytes; const Void_t *caller;
 #endif
 {
   mchunkptr oldp, newp;
   INTERNAL_SIZE_T nb, oldsize;
 
-  if (oldmem == 0) return malloc_check(bytes);
+  if (oldmem == 0) return malloc_check(bytes, NULL);
+  (void)mutex_lock(&main_arena.mutex);
   oldp = mem2chunk_check(oldmem);
   if(!oldp) {
+    (void)mutex_unlock(&main_arena.mutex);
     switch(check_action) {
     case 1:
-      fprintf(stderr, "realloc(): invalid pointer %lx!\n", (long)(oldmem));
+      fprintf(stderr, "realloc(): invalid pointer %p!\n", oldmem);
       break;
     case 2:
       abort();
     }
-    return malloc_check(bytes);
+    return malloc_check(bytes, NULL);
   }
   oldsize = chunksize(oldp);
 
   nb = request2size(bytes+1);
 
-  (void)mutex_lock(&main_arena.mutex);
 #if HAVE_MMAP
   if (chunk_is_mmapped(oldp)) {
 #if HAVE_MREMAP
@@ -3877,64 +4423,190 @@ realloc_check(oldmem, bytes) Void_t* oldmem; size_t bytes;
       /* Note the extra SIZE_SZ overhead. */
       if(oldsize - SIZE_SZ >= nb) newp = oldp; /* do nothing */
       else {
-       /* Must alloc, copy, free. */
-       newp = chunk_alloc(&main_arena, nb);
-       if (newp) {
-         MALLOC_COPY(chunk2mem(newp), oldmem, oldsize - 2*SIZE_SZ);
-         munmap_chunk(oldp);
-       }
+        /* Must alloc, copy, free. */
+        newp = (top_check() >= 0) ? chunk_alloc(&main_arena, nb) : NULL;
+        if (newp) {
+          MALLOC_COPY(chunk2mem(newp), oldmem, oldsize - 2*SIZE_SZ);
+          munmap_chunk(oldp);
+        }
       }
 #if HAVE_MREMAP
     }
 #endif
-  } else
+  } else {
 #endif /* HAVE_MMAP */
-    newp = chunk_realloc(&main_arena, oldp, oldsize, nb);
+    newp = (top_check() >= 0) ?
+      chunk_realloc(&main_arena, oldp, oldsize, nb) : NULL;
+#if 0 /* Erase freed memory. */
+    nb = chunksize(newp);
+    if(oldp<newp || oldp>=chunk_at_offset(newp, nb)) {
+      memset((char*)oldmem + 2*sizeof(mbinptr), 0,
+             oldsize - (2*sizeof(mbinptr)+2*SIZE_SZ+1));
+    } else if(nb > oldsize+SIZE_SZ) {
+      memset((char*)chunk2mem(newp) + oldsize, 0, nb - (oldsize+SIZE_SZ));
+    }
+#endif
+#if HAVE_MMAP
+  }
+#endif
   (void)mutex_unlock(&main_arena.mutex);
 
   if(!newp) return NULL;
-  nb = chunksize(newp);
-  if(chunk_is_mmapped(newp))
-    --nb;
-  else
-    nb += SIZE_SZ - 1;
-  *((char*)newp + nb) = MAGICBYTE;
-  return chunk2mem(newp);
+  return chunk2mem_check(newp, bytes);
 }
 
 static Void_t*
 #if __STD_C
-memalign_check(size_t alignment, size_t bytes)
+memalign_check(size_t alignment, size_t bytes, const Void_t *caller)
 #else
-memalign_check(alignment, bytes) size_t alignment; size_t bytes;
+memalign_check(alignment, bytes, caller)
+     size_t alignment; size_t bytes; const Void_t *caller;
 #endif
 {
   INTERNAL_SIZE_T nb;
   mchunkptr p;
 
-  if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes);
+  if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes, NULL);
   if (alignment <  MINSIZE) alignment = MINSIZE;
 
   nb = request2size(bytes+1);
   (void)mutex_lock(&main_arena.mutex);
-  p = chunk_align(&main_arena, nb, alignment);
+  p = (top_check() >= 0) ? chunk_align(&main_arena, nb, alignment) : NULL;
   (void)mutex_unlock(&main_arena.mutex);
   if(!p) return NULL;
-  nb = chunksize(p);
-  if(chunk_is_mmapped(p))
-    --nb;
-  else
-    nb += SIZE_SZ - 1;
-  *((char*)p + nb) = MAGICBYTE;
-  return chunk2mem(p);
+  return chunk2mem_check(p, bytes);
 }
 
-#endif /* defined(_LIBC) || defined(MALLOC_HOOKS) */
+#ifndef NO_THREADS
+
+/* The following hooks are used when the global initialization in
+   ptmalloc_init() hasn't completed yet. */
+
+static Void_t*
+#if __STD_C
+malloc_starter(size_t sz, const Void_t *caller)
+#else
+malloc_starter(sz, caller) size_t sz; const Void_t *caller;
+#endif
+{
+  mchunkptr victim = chunk_alloc(&main_arena, request2size(sz));
+
+  return victim ? chunk2mem(victim) : 0;
+}
+
+static void
+#if __STD_C
+free_starter(Void_t* mem, const Void_t *caller)
+#else
+free_starter(mem, caller) Void_t* mem; const Void_t *caller;
+#endif
+{
+  mchunkptr p;
+
+  if(!mem) return;
+  p = mem2chunk(mem);
+#if HAVE_MMAP
+  if (chunk_is_mmapped(p)) {
+    munmap_chunk(p);
+    return;
+  }
+#endif
+  chunk_free(&main_arena, p);
+}
+
+/* The following hooks are used while the `atfork' handling mechanism
+   is active. */
+
+static Void_t*
+#if __STD_C
+malloc_atfork (size_t sz, const Void_t *caller)
+#else
+malloc_atfork(sz, caller) size_t sz; const Void_t *caller;
+#endif
+{
+  Void_t *vptr = NULL;
+
+  tsd_getspecific(arena_key, vptr);
+  if(!vptr) {
+    mchunkptr victim = chunk_alloc(&main_arena, request2size(sz));
+    return victim ? chunk2mem(victim) : 0;
+  } else {
+    /* Suspend the thread until the `atfork' handlers have completed.
+       By that time, the hooks will have been reset as well, so that
+       mALLOc() can be used again. */
+    (void)mutex_lock(&list_lock);
+    (void)mutex_unlock(&list_lock);
+    return mALLOc(sz);
+  }
+}
+
+static void
+#if __STD_C
+free_atfork(Void_t* mem, const Void_t *caller)
+#else
+free_atfork(mem, caller) Void_t* mem; const Void_t *caller;
+#endif
+{
+  Void_t *vptr = NULL;
+  arena *ar_ptr;
+  mchunkptr p;                          /* chunk corresponding to mem */
+
+  if (mem == 0)                              /* free(0) has no effect */
+    return;
+
+  p = mem2chunk(mem);
+
+#if HAVE_MMAP
+  if (chunk_is_mmapped(p))                       /* release mmapped memory. */
+  {
+    munmap_chunk(p);
+    return;
+  }
+#endif
+
+  ar_ptr = arena_for_ptr(p);
+  tsd_getspecific(arena_key, vptr);
+  if(vptr)
+    (void)mutex_lock(&ar_ptr->mutex);
+  chunk_free(ar_ptr, p);
+  if(vptr)
+    (void)mutex_unlock(&ar_ptr->mutex);
+}
+
+#endif
+
+#endif /* defined _LIBC || defined MALLOC_HOOKS */
+
+\f
+
+#ifdef _LIBC
+weak_alias (__libc_calloc, __calloc) weak_alias (__libc_calloc, calloc)
+weak_alias (__libc_free, __cfree) weak_alias (__libc_free, cfree)
+weak_alias (__libc_free, __free) weak_alias (__libc_free, free)
+weak_alias (__libc_malloc, __malloc) weak_alias (__libc_malloc, malloc)
+weak_alias (__libc_memalign, __memalign) weak_alias (__libc_memalign, memalign)
+weak_alias (__libc_realloc, __realloc) weak_alias (__libc_realloc, realloc)
+weak_alias (__libc_valloc, __valloc) weak_alias (__libc_valloc, valloc)
+weak_alias (__libc_pvalloc, __pvalloc) weak_alias (__libc_pvalloc, pvalloc)
+weak_alias (__libc_mallinfo, __mallinfo) weak_alias (__libc_mallinfo, mallinfo)
+weak_alias (__libc_mallopt, __mallopt) weak_alias (__libc_mallopt, mallopt)
+
+weak_alias (__malloc_stats, malloc_stats)
+weak_alias (__malloc_usable_size, malloc_usable_size)
+weak_alias (__malloc_trim, malloc_trim)
+weak_alias (__malloc_get_state, malloc_get_state)
+weak_alias (__malloc_set_state, malloc_set_state)
+#endif
 
 /*
 
 History:
 
+    V2.6.4-pt3 Thu Feb 20 1997 Wolfram Gloger (wmglo@dent.med.uni-muenchen.de)
+      * Added malloc_get/set_state() (mainly for use in GNU emacs),
+        using interface from Marcus Daniels
+      * All parameters are now adjustable via environment variables
+
     V2.6.4-pt2 Sat Dec 14 1996 Wolfram Gloger (wmglo@dent.med.uni-muenchen.de)
       * Added debugging hooks
       * Fixed possible deadlock in realloc() when out of memory
@@ -3964,10 +4636,10 @@ History:
         Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
       * Use last_remainder in more cases.
       * Pack bins using idea from  colin@nyx10.cs.du.edu
-      * Use ordered bins instead of best-fit threshhold
+      * Use ordered bins instead of best-fit threshold
       * Eliminate block-local decls to simplify tracing and debugging.
       * Support another case of realloc via move into top
-      * Fix error occuring when initial sbrk_base not word-aligned.
+      * Fix error occurring when initial sbrk_base not word-aligned.
       * Rely on page size for units instead of SBRK_UNIT to
         avoid surprises about sbrk alignment conventions.
       * Add mallinfo, mallopt. Thanks to Raymond Nijssen