(root)/
glibc-2.38/
malloc/
malloc-debug.c
       1  /* Malloc debug DSO.
       2     Copyright (C) 2021-2023 Free Software Foundation, Inc.
       3     Copyright The GNU Toolchain Authors.
       4     This file is part of the GNU C Library.
       5  
       6     The GNU C Library is free software; you can redistribute it and/or
       7     modify it under the terms of the GNU Lesser General Public License as
       8     published by the Free Software Foundation; either version 2.1 of the
       9     License, or (at your option) any later version.
      10  
      11     The GNU C Library is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14     Lesser General Public License for more details.
      15  
      16     You should have received a copy of the GNU Lesser General Public
      17     License along with the GNU C Library; see the file COPYING.LIB.  If
      18     not, see <https://www.gnu.org/licenses/>.  */
      19  
      20  #include <atomic.h>
      21  #include <libc-symbols.h>
      22  #include <shlib-compat.h>
      23  #include <string.h>
      24  #include <unistd.h>
      25  #include <sys/param.h>
      26  
      27  /* Support only the glibc allocators.  */
      28  extern void *__libc_malloc (size_t);
      29  extern void __libc_free (void *);
      30  extern void *__libc_realloc (void *, size_t);
      31  extern void *__libc_memalign (size_t, size_t);
      32  extern void *__libc_valloc (size_t);
      33  extern void *__libc_pvalloc (size_t);
      34  extern void *__libc_calloc (size_t, size_t);
      35  
      36  #define DEBUG_FN(fn) \
      37    static __typeof (__libc_ ## fn) __debug_ ## fn
      38  
      39  DEBUG_FN(malloc);
      40  DEBUG_FN(free);
      41  DEBUG_FN(realloc);
      42  DEBUG_FN(memalign);
      43  DEBUG_FN(valloc);
      44  DEBUG_FN(pvalloc);
      45  DEBUG_FN(calloc);
      46  
      47  static int debug_initialized = -1;
      48  
      49  enum malloc_debug_hooks
      50  {
      51    MALLOC_NONE_HOOK = 0,
      52    MALLOC_MCHECK_HOOK = 1 << 0, /* mcheck()  */
      53    MALLOC_MTRACE_HOOK = 1 << 1, /* mtrace()  */
      54    MALLOC_CHECK_HOOK = 1 << 2,  /* MALLOC_CHECK_ or glibc.malloc.check.  */
      55  };
      56  static unsigned __malloc_debugging_hooks;
      57  
      58  static __always_inline bool
      59  __is_malloc_debug_enabled (enum malloc_debug_hooks flag)
      60  {
      61    return __malloc_debugging_hooks & flag;
      62  }
      63  
      64  static __always_inline void
      65  __malloc_debug_enable (enum malloc_debug_hooks flag)
      66  {
      67    __malloc_debugging_hooks |= flag;
      68  }
      69  
      70  static __always_inline void
      71  __malloc_debug_disable (enum malloc_debug_hooks flag)
      72  {
      73    __malloc_debugging_hooks &= ~flag;
      74  }
      75  
      76  #include "mcheck.c"
      77  #include "mtrace.c"
      78  #include "malloc-check.c"
      79  
      80  #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_24)
      81  extern void (*__malloc_initialize_hook) (void);
      82  compat_symbol_reference (libc, __malloc_initialize_hook,
      83  			 __malloc_initialize_hook, GLIBC_2_0);
      84  #endif
      85  
      86  static void *malloc_hook_ini (size_t, const void *) __THROW;
      87  static void *realloc_hook_ini (void *, size_t, const void *) __THROW;
      88  static void *memalign_hook_ini (size_t, size_t, const void *) __THROW;
      89  
      90  void (*__free_hook) (void *, const void *) = NULL;
      91  void *(*__malloc_hook) (size_t, const void *) = malloc_hook_ini;
      92  void *(*__realloc_hook) (void *, size_t, const void *) = realloc_hook_ini;
      93  void *(*__memalign_hook) (size_t, size_t, const void *) = memalign_hook_ini;
      94  
      95  /* Hooks for debugging versions.  The initial hooks just call the
      96     initialization routine, then do the normal work. */
      97  
      98  /* These hooks will get executed only through the interposed allocator
      99     functions in libc_malloc_debug.so.  This means that the calls to malloc,
     100     realloc, etc. will lead back into the interposed functions, which is what we
     101     want.
     102  
     103     These initial hooks are assumed to be called in a single-threaded context,
     104     so it is safe to reset all hooks at once upon initialization.  */
     105  
     106  static void
     107  generic_hook_ini (void)
     108  {
     109    debug_initialized = 0;
     110    __malloc_hook = NULL;
     111    __realloc_hook = NULL;
     112    __memalign_hook = NULL;
     113  
     114    /* malloc check does not quite co-exist with libc malloc, so initialize
     115       either on or the other.  */
     116    if (!initialize_malloc_check ())
     117      /* The compiler does not know that these functions are allocators, so it
     118         will not try to optimize it away.  */
     119      __libc_free (__libc_malloc (0));
     120  
     121  #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_24)
     122    void (*hook) (void) = __malloc_initialize_hook;
     123    if (hook != NULL)
     124      (*hook)();
     125  #endif
     126  
     127    debug_initialized = 1;
     128  }
     129  
     130  static void *
     131  malloc_hook_ini (size_t sz, const void *caller)
     132  {
     133    generic_hook_ini ();
     134    return __debug_malloc (sz);
     135  }
     136  
     137  static void *
     138  realloc_hook_ini (void *ptr, size_t sz, const void *caller)
     139  {
     140    generic_hook_ini ();
     141    return __debug_realloc (ptr, sz);
     142  }
     143  
     144  static void *
     145  memalign_hook_ini (size_t alignment, size_t sz, const void *caller)
     146  {
     147    generic_hook_ini ();
     148    return __debug_memalign (alignment, sz);
     149  }
     150  
     151  static size_t pagesize;
     152  
     153  /* These variables are used for undumping support.  Chunked are marked
     154     as using mmap, but we leave them alone if they fall into this
     155     range.  NB: The chunk size for these chunks only includes the
     156     initial size field (of SIZE_SZ bytes), there is no trailing size
     157     field (unlike with regular mmapped chunks).  */
     158  static mchunkptr dumped_main_arena_start; /* Inclusive.  */
     159  static mchunkptr dumped_main_arena_end;   /* Exclusive.  */
     160  
     161  /* True if the pointer falls into the dumped arena.  Use this after
     162     chunk_is_mmapped indicates a chunk is mmapped.  */
     163  #define DUMPED_MAIN_ARENA_CHUNK(p) \
     164    ((p) >= dumped_main_arena_start && (p) < dumped_main_arena_end)
     165  
     166  /* The allocator functions.  */
     167  
     168  static void *
     169  __debug_malloc (size_t bytes)
     170  {
     171    void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook);
     172    if (__builtin_expect (hook != NULL, 0))
     173      return (*hook)(bytes, RETURN_ADDRESS (0));
     174  
     175    void *victim = NULL;
     176    size_t orig_bytes = bytes;
     177    if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)
     178         || !malloc_mcheck_before (&bytes, &victim)))
     179      {
     180        victim = (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK)
     181  		? malloc_check (bytes) : __libc_malloc (bytes));
     182      }
     183    if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL)
     184      victim = malloc_mcheck_after (victim, orig_bytes);
     185    if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK))
     186      malloc_mtrace_after (victim, orig_bytes, RETURN_ADDRESS (0));
     187  
     188    return victim;
     189  }
     190  strong_alias (__debug_malloc, malloc)
     191  
     192  static void
     193  __debug_free (void *mem)
     194  {
     195    void (*hook) (void *, const void *) = atomic_forced_read (__free_hook);
     196    if (__builtin_expect (hook != NULL, 0))
     197      {
     198        (*hook)(mem, RETURN_ADDRESS (0));
     199        return;
     200      }
     201  
     202    if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK))
     203      mem = free_mcheck (mem);
     204  
     205    if (DUMPED_MAIN_ARENA_CHUNK (mem2chunk (mem)))
     206      /* Do nothing.  */;
     207    else if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     208      free_check (mem);
     209    else
     210      __libc_free (mem);
     211    if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK))
     212      free_mtrace (mem, RETURN_ADDRESS (0));
     213  }
     214  strong_alias (__debug_free, free)
     215  
     216  static void *
     217  __debug_realloc (void *oldmem, size_t bytes)
     218  {
     219    void *(*hook) (void *, size_t, const void *) =
     220      atomic_forced_read (__realloc_hook);
     221    if (__builtin_expect (hook != NULL, 0))
     222      return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));
     223  
     224    size_t orig_bytes = bytes, oldsize = 0;
     225    void *victim = NULL;
     226  
     227    if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)
     228         || !realloc_mcheck_before (&oldmem, &bytes, &oldsize, &victim)))
     229      {
     230        mchunkptr oldp = mem2chunk (oldmem);
     231  
     232        /* If this is a faked mmapped chunk from the dumped main arena,
     233  	 always make a copy (and do not free the old chunk).  */
     234        if (DUMPED_MAIN_ARENA_CHUNK (oldp))
     235  	{
     236  	  if (bytes == 0 && oldmem != NULL)
     237  	    victim = NULL;
     238  	  else
     239  	    {
     240  	      const INTERNAL_SIZE_T osize = chunksize (oldp);
     241  	      /* Must alloc, copy, free. */
     242  	      victim = __debug_malloc (bytes);
     243  	      /* Copy as many bytes as are available from the old chunk
     244  		 and fit into the new size.  NB: The overhead for faked
     245  		 mmapped chunks is only SIZE_SZ, not CHUNK_HDR_SZ as for
     246  		 regular mmapped chunks.  */
     247  	      if (victim != NULL)
     248  		{
     249  		  if (bytes > osize - SIZE_SZ)
     250  		    bytes = osize - SIZE_SZ;
     251  		  memcpy (victim, oldmem, bytes);
     252  		}
     253  	    }
     254  	}
     255        else if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     256  	victim =  realloc_check (oldmem, bytes);
     257        else
     258  	victim = __libc_realloc (oldmem, bytes);
     259      }
     260    if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL)
     261      victim = realloc_mcheck_after (victim, oldmem, orig_bytes,
     262  				   oldsize);
     263    if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK))
     264      realloc_mtrace_after (victim, oldmem, orig_bytes, RETURN_ADDRESS (0));
     265  
     266    return victim;
     267  }
     268  strong_alias (__debug_realloc, realloc)
     269  
     270  static void *
     271  _debug_mid_memalign (size_t alignment, size_t bytes, const void *address)
     272  {
     273    void *(*hook) (size_t, size_t, const void *) =
     274      atomic_forced_read (__memalign_hook);
     275    if (__builtin_expect (hook != NULL, 0))
     276      return (*hook)(alignment, bytes, address);
     277  
     278    void *victim = NULL;
     279    size_t orig_bytes = bytes;
     280  
     281    if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)
     282         || !memalign_mcheck_before (alignment, &bytes, &victim)))
     283      {
     284        victim = (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK)
     285  		? memalign_check (alignment, bytes)
     286  		: __libc_memalign (alignment, bytes));
     287      }
     288    if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL)
     289      victim = memalign_mcheck_after (victim, alignment, orig_bytes);
     290    if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK))
     291      memalign_mtrace_after (victim, orig_bytes, address);
     292  
     293    return victim;
     294  }
     295  
     296  static void *
     297  __debug_memalign (size_t alignment, size_t bytes)
     298  {
     299    return _debug_mid_memalign (alignment, bytes, RETURN_ADDRESS (0));
     300  }
     301  strong_alias (__debug_memalign, memalign)
     302  static void *
     303  __debug_aligned_alloc (size_t alignment, size_t bytes)
     304  {
     305    if (!powerof2 (alignment) || alignment == 0)
     306      return NULL;
     307    return _debug_mid_memalign (alignment, bytes, RETURN_ADDRESS (0));
     308  }
     309  strong_alias (__debug_aligned_alloc, aligned_alloc)
     310  
     311  static void *
     312  __debug_pvalloc (size_t bytes)
     313  {
     314    size_t rounded_bytes;
     315  
     316    if (!pagesize)
     317      pagesize = sysconf (_SC_PAGESIZE);
     318  
     319    /* ALIGN_UP with overflow check.  */
     320    if (__glibc_unlikely (__builtin_add_overflow (bytes,
     321  						pagesize - 1,
     322  						&rounded_bytes)))
     323      {
     324        errno = ENOMEM;
     325        return NULL;
     326      }
     327    rounded_bytes = rounded_bytes & -(pagesize - 1);
     328  
     329    return _debug_mid_memalign (pagesize, rounded_bytes, RETURN_ADDRESS (0));
     330  }
     331  strong_alias (__debug_pvalloc, pvalloc)
     332  
     333  static void *
     334  __debug_valloc (size_t bytes)
     335  {
     336    if (!pagesize)
     337      pagesize = sysconf (_SC_PAGESIZE);
     338  
     339    return _debug_mid_memalign (pagesize, bytes, RETURN_ADDRESS (0));
     340  }
     341  strong_alias (__debug_valloc, valloc)
     342  
     343  static int
     344  __debug_posix_memalign (void **memptr, size_t alignment, size_t bytes)
     345  {
     346    /* Test whether the SIZE argument is valid.  It must be a power of
     347       two multiple of sizeof (void *).  */
     348    if (alignment % sizeof (void *) != 0
     349        || !powerof2 (alignment / sizeof (void *))
     350        || alignment == 0)
     351      return EINVAL;
     352  
     353    *memptr = _debug_mid_memalign (alignment, bytes, RETURN_ADDRESS (0));
     354  
     355    if (*memptr == NULL)
     356      return ENOMEM;
     357  
     358    return 0;
     359  }
     360  strong_alias (__debug_posix_memalign, posix_memalign)
     361  
     362  static void *
     363  __debug_calloc (size_t nmemb, size_t size)
     364  {
     365    size_t bytes;
     366  
     367    if (__glibc_unlikely (__builtin_mul_overflow (nmemb, size, &bytes)))
     368      {
     369        errno = ENOMEM;
     370        return NULL;
     371      }
     372  
     373    void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook);
     374    if (__builtin_expect (hook != NULL, 0))
     375      {
     376        void *mem = (*hook)(bytes, RETURN_ADDRESS (0));
     377  
     378        if (mem != NULL)
     379  	memset (mem, 0, bytes);
     380  
     381        return mem;
     382      }
     383  
     384    size_t orig_bytes = bytes;
     385    void *victim = NULL;
     386  
     387    if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)
     388         || !malloc_mcheck_before (&bytes, &victim)))
     389      {
     390        victim = (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK)
     391  		? malloc_check (bytes) : __libc_malloc (bytes));
     392      }
     393    if (victim != NULL)
     394      {
     395        if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK))
     396  	victim = malloc_mcheck_after (victim, orig_bytes);
     397        memset (victim, 0, orig_bytes);
     398      }
     399    if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK))
     400      malloc_mtrace_after (victim, orig_bytes, RETURN_ADDRESS (0));
     401  
     402    return victim;
     403  }
     404  strong_alias (__debug_calloc, calloc)
     405  
     406  size_t
     407  malloc_usable_size (void *mem)
     408  {
     409    if (mem == NULL)
     410      return 0;
     411  
     412    if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK))
     413      return mcheck_usable_size (mem);
     414    if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     415      return malloc_check_get_size (mem);
     416  
     417    mchunkptr p = mem2chunk (mem);
     418    if (DUMPED_MAIN_ARENA_CHUNK (p))
     419      return chunksize (p) - SIZE_SZ;
     420  
     421    return musable (mem);
     422  }
     423  
     424  #define LIBC_SYMBOL(sym) libc_ ## sym
     425  #define SYMHANDLE(sym) sym ## _handle
     426  
     427  #define LOAD_SYM(sym) ({ \
     428    static void *SYMHANDLE (sym);						      \
     429    if (SYMHANDLE (sym) == NULL)						      \
     430      SYMHANDLE (sym) = dlsym (RTLD_NEXT, #sym);				      \
     431    SYMHANDLE (sym);							      \
     432  })
     433  
     434  int
     435  malloc_info (int options, FILE *fp)
     436  {
     437    if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     438      return __malloc_info (options, fp);
     439  
     440    int (*LIBC_SYMBOL (malloc_info)) (int, FILE *) = LOAD_SYM (malloc_info);
     441    if (LIBC_SYMBOL (malloc_info) == NULL)
     442      return -1;
     443  
     444    return LIBC_SYMBOL (malloc_info) (options, fp);
     445  }
     446  
     447  int
     448  mallopt (int param_number, int value)
     449  {
     450    if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     451      return __libc_mallopt (param_number, value);
     452  
     453    int (*LIBC_SYMBOL (mallopt)) (int, int) = LOAD_SYM (mallopt);
     454    if (LIBC_SYMBOL (mallopt) == NULL)
     455      return 0;
     456  
     457    return LIBC_SYMBOL (mallopt) (param_number, value);
     458  }
     459  
     460  void
     461  malloc_stats (void)
     462  {
     463    if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     464      return __malloc_stats ();
     465  
     466    void (*LIBC_SYMBOL (malloc_stats)) (void) = LOAD_SYM (malloc_stats);
     467    if (LIBC_SYMBOL (malloc_stats) == NULL)
     468      return;
     469  
     470    LIBC_SYMBOL (malloc_stats) ();
     471  }
     472  
     473  struct mallinfo2
     474  mallinfo2 (void)
     475  {
     476    if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     477      return __libc_mallinfo2 ();
     478  
     479    struct mallinfo2 (*LIBC_SYMBOL (mallinfo2)) (void) = LOAD_SYM (mallinfo2);
     480    if (LIBC_SYMBOL (mallinfo2) == NULL)
     481      {
     482        struct mallinfo2 ret = {0};
     483        return ret;
     484      }
     485  
     486    return LIBC_SYMBOL (mallinfo2) ();
     487  }
     488  
     489  struct mallinfo
     490  mallinfo (void)
     491  {
     492    if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     493      return __libc_mallinfo ();
     494  
     495    struct mallinfo (*LIBC_SYMBOL (mallinfo)) (void) = LOAD_SYM (mallinfo);
     496    if (LIBC_SYMBOL (mallinfo) == NULL)
     497      {
     498        struct mallinfo ret = {0};
     499        return ret;
     500      }
     501  
     502    return LIBC_SYMBOL (mallinfo) ();
     503  }
     504  
     505  int
     506  malloc_trim (size_t s)
     507  {
     508    if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
     509      return __malloc_trim (s);
     510  
     511    int (*LIBC_SYMBOL (malloc_trim)) (size_t) = LOAD_SYM (malloc_trim);
     512    if (LIBC_SYMBOL (malloc_trim) == NULL)
     513      return 0;
     514  
     515    return LIBC_SYMBOL (malloc_trim) (s);
     516  }
     517  
     518  #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_25)
     519  
     520  /* Support for restoring dumped heaps contained in historic Emacs
     521     executables.  The heap saving feature (malloc_get_state) is no
     522     longer implemented in this version of glibc, but we have a heap
     523     rewriter in malloc_set_state which transforms the heap into a
     524     version compatible with current malloc.  */
     525  
     526  #define MALLOC_STATE_MAGIC   0x444c4541l
     527  #define MALLOC_STATE_VERSION (0 * 0x100l + 5l) /* major*0x100 + minor */
     528  
     529  struct malloc_save_state
     530  {
     531    long magic;
     532    long version;
     533    mbinptr av[NBINS * 2 + 2];
     534    char *sbrk_base;
     535    int sbrked_mem_bytes;
     536    unsigned long trim_threshold;
     537    unsigned long top_pad;
     538    unsigned int n_mmaps_max;
     539    unsigned long mmap_threshold;
     540    int check_action;
     541    unsigned long max_sbrked_mem;
     542    unsigned long max_total_mem;	/* Always 0, for backwards compatibility.  */
     543    unsigned int n_mmaps;
     544    unsigned int max_n_mmaps;
     545    unsigned long mmapped_mem;
     546    unsigned long max_mmapped_mem;
     547    int using_malloc_checking;
     548    unsigned long max_fast;
     549    unsigned long arena_test;
     550    unsigned long arena_max;
     551    unsigned long narenas;
     552  };
     553  
     554  /* Dummy implementation which always fails.  We need to provide this
     555     symbol so that existing Emacs binaries continue to work with
     556     BIND_NOW.  */
     557  void *
     558  malloc_get_state (void)
     559  {
     560    __set_errno (ENOSYS);
     561    return NULL;
     562  }
     563  compat_symbol (libc_malloc_debug, malloc_get_state, malloc_get_state,
     564  	       GLIBC_2_0);
     565  
     566  int
     567  malloc_set_state (void *msptr)
     568  {
     569    struct malloc_save_state *ms = (struct malloc_save_state *) msptr;
     570  
     571    if (ms->magic != MALLOC_STATE_MAGIC)
     572      return -1;
     573  
     574    /* Must fail if the major version is too high. */
     575    if ((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl))
     576      return -2;
     577  
     578    if (debug_initialized == 1)
     579      return -1;
     580  
     581    bool check_was_enabled = __is_malloc_debug_enabled (MALLOC_CHECK_HOOK);
     582  
     583    /* It's not too late, so disable MALLOC_CHECK_ and all of the hooks.  */
     584    __malloc_hook = NULL;
     585    __realloc_hook = NULL;
     586    __free_hook = NULL;
     587    __memalign_hook = NULL;
     588    __malloc_debug_disable (MALLOC_CHECK_HOOK);
     589  
     590    /* We do not need to perform locking here because malloc_set_state
     591       must be called before the first call into the malloc subsystem (usually via
     592       __malloc_initialize_hook).  pthread_create always calls calloc and thus
     593       must be called only afterwards, so there cannot be more than one thread
     594       when we reach this point.  Also handle initialization if either we ended
     595       up being called before the first malloc or through the hook when
     596       malloc-check was enabled.  */
     597    if (debug_initialized < 0)
     598      generic_hook_ini ();
     599    else if (check_was_enabled)
     600      __libc_free (__libc_malloc (0));
     601  
     602    /* Patch the dumped heap.  We no longer try to integrate into the
     603       existing heap.  Instead, we mark the existing chunks as mmapped.
     604       Together with the update to dumped_main_arena_start and
     605       dumped_main_arena_end, realloc and free will recognize these
     606       chunks as dumped fake mmapped chunks and never free them.  */
     607  
     608    /* Find the chunk with the lowest address with the heap.  */
     609    mchunkptr chunk = NULL;
     610    {
     611      size_t *candidate = (size_t *) ms->sbrk_base;
     612      size_t *end = (size_t *) (ms->sbrk_base + ms->sbrked_mem_bytes);
     613      while (candidate < end)
     614        if (*candidate != 0)
     615  	{
     616  	  chunk = mem2chunk ((void *) (candidate + 1));
     617  	  break;
     618  	}
     619        else
     620  	++candidate;
     621    }
     622    if (chunk == NULL)
     623      return 0;
     624  
     625    /* Iterate over the dumped heap and patch the chunks so that they
     626       are treated as fake mmapped chunks.  */
     627    mchunkptr top = ms->av[2];
     628    while (chunk < top)
     629      {
     630        if (inuse (chunk))
     631  	{
     632  	  /* Mark chunk as mmapped, to trigger the fallback path.  */
     633  	  size_t size = chunksize (chunk);
     634  	  set_head (chunk, size | IS_MMAPPED);
     635  	}
     636        chunk = next_chunk (chunk);
     637      }
     638  
     639    /* The dumped fake mmapped chunks all lie in this address range.  */
     640    dumped_main_arena_start = (mchunkptr) ms->sbrk_base;
     641    dumped_main_arena_end = top;
     642  
     643    return 0;
     644  }
     645  compat_symbol (libc_malloc_debug, malloc_set_state, malloc_set_state,
     646  	       GLIBC_2_0);
     647  #endif
     648  
     649  /* Do not allow linking against the library.  */
     650  compat_symbol (libc_malloc_debug, aligned_alloc, aligned_alloc, GLIBC_2_16);
     651  compat_symbol (libc_malloc_debug, calloc, calloc, GLIBC_2_0);
     652  compat_symbol (libc_malloc_debug, free, free, GLIBC_2_0);
     653  compat_symbol (libc_malloc_debug, mallinfo2, mallinfo2, GLIBC_2_33);
     654  compat_symbol (libc_malloc_debug, mallinfo, mallinfo, GLIBC_2_0);
     655  compat_symbol (libc_malloc_debug, malloc_info, malloc_info, GLIBC_2_10);
     656  compat_symbol (libc_malloc_debug, malloc, malloc, GLIBC_2_0);
     657  compat_symbol (libc_malloc_debug, malloc_stats, malloc_stats, GLIBC_2_0);
     658  compat_symbol (libc_malloc_debug, malloc_trim, malloc_trim, GLIBC_2_0);
     659  compat_symbol (libc_malloc_debug, malloc_usable_size, malloc_usable_size,
     660  	       GLIBC_2_0);
     661  compat_symbol (libc_malloc_debug, mallopt, mallopt, GLIBC_2_0);
     662  compat_symbol (libc_malloc_debug, mcheck_check_all, mcheck_check_all,
     663  	       GLIBC_2_2);
     664  compat_symbol (libc_malloc_debug, mcheck, mcheck, GLIBC_2_0);
     665  compat_symbol (libc_malloc_debug, mcheck_pedantic, mcheck_pedantic, GLIBC_2_2);
     666  compat_symbol (libc_malloc_debug, memalign, memalign, GLIBC_2_0);
     667  compat_symbol (libc_malloc_debug, mprobe, mprobe, GLIBC_2_0);
     668  compat_symbol (libc_malloc_debug, mtrace, mtrace, GLIBC_2_0);
     669  compat_symbol (libc_malloc_debug, muntrace, muntrace, GLIBC_2_0);
     670  compat_symbol (libc_malloc_debug, posix_memalign, posix_memalign, GLIBC_2_2);
     671  compat_symbol (libc_malloc_debug, pvalloc, pvalloc, GLIBC_2_0);
     672  compat_symbol (libc_malloc_debug, realloc, realloc, GLIBC_2_0);
     673  compat_symbol (libc_malloc_debug, valloc, valloc, GLIBC_2_0);
     674  compat_symbol (libc_malloc_debug, __free_hook, __free_hook, GLIBC_2_0);
     675  compat_symbol (libc_malloc_debug, __malloc_hook, __malloc_hook, GLIBC_2_0);
     676  compat_symbol (libc_malloc_debug, __realloc_hook, __realloc_hook, GLIBC_2_0);
     677  compat_symbol (libc_malloc_debug, __memalign_hook, __memalign_hook, GLIBC_2_0);