xref: /illumos-gate/usr/src/tools/lintdump/lintdump.c (revision dd72704bd9e794056c558153663c739e2012d721)
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
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
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
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
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
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  * Although `fp' is read from, the original file offset is preserved.
453  */
454 static void
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
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
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
533 indent(void)
534 {
535 	indentlevel += 4;
536 }
537 
538 static void
539 unindent(void)
540 {
541 	indentlevel -= 4;
542 }
543 
544 static 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
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
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
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
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