unified build scheme: add build.info files
[openssl.git] / crypto / dso / dso_dlfcn.c
1 /*
2  * Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL project
3  * 2000.
4  */
5 /* ====================================================================
6  * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This product includes cryptographic software written by Eric Young
54  * (eay@cryptsoft.com).  This product includes software written by Tim
55  * Hudson (tjh@cryptsoft.com).
56  *
57  */
58
59 /*
60  * We need to do this early, because stdio.h includes the header files that
61  * handle _GNU_SOURCE and other similar macros.  Defining it later is simply
62  * too late, because those headers are protected from re- inclusion.
63  */
64 #ifndef _GNU_SOURCE
65 # define _GNU_SOURCE            /* make sure dladdr is declared */
66 #endif
67
68 #include <stdio.h>
69 #include "internal/cryptlib.h"
70 #include <openssl/dso.h>
71
72 #ifndef DSO_DLFCN
73 DSO_METHOD *DSO_METHOD_dlfcn(void)
74 {
75     return NULL;
76 }
77 #else
78
79 # ifdef HAVE_DLFCN_H
80 #  ifdef __osf__
81 #   define __EXTENSIONS__
82 #  endif
83 #  include <dlfcn.h>
84 #  define HAVE_DLINFO 1
85 #  if defined(_AIX) || defined(__CYGWIN__) || \
86      defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
87      (defined(__osf__) && !defined(RTLD_NEXT))     || \
88      (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
89         defined(__ANDROID__)
90 #   undef HAVE_DLINFO
91 #  endif
92 # endif
93
94 /* Part of the hack in "dlfcn_load" ... */
95 # define DSO_MAX_TRANSLATED_SIZE 256
96
97 static int dlfcn_load(DSO *dso);
98 static int dlfcn_unload(DSO *dso);
99 static void *dlfcn_bind_var(DSO *dso, const char *symname);
100 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
101 static char *dlfcn_name_converter(DSO *dso, const char *filename);
102 static char *dlfcn_merger(DSO *dso, const char *filespec1,
103                           const char *filespec2);
104 static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
105 static void *dlfcn_globallookup(const char *name);
106
107 static DSO_METHOD dso_meth_dlfcn = {
108     "OpenSSL 'dlfcn' shared library method",
109     dlfcn_load,
110     dlfcn_unload,
111     dlfcn_bind_var,
112     dlfcn_bind_func,
113     NULL,                       /* ctrl */
114     dlfcn_name_converter,
115     dlfcn_merger,
116     NULL,                       /* init */
117     NULL,                       /* finish */
118     dlfcn_pathbyaddr,
119     dlfcn_globallookup
120 };
121
122 DSO_METHOD *DSO_METHOD_dlfcn(void)
123 {
124     return (&dso_meth_dlfcn);
125 }
126
127 /*
128  * Prior to using the dlopen() function, we should decide on the flag we
129  * send. There's a few different ways of doing this and it's a messy
130  * venn-diagram to match up which platforms support what. So as we don't have
131  * autoconf yet, I'm implementing a hack that could be hacked further
132  * relatively easily to deal with cases as we find them. Initially this is to
133  * cope with OpenBSD.
134  */
135 # if defined(__OpenBSD__) || defined(__NetBSD__)
136 #  ifdef DL_LAZY
137 #   define DLOPEN_FLAG DL_LAZY
138 #  else
139 #   ifdef RTLD_NOW
140 #    define DLOPEN_FLAG RTLD_NOW
141 #   else
142 #    define DLOPEN_FLAG 0
143 #   endif
144 #  endif
145 # else
146 #  define DLOPEN_FLAG RTLD_NOW  /* Hope this works everywhere else */
147 # endif
148
149 /*
150  * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
151  * (void*) returned from dlopen().
152  */
153
154 static int dlfcn_load(DSO *dso)
155 {
156     void *ptr = NULL;
157     /* See applicable comments in dso_dl.c */
158     char *filename = DSO_convert_filename(dso, NULL);
159     int flags = DLOPEN_FLAG;
160
161     if (filename == NULL) {
162         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
163         goto err;
164     }
165 # ifdef RTLD_GLOBAL
166     if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
167         flags |= RTLD_GLOBAL;
168 # endif
169     ptr = dlopen(filename, flags);
170     if (ptr == NULL) {
171         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
172         ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
173         goto err;
174     }
175     if (!sk_void_push(dso->meth_data, (char *)ptr)) {
176         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
177         goto err;
178     }
179     /* Success */
180     dso->loaded_filename = filename;
181     return (1);
182  err:
183     /* Cleanup! */
184     OPENSSL_free(filename);
185     if (ptr != NULL)
186         dlclose(ptr);
187     return (0);
188 }
189
190 static int dlfcn_unload(DSO *dso)
191 {
192     void *ptr;
193     if (dso == NULL) {
194         DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
195         return (0);
196     }
197     if (sk_void_num(dso->meth_data) < 1)
198         return (1);
199     ptr = sk_void_pop(dso->meth_data);
200     if (ptr == NULL) {
201         DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
202         /*
203          * Should push the value back onto the stack in case of a retry.
204          */
205         sk_void_push(dso->meth_data, ptr);
206         return (0);
207     }
208     /* For now I'm not aware of any errors associated with dlclose() */
209     dlclose(ptr);
210     return (1);
211 }
212
213 static void *dlfcn_bind_var(DSO *dso, const char *symname)
214 {
215     void *ptr, *sym;
216
217     if ((dso == NULL) || (symname == NULL)) {
218         DSOerr(DSO_F_DLFCN_BIND_VAR, ERR_R_PASSED_NULL_PARAMETER);
219         return (NULL);
220     }
221     if (sk_void_num(dso->meth_data) < 1) {
222         DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_STACK_ERROR);
223         return (NULL);
224     }
225     ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
226     if (ptr == NULL) {
227         DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_NULL_HANDLE);
228         return (NULL);
229     }
230     sym = dlsym(ptr, symname);
231     if (sym == NULL) {
232         DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_SYM_FAILURE);
233         ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
234         return (NULL);
235     }
236     return (sym);
237 }
238
239 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
240 {
241     void *ptr;
242     union {
243         DSO_FUNC_TYPE sym;
244         void *dlret;
245     } u;
246
247     if ((dso == NULL) || (symname == NULL)) {
248         DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
249         return (NULL);
250     }
251     if (sk_void_num(dso->meth_data) < 1) {
252         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
253         return (NULL);
254     }
255     ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
256     if (ptr == NULL) {
257         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
258         return (NULL);
259     }
260     u.dlret = dlsym(ptr, symname);
261     if (u.dlret == NULL) {
262         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
263         ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
264         return (NULL);
265     }
266     return u.sym;
267 }
268
269 static char *dlfcn_merger(DSO *dso, const char *filespec1,
270                           const char *filespec2)
271 {
272     char *merged;
273
274     if (!filespec1 && !filespec2) {
275         DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
276         return (NULL);
277     }
278     /*
279      * If the first file specification is a rooted path, it rules. same goes
280      * if the second file specification is missing.
281      */
282     if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
283         merged = OPENSSL_malloc(strlen(filespec1) + 1);
284         if (merged == NULL) {
285             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
286             return (NULL);
287         }
288         strcpy(merged, filespec1);
289     }
290     /*
291      * If the first file specification is missing, the second one rules.
292      */
293     else if (!filespec1) {
294         merged = OPENSSL_malloc(strlen(filespec2) + 1);
295         if (merged == NULL) {
296             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
297             return (NULL);
298         }
299         strcpy(merged, filespec2);
300     } else {
301         /*
302          * This part isn't as trivial as it looks.  It assumes that the
303          * second file specification really is a directory, and makes no
304          * checks whatsoever.  Therefore, the result becomes the
305          * concatenation of filespec2 followed by a slash followed by
306          * filespec1.
307          */
308         int spec2len, len;
309
310         spec2len = strlen(filespec2);
311         len = spec2len + strlen(filespec1);
312
313         if (spec2len && filespec2[spec2len - 1] == '/') {
314             spec2len--;
315             len--;
316         }
317         merged = OPENSSL_malloc(len + 2);
318         if (merged == NULL) {
319             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
320             return (NULL);
321         }
322         strcpy(merged, filespec2);
323         merged[spec2len] = '/';
324         strcpy(&merged[spec2len + 1], filespec1);
325     }
326     return (merged);
327 }
328
329 # ifdef OPENSSL_SYS_MACOSX
330 #  define DSO_ext ".dylib"
331 #  define DSO_extlen 6
332 # else
333 #  define DSO_ext ".so"
334 #  define DSO_extlen 3
335 # endif
336
337 static char *dlfcn_name_converter(DSO *dso, const char *filename)
338 {
339     char *translated;
340     int len, rsize, transform;
341
342     len = strlen(filename);
343     rsize = len + 1;
344     transform = (strstr(filename, "/") == NULL);
345     if (transform) {
346         /* We will convert this to "%s.so" or "lib%s.so" etc */
347         rsize += DSO_extlen;    /* The length of ".so" */
348         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
349             rsize += 3;         /* The length of "lib" */
350     }
351     translated = OPENSSL_malloc(rsize);
352     if (translated == NULL) {
353         DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
354         return (NULL);
355     }
356     if (transform) {
357         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
358             sprintf(translated, "lib%s" DSO_ext, filename);
359         else
360             sprintf(translated, "%s" DSO_ext, filename);
361     } else
362         sprintf(translated, "%s", filename);
363     return (translated);
364 }
365
366 # ifdef __sgi
367 /*-
368 This is a quote from IRIX manual for dladdr(3c):
369
370      <dlfcn.h> does not contain a prototype for dladdr or definition of
371      Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
372      but contains no dladdr prototype and no IRIX library contains an
373      implementation.  Write your own declaration based on the code below.
374
375      The following code is dependent on internal interfaces that are not
376      part of the IRIX compatibility guarantee; however, there is no future
377      intention to change this interface, so on a practical level, the code
378      below is safe to use on IRIX.
379 */
380 #  include <rld_interface.h>
381 #  ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
382 #   define _RLD_INTERFACE_DLFCN_H_DLADDR
383 typedef struct Dl_info {
384     const char *dli_fname;
385     void *dli_fbase;
386     const char *dli_sname;
387     void *dli_saddr;
388     int dli_version;
389     int dli_reserved1;
390     long dli_reserved[4];
391 } Dl_info;
392 #  else
393 typedef struct Dl_info Dl_info;
394 #  endif
395 #  define _RLD_DLADDR             14
396
397 static int dladdr(void *address, Dl_info *dl)
398 {
399     void *v;
400     v = _rld_new_interface(_RLD_DLADDR, address, dl);
401     return (int)v;
402 }
403 # endif                         /* __sgi */
404
405 static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
406 {
407 # ifdef HAVE_DLINFO
408     Dl_info dli;
409     int len;
410
411     if (addr == NULL) {
412         union {
413             int (*f) (void *, char *, int);
414             void *p;
415         } t = {
416             dlfcn_pathbyaddr
417         };
418         addr = t.p;
419     }
420
421     if (dladdr(addr, &dli)) {
422         len = (int)strlen(dli.dli_fname);
423         if (sz <= 0)
424             return len + 1;
425         if (len >= sz)
426             len = sz - 1;
427         memcpy(path, dli.dli_fname, len);
428         path[len++] = 0;
429         return len;
430     }
431
432     ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
433 # endif
434     return -1;
435 }
436
437 static void *dlfcn_globallookup(const char *name)
438 {
439     void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
440
441     if (handle) {
442         ret = dlsym(handle, name);
443         dlclose(handle);
444     }
445
446     return ret;
447 }
448 #endif                          /* DSO_DLFCN */