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