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