Copyright consolidation 07/10
[openssl.git] / crypto / dso / dso_dlfcn.c
1 /*
2  * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 /*
11  * We need to do this early, because stdio.h includes the header files that
12  * handle _GNU_SOURCE and other similar macros.  Defining it later is simply
13  * too late, because those headers are protected from re- inclusion.
14  */
15 #ifndef _GNU_SOURCE
16 # define _GNU_SOURCE            /* make sure dladdr is declared */
17 #endif
18
19 #include "dso_locl.h"
20
21 #ifdef DSO_DLFCN
22
23 # ifdef HAVE_DLFCN_H
24 #  ifdef __osf__
25 #   define __EXTENSIONS__
26 #  endif
27 #  include <dlfcn.h>
28 #  define HAVE_DLINFO 1
29 #  if defined(_AIX) || defined(__CYGWIN__) || \
30      defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
31      (defined(__osf__) && !defined(RTLD_NEXT))     || \
32      (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
33         defined(__ANDROID__)
34 #   undef HAVE_DLINFO
35 #  endif
36 # endif
37
38 /* Part of the hack in "dlfcn_load" ... */
39 # define DSO_MAX_TRANSLATED_SIZE 256
40
41 static int dlfcn_load(DSO *dso);
42 static int dlfcn_unload(DSO *dso);
43 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
44 static char *dlfcn_name_converter(DSO *dso, const char *filename);
45 static char *dlfcn_merger(DSO *dso, const char *filespec1,
46                           const char *filespec2);
47 static void *dlfcn_globallookup(const char *name);
48
49 static DSO_METHOD dso_meth_dlfcn = {
50     "OpenSSL 'dlfcn' shared library method",
51     dlfcn_load,
52     dlfcn_unload,
53     dlfcn_bind_func,
54     NULL,                       /* ctrl */
55     dlfcn_name_converter,
56     dlfcn_merger,
57     NULL,                       /* init */
58     NULL,                       /* finish */
59     dlfcn_globallookup
60 };
61
62 DSO_METHOD *DSO_METHOD_openssl(void)
63 {
64     return &dso_meth_dlfcn;
65 }
66
67 /*
68  * Prior to using the dlopen() function, we should decide on the flag we
69  * send. There's a few different ways of doing this and it's a messy
70  * venn-diagram to match up which platforms support what. So as we don't have
71  * autoconf yet, I'm implementing a hack that could be hacked further
72  * relatively easily to deal with cases as we find them. Initially this is to
73  * cope with OpenBSD.
74  */
75 # if defined(__OpenBSD__) || defined(__NetBSD__)
76 #  ifdef DL_LAZY
77 #   define DLOPEN_FLAG DL_LAZY
78 #  else
79 #   ifdef RTLD_NOW
80 #    define DLOPEN_FLAG RTLD_NOW
81 #   else
82 #    define DLOPEN_FLAG 0
83 #   endif
84 #  endif
85 # else
86 #  define DLOPEN_FLAG RTLD_NOW  /* Hope this works everywhere else */
87 # endif
88
89 /*
90  * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
91  * (void*) returned from dlopen().
92  */
93
94 static int dlfcn_load(DSO *dso)
95 {
96     void *ptr = NULL;
97     /* See applicable comments in dso_dl.c */
98     char *filename = DSO_convert_filename(dso, NULL);
99     int flags = DLOPEN_FLAG;
100
101     if (filename == NULL) {
102         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
103         goto err;
104     }
105 # ifdef RTLD_GLOBAL
106     if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
107         flags |= RTLD_GLOBAL;
108 # endif
109     ptr = dlopen(filename, flags);
110     if (ptr == NULL) {
111         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
112         ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
113         goto err;
114     }
115     if (!sk_void_push(dso->meth_data, (char *)ptr)) {
116         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
117         goto err;
118     }
119     /* Success */
120     dso->loaded_filename = filename;
121     return (1);
122  err:
123     /* Cleanup! */
124     OPENSSL_free(filename);
125     if (ptr != NULL)
126         dlclose(ptr);
127     return (0);
128 }
129
130 static int dlfcn_unload(DSO *dso)
131 {
132     void *ptr;
133     if (dso == NULL) {
134         DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
135         return (0);
136     }
137     if (sk_void_num(dso->meth_data) < 1)
138         return (1);
139     ptr = sk_void_pop(dso->meth_data);
140     if (ptr == NULL) {
141         DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
142         /*
143          * Should push the value back onto the stack in case of a retry.
144          */
145         sk_void_push(dso->meth_data, ptr);
146         return (0);
147     }
148     /* For now I'm not aware of any errors associated with dlclose() */
149     dlclose(ptr);
150     return (1);
151 }
152
153 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
154 {
155     void *ptr;
156     union {
157         DSO_FUNC_TYPE sym;
158         void *dlret;
159     } u;
160
161     if ((dso == NULL) || (symname == NULL)) {
162         DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
163         return (NULL);
164     }
165     if (sk_void_num(dso->meth_data) < 1) {
166         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
167         return (NULL);
168     }
169     ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
170     if (ptr == NULL) {
171         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
172         return (NULL);
173     }
174     u.dlret = dlsym(ptr, symname);
175     if (u.dlret == NULL) {
176         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
177         ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
178         return (NULL);
179     }
180     return u.sym;
181 }
182
183 static char *dlfcn_merger(DSO *dso, const char *filespec1,
184                           const char *filespec2)
185 {
186     char *merged;
187
188     if (!filespec1 && !filespec2) {
189         DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
190         return (NULL);
191     }
192     /*
193      * If the first file specification is a rooted path, it rules. same goes
194      * if the second file specification is missing.
195      */
196     if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
197         merged = OPENSSL_strdup(filespec1);
198         if (merged == NULL) {
199             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
200             return (NULL);
201         }
202     }
203     /*
204      * If the first file specification is missing, the second one rules.
205      */
206     else if (!filespec1) {
207         merged = OPENSSL_strdup(filespec2);
208         if (merged == NULL) {
209             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
210             return (NULL);
211         }
212     } else {
213         /*
214          * This part isn't as trivial as it looks.  It assumes that the
215          * second file specification really is a directory, and makes no
216          * checks whatsoever.  Therefore, the result becomes the
217          * concatenation of filespec2 followed by a slash followed by
218          * filespec1.
219          */
220         int spec2len, len;
221
222         spec2len = strlen(filespec2);
223         len = spec2len + strlen(filespec1);
224
225         if (spec2len && filespec2[spec2len - 1] == '/') {
226             spec2len--;
227             len--;
228         }
229         merged = OPENSSL_malloc(len + 2);
230         if (merged == NULL) {
231             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
232             return (NULL);
233         }
234         strcpy(merged, filespec2);
235         merged[spec2len] = '/';
236         strcpy(&merged[spec2len + 1], filespec1);
237     }
238     return (merged);
239 }
240
241 static char *dlfcn_name_converter(DSO *dso, const char *filename)
242 {
243     char *translated;
244     int len, rsize, transform;
245
246     len = strlen(filename);
247     rsize = len + 1;
248     transform = (strstr(filename, "/") == NULL);
249     if (transform) {
250         /* We will convert this to "%s.so" or "lib%s.so" etc */
251         rsize += strlen(DSO_EXTENSION);    /* The length of ".so" */
252         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
253             rsize += 3;         /* The length of "lib" */
254     }
255     translated = OPENSSL_malloc(rsize);
256     if (translated == NULL) {
257         DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
258         return (NULL);
259     }
260     if (transform) {
261         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
262             sprintf(translated, "lib%s" DSO_EXTENSION, filename);
263         else
264             sprintf(translated, "%s" DSO_EXTENSION, filename);
265     } else
266         sprintf(translated, "%s", filename);
267     return (translated);
268 }
269
270 # ifdef __sgi
271 /*-
272 This is a quote from IRIX manual for dladdr(3c):
273
274      <dlfcn.h> does not contain a prototype for dladdr or definition of
275      Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
276      but contains no dladdr prototype and no IRIX library contains an
277      implementation.  Write your own declaration based on the code below.
278
279      The following code is dependent on internal interfaces that are not
280      part of the IRIX compatibility guarantee; however, there is no future
281      intention to change this interface, so on a practical level, the code
282      below is safe to use on IRIX.
283 */
284 #  include <rld_interface.h>
285 #  ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
286 #   define _RLD_INTERFACE_DLFCN_H_DLADDR
287 typedef struct Dl_info {
288     const char *dli_fname;
289     void *dli_fbase;
290     const char *dli_sname;
291     void *dli_saddr;
292     int dli_version;
293     int dli_reserved1;
294     long dli_reserved[4];
295 } Dl_info;
296 #  else
297 typedef struct Dl_info Dl_info;
298 #  endif
299 #  define _RLD_DLADDR             14
300
301 static int dladdr(void *address, Dl_info *dl)
302 {
303     void *v;
304     v = _rld_new_interface(_RLD_DLADDR, address, dl);
305     return (int)v;
306 }
307 # endif                         /* __sgi */
308
309 static void *dlfcn_globallookup(const char *name)
310 {
311     void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
312
313     if (handle) {
314         ret = dlsym(handle, name);
315         dlclose(handle);
316     }
317
318     return ret;
319 }
320 #endif                          /* DSO_DLFCN */