Branch data Line data Source code
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 : : # 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 : :
134 : 398 : DSO_METHOD *DSO_METHOD_dlfcn(void)
135 : : {
136 : 398 : 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 : 3154 : static int dlfcn_load(DSO *dso)
168 : : {
169 : 3154 : void *ptr = NULL;
170 : : /* See applicable comments in dso_dl.c */
171 : 3154 : char *filename = DSO_convert_filename(dso, NULL);
172 : 3154 : int flags = DLOPEN_FLAG;
173 : :
174 [ - + ]: 3154 : if(filename == NULL)
175 : : {
176 : 0 : DSOerr(DSO_F_DLFCN_LOAD,DSO_R_NO_FILENAME);
177 : 0 : goto err;
178 : : }
179 : :
180 : : #ifdef RTLD_GLOBAL
181 [ - + ]: 3154 : if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
182 : 0 : flags |= RTLD_GLOBAL;
183 : : #endif
184 : 3154 : ptr = dlopen(filename, flags);
185 [ + - ]: 3154 : if(ptr == NULL)
186 : : {
187 : 3154 : DSOerr(DSO_F_DLFCN_LOAD,DSO_R_LOAD_FAILED);
188 : 3154 : ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
189 : 3154 : goto err;
190 : : }
191 [ # # ]: 0 : if(!sk_void_push(dso->meth_data, (char *)ptr))
192 : : {
193 : 0 : DSOerr(DSO_F_DLFCN_LOAD,DSO_R_STACK_ERROR);
194 : 0 : goto err;
195 : : }
196 : : /* Success */
197 : 0 : dso->loaded_filename = filename;
198 : 0 : return(1);
199 : : err:
200 : : /* Cleanup! */
201 [ + - ]: 3154 : if(filename != NULL)
202 : 3154 : OPENSSL_free(filename);
203 [ - + ]: 3154 : if(ptr != NULL)
204 : 0 : dlclose(ptr);
205 : : return(0);
206 : : }
207 : :
208 : 3154 : static int dlfcn_unload(DSO *dso)
209 : : {
210 : : void *ptr;
211 [ - + ]: 3154 : if(dso == NULL)
212 : : {
213 : 0 : DSOerr(DSO_F_DLFCN_UNLOAD,ERR_R_PASSED_NULL_PARAMETER);
214 : 0 : return(0);
215 : : }
216 [ - + ]: 3154 : if(sk_void_num(dso->meth_data) < 1)
217 : : return(1);
218 : 0 : ptr = sk_void_pop(dso->meth_data);
219 [ # # ]: 0 : if(ptr == NULL)
220 : : {
221 : 0 : DSOerr(DSO_F_DLFCN_UNLOAD,DSO_R_NULL_HANDLE);
222 : : /* Should push the value back onto the stack in
223 : : * case of a retry. */
224 : 0 : sk_void_push(dso->meth_data, ptr);
225 : 0 : return(0);
226 : : }
227 : : /* For now I'm not aware of any errors associated with dlclose() */
228 : 0 : dlclose(ptr);
229 : 0 : return(1);
230 : : }
231 : :
232 : 0 : static void *dlfcn_bind_var(DSO *dso, const char *symname)
233 : : {
234 : : void *ptr, *sym;
235 : :
236 [ # # ]: 0 : if((dso == NULL) || (symname == NULL))
237 : : {
238 : 0 : DSOerr(DSO_F_DLFCN_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
239 : 0 : return(NULL);
240 : : }
241 [ # # ]: 0 : if(sk_void_num(dso->meth_data) < 1)
242 : : {
243 : 0 : DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_STACK_ERROR);
244 : 0 : return(NULL);
245 : : }
246 : 0 : ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
247 [ # # ]: 0 : if(ptr == NULL)
248 : : {
249 : 0 : DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_NULL_HANDLE);
250 : 0 : return(NULL);
251 : : }
252 : 0 : sym = dlsym(ptr, symname);
253 [ # # ]: 0 : if(sym == NULL)
254 : : {
255 : 0 : DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_SYM_FAILURE);
256 : 0 : ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
257 : 0 : return(NULL);
258 : : }
259 : : return(sym);
260 : : }
261 : :
262 : 0 : 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 [ # # ]: 0 : if((dso == NULL) || (symname == NULL))
271 : : {
272 : 0 : DSOerr(DSO_F_DLFCN_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER);
273 : 0 : return(NULL);
274 : : }
275 [ # # ]: 0 : if(sk_void_num(dso->meth_data) < 1)
276 : : {
277 : 0 : DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_STACK_ERROR);
278 : 0 : return(NULL);
279 : : }
280 : 0 : ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
281 [ # # ]: 0 : if(ptr == NULL)
282 : : {
283 : 0 : DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_NULL_HANDLE);
284 : 0 : return(NULL);
285 : : }
286 : 0 : u.dlret = dlsym(ptr, symname);
287 [ # # ]: 0 : if(u.dlret == NULL)
288 : : {
289 : 0 : DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_SYM_FAILURE);
290 : 0 : ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
291 : 0 : return(NULL);
292 : : }
293 : 0 : return u.sym;
294 : : }
295 : :
296 : 0 : static char *dlfcn_merger(DSO *dso, const char *filespec1,
297 : : const char *filespec2)
298 : : {
299 : : char *merged;
300 : :
301 [ # # ]: 0 : if(!filespec1 && !filespec2)
302 : : {
303 : 0 : DSOerr(DSO_F_DLFCN_MERGER,
304 : : ERR_R_PASSED_NULL_PARAMETER);
305 : 0 : 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 [ # # ][ # # ]: 0 : if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/'))
[ # # ]
310 : : {
311 : 0 : merged = OPENSSL_malloc(strlen(filespec1) + 1);
312 [ # # ]: 0 : if(!merged)
313 : : {
314 : 0 : DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
315 : 0 : return(NULL);
316 : : }
317 : : strcpy(merged, filespec1);
318 : : }
319 : : /* If the first file specification is missing, the second one rules. */
320 [ # # ]: 0 : else if (!filespec1)
321 : : {
322 : 0 : merged = OPENSSL_malloc(strlen(filespec2) + 1);
323 [ # # ]: 0 : if(!merged)
324 : : {
325 : 0 : DSOerr(DSO_F_DLFCN_MERGER,
326 : : ERR_R_MALLOC_FAILURE);
327 : 0 : 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 : 0 : spec2len = strlen(filespec2);
341 [ # # ]: 0 : len = spec2len + (filespec1 ? strlen(filespec1) : 0);
342 : :
343 [ # # ][ # # ]: 0 : if(filespec2 && filespec2[spec2len - 1] == '/')
344 : : {
345 : 0 : spec2len--;
346 : 0 : len--;
347 : : }
348 : 0 : merged = OPENSSL_malloc(len + 2);
349 [ # # ]: 0 : if(!merged)
350 : : {
351 : 0 : DSOerr(DSO_F_DLFCN_MERGER,
352 : : ERR_R_MALLOC_FAILURE);
353 : 0 : return(NULL);
354 : : }
355 : : strcpy(merged, filespec2);
356 : 0 : merged[spec2len] = '/';
357 : 0 : strcpy(&merged[spec2len + 1], filespec1);
358 : : }
359 : 0 : 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 : 3154 : static char *dlfcn_name_converter(DSO *dso, const char *filename)
372 : : {
373 : : char *translated;
374 : : int len, rsize, transform;
375 : :
376 : 3154 : len = strlen(filename);
377 : 3154 : rsize = len + 1;
378 : 3154 : transform = (strstr(filename, "/") == NULL);
379 [ + - ]: 3154 : if(transform)
380 : : {
381 : : /* We will convert this to "%s.so" or "lib%s.so" etc */
382 : 3154 : rsize += DSO_extlen; /* The length of ".so" */
383 [ + + ]: 3154 : if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
384 : 2873 : rsize += 3; /* The length of "lib" */
385 : : }
386 : 3154 : translated = OPENSSL_malloc(rsize);
387 [ - + ]: 3154 : if(translated == NULL)
388 : : {
389 : 0 : DSOerr(DSO_F_DLFCN_NAME_CONVERTER,
390 : : DSO_R_NAME_TRANSLATION_FAILED);
391 : 0 : return(NULL);
392 : : }
393 [ + - ]: 3154 : if(transform)
394 : : {
395 [ + + ]: 3154 : if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
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 : 3154 : 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 : 0 : static int dlfcn_pathbyaddr(void *addr,char *path,int sz)
445 : : {
446 : : #ifdef HAVE_DLINFO
447 : : Dl_info dli;
448 : : int len;
449 : :
450 [ # # ]: 0 : if (addr == NULL)
451 : : {
452 : 0 : union { int(*f)(void*,char*,int); void *p; } t =
453 : : { dlfcn_pathbyaddr };
454 : 0 : addr = t.p;
455 : : }
456 : :
457 [ # # ]: 0 : if (dladdr(addr,&dli))
458 : : {
459 : 0 : len = (int)strlen(dli.dli_fname);
460 [ # # ]: 0 : if (sz <= 0) return len+1;
461 [ # # ]: 0 : if (len >= sz) len=sz-1;
462 : 0 : memcpy(path,dli.dli_fname,len);
463 : 0 : path[len++]=0;
464 : 0 : return len;
465 : : }
466 : :
467 : 0 : ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
468 : : #endif
469 : 0 : return -1;
470 : : }
471 : :
472 : 0 : static void *dlfcn_globallookup(const char *name)
473 : : {
474 : 0 : void *ret = NULL,*handle = dlopen(NULL,RTLD_LAZY);
475 : :
476 [ # # ]: 0 : if (handle)
477 : : {
478 : 0 : ret = dlsym(handle,name);
479 : 0 : dlclose(handle);
480 : : }
481 : :
482 : 0 : return ret;
483 : : }
484 : : #endif /* DSO_DLFCN */
|