1 /* $NetBSD: ldconfig.c,v 1.40 2005/06/02 00:03:38 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 #include <sys/cdefs.h> 39 40 #ifndef lint 41 __RCSID("$NetBSD: ldconfig.c,v 1.40 2005/06/02 00:03:38 lukem Exp $"); 42 #endif 43 44 45 #include <sys/param.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <sys/file.h> 49 #include <sys/time.h> 50 #include <sys/mman.h> 51 #include <a.out.h> 52 #include <ctype.h> 53 #include <dirent.h> 54 #include <err.h> 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 #include <link_aout.h> 62 #include <paths.h> 63 64 #include "shlib.h" 65 66 #define _PATH_LD_SO_CONF "/etc/ld.so.conf" 67 68 #undef major 69 #undef minor 70 71 static int verbose; 72 static int nostd; 73 static int noconf; 74 static int justread; 75 static int merge; 76 77 struct shlib_list { 78 /* Internal list of shared libraries found */ 79 char *name; 80 char *path; 81 int dewey[MAXDEWEY]; 82 int ndewey; 83 #define major dewey[0] 84 #define minor dewey[1] 85 struct shlib_list *next; 86 }; 87 88 static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head; 89 static char *dir_list; 90 91 static void enter(char *, char *, char *, int *, int); 92 static int dodir(char *, int, int); 93 static int do_conf(void); 94 static int buildhints(void); 95 static int readhints(void); 96 static void listhints(void); 97 static int hinthash(char *, int, int); 98 99 int 100 main(int argc, char *argv[]) 101 { 102 int i, c; 103 int rval = 0; 104 105 while ((c = getopt(argc, argv, "cmrsSv")) != -1) { 106 switch (c) { 107 case 'c': 108 noconf = 1; 109 break; 110 case 'm': 111 merge = 1; 112 break; 113 case 'r': 114 justread = 1; 115 break; 116 case 's': 117 nostd = 1; 118 noconf = 1; 119 break; 120 case 'S': 121 nostd = 1; 122 break; 123 case 'v': 124 verbose = 1; 125 break; 126 default: 127 errx(1, "usage: %s [-c][-m][-r][-s][-S][-v][dir ...]", 128 getprogname()); 129 break; 130 } 131 } 132 133 dir_list = xmalloc(1); 134 *dir_list = '\0'; 135 136 if (justread || merge) { 137 if ((rval = readhints()) != 0) 138 return (rval); 139 if (justread) { 140 listhints(); 141 return (rval); 142 } 143 } 144 145 if (!nostd && !merge) 146 std_search_path(); 147 148 for (i = 0; i < n_search_dirs; i++) 149 rval |= dodir(search_dirs[i], 1, 0); 150 151 if (!noconf && !merge) 152 rval |= do_conf(); 153 154 for (i = optind; i < argc; i++) { 155 rval |= dodir(argv[i], 0, 1); 156 } 157 158 rval |= buildhints(); 159 160 return (rval); 161 } 162 163 int 164 do_conf(void) 165 { 166 FILE *conf; 167 char *line, *c; 168 char *cline = NULL; 169 size_t len; 170 int rval = 0; 171 #ifdef __ELF__ 172 char *aout_conf; 173 174 aout_conf = xmalloc(sizeof(_PATH_EMUL_AOUT) + 175 strlen(_PATH_LD_SO_CONF)); 176 strcpy(aout_conf, _PATH_EMUL_AOUT); 177 strcat(aout_conf, _PATH_LD_SO_CONF); 178 if ((conf = fopen(aout_conf, "r")) == NULL) { 179 if (verbose) 180 warnx("can't open `%s'", aout_conf); 181 return (0); 182 } 183 free(aout_conf); 184 #else 185 if ((conf = fopen(_PATH_LD_SO_CONF, "r")) == NULL) { 186 if (verbose) { 187 warnx("can't open `%s'", _PATH_LD_SO_CONF); 188 } 189 return (0); 190 } 191 #endif 192 193 while ((line = fgetln(conf, &len)) != NULL) { 194 if (*line != '/') 195 continue; 196 197 if (line[len-1] == '\n') { 198 line[--len] = '\0'; 199 } else { 200 cline = xmalloc(len+1); 201 memcpy(cline, line, len); 202 line = cline; 203 line[len] = '\0'; 204 } 205 206 while (isblank(*line)) { line++; len--; } 207 if ((c = strchr(line, '#')) == NULL) 208 c = line + len; 209 while (--c >= line && isblank(*c)) continue; 210 if (c >= line) { 211 *++c = '\0'; 212 rval |= dodir(line, 0, 1); 213 } 214 215 if (cline) { 216 free(cline); 217 cline = NULL; 218 } 219 } 220 221 (void) fclose(conf); 222 223 return (rval); 224 } 225 226 int 227 dodir(char *dir, int silent, 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((unsigned char)*(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(char *dir, char *file, char *name, int dewey[], int ndewey) 308 { 309 struct shlib_list *shp; 310 311 for (shp = shlib_head; shp; shp = shp->next) { 312 if (strcmp(name, shp->name) != 0 || major != shp->major) 313 continue; 314 315 /* Name matches existing entry */ 316 if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) { 317 318 /* Update this entry with higher versioned lib */ 319 if (verbose) 320 printf("Updating lib%s.%d.%d to %s/%s\n", 321 shp->name, shp->major, shp->minor, 322 dir, file); 323 324 free(shp->name); 325 shp->name = strdup(name); 326 free(shp->path); 327 shp->path = concat(dir, "/", file); 328 memcpy(shp->dewey, dewey, sizeof(shp->dewey)); 329 shp->ndewey = ndewey; 330 } 331 break; 332 } 333 334 if (shp) 335 /* Name exists: older version or just updated */ 336 return; 337 338 /* Allocate new list element */ 339 if (verbose) 340 printf("Adding %s/%s\n", dir, file); 341 342 shp = (struct shlib_list *)xmalloc(sizeof *shp); 343 shp->name = strdup(name); 344 shp->path = concat(dir, "/", file); 345 memcpy(shp->dewey, dewey, sizeof(shp->dewey)); 346 shp->ndewey = ndewey; 347 shp->next = NULL; 348 349 *shlib_tail = shp; 350 shlib_tail = &shp->next; 351 } 352 353 354 #if DEBUG 355 /* test */ 356 #undef _PATH_LD_HINTS 357 #define _PATH_LD_HINTS "./ld.so.hints" 358 #endif 359 360 /* XXX - should be a common function with rtld.c */ 361 int 362 hinthash(char *cp, int vmajor, int vminor) 363 { 364 int k = 0; 365 366 while (*cp) 367 k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; 368 369 k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; 370 #if 0 371 k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff; 372 #endif 373 374 return (k); 375 } 376 377 int 378 buildhints(void) 379 { 380 struct hints_header hdr; 381 struct hints_bucket *blist; 382 struct shlib_list *shp; 383 char *strtab; 384 int i, n, str_index = 0; 385 int strtab_sz = 0; /* Total length of strings */ 386 int nhints = 0; /* Total number of hints */ 387 int fd; 388 char *tempfile; 389 390 for (shp = shlib_head; shp; shp = shp->next) { 391 strtab_sz += 1 + strlen(shp->name); 392 strtab_sz += 1 + strlen(shp->path); 393 nhints++; 394 } 395 396 /* Fill hints file header */ 397 hdr.hh_magic = HH_MAGIC; 398 hdr.hh_version = LD_HINTS_VERSION_2; 399 hdr.hh_nbucket = 1 * nhints; 400 n = hdr.hh_nbucket * sizeof(struct hints_bucket); 401 hdr.hh_hashtab = sizeof(struct hints_header); 402 hdr.hh_strtab = hdr.hh_hashtab + n; 403 hdr.hh_dirlist = strtab_sz; 404 strtab_sz += 1 + strlen(dir_list); 405 hdr.hh_strtab_sz = strtab_sz; 406 hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz; 407 408 if (verbose) 409 printf("Totals: entries %d, buckets %ld, string size %d\n", 410 nhints, hdr.hh_nbucket, strtab_sz); 411 412 /* Allocate buckets and string table */ 413 blist = (struct hints_bucket *)xmalloc(n); 414 memset(blist, 0, n); 415 for (i = 0; i < hdr.hh_nbucket; i++) 416 /* Empty all buckets */ 417 blist[i].hi_next = -1; 418 419 strtab = (char *)xmalloc(strtab_sz); 420 421 /* Enter all */ 422 for (shp = shlib_head; shp; shp = shp->next) { 423 struct hints_bucket *bp; 424 425 bp = blist + 426 (hinthash(shp->name, shp->major, shp->minor) % hdr.hh_nbucket); 427 428 if (bp->hi_pathx) { 429 for (i = 0; i < hdr.hh_nbucket; i++) { 430 if (blist[i].hi_pathx == 0) 431 break; 432 } 433 if (i == hdr.hh_nbucket) { 434 warnx("Bummer!"); 435 return (-1); 436 } 437 while (bp->hi_next != -1) 438 bp = &blist[bp->hi_next]; 439 bp->hi_next = i; 440 bp = blist + i; 441 } 442 443 /* Insert strings in string table */ 444 bp->hi_namex = str_index; 445 strcpy(strtab + str_index, shp->name); 446 str_index += 1 + strlen(shp->name); 447 448 bp->hi_pathx = str_index; 449 strcpy(strtab + str_index, shp->path); 450 str_index += 1 + strlen(shp->path); 451 452 /* Copy versions */ 453 memcpy(bp->hi_dewey, shp->dewey, sizeof(bp->hi_dewey)); 454 bp->hi_ndewey = shp->ndewey; 455 } 456 457 /* Copy search directories */ 458 strcpy(strtab + str_index, dir_list); 459 str_index += 1 + strlen(dir_list); 460 461 /* Sanity check */ 462 if (str_index != strtab_sz) { 463 errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz); 464 } 465 466 tempfile = concat(_PATH_LD_HINTS, ".XXXXXX", ""); 467 if ((fd = mkstemp(tempfile)) == -1) { 468 warn("%s", tempfile); 469 return (-1); 470 } 471 472 if (write(fd, &hdr, sizeof(struct hints_header)) != 473 sizeof(struct hints_header)) { 474 warn("%s", _PATH_LD_HINTS); 475 return (-1); 476 } 477 if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) != 478 hdr.hh_nbucket * sizeof(struct hints_bucket)) { 479 warn("%s", _PATH_LD_HINTS); 480 return (-1); 481 } 482 if (write(fd, strtab, strtab_sz) != strtab_sz) { 483 warn("%s", _PATH_LD_HINTS); 484 return (-1); 485 } 486 if (fchmod(fd, 0444) == -1) { 487 warn("%s", _PATH_LD_HINTS); 488 return (-1); 489 } 490 if (close(fd) != 0) { 491 warn("%s", _PATH_LD_HINTS); 492 return (-1); 493 } 494 495 /* Install it */ 496 if (unlink(_PATH_LD_HINTS) != 0 && errno != ENOENT) { 497 warn("%s", _PATH_LD_HINTS); 498 return (-1); 499 } 500 501 if (rename(tempfile, _PATH_LD_HINTS) != 0) { 502 warn("%s", _PATH_LD_HINTS); 503 return (-1); 504 } 505 506 return (0); 507 } 508 509 static int 510 readhints(void) 511 { 512 int fd; 513 void *addr = (void *) -1; 514 size_t msize = 0; 515 struct hints_header *hdr; 516 struct hints_bucket *blist; 517 char *strtab; 518 struct shlib_list *shp; 519 int i; 520 struct stat st; 521 int error = 0; 522 523 if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) { 524 warn("%s", _PATH_LD_HINTS); 525 goto cleanup; 526 } 527 528 if (fstat(fd, &st) == -1) { 529 warn("%s", _PATH_LD_HINTS); 530 goto cleanup; 531 } 532 533 msize = (size_t)st.st_size; 534 if (msize < sizeof(*hdr)) { 535 warnx("%s: File too short", _PATH_LD_HINTS); 536 goto cleanup; 537 } 538 539 addr = mmap(0, msize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); 540 541 if (addr == (void *)-1) { 542 warn("%s", _PATH_LD_HINTS); 543 goto cleanup; 544 } 545 546 hdr = (struct hints_header *)addr; 547 if (HH_BADMAG(*hdr)) { 548 warnx("%s: Bad magic: %lo", 549 _PATH_LD_HINTS, hdr->hh_magic); 550 goto cleanup; 551 } 552 553 if (hdr->hh_version != LD_HINTS_VERSION_2) { 554 warnx("Unsupported version: %ld", hdr->hh_version); 555 goto cleanup; 556 } 557 558 559 blist = (struct hints_bucket *)((char *)addr + hdr->hh_hashtab); 560 strtab = ((char *)addr + hdr->hh_strtab); 561 562 for (i = 0; i < hdr->hh_nbucket; i++) { 563 struct hints_bucket *bp = &blist[i]; 564 565 /* Sanity check */ 566 if (bp->hi_namex >= hdr->hh_strtab_sz) { 567 warnx("Bad name index: %#x", bp->hi_namex); 568 goto cleanup; 569 } 570 if (bp->hi_pathx >= hdr->hh_strtab_sz) { 571 warnx("Bad path index: %#x", bp->hi_pathx); 572 goto cleanup; 573 } 574 575 /* Allocate new list element */ 576 shp = (struct shlib_list *)xmalloc(sizeof *shp); 577 shp->name = strdup(strtab + bp->hi_namex); 578 shp->path = strdup(strtab + bp->hi_pathx); 579 memcpy(shp->dewey, bp->hi_dewey, sizeof(shp->dewey)); 580 shp->ndewey = bp->hi_ndewey; 581 shp->next = NULL; 582 583 *shlib_tail = shp; 584 shlib_tail = &shp->next; 585 } 586 dir_list = strdup(strtab + hdr->hh_dirlist); 587 goto done; 588 cleanup: 589 error = 1; 590 done: 591 if (fd != -1) 592 close(fd); 593 if (addr != (void *)-1) 594 munmap(addr, msize); 595 return error; 596 597 } 598 599 static void 600 listhints(void) 601 { 602 struct shlib_list *shp; 603 int i; 604 605 printf("%s:\n", _PATH_LD_HINTS); 606 printf("\tsearch directories: %s\n", dir_list); 607 608 for (i = 0, shp = shlib_head; shp; i++, shp = shp->next) 609 printf("\t%d:-l%s.%d.%d => %s\n", 610 i, shp->name, shp->major, shp->minor, shp->path); 611 612 return; 613 } 614