xref: /illumos-gate/usr/src/tools/lintdump/lintdump.c (revision b4034a75)
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 2006 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 %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(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  */
453 static void
454 lsu_build(FILE *fp)
455 {
456 	union rec	rec;
457 	char		name[1024];
458 	int		nargs;
459 
460 	for (;;) {
461 		if (fread(&rec, sizeof (rec), 1, fp) != 1)
462 			return;
463 
464 		if (rec.l.decflag & LND)	/* end-of-pass marker */
465 			break;
466 
467 		getstr(fp, name, sizeof (name));
468 		nargs = rec.l.nargs;
469 
470 		if (rec.l.decflag & (LIB|LDS|LDI)) {
471 			if (nargs < 0)
472 				nargs = -nargs - 1;
473 
474 			(void) fseek(fp, sizeof (ATYPE) * nargs, SEEK_CUR);
475 			continue;
476 		}
477 
478 		if (rec.l.decflag & LSU) {
479 			if (lsu_add(name, &rec.l.type) != 0)
480 				warn("cannot allocate struct `%s' info", name);
481 
482 			for (; nargs > 0; nargs--) {
483 				(void) fseek(fp, sizeof (ATYPE), SEEK_CUR);
484 				getstr(fp, name, sizeof (name));
485 			}
486 		}
487 	}
488 }
489 
490 /*
491  * Empty the LSU table.
492  */
493 static void
494 lsu_empty(void)
495 {
496 	lsu_t		*lsup, *lsup_next;
497 	unsigned int	i;
498 
499 	for (i = 0; i < LSU_HASHSIZE; i++) {
500 		for (lsup = lsu_table[i]; lsup != NULL; lsup = lsup_next) {
501 			lsup_next = lsup->next;
502 			free(lsup);
503 		}
504 		lsu_table[i] = NULL;
505 	}
506 }
507 
508 /*
509  * Read the NUL-terminated string at `fp' into `buf', which is at most
510  * `bufsize' bytes.
511  */
512 static void
513 getstr(FILE *fp, char *buf, size_t bufsize)
514 {
515 	int c;
516 	size_t i;
517 
518 	for (i = 0; i < bufsize - 1; i++) {
519 		c = fgetc(fp);
520 		if (c == EOF || c == '\0' || !isascii(c))
521 			break;
522 		buf[i] = (char)c;
523 	}
524 
525 	buf[i] = '\0';
526 }
527 
528 static void
529 indent(void)
530 {
531 	indentlevel += 4;
532 }
533 
534 static void
535 unindent(void)
536 {
537 	indentlevel -= 4;
538 }
539 
540 static void
541 usage(void)
542 {
543 	(void) fprintf(stderr, "usage: %s [-i] [-p 1|2|3] [-r] lintlib"
544 	    " [ lintlib ... ]\n", progname);
545 	exit(EXIT_FAILURE);
546 }
547 
548 /* PRINTFLIKE1 */
549 static void
550 info(const char *format, ...)
551 {
552 	va_list alist;
553 	static int complete = 1;
554 
555 	if (complete)
556 		(void) printf("%*s", indentlevel, "");
557 
558 	va_start(alist, format);
559 	(void) vprintf(format, alist);
560 	va_end(alist);
561 
562 	complete = strrchr(format, '\n') != NULL;
563 }
564 
565 /* PRINTFLIKE2 */
566 static void
567 infohdr(const char *hdr, const char *format, ...)
568 {
569 	va_list alist;
570 	static int complete = 1;
571 
572 	if (complete)
573 		(void) printf("%7s: ", hdr);
574 
575 	va_start(alist, format);
576 	(void) vprintf(format, alist);
577 	va_end(alist);
578 
579 	complete = strrchr(format, '\n') != NULL;
580 }
581 
582 /* PRINTFLIKE1 */
583 static void
584 warn(const char *format, ...)
585 {
586 	va_list alist;
587 	char *errstr = strerror(errno);
588 
589 	(void) fprintf(stderr, "%s: warning: ", progname);
590 
591 	va_start(alist, format);
592 	(void) vfprintf(stderr, format, alist);
593 	va_end(alist);
594 
595 	if (strrchr(format, '\n') == NULL)
596 		(void) fprintf(stderr, ": %s\n", errstr);
597 }
598 
599 /* PRINTFLIKE1 */
600 static void
601 die(const char *format, ...)
602 {
603 	va_list alist;
604 	char *errstr = strerror(errno);
605 
606 	(void) fprintf(stderr, "%s: fatal: ", progname);
607 
608 	va_start(alist, format);
609 	(void) vfprintf(stderr, format, alist);
610 	va_end(alist);
611 
612 	if (strrchr(format, '\n') == NULL)
613 		(void) fprintf(stderr, ": %s\n", errstr);
614 
615 	exit(EXIT_FAILURE);
616 }
617