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 "$FreeBSD$"; 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-hints.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <sys/link_aout.h> 48 #include <objformat.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include "ldconfig.h" 55 #include "shlib.h" 56 #include "support.h" 57 58 #if DEBUG 59 /* test */ 60 #undef _PATH_LD_HINTS 61 #define _PATH_LD_HINTS "./ld.so.hints" 62 #undef _PATH_ELF_HINTS 63 #define _PATH_ELF_HINTS "./ld-elf.so.hints" 64 #endif 65 66 #define _PATH_LD32_HINTS "/var/run/ld32.so.hints" 67 #define _PATH_ELF32_HINTS "/var/run/ld-elf32.so.hints" 68 69 #undef major 70 #undef minor 71 72 static int verbose; 73 static int nostd; 74 static int justread; 75 static int merge; 76 static int rescan; 77 static char *hints_file; 78 79 struct shlib_list { 80 /* Internal list of shared libraries found */ 81 char *name; 82 char *path; 83 int dewey[MAXDEWEY]; 84 int ndewey; 85 #define major dewey[0] 86 #define minor dewey[1] 87 struct shlib_list *next; 88 }; 89 90 static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head; 91 static char *dir_list; 92 93 static int buildhints(void); 94 static int dodir(char *, int); 95 int dofile(char *, int); 96 static void enter(char *, char *, char *, int *, int); 97 static void listhints(void); 98 static int readhints(void); 99 static void usage(void); 100 101 int 102 main(argc, argv) 103 int argc; 104 char *argv[]; 105 { 106 int i, c; 107 int rval = 0; 108 int is_aout = 0; 109 int is_32 = 0; 110 111 while (argc > 1) { 112 if (strcmp(argv[1], "-aout") == 0) { 113 is_aout = 1; 114 argc--; 115 argv++; 116 } else if (strcmp(argv[1], "-elf") == 0) { 117 is_aout = 0; 118 argc--; 119 argv++; 120 } else if (strcmp(argv[1], "-32") == 0) { 121 is_32 = 1; 122 argc--; 123 argv++; 124 } else { 125 break; 126 } 127 } 128 129 if (is_32) 130 hints_file = is_aout ? _PATH_LD32_HINTS : _PATH_ELF32_HINTS; 131 else 132 hints_file = is_aout ? _PATH_LD_HINTS : _PATH_ELF_HINTS; 133 if (argc == 1) 134 rescan = 1; 135 else while((c = getopt(argc, argv, "Rf:imrsv")) != -1) { 136 switch (c) { 137 case 'R': 138 rescan = 1; 139 break; 140 case 'f': 141 hints_file = optarg; 142 break; 143 case 'i': 144 insecure = 1; 145 break; 146 case 'm': 147 merge = 1; 148 break; 149 case 'r': 150 justread = 1; 151 break; 152 case 's': 153 nostd = 1; 154 break; 155 case 'v': 156 verbose = 1; 157 break; 158 default: 159 usage(); 160 break; 161 } 162 } 163 164 if (!is_aout) { 165 if (justread) 166 list_elf_hints(hints_file); 167 else 168 update_elf_hints(hints_file, argc - optind, 169 argv + optind, merge || rescan); 170 return 0; 171 } 172 173 /* Here begins the aout libs processing */ 174 dir_list = strdup(""); 175 176 if (justread || merge || rescan) { 177 if ((rval = readhints()) != 0) 178 return rval; 179 } 180 181 if (!nostd && !merge && !rescan) 182 std_search_path(); 183 184 /* Add any directories/files from the command line */ 185 if (!justread) { 186 for (i = optind; i < argc; i++) { 187 struct stat stbuf; 188 189 if (stat(argv[i], &stbuf) == -1) { 190 warn("%s", argv[i]); 191 rval = -1; 192 } else if (strcmp(argv[i], "/usr/lib") == 0) { 193 warnx("WARNING! '%s' can not be used", argv[i]); 194 rval = -1; 195 } else { 196 /* 197 * See if this is a directory-containing 198 * file instead of a directory 199 */ 200 if (S_ISREG(stbuf.st_mode)) 201 rval |= dofile(argv[i], 0); 202 else 203 add_search_path(argv[i]); 204 } 205 } 206 } 207 208 for (i = 0; i < n_search_dirs; i++) { 209 char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]); 210 free(dir_list); 211 dir_list = cp; 212 } 213 214 if (justread) { 215 listhints(); 216 return 0; 217 } 218 219 for (i = 0; i < n_search_dirs; i++) 220 rval |= dodir(search_dirs[i], 1); 221 222 rval |= buildhints(); 223 224 return rval; 225 } 226 227 static void 228 usage() 229 { 230 fprintf(stderr, 231 "usage: ldconfig [-aout | -elf] [-Rimrsv] [-f hints_file] [dir | file ...]\n"); 232 exit(1); 233 } 234 235 int 236 dofile(fname, silent) 237 char *fname; 238 int silent; 239 { 240 FILE *hfp; 241 char buf[MAXPATHLEN]; 242 int rval = 0; 243 char *cp, *sp; 244 245 if ((hfp = fopen(fname, "r")) == NULL) { 246 warn("%s", fname); 247 return -1; 248 } 249 250 while (fgets(buf, sizeof(buf), hfp)) { 251 cp = buf; 252 while (isspace(*cp)) 253 cp++; 254 if (*cp == '#' || *cp == '\0') 255 continue; 256 sp = cp; 257 while (!isspace(*cp) && *cp != '\0') 258 cp++; 259 260 if (*cp != '\n') { 261 *cp = '\0'; 262 warnx("%s: trailing characters ignored", sp); 263 } 264 265 *cp = '\0'; 266 267 rval |= dodir(sp, silent); 268 } 269 270 (void)fclose(hfp); 271 return rval; 272 } 273 274 int 275 dodir(dir, silent) 276 char *dir; 277 int silent; 278 { 279 DIR *dd; 280 struct dirent *dp; 281 char name[MAXPATHLEN]; 282 int dewey[MAXDEWEY], ndewey; 283 284 if ((dd = opendir(dir)) == NULL) { 285 if (silent && errno == ENOENT) /* Ignore the error */ 286 return 0; 287 warn("%s", dir); 288 return -1; 289 } 290 291 while ((dp = readdir(dd)) != NULL) { 292 int n; 293 char *cp; 294 295 /* Check for `lib' prefix */ 296 if (dp->d_name[0] != 'l' || 297 dp->d_name[1] != 'i' || 298 dp->d_name[2] != 'b') 299 continue; 300 301 /* Copy the entry minus prefix */ 302 (void)strcpy(name, dp->d_name + 3); 303 n = strlen(name); 304 if (n < 4) 305 continue; 306 307 /* Find ".so." in name */ 308 for (cp = name + n - 4; cp > name; --cp) { 309 if (cp[0] == '.' && 310 cp[1] == 's' && 311 cp[2] == 'o' && 312 cp[3] == '.') 313 break; 314 } 315 if (cp <= name) 316 continue; 317 318 *cp = '\0'; 319 if (!isdigit(*(cp+4))) 320 continue; 321 322 bzero((caddr_t)dewey, sizeof(dewey)); 323 ndewey = getdewey(dewey, cp + 4); 324 if (ndewey < 2) 325 continue; 326 enter(dir, dp->d_name, name, dewey, ndewey); 327 } 328 329 closedir(dd); 330 return 0; 331 } 332 333 static void 334 enter(dir, file, name, dewey, ndewey) 335 char *dir, *file, *name; 336 int dewey[], ndewey; 337 { 338 struct shlib_list *shp; 339 340 for (shp = shlib_head; shp; shp = shp->next) { 341 if (strcmp(name, shp->name) != 0 || major != shp->major) 342 continue; 343 344 /* Name matches existing entry */ 345 if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) { 346 347 /* Update this entry with higher versioned lib */ 348 if (verbose) 349 printf("Updating lib%s.%d.%d to %s/%s\n", 350 shp->name, shp->major, shp->minor, 351 dir, file); 352 353 free(shp->name); 354 shp->name = strdup(name); 355 free(shp->path); 356 shp->path = concat(dir, "/", file); 357 bcopy(dewey, shp->dewey, sizeof(shp->dewey)); 358 shp->ndewey = ndewey; 359 } 360 break; 361 } 362 363 if (shp) 364 /* Name exists: older version or just updated */ 365 return; 366 367 /* Allocate new list element */ 368 if (verbose) 369 printf("Adding %s/%s\n", dir, file); 370 371 shp = (struct shlib_list *)xmalloc(sizeof *shp); 372 shp->name = strdup(name); 373 shp->path = concat(dir, "/", file); 374 bcopy(dewey, shp->dewey, sizeof(shp->dewey)); 375 shp->ndewey = ndewey; 376 shp->next = NULL; 377 378 *shlib_tail = shp; 379 shlib_tail = &shp->next; 380 } 381 382 383 int 384 hinthash(cp, vmajor) 385 char *cp; 386 int vmajor; 387 { 388 int k = 0; 389 390 while (*cp) 391 k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; 392 393 k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; 394 395 return k; 396 } 397 398 int 399 buildhints() 400 { 401 struct hints_header hdr; 402 struct hints_bucket *blist; 403 struct shlib_list *shp; 404 char *strtab; 405 int i, n, str_index = 0; 406 int strtab_sz = 0; /* Total length of strings */ 407 int nhints = 0; /* Total number of hints */ 408 int fd; 409 char *tmpfile; 410 411 for (shp = shlib_head; shp; shp = shp->next) { 412 strtab_sz += 1 + strlen(shp->name); 413 strtab_sz += 1 + strlen(shp->path); 414 nhints++; 415 } 416 417 /* Fill hints file header */ 418 hdr.hh_magic = HH_MAGIC; 419 hdr.hh_version = LD_HINTS_VERSION_2; 420 hdr.hh_nbucket = 1 * nhints; 421 n = hdr.hh_nbucket * sizeof(struct hints_bucket); 422 hdr.hh_hashtab = sizeof(struct hints_header); 423 hdr.hh_strtab = hdr.hh_hashtab + n; 424 hdr.hh_dirlist = strtab_sz; 425 strtab_sz += 1 + strlen(dir_list); 426 hdr.hh_strtab_sz = strtab_sz; 427 hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz; 428 429 if (verbose) 430 printf("Totals: entries %d, buckets %ld, string size %d\n", 431 nhints, (long)hdr.hh_nbucket, strtab_sz); 432 433 /* Allocate buckets and string table */ 434 blist = (struct hints_bucket *)xmalloc(n); 435 bzero((char *)blist, n); 436 for (i = 0; i < hdr.hh_nbucket; i++) 437 /* Empty all buckets */ 438 blist[i].hi_next = -1; 439 440 strtab = (char *)xmalloc(strtab_sz); 441 442 /* Enter all */ 443 for (shp = shlib_head; shp; shp = shp->next) { 444 struct hints_bucket *bp; 445 446 bp = blist + 447 (hinthash(shp->name, shp->major) % hdr.hh_nbucket); 448 449 if (bp->hi_pathx) { 450 int i; 451 452 for (i = 0; i < hdr.hh_nbucket; i++) { 453 if (blist[i].hi_pathx == 0) 454 break; 455 } 456 if (i == hdr.hh_nbucket) { 457 warnx("bummer!"); 458 return -1; 459 } 460 while (bp->hi_next != -1) 461 bp = &blist[bp->hi_next]; 462 bp->hi_next = i; 463 bp = blist + i; 464 } 465 466 /* Insert strings in string table */ 467 bp->hi_namex = str_index; 468 strcpy(strtab + str_index, shp->name); 469 str_index += 1 + strlen(shp->name); 470 471 bp->hi_pathx = str_index; 472 strcpy(strtab + str_index, shp->path); 473 str_index += 1 + strlen(shp->path); 474 475 /* Copy versions */ 476 bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey)); 477 bp->hi_ndewey = shp->ndewey; 478 } 479 480 /* Copy search directories */ 481 strcpy(strtab + str_index, dir_list); 482 str_index += 1 + strlen(dir_list); 483 484 /* Sanity check */ 485 if (str_index != strtab_sz) { 486 errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz); 487 } 488 489 tmpfile = concat(hints_file, ".XXXXXXXXXX", ""); 490 umask(0); /* Create with exact permissions */ 491 if ((fd = mkstemp(tmpfile)) == -1) { 492 warn("%s", tmpfile); 493 return -1; 494 } 495 fchmod(fd, 0444); 496 497 if (write(fd, &hdr, sizeof(struct hints_header)) != 498 sizeof(struct hints_header)) { 499 warn("%s", hints_file); 500 return -1; 501 } 502 if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) != 503 hdr.hh_nbucket * sizeof(struct hints_bucket)) { 504 warn("%s", hints_file); 505 return -1; 506 } 507 if (write(fd, strtab, strtab_sz) != strtab_sz) { 508 warn("%s", hints_file); 509 return -1; 510 } 511 if (close(fd) != 0) { 512 warn("%s", hints_file); 513 return -1; 514 } 515 516 /* Install it */ 517 if (unlink(hints_file) != 0 && errno != ENOENT) { 518 warn("%s", hints_file); 519 return -1; 520 } 521 522 if (rename(tmpfile, hints_file) != 0) { 523 warn("%s", hints_file); 524 return -1; 525 } 526 527 return 0; 528 } 529 530 static int 531 readhints() 532 { 533 int fd; 534 void *addr; 535 long fsize; 536 long msize; 537 struct hints_header *hdr; 538 struct hints_bucket *blist; 539 char *strtab; 540 struct shlib_list *shp; 541 int i; 542 543 if ((fd = open(hints_file, O_RDONLY, 0)) == -1) { 544 warn("%s", hints_file); 545 return -1; 546 } 547 548 msize = PAGE_SIZE; 549 addr = mmap(0, msize, PROT_READ, MAP_COPY, fd, 0); 550 551 if (addr == MAP_FAILED) { 552 warn("%s", hints_file); 553 return -1; 554 } 555 556 hdr = (struct hints_header *)addr; 557 if (HH_BADMAG(*hdr)) { 558 warnx("%s: bad magic: %lo", hints_file, 559 (unsigned long)hdr->hh_magic); 560 return -1; 561 } 562 563 if (hdr->hh_version != LD_HINTS_VERSION_1 && 564 hdr->hh_version != LD_HINTS_VERSION_2) { 565 warnx("unsupported version: %ld", (long)hdr->hh_version); 566 return -1; 567 } 568 569 if (hdr->hh_ehints > msize) { 570 fsize = hdr->hh_ehints; 571 munmap(addr, msize); 572 addr = mmap(0, fsize, PROT_READ, MAP_COPY, fd, 0); 573 if (addr == MAP_FAILED) { 574 warn("%s", hints_file); 575 return -1; 576 } 577 hdr = (struct hints_header *)addr; 578 } 579 close(fd); 580 581 blist = (struct hints_bucket *)(addr + hdr->hh_hashtab); 582 strtab = (char *)(addr + hdr->hh_strtab); 583 584 if (hdr->hh_version >= LD_HINTS_VERSION_2) 585 add_search_path(strtab + hdr->hh_dirlist); 586 else if (rescan) 587 errx(1, "%s too old and does not contain the search path", 588 hints_file); 589 590 if (rescan) 591 return 0; 592 593 for (i = 0; i < hdr->hh_nbucket; i++) { 594 struct hints_bucket *bp = &blist[i]; 595 596 /* Sanity check */ 597 if (bp->hi_namex >= hdr->hh_strtab_sz) { 598 warnx("bad name index: %#x", bp->hi_namex); 599 return -1; 600 } 601 if (bp->hi_pathx >= hdr->hh_strtab_sz) { 602 warnx("bad path index: %#x", bp->hi_pathx); 603 return -1; 604 } 605 606 /* Allocate new list element */ 607 shp = (struct shlib_list *)xmalloc(sizeof *shp); 608 shp->name = strdup(strtab + bp->hi_namex); 609 shp->path = strdup(strtab + bp->hi_pathx); 610 bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey)); 611 shp->ndewey = bp->hi_ndewey; 612 shp->next = NULL; 613 614 *shlib_tail = shp; 615 shlib_tail = &shp->next; 616 } 617 618 return 0; 619 } 620 621 static void 622 listhints() 623 { 624 struct shlib_list *shp; 625 int i; 626 627 printf("%s:\n", hints_file); 628 printf("\tsearch directories: %s\n", dir_list); 629 630 for (i = 0, shp = shlib_head; shp; i++, shp = shp->next) 631 printf("\t%d:-l%s.%d.%d => %s\n", 632 i, shp->name, shp->major, shp->minor, shp->path); 633 634 return; 635 } 636