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