Test program for atexit registering in atexit handler.
[kopensolaris-gnu/glibc.git] / dlfcn / dlerror.c
1 /* Return error detail for failing <dlfcn.h> functions.
2    Copyright (C) 1995-2000,2002,2003,2004,2005 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C 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    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
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 the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <dlfcn.h>
21 #include <libintl.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <bits/libc-lock.h>
27 #include <ldsodefs.h>
28
29 #if !defined SHARED && defined IS_IN_libdl
30
31 char *
32 dlerror (void)
33 {
34   return __dlerror ();
35 }
36
37 #else
38
39 /* Type for storing results of dynamic loading actions.  */
40 struct dl_action_result
41   {
42     int errcode;
43     int returned;
44     bool malloced;
45     const char *objname;
46     const char *errstring;
47   };
48 static struct dl_action_result last_result;
49 static struct dl_action_result *static_buf;
50
51 /* This is the key for the thread specific memory.  */
52 static __libc_key_t key;
53 __libc_once_define (static, once);
54
55 /* Destructor for the thread-specific data.  */
56 static void init (void);
57 static void free_key_mem (void *mem);
58
59
60 char *
61 __dlerror (void)
62 {
63   char *buf = NULL;
64   struct dl_action_result *result;
65
66 # ifdef SHARED
67   if (__builtin_expect (_dlfcn_hook != NULL, 0))
68     return _dlfcn_hook->dlerror ();
69 # endif
70
71   /* If we have not yet initialized the buffer do it now.  */
72   __libc_once (once, init);
73
74   /* Get error string.  */
75   result = (struct dl_action_result *) __libc_getspecific (key);
76   if (result == NULL)
77     result = &last_result;
78
79   /* Test whether we already returned the string.  */
80   if (result->returned != 0)
81     {
82       /* We can now free the string.  */
83       if (result->errstring != NULL)
84         {
85           if (strcmp (result->errstring, "out of memory") != 0)
86             free ((char *) result->errstring);
87           result->errstring = NULL;
88         }
89     }
90   else if (result->errstring != NULL)
91     {
92       buf = (char *) result->errstring;
93       int n;
94       if (result->errcode == 0)
95         n = __asprintf (&buf, "%s%s%s",
96                         result->objname,
97                         result->objname[0] == '\0' ? "" : ": ",
98                         _(result->errstring));
99       else
100         n = __asprintf (&buf, "%s%s%s: %s",
101                         result->objname,
102                         result->objname[0] == '\0' ? "" : ": ",
103                         _(result->errstring),
104                         strerror (result->errcode));
105       if (n != -1)
106         {
107           /* We don't need the error string anymore.  */
108           if (strcmp (result->errstring, "out of memory") != 0)
109             free ((char *) result->errstring);
110           result->errstring = buf;
111         }
112
113       /* Mark the error as returned.  */
114       result->returned = 1;
115     }
116
117   return buf;
118 }
119 # ifdef SHARED
120 strong_alias (__dlerror, dlerror)
121 # endif
122
123 int
124 internal_function
125 _dlerror_run (void (*operate) (void *), void *args)
126 {
127   struct dl_action_result *result;
128
129   /* If we have not yet initialized the buffer do it now.  */
130   __libc_once (once, init);
131
132   /* Get error string and number.  */
133   if (static_buf != NULL)
134     result = static_buf;
135   else
136     {
137       /* We don't use the static buffer and so we have a key.  Use it
138          to get the thread-specific buffer.  */
139       result = __libc_getspecific (key);
140       if (result == NULL)
141         {
142           result = (struct dl_action_result *) calloc (1, sizeof (*result));
143           if (result == NULL)
144             /* We are out of memory.  Since this is no really critical
145                situation we carry on by using the global variable.
146                This might lead to conflicts between the threads but
147                they soon all will have memory problems.  */
148             result = &last_result;
149           else
150             /* Set the tsd.  */
151             __libc_setspecific (key, result);
152         }
153     }
154
155   if (result->errstring != NULL)
156     {
157       /* Free the error string from the last failed command.  This can
158          happen if `dlerror' was not run after an error was found.  */
159       if (result->malloced)
160         free ((char *) result->errstring);
161       result->errstring = NULL;
162     }
163
164   result->errcode = GLRO(dl_catch_error) (&result->objname, &result->errstring,
165                                           &result->malloced, operate, args);
166
167   /* If no error we mark that no error string is available.  */
168   result->returned = result->errstring == NULL;
169
170   return result->errstring != NULL;
171 }
172
173
174 /* Initialize buffers for results.  */
175 static void
176 init (void)
177 {
178   if (__libc_key_create (&key, free_key_mem))
179     /* Creating the key failed.  This means something really went
180        wrong.  In any case use a static buffer which is better than
181        nothing.  */
182     static_buf = &last_result;
183 }
184
185
186 static void
187 check_free (struct dl_action_result *rec)
188 {
189   if (rec->errstring != NULL
190       && strcmp (rec->errstring, "out of memory") != 0)
191     {
192       /* We can free the string only if the allocation happened in the
193          C library used by the dynamic linker.  This means, it is
194          always the C library in the base namespave.  */
195       struct link_map *map = NULL;
196       Dl_info info;
197       if (_dl_addr (check_free, &info, &map, NULL) != 0
198           && map != NULL && map->l_ns == 0)
199         free ((char *) rec->errstring);
200     }
201 }
202
203
204 static void
205 __attribute__ ((destructor))
206 fini (void)
207 {
208   check_free (&last_result);
209 }
210
211
212 /* Free the thread specific data, this is done if a thread terminates.  */
213 static void
214 free_key_mem (void *mem)
215 {
216   check_free ((struct dl_action_result *) mem);
217
218   free (mem);
219   __libc_setspecific (key, NULL);
220 }
221
222 # ifdef SHARED
223
224 struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
225 libdl_hidden_data_def (_dlfcn_hook)
226
227 # else
228
229 static struct dlfcn_hook _dlfcn_hooks =
230   {
231     .dlopen = __dlopen,
232     .dlclose = __dlclose,
233     .dlsym = __dlsym,
234     .dlvsym = __dlvsym,
235     .dlerror = __dlerror,
236     .dladdr = __dladdr,
237     .dladdr1 = __dladdr1,
238     .dlinfo = __dlinfo,
239     .dlmopen = __dlmopen
240   };
241
242 void
243 __libc_register_dlfcn_hook (struct link_map *map)
244 {
245   struct dlfcn_hook **hook;
246
247   hook = (struct dlfcn_hook **) __libc_dlsym_private (map, "_dlfcn_hook");
248   if (hook != NULL)
249     *hook = &_dlfcn_hooks;
250 }
251 # endif
252 #endif