1295016Sjkim/* dso_dlfcn.c */ 2280304Sjkim/* 3280304Sjkim * Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL project 4280304Sjkim * 2000. 568651Skris */ 668651Skris/* ==================================================================== 768651Skris * Copyright (c) 2000 The OpenSSL Project. All rights reserved. 868651Skris * 968651Skris * Redistribution and use in source and binary forms, with or without 1068651Skris * modification, are permitted provided that the following conditions 1168651Skris * are met: 1268651Skris * 1368651Skris * 1. Redistributions of source code must retain the above copyright 14280304Sjkim * notice, this list of conditions and the following disclaimer. 1568651Skris * 1668651Skris * 2. Redistributions in binary form must reproduce the above copyright 1768651Skris * notice, this list of conditions and the following disclaimer in 1868651Skris * the documentation and/or other materials provided with the 1968651Skris * distribution. 2068651Skris * 2168651Skris * 3. All advertising materials mentioning features or use of this 2268651Skris * software must display the following acknowledgment: 2368651Skris * "This product includes software developed by the OpenSSL Project 2468651Skris * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 2568651Skris * 2668651Skris * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 2768651Skris * endorse or promote products derived from this software without 2868651Skris * prior written permission. For written permission, please contact 2968651Skris * licensing@OpenSSL.org. 3068651Skris * 3168651Skris * 5. Products derived from this software may not be called "OpenSSL" 3268651Skris * nor may "OpenSSL" appear in their names without prior written 3368651Skris * permission of the OpenSSL Project. 3468651Skris * 3568651Skris * 6. Redistributions of any form whatsoever must retain the following 3668651Skris * acknowledgment: 3768651Skris * "This product includes software developed by the OpenSSL Project 3868651Skris * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 3968651Skris * 4068651Skris * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 4168651Skris * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4268651Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 4368651Skris * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 4468651Skris * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 4568651Skris * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 4668651Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 4768651Skris * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 4868651Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 4968651Skris * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 5068651Skris * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 5168651Skris * OF THE POSSIBILITY OF SUCH DAMAGE. 5268651Skris * ==================================================================== 5368651Skris * 5468651Skris * This product includes cryptographic software written by Eric Young 5568651Skris * (eay@cryptsoft.com). This product includes software written by Tim 5668651Skris * Hudson (tjh@cryptsoft.com). 5768651Skris * 5868651Skris */ 5968651Skris 60280304Sjkim/* 61280304Sjkim * We need to do this early, because stdio.h includes the header files that 62280304Sjkim * handle _GNU_SOURCE and other similar macros. Defining it later is simply 63280304Sjkim * too late, because those headers are protected from re- inclusion. 64280304Sjkim */ 65276864Sjkim#ifndef _GNU_SOURCE 66280304Sjkim# define _GNU_SOURCE /* make sure dladdr is declared */ 67238405Sjkim#endif 68238405Sjkim 6968651Skris#include <stdio.h> 7068651Skris#include "cryptlib.h" 7168651Skris#include <openssl/dso.h> 7268651Skris 7368651Skris#ifndef DSO_DLFCN 7468651SkrisDSO_METHOD *DSO_METHOD_dlfcn(void) 75280304Sjkim{ 76280304Sjkim return NULL; 77280304Sjkim} 7868651Skris#else 7968651Skris 80280304Sjkim# ifdef HAVE_DLFCN_H 81280304Sjkim# ifdef __osf__ 82280304Sjkim# define __EXTENSIONS__ 83280304Sjkim# endif 84280304Sjkim# include <dlfcn.h> 85280304Sjkim# define HAVE_DLINFO 1 86280304Sjkim# if defined(_AIX) || defined(__CYGWIN__) || \ 87238405Sjkim defined(__SCO_VERSION__) || defined(_SCO_ELF) || \ 88238405Sjkim (defined(__osf__) && !defined(RTLD_NEXT)) || \ 89238405Sjkim (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \ 90280304Sjkim defined(__ANDROID__) 91280304Sjkim# undef HAVE_DLINFO 92280304Sjkim# endif 93238405Sjkim# endif 9468651Skris 9568651Skris/* Part of the hack in "dlfcn_load" ... */ 96280304Sjkim# define DSO_MAX_TRANSLATED_SIZE 256 9768651Skris 98109998Smarkmstatic int dlfcn_load(DSO *dso); 9968651Skrisstatic int dlfcn_unload(DSO *dso); 10068651Skrisstatic void *dlfcn_bind_var(DSO *dso, const char *symname); 10168651Skrisstatic DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname); 102280304Sjkim# if 0 10368651Skrisstatic int dlfcn_unbind(DSO *dso, char *symname, void *symptr); 10468651Skrisstatic int dlfcn_init(DSO *dso); 10568651Skrisstatic int dlfcn_finish(DSO *dso); 106109998Smarkmstatic long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg); 107280304Sjkim# endif 108109998Smarkmstatic char *dlfcn_name_converter(DSO *dso, const char *filename); 109160814Ssimonstatic char *dlfcn_merger(DSO *dso, const char *filespec1, 110280304Sjkim const char *filespec2); 111280304Sjkimstatic int dlfcn_pathbyaddr(void *addr, char *path, int sz); 112238405Sjkimstatic void *dlfcn_globallookup(const char *name); 11368651Skris 11468651Skrisstatic DSO_METHOD dso_meth_dlfcn = { 115280304Sjkim "OpenSSL 'dlfcn' shared library method", 116280304Sjkim dlfcn_load, 117280304Sjkim dlfcn_unload, 118280304Sjkim dlfcn_bind_var, 119280304Sjkim dlfcn_bind_func, 12068651Skris/* For now, "unbind" doesn't exist */ 121280304Sjkim# if 0 122280304Sjkim NULL, /* unbind_var */ 123280304Sjkim NULL, /* unbind_func */ 124280304Sjkim# endif 125280304Sjkim NULL, /* ctrl */ 126280304Sjkim dlfcn_name_converter, 127280304Sjkim dlfcn_merger, 128280304Sjkim NULL, /* init */ 129280304Sjkim NULL, /* finish */ 130280304Sjkim dlfcn_pathbyaddr, 131280304Sjkim dlfcn_globallookup 132280304Sjkim}; 13368651Skris 13468651SkrisDSO_METHOD *DSO_METHOD_dlfcn(void) 135280304Sjkim{ 136280304Sjkim return (&dso_meth_dlfcn); 137280304Sjkim} 13868651Skris 139280304Sjkim/* 140280304Sjkim * Prior to using the dlopen() function, we should decide on the flag we 141280304Sjkim * send. There's a few different ways of doing this and it's a messy 142280304Sjkim * venn-diagram to match up which platforms support what. So as we don't have 143280304Sjkim * autoconf yet, I'm implementing a hack that could be hacked further 144280304Sjkim * relatively easily to deal with cases as we find them. Initially this is to 145280304Sjkim * cope with OpenBSD. 146280304Sjkim */ 147280304Sjkim# if defined(__OpenBSD__) || defined(__NetBSD__) 148280304Sjkim# ifdef DL_LAZY 149280304Sjkim# define DLOPEN_FLAG DL_LAZY 150280304Sjkim# else 151280304Sjkim# ifdef RTLD_NOW 152280304Sjkim# define DLOPEN_FLAG RTLD_NOW 153280304Sjkim# else 154280304Sjkim# define DLOPEN_FLAG 0 155280304Sjkim# endif 156280304Sjkim# endif 157280304Sjkim# else 158280304Sjkim# ifdef OPENSSL_SYS_SUNOS 159280304Sjkim# define DLOPEN_FLAG 1 160280304Sjkim# else 161280304Sjkim# define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */ 162280304Sjkim# endif 163280304Sjkim# endif 16468651Skris 165280304Sjkim/* 166280304Sjkim * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle 167280304Sjkim * (void*) returned from dlopen(). 16868651Skris */ 16968651Skris 170109998Smarkmstatic int dlfcn_load(DSO *dso) 171280304Sjkim{ 172280304Sjkim void *ptr = NULL; 173280304Sjkim /* See applicable comments in dso_dl.c */ 174280304Sjkim char *filename = DSO_convert_filename(dso, NULL); 175280304Sjkim int flags = DLOPEN_FLAG; 17668651Skris 177280304Sjkim if (filename == NULL) { 178280304Sjkim DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME); 179280304Sjkim goto err; 180280304Sjkim } 181280304Sjkim# ifdef RTLD_GLOBAL 182280304Sjkim if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS) 183280304Sjkim flags |= RTLD_GLOBAL; 184280304Sjkim# endif 185280304Sjkim ptr = dlopen(filename, flags); 186280304Sjkim if (ptr == NULL) { 187280304Sjkim DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED); 188280304Sjkim ERR_add_error_data(4, "filename(", filename, "): ", dlerror()); 189280304Sjkim goto err; 190280304Sjkim } 191280304Sjkim if (!sk_void_push(dso->meth_data, (char *)ptr)) { 192280304Sjkim DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR); 193280304Sjkim goto err; 194280304Sjkim } 195280304Sjkim /* Success */ 196280304Sjkim dso->loaded_filename = filename; 197280304Sjkim return (1); 198280304Sjkim err: 199280304Sjkim /* Cleanup! */ 200280304Sjkim if (filename != NULL) 201280304Sjkim OPENSSL_free(filename); 202280304Sjkim if (ptr != NULL) 203280304Sjkim dlclose(ptr); 204280304Sjkim return (0); 205109998Smarkm} 20668651Skris 20768651Skrisstatic int dlfcn_unload(DSO *dso) 208280304Sjkim{ 209280304Sjkim void *ptr; 210280304Sjkim if (dso == NULL) { 211280304Sjkim DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER); 212280304Sjkim return (0); 213280304Sjkim } 214280304Sjkim if (sk_void_num(dso->meth_data) < 1) 215280304Sjkim return (1); 216280304Sjkim ptr = sk_void_pop(dso->meth_data); 217280304Sjkim if (ptr == NULL) { 218280304Sjkim DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE); 219280304Sjkim /* 220280304Sjkim * Should push the value back onto the stack in case of a retry. 221280304Sjkim */ 222280304Sjkim sk_void_push(dso->meth_data, ptr); 223280304Sjkim return (0); 224280304Sjkim } 225280304Sjkim /* For now I'm not aware of any errors associated with dlclose() */ 226280304Sjkim dlclose(ptr); 227280304Sjkim return (1); 228280304Sjkim} 22968651Skris 23068651Skrisstatic void *dlfcn_bind_var(DSO *dso, const char *symname) 231280304Sjkim{ 232280304Sjkim void *ptr, *sym; 23368651Skris 234280304Sjkim if ((dso == NULL) || (symname == NULL)) { 235280304Sjkim DSOerr(DSO_F_DLFCN_BIND_VAR, ERR_R_PASSED_NULL_PARAMETER); 236280304Sjkim return (NULL); 237280304Sjkim } 238280304Sjkim if (sk_void_num(dso->meth_data) < 1) { 239280304Sjkim DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_STACK_ERROR); 240280304Sjkim return (NULL); 241280304Sjkim } 242280304Sjkim ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1); 243280304Sjkim if (ptr == NULL) { 244280304Sjkim DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_NULL_HANDLE); 245280304Sjkim return (NULL); 246280304Sjkim } 247280304Sjkim sym = dlsym(ptr, symname); 248280304Sjkim if (sym == NULL) { 249280304Sjkim DSOerr(DSO_F_DLFCN_BIND_VAR, DSO_R_SYM_FAILURE); 250280304Sjkim ERR_add_error_data(4, "symname(", symname, "): ", dlerror()); 251280304Sjkim return (NULL); 252280304Sjkim } 253280304Sjkim return (sym); 254280304Sjkim} 25568651Skris 25668651Skrisstatic DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname) 257280304Sjkim{ 258280304Sjkim void *ptr; 259280304Sjkim union { 260280304Sjkim DSO_FUNC_TYPE sym; 261280304Sjkim void *dlret; 262280304Sjkim } u; 26368651Skris 264280304Sjkim if ((dso == NULL) || (symname == NULL)) { 265280304Sjkim DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER); 266280304Sjkim return (NULL); 267280304Sjkim } 268280304Sjkim if (sk_void_num(dso->meth_data) < 1) { 269280304Sjkim DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR); 270280304Sjkim return (NULL); 271280304Sjkim } 272280304Sjkim ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1); 273280304Sjkim if (ptr == NULL) { 274280304Sjkim DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE); 275280304Sjkim return (NULL); 276280304Sjkim } 277280304Sjkim u.dlret = dlsym(ptr, symname); 278280304Sjkim if (u.dlret == NULL) { 279280304Sjkim DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE); 280280304Sjkim ERR_add_error_data(4, "symname(", symname, "): ", dlerror()); 281280304Sjkim return (NULL); 282280304Sjkim } 283280304Sjkim return u.sym; 284280304Sjkim} 28568651Skris 286160814Ssimonstatic char *dlfcn_merger(DSO *dso, const char *filespec1, 287280304Sjkim const char *filespec2) 288280304Sjkim{ 289280304Sjkim char *merged; 290160814Ssimon 291280304Sjkim if (!filespec1 && !filespec2) { 292280304Sjkim DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER); 293280304Sjkim return (NULL); 294280304Sjkim } 295280304Sjkim /* 296280304Sjkim * If the first file specification is a rooted path, it rules. same goes 297280304Sjkim * if the second file specification is missing. 298280304Sjkim */ 299280304Sjkim if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) { 300280304Sjkim merged = OPENSSL_malloc(strlen(filespec1) + 1); 301280304Sjkim if (!merged) { 302280304Sjkim DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 303280304Sjkim return (NULL); 304280304Sjkim } 305280304Sjkim strcpy(merged, filespec1); 306280304Sjkim } 307280304Sjkim /* 308280304Sjkim * If the first file specification is missing, the second one rules. 309280304Sjkim */ 310280304Sjkim else if (!filespec1) { 311280304Sjkim merged = OPENSSL_malloc(strlen(filespec2) + 1); 312280304Sjkim if (!merged) { 313280304Sjkim DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 314280304Sjkim return (NULL); 315280304Sjkim } 316280304Sjkim strcpy(merged, filespec2); 317280304Sjkim } else { 318280304Sjkim /* 319280304Sjkim * This part isn't as trivial as it looks. It assumes that the 320280304Sjkim * second file specification really is a directory, and makes no 321280304Sjkim * checks whatsoever. Therefore, the result becomes the 322280304Sjkim * concatenation of filespec2 followed by a slash followed by 323280304Sjkim * filespec1. 324280304Sjkim */ 325280304Sjkim int spec2len, len; 326160814Ssimon 327280304Sjkim spec2len = strlen(filespec2); 328280304Sjkim len = spec2len + strlen(filespec1); 329160814Ssimon 330280304Sjkim if (spec2len && filespec2[spec2len - 1] == '/') { 331280304Sjkim spec2len--; 332280304Sjkim len--; 333280304Sjkim } 334280304Sjkim merged = OPENSSL_malloc(len + 2); 335280304Sjkim if (!merged) { 336280304Sjkim DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 337280304Sjkim return (NULL); 338280304Sjkim } 339280304Sjkim strcpy(merged, filespec2); 340280304Sjkim merged[spec2len] = '/'; 341280304Sjkim strcpy(&merged[spec2len + 1], filespec1); 342280304Sjkim } 343280304Sjkim return (merged); 344280304Sjkim} 345160814Ssimon 346280304Sjkim# ifdef OPENSSL_SYS_MACOSX 347280304Sjkim# define DSO_ext ".dylib" 348280304Sjkim# define DSO_extlen 6 349280304Sjkim# else 350280304Sjkim# define DSO_ext ".so" 351280304Sjkim# define DSO_extlen 3 352280304Sjkim# endif 353205128Ssimon 354109998Smarkmstatic char *dlfcn_name_converter(DSO *dso, const char *filename) 355280304Sjkim{ 356280304Sjkim char *translated; 357280304Sjkim int len, rsize, transform; 358109998Smarkm 359280304Sjkim len = strlen(filename); 360280304Sjkim rsize = len + 1; 361280304Sjkim transform = (strstr(filename, "/") == NULL); 362280304Sjkim if (transform) { 363280304Sjkim /* We will convert this to "%s.so" or "lib%s.so" etc */ 364280304Sjkim rsize += DSO_extlen; /* The length of ".so" */ 365280304Sjkim if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 366280304Sjkim rsize += 3; /* The length of "lib" */ 367280304Sjkim } 368280304Sjkim translated = OPENSSL_malloc(rsize); 369280304Sjkim if (translated == NULL) { 370280304Sjkim DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED); 371280304Sjkim return (NULL); 372280304Sjkim } 373280304Sjkim if (transform) { 374280304Sjkim if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 375280304Sjkim sprintf(translated, "lib%s" DSO_ext, filename); 376280304Sjkim else 377280304Sjkim sprintf(translated, "%s" DSO_ext, filename); 378280304Sjkim } else 379280304Sjkim sprintf(translated, "%s", filename); 380280304Sjkim return (translated); 381280304Sjkim} 38268651Skris 383280304Sjkim# ifdef __sgi 384280304Sjkim/*- 385238405SjkimThis is a quote from IRIX manual for dladdr(3c): 386238405Sjkim 387238405Sjkim <dlfcn.h> does not contain a prototype for dladdr or definition of 388238405Sjkim Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional, 389238405Sjkim but contains no dladdr prototype and no IRIX library contains an 390238405Sjkim implementation. Write your own declaration based on the code below. 391238405Sjkim 392238405Sjkim The following code is dependent on internal interfaces that are not 393238405Sjkim part of the IRIX compatibility guarantee; however, there is no future 394238405Sjkim intention to change this interface, so on a practical level, the code 395238405Sjkim below is safe to use on IRIX. 396238405Sjkim*/ 397280304Sjkim# include <rld_interface.h> 398280304Sjkim# ifndef _RLD_INTERFACE_DLFCN_H_DLADDR 399280304Sjkim# define _RLD_INTERFACE_DLFCN_H_DLADDR 400238405Sjkimtypedef struct Dl_info { 401280304Sjkim const char *dli_fname; 402280304Sjkim void *dli_fbase; 403280304Sjkim const char *dli_sname; 404280304Sjkim void *dli_saddr; 405280304Sjkim int dli_version; 406280304Sjkim int dli_reserved1; 407280304Sjkim long dli_reserved[4]; 408238405Sjkim} Dl_info; 409280304Sjkim# else 410238405Sjkimtypedef struct Dl_info Dl_info; 411280304Sjkim# endif 412280304Sjkim# define _RLD_DLADDR 14 413238405Sjkim 414238405Sjkimstatic int dladdr(void *address, Dl_info *dl) 415238405Sjkim{ 416280304Sjkim void *v; 417280304Sjkim v = _rld_new_interface(_RLD_DLADDR, address, dl); 418280304Sjkim return (int)v; 419238405Sjkim} 420280304Sjkim# endif /* __sgi */ 421238405Sjkim 422280304Sjkimstatic int dlfcn_pathbyaddr(void *addr, char *path, int sz) 423280304Sjkim{ 424280304Sjkim# ifdef HAVE_DLINFO 425280304Sjkim Dl_info dli; 426280304Sjkim int len; 427238405Sjkim 428280304Sjkim if (addr == NULL) { 429280304Sjkim union { 430280304Sjkim int (*f) (void *, char *, int); 431280304Sjkim void *p; 432280304Sjkim } t = { 433280304Sjkim dlfcn_pathbyaddr 434280304Sjkim }; 435280304Sjkim addr = t.p; 436280304Sjkim } 437238405Sjkim 438280304Sjkim if (dladdr(addr, &dli)) { 439280304Sjkim len = (int)strlen(dli.dli_fname); 440280304Sjkim if (sz <= 0) 441280304Sjkim return len + 1; 442280304Sjkim if (len >= sz) 443280304Sjkim len = sz - 1; 444280304Sjkim memcpy(path, dli.dli_fname, len); 445280304Sjkim path[len++] = 0; 446280304Sjkim return len; 447280304Sjkim } 448238405Sjkim 449280304Sjkim ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror()); 450280304Sjkim# endif 451280304Sjkim return -1; 452280304Sjkim} 453238405Sjkim 454238405Sjkimstatic void *dlfcn_globallookup(const char *name) 455280304Sjkim{ 456280304Sjkim void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY); 457238405Sjkim 458280304Sjkim if (handle) { 459280304Sjkim ret = dlsym(handle, name); 460280304Sjkim dlclose(handle); 461280304Sjkim } 462280304Sjkim 463280304Sjkim return ret; 464280304Sjkim} 465280304Sjkim#endif /* DSO_DLFCN */ 466