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
main(int argc,char ** argv)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
print_lintmod(const char * lnname,FILE * fp,FLENS * hp)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
print_pass(const char * lnname,FILE * fp)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
print_atype(ATYPE * atp,int nargs,ATYPE * args,const char * name)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
print_mods(const char * name,ATYPE * atp,int nargs,ATYPE * args,uint_t pos)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
lsu_add(const char * name,ATYPE * atp)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 *
lsu_lookup(T1WORD ty)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
lsu_build(FILE * fp)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
lsu_empty(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
getstr(FILE * fp,char * buf,size_t bufsize)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
indent(void)533 indent(void)
534 {
535 indentlevel += 4;
536 }
537
538 static void
unindent(void)539 unindent(void)
540 {
541 indentlevel -= 4;
542 }
543
544 static void
usage(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
info(const char * format,...)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
infohdr(const char * hdr,const char * format,...)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
warn(const char * format,...)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
die(const char * format,...)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