]> git.ipfire.org Git - thirdparty/openssl.git/blame - crypto/dso/dso_dlfcn.c
Move e_os.h to include/internal
[thirdparty/openssl.git] / crypto / dso / dso_dlfcn.c
CommitLineData
0f113f3e 1/*
38fc02a7 2 * Copyright 2000-2021 The OpenSSL Project Authors. All Rights Reserved.
8f4fac7f 3 *
b6a34e9a 4 * Licensed under the Apache License 2.0 (the "License"). You may not use
d2e9e320
RS
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
8f4fac7f
GT
8 */
9
0f113f3e
MC
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 */
c1844095 15#ifndef _GNU_SOURCE
0f113f3e 16# define _GNU_SOURCE /* make sure dladdr is declared */
3ecbd099
RL
17#endif
18
706457b7 19#include "dso_local.h"
d5f9166b 20#include "internal/e_os.h"
8f4fac7f 21
38186bfd 22#ifdef DSO_DLFCN
8f4fac7f 23
0f113f3e
MC
24# ifdef HAVE_DLFCN_H
25# ifdef __osf__
26# define __EXTENSIONS__
27# endif
28# include <dlfcn.h>
29# define HAVE_DLINFO 1
38f6f99c 30# if defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
afb41913 31 (defined(__osf__) && !defined(RTLD_NEXT)) || \
7a4ec19a 32 (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
08073700 33 defined(__ANDROID__) || defined(__TANDEM)
0f113f3e
MC
34# undef HAVE_DLINFO
35# endif
73133962 36# endif
8f4fac7f 37
b9e63915 38/* Part of the hack in "dlfcn_load" ... */
0f113f3e 39# define DSO_MAX_TRANSLATED_SIZE 256
b9e63915 40
51c8dc37 41static int dlfcn_load(DSO *dso);
8f4fac7f 42static int dlfcn_unload(DSO *dso);
e9a68cfb 43static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
51c8dc37 44static char *dlfcn_name_converter(DSO *dso, const char *filename);
cbecb3ac 45static char *dlfcn_merger(DSO *dso, const char *filespec1,
0f113f3e 46 const char *filespec2);
cb6ea61c 47static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
c6cb42e4 48static void *dlfcn_globallookup(const char *name);
8f4fac7f
GT
49
50static DSO_METHOD dso_meth_dlfcn = {
0f113f3e
MC
51 "OpenSSL 'dlfcn' shared library method",
52 dlfcn_load,
53 dlfcn_unload,
0f113f3e 54 dlfcn_bind_func,
0f113f3e
MC
55 NULL, /* ctrl */
56 dlfcn_name_converter,
57 dlfcn_merger,
58 NULL, /* init */
59 NULL, /* finish */
cb6ea61c 60 dlfcn_pathbyaddr,
0f113f3e
MC
61 dlfcn_globallookup
62};
8f4fac7f 63
38186bfd 64DSO_METHOD *DSO_METHOD_openssl(void)
0f113f3e 65{
38186bfd 66 return &dso_meth_dlfcn;
0f113f3e
MC
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
1a797ac6 90
0f113f3e
MC
91/*
92 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
93 * (void*) returned from dlopen().
8f4fac7f
GT
94 */
95
51c8dc37 96static int dlfcn_load(DSO *dso)
0f113f3e
MC
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;
3cb4e7dc 102 int saveerrno = get_last_sys_error();
0f113f3e
MC
103
104 if (filename == NULL) {
9311d0c4 105 ERR_raise(ERR_LIB_DSO, DSO_R_NO_FILENAME);
0f113f3e
MC
106 goto err;
107 }
108# ifdef RTLD_GLOBAL
109 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
110 flags |= RTLD_GLOBAL;
ea5def14
MK
111# endif
112# ifdef _AIX
113 if (filename[strlen(filename) - 1] == ')')
114 flags |= RTLD_MEMBER;
0f113f3e
MC
115# endif
116 ptr = dlopen(filename, flags);
117 if (ptr == NULL) {
a150f8e1
RL
118 ERR_raise_data(ERR_LIB_DSO, DSO_R_LOAD_FAILED,
119 "filename(%s): %s", filename, dlerror());
0f113f3e
MC
120 goto err;
121 }
3cb4e7dc
MC
122 /*
123 * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
124 * on a successful call.
125 */
126 set_sys_error(saveerrno);
0f113f3e 127 if (!sk_void_push(dso->meth_data, (char *)ptr)) {
9311d0c4 128 ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR);
0f113f3e
MC
129 goto err;
130 }
131 /* Success */
132 dso->loaded_filename = filename;
208fb891 133 return 1;
0f113f3e
MC
134 err:
135 /* Cleanup! */
b548a1f1 136 OPENSSL_free(filename);
0f113f3e
MC
137 if (ptr != NULL)
138 dlclose(ptr);
26a7d938 139 return 0;
51c8dc37 140}
8f4fac7f
GT
141
142static int dlfcn_unload(DSO *dso)
0f113f3e
MC
143{
144 void *ptr;
145 if (dso == NULL) {
9311d0c4 146 ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
26a7d938 147 return 0;
0f113f3e
MC
148 }
149 if (sk_void_num(dso->meth_data) < 1)
208fb891 150 return 1;
0f113f3e
MC
151 ptr = sk_void_pop(dso->meth_data);
152 if (ptr == NULL) {
9311d0c4 153 ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE);
0f113f3e
MC
154 /*
155 * Should push the value back onto the stack in case of a retry.
156 */
157 sk_void_push(dso->meth_data, ptr);
26a7d938 158 return 0;
0f113f3e
MC
159 }
160 /* For now I'm not aware of any errors associated with dlclose() */
161 dlclose(ptr);
208fb891 162 return 1;
0f113f3e 163}
8f4fac7f 164
e9a68cfb 165static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
0f113f3e
MC
166{
167 void *ptr;
168 union {
169 DSO_FUNC_TYPE sym;
170 void *dlret;
171 } u;
172
173 if ((dso == NULL) || (symname == NULL)) {
9311d0c4 174 ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
26a7d938 175 return NULL;
0f113f3e
MC
176 }
177 if (sk_void_num(dso->meth_data) < 1) {
9311d0c4 178 ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR);
26a7d938 179 return NULL;
0f113f3e
MC
180 }
181 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
182 if (ptr == NULL) {
9311d0c4 183 ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE);
26a7d938 184 return NULL;
0f113f3e
MC
185 }
186 u.dlret = dlsym(ptr, symname);
187 if (u.dlret == NULL) {
a150f8e1
RL
188 ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE,
189 "symname(%s): %s", symname, dlerror());
26a7d938 190 return NULL;
0f113f3e
MC
191 }
192 return u.sym;
193}
8f4fac7f 194
cbecb3ac 195static char *dlfcn_merger(DSO *dso, const char *filespec1,
0f113f3e
MC
196 const char *filespec2)
197{
198 char *merged;
199
200 if (!filespec1 && !filespec2) {
9311d0c4 201 ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
26a7d938 202 return NULL;
0f113f3e
MC
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] == '/')) {
297002a3 209 merged = OPENSSL_strdup(filespec1);
90945fa3 210 if (merged == NULL) {
9311d0c4 211 ERR_raise(ERR_LIB_DSO, ERR_R_MALLOC_FAILURE);
26a7d938 212 return NULL;
0f113f3e 213 }
0f113f3e
MC
214 }
215 /*
216 * If the first file specification is missing, the second one rules.
217 */
218 else if (!filespec1) {
297002a3 219 merged = OPENSSL_strdup(filespec2);
90945fa3 220 if (merged == NULL) {
9311d0c4 221 ERR_raise(ERR_LIB_DSO, ERR_R_MALLOC_FAILURE);
26a7d938 222 return NULL;
0f113f3e 223 }
b7573c59 224 } else {
0f113f3e
MC
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 */
0f113f3e
MC
232 int spec2len, len;
233
234 spec2len = strlen(filespec2);
b7573c59 235 len = spec2len + strlen(filespec1);
0f113f3e 236
b7573c59 237 if (spec2len && filespec2[spec2len - 1] == '/') {
0f113f3e
MC
238 spec2len--;
239 len--;
240 }
241 merged = OPENSSL_malloc(len + 2);
90945fa3 242 if (merged == NULL) {
9311d0c4 243 ERR_raise(ERR_LIB_DSO, ERR_R_MALLOC_FAILURE);
26a7d938 244 return NULL;
0f113f3e
MC
245 }
246 strcpy(merged, filespec2);
247 merged[spec2len] = '/';
248 strcpy(&merged[spec2len + 1], filespec1);
249 }
26a7d938 250 return merged;
0f113f3e 251}
4a620922 252
51c8dc37 253static char *dlfcn_name_converter(DSO *dso, const char *filename)
0f113f3e
MC
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 */
e987f9f2 263 rsize += strlen(DSO_EXTENSION); /* The length of ".so" */
0f113f3e
MC
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) {
9311d0c4 269 ERR_raise(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED);
26a7d938 270 return NULL;
0f113f3e
MC
271 }
272 if (transform) {
273 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
e987f9f2 274 sprintf(translated, "lib%s" DSO_EXTENSION, filename);
0f113f3e 275 else
e987f9f2 276 sprintf(translated, "%s" DSO_EXTENSION, filename);
0f113f3e
MC
277 } else
278 sprintf(translated, "%s", filename);
26a7d938 279 return translated;
0f113f3e
MC
280}
281
282# ifdef __sgi
23a22b4c 283/*-
7ed87653
AP
284This 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.
c8e1edaa 295*/
0f113f3e
MC
296# include <rld_interface.h>
297# ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
298# define _RLD_INTERFACE_DLFCN_H_DLADDR
7ed87653 299typedef struct Dl_info {
0f113f3e
MC
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];
7ed87653 307} Dl_info;
0f113f3e 308# else
7ed87653 309typedef struct Dl_info Dl_info;
0f113f3e
MC
310# endif
311# define _RLD_DLADDR 14
7ed87653
AP
312
313static int dladdr(void *address, Dl_info *dl)
314{
0f113f3e
MC
315 void *v;
316 v = _rld_new_interface(_RLD_DLADDR, address, dl);
317 return (int)v;
318}
319# endif /* __sgi */
320
4af14b7b
MK
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
331typedef 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 */
d47eb76c 339static int dladdr(void *ptr, Dl_info *dl)
4af14b7b 340{
d47eb76c 341 uintptr_t addr = (uintptr_t)ptr;
4af14b7b
MK
342 unsigned int found = 0;
343 struct ld_info *ldinfos, *next_ldi, *this_ldi;
344
ea5def14 345 if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
4af14b7b
MK
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;
d47eb76c
MK
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)))) {
ea5def14
MK
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;
4af14b7b 379 found = 1;
ea5def14
MK
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 {
4af14b7b 394 errno = ENOMEM;
ea5def14 395 }
4af14b7b 396 } else {
ea5def14
MK
397 next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
398 this_ldi->ldinfo_next);
4af14b7b
MK
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
cb6ea61c
MC
406static 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);
4af14b7b
MK
424 if (sz <= 0) {
425# ifdef _AIX
d47eb76c 426 OPENSSL_free((void *)dli.dli_fname);
4af14b7b 427# endif
cb6ea61c 428 return len + 1;
4af14b7b 429 }
cb6ea61c
MC
430 if (len >= sz)
431 len = sz - 1;
432 memcpy(path, dli.dli_fname, len);
433 path[len++] = 0;
4af14b7b 434# ifdef _AIX
d47eb76c 435 OPENSSL_free((void *)dli.dli_fname);
4af14b7b 436# endif
cb6ea61c
MC
437 return len;
438 }
439
440 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
441# endif
442 return -1;
443}
444
c6cb42e4 445static void *dlfcn_globallookup(const char *name)
0f113f3e
MC
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 */