2 * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved.
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
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"
28 # define __EXTENSIONS__
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)) || \
40 /* Part of the hack in "dlfcn_load" ... */
41 # define DSO_MAX_TRANSLATED_SIZE 256
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
);
52 static DSO_METHOD dso_meth_dlfcn
= {
53 "OpenSSL 'dlfcn' shared library method",
66 DSO_METHOD
*DSO_METHOD_openssl(void)
68 return &dso_meth_dlfcn
;
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
79 # if defined(__OpenBSD__) || defined(__NetBSD__)
81 # define DLOPEN_FLAG DL_LAZY
84 # define DLOPEN_FLAG RTLD_NOW
86 # define DLOPEN_FLAG 0
90 # define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
94 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
95 * (void*) returned from dlopen().
98 static int dlfcn_load(DSO
*dso
)
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();
106 if (filename
== NULL
) {
107 DSOerr(DSO_F_DLFCN_LOAD
, DSO_R_NO_FILENAME
);
111 if (dso
->flags
& DSO_FLAG_GLOBAL_SYMBOLS
)
112 flags
|= RTLD_GLOBAL
;
115 if (filename
[strlen(filename
) - 1] == ')')
116 flags
|= RTLD_MEMBER
;
118 ptr
= dlopen(filename
, flags
);
120 DSOerr(DSO_F_DLFCN_LOAD
, DSO_R_LOAD_FAILED
);
121 ERR_add_error_data(4, "filename(", filename
, "): ", dlerror());
125 * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
126 * on a successful call.
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
);
134 dso
->loaded_filename
= filename
;
138 OPENSSL_free(filename
);
144 static int dlfcn_unload(DSO
*dso
)
148 DSOerr(DSO_F_DLFCN_UNLOAD
, ERR_R_PASSED_NULL_PARAMETER
);
151 if (sk_void_num(dso
->meth_data
) < 1)
153 ptr
= sk_void_pop(dso
->meth_data
);
155 DSOerr(DSO_F_DLFCN_UNLOAD
, DSO_R_NULL_HANDLE
);
157 * Should push the value back onto the stack in case of a retry.
159 sk_void_push(dso
->meth_data
, ptr
);
162 /* For now I'm not aware of any errors associated with dlclose() */
167 static DSO_FUNC_TYPE
dlfcn_bind_func(DSO
*dso
, const char *symname
)
175 if ((dso
== NULL
) || (symname
== NULL
)) {
176 DSOerr(DSO_F_DLFCN_BIND_FUNC
, ERR_R_PASSED_NULL_PARAMETER
);
179 if (sk_void_num(dso
->meth_data
) < 1) {
180 DSOerr(DSO_F_DLFCN_BIND_FUNC
, DSO_R_STACK_ERROR
);
183 ptr
= sk_void_value(dso
->meth_data
, sk_void_num(dso
->meth_data
) - 1);
185 DSOerr(DSO_F_DLFCN_BIND_FUNC
, DSO_R_NULL_HANDLE
);
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());
197 static char *dlfcn_merger(DSO
*dso
, const char *filespec1
,
198 const char *filespec2
)
202 if (!filespec1
&& !filespec2
) {
203 DSOerr(DSO_F_DLFCN_MERGER
, ERR_R_PASSED_NULL_PARAMETER
);
207 * If the first file specification is a rooted path, it rules. same goes
208 * if the second file specification is missing.
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
);
218 * If the first file specification is missing, the second one rules.
220 else if (!filespec1
) {
221 merged
= OPENSSL_strdup(filespec2
);
222 if (merged
== NULL
) {
223 DSOerr(DSO_F_DLFCN_MERGER
, ERR_R_MALLOC_FAILURE
);
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
236 spec2len
= strlen(filespec2
);
237 len
= spec2len
+ strlen(filespec1
);
239 if (spec2len
&& filespec2
[spec2len
- 1] == '/') {
243 merged
= OPENSSL_malloc(len
+ 2);
244 if (merged
== NULL
) {
245 DSOerr(DSO_F_DLFCN_MERGER
, ERR_R_MALLOC_FAILURE
);
248 strcpy(merged
, filespec2
);
249 merged
[spec2len
] = '/';
250 strcpy(&merged
[spec2len
+ 1], filespec1
);
255 static char *dlfcn_name_converter(DSO
*dso
, const char *filename
)
258 int len
, rsize
, transform
;
260 len
= strlen(filename
);
262 transform
= (strstr(filename
, "/") == NULL
);
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" */
269 translated
= OPENSSL_malloc(rsize
);
270 if (translated
== NULL
) {
271 DSOerr(DSO_F_DLFCN_NAME_CONVERTER
, DSO_R_NAME_TRANSLATION_FAILED
);
275 if ((DSO_flags(dso
) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY
) == 0)
276 sprintf(translated
, "lib%s" DSO_EXTENSION
, filename
);
278 sprintf(translated
, "%s" DSO_EXTENSION
, filename
);
280 sprintf(translated
, "%s", filename
);
286 This is a quote from IRIX manual for dladdr(3c):
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.
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.
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
;
304 const char *dli_sname
;
308 long dli_reserved
[4];
311 typedef struct Dl_info Dl_info
;
313 # define _RLD_DLADDR 14
315 static int dladdr(void *address
, Dl_info
*dl
)
318 v
= _rld_new_interface(_RLD_DLADDR
, address
, dl
);
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
329 # include <sys/ldr.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
;
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
341 static int dladdr(void *ptr
, Dl_info
*dl
)
343 uintptr_t addr
= (uintptr_t)ptr
;
344 unsigned int found
= 0;
345 struct ld_info
*ldinfos
, *next_ldi
, *this_ldi
;
347 if ((ldinfos
= OPENSSL_malloc(DLFCN_LDINFO_SIZE
)) == NULL
) {
349 dl
->dli_fname
= NULL
;
353 if ((loadquery(L_GETINFO
, (void *)ldinfos
, DLFCN_LDINFO_SIZE
)) < 0) {
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)
360 OPENSSL_free((void *)ldinfos
);
361 dl
->dli_fname
= NULL
;
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
;
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;
382 if ((buffer
= OPENSSL_malloc(buffer_sz
)) != NULL
) {
383 OPENSSL_strlcpy(buffer
, this_ldi
->ldinfo_filename
, buffer_sz
);
384 if (member_len
> 0) {
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.
390 OPENSSL_strlcat(buffer
, "(", buffer_sz
);
391 OPENSSL_strlcat(buffer
, member
, buffer_sz
);
392 OPENSSL_strlcat(buffer
, ")", buffer_sz
);
394 dl
->dli_fname
= buffer
;
399 next_ldi
= (struct ld_info
*)((uintptr_t)this_ldi
+
400 this_ldi
->ldinfo_next
);
402 } while (this_ldi
->ldinfo_next
&& !found
);
403 OPENSSL_free((void *)ldinfos
);
404 return (found
&& dl
->dli_fname
!= NULL
);
408 static int dlfcn_pathbyaddr(void *addr
, char *path
, int sz
)
416 int (*f
) (void *, char *, int);
424 if (dladdr(addr
, &dli
)) {
425 len
= (int)strlen(dli
.dli_fname
);
428 OPENSSL_free((void *)dli
.dli_fname
);
434 memcpy(path
, dli
.dli_fname
, len
);
437 OPENSSL_free((void *)dli
.dli_fname
);
442 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
447 static void *dlfcn_globallookup(const char *name
)
449 void *ret
= NULL
, *handle
= dlopen(NULL
, RTLD_LAZY
);
452 ret
= dlsym(handle
, name
);
458 #endif /* DSO_DLFCN */