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