dso_dlfcn.c revision 267258
1/* dso_dlfcn.c -*- mode:C; c-file-style: "eay" -*- */
2/* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL
3 * project 2000.
4 */
5/* ====================================================================
6 * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in
17 *    the documentation and/or other materials provided with the
18 *    distribution.
19 *
20 * 3. All advertising materials mentioning features or use of this
21 *    software must display the following acknowledgment:
22 *    "This product includes software developed by the OpenSSL Project
23 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24 *
25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26 *    endorse or promote products derived from this software without
27 *    prior written permission. For written permission, please contact
28 *    licensing@OpenSSL.org.
29 *
30 * 5. Products derived from this software may not be called "OpenSSL"
31 *    nor may "OpenSSL" appear in their names without prior written
32 *    permission of the OpenSSL Project.
33 *
34 * 6. Redistributions of any form whatsoever must retain the following
35 *    acknowledgment:
36 *    "This product includes software developed by the OpenSSL Project
37 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50 * OF THE POSSIBILITY OF SUCH DAMAGE.
51 * ====================================================================
52 *
53 * This product includes cryptographic software written by Eric Young
54 * (eay@cryptsoft.com).  This product includes software written by Tim
55 * Hudson (tjh@cryptsoft.com).
56 *
57 */
58
59/* We need to do this early, because stdio.h includes the header files
60   that handle _GNU_SOURCE and other similar macros.  Defining it later
61   is simply too late, because those headers are protected from re-
62   inclusion.  */
63#ifdef __linux
64# ifndef _GNU_SOURCE
65#  define _GNU_SOURCE	/* make sure dladdr is declared */
66# endif
67#endif
68
69#include <stdio.h>
70#include "cryptlib.h"
71#include <openssl/dso.h>
72
73#ifndef DSO_DLFCN
74DSO_METHOD *DSO_METHOD_dlfcn(void)
75	{
76	return NULL;
77	}
78#else
79
80#ifdef HAVE_DLFCN_H
81# ifdef __osf__
82#  define __EXTENSIONS__
83# endif
84# include <dlfcn.h>
85# define HAVE_DLINFO 1
86# if defined(_AIX) || defined(__CYGWIN__) || \
87     defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
88     (defined(__osf__) && !defined(RTLD_NEXT))     || \
89     (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
90	defined(__ANDROID__)
91#  undef HAVE_DLINFO
92# endif
93#endif
94
95/* Part of the hack in "dlfcn_load" ... */
96#define DSO_MAX_TRANSLATED_SIZE 256
97
98static int dlfcn_load(DSO *dso);
99static int dlfcn_unload(DSO *dso);
100static void *dlfcn_bind_var(DSO *dso, const char *symname);
101static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
102#if 0
103static int dlfcn_unbind(DSO *dso, char *symname, void *symptr);
104static int dlfcn_init(DSO *dso);
105static int dlfcn_finish(DSO *dso);
106static long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg);
107#endif
108static char *dlfcn_name_converter(DSO *dso, const char *filename);
109static char *dlfcn_merger(DSO *dso, const char *filespec1,
110	const char *filespec2);
111static int dlfcn_pathbyaddr(void *addr,char *path,int sz);
112static void *dlfcn_globallookup(const char *name);
113
114static DSO_METHOD dso_meth_dlfcn = {
115	"OpenSSL 'dlfcn' shared library method",
116	dlfcn_load,
117	dlfcn_unload,
118	dlfcn_bind_var,
119	dlfcn_bind_func,
120/* For now, "unbind" doesn't exist */
121#if 0
122	NULL, /* unbind_var */
123	NULL, /* unbind_func */
124#endif
125	NULL, /* ctrl */
126	dlfcn_name_converter,
127	dlfcn_merger,
128	NULL, /* init */
129	NULL, /* finish */
130	dlfcn_pathbyaddr,
131	dlfcn_globallookup
132	};
133
134DSO_METHOD *DSO_METHOD_dlfcn(void)
135	{
136	return(&dso_meth_dlfcn);
137	}
138
139/* Prior to using the dlopen() function, we should decide on the flag
140 * we send. There's a few different ways of doing this and it's a
141 * messy venn-diagram to match up which platforms support what. So
142 * as we don't have autoconf yet, I'm implementing a hack that could
143 * be hacked further relatively easily to deal with cases as we find
144 * them. Initially this is to cope with OpenBSD. */
145#if defined(__OpenBSD__) || defined(__NetBSD__)
146#	ifdef DL_LAZY
147#		define DLOPEN_FLAG DL_LAZY
148#	else
149#		ifdef RTLD_NOW
150#			define DLOPEN_FLAG RTLD_NOW
151#		else
152#			define DLOPEN_FLAG 0
153#		endif
154#	endif
155#else
156#	ifdef OPENSSL_SYS_SUNOS
157#		define DLOPEN_FLAG 1
158#	else
159#		define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
160#	endif
161#endif
162
163/* For this DSO_METHOD, our meth_data STACK will contain;
164 * (i) the handle (void*) returned from dlopen().
165 */
166
167static int dlfcn_load(DSO *dso)
168	{
169	void *ptr = NULL;
170	/* See applicable comments in dso_dl.c */
171	char *filename = DSO_convert_filename(dso, NULL);
172	int flags = DLOPEN_FLAG;
173
174	if(filename == NULL)
175		{
176		DSOerr(DSO_F_DLFCN_LOAD,DSO_R_NO_FILENAME);
177		goto err;
178		}
179
180#ifdef RTLD_GLOBAL
181	if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
182		flags |= RTLD_GLOBAL;
183#endif
184	ptr = dlopen(filename, flags);
185	if(ptr == NULL)
186		{
187		DSOerr(DSO_F_DLFCN_LOAD,DSO_R_LOAD_FAILED);
188		ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
189		goto err;
190		}
191	if(!sk_void_push(dso->meth_data, (char *)ptr))
192		{
193		DSOerr(DSO_F_DLFCN_LOAD,DSO_R_STACK_ERROR);
194		goto err;
195		}
196	/* Success */
197	dso->loaded_filename = filename;
198	return(1);
199err:
200	/* Cleanup! */
201	if(filename != NULL)
202		OPENSSL_free(filename);
203	if(ptr != NULL)
204		dlclose(ptr);
205	return(0);
206}
207
208static int dlfcn_unload(DSO *dso)
209	{
210	void *ptr;
211	if(dso == NULL)
212		{
213		DSOerr(DSO_F_DLFCN_UNLOAD,ERR_R_PASSED_NULL_PARAMETER);
214		return(0);
215		}
216	if(sk_void_num(dso->meth_data) < 1)
217		return(1);
218	ptr = sk_void_pop(dso->meth_data);
219	if(ptr == NULL)
220		{
221		DSOerr(DSO_F_DLFCN_UNLOAD,DSO_R_NULL_HANDLE);
222		/* Should push the value back onto the stack in
223		 * case of a retry. */
224		sk_void_push(dso->meth_data, ptr);
225		return(0);
226		}
227	/* For now I'm not aware of any errors associated with dlclose() */
228	dlclose(ptr);
229	return(1);
230	}
231
232static void *dlfcn_bind_var(DSO *dso, const char *symname)
233	{
234	void *ptr, *sym;
235
236	if((dso == NULL) || (symname == NULL))
237		{
238		DSOerr(DSO_F_DLFCN_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
239		return(NULL);
240		}
241	if(sk_void_num(dso->meth_data) < 1)
242		{
243		DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_STACK_ERROR);
244		return(NULL);
245		}
246	ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
247	if(ptr == NULL)
248		{
249		DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_NULL_HANDLE);
250		return(NULL);
251		}
252	sym = dlsym(ptr, symname);
253	if(sym == NULL)
254		{
255		DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_SYM_FAILURE);
256		ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
257		return(NULL);
258		}
259	return(sym);
260	}
261
262static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
263	{
264	void *ptr;
265	union {
266		DSO_FUNC_TYPE sym;
267		void *dlret;
268	} u;
269
270	if((dso == NULL) || (symname == NULL))
271		{
272		DSOerr(DSO_F_DLFCN_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER);
273		return(NULL);
274		}
275	if(sk_void_num(dso->meth_data) < 1)
276		{
277		DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_STACK_ERROR);
278		return(NULL);
279		}
280	ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
281	if(ptr == NULL)
282		{
283		DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_NULL_HANDLE);
284		return(NULL);
285		}
286	u.dlret = dlsym(ptr, symname);
287	if(u.dlret == NULL)
288		{
289		DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_SYM_FAILURE);
290		ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
291		return(NULL);
292		}
293	return u.sym;
294	}
295
296static char *dlfcn_merger(DSO *dso, const char *filespec1,
297	const char *filespec2)
298	{
299	char *merged;
300
301	if(!filespec1 && !filespec2)
302		{
303		DSOerr(DSO_F_DLFCN_MERGER,
304				ERR_R_PASSED_NULL_PARAMETER);
305		return(NULL);
306		}
307	/* If the first file specification is a rooted path, it rules.
308	   same goes if the second file specification is missing. */
309	if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/'))
310		{
311		merged = OPENSSL_malloc(strlen(filespec1) + 1);
312		if(!merged)
313			{
314			DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
315			return(NULL);
316			}
317		strcpy(merged, filespec1);
318		}
319	/* If the first file specification is missing, the second one rules. */
320	else if (!filespec1)
321		{
322		merged = OPENSSL_malloc(strlen(filespec2) + 1);
323		if(!merged)
324			{
325			DSOerr(DSO_F_DLFCN_MERGER,
326				ERR_R_MALLOC_FAILURE);
327			return(NULL);
328			}
329		strcpy(merged, filespec2);
330		}
331	else
332		/* This part isn't as trivial as it looks.  It assumes that
333		   the second file specification really is a directory, and
334		   makes no checks whatsoever.  Therefore, the result becomes
335		   the concatenation of filespec2 followed by a slash followed
336		   by filespec1. */
337		{
338		int spec2len, len;
339
340		spec2len = strlen(filespec2);
341		len = spec2len + (filespec1 ? strlen(filespec1) : 0);
342
343		if(filespec2 && filespec2[spec2len - 1] == '/')
344			{
345			spec2len--;
346			len--;
347			}
348		merged = OPENSSL_malloc(len + 2);
349		if(!merged)
350			{
351			DSOerr(DSO_F_DLFCN_MERGER,
352				ERR_R_MALLOC_FAILURE);
353			return(NULL);
354			}
355		strcpy(merged, filespec2);
356		merged[spec2len] = '/';
357		strcpy(&merged[spec2len + 1], filespec1);
358		}
359	return(merged);
360	}
361
362#ifdef OPENSSL_SYS_MACOSX
363#define DSO_ext	".dylib"
364#define DSO_extlen 6
365#else
366#define DSO_ext	".so"
367#define DSO_extlen 3
368#endif
369
370
371static char *dlfcn_name_converter(DSO *dso, const char *filename)
372	{
373	char *translated;
374	int len, rsize, transform;
375
376	len = strlen(filename);
377	rsize = len + 1;
378	transform = (strstr(filename, "/") == NULL);
379	if(transform)
380		{
381		/* We will convert this to "%s.so" or "lib%s.so" etc */
382		rsize += DSO_extlen;	/* The length of ".so" */
383		if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
384			rsize += 3; /* The length of "lib" */
385		}
386	translated = OPENSSL_malloc(rsize);
387	if(translated == NULL)
388		{
389		DSOerr(DSO_F_DLFCN_NAME_CONVERTER,
390				DSO_R_NAME_TRANSLATION_FAILED);
391		return(NULL);
392		}
393	if(transform)
394		{
395		if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
396			sprintf(translated, "lib%s" DSO_ext, filename);
397		else
398			sprintf(translated, "%s" DSO_ext, filename);
399		}
400	else
401		sprintf(translated, "%s", filename);
402	return(translated);
403	}
404
405#ifdef __sgi
406/*
407This is a quote from IRIX manual for dladdr(3c):
408
409     <dlfcn.h> does not contain a prototype for dladdr or definition of
410     Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
411     but contains no dladdr prototype and no IRIX library contains an
412     implementation.  Write your own declaration based on the code below.
413
414     The following code is dependent on internal interfaces that are not
415     part of the IRIX compatibility guarantee; however, there is no future
416     intention to change this interface, so on a practical level, the code
417     below is safe to use on IRIX.
418*/
419#include <rld_interface.h>
420#ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
421#define _RLD_INTERFACE_DLFCN_H_DLADDR
422typedef struct Dl_info {
423    const char * dli_fname;
424    void       * dli_fbase;
425    const char * dli_sname;
426    void       * dli_saddr;
427    int          dli_version;
428    int          dli_reserved1;
429    long         dli_reserved[4];
430} Dl_info;
431#else
432typedef struct Dl_info Dl_info;
433#endif
434#define _RLD_DLADDR             14
435
436static int dladdr(void *address, Dl_info *dl)
437{
438	void *v;
439	v = _rld_new_interface(_RLD_DLADDR,address,dl);
440	return (int)v;
441}
442#endif /* __sgi */
443
444static int dlfcn_pathbyaddr(void *addr,char *path,int sz)
445	{
446#ifdef HAVE_DLINFO
447	Dl_info dli;
448	int len;
449
450	if (addr == NULL)
451		{
452		union	{ int(*f)(void*,char*,int); void *p; } t =
453			{ dlfcn_pathbyaddr };
454		addr = t.p;
455		}
456
457	if (dladdr(addr,&dli))
458		{
459		len = (int)strlen(dli.dli_fname);
460		if (sz <= 0) return len+1;
461		if (len >= sz) len=sz-1;
462		memcpy(path,dli.dli_fname,len);
463		path[len++]=0;
464		return len;
465		}
466
467	ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
468#endif
469	return -1;
470	}
471
472static void *dlfcn_globallookup(const char *name)
473	{
474	void *ret = NULL,*handle = dlopen(NULL,RTLD_LAZY);
475
476	if (handle)
477		{
478		ret = dlsym(handle,name);
479		dlclose(handle);
480		}
481
482	return ret;
483	}
484#endif /* DSO_DLFCN */
485