]> git.ipfire.org Git - thirdparty/openssl.git/blame - crypto/dso/dso_dlfcn.c
Add a CHANGES entry for the unrecognised record type change
[thirdparty/openssl.git] / crypto / dso / dso_dlfcn.c
CommitLineData
0f113f3e 1/*
d2e9e320 2 * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
8f4fac7f 3 *
d2e9e320
RS
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
8f4fac7f
GT
8 */
9
0f113f3e
MC
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 */
c1844095 15#ifndef _GNU_SOURCE
0f113f3e 16# define _GNU_SOURCE /* make sure dladdr is declared */
3ecbd099
RL
17#endif
18
73decf59 19#include "dso_locl.h"
8f4fac7f 20
38186bfd 21#ifdef DSO_DLFCN
8f4fac7f 22
0f113f3e
MC
23# ifdef HAVE_DLFCN_H
24# ifdef __osf__
25# define __EXTENSIONS__
26# endif
27# include <dlfcn.h>
28# define HAVE_DLINFO 1
29# if defined(_AIX) || defined(__CYGWIN__) || \
73133962 30 defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
afb41913 31 (defined(__osf__) && !defined(RTLD_NEXT)) || \
7a4ec19a 32 (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
0f113f3e
MC
33 defined(__ANDROID__)
34# undef HAVE_DLINFO
35# endif
73133962 36# endif
8f4fac7f 37
b9e63915 38/* Part of the hack in "dlfcn_load" ... */
0f113f3e 39# define DSO_MAX_TRANSLATED_SIZE 256
b9e63915 40
51c8dc37 41static int dlfcn_load(DSO *dso);
8f4fac7f 42static int dlfcn_unload(DSO *dso);
e9a68cfb 43static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
51c8dc37 44static char *dlfcn_name_converter(DSO *dso, const char *filename);
cbecb3ac 45static char *dlfcn_merger(DSO *dso, const char *filespec1,
0f113f3e 46 const char *filespec2);
c6cb42e4 47static void *dlfcn_globallookup(const char *name);
8f4fac7f
GT
48
49static DSO_METHOD dso_meth_dlfcn = {
0f113f3e
MC
50 "OpenSSL 'dlfcn' shared library method",
51 dlfcn_load,
52 dlfcn_unload,
0f113f3e 53 dlfcn_bind_func,
0f113f3e
MC
54 NULL, /* ctrl */
55 dlfcn_name_converter,
56 dlfcn_merger,
57 NULL, /* init */
58 NULL, /* finish */
0f113f3e
MC
59 dlfcn_globallookup
60};
8f4fac7f 61
38186bfd 62DSO_METHOD *DSO_METHOD_openssl(void)
0f113f3e 63{
38186bfd 64 return &dso_meth_dlfcn;
0f113f3e
MC
65}
66
67/*
68 * Prior to using the dlopen() function, we should decide on the flag we
69 * send. There's a few different ways of doing this and it's a messy
70 * venn-diagram to match up which platforms support what. So as we don't have
71 * autoconf yet, I'm implementing a hack that could be hacked further
72 * relatively easily to deal with cases as we find them. Initially this is to
73 * cope with OpenBSD.
74 */
75# if defined(__OpenBSD__) || defined(__NetBSD__)
76# ifdef DL_LAZY
77# define DLOPEN_FLAG DL_LAZY
78# else
79# ifdef RTLD_NOW
80# define DLOPEN_FLAG RTLD_NOW
81# else
82# define DLOPEN_FLAG 0
83# endif
84# endif
85# else
86# define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
87# endif
1a797ac6 88
0f113f3e
MC
89/*
90 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
91 * (void*) returned from dlopen().
8f4fac7f
GT
92 */
93
51c8dc37 94static int dlfcn_load(DSO *dso)
0f113f3e
MC
95{
96 void *ptr = NULL;
97 /* See applicable comments in dso_dl.c */
98 char *filename = DSO_convert_filename(dso, NULL);
99 int flags = DLOPEN_FLAG;
100
101 if (filename == NULL) {
102 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
103 goto err;
104 }
105# ifdef RTLD_GLOBAL
106 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
107 flags |= RTLD_GLOBAL;
108# endif
109 ptr = dlopen(filename, flags);
110 if (ptr == NULL) {
111 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
112 ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
113 goto err;
114 }
115 if (!sk_void_push(dso->meth_data, (char *)ptr)) {
116 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
117 goto err;
118 }
119 /* Success */
120 dso->loaded_filename = filename;
121 return (1);
122 err:
123 /* Cleanup! */
b548a1f1 124 OPENSSL_free(filename);
0f113f3e
MC
125 if (ptr != NULL)
126 dlclose(ptr);
127 return (0);
51c8dc37 128}
8f4fac7f
GT
129
130static int dlfcn_unload(DSO *dso)
0f113f3e
MC
131{
132 void *ptr;
133 if (dso == NULL) {
134 DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
135 return (0);
136 }
137 if (sk_void_num(dso->meth_data) < 1)
138 return (1);
139 ptr = sk_void_pop(dso->meth_data);
140 if (ptr == NULL) {
141 DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
142 /*
143 * Should push the value back onto the stack in case of a retry.
144 */
145 sk_void_push(dso->meth_data, ptr);
146 return (0);
147 }
148 /* For now I'm not aware of any errors associated with dlclose() */
149 dlclose(ptr);
150 return (1);
151}
8f4fac7f 152
e9a68cfb 153static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
0f113f3e
MC
154{
155 void *ptr;
156 union {
157 DSO_FUNC_TYPE sym;
158 void *dlret;
159 } u;
160
161 if ((dso == NULL) || (symname == NULL)) {
162 DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
163 return (NULL);
164 }
165 if (sk_void_num(dso->meth_data) < 1) {
166 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
167 return (NULL);
168 }
169 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
170 if (ptr == NULL) {
171 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
172 return (NULL);
173 }
174 u.dlret = dlsym(ptr, symname);
175 if (u.dlret == NULL) {
176 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
177 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
178 return (NULL);
179 }
180 return u.sym;
181}
8f4fac7f 182
cbecb3ac 183static char *dlfcn_merger(DSO *dso, const char *filespec1,
0f113f3e
MC
184 const char *filespec2)
185{
186 char *merged;
187
188 if (!filespec1 && !filespec2) {
189 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
190 return (NULL);
191 }
192 /*
193 * If the first file specification is a rooted path, it rules. same goes
194 * if the second file specification is missing.
195 */
196 if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
a89c9a0d 197 merged = OPENSSL_strdup(filespec1);
90945fa3 198 if (merged == NULL) {
0f113f3e
MC
199 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
200 return (NULL);
201 }
0f113f3e
MC
202 }
203 /*
204 * If the first file specification is missing, the second one rules.
205 */
206 else if (!filespec1) {
a89c9a0d 207 merged = OPENSSL_strdup(filespec2);
90945fa3 208 if (merged == NULL) {
0f113f3e
MC
209 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
210 return (NULL);
211 }
b7573c59 212 } else {
0f113f3e
MC
213 /*
214 * This part isn't as trivial as it looks. It assumes that the
215 * second file specification really is a directory, and makes no
216 * checks whatsoever. Therefore, the result becomes the
217 * concatenation of filespec2 followed by a slash followed by
218 * filespec1.
219 */
0f113f3e
MC
220 int spec2len, len;
221
222 spec2len = strlen(filespec2);
b7573c59 223 len = spec2len + strlen(filespec1);
0f113f3e 224
b7573c59 225 if (spec2len && filespec2[spec2len - 1] == '/') {
0f113f3e
MC
226 spec2len--;
227 len--;
228 }
229 merged = OPENSSL_malloc(len + 2);
90945fa3 230 if (merged == NULL) {
0f113f3e
MC
231 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
232 return (NULL);
233 }
234 strcpy(merged, filespec2);
235 merged[spec2len] = '/';
236 strcpy(&merged[spec2len + 1], filespec1);
237 }
238 return (merged);
239}
4a620922 240
51c8dc37 241static char *dlfcn_name_converter(DSO *dso, const char *filename)
0f113f3e
MC
242{
243 char *translated;
244 int len, rsize, transform;
245
246 len = strlen(filename);
247 rsize = len + 1;
248 transform = (strstr(filename, "/") == NULL);
249 if (transform) {
250 /* We will convert this to "%s.so" or "lib%s.so" etc */
e987f9f2 251 rsize += strlen(DSO_EXTENSION); /* The length of ".so" */
0f113f3e
MC
252 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
253 rsize += 3; /* The length of "lib" */
254 }
255 translated = OPENSSL_malloc(rsize);
256 if (translated == NULL) {
257 DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
258 return (NULL);
259 }
260 if (transform) {
261 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
e987f9f2 262 sprintf(translated, "lib%s" DSO_EXTENSION, filename);
0f113f3e 263 else
e987f9f2 264 sprintf(translated, "%s" DSO_EXTENSION, filename);
0f113f3e
MC
265 } else
266 sprintf(translated, "%s", filename);
267 return (translated);
268}
269
270# ifdef __sgi
23a22b4c 271/*-
7ed87653
AP
272This is a quote from IRIX manual for dladdr(3c):
273
274 <dlfcn.h> does not contain a prototype for dladdr or definition of
275 Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional,
276 but contains no dladdr prototype and no IRIX library contains an
277 implementation. Write your own declaration based on the code below.
278
279 The following code is dependent on internal interfaces that are not
280 part of the IRIX compatibility guarantee; however, there is no future
281 intention to change this interface, so on a practical level, the code
282 below is safe to use on IRIX.
c8e1edaa 283*/
0f113f3e
MC
284# include <rld_interface.h>
285# ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
286# define _RLD_INTERFACE_DLFCN_H_DLADDR
7ed87653 287typedef struct Dl_info {
0f113f3e
MC
288 const char *dli_fname;
289 void *dli_fbase;
290 const char *dli_sname;
291 void *dli_saddr;
292 int dli_version;
293 int dli_reserved1;
294 long dli_reserved[4];
7ed87653 295} Dl_info;
0f113f3e 296# else
7ed87653 297typedef struct Dl_info Dl_info;
0f113f3e
MC
298# endif
299# define _RLD_DLADDR 14
7ed87653
AP
300
301static int dladdr(void *address, Dl_info *dl)
302{
0f113f3e
MC
303 void *v;
304 v = _rld_new_interface(_RLD_DLADDR, address, dl);
305 return (int)v;
306}
307# endif /* __sgi */
308
c6cb42e4 309static void *dlfcn_globallookup(const char *name)
0f113f3e
MC
310{
311 void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
312
313 if (handle) {
314 ret = dlsym(handle, name);
315 dlclose(handle);
316 }
317
318 return ret;
319}
320#endif /* DSO_DLFCN */