Cygwin: enable the use of Dl_info and dladdr()
[openssl.git] / crypto / dso / dso_dlfcn.c
1 /*
2  * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (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 #include "e_os.h"
21
22 #ifdef DSO_DLFCN
23
24 # ifdef HAVE_DLFCN_H
25 #  ifdef __osf__
26 #   define __EXTENSIONS__
27 #  endif
28 #  include <dlfcn.h>
29 #  define HAVE_DLINFO 1
30 #  if 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 int dlfcn_pathbyaddr(void *addr, char *path, int sz);
48 static void *dlfcn_globallookup(const char *name);
49
50 static DSO_METHOD dso_meth_dlfcn = {
51     "OpenSSL 'dlfcn' shared library method",
52     dlfcn_load,
53     dlfcn_unload,
54     dlfcn_bind_func,
55     NULL,                       /* ctrl */
56     dlfcn_name_converter,
57     dlfcn_merger,
58     NULL,                       /* init */
59     NULL,                       /* finish */
60     dlfcn_pathbyaddr,
61     dlfcn_globallookup
62 };
63
64 DSO_METHOD *DSO_METHOD_openssl(void)
65 {
66     return &dso_meth_dlfcn;
67 }
68
69 /*
70  * Prior to using the dlopen() function, we should decide on the flag we
71  * send. There's a few different ways of doing this and it's a messy
72  * venn-diagram to match up which platforms support what. So as we don't have
73  * autoconf yet, I'm implementing a hack that could be hacked further
74  * relatively easily to deal with cases as we find them. Initially this is to
75  * cope with OpenBSD.
76  */
77 # if defined(__OpenBSD__) || defined(__NetBSD__)
78 #  ifdef DL_LAZY
79 #   define DLOPEN_FLAG DL_LAZY
80 #  else
81 #   ifdef RTLD_NOW
82 #    define DLOPEN_FLAG RTLD_NOW
83 #   else
84 #    define DLOPEN_FLAG 0
85 #   endif
86 #  endif
87 # else
88 #  define DLOPEN_FLAG RTLD_NOW  /* Hope this works everywhere else */
89 # endif
90
91 /*
92  * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
93  * (void*) returned from dlopen().
94  */
95
96 static int dlfcn_load(DSO *dso)
97 {
98     void *ptr = NULL;
99     /* See applicable comments in dso_dl.c */
100     char *filename = DSO_convert_filename(dso, NULL);
101     int flags = DLOPEN_FLAG;
102     int saveerrno = get_last_sys_error();
103
104     if (filename == NULL) {
105         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
106         goto err;
107     }
108 # ifdef RTLD_GLOBAL
109     if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
110         flags |= RTLD_GLOBAL;
111 # endif
112 # ifdef _AIX
113     if (filename[strlen(filename) - 1] == ')')
114         flags |= RTLD_MEMBER;
115 # endif
116     ptr = dlopen(filename, flags);
117     if (ptr == NULL) {
118         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
119         ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
120         goto err;
121     }
122     /*
123      * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
124      * on a successful call.
125      */
126     set_sys_error(saveerrno);
127     if (!sk_void_push(dso->meth_data, (char *)ptr)) {
128         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
129         goto err;
130     }
131     /* Success */
132     dso->loaded_filename = filename;
133     return 1;
134  err:
135     /* Cleanup! */
136     OPENSSL_free(filename);
137     if (ptr != NULL)
138         dlclose(ptr);
139     return 0;
140 }
141
142 static int dlfcn_unload(DSO *dso)
143 {
144     void *ptr;
145     if (dso == NULL) {
146         DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
147         return 0;
148     }
149     if (sk_void_num(dso->meth_data) < 1)
150         return 1;
151     ptr = sk_void_pop(dso->meth_data);
152     if (ptr == NULL) {
153         DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
154         /*
155          * Should push the value back onto the stack in case of a retry.
156          */
157         sk_void_push(dso->meth_data, ptr);
158         return 0;
159     }
160     /* For now I'm not aware of any errors associated with dlclose() */
161     dlclose(ptr);
162     return 1;
163 }
164
165 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
166 {
167     void *ptr;
168     union {
169         DSO_FUNC_TYPE sym;
170         void *dlret;
171     } u;
172
173     if ((dso == NULL) || (symname == NULL)) {
174         DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
175         return NULL;
176     }
177     if (sk_void_num(dso->meth_data) < 1) {
178         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
179         return NULL;
180     }
181     ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
182     if (ptr == NULL) {
183         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
184         return NULL;
185     }
186     u.dlret = dlsym(ptr, symname);
187     if (u.dlret == NULL) {
188         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
189         ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
190         return NULL;
191     }
192     return u.sym;
193 }
194
195 static char *dlfcn_merger(DSO *dso, const char *filespec1,
196                           const char *filespec2)
197 {
198     char *merged;
199
200     if (!filespec1 && !filespec2) {
201         DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
202         return NULL;
203     }
204     /*
205      * If the first file specification is a rooted path, it rules. same goes
206      * if the second file specification is missing.
207      */
208     if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
209         merged = OPENSSL_strdup(filespec1);
210         if (merged == NULL) {
211             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
212             return NULL;
213         }
214     }
215     /*
216      * If the first file specification is missing, the second one rules.
217      */
218     else if (!filespec1) {
219         merged = OPENSSL_strdup(filespec2);
220         if (merged == NULL) {
221             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
222             return NULL;
223         }
224     } else {
225         /*
226          * This part isn't as trivial as it looks.  It assumes that the
227          * second file specification really is a directory, and makes no
228          * checks whatsoever.  Therefore, the result becomes the
229          * concatenation of filespec2 followed by a slash followed by
230          * filespec1.
231          */
232         int spec2len, len;
233
234         spec2len = strlen(filespec2);
235         len = spec2len + strlen(filespec1);
236
237         if (spec2len && filespec2[spec2len - 1] == '/') {
238             spec2len--;
239             len--;
240         }
241         merged = OPENSSL_malloc(len + 2);
242         if (merged == NULL) {
243             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
244             return NULL;
245         }
246         strcpy(merged, filespec2);
247         merged[spec2len] = '/';
248         strcpy(&merged[spec2len + 1], filespec1);
249     }
250     return merged;
251 }
252
253 static char *dlfcn_name_converter(DSO *dso, const char *filename)
254 {
255     char *translated;
256     int len, rsize, transform;
257
258     len = strlen(filename);
259     rsize = len + 1;
260     transform = (strstr(filename, "/") == NULL);
261     if (transform) {
262         /* We will convert this to "%s.so" or "lib%s.so" etc */
263         rsize += strlen(DSO_EXTENSION);    /* The length of ".so" */
264         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
265             rsize += 3;         /* The length of "lib" */
266     }
267     translated = OPENSSL_malloc(rsize);
268     if (translated == NULL) {
269         DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
270         return NULL;
271     }
272     if (transform) {
273         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
274             sprintf(translated, "lib%s" DSO_EXTENSION, filename);
275         else
276             sprintf(translated, "%s" DSO_EXTENSION, filename);
277     } else
278         sprintf(translated, "%s", filename);
279     return translated;
280 }
281
282 # ifdef __sgi
283 /*-
284 This is a quote from IRIX manual for dladdr(3c):
285
286      <dlfcn.h> does not contain a prototype for dladdr or definition of
287      Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
288      but contains no dladdr prototype and no IRIX library contains an
289      implementation.  Write your own declaration based on the code below.
290
291      The following code is dependent on internal interfaces that are not
292      part of the IRIX compatibility guarantee; however, there is no future
293      intention to change this interface, so on a practical level, the code
294      below is safe to use on IRIX.
295 */
296 #  include <rld_interface.h>
297 #  ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
298 #   define _RLD_INTERFACE_DLFCN_H_DLADDR
299 typedef struct Dl_info {
300     const char *dli_fname;
301     void *dli_fbase;
302     const char *dli_sname;
303     void *dli_saddr;
304     int dli_version;
305     int dli_reserved1;
306     long dli_reserved[4];
307 } Dl_info;
308 #  else
309 typedef struct Dl_info Dl_info;
310 #  endif
311 #  define _RLD_DLADDR             14
312
313 static int dladdr(void *address, Dl_info *dl)
314 {
315     void *v;
316     v = _rld_new_interface(_RLD_DLADDR, address, dl);
317     return (int)v;
318 }
319 # endif                         /* __sgi */
320
321 # ifdef _AIX
322 /*-
323  * See IBM's AIX Version 7.2, Technical Reference:
324  *  Base Operating System and Extensions, Volume 1 and 2
325  *  https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
326  */
327 #  include <sys/ldr.h>
328 #  include <errno.h>
329 /* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
330 #  define DLFCN_LDINFO_SIZE 86976
331 typedef struct Dl_info {
332     const char *dli_fname;
333 } Dl_info;
334 /*
335  * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
336  * address of a function, which is just located in the DATA segment instead of
337  * the TEXT segment.
338  */
339 static int dladdr(void *ptr, Dl_info *dl)
340 {
341     uintptr_t addr = (uintptr_t)ptr;
342     unsigned int found = 0;
343     struct ld_info *ldinfos, *next_ldi, *this_ldi;
344
345     if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
346         errno = ENOMEM;
347         dl->dli_fname = NULL;
348         return 0;
349     }
350
351     if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
352         /*-
353          * Error handling is done through errno and dlerror() reading errno:
354          *  ENOMEM (ldinfos buffer is too small),
355          *  EINVAL (invalid flags),
356          *  EFAULT (invalid ldinfos ptr)
357          */
358         OPENSSL_free((void *)ldinfos);
359         dl->dli_fname = NULL;
360         return 0;
361     }
362     next_ldi = ldinfos;
363
364     do {
365         this_ldi = next_ldi;
366         if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
367              && (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
368                          this_ldi->ldinfo_textsize)))
369             || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
370                 && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
371                             this_ldi->ldinfo_datasize)))) {
372             char *buffer, *member;
373             size_t buffer_sz, member_len;
374
375             buffer_sz = strlen(this_ldi->ldinfo_filename) + 1;
376             member = this_ldi->ldinfo_filename + buffer_sz;
377             if ((member_len = strlen(member)) > 0)
378                 buffer_sz += 1 + member_len + 1;
379             found = 1;
380             if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) {
381                 OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
382                 if (member_len > 0) {
383                     /*
384                      * Need to respect a possible member name and not just
385                      * returning the path name in this case. See docs:
386                      * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER.
387                      */
388                     OPENSSL_strlcat(buffer, "(", buffer_sz);
389                     OPENSSL_strlcat(buffer, member, buffer_sz);
390                     OPENSSL_strlcat(buffer, ")", buffer_sz);
391                 }
392                 dl->dli_fname = buffer;
393             } else {
394                 errno = ENOMEM;
395             }
396         } else {
397             next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
398                                           this_ldi->ldinfo_next);
399         }
400     } while (this_ldi->ldinfo_next && !found);
401     OPENSSL_free((void *)ldinfos);
402     return (found && dl->dli_fname != NULL);
403 }
404 # endif                         /* _AIX */
405
406 static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
407 {
408 # ifdef HAVE_DLINFO
409     Dl_info dli;
410     int len;
411
412     if (addr == NULL) {
413         union {
414             int (*f) (void *, char *, int);
415             void *p;
416         } t = {
417             dlfcn_pathbyaddr
418         };
419         addr = t.p;
420     }
421
422     if (dladdr(addr, &dli)) {
423         len = (int)strlen(dli.dli_fname);
424         if (sz <= 0) {
425 #  ifdef _AIX
426             OPENSSL_free((void *)dli.dli_fname);
427 #  endif
428             return len + 1;
429         }
430         if (len >= sz)
431             len = sz - 1;
432         memcpy(path, dli.dli_fname, len);
433         path[len++] = 0;
434 #  ifdef _AIX
435         OPENSSL_free((void *)dli.dli_fname);
436 #  endif
437         return len;
438     }
439
440     ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
441 # endif
442     return -1;
443 }
444
445 static void *dlfcn_globallookup(const char *name)
446 {
447     void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
448
449     if (handle) {
450         ret = dlsym(handle, name);
451         dlclose(handle);
452     }
453
454     return ret;
455 }
456 #endif                          /* DSO_DLFCN */