Initial Import
[profile/ivi/alsa-lib.git] / src / dlmisc.c
1 /**
2  * \file dlmisc.c
3  * \brief dynamic loader helpers
4  * \author Jaroslav Kysela <perex@perex.cz>
5  * \date 2001
6  *
7  * Dynamic loader helpers
8  */
9 /*
10  *  Dynamic loader helpers
11  *  Copyright (c) 2000 by Jaroslav Kysela <perex@perex.cz>
12  *
13  *
14  *   This library is free software; you can redistribute it and/or modify
15  *   it under the terms of the GNU Lesser General Public License as
16  *   published by the Free Software Foundation; either version 2.1 of
17  *   the License, or (at your option) any later version.
18  *
19  *   This program is distributed in the hope that it will be useful,
20  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *   GNU Lesser General Public License for more details.
23  *
24  *   You should have received a copy of the GNU Lesser General Public
25  *   License along with this library; if not, write to the Free Software
26  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
27  *
28  */
29
30 #include "list.h"
31 #include "local.h"
32 #ifdef HAVE_LIBPTHREAD
33 #include <pthread.h>
34 #endif
35
36 #ifndef DOC_HIDDEN
37 #ifndef PIC
38 struct snd_dlsym_link *snd_dlsym_start = NULL;
39 #endif
40 #endif
41
42 /**
43  * \brief Opens a dynamic library - ALSA wrapper for \c dlopen.
44  * \param name name of the library, similar to \c dlopen.
45  * \param mode mode flags, similar to \c dlopen.
46  * \return Library handle if successful, otherwise \c NULL.
47  *
48  * This function can emulate dynamic linking for the static build of
49  * the alsa-lib library. In that case, \p name is set to \c NULL.
50  */
51 void *snd_dlopen(const char *name, int mode)
52 {
53 #ifndef PIC
54         if (name == NULL)
55                 return &snd_dlsym_start;
56 #else
57 #ifdef HAVE_LIBDL
58         if (name == NULL) {
59                 static const char * self = NULL;
60                 if (self == NULL) {
61                         Dl_info dlinfo;
62                         if (dladdr(snd_dlopen, &dlinfo) > 0)
63                                 self = dlinfo.dli_fname;
64                 }
65                 name = self;
66         }
67 #endif
68 #endif
69 #ifdef HAVE_LIBDL
70         return dlopen(name, mode);
71 #else
72         return NULL;
73 #endif
74 }
75
76 /**
77  * \brief Closes a dynamic library - ALSA wrapper for \c dlclose.
78  * \param handle Library handle, similar to \c dlclose.
79  * \return Zero if successful, otherwise an error code.
80  *
81  * This function can emulate dynamic linking for the static build of
82  * the alsa-lib library.
83  */
84 int snd_dlclose(void *handle)
85 {
86 #ifndef PIC
87         if (handle == &snd_dlsym_start)
88                 return 0;
89 #endif
90 #ifdef HAVE_LIBDL
91         return dlclose(handle);
92 #else
93         return 0;
94 #endif
95 }
96
97 /**
98  * \brief Verifies a dynamically loaded symbol.
99  * \param handle Library handle, similar to \c dlsym.
100  * \param name Symbol name.
101  * \param version Version of the symbol.
102  * \return Zero is successful, otherwise a negative error code.
103  *
104  * This function checks that the symbol with the version appended to its name
105  * does exist in the library.
106  */
107 static int snd_dlsym_verify(void *handle, const char *name, const char *version)
108 {
109 #ifdef HAVE_LIBDL
110         int res;
111         char *vname;
112         
113         if (handle == NULL)
114                 return -EINVAL;
115         vname = alloca(1 + strlen(name) + strlen(version) + 1);
116         if (vname == NULL)
117                 return -ENOMEM;
118         vname[0] = '_';
119         strcpy(vname + 1, name);
120         strcat(vname, version);
121         res = dlsym(handle, vname) == NULL ? -ENOENT : 0;
122         // printf("dlsym verify: %i, vname = '%s'\n", res, vname);
123         if (res < 0)
124                 SNDERR("unable to verify version for symbol %s", name);
125         return res;
126 #else
127         return 0;
128 #endif
129 }
130
131 /**
132  * \brief Resolves a symbol from a dynamic library - ALSA wrapper for \c dlsym.
133  * \param handle Library handle, similar to \c dlsym.
134  * \param name Symbol name.
135  * \param version Version of the symbol.
136  *
137  * This function can emulate dynamic linking for the static build of
138  * the alsa-lib library.
139  *
140  * This special version of the \c dlsym function checks also the version
141  * of the symbol. A versioned symbol should be defined using the
142  * #SND_DLSYM_BUILD_VERSION macro.
143  */
144 void *snd_dlsym(void *handle, const char *name, const char *version)
145 {
146         int err;
147
148 #ifndef PIC
149         if (handle == &snd_dlsym_start) {
150                 /* it's the funny part: */
151                 /* we are looking for a symbol in a static library */
152                 struct snd_dlsym_link *link = snd_dlsym_start;
153                 while (link) {
154                         if (!strcmp(name, link->dlsym_name))
155                                 return (void *)link->dlsym_ptr;
156                         link = link->next;
157                 }
158                 return NULL;
159         }
160 #endif
161 #ifdef HAVE_LIBDL
162         if (version) {
163                 err = snd_dlsym_verify(handle, name, version);
164                 if (err < 0)
165                         return NULL;
166         }
167         return dlsym(handle, name);
168 #else
169         return NULL;
170 #endif
171 }
172
173 /*
174  * dlobj cache
175  */
176
177 #ifndef DOC_HIDDEN
178 struct dlobj_cache {
179         const char *lib;
180         const char *name;
181         void *dlobj;
182         void *func;
183         unsigned int refcnt;
184         struct list_head list;
185 };
186
187 #ifdef HAVE_LIBPTHREAD
188 static pthread_mutex_t snd_dlobj_mutex = PTHREAD_MUTEX_INITIALIZER;
189
190 static inline void snd_dlobj_lock(void)
191 {
192         pthread_mutex_lock(&snd_dlobj_mutex);
193 }
194
195 static inline void snd_dlobj_unlock(void)
196 {
197         pthread_mutex_unlock(&snd_dlobj_mutex);
198 }
199 #else
200 static inline void snd_dlobj_lock(void) {}
201 static inline void snd_dlobj_unlock(void) {}
202 #endif
203
204 static LIST_HEAD(pcm_dlobj_list);
205
206 void *snd_dlobj_cache_get(const char *lib, const char *name,
207                           const char *version, int verbose)
208 {
209         struct list_head *p;
210         struct dlobj_cache *c;
211         void *func, *dlobj = NULL;
212         int dlobj_close = 0;
213
214         snd_dlobj_lock();
215         list_for_each(p, &pcm_dlobj_list) {
216                 c = list_entry(p, struct dlobj_cache, list);
217                 if (c->lib && lib && strcmp(c->lib, lib) != 0)
218                         continue;
219                 if (!c->lib && lib)
220                         continue;
221                 if (!lib && c->lib)
222                         continue;
223                 dlobj = c->dlobj;
224                 if (strcmp(c->name, name) == 0) {
225                         c->refcnt++;
226                         func = c->func;
227                         snd_dlobj_unlock();
228                         return func;
229                 }
230         }
231         if (dlobj == NULL) {
232                 dlobj = snd_dlopen(lib, RTLD_NOW);
233                 if (dlobj == NULL) {
234                         if (verbose)
235                                 SNDERR("Cannot open shared library %s",
236                                                 lib ? lib : "[builtin]");
237                         snd_dlobj_unlock();
238                         return NULL;
239                 }
240                 dlobj_close = 1;
241         }
242         func = snd_dlsym(dlobj, name, version);
243         if (func == NULL) {
244                 if (verbose)
245                         SNDERR("symbol %s is not defined inside %s",
246                                         name, lib ? lib : "[builtin]");
247                 goto __err;
248         }
249         c = malloc(sizeof(*c));
250         if (! c)
251                 goto __err;
252         c->refcnt = 1;
253         c->lib = lib ? strdup(lib) : NULL;
254         c->name = strdup(name);
255         if ((lib && ! c->lib) || ! c->name) {
256                 free((void *)c->name);
257                 free((void *)c->lib);
258                 free(c);
259               __err:
260                 if (dlobj_close)
261                         snd_dlclose(dlobj);
262                 snd_dlobj_unlock();
263                 return NULL;
264         }
265         c->dlobj = dlobj;
266         c->func = func;
267         list_add_tail(&c->list, &pcm_dlobj_list);
268         snd_dlobj_unlock();
269         return func;
270 }
271
272 int snd_dlobj_cache_put(void *func)
273 {
274         struct list_head *p;
275         struct dlobj_cache *c;
276         unsigned int refcnt;
277
278         snd_dlobj_lock();
279         list_for_each(p, &pcm_dlobj_list) {
280                 c = list_entry(p, struct dlobj_cache, list);
281                 if (c->func == func) {
282                         refcnt = c->refcnt;
283                         if (c->refcnt > 0)
284                                 c->refcnt--;
285                         snd_dlobj_unlock();
286                         return refcnt == 1 ? 0 : -EINVAL;
287                 }
288         }
289         snd_dlobj_unlock();
290         return -ENOENT;
291 }
292
293 void snd_dlobj_cache_cleanup(void)
294 {
295         struct list_head *p, *npos;
296         struct dlobj_cache *c;
297
298         snd_dlobj_lock();
299         list_for_each_safe(p, npos, &pcm_dlobj_list) {
300                 c = list_entry(p, struct dlobj_cache, list);
301                 if (c->refcnt == 0) {
302                         list_del(p);
303                         snd_dlclose(c->dlobj);
304                         free((void *)c->name); /* shut up gcc warning */
305                         free((void *)c->lib); /* shut up gcc warning */
306                         free(c);
307                 }
308         }
309         snd_dlobj_unlock();
310 }
311 #endif