dso_dlfcn.c revision 280304
12061Sjkh/* dso_dlfcn.c -*- mode:C; c-file-style: "eay" -*- */ 250479Speter/* 32061Sjkh * Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL project 438666Sjb * 2000. 532427Sjb */ 6111131Sru/* ==================================================================== 7111131Sru * Copyright (c) 2000 The OpenSSL Project. All rights reserved. 838666Sjb * 938666Sjb * Redistribution and use in source and binary forms, with or without 1038666Sjb * modification, are permitted provided that the following conditions 11159363Strhodes * are met: 1264049Salex * 1364049Salex * 1. Redistributions of source code must retain the above copyright 14116679Ssimokawa * notice, this list of conditions and the following disclaimer. 1566071Smarkm * 16116679Ssimokawa * 2. Redistributions in binary form must reproduce the above copyright 1773504Sobrien * notice, this list of conditions and the following disclaimer in 18204661Simp * the documentation and/or other materials provided with the 19158962Snetchild * distribution. 2038666Sjb * 21169597Sdes * 3. All advertising materials mentioning features or use of this 22169597Sdes * software must display the following acknowledgment: 23169597Sdes * "This product includes software developed by the OpenSSL Project 24169597Sdes * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 25169597Sdes * 26169597Sdes * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 27169597Sdes * endorse or promote products derived from this software without 28169597Sdes * prior written permission. For written permission, please contact 2932427Sjb * licensing@OpenSSL.org. 3038666Sjb * 31108451Sschweikh * 5. Products derived from this software may not be called "OpenSSL" 3238666Sjb * nor may "OpenSSL" appear in their names without prior written 3338666Sjb * permission of the OpenSSL Project. 3438666Sjb * 3538666Sjb * 6. Redistributions of any form whatsoever must retain the following 3617308Speter * acknowledgment: 3791606Skeramida * "This product includes software developed by the OpenSSL Project 3819175Sbde * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 3996205Sjwd * 40177794Spav * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 4138042Sbde * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4296205Sjwd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 4396205Sjwd * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 4438042Sbde * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 4596205Sjwd * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46159363Strhodes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47159363Strhodes * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 4817308Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 4996205Sjwd * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 5096205Sjwd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 5117308Speter * OF THE POSSIBILITY OF SUCH DAMAGE. 52148330Snetchild * ==================================================================== 53148330Snetchild * 54148330Snetchild * This product includes cryptographic software written by Eric Young 55148330Snetchild * (eay@cryptsoft.com). This product includes software written by Tim 56159831Sobrien * Hudson (tjh@cryptsoft.com). 57148330Snetchild * 58148330Snetchild */ 59148330Snetchild 60148330Snetchild/* 61178653Srwatson * We need to do this early, because stdio.h includes the header files that 62148330Snetchild * handle _GNU_SOURCE and other similar macros. Defining it later is simply 63148330Snetchild * too late, because those headers are protected from re- inclusion. 6496205Sjwd */ 6596205Sjwd#ifndef _GNU_SOURCE 6696205Sjwd# define _GNU_SOURCE /* make sure dladdr is declared */ 67162147Sru#endif 68162147Sru 6998723Sdillon#include <stdio.h> 7098723Sdillon#include "cryptlib.h" 7198723Sdillon#include <openssl/dso.h> 7238666Sjb 7338666Sjb#ifndef DSO_DLFCN 7417308SpeterDSO_METHOD *DSO_METHOD_dlfcn(void) 75123311Speter{ 76123311Speter return NULL; 77123311Speter} 78123311Speter#else 79175833Sjhb 80175833Sjhb# ifdef HAVE_DLFCN_H 81169597Sdes# ifdef __osf__ 82169597Sdes# define __EXTENSIONS__ 83169597Sdes# endif 84169597Sdes# include <dlfcn.h> 85159349Simp# define HAVE_DLINFO 1 86158962Snetchild# if defined(_AIX) || defined(__CYGWIN__) || \ 87158962Snetchild defined(__SCO_VERSION__) || defined(_SCO_ELF) || \ 88158962Snetchild (defined(__osf__) && !defined(RTLD_NEXT)) || \ 89156840Sru (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \ 90123311Speter defined(__ANDROID__) 91137288Speter# undef HAVE_DLINFO 92189760Simp# endif 93156740Sru# endif 942061Sjkh 9597769Sru/* Part of the hack in "dlfcn_load" ... */ 9697252Sru# define DSO_MAX_TRANSLATED_SIZE 256 97119579Sru 9897252Srustatic int dlfcn_load(DSO *dso); 9995730Srustatic int dlfcn_unload(DSO *dso); 10095793Srustatic void *dlfcn_bind_var(DSO *dso, const char *symname); 101111617Srustatic DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname); 10295730Sru# if 0 103116679Ssimokawastatic int dlfcn_unbind(DSO *dso, char *symname, void *symptr); 10495730Srustatic int dlfcn_init(DSO *dso); 105116679Ssimokawastatic int dlfcn_finish(DSO *dso); 10695730Srustatic long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg); 107110035Sru# endif 108107516Srustatic char *dlfcn_name_converter(DSO *dso, const char *filename); 109138921Srustatic char *dlfcn_merger(DSO *dso, const char *filespec1, 110156145Syar const char *filespec2); 111138921Srustatic int dlfcn_pathbyaddr(void *addr, char *path, int sz); 112133942Srustatic void *dlfcn_globallookup(const char *name); 113133942Sru 114156145Syarstatic DSO_METHOD dso_meth_dlfcn = { 115133942Sru "OpenSSL 'dlfcn' shared library method", 116110035Sru dlfcn_load, 117117234Sru dlfcn_unload, 118110035Sru dlfcn_bind_var, 119117229Sru dlfcn_bind_func, 120117234Sru/* For now, "unbind" doesn't exist */ 12154324Smarcel# if 0 12217308Speter NULL, /* unbind_var */ 123119519Smarcel NULL, /* unbind_func */ 124119519Smarcel# endif 125119519Smarcel NULL, /* ctrl */ 126119519Smarcel dlfcn_name_converter, 127119519Smarcel dlfcn_merger, 128119519Smarcel NULL, /* init */ 129119579Sru NULL, /* finish */ 130119519Smarcel dlfcn_pathbyaddr, 131119519Smarcel dlfcn_globallookup 132119519Smarcel}; 133119519Smarcel 134119519SmarcelDSO_METHOD *DSO_METHOD_dlfcn(void) 135126031Sgad{ 136126024Sgad return (&dso_meth_dlfcn); 137126024Sgad} 138126024Sgad 139126024Sgad/* 140126024Sgad * Prior to using the dlopen() function, we should decide on the flag we 141126024Sgad * send. There's a few different ways of doing this and it's a messy 142126024Sgad * venn-diagram to match up which platforms support what. So as we don't have 143126024Sgad * autoconf yet, I'm implementing a hack that could be hacked further 144126024Sgad * relatively easily to deal with cases as we find them. Initially this is to 145126024Sgad * cope with OpenBSD. 146126024Sgad */ 147126024Sgad# if defined(__OpenBSD__) || defined(__NetBSD__) 148126024Sgad# ifdef DL_LAZY 149126031Sgad# define DLOPEN_FLAG DL_LAZY 150126024Sgad# else 151126024Sgad# ifdef RTLD_NOW 152126024Sgad# define DLOPEN_FLAG RTLD_NOW 153172744Sdelphij# else 154126024Sgad# define DLOPEN_FLAG 0 155126024Sgad# endif 156126024Sgad# endif 157133376Sharti# else 158126024Sgad# ifdef OPENSSL_SYS_SUNOS 159126024Sgad# define DLOPEN_FLAG 1 160172744Sdelphij# else 161126024Sgad# define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */ 162126024Sgad# endif 163125885Sgad# endif 164125885Sgad 16538666Sjb/* 16617308Speter * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle 167119519Smarcel * (void*) returned from dlopen(). 168119579Sru */ 169133376Sharti 170110035Srustatic int dlfcn_load(DSO *dso) 1712302Spaul{ 17239206Sjkh void *ptr = NULL; 17339206Sjkh /* See applicable comments in dso_dl.c */ 17439206Sjkh char *filename = DSO_convert_filename(dso, NULL); 175133945Sru int flags = DLOPEN_FLAG; 176177609Sru 177177609Sru if (filename == NULL) { 178177609Sru DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME); 179177609Sru goto err; 180133945Sru } 181132358Smarkm# ifdef RTLD_GLOBAL 18217308Speter if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS) 18354324Smarcel flags |= RTLD_GLOBAL; 18454324Smarcel# endif 185132234Smarcel ptr = dlopen(filename, flags); 186132234Smarcel if (ptr == NULL) { 187132234Smarcel DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED); 188132234Smarcel ERR_add_error_data(4, "filename(", filename, "): ", dlerror()); 18954324Smarcel goto err; 19054324Smarcel } 19154324Smarcel if (!sk_void_push(dso->meth_data, (char *)ptr)) { 192118531Sru DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR); 19354324Smarcel goto err; 19454324Smarcel } 19554324Smarcel /* Success */ 19654324Smarcel dso->loaded_filename = filename; 19754324Smarcel return (1); 19854324Smarcel err: 199133376Sharti /* Cleanup! */ 20054324Smarcel if (filename != NULL) 201133376Sharti OPENSSL_free(filename); 202133376Sharti if (ptr != NULL) 20354324Smarcel dlclose(ptr); 20454324Smarcel return (0); 20554324Smarcel} 20654324Smarcel 20754324Smarcelstatic int dlfcn_unload(DSO *dso) 208133376Sharti{ 20954324Smarcel void *ptr; 21054324Smarcel if (dso == NULL) { 21154324Smarcel DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER); 212118531Sru return (0); 213118531Sru } 21454324Smarcel if (sk_void_num(dso->meth_data) < 1) 215132234Smarcel return (1); 216132234Smarcel ptr = sk_void_pop(dso->meth_data); 217132234Smarcel if (ptr == NULL) { 218132234Smarcel DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE); 219132234Smarcel /* 220132588Skensmith * Should push the value back onto the stack in case of a retry. 221132358Smarkm */ 222132234Smarcel sk_void_push(dso->meth_data, ptr); 223132358Smarkm return (0); 224132234Smarcel } 225132234Smarcel /* For now I'm not aware of any errors associated with dlclose() */ 226132234Smarcel dlclose(ptr); 22754324Smarcel return (1); 22854324Smarcel} 22995730Sru 23095730Srustatic void *dlfcn_bind_var(DSO *dso, const char *symname) 23195730Sru{ 23295730Sru void *ptr, *sym; 23395730Sru 23495730Sru if ((dso == NULL) || (symname == NULL)) { 23595730Sru DSOerr(DSO_F_DLFCN_BIND_VAR, ERR_R_PASSED_NULL_PARAMETER); 23638666Sjb return (NULL); 237107374Sru } 23817308Speter if (sk_void_num(dso->meth_data) < 1) { 23955678Smarcel DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_STACK_ERROR); 240143032Sharti return (NULL); 241138515Sharti } 242117793Sru ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1); 243110035Sru if (ptr == NULL) { 244174564Simp DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_NULL_HANDLE); 245110035Sru return (NULL); 2462061Sjkh } 24717308Speter sym = dlsym(ptr, symname); 248107516Sru if (sym == NULL) { 249174539Simp DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_SYM_FAILURE); 250174539Simp ERR_add_error_data(4, "symname(", symname, "): ", dlerror()); 25155678Smarcel return (NULL); 252107516Sru } 253107516Sru return (sym); 254107516Sru} 255174564Simp 256107516Srustatic DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname) 257139112Sru{ 258164470Sjb void *ptr; 259107516Sru union { 260122204Skris DSO_FUNC_TYPE sym; 26155678Smarcel void *dlret; 26255678Smarcel } u; 263116696Sru 26455678Smarcel if ((dso == NULL) || (symname == NULL)) { 265133376Sharti DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER); 266107516Sru return (NULL); 267107516Sru } 268107516Sru if (sk_void_num(dso->meth_data) < 1) { 269107516Sru DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR); 27055678Smarcel return (NULL); 271185499Salfred } 272185499Salfred ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1); 273185499Salfred if (ptr == NULL) { 274185499Salfred DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE); 27555678Smarcel return (NULL); 276111131Sru } 277111131Sru u.dlret = dlsym(ptr, symname); 278111131Sru if (u.dlret == NULL) { 279133945Sru DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE); 280111131Sru ERR_add_error_data(4, "symname(", symname, "): ", dlerror()); 281111131Sru return (NULL); 282201815Sbz } 283190628Sbz return u.sym; 284168280Smarcel} 285185499Salfred 286185499Salfredstatic char *dlfcn_merger(DSO *dso, const char *filespec1, 287185499Salfred const char *filespec2) 288185499Salfred{ 289185499Salfred char *merged; 290185499Salfred 291185499Salfred if (!filespec1 && !filespec2) { 292133945Sru DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER); 293133945Sru return (NULL); 294103985Sphk } 295103985Sphk /* 296103985Sphk * If the first file specification is a rooted path, it rules. same goes 297185499Salfred * if the second file specification is missing. 298185499Salfred */ 299185499Salfred if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) { 300168280Smarcel merged = OPENSSL_malloc(strlen(filespec1) + 1); 301162147Sru if (!merged) { 302162147Sru DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 303162147Sru return (NULL); 304179232Sjb } 305162147Sru strcpy(merged, filespec1); 306185250Sdes } 307202629Sed /* 308162147Sru * If the first file specification is missing, the second one rules. 309185250Sdes */ 310185499Salfred else if (!filespec1) { 311185499Salfred merged = OPENSSL_malloc(strlen(filespec2) + 1); 312162147Sru if (!merged) { 313179232Sjb DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 314205290Sdougb return (NULL); 315162147Sru } 316185250Sdes strcpy(merged, filespec2); 317185250Sdes } else { 318185499Salfred /* 319185499Salfred * This part isn't as trivial as it looks. It assumes that the 320103985Sphk * second file specification really is a directory, and makes no 321201815Sbz * checks whatsoever. Therefore, the result becomes the 322201815Sbz * concatenation of filespec2 followed by a slash followed by 323205290Sdougb * filespec1. 324201815Sbz */ 325201815Sbz int spec2len, len; 326201815Sbz 327202095Sbz spec2len = strlen(filespec2); 328202095Sbz len = spec2len + strlen(filespec1); 329202095Sbz 330201815Sbz if (spec2len && filespec2[spec2len - 1] == '/') { 331201815Sbz spec2len--; 332201815Sbz len--; 333201815Sbz } 334148154Sru merged = OPENSSL_malloc(len + 2); 335185250Sdes if (!merged) { 336185250Sdes DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 337201815Sbz return (NULL); 338148154Sru } 339201815Sbz strcpy(merged, filespec2); 340201815Sbz merged[spec2len] = '/'; 341201815Sbz strcpy(&merged[spec2len + 1], filespec1); 342148154Sru } 343133945Sru return (merged); 344133945Sru} 345103985Sphk 346118531Sru# ifdef OPENSSL_SYS_MACOSX 347118531Sru# define DSO_ext ".dylib" 348103985Sphk# define DSO_extlen 6 349185499Salfred# else 350185499Salfred# define DSO_ext ".so" 351185499Salfred# define DSO_extlen 3 352185499Salfred# endif 353185499Salfred 354185499Salfredstatic char *dlfcn_name_converter(DSO *dso, const char *filename) 355133945Sru{ 356185499Salfred char *translated; 357 int len, rsize, transform; 358 359 len = strlen(filename); 360 rsize = len + 1; 361 transform = (strstr(filename, "/") == NULL); 362 if (transform) { 363 /* We will convert this to "%s.so" or "lib%s.so" etc */ 364 rsize += DSO_extlen; /* The length of ".so" */ 365 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 366 rsize += 3; /* The length of "lib" */ 367 } 368 translated = OPENSSL_malloc(rsize); 369 if (translated == NULL) { 370 DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED); 371 return (NULL); 372 } 373 if (transform) { 374 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 375 sprintf(translated, "lib%s" DSO_ext, filename); 376 else 377 sprintf(translated, "%s" DSO_ext, filename); 378 } else 379 sprintf(translated, "%s", filename); 380 return (translated); 381} 382 383# ifdef __sgi 384/*- 385This is a quote from IRIX manual for dladdr(3c): 386 387 <dlfcn.h> does not contain a prototype for dladdr or definition of 388 Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional, 389 but contains no dladdr prototype and no IRIX library contains an 390 implementation. Write your own declaration based on the code below. 391 392 The following code is dependent on internal interfaces that are not 393 part of the IRIX compatibility guarantee; however, there is no future 394 intention to change this interface, so on a practical level, the code 395 below is safe to use on IRIX. 396*/ 397# include <rld_interface.h> 398# ifndef _RLD_INTERFACE_DLFCN_H_DLADDR 399# define _RLD_INTERFACE_DLFCN_H_DLADDR 400typedef struct Dl_info { 401 const char *dli_fname; 402 void *dli_fbase; 403 const char *dli_sname; 404 void *dli_saddr; 405 int dli_version; 406 int dli_reserved1; 407 long dli_reserved[4]; 408} Dl_info; 409# else 410typedef struct Dl_info Dl_info; 411# endif 412# define _RLD_DLADDR 14 413 414static int dladdr(void *address, Dl_info *dl) 415{ 416 void *v; 417 v = _rld_new_interface(_RLD_DLADDR, address, dl); 418 return (int)v; 419} 420# endif /* __sgi */ 421 422static int dlfcn_pathbyaddr(void *addr, char *path, int sz) 423{ 424# ifdef HAVE_DLINFO 425 Dl_info dli; 426 int len; 427 428 if (addr == NULL) { 429 union { 430 int (*f) (void *, char *, int); 431 void *p; 432 } t = { 433 dlfcn_pathbyaddr 434 }; 435 addr = t.p; 436 } 437 438 if (dladdr(addr, &dli)) { 439 len = (int)strlen(dli.dli_fname); 440 if (sz <= 0) 441 return len + 1; 442 if (len >= sz) 443 len = sz - 1; 444 memcpy(path, dli.dli_fname, len); 445 path[len++] = 0; 446 return len; 447 } 448 449 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror()); 450# endif 451 return -1; 452} 453 454static void *dlfcn_globallookup(const char *name) 455{ 456 void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY); 457 458 if (handle) { 459 ret = dlsym(handle, name); 460 dlclose(handle); 461 } 462 463 return ret; 464} 465#endif /* DSO_DLFCN */ 466