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