xref: /illumos-gate/usr/src/tools/lintdump/lintdump.c (revision fa5e8906)
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
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
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
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
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
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
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 *
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
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
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
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
536 indent(void)
537 {
538 	indentlevel += 4;
539 }
540 
541 static void
542 unindent(void)
543 {
544 	indentlevel -= 4;
545 }
546 
547 static 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
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
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
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
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