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 * $Id: ldconfig.c,v 1.13 1996/07/12 19:08:34 jkh Exp $ 31 */ 32 33 #include <sys/param.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <sys/file.h> 37 #include <sys/time.h> 38 #include <sys/mman.h> 39 #include <sys/resource.h> 40 #include <dirent.h> 41 #include <errno.h> 42 #include <err.h> 43 #include <ctype.h> 44 #include <fcntl.h> 45 #include <ar.h> 46 #include <ranlib.h> 47 #include <a.out.h> 48 #include <stab.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include <machine/param.h> 55 56 #include <link.h> 57 #include "shlib.h" 58 #include "support.h" 59 60 61 #undef major 62 #undef minor 63 64 extern char *__progname; 65 66 static int verbose; 67 static int nostd; 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)); 87 static int buildhints __P((void)); 88 static int readhints __P((void)); 89 static void listhints __P((void)); 90 91 int 92 main(argc, argv) 93 int argc; 94 char *argv[]; 95 { 96 int i, c; 97 int rval = 0; 98 99 while ((c = getopt(argc, argv, "mrsv")) != EOF) { 100 switch (c) { 101 case 'm': 102 merge = 1; 103 break; 104 case 'r': 105 justread = 1; 106 break; 107 case 's': 108 nostd = 1; 109 break; 110 case 'v': 111 verbose = 1; 112 break; 113 default: 114 errx(1, "Usage: %s [-mrsv] [dir ...]", 115 __progname); 116 break; 117 } 118 } 119 120 dir_list = strdup(""); 121 122 if (justread || merge) { 123 if ((rval = readhints()) != 0) 124 return rval; 125 } 126 127 if (!nostd && !merge) 128 std_search_path(); 129 130 for (i = optind; i < argc; i++) 131 add_search_path(argv[i]); 132 133 for (i = 0; i < n_search_dirs; i++) { 134 char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]); 135 free(dir_list); 136 dir_list = cp; 137 } 138 139 if (justread) { 140 listhints(); 141 return 0; 142 } 143 144 for (i = 0; i < n_search_dirs; i++) 145 rval |= dodir(search_dirs[i], 1); 146 147 rval |= buildhints(); 148 149 return rval; 150 } 151 152 int 153 dodir(dir, silent) 154 char *dir; 155 int silent; 156 { 157 DIR *dd; 158 struct dirent *dp; 159 char name[MAXPATHLEN]; 160 int dewey[MAXDEWEY], ndewey; 161 162 if ((dd = opendir(dir)) == NULL) { 163 if (!silent || errno != ENOENT) 164 warn("%s", dir); 165 return -1; 166 } 167 168 while ((dp = readdir(dd)) != NULL) { 169 register int n; 170 register char *cp; 171 172 /* Check for `lib' prefix */ 173 if (dp->d_name[0] != 'l' || 174 dp->d_name[1] != 'i' || 175 dp->d_name[2] != 'b') 176 continue; 177 178 /* Copy the entry minus prefix */ 179 (void)strcpy(name, dp->d_name + 3); 180 n = strlen(name); 181 if (n < 4) 182 continue; 183 184 /* Find ".so." in name */ 185 for (cp = name + n - 4; cp > name; --cp) { 186 if (cp[0] == '.' && 187 cp[1] == 's' && 188 cp[2] == 'o' && 189 cp[3] == '.') 190 break; 191 } 192 if (cp <= name) 193 continue; 194 195 *cp = '\0'; 196 if (!isdigit(*(cp+4))) 197 continue; 198 199 bzero((caddr_t)dewey, sizeof(dewey)); 200 ndewey = getdewey(dewey, cp + 4); 201 enter(dir, dp->d_name, name, dewey, ndewey); 202 } 203 204 return 0; 205 } 206 207 static void 208 enter(dir, file, name, dewey, ndewey) 209 char *dir, *file, *name; 210 int dewey[], ndewey; 211 { 212 struct shlib_list *shp; 213 214 for (shp = shlib_head; shp; shp = shp->next) { 215 if (strcmp(name, shp->name) != 0 || major != shp->major) 216 continue; 217 218 /* Name matches existing entry */ 219 if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) { 220 221 /* Update this entry with higher versioned lib */ 222 if (verbose) 223 printf("Updating lib%s.%d.%d to %s/%s\n", 224 shp->name, shp->major, shp->minor, 225 dir, file); 226 227 free(shp->name); 228 shp->name = strdup(name); 229 free(shp->path); 230 shp->path = concat(dir, "/", file); 231 bcopy(dewey, shp->dewey, sizeof(shp->dewey)); 232 shp->ndewey = ndewey; 233 } 234 break; 235 } 236 237 if (shp) 238 /* Name exists: older version or just updated */ 239 return; 240 241 /* Allocate new list element */ 242 if (verbose) 243 printf("Adding %s/%s\n", dir, file); 244 245 shp = (struct shlib_list *)xmalloc(sizeof *shp); 246 shp->name = strdup(name); 247 shp->path = concat(dir, "/", file); 248 bcopy(dewey, shp->dewey, MAXDEWEY); 249 shp->ndewey = ndewey; 250 shp->next = NULL; 251 252 *shlib_tail = shp; 253 shlib_tail = &shp->next; 254 } 255 256 257 #if DEBUG 258 /* test */ 259 #undef _PATH_LD_HINTS 260 #define _PATH_LD_HINTS "./ld.so.hints" 261 #endif 262 263 int 264 hinthash(cp, vmajor) 265 char *cp; 266 int vmajor; 267 { 268 int k = 0; 269 270 while (*cp) 271 k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; 272 273 k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; 274 275 return k; 276 } 277 278 int 279 buildhints() 280 { 281 struct hints_header hdr; 282 struct hints_bucket *blist; 283 struct shlib_list *shp; 284 char *strtab; 285 int i, n, str_index = 0; 286 int strtab_sz = 0; /* Total length of strings */ 287 int nhints = 0; /* Total number of hints */ 288 int fd; 289 char *tmpfile; 290 291 for (shp = shlib_head; shp; shp = shp->next) { 292 strtab_sz += 1 + strlen(shp->name); 293 strtab_sz += 1 + strlen(shp->path); 294 nhints++; 295 } 296 297 /* Fill hints file header */ 298 hdr.hh_magic = HH_MAGIC; 299 hdr.hh_version = LD_HINTS_VERSION_2; 300 hdr.hh_nbucket = 1 * nhints; 301 n = hdr.hh_nbucket * sizeof(struct hints_bucket); 302 hdr.hh_hashtab = sizeof(struct hints_header); 303 hdr.hh_strtab = hdr.hh_hashtab + n; 304 hdr.hh_dirlist = strtab_sz; 305 strtab_sz += 1 + strlen(dir_list); 306 hdr.hh_strtab_sz = strtab_sz; 307 hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz; 308 309 if (verbose) 310 printf("Totals: entries %d, buckets %ld, string size %d\n", 311 nhints, hdr.hh_nbucket, strtab_sz); 312 313 /* Allocate buckets and string table */ 314 blist = (struct hints_bucket *)xmalloc(n); 315 bzero((char *)blist, n); 316 for (i = 0; i < hdr.hh_nbucket; i++) 317 /* Empty all buckets */ 318 blist[i].hi_next = -1; 319 320 strtab = (char *)xmalloc(strtab_sz); 321 322 /* Enter all */ 323 for (shp = shlib_head; shp; shp = shp->next) { 324 struct hints_bucket *bp; 325 326 bp = blist + 327 (hinthash(shp->name, shp->major) % hdr.hh_nbucket); 328 329 if (bp->hi_pathx) { 330 int i; 331 332 for (i = 0; i < hdr.hh_nbucket; i++) { 333 if (blist[i].hi_pathx == 0) 334 break; 335 } 336 if (i == hdr.hh_nbucket) { 337 warnx("Bummer!"); 338 return -1; 339 } 340 while (bp->hi_next != -1) 341 bp = &blist[bp->hi_next]; 342 bp->hi_next = i; 343 bp = blist + i; 344 } 345 346 /* Insert strings in string table */ 347 bp->hi_namex = str_index; 348 strcpy(strtab + str_index, shp->name); 349 str_index += 1 + strlen(shp->name); 350 351 bp->hi_pathx = str_index; 352 strcpy(strtab + str_index, shp->path); 353 str_index += 1 + strlen(shp->path); 354 355 /* Copy versions */ 356 bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey)); 357 bp->hi_ndewey = shp->ndewey; 358 } 359 360 /* Copy search directories */ 361 strcpy(strtab + str_index, dir_list); 362 str_index += 1 + strlen(dir_list); 363 364 /* Sanity check */ 365 if (str_index != strtab_sz) { 366 errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz); 367 } 368 369 tmpfile = concat(_PATH_LD_HINTS, ".XXXXXX", ""); 370 if ((tmpfile = mktemp(tmpfile)) == NULL) { 371 warn("%s", tmpfile); 372 return -1; 373 } 374 375 umask(0); /* Create with exact permissions */ 376 if ((fd = open(tmpfile, O_RDWR|O_CREAT|O_TRUNC, 0444)) == -1) { 377 warn("%s", _PATH_LD_HINTS); 378 return -1; 379 } 380 381 if (write(fd, &hdr, sizeof(struct hints_header)) != 382 sizeof(struct hints_header)) { 383 warn("%s", _PATH_LD_HINTS); 384 return -1; 385 } 386 if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) != 387 hdr.hh_nbucket * sizeof(struct hints_bucket)) { 388 warn("%s", _PATH_LD_HINTS); 389 return -1; 390 } 391 if (write(fd, strtab, strtab_sz) != strtab_sz) { 392 warn("%s", _PATH_LD_HINTS); 393 return -1; 394 } 395 if (close(fd) != 0) { 396 warn("%s", _PATH_LD_HINTS); 397 return -1; 398 } 399 400 /* Install it */ 401 if (unlink(_PATH_LD_HINTS) != 0 && errno != ENOENT) { 402 warn("%s", _PATH_LD_HINTS); 403 return -1; 404 } 405 406 if (rename(tmpfile, _PATH_LD_HINTS) != 0) { 407 warn("%s", _PATH_LD_HINTS); 408 return -1; 409 } 410 411 return 0; 412 } 413 414 static int 415 readhints() 416 { 417 int fd; 418 caddr_t addr; 419 long msize; 420 struct hints_header *hdr; 421 struct hints_bucket *blist; 422 char *strtab; 423 struct shlib_list *shp; 424 int i; 425 426 if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) { 427 warn("%s", _PATH_LD_HINTS); 428 return -1; 429 } 430 431 msize = PAGE_SIZE; 432 addr = mmap(0, msize, PROT_READ, MAP_COPY, fd, 0); 433 434 if (addr == (caddr_t)-1) { 435 warn("%s", _PATH_LD_HINTS); 436 return -1; 437 } 438 439 hdr = (struct hints_header *)addr; 440 if (HH_BADMAG(*hdr)) { 441 warnx("%s: Bad magic: %o", 442 _PATH_LD_HINTS, hdr->hh_magic); 443 return -1; 444 } 445 446 if (hdr->hh_version != LD_HINTS_VERSION_1 && 447 hdr->hh_version != LD_HINTS_VERSION_2) { 448 warnx("Unsupported version: %d", hdr->hh_version); 449 return -1; 450 } 451 452 if (hdr->hh_ehints > msize) { 453 if (mmap(addr+msize, hdr->hh_ehints - msize, 454 PROT_READ, MAP_COPY|MAP_FIXED, 455 fd, msize) != (caddr_t)(addr+msize)) { 456 457 warn("%s", _PATH_LD_HINTS); 458 return -1; 459 } 460 } 461 close(fd); 462 463 blist = (struct hints_bucket *)(addr + hdr->hh_hashtab); 464 strtab = (char *)(addr + hdr->hh_strtab); 465 466 for (i = 0; i < hdr->hh_nbucket; i++) { 467 struct hints_bucket *bp = &blist[i]; 468 469 /* Sanity check */ 470 if (bp->hi_namex >= hdr->hh_strtab_sz) { 471 warnx("Bad name index: %#x", bp->hi_namex); 472 return -1; 473 } 474 if (bp->hi_pathx >= hdr->hh_strtab_sz) { 475 warnx("Bad path index: %#x", bp->hi_pathx); 476 return -1; 477 } 478 479 /* Allocate new list element */ 480 shp = (struct shlib_list *)xmalloc(sizeof *shp); 481 shp->name = strdup(strtab + bp->hi_namex); 482 shp->path = strdup(strtab + bp->hi_pathx); 483 bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey)); 484 shp->ndewey = bp->hi_ndewey; 485 shp->next = NULL; 486 487 *shlib_tail = shp; 488 shlib_tail = &shp->next; 489 } 490 if (hdr->hh_version >= LD_HINTS_VERSION_2) 491 add_search_path(strtab + hdr->hh_dirlist); 492 493 return 0; 494 } 495 496 static void 497 listhints() 498 { 499 struct shlib_list *shp; 500 int i; 501 502 printf("%s:\n", _PATH_LD_HINTS); 503 printf("\tsearch directories: %s\n", dir_list); 504 505 for (i = 0, shp = shlib_head; shp; i++, shp = shp->next) 506 printf("\t%d:-l%s.%d.%d => %s\n", 507 i, shp->name, shp->major, shp->minor, shp->path); 508 509 return; 510 } 511