1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Tool for dumping lint libraries. 29 */ 30 31 #include <ctype.h> 32 #include <errno.h> 33 #include <stdarg.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <sys/types.h> 38 39 #include "lnstuff.h" /* silly header name from alint */ 40 41 typedef struct lsu { 42 const char *name; 43 ATYPE atype; 44 struct lsu *next; 45 } lsu_t; 46 47 #define LSU_HASHSIZE 512 48 static lsu_t *lsu_table[LSU_HASHSIZE]; 49 50 static boolean_t showids = B_TRUE; 51 static boolean_t justrelpaths = B_FALSE; 52 static int justpass = -1; 53 static int indentlevel = 9; 54 static const char *progname; 55 56 static void info(const char *, ...); 57 static void infohdr(const char *, const char *, ...); 58 static void warn(const char *, ...); 59 static void die(const char *, ...); 60 static void usage(void); 61 static void indent(void); 62 static void unindent(void); 63 static void print_lintmod(const char *, FILE *, FLENS *); 64 static void print_pass(const char *, FILE *); 65 static void print_atype(ATYPE *, int, ATYPE *, const char *); 66 static void print_mods(const char *, ATYPE *, int, ATYPE *, uint_t); 67 static void getstr(FILE *, char *, size_t); 68 static void lsu_build(FILE *); 69 static void lsu_empty(void); 70 static int lsu_add(const char *, ATYPE *); 71 static lsu_t *lsu_lookup(unsigned long); 72 73 int 74 main(int argc, char **argv) 75 { 76 int i, c, mod; 77 FILE *fp; 78 FLENS hdr; 79 const char *lnname; 80 81 progname = strrchr(argv[0], '/'); 82 if (progname == NULL) 83 progname = argv[0]; 84 else 85 progname++; 86 87 while ((c = getopt(argc, argv, "ip:r")) != EOF) { 88 switch (c) { 89 case 'i': 90 showids = B_FALSE; 91 break; 92 case 'p': 93 justpass = strtoul(optarg, NULL, 0); 94 if (justpass < 1 || justpass > 3) 95 usage(); 96 break; 97 case 'r': 98 justrelpaths = B_TRUE; 99 break; 100 default: 101 usage(); 102 } 103 } 104 105 if (optind == argc) 106 usage(); 107 108 for (i = optind; i < argc; i++) { 109 fp = fopen(argv[i], "r"); 110 if (fp == NULL) { 111 warn("cannot open \"%s\"", argv[i]); 112 continue; 113 } 114 115 lnname = argv[i]; 116 if (justrelpaths && lnname[0] == '/') 117 lnname = strrchr(lnname, '/') + 1; 118 119 /* 120 * Dump out all of the modules in the lint object. 121 */ 122 for (mod = 1; fread(&hdr, sizeof (hdr), 1, fp) == 1; mod++) { 123 if (hdr.ver != LINTVER) { 124 warn("%s: unsupported lint object version " 125 "%d\n", argv[i], hdr.ver); 126 break; 127 } 128 129 if (mod == 1) 130 infohdr("LINTOBJ", "%s\n", lnname); 131 132 /* 133 * First build the table of structure/union names, 134 * then print the lint module. Finally, empty the 135 * table out before dumping the next module. 136 */ 137 lsu_build(fp); 138 print_lintmod(lnname, fp, &hdr); 139 lsu_empty(); 140 } 141 (void) fclose(fp); 142 } 143 144 return (EXIT_SUCCESS); 145 } 146 147 /* 148 * Print a lint module and advance past it in the stream. 149 */ 150 static void 151 print_lintmod(const char *lnname, FILE *fp, FLENS *hp) 152 { 153 ulong_t psizes[5]; 154 uint_t pass; 155 156 psizes[0] = 0; 157 psizes[1] = hp->f1; 158 psizes[2] = hp->f2; 159 psizes[3] = hp->f3; 160 psizes[4] = hp->f4; 161 162 infohdr("LINTMOD", "%hu: %lu+%lu+%lu+%lu = %lu bytes\n", hp->mno, 163 hp->f1, hp->f2, hp->f3, hp->f4, hp->f1 + hp->f2 + hp->f3 + hp->f4); 164 165 for (pass = 1; pass <= 4; pass++) { 166 if ((justpass < 0 || justpass == pass) && pass < 4) { 167 infohdr("SECTION", "PASS%u: %lu bytes\n", pass, 168 psizes[pass]); 169 print_pass(lnname, fp); 170 } else { 171 (void) fseek(fp, psizes[pass], SEEK_CUR); 172 } 173 } 174 } 175 176 /* 177 * Print out a PASS section of a lint module. 178 */ 179 static void 180 print_pass(const char *lnname, FILE *fp) 181 { 182 union rec rec; 183 int nargs; 184 char name[1024]; 185 ATYPE atype, *args; 186 LINE line; 187 boolean_t wasfile = B_FALSE; 188 189 for (;;) { 190 if (fread(&rec, sizeof (rec), 1, fp) != 1) 191 die("%s: unexpected end of file\n", lnname); 192 193 line = rec.l; 194 if (line.decflag & LND) /* end-of-pass marker */ 195 break; 196 197 getstr(fp, name, sizeof (name)); 198 199 /* 200 * Check if this is a file record. 201 */ 202 if (line.decflag & LFN) { 203 if (wasfile || !justrelpaths) 204 infohdr("FILE", "%s\n", name); 205 wasfile = B_TRUE; 206 continue; 207 } 208 wasfile = B_FALSE; 209 210 /* 211 * Check if this is a function or variable record. 212 */ 213 nargs = line.nargs; 214 if (line.decflag & (LIB|LDS|LDI|LPR|LDX|LDC|LRV|LUE|LUV|LUM)) { 215 if (nargs < 0) 216 nargs = -nargs - 1; 217 218 if (line.decflag & LDS) 219 info("static "); 220 else if (line.decflag & (LPR|LDX|LDC)) 221 info("extern "); 222 223 args = calloc(sizeof (atype), nargs); 224 if (args == NULL) 225 die("cannot allocate argument information"); 226 227 if (fread(args, sizeof (atype), nargs, fp) != nargs) 228 die("%s: unexpected end of file\n", lnname); 229 230 print_atype(&line.type, line.nargs, args, name); 231 free(args); 232 233 if (line.decflag & LRV) 234 info(" <returns value>"); 235 if (line.decflag & LUE) 236 info(" <use: side-effects context>"); 237 if (line.decflag & LUV) 238 info(" <use: return value context>"); 239 if (line.decflag & LUM) 240 info(" <use: unspecified context>"); 241 242 if (line.decflag & LPF) 243 info(" <PRINTFLIKE%d>", nargs); 244 else if (line.decflag & LSF) 245 info(" <SCANFLIKE%d>", nargs); 246 247 if (line.decflag & LDI) 248 info(" { <definition> }"); 249 else if (line.decflag & LDX) 250 info(" = <definition>"); 251 252 info(";\n"); 253 continue; 254 } 255 256 /* 257 * Check if this is a structure or union record. 258 */ 259 if (line.decflag & LSU) { 260 if (line.decflag & ~(LSU)) 261 info("??? "); 262 263 info("struct "); 264 if (name[0] != '.') 265 info("%s ", name); 266 if (showids) 267 info("<tag %lu> ", line.type.extra.ty); 268 info("{ \n"); 269 270 indent(); 271 for (; nargs > 0; nargs--) { 272 if (fread(&atype, sizeof (atype), 1, fp) != 1) { 273 die("%s: unexpected end of file\n", 274 lnname); 275 } 276 getstr(fp, name, sizeof (name)); 277 print_atype(&atype, 0, NULL, name); 278 info(";\n"); 279 } 280 unindent(); 281 info("};\n"); 282 continue; 283 } 284 285 warn("%s: unknown record type 0%o\n", lnname, line.decflag); 286 } 287 } 288 289 /* 290 * Print the C datatype or function `atp' named `name'. If `name' is a 291 * function, then `nargs' indicates the number of C datatypes pointed to 292 * by `args'. 293 */ 294 static void 295 print_atype(ATYPE *atp, int nargs, ATYPE *args, const char *name) 296 { 297 static const char *basetypes[] = { "", 298 "char", "unsigned char", "signed char", 299 "short", "unsigned short", "signed short", 300 "int", "unsigned int", "signed int", 301 "long", "unsigned long", "signed long", 302 "long long", "unsigned long long", "signed long long", 303 "enum", "float", "double", 304 "long double", "void", "struct", 305 "union", "_Bool", "<genchar>", 306 "<genshort>", "<genint>", "<genlong>", 307 "<genlonglong>" 308 }; 309 uint16_t basetype = atp->aty & LNQUAL; 310 lsu_t *lsup; 311 312 if (atp->aty & LCON) 313 info("const "); 314 if (atp->aty & LVOL) 315 info("volatile "); 316 if (atp->aty & LCONV) 317 info("integer const "); 318 319 if (basetype < 1 || 320 basetype > (sizeof (basetypes) / sizeof (*basetypes))) 321 info("<unknown type %x>", basetype); 322 323 switch (basetype) { 324 case LN_UNION: 325 case LN_STRUCT: 326 lsup = lsu_lookup(atp->extra.ty); 327 if (lsup != NULL && lsup->name[0] != '.') { 328 info("%s %s", basetypes[basetype], lsup->name); 329 } else { 330 info("%s", basetypes[basetype]); 331 if (showids) 332 info(" <tag %lu>", atp->extra.ty); 333 else 334 info(" <anon>"); 335 } 336 break; 337 default: 338 info("%s", basetypes[basetype]); 339 }; 340 341 print_mods(name, atp, nargs, args, 14); 342 } 343 344 /* 345 * Recursively print type modifiers. 346 */ 347 static void 348 print_mods(const char *name, ATYPE *atp, int nargs, ATYPE *args, uint_t pos) 349 { 350 int arg; 351 int mods = atp->dcl_mod >> (pos * 2); 352 int lastmods = atp->dcl_mod >> ((pos + 1) * 2); 353 boolean_t isvarargs = B_FALSE; 354 355 if (LN_ISPTR(mods)) { 356 if (!LN_ISPTR(lastmods) && !LN_ISFTN(lastmods)) 357 info(" "); 358 info("*"); 359 } 360 361 if (atp->dcl_con & (1 << pos)) 362 info(" const "); 363 if (atp->dcl_vol & (1 << pos)) 364 info(" volatile "); 365 366 if (pos != 0) { 367 if (LN_ISFTN(mods)) 368 info(" ("); 369 print_mods(name, atp, nargs, args, pos - 1); 370 if (LN_ISFTN(mods)) 371 info(")()"); 372 return; 373 } 374 375 if (name[0] == '\0') 376 return; 377 378 if (!LN_ISPTR(lastmods) && !LN_ISPTR(mods)) 379 info(" "); 380 info("%s", name); 381 382 if (LN_ISARY(mods)) { 383 info("[]"); 384 } else if (LN_ISFTN(mods)) { 385 info("("); 386 387 if (nargs < 0) { 388 nargs = -nargs - 1; 389 isvarargs = B_TRUE; 390 } 391 392 if (nargs == 0) { 393 info("void"); 394 } else { 395 for (arg = 0; arg < nargs; arg++) { 396 print_atype(&args[arg], 0, NULL, ""); 397 if ((arg + 1) < nargs) 398 info(", "); 399 else if (isvarargs) 400 info(", ..."); 401 } 402 } 403 info(")"); 404 } 405 } 406 407 /* 408 * Add an LSU entry to the LSU table. 409 */ 410 static int 411 lsu_add(const char *name, ATYPE *atp) 412 { 413 unsigned int i = atp->extra.ty % LSU_HASHSIZE; 414 lsu_t *lsup; 415 416 lsup = malloc(sizeof (lsu_t)); 417 if (lsup == NULL) 418 return (ENOMEM); 419 420 lsup->atype = *atp; 421 lsup->next = lsu_table[i]; 422 lsup->name = strdup(name); 423 if (lsup->name == NULL) { 424 free(lsup); 425 return (ENOMEM); 426 } 427 428 lsu_table[i] = lsup; 429 return (0); 430 } 431 432 /* 433 * Lookup an LSU entry by ID. 434 */ 435 static lsu_t * 436 lsu_lookup(T1WORD ty) 437 { 438 unsigned int i = ty % LSU_HASHSIZE; 439 lsu_t *lsup; 440 441 for (lsup = lsu_table[i]; lsup != NULL; lsup = lsup->next) { 442 if (lsup->atype.extra.ty == ty) 443 return (lsup); 444 } 445 446 return (NULL); 447 } 448 449 /* 450 * Read all LSU (structure and union definition) records in order to 451 * build a structure and union name table, called the LSU table. 452 * Although `fp' is read from, the original file offset is preserved. 453 */ 454 static void 455 lsu_build(FILE *fp) 456 { 457 union rec rec; 458 char name[1024]; 459 int nargs; 460 off_t curoff = ftello(fp); 461 462 for (;;) { 463 if (fread(&rec, sizeof (rec), 1, fp) != 1) 464 break; 465 466 if (rec.l.decflag & LND) /* end-of-pass marker */ 467 break; 468 469 getstr(fp, name, sizeof (name)); 470 nargs = rec.l.nargs; 471 472 if (rec.l.decflag & (LIB|LDS|LDI)) { 473 if (nargs < 0) 474 nargs = -nargs - 1; 475 476 (void) fseek(fp, sizeof (ATYPE) * nargs, SEEK_CUR); 477 continue; 478 } 479 480 if (rec.l.decflag & LSU) { 481 if (lsu_add(name, &rec.l.type) != 0) 482 warn("cannot allocate struct `%s' info", name); 483 484 for (; nargs > 0; nargs--) { 485 (void) fseek(fp, sizeof (ATYPE), SEEK_CUR); 486 getstr(fp, name, sizeof (name)); 487 } 488 } 489 } 490 491 (void) fseek(fp, curoff, SEEK_SET); 492 } 493 494 /* 495 * Empty the LSU table. 496 */ 497 static void 498 lsu_empty(void) 499 { 500 lsu_t *lsup, *lsup_next; 501 unsigned int i; 502 503 for (i = 0; i < LSU_HASHSIZE; i++) { 504 for (lsup = lsu_table[i]; lsup != NULL; lsup = lsup_next) { 505 lsup_next = lsup->next; 506 free(lsup); 507 } 508 lsu_table[i] = NULL; 509 } 510 } 511 512 /* 513 * Read the NUL-terminated string at `fp' into `buf', which is at most 514 * `bufsize' bytes. 515 */ 516 static void 517 getstr(FILE *fp, char *buf, size_t bufsize) 518 { 519 int c; 520 size_t i; 521 522 for (i = 0; i < bufsize - 1; i++) { 523 c = fgetc(fp); 524 if (c == EOF || c == '\0' || !isascii(c)) 525 break; 526 buf[i] = (char)c; 527 } 528 529 buf[i] = '\0'; 530 } 531 532 static void 533 indent(void) 534 { 535 indentlevel += 4; 536 } 537 538 static void 539 unindent(void) 540 { 541 indentlevel -= 4; 542 } 543 544 static void 545 usage(void) 546 { 547 (void) fprintf(stderr, "usage: %s [-i] [-p 1|2|3] [-r] lintobj" 548 " [ lintobj ... ]\n", progname); 549 exit(EXIT_FAILURE); 550 } 551 552 /* PRINTFLIKE1 */ 553 static void 554 info(const char *format, ...) 555 { 556 va_list alist; 557 static int complete = 1; 558 559 if (complete) 560 (void) printf("%*s", indentlevel, ""); 561 562 va_start(alist, format); 563 (void) vprintf(format, alist); 564 va_end(alist); 565 566 complete = strrchr(format, '\n') != NULL; 567 } 568 569 /* PRINTFLIKE2 */ 570 static void 571 infohdr(const char *hdr, const char *format, ...) 572 { 573 va_list alist; 574 static int complete = 1; 575 576 if (complete) 577 (void) printf("%7s: ", hdr); 578 579 va_start(alist, format); 580 (void) vprintf(format, alist); 581 va_end(alist); 582 583 complete = strrchr(format, '\n') != NULL; 584 } 585 586 /* PRINTFLIKE1 */ 587 static void 588 warn(const char *format, ...) 589 { 590 va_list alist; 591 char *errstr = strerror(errno); 592 593 (void) fprintf(stderr, "%s: warning: ", progname); 594 595 va_start(alist, format); 596 (void) vfprintf(stderr, format, alist); 597 va_end(alist); 598 599 if (strrchr(format, '\n') == NULL) 600 (void) fprintf(stderr, ": %s\n", errstr); 601 } 602 603 /* PRINTFLIKE1 */ 604 static void 605 die(const char *format, ...) 606 { 607 va_list alist; 608 char *errstr = strerror(errno); 609 610 (void) fprintf(stderr, "%s: fatal: ", progname); 611 612 va_start(alist, format); 613 (void) vfprintf(stderr, format, alist); 614 va_end(alist); 615 616 if (strrchr(format, '\n') == NULL) 617 (void) fprintf(stderr, ": %s\n", errstr); 618 619 exit(EXIT_FAILURE); 620 } 621