]>
Commit | Line | Data |
---|---|---|
2d2d9f2b | 1 | /* Malloc debug DSO. |
6d7e8eda | 2 | Copyright (C) 2021-2023 Free Software Foundation, Inc. |
88e316b0 | 3 | Copyright The GNU Toolchain Authors. |
2d2d9f2b SP |
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 | ||
c142eb25 SP |
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() */ | |
9dad716d | 53 | MALLOC_MTRACE_HOOK = 1 << 1, /* mtrace() */ |
b5bd5bfe | 54 | MALLOC_CHECK_HOOK = 1 << 2, /* MALLOC_CHECK_ or glibc.malloc.check. */ |
c142eb25 SP |
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" | |
9dad716d | 77 | #include "mtrace.c" |
b5bd5bfe | 78 | #include "malloc-check.c" |
c142eb25 | 79 | |
1e5a5866 | 80 | #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_24) |
c142eb25 SP |
81 | extern void (*__malloc_initialize_hook) (void); |
82 | compat_symbol_reference (libc, __malloc_initialize_hook, | |
83 | __malloc_initialize_hook, GLIBC_2_0); | |
1e5a5866 | 84 | #endif |
c142eb25 SP |
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; | |
b5bd5bfe SP |
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)); | |
c142eb25 | 120 | |
1e5a5866 | 121 | #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_24) |
c142eb25 SP |
122 | void (*hook) (void) = __malloc_initialize_hook; |
123 | if (hook != NULL) | |
124 | (*hook)(); | |
1e5a5866 | 125 | #endif |
b5bd5bfe | 126 | |
c142eb25 SP |
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 | } | |
2d2d9f2b SP |
150 | |
151 | static size_t pagesize; | |
152 | ||
0552fd2c SP |
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 | ||
2d2d9f2b SP |
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 | ||
c142eb25 SP |
175 | void *victim = NULL; |
176 | size_t orig_bytes = bytes; | |
b5bd5bfe SP |
177 | if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) |
178 | || !malloc_mcheck_before (&bytes, &victim))) | |
c142eb25 | 179 | { |
b5bd5bfe SP |
180 | victim = (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK) |
181 | ? malloc_check (bytes) : __libc_malloc (bytes)); | |
c142eb25 SP |
182 | } |
183 | if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL) | |
184 | victim = malloc_mcheck_after (victim, orig_bytes); | |
9dad716d SP |
185 | if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK)) |
186 | malloc_mtrace_after (victim, orig_bytes, RETURN_ADDRESS (0)); | |
c142eb25 SP |
187 | |
188 | return victim; | |
2d2d9f2b SP |
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 | } | |
c142eb25 SP |
201 | |
202 | if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)) | |
203 | mem = free_mcheck (mem); | |
b5bd5bfe | 204 | |
0552fd2c SP |
205 | if (DUMPED_MAIN_ARENA_CHUNK (mem2chunk (mem))) |
206 | /* Do nothing. */; | |
207 | else if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK)) | |
b5bd5bfe SP |
208 | free_check (mem); |
209 | else | |
210 | __libc_free (mem); | |
9dad716d SP |
211 | if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK)) |
212 | free_mtrace (mem, RETURN_ADDRESS (0)); | |
2d2d9f2b SP |
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 | ||
c142eb25 SP |
224 | size_t orig_bytes = bytes, oldsize = 0; |
225 | void *victim = NULL; | |
226 | ||
b5bd5bfe SP |
227 | if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) |
228 | || !realloc_mcheck_before (&oldmem, &bytes, &oldsize, &victim))) | |
c142eb25 | 229 | { |
0552fd2c SP |
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)) | |
b5bd5bfe SP |
256 | victim = realloc_check (oldmem, bytes); |
257 | else | |
258 | victim = __libc_realloc (oldmem, bytes); | |
c142eb25 SP |
259 | } |
260 | if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL) | |
261 | victim = realloc_mcheck_after (victim, oldmem, orig_bytes, | |
262 | oldsize); | |
9dad716d SP |
263 | if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK)) |
264 | realloc_mtrace_after (victim, oldmem, orig_bytes, RETURN_ADDRESS (0)); | |
c142eb25 SP |
265 | |
266 | return victim; | |
2d2d9f2b SP |
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 | ||
c142eb25 SP |
278 | void *victim = NULL; |
279 | size_t orig_bytes = bytes; | |
280 | ||
b5bd5bfe SP |
281 | if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) |
282 | || !memalign_mcheck_before (alignment, &bytes, &victim))) | |
c142eb25 | 283 | { |
b5bd5bfe SP |
284 | victim = (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK) |
285 | ? memalign_check (alignment, bytes) | |
286 | : __libc_memalign (alignment, bytes)); | |
c142eb25 SP |
287 | } |
288 | if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL) | |
289 | victim = memalign_mcheck_after (victim, alignment, orig_bytes); | |
9dad716d SP |
290 | if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK)) |
291 | memalign_mtrace_after (victim, orig_bytes, address); | |
c142eb25 SP |
292 | |
293 | return victim; | |
2d2d9f2b SP |
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) | |
d1417176 DD |
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) | |
2d2d9f2b SP |
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 | { | |
c142eb25 SP |
365 | size_t bytes; |
366 | ||
367 | if (__glibc_unlikely (__builtin_mul_overflow (nmemb, size, &bytes))) | |
368 | { | |
369 | errno = ENOMEM; | |
370 | return NULL; | |
371 | } | |
372 | ||
2d2d9f2b SP |
373 | void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook); |
374 | if (__builtin_expect (hook != NULL, 0)) | |
375 | { | |
2d2d9f2b SP |
376 | void *mem = (*hook)(bytes, RETURN_ADDRESS (0)); |
377 | ||
378 | if (mem != NULL) | |
379 | memset (mem, 0, bytes); | |
380 | ||
381 | return mem; | |
382 | } | |
383 | ||
c142eb25 SP |
384 | size_t orig_bytes = bytes; |
385 | void *victim = NULL; | |
386 | ||
b5bd5bfe SP |
387 | if ((!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) |
388 | || !malloc_mcheck_before (&bytes, &victim))) | |
c142eb25 | 389 | { |
b5bd5bfe SP |
390 | victim = (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK) |
391 | ? malloc_check (bytes) : __libc_malloc (bytes)); | |
c142eb25 SP |
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 | } | |
9dad716d SP |
399 | if (__is_malloc_debug_enabled (MALLOC_MTRACE_HOOK)) |
400 | malloc_mtrace_after (victim, orig_bytes, RETURN_ADDRESS (0)); | |
401 | ||
c142eb25 | 402 | return victim; |
2d2d9f2b SP |
403 | } |
404 | strong_alias (__debug_calloc, calloc) | |
b5bd5bfe SP |
405 | |
406 | size_t | |
407 | malloc_usable_size (void *mem) | |
408 | { | |
88e316b0 SP |
409 | if (mem == NULL) |
410 | return 0; | |
411 | ||
6856975e SP |
412 | if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)) |
413 | return mcheck_usable_size (mem); | |
b5bd5bfe SP |
414 | if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK)) |
415 | return malloc_check_get_size (mem); | |
416 | ||
88e316b0 SP |
417 | mchunkptr p = mem2chunk (mem); |
418 | if (DUMPED_MAIN_ARENA_CHUNK (p)) | |
419 | return chunksize (p) - SIZE_SZ; | |
0552fd2c | 420 | |
b5bd5bfe SP |
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 | } | |
0552fd2c SP |
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 subsytem (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 | |
1e5a5866 SP |
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); |