1 /* $OpenBSD: library_subr.c,v 1.41 2014/07/10 09:03:01 otto 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/param.h> 34 #include <sys/queue.h> 35 #include <limits.h> 36 #include <dirent.h> 37 38 #include "archdep.h" 39 #include "resolve.h" 40 #include "dir.h" 41 #include "sod.h" 42 43 char * _dl_default_path[2] = { "/usr/lib", NULL }; 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, const char *name, int namelen) 62 { 63 int match; 64 struct sod lsod; 65 const 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 /* 96 * _dl_cmp_sod() 97 * 98 * This fucntion compares sod structs. The major must match exactly, 99 * and the minor must be same or larger. 100 * 101 * sodp is updated with the minor if this matches. 102 */ 103 104 static int 105 _dl_cmp_sod(struct sod *sodp, const struct sod *lsod) 106 { 107 int match; 108 109 match = 1; 110 if ((_dl_strcmp((char *)lsod->sod_name, (char *)sodp->sod_name) == 0) && 111 (lsod->sod_library == sodp->sod_library) && 112 ((sodp->sod_major == -1) || (sodp->sod_major == lsod->sod_major)) && 113 ((sodp->sod_minor == -1) || 114 (lsod->sod_minor >= sodp->sod_minor))) { 115 match = 0; 116 117 /* return version matched */ 118 sodp->sod_major = lsod->sod_major; 119 sodp->sod_minor = lsod->sod_minor; 120 } 121 return match; 122 } 123 124 char _dl_hint_store[MAXPATHLEN]; 125 126 char * 127 _dl_find_shlib(struct sod *sodp, char **searchpath, int nohints) 128 { 129 char *hint, **pp; 130 struct dirent *dp; 131 int match, len; 132 _dl_DIR *dd; 133 struct sod tsod, bsod; /* transient and best sod */ 134 135 /* if we are to search default directories, and hints 136 * are not to be used, search the standard path from ldconfig 137 * (_dl_hint_search_path) or use the default path 138 */ 139 if (nohints) 140 goto nohints; 141 142 if (searchpath == NULL) { 143 /* search 'standard' locations, find any match in the hints */ 144 hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major, 145 sodp->sod_minor, NULL); 146 if (hint) 147 return hint; 148 } else { 149 /* search hints requesting matches for only 150 * the searchpath directories, 151 */ 152 for (pp = searchpath; *pp != NULL; pp++) { 153 hint = _dl_findhint((char *)sodp->sod_name, 154 sodp->sod_major, sodp->sod_minor, *pp); 155 if (hint != NULL) 156 return hint; 157 } 158 } 159 160 /* 161 * For each directory in the searchpath, read the directory 162 * entries looking for a match to sod. filename compare is 163 * done by _dl_match_file() 164 */ 165 nohints: 166 if (searchpath == NULL) { 167 if (_dl_hint_search_path != NULL) 168 searchpath = _dl_hint_search_path; 169 else 170 searchpath = _dl_default_path; 171 } 172 _dl_memset(&bsod, 0, sizeof(bsod)); 173 for (pp = searchpath; *pp != NULL; pp++) { 174 if ((dd = _dl_opendir(*pp)) != NULL) { 175 match = 0; 176 while ((dp = _dl_readdir(dd)) != NULL) { 177 tsod = *sodp; 178 if (_dl_match_file(&tsod, dp->d_name, 179 dp->d_namlen)) { 180 /* 181 * When a match is found, tsod is 182 * updated with the major+minor found. 183 * This version is compared with the 184 * largest so far (kept in bsod), 185 * and saved if larger. 186 */ 187 if (!match || 188 tsod.sod_major == -1 || 189 tsod.sod_major > bsod.sod_major || 190 ((tsod.sod_major == 191 bsod.sod_major) && 192 tsod.sod_minor > bsod.sod_minor)) { 193 bsod = tsod; 194 match = 1; 195 len = _dl_strlcpy( 196 _dl_hint_store, *pp, 197 MAXPATHLEN); 198 if (pp[0][len-1] != '/') { 199 _dl_hint_store[len] = 200 '/'; 201 len++; 202 } 203 _dl_strlcpy( 204 &_dl_hint_store[len], 205 dp->d_name, 206 MAXPATHLEN-len); 207 if (tsod.sod_major == -1) 208 break; 209 } 210 } 211 } 212 _dl_closedir(dd); 213 if (match) { 214 *sodp = bsod; 215 return (_dl_hint_store); 216 } 217 } 218 } 219 return NULL; 220 } 221 222 static elf_object_t * 223 _dl_lookup_object(const char *req_name, struct sod *req_sod) 224 { 225 elf_object_t *object = _dl_objects; 226 227 while (object) { 228 char *soname; 229 230 if (_dl_cmp_sod(req_sod, &object->sod) == 0) 231 return(object); 232 233 soname = (char *)object->Dyn.info[DT_SONAME]; 234 if (soname != NULL) { 235 if (_dl_strcmp(req_name, soname) == 0) 236 return(object); 237 } 238 239 object = object->next; 240 } 241 242 return(NULL); 243 } 244 245 static elf_object_t * 246 _dl_find_loaded_shlib(const char *req_name, struct sod req_sod, int flags) 247 { 248 elf_object_t *object; 249 250 object = _dl_lookup_object(req_name, &req_sod); 251 252 /* if not found retry with any minor */ 253 if (object == NULL && req_sod.sod_library && req_sod.sod_minor != -1) { 254 short orig_minor = req_sod.sod_minor; 255 req_sod.sod_minor = -1; 256 object = _dl_lookup_object(req_name, &req_sod); 257 258 if (object != NULL && req_sod.sod_minor < orig_minor) 259 _dl_printf("warning: lib%s.so.%d.%d: " 260 "minor version >= %d expected, " 261 "using it anyway\n", 262 req_sod.sod_name, req_sod.sod_major, 263 req_sod.sod_minor, orig_minor); 264 } 265 266 if (object) { /* Already loaded */ 267 object->obj_flags |= flags & DF_1_GLOBAL; 268 if (_dl_loading_object == NULL) 269 _dl_loading_object = object; 270 if (object->load_object != _dl_objects && 271 object->load_object != _dl_loading_object) { 272 _dl_link_grpref(object->load_object, _dl_loading_object); 273 } 274 } 275 276 return (object); 277 } 278 279 /* 280 * Load a shared object. Search order is: 281 * First check loaded objects for a matching shlib, otherwise: 282 * 283 * If the name contains a '/' use only the path preceding the 284 * library name and do not continue on to other methods if not 285 * found. 286 * search hints for match in path preceding library name 287 * this will only match specific library version. 288 * search path preceding library name 289 * this will find largest minor version in path provided 290 * try the LD_LIBRARY_PATH specification (if present) 291 * search hints for match in LD_LIBRARY_PATH dirs 292 * this will only match specific libary version. 293 * search LD_LIBRARY_PATH dirs for match. 294 * this will find largest minor version in first dir found. 295 * check DT_RPATH paths, (if present) 296 * search hints for match in DT_RPATH dirs 297 * this will only match specific libary version. 298 * search DT_RPATH dirs for match. 299 * this will find largest minor version in first dir found. 300 * last look in default search directory, either as specified 301 * by ldconfig or default to '/usr/lib' 302 */ 303 304 305 elf_object_t * 306 _dl_load_shlib(const char *libname, elf_object_t *parent, int type, int flags) 307 { 308 int try_any_minor, ignore_hints; 309 struct sod sod, req_sod; 310 elf_object_t *object = NULL; 311 char *hint; 312 313 try_any_minor = 0; 314 ignore_hints = 0; 315 316 if (_dl_strchr(libname, '/')) { 317 char *paths[2]; 318 char *lpath, *lname; 319 lpath = _dl_strdup(libname); 320 if (lpath == NULL) 321 _dl_exit(5); 322 lname = _dl_strrchr(lpath, '/'); 323 if (lname == NULL) { 324 _dl_free(lpath); 325 _dl_errno = DL_NOT_FOUND; 326 return (object); 327 } 328 *lname = '\0'; 329 lname++; 330 if (*lname == '\0') { 331 _dl_free(lpath); 332 _dl_errno = DL_NOT_FOUND; 333 return (object); 334 } 335 336 _dl_build_sod(lname, &sod); 337 req_sod = sod; 338 339 paths[0] = lpath; 340 paths[1] = NULL; 341 fullpathagain: 342 hint = _dl_find_shlib(&req_sod, paths, ignore_hints); 343 if (hint != NULL) 344 goto fullpathdone; 345 346 if (try_any_minor == 0) { 347 try_any_minor = 1; 348 ignore_hints = 1; 349 req_sod.sod_minor = -1; 350 goto fullpathagain; 351 } 352 _dl_errno = DL_NOT_FOUND; 353 fullpathdone: 354 _dl_free(lpath); 355 goto done; 356 } 357 358 _dl_build_sod(libname, &sod); 359 req_sod = sod; 360 361 object = _dl_find_loaded_shlib(libname, req_sod, flags); 362 if (object) { 363 _dl_free((char *)sod.sod_name); 364 return (object); 365 } 366 367 again: 368 /* No '/' in name. Scan the known places, LD_LIBRARY_PATH first. */ 369 if (_dl_libpath != NULL) { 370 hint = _dl_find_shlib(&req_sod, _dl_libpath, ignore_hints); 371 if (hint != NULL) 372 goto done; 373 } 374 375 /* Check DT_RPATH. */ 376 if (parent->rpath != NULL) { 377 hint = _dl_find_shlib(&req_sod, parent->rpath, ignore_hints); 378 if (hint != NULL) 379 goto done; 380 } 381 382 /* Check main program's DT_RPATH, if parent != main program */ 383 if (parent != _dl_objects && _dl_objects->rpath != NULL) { 384 hint = _dl_find_shlib(&req_sod, _dl_objects->rpath, ignore_hints); 385 if (hint != NULL) 386 goto done; 387 } 388 389 /* check 'standard' locations */ 390 hint = _dl_find_shlib(&req_sod, NULL, ignore_hints); 391 if (hint != NULL) 392 goto done; 393 394 if (try_any_minor == 0) { 395 try_any_minor = 1; 396 ignore_hints = 1; 397 req_sod.sod_minor = -1; 398 goto again; 399 } 400 _dl_errno = DL_NOT_FOUND; 401 done: 402 if (hint != NULL) { 403 if (req_sod.sod_minor < sod.sod_minor) 404 _dl_printf("warning: lib%s.so.%d.%d: " 405 "minor version >= %d expected, " 406 "using it anyway\n", 407 sod.sod_name, sod.sod_major, 408 req_sod.sod_minor, sod.sod_minor); 409 object = _dl_tryload_shlib(hint, type, flags); 410 } 411 _dl_free((char *)sod.sod_name); 412 return(object); 413 } 414 415 416 void 417 _dl_link_dlopen(elf_object_t *dep) 418 { 419 struct dep_node *n; 420 421 dep->opencount++; 422 423 if (OBJECT_DLREF_CNT(dep) > 1) 424 return; 425 426 n = _dl_malloc(sizeof *n); 427 if (n == NULL) 428 _dl_exit(5); 429 430 n->data = dep; 431 TAILQ_INSERT_TAIL(&_dlopened_child_list, n, next_sib); 432 433 DL_DEB(("linking %s as dlopen()ed\n", dep->load_name)); 434 } 435 436 static void 437 _dl_child_refcnt_decrement(elf_object_t *object) 438 { 439 struct dep_node *n; 440 441 object->refcount--; 442 if (OBJECT_REF_CNT(object) == 0) 443 TAILQ_FOREACH(n, &object->child_list, next_sib) 444 _dl_child_refcnt_decrement(n->data); 445 } 446 447 void 448 _dl_notify_unload_shlib(elf_object_t *object) 449 { 450 struct dep_node *n; 451 452 if (OBJECT_REF_CNT(object) == 0) 453 TAILQ_FOREACH(n, &object->child_list, next_sib) 454 _dl_child_refcnt_decrement(n->data); 455 456 if (OBJECT_DLREF_CNT(object) == 0) { 457 while ((n = TAILQ_FIRST(&object->grpref_list)) != NULL) { 458 TAILQ_REMOVE(&object->grpref_list, n, next_sib); 459 n->data->grprefcount--; 460 _dl_notify_unload_shlib(n->data); 461 _dl_free(n); 462 } 463 } 464 } 465 466 void 467 _dl_unload_dlopen(void) 468 { 469 struct dep_node *node; 470 471 TAILQ_FOREACH_REVERSE(node, &_dlopened_child_list, dlochld, next_sib) { 472 /* dont dlclose the main program */ 473 if (node->data == _dl_objects) 474 continue; 475 476 while (node->data->opencount > 0) { 477 node->data->opencount--; 478 _dl_notify_unload_shlib(node->data); 479 _dl_run_all_dtors(); 480 } 481 } 482 } 483 484 void 485 _dl_link_grpref(elf_object_t *load_group, elf_object_t *load_object) 486 { 487 struct dep_node *n; 488 489 n = _dl_malloc(sizeof *n); 490 if (n == NULL) 491 _dl_exit(7); 492 n->data = load_group; 493 TAILQ_INSERT_TAIL(&load_object->grpref_list, n, next_sib); 494 load_group->grprefcount++; 495 } 496 497 void 498 _dl_link_child(elf_object_t *dep, elf_object_t *p) 499 { 500 struct dep_node *n; 501 502 n = _dl_malloc(sizeof *n); 503 if (n == NULL) 504 _dl_exit(7); 505 n->data = dep; 506 TAILQ_INSERT_TAIL(&p->child_list, n, next_sib); 507 508 dep->refcount++; 509 510 DL_DEB(("linking dep %s as child of %s\n", dep->load_name, 511 p->load_name)); 512 } 513 514 void 515 _dl_link_grpsym(elf_object_t *object, int checklist) 516 { 517 struct dep_node *n; 518 519 if (checklist) { 520 TAILQ_FOREACH(n, &_dl_loading_object->grpsym_list, next_sib) 521 if (n->data == object) 522 return; /* found, dont bother adding */ 523 } else { 524 if (object->lastlookup == _dl_searchnum) { 525 return; /* found, dont bother adding */ 526 } 527 } 528 object->lastlookup = _dl_searchnum; 529 530 n = _dl_malloc(sizeof *n); 531 if (n == NULL) 532 _dl_exit(8); 533 n->data = object; 534 TAILQ_INSERT_TAIL(&_dl_loading_object->grpsym_list, n, next_sib); 535 } 536 537 void 538 _dl_cache_grpsym_list_setup(elf_object_t *object) 539 { 540 _dl_newsymsearch(); 541 _dl_cache_grpsym_list(object); 542 } 543 void 544 _dl_cache_grpsym_list(elf_object_t *object) 545 { 546 struct dep_node *n; 547 548 /* 549 * grpsym_list is an ordered list of all child libs of the 550 * _dl_loading_object with no dups. The order is equalivant 551 * to a breath-first traversal of the child list without dups. 552 */ 553 554 TAILQ_FOREACH(n, &object->child_list, next_sib) 555 _dl_link_grpsym(n->data, 0); 556 557 TAILQ_FOREACH(n, &object->child_list, next_sib) 558 _dl_cache_grpsym_list(n->data); 559 } 560