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 */
25 # define __EXTENSIONS__
28 # define HAVE_DLINFO 1
29 # if defined(__CYGWIN__) || \
30 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;
103 if (filename == NULL) {
104 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
108 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
109 flags |= RTLD_GLOBAL;
112 if (filename[strlen(filename) - 1] == ')')
113 flags |= RTLD_MEMBER;
115 ptr = dlopen(filename, flags);
117 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
118 ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
121 if (!sk_void_push(dso->meth_data, (char *)ptr)) {
122 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
126 dso->loaded_filename = filename;
130 OPENSSL_free(filename);
136 static int dlfcn_unload(DSO *dso)
140 DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
143 if (sk_void_num(dso->meth_data) < 1)
145 ptr = sk_void_pop(dso->meth_data);
147 DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
149 * Should push the value back onto the stack in case of a retry.
151 sk_void_push(dso->meth_data, ptr);
154 /* For now I'm not aware of any errors associated with dlclose() */
159 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
167 if ((dso == NULL) || (symname == NULL)) {
168 DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
171 if (sk_void_num(dso->meth_data) < 1) {
172 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
175 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
177 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
180 u.dlret = dlsym(ptr, symname);
181 if (u.dlret == NULL) {
182 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
183 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
189 static char *dlfcn_merger(DSO *dso, const char *filespec1,
190 const char *filespec2)
194 if (!filespec1 && !filespec2) {
195 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
199 * If the first file specification is a rooted path, it rules. same goes
200 * if the second file specification is missing.
202 if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
203 merged = OPENSSL_strdup(filespec1);
204 if (merged == NULL) {
205 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
210 * If the first file specification is missing, the second one rules.
212 else if (!filespec1) {
213 merged = OPENSSL_strdup(filespec2);
214 if (merged == NULL) {
215 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
220 * This part isn't as trivial as it looks. It assumes that the
221 * second file specification really is a directory, and makes no
222 * checks whatsoever. Therefore, the result becomes the
223 * concatenation of filespec2 followed by a slash followed by
228 spec2len = strlen(filespec2);
229 len = spec2len + strlen(filespec1);
231 if (spec2len && filespec2[spec2len - 1] == '/') {
235 merged = OPENSSL_malloc(len + 2);
236 if (merged == NULL) {
237 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
240 strcpy(merged, filespec2);
241 merged[spec2len] = '/';
242 strcpy(&merged[spec2len + 1], filespec1);
247 static char *dlfcn_name_converter(DSO *dso, const char *filename)
250 int len, rsize, transform;
252 len = strlen(filename);
254 transform = (strstr(filename, "/") == NULL);
256 /* We will convert this to "%s.so" or "lib%s.so" etc */
257 rsize += strlen(DSO_EXTENSION); /* The length of ".so" */
258 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
259 rsize += 3; /* The length of "lib" */
261 translated = OPENSSL_malloc(rsize);
262 if (translated == NULL) {
263 DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
267 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
268 sprintf(translated, "lib%s" DSO_EXTENSION, filename);
270 sprintf(translated, "%s" DSO_EXTENSION, filename);
272 sprintf(translated, "%s", filename);
278 This is a quote from IRIX manual for dladdr(3c):
280 <dlfcn.h> does not contain a prototype for dladdr or definition of
281 Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional,
282 but contains no dladdr prototype and no IRIX library contains an
283 implementation. Write your own declaration based on the code below.
285 The following code is dependent on internal interfaces that are not
286 part of the IRIX compatibility guarantee; however, there is no future
287 intention to change this interface, so on a practical level, the code
288 below is safe to use on IRIX.
290 # include <rld_interface.h>
291 # ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
292 # define _RLD_INTERFACE_DLFCN_H_DLADDR
293 typedef struct Dl_info {
294 const char *dli_fname;
296 const char *dli_sname;
300 long dli_reserved[4];
303 typedef struct Dl_info Dl_info;
305 # define _RLD_DLADDR 14
307 static int dladdr(void *address, Dl_info *dl)
310 v = _rld_new_interface(_RLD_DLADDR, address, dl);
317 * See IBM's AIX Version 7.2, Technical Reference:
318 * Base Operating System and Extensions, Volume 1 and 2
319 * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
321 # include <sys/ldr.h>
323 /* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
324 # define DLFCN_LDINFO_SIZE 86976
325 typedef struct Dl_info {
326 const char *dli_fname;
329 * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
330 * address of a function, which is just located in the DATA segment instead of
333 static int dladdr(void *ptr, Dl_info *dl)
335 uintptr_t addr = (uintptr_t)ptr;
336 unsigned int found = 0;
337 struct ld_info *ldinfos, *next_ldi, *this_ldi;
339 if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
341 dl->dli_fname = NULL;
345 if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
347 * Error handling is done through errno and dlerror() reading errno:
348 * ENOMEM (ldinfos buffer is too small),
349 * EINVAL (invalid flags),
350 * EFAULT (invalid ldinfos ptr)
352 OPENSSL_free((void *)ldinfos);
353 dl->dli_fname = NULL;
360 if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
361 && (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
362 this_ldi->ldinfo_textsize)))
363 || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
364 && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
365 this_ldi->ldinfo_datasize)))) {
366 char *buffer, *member;
367 size_t buffer_sz, member_len;
369 buffer_sz = strlen(this_ldi->ldinfo_filename) + 1;
370 member = this_ldi->ldinfo_filename + buffer_sz;
371 if ((member_len = strlen(member)) > 0)
372 buffer_sz += 1 + member_len + 1;
374 if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) {
375 OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
376 if (member_len > 0) {
378 * Need to respect a possible member name and not just
379 * returning the path name in this case. See docs:
380 * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER.
382 OPENSSL_strlcat(buffer, "(", buffer_sz);
383 OPENSSL_strlcat(buffer, member, buffer_sz);
384 OPENSSL_strlcat(buffer, ")", buffer_sz);
386 dl->dli_fname = buffer;
391 next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
392 this_ldi->ldinfo_next);
394 } while (this_ldi->ldinfo_next && !found);
395 OPENSSL_free((void *)ldinfos);
396 return (found && dl->dli_fname != NULL);
400 static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
408 int (*f) (void *, char *, int);
416 if (dladdr(addr, &dli)) {
417 len = (int)strlen(dli.dli_fname);
420 OPENSSL_free((void *)dli.dli_fname);
426 memcpy(path, dli.dli_fname, len);
429 OPENSSL_free((void *)dli.dli_fname);
434 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
439 static void *dlfcn_globallookup(const char *name)
441 void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
444 ret = dlsym(handle, name);
450 #endif /* DSO_DLFCN */