1 /* $NetBSD: ldconfig.c,v 1.34 2001/11/01 07:33:43 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Paul Kranenburg. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <sys/file.h> 43 #include <sys/time.h> 44 #include <sys/mman.h> 45 #include <a.out.h> 46 #include <ctype.h> 47 #include <dirent.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <link_aout.h> 56 #include <paths.h> 57 58 #include "shlib.h" 59 60 #define _PATH_LD_SO_CONF "/etc/ld.so.conf" 61 62 #undef major 63 #undef minor 64 65 static int verbose; 66 static int nostd; 67 static int noconf; 68 static int justread; 69 static int merge; 70 71 struct shlib_list { 72 /* Internal list of shared libraries found */ 73 char *name; 74 char *path; 75 int dewey[MAXDEWEY]; 76 int ndewey; 77 #define major dewey[0] 78 #define minor dewey[1] 79 struct shlib_list *next; 80 }; 81 82 static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head; 83 static char *dir_list; 84 85 static void enter __P((char *, char *, char *, int *, int)); 86 static int dodir __P((char *, int, int)); 87 static int do_conf __P((void)); 88 static int buildhints __P((void)); 89 static int readhints __P((void)); 90 static void listhints __P((void)); 91 static int hinthash __P((char *, int, int)); 92 int main __P((int, char *[])); 93 94 int 95 main(argc, argv) 96 int argc; 97 char *argv[]; 98 { 99 int i, c; 100 int rval = 0; 101 102 while ((c = getopt(argc, argv, "cmrsSv")) != -1) { 103 switch (c) { 104 case 'c': 105 noconf = 1; 106 break; 107 case 'm': 108 merge = 1; 109 break; 110 case 'r': 111 justread = 1; 112 break; 113 case 's': 114 nostd = 1; 115 noconf = 1; 116 break; 117 case 'S': 118 nostd = 1; 119 break; 120 case 'v': 121 verbose = 1; 122 break; 123 default: 124 errx(1, "Usage: %s [-c][-m][-r][-s][-S][-v][dir ...]", 125 getprogname()); 126 break; 127 } 128 } 129 130 dir_list = xmalloc(1); 131 *dir_list = '\0'; 132 133 if (justread || merge) { 134 if ((rval = readhints()) != 0) 135 return (rval); 136 if (justread) { 137 listhints(); 138 return (rval); 139 } 140 } 141 142 if (!nostd && !merge) 143 std_search_path(); 144 145 for (i = 0; i < n_search_dirs; i++) 146 rval |= dodir(search_dirs[i], 1, 0); 147 148 if (!noconf && !merge) 149 rval |= do_conf(); 150 151 for (i = optind; i < argc; i++) { 152 rval |= dodir(argv[i], 0, 1); 153 } 154 155 rval |= buildhints(); 156 157 return (rval); 158 } 159 160 int 161 do_conf () 162 { 163 FILE *conf; 164 char *line, *c; 165 char *cline = NULL; 166 size_t len; 167 int rval = 0; 168 #ifdef __ELF__ 169 char *aout_conf; 170 171 aout_conf = xmalloc(sizeof(_PATH_EMUL_AOUT) + 172 strlen(_PATH_LD_SO_CONF)); 173 strcpy(aout_conf, _PATH_EMUL_AOUT); 174 strcat(aout_conf, _PATH_LD_SO_CONF); 175 if ((conf = fopen(aout_conf, "r")) == NULL) { 176 if (verbose) 177 warnx("can't open `%s'", aout_conf); 178 return (0); 179 } 180 free(aout_conf); 181 #else 182 if ((conf = fopen(_PATH_LD_SO_CONF, "r")) == NULL) { 183 if (verbose) { 184 warnx("can't open `%s'", _PATH_LD_SO_CONF); 185 } 186 return (0); 187 } 188 #endif 189 190 while ((line = fgetln(conf, &len)) != NULL) { 191 if (*line != '/') 192 continue; 193 194 if (line[len-1] == '\n') { 195 line[--len] = '\0'; 196 } else { 197 cline = xmalloc(len+1); 198 memcpy(cline, line, len); 199 line = cline; 200 line[len] = '\0'; 201 } 202 203 while (isblank(*line)) { line++; len--; } 204 if ((c = strchr(line, '#')) == NULL) 205 c = line + len; 206 while (--c >= line && isblank(*c)) continue; 207 if (c >= line) { 208 *++c = '\0'; 209 rval |= dodir(line, 0, 1); 210 } 211 212 if (cline) { 213 free(cline); 214 cline = NULL; 215 } 216 } 217 218 (void) fclose(conf); 219 220 return (rval); 221 } 222 223 int 224 dodir(dir, silent, update_dir_list) 225 char *dir; 226 int silent; 227 int update_dir_list; 228 { 229 DIR *dd; 230 struct dirent *dp; 231 char name[MAXPATHLEN]; 232 int dewey[MAXDEWEY], ndewey; 233 234 if ((dd = opendir(dir)) == NULL) { 235 /* /emul/aout directories are allowed to not exist. 236 */ 237 if (!strncmp(dir, _PATH_EMUL_AOUT, sizeof(_PATH_EMUL_AOUT)-1)) 238 return 0; 239 if (!silent || errno != ENOENT) 240 warn("%s", dir); 241 return (-1); 242 } 243 244 if (update_dir_list) { 245 /* Check for duplicates? */ 246 char *cp = concat(dir_list, *dir_list?":":"", dir); 247 free(dir_list); 248 dir_list = cp; 249 } 250 251 while ((dp = readdir(dd)) != NULL) { 252 int n; 253 char *cp, *path; 254 FILE *fp; 255 struct exec ex; 256 257 /* Check for `lib' prefix */ 258 if (dp->d_name[0] != 'l' || 259 dp->d_name[1] != 'i' || 260 dp->d_name[2] != 'b') 261 continue; 262 263 /* Copy the entry minus prefix */ 264 (void)strcpy(name, dp->d_name + 3); 265 n = strlen(name); 266 if (n < 4) 267 continue; 268 269 /* Find ".so." in name */ 270 for (cp = name + n - 4; cp > name; --cp) { 271 if (cp[0] == '.' && 272 cp[1] == 's' && 273 cp[2] == 'o' && 274 cp[3] == '.') 275 break; 276 } 277 if (cp <= name) 278 continue; 279 280 path = concat(dir, "/", dp->d_name); 281 fp = fopen(path, "r"); 282 free(path); 283 if (fp == NULL) 284 continue; 285 n = fread(&ex, 1, sizeof(ex), fp); 286 fclose(fp); 287 if (n != sizeof(ex) 288 || N_GETMAGIC(ex) != ZMAGIC 289 || (N_GETFLAG(ex) & EX_DYNAMIC) == 0) 290 continue; 291 292 *cp = '\0'; 293 if (!isdigit(*(cp+4))) 294 continue; 295 296 memset(dewey, 0, sizeof(dewey)); 297 ndewey = getdewey(dewey, cp + 4); 298 enter(dir, dp->d_name, name, dewey, ndewey); 299 } 300 301 (void) closedir(dd); 302 303 return (0); 304 } 305 306 static void 307 enter(dir, file, name, dewey, ndewey) 308 char *dir, *file, *name; 309 int dewey[], ndewey; 310 { 311 struct shlib_list *shp; 312 313 for (shp = shlib_head; shp; shp = shp->next) { 314 if (strcmp(name, shp->name) != 0 || major != shp->major) 315 continue; 316 317 /* Name matches existing entry */ 318 if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) { 319 320 /* Update this entry with higher versioned lib */ 321 if (verbose) 322 printf("Updating lib%s.%d.%d to %s/%s\n", 323 shp->name, shp->major, shp->minor, 324 dir, file); 325 326 free(shp->name); 327 shp->name = strdup(name); 328 free(shp->path); 329 shp->path = concat(dir, "/", file); 330 memcpy(shp->dewey, dewey, sizeof(shp->dewey)); 331 shp->ndewey = ndewey; 332 } 333 break; 334 } 335 336 if (shp) 337 /* Name exists: older version or just updated */ 338 return; 339 340 /* Allocate new list element */ 341 if (verbose) 342 printf("Adding %s/%s\n", dir, file); 343 344 shp = (struct shlib_list *)xmalloc(sizeof *shp); 345 shp->name = strdup(name); 346 shp->path = concat(dir, "/", file); 347 memcpy(shp->dewey, dewey, MAXDEWEY); 348 shp->ndewey = ndewey; 349 shp->next = NULL; 350 351 *shlib_tail = shp; 352 shlib_tail = &shp->next; 353 } 354 355 356 #if DEBUG 357 /* test */ 358 #undef _PATH_LD_HINTS 359 #define _PATH_LD_HINTS "./ld.so.hints" 360 #endif 361 362 /* XXX - should be a common function with rtld.c */ 363 int 364 hinthash(cp, vmajor, vminor) 365 char *cp; 366 int vmajor, vminor; 367 { 368 int k = 0; 369 370 while (*cp) 371 k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; 372 373 k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; 374 #if 0 375 k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff; 376 #endif 377 378 return (k); 379 } 380 381 int 382 buildhints() 383 { 384 struct hints_header hdr; 385 struct hints_bucket *blist; 386 struct shlib_list *shp; 387 char *strtab; 388 int i, n, str_index = 0; 389 int strtab_sz = 0; /* Total length of strings */ 390 int nhints = 0; /* Total number of hints */ 391 int fd; 392 char *tempfile; 393 394 for (shp = shlib_head; shp; shp = shp->next) { 395 strtab_sz += 1 + strlen(shp->name); 396 strtab_sz += 1 + strlen(shp->path); 397 nhints++; 398 } 399 400 /* Fill hints file header */ 401 hdr.hh_magic = HH_MAGIC; 402 hdr.hh_version = LD_HINTS_VERSION_2; 403 hdr.hh_nbucket = 1 * nhints; 404 n = hdr.hh_nbucket * sizeof(struct hints_bucket); 405 hdr.hh_hashtab = sizeof(struct hints_header); 406 hdr.hh_strtab = hdr.hh_hashtab + n; 407 hdr.hh_dirlist = strtab_sz; 408 strtab_sz += 1 + strlen(dir_list); 409 hdr.hh_strtab_sz = strtab_sz; 410 hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz; 411 412 if (verbose) 413 printf("Totals: entries %d, buckets %ld, string size %d\n", 414 nhints, hdr.hh_nbucket, strtab_sz); 415 416 /* Allocate buckets and string table */ 417 blist = (struct hints_bucket *)xmalloc(n); 418 memset(blist, 0, n); 419 for (i = 0; i < hdr.hh_nbucket; i++) 420 /* Empty all buckets */ 421 blist[i].hi_next = -1; 422 423 strtab = (char *)xmalloc(strtab_sz); 424 425 /* Enter all */ 426 for (shp = shlib_head; shp; shp = shp->next) { 427 struct hints_bucket *bp; 428 429 bp = blist + 430 (hinthash(shp->name, shp->major, shp->minor) % hdr.hh_nbucket); 431 432 if (bp->hi_pathx) { 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 memcpy(bp->hi_dewey, shp->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 tempfile = concat(_PATH_LD_HINTS, ".XXXXXX", ""); 471 if ((fd = mkstemp(tempfile)) == -1) { 472 warn("%s", tempfile); 473 return (-1); 474 } 475 476 if (write(fd, &hdr, sizeof(struct hints_header)) != 477 sizeof(struct hints_header)) { 478 warn("%s", _PATH_LD_HINTS); 479 return (-1); 480 } 481 if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) != 482 hdr.hh_nbucket * sizeof(struct hints_bucket)) { 483 warn("%s", _PATH_LD_HINTS); 484 return (-1); 485 } 486 if (write(fd, strtab, strtab_sz) != strtab_sz) { 487 warn("%s", _PATH_LD_HINTS); 488 return (-1); 489 } 490 if (fchmod(fd, 0444) == -1) { 491 warn("%s", _PATH_LD_HINTS); 492 return (-1); 493 } 494 if (close(fd) != 0) { 495 warn("%s", _PATH_LD_HINTS); 496 return (-1); 497 } 498 499 /* Install it */ 500 if (unlink(_PATH_LD_HINTS) != 0 && errno != ENOENT) { 501 warn("%s", _PATH_LD_HINTS); 502 return (-1); 503 } 504 505 if (rename(tempfile, _PATH_LD_HINTS) != 0) { 506 warn("%s", _PATH_LD_HINTS); 507 return (-1); 508 } 509 510 return (0); 511 } 512 513 static int 514 readhints() 515 { 516 int fd; 517 void *addr = (void *) -1; 518 size_t msize; 519 struct hints_header *hdr; 520 struct hints_bucket *blist; 521 char *strtab; 522 struct shlib_list *shp; 523 int i; 524 struct stat st; 525 int error = 0; 526 527 if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) { 528 warn("%s", _PATH_LD_HINTS); 529 goto cleanup; 530 } 531 532 if (fstat(fd, &st) == -1) { 533 warn("%s", _PATH_LD_HINTS); 534 goto cleanup; 535 } 536 537 msize = (size_t)st.st_size; 538 if (msize < sizeof(*hdr)) { 539 warnx("%s: File too short", _PATH_LD_HINTS); 540 goto cleanup; 541 } 542 543 addr = mmap(0, msize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); 544 545 if (addr == (void *)-1) { 546 warn("%s", _PATH_LD_HINTS); 547 goto cleanup; 548 } 549 550 hdr = (struct hints_header *)addr; 551 if (HH_BADMAG(*hdr)) { 552 warnx("%s: Bad magic: %lo", 553 _PATH_LD_HINTS, hdr->hh_magic); 554 goto cleanup; 555 } 556 557 if (hdr->hh_version != LD_HINTS_VERSION_2) { 558 warnx("Unsupported version: %ld", hdr->hh_version); 559 goto cleanup; 560 } 561 562 563 blist = (struct hints_bucket *)((char *)addr + hdr->hh_hashtab); 564 strtab = ((char *)addr + hdr->hh_strtab); 565 566 for (i = 0; i < hdr->hh_nbucket; i++) { 567 struct hints_bucket *bp = &blist[i]; 568 569 /* Sanity check */ 570 if (bp->hi_namex >= hdr->hh_strtab_sz) { 571 warnx("Bad name index: %#x", bp->hi_namex); 572 goto cleanup; 573 } 574 if (bp->hi_pathx >= hdr->hh_strtab_sz) { 575 warnx("Bad path index: %#x", bp->hi_pathx); 576 goto cleanup; 577 } 578 579 /* Allocate new list element */ 580 shp = (struct shlib_list *)xmalloc(sizeof *shp); 581 shp->name = strdup(strtab + bp->hi_namex); 582 shp->path = strdup(strtab + bp->hi_pathx); 583 memcpy(shp->dewey, bp->hi_dewey, sizeof(shp->dewey)); 584 shp->ndewey = bp->hi_ndewey; 585 shp->next = NULL; 586 587 *shlib_tail = shp; 588 shlib_tail = &shp->next; 589 } 590 dir_list = strdup(strtab + hdr->hh_dirlist); 591 goto done; 592 cleanup: 593 error = 1; 594 done: 595 if (fd != -1) 596 close(fd); 597 if (addr != (void *)-1) 598 munmap(addr, msize); 599 return error; 600 601 } 602 603 static void 604 listhints() 605 { 606 struct shlib_list *shp; 607 int i; 608 609 printf("%s:\n", _PATH_LD_HINTS); 610 printf("\tsearch directories: %s\n", dir_list); 611 612 for (i = 0, shp = shlib_head; shp; i++, shp = shp->next) 613 printf("\t%d:-l%s.%d.%d => %s\n", 614 i, shp->name, shp->major, shp->minor, shp->path); 615 616 return; 617 } 618