(dlerror): Don't assume that __libc_internal_tsd_get is defined.
[kopensolaris-gnu/glibc.git] / elf / dlerror.c
1 /* Return error detail for failing <dlfcn.h> functions.
2    Copyright (C) 1995, 1996, 1997, 1998 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 Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <dlfcn.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <bits/libc-lock.h>
25 #include <elf/ldsodefs.h>
26
27
28 /* Type for storing results of dynamic loading actions.  */
29 struct dl_action_result
30   {
31     int errcode;
32     char *errstring;
33   };
34 static struct dl_action_result last_result;
35 static struct dl_action_result *static_buf;
36
37
38 /* This is the key for the thread specific memory.  */
39 static __libc_key_t key;
40
41 /* Destructor for the thread-specific data.  */
42 static void init (void);
43 static void free_key_mem (void *mem);
44
45
46 char *
47 dlerror (void)
48 {
49   static char *buf;
50   struct dl_action_result *result;
51
52   if (buf)
53     {
54       free (buf);
55       buf = NULL;
56     }
57
58   /* Get error string.  */
59   result = (struct dl_action_result *) __libc_getspecific (key);
60   if (result == NULL)
61     result = &last_result;
62
63   if (! result->errstring)
64     return NULL;
65
66   if (result->errcode == 0)
67     buf = result->errstring;
68   else
69     {
70       if (__asprintf (&buf, "%s: %s",
71                       result->errstring, strerror (result->errcode)) == -1)
72         buf = NULL;
73
74       /* We don't need the error string anymore.  */
75       free (result->errstring);
76     }
77
78   /* Reset the error indicator.  */
79   result->errstring = NULL;
80
81   return buf;
82 }
83
84 int
85 internal_function
86 _dlerror_run (void (*operate) (void *), void *args)
87 {
88   __libc_once_define (static, once);
89   struct dl_action_result *result;
90
91   /* If we have not yet initialized the buffer do it now.  */
92   __libc_once (once, init);
93
94   /* Get error string and number.  */
95   if (static_buf != NULL)
96     result = static_buf;
97   else
98     {
99       /* We don't use the static buffer and so we have a key.  Use it
100          to get the thread-specific buffer.  */
101       result = __libc_getspecific (key);
102       if (result == NULL)
103         {
104           result = (struct dl_action_result *) calloc (1, sizeof (*result));
105           if (result == NULL)
106             /* We are out of memory.  Since this is no really critical
107                situation we carry on by using the global variable.
108                This might lead to conflicts between the threads but
109                they soon all will have memory problems.  */
110             result = &last_result;
111           else
112             /* Set the tsd.  */
113             __libc_setspecific (key, result);
114         }
115     }
116
117   if (result->errstring != NULL)
118     /* Free the error string from the last failed command.  This can
119        happen if `dlerror' was not run after an error was found.  */
120     free (result->errstring);
121
122   result->errcode = _dl_catch_error (&result->errstring, operate, args);
123
124   return result->errstring != NULL;
125 }
126
127
128 /* Initialize buffers for results.  */
129 static void
130 init (void)
131 {
132   if (__libc_key_create (&key, free_key_mem))
133     /* Creating the key failed.  This means something really went
134        wrong.  In any case use a static buffer which is better than
135        nothing.  */
136     static_buf = &last_result;
137 }
138
139
140 /* Free the thread specific data, this is done if a thread terminates.  */
141 static void
142 free_key_mem (void *mem)
143 {
144   free (mem);
145   __libc_setspecific (key, NULL);
146 }