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