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 	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 
     98 static int dlfcn_load(DSO *dso);
     99 static int dlfcn_unload(DSO *dso);
    100 static void *dlfcn_bind_var(DSO *dso, const char *symname);
    101 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
    102 #if 0
    103 static int dlfcn_unbind(DSO *dso, char *symname, void *symptr);
    104 static int dlfcn_init(DSO *dso);
    105 static int dlfcn_finish(DSO *dso);
    106 static long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg);
    107 #endif
    108 static char *dlfcn_name_converter(DSO *dso, const char *filename);
    109 static char *dlfcn_merger(DSO *dso, const char *filespec1,
    110 	const char *filespec2);
    111 static int dlfcn_pathbyaddr(void *addr,char *path,int sz);
    112 static void *dlfcn_globallookup(const char *name);
    113 
    114 static 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 
    134 DSO_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 
    167 static 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);
    199 err:
    200 	/* Cleanup! */
    201 	if(filename != NULL)
    202 		OPENSSL_free(filename);
    203 	if(ptr != NULL)
    204 		dlclose(ptr);
    205 	return(0);
    206 }
    207 
    208 static 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 
    232 static 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 
    262 static 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 
    296 static 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 
    371 static 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 /*
    407 This 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
    422 typedef 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
    432 typedef struct Dl_info Dl_info;
    433 #endif
    434 #define _RLD_DLADDR             14
    435 
    436 static 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 
    444 static 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(4, "dlfcn_pathbyaddr(): ", dlerror());
    468 #endif
    469 	return -1;
    470 	}
    471 
    472 static 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