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