1/* More debugging hooks for `malloc'.
2 Copyright (C) 1991-2017 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
46static FILE *mallstream;
47static const char mallenv[] = "MALLOC_TRACE";
48static 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. */
56static void (*tr_old_free_hook) (__ptr_t ptr, const __ptr_t);
57static __ptr_t (*tr_old_malloc_hook) (size_t size, const __ptr_t);
58static __ptr_t (*tr_old_realloc_hook) (__ptr_t ptr, size_t size,
59 const __ptr_t);
60static __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
68extern void tr_break (void) __THROW;
69libc_hidden_proto (tr_break)
70void
71tr_break (void)
72{
73}
74libc_hidden_def (tr_break)
75
76static void internal_function
77tr_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
110static Dl_info *
111lock_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
123static void
124tr_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
149static __ptr_t
150tr_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
176static __ptr_t
177tr_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
224static __ptr_t
225tr_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. */
259static void __libc_freeres_fn_section
260release_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
274void
275mtrace (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 /* Be sure it doesn't malloc its buffer! */
304 malloc_trace_buffer = mtb;
305 setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
306 fprintf (mallstream, "= Start\n");
307 tr_old_free_hook = __free_hook;
308 __free_hook = tr_freehook;
309 tr_old_malloc_hook = __malloc_hook;
310 __malloc_hook = tr_mallochook;
311 tr_old_realloc_hook = __realloc_hook;
312 __realloc_hook = tr_reallochook;
313 tr_old_memalign_hook = __memalign_hook;
314 __memalign_hook = tr_memalignhook;
315#ifdef _LIBC
316 if (!added_atexit_handler)
317 {
318 extern void *__dso_handle __attribute__ ((__weak__));
319 added_atexit_handler = 1;
320 __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
321 &__dso_handle ? __dso_handle : NULL);
322 }
323#endif
324 }
325 else
326 free (mtb);
327 }
328}
329
330void
331muntrace (void)
332{
333 if (mallstream == NULL)
334 return;
335
336 /* Do the reverse of what done in mtrace: first reset the hooks and
337 MALLSTREAM, and only after that write the trailer and close the
338 file. */
339 FILE *f = mallstream;
340 mallstream = NULL;
341 __free_hook = tr_old_free_hook;
342 __malloc_hook = tr_old_malloc_hook;
343 __realloc_hook = tr_old_realloc_hook;
344 __memalign_hook = tr_old_memalign_hook;
345
346 fprintf (f, "= End\n");
347 fclose (f);
348}
349