xref: /illumos-gate/usr/src/cmd/sgs/nm/common/nm.c (revision bfed486ad8de8b8ebc6345a8e10accae08bf2f45)
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 (c) 1988 AT&T
24  * Copyright (c) 1989 AT&T
25  * All Rights Reserved
26  *
27  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <ctype.h>
37 #include <locale.h>
38 #include <libelf.h>
39 #include <sys/elf_SPARC.h>
40 
41 
42 /* exit return codes */
43 #define	NOARGS	1
44 #define	BADELF	2
45 #define	NOALLOC 3
46 
47 #include <fcntl.h>
48 #include <sys/stat.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <dlfcn.h>
52 
53 #include "sgs.h"
54 #include "conv.h"
55 #include "gelf.h"
56 
57 typedef struct {		/* structure to translate symbol table data */
58 	int  indx;
59 	char *name;
60 	GElf_Addr value;
61 	GElf_Xword size;
62 	int type;
63 	int bind;
64 	unsigned char other;
65 	unsigned int shndx;
66 	unsigned int flags;	/* flags relevant to entry */
67 } SYM;
68 
69 #define	FLG_SYM_SPECSEC	0x00000001	/* reserved scn index */
70 					/*	(SHN_ABS, SHN_COMMON, ...) */
71 
72 #define	UNDEFINED "U"
73 #define	BSS_GLOB  "B"
74 #define	BSS_WEAK  "B*"
75 #define	BSS_LOCL  "b"
76 #define	BSS_SECN  ".bss"
77 #define	REG_GLOB  "R"
78 #define	REG_WEAK  "R*"
79 #define	REG_LOCL  "r"
80 
81 #define	OPTSTR	":APDoxhvnursplLCVefgRTt:" /* option string for getopt() */
82 
83 #define	DATESIZE 60
84 
85 #define	TYPE 7
86 #define	BIND 3
87 
88 #define	DEF_MAX_SYM_SIZE 256
89 
90 static char *key[TYPE][BIND];
91 
92 /*
93  * Format type used for printing value and size items.
94  * The non-negative values here are used as array indices into
95  * several arrays found below. Renumbering, or adding items,
96  * will require changes to those arrays as well.
97  */
98 typedef enum {
99 	FMT_T_NONE = -1,	/* No format type yet assigned */
100 
101 	/* The following are used as array indices */
102 	FMT_T_DEC = 0,
103 	FMT_T_HEX = 1,
104 	FMT_T_OCT = 2
105 } FMT_T;
106 
107 /*
108  * Determine whether a proposed format type is compatible with the current
109  * setting. We allow setting the format as long as it hasn't already
110  * been done, or if the new setting is the same as the current one.
111  */
112 #define	COMPAT_FMT_FLAG(new_fmt_flag) \
113 	(fmt_flag == FMT_T_NONE) || (fmt_flag == new_fmt_flag)
114 
115 static FMT_T fmt_flag = FMT_T_NONE;	/* format style to use for value/size */
116 
117 static  int	/* flags: ?_flag corresponds to ? option */
118 	h_flag = 0,	/* suppress printing of headings */
119 	v_flag = 0,	/* sort external symbols by value */
120 	n_flag = 0,	/* sort external symbols by name */
121 	u_flag = 0,	/* print only undefined symbols */
122 	r_flag = 0,	/* prepend object file or archive name */
123 			/* to each symbol name */
124 	R_flag = 0,	/* if "-R" issued then prepend archive name, */
125 			/* object file name to each symbol */
126 	s_flag = 0,	/* print section name instead of section index */
127 	p_flag = 0,	/* produce terse output */
128 	P_flag = 0,	/* Portable format output */
129 	l_flag = 0,	/* produce long listing of output */
130 	L_flag = 0,	/* print SUNW_LDYNSYM instead of SYMTAB */
131 	D_flag = 0,	/* print DYNSYM instead of SYMTAB */
132 	C_flag = 0,	/* print decoded C++ names */
133 	A_flag = 0,	/* File name */
134 	e_flag = 0,	/* -e flag */
135 	g_flag = 0,	/* -g flag */
136 	V_flag = 0;	/* print version information */
137 static char A_header[DEF_MAX_SYM_SIZE+1] = {0};
138 
139 static char *prog_name;
140 static char *archive_name = (char *)0;
141 static int errflag = 0;
142 static void usage();
143 static void each_file(char *);
144 static void process(Elf *, char *);
145 static Elf_Scn * get_scnfd(Elf *, int, int);
146 static void get_symtab(Elf *, char *);
147 static SYM * readsyms(Elf_Data *, GElf_Sxword, Elf *, unsigned int,
148 			unsigned int);
149 static int compare(SYM *, SYM *);
150 static char *lookup(int, int);
151 static int  is_bss_section(unsigned int, Elf *, unsigned int);
152 static void print_ar_files(int, Elf *, char *);
153 static void print_symtab(Elf *, unsigned int, Elf_Scn *, GElf_Shdr *, char *);
154 static void parsename(char *);
155 static void parse_fn_and_print(const char *, char *);
156 static char d_buf[512];
157 static char p_buf[512];
158 static int exotic(char *s);
159 static void set_A_header(char *);
160 static char *FormatName(char *, const char *);
161 
162 
163 
164 /*
165  * Parses the command line options and then
166  * calls each_file() to process each file.
167  */
168 int
169 main(int argc, char *argv[], char *envp[])
170 {
171 	char	*optstr = OPTSTR; /* option string used by getopt() */
172 	int	optchar;
173 	FMT_T	new_fmt_flag;
174 
175 #ifndef	XPG4
176 	/*
177 	 * Check for a binary that better fits this architecture.
178 	 */
179 	(void) conv_check_native(argv, envp);
180 #endif
181 
182 	/* table of keyletters for use with -p and -P options */
183 	key[STT_NOTYPE][STB_LOCAL] = "n";
184 	key[STT_NOTYPE][STB_GLOBAL] = "N";
185 	key[STT_NOTYPE][STB_WEAK] = "N*";
186 	key[STT_OBJECT][STB_LOCAL] = "d";
187 	key[STT_OBJECT][STB_GLOBAL] = "D";
188 	key[STT_OBJECT][STB_WEAK] = "D*";
189 	key[STT_FUNC][STB_LOCAL] = "t";
190 	key[STT_FUNC][STB_GLOBAL] = "T";
191 	key[STT_FUNC][STB_WEAK] = "T*";
192 	key[STT_SECTION][STB_LOCAL] = "s";
193 	key[STT_SECTION][STB_GLOBAL] = "S";
194 	key[STT_SECTION][STB_WEAK] = "S*";
195 	key[STT_FILE][STB_LOCAL] = "f";
196 	key[STT_FILE][STB_GLOBAL] = "F";
197 	key[STT_FILE][STB_WEAK] = "F*";
198 	key[STT_COMMON][STB_LOCAL] = "c";
199 	key[STT_COMMON][STB_GLOBAL] = "C";
200 	key[STT_COMMON][STB_WEAK] = "C*";
201 	key[STT_TLS][STB_LOCAL] = "l";
202 	key[STT_TLS][STB_GLOBAL] = "L";
203 	key[STT_TLS][STB_WEAK] = "L*";
204 
205 	prog_name = argv[0];
206 
207 	(void) setlocale(LC_ALL, "");
208 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
209 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
210 #endif
211 	(void) textdomain(TEXT_DOMAIN);
212 
213 	while ((optchar = getopt(argc, argv, optstr)) != -1) {
214 		switch (optchar) {
215 		case 'o':	if (COMPAT_FMT_FLAG(FMT_T_OCT))
216 					fmt_flag = FMT_T_OCT;
217 				else
218 					(void) fprintf(stderr, gettext(
219 					    "%s: -x or -t set, -o ignored\n"),
220 					    prog_name);
221 				break;
222 		case 'x':	if (COMPAT_FMT_FLAG(FMT_T_HEX))
223 					fmt_flag = FMT_T_HEX;
224 				else
225 					(void) fprintf(stderr, gettext(
226 					    "%s: -o or -t set, -x ignored\n"),
227 					    prog_name);
228 				break;
229 		case 'h':	h_flag = 1;
230 				break;
231 		case 'v':	if (!n_flag)
232 					v_flag = 1;
233 				else
234 					(void) fprintf(stderr, gettext(
235 					    "%s: -n set, -v ignored\n"),
236 					    prog_name);
237 				break;
238 		case 'n':	if (!v_flag)
239 					n_flag = 1;
240 				else
241 					(void) fprintf(stderr, gettext(
242 					    "%s: -v set, -n ignored\n"),
243 					    prog_name);
244 				break;
245 		case 'u':	if (!e_flag && !g_flag)
246 					u_flag = 1;
247 				else
248 					(void) fprintf(stderr, gettext(
249 					    "%s: -e or -g set, -u ignored\n"),
250 					    prog_name);
251 				break;
252 		case 'e':	if (!u_flag && !g_flag)
253 					e_flag = 1;
254 				else
255 					(void) fprintf(stderr, gettext(
256 					    "%s: -u or -g set, -e ignored\n"),
257 					    prog_name);
258 				break;
259 		case 'g':	if (!u_flag && !e_flag)
260 					g_flag = 1;
261 				else
262 					(void) fprintf(stderr, gettext(
263 					    "%s: -u or -e set, -g ignored\n"),
264 					    prog_name);
265 				break;
266 		case 'r': 	if (R_flag) {
267 					R_flag = 0;
268 					(void) fprintf(stderr, gettext(
269 					    "%s: -r set, -R ignored\n"),
270 					    prog_name);
271 				}
272 				r_flag = 1;
273 				break;
274 		case 's':	s_flag = 1;
275 				break;
276 		case 'p':	if (P_flag == 1) {
277 					(void) fprintf(stderr, gettext(
278 					    "nm: -P set. -p ignored\n"));
279 				} else
280 					p_flag = 1;
281 				break;
282 		case 'P':	if (p_flag == 1) {
283 					(void) fprintf(stderr, gettext(
284 					    "nm: -p set. -P ignored\n"));
285 				} else
286 					P_flag = 1;
287 				break;
288 		case 'l':	l_flag = 1;
289 				break;
290 		case 'L':	if (D_flag == 1) {
291 					(void) fprintf(stderr, gettext(
292 					    "nm: -D set. -L ignored\n"));
293 				} else
294 					L_flag = 1;
295 				break;
296 		case 'D':	if (L_flag == 1) {
297 					(void) fprintf(stderr, gettext(
298 					    "nm: -L set. -D ignored\n"));
299 				} else
300 					D_flag = 1;
301 				break;
302 		case 'C':
303 				C_flag = 1;
304 				break;
305 		case 'A':	A_flag = 1;
306 				break;
307 		case 'V':	V_flag = 1;
308 				(void) fprintf(stderr, "nm: %s %s\n",
309 				    (const char *)SGU_PKG,
310 				    (const char *)SGU_REL);
311 				break;
312 		case 'f':	/* -f is a noop, see man page */
313 				break;
314 		case 'R':	if (!r_flag)
315 					R_flag = 1;
316 				else
317 					(void) fprintf(stderr, gettext(
318 					    "%s: -r set, -R ignored\n"),
319 					    prog_name);
320 				break;
321 		case 'T':
322 				break;
323 		case 't':	if (strcmp(optarg, "o") == 0) {
324 					new_fmt_flag = FMT_T_OCT;
325 				} else if (strcmp(optarg, "d") == 0) {
326 					new_fmt_flag = FMT_T_DEC;
327 				} else if (strcmp(optarg, "x") == 0) {
328 					new_fmt_flag = FMT_T_HEX;
329 				} else {
330 					new_fmt_flag = FMT_T_NONE;
331 				}
332 				if (new_fmt_flag == FMT_T_NONE) {
333 					(void) fprintf(stderr, gettext(
334 "nm: illegal format '%s' for -t is specified. -t ignored.\n"), optarg);
335 				} else if (COMPAT_FMT_FLAG(new_fmt_flag)) {
336 					fmt_flag = new_fmt_flag;
337 				} else {
338 					(void) fprintf(stderr, gettext(
339 				"nm: -t or -o or -x set. -t ignored.\n"));
340 				}
341 				break;
342 		case ':':	errflag += 1;
343 				(void) fprintf(stderr, gettext(
344 				    "nm: %c requires operand\n"), optopt);
345 				break;
346 		case '?':	errflag += 1;
347 				break;
348 		default:	break;
349 		}
350 	}
351 
352 	if (errflag || (optind >= argc)) {
353 		if (!(V_flag && (argc == 2))) {
354 			usage();
355 			exit(NOARGS);
356 		}
357 	}
358 
359 	/*
360 	 * If no explicit format style was specified, set the default
361 	 * here. In general, the default is for value and size items
362 	 * to be displayed in decimal format. The exception is that
363 	 * the default for -P is hexidecimal.
364 	 */
365 	if (fmt_flag == FMT_T_NONE)
366 		fmt_flag = P_flag ? FMT_T_HEX : FMT_T_DEC;
367 
368 
369 	while (optind < argc) {
370 		each_file(argv[optind]);
371 		optind++;
372 	}
373 	return (errflag);
374 }
375 
376 /*
377  * Print out a usage message in short form when program is invoked
378  * with insufficient or no arguments, and in long form when given
379  * either a ? or an invalid option.
380  */
381 static void
382 usage()
383 {
384 	(void) fprintf(stderr, gettext(
385 "Usage: nm [-APvChlnV] [-efox] [-r | -R]  [-g | -u] [-t format] file ...\n"));
386 }
387 
388 /*
389  * Takes a filename as input.  Test first for a valid version
390  * of libelf.a and exit on error.  Process each valid file
391  * or archive given as input on the command line.  Check
392  * for file type.  If it is an archive, call print_ar_files
393  * to process each member of the archive in the same manner
394  * as object files on the command line.  The same tests for
395  * valid object file type apply to regular archive members.
396  * If it is an ELF object file, process it; otherwise
397  * warn that it is an invalid file type and return from
398  * processing the file.
399  */
400 
401 static void
402 each_file(char *filename)
403 {
404 	Elf	*elf_file;
405 	int	fd;
406 	Elf_Kind   file_type;
407 
408 	struct stat64 buf;
409 
410 	Elf_Cmd cmd;
411 	errno = 0;
412 	if (stat64(filename, &buf) == -1)	{
413 		(void) fprintf(stderr, "%s: ", prog_name);
414 		perror(filename);
415 		errflag++;
416 		return;
417 	}
418 	if (elf_version(EV_CURRENT) == EV_NONE)	{
419 		(void) fprintf(stderr, gettext(
420 		    "%s: %s: Libelf is out of date\n"),
421 		    prog_name, filename);
422 		exit(BADELF);
423 	}
424 
425 	if ((fd = open((filename), O_RDONLY)) == -1) {
426 		(void) fprintf(stderr, gettext("%s: %s: cannot read file\n"),
427 		    prog_name, filename);
428 		errflag++;
429 		return;
430 	}
431 	cmd = ELF_C_READ;
432 	if ((elf_file = elf_begin(fd, cmd, (Elf *) 0)) == NULL)	{
433 		(void) fprintf(stderr,
434 		    "%s: %s: %s\n", prog_name, filename, elf_errmsg(-1));
435 		errflag++;
436 		(void) close(fd);
437 		return;
438 	}
439 	file_type = elf_kind(elf_file);
440 	if (file_type == ELF_K_AR) {
441 		print_ar_files(fd, elf_file, filename);
442 	} else {
443 		if (file_type == ELF_K_ELF) {
444 #ifndef XPG4
445 			if (u_flag && !h_flag) {
446 				/*
447 				 * u_flag is specified.
448 				 */
449 				if (p_flag)
450 					(void) printf("\n\n%s:\n\n", filename);
451 				else
452 					(void) printf(gettext(
453 				"\n\nUndefined symbols from %s:\n\n"),
454 					    filename);
455 			} else if (!h_flag & !P_flag)
456 #else
457 			if (!h_flag & !P_flag)
458 #endif
459 			{
460 				if (p_flag)
461 					(void) printf("\n\n%s:\n", filename);
462 				else {
463 					if (A_flag != 0)
464 						(void) printf("\n\n%s%s:\n",
465 						    A_header, filename);
466 					else
467 						(void) printf("\n\n%s:\n",
468 						    filename);
469 				}
470 			}
471 			archive_name = (char *)0;
472 			process(elf_file, filename);
473 		} else {
474 			(void) fprintf(stderr, gettext(
475 			    "%s: %s: invalid file type\n"),
476 			    prog_name, filename);
477 			errflag++;
478 		}
479 	}
480 	(void) elf_end(elf_file);
481 	(void) close(fd);
482 }
483 
484 /*
485  * Get the ELF header and, if it exists, call get_symtab()
486  * to begin processing of the file; otherwise, return from
487  * processing the file with a warning.
488  */
489 static void
490 process(Elf *elf_file, char *filename)
491 {
492 	GElf_Ehdr ehdr;
493 
494 	if (gelf_getehdr(elf_file, &ehdr) == NULL) {
495 		(void) fprintf(stderr,
496 		    "%s: %s: %s\n", prog_name, filename, elf_errmsg(-1));
497 		return;
498 	}
499 
500 	set_A_header(filename);
501 	get_symtab(elf_file, filename);
502 }
503 
504 /*
505  * Get section descriptor for the associated string table
506  * and verify that the type of the section pointed to is
507  * indeed of type STRTAB.  Returns a valid section descriptor
508  * or NULL on error.
509  */
510 static Elf_Scn *
511 get_scnfd(Elf * e_file, int shstrtab, int SCN_TYPE)
512 {
513 	Elf_Scn	*fd_scn;
514 	GElf_Shdr shdr;
515 
516 	if ((fd_scn = elf_getscn(e_file, shstrtab)) == NULL) {
517 		return (NULL);
518 	}
519 
520 	(void) gelf_getshdr(fd_scn, &shdr);
521 	if (shdr.sh_type != SCN_TYPE) {
522 		return (NULL);
523 	}
524 	return (fd_scn);
525 }
526 
527 
528 /*
529  * Print the symbol table.  This function does not print the contents
530  * of the symbol table but sets up the parameters and then calls
531  * print_symtab to print the symbols.  This function does not assume
532  * that there is only one section of type SYMTAB.  Input is an opened
533  * ELF file, a pointer to the ELF header, and the filename.
534  */
535 static void
536 get_symtab(Elf *elf_file, char *filename)
537 {
538 	Elf_Scn	*scn, *scnfd;
539 	Elf_Data *data;
540 	GElf_Word symtabtype;
541 	size_t shstrndx;
542 
543 	if (elf_getshstrndx(elf_file, &shstrndx) == 0) {
544 		(void) fprintf(stderr, gettext(
545 		    "%s: %s: could not get e_shstrndx\n"),
546 		    prog_name, filename);
547 		return;
548 	}
549 
550 	/* get section header string table */
551 	scnfd = get_scnfd(elf_file, shstrndx, SHT_STRTAB);
552 	if (scnfd == NULL) {
553 		(void) fprintf(stderr, gettext(
554 		    "%s: %s: could not get string table\n"),
555 		    prog_name, filename);
556 		return;
557 	}
558 
559 	data = elf_getdata(scnfd, NULL);
560 	if (data->d_size == 0) {
561 		(void) fprintf(stderr, gettext(
562 		    "%s: %s: no data in string table\n"),
563 		    prog_name, filename);
564 		return;
565 	}
566 
567 	if (D_flag)
568 		symtabtype = SHT_DYNSYM;
569 	else if (L_flag)
570 		symtabtype = SHT_SUNW_LDYNSYM;
571 	else
572 		symtabtype = SHT_SYMTAB;
573 
574 	scn = 0;
575 	while ((scn = elf_nextscn(elf_file, scn)) != 0)	{
576 		GElf_Shdr shdr;
577 
578 		if (gelf_getshdr(scn, &shdr) == NULL) {
579 			(void) fprintf(stderr, "%s: %s: %s:\n",
580 			    prog_name, filename, elf_errmsg(-1));
581 			return;
582 		}
583 
584 		if (shdr.sh_type == symtabtype)	{
585 			print_symtab(elf_file, shstrndx, scn,
586 			    &shdr, filename);
587 		}
588 	} /* end while */
589 }
590 
591 /*
592  * Process member files of an archive.  This function provides
593  * a loop through an archive equivalent the processing of
594  * each_file for individual object files.
595  */
596 static void
597 print_ar_files(int fd, Elf * elf_file, char *filename)
598 {
599 	Elf_Arhdr  *p_ar;
600 	Elf	*arf;
601 	Elf_Cmd    cmd;
602 	Elf_Kind   file_type;
603 
604 
605 	cmd = ELF_C_READ;
606 	archive_name = filename;
607 	while ((arf = elf_begin(fd, cmd, elf_file)) != 0) {
608 		p_ar = elf_getarhdr(arf);
609 		if (p_ar == NULL) {
610 			(void) fprintf(stderr, "%s: %s: %s\n",
611 			    prog_name, filename, elf_errmsg(-1));
612 			return;
613 		}
614 		if ((int)strncmp(p_ar->ar_name, "/", 1) == 0) {
615 			cmd = elf_next(arf);
616 			(void) elf_end(arf);
617 			continue;
618 		}
619 
620 		if (!h_flag & !P_flag) {
621 			if (p_flag)
622 				(void) printf("\n\n%s[%s]:\n",
623 				    filename, p_ar->ar_name);
624 			else {
625 				if (A_flag != 0)
626 					(void) printf("\n\n%s%s[%s]:\n",
627 					    A_header, filename, p_ar->ar_name);
628 				else
629 					(void) printf("\n\n%s[%s]:\n",
630 					    filename, p_ar->ar_name);
631 			}
632 		}
633 		file_type = elf_kind(arf);
634 		if (file_type == ELF_K_ELF) {
635 			process(arf, p_ar->ar_name);
636 		} else {
637 			(void) fprintf(stderr, gettext(
638 			    "%s: %s: invalid file type\n"),
639 			    prog_name, p_ar->ar_name);
640 			cmd = elf_next(arf);
641 			(void) elf_end(arf);
642 			errflag++;
643 			continue;
644 		}
645 
646 		cmd = elf_next(arf);
647 		(void) elf_end(arf);
648 	} /* end while */
649 }
650 
651 /*
652  * Print the symbol table according to the flags that were
653  * set, if any.  Input is an opened ELF file, the section name,
654  * the section header, the section descriptor, and the filename.
655  * First get the symbol table with a call to elf_getdata.
656  * Then translate the symbol table data in memory by calling
657  * readsyms().  This avoids duplication of function calls
658  * and improves sorting efficiency.  qsort is used when sorting
659  * is requested.
660  */
661 static void
662 print_symtab(Elf *elf_file, unsigned int shstrndx,
663 	Elf_Scn *p_sd, GElf_Shdr *shdr, char *filename)
664 {
665 
666 	Elf_Data * sd;
667 	SYM	*sym_data;
668 	SYM	*s;
669 	GElf_Sxword	count = 0;
670 	const int ndigits_arr[] = {
671 		10,		/* FMT_T_DEC */
672 		8,		/* FMT_T_HEX */
673 		11,		/* FMT_T_OCT */
674 	};
675 	static void print_header(int);
676 	int ndigits;
677 #ifndef XPG4
678 	static void print_with_uflag(SYM *, char *);
679 #endif
680 	static void print_with_pflag(int, Elf *, unsigned int, SYM *, char *);
681 	static void print_with_Pflag(int, Elf *, unsigned int, SYM *);
682 	static void print_with_otherflags(int, Elf *, unsigned int,
683 	    SYM *, char *);
684 
685 	/*
686 	 * Determine # of digits to use for each numeric value.
687 	 */
688 	ndigits = ndigits_arr[fmt_flag];
689 	if (gelf_getclass(elf_file) == ELFCLASS64)
690 		ndigits *= 2;
691 
692 	/*
693 	 * print header
694 	 */
695 	print_header(ndigits);
696 
697 	/*
698 	 * get symbol table data
699 	 */
700 	if (((sd = elf_getdata(p_sd, NULL)) == NULL) || (sd->d_size == 0)) {
701 		(void) printf(gettext("%s: %s - No symbol table data\n"),
702 		    prog_name, filename);
703 		return;
704 	}
705 	count = shdr->sh_size / shdr->sh_entsize;
706 
707 	/*
708 	 * translate symbol table data
709 	 */
710 	sym_data = readsyms(sd, count, elf_file, shdr->sh_link,
711 	    (unsigned int)elf_ndxscn(p_sd));
712 	if (sym_data == NULL) {
713 		(void) fprintf(stderr, gettext(
714 		    "%s: %s: problem reading symbol data\n"),
715 		    prog_name, filename);
716 		return;
717 	}
718 	qsort((char *)sym_data, count-1, sizeof (SYM),
719 	    (int (*)(const void *, const void *))compare);
720 	s = sym_data;
721 	while (count > 1) {
722 #ifndef XPG4
723 		if (u_flag) {
724 			/*
725 			 * U_flag specified
726 			 */
727 			print_with_uflag(sym_data, filename);
728 		} else if (p_flag)
729 #else
730 		if (p_flag)
731 #endif
732 			print_with_pflag(ndigits, elf_file, shstrndx,
733 			    sym_data, filename);
734 		else if (P_flag)
735 			print_with_Pflag(ndigits, elf_file, shstrndx,
736 			    sym_data);
737 		else
738 			print_with_otherflags(ndigits, elf_file,
739 			    shstrndx, sym_data, filename);
740 		sym_data++;
741 		count--;
742 	}
743 
744 	free(s);		/* allocated in readsym() */
745 }
746 
747 /*
748  * Return appropriate keyletter(s) for -p option.
749  * Returns an index into the key[][] table or NULL if
750  * the value of the keyletter is unknown.
751  */
752 static char *
753 lookup(int a, int b)
754 {
755 	return (((a < TYPE) && (b < BIND)) ? key[a][b] : NULL);
756 }
757 
758 /*
759  * Return TRUE(1) if the given section is ".bss" for "-p" option.
760  * Return FALSE(0) if not ".bss" section.
761  */
762 static int
763 is_bss_section(unsigned int shndx, Elf * elf_file, unsigned int shstrndx)
764 {
765 	Elf_Scn *scn		= elf_getscn(elf_file, shndx);
766 	char	*sym_name;
767 
768 	if (scn != NULL) {
769 		GElf_Shdr shdr;
770 		(void) gelf_getshdr(scn, &shdr);
771 		sym_name = elf_strptr(elf_file, shstrndx, shdr.sh_name);
772 		if (strcmp(BSS_SECN, sym_name) == 0)
773 			return (1);
774 	}
775 	return (0);
776 }
777 
778 /*
779  * Translate symbol table data particularly for sorting.
780  * Input is the symbol table data structure, number of symbols,
781  * opened ELF file, and the string table link offset.
782  */
783 static SYM *
784 readsyms(Elf_Data * data, GElf_Sxword num, Elf *elf,
785 	unsigned int link, unsigned int symscnndx)
786 {
787 	SYM		*s, *buf;
788 	GElf_Sym	sym;
789 	Elf32_Word	*symshndx = 0;
790 	unsigned int	nosymshndx = 0;
791 	int		i;
792 
793 	if ((buf = calloc(num, sizeof (SYM))) == NULL) {
794 		(void) fprintf(stderr, "%s: cannot calloc space\n", prog_name);
795 		return (NULL);
796 	}
797 
798 	s = buf;	/* save pointer to head of array */
799 
800 	for (i = 1; i < num; i++, buf++) {
801 		(void) gelf_getsym(data, i, &sym);
802 
803 		buf->indx = i;
804 		/* allow to work on machines where NULL-derefs dump core */
805 		if (sym.st_name == 0)
806 			buf->name = "";
807 		else if (C_flag) {
808 			const char *dn;
809 			char *name = (char *)elf_strptr(elf, link, sym.st_name);
810 			dn = conv_demangle_name(name);
811 			if (strcmp(dn, name) == 0) {	/* Not demangled */
812 				if (exotic(name)) {
813 					name = FormatName(name, d_buf);
814 				}
815 			} else {  /* name demangled */
816 				name = FormatName(name, dn);
817 			}
818 			buf->name = name;
819 		}
820 		else
821 			buf->name = (char *)elf_strptr(elf, link, sym.st_name);
822 
823 		buf->value	= sym.st_value;
824 		buf->size	= sym.st_size;
825 		buf->type	= GELF_ST_TYPE(sym.st_info);
826 		buf->bind	= GELF_ST_BIND(sym.st_info);
827 		buf->other	= sym.st_other;
828 		if ((sym.st_shndx == SHN_XINDEX) &&
829 		    (symshndx == 0) && (nosymshndx == 0)) {
830 			Elf_Scn		*_scn;
831 			GElf_Shdr	_shdr;
832 			_scn = 0;
833 			while ((_scn = elf_nextscn(elf, _scn)) != 0) {
834 				if (gelf_getshdr(_scn, &_shdr) == 0)
835 					break;
836 				if ((_shdr.sh_type == SHT_SYMTAB_SHNDX) &&
837 				    (_shdr.sh_link == symscnndx)) {
838 					Elf_Data	*_data;
839 					if ((_data = elf_getdata(_scn,
840 					    0)) != 0) {
841 						symshndx =
842 						    (Elf32_Word *)_data->d_buf;
843 						break;
844 					}
845 				}
846 			}
847 			nosymshndx = 1;
848 		}
849 		if ((symshndx) && (sym.st_shndx == SHN_XINDEX)) {
850 			buf->shndx = symshndx[i];
851 		} else {
852 			buf->shndx	= sym.st_shndx;
853 			if (sym.st_shndx >= SHN_LORESERVE)
854 				buf->flags |= FLG_SYM_SPECSEC;
855 		}
856 	}	/* end for loop */
857 	return (s);
858 }
859 
860 /*
861  * compare either by name or by value for sorting.
862  * This is the comparison function called by qsort to
863  * sort the symbols either by name or value when requested.
864  */
865 static int
866 compare(SYM *a, SYM *b)
867 {
868 	if (v_flag) {
869 		if (a->value > b->value)
870 			return (1);
871 		else
872 			return ((a->value == b->value) -1);
873 	} else
874 		return ((int)strcoll(a->name, b->name));
875 }
876 
877 /*
878  * Set up a header line for -A option.
879  */
880 static void
881 set_A_header(char *fname)
882 {
883 	if (A_flag == 0)
884 		return;
885 
886 	if (archive_name == (char *)0) {
887 		(void) snprintf(A_header, sizeof (A_header), "%s: ", fname);
888 	} else {
889 		(void) snprintf(A_header, sizeof (A_header), "%s[%s]: ",
890 		    archive_name, fname);
891 	}
892 }
893 
894 /*
895  * output functions
896  *	The following functions are called from
897  *	print_symtab().
898  */
899 
900 /*
901  * Print header line if needed.
902  *
903  * entry:
904  *	ndigits - # of digits to be used to format an integer
905  *		value, not counting any '0x' (hex) or '0' (octal) prefix.
906  */
907 static void
908 print_header(int ndigits)
909 {
910 	const char *fmt;
911 	const char *section_title;
912 	const int pad[] = {	/* Extra prefix characters for format */
913 		1,		/* FMT_T_DEC: '|' */
914 		3,		/* FMT_T_HEX: '|0x' */
915 		2,		/* FMT_T_OCT: '|0' */
916 	};
917 	if (
918 #ifndef XPG4
919 	    !u_flag &&
920 #endif
921 	    !h_flag && !p_flag && !P_flag) {
922 		(void) printf("\n");
923 		if (!s_flag) {
924 			fmt = "%-9s%-*s%-*s%-6s%-6s%-6s%-8s%s\n\n";
925 			section_title = "Shndx";
926 		} else {
927 			fmt = "%-9s%-*s%-*s%-6s%-6s%-6s%-15s%s\n\n";
928 			section_title = "Shname";
929 		}
930 		if (A_flag != 0)
931 			(void) printf("%s", A_header);
932 		ndigits += pad[fmt_flag];
933 		(void) printf(fmt, "[Index]", ndigits, " Value",
934 		    ndigits, " Size", "Type", "Bind",
935 		    "Other", section_title, "Name");
936 	}
937 }
938 
939 /*
940  * If the symbol can be printed, then return 1.
941  * If the symbol can not be printed, then return 0.
942  */
943 static int
944 is_sym_print(SYM *sym_data)
945 {
946 	/*
947 	 * If -u flag is specified,
948 	 *	the symbol has to be undefined.
949 	 */
950 	if (u_flag != 0) {
951 		if ((sym_data->shndx == SHN_UNDEF) &&
952 		    (strlen(sym_data->name) != 0))
953 			return (1);
954 		else
955 			return (0);
956 	}
957 
958 	/*
959 	 * If -e flag is specified,
960 	 *	the symbol has to be global or static.
961 	 */
962 	if (e_flag != 0) {
963 		switch (sym_data->type) {
964 		case STT_NOTYPE:
965 		case STT_OBJECT:
966 		case STT_FUNC:
967 		case STT_COMMON:
968 		case STT_TLS:
969 			switch (sym_data->bind) {
970 			case STB_LOCAL:
971 			case STB_GLOBAL:
972 			case STB_WEAK:
973 				return (1);
974 			default:
975 				return (0);
976 			}
977 		default:
978 			return (0);
979 		}
980 	}
981 
982 	/*
983 	 * If -g is specified,
984 	 *	the symbol has to be global.
985 	 */
986 	if (g_flag != 0) {
987 		switch (sym_data->type) {
988 		case STT_NOTYPE:
989 		case STT_OBJECT:
990 		case STT_FUNC:
991 		case STT_COMMON:
992 		case STT_TLS:
993 			switch (sym_data->bind) {
994 			case STB_GLOBAL:
995 			case STB_WEAK:
996 				return (1);
997 			default:
998 				return (0);
999 			}
1000 		default:
1001 			return (0);
1002 		}
1003 	}
1004 
1005 	/*
1006 	 * If it comes here, any symbol can be printed.
1007 	 *	(So basically, -f is no-op.)
1008 	 */
1009 	return (1);
1010 }
1011 
1012 #ifndef XPG4
1013 /*
1014  * -u flag specified
1015  */
1016 static void
1017 print_with_uflag(
1018 	SYM *sym_data,
1019 	char *filename
1020 )
1021 {
1022 	if ((sym_data->shndx == SHN_UNDEF) && (strlen(sym_data->name))) {
1023 		if (!r_flag) {
1024 			if (R_flag) {
1025 				if (archive_name != (char *)0)
1026 					(void) printf("   %s:%s:%s\n",
1027 					    archive_name, filename,
1028 					    sym_data->name);
1029 				else
1030 					(void) printf("    %s:%s\n",
1031 					    filename, sym_data->name);
1032 			}
1033 			else
1034 				(void) printf("    %s\n", sym_data->name);
1035 		}
1036 		else
1037 			(void) printf("    %s:%s\n", filename, sym_data->name);
1038 	}
1039 }
1040 #endif
1041 /*
1042  * -p flag specified
1043  */
1044 static void
1045 print_with_pflag(
1046 	int ndigits,
1047 	Elf *elf_file,
1048 	unsigned int shstrndx,
1049 	SYM *sym_data,
1050 	char *filename
1051 )
1052 {
1053 	char *sym_key	= NULL;
1054 	const char * const fmt[] = {
1055 		"%.*llu ",	/* FMT_T_DEC */
1056 		"0x%.*llx ",	/* FMT_T_HEX */
1057 		"0%.*llo "	/* FMT_T_OCT */
1058 	};
1059 
1060 	if (is_sym_print(sym_data) != 1)
1061 		return;
1062 	/*
1063 	 * -A header
1064 	 */
1065 	if (A_flag != 0)
1066 		(void) printf("%s", A_header);
1067 
1068 	/*
1069 	 * Symbol Value.
1070 	 *	(hex/octal/decimal)
1071 	 */
1072 	(void) printf(fmt[fmt_flag], ndigits, EC_ADDR(sym_data->value));
1073 
1074 
1075 	/*
1076 	 * Symbol Type.
1077 	 */
1078 	if ((sym_data->shndx == SHN_UNDEF) && (strlen(sym_data->name)))
1079 		sym_key = UNDEFINED;
1080 	else if (sym_data->type == STT_SPARC_REGISTER) {
1081 		switch (sym_data->bind) {
1082 			case STB_LOCAL  : sym_key = REG_LOCL;
1083 					break;
1084 			case STB_GLOBAL : sym_key = REG_GLOB;
1085 					break;
1086 			case STB_WEAK   : sym_key = REG_WEAK;
1087 					break;
1088 			default	: sym_key = REG_GLOB;
1089 					break;
1090 		}
1091 	} else if (((sym_data->flags & FLG_SYM_SPECSEC) == 0) &&
1092 	    is_bss_section((int)sym_data->shndx, elf_file, shstrndx)) {
1093 		switch (sym_data->bind) {
1094 			case STB_LOCAL  : sym_key = BSS_LOCL;
1095 					break;
1096 			case STB_GLOBAL : sym_key = BSS_GLOB;
1097 					break;
1098 			case STB_WEAK   : sym_key = BSS_WEAK;
1099 					break;
1100 			default	: sym_key = BSS_GLOB;
1101 					break;
1102 		}
1103 
1104 	}
1105 	else
1106 		sym_key = lookup(sym_data->type, sym_data->bind);
1107 
1108 	if (sym_key != NULL) {
1109 		if (!l_flag)
1110 			(void) printf("%c ", sym_key[0]);
1111 		else
1112 			(void) printf("%-3s", sym_key);
1113 	} else {
1114 		if (!l_flag)
1115 			(void) printf("%-2d", sym_data->type);
1116 		else
1117 			(void) printf("%-3d", sym_data->type);
1118 	}
1119 	if (!r_flag) {
1120 		if (R_flag) {
1121 			if (archive_name != (char *)0)
1122 				(void) printf("%s:%s:%s\n", archive_name,
1123 				    filename, sym_data->name);
1124 			else
1125 				(void) printf("%s:%s\n", filename,
1126 				    sym_data->name);
1127 		}
1128 		else
1129 			(void) printf("%s\n", sym_data->name);
1130 	}
1131 	else
1132 		(void) printf("%s:%s\n", filename, sym_data->name);
1133 }
1134 
1135 /*
1136  * -P flag specified
1137  */
1138 static void
1139 print_with_Pflag(
1140 	int ndigits,
1141 	Elf *elf_file,
1142 	unsigned int shstrndx,
1143 	SYM *sym_data
1144 )
1145 {
1146 	char *sym_key = NULL;
1147 #define	SYM_LEN 10
1148 	char sym_name[SYM_LEN+1];
1149 	size_t len;
1150 	const char * const fmt[] = {
1151 		"%*llu %*llu \n",	/* FMT_T_DEC */
1152 		"%*llx %*llx \n",	/* FMT_T_HEX */
1153 		"%*llo %*llo \n"	/* FMT_T_OCT */
1154 	};
1155 
1156 	if (is_sym_print(sym_data) != 1)
1157 		return;
1158 	/*
1159 	 * -A header
1160 	 */
1161 	if (A_flag != 0)
1162 		(void) printf("%s", A_header);
1163 
1164 	/*
1165 	 * Symbol name
1166 	 */
1167 	len = strlen(sym_data->name);
1168 	if (len >= SYM_LEN)
1169 		(void) printf("%s ", sym_data->name);
1170 	else {
1171 		(void) sprintf(sym_name, "%-10s", sym_data->name);
1172 		(void) printf("%s ", sym_name);
1173 	}
1174 
1175 	/*
1176 	 * Symbol Type.
1177 	 */
1178 	if ((sym_data->shndx == SHN_UNDEF) && (strlen(sym_data->name)))
1179 		sym_key = UNDEFINED;
1180 	else if (sym_data->type == STT_SPARC_REGISTER) {
1181 		switch (sym_data->bind) {
1182 			case STB_LOCAL  : sym_key = REG_LOCL;
1183 					break;
1184 			case STB_GLOBAL : sym_key = REG_GLOB;
1185 					break;
1186 			case STB_WEAK   : sym_key = REG_WEAK;
1187 					break;
1188 			default	: sym_key = REG_GLOB;
1189 					break;
1190 		}
1191 	} else if (((sym_data->flags & FLG_SYM_SPECSEC) == 0) &&
1192 	    is_bss_section((int)sym_data->shndx, elf_file, shstrndx)) {
1193 		switch (sym_data->bind) {
1194 			case STB_LOCAL  : sym_key = BSS_LOCL;
1195 					break;
1196 			case STB_GLOBAL : sym_key = BSS_GLOB;
1197 					break;
1198 			case STB_WEAK   : sym_key = BSS_WEAK;
1199 					break;
1200 			default	: sym_key = BSS_GLOB;
1201 					break;
1202 		}
1203 
1204 	} else
1205 		sym_key = lookup(sym_data->type, sym_data->bind);
1206 
1207 	if (sym_key != NULL) {
1208 		if (!l_flag)
1209 			(void) printf("%c ", sym_key[0]);
1210 		else
1211 			(void) printf("%-3s", sym_key);
1212 	} else {
1213 		if (!l_flag)
1214 			(void) printf("%-2d", sym_data->type);
1215 		else
1216 			(void) printf("%-3d", sym_data->type);
1217 	}
1218 
1219 	/*
1220 	 * Symbol Value & size
1221 	 *	(hex/octal/decimal)
1222 	 */
1223 	(void) printf(fmt[fmt_flag], ndigits, EC_ADDR(sym_data->value),
1224 	    ndigits, EC_XWORD(sym_data->size));
1225 }
1226 
1227 /*
1228  * other flags specified
1229  */
1230 static void
1231 print_with_otherflags(
1232 	int ndigits,
1233 	Elf *elf_file,
1234 	unsigned int shstrndx,
1235 	SYM *sym_data,
1236 	char *filename
1237 )
1238 {
1239 	const char * const fmt_value_size[] = {
1240 		"%*llu|%*lld|",		/* FMT_T_DEC */
1241 		"0x%.*llx|0x%.*llx|",	/* FMT_T_HEX */
1242 		"0%.*llo|0%.*llo|"	/* FMT_T_OCT */
1243 	};
1244 	const char * const fmt_int[] = {
1245 		"%-5d",			/* FMT_T_DEC */
1246 		"%#-5x",		/* FMT_T_HEX */
1247 		"%#-5o"			/* FMT_T_OCT */
1248 	};
1249 
1250 	if (is_sym_print(sym_data) != 1)
1251 		return;
1252 	(void) printf("%s", A_header);
1253 	(void) printf("[%d]\t|", sym_data->indx);
1254 	(void) printf(fmt_value_size[fmt_flag], ndigits,
1255 	    EC_ADDR(sym_data->value), ndigits, EC_XWORD(sym_data->size));
1256 
1257 	switch (sym_data->type) {
1258 	case STT_NOTYPE:(void) printf("%-5s", "NOTY"); break;
1259 	case STT_OBJECT:(void) printf("%-5s", "OBJT"); break;
1260 	case STT_FUNC:	(void) printf("%-5s", "FUNC"); break;
1261 	case STT_SECTION:(void) printf("%-5s", "SECT"); break;
1262 	case STT_FILE:	(void) printf("%-5s", "FILE"); break;
1263 	case STT_COMMON: (void) printf("%-5s", "COMM"); break;
1264 	case STT_TLS:	(void) printf("%-5s", "TLS "); break;
1265 	case STT_SPARC_REGISTER: (void) printf("%-5s", "REGI"); break;
1266 	default:
1267 		(void) printf(fmt_int[fmt_flag], sym_data->type);
1268 	}
1269 	(void) printf("|");
1270 	switch (sym_data->bind) {
1271 	case STB_LOCAL:	(void) printf("%-5s", "LOCL"); break;
1272 	case STB_GLOBAL:(void) printf("%-5s", "GLOB"); break;
1273 	case STB_WEAK:	(void) printf("%-5s", "WEAK"); break;
1274 	default:
1275 		(void) printf("%-5d", sym_data->bind);
1276 		(void) printf(fmt_int[fmt_flag], sym_data->bind);
1277 	}
1278 	(void) printf("|");
1279 	(void) printf(fmt_int[fmt_flag], sym_data->other);
1280 	(void)  printf("|");
1281 
1282 	if (sym_data->shndx == SHN_UNDEF) {
1283 		if (!s_flag)
1284 			(void) printf("%-7s", "UNDEF");
1285 		else
1286 			(void) printf("%-14s", "UNDEF");
1287 	} else if (sym_data->shndx == SHN_SUNW_IGNORE) {
1288 		if (!s_flag)
1289 			(void) printf("%-7s", "IGNORE");
1290 		else
1291 			(void) printf("%-14s", "IGNORE");
1292 	} else if ((sym_data->flags & FLG_SYM_SPECSEC) &&
1293 	    (sym_data->shndx == SHN_ABS)) {
1294 		if (!s_flag)
1295 			(void) printf("%-7s", "ABS");
1296 		else
1297 			(void) printf("%-14s", "ABS");
1298 	} else if ((sym_data->flags & FLG_SYM_SPECSEC) &&
1299 	    (sym_data->shndx == SHN_COMMON)) {
1300 		if (!s_flag)
1301 			(void) printf("%-7s", "COMMON");
1302 		else
1303 			(void) printf("%-14s", "COMMON");
1304 	} else {
1305 		if (s_flag) {
1306 			Elf_Scn *scn = elf_getscn(elf_file, sym_data->shndx);
1307 			GElf_Shdr shdr;
1308 
1309 			if ((gelf_getshdr(scn, &shdr) != 0) &&
1310 			    (shdr.sh_name != 0)) {
1311 				(void) printf("%-14s",
1312 				    (char *)elf_strptr(elf_file,
1313 				    shstrndx, shdr.sh_name));
1314 			} else {
1315 				(void) printf("%-14d", sym_data->shndx);
1316 			}
1317 		} else {
1318 			(void) printf("%-7d", sym_data->shndx);
1319 		}
1320 	}
1321 	(void) printf("|");
1322 	if (!r_flag) {
1323 		if (R_flag) {
1324 			if (archive_name != (char *)0)
1325 				(void) printf("%s:%s:%s\n", archive_name,
1326 				    filename, sym_data->name);
1327 			else
1328 				(void) printf("%s:%s\n", filename,
1329 				    sym_data->name);
1330 		}
1331 		else
1332 			(void) printf("%s\n", sym_data->name);
1333 	}
1334 	else
1335 		(void) printf("%s:%s\n", filename, sym_data->name);
1336 }
1337 
1338 /*
1339  * C++ name demangling supporting routines
1340  */
1341 static const char *ctor_str = "static constructor function for %s";
1342 static const char *dtor_str = "static destructor function for %s";
1343 static const char *ptbl_str = "pointer to the virtual table vector for %s";
1344 static const char *vtbl_str = "virtual table for %s";
1345 
1346 /*
1347  * alloc memory and create name in necessary format.
1348  * Return name string
1349  */
1350 static char *
1351 FormatName(char *OldName, const char *NewName)
1352 {
1353 	char *s = p_flag ?
1354 	    "%s\n             [%s]" :
1355 	    "%s\n\t\t\t\t\t\t       [%s]";
1356 	size_t length = strlen(s)+strlen(NewName)+strlen(OldName)-3;
1357 	char *hold = OldName;
1358 	OldName = malloc(length);
1359 	/*LINTED*/
1360 	(void) snprintf(OldName, length, s, NewName, hold);
1361 	return (OldName);
1362 }
1363 
1364 
1365 /*
1366  * Return 1 when s is an exotic name, 0 otherwise.  s remains unchanged,
1367  * the exotic name, if exists, is saved in d_buf.
1368  */
1369 static int
1370 exotic(char *s)
1371 {
1372 	int tag = 0;
1373 	if (strncmp(s, "__sti__", 7) == 0) {
1374 		s += 7; tag = 1;
1375 		parse_fn_and_print(ctor_str, s);
1376 	} else if (strncmp(s, "__std__", 7) == 0) {
1377 		s += 7; tag = 1;
1378 		parse_fn_and_print(dtor_str, s);
1379 	} else if (strncmp(s, "__vtbl__", 8) == 0) {
1380 		s += 8; tag = 1;
1381 		parsename(s);
1382 		(void) sprintf(d_buf, vtbl_str, p_buf);
1383 	} else if (strncmp(s, "__ptbl_vec__", 12) == 0) {
1384 		s += 12; tag = 1;
1385 		parse_fn_and_print(ptbl_str, s);
1386 	}
1387 	return (tag);
1388 }
1389 
1390 void
1391 parsename(char *s)
1392 {
1393 	register int len;
1394 	char c, *orig = s;
1395 	*p_buf = '\0';
1396 	(void) strcat(p_buf, "class ");
1397 	while (isdigit(*s)) s++;
1398 	c = *s;
1399 	*s = '\0';
1400 	len = atoi(orig);
1401 	*s = c;
1402 	if (*(s+len) == '\0') { /* only one class name */
1403 		(void) strcat(p_buf, s);
1404 		return;
1405 	} else
1406 	{ /* two classname  %drootname__%dchildname */
1407 		char *root, *child, *child_len_p;
1408 		int child_len;
1409 		root = s;
1410 		child = s + len + 2;
1411 		child_len_p = child;
1412 		if (!isdigit(*child)) {
1413 			/* ptbl file name */
1414 			/*  %drootname__%filename */
1415 			/* kludge for getting rid of '_' in file name */
1416 			char *p;
1417 			c = *(root + len);
1418 			*(root + len) = '\0';
1419 			(void) strcat(p_buf, root);
1420 			*(root + len) = c;
1421 			(void) strcat(p_buf, " in ");
1422 			for (p = child; *p != '_'; ++p)
1423 				;
1424 			c = *p;
1425 			*p = '.';
1426 			(void) strcat(p_buf, child);
1427 			*p = c;
1428 			return;
1429 		}
1430 
1431 		while (isdigit(*child))
1432 			child++;
1433 		c = *child;
1434 		*child = '\0';
1435 		child_len = atoi(child_len_p);
1436 		*child = c;
1437 		if (*(child + child_len) == '\0') {
1438 			(void) strcat(p_buf, child);
1439 			(void) strcat(p_buf, " derived from ");
1440 			c = *(root + len);
1441 			*(root + len) = '\0';
1442 			(void) strcat(p_buf, root);
1443 			*(root + len) = c;
1444 			return;
1445 		} else {
1446 			/* %drootname__%dchildname__filename */
1447 			/* kludge for getting rid of '_' in file name */
1448 			char *p;
1449 			c = *(child + child_len);
1450 			*(child + child_len) = '\0';
1451 			(void) strcat(p_buf, child);
1452 			*(child+child_len) = c;
1453 			(void) strcat(p_buf, " derived from ");
1454 			c = *(root + len);
1455 			*(root + len) = '\0';
1456 			(void) strcat(p_buf, root);
1457 			*(root + len) = c;
1458 			(void) strcat(p_buf, " in ");
1459 			for (p = child + child_len + 2; *p != '_'; ++p)
1460 				;
1461 			c = *p;
1462 			*p = '.';
1463 			(void) strcat(p_buf, child + child_len + 2);
1464 			*p = c;
1465 			return;
1466 		}
1467 	}
1468 }
1469 
1470 void
1471 parse_fn_and_print(const char *str, char *s)
1472 {
1473 	char		c, *p1, *p2;
1474 	size_t		sym_len = strlen(s);
1475 	int		yes = 1;
1476 	static char	*buff = 0;
1477 	static size_t	buf_size;
1478 
1479 	/*
1480 	 * We will need to modify the symbol (s) as we are analyzing it,
1481 	 * so copy it into a buffer so that we can play around with it.
1482 	 */
1483 	if (buff == NULL) {
1484 	buff = malloc(DEF_MAX_SYM_SIZE);
1485 	buf_size = DEF_MAX_SYM_SIZE;
1486 	}
1487 
1488 	if (++sym_len > buf_size) {
1489 	if (buff)
1490 		free(buff);
1491 	buff = malloc(sym_len);
1492 	buf_size = sym_len;
1493 	}
1494 
1495 	if (buff == NULL) {
1496 		(void) fprintf(stderr, gettext(
1497 		    "%s: cannot malloc space\n"), prog_name);
1498 		exit(NOALLOC);
1499 	}
1500 	s = strcpy(buff, s);
1501 
1502 	if ((p1 = p2 =  strstr(s, "_c_")) == NULL)
1503 		if ((p1 = p2 =  strstr(s, "_C_")) == NULL)
1504 			if ((p1 = p2 =  strstr(s, "_cc_")) == NULL)
1505 				if ((p1 = p2 =  strstr(s, "_cxx_")) == NULL)
1506 					if ((p1 = p2 = strstr(s, "_h_")) ==
1507 					    NULL)
1508 			yes = 0;
1509 			else
1510 						p2 += 2;
1511 				else
1512 					p2 += 4;
1513 			else
1514 				p2 += 3;
1515 		else
1516 			p2 += 2;
1517 	else
1518 		p2 += 2;
1519 
1520 	if (yes) {
1521 	*p1 = '.';
1522 		c = *p2;
1523 		*p2 = '\0';
1524 	}
1525 
1526 	for (s = p1;  *s != '_';  --s)
1527 		;
1528 	++s;
1529 
1530 	(void) sprintf(d_buf, str, s);
1531 
1532 	if (yes) {
1533 		*p1 = '_';
1534 		*p2 = c;
1535 	}
1536 }
1537