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