master
 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
13char *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
31static void *volatile freebuf_queue;
32
33void __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
45hidden 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
75hidden 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
83static int stub_invalid_handle(void *h)
84{
85	__dl_seterr("Invalid library handle %p", (void *)h);
86	return 1;
87}
88
89weak_alias(stub_invalid_handle, __dl_invalid_handle);