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