dso_dlfcn.c revision 221304ee937bc0910948a8be1320cb8cc4eb6d36
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(__OpenBSD__) && !defined(RTLD_SELF)) 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(4, "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