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