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