dso_dlfcn.c revision 280304
12061Sjkh/* dso_dlfcn.c -*- mode:C; c-file-style: "eay" -*- */
250479Speter/*
32061Sjkh * Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL project
438666Sjb * 2000.
532427Sjb */
6111131Sru/* ====================================================================
7111131Sru * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
838666Sjb *
938666Sjb * Redistribution and use in source and binary forms, with or without
1038666Sjb * modification, are permitted provided that the following conditions
11159363Strhodes * are met:
1264049Salex *
1364049Salex * 1. Redistributions of source code must retain the above copyright
14116679Ssimokawa *    notice, this list of conditions and the following disclaimer.
1566071Smarkm *
16116679Ssimokawa * 2. Redistributions in binary form must reproduce the above copyright
1773504Sobrien *    notice, this list of conditions and the following disclaimer in
18204661Simp *    the documentation and/or other materials provided with the
19158962Snetchild *    distribution.
2038666Sjb *
21169597Sdes * 3. All advertising materials mentioning features or use of this
22169597Sdes *    software must display the following acknowledgment:
23169597Sdes *    "This product includes software developed by the OpenSSL Project
24169597Sdes *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25169597Sdes *
26169597Sdes * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27169597Sdes *    endorse or promote products derived from this software without
28169597Sdes *    prior written permission. For written permission, please contact
2932427Sjb *    licensing@OpenSSL.org.
3038666Sjb *
31108451Sschweikh * 5. Products derived from this software may not be called "OpenSSL"
3238666Sjb *    nor may "OpenSSL" appear in their names without prior written
3338666Sjb *    permission of the OpenSSL Project.
3438666Sjb *
3538666Sjb * 6. Redistributions of any form whatsoever must retain the following
3617308Speter *    acknowledgment:
3791606Skeramida *    "This product includes software developed by the OpenSSL Project
3819175Sbde *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
3996205Sjwd *
40177794Spav * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
4138042Sbde * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4296205Sjwd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4396205Sjwd * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
4438042Sbde * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
4596205Sjwd * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46159363Strhodes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47159363Strhodes * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4817308Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
4996205Sjwd * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5096205Sjwd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
5117308Speter * OF THE POSSIBILITY OF SUCH DAMAGE.
52148330Snetchild * ====================================================================
53148330Snetchild *
54148330Snetchild * This product includes cryptographic software written by Eric Young
55148330Snetchild * (eay@cryptsoft.com).  This product includes software written by Tim
56159831Sobrien * Hudson (tjh@cryptsoft.com).
57148330Snetchild *
58148330Snetchild */
59148330Snetchild
60148330Snetchild/*
61178653Srwatson * We need to do this early, because stdio.h includes the header files that
62148330Snetchild * handle _GNU_SOURCE and other similar macros.  Defining it later is simply
63148330Snetchild * too late, because those headers are protected from re- inclusion.
6496205Sjwd */
6596205Sjwd#ifndef _GNU_SOURCE
6696205Sjwd# define _GNU_SOURCE            /* make sure dladdr is declared */
67162147Sru#endif
68162147Sru
6998723Sdillon#include <stdio.h>
7098723Sdillon#include "cryptlib.h"
7198723Sdillon#include <openssl/dso.h>
7238666Sjb
7338666Sjb#ifndef DSO_DLFCN
7417308SpeterDSO_METHOD *DSO_METHOD_dlfcn(void)
75123311Speter{
76123311Speter    return NULL;
77123311Speter}
78123311Speter#else
79175833Sjhb
80175833Sjhb# ifdef HAVE_DLFCN_H
81169597Sdes#  ifdef __osf__
82169597Sdes#   define __EXTENSIONS__
83169597Sdes#  endif
84169597Sdes#  include <dlfcn.h>
85159349Simp#  define HAVE_DLINFO 1
86158962Snetchild#  if defined(_AIX) || defined(__CYGWIN__) || \
87158962Snetchild     defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
88158962Snetchild     (defined(__osf__) && !defined(RTLD_NEXT))     || \
89156840Sru     (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
90123311Speter        defined(__ANDROID__)
91137288Speter#   undef HAVE_DLINFO
92189760Simp#  endif
93156740Sru# endif
942061Sjkh
9597769Sru/* Part of the hack in "dlfcn_load" ... */
9697252Sru# define DSO_MAX_TRANSLATED_SIZE 256
97119579Sru
9897252Srustatic int dlfcn_load(DSO *dso);
9995730Srustatic int dlfcn_unload(DSO *dso);
10095793Srustatic void *dlfcn_bind_var(DSO *dso, const char *symname);
101111617Srustatic DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
10295730Sru# if 0
103116679Ssimokawastatic int dlfcn_unbind(DSO *dso, char *symname, void *symptr);
10495730Srustatic int dlfcn_init(DSO *dso);
105116679Ssimokawastatic int dlfcn_finish(DSO *dso);
10695730Srustatic long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg);
107110035Sru# endif
108107516Srustatic char *dlfcn_name_converter(DSO *dso, const char *filename);
109138921Srustatic char *dlfcn_merger(DSO *dso, const char *filespec1,
110156145Syar                          const char *filespec2);
111138921Srustatic int dlfcn_pathbyaddr(void *addr, char *path, int sz);
112133942Srustatic void *dlfcn_globallookup(const char *name);
113133942Sru
114156145Syarstatic DSO_METHOD dso_meth_dlfcn = {
115133942Sru    "OpenSSL 'dlfcn' shared library method",
116110035Sru    dlfcn_load,
117117234Sru    dlfcn_unload,
118110035Sru    dlfcn_bind_var,
119117229Sru    dlfcn_bind_func,
120117234Sru/* For now, "unbind" doesn't exist */
12154324Smarcel# if 0
12217308Speter    NULL,                       /* unbind_var */
123119519Smarcel    NULL,                       /* unbind_func */
124119519Smarcel# endif
125119519Smarcel    NULL,                       /* ctrl */
126119519Smarcel    dlfcn_name_converter,
127119519Smarcel    dlfcn_merger,
128119519Smarcel    NULL,                       /* init */
129119579Sru    NULL,                       /* finish */
130119519Smarcel    dlfcn_pathbyaddr,
131119519Smarcel    dlfcn_globallookup
132119519Smarcel};
133119519Smarcel
134119519SmarcelDSO_METHOD *DSO_METHOD_dlfcn(void)
135126031Sgad{
136126024Sgad    return (&dso_meth_dlfcn);
137126024Sgad}
138126024Sgad
139126024Sgad/*
140126024Sgad * Prior to using the dlopen() function, we should decide on the flag we
141126024Sgad * send. There's a few different ways of doing this and it's a messy
142126024Sgad * venn-diagram to match up which platforms support what. So as we don't have
143126024Sgad * autoconf yet, I'm implementing a hack that could be hacked further
144126024Sgad * relatively easily to deal with cases as we find them. Initially this is to
145126024Sgad * cope with OpenBSD.
146126024Sgad */
147126024Sgad# if defined(__OpenBSD__) || defined(__NetBSD__)
148126024Sgad#  ifdef DL_LAZY
149126031Sgad#   define DLOPEN_FLAG DL_LAZY
150126024Sgad#  else
151126024Sgad#   ifdef RTLD_NOW
152126024Sgad#    define DLOPEN_FLAG RTLD_NOW
153172744Sdelphij#   else
154126024Sgad#    define DLOPEN_FLAG 0
155126024Sgad#   endif
156126024Sgad#  endif
157133376Sharti# else
158126024Sgad#  ifdef OPENSSL_SYS_SUNOS
159126024Sgad#   define DLOPEN_FLAG 1
160172744Sdelphij#  else
161126024Sgad#   define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
162126024Sgad#  endif
163125885Sgad# endif
164125885Sgad
16538666Sjb/*
16617308Speter * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
167119519Smarcel * (void*) returned from dlopen().
168119579Sru */
169133376Sharti
170110035Srustatic int dlfcn_load(DSO *dso)
1712302Spaul{
17239206Sjkh    void *ptr = NULL;
17339206Sjkh    /* See applicable comments in dso_dl.c */
17439206Sjkh    char *filename = DSO_convert_filename(dso, NULL);
175133945Sru    int flags = DLOPEN_FLAG;
176177609Sru
177177609Sru    if (filename == NULL) {
178177609Sru        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
179177609Sru        goto err;
180133945Sru    }
181132358Smarkm# ifdef RTLD_GLOBAL
18217308Speter    if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
18354324Smarcel        flags |= RTLD_GLOBAL;
18454324Smarcel# endif
185132234Smarcel    ptr = dlopen(filename, flags);
186132234Smarcel    if (ptr == NULL) {
187132234Smarcel        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
188132234Smarcel        ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
18954324Smarcel        goto err;
19054324Smarcel    }
19154324Smarcel    if (!sk_void_push(dso->meth_data, (char *)ptr)) {
192118531Sru        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
19354324Smarcel        goto err;
19454324Smarcel    }
19554324Smarcel    /* Success */
19654324Smarcel    dso->loaded_filename = filename;
19754324Smarcel    return (1);
19854324Smarcel err:
199133376Sharti    /* Cleanup! */
20054324Smarcel    if (filename != NULL)
201133376Sharti        OPENSSL_free(filename);
202133376Sharti    if (ptr != NULL)
20354324Smarcel        dlclose(ptr);
20454324Smarcel    return (0);
20554324Smarcel}
20654324Smarcel
20754324Smarcelstatic int dlfcn_unload(DSO *dso)
208133376Sharti{
20954324Smarcel    void *ptr;
21054324Smarcel    if (dso == NULL) {
21154324Smarcel        DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
212118531Sru        return (0);
213118531Sru    }
21454324Smarcel    if (sk_void_num(dso->meth_data) < 1)
215132234Smarcel        return (1);
216132234Smarcel    ptr = sk_void_pop(dso->meth_data);
217132234Smarcel    if (ptr == NULL) {
218132234Smarcel        DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
219132234Smarcel        /*
220132588Skensmith         * Should push the value back onto the stack in case of a retry.
221132358Smarkm         */
222132234Smarcel        sk_void_push(dso->meth_data, ptr);
223132358Smarkm        return (0);
224132234Smarcel    }
225132234Smarcel    /* For now I'm not aware of any errors associated with dlclose() */
226132234Smarcel    dlclose(ptr);
22754324Smarcel    return (1);
22854324Smarcel}
22995730Sru
23095730Srustatic void *dlfcn_bind_var(DSO *dso, const char *symname)
23195730Sru{
23295730Sru    void *ptr, *sym;
23395730Sru
23495730Sru    if ((dso == NULL) || (symname == NULL)) {
23595730Sru        DSOerr(DSO_F_DLFCN_BIND_VAR, ERR_R_PASSED_NULL_PARAMETER);
23638666Sjb        return (NULL);
237107374Sru    }
23817308Speter    if (sk_void_num(dso->meth_data) < 1) {
23955678Smarcel        DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_STACK_ERROR);
240143032Sharti        return (NULL);
241138515Sharti    }
242117793Sru    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
243110035Sru    if (ptr == NULL) {
244174564Simp        DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_NULL_HANDLE);
245110035Sru        return (NULL);
2462061Sjkh    }
24717308Speter    sym = dlsym(ptr, symname);
248107516Sru    if (sym == NULL) {
249174539Simp        DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_SYM_FAILURE);
250174539Simp        ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
25155678Smarcel        return (NULL);
252107516Sru    }
253107516Sru    return (sym);
254107516Sru}
255174564Simp
256107516Srustatic DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
257139112Sru{
258164470Sjb    void *ptr;
259107516Sru    union {
260122204Skris        DSO_FUNC_TYPE sym;
26155678Smarcel        void *dlret;
26255678Smarcel    } u;
263116696Sru
26455678Smarcel    if ((dso == NULL) || (symname == NULL)) {
265133376Sharti        DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
266107516Sru        return (NULL);
267107516Sru    }
268107516Sru    if (sk_void_num(dso->meth_data) < 1) {
269107516Sru        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
27055678Smarcel        return (NULL);
271185499Salfred    }
272185499Salfred    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
273185499Salfred    if (ptr == NULL) {
274185499Salfred        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
27555678Smarcel        return (NULL);
276111131Sru    }
277111131Sru    u.dlret = dlsym(ptr, symname);
278111131Sru    if (u.dlret == NULL) {
279133945Sru        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
280111131Sru        ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
281111131Sru        return (NULL);
282201815Sbz    }
283190628Sbz    return u.sym;
284168280Smarcel}
285185499Salfred
286185499Salfredstatic char *dlfcn_merger(DSO *dso, const char *filespec1,
287185499Salfred                          const char *filespec2)
288185499Salfred{
289185499Salfred    char *merged;
290185499Salfred
291185499Salfred    if (!filespec1 && !filespec2) {
292133945Sru        DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
293133945Sru        return (NULL);
294103985Sphk    }
295103985Sphk    /*
296103985Sphk     * If the first file specification is a rooted path, it rules. same goes
297185499Salfred     * if the second file specification is missing.
298185499Salfred     */
299185499Salfred    if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
300168280Smarcel        merged = OPENSSL_malloc(strlen(filespec1) + 1);
301162147Sru        if (!merged) {
302162147Sru            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
303162147Sru            return (NULL);
304179232Sjb        }
305162147Sru        strcpy(merged, filespec1);
306185250Sdes    }
307202629Sed    /*
308162147Sru     * If the first file specification is missing, the second one rules.
309185250Sdes     */
310185499Salfred    else if (!filespec1) {
311185499Salfred        merged = OPENSSL_malloc(strlen(filespec2) + 1);
312162147Sru        if (!merged) {
313179232Sjb            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
314205290Sdougb            return (NULL);
315162147Sru        }
316185250Sdes        strcpy(merged, filespec2);
317185250Sdes    } else {
318185499Salfred        /*
319185499Salfred         * This part isn't as trivial as it looks.  It assumes that the
320103985Sphk         * second file specification really is a directory, and makes no
321201815Sbz         * checks whatsoever.  Therefore, the result becomes the
322201815Sbz         * concatenation of filespec2 followed by a slash followed by
323205290Sdougb         * filespec1.
324201815Sbz         */
325201815Sbz        int spec2len, len;
326201815Sbz
327202095Sbz        spec2len = strlen(filespec2);
328202095Sbz        len = spec2len + strlen(filespec1);
329202095Sbz
330201815Sbz        if (spec2len && filespec2[spec2len - 1] == '/') {
331201815Sbz            spec2len--;
332201815Sbz            len--;
333201815Sbz        }
334148154Sru        merged = OPENSSL_malloc(len + 2);
335185250Sdes        if (!merged) {
336185250Sdes            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
337201815Sbz            return (NULL);
338148154Sru        }
339201815Sbz        strcpy(merged, filespec2);
340201815Sbz        merged[spec2len] = '/';
341201815Sbz        strcpy(&merged[spec2len + 1], filespec1);
342148154Sru    }
343133945Sru    return (merged);
344133945Sru}
345103985Sphk
346118531Sru# ifdef OPENSSL_SYS_MACOSX
347118531Sru#  define DSO_ext ".dylib"
348103985Sphk#  define DSO_extlen 6
349185499Salfred# else
350185499Salfred#  define DSO_ext ".so"
351185499Salfred#  define DSO_extlen 3
352185499Salfred# endif
353185499Salfred
354185499Salfredstatic char *dlfcn_name_converter(DSO *dso, const char *filename)
355133945Sru{
356185499Salfred    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