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