1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 2000, Boris Popov 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Boris Popov. 18 * 4. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/endian.h> 38 #include <sys/exec.h> 39 #include <sys/queue.h> 40 #include <sys/kernel.h> 41 #include <sys/reboot.h> 42 #include <sys/linker.h> 43 #include <sys/stat.h> 44 #include <sys/module.h> 45 46 #include <ctype.h> 47 #include <err.h> 48 #include <errno.h> 49 #include <fts.h> 50 #include <stdbool.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <machine/elf.h> 56 57 #include "ef.h" 58 59 #define MAXRECSIZE (64 << 10) /* 64k */ 60 #define check(val) if ((error = (val)) != 0) break 61 62 static bool dflag; /* do not create a hint file, only write on stdout */ 63 static int verbose; 64 65 static FILE *fxref; /* current hints file */ 66 67 static const char *xref_file = "linker.hints"; 68 69 /* 70 * A record is stored in the static buffer recbuf before going to disk. 71 */ 72 static char recbuf[MAXRECSIZE]; 73 static int recpos; /* current write position */ 74 static int reccnt; /* total record written to this file so far */ 75 76 static void 77 intalign(void) 78 { 79 80 recpos = roundup2(recpos, sizeof(int)); 81 } 82 83 static void 84 record_start(void) 85 { 86 87 recpos = 0; 88 memset(recbuf, 0, MAXRECSIZE); 89 } 90 91 static int 92 record_end(void) 93 { 94 95 if (recpos == 0) 96 return (0); 97 reccnt++; 98 intalign(); 99 fwrite(&recpos, sizeof(recpos), 1, fxref); 100 return (fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0); 101 } 102 103 static int 104 record_buf(const void *buf, size_t size) 105 { 106 107 if (MAXRECSIZE - recpos < size) 108 errx(1, "record buffer overflow"); 109 memcpy(recbuf + recpos, buf, size); 110 recpos += size; 111 return (0); 112 } 113 114 /* 115 * An int is stored in host order and aligned 116 */ 117 static int 118 record_int(int val) 119 { 120 121 intalign(); 122 return (record_buf(&val, sizeof(val))); 123 } 124 125 /* 126 * A string is stored as 1-byte length plus data, no padding 127 */ 128 static int 129 record_string(const char *str) 130 { 131 int error; 132 size_t len; 133 u_char val; 134 135 if (dflag) 136 return (0); 137 val = len = strlen(str); 138 if (len > 255) 139 errx(1, "string %s too long", str); 140 error = record_buf(&val, sizeof(val)); 141 if (error != 0) 142 return (error); 143 return (record_buf(str, len)); 144 } 145 146 /* From sys/isa/pnp.c */ 147 static char * 148 pnp_eisaformat(uint32_t id) 149 { 150 uint8_t *data; 151 static char idbuf[8]; 152 const char hextoascii[] = "0123456789abcdef"; 153 154 id = htole32(id); 155 data = (uint8_t *)&id; 156 idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); 157 idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); 158 idbuf[2] = '@' + (data[1] & 0x1f); 159 idbuf[3] = hextoascii[(data[2] >> 4)]; 160 idbuf[4] = hextoascii[(data[2] & 0xf)]; 161 idbuf[5] = hextoascii[(data[3] >> 4)]; 162 idbuf[6] = hextoascii[(data[3] & 0xf)]; 163 idbuf[7] = 0; 164 return (idbuf); 165 } 166 167 struct pnp_elt 168 { 169 int pe_kind; /* What kind of entry */ 170 #define TYPE_SZ_MASK 0x0f 171 #define TYPE_FLAGGED 0x10 /* all f's is a wildcard */ 172 #define TYPE_INT 0x20 /* Is a number */ 173 #define TYPE_PAIRED 0x40 174 #define TYPE_LE 0x80 /* Matches <= this value */ 175 #define TYPE_GE 0x100 /* Matches >= this value */ 176 #define TYPE_MASK 0x200 /* Specifies a mask to follow */ 177 #define TYPE_U8 (1 | TYPE_INT) 178 #define TYPE_V8 (1 | TYPE_INT | TYPE_FLAGGED) 179 #define TYPE_G16 (2 | TYPE_INT | TYPE_GE) 180 #define TYPE_L16 (2 | TYPE_INT | TYPE_LE) 181 #define TYPE_M16 (2 | TYPE_INT | TYPE_MASK) 182 #define TYPE_U16 (2 | TYPE_INT) 183 #define TYPE_V16 (2 | TYPE_INT | TYPE_FLAGGED) 184 #define TYPE_U32 (4 | TYPE_INT) 185 #define TYPE_V32 (4 | TYPE_INT | TYPE_FLAGGED) 186 #define TYPE_W32 (4 | TYPE_INT | TYPE_PAIRED) 187 #define TYPE_D 7 188 #define TYPE_Z 8 189 #define TYPE_P 9 190 #define TYPE_E 10 191 #define TYPE_T 11 192 int pe_offset; /* Offset within the element */ 193 char * pe_key; /* pnp key name */ 194 TAILQ_ENTRY(pnp_elt) next; /* Link */ 195 }; 196 typedef TAILQ_HEAD(pnp_head, pnp_elt) pnp_list; 197 198 /* 199 * this function finds the data from the pnp table, as described by the 200 * the description and creates a new output (new_desc). This output table 201 * is a form that's easier for the agent that's automatically loading the 202 * modules. 203 * 204 * The format output is the simplified string from this routine in the 205 * same basic format as the pnp string, as documented in sys/module.h. 206 * First a string describing the format is output, the a count of the 207 * number of records, then each record. The format string also describes 208 * the length of each entry (though it isn't a fixed length when strings 209 * are present). 210 * 211 * type Output Meaning 212 * I uint32_t Integer equality comparison 213 * J uint32_t Pair of uint16_t fields converted to native 214 * byte order. The two fields both must match. 215 * G uint32_t Greater than or equal to 216 * L uint32_t Less than or equal to 217 * M uint32_t Mask of which fields to test. Fields that 218 * take up space increment the count. This 219 * field must be first, and resets the count. 220 * D string Description of the device this pnp info is for 221 * Z string pnp string must match this 222 * T nothing T fields set pnp values that must be true for 223 * the entire table. 224 * Values are packed the same way that other values are packed in this file. 225 * Strings and int32_t's start on a 32-bit boundary and are padded with 0 226 * bytes. Objects that are smaller than uint32_t are converted, without 227 * sign extension to uint32_t to simplify parsing downstream. 228 */ 229 static int 230 parse_pnp_list(const char *desc, char **new_desc, pnp_list *list) 231 { 232 const char *walker, *ep; 233 const char *colon, *semi; 234 struct pnp_elt *elt; 235 char type[8], key[32]; 236 int off; 237 size_t new_desc_size; 238 FILE *fp; 239 240 walker = desc; 241 ep = desc + strlen(desc); 242 off = 0; 243 fp = open_memstream(new_desc, &new_desc_size); 244 if (fp == NULL) 245 err(1, "Could not open new memory stream"); 246 if (verbose > 1) 247 printf("Converting %s into a list\n", desc); 248 while (walker < ep) { 249 colon = strchr(walker, ':'); 250 semi = strchr(walker, ';'); 251 if (semi != NULL && semi < colon) 252 goto err; 253 if (colon - walker > sizeof(type)) 254 goto err; 255 strncpy(type, walker, colon - walker); 256 type[colon - walker] = '\0'; 257 if (semi != NULL) { 258 if (semi - colon >= sizeof(key)) 259 goto err; 260 strncpy(key, colon + 1, semi - colon - 1); 261 key[semi - colon - 1] = '\0'; 262 walker = semi + 1; 263 /* Fail safe if we have spaces after ; */ 264 while (walker < ep && isspace(*walker)) 265 walker++; 266 } else { 267 if (strlen(colon + 1) >= sizeof(key)) 268 goto err; 269 strcpy(key, colon + 1); 270 walker = ep; 271 } 272 if (verbose > 1) 273 printf("Found type %s for name %s\n", type, key); 274 /* Skip pointer place holders */ 275 if (strcmp(type, "P") == 0) { 276 off += sizeof(void *); 277 continue; 278 } 279 280 /* 281 * Add a node of the appropriate type 282 */ 283 elt = malloc(sizeof(struct pnp_elt) + strlen(key) + 1); 284 TAILQ_INSERT_TAIL(list, elt, next); 285 elt->pe_key = (char *)(elt + 1); 286 elt->pe_offset = off; 287 if (strcmp(type, "U8") == 0) 288 elt->pe_kind = TYPE_U8; 289 else if (strcmp(type, "V8") == 0) 290 elt->pe_kind = TYPE_V8; 291 else if (strcmp(type, "G16") == 0) 292 elt->pe_kind = TYPE_G16; 293 else if (strcmp(type, "L16") == 0) 294 elt->pe_kind = TYPE_L16; 295 else if (strcmp(type, "M16") == 0) 296 elt->pe_kind = TYPE_M16; 297 else if (strcmp(type, "U16") == 0) 298 elt->pe_kind = TYPE_U16; 299 else if (strcmp(type, "V16") == 0) 300 elt->pe_kind = TYPE_V16; 301 else if (strcmp(type, "U32") == 0) 302 elt->pe_kind = TYPE_U32; 303 else if (strcmp(type, "V32") == 0) 304 elt->pe_kind = TYPE_V32; 305 else if (strcmp(type, "W32") == 0) 306 elt->pe_kind = TYPE_W32; 307 else if (strcmp(type, "D") == 0) /* description char * */ 308 elt->pe_kind = TYPE_D; 309 else if (strcmp(type, "Z") == 0) /* char * to match */ 310 elt->pe_kind = TYPE_Z; 311 else if (strcmp(type, "P") == 0) /* Pointer -- ignored */ 312 elt->pe_kind = TYPE_P; 313 else if (strcmp(type, "E") == 0) /* EISA PNP ID, as uint32_t */ 314 elt->pe_kind = TYPE_E; 315 else if (strcmp(type, "T") == 0) 316 elt->pe_kind = TYPE_T; 317 else 318 goto err; 319 /* 320 * Maybe the rounding here needs to be more nuanced and/or somehow 321 * architecture specific. Fortunately, most tables in the system 322 * have sane ordering of types. 323 */ 324 if (elt->pe_kind & TYPE_INT) { 325 elt->pe_offset = roundup2(elt->pe_offset, elt->pe_kind & TYPE_SZ_MASK); 326 off = elt->pe_offset + (elt->pe_kind & TYPE_SZ_MASK); 327 } else if (elt->pe_kind == TYPE_E) { 328 /* Type E stored as Int, displays as string */ 329 elt->pe_offset = roundup2(elt->pe_offset, sizeof(uint32_t)); 330 off = elt->pe_offset + sizeof(uint32_t); 331 } else if (elt->pe_kind == TYPE_T) { 332 /* doesn't actually consume space in the table */ 333 off = elt->pe_offset; 334 } else { 335 elt->pe_offset = roundup2(elt->pe_offset, sizeof(void *)); 336 off = elt->pe_offset + sizeof(void *); 337 } 338 if (elt->pe_kind & TYPE_PAIRED) { 339 char *word, *ctx, newtype; 340 341 for (word = strtok_r(key, "/", &ctx); 342 word; word = strtok_r(NULL, "/", &ctx)) { 343 newtype = elt->pe_kind & TYPE_FLAGGED ? 'J' : 'I'; 344 fprintf(fp, "%c:%s;", newtype, word); 345 } 346 } 347 else { 348 char newtype; 349 350 if (elt->pe_kind & TYPE_FLAGGED) 351 newtype = 'J'; 352 else if (elt->pe_kind & TYPE_GE) 353 newtype = 'G'; 354 else if (elt->pe_kind & TYPE_LE) 355 newtype = 'L'; 356 else if (elt->pe_kind & TYPE_MASK) 357 newtype = 'M'; 358 else if (elt->pe_kind & TYPE_INT) 359 newtype = 'I'; 360 else if (elt->pe_kind == TYPE_D) 361 newtype = 'D'; 362 else if (elt->pe_kind == TYPE_Z || elt->pe_kind == TYPE_E) 363 newtype = 'Z'; 364 else if (elt->pe_kind == TYPE_T) 365 newtype = 'T'; 366 else 367 errx(1, "Impossible type %x\n", elt->pe_kind); 368 fprintf(fp, "%c:%s;", newtype, key); 369 } 370 } 371 if (ferror(fp) != 0) { 372 fclose(fp); 373 errx(1, "Exhausted space converting description %s", desc); 374 } 375 if (fclose(fp) != 0) 376 errx(1, "Failed to close memory stream"); 377 return (0); 378 err: 379 errx(1, "Parse error of description string %s", desc); 380 } 381 382 static int 383 parse_entry(struct mod_metadata *md, const char *cval, 384 struct elf_file *ef, const char *kldname) 385 { 386 struct mod_depend mdp; 387 struct mod_version mdv; 388 struct mod_pnp_match_info pnp; 389 char descr[1024]; 390 Elf_Off data; 391 int error, i; 392 size_t len; 393 char *walker; 394 void *table; 395 396 data = (Elf_Off)md->md_data; 397 error = 0; 398 record_start(); 399 switch (md->md_type) { 400 case MDT_DEPEND: 401 if (!dflag) 402 break; 403 check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); 404 printf(" depends on %s.%d (%d,%d)\n", cval, 405 mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); 406 break; 407 case MDT_VERSION: 408 check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); 409 if (dflag) { 410 printf(" interface %s.%d\n", cval, mdv.mv_version); 411 } else { 412 record_int(MDT_VERSION); 413 record_string(cval); 414 record_int(mdv.mv_version); 415 record_string(kldname); 416 } 417 break; 418 case MDT_MODULE: 419 if (dflag) { 420 printf(" module %s\n", cval); 421 } else { 422 record_int(MDT_MODULE); 423 record_string(cval); 424 record_string(kldname); 425 } 426 break; 427 case MDT_PNP_INFO: 428 check(EF_SEG_READ_REL(ef, data, sizeof(pnp), &pnp)); 429 check(EF_SEG_READ_STRING(ef, (Elf_Off)pnp.descr, sizeof(descr), descr)); 430 descr[sizeof(descr) - 1] = '\0'; 431 if (dflag) { 432 printf(" pnp info for bus %s format %s %d entries of %d bytes\n", 433 cval, descr, pnp.num_entry, pnp.entry_len); 434 } else { 435 pnp_list list; 436 struct pnp_elt *elt, *elt_tmp; 437 char *new_descr; 438 439 if (verbose > 1) 440 printf(" pnp info for bus %s format %s %d entries of %d bytes\n", 441 cval, descr, pnp.num_entry, pnp.entry_len); 442 /* 443 * Parse descr to weed out the chaff and to create a list 444 * of offsets to output. 445 */ 446 TAILQ_INIT(&list); 447 parse_pnp_list(descr, &new_descr, &list); 448 record_int(MDT_PNP_INFO); 449 record_string(cval); 450 record_string(new_descr); 451 record_int(pnp.num_entry); 452 len = pnp.num_entry * pnp.entry_len; 453 walker = table = malloc(len); 454 check(EF_SEG_READ_REL(ef, (Elf_Off)pnp.table, len, table)); 455 456 /* 457 * Walk the list and output things. We've collapsed all the 458 * variant forms of the table down to just ints and strings. 459 */ 460 for (i = 0; i < pnp.num_entry; i++) { 461 TAILQ_FOREACH(elt, &list, next) { 462 uint8_t v1; 463 uint16_t v2; 464 uint32_t v4; 465 int value; 466 char buffer[1024]; 467 468 if (elt->pe_kind == TYPE_W32) { 469 memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); 470 value = v4 & 0xffff; 471 record_int(value); 472 if (verbose > 1) 473 printf("W32:%#x", value); 474 value = (v4 >> 16) & 0xffff; 475 record_int(value); 476 if (verbose > 1) 477 printf(":%#x;", value); 478 } else if (elt->pe_kind & TYPE_INT) { 479 switch (elt->pe_kind & TYPE_SZ_MASK) { 480 case 1: 481 memcpy(&v1, walker + elt->pe_offset, sizeof(v1)); 482 if ((elt->pe_kind & TYPE_FLAGGED) && v1 == 0xff) 483 value = -1; 484 else 485 value = v1; 486 break; 487 case 2: 488 memcpy(&v2, walker + elt->pe_offset, sizeof(v2)); 489 if ((elt->pe_kind & TYPE_FLAGGED) && v2 == 0xffff) 490 value = -1; 491 else 492 value = v2; 493 break; 494 case 4: 495 memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); 496 if ((elt->pe_kind & TYPE_FLAGGED) && v4 == 0xffffffff) 497 value = -1; 498 else 499 value = v4; 500 break; 501 default: 502 errx(1, "Invalid size somehow %#x", elt->pe_kind); 503 } 504 if (verbose > 1) 505 printf("I:%#x;", value); 506 record_int(value); 507 } else if (elt->pe_kind == TYPE_T) { 508 /* Do nothing */ 509 } else { /* E, Z or D -- P already filtered */ 510 if (elt->pe_kind == TYPE_E) { 511 memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); 512 strcpy(buffer, pnp_eisaformat(v4)); 513 } else { 514 char *ptr; 515 516 ptr = *(char **)(walker + elt->pe_offset); 517 buffer[0] = '\0'; 518 if (ptr != NULL) { 519 EF_SEG_READ_STRING(ef, (Elf_Off)ptr, 520 sizeof(buffer), buffer); 521 buffer[sizeof(buffer) - 1] = '\0'; 522 } 523 } 524 if (verbose > 1) 525 printf("%c:%s;", elt->pe_kind == TYPE_E ? 'E' : (elt->pe_kind == TYPE_Z ? 'Z' : 'D'), buffer); 526 record_string(buffer); 527 } 528 } 529 if (verbose > 1) 530 printf("\n"); 531 walker += pnp.entry_len; 532 } 533 /* Now free it */ 534 TAILQ_FOREACH_SAFE(elt, &list, next, elt_tmp) { 535 TAILQ_REMOVE(&list, elt, next); 536 free(elt); 537 } 538 free(table); 539 } 540 break; 541 default: 542 warnx("unknown metadata record %d in file %s", md->md_type, kldname); 543 } 544 if (!error) 545 record_end(); 546 return (error); 547 } 548 549 static int 550 read_kld(char *filename, char *kldname) 551 { 552 struct mod_metadata md; 553 struct elf_file ef; 554 void **p; 555 int error, eftype; 556 long start, finish, entries, i; 557 char cval[MAXMODNAME + 1]; 558 559 if (verbose || dflag) 560 printf("%s\n", filename); 561 error = ef_open(filename, &ef, verbose); 562 if (error != 0) { 563 error = ef_obj_open(filename, &ef, verbose); 564 if (error != 0) { 565 if (verbose) 566 warnc(error, "elf_open(%s)", filename); 567 return (error); 568 } 569 } 570 eftype = EF_GET_TYPE(&ef); 571 if (eftype != EFT_KLD && eftype != EFT_KERNEL) { 572 EF_CLOSE(&ef); 573 return (0); 574 } 575 do { 576 check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, 577 &entries)); 578 check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, 579 (void *)&p)); 580 /* 581 * Do a first pass to find MDT_MODULE. It is required to be 582 * ordered first in the output linker.hints stream because it 583 * serves as an implicit record boundary between distinct klds 584 * in the stream. Other MDTs only make sense in the context of 585 * a specific MDT_MODULE. 586 * 587 * Some compilers (e.g., GCC 6.4.0 xtoolchain) or binutils 588 * (e.g., GNU binutils 2.32 objcopy/ld.bfd) can reorder 589 * MODULE_METADATA set entries relative to the source ordering. 590 * This is permitted by the C standard; memory layout of 591 * file-scope objects is left implementation-defined. There is 592 * no requirement that source code ordering is retained. 593 * 594 * Handle that here by taking two passes to ensure MDT_MODULE 595 * records are emitted to linker.hints before other MDT records 596 * in the same kld. 597 */ 598 for (i = 0; i < entries; i++) { 599 check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md), 600 &md)); 601 check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval, 602 sizeof(cval), cval)); 603 if (md.md_type == MDT_MODULE) { 604 parse_entry(&md, cval, &ef, kldname); 605 break; 606 } 607 } 608 if (error != 0) { 609 warnc(error, "error while reading %s", filename); 610 break; 611 } 612 613 /* 614 * Second pass for all !MDT_MODULE entries. 615 */ 616 for (i = 0; i < entries; i++) { 617 check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md), 618 &md)); 619 check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval, 620 sizeof(cval), cval)); 621 if (md.md_type != MDT_MODULE) 622 parse_entry(&md, cval, &ef, kldname); 623 } 624 if (error != 0) 625 warnc(error, "error while reading %s", filename); 626 free(p); 627 } while(0); 628 EF_CLOSE(&ef); 629 return (error); 630 } 631 632 /* 633 * Create a temp file in directory root, make sure we don't 634 * overflow the buffer for the destination name 635 */ 636 static FILE * 637 maketempfile(char *dest, const char *root) 638 { 639 char *p; 640 int n, fd; 641 642 p = strrchr(root, '/'); 643 n = p != NULL ? p - root + 1 : 0; 644 if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= 645 MAXPATHLEN) { 646 errno = ENAMETOOLONG; 647 return (NULL); 648 } 649 650 fd = mkstemp(dest); 651 if (fd < 0) 652 return (NULL); 653 fchmod(fd, 0644); /* nothing secret in the file */ 654 return (fdopen(fd, "w+")); 655 } 656 657 static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; 658 659 static void 660 usage(void) 661 { 662 663 fprintf(stderr, "%s\n", 664 "usage: kldxref [-Rdv] [-f hintsfile] path ..." 665 ); 666 exit(1); 667 } 668 669 static int 670 compare(const FTSENT *const *a, const FTSENT *const *b) 671 { 672 673 if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D) 674 return (1); 675 if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D) 676 return (-1); 677 return (strcmp((*a)->fts_name, (*b)->fts_name)); 678 } 679 680 int 681 main(int argc, char *argv[]) 682 { 683 FTS *ftsp; 684 FTSENT *p; 685 char *dot = NULL; 686 int opt, fts_options, ival; 687 struct stat sb; 688 689 fts_options = FTS_PHYSICAL; 690 691 while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { 692 switch (opt) { 693 case 'd': /* no hint file, only print on stdout */ 694 dflag = true; 695 break; 696 case 'f': /* use this name instead of linker.hints */ 697 xref_file = optarg; 698 break; 699 case 'v': 700 verbose++; 701 break; 702 case 'R': /* recurse on directories */ 703 fts_options |= FTS_COMFOLLOW; 704 break; 705 default: 706 usage(); 707 /* NOTREACHED */ 708 } 709 } 710 if (argc - optind < 1) 711 usage(); 712 argc -= optind; 713 argv += optind; 714 715 if (stat(argv[0], &sb) != 0) 716 err(1, "%s", argv[0]); 717 if ((sb.st_mode & S_IFDIR) == 0 && !dflag) { 718 errno = ENOTDIR; 719 err(1, "%s", argv[0]); 720 } 721 722 ftsp = fts_open(argv, fts_options, compare); 723 if (ftsp == NULL) 724 exit(1); 725 726 for (;;) { 727 p = fts_read(ftsp); 728 if ((p == NULL || p->fts_info == FTS_D) && fxref) { 729 /* close and rename the current hint file */ 730 fclose(fxref); 731 fxref = NULL; 732 if (reccnt != 0) { 733 rename(tempname, xrefname); 734 } else { 735 /* didn't find any entry, ignore this file */ 736 unlink(tempname); 737 unlink(xrefname); 738 } 739 } 740 if (p == NULL) 741 break; 742 if (p->fts_info == FTS_D && !dflag) { 743 /* visiting a new directory, create a new hint file */ 744 snprintf(xrefname, sizeof(xrefname), "%s/%s", 745 ftsp->fts_path, xref_file); 746 fxref = maketempfile(tempname, ftsp->fts_path); 747 if (fxref == NULL) 748 err(1, "can't create %s", tempname); 749 ival = 1; 750 fwrite(&ival, sizeof(ival), 1, fxref); 751 reccnt = 0; 752 } 753 /* skip non-files.. */ 754 if (p->fts_info != FTS_F) 755 continue; 756 /* 757 * Skip files that generate errors like .debug, .symbol and .pkgsave 758 * by generally skipping all files with 2 dots. 759 */ 760 dot = strchr(p->fts_name, '.'); 761 if (dot && strchr(dot + 1, '.') != NULL) 762 continue; 763 read_kld(p->fts_path, p->fts_name); 764 } 765 fts_close(ftsp); 766 return (0); 767 } 768