| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* More debugging hooks for `malloc'. | 
|  | 2 | Copyright (C) 1991-2016 Free Software Foundation, Inc. | 
|  | 3 | This file is part of the GNU C Library. | 
|  | 4 | Written April 2, 1991 by John Gilmore of Cygnus Support. | 
|  | 5 | Based on mcheck.c by Mike Haertel. | 
|  | 6 |  | 
|  | 7 | The GNU C Library is free software; you can redistribute it and/or | 
|  | 8 | modify it under the terms of the GNU Lesser General Public | 
|  | 9 | License as published by the Free Software Foundation; either | 
|  | 10 | version 2.1 of the License, or (at your option) any later version. | 
|  | 11 |  | 
|  | 12 | The GNU C Library is distributed in the hope that it will be useful, | 
|  | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | 15 | Lesser General Public License for more details. | 
|  | 16 |  | 
|  | 17 | You should have received a copy of the GNU Lesser General Public | 
|  | 18 | License along with the GNU C Library; if not, see | 
|  | 19 | <http://www.gnu.org/licenses/>.  */ | 
|  | 20 |  | 
|  | 21 | #ifndef _MALLOC_INTERNAL | 
|  | 22 | # define _MALLOC_INTERNAL | 
|  | 23 | # include <malloc.h> | 
|  | 24 | # include <mcheck.h> | 
|  | 25 | # include <libc-lock.h> | 
|  | 26 | #endif | 
|  | 27 |  | 
|  | 28 | #include <dlfcn.h> | 
|  | 29 | #include <fcntl.h> | 
|  | 30 | #include <stdio.h> | 
|  | 31 | #include <string.h> | 
|  | 32 | #include <stdlib.h> | 
|  | 33 |  | 
|  | 34 | #include <_itoa.h> | 
|  | 35 |  | 
|  | 36 | #include <libc-internal.h> | 
|  | 37 |  | 
|  | 38 | #include <libio/iolibio.h> | 
|  | 39 | #define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l) | 
|  | 40 | #define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp) | 
|  | 41 |  | 
|  | 42 | #include <kernel-features.h> | 
|  | 43 |  | 
|  | 44 | #define TRACE_BUFFER_SIZE 512 | 
|  | 45 |  | 
|  | 46 | static FILE *mallstream; | 
|  | 47 | static const char mallenv[] = "MALLOC_TRACE"; | 
|  | 48 | static char *malloc_trace_buffer; | 
|  | 49 |  | 
|  | 50 | __libc_lock_define_initialized (static, lock); | 
|  | 51 |  | 
|  | 52 | /* Address to breakpoint on accesses to... */ | 
|  | 53 | __ptr_t mallwatch; | 
|  | 54 |  | 
|  | 55 | /* Old hook values.  */ | 
|  | 56 | static void (*tr_old_free_hook) (__ptr_t ptr, const __ptr_t); | 
|  | 57 | static __ptr_t (*tr_old_malloc_hook) (size_t size, const __ptr_t); | 
|  | 58 | static __ptr_t (*tr_old_realloc_hook) (__ptr_t ptr, size_t size, | 
|  | 59 | const __ptr_t); | 
|  | 60 | static __ptr_t (*tr_old_memalign_hook) (size_t __alignment, size_t __size, | 
|  | 61 | const __ptr_t); | 
|  | 62 |  | 
|  | 63 | /* This function is called when the block being alloc'd, realloc'd, or | 
|  | 64 | freed has an address matching the variable "mallwatch".  In a debugger, | 
|  | 65 | set "mallwatch" to the address of interest, then put a breakpoint on | 
|  | 66 | tr_break.  */ | 
|  | 67 |  | 
|  | 68 | extern void tr_break (void) __THROW; | 
|  | 69 | libc_hidden_proto (tr_break) | 
|  | 70 | void | 
|  | 71 | tr_break (void) | 
|  | 72 | { | 
|  | 73 | } | 
|  | 74 | libc_hidden_def (tr_break) | 
|  | 75 |  | 
|  | 76 | static void internal_function | 
|  | 77 | tr_where (const __ptr_t caller, Dl_info *info) | 
|  | 78 | { | 
|  | 79 | if (caller != NULL) | 
|  | 80 | { | 
|  | 81 | if (info != NULL) | 
|  | 82 | { | 
|  | 83 | char *buf = (char *) ""; | 
|  | 84 | if (info->dli_sname != NULL) | 
|  | 85 | { | 
|  | 86 | size_t len = strlen (info->dli_sname); | 
|  | 87 | buf = alloca (len + 6 + 2 * sizeof (void *)); | 
|  | 88 |  | 
|  | 89 | buf[0] = '('; | 
|  | 90 | __stpcpy (_fitoa (caller >= (const __ptr_t) info->dli_saddr | 
|  | 91 | ? caller - (const __ptr_t) info->dli_saddr | 
|  | 92 | : (const __ptr_t) info->dli_saddr - caller, | 
|  | 93 | __stpcpy (__mempcpy (buf + 1, info->dli_sname, | 
|  | 94 | len), | 
|  | 95 | caller >= (__ptr_t) info->dli_saddr | 
|  | 96 | ? "+0x" : "-0x"), | 
|  | 97 | 16, 0), | 
|  | 98 | ")"); | 
|  | 99 | } | 
|  | 100 |  | 
|  | 101 | fprintf (mallstream, "@ %s%s%s[%p] ", | 
|  | 102 | info->dli_fname ? : "", info->dli_fname ? ":" : "", | 
|  | 103 | buf, caller); | 
|  | 104 | } | 
|  | 105 | else | 
|  | 106 | fprintf (mallstream, "@ [%p] ", caller); | 
|  | 107 | } | 
|  | 108 | } | 
|  | 109 |  | 
|  | 110 | static Dl_info * | 
|  | 111 | lock_and_info (const __ptr_t caller, Dl_info *mem) | 
|  | 112 | { | 
|  | 113 | if (caller == NULL) | 
|  | 114 | return NULL; | 
|  | 115 |  | 
|  | 116 | Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL; | 
|  | 117 |  | 
|  | 118 | __libc_lock_lock (lock); | 
|  | 119 |  | 
|  | 120 | return res; | 
|  | 121 | } | 
|  | 122 |  | 
|  | 123 | static void | 
|  | 124 | tr_freehook (__ptr_t ptr, const __ptr_t caller) | 
|  | 125 | { | 
|  | 126 | if (ptr == NULL) | 
|  | 127 | return; | 
|  | 128 |  | 
|  | 129 | Dl_info mem; | 
|  | 130 | Dl_info *info = lock_and_info (caller, &mem); | 
|  | 131 | tr_where (caller, info); | 
|  | 132 | /* Be sure to print it first.  */ | 
|  | 133 | fprintf (mallstream, "- %p\n", ptr); | 
|  | 134 | if (ptr == mallwatch) | 
|  | 135 | { | 
|  | 136 | __libc_lock_unlock (lock); | 
|  | 137 | tr_break (); | 
|  | 138 | __libc_lock_lock (lock); | 
|  | 139 | } | 
|  | 140 | __free_hook = tr_old_free_hook; | 
|  | 141 | if (tr_old_free_hook != NULL) | 
|  | 142 | (*tr_old_free_hook)(ptr, caller); | 
|  | 143 | else | 
|  | 144 | free (ptr); | 
|  | 145 | __free_hook = tr_freehook; | 
|  | 146 | __libc_lock_unlock (lock); | 
|  | 147 | } | 
|  | 148 |  | 
|  | 149 | static __ptr_t | 
|  | 150 | tr_mallochook (size_t size, const __ptr_t caller) | 
|  | 151 | { | 
|  | 152 | __ptr_t hdr; | 
|  | 153 |  | 
|  | 154 | Dl_info mem; | 
|  | 155 | Dl_info *info = lock_and_info (caller, &mem); | 
|  | 156 |  | 
|  | 157 | __malloc_hook = tr_old_malloc_hook; | 
|  | 158 | if (tr_old_malloc_hook != NULL) | 
|  | 159 | hdr = (__ptr_t) (*tr_old_malloc_hook)(size, caller); | 
|  | 160 | else | 
|  | 161 | hdr = (__ptr_t) malloc (size); | 
|  | 162 | __malloc_hook = tr_mallochook; | 
|  | 163 |  | 
|  | 164 | tr_where (caller, info); | 
|  | 165 | /* We could be printing a NULL here; that's OK.  */ | 
|  | 166 | fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); | 
|  | 167 |  | 
|  | 168 | __libc_lock_unlock (lock); | 
|  | 169 |  | 
|  | 170 | if (hdr == mallwatch) | 
|  | 171 | tr_break (); | 
|  | 172 |  | 
|  | 173 | return hdr; | 
|  | 174 | } | 
|  | 175 |  | 
|  | 176 | static __ptr_t | 
|  | 177 | tr_reallochook (__ptr_t ptr, size_t size, const __ptr_t caller) | 
|  | 178 | { | 
|  | 179 | __ptr_t hdr; | 
|  | 180 |  | 
|  | 181 | if (ptr == mallwatch) | 
|  | 182 | tr_break (); | 
|  | 183 |  | 
|  | 184 | Dl_info mem; | 
|  | 185 | Dl_info *info = lock_and_info (caller, &mem); | 
|  | 186 |  | 
|  | 187 | __free_hook = tr_old_free_hook; | 
|  | 188 | __malloc_hook = tr_old_malloc_hook; | 
|  | 189 | __realloc_hook = tr_old_realloc_hook; | 
|  | 190 | if (tr_old_realloc_hook != NULL) | 
|  | 191 | hdr = (__ptr_t) (*tr_old_realloc_hook)(ptr, size, caller); | 
|  | 192 | else | 
|  | 193 | hdr = (__ptr_t) realloc (ptr, size); | 
|  | 194 | __free_hook = tr_freehook; | 
|  | 195 | __malloc_hook = tr_mallochook; | 
|  | 196 | __realloc_hook = tr_reallochook; | 
|  | 197 |  | 
|  | 198 | tr_where (caller, info); | 
|  | 199 | if (hdr == NULL) | 
|  | 200 | { | 
|  | 201 | if (size != 0) | 
|  | 202 | /* Failed realloc.  */ | 
|  | 203 | fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size); | 
|  | 204 | else | 
|  | 205 | fprintf (mallstream, "- %p\n", ptr); | 
|  | 206 | } | 
|  | 207 | else if (ptr == NULL) | 
|  | 208 | fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); | 
|  | 209 | else | 
|  | 210 | { | 
|  | 211 | fprintf (mallstream, "< %p\n", ptr); | 
|  | 212 | tr_where (caller, info); | 
|  | 213 | fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size); | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 | __libc_lock_unlock (lock); | 
|  | 217 |  | 
|  | 218 | if (hdr == mallwatch) | 
|  | 219 | tr_break (); | 
|  | 220 |  | 
|  | 221 | return hdr; | 
|  | 222 | } | 
|  | 223 |  | 
|  | 224 | static __ptr_t | 
|  | 225 | tr_memalignhook (size_t alignment, size_t size, const __ptr_t caller) | 
|  | 226 | { | 
|  | 227 | __ptr_t hdr; | 
|  | 228 |  | 
|  | 229 | Dl_info mem; | 
|  | 230 | Dl_info *info = lock_and_info (caller, &mem); | 
|  | 231 |  | 
|  | 232 | __memalign_hook = tr_old_memalign_hook; | 
|  | 233 | __malloc_hook = tr_old_malloc_hook; | 
|  | 234 | if (tr_old_memalign_hook != NULL) | 
|  | 235 | hdr = (__ptr_t) (*tr_old_memalign_hook)(alignment, size, caller); | 
|  | 236 | else | 
|  | 237 | hdr = (__ptr_t) memalign (alignment, size); | 
|  | 238 | __memalign_hook = tr_memalignhook; | 
|  | 239 | __malloc_hook = tr_mallochook; | 
|  | 240 |  | 
|  | 241 | tr_where (caller, info); | 
|  | 242 | /* We could be printing a NULL here; that's OK.  */ | 
|  | 243 | fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); | 
|  | 244 |  | 
|  | 245 | __libc_lock_unlock (lock); | 
|  | 246 |  | 
|  | 247 | if (hdr == mallwatch) | 
|  | 248 | tr_break (); | 
|  | 249 |  | 
|  | 250 | return hdr; | 
|  | 251 | } | 
|  | 252 |  | 
|  | 253 |  | 
|  | 254 | #ifdef _LIBC | 
|  | 255 |  | 
|  | 256 | /* This function gets called to make sure all memory the library | 
|  | 257 | allocates get freed and so does not irritate the user when studying | 
|  | 258 | the mtrace output.  */ | 
|  | 259 | static void __libc_freeres_fn_section | 
|  | 260 | release_libc_mem (void) | 
|  | 261 | { | 
|  | 262 | /* Only call the free function if we still are running in mtrace mode.  */ | 
|  | 263 | if (mallstream != NULL) | 
|  | 264 | __libc_freeres (); | 
|  | 265 | } | 
|  | 266 | #endif | 
|  | 267 |  | 
|  | 268 |  | 
|  | 269 | /* We enable tracing if either the environment variable MALLOC_TRACE | 
|  | 270 | is set, or if the variable mallwatch has been patched to an address | 
|  | 271 | that the debugging user wants us to stop on.  When patching mallwatch, | 
|  | 272 | don't forget to set a breakpoint on tr_break!  */ | 
|  | 273 |  | 
|  | 274 | void | 
|  | 275 | mtrace (void) | 
|  | 276 | { | 
|  | 277 | #ifdef _LIBC | 
|  | 278 | static int added_atexit_handler; | 
|  | 279 | #endif | 
|  | 280 | char *mallfile; | 
|  | 281 |  | 
|  | 282 | /* Don't panic if we're called more than once.  */ | 
|  | 283 | if (mallstream != NULL) | 
|  | 284 | return; | 
|  | 285 |  | 
|  | 286 | #ifdef _LIBC | 
|  | 287 | /* When compiling the GNU libc we use the secure getenv function | 
|  | 288 | which prevents the misuse in case of SUID or SGID enabled | 
|  | 289 | programs.  */ | 
|  | 290 | mallfile = __libc_secure_getenv (mallenv); | 
|  | 291 | #else | 
|  | 292 | mallfile = getenv (mallenv); | 
|  | 293 | #endif | 
|  | 294 | if (mallfile != NULL || mallwatch != NULL) | 
|  | 295 | { | 
|  | 296 | char *mtb = malloc (TRACE_BUFFER_SIZE); | 
|  | 297 | if (mtb == NULL) | 
|  | 298 | return; | 
|  | 299 |  | 
|  | 300 | mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce"); | 
|  | 301 | if (mallstream != NULL) | 
|  | 302 | { | 
|  | 303 | #ifndef __ASSUME_O_CLOEXEC | 
|  | 304 | /* Make sure we close the file descriptor on exec.  */ | 
|  | 305 | int flags = __fcntl (fileno (mallstream), F_GETFD, 0); | 
|  | 306 | if (flags >= 0) | 
|  | 307 | { | 
|  | 308 | flags |= FD_CLOEXEC; | 
|  | 309 | __fcntl (fileno (mallstream), F_SETFD, flags); | 
|  | 310 | } | 
|  | 311 | #endif | 
|  | 312 | /* Be sure it doesn't malloc its buffer!  */ | 
|  | 313 | malloc_trace_buffer = mtb; | 
|  | 314 | setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE); | 
|  | 315 | fprintf (mallstream, "= Start\n"); | 
|  | 316 | tr_old_free_hook = __free_hook; | 
|  | 317 | __free_hook = tr_freehook; | 
|  | 318 | tr_old_malloc_hook = __malloc_hook; | 
|  | 319 | __malloc_hook = tr_mallochook; | 
|  | 320 | tr_old_realloc_hook = __realloc_hook; | 
|  | 321 | __realloc_hook = tr_reallochook; | 
|  | 322 | tr_old_memalign_hook = __memalign_hook; | 
|  | 323 | __memalign_hook = tr_memalignhook; | 
|  | 324 | #ifdef _LIBC | 
|  | 325 | if (!added_atexit_handler) | 
|  | 326 | { | 
|  | 327 | extern void *__dso_handle __attribute__ ((__weak__)); | 
|  | 328 | added_atexit_handler = 1; | 
|  | 329 | __cxa_atexit ((void (*)(void *))release_libc_mem, NULL, | 
|  | 330 | &__dso_handle ? __dso_handle : NULL); | 
|  | 331 | } | 
|  | 332 | #endif | 
|  | 333 | } | 
|  | 334 | else | 
|  | 335 | free (mtb); | 
|  | 336 | } | 
|  | 337 | } | 
|  | 338 |  | 
|  | 339 | void | 
|  | 340 | muntrace (void) | 
|  | 341 | { | 
|  | 342 | if (mallstream == NULL) | 
|  | 343 | return; | 
|  | 344 |  | 
|  | 345 | /* Do the reverse of what done in mtrace: first reset the hooks and | 
|  | 346 | MALLSTREAM, and only after that write the trailer and close the | 
|  | 347 | file.  */ | 
|  | 348 | FILE *f = mallstream; | 
|  | 349 | mallstream = NULL; | 
|  | 350 | __free_hook = tr_old_free_hook; | 
|  | 351 | __malloc_hook = tr_old_malloc_hook; | 
|  | 352 | __realloc_hook = tr_old_realloc_hook; | 
|  | 353 | __memalign_hook = tr_old_memalign_hook; | 
|  | 354 |  | 
|  | 355 | fprintf (f, "= End\n"); | 
|  | 356 | fclose (f); | 
|  | 357 | } |