]> git.ipfire.org Git - thirdparty/openssl.git/blob - crypto/dso/dso_dlfcn.c
NonStop port updates for 3.0.0.
[thirdparty/openssl.git] / crypto / dso / dso_dlfcn.c
1 /*
2 * Copyright 2000-2020 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_local.h"
20 #include "e_os.h"
21
22 DEFINE_STACK_OF(void)
23
24 #ifdef DSO_DLFCN
25
26 # ifdef HAVE_DLFCN_H
27 # ifdef __osf__
28 # define __EXTENSIONS__
29 # endif
30 # include <dlfcn.h>
31 # define HAVE_DLINFO 1
32 # if defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
33 (defined(__osf__) && !defined(RTLD_NEXT)) || \
34 (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
35 defined(__ANDROID__) || defined(__TANDEM)
36 # undef HAVE_DLINFO
37 # endif
38 # endif
39
40 /* Part of the hack in "dlfcn_load" ... */
41 # define DSO_MAX_TRANSLATED_SIZE 256
42
43 static int dlfcn_load(DSO *dso);
44 static int dlfcn_unload(DSO *dso);
45 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
46 static char *dlfcn_name_converter(DSO *dso, const char *filename);
47 static char *dlfcn_merger(DSO *dso, const char *filespec1,
48 const char *filespec2);
49 static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
50 static void *dlfcn_globallookup(const char *name);
51
52 static DSO_METHOD dso_meth_dlfcn = {
53 "OpenSSL 'dlfcn' shared library method",
54 dlfcn_load,
55 dlfcn_unload,
56 dlfcn_bind_func,
57 NULL, /* ctrl */
58 dlfcn_name_converter,
59 dlfcn_merger,
60 NULL, /* init */
61 NULL, /* finish */
62 dlfcn_pathbyaddr,
63 dlfcn_globallookup
64 };
65
66 DSO_METHOD *DSO_METHOD_openssl(void)
67 {
68 return &dso_meth_dlfcn;
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
92
93 /*
94 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
95 * (void*) returned from dlopen().
96 */
97
98 static int dlfcn_load(DSO *dso)
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;
104 int saveerrno = get_last_sys_error();
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;
113 # endif
114 # ifdef _AIX
115 if (filename[strlen(filename) - 1] == ')')
116 flags |= RTLD_MEMBER;
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 }
124 /*
125 * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
126 * on a successful call.
127 */
128 set_sys_error(saveerrno);
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;
135 return 1;
136 err:
137 /* Cleanup! */
138 OPENSSL_free(filename);
139 if (ptr != NULL)
140 dlclose(ptr);
141 return 0;
142 }
143
144 static int dlfcn_unload(DSO *dso)
145 {
146 void *ptr;
147 if (dso == NULL) {
148 DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
149 return 0;
150 }
151 if (sk_void_num(dso->meth_data) < 1)
152 return 1;
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);
160 return 0;
161 }
162 /* For now I'm not aware of any errors associated with dlclose() */
163 dlclose(ptr);
164 return 1;
165 }
166
167 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
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);
177 return NULL;
178 }
179 if (sk_void_num(dso->meth_data) < 1) {
180 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
181 return NULL;
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);
186 return NULL;
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());
192 return NULL;
193 }
194 return u.sym;
195 }
196
197 static char *dlfcn_merger(DSO *dso, const char *filespec1,
198 const char *filespec2)
199 {
200 char *merged;
201
202 if (!filespec1 && !filespec2) {
203 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
204 return NULL;
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] == '/')) {
211 merged = OPENSSL_strdup(filespec1);
212 if (merged == NULL) {
213 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
214 return NULL;
215 }
216 }
217 /*
218 * If the first file specification is missing, the second one rules.
219 */
220 else if (!filespec1) {
221 merged = OPENSSL_strdup(filespec2);
222 if (merged == NULL) {
223 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
224 return NULL;
225 }
226 } else {
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 */
234 int spec2len, len;
235
236 spec2len = strlen(filespec2);
237 len = spec2len + strlen(filespec1);
238
239 if (spec2len && filespec2[spec2len - 1] == '/') {
240 spec2len--;
241 len--;
242 }
243 merged = OPENSSL_malloc(len + 2);
244 if (merged == NULL) {
245 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
246 return NULL;
247 }
248 strcpy(merged, filespec2);
249 merged[spec2len] = '/';
250 strcpy(&merged[spec2len + 1], filespec1);
251 }
252 return merged;
253 }
254
255 static char *dlfcn_name_converter(DSO *dso, const char *filename)
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 */
265 rsize += strlen(DSO_EXTENSION); /* The length of ".so" */
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);
272 return NULL;
273 }
274 if (transform) {
275 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
276 sprintf(translated, "lib%s" DSO_EXTENSION, filename);
277 else
278 sprintf(translated, "%s" DSO_EXTENSION, filename);
279 } else
280 sprintf(translated, "%s", filename);
281 return translated;
282 }
283
284 # ifdef __sgi
285 /*-
286 This 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.
297 */
298 # include <rld_interface.h>
299 # ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
300 # define _RLD_INTERFACE_DLFCN_H_DLADDR
301 typedef struct Dl_info {
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];
309 } Dl_info;
310 # else
311 typedef struct Dl_info Dl_info;
312 # endif
313 # define _RLD_DLADDR 14
314
315 static int dladdr(void *address, Dl_info *dl)
316 {
317 void *v;
318 v = _rld_new_interface(_RLD_DLADDR, address, dl);
319 return (int)v;
320 }
321 # endif /* __sgi */
322
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
333 typedef 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 */
341 static int dladdr(void *ptr, Dl_info *dl)
342 {
343 uintptr_t addr = (uintptr_t)ptr;
344 unsigned int found = 0;
345 struct ld_info *ldinfos, *next_ldi, *this_ldi;
346
347 if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
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;
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)))) {
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;
381 found = 1;
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 {
396 errno = ENOMEM;
397 }
398 } else {
399 next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
400 this_ldi->ldinfo_next);
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
408 static 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);
426 if (sz <= 0) {
427 # ifdef _AIX
428 OPENSSL_free((void *)dli.dli_fname);
429 # endif
430 return len + 1;
431 }
432 if (len >= sz)
433 len = sz - 1;
434 memcpy(path, dli.dli_fname, len);
435 path[len++] = 0;
436 # ifdef _AIX
437 OPENSSL_free((void *)dli.dli_fname);
438 # endif
439 return len;
440 }
441
442 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
443 # endif
444 return -1;
445 }
446
447 static void *dlfcn_globallookup(const char *name)
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 */