X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;ds=sidebyside;f=src%2Fldso%2Fdlerror.c;h=dae0f3a9b24a93d01882fdafd4ed91cb29416375;hb=d055e6a45a17673b8dd3ec16e786bb2fe1700dd5;hp=c8c718ab3ccf5ec306f98c54ebb61ac295e4f24c;hpb=9b95fd0944e4206949e90633c3fed088202810ec;p=musl diff --git a/src/ldso/dlerror.c b/src/ldso/dlerror.c index c8c718ab..dae0f3a9 100644 --- a/src/ldso/dlerror.c +++ b/src/ldso/dlerror.c @@ -2,7 +2,13 @@ #include #include #include "pthread_impl.h" -#include "libc.h" +#include "dynlink.h" +#include "atomic.h" + +#define malloc __libc_malloc +#define calloc __libc_calloc +#define realloc __libc_realloc +#define free __libc_free char *dlerror() { @@ -16,21 +22,45 @@ char *dlerror() return s; } +/* Atomic singly-linked list, used to store list of thread-local dlerror + * buffers for deferred free. They cannot be freed at thread exit time + * because, by the time it's known they can be freed, the exiting thread + * is in a highly restrictive context where it cannot call (even the + * libc-internal) free. It also can't take locks; thus the atomic list. */ + +static void *volatile freebuf_queue; + void __dl_thread_cleanup(void) { pthread_t self = __pthread_self(); - if (self->dlerror_buf != (void *)-1) - free(self->dlerror_buf); + if (!self->dlerror_buf || self->dlerror_buf == (void *)-1) + return; + void *h; + do { + h = freebuf_queue; + *(void **)self->dlerror_buf = h; + } while (a_cas_p(&freebuf_queue, h, self->dlerror_buf) != h); } hidden void __dl_vseterr(const char *fmt, va_list ap) { + void **q; + do q = freebuf_queue; + while (q && a_cas_p(&freebuf_queue, q, 0) != q); + + while (q) { + void **p = *q; + free(q); + q = p; + } + va_list ap2; va_copy(ap2, ap); pthread_t self = __pthread_self(); if (self->dlerror_buf != (void *)-1) free(self->dlerror_buf); size_t len = vsnprintf(0, 0, fmt, ap2); + if (len < sizeof(void *)) len = sizeof(void *); va_end(ap2); char *buf = malloc(len+1); if (buf) { @@ -50,8 +80,6 @@ hidden void __dl_seterr(const char *fmt, ...) va_end(ap); } -hidden int __dl_invalid_handle(void *); - static int stub_invalid_handle(void *h) { __dl_seterr("Invalid library handle %p", (void *)h);