1 /* 2 * Copyright (c) 1993,1995 Paul Kranenburg 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Paul Kranenburg. 16 * 4. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #ifndef lint 32 static const char rcsid[] = 33 "$Id: ldconfig.c,v 1.25 1998/09/05 03:30:54 jdp Exp $"; 34 #endif /* not lint */ 35 36 #include <sys/param.h> 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/mman.h> 40 #include <a.out.h> 41 #include <ctype.h> 42 #include <dirent.h> 43 #include <elf.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <link.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 #include "ldconfig.h" 54 #include "shlib.h" 55 #include "support.h" 56 57 #if DEBUG 58 /* test */ 59 #undef _PATH_LD_HINTS 60 #define _PATH_LD_HINTS "./ld.so.hints" 61 #undef _PATH_ELF_HINTS 62 #define _PATH_ELF_HINTS "./ld-elf.so.hints" 63 #endif 64 65 #undef major 66 #undef minor 67 68 enum obj_format { Unknown, Aout, Elf }; 69 70 #ifndef DEFAULT_FORMAT 71 #ifdef __ELF__ 72 #define DEFAULT_FORMAT Elf 73 #else 74 #define DEFAULT_FORMAT Aout 75 #endif 76 #endif 77 78 static int verbose; 79 static int nostd; 80 static int justread; 81 static int merge; 82 static int rescan; 83 static char *hints_file; 84 85 struct shlib_list { 86 /* Internal list of shared libraries found */ 87 char *name; 88 char *path; 89 int dewey[MAXDEWEY]; 90 int ndewey; 91 #define major dewey[0] 92 #define minor dewey[1] 93 struct shlib_list *next; 94 }; 95 96 static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head; 97 static char *dir_list; 98 99 static int buildhints __P((void)); 100 static int dodir __P((char *, int)); 101 int dofile __P((char *, int)); 102 static void enter __P((char *, char *, char *, int *, int)); 103 static enum obj_format getobjfmt __P((int *, char **)); 104 static void listhints __P((void)); 105 static int readhints __P((void)); 106 static void usage __P((void)); 107 108 int 109 main(argc, argv) 110 int argc; 111 char *argv[]; 112 { 113 int i, c; 114 int rval = 0; 115 enum obj_format fmt; 116 117 fmt = getobjfmt(&argc, argv); 118 hints_file = fmt == Aout ? _PATH_LD_HINTS : _PATH_ELF_HINTS; 119 while ((c = getopt(argc, argv, "Rf:mrsv")) != -1) { 120 switch (c) { 121 case 'R': 122 rescan = 1; 123 break; 124 case 'f': 125 hints_file = optarg; 126 break; 127 case 'm': 128 merge = 1; 129 break; 130 case 'r': 131 justread = 1; 132 break; 133 case 's': 134 nostd = 1; 135 break; 136 case 'v': 137 verbose = 1; 138 break; 139 default: 140 usage(); 141 break; 142 } 143 } 144 145 if (fmt == Elf) { 146 if (justread) 147 list_elf_hints(hints_file); 148 else 149 update_elf_hints(hints_file, argc - optind, 150 argv + optind, merge || rescan); 151 return 0; 152 } 153 154 dir_list = strdup(""); 155 156 if (justread || merge || rescan) { 157 if ((rval = readhints()) != 0) 158 return rval; 159 } 160 161 if (!nostd && !merge && !rescan) 162 std_search_path(); 163 164 /* Add any directories/files from the command line */ 165 if (!justread) { 166 for (i = optind; i < argc; i++) { 167 struct stat stbuf; 168 169 if (stat(argv[i], &stbuf) == -1) { 170 warn("%s", argv[i]); 171 rval = -1; 172 } 173 else if (!strcmp(argv[i], "/usr/lib")) { 174 warnx("WARNING! '%s' can not be used", argv[i]); 175 rval = -1; 176 } 177 else { 178 /* 179 * See if this is a directory-containing 180 * file instead of a directory 181 */ 182 if (S_ISREG(stbuf.st_mode)) 183 rval |= dofile(argv[i], 0); 184 else 185 add_search_path(argv[i]); 186 } 187 } 188 } 189 190 for (i = 0; i < n_search_dirs; i++) { 191 char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]); 192 free(dir_list); 193 dir_list = cp; 194 } 195 196 if (justread) { 197 listhints(); 198 return 0; 199 } 200 201 for (i = 0; i < n_search_dirs; i++) 202 rval |= dodir(search_dirs[i], 1); 203 204 rval |= buildhints(); 205 206 return rval; 207 } 208 209 static enum obj_format 210 getobjfmt(argcp, argv) 211 int *argcp; 212 char **argv; 213 { 214 enum obj_format fmt; 215 char **src, **dst; 216 const char *env; 217 FILE *fp; 218 219 fmt = Unknown; 220 221 /* Scan for "-aout" or "-elf" arguments, deleting them as we go. */ 222 for (dst = src = argv + 1; *src != NULL; src++) { 223 if (strcmp(*src, "-aout") == 0) 224 fmt = Aout; 225 else if (strcmp(*src, "-elf") == 0) 226 fmt = Elf; 227 else 228 *dst++ = *src; 229 } 230 *dst = NULL; 231 *argcp -= src - dst; 232 if (fmt != Unknown) 233 return fmt; 234 235 /* Check the OBJFORMAT environment variable. */ 236 if ((env = getenv("OBJFORMAT")) != NULL) { 237 if (strcmp(env, "aout") == 0) 238 return Aout; 239 else if (strcmp(env, "elf") == 0) 240 return Elf; 241 } 242 243 /* Take a look at "/etc/objformat". */ 244 if ((fp = fopen("/etc/objformat", "r")) != NULL) { 245 char buf[1024]; 246 247 while (fgets(buf, sizeof buf, fp) != NULL) { 248 if (strcmp(buf, "OBJFORMAT=aout\n") == 0) 249 fmt = Aout; 250 else if (strcmp(buf, "OBJFORMAT=elf\n") == 0) 251 fmt = Elf; 252 else 253 warnx("Unrecognized line in /etc/objformat: %s", 254 buf); 255 } 256 fclose(fp); 257 } 258 if (fmt != Unknown) 259 return fmt; 260 261 /* As a last resort, use the compiled in default. */ 262 return DEFAULT_FORMAT; 263 } 264 265 static void 266 usage() 267 { 268 fprintf(stderr, 269 "usage: ldconfig [-Rmrsv] [-f hints_file] [dir | file ...]\n"); 270 exit(1); 271 } 272 273 int 274 dofile(fname, silent) 275 char *fname; 276 int silent; 277 { 278 FILE *hfp; 279 char buf[MAXPATHLEN]; 280 int rval = 0; 281 char *cp, *sp; 282 283 if ((hfp = fopen(fname, "r")) == NULL) { 284 warn("%s", fname); 285 return -1; 286 } 287 288 while (fgets(buf, sizeof(buf), hfp)) { 289 cp = buf; 290 while (isspace(*cp)) 291 cp++; 292 if (*cp == '#' || *cp == '\0') 293 continue; 294 sp = cp; 295 while (!isspace(*cp) && *cp != '\0') 296 cp++; 297 298 if (*cp != '\n') { 299 *cp = '\0'; 300 warnx("%s: trailing characters ignored", sp); 301 } 302 303 *cp = '\0'; 304 305 rval |= dodir(sp, silent); 306 } 307 308 (void)fclose(hfp); 309 return rval; 310 } 311 312 int 313 dodir(dir, silent) 314 char *dir; 315 int silent; 316 { 317 DIR *dd; 318 struct dirent *dp; 319 char name[MAXPATHLEN]; 320 int dewey[MAXDEWEY], ndewey; 321 322 if ((dd = opendir(dir)) == NULL) { 323 if (silent && errno == ENOENT) /* Ignore the error */ 324 return 0; 325 warn("%s", dir); 326 return -1; 327 } 328 329 while ((dp = readdir(dd)) != NULL) { 330 register int n; 331 register char *cp; 332 333 /* Check for `lib' prefix */ 334 if (dp->d_name[0] != 'l' || 335 dp->d_name[1] != 'i' || 336 dp->d_name[2] != 'b') 337 continue; 338 339 /* Copy the entry minus prefix */ 340 (void)strcpy(name, dp->d_name + 3); 341 n = strlen(name); 342 if (n < 4) 343 continue; 344 345 /* Find ".so." in name */ 346 for (cp = name + n - 4; cp > name; --cp) { 347 if (cp[0] == '.' && 348 cp[1] == 's' && 349 cp[2] == 'o' && 350 cp[3] == '.') 351 break; 352 } 353 if (cp <= name) 354 continue; 355 356 *cp = '\0'; 357 if (!isdigit(*(cp+4))) 358 continue; 359 360 bzero((caddr_t)dewey, sizeof(dewey)); 361 ndewey = getdewey(dewey, cp + 4); 362 if (ndewey < 2) 363 continue; 364 enter(dir, dp->d_name, name, dewey, ndewey); 365 } 366 367 closedir(dd); 368 return 0; 369 } 370 371 static void 372 enter(dir, file, name, dewey, ndewey) 373 char *dir, *file, *name; 374 int dewey[], ndewey; 375 { 376 struct shlib_list *shp; 377 378 for (shp = shlib_head; shp; shp = shp->next) { 379 if (strcmp(name, shp->name) != 0 || major != shp->major) 380 continue; 381 382 /* Name matches existing entry */ 383 if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) { 384 385 /* Update this entry with higher versioned lib */ 386 if (verbose) 387 printf("Updating lib%s.%d.%d to %s/%s\n", 388 shp->name, shp->major, shp->minor, 389 dir, file); 390 391 free(shp->name); 392 shp->name = strdup(name); 393 free(shp->path); 394 shp->path = concat(dir, "/", file); 395 bcopy(dewey, shp->dewey, sizeof(shp->dewey)); 396 shp->ndewey = ndewey; 397 } 398 break; 399 } 400 401 if (shp) 402 /* Name exists: older version or just updated */ 403 return; 404 405 /* Allocate new list element */ 406 if (verbose) 407 printf("Adding %s/%s\n", dir, file); 408 409 shp = (struct shlib_list *)xmalloc(sizeof *shp); 410 shp->name = strdup(name); 411 shp->path = concat(dir, "/", file); 412 bcopy(dewey, shp->dewey, MAXDEWEY); 413 shp->ndewey = ndewey; 414 shp->next = NULL; 415 416 *shlib_tail = shp; 417 shlib_tail = &shp->next; 418 } 419 420 421 int 422 hinthash(cp, vmajor) 423 char *cp; 424 int vmajor; 425 { 426 int k = 0; 427 428 while (*cp) 429 k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; 430 431 k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; 432 433 return k; 434 } 435 436 int 437 buildhints() 438 { 439 struct hints_header hdr; 440 struct hints_bucket *blist; 441 struct shlib_list *shp; 442 char *strtab; 443 int i, n, str_index = 0; 444 int strtab_sz = 0; /* Total length of strings */ 445 int nhints = 0; /* Total number of hints */ 446 int fd; 447 char *tmpfile; 448 449 for (shp = shlib_head; shp; shp = shp->next) { 450 strtab_sz += 1 + strlen(shp->name); 451 strtab_sz += 1 + strlen(shp->path); 452 nhints++; 453 } 454 455 /* Fill hints file header */ 456 hdr.hh_magic = HH_MAGIC; 457 hdr.hh_version = LD_HINTS_VERSION_2; 458 hdr.hh_nbucket = 1 * nhints; 459 n = hdr.hh_nbucket * sizeof(struct hints_bucket); 460 hdr.hh_hashtab = sizeof(struct hints_header); 461 hdr.hh_strtab = hdr.hh_hashtab + n; 462 hdr.hh_dirlist = strtab_sz; 463 strtab_sz += 1 + strlen(dir_list); 464 hdr.hh_strtab_sz = strtab_sz; 465 hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz; 466 467 if (verbose) 468 printf("Totals: entries %d, buckets %ld, string size %d\n", 469 nhints, (long)hdr.hh_nbucket, strtab_sz); 470 471 /* Allocate buckets and string table */ 472 blist = (struct hints_bucket *)xmalloc(n); 473 bzero((char *)blist, n); 474 for (i = 0; i < hdr.hh_nbucket; i++) 475 /* Empty all buckets */ 476 blist[i].hi_next = -1; 477 478 strtab = (char *)xmalloc(strtab_sz); 479 480 /* Enter all */ 481 for (shp = shlib_head; shp; shp = shp->next) { 482 struct hints_bucket *bp; 483 484 bp = blist + 485 (hinthash(shp->name, shp->major) % hdr.hh_nbucket); 486 487 if (bp->hi_pathx) { 488 int i; 489 490 for (i = 0; i < hdr.hh_nbucket; i++) { 491 if (blist[i].hi_pathx == 0) 492 break; 493 } 494 if (i == hdr.hh_nbucket) { 495 warnx("bummer!"); 496 return -1; 497 } 498 while (bp->hi_next != -1) 499 bp = &blist[bp->hi_next]; 500 bp->hi_next = i; 501 bp = blist + i; 502 } 503 504 /* Insert strings in string table */ 505 bp->hi_namex = str_index; 506 strcpy(strtab + str_index, shp->name); 507 str_index += 1 + strlen(shp->name); 508 509 bp->hi_pathx = str_index; 510 strcpy(strtab + str_index, shp->path); 511 str_index += 1 + strlen(shp->path); 512 513 /* Copy versions */ 514 bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey)); 515 bp->hi_ndewey = shp->ndewey; 516 } 517 518 /* Copy search directories */ 519 strcpy(strtab + str_index, dir_list); 520 str_index += 1 + strlen(dir_list); 521 522 /* Sanity check */ 523 if (str_index != strtab_sz) { 524 errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz); 525 } 526 527 tmpfile = concat(hints_file, ".XXXXXX", ""); 528 if ((tmpfile = mktemp(tmpfile)) == NULL) { 529 warn("%s", tmpfile); 530 return -1; 531 } 532 533 umask(0); /* Create with exact permissions */ 534 if ((fd = open(tmpfile, O_RDWR|O_CREAT|O_TRUNC, 0444)) == -1) { 535 warn("%s", hints_file); 536 return -1; 537 } 538 539 if (write(fd, &hdr, sizeof(struct hints_header)) != 540 sizeof(struct hints_header)) { 541 warn("%s", hints_file); 542 return -1; 543 } 544 if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) != 545 hdr.hh_nbucket * sizeof(struct hints_bucket)) { 546 warn("%s", hints_file); 547 return -1; 548 } 549 if (write(fd, strtab, strtab_sz) != strtab_sz) { 550 warn("%s", hints_file); 551 return -1; 552 } 553 if (close(fd) != 0) { 554 warn("%s", hints_file); 555 return -1; 556 } 557 558 /* Install it */ 559 if (unlink(hints_file) != 0 && errno != ENOENT) { 560 warn("%s", hints_file); 561 return -1; 562 } 563 564 if (rename(tmpfile, hints_file) != 0) { 565 warn("%s", hints_file); 566 return -1; 567 } 568 569 return 0; 570 } 571 572 static int 573 readhints() 574 { 575 int fd; 576 caddr_t addr; 577 long msize; 578 struct hints_header *hdr; 579 struct hints_bucket *blist; 580 char *strtab; 581 struct shlib_list *shp; 582 int i; 583 584 if ((fd = open(hints_file, O_RDONLY, 0)) == -1) { 585 warn("%s", hints_file); 586 return -1; 587 } 588 589 msize = PAGE_SIZE; 590 addr = mmap(0, msize, PROT_READ, MAP_COPY, fd, 0); 591 592 if (addr == (caddr_t)-1) { 593 warn("%s", hints_file); 594 return -1; 595 } 596 597 hdr = (struct hints_header *)addr; 598 if (HH_BADMAG(*hdr)) { 599 warnx("%s: bad magic: %lo", hints_file, 600 (unsigned long)hdr->hh_magic); 601 return -1; 602 } 603 604 if (hdr->hh_version != LD_HINTS_VERSION_1 && 605 hdr->hh_version != LD_HINTS_VERSION_2) { 606 warnx("unsupported version: %ld", (long)hdr->hh_version); 607 return -1; 608 } 609 610 if (hdr->hh_ehints > msize) { 611 if (mmap(addr+msize, hdr->hh_ehints - msize, 612 PROT_READ, MAP_COPY|MAP_FIXED, 613 fd, msize) != (caddr_t)(addr+msize)) { 614 615 warn("%s", hints_file); 616 return -1; 617 } 618 } 619 close(fd); 620 621 blist = (struct hints_bucket *)(addr + hdr->hh_hashtab); 622 strtab = (char *)(addr + hdr->hh_strtab); 623 624 if (hdr->hh_version >= LD_HINTS_VERSION_2) 625 add_search_path(strtab + hdr->hh_dirlist); 626 else if (rescan) 627 errx(1, "%s too old and does not contain the search path", 628 hints_file); 629 630 if (rescan) 631 return 0; 632 633 for (i = 0; i < hdr->hh_nbucket; i++) { 634 struct hints_bucket *bp = &blist[i]; 635 636 /* Sanity check */ 637 if (bp->hi_namex >= hdr->hh_strtab_sz) { 638 warnx("bad name index: %#x", bp->hi_namex); 639 return -1; 640 } 641 if (bp->hi_pathx >= hdr->hh_strtab_sz) { 642 warnx("bad path index: %#x", bp->hi_pathx); 643 return -1; 644 } 645 646 /* Allocate new list element */ 647 shp = (struct shlib_list *)xmalloc(sizeof *shp); 648 shp->name = strdup(strtab + bp->hi_namex); 649 shp->path = strdup(strtab + bp->hi_pathx); 650 bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey)); 651 shp->ndewey = bp->hi_ndewey; 652 shp->next = NULL; 653 654 *shlib_tail = shp; 655 shlib_tail = &shp->next; 656 } 657 658 return 0; 659 } 660 661 static void 662 listhints() 663 { 664 struct shlib_list *shp; 665 int i; 666 667 printf("%s:\n", hints_file); 668 printf("\tsearch directories: %s\n", dir_list); 669 670 for (i = 0, shp = shlib_head; shp; i++, shp = shp->next) 671 printf("\t%d:-l%s.%d.%d => %s\n", 672 i, shp->name, shp->major, shp->minor, shp->path); 673 674 return; 675 } 676