2 * Copyright 2000-2018 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 */
26 # define __EXTENSIONS__
29 # define HAVE_DLINFO 1
30 # if defined(__CYGWIN__) || \
31 defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
32 (defined(__osf__) && !defined(RTLD_NEXT)) || \
33 (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
39 /* Part of the hack in "dlfcn_load" ... */
40 # define DSO_MAX_TRANSLATED_SIZE 256
42 static int dlfcn_load(DSO *dso);
43 static int dlfcn_unload(DSO *dso);
44 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
45 static char *dlfcn_name_converter(DSO *dso, const char *filename);
46 static char *dlfcn_merger(DSO *dso, const char *filespec1,
47 const char *filespec2);
48 static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
49 static void *dlfcn_globallookup(const char *name);
51 static DSO_METHOD dso_meth_dlfcn = {
52 "OpenSSL 'dlfcn' shared library method",
65 DSO_METHOD *DSO_METHOD_openssl(void)
67 return &dso_meth_dlfcn;
71 * Prior to using the dlopen() function, we should decide on the flag we
72 * send. There's a few different ways of doing this and it's a messy
73 * venn-diagram to match up which platforms support what. So as we don't have
74 * autoconf yet, I'm implementing a hack that could be hacked further
75 * relatively easily to deal with cases as we find them. Initially this is to
78 # if defined(__OpenBSD__) || defined(__NetBSD__)
80 # define DLOPEN_FLAG DL_LAZY
83 # define DLOPEN_FLAG RTLD_NOW
85 # define DLOPEN_FLAG 0
89 # define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
93 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
94 * (void*) returned from dlopen().
97 static int dlfcn_load(DSO *dso)
100 /* See applicable comments in dso_dl.c */
101 char *filename = DSO_convert_filename(dso, NULL);
102 int flags = DLOPEN_FLAG;
103 int saveerrno = get_last_sys_error();
105 if (filename == NULL) {
106 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
110 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
111 flags |= RTLD_GLOBAL;
114 if (filename[strlen(filename) - 1] == ')')
115 flags |= RTLD_MEMBER;
117 ptr = dlopen(filename, flags);
119 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
120 ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
124 * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
125 * on a successful call.
127 set_sys_error(saveerrno);
128 if (!sk_void_push(dso->meth_data, (char *)ptr)) {
129 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
133 dso->loaded_filename = filename;
137 OPENSSL_free(filename);
143 static int dlfcn_unload(DSO *dso)
147 DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
150 if (sk_void_num(dso->meth_data) < 1)
152 ptr = sk_void_pop(dso->meth_data);
154 DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
156 * Should push the value back onto the stack in case of a retry.
158 sk_void_push(dso->meth_data, ptr);
161 /* For now I'm not aware of any errors associated with dlclose() */
166 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
174 if ((dso == NULL) || (symname == NULL)) {
175 DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
178 if (sk_void_num(dso->meth_data) < 1) {
179 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
182 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
184 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
187 u.dlret = dlsym(ptr, symname);
188 if (u.dlret == NULL) {
189 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
190 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
196 static char *dlfcn_merger(DSO *dso, const char *filespec1,
197 const char *filespec2)
201 if (!filespec1 && !filespec2) {
202 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
206 * If the first file specification is a rooted path, it rules. same goes
207 * if the second file specification is missing.
209 if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
210 merged = OPENSSL_strdup(filespec1);
211 if (merged == NULL) {
212 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
217 * If the first file specification is missing, the second one rules.
219 else if (!filespec1) {
220 merged = OPENSSL_strdup(filespec2);
221 if (merged == NULL) {
222 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
227 * This part isn't as trivial as it looks. It assumes that the
228 * second file specification really is a directory, and makes no
229 * checks whatsoever. Therefore, the result becomes the
230 * concatenation of filespec2 followed by a slash followed by
235 spec2len = strlen(filespec2);
236 len = spec2len + strlen(filespec1);
238 if (spec2len && filespec2[spec2len - 1] == '/') {
242 merged = OPENSSL_malloc(len + 2);
243 if (merged == NULL) {
244 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
247 strcpy(merged, filespec2);
248 merged[spec2len] = '/';
249 strcpy(&merged[spec2len + 1], filespec1);
254 static char *dlfcn_name_converter(DSO *dso, const char *filename)
257 int len, rsize, transform;
259 len = strlen(filename);
261 transform = (strstr(filename, "/") == NULL);
263 /* We will convert this to "%s.so" or "lib%s.so" etc */
264 rsize += strlen(DSO_EXTENSION); /* The length of ".so" */
265 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
266 rsize += 3; /* The length of "lib" */
268 translated = OPENSSL_malloc(rsize);
269 if (translated == NULL) {
270 DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
274 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
275 sprintf(translated, "lib%s" DSO_EXTENSION, filename);
277 sprintf(translated, "%s" DSO_EXTENSION, filename);
279 sprintf(translated, "%s", filename);
285 This is a quote from IRIX manual for dladdr(3c):
287 <dlfcn.h> does not contain a prototype for dladdr or definition of
288 Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional,
289 but contains no dladdr prototype and no IRIX library contains an
290 implementation. Write your own declaration based on the code below.
292 The following code is dependent on internal interfaces that are not
293 part of the IRIX compatibility guarantee; however, there is no future
294 intention to change this interface, so on a practical level, the code
295 below is safe to use on IRIX.
297 # include <rld_interface.h>
298 # ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
299 # define _RLD_INTERFACE_DLFCN_H_DLADDR
300 typedef struct Dl_info {
301 const char *dli_fname;
303 const char *dli_sname;
307 long dli_reserved[4];
310 typedef struct Dl_info Dl_info;
312 # define _RLD_DLADDR 14
314 static int dladdr(void *address, Dl_info *dl)
317 v = _rld_new_interface(_RLD_DLADDR, address, dl);
324 * See IBM's AIX Version 7.2, Technical Reference:
325 * Base Operating System and Extensions, Volume 1 and 2
326 * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
328 # include <sys/ldr.h>
330 /* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
331 # define DLFCN_LDINFO_SIZE 86976
332 typedef struct Dl_info {
333 const char *dli_fname;
336 * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
337 * address of a function, which is just located in the DATA segment instead of
340 static int dladdr(void *ptr, Dl_info *dl)
342 uintptr_t addr = (uintptr_t)ptr;
343 unsigned int found = 0;
344 struct ld_info *ldinfos, *next_ldi, *this_ldi;
346 if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
348 dl->dli_fname = NULL;
352 if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
354 * Error handling is done through errno and dlerror() reading errno:
355 * ENOMEM (ldinfos buffer is too small),
356 * EINVAL (invalid flags),
357 * EFAULT (invalid ldinfos ptr)
359 OPENSSL_free((void *)ldinfos);
360 dl->dli_fname = NULL;
367 if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
368 && (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
369 this_ldi->ldinfo_textsize)))
370 || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
371 && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
372 this_ldi->ldinfo_datasize)))) {
373 char *buffer, *member;
374 size_t buffer_sz, member_len;
376 buffer_sz = strlen(this_ldi->ldinfo_filename) + 1;
377 member = this_ldi->ldinfo_filename + buffer_sz;
378 if ((member_len = strlen(member)) > 0)
379 buffer_sz += 1 + member_len + 1;
381 if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) {
382 OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
383 if (member_len > 0) {
385 * Need to respect a possible member name and not just
386 * returning the path name in this case. See docs:
387 * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER.
389 OPENSSL_strlcat(buffer, "(", buffer_sz);
390 OPENSSL_strlcat(buffer, member, buffer_sz);
391 OPENSSL_strlcat(buffer, ")", buffer_sz);
393 dl->dli_fname = buffer;
398 next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
399 this_ldi->ldinfo_next);
401 } while (this_ldi->ldinfo_next && !found);
402 OPENSSL_free((void *)ldinfos);
403 return (found && dl->dli_fname != NULL);
407 static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
415 int (*f) (void *, char *, int);
423 if (dladdr(addr, &dli)) {
424 len = (int)strlen(dli.dli_fname);
427 OPENSSL_free((void *)dli.dli_fname);
433 memcpy(path, dli.dli_fname, len);
436 OPENSSL_free((void *)dli.dli_fname);
441 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
446 static void *dlfcn_globallookup(const char *name)
448 void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
451 ret = dlsym(handle, name);
457 #endif /* DSO_DLFCN */