1 /* Malloc debug DSO.
2    Copyright (C) 2021-2022 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
__is_malloc_debug_enabled(enum malloc_debug_hooks flag)59 __is_malloc_debug_enabled (enum malloc_debug_hooks flag)
60 {
61   return __malloc_debugging_hooks & flag;
62 }
63 
64 static __always_inline void
__malloc_debug_enable(enum malloc_debug_hooks flag)65 __malloc_debug_enable (enum malloc_debug_hooks flag)
66 {
67   __malloc_debugging_hooks |= flag;
68 }
69 
70 static __always_inline void
__malloc_debug_disable(enum malloc_debug_hooks flag)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
generic_hook_ini(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 *
malloc_hook_ini(size_t sz,const void * caller)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 *
realloc_hook_ini(void * ptr,size_t sz,const void * caller)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 *
memalign_hook_ini(size_t alignment,size_t sz,const void * caller)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 *
__debug_malloc(size_t bytes)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 }
strong_alias(__debug_malloc,malloc)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 }
strong_alias(__debug_free,free)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 }
strong_alias(__debug_realloc,realloc)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 *
__debug_memalign(size_t alignment,size_t bytes)297 __debug_memalign (size_t alignment, size_t bytes)
298 {
299   return _debug_mid_memalign (alignment, bytes, RETURN_ADDRESS (0));
300 }
strong_alias(__debug_memalign,memalign)301 strong_alias (__debug_memalign, memalign)
302 strong_alias (__debug_memalign, aligned_alloc)
303 
304 static void *
305 __debug_pvalloc (size_t bytes)
306 {
307   size_t rounded_bytes;
308 
309   if (!pagesize)
310     pagesize = sysconf (_SC_PAGESIZE);
311 
312   /* ALIGN_UP with overflow check.  */
313   if (__glibc_unlikely (__builtin_add_overflow (bytes,
314 						pagesize - 1,
315 						&rounded_bytes)))
316     {
317       errno = ENOMEM;
318       return NULL;
319     }
320   rounded_bytes = rounded_bytes & -(pagesize - 1);
321 
322   return _debug_mid_memalign (pagesize, rounded_bytes, RETURN_ADDRESS (0));
323 }
strong_alias(__debug_pvalloc,pvalloc)324 strong_alias (__debug_pvalloc, pvalloc)
325 
326 static void *
327 __debug_valloc (size_t bytes)
328 {
329   if (!pagesize)
330     pagesize = sysconf (_SC_PAGESIZE);
331 
332   return _debug_mid_memalign (pagesize, bytes, RETURN_ADDRESS (0));
333 }
strong_alias(__debug_valloc,valloc)334 strong_alias (__debug_valloc, valloc)
335 
336 static int
337 __debug_posix_memalign (void **memptr, size_t alignment, size_t bytes)
338 {
339   /* Test whether the SIZE argument is valid.  It must be a power of
340      two multiple of sizeof (void *).  */
341   if (alignment % sizeof (void *) != 0
342       || !powerof2 (alignment / sizeof (void *))
343       || alignment == 0)
344     return EINVAL;
345 
346   *memptr = _debug_mid_memalign (alignment, bytes, RETURN_ADDRESS (0));
347 
348   if (*memptr == NULL)
349     return ENOMEM;
350 
351   return 0;
352 }
strong_alias(__debug_posix_memalign,posix_memalign)353 strong_alias (__debug_posix_memalign, posix_memalign)
354 
355 static void *
356 __debug_calloc (size_t nmemb, size_t size)
357 {
358   size_t bytes;
359 
360   if (__glibc_unlikely (__builtin_mul_overflow (nmemb, size, &bytes)))
361     {
362       errno = ENOMEM;
363       return NULL;
364     }
365 
366   void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook);
367   if (__builtin_expect (hook != NULL, 0))
368     {
369       void *mem = (*hook)(bytes, RETURN_ADDRESS (0));
370 
371       if (mem != NULL)
372 	memset (mem, 0, bytes);
373 
374       return mem;
375     }
376 
377   size_t orig_bytes = bytes;
378   void *victim = NULL;
379 
380   if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)
381        || !malloc_mcheck_before (&bytes, &victim)))
382     {
383       victim = (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK)
384 		? malloc_check (bytes) : __libc_malloc (bytes));
385     }
386   if (victim != NULL)
387     {
388       if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK))
389 	victim = malloc_mcheck_after (victim, orig_bytes);
390       memset (victim, 0, orig_bytes);
391     }
392   if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK))
393     malloc_mtrace_after (victim, orig_bytes, RETURN_ADDRESS (0));
394 
395   return victim;
396 }
strong_alias(__debug_calloc,calloc)397 strong_alias (__debug_calloc, calloc)
398 
399 size_t
400 malloc_usable_size (void *mem)
401 {
402   if (mem == NULL)
403     return 0;
404 
405   if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK))
406     return mcheck_usable_size (mem);
407   if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
408     return malloc_check_get_size (mem);
409 
410   mchunkptr p = mem2chunk (mem);
411   if (DUMPED_MAIN_ARENA_CHUNK (p))
412     return chunksize (p) - SIZE_SZ;
413 
414   return musable (mem);
415 }
416 
417 #define LIBC_SYMBOL(sym) libc_ ## sym
418 #define SYMHANDLE(sym) sym ## _handle
419 
420 #define LOAD_SYM(sym) ({ \
421   static void *SYMHANDLE (sym);						      \
422   if (SYMHANDLE (sym) == NULL)						      \
423     SYMHANDLE (sym) = dlsym (RTLD_NEXT, #sym);				      \
424   SYMHANDLE (sym);							      \
425 })
426 
427 int
malloc_info(int options,FILE * fp)428 malloc_info (int options, FILE *fp)
429 {
430   if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
431     return __malloc_info (options, fp);
432 
433   int (*LIBC_SYMBOL (malloc_info)) (int, FILE *) = LOAD_SYM (malloc_info);
434   if (LIBC_SYMBOL (malloc_info) == NULL)
435     return -1;
436 
437   return LIBC_SYMBOL (malloc_info) (options, fp);
438 }
439 
440 int
mallopt(int param_number,int value)441 mallopt (int param_number, int value)
442 {
443   if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
444     return __libc_mallopt (param_number, value);
445 
446   int (*LIBC_SYMBOL (mallopt)) (int, int) = LOAD_SYM (mallopt);
447   if (LIBC_SYMBOL (mallopt) == NULL)
448     return 0;
449 
450   return LIBC_SYMBOL (mallopt) (param_number, value);
451 }
452 
453 void
malloc_stats(void)454 malloc_stats (void)
455 {
456   if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
457     return __malloc_stats ();
458 
459   void (*LIBC_SYMBOL (malloc_stats)) (void) = LOAD_SYM (malloc_stats);
460   if (LIBC_SYMBOL (malloc_stats) == NULL)
461     return;
462 
463   LIBC_SYMBOL (malloc_stats) ();
464 }
465 
466 struct mallinfo2
mallinfo2(void)467 mallinfo2 (void)
468 {
469   if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
470     return __libc_mallinfo2 ();
471 
472   struct mallinfo2 (*LIBC_SYMBOL (mallinfo2)) (void) = LOAD_SYM (mallinfo2);
473   if (LIBC_SYMBOL (mallinfo2) == NULL)
474     {
475       struct mallinfo2 ret = {0};
476       return ret;
477     }
478 
479   return LIBC_SYMBOL (mallinfo2) ();
480 }
481 
482 struct mallinfo
mallinfo(void)483 mallinfo (void)
484 {
485   if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
486     return __libc_mallinfo ();
487 
488   struct mallinfo (*LIBC_SYMBOL (mallinfo)) (void) = LOAD_SYM (mallinfo);
489   if (LIBC_SYMBOL (mallinfo) == NULL)
490     {
491       struct mallinfo ret = {0};
492       return ret;
493     }
494 
495   return LIBC_SYMBOL (mallinfo) ();
496 }
497 
498 int
malloc_trim(size_t s)499 malloc_trim (size_t s)
500 {
501   if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK))
502     return __malloc_trim (s);
503 
504   int (*LIBC_SYMBOL (malloc_trim)) (size_t) = LOAD_SYM (malloc_trim);
505   if (LIBC_SYMBOL (malloc_trim) == NULL)
506     return 0;
507 
508   return LIBC_SYMBOL (malloc_trim) (s);
509 }
510 
511 #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_25)
512 
513 /* Support for restoring dumped heaps contained in historic Emacs
514    executables.  The heap saving feature (malloc_get_state) is no
515    longer implemented in this version of glibc, but we have a heap
516    rewriter in malloc_set_state which transforms the heap into a
517    version compatible with current malloc.  */
518 
519 #define MALLOC_STATE_MAGIC   0x444c4541l
520 #define MALLOC_STATE_VERSION (0 * 0x100l + 5l) /* major*0x100 + minor */
521 
522 struct malloc_save_state
523 {
524   long magic;
525   long version;
526   mbinptr av[NBINS * 2 + 2];
527   char *sbrk_base;
528   int sbrked_mem_bytes;
529   unsigned long trim_threshold;
530   unsigned long top_pad;
531   unsigned int n_mmaps_max;
532   unsigned long mmap_threshold;
533   int check_action;
534   unsigned long max_sbrked_mem;
535   unsigned long max_total_mem;	/* Always 0, for backwards compatibility.  */
536   unsigned int n_mmaps;
537   unsigned int max_n_mmaps;
538   unsigned long mmapped_mem;
539   unsigned long max_mmapped_mem;
540   int using_malloc_checking;
541   unsigned long max_fast;
542   unsigned long arena_test;
543   unsigned long arena_max;
544   unsigned long narenas;
545 };
546 
547 /* Dummy implementation which always fails.  We need to provide this
548    symbol so that existing Emacs binaries continue to work with
549    BIND_NOW.  */
550 void *
malloc_get_state(void)551 malloc_get_state (void)
552 {
553   __set_errno (ENOSYS);
554   return NULL;
555 }
556 compat_symbol (libc_malloc_debug, malloc_get_state, malloc_get_state,
557 	       GLIBC_2_0);
558 
559 int
malloc_set_state(void * msptr)560 malloc_set_state (void *msptr)
561 {
562   struct malloc_save_state *ms = (struct malloc_save_state *) msptr;
563 
564   if (ms->magic != MALLOC_STATE_MAGIC)
565     return -1;
566 
567   /* Must fail if the major version is too high. */
568   if ((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl))
569     return -2;
570 
571   if (debug_initialized == 1)
572     return -1;
573 
574   bool check_was_enabled = __is_malloc_debug_enabled (MALLOC_CHECK_HOOK);
575 
576   /* It's not too late, so disable MALLOC_CHECK_ and all of the hooks.  */
577   __malloc_hook = NULL;
578   __realloc_hook = NULL;
579   __free_hook = NULL;
580   __memalign_hook = NULL;
581   __malloc_debug_disable (MALLOC_CHECK_HOOK);
582 
583   /* We do not need to perform locking here because malloc_set_state
584      must be called before the first call into the malloc subsytem (usually via
585      __malloc_initialize_hook).  pthread_create always calls calloc and thus
586      must be called only afterwards, so there cannot be more than one thread
587      when we reach this point.  Also handle initialization if either we ended
588      up being called before the first malloc or through the hook when
589      malloc-check was enabled.  */
590   if (debug_initialized < 0)
591     generic_hook_ini ();
592   else if (check_was_enabled)
593     __libc_free (__libc_malloc (0));
594 
595   /* Patch the dumped heap.  We no longer try to integrate into the
596      existing heap.  Instead, we mark the existing chunks as mmapped.
597      Together with the update to dumped_main_arena_start and
598      dumped_main_arena_end, realloc and free will recognize these
599      chunks as dumped fake mmapped chunks and never free them.  */
600 
601   /* Find the chunk with the lowest address with the heap.  */
602   mchunkptr chunk = NULL;
603   {
604     size_t *candidate = (size_t *) ms->sbrk_base;
605     size_t *end = (size_t *) (ms->sbrk_base + ms->sbrked_mem_bytes);
606     while (candidate < end)
607       if (*candidate != 0)
608 	{
609 	  chunk = mem2chunk ((void *) (candidate + 1));
610 	  break;
611 	}
612       else
613 	++candidate;
614   }
615   if (chunk == NULL)
616     return 0;
617 
618   /* Iterate over the dumped heap and patch the chunks so that they
619      are treated as fake mmapped chunks.  */
620   mchunkptr top = ms->av[2];
621   while (chunk < top)
622     {
623       if (inuse (chunk))
624 	{
625 	  /* Mark chunk as mmapped, to trigger the fallback path.  */
626 	  size_t size = chunksize (chunk);
627 	  set_head (chunk, size | IS_MMAPPED);
628 	}
629       chunk = next_chunk (chunk);
630     }
631 
632   /* The dumped fake mmapped chunks all lie in this address range.  */
633   dumped_main_arena_start = (mchunkptr) ms->sbrk_base;
634   dumped_main_arena_end = top;
635 
636   return 0;
637 }
638 compat_symbol (libc_malloc_debug, malloc_set_state, malloc_set_state,
639 	       GLIBC_2_0);
640 #endif
641 
642 /* Do not allow linking against the library.  */
643 compat_symbol (libc_malloc_debug, aligned_alloc, aligned_alloc, GLIBC_2_16);
644 compat_symbol (libc_malloc_debug, calloc, calloc, GLIBC_2_0);
645 compat_symbol (libc_malloc_debug, free, free, GLIBC_2_0);
646 compat_symbol (libc_malloc_debug, mallinfo2, mallinfo2, GLIBC_2_33);
647 compat_symbol (libc_malloc_debug, mallinfo, mallinfo, GLIBC_2_0);
648 compat_symbol (libc_malloc_debug, malloc_info, malloc_info, GLIBC_2_10);
649 compat_symbol (libc_malloc_debug, malloc, malloc, GLIBC_2_0);
650 compat_symbol (libc_malloc_debug, malloc_stats, malloc_stats, GLIBC_2_0);
651 compat_symbol (libc_malloc_debug, malloc_trim, malloc_trim, GLIBC_2_0);
652 compat_symbol (libc_malloc_debug, malloc_usable_size, malloc_usable_size,
653 	       GLIBC_2_0);
654 compat_symbol (libc_malloc_debug, mallopt, mallopt, GLIBC_2_0);
655 compat_symbol (libc_malloc_debug, mcheck_check_all, mcheck_check_all,
656 	       GLIBC_2_2);
657 compat_symbol (libc_malloc_debug, mcheck, mcheck, GLIBC_2_0);
658 compat_symbol (libc_malloc_debug, mcheck_pedantic, mcheck_pedantic, GLIBC_2_2);
659 compat_symbol (libc_malloc_debug, memalign, memalign, GLIBC_2_0);
660 compat_symbol (libc_malloc_debug, mprobe, mprobe, GLIBC_2_0);
661 compat_symbol (libc_malloc_debug, mtrace, mtrace, GLIBC_2_0);
662 compat_symbol (libc_malloc_debug, muntrace, muntrace, GLIBC_2_0);
663 compat_symbol (libc_malloc_debug, posix_memalign, posix_memalign, GLIBC_2_2);
664 compat_symbol (libc_malloc_debug, pvalloc, pvalloc, GLIBC_2_0);
665 compat_symbol (libc_malloc_debug, realloc, realloc, GLIBC_2_0);
666 compat_symbol (libc_malloc_debug, valloc, valloc, GLIBC_2_0);
667 compat_symbol (libc_malloc_debug, __free_hook, __free_hook, GLIBC_2_0);
668 compat_symbol (libc_malloc_debug, __malloc_hook, __malloc_hook, GLIBC_2_0);
669 compat_symbol (libc_malloc_debug, __realloc_hook, __realloc_hook, GLIBC_2_0);
670 compat_symbol (libc_malloc_debug, __memalign_hook, __memalign_hook, GLIBC_2_0);
671