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