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