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