dso_dlfcn.c revision 279264
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#ifndef _GNU_SOURCE
64# define _GNU_SOURCE	/* make sure dladdr is declared */
65#endif
66
67#include <stdio.h>
68#include "cryptlib.h"
69#include <openssl/dso.h>
70
71#ifndef DSO_DLFCN
72DSO_METHOD *DSO_METHOD_dlfcn(void)
73	{
74	return NULL;
75	}
76#else
77
78#ifdef HAVE_DLFCN_H
79# ifdef __osf__
80#  define __EXTENSIONS__
81# endif
82# include <dlfcn.h>
83# define HAVE_DLINFO 1
84# if defined(_AIX) || defined(__CYGWIN__) || \
85     defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
86     (defined(__osf__) && !defined(RTLD_NEXT))     || \
87     (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
88	defined(__ANDROID__)
89#  undef HAVE_DLINFO
90# endif
91#endif
92
93/* Part of the hack in "dlfcn_load" ... */
94#define DSO_MAX_TRANSLATED_SIZE 256
95
96static int dlfcn_load(DSO *dso);
97static int dlfcn_unload(DSO *dso);
98static void *dlfcn_bind_var(DSO *dso, const char *symname);
99static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
100#if 0
101static int dlfcn_unbind(DSO *dso, char *symname, void *symptr);
102static int dlfcn_init(DSO *dso);
103static int dlfcn_finish(DSO *dso);
104static long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg);
105#endif
106static char *dlfcn_name_converter(DSO *dso, const char *filename);
107static char *dlfcn_merger(DSO *dso, const char *filespec1,
108	const char *filespec2);
109static int dlfcn_pathbyaddr(void *addr,char *path,int sz);
110static void *dlfcn_globallookup(const char *name);
111
112static DSO_METHOD dso_meth_dlfcn = {
113	"OpenSSL 'dlfcn' shared library method",
114	dlfcn_load,
115	dlfcn_unload,
116	dlfcn_bind_var,
117	dlfcn_bind_func,
118/* For now, "unbind" doesn't exist */
119#if 0
120	NULL, /* unbind_var */
121	NULL, /* unbind_func */
122#endif
123	NULL, /* ctrl */
124	dlfcn_name_converter,
125	dlfcn_merger,
126	NULL, /* init */
127	NULL, /* finish */
128	dlfcn_pathbyaddr,
129	dlfcn_globallookup
130	};
131
132DSO_METHOD *DSO_METHOD_dlfcn(void)
133	{
134	return(&dso_meth_dlfcn);
135	}
136
137/* Prior to using the dlopen() function, we should decide on the flag
138 * we send. There's a few different ways of doing this and it's a
139 * messy venn-diagram to match up which platforms support what. So
140 * as we don't have autoconf yet, I'm implementing a hack that could
141 * be hacked further relatively easily to deal with cases as we find
142 * them. Initially this is to cope with OpenBSD. */
143#if defined(__OpenBSD__) || defined(__NetBSD__)
144#	ifdef DL_LAZY
145#		define DLOPEN_FLAG DL_LAZY
146#	else
147#		ifdef RTLD_NOW
148#			define DLOPEN_FLAG RTLD_NOW
149#		else
150#			define DLOPEN_FLAG 0
151#		endif
152#	endif
153#else
154#	ifdef OPENSSL_SYS_SUNOS
155#		define DLOPEN_FLAG 1
156#	else
157#		define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
158#	endif
159#endif
160
161/* For this DSO_METHOD, our meth_data STACK will contain;
162 * (i) the handle (void*) returned from dlopen().
163 */
164
165static int dlfcn_load(DSO *dso)
166	{
167	void *ptr = NULL;
168	/* See applicable comments in dso_dl.c */
169	char *filename = DSO_convert_filename(dso, NULL);
170	int flags = DLOPEN_FLAG;
171
172	if(filename == NULL)
173		{
174		DSOerr(DSO_F_DLFCN_LOAD,DSO_R_NO_FILENAME);
175		goto err;
176		}
177
178#ifdef RTLD_GLOBAL
179	if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
180		flags |= RTLD_GLOBAL;
181#endif
182	ptr = dlopen(filename, flags);
183	if(ptr == NULL)
184		{
185		DSOerr(DSO_F_DLFCN_LOAD,DSO_R_LOAD_FAILED);
186		ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
187		goto err;
188		}
189	if(!sk_void_push(dso->meth_data, (char *)ptr))
190		{
191		DSOerr(DSO_F_DLFCN_LOAD,DSO_R_STACK_ERROR);
192		goto err;
193		}
194	/* Success */
195	dso->loaded_filename = filename;
196	return(1);
197err:
198	/* Cleanup! */
199	if(filename != NULL)
200		OPENSSL_free(filename);
201	if(ptr != NULL)
202		dlclose(ptr);
203	return(0);
204}
205
206static int dlfcn_unload(DSO *dso)
207	{
208	void *ptr;
209	if(dso == NULL)
210		{
211		DSOerr(DSO_F_DLFCN_UNLOAD,ERR_R_PASSED_NULL_PARAMETER);
212		return(0);
213		}
214	if(sk_void_num(dso->meth_data) < 1)
215		return(1);
216	ptr = sk_void_pop(dso->meth_data);
217	if(ptr == NULL)
218		{
219		DSOerr(DSO_F_DLFCN_UNLOAD,DSO_R_NULL_HANDLE);
220		/* Should push the value back onto the stack in
221		 * case of a retry. */
222		sk_void_push(dso->meth_data, ptr);
223		return(0);
224		}
225	/* For now I'm not aware of any errors associated with dlclose() */
226	dlclose(ptr);
227	return(1);
228	}
229
230static void *dlfcn_bind_var(DSO *dso, const char *symname)
231	{
232	void *ptr, *sym;
233
234	if((dso == NULL) || (symname == NULL))
235		{
236		DSOerr(DSO_F_DLFCN_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
237		return(NULL);
238		}
239	if(sk_void_num(dso->meth_data) < 1)
240		{
241		DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_STACK_ERROR);
242		return(NULL);
243		}
244	ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
245	if(ptr == NULL)
246		{
247		DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_NULL_HANDLE);
248		return(NULL);
249		}
250	sym = dlsym(ptr, symname);
251	if(sym == NULL)
252		{
253		DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_SYM_FAILURE);
254		ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
255		return(NULL);
256		}
257	return(sym);
258	}
259
260static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
261	{
262	void *ptr;
263	union {
264		DSO_FUNC_TYPE sym;
265		void *dlret;
266	} u;
267
268	if((dso == NULL) || (symname == NULL))
269		{
270		DSOerr(DSO_F_DLFCN_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER);
271		return(NULL);
272		}
273	if(sk_void_num(dso->meth_data) < 1)
274		{
275		DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_STACK_ERROR);
276		return(NULL);
277		}
278	ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
279	if(ptr == NULL)
280		{
281		DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_NULL_HANDLE);
282		return(NULL);
283		}
284	u.dlret = dlsym(ptr, symname);
285	if(u.dlret == NULL)
286		{
287		DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_SYM_FAILURE);
288		ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
289		return(NULL);
290		}
291	return u.sym;
292	}
293
294static char *dlfcn_merger(DSO *dso, const char *filespec1,
295	const char *filespec2)
296	{
297	char *merged;
298
299	if(!filespec1 && !filespec2)
300		{
301		DSOerr(DSO_F_DLFCN_MERGER,
302				ERR_R_PASSED_NULL_PARAMETER);
303		return(NULL);
304		}
305	/* If the first file specification is a rooted path, it rules.
306	   same goes if the second file specification is missing. */
307	if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/'))
308		{
309		merged = OPENSSL_malloc(strlen(filespec1) + 1);
310		if(!merged)
311			{
312			DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
313			return(NULL);
314			}
315		strcpy(merged, filespec1);
316		}
317	/* If the first file specification is missing, the second one rules. */
318	else if (!filespec1)
319		{
320		merged = OPENSSL_malloc(strlen(filespec2) + 1);
321		if(!merged)
322			{
323			DSOerr(DSO_F_DLFCN_MERGER,
324				ERR_R_MALLOC_FAILURE);
325			return(NULL);
326			}
327		strcpy(merged, filespec2);
328		}
329	else
330		/* This part isn't as trivial as it looks.  It assumes that
331		   the second file specification really is a directory, and
332		   makes no checks whatsoever.  Therefore, the result becomes
333		   the concatenation of filespec2 followed by a slash followed
334		   by filespec1. */
335		{
336		int spec2len, len;
337
338		spec2len = strlen(filespec2);
339		len = spec2len + (filespec1 ? strlen(filespec1) : 0);
340
341		if(filespec2 && filespec2[spec2len - 1] == '/')
342			{
343			spec2len--;
344			len--;
345			}
346		merged = OPENSSL_malloc(len + 2);
347		if(!merged)
348			{
349			DSOerr(DSO_F_DLFCN_MERGER,
350				ERR_R_MALLOC_FAILURE);
351			return(NULL);
352			}
353		strcpy(merged, filespec2);
354		merged[spec2len] = '/';
355		strcpy(&merged[spec2len + 1], filespec1);
356		}
357	return(merged);
358	}
359
360#ifdef OPENSSL_SYS_MACOSX
361#define DSO_ext	".dylib"
362#define DSO_extlen 6
363#else
364#define DSO_ext	".so"
365#define DSO_extlen 3
366#endif
367
368
369static char *dlfcn_name_converter(DSO *dso, const char *filename)
370	{
371	char *translated;
372	int len, rsize, transform;
373
374	len = strlen(filename);
375	rsize = len + 1;
376	transform = (strstr(filename, "/") == NULL);
377	if(transform)
378		{
379		/* We will convert this to "%s.so" or "lib%s.so" etc */
380		rsize += DSO_extlen;	/* The length of ".so" */
381		if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
382			rsize += 3; /* The length of "lib" */
383		}
384	translated = OPENSSL_malloc(rsize);
385	if(translated == NULL)
386		{
387		DSOerr(DSO_F_DLFCN_NAME_CONVERTER,
388				DSO_R_NAME_TRANSLATION_FAILED);
389		return(NULL);
390		}
391	if(transform)
392		{
393		if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
394			sprintf(translated, "lib%s" DSO_ext, filename);
395		else
396			sprintf(translated, "%s" DSO_ext, filename);
397		}
398	else
399		sprintf(translated, "%s", filename);
400	return(translated);
401	}
402
403#ifdef __sgi
404/*
405This is a quote from IRIX manual for dladdr(3c):
406
407     <dlfcn.h> does not contain a prototype for dladdr or definition of
408     Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
409     but contains no dladdr prototype and no IRIX library contains an
410     implementation.  Write your own declaration based on the code below.
411
412     The following code is dependent on internal interfaces that are not
413     part of the IRIX compatibility guarantee; however, there is no future
414     intention to change this interface, so on a practical level, the code
415     below is safe to use on IRIX.
416*/
417#include <rld_interface.h>
418#ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
419#define _RLD_INTERFACE_DLFCN_H_DLADDR
420typedef struct Dl_info {
421    const char * dli_fname;
422    void       * dli_fbase;
423    const char * dli_sname;
424    void       * dli_saddr;
425    int          dli_version;
426    int          dli_reserved1;
427    long         dli_reserved[4];
428} Dl_info;
429#else
430typedef struct Dl_info Dl_info;
431#endif
432#define _RLD_DLADDR             14
433
434static int dladdr(void *address, Dl_info *dl)
435{
436	void *v;
437	v = _rld_new_interface(_RLD_DLADDR,address,dl);
438	return (int)v;
439}
440#endif /* __sgi */
441
442static int dlfcn_pathbyaddr(void *addr,char *path,int sz)
443	{
444#ifdef HAVE_DLINFO
445	Dl_info dli;
446	int len;
447
448	if (addr == NULL)
449		{
450		union	{ int(*f)(void*,char*,int); void *p; } t =
451			{ dlfcn_pathbyaddr };
452		addr = t.p;
453		}
454
455	if (dladdr(addr,&dli))
456		{
457		len = (int)strlen(dli.dli_fname);
458		if (sz <= 0) return len+1;
459		if (len >= sz) len=sz-1;
460		memcpy(path,dli.dli_fname,len);
461		path[len++]=0;
462		return len;
463		}
464
465	ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
466#endif
467	return -1;
468	}
469
470static void *dlfcn_globallookup(const char *name)
471	{
472	void *ret = NULL,*handle = dlopen(NULL,RTLD_LAZY);
473
474	if (handle)
475		{
476		ret = dlsym(handle,name);
477		dlclose(handle);
478		}
479
480	return ret;
481	}
482#endif /* DSO_DLFCN */
483