1#include <dlfcn.h>
2#include <stdlib.h>
3#include <stdarg.h>
4#include "pthread_impl.h"
5#include "dynlink.h"
6#include "atomic.h"
7
8char *dlerror()
9{
10 pthread_t self = __pthread_self();
11 if (!self->dlerror_flag) return 0;
12 self->dlerror_flag = 0;
13 char *s = self->dlerror_buf;
14 if (s == (void *)-1)
15 return "Dynamic linker failed to allocate memory for error message";
16 else
17 return s;
18}
19
20/* Atomic singly-linked list, used to store list of thread-local dlerror
21 * buffers for deferred free. They cannot be freed at thread exit time
22 * because, by the time it's known they can be freed, the exiting thread
23 * is in a highly restrictive context where it cannot call (even the
24 * libc-internal) free. It also can't take locks; thus the atomic list. */
25
26static void *volatile freebuf_queue;
27
28void __dl_thread_cleanup(void)
29{
30 pthread_t self = __pthread_self();
31 if (!self->dlerror_buf || self->dlerror_buf == (void *)-1)
32 return;
33 void *h;
34 do {
35 h = freebuf_queue;
36 *(void **)self->dlerror_buf = h;
37 } while (a_cas_p(&freebuf_queue, h, self->dlerror_buf) != h);
38}
39
40hidden void __dl_vseterr(const char *fmt, va_list ap)
41{
42 void **q;
43 do q = freebuf_queue;
44 while (q && a_cas_p(&freebuf_queue, q, 0) != q);
45
46 while (q) {
47 void **p = *q;
48 free(q);
49 q = p;
50 }
51
52 va_list ap2;
53 va_copy(ap2, ap);
54 pthread_t self = __pthread_self();
55 if (self->dlerror_buf != (void *)-1)
56 free(self->dlerror_buf);
57 size_t len = vsnprintf(0, 0, fmt, ap2);
58 if (len < sizeof(void *)) len = sizeof(void *);
59 va_end(ap2);
60 char *buf = malloc(len+1);
61 if (buf) {
62 vsnprintf(buf, len+1, fmt, ap);
63 } else {
64 buf = (void *)-1;
65 }
66 self->dlerror_buf = buf;
67 self->dlerror_flag = 1;
68}
69
70hidden void __dl_seterr(const char *fmt, ...)
71{
72 va_list ap;
73 va_start(ap, fmt);
74 __dl_vseterr(fmt, ap);
75 va_end(ap);
76}
77
78static int stub_invalid_handle(void *h)
79{
80 __dl_seterr("Invalid library handle %p", (void *)h);
81 return 1;
82}
83
84weak_alias(stub_invalid_handle, __dl_invalid_handle);