2 * Copyright 2000-2019 The OpenSSL Project Authors. All Rights Reserved.
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
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.
16 # define _GNU_SOURCE /* make sure dladdr is declared */
19 #include "dso_local.h"
26 # define __EXTENSIONS__
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)) || \
38 /* Part of the hack in "dlfcn_load" ... */
39 # define DSO_MAX_TRANSLATED_SIZE 256
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);
50 static DSO_METHOD dso_meth_dlfcn = {
51 "OpenSSL 'dlfcn' shared library method",
64 DSO_METHOD *DSO_METHOD_openssl(void)
66 return &dso_meth_dlfcn;
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
77 # if defined(__OpenBSD__) || defined(__NetBSD__)
79 # define DLOPEN_FLAG DL_LAZY
82 # define DLOPEN_FLAG RTLD_NOW
84 # define DLOPEN_FLAG 0
88 # define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
92 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
93 * (void*) returned from dlopen().
96 static int dlfcn_load(DSO *dso)
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();
104 if (filename == NULL) {
105 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
109 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
110 flags |= RTLD_GLOBAL;
113 if (filename[strlen(filename) - 1] == ')')
114 flags |= RTLD_MEMBER;
116 ptr = dlopen(filename, flags);
118 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
119 ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
123 * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
124 * on a successful call.
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);
132 dso->loaded_filename = filename;
136 OPENSSL_free(filename);
142 static int dlfcn_unload(DSO *dso)
146 DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
149 if (sk_void_num(dso->meth_data) < 1)
151 ptr = sk_void_pop(dso->meth_data);
153 DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
155 * Should push the value back onto the stack in case of a retry.
157 sk_void_push(dso->meth_data, ptr);
160 /* For now I'm not aware of any errors associated with dlclose() */
165 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
173 if ((dso == NULL) || (symname == NULL)) {
174 DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
177 if (sk_void_num(dso->meth_data) < 1) {
178 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
181 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
183 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
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());
195 static char *dlfcn_merger(DSO *dso, const char *filespec1,
196 const char *filespec2)
200 if (!filespec1 && !filespec2) {
201 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
205 * If the first file specification is a rooted path, it rules. same goes
206 * if the second file specification is missing.
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);
216 * If the first file specification is missing, the second one rules.
218 else if (!filespec1) {
219 merged = OPENSSL_strdup(filespec2);
220 if (merged == NULL) {
221 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
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
234 spec2len = strlen(filespec2);
235 len = spec2len + strlen(filespec1);
237 if (spec2len && filespec2[spec2len - 1] == '/') {
241 merged = OPENSSL_malloc(len + 2);
242 if (merged == NULL) {
243 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
246 strcpy(merged, filespec2);
247 merged[spec2len] = '/';
248 strcpy(&merged[spec2len + 1], filespec1);
253 static char *dlfcn_name_converter(DSO *dso, const char *filename)
256 int len, rsize, transform;
258 len = strlen(filename);
260 transform = (strstr(filename, "/") == NULL);
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" */
267 translated = OPENSSL_malloc(rsize);
268 if (translated == NULL) {
269 DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
273 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
274 sprintf(translated, "lib%s" DSO_EXTENSION, filename);
276 sprintf(translated, "%s" DSO_EXTENSION, filename);
278 sprintf(translated, "%s", filename);
284 This is a quote from IRIX manual for dladdr(3c):
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.
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.
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;
302 const char *dli_sname;
306 long dli_reserved[4];
309 typedef struct Dl_info Dl_info;
311 # define _RLD_DLADDR 14
313 static int dladdr(void *address, Dl_info *dl)
316 v = _rld_new_interface(_RLD_DLADDR, address, dl);
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
327 # include <sys/ldr.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;
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
339 static int dladdr(void *ptr, Dl_info *dl)
341 uintptr_t addr = (uintptr_t)ptr;
342 unsigned int found = 0;
343 struct ld_info *ldinfos, *next_ldi, *this_ldi;
345 if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
347 dl->dli_fname = NULL;
351 if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
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)
358 OPENSSL_free((void *)ldinfos);
359 dl->dli_fname = NULL;
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;
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;
380 if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) {
381 OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
382 if (member_len > 0) {
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.
388 OPENSSL_strlcat(buffer, "(", buffer_sz);
389 OPENSSL_strlcat(buffer, member, buffer_sz);
390 OPENSSL_strlcat(buffer, ")", buffer_sz);
392 dl->dli_fname = buffer;
397 next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
398 this_ldi->ldinfo_next);
400 } while (this_ldi->ldinfo_next && !found);
401 OPENSSL_free((void *)ldinfos);
402 return (found && dl->dli_fname != NULL);
406 static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
414 int (*f) (void *, char *, int);
422 if (dladdr(addr, &dli)) {
423 len = (int)strlen(dli.dli_fname);
426 OPENSSL_free((void *)dli.dli_fname);
432 memcpy(path, dli.dli_fname, len);
435 OPENSSL_free((void *)dli.dli_fname);
440 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
445 static void *dlfcn_globallookup(const char *name)
447 void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
450 ret = dlsym(handle, name);
456 #endif /* DSO_DLFCN */