xref: /illumos-gate/usr/src/tools/lintdump/lintdump.c (revision 9a016c63ca347047a236dff12f0da83aac8981d1)
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(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(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(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("unexpected end of data stream\n");
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("unexpected end of data stream\n");
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("unexpected end of data stream\n");
274 
275 				getstr(fp, name, sizeof (name));
276 				print_atype(&atype, 0, NULL, name);
277 				info(";\n");
278 			}
279 			unindent();
280 			info("};\n");
281 			continue;
282 		}
283 
284 		warn("unknown record type 0%o\n", line.decflag);
285 	}
286 }
287 
288 /*
289  * Print the C datatype or function `atp' named `name'.  If `name' is a
290  * function, then `nargs' indicates the number of C datatypes pointed to
291  * by `args'.
292  */
293 static void
294 print_atype(ATYPE *atp, int nargs, ATYPE *args, const char *name)
295 {
296 	static const char *basetypes[] = {		"",
297 		"char",		"unsigned char",	"signed char",
298 		"short",	"unsigned short",	"signed short",
299 		"int",		"unsigned int",		"signed int",
300 		"long",		"unsigned long",	"signed long",
301 		"long long",	"unsigned long long",	"signed long long",
302 		"enum",		"float",		"double",
303 		"long double",	"void",			"struct",
304 		"union",	"_Bool",		"<genchar>",
305 		"<genshort>",	"<genint>",		"<genlong>",
306 		"<genlonglong>"
307 	};
308 	uint16_t basetype = atp->aty & LNQUAL;
309 	lsu_t *lsup;
310 
311 	if (atp->aty & LCON)
312 		info("const ");
313 	if (atp->aty & LVOL)
314 		info("volatile ");
315 	if (atp->aty & LCONV)
316 		info("integer const ");
317 
318 	if (basetype < 1 ||
319 	    basetype > (sizeof (basetypes) / sizeof (*basetypes)))
320 		info("<unknown type %x>", basetype);
321 
322 	switch (basetype) {
323 	case LN_UNION:
324 	case LN_STRUCT:
325 		lsup = lsu_lookup(atp->extra.ty);
326 		if (lsup != NULL && lsup->name[0] != '.') {
327 			info("%s %s", basetypes[basetype], lsup->name);
328 		} else {
329 			info("%s", basetypes[basetype]);
330 			if (showids)
331 				info(" <tag %lu>", atp->extra.ty);
332 			else
333 				info(" <anon>");
334 		}
335 		break;
336 	default:
337 		info(basetypes[basetype]);
338 	};
339 
340 	print_mods(name, atp, nargs, args, 14);
341 }
342 
343 /*
344  * Recursively print type modifiers.
345  */
346 static void
347 print_mods(const char *name, ATYPE *atp, int nargs, ATYPE *args, uint_t pos)
348 {
349 	int arg;
350 	int mods = atp->dcl_mod >> (pos * 2);
351 	int lastmods = atp->dcl_mod >> ((pos + 1) * 2);
352 	boolean_t isvarargs = B_FALSE;
353 
354 	if (LN_ISPTR(mods)) {
355 		if (!LN_ISPTR(lastmods) && !LN_ISFTN(lastmods))
356 			info(" ");
357 		info("*");
358 	}
359 
360 	if (atp->dcl_con & (1 << pos))
361 		info(" const ");
362 	if (atp->dcl_vol & (1 << pos))
363 		info(" volatile ");
364 
365 	if (pos != 0) {
366 		if (LN_ISFTN(mods))
367 			info(" (");
368 		print_mods(name, atp, nargs, args, pos - 1);
369 		if (LN_ISFTN(mods))
370 			info(")()");
371 		return;
372 	}
373 
374 	if (name[0] == '\0')
375 		return;
376 
377 	if (!LN_ISPTR(lastmods) && !LN_ISPTR(mods))
378 		info(" ");
379 	info("%s", name);
380 
381 	if (LN_ISARY(mods)) {
382 		info("[]");
383 	} else if (LN_ISFTN(mods)) {
384 		info("(");
385 
386 		if (nargs < 0) {
387 			nargs = -nargs - 1;
388 			isvarargs = B_TRUE;
389 		}
390 
391 		if (nargs == 0) {
392 			info("void");
393 		} else {
394 			for (arg = 0; arg < nargs; arg++) {
395 				print_atype(&args[arg], 0, NULL, "");
396 				if ((arg + 1) < nargs)
397 					info(", ");
398 				else if (isvarargs)
399 					info(", ...");
400 			}
401 		}
402 		info(")");
403 	}
404 }
405 
406 /*
407  * Add an LSU entry to the LSU table.
408  */
409 static int
410 lsu_add(const char *name, ATYPE *atp)
411 {
412 	unsigned int	i = atp->extra.ty % LSU_HASHSIZE;
413 	lsu_t		*lsup;
414 
415 	lsup = malloc(sizeof (lsu_t));
416 	if (lsup == NULL)
417 		return (ENOMEM);
418 
419 	lsup->atype = *atp;
420 	lsup->next = lsu_table[i];
421 	lsup->name = strdup(name);
422 	if (lsup->name == NULL) {
423 		free(lsup);
424 		return (ENOMEM);
425 	}
426 
427 	lsu_table[i] = lsup;
428 	return (0);
429 }
430 
431 /*
432  * Lookup an LSU entry by ID.
433  */
434 static lsu_t *
435 lsu_lookup(T1WORD ty)
436 {
437 	unsigned int	i = ty % LSU_HASHSIZE;
438 	lsu_t		*lsup;
439 
440 	for (lsup = lsu_table[i]; lsup != NULL; lsup = lsup->next) {
441 		if (lsup->atype.extra.ty == ty)
442 			return (lsup);
443 	}
444 
445 	return (NULL);
446 }
447 
448 /*
449  * Read all LSU (structure and union definition) records in order to
450  * build a structure and union name table, called the LSU table.
451  */
452 static void
453 lsu_build(FILE *fp)
454 {
455 	union rec	rec;
456 	char		name[1024];
457 	int		nargs;
458 
459 	for (;;) {
460 		if (fread(&rec, sizeof (rec), 1, fp) != 1)
461 			return;
462 
463 		if (rec.l.decflag & LND)	/* end-of-pass marker */
464 			break;
465 
466 		getstr(fp, name, sizeof (name));
467 		nargs = rec.l.nargs;
468 
469 		if (rec.l.decflag & (LIB|LDS|LDI)) {
470 			if (nargs < 0)
471 				nargs = -nargs - 1;
472 
473 			(void) fseek(fp, sizeof (ATYPE) * nargs, SEEK_CUR);
474 			continue;
475 		}
476 
477 		if (rec.l.decflag & LSU) {
478 			if (lsu_add(name, &rec.l.type) != 0)
479 				warn("cannot allocate struct `%s' info", name);
480 
481 			for (; nargs > 0; nargs--) {
482 				(void) fseek(fp, sizeof (ATYPE), SEEK_CUR);
483 				getstr(fp, name, sizeof (name));
484 			}
485 		}
486 	}
487 }
488 
489 /*
490  * Empty the LSU table.
491  */
492 static void
493 lsu_empty(void)
494 {
495 	lsu_t		*lsup, *lsup_next;
496 	unsigned int	i;
497 
498 	for (i = 0; i < LSU_HASHSIZE; i++) {
499 		for (lsup = lsu_table[i]; lsup != NULL; lsup = lsup_next) {
500 			lsup_next = lsup->next;
501 			free(lsup);
502 		}
503 		lsu_table[i] = NULL;
504 	}
505 }
506 
507 /*
508  * Read the NUL-terminated string at `fp' into `buf', which is at most
509  * `bufsize' bytes.
510  */
511 static void
512 getstr(FILE *fp, char *buf, size_t bufsize)
513 {
514 	int c;
515 	size_t i;
516 
517 	for (i = 0; i < bufsize - 1; i++) {
518 		c = fgetc(fp);
519 		if (c == EOF || c == '\0' || !isascii(c))
520 			break;
521 		buf[i] = (char)c;
522 	}
523 
524 	buf[i] = '\0';
525 }
526 
527 static void
528 indent(void)
529 {
530 	indentlevel += 4;
531 }
532 
533 static void
534 unindent(void)
535 {
536 	indentlevel -= 4;
537 }
538 
539 static void
540 usage(void)
541 {
542 	(void) fprintf(stderr, "usage: %s [-i] [-p 1|2|3] [-r] lintlib"
543 	    " [ lintlib ... ]\n", progname);
544 	exit(EXIT_FAILURE);
545 }
546 
547 /* PRINTFLIKE1 */
548 static void
549 info(const char *format, ...)
550 {
551 	va_list alist;
552 	static int complete = 1;
553 
554 	if (complete)
555 		(void) printf("%*s", indentlevel, "");
556 
557 	va_start(alist, format);
558 	(void) vprintf(format, alist);
559 	va_end(alist);
560 
561 	complete = strrchr(format, '\n') != NULL;
562 }
563 
564 /* PRINTFLIKE2 */
565 static void
566 infohdr(const char *hdr, const char *format, ...)
567 {
568 	va_list alist;
569 	static int complete = 1;
570 
571 	if (complete)
572 		(void) printf("%7s: ", hdr);
573 
574 	va_start(alist, format);
575 	(void) vprintf(format, alist);
576 	va_end(alist);
577 
578 	complete = strrchr(format, '\n') != NULL;
579 }
580 
581 /* PRINTFLIKE1 */
582 static void
583 warn(const char *format, ...)
584 {
585 	va_list alist;
586 	char *errstr = strerror(errno);
587 
588 	(void) fprintf(stderr, "%s: warning: ", progname);
589 
590 	va_start(alist, format);
591 	(void) vfprintf(stderr, format, alist);
592 	va_end(alist);
593 
594 	if (strrchr(format, '\n') == NULL)
595 		(void) fprintf(stderr, ": %s\n", errstr);
596 }
597 
598 /* PRINTFLIKE1 */
599 static void
600 die(const char *format, ...)
601 {
602 	va_list alist;
603 	char *errstr = strerror(errno);
604 
605 	(void) fprintf(stderr, "%s: fatal: ", progname);
606 
607 	va_start(alist, format);
608 	(void) vfprintf(stderr, format, alist);
609 	va_end(alist);
610 
611 	if (strrchr(format, '\n') == NULL)
612 		(void) fprintf(stderr, ": %s\n", errstr);
613 
614 	exit(EXIT_FAILURE);
615 }
616