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