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