32386c6eaefa67dcd691eae4d26c0efde85dbf1b
[openssl.git] / 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 #ifndef _GNU_SOURCE
64 # define _GNU_SOURCE    /* make sure dladdr is declared */
65 #endif
66
67 #include <stdio.h>
68 #include "cryptlib.h"
69 #include <openssl/dso.h>
70
71 #ifndef DSO_DLFCN
72 DSO_METHOD *DSO_METHOD_dlfcn(void)
73         {
74         return NULL;
75         }
76 #else
77
78 #ifdef HAVE_DLFCN_H
79 # ifdef __osf__
80 #  define __EXTENSIONS__
81 # endif
82 # include <dlfcn.h>
83 # define HAVE_DLINFO 1
84 # if defined(_AIX) || defined(__CYGWIN__) || \
85      defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
86      (defined(__osf__) && !defined(RTLD_NEXT))     || \
87      (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
88         defined(__ANDROID__)
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 #       define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
155 #endif
156
157 /* For this DSO_METHOD, our meth_data STACK will contain;
158  * (i) the handle (void*) returned from dlopen().
159  */
160
161 static int dlfcn_load(DSO *dso)
162         {
163         void *ptr = NULL;
164         /* See applicable comments in dso_dl.c */
165         char *filename = DSO_convert_filename(dso, NULL);
166         int flags = DLOPEN_FLAG;
167
168         if(filename == NULL)
169                 {
170                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_NO_FILENAME);
171                 goto err;
172                 }
173
174 #ifdef RTLD_GLOBAL
175         if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
176                 flags |= RTLD_GLOBAL;
177 #endif
178         ptr = dlopen(filename, flags);
179         if(ptr == NULL)
180                 {
181                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_LOAD_FAILED);
182                 ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
183                 goto err;
184                 }
185         if(!sk_void_push(dso->meth_data, (char *)ptr))
186                 {
187                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_STACK_ERROR);
188                 goto err;
189                 }
190         /* Success */
191         dso->loaded_filename = filename;
192         return(1);
193 err:
194         /* Cleanup! */
195         if(filename != NULL)
196                 OPENSSL_free(filename);
197         if(ptr != NULL)
198                 dlclose(ptr);
199         return(0);
200 }
201
202 static int dlfcn_unload(DSO *dso)
203         {
204         void *ptr;
205         if(dso == NULL)
206                 {
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                 {
215                 DSOerr(DSO_F_DLFCN_UNLOAD,DSO_R_NULL_HANDLE);
216                 /* Should push the value back onto the stack in
217                  * case of a retry. */
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                 {
232                 DSOerr(DSO_F_DLFCN_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
233                 return(NULL);
234                 }
235         if(sk_void_num(dso->meth_data) < 1)
236                 {
237                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_STACK_ERROR);
238                 return(NULL);
239                 }
240         ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
241         if(ptr == NULL)
242                 {
243                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_NULL_HANDLE);
244                 return(NULL);
245                 }
246         sym = dlsym(ptr, symname);
247         if(sym == NULL)
248                 {
249                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_SYM_FAILURE);
250                 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
251                 return(NULL);
252                 }
253         return(sym);
254         }
255
256 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
257         {
258         void *ptr;
259         union {
260                 DSO_FUNC_TYPE sym;
261                 void *dlret;
262         } u;
263
264         if((dso == NULL) || (symname == NULL))
265                 {
266                 DSOerr(DSO_F_DLFCN_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER);
267                 return(NULL);
268                 }
269         if(sk_void_num(dso->meth_data) < 1)
270                 {
271                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_STACK_ERROR);
272                 return(NULL);
273                 }
274         ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
275         if(ptr == NULL)
276                 {
277                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_NULL_HANDLE);
278                 return(NULL);
279                 }
280         u.dlret = dlsym(ptr, symname);
281         if(u.dlret == NULL)
282                 {
283                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_SYM_FAILURE);
284                 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
285                 return(NULL);
286                 }
287         return u.sym;
288         }
289
290 static char *dlfcn_merger(DSO *dso, const char *filespec1,
291         const char *filespec2)
292         {
293         char *merged;
294
295         if(!filespec1 && !filespec2)
296                 {
297                 DSOerr(DSO_F_DLFCN_MERGER,
298                                 ERR_R_PASSED_NULL_PARAMETER);
299                 return(NULL);
300                 }
301         /* If the first file specification is a rooted path, it rules.
302            same goes if the second file specification is missing. */
303         if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/'))
304                 {
305                 merged = OPENSSL_malloc(strlen(filespec1) + 1);
306                 if(!merged)
307                         {
308                         DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
309                         return(NULL);
310                         }
311                 strcpy(merged, filespec1);
312                 }
313         /* If the first file specification is missing, the second one rules. */
314         else if (!filespec1)
315                 {
316                 merged = OPENSSL_malloc(strlen(filespec2) + 1);
317                 if(!merged)
318                         {
319                         DSOerr(DSO_F_DLFCN_MERGER,
320                                 ERR_R_MALLOC_FAILURE);
321                         return(NULL);
322                         }
323                 strcpy(merged, filespec2);
324                 }
325         else
326                 /* This part isn't as trivial as it looks.  It assumes that
327                    the second file specification really is a directory, and
328                    makes no checks whatsoever.  Therefore, the result becomes
329                    the concatenation of filespec2 followed by a slash followed
330                    by filespec1. */
331                 {
332                 int spec2len, len;
333
334                 spec2len = strlen(filespec2);
335                 len = spec2len + (filespec1 ? strlen(filespec1) : 0);
336
337                 if(filespec2 && filespec2[spec2len - 1] == '/')
338                         {
339                         spec2len--;
340                         len--;
341                         }
342                 merged = OPENSSL_malloc(len + 2);
343                 if(!merged)
344                         {
345                         DSOerr(DSO_F_DLFCN_MERGER,
346                                 ERR_R_MALLOC_FAILURE);
347                         return(NULL);
348                         }
349                 strcpy(merged, filespec2);
350                 merged[spec2len] = '/';
351                 strcpy(&merged[spec2len + 1], filespec1);
352                 }
353         return(merged);
354         }
355
356 #ifdef OPENSSL_SYS_MACOSX
357 #define DSO_ext ".dylib"
358 #define DSO_extlen 6
359 #else
360 #define DSO_ext ".so"
361 #define DSO_extlen 3
362 #endif
363
364
365 static char *dlfcn_name_converter(DSO *dso, const char *filename)
366         {
367         char *translated;
368         int len, rsize, transform;
369
370         len = strlen(filename);
371         rsize = len + 1;
372         transform = (strstr(filename, "/") == NULL);
373         if(transform)
374                 {
375                 /* We will convert this to "%s.so" or "lib%s.so" etc */
376                 rsize += DSO_extlen;    /* The length of ".so" */
377                 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
378                         rsize += 3; /* The length of "lib" */
379                 }
380         translated = OPENSSL_malloc(rsize);
381         if(translated == NULL)
382                 {
383                 DSOerr(DSO_F_DLFCN_NAME_CONVERTER,
384                                 DSO_R_NAME_TRANSLATION_FAILED);
385                 return(NULL);
386                 }
387         if(transform)
388                 {
389                 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
390                         sprintf(translated, "lib%s" DSO_ext, filename);
391                 else
392                         sprintf(translated, "%s" DSO_ext, filename);
393                 }
394         else
395                 sprintf(translated, "%s", filename);
396         return(translated);
397         }
398
399 #ifdef __sgi
400 /*
401 This is a quote from IRIX manual for dladdr(3c):
402
403      <dlfcn.h> does not contain a prototype for dladdr or definition of
404      Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
405      but contains no dladdr prototype and no IRIX library contains an
406      implementation.  Write your own declaration based on the code below.
407
408      The following code is dependent on internal interfaces that are not
409      part of the IRIX compatibility guarantee; however, there is no future
410      intention to change this interface, so on a practical level, the code
411      below is safe to use on IRIX.
412 */
413 #include <rld_interface.h>
414 #ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
415 #define _RLD_INTERFACE_DLFCN_H_DLADDR
416 typedef struct Dl_info {
417     const char * dli_fname;
418     void       * dli_fbase;
419     const char * dli_sname;
420     void       * dli_saddr;
421     int          dli_version;
422     int          dli_reserved1;
423     long         dli_reserved[4];
424 } Dl_info;
425 #else
426 typedef struct Dl_info Dl_info;
427 #endif
428 #define _RLD_DLADDR             14
429
430 static int dladdr(void *address, Dl_info *dl)
431 {
432         void *v;
433         v = _rld_new_interface(_RLD_DLADDR,address,dl);
434         return (int)v;
435 }
436 #endif /* __sgi */
437
438 static int dlfcn_pathbyaddr(void *addr,char *path,int sz)
439         {
440 #ifdef HAVE_DLINFO
441         Dl_info dli;
442         int len;
443
444         if (addr == NULL)
445                 {
446                 union   { int(*f)(void*,char*,int); void *p; } t =
447                         { dlfcn_pathbyaddr };
448                 addr = t.p;
449                 }
450
451         if (dladdr(addr,&dli))
452                 {
453                 len = (int)strlen(dli.dli_fname);
454                 if (sz <= 0) return len+1;
455                 if (len >= sz) len=sz-1;
456                 memcpy(path,dli.dli_fname,len);
457                 path[len++]=0;
458                 return len;
459                 }
460
461         ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
462 #endif
463         return -1;
464         }
465
466 static void *dlfcn_globallookup(const char *name)
467         {
468         void *ret = NULL,*handle = dlopen(NULL,RTLD_LAZY);
469         
470         if (handle)
471                 {
472                 ret = dlsym(handle,name);
473                 dlclose(handle);
474                 }
475
476         return ret;
477         }
478 #endif /* DSO_DLFCN */