14bd322fb87ff634f90d54b87439870c727b1083
[dragonfly.git] / crypto / openssl / crypto / dso / dso_dlfcn.c
1 /* dso_dlfcn.c -*- mode:C; c-file-style: "eay" -*- */
2 /* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL
3  * project 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 /* We need to do this early, because stdio.h includes the header files
60    that handle _GNU_SOURCE and other similar macros.  Defining it later
61    is simply too late, because those headers are protected from re-
62    inclusion.  */
63 #ifdef __linux
64 # ifndef _GNU_SOURCE
65 #  define _GNU_SOURCE   /* make sure dladdr is declared */
66 # endif
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(__OpenBSD__) && !defined(RTLD_SELF))
89 #  undef HAVE_DLINFO
90 # endif
91 #endif
92
93 /* Part of the hack in "dlfcn_load" ... */
94 #define DSO_MAX_TRANSLATED_SIZE 256
95
96 static int dlfcn_load(DSO *dso);
97 static int dlfcn_unload(DSO *dso);
98 static void *dlfcn_bind_var(DSO *dso, const char *symname);
99 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
100 #if 0
101 static int dlfcn_unbind(DSO *dso, char *symname, void *symptr);
102 static int dlfcn_init(DSO *dso);
103 static int dlfcn_finish(DSO *dso);
104 static long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg);
105 #endif
106 static char *dlfcn_name_converter(DSO *dso, const char *filename);
107 static char *dlfcn_merger(DSO *dso, const char *filespec1,
108         const char *filespec2);
109 static int dlfcn_pathbyaddr(void *addr,char *path,int sz);
110 static void *dlfcn_globallookup(const char *name);
111
112 static DSO_METHOD dso_meth_dlfcn = {
113         "OpenSSL 'dlfcn' shared library method",
114         dlfcn_load,
115         dlfcn_unload,
116         dlfcn_bind_var,
117         dlfcn_bind_func,
118 /* For now, "unbind" doesn't exist */
119 #if 0
120         NULL, /* unbind_var */
121         NULL, /* unbind_func */
122 #endif
123         NULL, /* ctrl */
124         dlfcn_name_converter,
125         dlfcn_merger,
126         NULL, /* init */
127         NULL, /* finish */
128         dlfcn_pathbyaddr,
129         dlfcn_globallookup
130         };
131
132 DSO_METHOD *DSO_METHOD_dlfcn(void)
133         {
134         return(&dso_meth_dlfcn);
135         }
136
137 /* Prior to using the dlopen() function, we should decide on the flag
138  * we send. There's a few different ways of doing this and it's a
139  * messy venn-diagram to match up which platforms support what. So
140  * as we don't have autoconf yet, I'm implementing a hack that could
141  * be hacked further relatively easily to deal with cases as we find
142  * them. Initially this is to cope with OpenBSD. */
143 #if defined(__OpenBSD__) || defined(__NetBSD__)
144 #       ifdef DL_LAZY
145 #               define DLOPEN_FLAG DL_LAZY
146 #       else
147 #               ifdef RTLD_NOW
148 #                       define DLOPEN_FLAG RTLD_NOW
149 #               else
150 #                       define DLOPEN_FLAG 0
151 #               endif
152 #       endif
153 #else
154 #       ifdef OPENSSL_SYS_SUNOS
155 #               define DLOPEN_FLAG 1
156 #       else
157 #               define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
158 #       endif
159 #endif
160
161 /* For this DSO_METHOD, our meth_data STACK will contain;
162  * (i) the handle (void*) returned from dlopen().
163  */
164
165 static int dlfcn_load(DSO *dso)
166         {
167         void *ptr = NULL;
168         /* See applicable comments in dso_dl.c */
169         char *filename = DSO_convert_filename(dso, NULL);
170         int flags = DLOPEN_FLAG;
171
172         if(filename == NULL)
173                 {
174                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_NO_FILENAME);
175                 goto err;
176                 }
177
178 #ifdef RTLD_GLOBAL
179         if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
180                 flags |= RTLD_GLOBAL;
181 #endif
182         ptr = dlopen(filename, flags);
183         if(ptr == NULL)
184                 {
185                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_LOAD_FAILED);
186                 ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
187                 goto err;
188                 }
189         if(!sk_void_push(dso->meth_data, (char *)ptr))
190                 {
191                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_STACK_ERROR);
192                 goto err;
193                 }
194         /* Success */
195         dso->loaded_filename = filename;
196         return(1);
197 err:
198         /* Cleanup! */
199         if(filename != NULL)
200                 OPENSSL_free(filename);
201         if(ptr != NULL)
202                 dlclose(ptr);
203         return(0);
204 }
205
206 static int dlfcn_unload(DSO *dso)
207         {
208         void *ptr;
209         if(dso == NULL)
210                 {
211                 DSOerr(DSO_F_DLFCN_UNLOAD,ERR_R_PASSED_NULL_PARAMETER);
212                 return(0);
213                 }
214         if(sk_void_num(dso->meth_data) < 1)
215                 return(1);
216         ptr = sk_void_pop(dso->meth_data);
217         if(ptr == NULL)
218                 {
219                 DSOerr(DSO_F_DLFCN_UNLOAD,DSO_R_NULL_HANDLE);
220                 /* Should push the value back onto the stack in
221                  * case of a retry. */
222                 sk_void_push(dso->meth_data, ptr);
223                 return(0);
224                 }
225         /* For now I'm not aware of any errors associated with dlclose() */
226         dlclose(ptr);
227         return(1);
228         }
229
230 static void *dlfcn_bind_var(DSO *dso, const char *symname)
231         {
232         void *ptr, *sym;
233
234         if((dso == NULL) || (symname == NULL))
235                 {
236                 DSOerr(DSO_F_DLFCN_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
237                 return(NULL);
238                 }
239         if(sk_void_num(dso->meth_data) < 1)
240                 {
241                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_STACK_ERROR);
242                 return(NULL);
243                 }
244         ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
245         if(ptr == NULL)
246                 {
247                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_NULL_HANDLE);
248                 return(NULL);
249                 }
250         sym = dlsym(ptr, symname);
251         if(sym == NULL)
252                 {
253                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_SYM_FAILURE);
254                 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
255                 return(NULL);
256                 }
257         return(sym);
258         }
259
260 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
261         {
262         void *ptr;
263         union {
264                 DSO_FUNC_TYPE sym;
265                 void *dlret;
266         } u;
267
268         if((dso == NULL) || (symname == NULL))
269                 {
270                 DSOerr(DSO_F_DLFCN_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER);
271                 return(NULL);
272                 }
273         if(sk_void_num(dso->meth_data) < 1)
274                 {
275                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_STACK_ERROR);
276                 return(NULL);
277                 }
278         ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
279         if(ptr == NULL)
280                 {
281                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_NULL_HANDLE);
282                 return(NULL);
283                 }
284         u.dlret = dlsym(ptr, symname);
285         if(u.dlret == NULL)
286                 {
287                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_SYM_FAILURE);
288                 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
289                 return(NULL);
290                 }
291         return u.sym;
292         }
293
294 static char *dlfcn_merger(DSO *dso, const char *filespec1,
295         const char *filespec2)
296         {
297         char *merged;
298
299         if(!filespec1 && !filespec2)
300                 {
301                 DSOerr(DSO_F_DLFCN_MERGER,
302                                 ERR_R_PASSED_NULL_PARAMETER);
303                 return(NULL);
304                 }
305         /* If the first file specification is a rooted path, it rules.
306            same goes if the second file specification is missing. */
307         if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/'))
308                 {
309                 merged = OPENSSL_malloc(strlen(filespec1) + 1);
310                 if(!merged)
311                         {
312                         DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
313                         return(NULL);
314                         }
315                 strcpy(merged, filespec1);
316                 }
317         /* If the first file specification is missing, the second one rules. */
318         else if (!filespec1)
319                 {
320                 merged = OPENSSL_malloc(strlen(filespec2) + 1);
321                 if(!merged)
322                         {
323                         DSOerr(DSO_F_DLFCN_MERGER,
324                                 ERR_R_MALLOC_FAILURE);
325                         return(NULL);
326                         }
327                 strcpy(merged, filespec2);
328                 }
329         else
330                 /* This part isn't as trivial as it looks.  It assumes that
331                    the second file specification really is a directory, and
332                    makes no checks whatsoever.  Therefore, the result becomes
333                    the concatenation of filespec2 followed by a slash followed
334                    by filespec1. */
335                 {
336                 int spec2len, len;
337
338                 spec2len = strlen(filespec2);
339                 len = spec2len + (filespec1 ? strlen(filespec1) : 0);
340
341                 if(filespec2 && filespec2[spec2len - 1] == '/')
342                         {
343                         spec2len--;
344                         len--;
345                         }
346                 merged = OPENSSL_malloc(len + 2);
347                 if(!merged)
348                         {
349                         DSOerr(DSO_F_DLFCN_MERGER,
350                                 ERR_R_MALLOC_FAILURE);
351                         return(NULL);
352                         }
353                 strcpy(merged, filespec2);
354                 merged[spec2len] = '/';
355                 strcpy(&merged[spec2len + 1], filespec1);
356                 }
357         return(merged);
358         }
359
360 #ifdef OPENSSL_SYS_MACOSX
361 #define DSO_ext ".dylib"
362 #define DSO_extlen 6
363 #else
364 #define DSO_ext ".so"
365 #define DSO_extlen 3
366 #endif
367
368
369 static char *dlfcn_name_converter(DSO *dso, const char *filename)
370         {
371         char *translated;
372         int len, rsize, transform;
373
374         len = strlen(filename);
375         rsize = len + 1;
376         transform = (strstr(filename, "/") == NULL);
377         if(transform)
378                 {
379                 /* We will convert this to "%s.so" or "lib%s.so" etc */
380                 rsize += DSO_extlen;    /* The length of ".so" */
381                 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
382                         rsize += 3; /* The length of "lib" */
383                 }
384         translated = OPENSSL_malloc(rsize);
385         if(translated == NULL)
386                 {
387                 DSOerr(DSO_F_DLFCN_NAME_CONVERTER,
388                                 DSO_R_NAME_TRANSLATION_FAILED);
389                 return(NULL);
390                 }
391         if(transform)
392                 {
393                 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
394                         sprintf(translated, "lib%s" DSO_ext, filename);
395                 else
396                         sprintf(translated, "%s" DSO_ext, filename);
397                 }
398         else
399                 sprintf(translated, "%s", filename);
400         return(translated);
401         }
402
403 #ifdef __sgi
404 /*
405 This is a quote from IRIX manual for dladdr(3c):
406
407      <dlfcn.h> does not contain a prototype for dladdr or definition of
408      Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
409      but contains no dladdr prototype and no IRIX library contains an
410      implementation.  Write your own declaration based on the code below.
411
412      The following code is dependent on internal interfaces that are not
413      part of the IRIX compatibility guarantee; however, there is no future
414      intention to change this interface, so on a practical level, the code
415      below is safe to use on IRIX.
416 */
417 #include <rld_interface.h>
418 #ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
419 #define _RLD_INTERFACE_DLFCN_H_DLADDR
420 typedef struct Dl_info {
421     const char * dli_fname;
422     void       * dli_fbase;
423     const char * dli_sname;
424     void       * dli_saddr;
425     int          dli_version;
426     int          dli_reserved1;
427     long         dli_reserved[4];
428 } Dl_info;
429 #else
430 typedef struct Dl_info Dl_info;
431 #endif
432 #define _RLD_DLADDR             14
433
434 static int dladdr(void *address, Dl_info *dl)
435 {
436         void *v;
437         v = _rld_new_interface(_RLD_DLADDR,address,dl);
438         return (int)v;
439 }
440 #endif /* __sgi */
441
442 static int dlfcn_pathbyaddr(void *addr,char *path,int sz)
443         {
444 #ifdef HAVE_DLINFO
445         Dl_info dli;
446         int len;
447
448         if (addr == NULL)
449                 {
450                 union   { int(*f)(void*,char*,int); void *p; } t =
451                         { dlfcn_pathbyaddr };
452                 addr = t.p;
453                 }
454
455         if (dladdr(addr,&dli))
456                 {
457                 len = (int)strlen(dli.dli_fname);
458                 if (sz <= 0) return len+1;
459                 if (len >= sz) len=sz-1;
460                 memcpy(path,dli.dli_fname,len);
461                 path[len++]=0;
462                 return len;
463                 }
464
465         ERR_add_error_data(4, "dlfcn_pathbyaddr(): ", dlerror());
466 #endif
467         return -1;
468         }
469
470 static void *dlfcn_globallookup(const char *name)
471         {
472         void *ret = NULL,*handle = dlopen(NULL,RTLD_LAZY);
473         
474         if (handle)
475                 {
476                 ret = dlsym(handle,name);
477                 dlclose(handle);
478                 }
479
480         return ret;
481         }
482 #endif /* DSO_DLFCN */