1 /* $OpenBSD: library_subr.c,v 1.28 2008/04/11 15:30:16 kurt Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Dale Rahn 5 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #define _DYN_LOADER 31 32 #include <sys/types.h> 33 #include <sys/syslimits.h> 34 #include <sys/param.h> 35 #include <sys/queue.h> 36 #include <dirent.h> 37 38 #include "archdep.h" 39 #include "resolve.h" 40 #include "dir.h" 41 #include "sod.h" 42 43 #define DEFAULT_PATH "/usr/lib" 44 45 46 /* STATIC DATA */ 47 struct dlochld _dlopened_child_list; 48 49 50 /* 51 * _dl_match_file() 52 * 53 * This fucntion determines if a given name matches what is specified 54 * in a struct sod. The major must match exactly, and the minor must 55 * be same or larger. 56 * 57 * sodp is updated with the minor if this matches. 58 */ 59 60 int 61 _dl_match_file(struct sod *sodp, char *name, int namelen) 62 { 63 int match; 64 struct sod lsod; 65 char *lname; 66 67 lname = name; 68 if (sodp->sod_library) { 69 if (_dl_strncmp(name, "lib", 3)) 70 return 0; 71 lname += 3; 72 } 73 if (_dl_strncmp(lname, (char *)sodp->sod_name, 74 _dl_strlen((char *)sodp->sod_name))) 75 return 0; 76 77 _dl_build_sod(name, &lsod); 78 79 match = 0; 80 if ((_dl_strcmp((char *)lsod.sod_name, (char *)sodp->sod_name) == 0) && 81 (lsod.sod_library == sodp->sod_library) && 82 ((sodp->sod_major == -1) || (sodp->sod_major == lsod.sod_major)) && 83 ((sodp->sod_minor == -1) || 84 (lsod.sod_minor >= sodp->sod_minor))) { 85 match = 1; 86 87 /* return version matched */ 88 sodp->sod_major = lsod.sod_major; 89 sodp->sod_minor = lsod.sod_minor; 90 } 91 _dl_free((char *)lsod.sod_name); 92 return match; 93 } 94 95 char _dl_hint_store[MAXPATHLEN]; 96 97 char * 98 _dl_find_shlib(struct sod *sodp, const char *searchpath, int nohints) 99 { 100 char *hint, lp[PATH_MAX + 10], *path; 101 struct dirent *dp; 102 const char *pp; 103 int match, len; 104 DIR *dd; 105 struct sod tsod, bsod; /* transient and best sod */ 106 107 /* if we are to search default directories, and hints 108 * are not to be used, search the standard path from ldconfig 109 * (_dl_hint_search_path) or use the default path 110 */ 111 if (nohints) 112 goto nohints; 113 114 if (searchpath == NULL) { 115 /* search 'standard' locations, find any match in the hints */ 116 hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major, 117 sodp->sod_minor, NULL); 118 if (hint) 119 return hint; 120 } else { 121 /* search hints requesting matches for only 122 * the searchpath directories, 123 */ 124 pp = searchpath; 125 while (pp) { 126 path = lp; 127 while (path < lp + PATH_MAX && 128 *pp && *pp != ':' && *pp != ';') 129 *path++ = *pp++; 130 *path = 0; 131 132 /* interpret "" as curdir "." */ 133 if (lp[0] == '\0') { 134 lp[0] = '.'; 135 lp[1] = '\0'; 136 } 137 138 hint = _dl_findhint((char *)sodp->sod_name, 139 sodp->sod_major, sodp->sod_minor, lp); 140 if (hint != NULL) 141 return hint; 142 143 if (*pp) /* Try curdir if ':' at end */ 144 pp++; 145 else 146 pp = 0; 147 } 148 } 149 150 /* 151 * For each directory in the searchpath, read the directory 152 * entries looking for a match to sod. filename compare is 153 * done by _dl_match_file() 154 */ 155 nohints: 156 if (searchpath == NULL) { 157 if (_dl_hint_search_path != NULL) 158 searchpath = _dl_hint_search_path; 159 else 160 searchpath = DEFAULT_PATH; 161 } 162 pp = searchpath; 163 while (pp) { 164 path = lp; 165 while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';') 166 *path++ = *pp++; 167 *path = 0; 168 169 /* interpret "" as curdir "." */ 170 if (lp[0] == '\0') { 171 lp[0] = '.'; 172 lp[1] = '\0'; 173 } 174 175 if ((dd = _dl_opendir(lp)) != NULL) { 176 match = 0; 177 while ((dp = _dl_readdir(dd)) != NULL) { 178 tsod = *sodp; 179 if (_dl_match_file(&tsod, dp->d_name, 180 dp->d_namlen)) { 181 /* 182 * When a match is found, tsod is 183 * updated with the major+minor found. 184 * This version is compared with the 185 * largest so far (kept in bsod), 186 * and saved if larger. 187 */ 188 if (!match || 189 tsod.sod_major == -1 || 190 tsod.sod_major > bsod.sod_major || 191 ((tsod.sod_major == 192 bsod.sod_major) && 193 tsod.sod_minor > bsod.sod_minor)) { 194 bsod = tsod; 195 match = 1; 196 len = _dl_strlcpy( 197 _dl_hint_store, lp, 198 MAXPATHLEN); 199 if (lp[len-1] != '/') { 200 _dl_hint_store[len] = 201 '/'; 202 len++; 203 } 204 _dl_strlcpy( 205 &_dl_hint_store[len], 206 dp->d_name, 207 MAXPATHLEN-len); 208 if (tsod.sod_major == -1) 209 break; 210 } 211 } 212 } 213 _dl_closedir(dd); 214 if (match) { 215 *sodp = bsod; 216 return (_dl_hint_store); 217 } 218 } 219 220 if (*pp) /* Try curdir if ':' at end */ 221 pp++; 222 else 223 pp = 0; 224 } 225 return NULL; 226 } 227 228 /* 229 * Load a shared object. Search order is: 230 * If the name contains a '/' use only the path preceding the 231 * library name and do not continue on to other methods if not 232 * found. 233 * search hints for match in path preceding library name 234 * this will only match specific library version. 235 * search path preceding library name 236 * this will find largest minor version in path provided 237 * try the LD_LIBRARY_PATH specification (if present) 238 * search hints for match in LD_LIBRARY_PATH dirs 239 * this will only match specific libary version. 240 * search LD_LIBRARY_PATH dirs for match. 241 * this will find largest minor version in first dir found. 242 * check DT_RPATH paths, (if present) 243 * search hints for match in DT_RPATH dirs 244 * this will only match specific libary version. 245 * search DT_RPATH dirs for match. 246 * this will find largest minor version in first dir found. 247 * last look in default search directory, either as specified 248 * by ldconfig or default to '/usr/lib' 249 */ 250 251 252 elf_object_t * 253 _dl_load_shlib(const char *libname, elf_object_t *parent, int type, int flags) 254 { 255 int try_any_minor, ignore_hints; 256 struct sod sod, req_sod; 257 elf_object_t *object = NULL; 258 char *hint; 259 260 try_any_minor = 0; 261 ignore_hints = 0; 262 263 if (_dl_strchr(libname, '/')) { 264 char *lpath, *lname; 265 lpath = _dl_strdup(libname); 266 lname = _dl_strrchr(lpath, '/'); 267 if (lname == NULL) { 268 _dl_free(lpath); 269 _dl_errno = DL_NOT_FOUND; 270 return (object); 271 } 272 *lname = '\0'; 273 lname++; 274 if (*lname == '\0') { 275 _dl_free(lpath); 276 _dl_errno = DL_NOT_FOUND; 277 return (object); 278 } 279 280 _dl_build_sod(lname, &sod); 281 req_sod = sod; 282 283 fullpathagain: 284 hint = _dl_find_shlib(&req_sod, lpath, ignore_hints); 285 if (hint != NULL) 286 goto fullpathdone; 287 288 if (try_any_minor == 0) { 289 try_any_minor = 1; 290 ignore_hints = 1; 291 req_sod.sod_minor = -1; 292 goto fullpathagain; 293 } 294 _dl_errno = DL_NOT_FOUND; 295 fullpathdone: 296 _dl_free(lpath); 297 goto done; 298 } 299 300 _dl_build_sod(libname, &sod); 301 req_sod = sod; 302 303 again: 304 /* No '/' in name. Scan the known places, LD_LIBRARY_PATH first. */ 305 if (_dl_libpath != NULL) { 306 hint = _dl_find_shlib(&req_sod, _dl_libpath, ignore_hints); 307 if (hint != NULL) 308 goto done; 309 } 310 311 /* Check DT_RPATH. */ 312 if (parent->dyn.rpath != NULL) { 313 hint = _dl_find_shlib(&req_sod, parent->dyn.rpath, ignore_hints); 314 if (hint != NULL) 315 goto done; 316 } 317 318 /* Check main program's DT_RPATH, if parent != main program */ 319 if (parent != _dl_objects && _dl_objects->dyn.rpath != NULL) { 320 hint = _dl_find_shlib(&req_sod, _dl_objects->dyn.rpath, ignore_hints); 321 if (hint != NULL) 322 goto done; 323 } 324 325 /* check 'standard' locations */ 326 hint = _dl_find_shlib(&req_sod, NULL, ignore_hints); 327 if (hint != NULL) 328 goto done; 329 330 if (try_any_minor == 0) { 331 try_any_minor = 1; 332 ignore_hints = 1; 333 req_sod.sod_minor = -1; 334 goto again; 335 } 336 _dl_errno = DL_NOT_FOUND; 337 done: 338 if (hint != NULL) { 339 if (req_sod.sod_minor < sod.sod_minor) 340 _dl_printf("warning: lib%s.so.%d.%d: " 341 "minor version >= %d expected, " 342 "using it anyway\n", 343 sod.sod_name, sod.sod_major, 344 req_sod.sod_minor, sod.sod_minor); 345 object = _dl_tryload_shlib(hint, type, flags); 346 } 347 _dl_free((char *)sod.sod_name); 348 return(object); 349 } 350 351 352 void 353 _dl_link_dlopen(elf_object_t *dep) 354 { 355 struct dep_node *n; 356 357 dep->opencount++; 358 359 if (OBJECT_DLREF_CNT(dep) > 1) 360 return; 361 362 n = _dl_malloc(sizeof *n); 363 if (n == NULL) 364 _dl_exit(5); 365 366 n->data = dep; 367 TAILQ_INSERT_TAIL(&_dlopened_child_list, n, next_sib); 368 369 DL_DEB(("linking %s as dlopen()ed\n", dep->load_name)); 370 } 371 372 void 373 _dl_child_refcnt_decrement(elf_object_t *object) 374 { 375 struct dep_node *n; 376 377 object->refcount--; 378 if (OBJECT_REF_CNT(object) == 0) 379 TAILQ_FOREACH(n, &object->child_list, next_sib) 380 _dl_child_refcnt_decrement(n->data); 381 } 382 383 void 384 _dl_notify_unload_shlib(elf_object_t *object) 385 { 386 struct dep_node *n; 387 388 if (OBJECT_REF_CNT(object) == 0) 389 TAILQ_FOREACH(n, &object->child_list, next_sib) 390 _dl_child_refcnt_decrement(n->data); 391 392 if (OBJECT_DLREF_CNT(object) == 0) { 393 TAILQ_FOREACH(n, &object->grpref_list, next_sib) { 394 n->data->grprefcount--; 395 _dl_notify_unload_shlib(n->data); 396 } 397 } 398 } 399 400 void 401 _dl_unload_dlopen(void) 402 { 403 struct dep_node *node; 404 405 TAILQ_FOREACH_REVERSE(node, &_dlopened_child_list, dlochld, next_sib) { 406 /* dont dlclose the main program */ 407 if (node->data == _dl_objects) 408 continue; 409 410 while (node->data->opencount > 0) { 411 node->data->opencount--; 412 _dl_notify_unload_shlib(node->data); 413 _dl_run_all_dtors(); 414 } 415 } 416 } 417 418 void 419 _dl_link_grpref(elf_object_t *load_group, elf_object_t *load_object) 420 { 421 struct dep_node *n; 422 423 n = _dl_malloc(sizeof *n); 424 if (n == NULL) 425 _dl_exit(7); 426 n->data = load_group; 427 TAILQ_INSERT_TAIL(&load_object->grpref_list, n, next_sib); 428 load_group->grprefcount++; 429 } 430 431 void 432 _dl_link_child(elf_object_t *dep, elf_object_t *p) 433 { 434 struct dep_node *n; 435 436 n = _dl_malloc(sizeof *n); 437 if (n == NULL) 438 _dl_exit(7); 439 n->data = dep; 440 TAILQ_INSERT_TAIL(&p->child_list, n, next_sib); 441 442 dep->refcount++; 443 444 DL_DEB(("linking dep %s as child of %s\n", dep->load_name, 445 p->load_name)); 446 } 447 448 void 449 _dl_link_grpsym(elf_object_t *object) 450 { 451 struct dep_node *n; 452 453 TAILQ_FOREACH(n, &_dl_loading_object->grpsym_list, next_sib) 454 if (n->data == object) 455 return; /* found, dont bother adding */ 456 457 n = _dl_malloc(sizeof *n); 458 if (n == NULL) 459 _dl_exit(8); 460 n->data = object; 461 TAILQ_INSERT_TAIL(&_dl_loading_object->grpsym_list, n, next_sib); 462 } 463 464 void 465 _dl_cache_grpsym_list(elf_object_t *object) 466 { 467 struct dep_node *n; 468 469 /* 470 * grpsym_list is an ordered list of all child libs of the 471 * _dl_loading_object with no dups. The order is equalivant 472 * to a breath-first traversal of the child list without dups. 473 */ 474 475 TAILQ_FOREACH(n, &object->child_list, next_sib) 476 _dl_link_grpsym(n->data); 477 478 TAILQ_FOREACH(n, &object->child_list, next_sib) 479 _dl_cache_grpsym_list(n->data); 480 } 481