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