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