Line data Source code
1 : /* dso_dlfcn.c -*- mode:C; c-file-style: "eay" -*- */
2 : /*
3 : * Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL project
4 : * 2000.
5 : */
6 : /* ====================================================================
7 : * Copyright (c) 2000 The OpenSSL Project. All rights reserved.
8 : *
9 : * Redistribution and use in source and binary forms, with or without
10 : * modification, are permitted provided that the following conditions
11 : * are met:
12 : *
13 : * 1. Redistributions of source code must retain the above copyright
14 : * notice, this list of conditions and the following disclaimer.
15 : *
16 : * 2. Redistributions in binary form must reproduce the above copyright
17 : * notice, this list of conditions and the following disclaimer in
18 : * the documentation and/or other materials provided with the
19 : * distribution.
20 : *
21 : * 3. All advertising materials mentioning features or use of this
22 : * software must display the following acknowledgment:
23 : * "This product includes software developed by the OpenSSL Project
24 : * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25 : *
26 : * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27 : * endorse or promote products derived from this software without
28 : * prior written permission. For written permission, please contact
29 : * licensing@OpenSSL.org.
30 : *
31 : * 5. Products derived from this software may not be called "OpenSSL"
32 : * nor may "OpenSSL" appear in their names without prior written
33 : * permission of the OpenSSL Project.
34 : *
35 : * 6. Redistributions of any form whatsoever must retain the following
36 : * acknowledgment:
37 : * "This product includes software developed by the OpenSSL Project
38 : * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
39 : *
40 : * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41 : * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
44 : * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 : * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49 : * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51 : * OF THE POSSIBILITY OF SUCH DAMAGE.
52 : * ====================================================================
53 : *
54 : * This product includes cryptographic software written by Eric Young
55 : * (eay@cryptsoft.com). This product includes software written by Tim
56 : * Hudson (tjh@cryptsoft.com).
57 : *
58 : */
59 :
60 : /*
61 : * We need to do this early, because stdio.h includes the header files that
62 : * handle _GNU_SOURCE and other similar macros. Defining it later is simply
63 : * too late, because those headers are protected from re- inclusion.
64 : */
65 : #ifndef _GNU_SOURCE
66 : # define _GNU_SOURCE /* make sure dladdr is declared */
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 0 : DSO_METHOD *DSO_METHOD_dlfcn(void)
135 : {
136 0 : return (&dso_meth_dlfcn);
137 : }
138 :
139 : /*
140 : * Prior to using the dlopen() function, we should decide on the flag we
141 : * send. There's a few different ways of doing this and it's a messy
142 : * venn-diagram to match up which platforms support what. So as we don't have
143 : * autoconf yet, I'm implementing a hack that could be hacked further
144 : * relatively easily to deal with cases as we find them. Initially this is to
145 : * cope with OpenBSD.
146 : */
147 : # if defined(__OpenBSD__) || defined(__NetBSD__)
148 : # ifdef DL_LAZY
149 : # define DLOPEN_FLAG DL_LAZY
150 : # else
151 : # ifdef RTLD_NOW
152 : # define DLOPEN_FLAG RTLD_NOW
153 : # else
154 : # define DLOPEN_FLAG 0
155 : # endif
156 : # endif
157 : # else
158 : # ifdef OPENSSL_SYS_SUNOS
159 : # define DLOPEN_FLAG 1
160 : # else
161 : # define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
162 : # endif
163 : # endif
164 :
165 : /*
166 : * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
167 : * (void*) returned from dlopen().
168 : */
169 :
170 0 : static int dlfcn_load(DSO *dso)
171 : {
172 : void *ptr = NULL;
173 : /* See applicable comments in dso_dl.c */
174 0 : char *filename = DSO_convert_filename(dso, NULL);
175 : int flags = DLOPEN_FLAG;
176 :
177 0 : if (filename == NULL) {
178 0 : DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
179 0 : goto err;
180 : }
181 : # ifdef RTLD_GLOBAL
182 0 : if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
183 : flags |= RTLD_GLOBAL;
184 : # endif
185 0 : ptr = dlopen(filename, flags);
186 0 : if (ptr == NULL) {
187 0 : DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
188 0 : ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
189 0 : goto err;
190 : }
191 0 : if (!sk_void_push(dso->meth_data, (char *)ptr)) {
192 0 : DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
193 0 : goto err;
194 : }
195 : /* Success */
196 0 : dso->loaded_filename = filename;
197 0 : return (1);
198 : err:
199 : /* Cleanup! */
200 0 : if (filename != NULL)
201 0 : OPENSSL_free(filename);
202 0 : if (ptr != NULL)
203 0 : dlclose(ptr);
204 : return (0);
205 : }
206 :
207 0 : static int dlfcn_unload(DSO *dso)
208 : {
209 : void *ptr;
210 0 : if (dso == NULL) {
211 0 : DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
212 0 : return (0);
213 : }
214 0 : if (sk_void_num(dso->meth_data) < 1)
215 : return (1);
216 0 : ptr = sk_void_pop(dso->meth_data);
217 0 : if (ptr == NULL) {
218 0 : DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
219 : /*
220 : * Should push the value back onto the stack in case of a retry.
221 : */
222 0 : sk_void_push(dso->meth_data, ptr);
223 0 : return (0);
224 : }
225 : /* For now I'm not aware of any errors associated with dlclose() */
226 0 : dlclose(ptr);
227 0 : return (1);
228 : }
229 :
230 0 : static void *dlfcn_bind_var(DSO *dso, const char *symname)
231 : {
232 : void *ptr, *sym;
233 :
234 0 : if ((dso == NULL) || (symname == NULL)) {
235 0 : DSOerr(DSO_F_DLFCN_BIND_VAR, ERR_R_PASSED_NULL_PARAMETER);
236 0 : return (NULL);
237 : }
238 0 : if (sk_void_num(dso->meth_data) < 1) {
239 0 : DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_STACK_ERROR);
240 0 : return (NULL);
241 : }
242 0 : ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
243 0 : if (ptr == NULL) {
244 0 : DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_NULL_HANDLE);
245 0 : return (NULL);
246 : }
247 0 : sym = dlsym(ptr, symname);
248 0 : if (sym == NULL) {
249 0 : DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_SYM_FAILURE);
250 0 : ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
251 0 : return (NULL);
252 : }
253 : return (sym);
254 : }
255 :
256 0 : static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
257 : {
258 : void *ptr;
259 : union {
260 : DSO_FUNC_TYPE sym;
261 : void *dlret;
262 : } u;
263 :
264 0 : if ((dso == NULL) || (symname == NULL)) {
265 0 : DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
266 0 : return (NULL);
267 : }
268 0 : if (sk_void_num(dso->meth_data) < 1) {
269 0 : DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
270 0 : return (NULL);
271 : }
272 0 : ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
273 0 : if (ptr == NULL) {
274 0 : DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
275 0 : return (NULL);
276 : }
277 0 : u.dlret = dlsym(ptr, symname);
278 0 : if (u.dlret == NULL) {
279 0 : DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
280 0 : ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
281 0 : return (NULL);
282 : }
283 0 : return u.sym;
284 : }
285 :
286 0 : static char *dlfcn_merger(DSO *dso, const char *filespec1,
287 : const char *filespec2)
288 : {
289 : char *merged;
290 :
291 0 : if (!filespec1 && !filespec2) {
292 0 : DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
293 0 : return (NULL);
294 : }
295 : /*
296 : * If the first file specification is a rooted path, it rules. same goes
297 : * if the second file specification is missing.
298 : */
299 0 : if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
300 0 : merged = OPENSSL_malloc(strlen(filespec1) + 1);
301 0 : if (!merged) {
302 0 : DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
303 0 : return (NULL);
304 : }
305 : strcpy(merged, filespec1);
306 : }
307 : /*
308 : * If the first file specification is missing, the second one rules.
309 : */
310 0 : else if (!filespec1) {
311 0 : merged = OPENSSL_malloc(strlen(filespec2) + 1);
312 0 : if (!merged) {
313 0 : DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
314 0 : return (NULL);
315 : }
316 : strcpy(merged, filespec2);
317 : } else {
318 : /*
319 : * This part isn't as trivial as it looks. It assumes that the
320 : * second file specification really is a directory, and makes no
321 : * checks whatsoever. Therefore, the result becomes the
322 : * concatenation of filespec2 followed by a slash followed by
323 : * filespec1.
324 : */
325 : int spec2len, len;
326 :
327 0 : spec2len = strlen(filespec2);
328 0 : len = spec2len + strlen(filespec1);
329 :
330 0 : if (spec2len && filespec2[spec2len - 1] == '/') {
331 0 : spec2len--;
332 0 : len--;
333 : }
334 0 : merged = OPENSSL_malloc(len + 2);
335 0 : if (!merged) {
336 0 : DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
337 0 : return (NULL);
338 : }
339 : strcpy(merged, filespec2);
340 0 : merged[spec2len] = '/';
341 0 : strcpy(&merged[spec2len + 1], filespec1);
342 : }
343 0 : return (merged);
344 : }
345 :
346 : # ifdef OPENSSL_SYS_MACOSX
347 : # define DSO_ext ".dylib"
348 : # define DSO_extlen 6
349 : # else
350 : # define DSO_ext ".so"
351 : # define DSO_extlen 3
352 : # endif
353 :
354 0 : static char *dlfcn_name_converter(DSO *dso, const char *filename)
355 : {
356 : char *translated;
357 : int len, rsize, transform;
358 :
359 0 : len = strlen(filename);
360 0 : rsize = len + 1;
361 0 : transform = (strstr(filename, "/") == NULL);
362 0 : if (transform) {
363 : /* We will convert this to "%s.so" or "lib%s.so" etc */
364 0 : rsize += DSO_extlen; /* The length of ".so" */
365 0 : if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
366 0 : rsize += 3; /* The length of "lib" */
367 : }
368 0 : translated = OPENSSL_malloc(rsize);
369 0 : if (translated == NULL) {
370 0 : DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
371 0 : return (NULL);
372 : }
373 0 : if (transform) {
374 0 : if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
375 : sprintf(translated, "lib%s" DSO_ext, filename);
376 : else
377 : sprintf(translated, "%s" DSO_ext, filename);
378 : } else
379 : sprintf(translated, "%s", filename);
380 0 : return (translated);
381 : }
382 :
383 : # ifdef __sgi
384 : /*-
385 : This is a quote from IRIX manual for dladdr(3c):
386 :
387 : <dlfcn.h> does not contain a prototype for dladdr or definition of
388 : Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional,
389 : but contains no dladdr prototype and no IRIX library contains an
390 : implementation. Write your own declaration based on the code below.
391 :
392 : The following code is dependent on internal interfaces that are not
393 : part of the IRIX compatibility guarantee; however, there is no future
394 : intention to change this interface, so on a practical level, the code
395 : below is safe to use on IRIX.
396 : */
397 : # include <rld_interface.h>
398 : # ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
399 : # define _RLD_INTERFACE_DLFCN_H_DLADDR
400 : typedef struct Dl_info {
401 : const char *dli_fname;
402 : void *dli_fbase;
403 : const char *dli_sname;
404 : void *dli_saddr;
405 : int dli_version;
406 : int dli_reserved1;
407 : long dli_reserved[4];
408 : } Dl_info;
409 : # else
410 : typedef struct Dl_info Dl_info;
411 : # endif
412 : # define _RLD_DLADDR 14
413 :
414 : static int dladdr(void *address, Dl_info *dl)
415 : {
416 : void *v;
417 : v = _rld_new_interface(_RLD_DLADDR, address, dl);
418 : return (int)v;
419 : }
420 : # endif /* __sgi */
421 :
422 0 : static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
423 : {
424 : # ifdef HAVE_DLINFO
425 : Dl_info dli;
426 : int len;
427 :
428 0 : if (addr == NULL) {
429 : union {
430 : int (*f) (void *, char *, int);
431 : void *p;
432 : } t = {
433 : dlfcn_pathbyaddr
434 : };
435 : addr = t.p;
436 : }
437 :
438 0 : if (dladdr(addr, &dli)) {
439 0 : len = (int)strlen(dli.dli_fname);
440 0 : if (sz <= 0)
441 0 : return len + 1;
442 0 : if (len >= sz)
443 0 : len = sz - 1;
444 0 : memcpy(path, dli.dli_fname, len);
445 0 : path[len++] = 0;
446 0 : return len;
447 : }
448 :
449 0 : ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
450 : # endif
451 0 : return -1;
452 : }
453 :
454 0 : static void *dlfcn_globallookup(const char *name)
455 : {
456 0 : void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
457 :
458 0 : if (handle) {
459 0 : ret = dlsym(handle, name);
460 0 : dlclose(handle);
461 : }
462 :
463 0 : return ret;
464 : }
465 : #endif /* DSO_DLFCN */
|