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