dso_dlfcn.c revision 296341
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
74DSO_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
98static int dlfcn_load(DSO *dso);
99static int dlfcn_unload(DSO *dso);
100static void *dlfcn_bind_var(DSO *dso, const char *symname);
101static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
102# if 0
103static int dlfcn_unbind(DSO *dso, char *symname, void *symptr);
104static int dlfcn_init(DSO *dso);
105static int dlfcn_finish(DSO *dso);
106static long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg);
107# endif
108static char *dlfcn_name_converter(DSO *dso, const char *filename);
109static char *dlfcn_merger(DSO *dso, const char *filespec1,
110                          const char *filespec2);
111static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
112static void *dlfcn_globallookup(const char *name);
113
114static 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
134DSO_METHOD *DSO_METHOD_dlfcn(void)
135{
136    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
170static int dlfcn_load(DSO *dso)
171{
172    void *ptr = NULL;
173    /* See applicable comments in dso_dl.c */
174    char *filename = DSO_convert_filename(dso, NULL);
175    int flags = DLOPEN_FLAG;
176
177    if (filename == NULL) {
178        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
179        goto err;
180    }
181# ifdef RTLD_GLOBAL
182    if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
183        flags |= RTLD_GLOBAL;
184# endif
185    ptr = dlopen(filename, flags);
186    if (ptr == NULL) {
187        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
188        ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
189        goto err;
190    }
191    if (!sk_void_push(dso->meth_data, (char *)ptr)) {
192        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
193        goto err;
194    }
195    /* Success */
196    dso->loaded_filename = filename;
197    return (1);
198 err:
199    /* Cleanup! */
200    if (filename != NULL)
201        OPENSSL_free(filename);
202    if (ptr != NULL)
203        dlclose(ptr);
204    return (0);
205}
206
207static int dlfcn_unload(DSO *dso)
208{
209    void *ptr;
210    if (dso == NULL) {
211        DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
212        return (0);
213    }
214    if (sk_void_num(dso->meth_data) < 1)
215        return (1);
216    ptr = sk_void_pop(dso->meth_data);
217    if (ptr == NULL) {
218        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        sk_void_push(dso->meth_data, ptr);
223        return (0);
224    }
225    /* For now I'm not aware of any errors associated with dlclose() */
226    dlclose(ptr);
227    return (1);
228}
229
230static void *dlfcn_bind_var(DSO *dso, const char *symname)
231{
232    void *ptr, *sym;
233
234    if ((dso == NULL) || (symname == NULL)) {
235        DSOerr(DSO_F_DLFCN_BIND_VAR, ERR_R_PASSED_NULL_PARAMETER);
236        return (NULL);
237    }
238    if (sk_void_num(dso->meth_data) < 1) {
239        DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_STACK_ERROR);
240        return (NULL);
241    }
242    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
243    if (ptr == NULL) {
244        DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_NULL_HANDLE);
245        return (NULL);
246    }
247    sym = dlsym(ptr, symname);
248    if (sym == NULL) {
249        DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_SYM_FAILURE);
250        ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
251        return (NULL);
252    }
253    return (sym);
254}
255
256static 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    if ((dso == NULL) || (symname == NULL)) {
265        DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
266        return (NULL);
267    }
268    if (sk_void_num(dso->meth_data) < 1) {
269        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
270        return (NULL);
271    }
272    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
273    if (ptr == NULL) {
274        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
275        return (NULL);
276    }
277    u.dlret = dlsym(ptr, symname);
278    if (u.dlret == NULL) {
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 u.sym;
284}
285
286static char *dlfcn_merger(DSO *dso, const char *filespec1,
287                          const char *filespec2)
288{
289    char *merged;
290
291    if (!filespec1 && !filespec2) {
292        DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
293        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    if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
300        merged = OPENSSL_malloc(strlen(filespec1) + 1);
301        if (!merged) {
302            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
303            return (NULL);
304        }
305        strcpy(merged, filespec1);
306    }
307    /*
308     * If the first file specification is missing, the second one rules.
309     */
310    else if (!filespec1) {
311        merged = OPENSSL_malloc(strlen(filespec2) + 1);
312        if (!merged) {
313            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
314            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        spec2len = strlen(filespec2);
328        len = spec2len + strlen(filespec1);
329
330        if (spec2len && filespec2[spec2len - 1] == '/') {
331            spec2len--;
332            len--;
333        }
334        merged = OPENSSL_malloc(len + 2);
335        if (!merged) {
336            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
337            return (NULL);
338        }
339        strcpy(merged, filespec2);
340        merged[spec2len] = '/';
341        strcpy(&merged[spec2len + 1], filespec1);
342    }
343    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
354static char *dlfcn_name_converter(DSO *dso, const char *filename)
355{
356    char *translated;
357    int len, rsize, transform;
358
359    len = strlen(filename);
360    rsize = len + 1;
361    transform = (strstr(filename, "/") == NULL);
362    if (transform) {
363        /* We will convert this to "%s.so" or "lib%s.so" etc */
364        rsize += DSO_extlen;    /* 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        DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
371        return (NULL);
372    }
373    if (transform) {
374        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    return (translated);
381}
382
383# ifdef __sgi
384/*-
385This 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
400typedef 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
410typedef struct Dl_info Dl_info;
411#  endif
412#  define _RLD_DLADDR             14
413
414static 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
422static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
423{
424# ifdef HAVE_DLINFO
425    Dl_info dli;
426    int len;
427
428    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    if (dladdr(addr, &dli)) {
439        len = (int)strlen(dli.dli_fname);
440        if (sz <= 0)
441            return len + 1;
442        if (len >= sz)
443            len = sz - 1;
444        memcpy(path, dli.dli_fname, len);
445        path[len++] = 0;
446        return len;
447    }
448
449    ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
450# endif
451    return -1;
452}
453
454static void *dlfcn_globallookup(const char *name)
455{
456    void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
457
458    if (handle) {
459        ret = dlsym(handle, name);
460        dlclose(handle);
461    }
462
463    return ret;
464}
465#endif                          /* DSO_DLFCN */
466