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 TAILQ_INIT(list); 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 void 384 free_pnp_list(char *new_desc, pnp_list *list) 385 { 386 struct pnp_elt *elt, *elt_tmp; 387 388 TAILQ_FOREACH_SAFE(elt, list, next, elt_tmp) { 389 TAILQ_REMOVE(list, elt, next); 390 free(elt); 391 } 392 free(new_desc); 393 } 394 395 static void 396 parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker) 397 { 398 uint8_t v1; 399 uint16_t v2; 400 uint32_t v4; 401 int value; 402 char buffer[1024]; 403 404 if (elt->pe_kind == TYPE_W32) { 405 memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); 406 value = v4 & 0xffff; 407 record_int(value); 408 if (verbose > 1) 409 printf("W32:%#x", value); 410 value = (v4 >> 16) & 0xffff; 411 record_int(value); 412 if (verbose > 1) 413 printf(":%#x;", value); 414 } else if (elt->pe_kind & TYPE_INT) { 415 switch (elt->pe_kind & TYPE_SZ_MASK) { 416 case 1: 417 memcpy(&v1, walker + elt->pe_offset, sizeof(v1)); 418 if ((elt->pe_kind & TYPE_FLAGGED) && v1 == 0xff) 419 value = -1; 420 else 421 value = v1; 422 break; 423 case 2: 424 memcpy(&v2, walker + elt->pe_offset, sizeof(v2)); 425 if ((elt->pe_kind & TYPE_FLAGGED) && v2 == 0xffff) 426 value = -1; 427 else 428 value = v2; 429 break; 430 case 4: 431 memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); 432 if ((elt->pe_kind & TYPE_FLAGGED) && v4 == 0xffffffff) 433 value = -1; 434 else 435 value = v4; 436 break; 437 default: 438 errx(1, "Invalid size somehow %#x", elt->pe_kind); 439 } 440 if (verbose > 1) 441 printf("I:%#x;", value); 442 record_int(value); 443 } else if (elt->pe_kind == TYPE_T) { 444 /* Do nothing */ 445 } else { /* E, Z or D -- P already filtered */ 446 if (elt->pe_kind == TYPE_E) { 447 memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); 448 strcpy(buffer, pnp_eisaformat(v4)); 449 } else { 450 char *ptr; 451 452 ptr = *(char **)(walker + elt->pe_offset); 453 buffer[0] = '\0'; 454 if (ptr != NULL) { 455 EF_SEG_READ_STRING(ef, (Elf_Off)ptr, 456 sizeof(buffer), buffer); 457 buffer[sizeof(buffer) - 1] = '\0'; 458 } 459 } 460 if (verbose > 1) 461 printf("%c:%s;", elt->pe_kind == TYPE_E ? 'E' : 462 (elt->pe_kind == TYPE_Z ? 'Z' : 'D'), buffer); 463 record_string(buffer); 464 } 465 } 466 467 static void 468 record_pnp_info(struct elf_file *ef, const char *cval, 469 struct mod_pnp_match_info *pnp, const char *descr) 470 { 471 pnp_list list; 472 struct pnp_elt *elt; 473 char *new_descr, *walker; 474 void *table; 475 size_t len; 476 int error, i; 477 478 if (verbose > 1) 479 printf(" pnp info for bus %s format %s %d entries of %d bytes\n", 480 cval, descr, pnp->num_entry, pnp->entry_len); 481 482 /* 483 * Parse descr to weed out the chaff and to create a list 484 * of offsets to output. 485 */ 486 parse_pnp_list(descr, &new_descr, &list); 487 record_int(MDT_PNP_INFO); 488 record_string(cval); 489 record_string(new_descr); 490 record_int(pnp->num_entry); 491 len = pnp->num_entry * pnp->entry_len; 492 table = malloc(len); 493 error = EF_SEG_READ_REL(ef, (Elf_Off)pnp->table, len, table); 494 if (error != 0) { 495 free_pnp_list(new_descr, &list); 496 free(table); 497 return; 498 } 499 500 /* 501 * Walk the list and output things. We've collapsed all the 502 * variant forms of the table down to just ints and strings. 503 */ 504 walker = table; 505 for (i = 0; i < pnp->num_entry; i++) { 506 TAILQ_FOREACH(elt, &list, next) { 507 parse_pnp_entry(ef, elt, walker); 508 } 509 if (verbose > 1) 510 printf("\n"); 511 walker += pnp->entry_len; 512 } 513 514 /* Now free it */ 515 free_pnp_list(new_descr, &list); 516 free(table); 517 } 518 519 static int 520 parse_entry(struct mod_metadata *md, const char *cval, 521 struct elf_file *ef, const char *kldname) 522 { 523 struct mod_depend mdp; 524 struct mod_version mdv; 525 struct mod_pnp_match_info pnp; 526 char descr[1024]; 527 Elf_Off data; 528 int error; 529 530 data = (Elf_Off)md->md_data; 531 error = 0; 532 record_start(); 533 switch (md->md_type) { 534 case MDT_DEPEND: 535 if (!dflag) 536 break; 537 check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); 538 printf(" depends on %s.%d (%d,%d)\n", cval, 539 mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); 540 break; 541 case MDT_VERSION: 542 check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); 543 if (dflag) { 544 printf(" interface %s.%d\n", cval, mdv.mv_version); 545 } else { 546 record_int(MDT_VERSION); 547 record_string(cval); 548 record_int(mdv.mv_version); 549 record_string(kldname); 550 } 551 break; 552 case MDT_MODULE: 553 if (dflag) { 554 printf(" module %s\n", cval); 555 } else { 556 record_int(MDT_MODULE); 557 record_string(cval); 558 record_string(kldname); 559 } 560 break; 561 case MDT_PNP_INFO: 562 check(EF_SEG_READ_REL(ef, data, sizeof(pnp), &pnp)); 563 check(EF_SEG_READ_STRING(ef, (Elf_Off)pnp.descr, sizeof(descr), descr)); 564 descr[sizeof(descr) - 1] = '\0'; 565 if (dflag) { 566 printf(" pnp info for bus %s format %s %d entries of %d bytes\n", 567 cval, descr, pnp.num_entry, pnp.entry_len); 568 } else { 569 record_pnp_info(ef, cval, &pnp, descr); 570 } 571 break; 572 default: 573 warnx("unknown metadata record %d in file %s", md->md_type, kldname); 574 } 575 if (!error) 576 record_end(); 577 return (error); 578 } 579 580 static int 581 read_kld(char *filename, char *kldname) 582 { 583 struct mod_metadata md; 584 struct elf_file ef; 585 void **p; 586 int error, eftype; 587 long start, finish, entries, i; 588 char cval[MAXMODNAME + 1]; 589 590 if (verbose || dflag) 591 printf("%s\n", filename); 592 error = ef_open(filename, &ef, verbose); 593 if (error != 0) { 594 error = ef_obj_open(filename, &ef, verbose); 595 if (error != 0) { 596 if (verbose) 597 warnc(error, "elf_open(%s)", filename); 598 return (error); 599 } 600 } 601 eftype = EF_GET_TYPE(&ef); 602 if (eftype != EFT_KLD && eftype != EFT_KERNEL) { 603 EF_CLOSE(&ef); 604 return (0); 605 } 606 do { 607 check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, 608 &entries)); 609 check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, 610 (void *)&p)); 611 /* 612 * Do a first pass to find MDT_MODULE. It is required to be 613 * ordered first in the output linker.hints stream because it 614 * serves as an implicit record boundary between distinct klds 615 * in the stream. Other MDTs only make sense in the context of 616 * a specific MDT_MODULE. 617 * 618 * Some compilers (e.g., GCC 6.4.0 xtoolchain) or binutils 619 * (e.g., GNU binutils 2.32 objcopy/ld.bfd) can reorder 620 * MODULE_METADATA set entries relative to the source ordering. 621 * This is permitted by the C standard; memory layout of 622 * file-scope objects is left implementation-defined. There is 623 * no requirement that source code ordering is retained. 624 * 625 * Handle that here by taking two passes to ensure MDT_MODULE 626 * records are emitted to linker.hints before other MDT records 627 * in the same kld. 628 */ 629 for (i = 0; i < entries; i++) { 630 check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md), 631 &md)); 632 check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval, 633 sizeof(cval), cval)); 634 if (md.md_type == MDT_MODULE) { 635 parse_entry(&md, cval, &ef, kldname); 636 break; 637 } 638 } 639 if (error != 0) { 640 warnc(error, "error while reading %s", filename); 641 break; 642 } 643 644 /* 645 * Second pass for all !MDT_MODULE entries. 646 */ 647 for (i = 0; i < entries; i++) { 648 check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md), 649 &md)); 650 check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval, 651 sizeof(cval), cval)); 652 if (md.md_type != MDT_MODULE) 653 parse_entry(&md, cval, &ef, kldname); 654 } 655 if (error != 0) 656 warnc(error, "error while reading %s", filename); 657 free(p); 658 } while(0); 659 EF_CLOSE(&ef); 660 return (error); 661 } 662 663 /* 664 * Create a temp file in directory root, make sure we don't 665 * overflow the buffer for the destination name 666 */ 667 static FILE * 668 maketempfile(char *dest, const char *root) 669 { 670 char *p; 671 int n, fd; 672 673 p = strrchr(root, '/'); 674 n = p != NULL ? p - root + 1 : 0; 675 if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= 676 MAXPATHLEN) { 677 errno = ENAMETOOLONG; 678 return (NULL); 679 } 680 681 fd = mkstemp(dest); 682 if (fd < 0) 683 return (NULL); 684 fchmod(fd, 0644); /* nothing secret in the file */ 685 return (fdopen(fd, "w+")); 686 } 687 688 static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; 689 690 static void 691 usage(void) 692 { 693 694 fprintf(stderr, "%s\n", 695 "usage: kldxref [-Rdv] [-f hintsfile] path ..." 696 ); 697 exit(1); 698 } 699 700 static int 701 compare(const FTSENT *const *a, const FTSENT *const *b) 702 { 703 704 if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D) 705 return (1); 706 if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D) 707 return (-1); 708 return (strcmp((*a)->fts_name, (*b)->fts_name)); 709 } 710 711 int 712 main(int argc, char *argv[]) 713 { 714 FTS *ftsp; 715 FTSENT *p; 716 char *dot = NULL; 717 int opt, fts_options, ival; 718 struct stat sb; 719 720 fts_options = FTS_PHYSICAL; 721 722 while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { 723 switch (opt) { 724 case 'd': /* no hint file, only print on stdout */ 725 dflag = true; 726 break; 727 case 'f': /* use this name instead of linker.hints */ 728 xref_file = optarg; 729 break; 730 case 'v': 731 verbose++; 732 break; 733 case 'R': /* recurse on directories */ 734 fts_options |= FTS_COMFOLLOW; 735 break; 736 default: 737 usage(); 738 /* NOTREACHED */ 739 } 740 } 741 if (argc - optind < 1) 742 usage(); 743 argc -= optind; 744 argv += optind; 745 746 if (stat(argv[0], &sb) != 0) 747 err(1, "%s", argv[0]); 748 if ((sb.st_mode & S_IFDIR) == 0 && !dflag) { 749 errno = ENOTDIR; 750 err(1, "%s", argv[0]); 751 } 752 753 ftsp = fts_open(argv, fts_options, compare); 754 if (ftsp == NULL) 755 exit(1); 756 757 for (;;) { 758 p = fts_read(ftsp); 759 if ((p == NULL || p->fts_info == FTS_D) && fxref) { 760 /* close and rename the current hint file */ 761 fclose(fxref); 762 fxref = NULL; 763 if (reccnt != 0) { 764 rename(tempname, xrefname); 765 } else { 766 /* didn't find any entry, ignore this file */ 767 unlink(tempname); 768 unlink(xrefname); 769 } 770 } 771 if (p == NULL) 772 break; 773 if (p->fts_info == FTS_D && !dflag) { 774 /* visiting a new directory, create a new hint file */ 775 snprintf(xrefname, sizeof(xrefname), "%s/%s", 776 ftsp->fts_path, xref_file); 777 fxref = maketempfile(tempname, ftsp->fts_path); 778 if (fxref == NULL) 779 err(1, "can't create %s", tempname); 780 ival = 1; 781 fwrite(&ival, sizeof(ival), 1, fxref); 782 reccnt = 0; 783 } 784 /* skip non-files.. */ 785 if (p->fts_info != FTS_F) 786 continue; 787 /* 788 * Skip files that generate errors like .debug, .symbol and .pkgsave 789 * by generally skipping all files with 2 dots. 790 */ 791 dot = strchr(p->fts_name, '.'); 792 if (dot && strchr(dot + 1, '.') != NULL) 793 continue; 794 read_kld(p->fts_path, p->fts_name); 795 } 796 fts_close(ftsp); 797 return (0); 798 } 799