| 1 | /* loader-dlopen.c --  dynamic linking with dlopen/dlsym | 
|---|
| 2 |  | 
|---|
| 3 | Copyright (C) 1998-2000, 2004, 2006-2008, 2011-2015 Free Software | 
|---|
| 4 | Foundation, Inc. | 
|---|
| 5 | Written by Thomas Tanner, 1998 | 
|---|
| 6 |  | 
|---|
| 7 | NOTE: The canonical source of this file is maintained with the | 
|---|
| 8 | GNU Libtool package.  Report bugs to bug-libtool@gnu.org. | 
|---|
| 9 |  | 
|---|
| 10 | GNU Libltdl is free software; you can redistribute it and/or | 
|---|
| 11 | modify it under the terms of the GNU Lesser General Public | 
|---|
| 12 | License as published by the Free Software Foundation; either | 
|---|
| 13 | version 2 of the License, or (at your option) any later version. | 
|---|
| 14 |  | 
|---|
| 15 | As a special exception to the GNU Lesser General Public License, | 
|---|
| 16 | if you distribute this file as part of a program or library that | 
|---|
| 17 | is built using GNU Libtool, you may include this file under the | 
|---|
| 18 | same distribution terms that you use for the rest of that program. | 
|---|
| 19 |  | 
|---|
| 20 | GNU Libltdl is distributed in the hope that it will be useful, | 
|---|
| 21 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 23 | GNU Lesser General Public License for more details. | 
|---|
| 24 |  | 
|---|
| 25 | You should have received a copy of the GNU Lesser General Public | 
|---|
| 26 | License along with GNU Libltdl; see the file COPYING.LIB.  If not, a | 
|---|
| 27 | copy can be downloaded from  http://www.gnu.org/licenses/lgpl.html, | 
|---|
| 28 | or obtained by writing to the Free Software Foundation, Inc., | 
|---|
| 29 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
|---|
| 30 | */ | 
|---|
| 31 |  | 
|---|
| 32 | #include "lt__private.h" | 
|---|
| 33 | #include "lt_dlloader.h" | 
|---|
| 34 |  | 
|---|
| 35 | /* Use the preprocessor to rename non-static symbols to avoid namespace | 
|---|
| 36 | collisions when the loader code is statically linked into libltdl. | 
|---|
| 37 | Use the "<module_name>_LTX_" prefix so that the symbol addresses can | 
|---|
| 38 | be fetched from the preloaded symbol list by lt_dlsym():  */ | 
|---|
| 39 | #define get_vtable	dlopen_LTX_get_vtable | 
|---|
| 40 |  | 
|---|
| 41 | LT_BEGIN_C_DECLS | 
|---|
| 42 | LT_SCOPE lt_dlvtable *get_vtable (lt_user_data loader_data); | 
|---|
| 43 | LT_END_C_DECLS | 
|---|
| 44 |  | 
|---|
| 45 |  | 
|---|
| 46 | /* Boilerplate code to set up the vtable for hooking this loader into | 
|---|
| 47 | libltdl's loader list:  */ | 
|---|
| 48 | static int	 vl_exit  (lt_user_data loader_data); | 
|---|
| 49 | static lt_module vm_open  (lt_user_data loader_data, const char *filename, | 
|---|
| 50 | lt_dladvise advise); | 
|---|
| 51 | static int	 vm_close (lt_user_data loader_data, lt_module module); | 
|---|
| 52 | static void *	 vm_sym   (lt_user_data loader_data, lt_module module, | 
|---|
| 53 | const char *symbolname); | 
|---|
| 54 |  | 
|---|
| 55 | static lt_dlvtable *vtable = 0; | 
|---|
| 56 |  | 
|---|
| 57 | /* Return the vtable for this loader, only the name and sym_prefix | 
|---|
| 58 | attributes (plus the virtual function implementations, obviously) | 
|---|
| 59 | change between loaders.  */ | 
|---|
| 60 | lt_dlvtable * | 
|---|
| 61 | get_vtable (lt_user_data loader_data) | 
|---|
| 62 | { | 
|---|
| 63 | if (!vtable) | 
|---|
| 64 | { | 
|---|
| 65 | vtable = (lt_dlvtable *) lt__zalloc (sizeof *vtable); | 
|---|
| 66 | } | 
|---|
| 67 |  | 
|---|
| 68 | if (vtable && !vtable->name) | 
|---|
| 69 | { | 
|---|
| 70 | vtable->name		= "lt_dlopen"; | 
|---|
| 71 | #if defined DLSYM_USCORE | 
|---|
| 72 | vtable->sym_prefix	= "_"; | 
|---|
| 73 | #endif | 
|---|
| 74 | vtable->module_open	= vm_open; | 
|---|
| 75 | vtable->module_close	= vm_close; | 
|---|
| 76 | vtable->find_sym		= vm_sym; | 
|---|
| 77 | vtable->dlloader_exit	= vl_exit; | 
|---|
| 78 | vtable->dlloader_data	= loader_data; | 
|---|
| 79 | vtable->priority		= LT_DLLOADER_PREPEND; | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 | if (vtable && (vtable->dlloader_data != loader_data)) | 
|---|
| 83 | { | 
|---|
| 84 | LT__SETERROR (INIT_LOADER); | 
|---|
| 85 | return 0; | 
|---|
| 86 | } | 
|---|
| 87 |  | 
|---|
| 88 | return vtable; | 
|---|
| 89 | } | 
|---|
| 90 |  | 
|---|
| 91 |  | 
|---|
| 92 |  | 
|---|
| 93 | /* --- IMPLEMENTATION --- */ | 
|---|
| 94 |  | 
|---|
| 95 |  | 
|---|
| 96 | #if defined HAVE_DLFCN_H | 
|---|
| 97 | #  include <dlfcn.h> | 
|---|
| 98 | #endif | 
|---|
| 99 |  | 
|---|
| 100 | #if defined HAVE_SYS_DL_H | 
|---|
| 101 | #  include <sys/dl.h> | 
|---|
| 102 | #endif | 
|---|
| 103 |  | 
|---|
| 104 |  | 
|---|
| 105 | /* We may have to define LT_LAZY_OR_NOW in the command line if we | 
|---|
| 106 | find out it does not work in some platform. */ | 
|---|
| 107 | #if !defined LT_LAZY_OR_NOW | 
|---|
| 108 | #  if defined RTLD_LAZY | 
|---|
| 109 | #    define LT_LAZY_OR_NOW	RTLD_LAZY | 
|---|
| 110 | #  else | 
|---|
| 111 | #    if defined DL_LAZY | 
|---|
| 112 | #      define LT_LAZY_OR_NOW	DL_LAZY | 
|---|
| 113 | #    endif | 
|---|
| 114 | #  endif /* !RTLD_LAZY */ | 
|---|
| 115 | #endif | 
|---|
| 116 | #if !defined LT_LAZY_OR_NOW | 
|---|
| 117 | #  if defined RTLD_NOW | 
|---|
| 118 | #    define LT_LAZY_OR_NOW	RTLD_NOW | 
|---|
| 119 | #  else | 
|---|
| 120 | #    if defined DL_NOW | 
|---|
| 121 | #      define LT_LAZY_OR_NOW	DL_NOW | 
|---|
| 122 | #    endif | 
|---|
| 123 | #  endif /* !RTLD_NOW */ | 
|---|
| 124 | #endif | 
|---|
| 125 | #if !defined LT_LAZY_OR_NOW | 
|---|
| 126 | #  define LT_LAZY_OR_NOW	0 | 
|---|
| 127 | #endif /* !LT_LAZY_OR_NOW */ | 
|---|
| 128 |  | 
|---|
| 129 | /* We only support local and global symbols from modules for loaders | 
|---|
| 130 | that provide such a thing, otherwise the system default is used.  */ | 
|---|
| 131 | #if !defined RTLD_GLOBAL | 
|---|
| 132 | #  if defined DL_GLOBAL | 
|---|
| 133 | #    define RTLD_GLOBAL		DL_GLOBAL | 
|---|
| 134 | #  endif | 
|---|
| 135 | #endif /* !RTLD_GLOBAL */ | 
|---|
| 136 | #if !defined RTLD_LOCAL | 
|---|
| 137 | #  if defined DL_LOCAL | 
|---|
| 138 | #    define RTLD_LOCAL		DL_LOCAL | 
|---|
| 139 | #  endif | 
|---|
| 140 | #endif /* !RTLD_LOCAL */ | 
|---|
| 141 |  | 
|---|
| 142 | #if defined HAVE_DLERROR | 
|---|
| 143 | #  define DLERROR(arg)	dlerror () | 
|---|
| 144 | #else | 
|---|
| 145 | #  define DLERROR(arg)	LT__STRERROR (arg) | 
|---|
| 146 | #endif | 
|---|
| 147 |  | 
|---|
| 148 | #define DL__SETERROR(errorcode) \ | 
|---|
| 149 | LT__SETERRORSTR (DLERROR (errorcode)) | 
|---|
| 150 |  | 
|---|
| 151 |  | 
|---|
| 152 | /* A function called through the vtable when this loader is no | 
|---|
| 153 | longer needed by the application.  */ | 
|---|
| 154 | static int | 
|---|
| 155 | vl_exit (lt_user_data loader_data LT__UNUSED) | 
|---|
| 156 | { | 
|---|
| 157 | vtable = NULL; | 
|---|
| 158 | return 0; | 
|---|
| 159 | } | 
|---|
| 160 |  | 
|---|
| 161 |  | 
|---|
| 162 | /* A function called through the vtable to open a module with this | 
|---|
| 163 | loader.  Returns an opaque representation of the newly opened | 
|---|
| 164 | module for processing with this loader's other vtable functions.  */ | 
|---|
| 165 | static lt_module | 
|---|
| 166 | vm_open (lt_user_data loader_data LT__UNUSED, const char *filename, | 
|---|
| 167 | lt_dladvise advise) | 
|---|
| 168 | { | 
|---|
| 169 | int		module_flags = LT_LAZY_OR_NOW; | 
|---|
| 170 | lt_module	module; | 
|---|
| 171 | #ifdef RTLD_MEMBER | 
|---|
| 172 | int		len = LT_STRLEN (filename); | 
|---|
| 173 | #endif | 
|---|
| 174 |  | 
|---|
| 175 | if (advise) | 
|---|
| 176 | { | 
|---|
| 177 | #ifdef RTLD_GLOBAL | 
|---|
| 178 | /* If there is some means of asking for global symbol resolution, | 
|---|
| 179 | do so.  */ | 
|---|
| 180 | if (advise->is_symglobal) | 
|---|
| 181 | module_flags |= RTLD_GLOBAL; | 
|---|
| 182 | #else | 
|---|
| 183 | /* Otherwise, reset that bit so the caller can tell it wasn't | 
|---|
| 184 | acted on.  */ | 
|---|
| 185 | advise->is_symglobal = 0; | 
|---|
| 186 | #endif | 
|---|
| 187 |  | 
|---|
| 188 | /* And similarly for local only symbol resolution.  */ | 
|---|
| 189 | #ifdef RTLD_LOCAL | 
|---|
| 190 | if (advise->is_symlocal) | 
|---|
| 191 | module_flags |= RTLD_LOCAL; | 
|---|
| 192 | #else | 
|---|
| 193 | advise->is_symlocal = 0; | 
|---|
| 194 | #endif | 
|---|
| 195 | } | 
|---|
| 196 |  | 
|---|
| 197 | #ifdef RTLD_MEMBER /* AIX */ | 
|---|
| 198 | if (len >= 4) /* at least "l(m)" */ | 
|---|
| 199 | { | 
|---|
| 200 | /* Advise loading an archive member only if the filename really | 
|---|
| 201 | contains both the opening and closing parent, and a member. */ | 
|---|
| 202 | if (filename[len-1] == ')') | 
|---|
| 203 | { | 
|---|
| 204 | const char *opening = strrchr(filename, '('); | 
|---|
| 205 | if (opening && opening < (filename+len-2) && strchr(opening+1, '/') == NULL) | 
|---|
| 206 | module_flags |= RTLD_MEMBER; | 
|---|
| 207 | } | 
|---|
| 208 | } | 
|---|
| 209 | #endif | 
|---|
| 210 |  | 
|---|
| 211 | module = dlopen (filename, module_flags); | 
|---|
| 212 |  | 
|---|
| 213 | #if defined RTLD_MEMBER && defined LT_SHARED_LIB_MEMBER | 
|---|
| 214 | if (!module && len && !(module_flags & RTLD_MEMBER) && errno == ENOEXEC) | 
|---|
| 215 | { | 
|---|
| 216 | /* Loading without a member specified failed with "Exec format error". | 
|---|
| 217 | So the file is there, but either has wrong bitwidth, or is an | 
|---|
| 218 | archive eventually containing the default shared archive member. | 
|---|
| 219 | Retry with default member, getting same error in worst case. */ | 
|---|
| 220 | const char *member = LT_SHARED_LIB_MEMBER; | 
|---|
| 221 |  | 
|---|
| 222 | char *attempt = MALLOC (char, len + strlen (member) + 1); | 
|---|
| 223 | if (!attempt) | 
|---|
| 224 | { | 
|---|
| 225 | LT__SETERROR (NO_MEMORY); | 
|---|
| 226 | return module; | 
|---|
| 227 | } | 
|---|
| 228 |  | 
|---|
| 229 | sprintf (attempt, "%s%s", filename, member); | 
|---|
| 230 | module = vm_open (loader_data, attempt, advise); | 
|---|
| 231 | FREE (attempt); | 
|---|
| 232 | return module; | 
|---|
| 233 | } | 
|---|
| 234 | #endif | 
|---|
| 235 |  | 
|---|
| 236 | if (!module) | 
|---|
| 237 | { | 
|---|
| 238 | DL__SETERROR (CANNOT_OPEN); | 
|---|
| 239 | } | 
|---|
| 240 |  | 
|---|
| 241 | return module; | 
|---|
| 242 | } | 
|---|
| 243 |  | 
|---|
| 244 |  | 
|---|
| 245 | /* A function called through the vtable when a particular module | 
|---|
| 246 | should be unloaded.  */ | 
|---|
| 247 | static int | 
|---|
| 248 | vm_close (lt_user_data loader_data LT__UNUSED, lt_module module) | 
|---|
| 249 | { | 
|---|
| 250 | int errors = 0; | 
|---|
| 251 |  | 
|---|
| 252 | if (dlclose (module) != 0) | 
|---|
| 253 | { | 
|---|
| 254 | DL__SETERROR (CANNOT_CLOSE); | 
|---|
| 255 | ++errors; | 
|---|
| 256 | } | 
|---|
| 257 |  | 
|---|
| 258 | return errors; | 
|---|
| 259 | } | 
|---|
| 260 |  | 
|---|
| 261 |  | 
|---|
| 262 | /* A function called through the vtable to get the address of | 
|---|
| 263 | a symbol loaded from a particular module.  */ | 
|---|
| 264 | static void * | 
|---|
| 265 | vm_sym (lt_user_data loader_data LT__UNUSED, lt_module module, const char *name) | 
|---|
| 266 | { | 
|---|
| 267 | void *address = dlsym (module, name); | 
|---|
| 268 |  | 
|---|
| 269 | if (!address) | 
|---|
| 270 | { | 
|---|
| 271 | DL__SETERROR (SYMBOL_NOT_FOUND); | 
|---|
| 272 | } | 
|---|
| 273 |  | 
|---|
| 274 | return address; | 
|---|
| 275 | } | 
|---|
| 276 |  | 
|---|