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