Fix: pre-fault TLS in ust-malloc instrumentation
[lttng-ust.git] / liblttng-ust-libc-wrapper / lttng-ust-malloc.c
CommitLineData
b27f8e75
MD
1/*
2 * Copyright (C) 2009 Pierre-Marc Fournier
1622ba22 3 * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
c39c72ee
PMF
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
6d4658aa 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c39c72ee
PMF
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
e541a28d 20#define _GNU_SOURCE
f02baefb 21#include <lttng/ust-dlfcn.h>
e541a28d
PMF
22#include <sys/types.h>
23#include <stdio.h>
2594a5b4 24#include <assert.h>
4c3536e0
MD
25#include <urcu/system.h>
26#include <urcu/uatomic.h>
2594a5b4 27#include <urcu/compiler.h>
8c06ba6f 28#include <urcu/tls-compat.h>
20ef5166 29#include <urcu/arch.h>
2594a5b4 30#include <lttng/align.h>
82556ac0 31#include <helper.h>
1622ba22
MD
32
33#define TRACEPOINT_DEFINE
34#define TRACEPOINT_CREATE_PROBES
52c95399 35#define TP_IP_PARAM ip
1622ba22 36#include "ust_libc.h"
fbd8191b 37
f95b2888
SS
38#define STATIC_CALLOC_LEN 4096
39static char static_calloc_buf[STATIC_CALLOC_LEN];
4c3536e0 40static unsigned long static_calloc_buf_offset;
f95b2888 41
2594a5b4
MD
42struct alloc_functions {
43 void *(*calloc)(size_t nmemb, size_t size);
44 void *(*malloc)(size_t size);
45 void (*free)(void *ptr);
46 void *(*realloc)(void *ptr, size_t size);
47 void *(*memalign)(size_t alignment, size_t size);
48 int (*posix_memalign)(void **memptr, size_t alignment, size_t size);
49};
50
51static
52struct alloc_functions cur_alloc;
53
8c06ba6f
MD
54/*
55 * Make sure our own use of the LTS compat layer will not cause infinite
56 * recursion by calling calloc.
57 */
58
59static
60void *static_calloc(size_t nmemb, size_t size);
61
20ef5166
MD
62/*
63 * pthread mutex replacement for URCU tls compat layer.
64 */
65static int ust_malloc_lock;
66
67static __attribute__((unused))
68void ust_malloc_spin_lock(pthread_mutex_t *lock)
69{
70 /*
71 * The memory barrier within cmpxchg takes care of ordering
72 * memory accesses with respect to the start of the critical
73 * section.
74 */
75 while (uatomic_cmpxchg(&ust_malloc_lock, 0, 1) != 0)
76 caa_cpu_relax();
77}
78
79static __attribute__((unused))
80void ust_malloc_spin_unlock(pthread_mutex_t *lock)
81{
82 /*
83 * Ensure memory accesses within the critical section do not
84 * leak outside.
85 */
86 cmm_smp_mb();
87 uatomic_set(&ust_malloc_lock, 0);
88}
89
8c06ba6f 90#define calloc static_calloc
20ef5166
MD
91#define pthread_mutex_lock ust_malloc_spin_lock
92#define pthread_mutex_unlock ust_malloc_spin_unlock
8c06ba6f 93static DEFINE_URCU_TLS(int, malloc_nesting);
20ef5166
MD
94#undef ust_malloc_spin_unlock
95#undef ust_malloc_spin_lock
8c06ba6f
MD
96#undef calloc
97
2594a5b4
MD
98/*
99 * Static allocator to use when initially executing dlsym(). It keeps a
100 * size_t value of each object size prior to the object.
101 */
102static
103void *static_calloc_aligned(size_t nmemb, size_t size, size_t alignment)
f95b2888 104{
2594a5b4
MD
105 size_t prev_offset, new_offset, res_offset, aligned_offset;
106
107 if (nmemb * size == 0) {
108 return NULL;
109 }
f95b2888 110
4c3536e0
MD
111 /*
112 * Protect static_calloc_buf_offset from concurrent updates
113 * using a cmpxchg loop rather than a mutex to remove a
114 * dependency on pthread. This will minimize the risk of bad
115 * interaction between mutex and malloc instrumentation.
116 */
117 res_offset = CMM_LOAD_SHARED(static_calloc_buf_offset);
118 do {
119 prev_offset = res_offset;
2594a5b4
MD
120 aligned_offset = ALIGN(prev_offset + sizeof(size_t), alignment);
121 new_offset = aligned_offset + nmemb * size;
122 if (new_offset > sizeof(static_calloc_buf)) {
123 abort();
4c3536e0 124 }
4c3536e0
MD
125 } while ((res_offset = uatomic_cmpxchg(&static_calloc_buf_offset,
126 prev_offset, new_offset)) != prev_offset);
2594a5b4
MD
127 *(size_t *) &static_calloc_buf[aligned_offset - sizeof(size_t)] = size;
128 return &static_calloc_buf[aligned_offset];
129}
130
131static
132void *static_calloc(size_t nmemb, size_t size)
133{
134 void *retval;
135
136 retval = static_calloc_aligned(nmemb, size, 1);
2594a5b4
MD
137 return retval;
138}
139
140static
141void *static_malloc(size_t size)
142{
143 void *retval;
144
145 retval = static_calloc_aligned(1, size, 1);
2594a5b4
MD
146 return retval;
147}
148
149static
150void static_free(void *ptr)
151{
152 /* no-op. */
2594a5b4
MD
153}
154
155static
156void *static_realloc(void *ptr, size_t size)
157{
158 size_t *old_size = NULL;
159 void *retval;
160
161 if (size == 0) {
162 retval = NULL;
163 goto end;
164 }
165
166 if (ptr) {
167 old_size = (size_t *) ptr - 1;
168 if (size <= *old_size) {
169 /* We can re-use the old entry. */
170 *old_size = size;
171 retval = ptr;
172 goto end;
173 }
174 }
175 /* We need to expand. Don't free previous memory location. */
176 retval = static_calloc_aligned(1, size, 1);
177 assert(retval);
178 if (ptr)
179 memcpy(retval, ptr, *old_size);
180end:
2594a5b4
MD
181 return retval;
182}
183
184static
185void *static_memalign(size_t alignment, size_t size)
186{
187 void *retval;
188
189 retval = static_calloc_aligned(1, size, alignment);
2594a5b4
MD
190 return retval;
191}
192
193static
194int static_posix_memalign(void **memptr, size_t alignment, size_t size)
195{
2594a5b4
MD
196 void *ptr;
197
198 /* Check for power of 2, larger than void *. */
199 if (alignment & (alignment - 1)
200 || alignment < sizeof(void *)
201 || alignment == 0) {
2594a5b4
MD
202 goto end;
203 }
204 ptr = static_calloc_aligned(1, size, alignment);
205 *memptr = ptr;
2594a5b4 206end:
2594a5b4
MD
207 return 0;
208}
209
210static
211void setup_static_allocator(void)
212{
213 assert(cur_alloc.calloc == NULL);
214 cur_alloc.calloc = static_calloc;
215 assert(cur_alloc.malloc == NULL);
216 cur_alloc.malloc = static_malloc;
217 assert(cur_alloc.free == NULL);
218 cur_alloc.free = static_free;
219 assert(cur_alloc.realloc == NULL);
220 cur_alloc.realloc = static_realloc;
221 assert(cur_alloc.memalign == NULL);
222 cur_alloc.memalign = static_memalign;
223 assert(cur_alloc.posix_memalign == NULL);
224 cur_alloc.posix_memalign = static_posix_memalign;
225}
226
227static
228void lookup_all_symbols(void)
229{
230 struct alloc_functions af;
231
232 /*
233 * Temporarily redirect allocation functions to
234 * static_calloc_aligned, and free function to static_free
235 * (no-op), until the dlsym lookup has completed.
236 */
237 setup_static_allocator();
238
239 /* Perform the actual lookups */
240 af.calloc = dlsym(RTLD_NEXT, "calloc");
241 af.malloc = dlsym(RTLD_NEXT, "malloc");
242 af.free = dlsym(RTLD_NEXT, "free");
243 af.realloc = dlsym(RTLD_NEXT, "realloc");
244 af.memalign = dlsym(RTLD_NEXT, "memalign");
245 af.posix_memalign = dlsym(RTLD_NEXT, "posix_memalign");
246
247 /* Populate the new allocator functions */
248 memcpy(&cur_alloc, &af, sizeof(cur_alloc));
f95b2888
SS
249}
250
e541a28d
PMF
251void *malloc(size_t size)
252{
1c184644
PMF
253 void *retval;
254
8c06ba6f 255 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
256 if (cur_alloc.malloc == NULL) {
257 lookup_all_symbols();
258 if (cur_alloc.malloc == NULL) {
e541a28d 259 fprintf(stderr, "mallocwrap: unable to find malloc\n");
2594a5b4 260 abort();
e541a28d
PMF
261 }
262 }
2594a5b4 263 retval = cur_alloc.malloc(size);
8c06ba6f 264 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa 265 tracepoint(lttng_ust_libc, malloc,
82556ac0 266 size, retval, LTTNG_UST_CALLER_IP());
8c06ba6f
MD
267 }
268 URCU_TLS(malloc_nesting)--;
1c184644
PMF
269 return retval;
270}
271
272void free(void *ptr)
273{
8c06ba6f 274 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
275 /*
276 * Check whether the memory was allocated with
277 * static_calloc_align, in which case there is nothing to free.
f95b2888 278 */
2594a5b4
MD
279 if (caa_unlikely((char *)ptr >= static_calloc_buf &&
280 (char *)ptr < static_calloc_buf + STATIC_CALLOC_LEN)) {
8c06ba6f
MD
281 goto end;
282 }
283
284 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa 285 tracepoint(lttng_ust_libc, free,
82556ac0 286 ptr, LTTNG_UST_CALLER_IP());
f95b2888 287 }
1c184644 288
2594a5b4
MD
289 if (cur_alloc.free == NULL) {
290 lookup_all_symbols();
291 if (cur_alloc.free == NULL) {
1c184644 292 fprintf(stderr, "mallocwrap: unable to find free\n");
2594a5b4 293 abort();
1c184644
PMF
294 }
295 }
2594a5b4 296 cur_alloc.free(ptr);
8c06ba6f
MD
297end:
298 URCU_TLS(malloc_nesting)--;
e541a28d 299}
f95b2888
SS
300
301void *calloc(size_t nmemb, size_t size)
302{
f95b2888
SS
303 void *retval;
304
8c06ba6f 305 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
306 if (cur_alloc.calloc == NULL) {
307 lookup_all_symbols();
308 if (cur_alloc.calloc == NULL) {
f95b2888 309 fprintf(stderr, "callocwrap: unable to find calloc\n");
2594a5b4 310 abort();
f95b2888
SS
311 }
312 }
2594a5b4 313 retval = cur_alloc.calloc(nmemb, size);
8c06ba6f 314 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa 315 tracepoint(lttng_ust_libc, calloc,
82556ac0 316 nmemb, size, retval, LTTNG_UST_CALLER_IP());
8c06ba6f
MD
317 }
318 URCU_TLS(malloc_nesting)--;
f95b2888
SS
319 return retval;
320}
321
322void *realloc(void *ptr, size_t size)
323{
f95b2888
SS
324 void *retval;
325
8c06ba6f
MD
326 URCU_TLS(malloc_nesting)++;
327 /*
328 * Check whether the memory was allocated with
2594a5b4
MD
329 * static_calloc_align, in which case there is nothing
330 * to free, and we need to copy the old data.
331 */
332 if (caa_unlikely((char *)ptr >= static_calloc_buf &&
333 (char *)ptr < static_calloc_buf + STATIC_CALLOC_LEN)) {
334 size_t *old_size;
335
336 old_size = (size_t *) ptr - 1;
337 if (cur_alloc.calloc == NULL) {
338 lookup_all_symbols();
339 if (cur_alloc.calloc == NULL) {
340 fprintf(stderr, "reallocwrap: unable to find calloc\n");
341 abort();
342 }
343 }
344 retval = cur_alloc.calloc(1, size);
345 if (retval) {
346 memcpy(retval, ptr, *old_size);
347 }
8c06ba6f
MD
348 /*
349 * Mimick that a NULL pointer has been received, so
350 * memory allocation analysis based on the trace don't
351 * get confused by the address from the static
352 * allocator.
353 */
354 ptr = NULL;
2594a5b4
MD
355 goto end;
356 }
357
358 if (cur_alloc.realloc == NULL) {
359 lookup_all_symbols();
360 if (cur_alloc.realloc == NULL) {
f95b2888 361 fprintf(stderr, "reallocwrap: unable to find realloc\n");
2594a5b4 362 abort();
f95b2888
SS
363 }
364 }
2594a5b4
MD
365 retval = cur_alloc.realloc(ptr, size);
366end:
8c06ba6f 367 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa 368 tracepoint(lttng_ust_libc, realloc,
82556ac0 369 ptr, size, retval, LTTNG_UST_CALLER_IP());
8c06ba6f
MD
370 }
371 URCU_TLS(malloc_nesting)--;
f95b2888
SS
372 return retval;
373}
9d34b226
SS
374
375void *memalign(size_t alignment, size_t size)
376{
9d34b226
SS
377 void *retval;
378
8c06ba6f 379 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
380 if (cur_alloc.memalign == NULL) {
381 lookup_all_symbols();
382 if (cur_alloc.memalign == NULL) {
9d34b226 383 fprintf(stderr, "memalignwrap: unable to find memalign\n");
2594a5b4 384 abort();
9d34b226
SS
385 }
386 }
2594a5b4 387 retval = cur_alloc.memalign(alignment, size);
8c06ba6f 388 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa
AB
389 tracepoint(lttng_ust_libc, memalign,
390 alignment, size, retval,
82556ac0 391 LTTNG_UST_CALLER_IP());
8c06ba6f
MD
392 }
393 URCU_TLS(malloc_nesting)--;
9d34b226
SS
394 return retval;
395}
396
397int posix_memalign(void **memptr, size_t alignment, size_t size)
398{
9d34b226
SS
399 int retval;
400
8c06ba6f 401 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
402 if (cur_alloc.posix_memalign == NULL) {
403 lookup_all_symbols();
404 if (cur_alloc.posix_memalign == NULL) {
9d34b226 405 fprintf(stderr, "posix_memalignwrap: unable to find posix_memalign\n");
2594a5b4 406 abort();
9d34b226
SS
407 }
408 }
2594a5b4 409 retval = cur_alloc.posix_memalign(memptr, alignment, size);
8c06ba6f 410 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa
AB
411 tracepoint(lttng_ust_libc, posix_memalign,
412 *memptr, alignment, size,
82556ac0 413 retval, LTTNG_UST_CALLER_IP());
8c06ba6f
MD
414 }
415 URCU_TLS(malloc_nesting)--;
9d34b226
SS
416 return retval;
417}
2594a5b4 418
11dc9288
MD
419static
420void lttng_ust_fixup_malloc_nesting_tls(void)
421{
422 asm volatile ("" : : "m" (URCU_TLS(malloc_nesting)));
423}
424
2594a5b4
MD
425__attribute__((constructor))
426void lttng_ust_malloc_wrapper_init(void)
427{
428 /* Initialization already done */
429 if (cur_alloc.calloc) {
430 return;
431 }
11dc9288 432 lttng_ust_fixup_malloc_nesting_tls();
2594a5b4
MD
433 /*
434 * Ensure the allocator is in place before the process becomes
435 * multithreaded.
436 */
437 lookup_all_symbols();
438}
This page took 0.049108 seconds and 4 git commands to generate.