1/* More debugging hooks for `malloc'.
2 Copyright (C) 1991-2018 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#include <dso_handle.h>
38
39#include <libio/iolibio.h>
40#define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
41#define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
42
43#include <kernel-features.h>
44
45#define TRACE_BUFFER_SIZE 512
46
47static FILE *mallstream;
48static const char mallenv[] = "MALLOC_TRACE";
49static char *malloc_trace_buffer;
50
51__libc_lock_define_initialized (static, lock);
52
53/* Address to breakpoint on accesses to... */
54void *mallwatch;
55
56/* Old hook values. */
57static void (*tr_old_free_hook) (void *ptr, const void *);
58static void *(*tr_old_malloc_hook) (size_t size, const void *);
59static void *(*tr_old_realloc_hook) (void *ptr, size_t size,
60 const void *);
61static void *(*tr_old_memalign_hook) (size_t __alignment, size_t __size,
62 const void *);
63
64/* This function is called when the block being alloc'd, realloc'd, or
65 freed has an address matching the variable "mallwatch". In a debugger,
66 set "mallwatch" to the address of interest, then put a breakpoint on
67 tr_break. */
68
69extern void tr_break (void) __THROW;
70libc_hidden_proto (tr_break)
71void
72tr_break (void)
73{
74}
75libc_hidden_def (tr_break)
76
77static void
78tr_where (const void *caller, Dl_info *info)
79{
80 if (caller != NULL)
81 {
82 if (info != NULL)
83 {
84 char *buf = (char *) "";
85 if (info->dli_sname != NULL)
86 {
87 size_t len = strlen (info->dli_sname);
88 buf = alloca (len + 6 + 2 * sizeof (void *));
89
90 buf[0] = '(';
91 __stpcpy (_fitoa (caller >= (const void *) info->dli_saddr
92 ? caller - (const void *) info->dli_saddr
93 : (const void *) info->dli_saddr - caller,
94 __stpcpy (__mempcpy (buf + 1, info->dli_sname,
95 len),
96 caller >= (void *) info->dli_saddr
97 ? "+0x" : "-0x"),
98 16, 0),
99 ")");
100 }
101
102 fprintf (mallstream, "@ %s%s%s[%p] ",
103 info->dli_fname ? : "", info->dli_fname ? ":" : "",
104 buf, caller);
105 }
106 else
107 fprintf (mallstream, "@ [%p] ", caller);
108 }
109}
110
111static Dl_info *
112lock_and_info (const void *caller, Dl_info *mem)
113{
114 if (caller == NULL)
115 return NULL;
116
117 Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL;
118
119 __libc_lock_lock (lock);
120
121 return res;
122}
123
124static void
125tr_freehook (void *ptr, const void *caller)
126{
127 if (ptr == NULL)
128 return;
129
130 Dl_info mem;
131 Dl_info *info = lock_and_info (caller, &mem);
132 tr_where (caller, info);
133 /* Be sure to print it first. */
134 fprintf (mallstream, "- %p\n", ptr);
135 if (ptr == mallwatch)
136 {
137 __libc_lock_unlock (lock);
138 tr_break ();
139 __libc_lock_lock (lock);
140 }
141 __free_hook = tr_old_free_hook;
142 if (tr_old_free_hook != NULL)
143 (*tr_old_free_hook)(ptr, caller);
144 else
145 free (ptr);
146 __free_hook = tr_freehook;
147 __libc_lock_unlock (lock);
148}
149
150static void *
151tr_mallochook (size_t size, const void *caller)
152{
153 void *hdr;
154
155 Dl_info mem;
156 Dl_info *info = lock_and_info (caller, &mem);
157
158 __malloc_hook = tr_old_malloc_hook;
159 if (tr_old_malloc_hook != NULL)
160 hdr = (void *) (*tr_old_malloc_hook)(size, caller);
161 else
162 hdr = (void *) malloc (size);
163 __malloc_hook = tr_mallochook;
164
165 tr_where (caller, info);
166 /* We could be printing a NULL here; that's OK. */
167 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
168
169 __libc_lock_unlock (lock);
170
171 if (hdr == mallwatch)
172 tr_break ();
173
174 return hdr;
175}
176
177static void *
178tr_reallochook (void *ptr, size_t size, const void *caller)
179{
180 void *hdr;
181
182 if (ptr == mallwatch)
183 tr_break ();
184
185 Dl_info mem;
186 Dl_info *info = lock_and_info (caller, &mem);
187
188 __free_hook = tr_old_free_hook;
189 __malloc_hook = tr_old_malloc_hook;
190 __realloc_hook = tr_old_realloc_hook;
191 if (tr_old_realloc_hook != NULL)
192 hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller);
193 else
194 hdr = (void *) realloc (ptr, size);
195 __free_hook = tr_freehook;
196 __malloc_hook = tr_mallochook;
197 __realloc_hook = tr_reallochook;
198
199 tr_where (caller, info);
200 if (hdr == NULL)
201 {
202 if (size != 0)
203 /* Failed realloc. */
204 fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
205 else
206 fprintf (mallstream, "- %p\n", ptr);
207 }
208 else if (ptr == NULL)
209 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
210 else
211 {
212 fprintf (mallstream, "< %p\n", ptr);
213 tr_where (caller, info);
214 fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
215 }
216
217 __libc_lock_unlock (lock);
218
219 if (hdr == mallwatch)
220 tr_break ();
221
222 return hdr;
223}
224
225static void *
226tr_memalignhook (size_t alignment, size_t size, const void *caller)
227{
228 void *hdr;
229
230 Dl_info mem;
231 Dl_info *info = lock_and_info (caller, &mem);
232
233 __memalign_hook = tr_old_memalign_hook;
234 __malloc_hook = tr_old_malloc_hook;
235 if (tr_old_memalign_hook != NULL)
236 hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller);
237 else
238 hdr = (void *) memalign (alignment, size);
239 __memalign_hook = tr_memalignhook;
240 __malloc_hook = tr_mallochook;
241
242 tr_where (caller, info);
243 /* We could be printing a NULL here; that's OK. */
244 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
245
246 __libc_lock_unlock (lock);
247
248 if (hdr == mallwatch)
249 tr_break ();
250
251 return hdr;
252}
253
254
255#ifdef _LIBC
256
257/* This function gets called to make sure all memory the library
258 allocates get freed and so does not irritate the user when studying
259 the mtrace output. */
260static void __libc_freeres_fn_section
261release_libc_mem (void)
262{
263 /* Only call the free function if we still are running in mtrace mode. */
264 if (mallstream != NULL)
265 __libc_freeres ();
266}
267#endif
268
269
270/* We enable tracing if either the environment variable MALLOC_TRACE
271 is set, or if the variable mallwatch has been patched to an address
272 that the debugging user wants us to stop on. When patching mallwatch,
273 don't forget to set a breakpoint on tr_break! */
274
275void
276mtrace (void)
277{
278#ifdef _LIBC
279 static int added_atexit_handler;
280#endif
281 char *mallfile;
282
283 /* Don't panic if we're called more than once. */
284 if (mallstream != NULL)
285 return;
286
287#ifdef _LIBC
288 /* When compiling the GNU libc we use the secure getenv function
289 which prevents the misuse in case of SUID or SGID enabled
290 programs. */
291 mallfile = __libc_secure_getenv (mallenv);
292#else
293 mallfile = getenv (mallenv);
294#endif
295 if (mallfile != NULL || mallwatch != NULL)
296 {
297 char *mtb = malloc (TRACE_BUFFER_SIZE);
298 if (mtb == NULL)
299 return;
300
301 mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
302 if (mallstream != NULL)
303 {
304 /* Be sure it doesn't malloc its buffer! */
305 malloc_trace_buffer = mtb;
306 setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
307 fprintf (mallstream, "= Start\n");
308 tr_old_free_hook = __free_hook;
309 __free_hook = tr_freehook;
310 tr_old_malloc_hook = __malloc_hook;
311 __malloc_hook = tr_mallochook;
312 tr_old_realloc_hook = __realloc_hook;
313 __realloc_hook = tr_reallochook;
314 tr_old_memalign_hook = __memalign_hook;
315 __memalign_hook = tr_memalignhook;
316#ifdef _LIBC
317 if (!added_atexit_handler)
318 {
319 added_atexit_handler = 1;
320 __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
321 __dso_handle);
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